Home | History | Annotate | Download | only in svg
      1 /**
      2  * Copyright (C) 2007 Rob Buis <buis (at) kde.org>
      3  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      4  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Library General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Library General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Library General Public License
     17  * along with this library; see the file COPYING.LIB.  If not, write to
     18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19  * Boston, MA 02110-1301, USA.
     20  */
     21 
     22 #include "config.h"
     23 #include "core/rendering/svg/SVGInlineTextBox.h"
     24 
     25 #include "core/frame/Frame.h"
     26 #include "core/frame/FrameView.h"
     27 #include "core/rendering/HitTestResult.h"
     28 #include "core/rendering/InlineFlowBox.h"
     29 #include "core/rendering/PointerEventsHitRules.h"
     30 #include "core/rendering/style/ShadowList.h"
     31 #include "core/rendering/svg/RenderSVGInlineText.h"
     32 #include "core/rendering/svg/RenderSVGResource.h"
     33 #include "core/rendering/svg/RenderSVGResourceSolidColor.h"
     34 #include "core/rendering/svg/SVGResourcesCache.h"
     35 #include "core/rendering/svg/SVGTextRunRenderingContext.h"
     36 #include "platform/FloatConversion.h"
     37 #include "platform/fonts/FontCache.h"
     38 #include "platform/graphics/DrawLooper.h"
     39 #include "platform/graphics/GraphicsContextStateSaver.h"
     40 
     41 using namespace std;
     42 
     43 namespace WebCore {
     44 
     45 struct ExpectedSVGInlineTextBoxSize : public InlineTextBox {
     46     float float1;
     47     uint32_t bitfields : 5;
     48     void* pointer;
     49     Vector<SVGTextFragment> vector;
     50 };
     51 
     52 COMPILE_ASSERT(sizeof(SVGInlineTextBox) == sizeof(ExpectedSVGInlineTextBoxSize), SVGInlineTextBox_is_not_of_expected_size);
     53 
     54 SVGInlineTextBox::SVGInlineTextBox(RenderObject* object)
     55     : InlineTextBox(object)
     56     , m_logicalHeight(0)
     57     , m_paintingResourceMode(ApplyToDefaultMode)
     58     , m_startsNewTextChunk(false)
     59     , m_paintingResource(0)
     60 {
     61 }
     62 
     63 void SVGInlineTextBox::dirtyLineBoxes()
     64 {
     65     InlineTextBox::dirtyLineBoxes();
     66 
     67     // Clear the now stale text fragments
     68     clearTextFragments();
     69 
     70     // And clear any following text fragments as the text on which they
     71     // depend may now no longer exist, or glyph positions may be wrong
     72     InlineTextBox* nextBox = nextTextBox();
     73     if (nextBox)
     74         nextBox->dirtyLineBoxes();
     75 }
     76 
     77 int SVGInlineTextBox::offsetForPosition(float, bool) const
     78 {
     79     // SVG doesn't use the standard offset <-> position selection system, as it's not suitable for SVGs complex needs.
     80     // vertical text selection, inline boxes spanning multiple lines (contrary to HTML, etc.)
     81     ASSERT_NOT_REACHED();
     82     return 0;
     83 }
     84 
     85 int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, float position, bool includePartialGlyphs) const
     86 {
     87     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
     88     ASSERT(textRenderer);
     89 
     90     float scalingFactor = textRenderer->scalingFactor();
     91     ASSERT(scalingFactor);
     92 
     93     RenderStyle* style = textRenderer->style();
     94     ASSERT(style);
     95 
     96     TextRun textRun = constructTextRun(style, fragment);
     97 
     98     // Eventually handle lengthAdjust="spacingAndGlyphs".
     99     // FIXME: Handle vertical text.
    100     AffineTransform fragmentTransform;
    101     fragment.buildFragmentTransform(fragmentTransform);
    102     if (!fragmentTransform.isIdentity())
    103         textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragmentTransform.xScale()));
    104 
    105     return fragment.characterOffset - start() + textRenderer->scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs);
    106 }
    107 
    108 float SVGInlineTextBox::positionForOffset(int) const
    109 {
    110     // SVG doesn't use the offset <-> position selection system.
    111     ASSERT_NOT_REACHED();
    112     return 0;
    113 }
    114 
    115 FloatRect SVGInlineTextBox::selectionRectForTextFragment(const SVGTextFragment& fragment, int startPosition, int endPosition, RenderStyle* style)
    116 {
    117     ASSERT(startPosition < endPosition);
    118     ASSERT(style);
    119 
    120     FontCachePurgePreventer fontCachePurgePreventer;
    121 
    122     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
    123     ASSERT(textRenderer);
    124 
    125     float scalingFactor = textRenderer->scalingFactor();
    126     ASSERT(scalingFactor);
    127 
    128     const Font& scaledFont = textRenderer->scaledFont();
    129     const FontMetrics& scaledFontMetrics = scaledFont.fontMetrics();
    130     FloatPoint textOrigin(fragment.x, fragment.y);
    131     if (scalingFactor != 1)
    132         textOrigin.scale(scalingFactor, scalingFactor);
    133 
    134     textOrigin.move(0, -scaledFontMetrics.floatAscent());
    135 
    136     FloatRect selectionRect = scaledFont.selectionRectForText(constructTextRun(style, fragment), textOrigin, fragment.height * scalingFactor, startPosition, endPosition);
    137     if (scalingFactor == 1)
    138         return selectionRect;
    139 
    140     selectionRect.scale(1 / scalingFactor);
    141     return selectionRect;
    142 }
    143 
    144 LayoutRect SVGInlineTextBox::localSelectionRect(int startPosition, int endPosition)
    145 {
    146     int boxStart = start();
    147     startPosition = max(startPosition - boxStart, 0);
    148     endPosition = min(endPosition - boxStart, static_cast<int>(len()));
    149     if (startPosition >= endPosition)
    150         return LayoutRect();
    151 
    152     RenderText* text = textRenderer();
    153     ASSERT(text);
    154 
    155     RenderStyle* style = text->style();
    156     ASSERT(style);
    157 
    158     AffineTransform fragmentTransform;
    159     FloatRect selectionRect;
    160     int fragmentStartPosition = 0;
    161     int fragmentEndPosition = 0;
    162 
    163     unsigned textFragmentsSize = m_textFragments.size();
    164     for (unsigned i = 0; i < textFragmentsSize; ++i) {
    165         const SVGTextFragment& fragment = m_textFragments.at(i);
    166 
    167         fragmentStartPosition = startPosition;
    168         fragmentEndPosition = endPosition;
    169         if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
    170             continue;
    171 
    172         FloatRect fragmentRect = selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style);
    173         fragment.buildFragmentTransform(fragmentTransform);
    174         if (!fragmentTransform.isIdentity())
    175             fragmentRect = fragmentTransform.mapRect(fragmentRect);
    176 
    177         selectionRect.unite(fragmentRect);
    178     }
    179 
    180     return enclosingIntRect(selectionRect);
    181 }
    182 
    183 static inline bool textShouldBePainted(RenderSVGInlineText* textRenderer)
    184 {
    185     // Font::pixelSize(), returns FontDescription::computedPixelSize(), which returns "int(x + 0.5)".
    186     // If the absolute font size on screen is below x=0.5, don't render anything.
    187     return textRenderer->scaledFont().pixelSize();
    188 }
    189 
    190 void SVGInlineTextBox::paintSelectionBackground(PaintInfo& paintInfo)
    191 {
    192     ASSERT(paintInfo.shouldPaintWithinRoot(renderer()));
    193     ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
    194     ASSERT(truncation() == cNoTruncation);
    195 
    196     if (renderer()->style()->visibility() != VISIBLE)
    197         return;
    198 
    199     RenderObject* parentRenderer = parent()->renderer();
    200     ASSERT(parentRenderer);
    201     ASSERT(!parentRenderer->document().printing());
    202 
    203     // Determine whether or not we're selected.
    204     bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection;
    205     bool hasSelection = selectionState() != RenderObject::SelectionNone;
    206     if (!hasSelection || paintSelectedTextOnly)
    207         return;
    208 
    209     Color backgroundColor = renderer()->selectionBackgroundColor();
    210     if (!backgroundColor.isValid() || !backgroundColor.alpha())
    211         return;
    212 
    213     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
    214     ASSERT(textRenderer);
    215     if (!textShouldBePainted(textRenderer))
    216         return;
    217 
    218     RenderStyle* style = parentRenderer->style();
    219     ASSERT(style);
    220 
    221     RenderStyle* selectionStyle = style;
    222     if (hasSelection) {
    223         selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION);
    224         if (!selectionStyle)
    225             selectionStyle = style;
    226     }
    227 
    228     int startPosition, endPosition;
    229     selectionStartEnd(startPosition, endPosition);
    230 
    231     int fragmentStartPosition = 0;
    232     int fragmentEndPosition = 0;
    233     AffineTransform fragmentTransform;
    234     unsigned textFragmentsSize = m_textFragments.size();
    235     for (unsigned i = 0; i < textFragmentsSize; ++i) {
    236         SVGTextFragment& fragment = m_textFragments.at(i);
    237         ASSERT(!m_paintingResource);
    238 
    239         fragmentStartPosition = startPosition;
    240         fragmentEndPosition = endPosition;
    241         if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
    242             continue;
    243 
    244         GraphicsContextStateSaver stateSaver(*paintInfo.context);
    245         fragment.buildFragmentTransform(fragmentTransform);
    246         if (!fragmentTransform.isIdentity())
    247             paintInfo.context->concatCTM(fragmentTransform);
    248 
    249         paintInfo.context->setFillColor(backgroundColor);
    250         paintInfo.context->fillRect(selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style), backgroundColor);
    251 
    252         m_paintingResourceMode = ApplyToDefaultMode;
    253     }
    254 
    255     ASSERT(!m_paintingResource);
    256 }
    257 
    258 void SVGInlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint&, LayoutUnit, LayoutUnit)
    259 {
    260     ASSERT(paintInfo.shouldPaintWithinRoot(renderer()));
    261     ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
    262     ASSERT(truncation() == cNoTruncation);
    263 
    264     if (renderer()->style()->visibility() != VISIBLE)
    265         return;
    266 
    267     // Note: We're explicitely not supporting composition & custom underlines and custom highlighters - unlike InlineTextBox.
    268     // If we ever need that for SVG, it's very easy to refactor and reuse the code.
    269 
    270     RenderObject* parentRenderer = parent()->renderer();
    271     ASSERT(parentRenderer);
    272 
    273     bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection;
    274     bool hasSelection = !parentRenderer->document().printing() && selectionState() != RenderObject::SelectionNone;
    275     if (!hasSelection && paintSelectedTextOnly)
    276         return;
    277 
    278     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
    279     ASSERT(textRenderer);
    280     if (!textShouldBePainted(textRenderer))
    281         return;
    282 
    283     RenderStyle* style = parentRenderer->style();
    284     ASSERT(style);
    285 
    286     const SVGRenderStyle* svgStyle = style->svgStyle();
    287     ASSERT(svgStyle);
    288 
    289     bool hasFill = svgStyle->hasFill();
    290     bool hasVisibleStroke = svgStyle->hasVisibleStroke();
    291 
    292     RenderStyle* selectionStyle = style;
    293     if (hasSelection) {
    294         selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION);
    295         if (selectionStyle) {
    296             const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle();
    297             ASSERT(svgSelectionStyle);
    298 
    299             if (!hasFill)
    300                 hasFill = svgSelectionStyle->hasFill();
    301             if (!hasVisibleStroke)
    302                 hasVisibleStroke = svgSelectionStyle->hasVisibleStroke();
    303         } else
    304             selectionStyle = style;
    305     }
    306 
    307     if (textRenderer->frame() && textRenderer->frame()->view() && textRenderer->frame()->view()->paintBehavior() & PaintBehaviorRenderingSVGMask) {
    308         hasFill = true;
    309         hasVisibleStroke = false;
    310     }
    311 
    312     AffineTransform fragmentTransform;
    313     unsigned textFragmentsSize = m_textFragments.size();
    314     for (unsigned i = 0; i < textFragmentsSize; ++i) {
    315         SVGTextFragment& fragment = m_textFragments.at(i);
    316         ASSERT(!m_paintingResource);
    317 
    318         GraphicsContextStateSaver stateSaver(*paintInfo.context);
    319         fragment.buildFragmentTransform(fragmentTransform);
    320         if (!fragmentTransform.isIdentity())
    321             paintInfo.context->concatCTM(fragmentTransform);
    322 
    323         // Spec: All text decorations except line-through should be drawn before the text is filled and stroked; thus, the text is rendered on top of these decorations.
    324         unsigned decorations = style->textDecorationsInEffect();
    325         if (decorations & TextDecorationUnderline)
    326             paintDecoration(paintInfo.context, TextDecorationUnderline, fragment);
    327         if (decorations & TextDecorationOverline)
    328             paintDecoration(paintInfo.context, TextDecorationOverline, fragment);
    329 
    330         for (int i = 0; i < 3; i++) {
    331             switch (svgStyle->paintOrderType(i)) {
    332             case PT_FILL:
    333                 // Fill text
    334                 if (hasFill) {
    335                     m_paintingResourceMode = ApplyToFillMode | ApplyToTextMode;
    336                     paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly);
    337                 }
    338                 break;
    339             case PT_STROKE:
    340                 // Stroke text
    341                 if (hasVisibleStroke) {
    342                     m_paintingResourceMode = ApplyToStrokeMode | ApplyToTextMode;
    343                     paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly);
    344                 }
    345                 break;
    346             case PT_MARKERS:
    347                 // Markers don't apply to text
    348                 break;
    349             default:
    350                 ASSERT_NOT_REACHED();
    351                 break;
    352             }
    353         }
    354 
    355         // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text.
    356         if (decorations & TextDecorationLineThrough)
    357             paintDecoration(paintInfo.context, TextDecorationLineThrough, fragment);
    358 
    359         m_paintingResourceMode = ApplyToDefaultMode;
    360     }
    361 
    362     ASSERT(!m_paintingResource);
    363 }
    364 
    365 bool SVGInlineTextBox::acquirePaintingResource(GraphicsContext*& context, float scalingFactor, RenderObject* renderer, RenderStyle* style)
    366 {
    367     ASSERT(scalingFactor);
    368     ASSERT(renderer);
    369     ASSERT(style);
    370     ASSERT(m_paintingResourceMode != ApplyToDefaultMode);
    371 
    372     Color fallbackColor;
    373     if (m_paintingResourceMode & ApplyToFillMode)
    374         m_paintingResource = RenderSVGResource::fillPaintingResource(renderer, style, fallbackColor);
    375     else if (m_paintingResourceMode & ApplyToStrokeMode)
    376         m_paintingResource = RenderSVGResource::strokePaintingResource(renderer, style, fallbackColor);
    377     else {
    378         // We're either called for stroking or filling.
    379         ASSERT_NOT_REACHED();
    380     }
    381 
    382     if (!m_paintingResource)
    383         return false;
    384 
    385     if (!m_paintingResource->applyResource(renderer, style, context, m_paintingResourceMode)) {
    386         if (fallbackColor.isValid()) {
    387             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
    388             fallbackResource->setColor(fallbackColor);
    389 
    390             m_paintingResource = fallbackResource;
    391             m_paintingResource->applyResource(renderer, style, context, m_paintingResourceMode);
    392         }
    393     }
    394 
    395     if (scalingFactor != 1 && m_paintingResourceMode & ApplyToStrokeMode)
    396         context->setStrokeThickness(context->strokeThickness() * scalingFactor);
    397 
    398     return true;
    399 }
    400 
    401 void SVGInlineTextBox::releasePaintingResource(GraphicsContext*& context, const Path* path)
    402 {
    403     ASSERT(m_paintingResource);
    404 
    405     RenderObject* parentRenderer = parent()->renderer();
    406     ASSERT(parentRenderer);
    407 
    408     m_paintingResource->postApplyResource(parentRenderer, context, m_paintingResourceMode, path, /*RenderSVGShape*/ 0);
    409     m_paintingResource = 0;
    410 }
    411 
    412 bool SVGInlineTextBox::prepareGraphicsContextForTextPainting(GraphicsContext*& context, float scalingFactor, TextRun& textRun, RenderStyle* style)
    413 {
    414     bool acquiredResource = acquirePaintingResource(context, scalingFactor, parent()->renderer(), style);
    415     if (!acquiredResource)
    416         return false;
    417 
    418 #if ENABLE(SVG_FONTS)
    419     // SVG Fonts need access to the painting resource used to draw the current text chunk.
    420     TextRun::RenderingContext* renderingContext = textRun.renderingContext();
    421     if (renderingContext)
    422         static_cast<SVGTextRunRenderingContext*>(renderingContext)->setActivePaintingResource(m_paintingResource);
    423 #endif
    424 
    425     return true;
    426 }
    427 
    428 void SVGInlineTextBox::restoreGraphicsContextAfterTextPainting(GraphicsContext*& context, TextRun& textRun)
    429 {
    430     releasePaintingResource(context, /* path */0);
    431 
    432 #if ENABLE(SVG_FONTS)
    433     TextRun::RenderingContext* renderingContext = textRun.renderingContext();
    434     if (renderingContext)
    435         static_cast<SVGTextRunRenderingContext*>(renderingContext)->setActivePaintingResource(0);
    436 #endif
    437 }
    438 
    439 TextRun SVGInlineTextBox::constructTextRun(RenderStyle* style, const SVGTextFragment& fragment) const
    440 {
    441     ASSERT(style);
    442     ASSERT(textRenderer());
    443 
    444     RenderText* text = textRenderer();
    445     ASSERT(text);
    446 
    447     // FIXME(crbug.com/264211): This should not be necessary but can occur if we
    448     //                          layout during layout. Remove this when 264211 is fixed.
    449     RELEASE_ASSERT(!text->needsLayout());
    450 
    451     TextRun run(static_cast<const LChar*>(0) // characters, will be set below if non-zero.
    452                 , 0 // length, will be set below if non-zero.
    453                 , 0 // xPos, only relevant with allowTabs=true
    454                 , 0 // padding, only relevant for justified text, not relevant for SVG
    455                 , TextRun::AllowTrailingExpansion
    456                 , direction()
    457                 , dirOverride() || style->rtlOrdering() == VisualOrder /* directionalOverride */);
    458 
    459     if (fragment.length) {
    460         if (text->is8Bit())
    461             run.setText(text->characters8() + fragment.characterOffset, fragment.length);
    462         else
    463             run.setText(text->characters16() + fragment.characterOffset, fragment.length);
    464     }
    465 
    466     if (textRunNeedsRenderingContext(style->font()))
    467         run.setRenderingContext(SVGTextRunRenderingContext::create(text));
    468 
    469     run.disableRoundingHacks();
    470 
    471     // We handle letter & word spacing ourselves.
    472     run.disableSpacing();
    473 
    474     // Propagate the maximum length of the characters buffer to the TextRun, even when we're only processing a substring.
    475     run.setCharactersLength(text->textLength() - fragment.characterOffset);
    476     ASSERT(run.charactersLength() >= run.length());
    477     return run;
    478 }
    479 
    480 bool SVGInlineTextBox::mapStartEndPositionsIntoFragmentCoordinates(const SVGTextFragment& fragment, int& startPosition, int& endPosition) const
    481 {
    482     if (startPosition >= endPosition)
    483         return false;
    484 
    485     int offset = static_cast<int>(fragment.characterOffset) - start();
    486     int length = static_cast<int>(fragment.length);
    487 
    488     if (startPosition >= offset + length || endPosition <= offset)
    489         return false;
    490 
    491     if (startPosition < offset)
    492         startPosition = 0;
    493     else
    494         startPosition -= offset;
    495 
    496     if (endPosition > offset + length)
    497         endPosition = length;
    498     else {
    499         ASSERT(endPosition >= offset);
    500         endPosition -= offset;
    501     }
    502 
    503     ASSERT(startPosition < endPosition);
    504     return true;
    505 }
    506 
    507 static inline float positionOffsetForDecoration(TextDecoration decoration, const FontMetrics& fontMetrics, float thickness)
    508 {
    509     // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified.
    510     // Compatible with Batik/Opera.
    511     if (decoration == TextDecorationUnderline)
    512         return fontMetrics.floatAscent() + thickness * 1.5f;
    513     if (decoration == TextDecorationOverline)
    514         return thickness;
    515     if (decoration == TextDecorationLineThrough)
    516         return fontMetrics.floatAscent() * 5 / 8.0f;
    517 
    518     ASSERT_NOT_REACHED();
    519     return 0.0f;
    520 }
    521 
    522 static inline float thicknessForDecoration(TextDecoration, const Font& font)
    523 {
    524     // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified.
    525     // Compatible with Batik/Opera
    526     return font.size() / 20.0f;
    527 }
    528 
    529 static inline RenderObject* findRenderObjectDefininingTextDecoration(InlineFlowBox* parentBox)
    530 {
    531     // Lookup first render object in parent hierarchy which has text-decoration set.
    532     RenderObject* renderer = 0;
    533     while (parentBox) {
    534         renderer = parentBox->renderer();
    535 
    536         if (renderer->style() && renderer->style()->textDecoration() != TextDecorationNone)
    537             break;
    538 
    539         parentBox = parentBox->parent();
    540     }
    541 
    542     ASSERT(renderer);
    543     return renderer;
    544 }
    545 
    546 void SVGInlineTextBox::paintDecoration(GraphicsContext* context, TextDecoration decoration, const SVGTextFragment& fragment)
    547 {
    548     if (textRenderer()->style()->textDecorationsInEffect() == TextDecorationNone)
    549         return;
    550 
    551     // Find out which render style defined the text-decoration, as its fill/stroke properties have to be used for drawing instead of ours.
    552     RenderObject* decorationRenderer = findRenderObjectDefininingTextDecoration(parent());
    553     RenderStyle* decorationStyle = decorationRenderer->style();
    554     ASSERT(decorationStyle);
    555 
    556     if (decorationStyle->visibility() == HIDDEN)
    557         return;
    558 
    559     const SVGRenderStyle* svgDecorationStyle = decorationStyle->svgStyle();
    560     ASSERT(svgDecorationStyle);
    561 
    562     bool hasDecorationFill = svgDecorationStyle->hasFill();
    563     bool hasVisibleDecorationStroke = svgDecorationStyle->hasVisibleStroke();
    564 
    565     if (hasDecorationFill) {
    566         m_paintingResourceMode = ApplyToFillMode;
    567         paintDecorationWithStyle(context, decoration, fragment, decorationRenderer);
    568     }
    569 
    570     if (hasVisibleDecorationStroke) {
    571         m_paintingResourceMode = ApplyToStrokeMode;
    572         paintDecorationWithStyle(context, decoration, fragment, decorationRenderer);
    573     }
    574 }
    575 
    576 void SVGInlineTextBox::paintDecorationWithStyle(GraphicsContext* context, TextDecoration decoration, const SVGTextFragment& fragment, RenderObject* decorationRenderer)
    577 {
    578     ASSERT(!m_paintingResource);
    579     ASSERT(m_paintingResourceMode != ApplyToDefaultMode);
    580 
    581     RenderStyle* decorationStyle = decorationRenderer->style();
    582     ASSERT(decorationStyle);
    583 
    584     float scalingFactor = 1;
    585     Font scaledFont;
    586     RenderSVGInlineText::computeNewScaledFontForStyle(decorationRenderer, decorationStyle, scalingFactor, scaledFont);
    587     ASSERT(scalingFactor);
    588 
    589     // The initial y value refers to overline position.
    590     float thickness = thicknessForDecoration(decoration, scaledFont);
    591 
    592     if (fragment.width <= 0 && thickness <= 0)
    593         return;
    594 
    595     FloatPoint decorationOrigin(fragment.x, fragment.y);
    596     float width = fragment.width;
    597     const FontMetrics& scaledFontMetrics = scaledFont.fontMetrics();
    598 
    599     GraphicsContextStateSaver stateSaver(*context);
    600     if (scalingFactor != 1) {
    601         width *= scalingFactor;
    602         decorationOrigin.scale(scalingFactor, scalingFactor);
    603         context->scale(FloatSize(1 / scalingFactor, 1 / scalingFactor));
    604     }
    605 
    606     decorationOrigin.move(0, -scaledFontMetrics.floatAscent() + positionOffsetForDecoration(decoration, scaledFontMetrics, thickness));
    607 
    608     Path path;
    609     path.addRect(FloatRect(decorationOrigin, FloatSize(width, thickness)));
    610 
    611     if (acquirePaintingResource(context, scalingFactor, decorationRenderer, decorationStyle))
    612         releasePaintingResource(context, &path);
    613 }
    614 
    615 void SVGInlineTextBox::paintTextWithShadows(GraphicsContext* context, RenderStyle* style, TextRun& textRun, const SVGTextFragment& fragment, int startPosition, int endPosition)
    616 {
    617     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
    618     ASSERT(textRenderer);
    619 
    620     float scalingFactor = textRenderer->scalingFactor();
    621     ASSERT(scalingFactor);
    622 
    623     const Font& scaledFont = textRenderer->scaledFont();
    624     const ShadowList* shadowList = style->textShadow();
    625 
    626     // Text shadows are disabled when printing. http://crbug.com/258321
    627     bool hasShadow = shadowList && !context->printing();
    628 
    629     FloatPoint textOrigin(fragment.x, fragment.y);
    630     FloatSize textSize(fragment.width, fragment.height);
    631 
    632     if (scalingFactor != 1) {
    633         textOrigin.scale(scalingFactor, scalingFactor);
    634         textSize.scale(scalingFactor);
    635         context->save();
    636         context->scale(FloatSize(1 / scalingFactor, 1 / scalingFactor));
    637     }
    638 
    639     if (hasShadow) {
    640         DrawLooper drawLooper;
    641         for (size_t i = shadowList->shadows().size(); i--; ) {
    642             const ShadowData& shadow = shadowList->shadows()[i];
    643             FloatSize offset(shadow.x(), shadow.y());
    644             drawLooper.addShadow(offset, shadow.blur(), shadow.color(),
    645                 DrawLooper::ShadowRespectsTransforms, DrawLooper::ShadowRespectsAlpha);
    646         }
    647         drawLooper.addUnmodifiedContent();
    648         context->setDrawLooper(drawLooper);
    649     }
    650 
    651     if (prepareGraphicsContextForTextPainting(context, scalingFactor, textRun, style)) {
    652         TextRunPaintInfo textRunPaintInfo(textRun);
    653         textRunPaintInfo.from = startPosition;
    654         textRunPaintInfo.to = endPosition;
    655         textRunPaintInfo.bounds = FloatRect(textOrigin, textSize);
    656         scaledFont.drawText(context, textRunPaintInfo, textOrigin);
    657         restoreGraphicsContextAfterTextPainting(context, textRun);
    658     }
    659 
    660     if (scalingFactor != 1)
    661         context->restore();
    662     else if (hasShadow)
    663         context->clearShadow();
    664 }
    665 
    666 void SVGInlineTextBox::paintText(GraphicsContext* context, RenderStyle* style, RenderStyle* selectionStyle, const SVGTextFragment& fragment, bool hasSelection, bool paintSelectedTextOnly)
    667 {
    668     ASSERT(style);
    669     ASSERT(selectionStyle);
    670 
    671     int startPosition = 0;
    672     int endPosition = 0;
    673     if (hasSelection) {
    674         selectionStartEnd(startPosition, endPosition);
    675         hasSelection = mapStartEndPositionsIntoFragmentCoordinates(fragment, startPosition, endPosition);
    676     }
    677 
    678     // Fast path if there is no selection, just draw the whole chunk part using the regular style
    679     TextRun textRun = constructTextRun(style, fragment);
    680     if (!hasSelection || startPosition >= endPosition) {
    681         paintTextWithShadows(context, style, textRun, fragment, 0, fragment.length);
    682         return;
    683     }
    684 
    685     // Eventually draw text using regular style until the start position of the selection
    686     if (startPosition > 0 && !paintSelectedTextOnly)
    687         paintTextWithShadows(context, style, textRun, fragment, 0, startPosition);
    688 
    689     // Draw text using selection style from the start to the end position of the selection
    690     if (style != selectionStyle)
    691         SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, selectionStyle);
    692 
    693     TextRun selectionTextRun = constructTextRun(selectionStyle, fragment);
    694     paintTextWithShadows(context, selectionStyle, textRun, fragment, startPosition, endPosition);
    695 
    696     if (style != selectionStyle)
    697         SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, style);
    698 
    699     // Eventually draw text using regular style from the end position of the selection to the end of the current chunk part
    700     if (endPosition < static_cast<int>(fragment.length) && !paintSelectedTextOnly)
    701         paintTextWithShadows(context, style, textRun, fragment, endPosition, fragment.length);
    702 }
    703 
    704 FloatRect SVGInlineTextBox::calculateBoundaries() const
    705 {
    706     FloatRect textRect;
    707 
    708     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
    709     ASSERT(textRenderer);
    710 
    711     float scalingFactor = textRenderer->scalingFactor();
    712     ASSERT(scalingFactor);
    713 
    714     float baseline = textRenderer->scaledFont().fontMetrics().floatAscent() / scalingFactor;
    715 
    716     AffineTransform fragmentTransform;
    717     unsigned textFragmentsSize = m_textFragments.size();
    718     for (unsigned i = 0; i < textFragmentsSize; ++i) {
    719         const SVGTextFragment& fragment = m_textFragments.at(i);
    720         FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height);
    721         fragment.buildFragmentTransform(fragmentTransform);
    722         if (!fragmentTransform.isIdentity())
    723             fragmentRect = fragmentTransform.mapRect(fragmentRect);
    724 
    725         textRect.unite(fragmentRect);
    726     }
    727 
    728     return textRect;
    729 }
    730 
    731 bool SVGInlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit, LayoutUnit)
    732 {
    733     // FIXME: integrate with InlineTextBox::nodeAtPoint better.
    734     ASSERT(!isLineBreak());
    735 
    736     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, request, renderer()->style()->pointerEvents());
    737     bool isVisible = renderer()->style()->visibility() == VISIBLE;
    738     if (isVisible || !hitRules.requireVisible) {
    739         if (hitRules.canHitBoundingBox
    740             || (hitRules.canHitStroke && (renderer()->style()->svgStyle()->hasStroke() || !hitRules.requireStroke))
    741             || (hitRules.canHitFill && (renderer()->style()->svgStyle()->hasFill() || !hitRules.requireFill))) {
    742             FloatPoint boxOrigin(x(), y());
    743             boxOrigin.moveBy(accumulatedOffset);
    744             FloatRect rect(boxOrigin, size());
    745             if (locationInContainer.intersects(rect)) {
    746                 renderer()->updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
    747                 if (!result.addNodeToRectBasedTestResult(renderer()->node(), request, locationInContainer, rect))
    748                     return true;
    749              }
    750         }
    751     }
    752     return false;
    753 }
    754 
    755 } // namespace WebCore
    756