• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) Research In Motion Limited 2010. 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 "SVGTextChunkBuilder.h"
24 
25 #include "RenderSVGInlineText.h"
26 #include "SVGElement.h"
27 #include "SVGInlineTextBox.h"
28 
29 namespace WebCore {
30 
SVGTextChunkBuilder()31 SVGTextChunkBuilder::SVGTextChunkBuilder()
32 {
33 }
34 
transformationForTextBox(SVGInlineTextBox * textBox,AffineTransform & transform) const35 void SVGTextChunkBuilder::transformationForTextBox(SVGInlineTextBox* textBox, AffineTransform& transform) const
36 {
37     DEFINE_STATIC_LOCAL(const AffineTransform, s_identityTransform, ());
38     if (!m_textBoxTransformations.contains(textBox)) {
39         transform = s_identityTransform;
40         return;
41     }
42 
43     transform = m_textBoxTransformations.get(textBox);
44 }
45 
buildTextChunks(Vector<SVGInlineTextBox * > & lineLayoutBoxes)46 void SVGTextChunkBuilder::buildTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes)
47 {
48     if (lineLayoutBoxes.isEmpty())
49         return;
50 
51     bool foundStart = false;
52     unsigned lastChunkStartPosition = 0;
53     unsigned boxPosition = 0;
54     unsigned boxCount = lineLayoutBoxes.size();
55     for (; boxPosition < boxCount; ++boxPosition) {
56         SVGInlineTextBox* textBox = lineLayoutBoxes[boxPosition];
57         if (!textBox->startsNewTextChunk())
58             continue;
59 
60         if (!foundStart) {
61             lastChunkStartPosition = boxPosition;
62             foundStart = true;
63         } else {
64             ASSERT(boxPosition > lastChunkStartPosition);
65             addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition);
66             lastChunkStartPosition = boxPosition;
67         }
68     }
69 
70     if (!foundStart)
71         return;
72 
73     if (boxPosition - lastChunkStartPosition > 0)
74         addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition);
75 }
76 
layoutTextChunks(Vector<SVGInlineTextBox * > & lineLayoutBoxes)77 void SVGTextChunkBuilder::layoutTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes)
78 {
79     buildTextChunks(lineLayoutBoxes);
80     if (m_textChunks.isEmpty())
81         return;
82 
83     unsigned chunkCount = m_textChunks.size();
84     for (unsigned i = 0; i < chunkCount; ++i)
85         processTextChunk(m_textChunks[i]);
86 
87     m_textChunks.clear();
88 }
89 
addTextChunk(Vector<SVGInlineTextBox * > & lineLayoutBoxes,unsigned boxStart,unsigned boxCount)90 void SVGTextChunkBuilder::addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxStart, unsigned boxCount)
91 {
92     SVGInlineTextBox* textBox = lineLayoutBoxes[boxStart];
93     ASSERT(textBox);
94 
95     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer());
96     ASSERT(textRenderer);
97 
98     const RenderStyle* style = textRenderer->style();
99     ASSERT(style);
100 
101     const SVGRenderStyle* svgStyle = style->svgStyle();
102     ASSERT(svgStyle);
103 
104     // Build chunk style flags.
105     unsigned chunkStyle = SVGTextChunk::DefaultStyle;
106 
107     // Handle 'direction' property.
108     if (!style->isLeftToRightDirection())
109         chunkStyle |= SVGTextChunk::RightToLeftText;
110 
111     // Handle 'writing-mode' property.
112     if (svgStyle->isVerticalWritingMode())
113         chunkStyle |= SVGTextChunk::VerticalText;
114 
115     // Handle 'text-anchor' property.
116     switch (svgStyle->textAnchor()) {
117     case TA_START:
118         break;
119     case TA_MIDDLE:
120         chunkStyle |= SVGTextChunk::MiddleAnchor;
121         break;
122     case TA_END:
123         chunkStyle |= SVGTextChunk::EndAnchor;
124         break;
125     };
126 
127     // Handle 'lengthAdjust' property.
128     float desiredTextLength = 0;
129     if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textRenderer->parent())) {
130         desiredTextLength = textContentElement->specifiedTextLength().value(textContentElement);
131 
132         switch (static_cast<SVGTextContentElement::SVGLengthAdjustType>(textContentElement->lengthAdjust())) {
133         case SVGTextContentElement::LENGTHADJUST_UNKNOWN:
134             break;
135         case SVGTextContentElement::LENGTHADJUST_SPACING:
136             chunkStyle |= SVGTextChunk::LengthAdjustSpacing;
137             break;
138         case SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS:
139             chunkStyle |= SVGTextChunk::LengthAdjustSpacingAndGlyphs;
140             break;
141         };
142     }
143 
144     SVGTextChunk chunk(chunkStyle, desiredTextLength);
145 
146     Vector<SVGInlineTextBox*>& boxes = chunk.boxes();
147     for (unsigned i = boxStart; i < boxStart + boxCount; ++i)
148         boxes.append(lineLayoutBoxes[i]);
149 
150     m_textChunks.append(chunk);
151 }
152 
processTextChunk(const SVGTextChunk & chunk)153 void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk)
154 {
155     bool processTextLength = chunk.hasDesiredTextLength();
156     bool processTextAnchor = chunk.hasTextAnchor();
157     if (!processTextAnchor && !processTextLength)
158         return;
159 
160     const Vector<SVGInlineTextBox*>& boxes = chunk.boxes();
161     unsigned boxCount = boxes.size();
162     if (!boxCount)
163         return;
164 
165     // Calculate absolute length of whole text chunk (starting from text box 'start', spanning 'length' text boxes).
166     float chunkLength = 0;
167     unsigned chunkCharacters = 0;
168     chunk.calculateLength(chunkLength, chunkCharacters);
169 
170     bool isVerticalText = chunk.isVerticalText();
171     if (processTextLength) {
172         if (chunk.hasLengthAdjustSpacing()) {
173             float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters;
174             unsigned atCharacter = 0;
175             for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
176                 Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments();
177                 if (fragments.isEmpty())
178                     continue;
179                 processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter);
180             }
181         } else {
182             ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs());
183             float textLengthScale = chunk.desiredTextLength() / chunkLength;
184             AffineTransform spacingAndGlyphsTransform;
185 
186             bool foundFirstFragment = false;
187             for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
188                 SVGInlineTextBox* textBox = boxes[boxPosition];
189                 Vector<SVGTextFragment>& fragments = textBox->textFragments();
190                 if (fragments.isEmpty())
191                     continue;
192 
193                 if (!foundFirstFragment) {
194                     foundFirstFragment = true;
195                     buildSpacingAndGlyphsTransform(isVerticalText, textLengthScale, fragments.first(), spacingAndGlyphsTransform);
196                 }
197 
198                 m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform);
199             }
200         }
201     }
202 
203     if (!processTextAnchor)
204         return;
205 
206     // If we previously applied a lengthAdjust="spacing" correction, we have to recalculate the chunk length, to be able to apply the text-anchor shift.
207     if (processTextLength && chunk.hasLengthAdjustSpacing()) {
208         chunkLength = 0;
209         chunkCharacters = 0;
210         chunk.calculateLength(chunkLength, chunkCharacters);
211     }
212 
213     float textAnchorShift = chunk.calculateTextAnchorShift(chunkLength);
214     for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
215         Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments();
216         if (fragments.isEmpty())
217             continue;
218         processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments);
219     }
220 }
221 
processTextLengthSpacingCorrection(bool isVerticalText,float textLengthShift,Vector<SVGTextFragment> & fragments,unsigned & atCharacter)222 void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharacter)
223 {
224     unsigned fragmentCount = fragments.size();
225     for (unsigned i = 0; i < fragmentCount; ++i) {
226         SVGTextFragment& fragment = fragments[i];
227 
228         if (isVerticalText)
229             fragment.y += textLengthShift * atCharacter;
230         else
231             fragment.x += textLengthShift * atCharacter;
232 
233         atCharacter += fragment.length;
234     }
235 }
236 
processTextAnchorCorrection(bool isVerticalText,float textAnchorShift,Vector<SVGTextFragment> & fragments)237 void SVGTextChunkBuilder::processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector<SVGTextFragment>& fragments)
238 {
239     unsigned fragmentCount = fragments.size();
240     for (unsigned i = 0; i < fragmentCount; ++i) {
241         SVGTextFragment& fragment = fragments[i];
242 
243         if (isVerticalText)
244             fragment.y += textAnchorShift;
245         else
246             fragment.x += textAnchorShift;
247     }
248 }
249 
buildSpacingAndGlyphsTransform(bool isVerticalText,float scale,const SVGTextFragment & fragment,AffineTransform & spacingAndGlyphsTransform)250 void SVGTextChunkBuilder::buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform)
251 {
252     spacingAndGlyphsTransform.translate(fragment.x, fragment.y);
253 
254     if (isVerticalText)
255         spacingAndGlyphsTransform.scaleNonUniform(1, scale);
256     else
257         spacingAndGlyphsTransform.scaleNonUniform(scale, 1);
258 
259     spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y);
260 }
261 
262 }
263 
264 #endif // ENABLE(SVG)
265