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