| 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 |