OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. | 2 * Copyright (C) Research In Motion Limited 2010-2012. 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/SVGTextLayoutEngine.h" | 21 #include "core/layout/svg/SVGTextLayoutEngine.h" |
22 | 22 |
23 #include "core/layout/svg/LayoutSVGInlineText.h" | 23 #include "core/layout/svg/LayoutSVGInlineText.h" |
24 #include "core/layout/svg/LayoutSVGTextPath.h" | 24 #include "core/layout/svg/LayoutSVGTextPath.h" |
25 #include "core/layout/svg/SVGTextChunkBuilder.h" | 25 #include "core/layout/svg/SVGTextChunkBuilder.h" |
26 #include "core/layout/svg/SVGTextLayoutEngineBaseline.h" | 26 #include "core/layout/svg/SVGTextLayoutEngineBaseline.h" |
27 #include "core/layout/svg/SVGTextLayoutEngineSpacing.h" | 27 #include "core/layout/svg/SVGTextLayoutEngineSpacing.h" |
| 28 #include "core/layout/svg/line/SVGInlineFlowBox.h" |
28 #include "core/layout/svg/line/SVGInlineTextBox.h" | 29 #include "core/layout/svg/line/SVGInlineTextBox.h" |
29 #include "core/svg/SVGElement.h" | 30 #include "core/svg/SVGElement.h" |
30 #include "core/svg/SVGLengthContext.h" | 31 #include "core/svg/SVGLengthContext.h" |
31 #include "core/svg/SVGTextContentElement.h" | 32 #include "core/svg/SVGTextContentElement.h" |
32 | 33 |
33 namespace blink { | 34 namespace blink { |
34 | 35 |
35 SVGTextLayoutEngine::SVGTextLayoutEngine(Vector<SVGTextLayoutAttributes*>& layou
tAttributes) | 36 SVGTextLayoutEngine::SVGTextLayoutEngine(Vector<SVGTextLayoutAttributes*>& layou
tAttributes) |
36 : m_layoutAttributes(layoutAttributes) | 37 : m_layoutAttributes(layoutAttributes) |
37 , m_layoutAttributesPosition(0) | 38 , m_layoutAttributesPosition(0) |
38 , m_logicalCharacterOffset(0) | 39 , m_logicalCharacterOffset(0) |
39 , m_logicalMetricsListOffset(0) | 40 , m_logicalMetricsListOffset(0) |
40 , m_x(0) | 41 , m_x(0) |
41 , m_y(0) | 42 , m_y(0) |
42 , m_dx(0) | 43 , m_dx(0) |
43 , m_dy(0) | 44 , m_dy(0) |
44 , m_isVerticalText(false) | 45 , m_isVerticalText(false) |
45 , m_inPathLayout(false) | 46 , m_inPathLayout(false) |
| 47 , m_textLengthSpacingInEffect(false) |
46 , m_textPathCalculator(0) | 48 , m_textPathCalculator(0) |
47 , m_textPathLength(0) | 49 , m_textPathLength(0) |
48 , m_textPathCurrentOffset(0) | 50 , m_textPathCurrentOffset(0) |
49 , m_textPathSpacing(0) | 51 , m_textPathSpacing(0) |
50 , m_textPathScaling(1) | 52 , m_textPathScaling(1) |
51 { | 53 { |
52 ASSERT(!m_layoutAttributes.isEmpty()); | 54 ASSERT(!m_layoutAttributes.isEmpty()); |
53 } | 55 } |
54 | 56 |
55 void SVGTextLayoutEngine::updateCharacterPositionIfNeeded(float& x, float& y) | 57 void SVGTextLayoutEngine::updateCharacterPositionIfNeeded(float& x, float& y) |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
133 for (unsigned i = m_currentTextFragment.metricsListOffset; i < visua
lMetricsListOffset; ++i) | 135 for (unsigned i = m_currentTextFragment.metricsListOffset; i < visua
lMetricsListOffset; ++i) |
134 length += textMetricsValues.at(i).width(); | 136 length += textMetricsValues.at(i).width(); |
135 m_currentTextFragment.width = length; | 137 m_currentTextFragment.width = length; |
136 } | 138 } |
137 } | 139 } |
138 | 140 |
139 textBox->textFragments().append(m_currentTextFragment); | 141 textBox->textFragments().append(m_currentTextFragment); |
140 m_currentTextFragment = SVGTextFragment(); | 142 m_currentTextFragment = SVGTextFragment(); |
141 } | 143 } |
142 | 144 |
143 bool SVGTextLayoutEngine::parentDefinesTextLength(LayoutObject* parent) const | 145 void SVGTextLayoutEngine::beginTextPathLayout(SVGInlineFlowBox* flowBox) |
144 { | 146 { |
145 LayoutObject* currentParent = parent; | 147 // Build text chunks for all <textPath> children, using the line layout algo
rithm. |
146 while (currentParent) { | 148 // This is needeed as text-anchor is just an additional startOffset for text
paths. |
147 if (SVGTextContentElement* textContentElement = SVGTextContentElement::e
lementFromLayoutObject(currentParent)) { | 149 SVGTextLayoutEngine lineLayout(m_layoutAttributes); |
148 SVGLengthContext lengthContext(textContentElement); | 150 lineLayout.m_textLengthSpacingInEffect = m_textLengthSpacingInEffect; |
149 if (textContentElement->lengthAdjust()->currentValue()->enumValue()
== SVGLengthAdjustSpacing && textContentElement->textLengthIsSpecifiedByUser()) | 151 lineLayout.layoutCharactersInTextBoxes(flowBox); |
150 return true; | |
151 } | |
152 | |
153 if (currentParent->isSVGText()) | |
154 return false; | |
155 | |
156 currentParent = currentParent->parent(); | |
157 } | |
158 | |
159 ASSERT_NOT_REACHED(); | |
160 return false; | |
161 } | |
162 | |
163 void SVGTextLayoutEngine::beginTextPathLayout(LayoutObject* object, SVGTextLayou
tEngine& lineLayout) | |
164 { | |
165 ASSERT(object); | |
166 | 152 |
167 m_inPathLayout = true; | 153 m_inPathLayout = true; |
168 LayoutSVGTextPath* textPath = toLayoutSVGTextPath(object); | 154 LayoutSVGTextPath* textPath = &toLayoutSVGTextPath(flowBox->layoutObject()); |
169 | 155 |
170 Path path = textPath->layoutPath(); | 156 Path path = textPath->layoutPath(); |
171 if (path.isEmpty()) | 157 if (path.isEmpty()) |
172 return; | 158 return; |
173 m_textPathCalculator = new Path::PositionCalculator(path); | 159 m_textPathCalculator = new Path::PositionCalculator(path); |
174 m_textPathStartOffset = textPath->startOffset(); | 160 m_textPathStartOffset = textPath->startOffset(); |
175 m_textPathLength = path.length(); | 161 m_textPathLength = path.length(); |
176 if (m_textPathStartOffset > 0 && m_textPathStartOffset <= 1) | 162 if (m_textPathStartOffset > 0 && m_textPathStartOffset <= 1) |
177 m_textPathStartOffset *= m_textPathLength; | 163 m_textPathStartOffset *= m_textPathLength; |
178 | 164 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
231 textBox->clearTextFragments(); | 217 textBox->clearTextFragments(); |
232 m_isVerticalText = style.svgStyle().isVerticalWritingMode(); | 218 m_isVerticalText = style.svgStyle().isVerticalWritingMode(); |
233 layoutTextOnLineOrPath(textBox, text, style); | 219 layoutTextOnLineOrPath(textBox, text, style); |
234 | 220 |
235 if (m_inPathLayout) | 221 if (m_inPathLayout) |
236 return; | 222 return; |
237 | 223 |
238 m_lineLayoutBoxes.append(textBox); | 224 m_lineLayoutBoxes.append(textBox); |
239 } | 225 } |
240 | 226 |
| 227 static bool definesTextLengthWithSpacing(const InlineFlowBox* start) |
| 228 { |
| 229 SVGTextContentElement* textContentElement = SVGTextContentElement::elementFr
omLayoutObject(&start->layoutObject()); |
| 230 return textContentElement |
| 231 && textContentElement->lengthAdjust()->currentValue()->enumValue() == SV
GLengthAdjustSpacing |
| 232 && textContentElement->textLengthIsSpecifiedByUser(); |
| 233 } |
| 234 |
| 235 void SVGTextLayoutEngine::layoutCharactersInTextBoxes(InlineFlowBox* start) |
| 236 { |
| 237 bool textLengthSpacingInEffect = m_textLengthSpacingInEffect || definesTextL
engthWithSpacing(start); |
| 238 TemporaryChange<bool> textLengthSpacingScope(m_textLengthSpacingInEffect, te
xtLengthSpacingInEffect); |
| 239 |
| 240 for (InlineBox* child = start->firstChild(); child; child = child->nextOnLin
e()) { |
| 241 if (child->isSVGInlineTextBox()) { |
| 242 ASSERT(child->layoutObject().isSVGInlineText()); |
| 243 layoutInlineTextBox(toSVGInlineTextBox(child)); |
| 244 } else { |
| 245 // Skip generated content. |
| 246 Node* node = child->layoutObject().node(); |
| 247 if (!node) |
| 248 continue; |
| 249 |
| 250 SVGInlineFlowBox* flowBox = toSVGInlineFlowBox(child); |
| 251 bool isTextPath = isSVGTextPathElement(*node); |
| 252 if (isTextPath) |
| 253 beginTextPathLayout(flowBox); |
| 254 |
| 255 layoutCharactersInTextBoxes(flowBox); |
| 256 |
| 257 if (isTextPath) |
| 258 endTextPathLayout(); |
| 259 } |
| 260 } |
| 261 } |
| 262 |
241 void SVGTextLayoutEngine::finishLayout() | 263 void SVGTextLayoutEngine::finishLayout() |
242 { | 264 { |
243 m_visualMetricsIterator = SVGInlineTextMetricsIterator(); | 265 m_visualMetricsIterator = SVGInlineTextMetricsIterator(); |
244 | 266 |
245 // After all text fragments are stored in their correpsonding SVGInlineTextB
oxes, we can layout individual text chunks. | 267 // After all text fragments are stored in their correpsonding SVGInlineTextB
oxes, we can layout individual text chunks. |
246 // Chunk layouting is only performed for line layout boxes, not for path lay
out, where it has already been done. | 268 // Chunk layouting is only performed for line layout boxes, not for path lay
out, where it has already been done. |
247 SVGTextChunkBuilder chunkLayoutBuilder; | 269 SVGTextChunkBuilder chunkLayoutBuilder; |
248 chunkLayoutBuilder.processTextChunks(m_lineLayoutBoxes); | 270 chunkLayoutBuilder.processTextChunks(m_lineLayoutBoxes); |
249 | 271 |
250 // Finalize transform matrices, after the chunk layout corrections have been
applied, and all fragment x/y positions are finalized. | 272 // Finalize transform matrices, after the chunk layout corrections have been
applied, and all fragment x/y positions are finalized. |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
309 { | 331 { |
310 ++m_logicalMetricsListOffset; | 332 ++m_logicalMetricsListOffset; |
311 m_logicalCharacterOffset += logicalMetrics.length(); | 333 m_logicalCharacterOffset += logicalMetrics.length(); |
312 } | 334 } |
313 | 335 |
314 void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, cons
t LayoutSVGInlineText& text, const ComputedStyle& style) | 336 void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, cons
t LayoutSVGInlineText& text, const ComputedStyle& style) |
315 { | 337 { |
316 if (m_inPathLayout && !m_textPathCalculator) | 338 if (m_inPathLayout && !m_textPathCalculator) |
317 return; | 339 return; |
318 | 340 |
319 LayoutObject* textParent = text.parent(); | |
320 bool definesTextLength = textParent ? parentDefinesTextLength(textParent) :
false; | |
321 | |
322 const SVGComputedStyle& svgStyle = style.svgStyle(); | 341 const SVGComputedStyle& svgStyle = style.svgStyle(); |
323 | 342 |
324 // Find the start of the current text box in the metrics list. | 343 // Find the start of the current text box in the metrics list. |
325 m_visualMetricsIterator.advanceToTextStart(&text, textBox->start()); | 344 m_visualMetricsIterator.advanceToTextStart(&text, textBox->start()); |
326 | 345 |
327 const Font& font = style.font(); | 346 const Font& font = style.font(); |
328 | 347 |
329 SVGTextLayoutEngineSpacing spacingLayout(font, style.effectiveZoom()); | 348 SVGTextLayoutEngineSpacing spacingLayout(font, style.effectiveZoom()); |
330 SVGTextLayoutEngineBaseline baselineLayout(font, style.effectiveZoom()); | 349 SVGTextLayoutEngineBaseline baselineLayout(font, style.effectiveZoom()); |
331 | 350 |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
447 x += baselineShift; | 466 x += baselineShift; |
448 else | 467 else |
449 y -= baselineShift; | 468 y -= baselineShift; |
450 | 469 |
451 x += m_dx; | 470 x += m_dx; |
452 y += m_dy; | 471 y += m_dy; |
453 } | 472 } |
454 | 473 |
455 // Determine whether we have to start a new fragment. | 474 // Determine whether we have to start a new fragment. |
456 bool shouldStartNewFragment = m_dx || m_dy || m_isVerticalText || m_inPa
thLayout || angle || angle != lastAngle | 475 bool shouldStartNewFragment = m_dx || m_dy || m_isVerticalText || m_inPa
thLayout || angle || angle != lastAngle |
457 || orientationAngle || applySpacingToNextCharacter || definesTextLen
gth; | 476 || orientationAngle || applySpacingToNextCharacter || m_textLengthSp
acingInEffect; |
458 | 477 |
459 // If we already started a fragment, close it now. | 478 // If we already started a fragment, close it now. |
460 if (didStartTextFragment && shouldStartNewFragment) { | 479 if (didStartTextFragment && shouldStartNewFragment) { |
461 applySpacingToNextCharacter = false; | 480 applySpacingToNextCharacter = false; |
462 recordTextFragment(textBox); | 481 recordTextFragment(textBox); |
463 } | 482 } |
464 | 483 |
465 // Eventually start a new fragment, if not yet done. | 484 // Eventually start a new fragment, if not yet done. |
466 if (!didStartTextFragment || shouldStartNewFragment) { | 485 if (!didStartTextFragment || shouldStartNewFragment) { |
467 ASSERT(!m_currentTextFragment.characterOffset); | 486 ASSERT(!m_currentTextFragment.characterOffset); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
517 } | 536 } |
518 | 537 |
519 if (!didStartTextFragment) | 538 if (!didStartTextFragment) |
520 return; | 539 return; |
521 | 540 |
522 // Close last open fragment, if needed. | 541 // Close last open fragment, if needed. |
523 recordTextFragment(textBox); | 542 recordTextFragment(textBox); |
524 } | 543 } |
525 | 544 |
526 } | 545 } |
OLD | NEW |