Unreviewed, rolling out r116498.
[WebKit-https.git] / 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     : m_textLength(0)
36 {
37 }
38
39 void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextRenderer(RenderSVGInlineText* text)
40 {
41     ASSERT(text);
42
43     RenderSVGText* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text);
44     if (!textRoot)
45         return;
46
47     if (!buildLayoutAttributesIfNeeded(textRoot))
48         return;
49
50     m_metricsBuilder.buildMetricsAndLayoutAttributes(textRoot, text, m_characterDataMap);
51 }
52
53 void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForWholeTree(RenderSVGText* textRoot)
54 {
55     ASSERT(textRoot);
56
57     if (!buildLayoutAttributesIfNeeded(textRoot))
58         return;
59
60     m_metricsBuilder.buildMetricsAndLayoutAttributes(textRoot, 0, m_characterDataMap);
61 }
62
63 void SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer(RenderSVGInlineText* text)
64 {
65     ASSERT(text);
66     m_metricsBuilder.measureTextRenderer(text);
67 }
68
69 void SVGTextLayoutAttributesBuilder::rebuildMetricsForWholeTree(RenderSVGText* textRoot)
70 {
71     ASSERT(textRoot);
72     Vector<SVGTextLayoutAttributes*>& layoutAttributes = textRoot->layoutAttributes();
73
74     size_t layoutAttributesSize = layoutAttributes.size();
75     for (size_t i = 0; i < layoutAttributesSize; ++i)
76         m_metricsBuilder.measureTextRenderer(layoutAttributes[i]->context());
77 }
78
79 bool SVGTextLayoutAttributesBuilder::buildLayoutAttributesIfNeeded(RenderSVGText* textRoot)
80 {
81     ASSERT(textRoot);
82
83     if (m_textPositions.isEmpty()) {
84         m_textLength = 0;
85         const UChar* lastCharacter = 0;
86         collectTextPositioningElements(textRoot, lastCharacter);
87     }
88
89     m_characterDataMap.clear();
90     if (!m_textLength)
91         return false;
92
93     buildLayoutAttributes(textRoot);
94     return true;
95 }
96
97 static inline void processRenderSVGInlineText(RenderSVGInlineText* text, unsigned& atCharacter, const UChar*& lastCharacter)
98 {
99     if (text->style()->whiteSpace() == PRE) {
100         atCharacter += text->textLength();
101         return;
102     }
103
104     const UChar* characters = text->characters();
105     unsigned textLength = text->textLength();    
106     for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) {
107         const UChar* currentCharacter = characters + textPosition;
108         if (*currentCharacter == ' ' && (!lastCharacter || *lastCharacter == ' '))
109             continue;
110
111         lastCharacter = currentCharacter;
112         ++atCharacter;
113     }
114 }
115
116 void SVGTextLayoutAttributesBuilder::collectTextPositioningElements(RenderObject* start, const UChar*& lastCharacter)
117 {
118     ASSERT(!start->isSVGText() || m_textPositions.isEmpty());
119
120     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 
121         if (child->isSVGInlineText()) {
122             processRenderSVGInlineText(toRenderSVGInlineText(child), m_textLength, lastCharacter);
123             continue;
124         }
125
126         if (!child->isSVGInline())
127             continue;
128
129         SVGTextPositioningElement* element = SVGTextPositioningElement::elementFromRenderer(child);
130         unsigned atPosition = m_textPositions.size();
131         if (element)
132             m_textPositions.append(TextPosition(element, m_textLength));
133
134         collectTextPositioningElements(child, lastCharacter);
135
136         if (!element)
137             continue;
138
139         // Update text position, after we're back from recursion.
140         TextPosition& position = m_textPositions[atPosition];
141         ASSERT(!position.length);
142         position.length = m_textLength - position.start;
143     }
144 }
145
146 void SVGTextLayoutAttributesBuilder::buildLayoutAttributes(RenderSVGText* textRoot)
147 {
148     ASSERT(m_textLength);
149
150     SVGTextPositioningElement* outermostTextElement = SVGTextPositioningElement::elementFromRenderer(textRoot);
151     ASSERT(outermostTextElement);
152
153     // Grab outermost <text> element value lists and insert them in the character data map.
154     TextPosition wholeTextPosition(outermostTextElement, 0, m_textLength);
155     fillCharacterDataMap(wholeTextPosition);
156
157     // Handle x/y default attributes.
158     SVGCharacterDataMap::iterator it = m_characterDataMap.find(1);
159     if (it == m_characterDataMap.end()) {
160         SVGCharacterData data;
161         data.x = 0;
162         data.y = 0;
163         m_characterDataMap.set(1, data);
164     } else {
165         SVGCharacterData& data = it->second;
166         if (data.x == SVGTextLayoutAttributes::emptyValue())
167             data.x = 0;
168         if (data.y == SVGTextLayoutAttributes::emptyValue())
169             data.y = 0;
170     }
171
172     // Fill character data map using child text positioning elements in top-down order. 
173     unsigned size = m_textPositions.size();
174     for (unsigned i = 0; i < size; ++i)
175         fillCharacterDataMap(m_textPositions[i]);
176
177 #if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0
178     fprintf(stderr, "\nDumping ALL layout attributes for RenderSVGText, renderer=%p, node=%p (m_textLength: %i)\n", textRoot, textRoot->node(), m_textLength);
179     m_characterDataMap.dump();
180 #endif
181 }
182
183 static inline void updateCharacterData(unsigned i, float& lastRotation, SVGCharacterData& data, const SVGLengthContext& lengthContext, const SVGLengthList* xList, const SVGLengthList* yList, const SVGLengthList* dxList, const SVGLengthList* dyList, const SVGNumberList* rotateList)
184 {
185     if (xList)
186         data.x = xList->at(i).value(lengthContext);
187     if (yList)
188         data.y = yList->at(i).value(lengthContext);
189     if (dxList)
190         data.dx = dxList->at(i).value(lengthContext);
191     if (dyList)
192         data.dy = dyList->at(i).value(lengthContext);
193     if (rotateList) {
194         data.rotate = rotateList->at(i);
195         lastRotation = data.rotate;
196     }
197 }
198
199 void SVGTextLayoutAttributesBuilder::fillCharacterDataMap(const TextPosition& position)
200 {
201     const SVGLengthList& xList = position.element->x();
202     const SVGLengthList& yList = position.element->y();
203     const SVGLengthList& dxList = position.element->dx();
204     const SVGLengthList& dyList = position.element->dy();
205     const SVGNumberList& rotateList = position.element->rotate();
206
207     unsigned xListSize = xList.size();
208     unsigned yListSize = yList.size();
209     unsigned dxListSize = dxList.size();
210     unsigned dyListSize = dyList.size();
211     unsigned rotateListSize = rotateList.size();
212     if (!xListSize && !yListSize && !dxListSize && !dyListSize && !rotateListSize)
213         return;
214
215     float lastRotation = SVGTextLayoutAttributes::emptyValue();
216     SVGLengthContext lengthContext(position.element);
217     for (unsigned i = 0; i < position.length; ++i) {
218         const SVGLengthList* xListPtr = i < xListSize ? &xList : 0;
219         const SVGLengthList* yListPtr = i < yListSize ? &yList : 0;
220         const SVGLengthList* dxListPtr = i < dxListSize ? &dxList : 0;
221         const SVGLengthList* dyListPtr = i < dyListSize ? &dyList : 0;
222         const SVGNumberList* rotateListPtr = i < rotateListSize ? &rotateList : 0;
223         if (!xListPtr && !yListPtr && !dxListPtr && !dyListPtr && !rotateListPtr)
224             break;
225
226         SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1);
227         if (it == m_characterDataMap.end()) {
228             SVGCharacterData data;
229             updateCharacterData(i, lastRotation, data, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr);
230             m_characterDataMap.set(position.start + i + 1, data);
231             continue;
232         }
233
234         updateCharacterData(i, lastRotation, it->second, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr);
235     }
236
237     // The last rotation value always spans the whole scope.
238     if (lastRotation == SVGTextLayoutAttributes::emptyValue())
239         return;
240
241     for (unsigned i = rotateList.size(); i < position.length; ++i) {
242         SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1);
243         if (it == m_characterDataMap.end()) {
244             SVGCharacterData data;
245             data.rotate = lastRotation;
246             m_characterDataMap.set(position.start + i + 1, data);
247             continue;
248         }
249
250         it->second.rotate = lastRotation;
251     }
252 }
253
254 }
255
256 #endif // ENABLE(SVG)