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