| 1 | /* |
| 2 | * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. |
| 3 | * |
| 4 | * This library is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU Library General Public |
| 6 | * License as published by the Free Software Foundation; either |
| 7 | * version 2 of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * This library is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | * Library General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU Library General Public License |
| 15 | * along with this library; see the file COPYING.LIB. If not, write to |
| 16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 17 | * Boston, MA 02110-1301, USA. |
| 18 | */ |
| 19 | |
| 20 | #include "config.h" |
| 21 | #include "SVGTextMetricsBuilder.h" |
| 22 | |
| 23 | #include "RenderChildIterator.h" |
| 24 | #include "RenderSVGInline.h" |
| 25 | #include "RenderSVGInlineText.h" |
| 26 | #include "RenderSVGText.h" |
| 27 | |
| 28 | namespace WebCore { |
| 29 | |
| 30 | SVGTextMetricsBuilder::SVGTextMetricsBuilder() |
| 31 | : m_text(0) |
| 32 | , m_run(StringView()) |
| 33 | , m_textPosition(0) |
| 34 | , m_isComplexText(false) |
| 35 | , m_totalWidth(0) |
| 36 | { |
| 37 | } |
| 38 | |
| 39 | inline bool SVGTextMetricsBuilder::currentCharacterStartsSurrogatePair() const |
| 40 | { |
| 41 | return U16_IS_LEAD(m_run[m_textPosition]) && (m_textPosition + 1) < m_run.length() && U16_IS_TRAIL(m_run[m_textPosition + 1]); |
| 42 | } |
| 43 | |
| 44 | bool SVGTextMetricsBuilder::advance() |
| 45 | { |
| 46 | m_textPosition += m_currentMetrics.length(); |
| 47 | if (m_textPosition >= m_run.length()) |
| 48 | return false; |
| 49 | |
| 50 | if (m_isComplexText) |
| 51 | advanceComplexText(); |
| 52 | else |
| 53 | advanceSimpleText(); |
| 54 | |
| 55 | return m_currentMetrics.length() > 0; |
| 56 | } |
| 57 | |
| 58 | void SVGTextMetricsBuilder::advanceSimpleText() |
| 59 | { |
| 60 | GlyphBuffer glyphBuffer; |
| 61 | unsigned metricsLength = m_simpleWidthIterator->advance(m_textPosition + 1, &glyphBuffer); |
| 62 | if (!metricsLength) { |
| 63 | m_currentMetrics = SVGTextMetrics(); |
| 64 | return; |
| 65 | } |
| 66 | |
| 67 | float currentWidth = m_simpleWidthIterator->runWidthSoFar() - m_totalWidth; |
| 68 | m_totalWidth = m_simpleWidthIterator->runWidthSoFar(); |
| 69 | |
| 70 | m_currentMetrics = SVGTextMetrics(*m_text, metricsLength, currentWidth); |
| 71 | } |
| 72 | |
| 73 | void SVGTextMetricsBuilder::advanceComplexText() |
| 74 | { |
| 75 | unsigned metricsLength = currentCharacterStartsSurrogatePair() ? 2 : 1; |
| 76 | m_currentMetrics = SVGTextMetrics::measureCharacterRange(*m_text, m_textPosition, metricsLength); |
| 77 | m_complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(*m_text, 0, m_textPosition + metricsLength); |
| 78 | ASSERT(m_currentMetrics.length() == metricsLength); |
| 79 | |
| 80 | // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken |
| 81 | // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping. |
| 82 | // So whenever currentWidth != currentMetrics.width(), we are processing a text run whose length is |
| 83 | // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated. |
| 84 | float currentWidth = m_complexStartToCurrentMetrics.width() - m_totalWidth; |
| 85 | if (currentWidth != m_currentMetrics.width()) |
| 86 | m_currentMetrics.setWidth(currentWidth); |
| 87 | |
| 88 | m_totalWidth = m_complexStartToCurrentMetrics.width(); |
| 89 | } |
| 90 | |
| 91 | void SVGTextMetricsBuilder::initializeMeasurementWithTextRenderer(RenderSVGInlineText& text) |
| 92 | { |
| 93 | m_text = &text; |
| 94 | m_textPosition = 0; |
| 95 | m_currentMetrics = SVGTextMetrics(); |
| 96 | m_complexStartToCurrentMetrics = SVGTextMetrics(); |
| 97 | m_totalWidth = 0; |
| 98 | |
| 99 | const FontCascade& scaledFont = text.scaledFont(); |
| 100 | m_run = SVGTextMetrics::constructTextRun(text); |
| 101 | m_isComplexText = scaledFont.codePath(m_run) == FontCascade::Complex; |
| 102 | |
| 103 | if (m_isComplexText) |
| 104 | m_simpleWidthIterator = nullptr; |
| 105 | else |
| 106 | m_simpleWidthIterator = std::make_unique<WidthIterator>(&scaledFont, m_run); |
| 107 | } |
| 108 | |
| 109 | struct MeasureTextData { |
| 110 | MeasureTextData(SVGCharacterDataMap* characterDataMap) |
| 111 | : allCharactersMap(characterDataMap) |
| 112 | , lastCharacter(0) |
| 113 | , processRenderer(false) |
| 114 | , valueListPosition(0) |
| 115 | , skippedCharacters(0) |
| 116 | { |
| 117 | } |
| 118 | |
| 119 | SVGCharacterDataMap* allCharactersMap; |
| 120 | UChar lastCharacter; |
| 121 | bool processRenderer; |
| 122 | unsigned valueListPosition; |
| 123 | unsigned skippedCharacters; |
| 124 | }; |
| 125 | |
| 126 | void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText& text, MeasureTextData* data) |
| 127 | { |
| 128 | SVGTextLayoutAttributes* attributes = text.layoutAttributes(); |
| 129 | Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues(); |
| 130 | if (data->processRenderer) { |
| 131 | if (data->allCharactersMap) |
| 132 | attributes->clear(); |
| 133 | else |
| 134 | textMetricsValues->clear(); |
| 135 | } |
| 136 | |
| 137 | initializeMeasurementWithTextRenderer(text); |
| 138 | bool preserveWhiteSpace = text.style().whiteSpace() == WhiteSpace::Pre; |
| 139 | int surrogatePairCharacters = 0; |
| 140 | |
| 141 | while (advance()) { |
| 142 | UChar currentCharacter = m_run[m_textPosition]; |
| 143 | if (currentCharacter == ' ' && !preserveWhiteSpace && (!data->lastCharacter || data->lastCharacter == ' ')) { |
| 144 | if (data->processRenderer) |
| 145 | textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics)); |
| 146 | if (data->allCharactersMap) |
| 147 | data->skippedCharacters += m_currentMetrics.length(); |
| 148 | continue; |
| 149 | } |
| 150 | |
| 151 | if (data->processRenderer) { |
| 152 | if (data->allCharactersMap) { |
| 153 | const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + m_textPosition - data->skippedCharacters - surrogatePairCharacters + 1); |
| 154 | if (it != data->allCharactersMap->end()) |
| 155 | attributes->characterDataMap().set(m_textPosition + 1, it->value); |
| 156 | } |
| 157 | textMetricsValues->append(m_currentMetrics); |
| 158 | } |
| 159 | |
| 160 | if (data->allCharactersMap && currentCharacterStartsSurrogatePair()) |
| 161 | surrogatePairCharacters++; |
| 162 | |
| 163 | data->lastCharacter = currentCharacter; |
| 164 | } |
| 165 | |
| 166 | if (!data->allCharactersMap) |
| 167 | return; |
| 168 | |
| 169 | data->valueListPosition += m_textPosition - data->skippedCharacters; |
| 170 | data->skippedCharacters = 0; |
| 171 | } |
| 172 | |
| 173 | void SVGTextMetricsBuilder::walkTree(RenderElement& start, RenderSVGInlineText* stopAtLeaf, MeasureTextData* data) |
| 174 | { |
| 175 | for (auto& child : childrenOfType<RenderObject>(start)) { |
| 176 | if (is<RenderSVGInlineText>(child)) { |
| 177 | auto& text = downcast<RenderSVGInlineText>(child); |
| 178 | if (stopAtLeaf && stopAtLeaf != &text) { |
| 179 | data->processRenderer = false; |
| 180 | measureTextRenderer(text, data); |
| 181 | continue; |
| 182 | } |
| 183 | |
| 184 | data->processRenderer = true; |
| 185 | measureTextRenderer(text, data); |
| 186 | if (stopAtLeaf) |
| 187 | return; |
| 188 | |
| 189 | continue; |
| 190 | } |
| 191 | |
| 192 | if (!is<RenderSVGInline>(child)) |
| 193 | continue; |
| 194 | |
| 195 | walkTree(downcast<RenderSVGInline>(child), stopAtLeaf, data); |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText& text) |
| 200 | { |
| 201 | auto* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text); |
| 202 | if (!textRoot) |
| 203 | return; |
| 204 | |
| 205 | MeasureTextData data(nullptr); |
| 206 | walkTree(*textRoot, &text, &data); |
| 207 | } |
| 208 | |
| 209 | void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(RenderSVGText& textRoot, RenderSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap) |
| 210 | { |
| 211 | MeasureTextData data(&allCharactersMap); |
| 212 | walkTree(textRoot, stopAtLeaf, &data); |
| 213 | } |
| 214 | |
| 215 | } |
| 216 | |