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
22 #include "core/rendering/svg/SVGTextMetricsBuilder.h"
23
24 #include "core/rendering/svg/RenderSVGInline.h"
25 #include "core/rendering/svg/RenderSVGInlineText.h"
26 #include "core/rendering/svg/RenderSVGText.h"
27 #include "core/rendering/svg/SVGTextMetrics.h"
28 #include "platform/fonts/GlyphBuffer.h"
29 #include "platform/fonts/WidthIterator.h"
30 #include "platform/text/BidiCharacterRun.h"
31 #include "platform/text/BidiResolver.h"
32 #include "platform/text/TextDirection.h"
33 #include "platform/text/TextPath.h"
34 #include "platform/text/TextRun.h"
35 #include "platform/text/TextRunIterator.h"
36 #include "wtf/Vector.h"
37
38 namespace WebCore {
39
40 class SVGTextMetricsCalculator {
41 public:
42 SVGTextMetricsCalculator(RenderSVGInlineText*);
43 ~SVGTextMetricsCalculator();
44
45 SVGTextMetrics computeMetricsForCharacter(unsigned textPosition);
textLength() const46 unsigned textLength() const { return static_cast<unsigned>(m_run.charactersLength()); }
47
characterStartsSurrogatePair(unsigned textPosition) const48 bool characterStartsSurrogatePair(unsigned textPosition) const
49 {
50 return U16_IS_LEAD(m_run[textPosition]) && textPosition + 1 < textLength() && U16_IS_TRAIL(m_run[textPosition + 1]);
51 }
characterIsWhiteSpace(unsigned textPosition) const52 bool characterIsWhiteSpace(unsigned textPosition) const
53 {
54 return m_run[textPosition] == ' ';
55 }
56
57 private:
58 void setupBidiRuns();
59 SVGTextMetrics computeMetricsForCharacterSimple(unsigned textPosition);
60 SVGTextMetrics computeMetricsForCharacterComplex(unsigned textPosition);
61
62 RenderSVGInlineText* m_text;
63 BidiCharacterRun* m_bidiRun;
64 TextRun m_run;
65 BidiResolver<TextRunIterator, BidiCharacterRun> m_bidiResolver;
66 bool m_isComplexText;
67 float m_totalWidth;
68 TextDirection m_textDirection;
69
70 // Simple text only.
71 OwnPtr<WidthIterator> m_simpleWidthIterator;
72 };
73
SVGTextMetricsCalculator(RenderSVGInlineText * text)74 SVGTextMetricsCalculator::SVGTextMetricsCalculator(RenderSVGInlineText* text)
75 : m_text(text)
76 , m_bidiRun(0)
77 , m_run(SVGTextMetrics::constructTextRun(text, 0, text->textLength()))
78 , m_isComplexText(false)
79 , m_totalWidth(0)
80 {
81 const Font& scaledFont = text->scaledFont();
82 CodePath codePath = scaledFont.codePath(m_run);
83 m_isComplexText = codePath == ComplexPath;
84 m_run.setCharacterScanForCodePath(!m_isComplexText);
85
86 if (!m_isComplexText)
87 m_simpleWidthIterator = adoptPtr(new WidthIterator(&scaledFont, m_run));
88 else
89 setupBidiRuns();
90 }
91
~SVGTextMetricsCalculator()92 SVGTextMetricsCalculator::~SVGTextMetricsCalculator()
93 {
94 if (m_bidiRun)
95 m_bidiResolver.runs().deleteRuns();
96 }
97
setupBidiRuns()98 void SVGTextMetricsCalculator::setupBidiRuns()
99 {
100 RenderStyle* style = m_text->style();
101 m_textDirection = style->direction();
102 if (isOverride(style->unicodeBidi()))
103 return;
104
105 BidiStatus status(LTR, false);
106 status.last = status.lastStrong = WTF::Unicode::OtherNeutral;
107 m_bidiResolver.setStatus(status);
108 m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0));
109 const bool hardLineBreak = false;
110 const bool reorderRuns = false;
111 m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.length()), NoVisualOverride, hardLineBreak, reorderRuns);
112 BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs();
113 m_bidiRun = bidiRuns.firstRun();
114 }
115
computeMetricsForCharacterSimple(unsigned textPosition)116 SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacterSimple(unsigned textPosition)
117 {
118 GlyphBuffer glyphBuffer;
119 unsigned metricsLength = m_simpleWidthIterator->advance(textPosition + 1, &glyphBuffer);
120 if (!metricsLength)
121 return SVGTextMetrics();
122
123 float currentWidth = m_simpleWidthIterator->runWidthSoFar() - m_totalWidth;
124 m_totalWidth = m_simpleWidthIterator->runWidthSoFar();
125
126 Glyph glyphId = glyphBuffer.glyphAt(0);
127 return SVGTextMetrics(m_text, textPosition, metricsLength, currentWidth, glyphId);
128 }
129
computeMetricsForCharacterComplex(unsigned textPosition)130 SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacterComplex(unsigned textPosition)
131 {
132 unsigned metricsLength = characterStartsSurrogatePair(textPosition) ? 2 : 1;
133 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(m_text, textPosition, metricsLength, m_textDirection);
134 ASSERT(metrics.length() == metricsLength);
135
136 unsigned startPosition = m_bidiRun ? m_bidiRun->start() : 0;
137 ASSERT(startPosition <= textPosition);
138 SVGTextMetrics complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(m_text, startPosition, textPosition - startPosition + metricsLength, m_textDirection);
139 // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken
140 // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping.
141 // So whenever currentWidth != currentMetrics.width(), we are processing a text run whose length is
142 // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated.
143 float currentWidth = complexStartToCurrentMetrics.width() - m_totalWidth;
144 if (currentWidth != metrics.width())
145 metrics.setWidth(currentWidth);
146
147 m_totalWidth = complexStartToCurrentMetrics.width();
148 return metrics;
149 }
150
computeMetricsForCharacter(unsigned textPosition)151 SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacter(unsigned textPosition)
152 {
153 if (m_bidiRun) {
154 if (textPosition >= static_cast<unsigned>(m_bidiRun->stop())) {
155 m_bidiRun = m_bidiRun->next();
156 // New BiDi run means new reference position for measurements, so reset |m_totalWidth|.
157 m_totalWidth = 0;
158 }
159 ASSERT(m_bidiRun);
160 ASSERT(static_cast<int>(textPosition) < m_bidiRun->stop());
161 m_textDirection = m_bidiRun->direction();
162 }
163
164 if (m_isComplexText)
165 return computeMetricsForCharacterComplex(textPosition);
166
167 return computeMetricsForCharacterSimple(textPosition);
168 }
169
170 struct MeasureTextData {
MeasureTextDataWebCore::MeasureTextData171 MeasureTextData(SVGCharacterDataMap* characterDataMap)
172 : allCharactersMap(characterDataMap)
173 , lastCharacterWasWhiteSpace(true)
174 , valueListPosition(0)
175 {
176 }
177
178 SVGCharacterDataMap* allCharactersMap;
179 bool lastCharacterWasWhiteSpace;
180 unsigned valueListPosition;
181 };
182
measureTextRenderer(RenderSVGInlineText * text,MeasureTextData * data,bool processRenderer)183 static void measureTextRenderer(RenderSVGInlineText* text, MeasureTextData* data, bool processRenderer)
184 {
185 ASSERT(text);
186
187 SVGTextLayoutAttributes* attributes = text->layoutAttributes();
188 Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues();
189 if (processRenderer) {
190 if (data->allCharactersMap)
191 attributes->clear();
192 else
193 textMetricsValues->clear();
194 }
195
196 SVGTextMetricsCalculator calculator(text);
197 bool preserveWhiteSpace = text->style()->whiteSpace() == PRE;
198 unsigned surrogatePairCharacters = 0;
199 unsigned skippedCharacters = 0;
200 unsigned textPosition = 0;
201 unsigned textLength = calculator.textLength();
202
203 SVGTextMetrics currentMetrics;
204 for (; textPosition < textLength; textPosition += currentMetrics.length()) {
205 currentMetrics = calculator.computeMetricsForCharacter(textPosition);
206 if (!currentMetrics.length())
207 break;
208
209 bool characterIsWhiteSpace = calculator.characterIsWhiteSpace(textPosition);
210 if (characterIsWhiteSpace && !preserveWhiteSpace && data->lastCharacterWasWhiteSpace) {
211 if (processRenderer)
212 textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics));
213 if (data->allCharactersMap)
214 skippedCharacters += currentMetrics.length();
215 continue;
216 }
217
218 if (processRenderer) {
219 if (data->allCharactersMap) {
220 const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + textPosition - skippedCharacters - surrogatePairCharacters + 1);
221 if (it != data->allCharactersMap->end())
222 attributes->characterDataMap().set(textPosition + 1, it->value);
223 }
224 textMetricsValues->append(currentMetrics);
225 }
226
227 if (data->allCharactersMap && calculator.characterStartsSurrogatePair(textPosition))
228 surrogatePairCharacters++;
229
230 data->lastCharacterWasWhiteSpace = characterIsWhiteSpace;
231 }
232
233 if (!data->allCharactersMap)
234 return;
235
236 data->valueListPosition += textPosition - skippedCharacters;
237 }
238
walkTree(RenderSVGText * start,RenderSVGInlineText * stopAtLeaf,MeasureTextData * data)239 static void walkTree(RenderSVGText* start, RenderSVGInlineText* stopAtLeaf, MeasureTextData* data)
240 {
241 RenderObject* child = start->firstChild();
242 while (child) {
243 if (child->isSVGInlineText()) {
244 RenderSVGInlineText* text = toRenderSVGInlineText(child);
245 measureTextRenderer(text, data, !stopAtLeaf || stopAtLeaf == text);
246 if (stopAtLeaf && stopAtLeaf == text)
247 return;
248 } else if (child->isSVGInline()) {
249 // Visit children of text content elements.
250 if (RenderObject* inlineChild = toRenderSVGInline(child)->firstChild()) {
251 child = inlineChild;
252 continue;
253 }
254 }
255 child = child->nextInPreOrderAfterChildren(start);
256 }
257 }
258
measureTextRenderer(RenderSVGInlineText * text)259 void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText* text)
260 {
261 ASSERT(text);
262
263 RenderSVGText* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text);
264 if (!textRoot)
265 return;
266
267 MeasureTextData data(0);
268 walkTree(textRoot, text, &data);
269 }
270
buildMetricsAndLayoutAttributes(RenderSVGText * textRoot,RenderSVGInlineText * stopAtLeaf,SVGCharacterDataMap & allCharactersMap)271 void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(RenderSVGText* textRoot, RenderSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap)
272 {
273 ASSERT(textRoot);
274 MeasureTextData data(&allCharactersMap);
275 walkTree(textRoot, stopAtLeaf, &data);
276 }
277
278 }
279