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