1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_cppcanvas.hxx" 30 31 #include <canvas/debug.hxx> 32 #include <tools/diagnose_ex.h> 33 #include <canvas/verbosetrace.hxx> 34 #include <com/sun/star/rendering/RenderState.hpp> 35 #include <com/sun/star/rendering/XCanvas.hpp> 36 #include <basegfx/numeric/ftools.hxx> 37 #include <basegfx/tools/canvastools.hxx> 38 #include <basegfx/polygon/b2dpolygontools.hxx> 39 #include <basegfx/polygon/b2dpolygon.hxx> 40 #include <basegfx/range/b2drectangle.hxx> 41 #include <basegfx/vector/b2dvector.hxx> 42 #include <canvas/canvastools.hxx> 43 #include <vcl/gdimtf.hxx> 44 #include <vcl/metaact.hxx> 45 #include <vcl/virdev.hxx> 46 #include <vcl/metric.hxx> 47 #include <tools/poly.hxx> 48 #include "mtftools.hxx" 49 #include "outdevstate.hxx" 50 #include "polypolyaction.hxx" 51 #include <basegfx/matrix/b2dhommatrixtools.hxx> 52 53 54 55 using namespace ::com::sun::star; 56 57 namespace cppcanvas 58 { 59 namespace tools 60 { 61 void initRenderState( rendering::RenderState& renderState, 62 const ::cppcanvas::internal::OutDevState& outdevState ) 63 { 64 ::canvas::tools::initRenderState( renderState ); 65 ::canvas::tools::setRenderStateTransform( renderState, 66 outdevState.transform ); 67 renderState.Clip = outdevState.xClipPoly; 68 } 69 70 ::Size getBaselineOffset( const ::cppcanvas::internal::OutDevState& outdevState, 71 const VirtualDevice& rVDev ) 72 { 73 const ::FontMetric& aMetric = rVDev.GetFontMetric(); 74 75 // calc offset for text output, the XCanvas always renders 76 // baseline offset. 77 switch( outdevState.textReferencePoint ) 78 { 79 case ALIGN_TOP: 80 return ::Size( 0, 81 aMetric.GetIntLeading() + aMetric.GetAscent() ); 82 83 default: 84 ENSURE_OR_THROW( false, 85 "tools::getBaselineOffset(): Unexpected TextAlign value" ); 86 // FALLTHROUGH intended (to calm compiler warning - case won't happen) 87 case ALIGN_BASELINE: 88 return ::Size( 0, 0 ); 89 90 case ALIGN_BOTTOM: 91 return ::Size( 0, 92 -aMetric.GetDescent() ); 93 94 } 95 } 96 97 ::basegfx::B2DHomMatrix& calcLogic2PixelLinearTransform( ::basegfx::B2DHomMatrix& o_rMatrix, 98 const VirtualDevice& rVDev ) 99 { 100 // select size value in the middle of the available range, 101 // to have headroom both when map mode scales up, and when 102 // it scales down. 103 const ::Size aSizeLogic( 0x00010000L, 104 0x00010000L ); 105 106 const ::Size aSizePixel( rVDev.LogicToPixel( aSizeLogic ) ); 107 108 o_rMatrix = basegfx::tools::createScaleB2DHomMatrix( 109 aSizePixel.Width() / (double)aSizeLogic.Width(), 110 aSizePixel.Height() / (double)aSizeLogic.Height() ); 111 112 return o_rMatrix; 113 } 114 115 ::basegfx::B2DHomMatrix& calcLogic2PixelAffineTransform( ::basegfx::B2DHomMatrix& o_rMatrix, 116 const VirtualDevice& rVDev ) 117 { 118 // retrieves scale 119 calcLogic2PixelLinearTransform(o_rMatrix, rVDev); 120 121 // translate according to curr map mode/pref map mode offset 122 const ::Point aEmptyPoint; 123 const ::Point& rTranslatedPoint( 124 rVDev.LogicToPixel( aEmptyPoint )); 125 126 o_rMatrix.translate(rTranslatedPoint.X(), 127 rTranslatedPoint.Y()); 128 129 return o_rMatrix; 130 } 131 132 bool modifyClip( rendering::RenderState& o_rRenderState, 133 const struct ::cppcanvas::internal::OutDevState& rOutdevState, 134 const CanvasSharedPtr& rCanvas, 135 const ::basegfx::B2DPoint& rOffset, 136 const ::basegfx::B2DVector* pScaling, 137 const double* pRotation ) 138 { 139 const ::Point aEmptyPoint; 140 141 const bool bOffsetting( !rOffset.equalZero() ); 142 const bool bScaling( pScaling && 143 pScaling->getX() != 1.0 && 144 pScaling->getY() != 1.0 ); 145 const bool bRotation( pRotation && 146 *pRotation != 0.0 ); 147 148 if( !bOffsetting && !bScaling && !bRotation ) 149 return false; // nothing to do 150 151 if( rOutdevState.clip.count() ) 152 { 153 // general polygon case 154 155 ::basegfx::B2DPolyPolygon aLocalClip( rOutdevState.clip ); 156 ::basegfx::B2DHomMatrix aTransform; 157 158 if( bOffsetting ) 159 aTransform.translate( -rOffset.getX(), 160 -rOffset.getY() ); 161 if( bScaling ) 162 aTransform.scale( 1.0/pScaling->getX(), 1.0/pScaling->getY() ); 163 164 if( bRotation ) 165 aTransform.rotate( - *pRotation ); 166 167 aLocalClip.transform( aTransform ); 168 169 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 170 rCanvas->getUNOCanvas()->getDevice(), 171 aLocalClip ); 172 173 return true; 174 } 175 else if( !rOutdevState.clipRect.IsEmpty() ) 176 { 177 // simple rect case 178 179 const ::Rectangle aLocalClipRect( rOutdevState.clipRect ); 180 181 if( bRotation ) 182 { 183 // rotation involved - convert to polygon first, 184 // then transform that 185 ::basegfx::B2DPolygon aLocalClip( 186 ::basegfx::tools::createPolygonFromRect( 187 ::basegfx::B2DRectangle( 188 (double)(aLocalClipRect.Left()), 189 (double)(aLocalClipRect.Top()), 190 (double)(aLocalClipRect.Right()), 191 (double)(aLocalClipRect.Bottom()) ) ) ); 192 ::basegfx::B2DHomMatrix aTransform; 193 194 if( bOffsetting ) 195 aTransform.translate( -rOffset.getX(), 196 -rOffset.getY() ); 197 if( bScaling ) 198 aTransform.scale( 1.0/pScaling->getX(), 1.0/pScaling->getY() ); 199 200 aTransform.rotate( - *pRotation ); 201 202 aLocalClip.transform( aTransform ); 203 204 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 205 rCanvas->getUNOCanvas()->getDevice(), 206 ::basegfx::B2DPolyPolygon( aLocalClip ) ); 207 } 208 else if( bScaling ) 209 { 210 // scale and offset - do it on the fly, have to 211 // convert to float anyway. 212 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 213 rCanvas->getUNOCanvas()->getDevice(), 214 ::basegfx::B2DPolyPolygon( 215 ::basegfx::tools::createPolygonFromRect( 216 ::basegfx::B2DRectangle( 217 (double)(aLocalClipRect.Left() - rOffset.getX())/pScaling->getX(), 218 (double)(aLocalClipRect.Top() - rOffset.getY())/pScaling->getY(), 219 (double)(aLocalClipRect.Right() - rOffset.getX())/pScaling->getX(), 220 (double)(aLocalClipRect.Bottom() - rOffset.getY())/pScaling->getY() ) ) ) ); 221 } 222 else 223 { 224 // offset only - do it on the fly, have to convert 225 // to float anyway. 226 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 227 rCanvas->getUNOCanvas()->getDevice(), 228 ::basegfx::B2DPolyPolygon( 229 ::basegfx::tools::createPolygonFromRect( 230 ::basegfx::B2DRectangle( aLocalClipRect.Left() - rOffset.getX(), 231 aLocalClipRect.Top() - rOffset.getY(), 232 aLocalClipRect.Right() - rOffset.getX(), 233 aLocalClipRect.Bottom() - rOffset.getY() ) ) ) ); 234 } 235 236 return true; 237 } 238 239 // empty clip, nothing to do 240 return false; 241 } 242 243 bool modifyClip( rendering::RenderState& o_rRenderState, 244 const struct ::cppcanvas::internal::OutDevState& rOutdevState, 245 const CanvasSharedPtr& rCanvas, 246 const ::Point& rOffset, 247 const ::basegfx::B2DVector* pScaling, 248 const double* pRotation ) 249 { 250 return modifyClip( o_rRenderState, 251 rOutdevState, 252 rCanvas, 253 ::basegfx::B2DPoint( rOffset.X(), 254 rOffset.Y() ), 255 pScaling, 256 pRotation ); 257 } 258 259 bool modifyClip( rendering::RenderState& o_rRenderState, 260 const struct ::cppcanvas::internal::OutDevState& rOutdevState, 261 const CanvasSharedPtr& rCanvas, 262 const ::basegfx::B2DHomMatrix& rTransform ) 263 { 264 if( !rTransform.isIdentity() || 265 !rTransform.isInvertible() ) 266 return false; // nothing to do 267 268 ::basegfx::B2DPolyPolygon aLocalClip; 269 270 if( rOutdevState.clip.count() ) 271 { 272 aLocalClip = rOutdevState.clip; 273 } 274 else if( !rOutdevState.clipRect.IsEmpty() ) 275 { 276 const ::Rectangle aLocalClipRect( rOutdevState.clipRect ); 277 278 aLocalClip = ::basegfx::B2DPolyPolygon( 279 ::basegfx::tools::createPolygonFromRect( 280 ::basegfx::B2DRectangle( 281 aLocalClipRect.Left(), 282 aLocalClipRect.Top(), 283 aLocalClipRect.Right(), 284 aLocalClipRect.Bottom() ) ) ); 285 } 286 else 287 { 288 // empty clip, nothing to do 289 return false; 290 } 291 292 // invert transformation and modify 293 ::basegfx::B2DHomMatrix aLocalTransform( rTransform ); 294 aLocalTransform.invert(); 295 296 aLocalClip.transform( aLocalTransform ); 297 298 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 299 rCanvas->getUNOCanvas()->getDevice(), 300 aLocalClip ); 301 302 return true; 303 } 304 305 // create overline/underline/strikeout line info struct 306 TextLineInfo createTextLineInfo( const ::VirtualDevice& rVDev, 307 const ::cppcanvas::internal::OutDevState& rState ) 308 { 309 const sal_Bool bOldMode( rVDev.IsMapModeEnabled() ); 310 311 // #i68512# Force metric regeneration with mapmode enabled 312 // (prolly OutDev bug) 313 rVDev.GetFontMetric(); 314 315 // will restore map mode below 316 const_cast< ::VirtualDevice& >(rVDev).EnableMapMode( sal_False ); 317 318 const ::FontMetric aMetric = rVDev.GetFontMetric(); 319 320 TextLineInfo aTextInfo( 321 (aMetric.GetDescent() + 2) / 4.0, 322 ((aMetric.GetIntLeading() + 1.5) / 3.0), 323 (aMetric.GetIntLeading() / 2.0) - aMetric.GetAscent(), 324 aMetric.GetDescent() / 2.0, 325 (aMetric.GetIntLeading() - aMetric.GetAscent()) / 3.0, 326 rState.textOverlineStyle, 327 rState.textUnderlineStyle, 328 rState.textStrikeoutStyle ); 329 330 const_cast< ::VirtualDevice& >(rVDev).EnableMapMode( bOldMode ); 331 332 return aTextInfo; 333 } 334 335 namespace 336 { 337 void appendRect( ::basegfx::B2DPolyPolygon& o_rPoly, 338 const ::basegfx::B2DPoint& rStartPos, 339 const double nX1, 340 const double nY1, 341 const double nX2, 342 const double nY2 ) 343 { 344 const double x( rStartPos.getX() ); 345 const double y( rStartPos.getY() ); 346 347 o_rPoly.append( 348 ::basegfx::tools::createPolygonFromRect( 349 ::basegfx::B2DRectangle( x + nX1, y + nY1, x + nX2, y + nY2 ) ) ); 350 } 351 352 void appendRect( ::basegfx::B2DPolyPolygon& o_rPoly, 353 const double nX1, 354 const double nY1, 355 const double nX2, 356 const double nY2 ) 357 { 358 o_rPoly.append( 359 ::basegfx::tools::createPolygonFromRect( 360 ::basegfx::B2DRectangle( nX1, nY1, nX2, nY2 ) ) ); 361 } 362 363 void appendDashes( ::basegfx::B2DPolyPolygon& o_rPoly, 364 const double nX, 365 const double nY, 366 const double nLineWidth, 367 const double nLineHeight, 368 const double nDashWidth, 369 const double nDashSkip ) 370 { 371 const sal_Int32 nNumLoops( 372 static_cast< sal_Int32 >( 373 ::std::max( 1.0, 374 nLineWidth / nDashSkip ) + .5) ); 375 376 double x = nX; 377 for( sal_Int32 i=0; i<nNumLoops; ++i ) 378 { 379 appendRect( o_rPoly, 380 x, nY, 381 x + nDashWidth, nY + nLineHeight ); 382 383 x += nDashSkip; 384 } 385 } 386 } 387 388 // create line actions for text such as underline and 389 // strikeout 390 ::basegfx::B2DPolyPolygon createTextLinesPolyPolygon( const ::basegfx::B2DPoint rStartPos, 391 const double& rLineWidth, 392 const TextLineInfo& rTextLineInfo ) 393 { 394 // fill the polypolygon with all text lines 395 ::basegfx::B2DPolyPolygon aTextLinesPolyPoly; 396 397 switch( rTextLineInfo.mnOverlineStyle ) 398 { 399 case UNDERLINE_NONE: // nothing to do 400 // FALLTHROUGH intended 401 case UNDERLINE_DONTKNOW: 402 break; 403 404 case UNDERLINE_SMALLWAVE: // TODO(F3): NYI 405 // FALLTHROUGH intended 406 case UNDERLINE_WAVE: // TODO(F3): NYI 407 // FALLTHROUGH intended 408 case UNDERLINE_SINGLE: 409 appendRect( 410 aTextLinesPolyPoly, 411 rStartPos, 412 0, 413 rTextLineInfo.mnOverlineOffset, 414 rLineWidth, 415 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight ); 416 break; 417 418 case UNDERLINE_BOLDDOTTED: // TODO(F3): NYI 419 // FALLTHROUGH intended 420 case UNDERLINE_BOLDDASH: // TODO(F3): NYI 421 // FALLTHROUGH intended 422 case UNDERLINE_BOLDLONGDASH: // TODO(F3): NYI 423 // FALLTHROUGH intended 424 case UNDERLINE_BOLDDASHDOT: // TODO(F3): NYI 425 // FALLTHROUGH intended 426 case UNDERLINE_BOLDDASHDOTDOT:// TODO(F3): NYI 427 // FALLTHROUGH intended 428 case UNDERLINE_BOLDWAVE: // TODO(F3): NYI 429 // FALLTHROUGH intended 430 case UNDERLINE_BOLD: 431 appendRect( 432 aTextLinesPolyPoly, 433 rStartPos, 434 0, 435 rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight, 436 rLineWidth, 437 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight ); 438 break; 439 440 case UNDERLINE_DOUBLEWAVE: // TODO(F3): NYI 441 // FALLTHROUGH intended 442 case UNDERLINE_DOUBLE: 443 appendRect( 444 aTextLinesPolyPoly, 445 rStartPos, 446 0, 447 rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight * 2.0 , 448 rLineWidth, 449 rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight ); 450 451 appendRect( 452 aTextLinesPolyPoly, 453 rStartPos, 454 0, 455 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight, 456 rLineWidth, 457 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight * 2.0 ); 458 break; 459 460 case UNDERLINE_DASHDOTDOT: // TODO(F3): NYI 461 // FALLTHROUGH intended 462 case UNDERLINE_DOTTED: 463 appendDashes( 464 aTextLinesPolyPoly, 465 rStartPos.getX(), 466 rStartPos.getY() + rTextLineInfo.mnOverlineOffset, 467 rLineWidth, 468 rTextLineInfo.mnOverlineHeight, 469 rTextLineInfo.mnOverlineHeight, 470 2*rTextLineInfo.mnOverlineHeight ); 471 break; 472 473 case UNDERLINE_DASHDOT: // TODO(F3): NYI 474 // FALLTHROUGH intended 475 case UNDERLINE_DASH: 476 appendDashes( 477 aTextLinesPolyPoly, 478 rStartPos.getX(), 479 rStartPos.getY() + rTextLineInfo.mnOverlineOffset, 480 rLineWidth, 481 rTextLineInfo.mnOverlineHeight, 482 3*rTextLineInfo.mnOverlineHeight, 483 6*rTextLineInfo.mnOverlineHeight ); 484 break; 485 486 case UNDERLINE_LONGDASH: 487 appendDashes( 488 aTextLinesPolyPoly, 489 rStartPos.getX(), 490 rStartPos.getY() + rTextLineInfo.mnOverlineOffset, 491 rLineWidth, 492 rTextLineInfo.mnOverlineHeight, 493 6*rTextLineInfo.mnOverlineHeight, 494 12*rTextLineInfo.mnOverlineHeight ); 495 break; 496 497 default: 498 ENSURE_OR_THROW( false, 499 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected overline case" ); 500 } 501 502 switch( rTextLineInfo.mnUnderlineStyle ) 503 { 504 case UNDERLINE_NONE: // nothing to do 505 // FALLTHROUGH intended 506 case UNDERLINE_DONTKNOW: 507 break; 508 509 case UNDERLINE_SMALLWAVE: // TODO(F3): NYI 510 // FALLTHROUGH intended 511 case UNDERLINE_WAVE: // TODO(F3): NYI 512 // FALLTHROUGH intended 513 case UNDERLINE_SINGLE: 514 appendRect( 515 aTextLinesPolyPoly, 516 rStartPos, 517 0, 518 rTextLineInfo.mnUnderlineOffset, 519 rLineWidth, 520 rTextLineInfo.mnUnderlineOffset + rTextLineInfo.mnLineHeight ); 521 break; 522 523 case UNDERLINE_BOLDDOTTED: // TODO(F3): NYI 524 // FALLTHROUGH intended 525 case UNDERLINE_BOLDDASH: // TODO(F3): NYI 526 // FALLTHROUGH intended 527 case UNDERLINE_BOLDLONGDASH: // TODO(F3): NYI 528 // FALLTHROUGH intended 529 case UNDERLINE_BOLDDASHDOT: // TODO(F3): NYI 530 // FALLTHROUGH intended 531 case UNDERLINE_BOLDDASHDOTDOT:// TODO(F3): NYI 532 // FALLTHROUGH intended 533 case UNDERLINE_BOLDWAVE: // TODO(F3): NYI 534 // FALLTHROUGH intended 535 case UNDERLINE_BOLD: 536 appendRect( 537 aTextLinesPolyPoly, 538 rStartPos, 539 0, 540 rTextLineInfo.mnUnderlineOffset, 541 rLineWidth, 542 rTextLineInfo.mnUnderlineOffset + 2*rTextLineInfo.mnLineHeight ); 543 break; 544 545 case UNDERLINE_DOUBLEWAVE: // TODO(F3): NYI 546 // FALLTHROUGH intended 547 case UNDERLINE_DOUBLE: 548 appendRect( 549 aTextLinesPolyPoly, 550 rStartPos, 551 0, 552 rTextLineInfo.mnUnderlineOffset - rTextLineInfo.mnLineHeight, 553 rLineWidth, 554 rTextLineInfo.mnUnderlineOffset ); 555 556 appendRect( 557 aTextLinesPolyPoly, 558 rStartPos, 559 0, 560 rTextLineInfo.mnUnderlineOffset + 2*rTextLineInfo.mnLineHeight, 561 rLineWidth, 562 rTextLineInfo.mnUnderlineOffset + 3*rTextLineInfo.mnLineHeight ); 563 break; 564 565 case UNDERLINE_DASHDOTDOT: // TODO(F3): NYI 566 // FALLTHROUGH intended 567 case UNDERLINE_DOTTED: 568 appendDashes( 569 aTextLinesPolyPoly, 570 rStartPos.getX(), 571 rStartPos.getY() + rTextLineInfo.mnUnderlineOffset, 572 rLineWidth, 573 rTextLineInfo.mnLineHeight, 574 rTextLineInfo.mnLineHeight, 575 2*rTextLineInfo.mnLineHeight ); 576 break; 577 578 case UNDERLINE_DASHDOT: // TODO(F3): NYI 579 // FALLTHROUGH intended 580 case UNDERLINE_DASH: 581 appendDashes( 582 aTextLinesPolyPoly, 583 rStartPos.getX(), 584 rStartPos.getY() + rTextLineInfo.mnUnderlineOffset, 585 rLineWidth, 586 rTextLineInfo.mnLineHeight, 587 3*rTextLineInfo.mnLineHeight, 588 6*rTextLineInfo.mnLineHeight ); 589 break; 590 591 case UNDERLINE_LONGDASH: 592 appendDashes( 593 aTextLinesPolyPoly, 594 rStartPos.getX(), 595 rStartPos.getY() + rTextLineInfo.mnUnderlineOffset, 596 rLineWidth, 597 rTextLineInfo.mnLineHeight, 598 6*rTextLineInfo.mnLineHeight, 599 12*rTextLineInfo.mnLineHeight ); 600 break; 601 602 default: 603 ENSURE_OR_THROW( false, 604 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected underline case" ); 605 } 606 607 switch( rTextLineInfo.mnStrikeoutStyle ) 608 { 609 case STRIKEOUT_NONE: // nothing to do 610 // FALLTHROUGH intended 611 case STRIKEOUT_DONTKNOW: 612 break; 613 614 case STRIKEOUT_SLASH: // TODO(Q1): we should handle this in the text layer 615 // FALLTHROUGH intended 616 case STRIKEOUT_X: 617 break; 618 619 case STRIKEOUT_SINGLE: 620 appendRect( 621 aTextLinesPolyPoly, 622 rStartPos, 623 0, 624 rTextLineInfo.mnStrikeoutOffset, 625 rLineWidth, 626 rTextLineInfo.mnStrikeoutOffset + rTextLineInfo.mnLineHeight ); 627 break; 628 629 case STRIKEOUT_BOLD: 630 appendRect( 631 aTextLinesPolyPoly, 632 rStartPos, 633 0, 634 rTextLineInfo.mnStrikeoutOffset, 635 rLineWidth, 636 rTextLineInfo.mnStrikeoutOffset + 2*rTextLineInfo.mnLineHeight ); 637 break; 638 639 case STRIKEOUT_DOUBLE: 640 appendRect( 641 aTextLinesPolyPoly, 642 rStartPos, 643 0, 644 rTextLineInfo.mnStrikeoutOffset - rTextLineInfo.mnLineHeight, 645 rLineWidth, 646 rTextLineInfo.mnStrikeoutOffset ); 647 648 appendRect( 649 aTextLinesPolyPoly, 650 rStartPos, 651 0, 652 rTextLineInfo.mnStrikeoutOffset + 2*rTextLineInfo.mnLineHeight, 653 rLineWidth, 654 rTextLineInfo.mnStrikeoutOffset + 3*rTextLineInfo.mnLineHeight ); 655 break; 656 657 default: 658 ENSURE_OR_THROW( false, 659 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected strikeout case" ); 660 } 661 662 return aTextLinesPolyPoly; 663 } 664 665 ::basegfx::B2DRange calcDevicePixelBounds( const ::basegfx::B2DRange& rBounds, 666 const rendering::ViewState& viewState, 667 const rendering::RenderState& renderState ) 668 { 669 ::basegfx::B2DHomMatrix aTransform; 670 ::canvas::tools::mergeViewAndRenderTransform( aTransform, 671 viewState, 672 renderState ); 673 674 ::basegfx::B2DRange aTransformedBounds; 675 return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds, 676 rBounds, 677 aTransform ); 678 } 679 680 // create line actions for text such as underline and 681 // strikeout 682 ::basegfx::B2DPolyPolygon createTextLinesPolyPolygon( const double& rStartOffset, 683 const double& rLineWidth, 684 const TextLineInfo& rTextLineInfo ) 685 { 686 return createTextLinesPolyPolygon( 687 ::basegfx::B2DPoint( rStartOffset, 688 0.0 ), 689 rLineWidth, 690 rTextLineInfo ); 691 } 692 } 693 } 694