Home | History | Annotate | Download | only in rendering
      1 /*
      2  * This file is part of the WebKit project.
      3  *
      4  * Copyright (C) 2006 Oliver Hunt <ojh16 (at) student.canterbury.ac.nz>
      5  *           (C) 2006 Apple Computer Inc.
      6  *           (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Library General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Library General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Library General Public License
     19  * along with this library; see the file COPYING.LIB.  If not, write to
     20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21  * Boston, MA 02110-1301, USA.
     22  *
     23  */
     24 
     25 #include "config.h"
     26 
     27 #if ENABLE(SVG)
     28 #include "SVGRootInlineBox.h"
     29 
     30 #include "Editor.h"
     31 #include "Frame.h"
     32 #include "GraphicsContext.h"
     33 #include "RenderBlock.h"
     34 #include "RenderSVGRoot.h"
     35 #include "SVGInlineFlowBox.h"
     36 #include "SVGInlineTextBox.h"
     37 #include "SVGFontElement.h"
     38 #include "SVGPaintServer.h"
     39 #include "SVGRenderStyleDefs.h"
     40 #include "SVGRenderSupport.h"
     41 #include "SVGResourceFilter.h"
     42 #include "SVGTextPositioningElement.h"
     43 #include "SVGURIReference.h"
     44 #include "Text.h"
     45 #include "UnicodeRange.h"
     46 
     47 #include <float.h>
     48 
     49 // Text chunk creation is complex and the whole process
     50 // can easily be traced by setting this variable > 0.
     51 #define DEBUG_CHUNK_BUILDING 0
     52 
     53 namespace WebCore {
     54 
     55 static inline bool isVerticalWritingMode(const SVGRenderStyle* style)
     56 {
     57     return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB;
     58 }
     59 
     60 static inline EAlignmentBaseline dominantBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font)
     61 {
     62     ASSERT(text);
     63 
     64     const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0;
     65     ASSERT(style);
     66 
     67     const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0;
     68 
     69     EDominantBaseline baseline = style->dominantBaseline();
     70     if (baseline == DB_AUTO) {
     71         if (isVerticalText)
     72             baseline = DB_CENTRAL;
     73         else
     74             baseline = DB_ALPHABETIC;
     75     }
     76 
     77     switch (baseline) {
     78     case DB_USE_SCRIPT:
     79         // TODO: The dominant-baseline and the baseline-table components are set by
     80         //       determining the predominant script of the character data content.
     81         return AB_ALPHABETIC;
     82     case DB_NO_CHANGE:
     83     {
     84         if (parentStyle)
     85             return dominantBaselineToShift(isVerticalText, text->parent(), font);
     86 
     87         ASSERT_NOT_REACHED();
     88         return AB_AUTO;
     89     }
     90     case DB_RESET_SIZE:
     91     {
     92         if (parentStyle)
     93             return dominantBaselineToShift(isVerticalText, text->parent(), font);
     94 
     95         ASSERT_NOT_REACHED();
     96         return AB_AUTO;
     97     }
     98     case DB_IDEOGRAPHIC:
     99         return AB_IDEOGRAPHIC;
    100     case DB_ALPHABETIC:
    101         return AB_ALPHABETIC;
    102     case DB_HANGING:
    103         return AB_HANGING;
    104     case DB_MATHEMATICAL:
    105         return AB_MATHEMATICAL;
    106     case DB_CENTRAL:
    107         return AB_CENTRAL;
    108     case DB_MIDDLE:
    109         return AB_MIDDLE;
    110     case DB_TEXT_AFTER_EDGE:
    111         return AB_TEXT_AFTER_EDGE;
    112     case DB_TEXT_BEFORE_EDGE:
    113         return AB_TEXT_BEFORE_EDGE;
    114     default:
    115         ASSERT_NOT_REACHED();
    116         return AB_AUTO;
    117     }
    118 }
    119 
    120 static inline float alignmentBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font)
    121 {
    122     ASSERT(text);
    123 
    124     const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0;
    125     ASSERT(style);
    126 
    127     const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0;
    128 
    129     EAlignmentBaseline baseline = style->alignmentBaseline();
    130     if (baseline == AB_AUTO) {
    131         if (parentStyle && style->dominantBaseline() == DB_AUTO)
    132             baseline = dominantBaselineToShift(isVerticalText, text->parent(), font);
    133         else
    134             baseline = dominantBaselineToShift(isVerticalText, text, font);
    135 
    136         ASSERT(baseline != AB_AUTO);
    137     }
    138 
    139     // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
    140     switch (baseline) {
    141     case AB_BASELINE:
    142     {
    143         if (parentStyle)
    144             return dominantBaselineToShift(isVerticalText, text->parent(), font);
    145 
    146         return 0.0f;
    147     }
    148     case AB_BEFORE_EDGE:
    149     case AB_TEXT_BEFORE_EDGE:
    150         return font.ascent();
    151     case AB_MIDDLE:
    152         return font.xHeight() / 2.0f;
    153     case AB_CENTRAL:
    154         // Not needed, we're taking this into account already for vertical text!
    155         // return (font.ascent() - font.descent()) / 2.0f;
    156         return 0.0f;
    157     case AB_AFTER_EDGE:
    158     case AB_TEXT_AFTER_EDGE:
    159     case AB_IDEOGRAPHIC:
    160         return font.descent();
    161     case AB_ALPHABETIC:
    162         return 0.0f;
    163     case AB_HANGING:
    164         return font.ascent() * 8.0f / 10.0f;
    165     case AB_MATHEMATICAL:
    166         return font.ascent() / 2.0f;
    167     default:
    168         ASSERT_NOT_REACHED();
    169         return 0.0f;
    170     }
    171 }
    172 
    173 static inline float glyphOrientationToAngle(const SVGRenderStyle* svgStyle, bool isVerticalText, const UChar& character)
    174 {
    175     switch (isVerticalText ? svgStyle->glyphOrientationVertical() : svgStyle->glyphOrientationHorizontal()) {
    176     case GO_AUTO:
    177     {
    178         // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees.
    179         //       Text which is not fullwidth will be set with a glyph-orientation of 90-degrees.
    180         unsigned int unicodeRange = findCharUnicodeRange(character);
    181         if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic)
    182             return 90.0f;
    183 
    184         return 0.0f;
    185     }
    186     case GO_90DEG:
    187         return 90.0f;
    188     case GO_180DEG:
    189         return 180.0f;
    190     case GO_270DEG:
    191         return 270.0f;
    192     case GO_0DEG:
    193     default:
    194         return 0.0f;
    195     }
    196 }
    197 
    198 static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)
    199 {
    200     return fabsf(fmodf(orientationAngle, 180.0f)) == 0.0f;
    201 }
    202 
    203 static inline float calculateGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float orientationAngle, float glyphWidth, float glyphHeight, const Font& font, SVGChar& svgChar, float& xOrientationShift, float& yOrientationShift)
    204 {
    205     bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(orientationAngle);
    206 
    207     // The function is based on spec requirements:
    208     //
    209     // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of
    210     // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph.
    211     //
    212     // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of
    213     // 180 degrees,then the current text position is incremented according to the horizontal metrics of the glyph.
    214 
    215     // vertical orientation handling
    216     if (isVerticalText) {
    217         if (orientationAngle == 0.0f) {
    218             xOrientationShift = -glyphWidth / 2.0f;
    219             yOrientationShift = font.ascent();
    220         } else if (orientationAngle == 90.0f) {
    221             xOrientationShift = -glyphHeight;
    222             yOrientationShift = font.descent();
    223             svgChar.orientationShiftY = -font.ascent();
    224         } else if (orientationAngle == 270.0f) {
    225             xOrientationShift = glyphHeight;
    226             yOrientationShift = font.descent();
    227             svgChar.orientationShiftX = -glyphWidth;
    228             svgChar.orientationShiftY = -font.ascent();
    229         } else if (orientationAngle == 180.0f) {
    230             yOrientationShift = font.ascent();
    231             svgChar.orientationShiftX = -glyphWidth / 2.0f;
    232             svgChar.orientationShiftY = font.ascent() - font.descent();
    233         }
    234 
    235         // vertical advance calculation
    236         if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees)
    237             return glyphWidth;
    238 
    239         return glyphHeight;
    240     }
    241 
    242     // horizontal orientation handling
    243     if (orientationAngle == 90.0f) {
    244         xOrientationShift = glyphWidth / 2.0f;
    245         yOrientationShift = -font.descent();
    246         svgChar.orientationShiftX = -glyphWidth / 2.0f - font.descent();
    247         svgChar.orientationShiftY = font.descent();
    248     } else if (orientationAngle == 270.0f) {
    249         xOrientationShift = -glyphWidth / 2.0f;
    250         yOrientationShift = -font.descent();
    251         svgChar.orientationShiftX = -glyphWidth / 2.0f + font.descent();
    252         svgChar.orientationShiftY = glyphHeight;
    253     } else if (orientationAngle == 180.0f) {
    254         xOrientationShift = glyphWidth / 2.0f;
    255         svgChar.orientationShiftX = -glyphWidth / 2.0f;
    256         svgChar.orientationShiftY = font.ascent() - font.descent();
    257     }
    258 
    259     // horizontal advance calculation
    260     if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees)
    261         return glyphHeight;
    262 
    263     return glyphWidth;
    264 }
    265 
    266 static inline void startTextChunk(SVGTextChunkLayoutInfo& info)
    267 {
    268     info.chunk.boxes.clear();
    269     info.chunk.boxes.append(SVGInlineBoxCharacterRange());
    270 
    271     info.chunk.start = info.it;
    272     info.assignChunkProperties = true;
    273 }
    274 
    275 static inline void closeTextChunk(SVGTextChunkLayoutInfo& info)
    276 {
    277     ASSERT(!info.chunk.boxes.last().isOpen());
    278     ASSERT(info.chunk.boxes.last().isClosed());
    279 
    280     info.chunk.end = info.it;
    281     ASSERT(info.chunk.end >= info.chunk.start);
    282 
    283     info.svgTextChunks.append(info.chunk);
    284 }
    285 
    286 RenderSVGRoot* findSVGRootObject(RenderObject* start)
    287 {
    288     // Find associated root inline box.
    289     while (start && !start->isSVGRoot())
    290         start = start->parent();
    291     ASSERT(start);
    292     return toRenderSVGRoot(start);
    293 }
    294 
    295 static inline FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>& chars)
    296 {
    297     return topLeftPositionOfCharacterRange(chars.begin(), chars.end());
    298 }
    299 
    300 FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator it, Vector<SVGChar>::iterator end)
    301 {
    302     float lowX = FLT_MAX, lowY = FLT_MAX;
    303     for (; it != end; ++it) {
    304         if (it->isHidden())
    305             continue;
    306 
    307         float x = (*it).x;
    308         float y = (*it).y;
    309 
    310         if (x < lowX)
    311             lowX = x;
    312 
    313         if (y < lowY)
    314             lowY = y;
    315     }
    316 
    317     return FloatPoint(lowX, lowY);
    318 }
    319 
    320 // Helper function
    321 static float calculateKerning(RenderObject* item)
    322 {
    323     const Font& font = item->style()->font();
    324     const SVGRenderStyle* svgStyle = item->style()->svgStyle();
    325 
    326     float kerning = 0.0f;
    327     if (CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->kerning())) {
    328         kerning = primitive->getFloatValue();
    329 
    330         if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE && font.pixelSize() > 0)
    331             kerning = kerning / 100.0f * font.pixelSize();
    332     }
    333 
    334     return kerning;
    335 }
    336 
    337 // Helper class for paint()
    338 struct SVGRootInlineBoxPaintWalker {
    339     SVGRootInlineBoxPaintWalker(SVGRootInlineBox* rootBox, SVGResourceFilter* rootFilter, RenderObject::PaintInfo paintInfo, int tx, int ty)
    340         : m_rootBox(rootBox)
    341         , m_chunkStarted(false)
    342         , m_paintInfo(paintInfo)
    343         , m_savedInfo(paintInfo)
    344         , m_boundingBox(tx + rootBox->x(), ty + rootBox->y(), rootBox->width(), rootBox->height())
    345         , m_filter(0)
    346         , m_rootFilter(rootFilter)
    347         , m_fillPaintServer(0)
    348         , m_strokePaintServer(0)
    349         , m_fillPaintServerObject(0)
    350         , m_strokePaintServerObject(0)
    351         , m_tx(tx)
    352         , m_ty(ty)
    353     {
    354     }
    355 
    356     ~SVGRootInlineBoxPaintWalker()
    357     {
    358         ASSERT(!m_filter);
    359         ASSERT(!m_fillPaintServer);
    360         ASSERT(!m_fillPaintServerObject);
    361         ASSERT(!m_strokePaintServer);
    362         ASSERT(!m_strokePaintServerObject);
    363         ASSERT(!m_chunkStarted);
    364     }
    365 
    366     bool mayHaveSelection(InlineBox* box) const
    367     {
    368         int selectionStart = 0, selectionEnd = 0;
    369         box->renderer()->selectionStartEnd(selectionStart, selectionEnd);
    370         return selectionStart < selectionEnd;
    371     }
    372 
    373     void teardownFillPaintServer()
    374     {
    375         if (!m_fillPaintServer)
    376             return;
    377 
    378         m_fillPaintServer->teardown(m_paintInfo.context, m_fillPaintServerObject, ApplyToFillTargetType, true);
    379 
    380         m_fillPaintServer = 0;
    381         m_fillPaintServerObject = 0;
    382     }
    383 
    384     void teardownStrokePaintServer()
    385     {
    386         if (!m_strokePaintServer)
    387             return;
    388 
    389         m_strokePaintServer->teardown(m_paintInfo.context, m_strokePaintServerObject, ApplyToStrokeTargetType, true);
    390 
    391         m_strokePaintServer = 0;
    392         m_strokePaintServerObject = 0;
    393     }
    394 
    395     void chunkStartCallback(InlineBox* box)
    396     {
    397         ASSERT(!m_chunkStarted);
    398         m_chunkStarted = true;
    399 
    400         InlineFlowBox* flowBox = box->parent();
    401 
    402         // Initialize text rendering
    403         RenderObject* object = flowBox->renderer();
    404         ASSERT(object);
    405 
    406         m_savedInfo = m_paintInfo;
    407         m_paintInfo.context->save();
    408 
    409         // FIXME: Why is this done here instead of in RenderSVGText?
    410         if (!flowBox->isRootInlineBox())
    411             SVGRenderBase::prepareToRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_rootFilter);
    412     }
    413 
    414     void chunkEndCallback(InlineBox* box)
    415     {
    416         ASSERT(m_chunkStarted);
    417         m_chunkStarted = false;
    418 
    419         InlineFlowBox* flowBox = box->parent();
    420 
    421         RenderObject* object = flowBox->renderer();
    422         ASSERT(object);
    423 
    424         // Clean up last used paint server
    425         teardownFillPaintServer();
    426         teardownStrokePaintServer();
    427 
    428         // Finalize text rendering
    429         if (!flowBox->isRootInlineBox()) {
    430             SVGRenderBase::finishRenderSVGContent(object, m_paintInfo, m_filter, m_savedInfo.context);
    431             m_filter = 0;
    432         }
    433 
    434         // Restore context & repaint rect
    435         m_paintInfo.context->restore();
    436         m_paintInfo.rect = m_savedInfo.rect;
    437     }
    438 
    439     bool chunkSetupBackgroundCallback(InlineBox* /*box*/)
    440     {
    441         m_textPaintInfo.subphase = SVGTextPaintSubphaseBackground;
    442         return true;
    443     }
    444 
    445     bool chunkSetupFillCallback(InlineBox* box)
    446     {
    447         InlineFlowBox* flowBox = box->parent();
    448 
    449         // Setup fill paint server
    450         RenderObject* object = flowBox->renderer();
    451         ASSERT(object);
    452 
    453         ASSERT(!m_strokePaintServer);
    454         teardownFillPaintServer();
    455 
    456         m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphFill;
    457         m_fillPaintServer = SVGPaintServer::fillPaintServer(object->style(), object);
    458         if (m_fillPaintServer) {
    459             m_fillPaintServer->setup(m_paintInfo.context, object, ApplyToFillTargetType, true);
    460             m_fillPaintServerObject = object;
    461             return true;
    462         }
    463 
    464         return false;
    465     }
    466 
    467     bool chunkSetupFillSelectionCallback(InlineBox* box)
    468     {
    469         InlineFlowBox* flowBox = box->parent();
    470 
    471         // Setup fill paint server
    472         RenderObject* object = flowBox->renderer();
    473         ASSERT(object);
    474         RenderStyle* style = object->getCachedPseudoStyle(SELECTION);
    475         if (!style)
    476             style = object->style();
    477 
    478         ASSERT(!m_strokePaintServer);
    479         teardownFillPaintServer();
    480 
    481         if (!mayHaveSelection(box))
    482             return false;
    483 
    484         m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphFillSelection;
    485         m_fillPaintServer = SVGPaintServer::fillPaintServer(style, object);
    486         if (m_fillPaintServer) {
    487             m_fillPaintServer->setup(m_paintInfo.context, object, style, ApplyToFillTargetType, true);
    488             m_fillPaintServerObject = object;
    489             return true;
    490         }
    491 
    492         return false;
    493     }
    494 
    495     bool chunkSetupStrokeCallback(InlineBox* box)
    496     {
    497         InlineFlowBox* flowBox = box->parent();
    498 
    499         // Setup stroke paint server
    500         RenderObject* object = flowBox->renderer();
    501         ASSERT(object);
    502 
    503         // If we're both stroked & filled, teardown fill paint server before stroking.
    504         teardownFillPaintServer();
    505         teardownStrokePaintServer();
    506 
    507         m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphStroke;
    508         m_strokePaintServer = SVGPaintServer::strokePaintServer(object->style(), object);
    509 
    510         if (m_strokePaintServer) {
    511             m_strokePaintServer->setup(m_paintInfo.context, object, ApplyToStrokeTargetType, true);
    512             m_strokePaintServerObject = object;
    513             return true;
    514         }
    515 
    516         return false;
    517     }
    518 
    519     bool chunkSetupStrokeSelectionCallback(InlineBox* box)
    520     {
    521         InlineFlowBox* flowBox = box->parent();
    522 
    523         // Setup stroke paint server
    524         RenderObject* object = flowBox->renderer();
    525         ASSERT(object);
    526         RenderStyle* style = object->getCachedPseudoStyle(SELECTION);
    527         if (!style)
    528             style = object->style();
    529 
    530         // If we're both stroked & filled, teardown fill paint server before stroking.
    531         teardownFillPaintServer();
    532         teardownStrokePaintServer();
    533 
    534         if (!mayHaveSelection(box))
    535             return false;
    536 
    537         m_textPaintInfo.subphase = SVGTextPaintSubphaseGlyphStrokeSelection;
    538         m_strokePaintServer = SVGPaintServer::strokePaintServer(style, object);
    539         if (m_strokePaintServer) {
    540             m_strokePaintServer->setup(m_paintInfo.context, object, style, ApplyToStrokeTargetType, true);
    541             m_strokePaintServerObject = object;
    542             return true;
    543         }
    544 
    545         return false;
    546     }
    547 
    548     bool chunkSetupForegroundCallback(InlineBox* /*box*/)
    549     {
    550         teardownFillPaintServer();
    551         teardownStrokePaintServer();
    552 
    553         m_textPaintInfo.subphase = SVGTextPaintSubphaseForeground;
    554 
    555         return true;
    556     }
    557 
    558     SVGPaintServer* activePaintServer() const
    559     {
    560         switch (m_textPaintInfo.subphase) {
    561         case SVGTextPaintSubphaseGlyphFill:
    562         case SVGTextPaintSubphaseGlyphFillSelection:
    563             ASSERT(m_fillPaintServer);
    564             return m_fillPaintServer;
    565         case SVGTextPaintSubphaseGlyphStroke:
    566         case SVGTextPaintSubphaseGlyphStrokeSelection:
    567             ASSERT(m_strokePaintServer);
    568             return m_strokePaintServer;
    569         case SVGTextPaintSubphaseBackground:
    570         case SVGTextPaintSubphaseForeground:
    571         default:
    572             return 0;
    573         }
    574     }
    575 
    576     void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm,
    577                               const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end)
    578     {
    579         RenderText* text = textBox->textRenderer();
    580         ASSERT(text);
    581 
    582         RenderStyle* styleToUse = text->style(textBox->isFirstLineStyle());
    583         ASSERT(styleToUse);
    584 
    585         startOffset += textBox->start();
    586 
    587         int textDecorations = styleToUse->textDecorationsInEffect();
    588 
    589         int textWidth = 0;
    590         IntPoint decorationOrigin;
    591         SVGTextDecorationInfo info;
    592 
    593         if (!chunkCtm.isIdentity())
    594             m_paintInfo.context->concatCTM(chunkCtm);
    595 
    596         for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
    597             if (it->isHidden())
    598                 continue;
    599 
    600             // Determine how many characters - starting from the current - can be drawn at once.
    601             Vector<SVGChar>::iterator itSearch = it + 1;
    602             while (itSearch != end) {
    603                 if (itSearch->drawnSeperated || itSearch->isHidden())
    604                     break;
    605 
    606                 itSearch++;
    607             }
    608 
    609             const UChar* stringStart = text->characters() + startOffset + (it - start);
    610             unsigned int stringLength = itSearch - it;
    611 
    612             // Paint decorations, that have to be drawn before the text gets drawn
    613             if (textDecorations != TDNONE && m_paintInfo.phase != PaintPhaseSelection) {
    614                 textWidth = styleToUse->font().width(svgTextRunForInlineTextBox(stringStart, stringLength, styleToUse, textBox, (*it).x));
    615                 decorationOrigin = IntPoint((int) (*it).x, (int) (*it).y - styleToUse->font().ascent());
    616                 info = m_rootBox->retrievePaintServersForTextDecoration(text);
    617             }
    618 
    619             if (textDecorations & UNDERLINE && textWidth != 0.0f)
    620                 textBox->paintDecoration(UNDERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
    621 
    622             if (textDecorations & OVERLINE && textWidth != 0.0f)
    623                 textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
    624 
    625             // Paint text
    626             m_textPaintInfo.activePaintServer = activePaintServer();
    627             textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, m_textPaintInfo);
    628 
    629             // Paint decorations, that have to be drawn afterwards
    630             if (textDecorations & LINE_THROUGH && textWidth != 0.0f)
    631                 textBox->paintDecoration(LINE_THROUGH, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
    632 
    633             // Skip processed characters
    634             it = itSearch - 1;
    635         }
    636 
    637         if (!chunkCtm.isIdentity())
    638             m_paintInfo.context->concatCTM(chunkCtm.inverse());
    639     }
    640 
    641 private:
    642     SVGRootInlineBox* m_rootBox;
    643     bool m_chunkStarted : 1;
    644 
    645     RenderObject::PaintInfo m_paintInfo;
    646     RenderObject::PaintInfo m_savedInfo;
    647 
    648     FloatRect m_boundingBox;
    649     SVGResourceFilter* m_filter;
    650     SVGResourceFilter* m_rootFilter;
    651 
    652     SVGPaintServer* m_fillPaintServer;
    653     SVGPaintServer* m_strokePaintServer;
    654 
    655     RenderObject* m_fillPaintServerObject;
    656     RenderObject* m_strokePaintServerObject;
    657 
    658     int m_tx;
    659     int m_ty;
    660 
    661     SVGTextPaintInfo m_textPaintInfo;
    662 };
    663 
    664 void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty)
    665 {
    666     if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground)
    667         return;
    668 
    669     RenderObject::PaintInfo savedInfo(paintInfo);
    670     paintInfo.context->save();
    671 
    672     SVGResourceFilter* filter = 0;
    673     FloatRect boundingBox(tx + x(), ty + y(), width(), height());
    674 
    675     // Initialize text rendering
    676     if (SVGRenderBase::prepareToRenderSVGContent(renderer(), paintInfo, boundingBox, filter)) {
    677         // Render text, chunk-by-chunk
    678         SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty);
    679         SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback,
    680                                                                &SVGRootInlineBoxPaintWalker::chunkPortionCallback,
    681                                                                &SVGRootInlineBoxPaintWalker::chunkStartCallback,
    682                                                                &SVGRootInlineBoxPaintWalker::chunkEndCallback,
    683                                                                &SVGRootInlineBoxPaintWalker::chunkSetupBackgroundCallback,
    684                                                                &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback,
    685                                                                &SVGRootInlineBoxPaintWalker::chunkSetupFillSelectionCallback,
    686                                                                &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback,
    687                                                                &SVGRootInlineBoxPaintWalker::chunkSetupStrokeSelectionCallback,
    688                                                                &SVGRootInlineBoxPaintWalker::chunkSetupForegroundCallback);
    689 
    690         walkTextChunks(&walker);
    691     }
    692 
    693     // Finalize text rendering
    694     SVGRenderBase::finishRenderSVGContent(renderer(), paintInfo, filter, savedInfo.context);
    695     paintInfo.context->restore();
    696 }
    697 
    698 int SVGRootInlineBox::placeBoxesHorizontally(int, int& leftPosition, int& rightPosition, bool&)
    699 {
    700     // Remove any offsets caused by RTL text layout
    701     leftPosition = 0;
    702     rightPosition = 0;
    703     return 0;
    704 }
    705 
    706 int SVGRootInlineBox::verticallyAlignBoxes(int)
    707 {
    708     // height is set by layoutInlineBoxes.
    709     return height();
    710 }
    711 
    712 float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range)
    713 {
    714     ASSERT(!range.isOpen());
    715     ASSERT(range.isClosed());
    716     ASSERT(range.box->isInlineTextBox());
    717 
    718     InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box);
    719     RenderText* text = textBox->textRenderer();
    720     RenderStyle* style = text->style();
    721 
    722     return style->font().floatWidth(svgTextRunForInlineTextBox(text->characters() + textBox->start() + range.startOffset, range.endOffset - range.startOffset, style, textBox, 0));
    723 }
    724 
    725 float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range)
    726 {
    727     ASSERT(!range.isOpen());
    728     ASSERT(range.isClosed());
    729     ASSERT(range.box->isInlineTextBox());
    730 
    731     InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box);
    732     RenderText* text = textBox->textRenderer();
    733     const Font& font = text->style()->font();
    734 
    735     return (range.endOffset - range.startOffset) * (font.ascent() + font.descent());
    736 }
    737 
    738 TextRun svgTextRunForInlineTextBox(const UChar* c, int len, RenderStyle* style, const InlineTextBox* textBox, float xPos)
    739 {
    740     ASSERT(textBox);
    741     ASSERT(style);
    742 
    743     TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->direction() == RTL, textBox->m_dirOverride || style->visuallyOrdered());
    744 
    745 #if ENABLE(SVG_FONTS)
    746     run.setReferencingRenderObject(textBox->textRenderer()->parent());
    747 #endif
    748 
    749     // We handle letter & word spacing ourselves
    750     run.disableSpacing();
    751     return run;
    752 }
    753 
    754 static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWidthOnly)
    755 {
    756     float length = 0.0f;
    757     Vector<SVGChar>::iterator charIt = chunk.start;
    758 
    759     Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin();
    760     Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end();
    761 
    762     for (; it != end; ++it) {
    763         SVGInlineBoxCharacterRange& range = *it;
    764 
    765         SVGInlineTextBox* box = static_cast<SVGInlineTextBox*>(range.box);
    766         RenderStyle* style = box->renderer()->style();
    767 
    768         for (int i = range.startOffset; i < range.endOffset; ++i) {
    769             ASSERT(charIt <= chunk.end);
    770 
    771             // Determine how many characters - starting from the current - can be measured at once.
    772             // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width
    773             // of a string is not the sum of the boundaries of all contained glyphs.
    774             Vector<SVGChar>::iterator itSearch = charIt + 1;
    775             Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i;
    776             while (itSearch != endSearch) {
    777                 // No need to check for 'isHidden()' here as this function is not called for text paths.
    778                 if (itSearch->drawnSeperated)
    779                     break;
    780 
    781                 itSearch++;
    782             }
    783 
    784             unsigned int positionOffset = itSearch - charIt;
    785 
    786             // Calculate width/height of subrange
    787             SVGInlineBoxCharacterRange subRange;
    788             subRange.box = range.box;
    789             subRange.startOffset = i;
    790             subRange.endOffset = i + positionOffset;
    791 
    792             if (calcWidthOnly)
    793                 length += cummulatedWidthOfInlineBoxCharacterRange(subRange);
    794             else
    795                 length += cummulatedHeightOfInlineBoxCharacterRange(subRange);
    796 
    797             // Calculate gap between the previous & current range
    798             // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account
    799             // so add "40" as width, and analogous for B & C, add "20" as width.
    800             if (itSearch > chunk.start && itSearch < chunk.end) {
    801                 SVGChar& lastCharacter = *(itSearch - 1);
    802                 SVGChar& currentCharacter = *itSearch;
    803 
    804                 int offset = box->direction() == RTL ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1;
    805 
    806                 // FIXME: does this need to change to handle multichar glyphs?
    807                 int charsConsumed = 1;
    808                 String glyphName;
    809                 if (calcWidthOnly) {
    810                     float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName);
    811                     length += currentCharacter.x - lastCharacter.x - lastGlyphWidth;
    812                 } else {
    813                     float lastGlyphHeight = box->calculateGlyphHeight(style, offset, 0);
    814                     length += currentCharacter.y - lastCharacter.y - lastGlyphHeight;
    815                 }
    816             }
    817 
    818             // Advance processed characters
    819             i += positionOffset - 1;
    820             charIt = itSearch;
    821         }
    822     }
    823 
    824     ASSERT(charIt == chunk.end);
    825     return length;
    826 }
    827 
    828 static float cummulatedWidthOfTextChunk(SVGTextChunk& chunk)
    829 {
    830     return cummulatedWidthOrHeightOfTextChunk(chunk, true);
    831 }
    832 
    833 static float cummulatedHeightOfTextChunk(SVGTextChunk& chunk)
    834 {
    835     return cummulatedWidthOrHeightOfTextChunk(chunk, false);
    836 }
    837 
    838 static float calculateTextAnchorShiftForTextChunk(SVGTextChunk& chunk, ETextAnchor anchor)
    839 {
    840     float shift = 0.0f;
    841 
    842     if (chunk.isVerticalText)
    843         shift = cummulatedHeightOfTextChunk(chunk);
    844     else
    845         shift = cummulatedWidthOfTextChunk(chunk);
    846 
    847     if (anchor == TA_MIDDLE)
    848         shift *= -0.5f;
    849     else
    850         shift *= -1.0f;
    851 
    852     return shift;
    853 }
    854 
    855 static void applyTextAnchorToTextChunk(SVGTextChunk& chunk)
    856 {
    857     // This method is not called for chunks containing chars aligned on a path.
    858     // -> all characters are visible, no need to check for "isHidden()" anywhere.
    859 
    860     if (chunk.anchor == TA_START)
    861         return;
    862 
    863     float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor);
    864 
    865     // Apply correction to chunk
    866     Vector<SVGChar>::iterator chunkIt = chunk.start;
    867     for (; chunkIt != chunk.end; ++chunkIt) {
    868         SVGChar& curChar = *chunkIt;
    869 
    870         if (chunk.isVerticalText)
    871             curChar.y += shift;
    872         else
    873             curChar.x += shift;
    874     }
    875 
    876     // Move inline boxes
    877     Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
    878     Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
    879 
    880     for (; boxIt != boxEnd; ++boxIt) {
    881         SVGInlineBoxCharacterRange& range = *boxIt;
    882 
    883         InlineBox* curBox = range.box;
    884         ASSERT(curBox->isInlineTextBox());
    885 
    886         // Move target box
    887         if (chunk.isVerticalText)
    888             curBox->setY(curBox->y() + static_cast<int>(shift));
    889         else
    890             curBox->setX(curBox->x() + static_cast<int>(shift));
    891     }
    892 }
    893 
    894 static float calculateTextLengthCorrectionForTextChunk(SVGTextChunk& chunk, ELengthAdjust lengthAdjust, float& computedLength)
    895 {
    896     if (chunk.textLength <= 0.0f)
    897         return 0.0f;
    898 
    899     float computedWidth = cummulatedWidthOfTextChunk(chunk);
    900     float computedHeight = cummulatedHeightOfTextChunk(chunk);
    901 
    902     if ((computedWidth <= 0.0f && !chunk.isVerticalText) ||
    903         (computedHeight <= 0.0f && chunk.isVerticalText))
    904         return 0.0f;
    905 
    906     if (chunk.isVerticalText)
    907         computedLength = computedHeight;
    908     else
    909         computedLength = computedWidth;
    910 
    911     if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
    912         if (chunk.isVerticalText)
    913             chunk.ctm.scaleNonUniform(1.0f, chunk.textLength / computedLength);
    914         else
    915             chunk.ctm.scaleNonUniform(chunk.textLength / computedLength, 1.0f);
    916 
    917         return 0.0f;
    918     }
    919 
    920     return (chunk.textLength - computedLength) / float(chunk.end - chunk.start);
    921 }
    922 
    923 static void applyTextLengthCorrectionToTextChunk(SVGTextChunk& chunk)
    924 {
    925     // This method is not called for chunks containing chars aligned on a path.
    926     // -> all characters are visible, no need to check for "isHidden()" anywhere.
    927 
    928     // lengthAdjust="spacingAndGlyphs" is handled by modifying chunk.ctm
    929     float computedLength = 0.0f;
    930     float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, computedLength);
    931 
    932     if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
    933         SVGChar& firstChar = *(chunk.start);
    934 
    935         // Assure we apply the chunk scaling in the right origin
    936         AffineTransform newChunkCtm(chunk.ctm);
    937         newChunkCtm.translateRight(firstChar.x, firstChar.y);
    938         newChunkCtm.translate(-firstChar.x, -firstChar.y);
    939 
    940         chunk.ctm = newChunkCtm;
    941     }
    942 
    943     // Apply correction to chunk
    944     if (spacingToApply != 0.0f) {
    945         Vector<SVGChar>::iterator chunkIt = chunk.start;
    946         for (; chunkIt != chunk.end; ++chunkIt) {
    947             SVGChar& curChar = *chunkIt;
    948             curChar.drawnSeperated = true;
    949 
    950             if (chunk.isVerticalText)
    951                 curChar.y += (chunkIt - chunk.start) * spacingToApply;
    952             else
    953                 curChar.x += (chunkIt - chunk.start) * spacingToApply;
    954         }
    955     }
    956 }
    957 
    958 void SVGRootInlineBox::computePerCharacterLayoutInformation()
    959 {
    960     // Clean up any previous layout information
    961     m_svgChars.clear();
    962     m_svgTextChunks.clear();
    963 
    964     // Build layout information for all contained render objects
    965     SVGCharacterLayoutInfo info(m_svgChars);
    966     buildLayoutInformation(this, info);
    967 
    968     // Now all layout information are available for every character
    969     // contained in any of our child inline/flow boxes. Build list
    970     // of text chunks now, to be able to apply text-anchor shifts.
    971     buildTextChunks(m_svgChars, m_svgTextChunks, this);
    972 
    973     // Layout all text chunks
    974     // text-anchor needs to be applied to individual chunks.
    975     layoutTextChunks();
    976 
    977     // Finally the top left position of our box is known.
    978     // Propogate this knownledge to our RenderSVGText parent.
    979     FloatPoint topLeft = topLeftPositionOfCharacterRange(m_svgChars);
    980     block()->setLocation((int) floorf(topLeft.x()), (int) floorf(topLeft.y()));
    981 
    982     // Layout all InlineText/Flow boxes
    983     // BEWARE: This requires the root top/left position to be set correctly before!
    984     layoutInlineBoxes();
    985 }
    986 
    987 void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo& info)
    988 {
    989     if (start->isRootInlineBox()) {
    990         ASSERT(start->renderer()->node()->hasTagName(SVGNames::textTag));
    991 
    992         SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(start->renderer()->node());
    993         ASSERT(positioningElement);
    994         ASSERT(positioningElement->parentNode());
    995 
    996         info.addLayoutInformation(positioningElement);
    997     }
    998 
    999     LastGlyphInfo lastGlyph;
   1000 
   1001     for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
   1002         if (curr->renderer()->isText())
   1003             buildLayoutInformationForTextBox(info, static_cast<InlineTextBox*>(curr), lastGlyph);
   1004         else {
   1005             ASSERT(curr->isInlineFlowBox());
   1006             InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
   1007 
   1008             if (!flowBox->renderer()->node())
   1009                 continue; // Skip generated content.
   1010 
   1011             bool isAnchor = flowBox->renderer()->node()->hasTagName(SVGNames::aTag);
   1012             bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag);
   1013 
   1014             if (!isTextPath && !isAnchor) {
   1015                 SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(flowBox->renderer()->node());
   1016                 ASSERT(positioningElement);
   1017                 ASSERT(positioningElement->parentNode());
   1018 
   1019                 info.addLayoutInformation(positioningElement);
   1020             } else if (!isAnchor) {
   1021                 info.setInPathLayout(true);
   1022 
   1023                 // Handle text-anchor/textLength on path, which is special.
   1024                 SVGTextContentElement* textContent = 0;
   1025                 Node* node = flowBox->renderer()->node();
   1026                 if (node && node->isSVGElement())
   1027                     textContent = static_cast<SVGTextContentElement*>(node);
   1028                 ASSERT(textContent);
   1029 
   1030                 ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
   1031                 ETextAnchor anchor = flowBox->renderer()->style()->svgStyle()->textAnchor();
   1032                 float textAnchorStartOffset = 0.0f;
   1033 
   1034                 // Initialize sub-layout. We need to create text chunks from the textPath
   1035                 // children using our standard layout code, to be able to measure the
   1036                 // text length using our normal methods and not textPath specific hacks.
   1037                 Vector<SVGChar> tempChars;
   1038                 Vector<SVGTextChunk> tempChunks;
   1039 
   1040                 SVGCharacterLayoutInfo tempInfo(tempChars);
   1041                 buildLayoutInformation(flowBox, tempInfo);
   1042 
   1043                 buildTextChunks(tempChars, tempChunks, flowBox);
   1044 
   1045                 Vector<SVGTextChunk>::iterator it = tempChunks.begin();
   1046                 Vector<SVGTextChunk>::iterator end = tempChunks.end();
   1047 
   1048                 float computedLength = 0.0f;
   1049 
   1050                 for (; it != end; ++it) {
   1051                     SVGTextChunk& chunk = *it;
   1052 
   1053                     // Apply text-length calculation
   1054                     info.pathExtraAdvance += calculateTextLengthCorrectionForTextChunk(chunk, lengthAdjust, computedLength);
   1055 
   1056                     if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
   1057                         info.pathTextLength += computedLength;
   1058                         info.pathChunkLength += chunk.textLength;
   1059                     }
   1060 
   1061                     // Calculate text-anchor start offset
   1062                     if (anchor == TA_START)
   1063                         continue;
   1064 
   1065                     textAnchorStartOffset += calculateTextAnchorShiftForTextChunk(chunk, anchor);
   1066                 }
   1067 
   1068                 info.addLayoutInformation(flowBox, textAnchorStartOffset);
   1069             }
   1070 
   1071             float shiftxSaved = info.shiftx;
   1072             float shiftySaved = info.shifty;
   1073 
   1074             buildLayoutInformation(flowBox, info);
   1075             info.processedChunk(shiftxSaved, shiftySaved);
   1076 
   1077             if (isTextPath)
   1078                 info.setInPathLayout(false);
   1079         }
   1080     }
   1081 }
   1082 
   1083 void SVGRootInlineBox::layoutInlineBoxes()
   1084 {
   1085     int lowX = INT_MAX;
   1086     int lowY = INT_MAX;
   1087     int highX = INT_MIN;
   1088     int highY = INT_MIN;
   1089 
   1090     // Layout all child boxes
   1091     Vector<SVGChar>::iterator it = m_svgChars.begin();
   1092     layoutInlineBoxes(this, it, lowX, highX, lowY, highY);
   1093     ASSERT(it == m_svgChars.end());
   1094 }
   1095 
   1096 void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& lowX, int& highX, int& lowY, int& highY)
   1097 {
   1098     for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
   1099         RenderStyle* style = curr->renderer()->style();
   1100         if (curr->renderer()->isText()) {
   1101             SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(curr);
   1102             unsigned length = textBox->len();
   1103 
   1104             SVGChar curChar = *it;
   1105             ASSERT(it != m_svgChars.end());
   1106 
   1107             FloatRect stringRect;
   1108             for (unsigned i = 0; i < length; ++i) {
   1109                 ASSERT(it != m_svgChars.end());
   1110 
   1111                 if (it->isHidden()) {
   1112                     ++it;
   1113                     continue;
   1114                 }
   1115 
   1116                 stringRect.unite(textBox->calculateGlyphBoundaries(style, textBox->start() + i, *it));
   1117                 ++it;
   1118             }
   1119 
   1120             IntRect enclosedStringRect = enclosingIntRect(stringRect);
   1121 
   1122             int minX = enclosedStringRect.x();
   1123             int maxX = minX + enclosedStringRect.width();
   1124 
   1125             int minY = enclosedStringRect.y();
   1126             int maxY = minY + enclosedStringRect.height();
   1127 
   1128             curr->setX(minX - block()->x());
   1129             curr->setWidth(enclosedStringRect.width());
   1130 
   1131             curr->setY(minY - block()->y());
   1132             textBox->setHeight(enclosedStringRect.height());
   1133 
   1134             if (minX < lowX)
   1135                 lowX = minX;
   1136 
   1137             if (maxX > highX)
   1138                 highX = maxX;
   1139 
   1140             if (minY < lowY)
   1141                 lowY = minY;
   1142 
   1143             if (maxY > highY)
   1144                 highY = maxY;
   1145         } else {
   1146             ASSERT(curr->isInlineFlowBox());
   1147 
   1148             int minX = INT_MAX;
   1149             int minY = INT_MAX;
   1150             int maxX = INT_MIN;
   1151             int maxY = INT_MIN;
   1152 
   1153             InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
   1154 
   1155             if (!flowBox->renderer()->node())
   1156                 continue; // Skip generated content.
   1157 
   1158             layoutInlineBoxes(flowBox, it, minX, maxX, minY, maxY);
   1159 
   1160             curr->setX(minX - block()->x());
   1161             curr->setWidth(maxX - minX);
   1162 
   1163             curr->setY(minY - block()->y());
   1164             static_cast<SVGInlineFlowBox*>(curr)->setHeight(maxY - minY);
   1165 
   1166             if (minX < lowX)
   1167                 lowX = minX;
   1168 
   1169             if (maxX > highX)
   1170                 highX = maxX;
   1171 
   1172             if (minY < lowY)
   1173                 lowY = minY;
   1174 
   1175             if (maxY > highY)
   1176                 highY = maxY;
   1177         }
   1178     }
   1179 
   1180     if (start->isSVGRootInlineBox()) {
   1181         int top = lowY - block()->y();
   1182         int bottom = highY - block()->y();
   1183 
   1184         start->setX(lowX - block()->x());
   1185         start->setY(top);
   1186 
   1187         start->setWidth(highX - lowX);
   1188         static_cast<SVGRootInlineBox*>(start)->setHeight(highY - lowY);
   1189 
   1190         start->computeVerticalOverflow(top, bottom, true);
   1191         static_cast<SVGRootInlineBox*>(start)->setLineTopBottomPositions(top, bottom);
   1192     }
   1193 }
   1194 
   1195 void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& info, InlineTextBox* textBox, LastGlyphInfo& lastGlyph)
   1196 {
   1197     RenderText* text = textBox->textRenderer();
   1198     ASSERT(text);
   1199 
   1200     RenderStyle* style = text->style(textBox->isFirstLineStyle());
   1201     ASSERT(style);
   1202 
   1203     const Font& font = style->font();
   1204     SVGInlineTextBox* svgTextBox = static_cast<SVGInlineTextBox*>(textBox);
   1205 
   1206     unsigned length = textBox->len();
   1207 
   1208     const SVGRenderStyle* svgStyle = style->svgStyle();
   1209     bool isVerticalText = isVerticalWritingMode(svgStyle);
   1210 
   1211     int charsConsumed = 0;
   1212     for (unsigned i = 0; i < length; i += charsConsumed) {
   1213         SVGChar svgChar;
   1214 
   1215         if (info.inPathLayout())
   1216             svgChar.pathData = SVGCharOnPath::create();
   1217 
   1218         float glyphWidth = 0.0f;
   1219         float glyphHeight = 0.0f;
   1220 
   1221         int extraCharsAvailable = length - i - 1;
   1222 
   1223         String unicodeStr;
   1224         String glyphName;
   1225         if (textBox->direction() == RTL) {
   1226             glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName);
   1227             glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable);
   1228             unicodeStr = String(textBox->textRenderer()->text()->characters() + textBox->end() - i, charsConsumed);
   1229         } else {
   1230             glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed, glyphName);
   1231             glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->start() + i, extraCharsAvailable);
   1232             unicodeStr = String(textBox->textRenderer()->text()->characters() + textBox->start() + i, charsConsumed);
   1233         }
   1234 
   1235         bool assignedX = false;
   1236         bool assignedY = false;
   1237 
   1238         if (info.xValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && !isVerticalText))) {
   1239             if (!isVerticalText)
   1240                 svgChar.newTextChunk = true;
   1241 
   1242             assignedX = true;
   1243             svgChar.drawnSeperated = true;
   1244             info.curx = info.xValueNext();
   1245         }
   1246 
   1247         if (info.yValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && isVerticalText))) {
   1248             if (isVerticalText)
   1249                 svgChar.newTextChunk = true;
   1250 
   1251             assignedY = true;
   1252             svgChar.drawnSeperated = true;
   1253             info.cury = info.yValueNext();
   1254         }
   1255 
   1256         float dx = 0.0f;
   1257         float dy = 0.0f;
   1258 
   1259         // Apply x-axis shift
   1260         if (info.dxValueAvailable()) {
   1261             svgChar.drawnSeperated = true;
   1262 
   1263             dx = info.dxValueNext();
   1264             info.dx += dx;
   1265 
   1266             if (!info.inPathLayout())
   1267                 info.curx += dx;
   1268         }
   1269 
   1270         // Apply y-axis shift
   1271         if (info.dyValueAvailable()) {
   1272             svgChar.drawnSeperated = true;
   1273 
   1274             dy = info.dyValueNext();
   1275             info.dy += dy;
   1276 
   1277             if (!info.inPathLayout())
   1278                 info.cury += dy;
   1279         }
   1280 
   1281         // Take letter & word spacing and kerning into account
   1282         float spacing = font.letterSpacing() + calculateKerning(textBox->renderer()->node()->renderer());
   1283 
   1284         const UChar* currentCharacter = text->characters() + (textBox->direction() == RTL ? textBox->end() - i : textBox->start() + i);
   1285         const UChar* lastCharacter = 0;
   1286 
   1287         if (textBox->direction() == RTL) {
   1288             if (i < textBox->end())
   1289                 lastCharacter = text->characters() + textBox->end() - i +  1;
   1290         } else {
   1291             if (i > 0)
   1292                 lastCharacter = text->characters() + textBox->start() + i - 1;
   1293         }
   1294 
   1295         if (info.nextDrawnSeperated || spacing != 0.0f) {
   1296             info.nextDrawnSeperated = false;
   1297             svgChar.drawnSeperated = true;
   1298         }
   1299 
   1300         if (currentCharacter && Font::treatAsSpace(*currentCharacter) && lastCharacter && !Font::treatAsSpace(*lastCharacter)) {
   1301             spacing += font.wordSpacing();
   1302 
   1303             if (spacing != 0.0f && !info.inPathLayout())
   1304                 info.nextDrawnSeperated = true;
   1305         }
   1306 
   1307         float orientationAngle = glyphOrientationToAngle(svgStyle, isVerticalText, *currentCharacter);
   1308 
   1309         float xOrientationShift = 0.0f;
   1310         float yOrientationShift = 0.0f;
   1311         float glyphAdvance = calculateGlyphAdvanceAndShiftRespectingOrientation(isVerticalText, orientationAngle, glyphWidth, glyphHeight,
   1312                                                                                 font, svgChar, xOrientationShift, yOrientationShift);
   1313 
   1314         // Handle textPath layout mode
   1315         if (info.inPathLayout()) {
   1316             float extraAdvance = isVerticalText ? dy : dx;
   1317             float newOffset = FLT_MIN;
   1318 
   1319             if (assignedX && !isVerticalText)
   1320                 newOffset = info.curx;
   1321             else if (assignedY && isVerticalText)
   1322                 newOffset = info.cury;
   1323 
   1324             float correctedGlyphAdvance = glyphAdvance;
   1325 
   1326             // Handle lengthAdjust="spacingAndGlyphs" by specifying per-character scale operations
   1327             if (info.pathTextLength > 0.0f && info.pathChunkLength > 0.0f) {
   1328                 if (isVerticalText) {
   1329                     svgChar.pathData->yScale = info.pathChunkLength / info.pathTextLength;
   1330                     spacing *= svgChar.pathData->yScale;
   1331                     correctedGlyphAdvance *= svgChar.pathData->yScale;
   1332                 } else {
   1333                     svgChar.pathData->xScale = info.pathChunkLength / info.pathTextLength;
   1334                     spacing *= svgChar.pathData->xScale;
   1335                     correctedGlyphAdvance *= svgChar.pathData->xScale;
   1336                 }
   1337             }
   1338 
   1339             // Handle letter & word spacing on text path
   1340             float pathExtraAdvance = info.pathExtraAdvance;
   1341             info.pathExtraAdvance += spacing;
   1342 
   1343             svgChar.pathData->hidden = !info.nextPathLayoutPointAndAngle(correctedGlyphAdvance, extraAdvance, newOffset);
   1344             svgChar.drawnSeperated = true;
   1345 
   1346             info.pathExtraAdvance = pathExtraAdvance;
   1347         }
   1348 
   1349         // Apply rotation
   1350         if (info.angleValueAvailable())
   1351             info.angle = info.angleValueNext();
   1352 
   1353         // Apply baseline-shift
   1354         if (info.baselineShiftValueAvailable()) {
   1355             svgChar.drawnSeperated = true;
   1356             float shift = info.baselineShiftValueNext();
   1357 
   1358             if (isVerticalText)
   1359                 info.shiftx += shift;
   1360             else
   1361                 info.shifty -= shift;
   1362         }
   1363 
   1364         // Take dominant-baseline / alignment-baseline into account
   1365         yOrientationShift += alignmentBaselineToShift(isVerticalText, text, font);
   1366 
   1367         svgChar.x = info.curx;
   1368         svgChar.y = info.cury;
   1369         svgChar.angle = info.angle;
   1370 
   1371         // For text paths any shift (dx/dy/baseline-shift) has to be applied after the rotation
   1372         if (!info.inPathLayout()) {
   1373             svgChar.x += info.shiftx + xOrientationShift;
   1374             svgChar.y += info.shifty + yOrientationShift;
   1375 
   1376             if (orientationAngle != 0.0f)
   1377                 svgChar.angle += orientationAngle;
   1378 
   1379             if (svgChar.angle != 0.0f)
   1380                 svgChar.drawnSeperated = true;
   1381         } else {
   1382             svgChar.pathData->orientationAngle = orientationAngle;
   1383 
   1384             if (isVerticalText)
   1385                 svgChar.angle -= 90.0f;
   1386 
   1387             svgChar.pathData->xShift = info.shiftx + xOrientationShift;
   1388             svgChar.pathData->yShift = info.shifty + yOrientationShift;
   1389 
   1390             // Translate to glyph midpoint
   1391             if (isVerticalText) {
   1392                 svgChar.pathData->xShift += info.dx;
   1393                 svgChar.pathData->yShift -= glyphAdvance / 2.0f;
   1394             } else {
   1395                 svgChar.pathData->xShift -= glyphAdvance / 2.0f;
   1396                 svgChar.pathData->yShift += info.dy;
   1397             }
   1398         }
   1399 
   1400         double kerning = 0.0;
   1401 #if ENABLE(SVG_FONTS)
   1402         SVGFontElement* svgFont = 0;
   1403         if (style->font().isSVGFont())
   1404             svgFont = style->font().svgFont();
   1405 
   1406         if (lastGlyph.isValid && style->font().isSVGFont()) {
   1407             SVGHorizontalKerningPair kerningPair;
   1408             if (svgFont->getHorizontalKerningPairForStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeStr, glyphName, kerningPair))
   1409                 kerning = kerningPair.kerning;
   1410         }
   1411 
   1412         if (style->font().isSVGFont()) {
   1413             lastGlyph.unicode = unicodeStr;
   1414             lastGlyph.glyphName = glyphName;
   1415             lastGlyph.isValid = true;
   1416         } else
   1417             lastGlyph.isValid = false;
   1418 #endif
   1419 
   1420         svgChar.x -= (float)kerning;
   1421 
   1422         // Advance to new position
   1423         if (isVerticalText) {
   1424             svgChar.drawnSeperated = true;
   1425             info.cury += glyphAdvance + spacing;
   1426         } else
   1427             info.curx += glyphAdvance + spacing - (float)kerning;
   1428 
   1429         // Advance to next character group
   1430         for (int k = 0; k < charsConsumed; ++k) {
   1431             info.svgChars.append(svgChar);
   1432             info.processedSingleCharacter();
   1433             svgChar.drawnSeperated = false;
   1434             svgChar.newTextChunk = false;
   1435         }
   1436     }
   1437 }
   1438 
   1439 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, Vector<SVGTextChunk>& svgTextChunks, InlineFlowBox* start)
   1440 {
   1441     SVGTextChunkLayoutInfo info(svgTextChunks);
   1442     info.it = svgChars.begin();
   1443     info.chunk.start = svgChars.begin();
   1444     info.chunk.end = svgChars.begin();
   1445 
   1446     buildTextChunks(svgChars, start, info);
   1447     ASSERT(info.it == svgChars.end());
   1448 }
   1449 
   1450 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* start, SVGTextChunkLayoutInfo& info)
   1451 {
   1452 #if DEBUG_CHUNK_BUILDING > 1
   1453     fprintf(stderr, " -> buildTextChunks(start=%p)\n", start);
   1454 #endif
   1455 
   1456     for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
   1457         if (curr->renderer()->isText()) {
   1458             InlineTextBox* textBox = static_cast<InlineTextBox*>(curr);
   1459 
   1460             unsigned length = textBox->len();
   1461             if (!length)
   1462                 continue;
   1463 
   1464 #if DEBUG_CHUNK_BUILDING > 1
   1465             fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n",
   1466                             textBox, length, textBox->start(), textBox->end(), (int) info.handlingTextPath);
   1467 #endif
   1468 
   1469             RenderText* text = textBox->textRenderer();
   1470             ASSERT(text);
   1471             ASSERT(text->node());
   1472 
   1473             SVGTextContentElement* textContent = 0;
   1474             Node* node = text->node()->parent();
   1475             while (node && node->isSVGElement() && !textContent) {
   1476                 if (static_cast<SVGElement*>(node)->isTextContent())
   1477                     textContent = static_cast<SVGTextContentElement*>(node);
   1478                 else
   1479                     node = node->parentNode();
   1480             }
   1481             ASSERT(textContent);
   1482 
   1483             // Start new character range for the first chunk
   1484             bool isFirstCharacter = info.svgTextChunks.isEmpty() && info.chunk.start == info.it && info.chunk.start == info.chunk.end;
   1485             if (isFirstCharacter) {
   1486                 ASSERT(info.chunk.boxes.isEmpty());
   1487                 info.chunk.boxes.append(SVGInlineBoxCharacterRange());
   1488             } else
   1489                 ASSERT(!info.chunk.boxes.isEmpty());
   1490 
   1491             // Walk string to find out new chunk positions, if existent
   1492             for (unsigned i = 0; i < length; ++i) {
   1493                 ASSERT(info.it != svgChars.end());
   1494 
   1495                 SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
   1496                 if (range.isOpen()) {
   1497                     range.box = curr;
   1498                     range.startOffset = (i == 0 ? 0 : i - 1);
   1499 
   1500 #if DEBUG_CHUNK_BUILDING > 1
   1501                     fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset);
   1502 #endif
   1503                 }
   1504 
   1505                 // If a new (or the first) chunk has been started, record it's text-anchor and writing mode.
   1506                 if (info.assignChunkProperties) {
   1507                     info.assignChunkProperties = false;
   1508 
   1509                     info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
   1510                     info.chunk.isTextPath = info.handlingTextPath;
   1511                     info.chunk.anchor = text->style()->svgStyle()->textAnchor();
   1512                     info.chunk.textLength = textContent->textLength().value(textContent);
   1513                     info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
   1514 
   1515 #if DEBUG_CHUNK_BUILDING > 1
   1516                     fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", info.chunk.isVerticalText, info.chunk.anchor);
   1517 #endif
   1518                 }
   1519 
   1520                 if (i > 0 && !isFirstCharacter && (*info.it).newTextChunk) {
   1521                     // Close mid chunk & character range
   1522                     ASSERT(!range.isOpen());
   1523                     ASSERT(!range.isClosed());
   1524 
   1525                     range.endOffset = i;
   1526                     closeTextChunk(info);
   1527 
   1528 #if DEBUG_CHUNK_BUILDING > 1
   1529                     fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset);
   1530 #endif
   1531 
   1532                     // Prepare for next chunk, if we're not at the end
   1533                     startTextChunk(info);
   1534                     if (i + 1 == length) {
   1535 #if DEBUG_CHUNK_BUILDING > 1
   1536                         fprintf(stderr, " | -> Record last chunk of inline text box!\n");
   1537 #endif
   1538 
   1539                         startTextChunk(info);
   1540                         SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
   1541 
   1542                         info.assignChunkProperties = false;
   1543                         info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
   1544                         info.chunk.isTextPath = info.handlingTextPath;
   1545                         info.chunk.anchor = text->style()->svgStyle()->textAnchor();
   1546                         info.chunk.textLength = textContent->textLength().value(textContent);
   1547                         info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
   1548 
   1549                         range.box = curr;
   1550                         range.startOffset = i;
   1551 
   1552                         ASSERT(!range.isOpen());
   1553                         ASSERT(!range.isClosed());
   1554                     }
   1555                 }
   1556 
   1557                 // This should only hold true for the first character of the first chunk
   1558                 if (isFirstCharacter)
   1559                     isFirstCharacter = false;
   1560 
   1561                 ++info.it;
   1562             }
   1563 
   1564 #if DEBUG_CHUNK_BUILDING > 1
   1565             fprintf(stderr, " -> Finished inline text box!\n");
   1566 #endif
   1567 
   1568             SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
   1569             if (!range.isOpen() && !range.isClosed()) {
   1570 #if DEBUG_CHUNK_BUILDING > 1
   1571                 fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length);
   1572 #endif
   1573 
   1574                 // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk.
   1575                 range.endOffset = length;
   1576 
   1577                 if (info.it != svgChars.end()) {
   1578 #if DEBUG_CHUNK_BUILDING > 1
   1579                     fprintf(stderr, " -> Not at last character yet!\n");
   1580 #endif
   1581 
   1582                     // If we're not at the end of the last box to be processed, and if the next
   1583                     // character starts a new chunk, then close the current chunk and start a new one.
   1584                     if ((*info.it).newTextChunk) {
   1585 #if DEBUG_CHUNK_BUILDING > 1
   1586                         fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n");
   1587 #endif
   1588 
   1589                         closeTextChunk(info);
   1590                         startTextChunk(info);
   1591                     } else {
   1592                         // Just start a new character range
   1593                         info.chunk.boxes.append(SVGInlineBoxCharacterRange());
   1594 
   1595 #if DEBUG_CHUNK_BUILDING > 1
   1596                         fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n");
   1597 #endif
   1598                     }
   1599                 } else {
   1600 #if DEBUG_CHUNK_BUILDING > 1
   1601                     fprintf(stderr, " -> Closing final chunk! Finished processing!\n");
   1602 #endif
   1603 
   1604                     // Close final chunk, once we're at the end of the last box
   1605                     closeTextChunk(info);
   1606                 }
   1607             }
   1608         } else {
   1609             ASSERT(curr->isInlineFlowBox());
   1610             InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
   1611 
   1612             if (!flowBox->renderer()->node())
   1613                 continue; // Skip generated content.
   1614 
   1615             bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag);
   1616 
   1617 #if DEBUG_CHUNK_BUILDING > 1
   1618             fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath);
   1619 #endif
   1620 
   1621             if (isTextPath)
   1622                 info.handlingTextPath = true;
   1623 
   1624             buildTextChunks(svgChars, flowBox, info);
   1625 
   1626             if (isTextPath)
   1627                 info.handlingTextPath = false;
   1628         }
   1629     }
   1630 
   1631 #if DEBUG_CHUNK_BUILDING > 1
   1632     fprintf(stderr, " <- buildTextChunks(start=%p)\n", start);
   1633 #endif
   1634 }
   1635 
   1636 const Vector<SVGTextChunk>& SVGRootInlineBox::svgTextChunks() const
   1637 {
   1638     return m_svgTextChunks;
   1639 }
   1640 
   1641 void SVGRootInlineBox::layoutTextChunks()
   1642 {
   1643     Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
   1644     Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end();
   1645 
   1646     for (; it != end; ++it) {
   1647         SVGTextChunk& chunk = *it;
   1648 
   1649 #if DEBUG_CHUNK_BUILDING > 0
   1650         {
   1651             fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n",
   1652                     (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText,
   1653                     (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int) (chunk.end - chunk.start));
   1654 
   1655             Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
   1656             Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
   1657 
   1658             unsigned int i = 0;
   1659             for (; boxIt != boxEnd; ++boxIt) {
   1660                 SVGInlineBoxCharacterRange& range = *boxIt; i++;
   1661                 fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box);
   1662             }
   1663         }
   1664 #endif
   1665 
   1666         if (chunk.isTextPath)
   1667             continue;
   1668 
   1669         // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts.
   1670         applyTextLengthCorrectionToTextChunk(chunk);
   1671 
   1672         // text-anchor is already handled for textPath layouts.
   1673         applyTextAnchorToTextChunk(chunk);
   1674     }
   1675 }
   1676 
   1677 static inline void addPaintServerToTextDecorationInfo(ETextDecoration decoration, SVGTextDecorationInfo& info, RenderObject* object)
   1678 {
   1679     if (object->style()->svgStyle()->hasFill())
   1680         info.fillServerMap.set(decoration, object);
   1681 
   1682     if (object->style()->svgStyle()->hasStroke())
   1683         info.strokeServerMap.set(decoration, object);
   1684 }
   1685 
   1686 SVGTextDecorationInfo SVGRootInlineBox::retrievePaintServersForTextDecoration(RenderObject* start)
   1687 {
   1688     ASSERT(start);
   1689 
   1690     Vector<RenderObject*> parentChain;
   1691     while ((start = start->parent())) {
   1692         parentChain.prepend(start);
   1693 
   1694         // Stop at our direct <text> parent.
   1695         if (start->isSVGText())
   1696             break;
   1697     }
   1698 
   1699     Vector<RenderObject*>::iterator it = parentChain.begin();
   1700     Vector<RenderObject*>::iterator end = parentChain.end();
   1701 
   1702     SVGTextDecorationInfo info;
   1703 
   1704     for (; it != end; ++it) {
   1705         RenderObject* object = *it;
   1706         ASSERT(object);
   1707 
   1708         RenderStyle* style = object->style();
   1709         ASSERT(style);
   1710 
   1711         int decorations = style->textDecoration();
   1712         if (decorations != NONE) {
   1713             if (decorations & OVERLINE)
   1714                 addPaintServerToTextDecorationInfo(OVERLINE, info, object);
   1715 
   1716             if (decorations & UNDERLINE)
   1717                 addPaintServerToTextDecorationInfo(UNDERLINE, info, object);
   1718 
   1719             if (decorations & LINE_THROUGH)
   1720                 addPaintServerToTextDecorationInfo(LINE_THROUGH, info, object);
   1721         }
   1722     }
   1723 
   1724     return info;
   1725 }
   1726 
   1727 void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase* walker, const SVGInlineTextBox* textBox)
   1728 {
   1729     ASSERT(walker);
   1730 
   1731     Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
   1732     Vector<SVGTextChunk>::iterator itEnd = m_svgTextChunks.end();
   1733 
   1734     for (; it != itEnd; ++it) {
   1735         SVGTextChunk& curChunk = *it;
   1736 
   1737         Vector<SVGInlineBoxCharacterRange>::iterator boxIt = curChunk.boxes.begin();
   1738         Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = curChunk.boxes.end();
   1739 
   1740         InlineBox* lastNotifiedBox = 0;
   1741         InlineBox* prevBox = 0;
   1742 
   1743         unsigned int chunkOffset = 0;
   1744         bool startedFirstChunk = false;
   1745 
   1746         for (; boxIt != boxEnd; ++boxIt) {
   1747             SVGInlineBoxCharacterRange& range = *boxIt;
   1748 
   1749             ASSERT(range.box->isInlineTextBox());
   1750             SVGInlineTextBox* rangeTextBox = static_cast<SVGInlineTextBox*>(range.box);
   1751 
   1752             if (textBox && rangeTextBox != textBox) {
   1753                 chunkOffset += range.endOffset - range.startOffset;
   1754                 continue;
   1755             }
   1756 
   1757             // Eventually notify that we started a new chunk
   1758             if (!textBox && !startedFirstChunk) {
   1759                 startedFirstChunk = true;
   1760 
   1761                 lastNotifiedBox = range.box;
   1762                 walker->start(range.box);
   1763             } else {
   1764                 // Eventually apply new style, as this chunk spans multiple boxes (with possible different styling)
   1765                 if (prevBox && prevBox != range.box) {
   1766                     lastNotifiedBox = range.box;
   1767 
   1768                     walker->end(prevBox);
   1769                     walker->start(lastNotifiedBox);
   1770                 }
   1771             }
   1772 
   1773             unsigned int length = range.endOffset - range.startOffset;
   1774 
   1775             Vector<SVGChar>::iterator itCharBegin = curChunk.start + chunkOffset;
   1776             Vector<SVGChar>::iterator itCharEnd = curChunk.start + chunkOffset + length;
   1777             ASSERT(itCharEnd <= curChunk.end);
   1778 
   1779             // Process this chunk portion
   1780             if (textBox)
   1781                 (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
   1782             else {
   1783                 if (walker->setupBackground(range.box))
   1784                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
   1785 
   1786                 if (walker->setupFill(range.box))
   1787                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
   1788 
   1789                 if (walker->setupFillSelection(range.box))
   1790                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
   1791 
   1792                 if (walker->setupStroke(range.box))
   1793                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
   1794 
   1795                 if (walker->setupStrokeSelection(range.box))
   1796                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
   1797 
   1798                 if (walker->setupForeground(range.box))
   1799                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
   1800 
   1801             }
   1802 
   1803             chunkOffset += length;
   1804 
   1805             if (!textBox)
   1806                 prevBox = range.box;
   1807         }
   1808 
   1809         if (!textBox && startedFirstChunk)
   1810             walker->end(lastNotifiedBox);
   1811     }
   1812 }
   1813 
   1814 } // namespace WebCore
   1815 
   1816 #endif // ENABLE(SVG)
   1817