dd7469f8bb56f4a7e11adaef66e2efa918e10102
[WebKit-https.git] / Source / WebCore / rendering / svg / SVGTextMetricsBuilder.cpp
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 "RenderSVGInlineText.h"
24 #include "RenderSVGText.h"
25 #include "SVGTextRunRenderingContext.h"
26
27 namespace WebCore {
28
29 SVGTextMetricsBuilder::SVGTextMetricsBuilder()
30     : m_text(0)
31     , m_run(static_cast<const UChar*>(0), 0)
32     , m_textPosition(0)
33     , m_isComplexText(false)
34     , m_totalWidth(0)
35 {
36 }
37
38 inline bool SVGTextMetricsBuilder::currentCharacterStartsSurrogatePair() const
39 {
40     return U16_IS_LEAD(m_run[m_textPosition]) && int(m_textPosition + 1) < m_run.charactersLength() && U16_IS_TRAIL(m_run[m_textPosition + 1]);
41 }
42
43 bool SVGTextMetricsBuilder::advance()
44 {
45     m_textPosition += m_currentMetrics.length();
46     if (int(m_textPosition) >= m_run.charactersLength())
47         return false;
48
49     if (m_isComplexText)
50         advanceComplexText();
51     else
52         advanceSimpleText();
53
54     return m_currentMetrics.length() > 0;
55 }
56
57 void SVGTextMetricsBuilder::advanceSimpleText()
58 {
59     GlyphBuffer glyphBuffer;
60     unsigned metricsLength = m_simpleWidthIterator->advance(m_textPosition + 1, &glyphBuffer);
61     if (!metricsLength) {
62         m_currentMetrics = SVGTextMetrics();
63         return;
64     }
65
66     float currentWidth = m_simpleWidthIterator->runWidthSoFar() - m_totalWidth;
67     m_totalWidth = m_simpleWidthIterator->runWidthSoFar();
68
69 #if ENABLE(SVG_FONTS)
70     m_currentMetrics = SVGTextMetrics(*m_text, m_textPosition, metricsLength, currentWidth, m_simpleWidthIterator->lastGlyphName());
71 #else
72     m_currentMetrics = SVGTextMetrics(*m_text, m_textPosition, metricsLength, currentWidth, emptyString());
73 #endif
74 }
75
76 void SVGTextMetricsBuilder::advanceComplexText()
77 {
78     unsigned metricsLength = currentCharacterStartsSurrogatePair() ? 2 : 1;
79     m_currentMetrics = SVGTextMetrics::measureCharacterRange(*m_text, m_textPosition, metricsLength);
80     m_complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(*m_text, 0, m_textPosition + metricsLength);
81     ASSERT(m_currentMetrics.length() == metricsLength);
82
83     // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken
84     // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping.
85     // So whenever currentWidth != currentMetrics.width(), we are processing a text run whose length is
86     // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated.
87     float currentWidth = m_complexStartToCurrentMetrics.width() - m_totalWidth;
88     if (currentWidth != m_currentMetrics.width())
89         m_currentMetrics.setWidth(currentWidth);
90
91     m_totalWidth = m_complexStartToCurrentMetrics.width();
92 }
93
94 void SVGTextMetricsBuilder::initializeMeasurementWithTextRenderer(RenderSVGInlineText* text)
95 {
96     m_text = text;
97     m_textPosition = 0;
98     m_currentMetrics = SVGTextMetrics();
99     m_complexStartToCurrentMetrics = SVGTextMetrics();
100     m_totalWidth = 0;
101
102     const Font& scaledFont = text->scaledFont();
103     m_run = SVGTextMetrics::constructTextRun(*text);
104     m_isComplexText = scaledFont.codePath(m_run) == Font::Complex;
105
106     if (m_isComplexText)
107         m_simpleWidthIterator.clear();
108     else
109         m_simpleWidthIterator = adoptPtr(new WidthIterator(&scaledFont, m_run));
110 }
111
112 struct MeasureTextData {
113     MeasureTextData(SVGCharacterDataMap* characterDataMap)
114         : allCharactersMap(characterDataMap)
115         , lastCharacter(0)
116         , processRenderer(false)
117         , valueListPosition(0)
118         , skippedCharacters(0)
119     {
120     }
121
122     SVGCharacterDataMap* allCharactersMap;
123     UChar lastCharacter;
124     bool processRenderer;
125     unsigned valueListPosition;
126     unsigned skippedCharacters;
127 };
128
129 void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText* text, MeasureTextData* data)
130 {
131     ASSERT(text);
132
133     SVGTextLayoutAttributes* attributes = text->layoutAttributes();
134     Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues();
135     if (data->processRenderer) {
136         if (data->allCharactersMap)
137             attributes->clear();
138         else
139             textMetricsValues->clear();
140     }
141
142     initializeMeasurementWithTextRenderer(text);
143     bool preserveWhiteSpace = text->style().whiteSpace() == PRE;
144     int surrogatePairCharacters = 0;
145
146     while (advance()) {
147         UChar currentCharacter = m_run[m_textPosition];
148         if (currentCharacter == ' ' && !preserveWhiteSpace && (!data->lastCharacter || data->lastCharacter == ' ')) {
149             if (data->processRenderer)
150                 textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics));
151             if (data->allCharactersMap)
152                 data->skippedCharacters += m_currentMetrics.length();
153             continue;
154         }
155
156         if (data->processRenderer) {
157             if (data->allCharactersMap) {
158                 const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + m_textPosition - data->skippedCharacters - surrogatePairCharacters + 1);
159                 if (it != data->allCharactersMap->end())
160                     attributes->characterDataMap().set(m_textPosition + 1, it->value);
161             }
162             textMetricsValues->append(m_currentMetrics);
163         }
164
165         if (data->allCharactersMap && currentCharacterStartsSurrogatePair())
166             surrogatePairCharacters++;
167
168         data->lastCharacter = currentCharacter;
169     }
170
171     if (!data->allCharactersMap)
172         return;
173
174     data->valueListPosition += m_textPosition - data->skippedCharacters;
175     data->skippedCharacters = 0;
176 }
177
178 void SVGTextMetricsBuilder::walkTree(RenderElement& start, RenderSVGInlineText* stopAtLeaf, MeasureTextData* data)
179 {
180     for (auto child = start.firstChild(); child; child = child->nextSibling()) {
181         if (child->isSVGInlineText()) {
182             RenderSVGInlineText* text = toRenderSVGInlineText(child);
183             if (stopAtLeaf && stopAtLeaf != text) {
184                 data->processRenderer = false;
185                 measureTextRenderer(text, data);
186                 continue;
187             }
188
189             data->processRenderer = true;
190             measureTextRenderer(text, data);
191             if (stopAtLeaf)
192                 return;
193
194             continue;
195         }
196
197         if (!child->isSVGInline())
198             continue;
199
200         walkTree(toRenderElement(*child), stopAtLeaf, data);
201     }
202 }
203
204 void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText* text)
205 {
206     ASSERT(text);
207
208     auto* textRoot = RenderSVGText::locateRenderSVGTextAncestor(*text);
209     if (!textRoot)
210         return;
211
212     MeasureTextData data(0);
213     walkTree(*textRoot, text, &data);
214 }
215
216 void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(RenderSVGText* textRoot, RenderSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap)
217 {
218     ASSERT(textRoot);
219     MeasureTextData data(&allCharactersMap);
220     walkTree(*textRoot, stopAtLeaf, &data);
221 }
222
223 }