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