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