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