initial import
[vuplus_webkit] / Source / WebCore / rendering / svg / SVGTextLayoutAttributesBuilder.cpp
1 /*
2  * Copyright (C) Research In Motion Limited 2010-2011. 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 #if ENABLE(SVG)
23 #include "SVGTextLayoutAttributesBuilder.h"
24
25 #include "RenderSVGInlineText.h"
26 #include "RenderSVGText.h"
27 #include "SVGTextPositioningElement.h"
28
29 // Set to a value > 0 to dump the text layout attributes
30 #define DUMP_TEXT_LAYOUT_ATTRIBUTES 0
31
32 namespace WebCore {
33
34 SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder()
35 {
36 }
37
38 void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextSubtree(RenderSVGText* textRoot)
39 {
40     ASSERT(textRoot);
41
42     // We always clear our current attribute as we don't want to keep any stale ones that could survive DOM modification.
43     Vector<SVGTextLayoutAttributes>& allAttributes = textRoot->layoutAttributes();
44     allAttributes.clear();
45
46      // Build list of x/y/dx/dy/rotate values for each subtree element that may define these values (tspan/textPath etc).
47     unsigned atCharacter = 0;
48     UChar lastCharacter = '\0';
49     collectTextPositioningElements(textRoot, atCharacter, lastCharacter);
50
51     if (!atCharacter)
52         return;
53
54     // Collect x/y/dx/dy/rotate values for each character, stored in the m_positioningLists.xValues()/etc. lists.
55     buildLayoutAttributesForAllCharacters(textRoot, atCharacter);
56
57     // Propagate layout attributes to each RenderSVGInlineText object, and the whole list to the RenderSVGText root.
58     atCharacter = 0;
59     lastCharacter = '\0';
60     propagateLayoutAttributes(textRoot, allAttributes, atCharacter, lastCharacter);
61 }
62
63 static inline void extractFloatValuesFromSVGLengthList(SVGElement* lengthContext, const SVGLengthList& list, Vector<float>& floatValues, unsigned textContentLength)
64 {
65     ASSERT(lengthContext);
66
67     unsigned length = list.size();
68     if (length > textContentLength)
69         length = textContentLength;
70     floatValues.reserveCapacity(length);
71
72     for (unsigned i = 0; i < length; ++i) {
73         const SVGLength& length = list.at(i);
74         floatValues.append(length.value(lengthContext));
75     }
76 }
77
78 static inline void extractFloatValuesFromSVGNumberList(const SVGNumberList& list, Vector<float>& floatValues, unsigned textContentLength)
79 {
80     unsigned length = list.size();
81     if (length > textContentLength)
82         length = textContentLength;
83     floatValues.reserveCapacity(length);
84
85     for (unsigned i = 0; i < length; ++i)
86         floatValues.append(list.at(i));
87 }
88
89
90 static inline bool characterIsSpace(const UChar& character)
91 {
92     return character == ' ';
93 }
94
95 static inline bool characterIsSpaceOrNull(const UChar& character)
96 {
97     return character == ' ' || character == '\0';
98 }
99
100 static inline bool shouldPreserveAllWhiteSpace(RenderStyle* style)
101 {
102     ASSERT(style);
103     return style->whiteSpace() == PRE;
104 }
105  
106 static inline void processRenderSVGInlineText(RenderSVGInlineText* text, unsigned& atCharacter, UChar& lastCharacter)
107 {
108     if (shouldPreserveAllWhiteSpace(text->style())) {
109         atCharacter += text->textLength();
110         return;
111     }
112
113     const UChar* characters = text->characters();
114     unsigned textLength = text->textLength();    
115     for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) {
116         const UChar& currentCharacter = characters[textPosition];
117         if (characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter))
118             continue;
119
120         lastCharacter = currentCharacter;
121         ++atCharacter;
122     }
123 }
124
125 void SVGTextLayoutAttributesBuilder::collectTextPositioningElements(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter)
126 {
127     ASSERT(!start->isSVGText() || m_textPositions.isEmpty());
128
129     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 
130         if (child->isSVGInlineText()) {
131             processRenderSVGInlineText(toRenderSVGInlineText(child), atCharacter, lastCharacter);
132             continue;
133         }
134
135         if (!child->isSVGInline())
136             continue;
137
138         SVGTextPositioningElement* element = SVGTextPositioningElement::elementFromRenderer(child);
139         unsigned atPosition = m_textPositions.size();
140         if (element)
141             m_textPositions.append(TextPosition(element, atCharacter));
142
143         collectTextPositioningElements(child, atCharacter, lastCharacter);
144
145         if (!element)
146             continue;
147
148         // Update text position, after we're back from recursion.
149         TextPosition& position = m_textPositions[atPosition];
150         ASSERT(!position.length);
151         position.length = atCharacter - position.start;
152     }
153 }
154
155 void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForAllCharacters(RenderSVGText* textRoot, unsigned textLength)
156 {
157     ASSERT(textLength);
158
159     SVGTextPositioningElement* outermostTextElement = SVGTextPositioningElement::elementFromRenderer(textRoot);
160     ASSERT(outermostTextElement);
161
162     // Fill the lists with the special emptyValue marker.
163     m_positioningLists.fillWithEmptyValues(textLength);
164
165     // Grab outermost <text> element value lists and insert them in the m_positioningLists.
166     TextPosition wholeTextPosition(outermostTextElement, 0, textLength);
167     fillAttributesAtPosition(wholeTextPosition);
168
169     // Handle x/y default attributes.
170     float& xFirst = m_positioningLists.xValues.first();
171     if (xFirst == SVGTextLayoutAttributes::emptyValue())
172         xFirst = 0;
173
174     float& yFirst = m_positioningLists.yValues.first();
175     if (yFirst == SVGTextLayoutAttributes::emptyValue())
176         yFirst = 0;
177
178     // Fill m_positioningLists using child text positioning elements in top-down order. 
179     unsigned size = m_textPositions.size();
180     for (unsigned i = 0; i < size; ++i)
181         fillAttributesAtPosition(m_textPositions[i]);
182
183     // Now m_positioningLists.contains a x/y/dx/dy/rotate value for each character in the <text> subtree.
184 }
185
186 void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, Vector<SVGTextLayoutAttributes>& allAttributes, unsigned& atCharacter, UChar& lastCharacter) const
187 {
188     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 
189         if (child->isSVGInlineText()) {
190             RenderSVGInlineText* text = toRenderSVGInlineText(child);
191             const UChar* characters = text->characters();
192             unsigned textLength = text->textLength();
193             bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style());
194
195             SVGTextLayoutAttributes attributes(text);
196             attributes.reserveCapacity(textLength);
197     
198             unsigned valueListPosition = atCharacter;
199             unsigned metricsLength = 1;
200             SVGTextMetrics lastMetrics = SVGTextMetrics::emptyMetrics();
201
202             for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) {
203                 const UChar& currentCharacter = characters[textPosition];
204
205                 SVGTextMetrics startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 1);
206                 SVGTextMetrics currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1);
207                 metricsLength = currentMetrics.length();
208
209                 // Eventually handle surrogate pairs here.
210                 if (!metricsLength) {
211                     if (textPosition + 1 == textLength)
212                         break;
213
214                     startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 2);
215                     currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 2);
216                     metricsLength = currentMetrics.length();
217                 }
218
219                 if (!metricsLength)
220                     break;
221                 
222                 // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken
223                 // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping.
224                 // So whenever runWidthAdvance != currentMetrics.width(), we are processing a text run whose length is
225                 // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated.
226                 float runWidthAdvance = startToCurrentMetrics.width() - lastMetrics.width();
227                 if (runWidthAdvance != currentMetrics.width())
228                     currentMetrics.setWidth(runWidthAdvance);
229
230                 lastMetrics = startToCurrentMetrics;
231
232                 if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) {
233                     attributes.positioningLists().appendEmptyValues();
234                     attributes.textMetricsValues().append(SVGTextMetrics::emptyMetrics());
235                     continue;
236                 }
237
238                 SVGTextLayoutAttributes::PositioningLists& positioningLists = attributes.positioningLists();
239                 positioningLists.appendValuesFromPosition(m_positioningLists, valueListPosition);
240                 attributes.textMetricsValues().append(currentMetrics);
241
242                 // Pad x/y/dx/dy/rotate value lists with empty values, if the metrics span more than one character.
243                 if (metricsLength > 1) {
244                     for (unsigned i = 0; i < metricsLength - 1; ++i)
245                         positioningLists.appendEmptyValues();
246                 }
247
248                 lastCharacter = currentCharacter;
249                 valueListPosition += metricsLength;
250             }
251
252 #if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0
253             fprintf(stderr, "\nDumping layout attributes for RenderSVGInlineText, renderer=%p, node=%p (atCharacter: %i)\n", text, text->node(), atCharacter);
254             fprintf(stderr, "BiDi properties: unicode-bidi=%i, block direction=%i\n", text->style()->unicodeBidi(), text->style()->direction());
255             attributes.dump();
256 #endif
257
258             text->storeLayoutAttributes(attributes);
259             allAttributes.append(attributes);
260             atCharacter = valueListPosition;
261             continue;
262         }
263
264         if (!child->isSVGInline())
265             continue;
266
267         propagateLayoutAttributes(child, allAttributes, atCharacter, lastCharacter);
268     }
269 }
270
271 static inline void fillListAtPosition(Vector<float>& allValues, Vector<float>& values, unsigned start)
272 {
273     unsigned valuesSize = values.size();
274     for (unsigned i = 0; i < valuesSize; ++i)
275         allValues[start + i] = values[i];
276 }
277
278 void SVGTextLayoutAttributesBuilder::fillAttributesAtPosition(const TextPosition& position)
279 {
280     Vector<float> values;
281     extractFloatValuesFromSVGLengthList(position.element, position.element->x(), values, position.length);
282     fillListAtPosition(m_positioningLists.xValues, values, position.start);
283
284     values.clear();
285     extractFloatValuesFromSVGLengthList(position.element, position.element->y(), values, position.length);
286     fillListAtPosition(m_positioningLists.yValues, values, position.start);
287
288     values.clear();
289     extractFloatValuesFromSVGLengthList(position.element, position.element->dx(), values, position.length);
290     fillListAtPosition(m_positioningLists.dxValues, values, position.start);
291
292     values.clear();
293     extractFloatValuesFromSVGLengthList(position.element, position.element->dy(), values, position.length);
294     fillListAtPosition(m_positioningLists.dyValues, values, position.start);
295
296     values.clear();
297     extractFloatValuesFromSVGNumberList(position.element->rotate(), values, position.length);
298     fillListAtPosition(m_positioningLists.rotateValues, values, position.start);
299
300     // The last rotation value always spans the whole scope.
301     if (values.isEmpty())
302         return;
303
304     float lastValue = values.last();
305     for (unsigned i = values.size(); i < position.length; ++i)
306         m_positioningLists.rotateValues[position.start + i] = lastValue;
307 }
308
309 }
310
311 #endif // ENABLE(SVG)