Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) Research In Motion Limited 2010-2012. 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/SVGTextLayoutEngine.h"
     23 
     24 #include "core/rendering/svg/RenderSVGInlineText.h"
     25 #include "core/rendering/svg/RenderSVGTextPath.h"
     26 #include "core/rendering/svg/SVGInlineTextBox.h"
     27 #include "core/rendering/svg/SVGTextLayoutEngineBaseline.h"
     28 #include "core/rendering/svg/SVGTextLayoutEngineSpacing.h"
     29 #include "core/svg/SVGElement.h"
     30 #include "core/svg/SVGLengthContext.h"
     31 
     32 // Set to a value > 0 to dump the text fragments
     33 #define DUMP_TEXT_FRAGMENTS 0
     34 
     35 namespace WebCore {
     36 
     37 SVGTextLayoutEngine::SVGTextLayoutEngine(Vector<SVGTextLayoutAttributes*>& layoutAttributes)
     38     : m_layoutAttributes(layoutAttributes)
     39     , m_layoutAttributesPosition(0)
     40     , m_logicalCharacterOffset(0)
     41     , m_logicalMetricsListOffset(0)
     42     , m_visualCharacterOffset(0)
     43     , m_visualMetricsListOffset(0)
     44     , m_x(0)
     45     , m_y(0)
     46     , m_dx(0)
     47     , m_dy(0)
     48     , m_isVerticalText(false)
     49     , m_inPathLayout(false)
     50     , m_textPathLength(0)
     51     , m_textPathCurrentOffset(0)
     52     , m_textPathSpacing(0)
     53     , m_textPathScaling(1)
     54 {
     55     ASSERT(!m_layoutAttributes.isEmpty());
     56 }
     57 
     58 void SVGTextLayoutEngine::updateCharacerPositionIfNeeded(float& x, float& y)
     59 {
     60     if (m_inPathLayout)
     61         return;
     62 
     63     // Replace characters x/y position, with the current text position plus any
     64     // relative adjustments, if it doesn't specify an absolute position itself.
     65     if (x == SVGTextLayoutAttributes::emptyValue())
     66         x = m_x + m_dx;
     67 
     68     if (y == SVGTextLayoutAttributes::emptyValue())
     69         y = m_y + m_dy;
     70 
     71     m_dx = 0;
     72     m_dy = 0;
     73 }
     74 
     75 void SVGTextLayoutEngine::updateCurrentTextPosition(float x, float y, float glyphAdvance)
     76 {
     77     // Update current text position after processing the character.
     78     if (m_isVerticalText) {
     79         m_x = x;
     80         m_y = y + glyphAdvance;
     81     } else {
     82         m_x = x + glyphAdvance;
     83         m_y = y;
     84     }
     85 }
     86 
     87 void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(float dx, float dy)
     88 {
     89     // Update relative positioning information.
     90     if (dx == SVGTextLayoutAttributes::emptyValue() && dy == SVGTextLayoutAttributes::emptyValue())
     91         return;
     92 
     93     if (dx == SVGTextLayoutAttributes::emptyValue())
     94         dx = 0;
     95     if (dy == SVGTextLayoutAttributes::emptyValue())
     96         dy = 0;
     97 
     98     if (m_inPathLayout) {
     99         if (m_isVerticalText) {
    100             m_dx += dx;
    101             m_dy = dy;
    102         } else {
    103             m_dx = dx;
    104             m_dy += dy;
    105         }
    106 
    107         return;
    108     }
    109 
    110     m_dx = dx;
    111     m_dy = dy;
    112 }
    113 
    114 void SVGTextLayoutEngine::recordTextFragment(SVGInlineTextBox* textBox, Vector<SVGTextMetrics>& textMetricsValues)
    115 {
    116     ASSERT(!m_currentTextFragment.length);
    117     ASSERT(m_visualMetricsListOffset > 0);
    118 
    119     // Figure out length of fragment.
    120     m_currentTextFragment.length = m_visualCharacterOffset - m_currentTextFragment.characterOffset;
    121 
    122     // Figure out fragment metrics.
    123     SVGTextMetrics& lastCharacterMetrics = textMetricsValues.at(m_visualMetricsListOffset - 1);
    124     m_currentTextFragment.width = lastCharacterMetrics.width();
    125     m_currentTextFragment.height = lastCharacterMetrics.height();
    126 
    127     if (m_currentTextFragment.length > 1) {
    128         // SVGTextLayoutAttributesBuilder assures that the length of the range is equal to the sum of the individual lengths of the glyphs.
    129         float length = 0;
    130         if (m_isVerticalText) {
    131             for (unsigned i = m_currentTextFragment.metricsListOffset; i < m_visualMetricsListOffset; ++i)
    132                 length += textMetricsValues.at(i).height();
    133             m_currentTextFragment.height = length;
    134         } else {
    135             for (unsigned i = m_currentTextFragment.metricsListOffset; i < m_visualMetricsListOffset; ++i)
    136                 length += textMetricsValues.at(i).width();
    137             m_currentTextFragment.width = length;
    138         }
    139     }
    140 
    141     textBox->textFragments().append(m_currentTextFragment);
    142     m_currentTextFragment = SVGTextFragment();
    143 }
    144 
    145 bool SVGTextLayoutEngine::parentDefinesTextLength(RenderObject* parent) const
    146 {
    147     RenderObject* currentParent = parent;
    148     while (currentParent) {
    149         if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(currentParent)) {
    150             SVGLengthContext lengthContext(textContentElement);
    151             if (textContentElement->lengthAdjustCurrentValue() == SVGLengthAdjustSpacing && textContentElement->specifiedTextLength().value(lengthContext) > 0)
    152                 return true;
    153         }
    154 
    155         if (currentParent->isSVGText())
    156             return false;
    157 
    158         currentParent = currentParent->parent();
    159     }
    160 
    161     ASSERT_NOT_REACHED();
    162     return false;
    163 }
    164 
    165 void SVGTextLayoutEngine::beginTextPathLayout(RenderObject* object, SVGTextLayoutEngine& lineLayout)
    166 {
    167     ASSERT(object);
    168 
    169     m_inPathLayout = true;
    170     RenderSVGTextPath* textPath = toRenderSVGTextPath(object);
    171 
    172     m_textPath = textPath->layoutPath();
    173     if (m_textPath.isEmpty())
    174         return;
    175     m_textPathStartOffset = textPath->startOffset();
    176     m_textPathLength = m_textPath.length();
    177     if (m_textPathStartOffset > 0 && m_textPathStartOffset <= 1)
    178         m_textPathStartOffset *= m_textPathLength;
    179 
    180     float totalLength = 0;
    181     unsigned totalCharacters = 0;
    182 
    183     lineLayout.m_chunkLayoutBuilder.buildTextChunks(lineLayout.m_lineLayoutBoxes);
    184     const Vector<SVGTextChunk>& textChunks = lineLayout.m_chunkLayoutBuilder.textChunks();
    185 
    186     unsigned size = textChunks.size();
    187     for (unsigned i = 0; i < size; ++i) {
    188         const SVGTextChunk& chunk = textChunks.at(i);
    189 
    190         float length = 0;
    191         unsigned characters = 0;
    192         chunk.calculateLength(length, characters);
    193 
    194         // Handle text-anchor as additional start offset for text paths.
    195         m_textPathStartOffset += chunk.calculateTextAnchorShift(length);
    196 
    197         totalLength += length;
    198         totalCharacters += characters;
    199     }
    200 
    201     m_textPathCurrentOffset = m_textPathStartOffset;
    202 
    203     // Eventually handle textLength adjustments.
    204     SVGLengthAdjustType lengthAdjust = SVGLengthAdjustUnknown;
    205     float desiredTextLength = 0;
    206 
    207     if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textPath)) {
    208         SVGLengthContext lengthContext(textContentElement);
    209         lengthAdjust = textContentElement->lengthAdjustCurrentValue();
    210         desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext);
    211     }
    212 
    213     if (!desiredTextLength)
    214         return;
    215 
    216     if (lengthAdjust == SVGLengthAdjustSpacing)
    217         m_textPathSpacing = (desiredTextLength - totalLength) / totalCharacters;
    218     else
    219         m_textPathScaling = desiredTextLength / totalLength;
    220 }
    221 
    222 void SVGTextLayoutEngine::endTextPathLayout()
    223 {
    224     m_inPathLayout = false;
    225     m_textPath = Path();
    226     m_textPathLength = 0;
    227     m_textPathStartOffset = 0;
    228     m_textPathCurrentOffset = 0;
    229     m_textPathSpacing = 0;
    230     m_textPathScaling = 1;
    231 }
    232 
    233 void SVGTextLayoutEngine::layoutInlineTextBox(SVGInlineTextBox* textBox)
    234 {
    235     ASSERT(textBox);
    236 
    237     RenderSVGInlineText* text = toRenderSVGInlineText(textBox->textRenderer());
    238     ASSERT(text);
    239     ASSERT(text->parent());
    240     ASSERT(text->parent()->node());
    241     ASSERT(text->parent()->node()->isSVGElement());
    242 
    243     const RenderStyle* style = text->style();
    244     ASSERT(style);
    245 
    246     textBox->clearTextFragments();
    247     m_isVerticalText = style->svgStyle()->isVerticalWritingMode();
    248     layoutTextOnLineOrPath(textBox, text, style);
    249 
    250     if (m_inPathLayout) {
    251         m_pathLayoutBoxes.append(textBox);
    252         return;
    253     }
    254 
    255     m_lineLayoutBoxes.append(textBox);
    256 }
    257 
    258 #if DUMP_TEXT_FRAGMENTS > 0
    259 static inline void dumpTextBoxes(Vector<SVGInlineTextBox*>& boxes)
    260 {
    261     unsigned boxCount = boxes.size();
    262     fprintf(stderr, "Dumping all text fragments in text sub tree, %i boxes\n", boxCount);
    263 
    264     for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
    265         SVGInlineTextBox* textBox = boxes.at(boxPosition);
    266         Vector<SVGTextFragment>& fragments = textBox->textFragments();
    267         fprintf(stderr, "-> Box %i: Dumping text fragments for SVGInlineTextBox, textBox=%p, textRenderer=%p\n", boxPosition, textBox, textBox->textRenderer());
    268         fprintf(stderr, "        textBox properties, start=%i, len=%i, box direction=%i\n", textBox->start(), textBox->len(), textBox->direction());
    269         fprintf(stderr, "   textRenderer properties, textLength=%i\n", textBox->textRenderer()->textLength());
    270 
    271         String characters = textBox->textRenderer()->text();
    272 
    273         unsigned fragmentCount = fragments.size();
    274         for (unsigned i = 0; i < fragmentCount; ++i) {
    275             SVGTextFragment& fragment = fragments.at(i);
    276             String fragmentString = characters.substring(fragment.characterOffset, fragment.length);
    277             fprintf(stderr, "    -> Fragment %i, x=%lf, y=%lf, width=%lf, height=%lf, characterOffset=%i, length=%i, characters='%s'\n"
    278                           , i, fragment.x, fragment.y, fragment.width, fragment.height, fragment.characterOffset, fragment.length, fragmentString.utf8().data());
    279         }
    280     }
    281 }
    282 #endif
    283 
    284 void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& boxes)
    285 {
    286     unsigned boxCount = boxes.size();
    287     if (!boxCount)
    288         return;
    289 
    290     AffineTransform textBoxTransformation;
    291     for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
    292         SVGInlineTextBox* textBox = boxes.at(boxPosition);
    293         Vector<SVGTextFragment>& fragments = textBox->textFragments();
    294 
    295         unsigned fragmentCount = fragments.size();
    296         for (unsigned i = 0; i < fragmentCount; ++i) {
    297             m_chunkLayoutBuilder.transformationForTextBox(textBox, textBoxTransformation);
    298             if (textBoxTransformation.isIdentity())
    299                 continue;
    300             ASSERT(fragments[i].lengthAdjustTransform.isIdentity());
    301             fragments[i].lengthAdjustTransform = textBoxTransformation;
    302         }
    303     }
    304 
    305     boxes.clear();
    306 }
    307 
    308 void SVGTextLayoutEngine::finishLayout()
    309 {
    310     // After all text fragments are stored in their correpsonding SVGInlineTextBoxes, we can layout individual text chunks.
    311     // Chunk layouting is only performed for line layout boxes, not for path layout, where it has already been done.
    312     m_chunkLayoutBuilder.layoutTextChunks(m_lineLayoutBoxes);
    313 
    314     // Finalize transform matrices, after the chunk layout corrections have been applied, and all fragment x/y positions are finalized.
    315     if (!m_lineLayoutBoxes.isEmpty()) {
    316 #if DUMP_TEXT_FRAGMENTS > 0
    317         fprintf(stderr, "Line layout: ");
    318         dumpTextBoxes(m_lineLayoutBoxes);
    319 #endif
    320 
    321         finalizeTransformMatrices(m_lineLayoutBoxes);
    322     }
    323 
    324     if (!m_pathLayoutBoxes.isEmpty()) {
    325 #if DUMP_TEXT_FRAGMENTS > 0
    326         fprintf(stderr, "Path layout: ");
    327         dumpTextBoxes(m_pathLayoutBoxes);
    328 #endif
    329 
    330         finalizeTransformMatrices(m_pathLayoutBoxes);
    331     }
    332 }
    333 
    334 bool SVGTextLayoutEngine::currentLogicalCharacterAttributes(SVGTextLayoutAttributes*& logicalAttributes)
    335 {
    336     if (m_layoutAttributesPosition == m_layoutAttributes.size())
    337         return false;
    338 
    339     logicalAttributes = m_layoutAttributes[m_layoutAttributesPosition];
    340     ASSERT(logicalAttributes);
    341 
    342     if (m_logicalCharacterOffset != logicalAttributes->context()->textLength())
    343         return true;
    344 
    345     ++m_layoutAttributesPosition;
    346     if (m_layoutAttributesPosition == m_layoutAttributes.size())
    347         return false;
    348 
    349     logicalAttributes = m_layoutAttributes[m_layoutAttributesPosition];
    350     m_logicalMetricsListOffset = 0;
    351     m_logicalCharacterOffset = 0;
    352     return true;
    353 }
    354 
    355 bool SVGTextLayoutEngine::currentLogicalCharacterMetrics(SVGTextLayoutAttributes*& logicalAttributes, SVGTextMetrics& logicalMetrics)
    356 {
    357     Vector<SVGTextMetrics>* textMetricsValues = &logicalAttributes->textMetricsValues();
    358     unsigned textMetricsSize = textMetricsValues->size();
    359     while (true) {
    360         if (m_logicalMetricsListOffset == textMetricsSize) {
    361             if (!currentLogicalCharacterAttributes(logicalAttributes))
    362                 return false;
    363 
    364             textMetricsValues = &logicalAttributes->textMetricsValues();
    365             textMetricsSize = textMetricsValues->size();
    366             continue;
    367         }
    368 
    369         ASSERT(textMetricsSize);
    370         ASSERT(m_logicalMetricsListOffset < textMetricsSize);
    371         logicalMetrics = textMetricsValues->at(m_logicalMetricsListOffset);
    372         if (logicalMetrics.isEmpty() || (!logicalMetrics.width() && !logicalMetrics.height())) {
    373             advanceToNextLogicalCharacter(logicalMetrics);
    374             continue;
    375         }
    376 
    377         // Stop if we found the next valid logical text metrics object.
    378         return true;
    379     }
    380 
    381     ASSERT_NOT_REACHED();
    382     return true;
    383 }
    384 
    385 bool SVGTextLayoutEngine::currentVisualCharacterMetrics(SVGInlineTextBox* textBox, Vector<SVGTextMetrics>& visualMetricsValues, SVGTextMetrics& visualMetrics)
    386 {
    387     ASSERT(!visualMetricsValues.isEmpty());
    388     unsigned textMetricsSize = visualMetricsValues.size();
    389     unsigned boxStart = textBox->start();
    390     unsigned boxLength = textBox->len();
    391 
    392     while (m_visualMetricsListOffset < textMetricsSize) {
    393         // Advance to text box start location.
    394         if (m_visualCharacterOffset < boxStart) {
    395             advanceToNextVisualCharacter(visualMetricsValues[m_visualMetricsListOffset]);
    396             continue;
    397         }
    398 
    399         // Stop if we've finished processing this text box.
    400         if (m_visualCharacterOffset >= boxStart + boxLength)
    401             return false;
    402 
    403         visualMetrics = visualMetricsValues[m_visualMetricsListOffset];
    404         return true;
    405     }
    406 
    407     return false;
    408 }
    409 
    410 void SVGTextLayoutEngine::advanceToNextLogicalCharacter(const SVGTextMetrics& logicalMetrics)
    411 {
    412     ++m_logicalMetricsListOffset;
    413     m_logicalCharacterOffset += logicalMetrics.length();
    414 }
    415 
    416 void SVGTextLayoutEngine::advanceToNextVisualCharacter(const SVGTextMetrics& visualMetrics)
    417 {
    418     ++m_visualMetricsListOffset;
    419     m_visualCharacterOffset += visualMetrics.length();
    420 }
    421 
    422 void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, RenderSVGInlineText* text, const RenderStyle* style)
    423 {
    424     if (m_inPathLayout && m_textPath.isEmpty())
    425         return;
    426 
    427     SVGElement* lengthContext = toSVGElement(text->parent()->node());
    428 
    429     RenderObject* textParent = text->parent();
    430     bool definesTextLength = textParent ? parentDefinesTextLength(textParent) : false;
    431 
    432     const SVGRenderStyle* svgStyle = style->svgStyle();
    433     ASSERT(svgStyle);
    434 
    435     m_visualMetricsListOffset = 0;
    436     m_visualCharacterOffset = 0;
    437 
    438     Vector<SVGTextMetrics>& visualMetricsValues = text->layoutAttributes()->textMetricsValues();
    439     ASSERT(!visualMetricsValues.isEmpty());
    440 
    441     const Font& font = style->font();
    442 
    443     SVGTextLayoutEngineSpacing spacingLayout(font);
    444     SVGTextLayoutEngineBaseline baselineLayout(font);
    445 
    446     bool didStartTextFragment = false;
    447     bool applySpacingToNextCharacter = false;
    448 
    449     float lastAngle = 0;
    450     float baselineShift = baselineLayout.calculateBaselineShift(svgStyle, lengthContext);
    451     baselineShift -= baselineLayout.calculateAlignmentBaselineShift(m_isVerticalText, text);
    452 
    453     // Main layout algorithm.
    454     while (true) {
    455         // Find the start of the current text box in this list, respecting ligatures.
    456         SVGTextMetrics visualMetrics(SVGTextMetrics::SkippedSpaceMetrics);
    457         if (!currentVisualCharacterMetrics(textBox, visualMetricsValues, visualMetrics))
    458             break;
    459 
    460         if (visualMetrics.isEmpty()) {
    461             advanceToNextVisualCharacter(visualMetrics);
    462             continue;
    463         }
    464 
    465         SVGTextLayoutAttributes* logicalAttributes = 0;
    466         if (!currentLogicalCharacterAttributes(logicalAttributes))
    467             break;
    468 
    469         ASSERT(logicalAttributes);
    470         SVGTextMetrics logicalMetrics(SVGTextMetrics::SkippedSpaceMetrics);
    471         if (!currentLogicalCharacterMetrics(logicalAttributes, logicalMetrics))
    472             break;
    473 
    474         SVGCharacterDataMap& characterDataMap = logicalAttributes->characterDataMap();
    475         SVGCharacterData data;
    476         SVGCharacterDataMap::iterator it = characterDataMap.find(m_logicalCharacterOffset + 1);
    477         if (it != characterDataMap.end())
    478             data = it->value;
    479 
    480         float x = data.x;
    481         float y = data.y;
    482 
    483         // When we've advanced to the box start offset, determine using the original x/y values,
    484         // whether this character starts a new text chunk, before doing any further processing.
    485         if (m_visualCharacterOffset == textBox->start())
    486             textBox->setStartsNewTextChunk(logicalAttributes->context()->characterStartsNewTextChunk(m_logicalCharacterOffset));
    487 
    488         float angle = data.rotate == SVGTextLayoutAttributes::emptyValue() ? 0 : data.rotate;
    489 
    490         // Calculate glyph orientation angle.
    491         UChar currentCharacter = text->characterAt(m_visualCharacterOffset);
    492         float orientationAngle = baselineLayout.calculateGlyphOrientationAngle(m_isVerticalText, svgStyle, currentCharacter);
    493 
    494         // Calculate glyph advance & x/y orientation shifts.
    495         float xOrientationShift = 0;
    496         float yOrientationShift = 0;
    497         float glyphAdvance = baselineLayout.calculateGlyphAdvanceAndOrientation(m_isVerticalText, visualMetrics, orientationAngle, xOrientationShift, yOrientationShift);
    498 
    499         // Assign current text position to x/y values, if needed.
    500         updateCharacerPositionIfNeeded(x, y);
    501 
    502         // Apply dx/dy value adjustments to current text position, if needed.
    503         updateRelativePositionAdjustmentsIfNeeded(data.dx, data.dy);
    504 
    505         // Calculate SVG Fonts kerning, if needed.
    506         float kerning = spacingLayout.calculateSVGKerning(m_isVerticalText, visualMetrics.glyph());
    507 
    508         // Calculate CSS 'kerning', 'letter-spacing' and 'word-spacing' for next character, if needed.
    509         float spacing = spacingLayout.calculateCSSKerningAndSpacing(svgStyle, lengthContext, currentCharacter);
    510 
    511         float textPathOffset = 0;
    512         if (m_inPathLayout) {
    513             float scaledGlyphAdvance = glyphAdvance * m_textPathScaling;
    514             if (m_isVerticalText) {
    515                 // If there's an absolute y position available, it marks the beginning of a new position along the path.
    516                 if (y != SVGTextLayoutAttributes::emptyValue())
    517                     m_textPathCurrentOffset = y + m_textPathStartOffset;
    518 
    519                 m_textPathCurrentOffset += m_dy - kerning;
    520                 m_dy = 0;
    521 
    522                 // Apply dx/dy correction and setup translations that move to the glyph midpoint.
    523                 xOrientationShift += m_dx + baselineShift;
    524                 yOrientationShift -= scaledGlyphAdvance / 2;
    525             } else {
    526                 // If there's an absolute x position available, it marks the beginning of a new position along the path.
    527                 if (x != SVGTextLayoutAttributes::emptyValue())
    528                     m_textPathCurrentOffset = x + m_textPathStartOffset;
    529 
    530                 m_textPathCurrentOffset += m_dx - kerning;
    531                 m_dx = 0;
    532 
    533                 // Apply dx/dy correction and setup translations that move to the glyph midpoint.
    534                 xOrientationShift -= scaledGlyphAdvance / 2;
    535                 yOrientationShift += m_dy - baselineShift;
    536             }
    537 
    538             // Calculate current offset along path.
    539             textPathOffset = m_textPathCurrentOffset + scaledGlyphAdvance / 2;
    540 
    541             // Move to next character.
    542             m_textPathCurrentOffset += scaledGlyphAdvance + m_textPathSpacing + spacing * m_textPathScaling;
    543 
    544             // Skip character, if we're before the path.
    545             if (textPathOffset < 0) {
    546                 advanceToNextLogicalCharacter(logicalMetrics);
    547                 advanceToNextVisualCharacter(visualMetrics);
    548                 continue;
    549             }
    550 
    551             // Stop processing, if the next character lies behind the path.
    552             if (textPathOffset > m_textPathLength)
    553                 break;
    554 
    555             bool ok = false;
    556             FloatPoint point = m_textPath.pointAtLength(textPathOffset, ok);
    557             ASSERT(ok);
    558 
    559             x = point.x();
    560             y = point.y();
    561             angle = m_textPath.normalAngleAtLength(textPathOffset, ok);
    562             ASSERT(ok);
    563 
    564             // For vertical text on path, the actual angle has to be rotated 90 degrees anti-clockwise, not the orientation angle!
    565             if (m_isVerticalText)
    566                 angle -= 90;
    567         } else {
    568             // Apply all previously calculated shift values.
    569             if (m_isVerticalText) {
    570                 x += baselineShift;
    571                 y -= kerning;
    572             } else {
    573                 x -= kerning;
    574                 y -= baselineShift;
    575             }
    576 
    577             x += m_dx;
    578             y += m_dy;
    579         }
    580 
    581         // Determine whether we have to start a new fragment.
    582         bool shouldStartNewFragment = m_dx || m_dy || m_isVerticalText || m_inPathLayout || angle || angle != lastAngle
    583             || orientationAngle || kerning || applySpacingToNextCharacter || definesTextLength;
    584 
    585         // If we already started a fragment, close it now.
    586         if (didStartTextFragment && shouldStartNewFragment) {
    587             applySpacingToNextCharacter = false;
    588             recordTextFragment(textBox, visualMetricsValues);
    589         }
    590 
    591         // Eventually start a new fragment, if not yet done.
    592         if (!didStartTextFragment || shouldStartNewFragment) {
    593             ASSERT(!m_currentTextFragment.characterOffset);
    594             ASSERT(!m_currentTextFragment.length);
    595 
    596             didStartTextFragment = true;
    597             m_currentTextFragment.characterOffset = m_visualCharacterOffset;
    598             m_currentTextFragment.metricsListOffset = m_visualMetricsListOffset;
    599             m_currentTextFragment.x = x;
    600             m_currentTextFragment.y = y;
    601 
    602             // Build fragment transformation.
    603             if (angle)
    604                 m_currentTextFragment.transform.rotate(angle);
    605 
    606             if (xOrientationShift || yOrientationShift)
    607                 m_currentTextFragment.transform.translate(xOrientationShift, yOrientationShift);
    608 
    609             if (orientationAngle)
    610                 m_currentTextFragment.transform.rotate(orientationAngle);
    611 
    612             m_currentTextFragment.isTextOnPath = m_inPathLayout && m_textPathScaling != 1;
    613             if (m_currentTextFragment.isTextOnPath) {
    614                 if (m_isVerticalText)
    615                     m_currentTextFragment.lengthAdjustTransform.scaleNonUniform(1, m_textPathScaling);
    616                 else
    617                     m_currentTextFragment.lengthAdjustTransform.scaleNonUniform(m_textPathScaling, 1);
    618             }
    619         }
    620 
    621         // Update current text position, after processing of the current character finished.
    622         if (m_inPathLayout)
    623             updateCurrentTextPosition(x, y, glyphAdvance);
    624         else {
    625             // Apply CSS 'kerning', 'letter-spacing' and 'word-spacing' to next character, if needed.
    626             if (spacing)
    627                 applySpacingToNextCharacter = true;
    628 
    629             float xNew = x - m_dx;
    630             float yNew = y - m_dy;
    631 
    632             if (m_isVerticalText)
    633                 xNew -= baselineShift;
    634             else
    635                 yNew += baselineShift;
    636 
    637             updateCurrentTextPosition(xNew, yNew, glyphAdvance + spacing);
    638         }
    639 
    640         advanceToNextLogicalCharacter(logicalMetrics);
    641         advanceToNextVisualCharacter(visualMetrics);
    642         lastAngle = angle;
    643     }
    644 
    645     if (!didStartTextFragment)
    646         return;
    647 
    648     // Close last open fragment, if needed.
    649     recordTextFragment(textBox, visualMetricsValues);
    650 }
    651 
    652 }
    653