OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | 2 * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
3 * | 3 * |
4 * This library is free software; you can redistribute it and/or | 4 * This library is free software; you can redistribute it and/or |
5 * modify it under the terms of the GNU Library General Public | 5 * modify it under the terms of the GNU Library General Public |
6 * License as published by the Free Software Foundation; either | 6 * License as published by the Free Software Foundation; either |
7 * version 2 of the License, or (at your option) any later version. | 7 * version 2 of the License, or (at your option) any later version. |
8 * | 8 * |
9 * This library is distributed in the hope that it will be useful, | 9 * This library is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 * Library General Public License for more details. | 12 * Library General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU Library General Public License | 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 | 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, | 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 * Boston, MA 02110-1301, USA. | 17 * Boston, MA 02110-1301, USA. |
18 */ | 18 */ |
19 | 19 |
20 #include "config.h" | 20 #include "config.h" |
21 #include "core/layout/svg/SVGTextChunkBuilder.h" | 21 #include "core/layout/svg/SVGTextChunkBuilder.h" |
22 | 22 |
23 #include "core/layout/svg/LayoutSVGInlineText.h" | 23 #include "core/layout/svg/LayoutSVGInlineText.h" |
24 #include "core/layout/svg/SVGTextChunk.h" | |
25 #include "core/layout/svg/line/SVGInlineTextBox.h" | 24 #include "core/layout/svg/line/SVGInlineTextBox.h" |
26 #include "core/svg/SVGLengthContext.h" | 25 #include "core/svg/SVGLengthContext.h" |
27 #include "core/svg/SVGTextContentElement.h" | 26 #include "core/svg/SVGTextContentElement.h" |
28 | 27 |
29 namespace blink { | 28 namespace blink { |
30 | 29 |
31 namespace { | 30 namespace { |
32 | 31 |
33 SVGTextChunk createTextChunk(const SVGInlineTextBox& textBox) | 32 float calculateTextAnchorShift(const ComputedStyle& style, float length) |
34 { | 33 { |
35 LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText(textBox.layout Object()); | 34 ETextAnchor textAnchor = style.svgStyle().textAnchor(); |
36 const ComputedStyle& style = textLayoutObject.styleRef(); | 35 if (textAnchor == TA_MIDDLE) |
37 const SVGComputedStyle& svgStyle = style.svgStyle(); | 36 return -length / 2; |
37 bool isLTR = style.isLeftToRightDirection(); | |
38 if (textAnchor == TA_END) | |
39 return isLTR ? -length : 0; | |
40 return isLTR ? 0 : -length; | |
41 } | |
38 | 42 |
39 // Build chunk style flags. | 43 bool needsTextAnchorAdjustment(const ComputedStyle& style) |
40 unsigned chunkStyle = SVGTextChunk::DefaultStyle; | 44 { |
41 | 45 ETextAnchor textAnchor = style.svgStyle().textAnchor(); |
pdr.
2015/06/04 20:22:02
Nit: These may be a little cleaner as a switch sta
fs
2015/06/05 08:42:32
I had this in PS1, but then some of the bots bombe
| |
42 // Handle 'direction' property. | 46 if (textAnchor == TA_MIDDLE) |
43 if (!style.isLeftToRightDirection()) | 47 return true; |
44 chunkStyle |= SVGTextChunk::RightToLeftText; | 48 bool isLTR = style.isLeftToRightDirection(); |
45 | 49 return (isLTR && textAnchor == TA_END) |
46 // Handle 'writing-mode' property. | 50 || (!isLTR && textAnchor == TA_START); |
47 if (svgStyle.isVerticalWritingMode()) | |
48 chunkStyle |= SVGTextChunk::VerticalText; | |
49 | |
50 // Handle 'text-anchor' property. | |
51 switch (svgStyle.textAnchor()) { | |
52 case TA_START: | |
53 break; | |
54 case TA_MIDDLE: | |
55 chunkStyle |= SVGTextChunk::MiddleAnchor; | |
56 break; | |
57 case TA_END: | |
58 chunkStyle |= SVGTextChunk::EndAnchor; | |
59 break; | |
60 } | |
61 | |
62 // Handle 'lengthAdjust' property. | |
63 float desiredTextLength = 0; | |
64 if (SVGTextContentElement* textContentElement = SVGTextContentElement::eleme ntFromLayoutObject(textLayoutObject.parent())) { | |
65 SVGLengthContext lengthContext(textContentElement); | |
66 if (textContentElement->textLengthIsSpecifiedByUser()) | |
67 desiredTextLength = textContentElement->textLength()->currentValue() ->value(lengthContext); | |
68 else | |
69 desiredTextLength = 0; | |
70 | |
71 switch (textContentElement->lengthAdjust()->currentValue()->enumValue()) { | |
72 case SVGLengthAdjustUnknown: | |
73 break; | |
74 case SVGLengthAdjustSpacing: | |
75 chunkStyle |= SVGTextChunk::LengthAdjustSpacing; | |
76 break; | |
77 case SVGLengthAdjustSpacingAndGlyphs: | |
78 chunkStyle |= SVGTextChunk::LengthAdjustSpacingAndGlyphs; | |
79 break; | |
80 } | |
81 } | |
82 | |
83 return SVGTextChunk(chunkStyle, desiredTextLength); | |
84 } | 51 } |
85 | 52 |
86 class ChunkLengthAccumulator { | 53 class ChunkLengthAccumulator { |
87 public: | 54 public: |
88 ChunkLengthAccumulator(bool isVertical) | 55 ChunkLengthAccumulator(bool isVertical) |
89 : m_numCharacters(0) | 56 : m_numCharacters(0) |
90 , m_length(0) | 57 , m_length(0) |
91 , m_isVertical(isVertical) | 58 , m_isVertical(isVertical) |
92 { | 59 { |
93 } | 60 } |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
176 SVGTextPathChunkBuilder::SVGTextPathChunkBuilder() | 143 SVGTextPathChunkBuilder::SVGTextPathChunkBuilder() |
177 : SVGTextChunkBuilder() | 144 : SVGTextChunkBuilder() |
178 , m_totalLength(0) | 145 , m_totalLength(0) |
179 , m_totalCharacters(0) | 146 , m_totalCharacters(0) |
180 , m_totalTextAnchorShift(0) | 147 , m_totalTextAnchorShift(0) |
181 { | 148 { |
182 } | 149 } |
183 | 150 |
184 void SVGTextPathChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, Box ListConstIterator boxEnd) | 151 void SVGTextPathChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, Box ListConstIterator boxEnd) |
185 { | 152 { |
186 SVGTextChunk chunk = createTextChunk(**boxStart); | 153 const ComputedStyle& style = (*boxStart)->layoutObject().styleRef(); |
187 | 154 |
188 ChunkLengthAccumulator lengthAccumulator(chunk.isVerticalText()); | 155 ChunkLengthAccumulator lengthAccumulator(style.svgStyle().isVerticalWritingM ode()); |
189 lengthAccumulator.processRange(boxStart, boxEnd); | 156 lengthAccumulator.processRange(boxStart, boxEnd); |
190 | 157 |
191 // Handle text-anchor as additional start offset for text paths. | 158 // Handle text-anchor as additional start offset for text paths. |
192 m_totalTextAnchorShift += chunk.calculateTextAnchorShift(lengthAccumulator.l ength()); | 159 m_totalTextAnchorShift += calculateTextAnchorShift(style, lengthAccumulator. length()); |
193 | 160 |
194 m_totalLength += lengthAccumulator.length(); | 161 m_totalLength += lengthAccumulator.length(); |
195 m_totalCharacters += lengthAccumulator.numCharacters(); | 162 m_totalCharacters += lengthAccumulator.numCharacters(); |
196 } | 163 } |
197 | 164 |
198 static void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, con st SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform) | 165 static void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, con st SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform) |
199 { | 166 { |
200 spacingAndGlyphsTransform.translate(fragment.x, fragment.y); | 167 spacingAndGlyphsTransform.translate(fragment.x, fragment.y); |
201 | 168 |
202 if (isVerticalText) | 169 if (isVerticalText) |
203 spacingAndGlyphsTransform.scaleNonUniform(1, scale); | 170 spacingAndGlyphsTransform.scaleNonUniform(1, scale); |
204 else | 171 else |
205 spacingAndGlyphsTransform.scaleNonUniform(scale, 1); | 172 spacingAndGlyphsTransform.scaleNonUniform(scale, 1); |
206 | 173 |
207 spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); | 174 spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); |
208 } | 175 } |
209 | 176 |
210 void SVGTextChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, BoxList ConstIterator boxEnd) | 177 void SVGTextChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, BoxList ConstIterator boxEnd) |
211 { | 178 { |
212 SVGTextChunk chunk = createTextChunk(**boxStart); | 179 ASSERT(*boxStart); |
213 | 180 |
214 bool processTextLength = chunk.hasDesiredTextLength(); | 181 const LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText((*boxSta rt)->layoutObject()); |
215 bool processTextAnchor = chunk.hasTextAnchor(); | 182 const ComputedStyle& style = textLayoutObject.styleRef(); |
183 | |
184 // Handle 'lengthAdjust' property. | |
185 float desiredTextLength = 0; | |
186 SVGLengthAdjustType lengthAdjust = SVGLengthAdjustUnknown; | |
187 if (SVGTextContentElement* textContentElement = SVGTextContentElement::eleme ntFromLayoutObject(textLayoutObject.parent())) { | |
188 lengthAdjust = textContentElement->lengthAdjust()->currentValue()->enumV alue(); | |
189 | |
190 SVGLengthContext lengthContext(textContentElement); | |
191 if (textContentElement->textLengthIsSpecifiedByUser()) | |
192 desiredTextLength = textContentElement->textLength()->currentValue() ->value(lengthContext); | |
193 else | |
194 desiredTextLength = 0; | |
195 } | |
196 | |
197 bool processTextLength = desiredTextLength > 0; | |
198 bool processTextAnchor = needsTextAnchorAdjustment(style); | |
216 if (!processTextAnchor && !processTextLength) | 199 if (!processTextAnchor && !processTextLength) |
217 return; | 200 return; |
218 | 201 |
219 bool isVerticalText = chunk.isVerticalText(); | 202 bool isVerticalText = style.svgStyle().isVerticalWritingMode(); |
220 | 203 |
221 // Calculate absolute length of whole text chunk (starting from text box 'st art', spanning 'length' text boxes). | 204 // Calculate absolute length of whole text chunk (starting from text box 'st art', spanning 'length' text boxes). |
222 ChunkLengthAccumulator lengthAccumulator(isVerticalText); | 205 ChunkLengthAccumulator lengthAccumulator(isVerticalText); |
223 lengthAccumulator.processRange(boxStart, boxEnd); | 206 lengthAccumulator.processRange(boxStart, boxEnd); |
224 | 207 |
225 if (processTextLength) { | 208 if (processTextLength) { |
226 float chunkLength = lengthAccumulator.length(); | 209 float chunkLength = lengthAccumulator.length(); |
227 if (chunk.hasLengthAdjustSpacing()) { | 210 if (lengthAdjust == SVGLengthAdjustSpacing) { |
228 float textLengthShift = (chunk.desiredTextLength() - chunkLength) / lengthAccumulator.numCharacters(); | 211 float textLengthShift = (desiredTextLength - chunkLength) / lengthAc cumulator.numCharacters(); |
229 unsigned atCharacter = 0; | 212 unsigned atCharacter = 0; |
230 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 213 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
231 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments() ; | 214 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments() ; |
232 if (fragments.isEmpty()) | 215 if (fragments.isEmpty()) |
233 continue; | 216 continue; |
234 processTextLengthSpacingCorrection(isVerticalText, textLengthShi ft, fragments, atCharacter); | 217 processTextLengthSpacingCorrection(isVerticalText, textLengthShi ft, fragments, atCharacter); |
235 } | 218 } |
236 | 219 |
237 // Fragments have been adjusted, we have to recalculate the chunk | 220 // Fragments have been adjusted, we have to recalculate the chunk |
238 // length, to be able to apply the text-anchor shift. | 221 // length, to be able to apply the text-anchor shift. |
239 if (processTextAnchor) { | 222 if (processTextAnchor) { |
240 lengthAccumulator.reset(); | 223 lengthAccumulator.reset(); |
241 lengthAccumulator.processRange(boxStart, boxEnd); | 224 lengthAccumulator.processRange(boxStart, boxEnd); |
242 } | 225 } |
243 } else { | 226 } else { |
244 ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs()); | 227 ASSERT(lengthAdjust == SVGLengthAdjustSpacingAndGlyphs); |
245 float textLengthScale = chunk.desiredTextLength() / chunkLength; | 228 float textLengthScale = desiredTextLength / chunkLength; |
246 AffineTransform spacingAndGlyphsTransform; | 229 AffineTransform spacingAndGlyphsTransform; |
247 | 230 |
248 bool foundFirstFragment = false; | 231 bool foundFirstFragment = false; |
249 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 232 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
250 SVGInlineTextBox* textBox = *boxIter; | 233 SVGInlineTextBox* textBox = *boxIter; |
251 Vector<SVGTextFragment>& fragments = textBox->textFragments(); | 234 Vector<SVGTextFragment>& fragments = textBox->textFragments(); |
252 if (fragments.isEmpty()) | 235 if (fragments.isEmpty()) |
253 continue; | 236 continue; |
254 | 237 |
255 if (!foundFirstFragment) { | 238 if (!foundFirstFragment) { |
256 foundFirstFragment = true; | 239 foundFirstFragment = true; |
257 buildSpacingAndGlyphsTransform(isVerticalText, textLengthSca le, fragments.first(), spacingAndGlyphsTransform); | 240 buildSpacingAndGlyphsTransform(isVerticalText, textLengthSca le, fragments.first(), spacingAndGlyphsTransform); |
258 } | 241 } |
259 | 242 |
260 m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform) ; | 243 m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform) ; |
261 } | 244 } |
262 } | 245 } |
263 } | 246 } |
264 | 247 |
265 if (!processTextAnchor) | 248 if (!processTextAnchor) |
266 return; | 249 return; |
267 | 250 |
268 float textAnchorShift = chunk.calculateTextAnchorShift(lengthAccumulator.len gth()); | 251 float textAnchorShift = calculateTextAnchorShift(style, lengthAccumulator.le ngth()); |
269 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 252 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
270 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); | 253 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); |
271 if (fragments.isEmpty()) | 254 if (fragments.isEmpty()) |
272 continue; | 255 continue; |
273 processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); | 256 processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); |
274 } | 257 } |
275 } | 258 } |
276 | 259 |
277 void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText , float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharact er) | 260 void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText , float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharact er) |
278 { | 261 { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
313 continue; | 296 continue; |
314 | 297 |
315 for (SVGTextFragment& fragment : textBox->textFragments()) { | 298 for (SVGTextFragment& fragment : textBox->textFragments()) { |
316 ASSERT(fragment.lengthAdjustTransform.isIdentity()); | 299 ASSERT(fragment.lengthAdjustTransform.isIdentity()); |
317 fragment.lengthAdjustTransform = textBoxTransformation; | 300 fragment.lengthAdjustTransform = textBoxTransformation; |
318 } | 301 } |
319 } | 302 } |
320 } | 303 } |
321 | 304 |
322 } | 305 } |
OLD | NEW |