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