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
28namespace WebCore {
29
30SVGTextMetricsBuilder::SVGTextMetricsBuilder()
31 : m_text(0)
32 , m_run(StringView())
33 , m_textPosition(0)
34 , m_isComplexText(false)
35 , m_totalWidth(0)
36{
37}
38
39inline 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
44bool 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
58void 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
73void 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
91void 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
109struct 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
126void 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
173void 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
199void 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
209void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(RenderSVGText& textRoot, RenderSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap)
210{
211 MeasureTextData data(&allCharactersMap);
212 walkTree(textRoot, stopAtLeaf, &data);
213}
214
215}
216