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