Remove unnecessary #include SVGResourcesCache.h in SVGDocumentExtensions.h; use forwa...
[WebKit-https.git] / Source / WebCore / rendering / svg / SVGInlineTextBox.cpp
1 /**
2  * Copyright (C) 2007 Rob Buis <buis@kde.org>
3  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@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 "SVGInlineTextBox.h"
24
25 #if ENABLE(SVG)
26 #include "FloatConversion.h"
27 #include "FontCache.h"
28 #include "GraphicsContext.h"
29 #include "HitTestResult.h"
30 #include "InlineFlowBox.h"
31 #include "PointerEventsHitRules.h"
32 #include "RenderBlock.h"
33 #include "RenderSVGInlineText.h"
34 #include "RenderSVGResource.h"
35 #include "RenderSVGResourceSolidColor.h"
36 #include "SVGImageBufferTools.h"
37 #include "SVGResourcesCache.h"
38 #include "SVGRootInlineBox.h"
39 #include "SVGTextRunRenderingContext.h"
40
41 using namespace std;
42
43 namespace WebCore {
44
45 SVGInlineTextBox::SVGInlineTextBox(RenderObject* object)
46     : InlineTextBox(object)
47     , m_logicalHeight(0)
48     , m_paintingResourceMode(ApplyToDefaultMode)
49     , m_startsNewTextChunk(false)
50     , m_paintingResource(0)
51 {
52 }
53
54 int SVGInlineTextBox::offsetForPosition(float, bool) const
55 {
56     // SVG doesn't use the standard offset <-> position selection system, as it's not suitable for SVGs complex needs.
57     // vertical text selection, inline boxes spanning multiple lines (contrary to HTML, etc.)
58     ASSERT_NOT_REACHED();
59     return 0;
60 }
61
62 int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, float position, bool includePartialGlyphs) const
63 {
64     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
65     ASSERT(textRenderer);
66
67     float scalingFactor = textRenderer->scalingFactor();
68     ASSERT(scalingFactor);
69
70     RenderStyle* style = textRenderer->style();
71     ASSERT(style);
72
73     TextRun textRun = constructTextRun(style, fragment);
74
75     // Eventually handle lengthAdjust="spacingAndGlyphs".
76     // FIXME: Handle vertical text.
77     AffineTransform fragmentTransform;
78     fragment.buildFragmentTransform(fragmentTransform);
79     if (!fragmentTransform.isIdentity())
80         textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragmentTransform.xScale()));
81
82     return fragment.characterOffset - start() + textRenderer->scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs);
83 }
84
85 float SVGInlineTextBox::positionForOffset(int) const
86 {
87     // SVG doesn't use the offset <-> position selection system. 
88     ASSERT_NOT_REACHED();
89     return 0;
90 }
91
92 FloatRect SVGInlineTextBox::selectionRectForTextFragment(const SVGTextFragment& fragment, int startPosition, int endPosition, RenderStyle* style)
93 {
94     ASSERT(startPosition < endPosition);
95     ASSERT(style);
96
97     FontCachePurgePreventer fontCachePurgePreventer;
98
99     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
100     ASSERT(textRenderer);
101
102     float scalingFactor = textRenderer->scalingFactor();
103     ASSERT(scalingFactor);
104
105     const Font& scaledFont = textRenderer->scaledFont();
106     const FontMetrics& scaledFontMetrics = scaledFont.fontMetrics();
107     FloatPoint textOrigin(fragment.x, fragment.y);
108     if (scalingFactor != 1)
109         textOrigin.scale(scalingFactor, scalingFactor);
110
111     textOrigin.move(0, -scaledFontMetrics.floatAscent());
112
113     FloatRect selectionRect = scaledFont.selectionRectForText(constructTextRun(style, fragment), textOrigin, fragment.height * scalingFactor, startPosition, endPosition);
114     if (scalingFactor == 1)
115         return selectionRect;
116
117     selectionRect.scale(1 / scalingFactor);
118     return selectionRect;
119 }
120
121 IntRect SVGInlineTextBox::localSelectionRect(int startPosition, int endPosition)
122 {
123     int boxStart = start();
124     startPosition = max(startPosition - boxStart, 0);
125     endPosition = min(endPosition - boxStart, static_cast<int>(len()));
126     if (startPosition >= endPosition)
127         return IntRect();
128
129     RenderText* text = textRenderer();
130     ASSERT(text);
131
132     RenderStyle* style = text->style();
133     ASSERT(style);
134
135     AffineTransform fragmentTransform;
136     FloatRect selectionRect;
137     int fragmentStartPosition = 0;
138     int fragmentEndPosition = 0;
139
140     unsigned textFragmentsSize = m_textFragments.size();
141     for (unsigned i = 0; i < textFragmentsSize; ++i) {
142         const SVGTextFragment& fragment = m_textFragments.at(i);
143
144         fragmentStartPosition = startPosition;
145         fragmentEndPosition = endPosition;
146         if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
147             continue;
148
149         FloatRect fragmentRect = selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style);
150         fragment.buildFragmentTransform(fragmentTransform);
151         if (!fragmentTransform.isIdentity())
152             fragmentRect = fragmentTransform.mapRect(fragmentRect);
153
154         selectionRect.unite(fragmentRect);
155     }
156
157     return enclosingIntRect(selectionRect);
158 }
159
160 static inline bool textShouldBePainted(RenderSVGInlineText* textRenderer)
161 {
162     // Font::pixelSize(), returns FontDescription::computedPixelSize(), which returns "int(x + 0.5)".
163     // If the absolute font size on screen is below x=0.5, don't render anything.
164     return textRenderer->scaledFont().pixelSize();
165 }
166
167 void SVGInlineTextBox::paintSelectionBackground(PaintInfo& paintInfo)
168 {
169     ASSERT(paintInfo.shouldPaintWithinRoot(renderer()));
170     ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
171     ASSERT(truncation() == cNoTruncation);
172
173     if (renderer()->style()->visibility() != VISIBLE)
174         return;
175
176     RenderObject* parentRenderer = parent()->renderer();
177     ASSERT(parentRenderer);
178     ASSERT(!parentRenderer->document()->printing());
179
180     // Determine whether or not we're selected.
181     bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection;
182     bool hasSelection = selectionState() != RenderObject::SelectionNone;
183     if (!hasSelection || paintSelectedTextOnly)
184         return;
185
186     Color backgroundColor = renderer()->selectionBackgroundColor();
187     if (!backgroundColor.isValid() || !backgroundColor.alpha())
188         return;
189
190     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
191     ASSERT(textRenderer);
192     if (!textShouldBePainted(textRenderer))
193         return;
194
195     RenderStyle* style = parentRenderer->style();
196     ASSERT(style);
197
198     const SVGRenderStyle* svgStyle = style->svgStyle();
199     ASSERT(svgStyle);
200
201     bool hasFill = svgStyle->hasFill();
202     bool hasStroke = svgStyle->hasStroke();
203
204     RenderStyle* selectionStyle = style;
205     if (hasSelection) {
206         selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION);
207         if (selectionStyle) {
208             const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle();
209             ASSERT(svgSelectionStyle);
210
211             if (!hasFill)
212                 hasFill = svgSelectionStyle->hasFill();
213             if (!hasStroke)
214                 hasStroke = svgSelectionStyle->hasStroke();
215         } else
216             selectionStyle = style;
217     }
218
219     int startPosition, endPosition;
220     selectionStartEnd(startPosition, endPosition);
221
222     int fragmentStartPosition = 0;
223     int fragmentEndPosition = 0;
224     AffineTransform fragmentTransform;
225     unsigned textFragmentsSize = m_textFragments.size();
226     for (unsigned i = 0; i < textFragmentsSize; ++i) {
227         SVGTextFragment& fragment = m_textFragments.at(i);
228         ASSERT(!m_paintingResource);
229
230         fragmentStartPosition = startPosition;
231         fragmentEndPosition = endPosition;
232         if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
233             continue;
234
235         GraphicsContextStateSaver stateSaver(*paintInfo.context);
236         fragment.buildFragmentTransform(fragmentTransform);
237         if (!fragmentTransform.isIdentity())
238             paintInfo.context->concatCTM(fragmentTransform);
239
240         paintInfo.context->setFillColor(backgroundColor, style->colorSpace());
241         paintInfo.context->fillRect(selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style), backgroundColor, style->colorSpace());
242
243         m_paintingResourceMode = ApplyToDefaultMode;
244     }
245
246     ASSERT(!m_paintingResource);
247 }
248
249 void SVGInlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint&, LayoutUnit, LayoutUnit)
250 {
251     ASSERT(paintInfo.shouldPaintWithinRoot(renderer()));
252     ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
253     ASSERT(truncation() == cNoTruncation);
254
255     if (renderer()->style()->visibility() != VISIBLE)
256         return;
257
258     // Note: We're explicitely not supporting composition & custom underlines and custom highlighters - unlike InlineTextBox.
259     // If we ever need that for SVG, it's very easy to refactor and reuse the code.
260
261     RenderObject* parentRenderer = parent()->renderer();
262     ASSERT(parentRenderer);
263
264     bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection;
265     bool hasSelection = !parentRenderer->document()->printing() && selectionState() != RenderObject::SelectionNone;
266     if (!hasSelection && paintSelectedTextOnly)
267         return;
268
269     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
270     ASSERT(textRenderer);
271     if (!textShouldBePainted(textRenderer))
272         return;
273
274     RenderStyle* style = parentRenderer->style();
275     ASSERT(style);
276
277     const SVGRenderStyle* svgStyle = style->svgStyle();
278     ASSERT(svgStyle);
279
280     bool hasFill = svgStyle->hasFill();
281     bool hasStroke = svgStyle->hasStroke();
282
283     RenderStyle* selectionStyle = style;
284     if (hasSelection) {
285         selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION);
286         if (selectionStyle) {
287             const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle();
288             ASSERT(svgSelectionStyle);
289
290             if (!hasFill)
291                 hasFill = svgSelectionStyle->hasFill();
292             if (!hasStroke)
293                 hasStroke = svgSelectionStyle->hasStroke();
294         } else
295             selectionStyle = style;
296     }
297
298     AffineTransform fragmentTransform;
299     unsigned textFragmentsSize = m_textFragments.size();
300     for (unsigned i = 0; i < textFragmentsSize; ++i) {
301         SVGTextFragment& fragment = m_textFragments.at(i);
302         ASSERT(!m_paintingResource);
303
304         GraphicsContextStateSaver stateSaver(*paintInfo.context);
305         fragment.buildFragmentTransform(fragmentTransform);
306         if (!fragmentTransform.isIdentity())
307             paintInfo.context->concatCTM(fragmentTransform);
308
309         // 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.
310         int decorations = style->textDecorationsInEffect();
311         if (decorations & UNDERLINE)
312             paintDecoration(paintInfo.context, UNDERLINE, fragment);
313         if (decorations & OVERLINE)
314             paintDecoration(paintInfo.context, OVERLINE, fragment);
315
316         // Fill text
317         if (hasFill) {
318             m_paintingResourceMode = ApplyToFillMode | ApplyToTextMode;
319             paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly);
320         }
321
322         // Stroke text
323         if (hasStroke) {
324             m_paintingResourceMode = ApplyToStrokeMode | ApplyToTextMode;
325             paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly);
326         }
327
328         // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text.
329         if (decorations & LINE_THROUGH)
330             paintDecoration(paintInfo.context, LINE_THROUGH, fragment);
331
332         m_paintingResourceMode = ApplyToDefaultMode;
333     }
334
335     ASSERT(!m_paintingResource);
336 }
337
338 bool SVGInlineTextBox::acquirePaintingResource(GraphicsContext*& context, float scalingFactor, RenderObject* renderer, RenderStyle* style)
339 {
340     ASSERT(scalingFactor);
341     ASSERT(renderer);
342     ASSERT(style);
343     ASSERT(m_paintingResourceMode != ApplyToDefaultMode);
344
345     Color fallbackColor;
346     if (m_paintingResourceMode & ApplyToFillMode)
347         m_paintingResource = RenderSVGResource::fillPaintingResource(renderer, style, fallbackColor);
348     else if (m_paintingResourceMode & ApplyToStrokeMode)
349         m_paintingResource = RenderSVGResource::strokePaintingResource(renderer, style, fallbackColor);
350     else {
351         // We're either called for stroking or filling.
352         ASSERT_NOT_REACHED();
353     }
354
355     if (!m_paintingResource)
356         return false;
357
358     if (!m_paintingResource->applyResource(renderer, style, context, m_paintingResourceMode)) {
359         if (fallbackColor.isValid()) {
360             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
361             fallbackResource->setColor(fallbackColor);
362
363             m_paintingResource = fallbackResource;
364             m_paintingResource->applyResource(renderer, style, context, m_paintingResourceMode);
365         }
366     }
367
368     if (scalingFactor != 1 && m_paintingResourceMode & ApplyToStrokeMode)
369         context->setStrokeThickness(context->strokeThickness() * scalingFactor);
370
371     return true;
372 }
373
374 void SVGInlineTextBox::releasePaintingResource(GraphicsContext*& context, const Path* path)
375 {
376     ASSERT(m_paintingResource);
377
378     RenderObject* parentRenderer = parent()->renderer();
379     ASSERT(parentRenderer);
380
381     m_paintingResource->postApplyResource(parentRenderer, context, m_paintingResourceMode, path);
382     m_paintingResource = 0;
383 }
384
385 bool SVGInlineTextBox::prepareGraphicsContextForTextPainting(GraphicsContext*& context, float scalingFactor, TextRun& textRun, RenderStyle* style)
386 {
387     bool acquiredResource = acquirePaintingResource(context, scalingFactor, parent()->renderer(), style);
388     if (!acquiredResource)
389         return false;
390
391 #if ENABLE(SVG_FONTS)
392     // SVG Fonts need access to the painting resource used to draw the current text chunk.
393     TextRun::RenderingContext* renderingContext = textRun.renderingContext();
394     if (renderingContext)
395         static_cast<SVGTextRunRenderingContext*>(renderingContext)->setActivePaintingResource(m_paintingResource);
396 #endif
397
398     return true;
399 }
400
401 void SVGInlineTextBox::restoreGraphicsContextAfterTextPainting(GraphicsContext*& context, TextRun& textRun)
402 {
403     releasePaintingResource(context, /* path */0);
404
405 #if ENABLE(SVG_FONTS)
406     TextRun::RenderingContext* renderingContext = textRun.renderingContext();
407     if (renderingContext)
408         static_cast<SVGTextRunRenderingContext*>(renderingContext)->setActivePaintingResource(0);
409 #else
410     UNUSED_PARAM(textRun);
411 #endif
412 }
413
414 TextRun SVGInlineTextBox::constructTextRun(RenderStyle* style, const SVGTextFragment& fragment) const
415 {
416     ASSERT(style);
417     ASSERT(textRenderer());
418
419     RenderText* text = textRenderer();
420     ASSERT(text);
421
422     TextRun run(text->characters() + fragment.characterOffset
423                 , fragment.length
424                 , false /* allowTabs */
425                 , 0 /* xPos, only relevant with allowTabs=true */
426                 , 0 /* padding, only relevant for justified text, not relevant for SVG */
427                 , TextRun::AllowTrailingExpansion
428                 , direction()
429                 , m_dirOverride || style->rtlOrdering() == VisualOrder /* directionalOverride */);
430
431     if (textRunNeedsRenderingContext(style->font()))
432         run.setRenderingContext(SVGTextRunRenderingContext::create(text));
433
434     run.disableRoundingHacks();
435
436     // We handle letter & word spacing ourselves.
437     run.disableSpacing();
438
439     // Propagate the maximum length of the characters buffer to the TextRun, even when we're only processing a substring.
440     run.setCharactersLength(text->textLength() - fragment.characterOffset);
441     ASSERT(run.charactersLength() >= run.length());
442     return run;
443 }
444
445 bool SVGInlineTextBox::mapStartEndPositionsIntoFragmentCoordinates(const SVGTextFragment& fragment, int& startPosition, int& endPosition) const
446 {
447     if (startPosition >= endPosition)
448         return false;
449
450     int offset = static_cast<int>(fragment.characterOffset) - start();
451     int length = static_cast<int>(fragment.length);
452
453     if (startPosition >= offset + length || endPosition <= offset)
454         return false;
455
456     if (startPosition < offset)
457         startPosition = 0;
458     else
459         startPosition -= offset;
460
461     if (endPosition > offset + length)
462         endPosition = length;
463     else {
464         ASSERT(endPosition >= offset);
465         endPosition -= offset;
466     }
467
468     ASSERT(startPosition < endPosition);
469     return true;
470 }
471
472 static inline float positionOffsetForDecoration(ETextDecoration decoration, const FontMetrics& fontMetrics, float thickness)
473 {
474     // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified.
475     // Compatible with Batik/Opera.
476     if (decoration == UNDERLINE)
477         return fontMetrics.floatAscent() + thickness * 1.5f;
478     if (decoration == OVERLINE)
479         return thickness;
480     if (decoration == LINE_THROUGH)
481         return fontMetrics.floatAscent() * 5 / 8.0f;
482
483     ASSERT_NOT_REACHED();
484     return 0.0f;
485 }
486
487 static inline float thicknessForDecoration(ETextDecoration, const Font& font)
488 {
489     // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified.
490     // Compatible with Batik/Opera
491     return font.size() / 20.0f;
492 }
493
494 static inline RenderObject* findRenderObjectDefininingTextDecoration(InlineFlowBox* parentBox)
495 {
496     // Lookup first render object in parent hierarchy which has text-decoration set.
497     RenderObject* renderer = 0;
498     while (parentBox) {
499         renderer = parentBox->renderer();
500
501         if (renderer->style() && renderer->style()->textDecoration() != TDNONE)
502             break;
503
504         parentBox = parentBox->parent();
505     }
506
507     ASSERT(renderer);
508     return renderer;
509 }
510
511 void SVGInlineTextBox::paintDecoration(GraphicsContext* context, ETextDecoration decoration, const SVGTextFragment& fragment)
512 {
513     if (textRenderer()->style()->textDecorationsInEffect() == TDNONE)
514         return;
515
516     // Find out which render style defined the text-decoration, as its fill/stroke properties have to be used for drawing instead of ours.
517     RenderObject* decorationRenderer = findRenderObjectDefininingTextDecoration(parent());
518     RenderStyle* decorationStyle = decorationRenderer->style();
519     ASSERT(decorationStyle);
520
521     if (decorationStyle->visibility() == HIDDEN)
522         return;
523
524     const SVGRenderStyle* svgDecorationStyle = decorationStyle->svgStyle();
525     ASSERT(svgDecorationStyle);
526
527     bool hasDecorationFill = svgDecorationStyle->hasFill();
528     bool hasDecorationStroke = svgDecorationStyle->hasStroke();
529
530     if (hasDecorationFill) {
531         m_paintingResourceMode = ApplyToFillMode;
532         paintDecorationWithStyle(context, decoration, fragment, decorationRenderer);
533     }
534
535     if (hasDecorationStroke) {
536         m_paintingResourceMode = ApplyToStrokeMode;
537         paintDecorationWithStyle(context, decoration, fragment, decorationRenderer);
538     }
539 }
540
541 static inline void normalizeTransform(AffineTransform& transform)
542 {
543     // Obtain consistent numerical results for the AffineTransform on both 32/64bit platforms.
544     // Tested with SnowLeopard on Core Duo vs. Core 2 Duo.
545     static const float s_floatEpsilon = std::numeric_limits<float>::epsilon();
546
547     if (fabs(transform.a() - 1) <= s_floatEpsilon)
548         transform.setA(1);
549     else if (fabs(transform.a() + 1) <= s_floatEpsilon)
550         transform.setA(-1);
551
552     if (fabs(transform.d() - 1) <= s_floatEpsilon)
553         transform.setD(1);
554     else if (fabs(transform.d() + 1) <= s_floatEpsilon)
555         transform.setD(-1);
556
557     if (fabs(transform.e()) <= s_floatEpsilon)
558         transform.setE(0);
559
560     if (fabs(transform.f()) <= s_floatEpsilon)
561         transform.setF(0);
562 }
563
564 void SVGInlineTextBox::paintDecorationWithStyle(GraphicsContext* context, ETextDecoration decoration, const SVGTextFragment& fragment, RenderObject* decorationRenderer)
565 {
566     ASSERT(!m_paintingResource);
567     ASSERT(m_paintingResourceMode != ApplyToDefaultMode);
568
569     RenderStyle* decorationStyle = decorationRenderer->style();
570     ASSERT(decorationStyle);
571
572     float scalingFactor = 1;
573     Font scaledFont;
574     RenderSVGInlineText::computeNewScaledFontForStyle(decorationRenderer, decorationStyle, scalingFactor, scaledFont);
575     ASSERT(scalingFactor);
576
577     // The initial y value refers to overline position.
578     float thickness = thicknessForDecoration(decoration, scaledFont);
579
580     if (fragment.width <= 0 && thickness <= 0)
581         return;
582
583     FloatPoint decorationOrigin(fragment.x, fragment.y);
584     float width = fragment.width;
585     const FontMetrics& scaledFontMetrics = scaledFont.fontMetrics();
586
587     GraphicsContextStateSaver stateSaver(*context);
588     if (scalingFactor != 1) {
589         width *= scalingFactor;
590         decorationOrigin.scale(scalingFactor, scalingFactor);
591
592         AffineTransform newTransform = context->getCTM();
593         newTransform.scale(1 / scalingFactor);
594         normalizeTransform(newTransform);
595
596         context->setCTM(newTransform);
597     }
598
599     decorationOrigin.move(0, -scaledFontMetrics.floatAscent() + positionOffsetForDecoration(decoration, scaledFontMetrics, thickness));
600
601     Path path;
602     path.addRect(FloatRect(decorationOrigin, FloatSize(width, thickness)));
603
604     if (acquirePaintingResource(context, scalingFactor, decorationRenderer, decorationStyle))
605         releasePaintingResource(context, &path);
606 }
607
608 void SVGInlineTextBox::paintTextWithShadows(GraphicsContext* context, RenderStyle* style, TextRun& textRun, const SVGTextFragment& fragment, int startPosition, int endPosition)
609 {
610     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
611     ASSERT(textRenderer);
612
613     float scalingFactor = textRenderer->scalingFactor();
614     ASSERT(scalingFactor);
615
616     const Font& scaledFont = textRenderer->scaledFont();
617     const ShadowData* shadow = style->textShadow();
618
619     FloatPoint textOrigin(fragment.x, fragment.y);
620     FloatSize textSize(fragment.width, fragment.height);
621
622     if (scalingFactor != 1) {
623         textOrigin.scale(scalingFactor, scalingFactor);
624         textSize.scale(scalingFactor);
625     }
626
627     FloatRect shadowRect(FloatPoint(textOrigin.x(), textOrigin.y() - scaledFont.fontMetrics().floatAscent()), textSize);
628
629     do {
630         if (!prepareGraphicsContextForTextPainting(context, scalingFactor, textRun, style))
631             break;
632
633         FloatSize extraOffset;
634         if (shadow)
635             extraOffset = applyShadowToGraphicsContext(context, shadow, shadowRect, false /* stroked */, true /* opaque */, true /* horizontal */);
636
637         AffineTransform originalTransform;
638         if (scalingFactor != 1) {
639             originalTransform = context->getCTM();
640
641             AffineTransform newTransform = originalTransform;
642             newTransform.scale(1 / scalingFactor);
643             normalizeTransform(newTransform);
644
645             context->setCTM(newTransform);
646         }
647
648         scaledFont.drawText(context, textRun, textOrigin + extraOffset, startPosition, endPosition);
649
650         if (scalingFactor != 1)
651             context->setCTM(originalTransform);
652
653         restoreGraphicsContextAfterTextPainting(context, textRun);
654
655         if (!shadow)
656             break;
657
658         if (shadow->next())
659             context->restore();
660         else
661             context->clearShadow();
662
663         shadow = shadow->next();
664     } while (shadow);
665 }
666
667 void SVGInlineTextBox::paintText(GraphicsContext* context, RenderStyle* style, RenderStyle* selectionStyle, const SVGTextFragment& fragment, 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);
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);
689
690     // Draw text using selection style from the start to the end position of the selection
691     if (style != selectionStyle)
692         SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, selectionStyle);
693
694     TextRun selectionTextRun = constructTextRun(selectionStyle, fragment);
695     paintTextWithShadows(context, selectionStyle, textRun, fragment, startPosition, endPosition);
696
697     if (style != selectionStyle)
698         SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, style);
699
700     // Eventually draw text using regular style from the end position of the selection to the end of the current chunk part
701     if (endPosition < static_cast<int>(fragment.length) && !paintSelectedTextOnly)
702         paintTextWithShadows(context, style, textRun, fragment, endPosition, fragment.length);
703 }
704
705 IntRect SVGInlineTextBox::calculateBoundaries() const
706 {
707     FloatRect textRect;
708
709     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
710     ASSERT(textRenderer);
711
712     float scalingFactor = textRenderer->scalingFactor();
713     ASSERT(scalingFactor);
714
715     float baseline = textRenderer->scaledFont().fontMetrics().floatAscent() / scalingFactor;
716
717     AffineTransform fragmentTransform;
718     unsigned textFragmentsSize = m_textFragments.size();
719     for (unsigned i = 0; i < textFragmentsSize; ++i) {
720         const SVGTextFragment& fragment = m_textFragments.at(i);
721         FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height);
722         fragment.buildFragmentTransform(fragmentTransform);
723         if (!fragmentTransform.isIdentity())
724             fragmentRect = fragmentTransform.mapRect(fragmentRect);
725
726         textRect.unite(fragmentRect);
727     }
728
729     return enclosingIntRect(textRect);
730 }
731
732 bool SVGInlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit, LayoutUnit)
733 {
734     // FIXME: integrate with InlineTextBox::nodeAtPoint better.
735     ASSERT(!isLineBreak());
736
737     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, request, renderer()->style()->pointerEvents());
738     bool isVisible = renderer()->style()->visibility() == VISIBLE;
739     if (isVisible || !hitRules.requireVisible) {
740         if ((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 (rect.intersects(result.rectForPoint(pointInContainer))) {
746                 renderer()->updateHitTestResult(result, pointInContainer - toLayoutSize(accumulatedOffset));
747                 if (!result.addNodeToRectBasedTestResult(renderer()->node(), pointInContainer, rect))
748                     return true;
749              }
750         }
751     }
752     return false;
753 }
754
755 } // namespace WebCore
756
757 #endif