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