Warning, file /frameworks/khtml/src/rendering/SVGRootInlineBox.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * This file is part of the WebKit project.
0003  *
0004  * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
0005  *           (C) 2006 Apple Computer Inc.
0006  *           (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
0007  *
0008  * This library is free software; you can redistribute it and/or
0009  * modify it under the terms of the GNU Library General Public
0010  * License as published by the Free Software Foundation; either
0011  * version 2 of the License, or (at your option) any later version.
0012  *
0013  * This library is distributed in the hope that it will be useful,
0014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016  * Library General Public License for more details.
0017  *
0018  * You should have received a copy of the GNU Library General Public License
0019  * along with this library; see the file COPYING.LIB.  If not, write to
0020  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021  * Boston, MA 02110-1301, USA.
0022  *
0023  */
0024 
0025 #include "wtf/Platform.h"
0026 
0027 #if ENABLE(SVG)
0028 #include "SVGRootInlineBox.h"
0029 
0030 /*#include "Editor.h"
0031 #include "Frame.h"
0032 #include "GraphicsContext.h"*/
0033 #include "RenderSVGRoot.h"
0034 #include "SVGInlineFlowBox.h"
0035 #include "SVGInlineTextBox.h"
0036 #include "SVGFontElement.h"
0037 #include "SVGPaintServer.h"
0038 #include "SVGRenderStyleDefs.h"
0039 #include "SVGRenderSupport.h"
0040 #include "SVGResourceFilter.h"
0041 #include "SVGTextPositioningElement.h"
0042 #include "SVGURIReference.h"
0043 //#include "Text.h"
0044 //#include "UnicodeRange.h"
0045 
0046 #include <float.h>
0047 #include <math.h>
0048 
0049 // Text chunk creation is complex and the whole process
0050 // can easily be traced by setting this variable > 0.
0051 #define DEBUG_CHUNK_BUILDING 0
0052 
0053 namespace WebCore
0054 {
0055 
0056 static inline bool isVerticalWritingMode(const SVGRenderStyle *style)
0057 {
0058     return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB;
0059 }
0060 
0061 static inline EAlignmentBaseline dominantBaselineToShift(bool isVerticalText, const RenderObject *text, const Font &font)
0062 {
0063     ASSERT(text);
0064 
0065     const SVGRenderStyle *style = text->style() ? text->style()->svgStyle() : nullptr;
0066     ASSERT(style);
0067 
0068     const SVGRenderStyle *parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : nullptr;
0069 
0070     EDominantBaseline baseline = style->dominantBaseline();
0071     if (baseline == DB_AUTO) {
0072         if (isVerticalText) {
0073             baseline = DB_CENTRAL;
0074         } else {
0075             baseline = DB_ALPHABETIC;
0076         }
0077     }
0078 
0079     switch (baseline) {
0080     case DB_USE_SCRIPT:
0081         // TODO: The dominant-baseline and the baseline-table components are set by
0082         //       determining the predominant script of the character data content.
0083         return AB_ALPHABETIC;
0084     case DB_NO_CHANGE: {
0085         if (parentStyle) {
0086             return dominantBaselineToShift(isVerticalText, text->parent(), font);
0087         }
0088 
0089         ASSERT_NOT_REACHED();
0090         return AB_AUTO;
0091     }
0092     case DB_RESET_SIZE: {
0093         if (parentStyle) {
0094             return dominantBaselineToShift(isVerticalText, text->parent(), font);
0095         }
0096 
0097         ASSERT_NOT_REACHED();
0098         return AB_AUTO;
0099     }
0100     case DB_IDEOGRAPHIC:
0101         return AB_IDEOGRAPHIC;
0102     case DB_ALPHABETIC:
0103         return AB_ALPHABETIC;
0104     case DB_HANGING:
0105         return AB_HANGING;
0106     case DB_MATHEMATICAL:
0107         return AB_MATHEMATICAL;
0108     case DB_CENTRAL:
0109         return AB_CENTRAL;
0110     case DB_MIDDLE:
0111         return AB_MIDDLE;
0112     case DB_TEXT_AFTER_EDGE:
0113         return AB_TEXT_AFTER_EDGE;
0114     case DB_TEXT_BEFORE_EDGE:
0115         return AB_TEXT_BEFORE_EDGE;
0116     default:
0117         ASSERT_NOT_REACHED();
0118         return AB_AUTO;
0119     }
0120 }
0121 
0122 static inline float alignmentBaselineToShift(bool isVerticalText, const RenderObject *text, const Font &font)
0123 {
0124     ASSERT(text);
0125 
0126     const SVGRenderStyle *style = text->style() ? text->style()->svgStyle() : nullptr;
0127     ASSERT(style);
0128 
0129     const SVGRenderStyle *parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : nullptr;
0130 
0131     EAlignmentBaseline baseline = style->alignmentBaseline();
0132     if (baseline == AB_AUTO) {
0133         if (parentStyle && style->dominantBaseline() == DB_AUTO) {
0134             baseline = dominantBaselineToShift(isVerticalText, text->parent(), font);
0135         } else {
0136             baseline = dominantBaselineToShift(isVerticalText, text, font);
0137         }
0138 
0139         ASSERT(baseline != AB_AUTO);
0140     }
0141 
0142     // Note: https://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
0143     switch (baseline) {
0144     case AB_BASELINE: {
0145         if (parentStyle) {
0146             return dominantBaselineToShift(isVerticalText, text->parent(), font);
0147         }
0148 
0149         return 0.0f;
0150     }
0151     case AB_BEFORE_EDGE:
0152     case AB_TEXT_BEFORE_EDGE:
0153         return font.ascent();
0154     case AB_MIDDLE:
0155         return font.xHeight() / 2.0f;
0156     case AB_CENTRAL:
0157         // Not needed, we're taking this into account already for vertical text!
0158         // return (font.ascent() - font.descent()) / 2.0f;
0159         return 0.0f;
0160     case AB_AFTER_EDGE:
0161     case AB_TEXT_AFTER_EDGE:
0162     case AB_IDEOGRAPHIC:
0163         return font.descent();
0164     case AB_ALPHABETIC:
0165         return 0.0f;
0166     case AB_HANGING:
0167         return font.ascent() * 8.0f / 10.0f;
0168     case AB_MATHEMATICAL:
0169         return font.ascent() / 2.0f;
0170     default:
0171         ASSERT_NOT_REACHED();
0172         return 0.0f;
0173     }
0174 }
0175 
0176 static inline float glyphOrientationToAngle(const SVGRenderStyle *svgStyle, bool isVerticalText, const UChar &character)
0177 {
0178     Q_UNUSED(character);
0179     switch (isVerticalText ? svgStyle->glyphOrientationVertical() : svgStyle->glyphOrientationHorizontal()) {
0180     case GO_AUTO: {
0181         // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees.
0182         //       Text which is not fullwidth will be set with a glyph-orientation of 90-degrees.
0183         /*FIXME: khtml porting; unsigned int unicodeRange = findCharUnicodeRange(character);
0184         if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic)
0185             return 90.0f;*/
0186 
0187         return 0.0f;
0188     }
0189     case GO_90DEG:
0190         return 90.0f;
0191     case GO_180DEG:
0192         return 180.0f;
0193     case GO_270DEG:
0194         return 270.0f;
0195     case GO_0DEG:
0196     default:
0197         return 0.0f;
0198     }
0199 }
0200 
0201 static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)
0202 {
0203     return fabsf(fmodf(orientationAngle, 180.0f)) == 0.0f;
0204 }
0205 
0206 static inline float calculateGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float orientationAngle, float glyphWidth, float glyphHeight, const Font &font, SVGChar &svgChar, float &xOrientationShift, float &yOrientationShift)
0207 {
0208     bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(orientationAngle);
0209 
0210     // The function is based on spec requirements:
0211     //
0212     // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of
0213     // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph.
0214     //
0215     // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of
0216     // 180 degrees,then the current text position is incremented according to the horizontal metrics of the glyph.
0217 
0218     // vertical orientation handling
0219     if (isVerticalText) {
0220         if (orientationAngle == 0.0f) {
0221             xOrientationShift = -glyphWidth / 2.0f;
0222             yOrientationShift = font.ascent();
0223         } else if (orientationAngle == 90.0f) {
0224             xOrientationShift = -glyphHeight;
0225             yOrientationShift = font.descent();
0226             svgChar.orientationShiftY = -font.ascent();
0227         } else if (orientationAngle == 270.0f) {
0228             xOrientationShift = glyphHeight;
0229             yOrientationShift = font.descent();
0230             svgChar.orientationShiftX = -glyphWidth;
0231             svgChar.orientationShiftY = -font.ascent();
0232         } else if (orientationAngle == 180.0f) {
0233             yOrientationShift = font.ascent();
0234             svgChar.orientationShiftX = -glyphWidth / 2.0f;
0235             svgChar.orientationShiftY = font.ascent() - font.descent();
0236         }
0237 
0238         // vertical advance calculation
0239         if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees) {
0240             return glyphWidth;
0241         }
0242 
0243         return glyphHeight;
0244     }
0245 
0246     // horizontal orientation handling
0247     if (orientationAngle == 90.0f) {
0248         xOrientationShift = glyphWidth / 2.0f;
0249         yOrientationShift = -font.descent();
0250         svgChar.orientationShiftX = -glyphWidth / 2.0f - font.descent();
0251         svgChar.orientationShiftY = font.descent();
0252     } else if (orientationAngle == 270.0f) {
0253         xOrientationShift = -glyphWidth / 2.0f;
0254         yOrientationShift = -font.descent();
0255         svgChar.orientationShiftX = -glyphWidth / 2.0f + font.descent();
0256         svgChar.orientationShiftY = glyphHeight;
0257     } else if (orientationAngle == 180.0f) {
0258         xOrientationShift = glyphWidth / 2.0f;
0259         svgChar.orientationShiftX = -glyphWidth / 2.0f;
0260         svgChar.orientationShiftY = font.ascent() - font.descent();
0261     }
0262 
0263     // horizontal advance calculation
0264     if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees) {
0265         return glyphHeight;
0266     }
0267 
0268     return glyphWidth;
0269 }
0270 
0271 static inline void startTextChunk(SVGTextChunkLayoutInfo &info)
0272 {
0273     info.chunk.boxes.clear();
0274     info.chunk.boxes.append(SVGInlineBoxCharacterRange());
0275 
0276     info.chunk.start = info.it;
0277     info.assignChunkProperties = true;
0278 }
0279 
0280 static inline void closeTextChunk(SVGTextChunkLayoutInfo &info)
0281 {
0282     ASSERT(!info.chunk.boxes.last().isOpen());
0283     ASSERT(info.chunk.boxes.last().isClosed());
0284 
0285     info.chunk.end = info.it;
0286     ASSERT(info.chunk.end >= info.chunk.start);
0287 
0288     info.svgTextChunks.append(info.chunk);
0289 }
0290 
0291 RenderSVGRoot *findSVGRootObject(RenderObject *start)
0292 {
0293     // Find associated root inline box
0294     while (start && !start->isSVGRoot()) {
0295         start = start->parent();
0296     }
0297 
0298     ASSERT(start);
0299     ASSERT(start->isSVGRoot());
0300 
0301     return static_cast<RenderSVGRoot *>(start);
0302 }
0303 
0304 static inline FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar> &chars)
0305 {
0306     return topLeftPositionOfCharacterRange(chars.begin(), chars.end());
0307 }
0308 
0309 FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator it, Vector<SVGChar>::iterator end)
0310 {
0311     float lowX = FLT_MAX, lowY = FLT_MAX;
0312     for (; it != end; ++it) {
0313         if (it->isHidden()) {
0314             continue;
0315         }
0316 
0317         float x = (*it).x;
0318         float y = (*it).y;
0319 
0320         if (x < lowX) {
0321             lowX = x;
0322         }
0323 
0324         if (y < lowY) {
0325             lowY = y;
0326         }
0327     }
0328 
0329     return FloatPoint(lowX, lowY);
0330 }
0331 
0332 /* FIXME
0333 // Helper function
0334 static float calculateKerning(RenderObject* item)
0335 {
0336     Q_UNUSED(item);
0337     const Font& font = item->style()->font();
0338     const SVGRenderStyle* svgStyle = item->style()->svgStyle();
0339 
0340     float kerning = 0.0f;
0341     if (CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->kerning())) {
0342         kerning = primitive->getFloatValue();
0343 
0344         if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE && font.pixelSize() > 0)
0345             kerning = kerning / 100.0f * font.pixelSize();
0346     }
0347 
0348     return kerning;
0349 }
0350 */
0351 
0352 // Helper class for paint()
0353 struct SVGRootInlineBoxPaintWalker {
0354     SVGRootInlineBoxPaintWalker(SVGRootInlineBox *rootBox, SVGResourceFilter *rootFilter, RenderObject::PaintInfo paintInfo, int tx, int ty)
0355         : m_rootBox(rootBox)
0356         , m_chunkStarted(false)
0357         , m_paintInfo(paintInfo)
0358         , m_savedInfo(paintInfo)
0359         , m_boundingBox(tx + rootBox->xPos(), ty + rootBox->yPos(), rootBox->width(), rootBox->height())
0360         , m_filter(nullptr)
0361         , m_rootFilter(rootFilter)
0362         , m_fillPaintServer(nullptr)
0363         , m_strokePaintServer(nullptr)
0364         , m_fillPaintServerObject(nullptr)
0365         , m_strokePaintServerObject(nullptr)
0366         , m_tx(tx)
0367         , m_ty(ty)
0368     {
0369     }
0370 
0371     ~SVGRootInlineBoxPaintWalker()
0372     {
0373         ASSERT(!m_filter);
0374         ASSERT(!m_fillPaintServer);
0375         ASSERT(!m_fillPaintServerObject);
0376         ASSERT(!m_strokePaintServer);
0377         ASSERT(!m_strokePaintServerObject);
0378         ASSERT(!m_chunkStarted);
0379     }
0380 
0381     void teardownFillPaintServer()
0382     {
0383         if (!m_fillPaintServer) {
0384             return;
0385         }
0386 
0387         m_fillPaintServer->teardown(m_paintInfo.p, nullptr, m_fillPaintServerObject, ApplyToFillTargetType, true);
0388 
0389         m_fillPaintServer = nullptr;
0390         m_fillPaintServerObject = nullptr;
0391     }
0392 
0393     void teardownStrokePaintServer()
0394     {
0395         if (!m_strokePaintServer) {
0396             return;
0397         }
0398 
0399         m_strokePaintServer->teardown(m_paintInfo.p, nullptr, m_strokePaintServerObject, ApplyToStrokeTargetType, true);
0400 
0401         m_strokePaintServer = nullptr;
0402         m_strokePaintServerObject = nullptr;
0403     }
0404 
0405     void chunkStartCallback(InlineBox *box)
0406     {
0407         ASSERT(!m_chunkStarted);
0408         m_chunkStarted = true;
0409 
0410         InlineFlowBox *flowBox = box->parent();
0411 
0412         // Initialize text rendering
0413         RenderObject *object = flowBox->object();
0414         ASSERT(object);
0415 
0416         m_savedInfo = m_paintInfo;
0417         //m_paintInfo.context->save();
0418 
0419         if (!flowBox->isRootInlineBox()) {
0420             ;//FIXME m_paintInfo.context->concatCTM(m_rootBox->object()->localTransform());
0421         }
0422 
0423         //m_paintInfo.context->concatCTM(object->localTransform());
0424         m_paintInfo.p->setWorldMatrix(object->localTransform(), true);
0425 
0426         if (!flowBox->isRootInlineBox()) {
0427             prepareToRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_rootFilter);
0428             // FIXME khtml m_paintInfo.rect = object->localTransform().inverse().mapRect(m_paintInfo.rect);
0429         }
0430     }
0431 
0432     void chunkEndCallback(InlineBox *box)
0433     {
0434         ASSERT(m_chunkStarted);
0435         m_chunkStarted = false;
0436 
0437         InlineFlowBox *flowBox = box->parent();
0438 
0439         RenderObject *object = flowBox->object();
0440         ASSERT(object);
0441 
0442         // Clean up last used paint server
0443         teardownFillPaintServer();
0444         teardownStrokePaintServer();
0445 
0446         // Finalize text rendering
0447         if (!flowBox->isRootInlineBox()) {
0448             //FIXME khtml finishRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_savedInfo.context);
0449             m_filter = nullptr;
0450         }
0451 
0452         // Restore context & repaint rect
0453         //FIXME m_paintInfo.context->restore();
0454         //FIXME m_paintInfo.rect = m_savedInfo.rect;
0455     }
0456 
0457     bool chunkSetupFillCallback(InlineBox *box)
0458     {
0459         InlineFlowBox *flowBox = box->parent();
0460 
0461         // Setup fill paint server
0462         RenderObject *object = flowBox->object();
0463         ASSERT(object);
0464 
0465         ASSERT(!m_strokePaintServer);
0466         teardownFillPaintServer();
0467 
0468         m_fillPaintServer = SVGPaintServer::fillPaintServer(object->style(), object);
0469         if (m_fillPaintServer) {
0470             m_fillPaintServer->setup(m_paintInfo.p, nullptr, object, ApplyToFillTargetType, true);
0471             m_fillPaintServerObject = object;
0472             return true;
0473         }
0474 
0475         return false;
0476     }
0477 
0478     bool chunkSetupStrokeCallback(InlineBox *box)
0479     {
0480         InlineFlowBox *flowBox = box->parent();
0481 
0482         // Setup stroke paint server
0483         RenderObject *object = flowBox->object();
0484         ASSERT(object);
0485 
0486         // If we're both stroked & filled, teardown fill paint server before stroking.
0487         teardownFillPaintServer();
0488         teardownStrokePaintServer();
0489 
0490         m_strokePaintServer = SVGPaintServer::strokePaintServer(object->style(), object);
0491 
0492         if (m_strokePaintServer) {
0493             m_strokePaintServer->setup(m_paintInfo.p, nullptr, object, ApplyToStrokeTargetType, true);
0494             m_strokePaintServerObject = object;
0495             return true;
0496         }
0497 
0498         return false;
0499     }
0500 
0501     void chunkPortionCallback(SVGInlineTextBox *textBox, int startOffset, const AffineTransform &chunkCtm,
0502                               const Vector<SVGChar>::iterator &start, const Vector<SVGChar>::iterator &end)
0503     {
0504         Q_UNUSED(chunkCtm);
0505         //qCDebug(KHTML_LOG) << "text chunk rendering code here";
0506         RenderText *text = textBox->/*textObject()*/renderText();
0507         ASSERT(text);
0508 
0509         RenderStyle *styleToUse = text->style(/*textBox->isFirstLineStyle()*/);
0510         ASSERT(styleToUse);
0511 
0512         startOffset += textBox->start();
0513 
0514         int textDecorations = styleToUse->textDecorationsInEffect();
0515 
0516         // khtml int textWidth = 0;
0517         IntPoint decorationOrigin;
0518         SVGTextDecorationInfo info;
0519 
0520         /*FIXME khtml if (!chunkCtm.isIdentity())
0521             m_paintInfo.context->concatCTM(chunkCtm);*/
0522 
0523         for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
0524             if (it->isHidden()) {
0525                 continue;
0526             }
0527 
0528             // Determine how many characters - starting from the current - can be drawn at once.
0529             Vector<SVGChar>::iterator itSearch = it + 1;
0530             while (itSearch != end) {
0531                 if (itSearch->drawnSeperated || itSearch->isHidden()) {
0532                     break;
0533                 }
0534 
0535                 itSearch++;
0536             }
0537 
0538             /*FIXME khtmlconst*/ UChar *stringStart = text->text() + startOffset + (it - start);
0539             unsigned int stringLength = itSearch - it;
0540 
0541             // Paint decorations, that have to be drawn before the text gets drawn
0542             if (textDecorations != TDNONE /*FIXME khtml && m_paintInfo.phase != PaintPhaseSelection*/) {
0543                 //khtml textWidth = styleToUse->font().width(svgTextRunForInlineTextBox(stringStart, stringLength, styleToUse, textBox, (*it).x));
0544                 //FIXME khtml textWidth = styleToUse->htmlFont().width(stringStart, stringLength, 0, stringLength, false /*fast algo*/);
0545                 decorationOrigin = IntPoint((int)(*it).x, (int)(*it).y - styleToUse->htmlFont().ascent());
0546                 info = m_rootBox->retrievePaintServersForTextDecoration(text);
0547             }
0548 
0549             /*if (textDecorations & UNDERLINE && textWidth != 0.0f)
0550                 textBox->paintDecoration(UNDERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
0551 
0552             if (textDecorations & OVERLINE && textWidth != 0.0f)
0553                 textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);*/
0554 
0555             // Paint text
0556             SVGPaintServer *activePaintServer = m_fillPaintServer;
0557             if (!activePaintServer) {
0558                 activePaintServer = m_strokePaintServer;
0559             }
0560 
0561             ASSERT(activePaintServer);
0562             textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, activePaintServer);
0563 
0564             // Paint decorations, that have to be drawn afterwards
0565             /* FIXME khtml if (textDecorations & LINE_THROUGH && textWidth != 0.0f)
0566                 textBox->paintDecoration(LINE_THROUGH, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);*/
0567 
0568             // Skip processed characters
0569             it = itSearch - 1;
0570         }
0571 
0572         /* FIXME khtml if (!chunkCtm.isIdentity())
0573             m_paintInfo.context->concatCTM(chunkCtm.inverse());*/
0574     }
0575 
0576 private:
0577     SVGRootInlineBox *m_rootBox;
0578     bool m_chunkStarted : 1;
0579 
0580     RenderObject::PaintInfo m_paintInfo;
0581     RenderObject::PaintInfo m_savedInfo;
0582 
0583     FloatRect m_boundingBox;
0584     SVGResourceFilter *m_filter;
0585     SVGResourceFilter *m_rootFilter;
0586 
0587     SVGPaintServer *m_fillPaintServer;
0588     SVGPaintServer *m_strokePaintServer;
0589 
0590     RenderObject *m_fillPaintServerObject;
0591     RenderObject *m_strokePaintServerObject;
0592 
0593     int m_tx;
0594     int m_ty;
0595 };
0596 
0597 void SVGRootInlineBox::paint(RenderObject::PaintInfo &paintInfo, int tx, int ty)
0598 {
0599     //if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground)
0600     //    return;
0601 
0602     RenderObject::PaintInfo savedInfo(paintInfo);
0603     //paintInfo.context->save();
0604 
0605     SVGResourceFilter *filter = nullptr;
0606     FloatRect boundingBox(tx + xPos(), ty + yPos(), width(), height());
0607 
0608     // Initialize text rendering
0609     paintInfo.p->setWorldMatrix(object()->localTransform(), true);
0610     prepareToRenderSVGContent(object(), paintInfo, boundingBox, filter);
0611     paintInfo.p->setWorldMatrix(object()->localTransform().inverse(), true);
0612 
0613     //qCDebug(KHTML_LOG) << "paint at" << tx << ty;
0614     //qCDebug(KHTML_LOG) << "pos: (" << (tx + xPos()) << "," << (ty + yPos()) << ")";
0615     //qCDebug(KHTML_LOG) << "size: " << width() << "x" << height();
0616 
0617     // Render text, chunk-by-chunk
0618     SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty);
0619     SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback,
0620             &SVGRootInlineBoxPaintWalker::chunkPortionCallback,
0621             &SVGRootInlineBoxPaintWalker::chunkStartCallback,
0622             &SVGRootInlineBoxPaintWalker::chunkEndCallback,
0623             &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback,
0624             &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback);
0625 
0626     walkTextChunks(&walker);
0627 
0628     // Finalize text rendering
0629     //FIXME khtml finishRenderSVGContent(object(), paintInfo, boundingBox, filter, savedInfo.context);
0630     //paintInfo.context->restore();
0631 }
0632 
0633 int SVGRootInlineBox::placeBoxesHorizontally(int, int &leftPosition, int &rightPosition, bool &)
0634 {
0635     // Remove any offsets caused by RTL text layout
0636     leftPosition = 0;
0637     rightPosition = 0;
0638     return 0;
0639 }
0640 
0641 void SVGRootInlineBox::verticallyAlignBoxes(int &heightOfBlock)
0642 {
0643     // height is set by layoutInlineBoxes.
0644     heightOfBlock = height();
0645 }
0646 
0647 float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange &range)
0648 {
0649     ASSERT(!range.isOpen());
0650     ASSERT(range.isClosed());
0651     ASSERT(range.box->isInlineTextBox());
0652 
0653     InlineTextBox *textBox = static_cast<InlineTextBox *>(range.box);
0654     RenderText *text = textBox->renderText();
0655     RenderStyle *style = text->style();
0656 
0657     return style->htmlFont().floatWidth(text->text(), textBox->start() + range.startOffset,
0658                                         range.endOffset - range.startOffset);
0659 }
0660 
0661 float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange &range)
0662 {
0663     ASSERT(!range.isOpen());
0664     ASSERT(range.isClosed());
0665     ASSERT(range.box->isInlineTextBox());
0666 
0667     InlineTextBox *textBox = static_cast<InlineTextBox *>(range.box);
0668     RenderText *text = textBox->renderText();
0669     const khtml::Font &font = text->style()->htmlFont();
0670     return (range.endOffset - range.startOffset) * (font.ascent() + font.descent());
0671     return 0;
0672 }
0673 
0674 /*TextRun svgTextRunForInlineTextBox(const UChar* c, int len, RenderStyle* style, const InlineTextBox* textBox, float xPos)
0675 {
0676     ASSERT(textBox);
0677     ASSERT(style);
0678 
0679     TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->m_reversed, textBox->m_dirOverride || style->visuallyOrdered());
0680 
0681 #if ENABLE(SVG_FONTS)
0682     run.setReferencingRenderObject(textBox->textObject()->parent());
0683 #endif
0684 
0685     // We handle letter & word spacing ourselves
0686     run.disableSpacing();
0687     return run;
0688 }*/
0689 
0690 static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk &chunk, bool calcWidthOnly)
0691 {
0692     float length = 0.0f;
0693     Vector<SVGChar>::iterator charIt = chunk.start;
0694 
0695     Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin();
0696     Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end();
0697 
0698     for (; it != end; ++it) {
0699         SVGInlineBoxCharacterRange &range = *it;
0700         // qCDebug(KHTML_LOG) << "box range:" << range.startOffset << range.endOffset;
0701 
0702         SVGInlineTextBox *box = static_cast<SVGInlineTextBox *>(range.box);
0703         RenderStyle *style = box->object()->style();
0704 
0705         for (int i = range.startOffset; i < range.endOffset; ++i) {
0706             ASSERT(charIt <= chunk.end);
0707 
0708             // Determine how many characters - starting from the current - can be measured at once.
0709             // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width
0710             // of a string is not the sum of the boundaries of all contained glyphs.
0711             Vector<SVGChar>::iterator itSearch = charIt + 1;
0712             Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i;
0713             while (itSearch != endSearch) {
0714                 // No need to check for 'isHidden()' here as this function is not called for text paths.
0715                 if (itSearch->drawnSeperated) {
0716                     break;
0717                 }
0718 
0719                 itSearch++;
0720             }
0721 
0722             unsigned int positionOffset = itSearch - charIt;
0723 
0724             // Calculate width/height of subrange
0725             SVGInlineBoxCharacterRange subRange;
0726             subRange.box = range.box;
0727             subRange.startOffset = i;
0728             subRange.endOffset = i + positionOffset;
0729             // qCDebug(KHTML_LOG) << "got subrange:" << subRange.startOffset << subRange.endOffset;
0730 
0731             if (calcWidthOnly) {
0732                 length += cummulatedWidthOfInlineBoxCharacterRange(subRange);
0733             } else {
0734                 length += cummulatedHeightOfInlineBoxCharacterRange(subRange);
0735             }
0736 
0737             // Calculate gap between the previous & current range
0738             // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account
0739             // so add "40" as width, and analogous for B & C, add "20" as width.
0740             if (itSearch > chunk.start && itSearch < chunk.end) {
0741                 SVGChar &lastCharacter = *(itSearch - 1);
0742                 SVGChar &currentCharacter = *itSearch;
0743 
0744                 int offset = box->m_reversed ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1;
0745 
0746                 // FIXME: does this need to change to handle multichar glyphs?
0747                 int charsConsumed = 1;
0748                 String glyphName;
0749                 if (calcWidthOnly) {
0750                     float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName);
0751                     length += currentCharacter.x - lastCharacter.x - lastGlyphWidth;
0752                 } else {
0753                     float lastGlyphHeight = box->calculateGlyphHeight(style, offset, 0);
0754                     length += currentCharacter.y - lastCharacter.y - lastGlyphHeight;
0755                 }
0756             }
0757 
0758             // Advance processed characters
0759             i += positionOffset - 1;
0760             charIt = itSearch;
0761         }
0762     }
0763 
0764     ASSERT(charIt == chunk.end);
0765     return length;
0766 }
0767 
0768 static float cummulatedWidthOfTextChunk(SVGTextChunk &chunk)
0769 {
0770     return cummulatedWidthOrHeightOfTextChunk(chunk, true);
0771 }
0772 
0773 static float cummulatedHeightOfTextChunk(SVGTextChunk &chunk)
0774 {
0775     return cummulatedWidthOrHeightOfTextChunk(chunk, false);
0776 }
0777 
0778 static float calculateTextAnchorShiftForTextChunk(SVGTextChunk &chunk, ETextAnchor anchor)
0779 {
0780     float shift = 0.0f;
0781 
0782     if (chunk.isVerticalText) {
0783         shift = cummulatedHeightOfTextChunk(chunk);
0784     } else {
0785         shift = cummulatedWidthOfTextChunk(chunk);
0786     }
0787 
0788     // qCDebug(KHTML_LOG) << anchor << shift << TA_MIDDLE;
0789 
0790     if (anchor == TA_MIDDLE) {
0791         shift *= -0.5f;
0792     } else {
0793         shift *= -1.0f;
0794     }
0795 
0796     return shift;
0797 }
0798 
0799 static void applyTextAnchorToTextChunk(SVGTextChunk &chunk)
0800 {
0801     // This method is not called for chunks containing chars aligned on a path.
0802     // -> all characters are visible, no need to check for "isHidden()" anywhere.
0803 
0804     if (chunk.anchor == TA_START) {
0805         return;
0806     }
0807 
0808     float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor);
0809 
0810     // Apply correction to chunk
0811     Vector<SVGChar>::iterator chunkIt = chunk.start;
0812     for (; chunkIt != chunk.end; ++chunkIt) {
0813         SVGChar &curChar = *chunkIt;
0814 
0815         if (chunk.isVerticalText) {
0816             curChar.y += shift;
0817         } else {
0818             curChar.x += shift;
0819         }
0820     }
0821 
0822     // Move inline boxes
0823     Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
0824     Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
0825 
0826     for (; boxIt != boxEnd; ++boxIt) {
0827         SVGInlineBoxCharacterRange &range = *boxIt;
0828 
0829         InlineBox *curBox = range.box;
0830         ASSERT(curBox->isInlineTextBox());
0831         ASSERT(curBox->parent() && (curBox->parent()->isRootInlineBox() || curBox->parent()->isInlineFlowBox()));
0832 
0833         // Move target box
0834         if (chunk.isVerticalText) {
0835             curBox->setYPos(curBox->yPos() + static_cast<int>(shift));
0836         } else {
0837             curBox->setXPos(curBox->xPos() + static_cast<int>(shift));
0838         }
0839     }
0840 }
0841 
0842 static float calculateTextLengthCorrectionForTextChunk(SVGTextChunk &chunk, ELengthAdjust lengthAdjust, float &computedLength)
0843 {
0844     //qCDebug(KHTML_LOG) << "text length";
0845     if (chunk.textLength <= 0.0f) {
0846         return 0.0f;
0847     }
0848 
0849     float computedWidth = cummulatedWidthOfTextChunk(chunk);
0850     float computedHeight = cummulatedHeightOfTextChunk(chunk);
0851 
0852     if ((computedWidth <= 0.0f && !chunk.isVerticalText) ||
0853             (computedHeight <= 0.0f && chunk.isVerticalText)) {
0854         return 0.0f;
0855     }
0856 
0857     if (chunk.isVerticalText) {
0858         computedLength = computedHeight;
0859     } else {
0860         computedLength = computedWidth;
0861     }
0862 
0863     if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
0864         if (chunk.isVerticalText) {
0865             chunk.ctm.scale(1.0f, chunk.textLength / computedLength);
0866         } else {
0867             chunk.ctm.scale(chunk.textLength / computedLength, 1.0f);
0868         }
0869 
0870         return 0.0f;
0871     }
0872 
0873     return (chunk.textLength - computedLength) / float(chunk.end - chunk.start);
0874 }
0875 
0876 static void applyTextLengthCorrectionToTextChunk(SVGTextChunk &chunk)
0877 {
0878     // This method is not called for chunks containing chars aligned on a path.
0879     // -> all characters are visible, no need to check for "isHidden()" anywhere.
0880 
0881     // lengthAdjust="spacingAndGlyphs" is handled by modifying chunk.ctm
0882     float computedLength = 0.0f;
0883     float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, computedLength);
0884 
0885     if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
0886         SVGChar &firstChar = *(chunk.start);
0887 
0888         // Assure we apply the chunk scaling in the right origin
0889         AffineTransform newChunkCtm;
0890         newChunkCtm.translate(firstChar.x, firstChar.y);
0891         newChunkCtm = chunk.ctm * newChunkCtm;
0892         newChunkCtm.translate(-firstChar.x, -firstChar.y);
0893 
0894         chunk.ctm = newChunkCtm;
0895     }
0896 
0897     // Apply correction to chunk
0898     if (spacingToApply != 0.0f) {
0899         Vector<SVGChar>::iterator chunkIt = chunk.start;
0900         for (; chunkIt != chunk.end; ++chunkIt) {
0901             SVGChar &curChar = *chunkIt;
0902             curChar.drawnSeperated = true;
0903 
0904             if (chunk.isVerticalText) {
0905                 curChar.y += (chunkIt - chunk.start) * spacingToApply;
0906             } else {
0907                 curChar.x += (chunkIt - chunk.start) * spacingToApply;
0908             }
0909         }
0910     }
0911 }
0912 
0913 void SVGRootInlineBox::computePerCharacterLayoutInformation()
0914 {
0915     //qCDebug(KHTML_LOG) << "computePerCharacterLayoutInformation()";
0916     // Clean up any previous layout information
0917     m_svgChars.clear();
0918     m_svgTextChunks.clear();
0919 
0920     // Build layout information for all contained render objects
0921     SVGCharacterLayoutInfo info(m_svgChars);
0922     //qCDebug(KHTML_LOG) << "before build layout info";
0923     buildLayoutInformation(this, info);
0924     //qCDebug(KHTML_LOG) << "after build layout info";
0925 
0926     // Now all layout information are available for every character
0927     // contained in any of our child inline/flow boxes. Build list
0928     // of text chunks now, to be able to apply text-anchor shifts.
0929     buildTextChunks(m_svgChars, m_svgTextChunks, this);
0930     //qCDebug(KHTML_LOG) << "after build text chunks";
0931 
0932     // Layout all text chunks
0933     // text-anchor needs to be applied to individual chunks.
0934     layoutTextChunks();
0935     //qCDebug(KHTML_LOG) << "after layout text chunks";
0936 
0937     // Finally the top left position of our box is known.
0938     // Propagate this knownledge to our RenderSVGText parent.
0939     FloatPoint topLeft = topLeftPositionOfCharacterRange(m_svgChars);
0940     object()->setPos((int) floorf(topLeft.x()), (int) floorf(topLeft.y()));
0941 
0942     // Layout all InlineText/Flow boxes
0943     // BEWARE: This requires the root top/left position to be set correctly before!
0944     //qCDebug(KHTML_LOG) << "before layout inline boxes";
0945     layoutInlineBoxes();
0946     //qCDebug(KHTML_LOG) << "at the end";
0947 }
0948 
0949 void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox *start, SVGCharacterLayoutInfo &info)
0950 {
0951     if (start->isRootInlineBox()) {
0952         ASSERT(start->object()->element()->hasTagName(SVGNames::textTag));
0953 
0954         SVGTextPositioningElement *positioningElement = static_cast<SVGTextPositioningElement *>(start->object()->element());
0955         ASSERT(positioningElement);
0956         ASSERT(positioningElement->parentNode());
0957 
0958         info.addLayoutInformation(positioningElement);
0959     }
0960 
0961     LastGlyphInfo lastGlyph;
0962 
0963     for (InlineBox *curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
0964         if (curr->object()->isText()) {
0965             buildLayoutInformationForTextBox(info, static_cast<InlineTextBox *>(curr), lastGlyph);
0966         } else {
0967             ASSERT(curr->isInlineFlowBox());
0968             InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(curr);
0969 
0970             bool isAnchor = flowBox->object()->element()->hasTagName(SVGNames::aTag);
0971             bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
0972 
0973             if (!isTextPath && !isAnchor) {
0974                 SVGTextPositioningElement *positioningElement = static_cast<SVGTextPositioningElement *>(flowBox->object()->element());
0975                 ASSERT(positioningElement);
0976                 ASSERT(positioningElement->parentNode());
0977 
0978                 info.addLayoutInformation(positioningElement);
0979             } else if (!isAnchor) {
0980                 info.setInPathLayout(true);
0981 
0982                 // Handle text-anchor/textLength on path, which is special.
0983                 SVGTextContentElement *textContent = nullptr;
0984                 Node *node = flowBox->object()->element();
0985                 if (node && node->isSVGElement()) {
0986                     textContent = static_cast<SVGTextContentElement *>(node);
0987                 }
0988                 ASSERT(textContent);
0989 
0990                 ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
0991                 ETextAnchor anchor = flowBox->object()->style()->svgStyle()->textAnchor();
0992                 float textAnchorStartOffset = 0.0f;
0993 
0994                 // Initialize sub-layout. We need to create text chunks from the textPath
0995                 // children using our standard layout code, to be able to measure the
0996                 // text length using our normal methods and not textPath specific hacks.
0997                 Vector<SVGChar> tempChars;
0998                 Vector<SVGTextChunk> tempChunks;
0999 
1000                 SVGCharacterLayoutInfo tempInfo(tempChars);
1001                 buildLayoutInformation(flowBox, tempInfo);
1002 
1003                 buildTextChunks(tempChars, tempChunks, flowBox);
1004 
1005                 Vector<SVGTextChunk>::iterator it = tempChunks.begin();
1006                 Vector<SVGTextChunk>::iterator end = tempChunks.end();
1007 
1008                 AffineTransform ctm;
1009                 float computedLength = 0.0f;
1010 
1011                 for (; it != end; ++it) {
1012                     SVGTextChunk &chunk = *it;
1013 
1014                     // Apply text-length calculation
1015                     info.pathExtraAdvance += calculateTextLengthCorrectionForTextChunk(chunk, lengthAdjust, computedLength);
1016 
1017                     if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
1018                         info.pathTextLength += computedLength;
1019                         info.pathChunkLength += chunk.textLength;
1020                     }
1021 
1022                     // Calculate text-anchor start offset
1023                     if (anchor == TA_START) {
1024                         continue;
1025                     }
1026 
1027                     textAnchorStartOffset += calculateTextAnchorShiftForTextChunk(chunk, anchor);
1028                 }
1029 
1030                 info.addLayoutInformation(flowBox, textAnchorStartOffset);
1031             }
1032 
1033             float shiftxSaved = info.shiftx;
1034             float shiftySaved = info.shifty;
1035 
1036             buildLayoutInformation(flowBox, info);
1037             info.processedChunk(shiftxSaved, shiftySaved);
1038 
1039             if (isTextPath) {
1040                 info.setInPathLayout(false);
1041             }
1042         }
1043     }
1044 }
1045 
1046 void SVGRootInlineBox::layoutInlineBoxes()
1047 {
1048     int lowX = INT_MAX;
1049     int lowY = INT_MAX;
1050     int highX = INT_MIN;
1051     int highY = INT_MIN;
1052 
1053     // Layout all child boxes
1054     Vector<SVGChar>::iterator it = m_svgChars.begin();
1055     layoutInlineBoxes(this, it, lowX, highX, lowY, highY);
1056     ASSERT(it == m_svgChars.end());
1057 }
1058 
1059 void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox *start, Vector<SVGChar>::iterator &it, int &lowX, int &highX, int &lowY, int &highY)
1060 {
1061     for (InlineBox *curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
1062         RenderStyle *style = curr->object()->style();
1063         const Font &font = style->htmlFont();
1064 
1065         if (curr->object()->isText()) {
1066             SVGInlineTextBox *textBox = static_cast<SVGInlineTextBox *>(curr);
1067             unsigned length = textBox->len();
1068 
1069             SVGChar curChar = *it;
1070             ASSERT(it != m_svgChars.end());
1071 
1072             FloatRect stringRect;
1073             for (unsigned i = 0; i < length; ++i) {
1074                 ASSERT(it != m_svgChars.end());
1075 
1076                 if (it->isHidden()) {
1077                     ++it;
1078                     continue;
1079                 }
1080 
1081                 stringRect.unite(textBox->calculateGlyphBoundaries(style, textBox->start() + i, *it));
1082                 ++it;
1083             }
1084 
1085             IntRect enclosedStringRect = enclosingIntRect(stringRect);
1086 
1087             int minX = enclosedStringRect.x();
1088             int maxX = minX + enclosedStringRect.width();
1089 
1090             int minY = enclosedStringRect.y();
1091             int maxY = minY + enclosedStringRect.height();
1092 
1093             curr->setXPos(minX - object()->xPos());
1094             curr->setWidth(enclosedStringRect.width());
1095 
1096             curr->setYPos(minY - object()->yPos());
1097             curr->setBaseline(font.ascent());
1098             curr->setHeight(enclosedStringRect.height());
1099 
1100             if (minX < lowX) {
1101                 lowX = minX;
1102             }
1103 
1104             if (maxX > highX) {
1105                 highX = maxX;
1106             }
1107 
1108             if (minY < lowY) {
1109                 lowY = minY;
1110             }
1111 
1112             if (maxY > highY) {
1113                 highY = maxY;
1114             }
1115         } else {
1116             ASSERT(curr->isInlineFlowBox());
1117 
1118             int minX = INT_MAX;
1119             int minY = INT_MAX;
1120             int maxX = INT_MIN;
1121             int maxY = INT_MIN;
1122 
1123             InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(curr);
1124             layoutInlineBoxes(flowBox, it, minX, maxX, minY, maxY);
1125 
1126             curr->setXPos(minX - object()->xPos());
1127             curr->setWidth(maxX - minX);
1128 
1129             curr->setYPos(minY - object()->yPos());
1130             curr->setBaseline(font.ascent());
1131             curr->setHeight(maxY - minY);
1132 
1133             if (minX < lowX) {
1134                 lowX = minX;
1135             }
1136 
1137             if (maxX > highX) {
1138                 highX = maxX;
1139             }
1140 
1141             if (minY < lowY) {
1142                 lowY = minY;
1143             }
1144 
1145             if (maxY > highY) {
1146                 highY = maxY;
1147             }
1148         }
1149     }
1150 
1151     if (start->isRootInlineBox()) {
1152         int top = lowY - object()->yPos();
1153         //int bottom = highY - object()->yPos();
1154 
1155         start->setXPos(lowX - object()->xPos());
1156         start->setYPos(top);
1157 
1158         start->setWidth(highX - lowX);
1159         start->setHeight(highY - lowY);
1160 
1161         /*FIXME start->setVerticalOverflowPositions(top, bottom);
1162         start->setVerticalSelectionPositions(top, bottom);*/
1163     }
1164 }
1165 
1166 void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo &info, InlineTextBox *textBox, LastGlyphInfo &lastGlyph)
1167 {
1168     Q_UNUSED(lastGlyph);
1169 
1170     RenderText *text = textBox->renderText();
1171     ASSERT(text);
1172 
1173     RenderStyle *style = text->style(/*textBox->isFirstLineStyle()*/);
1174     ASSERT(style);
1175 
1176     const Font &font = style->htmlFont();
1177     SVGInlineTextBox *svgTextBox = static_cast<SVGInlineTextBox *>(textBox);
1178 
1179     unsigned length = textBox->len();
1180 
1181     const SVGRenderStyle *svgStyle = style->svgStyle();
1182     bool isVerticalText = isVerticalWritingMode(svgStyle);
1183 
1184     int charsConsumed = 0;
1185     for (unsigned i = 0; i < length; i += charsConsumed) {
1186         SVGChar svgChar;
1187 
1188         if (info.inPathLayout()) {
1189             svgChar.pathData = SVGCharOnPath::create();
1190         }
1191 
1192         float glyphWidth = 0.0f;
1193         float glyphHeight = 0.0f;
1194 
1195         int extraCharsAvailable = length - i - 1;
1196 
1197         String unicodeStr;
1198         String glyphName;
1199         if (textBox->m_reversed) {
1200             glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName);
1201             glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable);
1202             unicodeStr = String(textBox->renderText()->text()/*->characters()*/ + textBox->end() - i, charsConsumed);
1203         } else {
1204             glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed, glyphName);
1205             glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->start() + i, extraCharsAvailable);
1206             unicodeStr = String(textBox->renderText()->text()/*->characters()*/ + textBox->start() + i, charsConsumed);
1207         }
1208 
1209         bool assignedX = false;
1210         bool assignedY = false;
1211 
1212         if (info.xValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && !isVerticalText))) {
1213             if (!isVerticalText) {
1214                 svgChar.newTextChunk = true;
1215             }
1216 
1217             assignedX = true;
1218             svgChar.drawnSeperated = true;
1219             info.curx = info.xValueNext();
1220         }
1221 
1222         if (info.yValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && isVerticalText))) {
1223             if (isVerticalText) {
1224                 svgChar.newTextChunk = true;
1225             }
1226 
1227             assignedY = true;
1228             svgChar.drawnSeperated = true;
1229             info.cury = info.yValueNext();
1230         }
1231 
1232         float dx = 0.0f;
1233         float dy = 0.0f;
1234 
1235         // Apply x-axis shift
1236         if (info.dxValueAvailable()) {
1237             svgChar.drawnSeperated = true;
1238 
1239             dx = info.dxValueNext();
1240             info.dx += dx;
1241 
1242             if (!info.inPathLayout()) {
1243                 info.curx += dx;
1244             }
1245         }
1246 
1247         // Apply y-axis shift
1248         if (info.dyValueAvailable()) {
1249             svgChar.drawnSeperated = true;
1250 
1251             dy = info.dyValueNext();
1252             info.dy += dy;
1253 
1254             if (!info.inPathLayout()) {
1255                 info.cury += dy;
1256             }
1257         }
1258 
1259         // Take letter & word spacing and kerning into account
1260         float spacing = 0;//FIXME font.letterSpacing() + calculateKerning(textBox->object()->element()->renderer());
1261 
1262         const UChar *currentCharacter = text->text()/*->characters()*/ + (textBox->m_reversed ? textBox->end() - i : textBox->start() + i);
1263 #if 0 // FIXME (see below)
1264         const UChar *lastCharacter = 0;
1265 
1266         if (textBox->m_reversed) {
1267             if (i < textBox->end()) {
1268                 lastCharacter = text->text()/*characters()*/ + textBox->end() - i +  1;
1269             }
1270         } else {
1271             if (i > 0) {
1272                 lastCharacter = text->text()/*characters()*/ + textBox->start() + i - 1;
1273             }
1274         }
1275 #endif
1276 
1277         if (info.nextDrawnSeperated || spacing != 0.0f) {
1278             info.nextDrawnSeperated = false;
1279             svgChar.drawnSeperated = true;
1280         }
1281 
1282         /*FIXME if (currentCharacter && Font::treatAsSpace(*currentCharacter) && lastCharacter && !Font::treatAsSpace(*lastCharacter)) {
1283             spacing += font.wordSpacing();
1284 
1285             if (spacing != 0.0f && !info.inPathLayout())
1286                 info.nextDrawnSeperated = true;
1287         }*/
1288 
1289         float orientationAngle = glyphOrientationToAngle(svgStyle, isVerticalText, *currentCharacter);
1290 
1291         float xOrientationShift = 0.0f;
1292         float yOrientationShift = 0.0f;
1293         float glyphAdvance = calculateGlyphAdvanceAndShiftRespectingOrientation(isVerticalText, orientationAngle, glyphWidth, glyphHeight,
1294                              font, svgChar, xOrientationShift, yOrientationShift);
1295 
1296         // Handle textPath layout mode
1297         if (info.inPathLayout()) {
1298             float extraAdvance = isVerticalText ? dy : dx;
1299             float newOffset = FLT_MIN;
1300 
1301             if (assignedX && !isVerticalText) {
1302                 newOffset = info.curx;
1303             } else if (assignedY && isVerticalText) {
1304                 newOffset = info.cury;
1305             }
1306 
1307             float correctedGlyphAdvance = glyphAdvance;
1308 
1309             // Handle lengthAdjust="spacingAndGlyphs" by specifying per-character scale operations
1310             if (info.pathTextLength > 0.0f && info.pathChunkLength > 0.0f) {
1311                 if (isVerticalText) {
1312                     svgChar.pathData->yScale = info.pathChunkLength / info.pathTextLength;
1313                     spacing *= svgChar.pathData->yScale;
1314                     correctedGlyphAdvance *= svgChar.pathData->yScale;
1315                 } else {
1316                     svgChar.pathData->xScale = info.pathChunkLength / info.pathTextLength;
1317                     spacing *= svgChar.pathData->xScale;
1318                     correctedGlyphAdvance *= svgChar.pathData->xScale;
1319                 }
1320             }
1321 
1322             // Handle letter & word spacing on text path
1323             float pathExtraAdvance = info.pathExtraAdvance;
1324             info.pathExtraAdvance += spacing;
1325 
1326             svgChar.pathData->hidden = !info.nextPathLayoutPointAndAngle(correctedGlyphAdvance, extraAdvance, newOffset);
1327             svgChar.drawnSeperated = true;
1328 
1329             info.pathExtraAdvance = pathExtraAdvance;
1330         }
1331 
1332         // Apply rotation
1333         if (info.angleValueAvailable()) {
1334             info.angle = info.angleValueNext();
1335         }
1336 
1337         // Apply baseline-shift
1338         if (info.baselineShiftValueAvailable()) {
1339             svgChar.drawnSeperated = true;
1340             float shift = info.baselineShiftValueNext();
1341 
1342             if (isVerticalText) {
1343                 info.shiftx += shift;
1344             } else {
1345                 info.shifty -= shift;
1346             }
1347         }
1348 
1349         // Take dominant-baseline / alignment-baseline into account
1350         yOrientationShift += alignmentBaselineToShift(isVerticalText, text, font);
1351 
1352         svgChar.x = info.curx;
1353         svgChar.y = info.cury;
1354         svgChar.angle = info.angle;
1355 
1356         // For text paths any shift (dx/dy/baseline-shift) has to be applied after the rotation
1357         if (!info.inPathLayout()) {
1358             svgChar.x += info.shiftx + xOrientationShift;
1359             svgChar.y += info.shifty + yOrientationShift;
1360 
1361             if (orientationAngle != 0.0f) {
1362                 svgChar.angle += orientationAngle;
1363             }
1364 
1365             if (svgChar.angle != 0.0f) {
1366                 svgChar.drawnSeperated = true;
1367             }
1368         } else {
1369             svgChar.pathData->orientationAngle = orientationAngle;
1370 
1371             if (isVerticalText) {
1372                 svgChar.angle -= 90.0f;
1373             }
1374 
1375             svgChar.pathData->xShift = info.shiftx + xOrientationShift;
1376             svgChar.pathData->yShift = info.shifty + yOrientationShift;
1377 
1378             // Translate to glyph midpoint
1379             if (isVerticalText) {
1380                 svgChar.pathData->xShift += info.dx;
1381                 svgChar.pathData->yShift -= glyphAdvance / 2.0f;
1382             } else {
1383                 svgChar.pathData->xShift -= glyphAdvance / 2.0f;
1384                 svgChar.pathData->yShift += info.dy;
1385             }
1386         }
1387 
1388         double kerning = 0.0;
1389 #if ENABLE(SVG_FONTS)
1390         /*FIXME khtml SVGFontElement* svgFont = 0;
1391         if (style->font().isSVGFont())
1392             svgFont = style->font().svgFont();
1393 
1394         if (lastGlyph.isValid && style->font().isSVGFont()) {
1395             SVGHorizontalKerningPair kerningPair;
1396             if (svgFont->getHorizontalKerningPairForStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeStr, glyphName, kerningPair))
1397                 kerning = kerningPair.kerning;
1398         }
1399 
1400         if (style->font().isSVGFont()) {
1401             lastGlyph.unicode = unicodeStr;
1402             lastGlyph.glyphName = glyphName;
1403             lastGlyph.isValid = true;
1404         } else
1405             lastGlyph.isValid = false;*/
1406 #endif
1407 
1408         svgChar.x -= (float)kerning;
1409 
1410         // Advance to new position
1411         if (isVerticalText) {
1412             svgChar.drawnSeperated = true;
1413             info.cury += glyphAdvance + spacing;
1414         } else {
1415             info.curx += glyphAdvance + spacing - (float)kerning;
1416         }
1417 
1418         // Advance to next character group
1419         for (int k = 0; k < charsConsumed; ++k) {
1420             info.svgChars.append(svgChar);
1421             info.processedSingleCharacter();
1422             svgChar.drawnSeperated = false;
1423             svgChar.newTextChunk = false;
1424         }
1425     }
1426 }
1427 
1428 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar> &svgChars, Vector<SVGTextChunk> &svgTextChunks, InlineFlowBox *start)
1429 {
1430     SVGTextChunkLayoutInfo info(svgTextChunks);
1431     info.it = svgChars.begin();
1432     info.chunk.start = svgChars.begin();
1433     info.chunk.end = svgChars.begin();
1434 
1435     buildTextChunks(svgChars, start, info);
1436     ASSERT(info.it == svgChars.end());
1437 }
1438 
1439 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar> &svgChars, InlineFlowBox *start, SVGTextChunkLayoutInfo &info)
1440 {
1441 #if DEBUG_CHUNK_BUILDING > 1
1442     fprintf(stderr, " -> buildTextChunks(start=%p)\n", start);
1443 #endif
1444 
1445     for (InlineBox *curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
1446         if (curr->object()->isText()) {
1447             InlineTextBox *textBox = static_cast<InlineTextBox *>(curr);
1448 
1449             unsigned length = textBox->len();
1450             if (!length) {
1451                 continue;
1452             }
1453 
1454 #if DEBUG_CHUNK_BUILDING > 1
1455             fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n",
1456                     textBox, length, textBox->start(), textBox->end(), (int) info.handlingTextPath);
1457 #endif
1458 
1459             RenderText *text = textBox->renderText();
1460             ASSERT(text);
1461             ASSERT(text->element());
1462 
1463             SVGTextContentElement *textContent = nullptr;
1464             Node *node = text->element()->parent();
1465             if (node && node->isSVGElement()) {
1466                 textContent = static_cast<SVGTextContentElement *>(node);
1467             }
1468             ASSERT(textContent);
1469 
1470             // Start new character range for the first chunk
1471             bool isFirstCharacter = info.svgTextChunks.isEmpty() && info.chunk.start == info.it && info.chunk.start == info.chunk.end;
1472             if (isFirstCharacter) {
1473                 ASSERT(info.chunk.boxes.isEmpty());
1474                 info.chunk.boxes.append(SVGInlineBoxCharacterRange());
1475             } else {
1476                 ASSERT(!info.chunk.boxes.isEmpty());
1477             }
1478 
1479             // Walk string to find out new chunk positions, if existent
1480             for (unsigned i = 0; i < length; ++i) {
1481                 ASSERT(info.it != svgChars.end());
1482 
1483                 SVGInlineBoxCharacterRange &range = info.chunk.boxes.last();
1484                 if (range.isOpen()) {
1485                     range.box = curr;
1486                     range.startOffset = (i == 0 ? 0 : i - 1);
1487 
1488 #if DEBUG_CHUNK_BUILDING > 1
1489                     fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset);
1490 #endif
1491                 }
1492 
1493                 // If a new (or the first) chunk has been started, record it's text-anchor and writing mode.
1494                 if (info.assignChunkProperties) {
1495                     info.assignChunkProperties = false;
1496 
1497                     info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
1498                     info.chunk.isTextPath = info.handlingTextPath;
1499                     info.chunk.anchor = text->style()->svgStyle()->textAnchor();
1500                     info.chunk.textLength = textContent->textLength().value();
1501                     info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
1502 
1503 #if DEBUG_CHUNK_BUILDING > 1
1504                     fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", info.chunk.isVerticalText, info.chunk.anchor);
1505 #endif
1506                 }
1507 
1508                 if (i > 0 && !isFirstCharacter && (*info.it).newTextChunk) {
1509                     // Close mid chunk & character range
1510                     ASSERT(!range.isOpen());
1511                     ASSERT(!range.isClosed());
1512 
1513                     range.endOffset = i;
1514                     closeTextChunk(info);
1515 
1516 #if DEBUG_CHUNK_BUILDING > 1
1517                     fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset);
1518 #endif
1519 
1520                     // Prepare for next chunk, if we're not at the end
1521                     startTextChunk(info);
1522                     if (i + 1 == length) {
1523 #if DEBUG_CHUNK_BUILDING > 1
1524                         fprintf(stderr, " | -> Record last chunk of inline text box!\n");
1525 #endif
1526 
1527                         startTextChunk(info);
1528                         SVGInlineBoxCharacterRange &range = info.chunk.boxes.last();
1529 
1530                         info.assignChunkProperties = false;
1531                         info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
1532                         info.chunk.isTextPath = info.handlingTextPath;
1533                         info.chunk.anchor = text->style()->svgStyle()->textAnchor();
1534                         info.chunk.textLength = textContent->textLength().value();
1535                         info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
1536 
1537                         range.box = curr;
1538                         range.startOffset = i;
1539 
1540                         ASSERT(!range.isOpen());
1541                         ASSERT(!range.isClosed());
1542                     }
1543                 }
1544 
1545                 // This should only hold true for the first character of the first chunk
1546                 if (isFirstCharacter) {
1547                     isFirstCharacter = false;
1548                 }
1549 
1550                 ++info.it;
1551             }
1552 
1553 #if DEBUG_CHUNK_BUILDING > 1
1554             fprintf(stderr, " -> Finished inline text box!\n");
1555 #endif
1556 
1557             SVGInlineBoxCharacterRange &range = info.chunk.boxes.last();
1558             if (!range.isOpen() && !range.isClosed()) {
1559 #if DEBUG_CHUNK_BUILDING > 1
1560                 fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length);
1561 #endif
1562 
1563                 // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk.
1564                 range.endOffset = length;
1565 
1566                 if (info.it != svgChars.end()) {
1567 #if DEBUG_CHUNK_BUILDING > 1
1568                     fprintf(stderr, " -> Not at last character yet!\n");
1569 #endif
1570 
1571                     // If we're not at the end of the last box to be processed, and if the next
1572                     // character starts a new chunk, then close the current chunk and start a new one.
1573                     if ((*info.it).newTextChunk) {
1574 #if DEBUG_CHUNK_BUILDING > 1
1575                         fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n");
1576 #endif
1577 
1578                         closeTextChunk(info);
1579                         startTextChunk(info);
1580                     } else {
1581                         // Just start a new character range
1582                         info.chunk.boxes.append(SVGInlineBoxCharacterRange());
1583 
1584 #if DEBUG_CHUNK_BUILDING > 1
1585                         fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n");
1586 #endif
1587                     }
1588                 } else {
1589 #if DEBUG_CHUNK_BUILDING > 1
1590                     fprintf(stderr, " -> Closing final chunk! Finished processing!\n");
1591 #endif
1592 
1593                     // Close final chunk, once we're at the end of the last box
1594                     closeTextChunk(info);
1595                 }
1596             }
1597         } else {
1598             ASSERT(curr->isInlineFlowBox());
1599             InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(curr);
1600 
1601             bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
1602 
1603 #if DEBUG_CHUNK_BUILDING > 1
1604             fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath);
1605 #endif
1606 
1607             if (isTextPath) {
1608                 info.handlingTextPath = true;
1609             }
1610 
1611             buildTextChunks(svgChars, flowBox, info);
1612 
1613             if (isTextPath) {
1614                 info.handlingTextPath = false;
1615             }
1616         }
1617     }
1618 
1619 #if DEBUG_CHUNK_BUILDING > 1
1620     fprintf(stderr, " <- buildTextChunks(start=%p)\n", start);
1621 #endif
1622 }
1623 
1624 const Vector<SVGTextChunk> &SVGRootInlineBox::svgTextChunks() const
1625 {
1626     return m_svgTextChunks;
1627 }
1628 
1629 void SVGRootInlineBox::layoutTextChunks()
1630 {
1631     Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
1632     Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end();
1633 
1634     for (; it != end; ++it) {
1635         SVGTextChunk &chunk = *it;
1636 
1637 #if DEBUG_CHUNK_BUILDING > 0
1638         {
1639             fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n",
1640                     (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText,
1641                     (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int)(chunk.end - chunk.start));
1642 
1643             Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
1644             Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
1645 
1646             unsigned int i = 0;
1647             for (; boxIt != boxEnd; ++boxIt) {
1648                 SVGInlineBoxCharacterRange &range = *boxIt; i++;
1649                 fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box);
1650             }
1651         }
1652 #endif
1653 
1654         if (chunk.isTextPath) {
1655             continue;
1656         }
1657 
1658         // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts.
1659         applyTextLengthCorrectionToTextChunk(chunk);
1660 
1661         // text-anchor is already handled for textPath layouts.
1662         applyTextAnchorToTextChunk(chunk);
1663     }
1664 }
1665 
1666 static inline void addPaintServerToTextDecorationInfo(ETextDecoration decoration, SVGTextDecorationInfo &info, RenderObject *object)
1667 {
1668     if (object->style()->svgStyle()->hasFill()) {
1669         info.fillServerMap.set(decoration, object);
1670     }
1671 
1672     if (object->style()->svgStyle()->hasStroke()) {
1673         info.strokeServerMap.set(decoration, object);
1674     }
1675 }
1676 
1677 SVGTextDecorationInfo SVGRootInlineBox::retrievePaintServersForTextDecoration(RenderObject *start)
1678 {
1679     ASSERT(start);
1680 
1681     Vector<RenderObject *> parentChain;
1682     while ((start = start->parent())) {
1683         parentChain.prepend(start);
1684 
1685         // Stop at our direct <text> parent.
1686         if (start->isSVGText()) {
1687             break;
1688         }
1689     }
1690 
1691     Vector<RenderObject *>::iterator it = parentChain.begin();
1692     Vector<RenderObject *>::iterator end = parentChain.end();
1693 
1694     SVGTextDecorationInfo info;
1695 
1696     for (; it != end; ++it) {
1697         RenderObject *object = *it;
1698         ASSERT(object);
1699 
1700         RenderStyle *style = object->style();
1701         ASSERT(style);
1702 
1703         int decorations = style->textDecoration();
1704         if (decorations != NONE) {
1705             if (decorations & OVERLINE) {
1706                 addPaintServerToTextDecorationInfo(OVERLINE, info, object);
1707             }
1708 
1709             if (decorations & UNDERLINE) {
1710                 addPaintServerToTextDecorationInfo(UNDERLINE, info, object);
1711             }
1712 
1713             if (decorations & LINE_THROUGH) {
1714                 addPaintServerToTextDecorationInfo(LINE_THROUGH, info, object);
1715             }
1716         }
1717     }
1718 
1719     return info;
1720 }
1721 
1722 void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase *walker, const SVGInlineTextBox *textBox)
1723 {
1724     ASSERT(walker);
1725 
1726     Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
1727     Vector<SVGTextChunk>::iterator itEnd = m_svgTextChunks.end();
1728 
1729     for (; it != itEnd; ++it) {
1730         SVGTextChunk &curChunk = *it;
1731 
1732         Vector<SVGInlineBoxCharacterRange>::iterator boxIt = curChunk.boxes.begin();
1733         Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = curChunk.boxes.end();
1734 
1735         InlineBox *lastNotifiedBox = nullptr;
1736         InlineBox *prevBox = nullptr;
1737 
1738         unsigned int chunkOffset = 0;
1739         bool startedFirstChunk = false;
1740 
1741         for (; boxIt != boxEnd; ++boxIt) {
1742             SVGInlineBoxCharacterRange &range = *boxIt;
1743 
1744             ASSERT(range.box->isInlineTextBox());
1745             SVGInlineTextBox *rangeTextBox = static_cast<SVGInlineTextBox *>(range.box);
1746 
1747             if (textBox && rangeTextBox != textBox) {
1748                 chunkOffset += range.endOffset - range.startOffset;
1749                 continue;
1750             }
1751 
1752             // Eventually notify that we started a new chunk
1753             if (!textBox && !startedFirstChunk) {
1754                 startedFirstChunk = true;
1755 
1756                 lastNotifiedBox = range.box;
1757                 walker->start(range.box);
1758             } else {
1759                 // Eventually apply new style, as this chunk spans multiple boxes (with possible different styling)
1760                 if (prevBox && prevBox != range.box) {
1761                     lastNotifiedBox = range.box;
1762 
1763                     walker->end(prevBox);
1764                     walker->start(lastNotifiedBox);
1765                 }
1766             }
1767 
1768             unsigned int length = range.endOffset - range.startOffset;
1769 
1770             Vector<SVGChar>::iterator itCharBegin = curChunk.start + chunkOffset;
1771             Vector<SVGChar>::iterator itCharEnd = curChunk.start + chunkOffset + length;
1772             ASSERT(itCharEnd <= curChunk.end);
1773 
1774             // Process this chunk portion
1775             if (textBox) {
1776                 (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1777             } else {
1778                 if (walker->setupFill(range.box)) {
1779                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1780                 }
1781 
1782                 if (walker->setupStroke(range.box)) {
1783                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1784                 }
1785             }
1786 
1787             chunkOffset += length;
1788 
1789             if (!textBox) {
1790                 prevBox = range.box;
1791             }
1792         }
1793 
1794         if (!textBox && startedFirstChunk) {
1795             walker->end(lastNotifiedBox);
1796         }
1797     }
1798 }
1799 
1800 } // namespace WebCore
1801 
1802 #endif // ENABLE(SVG)