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