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 bool isLTR = style.isLeftToRightDirection(); |
36 const ComputedStyle& style = textLayoutObject.styleRef(); | 35 switch (style.svgStyle().textAnchor()) { |
37 const SVGComputedStyle& svgStyle = style.svgStyle(); | 36 default: |
| 37 ASSERT_NOT_REACHED(); |
| 38 case TA_START: |
| 39 return isLTR ? 0 : -length; |
| 40 case TA_MIDDLE: |
| 41 return -length / 2; |
| 42 case TA_END: |
| 43 return isLTR ? -length : 0; |
| 44 } |
| 45 } |
38 | 46 |
39 // Build chunk style flags. | 47 bool needsTextAnchorAdjustment(const ComputedStyle& style) |
40 unsigned chunkStyle = SVGTextChunk::DefaultStyle; | 48 { |
41 | 49 bool isLTR = style.isLeftToRightDirection(); |
42 // Handle 'direction' property. | 50 switch (style.svgStyle().textAnchor()) { |
43 if (!style.isLeftToRightDirection()) | 51 default: |
44 chunkStyle |= SVGTextChunk::RightToLeftText; | 52 ASSERT_NOT_REACHED(); |
45 | |
46 // Handle 'writing-mode' property. | |
47 if (svgStyle.isVerticalWritingMode()) | |
48 chunkStyle |= SVGTextChunk::VerticalText; | |
49 | |
50 // Handle 'text-anchor' property. | |
51 switch (svgStyle.textAnchor()) { | |
52 case TA_START: | 53 case TA_START: |
53 break; | 54 return !isLTR; |
54 case TA_MIDDLE: | 55 case TA_MIDDLE: |
55 chunkStyle |= SVGTextChunk::MiddleAnchor; | 56 return true; |
56 break; | |
57 case TA_END: | 57 case TA_END: |
58 chunkStyle |= SVGTextChunk::EndAnchor; | 58 return isLTR; |
59 break; | |
60 } | 59 } |
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 } | 60 } |
85 | 61 |
86 class ChunkLengthAccumulator { | 62 class ChunkLengthAccumulator { |
87 public: | 63 public: |
88 ChunkLengthAccumulator(bool isVertical) | 64 ChunkLengthAccumulator(bool isVertical) |
89 : m_numCharacters(0) | 65 : m_numCharacters(0) |
90 , m_length(0) | 66 , m_length(0) |
91 , m_isVertical(isVertical) | 67 , m_isVertical(isVertical) |
92 { | 68 { |
93 } | 69 } |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
176 SVGTextPathChunkBuilder::SVGTextPathChunkBuilder() | 152 SVGTextPathChunkBuilder::SVGTextPathChunkBuilder() |
177 : SVGTextChunkBuilder() | 153 : SVGTextChunkBuilder() |
178 , m_totalLength(0) | 154 , m_totalLength(0) |
179 , m_totalCharacters(0) | 155 , m_totalCharacters(0) |
180 , m_totalTextAnchorShift(0) | 156 , m_totalTextAnchorShift(0) |
181 { | 157 { |
182 } | 158 } |
183 | 159 |
184 void SVGTextPathChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, Box
ListConstIterator boxEnd) | 160 void SVGTextPathChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, Box
ListConstIterator boxEnd) |
185 { | 161 { |
186 SVGTextChunk chunk = createTextChunk(**boxStart); | 162 const ComputedStyle& style = (*boxStart)->layoutObject().styleRef(); |
187 | 163 |
188 ChunkLengthAccumulator lengthAccumulator(chunk.isVerticalText()); | 164 ChunkLengthAccumulator lengthAccumulator(style.svgStyle().isVerticalWritingM
ode()); |
189 lengthAccumulator.processRange(boxStart, boxEnd); | 165 lengthAccumulator.processRange(boxStart, boxEnd); |
190 | 166 |
191 // Handle text-anchor as additional start offset for text paths. | 167 // Handle text-anchor as additional start offset for text paths. |
192 m_totalTextAnchorShift += chunk.calculateTextAnchorShift(lengthAccumulator.l
ength()); | 168 m_totalTextAnchorShift += calculateTextAnchorShift(style, lengthAccumulator.
length()); |
193 | 169 |
194 m_totalLength += lengthAccumulator.length(); | 170 m_totalLength += lengthAccumulator.length(); |
195 m_totalCharacters += lengthAccumulator.numCharacters(); | 171 m_totalCharacters += lengthAccumulator.numCharacters(); |
196 } | 172 } |
197 | 173 |
198 static void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, con
st SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform) | 174 static void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, con
st SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform) |
199 { | 175 { |
200 spacingAndGlyphsTransform.translate(fragment.x, fragment.y); | 176 spacingAndGlyphsTransform.translate(fragment.x, fragment.y); |
201 | 177 |
202 if (isVerticalText) | 178 if (isVerticalText) |
203 spacingAndGlyphsTransform.scaleNonUniform(1, scale); | 179 spacingAndGlyphsTransform.scaleNonUniform(1, scale); |
204 else | 180 else |
205 spacingAndGlyphsTransform.scaleNonUniform(scale, 1); | 181 spacingAndGlyphsTransform.scaleNonUniform(scale, 1); |
206 | 182 |
207 spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); | 183 spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); |
208 } | 184 } |
209 | 185 |
210 void SVGTextChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, BoxList
ConstIterator boxEnd) | 186 void SVGTextChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, BoxList
ConstIterator boxEnd) |
211 { | 187 { |
212 SVGTextChunk chunk = createTextChunk(**boxStart); | 188 ASSERT(*boxStart); |
213 | 189 |
214 bool processTextLength = chunk.hasDesiredTextLength(); | 190 const LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText((*boxSta
rt)->layoutObject()); |
215 bool processTextAnchor = chunk.hasTextAnchor(); | 191 const ComputedStyle& style = textLayoutObject.styleRef(); |
| 192 |
| 193 // Handle 'lengthAdjust' property. |
| 194 float desiredTextLength = 0; |
| 195 SVGLengthAdjustType lengthAdjust = SVGLengthAdjustUnknown; |
| 196 if (SVGTextContentElement* textContentElement = SVGTextContentElement::eleme
ntFromLayoutObject(textLayoutObject.parent())) { |
| 197 lengthAdjust = textContentElement->lengthAdjust()->currentValue()->enumV
alue(); |
| 198 |
| 199 SVGLengthContext lengthContext(textContentElement); |
| 200 if (textContentElement->textLengthIsSpecifiedByUser()) |
| 201 desiredTextLength = textContentElement->textLength()->currentValue()
->value(lengthContext); |
| 202 else |
| 203 desiredTextLength = 0; |
| 204 } |
| 205 |
| 206 bool processTextLength = desiredTextLength > 0; |
| 207 bool processTextAnchor = needsTextAnchorAdjustment(style); |
216 if (!processTextAnchor && !processTextLength) | 208 if (!processTextAnchor && !processTextLength) |
217 return; | 209 return; |
218 | 210 |
219 bool isVerticalText = chunk.isVerticalText(); | 211 bool isVerticalText = style.svgStyle().isVerticalWritingMode(); |
220 | 212 |
221 // Calculate absolute length of whole text chunk (starting from text box 'st
art', spanning 'length' text boxes). | 213 // Calculate absolute length of whole text chunk (starting from text box 'st
art', spanning 'length' text boxes). |
222 ChunkLengthAccumulator lengthAccumulator(isVerticalText); | 214 ChunkLengthAccumulator lengthAccumulator(isVerticalText); |
223 lengthAccumulator.processRange(boxStart, boxEnd); | 215 lengthAccumulator.processRange(boxStart, boxEnd); |
224 | 216 |
225 if (processTextLength) { | 217 if (processTextLength) { |
226 float chunkLength = lengthAccumulator.length(); | 218 float chunkLength = lengthAccumulator.length(); |
227 if (chunk.hasLengthAdjustSpacing()) { | 219 if (lengthAdjust == SVGLengthAdjustSpacing) { |
228 float textLengthShift = (chunk.desiredTextLength() - chunkLength) /
lengthAccumulator.numCharacters(); | 220 float textLengthShift = (desiredTextLength - chunkLength) / lengthAc
cumulator.numCharacters(); |
229 unsigned atCharacter = 0; | 221 unsigned atCharacter = 0; |
230 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 222 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
231 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments()
; | 223 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments()
; |
232 if (fragments.isEmpty()) | 224 if (fragments.isEmpty()) |
233 continue; | 225 continue; |
234 processTextLengthSpacingCorrection(isVerticalText, textLengthShi
ft, fragments, atCharacter); | 226 processTextLengthSpacingCorrection(isVerticalText, textLengthShi
ft, fragments, atCharacter); |
235 } | 227 } |
236 | 228 |
237 // Fragments have been adjusted, we have to recalculate the chunk | 229 // Fragments have been adjusted, we have to recalculate the chunk |
238 // length, to be able to apply the text-anchor shift. | 230 // length, to be able to apply the text-anchor shift. |
239 if (processTextAnchor) { | 231 if (processTextAnchor) { |
240 lengthAccumulator.reset(); | 232 lengthAccumulator.reset(); |
241 lengthAccumulator.processRange(boxStart, boxEnd); | 233 lengthAccumulator.processRange(boxStart, boxEnd); |
242 } | 234 } |
243 } else { | 235 } else { |
244 ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs()); | 236 ASSERT(lengthAdjust == SVGLengthAdjustSpacingAndGlyphs); |
245 float textLengthScale = chunk.desiredTextLength() / chunkLength; | 237 float textLengthScale = desiredTextLength / chunkLength; |
246 AffineTransform spacingAndGlyphsTransform; | 238 AffineTransform spacingAndGlyphsTransform; |
247 | 239 |
248 bool foundFirstFragment = false; | 240 bool foundFirstFragment = false; |
249 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 241 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
250 SVGInlineTextBox* textBox = *boxIter; | 242 SVGInlineTextBox* textBox = *boxIter; |
251 Vector<SVGTextFragment>& fragments = textBox->textFragments(); | 243 Vector<SVGTextFragment>& fragments = textBox->textFragments(); |
252 if (fragments.isEmpty()) | 244 if (fragments.isEmpty()) |
253 continue; | 245 continue; |
254 | 246 |
255 if (!foundFirstFragment) { | 247 if (!foundFirstFragment) { |
256 foundFirstFragment = true; | 248 foundFirstFragment = true; |
257 buildSpacingAndGlyphsTransform(isVerticalText, textLengthSca
le, fragments.first(), spacingAndGlyphsTransform); | 249 buildSpacingAndGlyphsTransform(isVerticalText, textLengthSca
le, fragments.first(), spacingAndGlyphsTransform); |
258 } | 250 } |
259 | 251 |
260 m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform)
; | 252 m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform)
; |
261 } | 253 } |
262 } | 254 } |
263 } | 255 } |
264 | 256 |
265 if (!processTextAnchor) | 257 if (!processTextAnchor) |
266 return; | 258 return; |
267 | 259 |
268 float textAnchorShift = chunk.calculateTextAnchorShift(lengthAccumulator.len
gth()); | 260 float textAnchorShift = calculateTextAnchorShift(style, lengthAccumulator.le
ngth()); |
269 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 261 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
270 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); | 262 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); |
271 if (fragments.isEmpty()) | 263 if (fragments.isEmpty()) |
272 continue; | 264 continue; |
273 processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); | 265 processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); |
274 } | 266 } |
275 } | 267 } |
276 | 268 |
277 void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText
, float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharact
er) | 269 void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText
, float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharact
er) |
278 { | 270 { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
313 continue; | 305 continue; |
314 | 306 |
315 for (SVGTextFragment& fragment : textBox->textFragments()) { | 307 for (SVGTextFragment& fragment : textBox->textFragments()) { |
316 ASSERT(fragment.lengthAdjustTransform.isIdentity()); | 308 ASSERT(fragment.lengthAdjustTransform.isIdentity()); |
317 fragment.lengthAdjustTransform = textBoxTransformation; | 309 fragment.lengthAdjustTransform = textBoxTransformation; |
318 } | 310 } |
319 } | 311 } |
320 } | 312 } |
321 | 313 |
322 } | 314 } |
OLD | NEW |