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 <osl/mutex.hxx> 35 #include <vos/mutex.hxx> 36 #include <vcl/svapp.hxx> 37 #include <rtl/logfile.hxx> 38 #include <comphelper/sequence.hxx> 39 #include <comphelper/anytostring.hxx> 40 #include <cppuhelper/exc_hlp.hxx> 41 #include <cppcanvas/canvas.hxx> 42 #include <com/sun/star/rendering/XGraphicDevice.hpp> 43 #include <com/sun/star/rendering/TexturingMode.hpp> 44 #include <com/sun/star/uno/Sequence.hxx> 45 #include <com/sun/star/geometry/RealPoint2D.hpp> 46 #include <com/sun/star/rendering/PanoseProportion.hpp> 47 #include <com/sun/star/rendering/ViewState.hpp> 48 #include <com/sun/star/rendering/RenderState.hpp> 49 #include <com/sun/star/rendering/XCanvasFont.hpp> 50 #include <com/sun/star/rendering/XPolyPolygon2D.hpp> 51 #include <com/sun/star/rendering/XCanvas.hpp> 52 #include <com/sun/star/rendering/PathCapType.hpp> 53 #include <com/sun/star/rendering/PathJoinType.hpp> 54 #include <basegfx/tools/canvastools.hxx> 55 #include <basegfx/tools/gradienttools.hxx> 56 #include <basegfx/numeric/ftools.hxx> 57 #include <basegfx/polygon/b2dpolypolygontools.hxx> 58 #include <basegfx/polygon/b2dpolygontools.hxx> 59 #include <basegfx/polygon/b2dpolygon.hxx> 60 #include <basegfx/polygon/b2dpolypolygon.hxx> 61 #include <basegfx/matrix/b2dhommatrix.hxx> 62 #include <basegfx/vector/b2dsize.hxx> 63 #include <basegfx/range/b2drectangle.hxx> 64 #include <basegfx/point/b2dpoint.hxx> 65 #include <basegfx/tuple/b2dtuple.hxx> 66 #include <basegfx/polygon/b2dpolygonclipper.hxx> 67 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 68 #include <canvas/canvastools.hxx> 69 #include <vcl/canvastools.hxx> 70 #include <vcl/salbtype.hxx> 71 #include <vcl/gdimtf.hxx> 72 #include <vcl/metaact.hxx> 73 #include <vcl/virdev.hxx> 74 #include <vcl/metric.hxx> 75 #include <vcl/graphictools.hxx> 76 #include <tools/poly.hxx> 77 #include <i18npool/mslangid.hxx> 78 #include <implrenderer.hxx> 79 #include <tools.hxx> 80 #include <outdevstate.hxx> 81 #include <action.hxx> 82 #include <bitmapaction.hxx> 83 #include <lineaction.hxx> 84 #include <pointaction.hxx> 85 #include <polypolyaction.hxx> 86 #include <rendergraphicaction.hxx> 87 #include <textaction.hxx> 88 #include <transparencygroupaction.hxx> 89 #include <vector> 90 #include <algorithm> 91 #include <iterator> 92 #include <boost/scoped_array.hpp> 93 #include "mtftools.hxx" 94 #include "outdevstate.hxx" 95 #include <basegfx/matrix/b2dhommatrixtools.hxx> 96 97 98 using namespace ::com::sun::star; 99 100 101 // free support functions 102 // ====================== 103 namespace 104 { 105 template < class MetaActionType > void setStateColor( MetaActionType* pAct, 106 bool& rIsColorSet, 107 uno::Sequence< double >& rColorSequence, 108 const cppcanvas::CanvasSharedPtr& rCanvas ) 109 { 110 // set rIsColorSet and check for true at the same time 111 if( (rIsColorSet=pAct->IsSetting()) != false ) 112 { 113 ::Color aColor( pAct->GetColor() ); 114 115 // force alpha part of color to 116 // opaque. transparent painting is done 117 // explicitely via META_TRANSPARENT_ACTION 118 aColor.SetTransparency(0); 119 //aColor.SetTransparency(128); 120 121 rColorSequence = ::vcl::unotools::colorToDoubleSequence( 122 aColor, 123 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); 124 } 125 } 126 127 128 // state stack manipulators 129 // ------------------------ 130 void clearStateStack( ::cppcanvas::internal::VectorOfOutDevStates& rStates ) 131 { 132 rStates.clear(); 133 const ::cppcanvas::internal::OutDevState aDefaultState; 134 rStates.push_back( aDefaultState ); 135 } 136 137 ::cppcanvas::internal::OutDevState& getState( ::cppcanvas::internal::VectorOfOutDevStates& rStates ) 138 { 139 return rStates.back(); 140 } 141 142 const ::cppcanvas::internal::OutDevState& getState( const ::cppcanvas::internal::VectorOfOutDevStates& rStates ) 143 { 144 return rStates.back(); 145 } 146 147 void pushState( ::cppcanvas::internal::VectorOfOutDevStates& rStates, 148 sal_uInt16 nFlags ) 149 { 150 rStates.push_back( getState( rStates ) ); 151 getState( rStates ).pushFlags = nFlags; 152 } 153 154 void popState( ::cppcanvas::internal::VectorOfOutDevStates& rStates ) 155 { 156 if( getState( rStates ).pushFlags != PUSH_ALL ) 157 { 158 // a state is pushed which is incomplete, i.e. does not 159 // restore everything to the previous stack level when 160 // popped. 161 // That means, we take the old state, and restore every 162 // OutDevState member whose flag is set, from the new to the 163 // old state. Then the new state gets overwritten by the 164 // calculated state 165 166 // preset to-be-calculated new state with old state 167 ::cppcanvas::internal::OutDevState aCalculatedNewState( getState( rStates ) ); 168 169 // selectively copy to-be-restored content over saved old 170 // state 171 rStates.pop_back(); 172 173 const ::cppcanvas::internal::OutDevState& rNewState( getState( rStates ) ); 174 175 if( (aCalculatedNewState.pushFlags & PUSH_LINECOLOR) ) 176 { 177 aCalculatedNewState.lineColor = rNewState.lineColor; 178 aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet; 179 } 180 181 if( (aCalculatedNewState.pushFlags & PUSH_FILLCOLOR) ) 182 { 183 aCalculatedNewState.fillColor = rNewState.fillColor; 184 aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet; 185 } 186 187 if( (aCalculatedNewState.pushFlags & PUSH_FONT) ) 188 { 189 aCalculatedNewState.xFont = rNewState.xFont; 190 aCalculatedNewState.fontRotation = rNewState.fontRotation; 191 aCalculatedNewState.textReliefStyle = rNewState.textReliefStyle; 192 aCalculatedNewState.textOverlineStyle = rNewState.textOverlineStyle; 193 aCalculatedNewState.textUnderlineStyle = rNewState.textUnderlineStyle; 194 aCalculatedNewState.textStrikeoutStyle = rNewState.textStrikeoutStyle; 195 aCalculatedNewState.textEmphasisMarkStyle = rNewState.textEmphasisMarkStyle; 196 aCalculatedNewState.isTextEffectShadowSet = rNewState.isTextEffectShadowSet; 197 aCalculatedNewState.isTextWordUnderlineSet = rNewState.isTextWordUnderlineSet; 198 aCalculatedNewState.isTextOutlineModeSet = rNewState.isTextOutlineModeSet; 199 } 200 201 if( (aCalculatedNewState.pushFlags & PUSH_TEXTCOLOR) ) 202 { 203 aCalculatedNewState.textColor = rNewState.textColor; 204 } 205 206 if( (aCalculatedNewState.pushFlags & PUSH_MAPMODE) ) 207 { 208 aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform; 209 } 210 211 if( (aCalculatedNewState.pushFlags & PUSH_CLIPREGION) ) 212 { 213 aCalculatedNewState.clip = rNewState.clip; 214 aCalculatedNewState.clipRect = rNewState.clipRect; 215 aCalculatedNewState.xClipPoly = rNewState.xClipPoly; 216 } 217 218 // TODO(F2): Raster ops NYI 219 // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) ) 220 // { 221 // } 222 223 if( (aCalculatedNewState.pushFlags & PUSH_TEXTFILLCOLOR) ) 224 { 225 aCalculatedNewState.textFillColor = rNewState.textFillColor; 226 aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet; 227 } 228 229 if( (aCalculatedNewState.pushFlags & PUSH_TEXTALIGN) ) 230 { 231 aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint; 232 } 233 234 // TODO(F1): Refpoint handling NYI 235 // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) ) 236 // { 237 // } 238 239 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLINECOLOR) ) 240 { 241 aCalculatedNewState.textLineColor = rNewState.textLineColor; 242 aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet; 243 } 244 245 if( (aCalculatedNewState.pushFlags & PUSH_TEXTLAYOUTMODE) ) 246 { 247 aCalculatedNewState.textAlignment = rNewState.textAlignment; 248 aCalculatedNewState.textDirection = rNewState.textDirection; 249 } 250 251 // TODO(F2): Text language handling NYI 252 // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) ) 253 // { 254 // } 255 256 // always copy push mode 257 aCalculatedNewState.pushFlags = rNewState.pushFlags; 258 259 // flush to stack 260 getState( rStates ) = aCalculatedNewState; 261 } 262 else 263 { 264 rStates.pop_back(); 265 } 266 } 267 268 void setupStrokeAttributes( rendering::StrokeAttributes& o_rStrokeAttributes, 269 const ::cppcanvas::internal::ActionFactoryParameters& rParms, 270 const LineInfo& rLineInfo ) 271 { 272 const ::basegfx::B2DSize aWidth( rLineInfo.GetWidth(), 0 ); 273 o_rStrokeAttributes.StrokeWidth = 274 (getState( rParms.mrStates ).mapModeTransform * aWidth).getX(); 275 276 // setup reasonable defaults 277 o_rStrokeAttributes.MiterLimit = 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0 278 o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT; 279 o_rStrokeAttributes.EndCapType = rendering::PathCapType::BUTT; 280 281 switch(rLineInfo.GetLineJoin()) 282 { 283 default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE 284 o_rStrokeAttributes.JoinType = rendering::PathJoinType::NONE; 285 break; 286 case basegfx::B2DLINEJOIN_BEVEL: 287 o_rStrokeAttributes.JoinType = rendering::PathJoinType::BEVEL; 288 break; 289 case basegfx::B2DLINEJOIN_MITER: 290 o_rStrokeAttributes.JoinType = rendering::PathJoinType::MITER; 291 break; 292 case basegfx::B2DLINEJOIN_ROUND: 293 o_rStrokeAttributes.JoinType = rendering::PathJoinType::ROUND; 294 break; 295 } 296 297 if( LINE_DASH == rLineInfo.GetStyle() ) 298 { 299 const ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) ); 300 301 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing. 302 303 // interpret dash info only if explicitely enabled as 304 // style 305 const ::basegfx::B2DSize aDistance( rLineInfo.GetDistance(), 0 ); 306 const double nDistance( (rState.mapModeTransform * aDistance).getX() ); 307 308 const ::basegfx::B2DSize aDashLen( rLineInfo.GetDashLen(), 0 ); 309 const double nDashLen( (rState.mapModeTransform * aDashLen).getX() ); 310 311 const ::basegfx::B2DSize aDotLen( rLineInfo.GetDotLen(), 0 ); 312 const double nDotLen( (rState.mapModeTransform * aDotLen).getX() ); 313 314 const sal_Int32 nNumArryEntries( 2*rLineInfo.GetDashCount() + 315 2*rLineInfo.GetDotCount() ); 316 317 o_rStrokeAttributes.DashArray.realloc( nNumArryEntries ); 318 double* pDashArray = o_rStrokeAttributes.DashArray.getArray(); 319 320 321 // iteratively fill dash array, first with dashs, then 322 // with dots. 323 // =================================================== 324 325 sal_Int32 nCurrEntry=0; 326 327 for( sal_Int32 i=0; i<rLineInfo.GetDashCount(); ++i ) 328 { 329 pDashArray[nCurrEntry++] = nDashLen; 330 pDashArray[nCurrEntry++] = nDistance; 331 } 332 for( sal_Int32 i=0; i<rLineInfo.GetDotCount(); ++i ) 333 { 334 pDashArray[nCurrEntry++] = nDotLen; 335 pDashArray[nCurrEntry++] = nDistance; 336 } 337 } 338 } 339 340 341 /** Create masked BitmapEx, where the white areas of rBitmap are 342 transparent, and the other appear in rMaskColor. 343 */ 344 BitmapEx createMaskBmpEx( const Bitmap& rBitmap, 345 const ::Color& rMaskColor ) 346 { 347 const ::Color aWhite( COL_WHITE ); 348 BitmapPalette aBiLevelPalette(2); 349 aBiLevelPalette[0] = aWhite; 350 aBiLevelPalette[1] = rMaskColor; 351 352 Bitmap aMask( rBitmap.CreateMask( aWhite )); 353 Bitmap aSolid( rBitmap.GetSizePixel(), 354 1, 355 &aBiLevelPalette ); 356 aSolid.Erase( rMaskColor ); 357 358 return BitmapEx( aSolid, aMask ); 359 } 360 361 /** Shameless rip from vcl/source/gdi/outdev3.cxx 362 363 Should consolidate, into something like basetxt... 364 */ 365 sal_Unicode getLocalizedChar( sal_Unicode nChar, LanguageType eLang ) 366 { 367 // currently only conversion from ASCII digits is interesting 368 if( (nChar < '0') || ('9' < nChar) ) 369 return nChar; 370 371 sal_Unicode nOffset(0); 372 // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region. 373 // CAVEAT! To some like Mongolian MS assigned the same primary language 374 // although the script type is different! 375 switch( eLang & LANGUAGE_MASK_PRIMARY ) 376 { 377 default: 378 break; 379 380 case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY: 381 case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY: 382 case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //??? 383 nOffset = 0x0660 - '0'; // arabic/persian/urdu 384 break; 385 case LANGUAGE_BENGALI & LANGUAGE_MASK_PRIMARY: 386 nOffset = 0x09E6 - '0'; // bengali 387 break; 388 case LANGUAGE_BURMESE & LANGUAGE_MASK_PRIMARY: 389 nOffset = 0x1040 - '0'; // burmese 390 break; 391 case LANGUAGE_HINDI & LANGUAGE_MASK_PRIMARY: 392 nOffset = 0x0966 - '0'; // devanagari 393 break; 394 case LANGUAGE_GUJARATI & LANGUAGE_MASK_PRIMARY: 395 nOffset = 0x0AE6 - '0'; // gujarati 396 break; 397 case LANGUAGE_KANNADA & LANGUAGE_MASK_PRIMARY: 398 nOffset = 0x0CE6 - '0'; // kannada 399 break; 400 case LANGUAGE_KHMER & LANGUAGE_MASK_PRIMARY: 401 nOffset = 0x17E0 - '0'; // khmer 402 break; 403 case LANGUAGE_LAO & LANGUAGE_MASK_PRIMARY: 404 nOffset = 0x0ED0 - '0'; // lao 405 break; 406 case LANGUAGE_MALAYALAM & LANGUAGE_MASK_PRIMARY: 407 nOffset = 0x0D66 - '0'; // malayalam 408 break; 409 case LANGUAGE_MONGOLIAN & LANGUAGE_MASK_PRIMARY: 410 if (eLang == LANGUAGE_MONGOLIAN_MONGOLIAN) 411 nOffset = 0x1810 - '0'; // mongolian 412 else 413 nOffset = 0; // mongolian cyrillic 414 break; 415 case LANGUAGE_ORIYA & LANGUAGE_MASK_PRIMARY: 416 nOffset = 0x0B66 - '0'; // oriya 417 break; 418 case LANGUAGE_TAMIL & LANGUAGE_MASK_PRIMARY: 419 nOffset = 0x0BE7 - '0'; // tamil 420 break; 421 case LANGUAGE_TELUGU & LANGUAGE_MASK_PRIMARY: 422 nOffset = 0x0C66 - '0'; // telugu 423 break; 424 case LANGUAGE_THAI & LANGUAGE_MASK_PRIMARY: 425 nOffset = 0x0E50 - '0'; // thai 426 break; 427 case LANGUAGE_TIBETAN & LANGUAGE_MASK_PRIMARY: 428 nOffset = 0x0F20 - '0'; // tibetan 429 break; 430 } 431 432 nChar = sal::static_int_cast<sal_Unicode>(nChar + nOffset); 433 return nChar; 434 } 435 436 void convertToLocalizedNumerals( XubString& rStr, 437 LanguageType eTextLanguage ) 438 { 439 const sal_Unicode* pBase = rStr.GetBuffer(); 440 const sal_Unicode* pBegin = pBase + 0; 441 const xub_StrLen nEndIndex = rStr.Len(); 442 const sal_Unicode* pEnd = pBase + nEndIndex; 443 444 for( ; pBegin < pEnd; ++pBegin ) 445 { 446 // TODO: are there non-digit localizations? 447 if( (*pBegin >= '0') && (*pBegin <= '9') ) 448 { 449 // translate characters to local preference 450 sal_Unicode cChar = getLocalizedChar( *pBegin, eTextLanguage ); 451 if( cChar != *pBegin ) 452 rStr.SetChar( sal::static_int_cast<sal_uInt16>(pBegin - pBase), cChar ); 453 } 454 } 455 } 456 } 457 458 459 namespace cppcanvas 460 { 461 namespace internal 462 { 463 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon& rPolyPoly, 464 const ActionFactoryParameters& rParms ) 465 { 466 const OutDevState& rState( getState( rParms.mrStates ) ); 467 if( (!rState.isLineColorSet && 468 !rState.isFillColorSet) || 469 (rState.lineColor.getLength() == 0 && 470 rState.fillColor.getLength() == 0) ) 471 { 472 return false; 473 } 474 475 ActionSharedPtr pPolyAction( 476 internal::PolyPolyActionFactory::createPolyPolyAction( 477 rPolyPoly, rParms.mrCanvas, rState ) ); 478 479 if( pPolyAction ) 480 { 481 maActions.push_back( 482 MtfAction( 483 pPolyAction, 484 rParms.mrCurrActionIndex ) ); 485 486 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1; 487 } 488 489 return true; 490 } 491 492 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon& rPoly, 493 const ActionFactoryParameters& rParms ) 494 { 495 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly ), 496 rParms ); 497 } 498 499 void ImplRenderer::skipContent( GDIMetaFile& rMtf, 500 const char* pCommentString, 501 sal_Int32& io_rCurrActionIndex ) const 502 { 503 ENSURE_OR_THROW( pCommentString, 504 "ImplRenderer::skipContent(): NULL string given" ); 505 506 MetaAction* pCurrAct; 507 while( (pCurrAct=rMtf.NextAction()) != NULL ) 508 { 509 // increment action index, we've skipped an action. 510 ++io_rCurrActionIndex; 511 512 if( pCurrAct->GetType() == META_COMMENT_ACTION && 513 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( 514 pCommentString ) == COMPARE_EQUAL ) 515 { 516 // requested comment found, done 517 return; 518 } 519 } 520 521 // EOF 522 return; 523 } 524 525 bool ImplRenderer::isActionContained( GDIMetaFile& rMtf, 526 const char* pCommentString, 527 sal_uInt16 nType ) const 528 { 529 ENSURE_OR_THROW( pCommentString, 530 "ImplRenderer::isActionContained(): NULL string given" ); 531 532 bool bRet( false ); 533 534 // at least _one_ call to GDIMetaFile::NextAction() is 535 // executed 536 sal_uIntPtr nPos( 1 ); 537 538 MetaAction* pCurrAct; 539 while( (pCurrAct=rMtf.NextAction()) != NULL ) 540 { 541 if( pCurrAct->GetType() == nType ) 542 { 543 bRet = true; // action type found 544 break; 545 } 546 547 if( pCurrAct->GetType() == META_COMMENT_ACTION && 548 static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( 549 pCommentString ) == COMPARE_EQUAL ) 550 { 551 // delimiting end comment found, done 552 bRet = false; // not yet found 553 break; 554 } 555 556 ++nPos; 557 } 558 559 // rewind metafile to previous position (this method must 560 // not change the current metaaction) 561 while( nPos-- ) 562 rMtf.WindPrev(); 563 564 if( !pCurrAct ) 565 { 566 // EOF, and not yet found 567 bRet = false; 568 } 569 570 return bRet; 571 } 572 573 void ImplRenderer::createGradientAction( const ::PolyPolygon& rPoly, 574 const ::Gradient& rGradient, 575 const ActionFactoryParameters& rParms, 576 bool bIsPolygonRectangle, 577 bool bSubsettableActions ) 578 { 579 DBG_TESTSOLARMUTEX(); 580 581 ::basegfx::B2DPolyPolygon aDevicePoly( rPoly.getB2DPolyPolygon() ); 582 aDevicePoly.transform( getState( rParms.mrStates ).mapModeTransform ); 583 584 // decide, whether this gradient can be rendered natively 585 // by the canvas, or must be emulated via VCL gradient 586 // action extraction. 587 const sal_uInt16 nSteps( rGradient.GetSteps() ); 588 589 if( // step count is infinite, can use native canvas 590 // gradients here 591 nSteps == 0 || 592 // step count is sufficiently high, such that no 593 // discernible difference should be visible. 594 nSteps > 64 ) 595 { 596 uno::Reference< lang::XMultiServiceFactory> xFactory( 597 rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() ); 598 599 if( xFactory.is() ) 600 { 601 rendering::Texture aTexture; 602 603 aTexture.RepeatModeX = rendering::TexturingMode::CLAMP; 604 aTexture.RepeatModeY = rendering::TexturingMode::CLAMP; 605 aTexture.Alpha = 1.0; 606 607 608 // setup start/end color values 609 // ---------------------------- 610 611 // scale color coefficients with gradient intensities 612 const sal_uInt16 nStartIntensity( rGradient.GetStartIntensity() ); 613 ::Color aVCLStartColor( rGradient.GetStartColor() ); 614 aVCLStartColor.SetRed( (sal_uInt8)(aVCLStartColor.GetRed() * nStartIntensity / 100) ); 615 aVCLStartColor.SetGreen( (sal_uInt8)(aVCLStartColor.GetGreen() * nStartIntensity / 100) ); 616 aVCLStartColor.SetBlue( (sal_uInt8)(aVCLStartColor.GetBlue() * nStartIntensity / 100) ); 617 618 const sal_uInt16 nEndIntensity( rGradient.GetEndIntensity() ); 619 ::Color aVCLEndColor( rGradient.GetEndColor() ); 620 aVCLEndColor.SetRed( (sal_uInt8)(aVCLEndColor.GetRed() * nEndIntensity / 100) ); 621 aVCLEndColor.SetGreen( (sal_uInt8)(aVCLEndColor.GetGreen() * nEndIntensity / 100) ); 622 aVCLEndColor.SetBlue( (sal_uInt8)(aVCLEndColor.GetBlue() * nEndIntensity / 100) ); 623 624 uno::Reference<rendering::XColorSpace> xColorSpace( 625 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace()); 626 const uno::Sequence< double > aStartColor( 627 ::vcl::unotools::colorToDoubleSequence( aVCLStartColor, 628 xColorSpace )); 629 const uno::Sequence< double > aEndColor( 630 ::vcl::unotools::colorToDoubleSequence( aVCLEndColor, 631 xColorSpace )); 632 633 uno::Sequence< uno::Sequence < double > > aColors(2); 634 uno::Sequence< double > aStops(2); 635 636 if( rGradient.GetStyle() == GRADIENT_AXIAL ) 637 { 638 aStops.realloc(3); 639 aColors.realloc(3); 640 641 aStops[0] = 0.0; 642 aStops[1] = 0.5; 643 aStops[2] = 1.0; 644 645 aColors[0] = aEndColor; 646 aColors[1] = aStartColor; 647 aColors[2] = aEndColor; 648 } 649 else 650 { 651 aStops[0] = 0.0; 652 aStops[1] = 1.0; 653 654 aColors[0] = aStartColor; 655 aColors[1] = aEndColor; 656 } 657 658 const ::basegfx::B2DRectangle aBounds( 659 ::basegfx::tools::getRange(aDevicePoly) ); 660 const ::basegfx::B2DVector aOffset( 661 rGradient.GetOfsX() / 100.0, 662 rGradient.GetOfsY() / 100.0); 663 double fRotation( rGradient.GetAngle() * M_PI / 1800.0 ); 664 const double fBorder( rGradient.GetBorder() / 100.0 ); 665 666 basegfx::B2DHomMatrix aRot90; 667 aRot90.rotate(M_PI_2); 668 669 basegfx::ODFGradientInfo aGradInfo; 670 rtl::OUString aGradientService; 671 switch( rGradient.GetStyle() ) 672 { 673 case GRADIENT_LINEAR: 674 basegfx::tools::createLinearODFGradientInfo(aGradInfo, 675 aBounds, 676 nSteps, 677 fBorder, 678 fRotation); 679 // map odf to svg gradient orientation - x 680 // instead of y direction 681 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90; 682 aGradientService = rtl::OUString::createFromAscii("LinearGradient"); 683 break; 684 685 case GRADIENT_AXIAL: 686 { 687 // Adapt the border so that it is suitable 688 // for the axial gradient. An axial 689 // gradient consists of two linear 690 // gradients. Each of those covers half 691 // of the total size. In order to 692 // compensate for the condensed display of 693 // the linear gradients, we have to 694 // enlarge the area taken up by the actual 695 // gradient (1-fBorder). After that we 696 // have to turn the result back into a 697 // border value, hence the second (left 698 // most 1-... 699 const double fAxialBorder (1-2*(1-fBorder)); 700 basegfx::tools::createAxialODFGradientInfo(aGradInfo, 701 aBounds, 702 nSteps, 703 fAxialBorder, 704 fRotation); 705 // map odf to svg gradient orientation - x 706 // instead of y direction 707 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90; 708 709 // map odf axial gradient to 3-stop linear 710 // gradient - shift left by 0.5 711 basegfx::B2DHomMatrix aShift; 712 aShift.translate(-0.5,0); 713 aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aShift; 714 715 aGradientService = rtl::OUString::createFromAscii("LinearGradient"); 716 break; 717 } 718 719 case GRADIENT_RADIAL: 720 basegfx::tools::createRadialODFGradientInfo(aGradInfo, 721 aBounds, 722 aOffset, 723 nSteps, 724 fBorder); 725 aGradientService = rtl::OUString::createFromAscii("EllipticalGradient"); 726 break; 727 728 case GRADIENT_ELLIPTICAL: 729 basegfx::tools::createEllipticalODFGradientInfo(aGradInfo, 730 aBounds, 731 aOffset, 732 nSteps, 733 fBorder, 734 fRotation); 735 aGradientService = rtl::OUString::createFromAscii("EllipticalGradient"); 736 break; 737 738 case GRADIENT_SQUARE: 739 basegfx::tools::createSquareODFGradientInfo(aGradInfo, 740 aBounds, 741 aOffset, 742 nSteps, 743 fBorder, 744 fRotation); 745 aGradientService = rtl::OUString::createFromAscii("RectangularGradient"); 746 break; 747 748 case GRADIENT_RECT: 749 basegfx::tools::createRectangularODFGradientInfo(aGradInfo, 750 aBounds, 751 aOffset, 752 nSteps, 753 fBorder, 754 fRotation); 755 aGradientService = rtl::OUString::createFromAscii("RectangularGradient"); 756 break; 757 758 default: 759 ENSURE_OR_THROW( false, 760 "ImplRenderer::createGradientAction(): Unexpected gradient type" ); 761 break; 762 } 763 764 // As the texture coordinate space is relative to 765 // the polygon coordinate space (NOT to the 766 // polygon itself), move gradient to the start of 767 // the actual polygon. If we skip this, the 768 // gradient will always display at the origin, and 769 // not within the polygon bound (which might be 770 // miles away from the origin). 771 aGradInfo.maTextureTransform.translate( aBounds.getMinX(), 772 aBounds.getMinY() ); 773 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform, 774 aGradInfo.maTextureTransform ); 775 776 uno::Sequence<uno::Any> args(3); 777 beans::PropertyValue aProp; 778 aProp.Name = rtl::OUString::createFromAscii("Colors"); 779 aProp.Value <<= aColors; 780 args[0] <<= aProp; 781 aProp.Name = rtl::OUString::createFromAscii("Stops"); 782 aProp.Value <<= aStops; 783 args[1] <<= aProp; 784 aProp.Name = rtl::OUString::createFromAscii("AspectRatio"); 785 aProp.Value <<= aGradInfo.mfAspectRatio; 786 args[2] <<= aProp; 787 788 aTexture.Gradient.set( 789 xFactory->createInstanceWithArguments(aGradientService, 790 args), 791 uno::UNO_QUERY); 792 if( aTexture.Gradient.is() ) 793 { 794 ActionSharedPtr pPolyAction( 795 internal::PolyPolyActionFactory::createPolyPolyAction( 796 aDevicePoly, 797 rParms.mrCanvas, 798 getState( rParms.mrStates ), 799 aTexture ) ); 800 801 if( pPolyAction ) 802 { 803 maActions.push_back( 804 MtfAction( 805 pPolyAction, 806 rParms.mrCurrActionIndex ) ); 807 808 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1; 809 } 810 811 // done, using native gradients 812 return; 813 } 814 } 815 } 816 817 // cannot currently use native canvas gradients, as a 818 // finite step size is given (this funny feature is not 819 // supported by the XCanvas API) 820 pushState( rParms.mrStates, PUSH_ALL ); 821 822 if( !bIsPolygonRectangle ) 823 { 824 // only clip, if given polygon is not a rectangle in 825 // the first place (the gradient is always limited to 826 // the given bound rect) 827 updateClipping( 828 aDevicePoly, 829 rParms, 830 true ); 831 } 832 833 GDIMetaFile aTmpMtf; 834 rParms.mrVDev.AddGradientActions( rPoly.GetBoundRect(), 835 rGradient, 836 aTmpMtf ); 837 838 createActions( aTmpMtf, rParms, bSubsettableActions ); 839 840 popState( rParms.mrStates ); 841 } 842 843 uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( double& o_rFontRotation, 844 const ::Font& rFont, 845 const ActionFactoryParameters& rParms ) const 846 { 847 rendering::FontRequest aFontRequest; 848 849 if( rParms.mrParms.maFontName.is_initialized() ) 850 aFontRequest.FontDescription.FamilyName = *rParms.mrParms.maFontName; 851 else 852 aFontRequest.FontDescription.FamilyName = rFont.GetName(); 853 854 aFontRequest.FontDescription.StyleName = rFont.GetStyleName(); 855 856 aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO; 857 aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO; 858 859 // TODO(F2): improve vclenum->panose conversion 860 aFontRequest.FontDescription.FontDescription.Weight = 861 rParms.mrParms.maFontWeight.is_initialized() ? 862 *rParms.mrParms.maFontWeight : 863 ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) ); 864 aFontRequest.FontDescription.FontDescription.Letterform = 865 rParms.mrParms.maFontLetterForm.is_initialized() ? 866 *rParms.mrParms.maFontLetterForm : 867 (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9; 868 aFontRequest.FontDescription.FontDescription.Proportion = 869 rParms.mrParms.maFontProportion.is_initialized() ? 870 *rParms.mrParms.maFontProportion : 871 (rFont.GetPitch() == PITCH_FIXED) 872 ? rendering::PanoseProportion::MONO_SPACED 873 : rendering::PanoseProportion::ANYTHING; 874 875 LanguageType aLang = rFont.GetLanguage(); 876 aFontRequest.Locale = MsLangId::convertLanguageToLocale(aLang, false); 877 878 // setup state-local text transformation, 879 // if the font be rotated 880 const short nFontAngle( rFont.GetOrientation() ); 881 if( nFontAngle != 0 ) 882 { 883 // set to unity transform rotated by font angle 884 const double nAngle( nFontAngle * (F_PI / 1800.0) ); 885 o_rFontRotation = -nAngle; 886 } 887 else 888 { 889 o_rFontRotation = 0.0; 890 } 891 892 geometry::Matrix2D aFontMatrix; 893 ::canvas::tools::setIdentityMatrix2D( aFontMatrix ); 894 895 // TODO(F2): use correct scale direction, font 896 // height might be width or anything else 897 898 // TODO(Q3): This code smells of programming by 899 // coincidence (the next two if statements) 900 const ::Size rFontSizeLog( rFont.GetSize() ); 901 const sal_Int32 nFontWidthLog = rFontSizeLog.Width(); 902 if( nFontWidthLog != 0 ) 903 { 904 ::Font aTestFont = rFont; 905 aTestFont.SetWidth( 0 ); 906 sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth(); 907 if( nNormalWidth != nFontWidthLog ) 908 if( nNormalWidth ) 909 aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth; 910 } 911 912 // #i52608# apply map mode scale also to font matrix - an 913 // anisotrophic mapmode must be reflected in an 914 // anisotrophic font matrix scale. 915 const OutDevState& rState( getState( rParms.mrStates ) ); 916 if( !::basegfx::fTools::equal( 917 rState.mapModeTransform.get(0,0), 918 rState.mapModeTransform.get(1,1)) ) 919 { 920 const double nScaleX( rState.mapModeTransform.get(0,0) ); 921 const double nScaleY( rState.mapModeTransform.get(1,1) ); 922 923 // note: no reason to check for division by zero, we 924 // always have the value closer (or equal) to zero as 925 // the nominator. 926 if( fabs(nScaleX) < fabs(nScaleY) ) 927 aFontMatrix.m00 *= nScaleX / nScaleY; 928 else 929 aFontMatrix.m11 *= nScaleY / nScaleX; 930 } 931 aFontRequest.CellSize = (rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY(); 932 933 return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest, 934 uno::Sequence< beans::PropertyValue >(), 935 aFontMatrix ); 936 } 937 938 // create text effects such as shadow/relief/embossed 939 void ImplRenderer::createTextAction( const ::Point& rStartPoint, 940 const String rString, 941 int nIndex, 942 int nLength, 943 const sal_Int32* pCharWidths, 944 const ActionFactoryParameters& rParms, 945 bool bSubsettableActions ) 946 { 947 ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.Len() + nIndex, 948 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" ); 949 950 if( !nLength ) 951 return; // zero-length text, no visible output 952 953 const OutDevState& rState( getState( rParms.mrStates ) ); 954 955 // TODO(F2): implement all text effects 956 // if( rState.textAlignment ); // TODO(F2): NYI 957 958 ::Color aShadowColor( COL_AUTO ); 959 ::Color aReliefColor( COL_AUTO ); 960 ::Size aShadowOffset; 961 ::Size aReliefOffset; 962 963 uno::Reference<rendering::XColorSpace> xColorSpace( 964 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); 965 966 if( rState.isTextEffectShadowSet ) 967 { 968 // calculate shadow offset (similar to outdev3.cxx) 969 // TODO(F3): better match with outdev3.cxx 970 sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0)); 971 if( nShadowOffset < 1 ) 972 nShadowOffset = 1; 973 974 aShadowOffset.setWidth( nShadowOffset ); 975 aShadowOffset.setHeight( nShadowOffset ); 976 977 // determine shadow color (from outdev3.cxx) 978 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor( 979 rState.textColor, xColorSpace ); 980 bool bIsDark = (aTextColor.GetColor() == COL_BLACK) 981 || (aTextColor.GetLuminance() < 8); 982 983 aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK; 984 aShadowColor.SetTransparency( aTextColor.GetTransparency() ); 985 } 986 987 if( rState.textReliefStyle ) 988 { 989 // calculate relief offset (similar to outdev3.cxx) 990 sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height(); 991 nReliefOffset += nReliefOffset/2; 992 if( nReliefOffset < 1 ) 993 nReliefOffset = 1; 994 995 if( rState.textReliefStyle == RELIEF_ENGRAVED ) 996 nReliefOffset = -nReliefOffset; 997 998 aReliefOffset.setWidth( nReliefOffset ); 999 aReliefOffset.setHeight( nReliefOffset ); 1000 1001 // determine relief color (from outdev3.cxx) 1002 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor( 1003 rState.textColor, xColorSpace ); 1004 1005 aReliefColor = ::Color( COL_LIGHTGRAY ); 1006 1007 // we don't have a automatic color, so black is always 1008 // drawn on white (literally copied from 1009 // vcl/source/gdi/outdev3.cxx) 1010 if( aTextColor.GetColor() == COL_BLACK ) 1011 { 1012 aTextColor = ::Color( COL_WHITE ); 1013 getState( rParms.mrStates ).textColor = 1014 ::vcl::unotools::colorToDoubleSequence( 1015 aTextColor, xColorSpace ); 1016 } 1017 1018 if( aTextColor.GetColor() == COL_WHITE ) 1019 aReliefColor = ::Color( COL_BLACK ); 1020 aReliefColor.SetTransparency( aTextColor.GetTransparency() ); 1021 } 1022 1023 // create the actual text action 1024 ActionSharedPtr pTextAction( 1025 TextActionFactory::createTextAction( 1026 rStartPoint, 1027 aReliefOffset, 1028 aReliefColor, 1029 aShadowOffset, 1030 aShadowColor, 1031 rString, 1032 nIndex, 1033 nLength, 1034 pCharWidths, 1035 rParms.mrVDev, 1036 rParms.mrCanvas, 1037 rState, 1038 rParms.mrParms, 1039 bSubsettableActions ) ); 1040 1041 ActionSharedPtr pStrikeoutTextAction; 1042 1043 if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH ) 1044 { 1045 long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength ); 1046 1047 xub_Unicode pChars[5]; 1048 if ( rState.textStrikeoutStyle == STRIKEOUT_X ) 1049 pChars[0] = 'X'; 1050 else 1051 pChars[0] = '/'; 1052 pChars[3]=pChars[2]=pChars[1]=pChars[0]; 1053 1054 long nStrikeoutWidth = nWidth; 1055 String aStrikeoutTest( pChars, 4 ); 1056 1057 if( aStrikeoutTest.Len() ) 1058 { 1059 nStrikeoutWidth = ( rParms.mrVDev.GetTextWidth( aStrikeoutTest ) + 2 ) / 4; 1060 aStrikeoutTest.Erase(); 1061 1062 if( nStrikeoutWidth <= 0 ) 1063 nStrikeoutWidth = 1; 1064 } 1065 1066 long nMaxWidth = nStrikeoutWidth/2; 1067 if ( nMaxWidth < 2 ) 1068 nMaxWidth = 2; 1069 nMaxWidth += nWidth + 1; 1070 1071 long nFullStrikeoutWidth = 0; 1072 String aStrikeoutText( pChars, 0 ); 1073 while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 ) 1074 aStrikeoutText += pChars[0]; 1075 1076 1077 sal_Int32 nStartPos = 0; 1078 xub_StrLen nLen = aStrikeoutText.Len(); 1079 1080 if( nLen ) 1081 { 1082 long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen; 1083 nStrikeoutWidth += nInterval; 1084 sal_Int32* pStrikeoutCharWidths = new sal_Int32[nLen]; 1085 1086 for ( int i = 0;i<nLen; i++) 1087 { 1088 pStrikeoutCharWidths[i] = nStrikeoutWidth; 1089 } 1090 1091 for ( int i = 1;i< nLen; i++ ) 1092 { 1093 pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ]; 1094 } 1095 1096 pStrikeoutTextAction = 1097 TextActionFactory::createTextAction( 1098 rStartPoint, 1099 aReliefOffset, 1100 aReliefColor, 1101 aShadowOffset, 1102 aShadowColor, 1103 aStrikeoutText, 1104 nStartPos, 1105 aStrikeoutText.Len(), 1106 pStrikeoutCharWidths, 1107 rParms.mrVDev, 1108 rParms.mrCanvas, 1109 rState, 1110 rParms.mrParms, 1111 bSubsettableActions ) ; 1112 } 1113 } 1114 1115 if( pTextAction ) 1116 { 1117 maActions.push_back( 1118 MtfAction( 1119 pTextAction, 1120 rParms.mrCurrActionIndex ) ); 1121 1122 if ( pStrikeoutTextAction ) 1123 { 1124 maActions.push_back( 1125 MtfAction( 1126 pStrikeoutTextAction, 1127 rParms.mrCurrActionIndex ) ); 1128 } 1129 1130 rParms.mrCurrActionIndex += pTextAction->getActionCount()-1; 1131 } 1132 } 1133 1134 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly, 1135 const ActionFactoryParameters& rParms, 1136 bool bIntersect ) 1137 { 1138 ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) ); 1139 ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly ); 1140 1141 const bool bEmptyClipRect( rState.clipRect.IsEmpty() ); 1142 const bool bEmptyClipPoly( rState.clip.count() == 0 ); 1143 1144 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect, 1145 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" ); 1146 1147 if( !bIntersect || 1148 (bEmptyClipRect && bEmptyClipPoly) ) 1149 { 1150 rState.clip = rClipPoly; 1151 } 1152 else 1153 { 1154 if( !bEmptyClipRect ) 1155 { 1156 // TODO(P3): Use Liang-Barsky polygon clip here, 1157 // after all, one object is just a rectangle! 1158 1159 // convert rect to polygon beforehand, must revert 1160 // to general polygon clipping here. 1161 rState.clip = ::basegfx::B2DPolyPolygon( 1162 ::basegfx::tools::createPolygonFromRect( 1163 // #121100# VCL rectangular clips always 1164 // include one more pixel to the right 1165 // and the bottom 1166 ::basegfx::B2DRectangle( rState.clipRect.Left(), 1167 rState.clipRect.Top(), 1168 rState.clipRect.Right()+1, 1169 rState.clipRect.Bottom()+1 ) ) ); 1170 } 1171 1172 // AW: Simplified 1173 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon( 1174 aClipPoly, rState.clip, true, false); 1175 } 1176 1177 // by now, our clip resides in the OutDevState::clip 1178 // poly-polygon. 1179 rState.clipRect.SetEmpty(); 1180 1181 if( rState.clip.count() == 0 ) 1182 { 1183 if( rState.clipRect.IsEmpty() ) 1184 { 1185 rState.xClipPoly.clear(); 1186 } 1187 else 1188 { 1189 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1190 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1191 ::basegfx::B2DPolyPolygon( 1192 ::basegfx::tools::createPolygonFromRect( 1193 // #121100# VCL rectangular clips 1194 // always include one more pixel to 1195 // the right and the bottom 1196 ::basegfx::B2DRectangle( rState.clipRect.Left(), 1197 rState.clipRect.Top(), 1198 rState.clipRect.Right()+1, 1199 rState.clipRect.Bottom()+1 ) ) ) ); 1200 } 1201 } 1202 else 1203 { 1204 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1205 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1206 rState.clip ); 1207 } 1208 } 1209 1210 void ImplRenderer::updateClipping( const ::Rectangle& rClipRect, 1211 const ActionFactoryParameters& rParms, 1212 bool bIntersect ) 1213 { 1214 ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) ); 1215 1216 const bool bEmptyClipRect( rState.clipRect.IsEmpty() ); 1217 const bool bEmptyClipPoly( rState.clip.count() == 0 ); 1218 1219 ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect, 1220 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" ); 1221 1222 if( !bIntersect || 1223 (bEmptyClipRect && bEmptyClipPoly) ) 1224 { 1225 rState.clipRect = rClipRect; 1226 rState.clip.clear(); 1227 } 1228 else if( bEmptyClipPoly ) 1229 { 1230 rState.clipRect.Intersection( rClipRect ); 1231 rState.clip.clear(); 1232 } 1233 else 1234 { 1235 // TODO(P3): Handle a fourth case here, when all clip 1236 // polygons are rectangular, once B2DMultiRange's 1237 // sweep line implementation is done. 1238 1239 // general case: convert to polygon and clip 1240 // ----------------------------------------- 1241 1242 // convert rect to polygon beforehand, must revert 1243 // to general polygon clipping here. 1244 ::basegfx::B2DPolyPolygon aClipPoly( 1245 ::basegfx::tools::createPolygonFromRect( 1246 ::basegfx::B2DRectangle( rClipRect.Left(), 1247 rClipRect.Top(), 1248 rClipRect.Right(), 1249 rClipRect.Bottom() ) ) ); 1250 1251 rState.clipRect.SetEmpty(); 1252 1253 // AW: Simplified 1254 rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon( 1255 aClipPoly, rState.clip, true, false); 1256 } 1257 1258 if( rState.clip.count() == 0 ) 1259 { 1260 if( rState.clipRect.IsEmpty() ) 1261 { 1262 rState.xClipPoly.clear(); 1263 } 1264 else 1265 { 1266 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1267 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1268 ::basegfx::B2DPolyPolygon( 1269 ::basegfx::tools::createPolygonFromRect( 1270 // #121100# VCL rectangular clips 1271 // always include one more pixel to 1272 // the right and the bottom 1273 ::basegfx::B2DRectangle( rState.clipRect.Left(), 1274 rState.clipRect.Top(), 1275 rState.clipRect.Right()+1, 1276 rState.clipRect.Bottom()+1 ) ) ) ); 1277 } 1278 } 1279 else 1280 { 1281 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( 1282 rParms.mrCanvas->getUNOCanvas()->getDevice(), 1283 rState.clip ); 1284 } 1285 } 1286 1287 bool ImplRenderer::createActions( GDIMetaFile& rMtf, 1288 const ActionFactoryParameters& rFactoryParms, 1289 bool bSubsettableActions ) 1290 { 1291 /* TODO(P2): interpret mtf-comments 1292 ================================ 1293 1294 - gradient fillings (do that via comments) 1295 1296 - think about mapping. _If_ we do everything in logical 1297 coordinates (which would solve the probs for stroke 1298 widths and text offsets), then we would have to 1299 recalc scaling for every drawing operation. This is 1300 because the outdev map mode might change at any time. 1301 Also keep in mind, that, although we've double precision 1302 float arithmetic now, different offsets might still 1303 generate different roundings (aka 1304 'OutputDevice::SetPixelOffset()) 1305 1306 */ 1307 1308 // alias common parameters 1309 VectorOfOutDevStates& rStates(rFactoryParms.mrStates); 1310 const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas); 1311 ::VirtualDevice& rVDev(rFactoryParms.mrVDev); 1312 const Parameters& rParms(rFactoryParms.mrParms); 1313 sal_Int32& io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex); 1314 1315 1316 // Loop over every metaaction 1317 // ========================== 1318 MetaAction* pCurrAct; 1319 1320 // TODO(P1): think about caching 1321 for( pCurrAct=rMtf.FirstAction(); 1322 pCurrAct; 1323 pCurrAct = rMtf.NextAction() ) 1324 { 1325 // execute every action, to keep VDev state up-to-date 1326 // currently used only for 1327 // - the map mode 1328 // - the line/fill color when processing a META_TRANSPARENT_ACTION 1329 // - SetFont to process font metric specific actions 1330 pCurrAct->Execute( &rVDev ); 1331 1332 switch( pCurrAct->GetType() ) 1333 { 1334 // ------------------------------------------------------------ 1335 1336 // In the first part of this monster-switch, we 1337 // handle all state-changing meta actions. These 1338 // are all handled locally. 1339 1340 // ------------------------------------------------------------ 1341 1342 case META_PUSH_ACTION: 1343 { 1344 MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct); 1345 pushState( rStates, 1346 pPushAction->GetFlags() ); 1347 } 1348 break; 1349 1350 case META_POP_ACTION: 1351 popState( rStates ); 1352 break; 1353 1354 case META_TEXTLANGUAGE_ACTION: 1355 // FALLTHROUGH intended 1356 case META_REFPOINT_ACTION: 1357 // handled via pCurrAct->Execute( &rVDev ) 1358 break; 1359 1360 case META_MAPMODE_ACTION: 1361 // modify current mapModeTransformation 1362 // transformation, such that subsequent 1363 // coordinates map correctly 1364 tools::calcLogic2PixelAffineTransform( getState( rStates ).mapModeTransform, 1365 rVDev ); 1366 break; 1367 1368 // monitor clip regions, to assemble clip polygon on our own 1369 case META_CLIPREGION_ACTION: 1370 { 1371 MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct); 1372 1373 if( !pClipAction->IsClipping() ) 1374 { 1375 // clear clipping 1376 getState( rStates ).clip.clear(); 1377 } 1378 else 1379 { 1380 if( !pClipAction->GetRegion().HasPolyPolygon() ) 1381 { 1382 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip " 1383 "region encountered, falling back to bounding box!" ); 1384 1385 // #121806# explicitely kept integer 1386 Rectangle aClipRect( 1387 rVDev.LogicToPixel( 1388 pClipAction->GetRegion().GetBoundRect() ) ); 1389 1390 // intersect current clip with given rect 1391 updateClipping( 1392 aClipRect, 1393 rFactoryParms, 1394 false ); 1395 } 1396 else 1397 { 1398 // set new clip polygon (don't intersect 1399 // with old one, just set it) 1400 1401 // #121806# explicitely kept integer 1402 updateClipping( 1403 rVDev.LogicToPixel( 1404 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(), 1405 rFactoryParms, 1406 false ); 1407 } 1408 } 1409 1410 break; 1411 } 1412 1413 case META_ISECTRECTCLIPREGION_ACTION: 1414 { 1415 MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct); 1416 1417 // #121806# explicitely kept integer 1418 Rectangle aClipRect( 1419 rVDev.LogicToPixel( pClipAction->GetRect() ) ); 1420 1421 // intersect current clip with given rect 1422 updateClipping( 1423 aClipRect, 1424 rFactoryParms, 1425 true ); 1426 1427 break; 1428 } 1429 1430 case META_ISECTREGIONCLIPREGION_ACTION: 1431 { 1432 MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct); 1433 1434 if( !pClipAction->GetRegion().HasPolyPolygon() ) 1435 { 1436 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip " 1437 "region encountered, falling back to bounding box!" ); 1438 1439 // #121806# explicitely kept integer 1440 Rectangle aClipRect( 1441 rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) ); 1442 1443 // intersect current clip with given rect 1444 updateClipping( 1445 aClipRect, 1446 rFactoryParms, 1447 true ); 1448 } 1449 else 1450 { 1451 // intersect current clip with given clip polygon 1452 1453 // #121806# explicitely kept integer 1454 updateClipping( 1455 rVDev.LogicToPixel( 1456 pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(), 1457 rFactoryParms, 1458 true ); 1459 } 1460 1461 break; 1462 } 1463 1464 case META_MOVECLIPREGION_ACTION: 1465 // TODO(F2): NYI 1466 break; 1467 1468 case META_LINECOLOR_ACTION: 1469 if( !rParms.maLineColor.is_initialized() ) 1470 { 1471 setStateColor( static_cast<MetaLineColorAction*>(pCurrAct), 1472 getState( rStates ).isLineColorSet, 1473 getState( rStates ).lineColor, 1474 rCanvas ); 1475 } 1476 break; 1477 1478 case META_FILLCOLOR_ACTION: 1479 if( !rParms.maFillColor.is_initialized() ) 1480 { 1481 setStateColor( static_cast<MetaFillColorAction*>(pCurrAct), 1482 getState( rStates ).isFillColorSet, 1483 getState( rStates ).fillColor, 1484 rCanvas ); 1485 } 1486 break; 1487 1488 case META_TEXTCOLOR_ACTION: 1489 { 1490 if( !rParms.maTextColor.is_initialized() ) 1491 { 1492 // Text color is set unconditionally, thus, no 1493 // use of setStateColor here 1494 ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() ); 1495 1496 // force alpha part of color to 1497 // opaque. transparent painting is done 1498 // explicitely via META_TRANSPARENT_ACTION 1499 aColor.SetTransparency(0); 1500 1501 getState( rStates ).textColor = 1502 ::vcl::unotools::colorToDoubleSequence( 1503 aColor, 1504 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ); 1505 } 1506 } 1507 break; 1508 1509 case META_TEXTFILLCOLOR_ACTION: 1510 if( !rParms.maTextColor.is_initialized() ) 1511 { 1512 setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct), 1513 getState( rStates ).isTextFillColorSet, 1514 getState( rStates ).textFillColor, 1515 rCanvas ); 1516 } 1517 break; 1518 1519 case META_TEXTLINECOLOR_ACTION: 1520 if( !rParms.maTextColor.is_initialized() ) 1521 { 1522 setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct), 1523 getState( rStates ).isTextLineColorSet, 1524 getState( rStates ).textLineColor, 1525 rCanvas ); 1526 } 1527 break; 1528 1529 case META_TEXTALIGN_ACTION: 1530 { 1531 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1532 const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() ); 1533 1534 rState.textReferencePoint = eTextAlign; 1535 } 1536 break; 1537 1538 case META_FONT_ACTION: 1539 { 1540 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1541 const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() ); 1542 1543 rState.xFont = createFont( rState.fontRotation, 1544 rFont, 1545 rFactoryParms ); 1546 1547 // TODO(Q2): define and use appropriate enumeration types 1548 rState.textReliefStyle = (sal_Int8)rFont.GetRelief(); 1549 rState.textOverlineStyle = (sal_Int8)rFont.GetOverline(); 1550 rState.textUnderlineStyle = rParms.maFontUnderline.is_initialized() ? 1551 (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) : 1552 (sal_Int8)rFont.GetUnderline(); 1553 rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout(); 1554 rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark(); 1555 rState.isTextEffectShadowSet = (rFont.IsShadow() != sal_False); 1556 rState.isTextWordUnderlineSet = (rFont.IsWordLineMode() != sal_False); 1557 rState.isTextOutlineModeSet = (rFont.IsOutline() != sal_False); 1558 } 1559 break; 1560 1561 case META_RASTEROP_ACTION: 1562 // TODO(F2): NYI 1563 break; 1564 1565 case META_LAYOUTMODE_ACTION: 1566 { 1567 // TODO(F2): A lot is missing here 1568 int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode(); 1569 ::cppcanvas::internal::OutDevState& rState = getState( rStates ); 1570 switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) ) 1571 { 1572 case TEXT_LAYOUT_BIDI_LTR: 1573 rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT; 1574 break; 1575 1576 case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG): 1577 rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT; 1578 break; 1579 1580 case TEXT_LAYOUT_BIDI_RTL: 1581 rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT; 1582 break; 1583 1584 case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG): 1585 rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT; 1586 break; 1587 } 1588 1589 rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED; 1590 if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) ) 1591 && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) ) 1592 { 1593 rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED; 1594 } 1595 } 1596 break; 1597 1598 // ------------------------------------------------------------ 1599 1600 // In the second part of this monster-switch, we 1601 // handle all recursing meta actions. These are the 1602 // ones generating a metafile by themselves, which is 1603 // then processed by recursively calling this method. 1604 1605 // ------------------------------------------------------------ 1606 1607 case META_GRADIENT_ACTION: 1608 { 1609 MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct); 1610 createGradientAction( ::Polygon( pGradAct->GetRect() ), 1611 pGradAct->GetGradient(), 1612 rFactoryParms, 1613 true, 1614 bSubsettableActions ); 1615 } 1616 break; 1617 1618 case META_HATCH_ACTION: 1619 { 1620 // TODO(F2): use native Canvas hatches here 1621 GDIMetaFile aTmpMtf; 1622 1623 rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(), 1624 static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(), 1625 aTmpMtf ); 1626 createActions( aTmpMtf, rFactoryParms, 1627 bSubsettableActions ); 1628 } 1629 break; 1630 1631 case META_EPS_ACTION: 1632 { 1633 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pCurrAct); 1634 const GDIMetaFile& rSubstitute = pAct->GetSubstitute(); 1635 1636 // #121806# explicitely kept integer 1637 const Size aMtfSize( rSubstitute.GetPrefSize() ); 1638 const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize, 1639 rSubstitute.GetPrefMapMode() ) ); 1640 1641 // #i44110# correct null-sized output - there 1642 // are metafiles which have zero size in at 1643 // least one dimension 1644 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ), 1645 ::std::max( aMtfSizePixPre.Height(), 1L ) ); 1646 1647 // Setup local transform, such that the 1648 // metafile renders itself into the given 1649 // output rectangle 1650 pushState( rStates, PUSH_ALL ); 1651 1652 rVDev.Push(); 1653 rVDev.SetMapMode( rSubstitute.GetPrefMapMode() ); 1654 1655 const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) ); 1656 const ::Size& rSize( rVDev.LogicToPixel( pAct->GetSize() ) ); 1657 1658 getState( rStates ).transform.translate( rPos.X(), 1659 rPos.Y() ); 1660 getState( rStates ).transform.scale( (double)rSize.Width() / aMtfSizePix.Width(), 1661 (double)rSize.Height() / aMtfSizePix.Height() ); 1662 1663 createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()), 1664 rFactoryParms, 1665 bSubsettableActions ); 1666 1667 rVDev.Pop(); 1668 popState( rStates ); 1669 } 1670 break; 1671 1672 // handle metafile comments, to retrieve 1673 // meta-information for gradients, fills and 1674 // strokes. May skip actions, and may recurse. 1675 case META_COMMENT_ACTION: 1676 { 1677 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct); 1678 1679 // Handle gradients 1680 if ( pAct->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL ) 1681 { 1682 MetaGradientExAction* pGradAction = NULL; 1683 bool bDone( false ); 1684 while( !bDone && 1685 (pCurrAct=rMtf.NextAction()) != NULL ) 1686 { 1687 switch( pCurrAct->GetType() ) 1688 { 1689 // extract gradient info 1690 case META_GRADIENTEX_ACTION: 1691 pGradAction = static_cast<MetaGradientExAction*>(pCurrAct); 1692 break; 1693 1694 // skip broken-down rendering, output gradient when sequence is ended 1695 case META_COMMENT_ACTION: 1696 if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL ) 1697 { 1698 bDone = true; 1699 1700 if( pGradAction ) 1701 { 1702 createGradientAction( pGradAction->GetPolyPolygon(), 1703 pGradAction->GetGradient(), 1704 rFactoryParms, 1705 false, 1706 bSubsettableActions ); 1707 } 1708 } 1709 break; 1710 } 1711 } 1712 } 1713 // TODO(P2): Handle drawing layer strokes, via 1714 // XPATHSTROKE_SEQ_BEGIN comment 1715 1716 // Handle drawing layer fills 1717 else if( pAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) ) 1718 { 1719 const sal_uInt8* pData = pAct->GetData(); 1720 if ( pData ) 1721 { 1722 SvMemoryStream aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ ); 1723 1724 SvtGraphicFill aFill; 1725 aMemStm >> aFill; 1726 1727 // TODO(P2): Also handle gradients and 1728 // hatches like this 1729 1730 // only evaluate comment for pure 1731 // bitmap fills. If a transparency 1732 // gradient is involved (denoted by 1733 // the FloatTransparent action), take 1734 // the normal meta actions. 1735 if( aFill.getFillType() == SvtGraphicFill::fillTexture && 1736 !isActionContained( rMtf, 1737 "XPATHFILL_SEQ_END", 1738 META_FLOATTRANSPARENT_ACTION ) ) 1739 { 1740 rendering::Texture aTexture; 1741 1742 // TODO(F1): the SvtGraphicFill 1743 // can also transport metafiles 1744 // here, handle that case, too 1745 Graphic aGraphic; 1746 aFill.getGraphic( aGraphic ); 1747 1748 BitmapEx aBmpEx( aGraphic.GetBitmapEx() ); 1749 const ::Size aBmpSize( aBmpEx.GetSizePixel() ); 1750 1751 ::SvtGraphicFill::Transform aTransform; 1752 aFill.getTransform( aTransform ); 1753 1754 ::basegfx::B2DHomMatrix aMatrix; 1755 1756 // convert to basegfx matrix 1757 aMatrix.set(0,0, aTransform.matrix[ 0 ] ); 1758 aMatrix.set(0,1, aTransform.matrix[ 1 ] ); 1759 aMatrix.set(0,2, aTransform.matrix[ 2 ] ); 1760 aMatrix.set(1,0, aTransform.matrix[ 3 ] ); 1761 aMatrix.set(1,1, aTransform.matrix[ 4 ] ); 1762 aMatrix.set(1,2, aTransform.matrix[ 5 ] ); 1763 1764 ::basegfx::B2DHomMatrix aScale; 1765 aScale.scale( aBmpSize.Width(), 1766 aBmpSize.Height() ); 1767 1768 // post-multiply with the bitmap 1769 // size (XCanvas' texture assumes 1770 // the given bitmap to be 1771 // normalized to [0,1]x[0,1] 1772 // rectangle) 1773 aMatrix = aMatrix * aScale; 1774 1775 // pre-multiply with the 1776 // logic-to-pixel scale factor 1777 // (the metafile comment works in 1778 // logical coordinates). 1779 ::basegfx::B2DHomMatrix aLogic2PixelTransform; 1780 aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform, 1781 rVDev ); 1782 1783 ::basegfx::unotools::affineMatrixFromHomMatrix( 1784 aTexture.AffineTransform, 1785 aMatrix ); 1786 1787 aTexture.Alpha = 1.0 - aFill.getTransparency(); 1788 aTexture.Bitmap = 1789 ::vcl::unotools::xBitmapFromBitmapEx( 1790 rCanvas->getUNOCanvas()->getDevice(), 1791 aBmpEx ); 1792 if( aFill.isTiling() ) 1793 { 1794 aTexture.RepeatModeX = rendering::TexturingMode::REPEAT; 1795 aTexture.RepeatModeY = rendering::TexturingMode::REPEAT; 1796 } 1797 else 1798 { 1799 aTexture.RepeatModeX = rendering::TexturingMode::NONE; 1800 aTexture.RepeatModeY = rendering::TexturingMode::NONE; 1801 } 1802 1803 ::PolyPolygon aPath; 1804 aFill.getPath( aPath ); 1805 1806 ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() ); 1807 aPoly.transform( getState( rStates ).mapModeTransform ); 1808 ActionSharedPtr pPolyAction( 1809 internal::PolyPolyActionFactory::createPolyPolyAction( 1810 aPoly, 1811 rCanvas, 1812 getState( rStates ), 1813 aTexture ) ); 1814 1815 if( pPolyAction ) 1816 { 1817 maActions.push_back( 1818 MtfAction( 1819 pPolyAction, 1820 io_rCurrActionIndex ) ); 1821 1822 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 1823 } 1824 1825 // skip broken-down render output 1826 skipContent( rMtf, 1827 "XPATHFILL_SEQ_END", 1828 io_rCurrActionIndex ); 1829 } 1830 } 1831 } 1832 } 1833 break; 1834 1835 // ------------------------------------------------------------ 1836 1837 // In the third part of this monster-switch, we 1838 // handle all 'acting' meta actions. These are all 1839 // processed by constructing function objects for 1840 // them, which will later ease caching. 1841 1842 // ------------------------------------------------------------ 1843 1844 case META_POINT_ACTION: 1845 { 1846 const OutDevState& rState( getState( rStates ) ); 1847 if( rState.lineColor.getLength() ) 1848 { 1849 ActionSharedPtr pPointAction( 1850 internal::PointActionFactory::createPointAction( 1851 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( 1852 static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ), 1853 rCanvas, 1854 rState ) ); 1855 1856 if( pPointAction ) 1857 { 1858 maActions.push_back( 1859 MtfAction( 1860 pPointAction, 1861 io_rCurrActionIndex ) ); 1862 1863 io_rCurrActionIndex += pPointAction->getActionCount()-1; 1864 } 1865 } 1866 } 1867 break; 1868 1869 case META_PIXEL_ACTION: 1870 { 1871 const OutDevState& rState( getState( rStates ) ); 1872 if( rState.lineColor.getLength() ) 1873 { 1874 ActionSharedPtr pPointAction( 1875 internal::PointActionFactory::createPointAction( 1876 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( 1877 static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ), 1878 rCanvas, 1879 rState, 1880 static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) ); 1881 1882 if( pPointAction ) 1883 { 1884 maActions.push_back( 1885 MtfAction( 1886 pPointAction, 1887 io_rCurrActionIndex ) ); 1888 1889 io_rCurrActionIndex += pPointAction->getActionCount()-1; 1890 } 1891 } 1892 } 1893 break; 1894 1895 case META_LINE_ACTION: 1896 { 1897 const OutDevState& rState( getState( rStates ) ); 1898 if( rState.lineColor.getLength() ) 1899 { 1900 MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct); 1901 1902 const LineInfo& rLineInfo( pLineAct->GetLineInfo() ); 1903 1904 const ::basegfx::B2DPoint aStartPoint( 1905 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() )); 1906 const ::basegfx::B2DPoint aEndPoint( 1907 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() )); 1908 1909 ActionSharedPtr pLineAction; 1910 1911 if( rLineInfo.IsDefault() ) 1912 { 1913 // plain hair line 1914 pLineAction = 1915 internal::LineActionFactory::createLineAction( 1916 aStartPoint, 1917 aEndPoint, 1918 rCanvas, 1919 rState ); 1920 1921 if( pLineAction ) 1922 { 1923 maActions.push_back( 1924 MtfAction( 1925 pLineAction, 1926 io_rCurrActionIndex ) ); 1927 1928 io_rCurrActionIndex += pLineAction->getActionCount()-1; 1929 } 1930 } 1931 else if( LINE_NONE != rLineInfo.GetStyle() ) 1932 { 1933 // 'thick' line 1934 rendering::StrokeAttributes aStrokeAttributes; 1935 1936 setupStrokeAttributes( aStrokeAttributes, 1937 rFactoryParms, 1938 rLineInfo ); 1939 1940 // XCanvas can only stroke polygons, 1941 // not simple lines - thus, handle 1942 // this case via the polypolygon 1943 // action 1944 ::basegfx::B2DPolygon aPoly; 1945 aPoly.append( aStartPoint ); 1946 aPoly.append( aEndPoint ); 1947 pLineAction = 1948 internal::PolyPolyActionFactory::createPolyPolyAction( 1949 ::basegfx::B2DPolyPolygon( aPoly ), 1950 rCanvas, rState, aStrokeAttributes ); 1951 1952 if( pLineAction ) 1953 { 1954 maActions.push_back( 1955 MtfAction( 1956 pLineAction, 1957 io_rCurrActionIndex ) ); 1958 1959 io_rCurrActionIndex += pLineAction->getActionCount()-1; 1960 } 1961 } 1962 // else: line style is default 1963 // (i.e. invisible), don't generate action 1964 } 1965 } 1966 break; 1967 1968 case META_RECT_ACTION: 1969 { 1970 const Rectangle& rRect( 1971 static_cast<MetaRectAction*>(pCurrAct)->GetRect() ); 1972 1973 if( rRect.IsEmpty() ) 1974 break; 1975 1976 const OutDevState& rState( getState( rStates ) ); 1977 const ::basegfx::B2DPoint aTopLeftPixel( 1978 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) ); 1979 const ::basegfx::B2DPoint aBottomRightPixel( 1980 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 1981 // #121100# OutputDevice::DrawRect() fills 1982 // rectangles Apple-like, i.e. with one 1983 // additional pixel to the right and bottom. 1984 ::basegfx::B2DPoint(1,1) ); 1985 1986 createFillAndStroke( ::basegfx::tools::createPolygonFromRect( 1987 ::basegfx::B2DRange( aTopLeftPixel, 1988 aBottomRightPixel )), 1989 rFactoryParms ); 1990 break; 1991 } 1992 1993 case META_ROUNDRECT_ACTION: 1994 { 1995 const Rectangle& rRect( 1996 static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect()); 1997 1998 if( rRect.IsEmpty() ) 1999 break; 2000 2001 ::basegfx::B2DPolygon aPoly( 2002 ::basegfx::tools::createPolygonFromRect( 2003 ::basegfx::B2DRange( 2004 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ), 2005 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 2006 ::basegfx::B2DPoint(1,1) ), 2007 static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound(), 2008 static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() )); 2009 aPoly.transform( getState( rStates ).mapModeTransform ); 2010 2011 createFillAndStroke( aPoly, 2012 rFactoryParms ); 2013 } 2014 break; 2015 2016 case META_ELLIPSE_ACTION: 2017 { 2018 const Rectangle& rRect( 2019 static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() ); 2020 2021 if( rRect.IsEmpty() ) 2022 break; 2023 2024 const ::basegfx::B2DRange aRange( 2025 ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ), 2026 ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) + 2027 ::basegfx::B2DPoint(1,1) ); 2028 2029 ::basegfx::B2DPolygon aPoly( 2030 ::basegfx::tools::createPolygonFromEllipse( 2031 aRange.getCenter(), 2032 aRange.getWidth(), 2033 aRange.getHeight() )); 2034 aPoly.transform( getState( rStates ).mapModeTransform ); 2035 2036 createFillAndStroke( aPoly, 2037 rFactoryParms ); 2038 } 2039 break; 2040 2041 case META_ARC_ACTION: 2042 { 2043 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2044 const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(), 2045 static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(), 2046 static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC ); 2047 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2048 aPoly.transform( getState( rStates ).mapModeTransform ); 2049 2050 createFillAndStroke( aPoly, 2051 rFactoryParms ); 2052 } 2053 break; 2054 2055 case META_PIE_ACTION: 2056 { 2057 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2058 const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(), 2059 static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(), 2060 static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE ); 2061 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2062 aPoly.transform( getState( rStates ).mapModeTransform ); 2063 2064 createFillAndStroke( aPoly, 2065 rFactoryParms ); 2066 } 2067 break; 2068 2069 case META_CHORD_ACTION: 2070 { 2071 // TODO(F1): Missing basegfx functionality. Mind empty rects! 2072 const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(), 2073 static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(), 2074 static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD ); 2075 ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() ); 2076 aPoly.transform( getState( rStates ).mapModeTransform ); 2077 2078 createFillAndStroke( aPoly, 2079 rFactoryParms ); 2080 } 2081 break; 2082 2083 case META_POLYLINE_ACTION: 2084 { 2085 const OutDevState& rState( getState( rStates ) ); 2086 if( rState.lineColor.getLength() || 2087 rState.fillColor.getLength() ) 2088 { 2089 MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct); 2090 2091 const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() ); 2092 ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() ); 2093 aPoly.transform( rState.mapModeTransform ); 2094 2095 ActionSharedPtr pLineAction; 2096 2097 if( rLineInfo.IsDefault() ) 2098 { 2099 // plain hair line polygon 2100 pLineAction = 2101 internal::PolyPolyActionFactory::createLinePolyPolyAction( 2102 ::basegfx::B2DPolyPolygon(aPoly), 2103 rCanvas, 2104 rState ); 2105 2106 if( pLineAction ) 2107 { 2108 maActions.push_back( 2109 MtfAction( 2110 pLineAction, 2111 io_rCurrActionIndex ) ); 2112 2113 io_rCurrActionIndex += pLineAction->getActionCount()-1; 2114 } 2115 } 2116 else if( LINE_NONE != rLineInfo.GetStyle() ) 2117 { 2118 // 'thick' line polygon 2119 rendering::StrokeAttributes aStrokeAttributes; 2120 2121 setupStrokeAttributes( aStrokeAttributes, 2122 rFactoryParms, 2123 rLineInfo ); 2124 2125 pLineAction = 2126 internal::PolyPolyActionFactory::createPolyPolyAction( 2127 ::basegfx::B2DPolyPolygon(aPoly), 2128 rCanvas, 2129 rState, 2130 aStrokeAttributes ) ; 2131 2132 if( pLineAction ) 2133 { 2134 maActions.push_back( 2135 MtfAction( 2136 pLineAction, 2137 io_rCurrActionIndex ) ); 2138 2139 io_rCurrActionIndex += pLineAction->getActionCount()-1; 2140 } 2141 } 2142 // else: line style is default 2143 // (i.e. invisible), don't generate action 2144 } 2145 } 2146 break; 2147 2148 case META_POLYGON_ACTION: 2149 { 2150 ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() ); 2151 aPoly.transform( getState( rStates ).mapModeTransform ); 2152 createFillAndStroke( aPoly, 2153 rFactoryParms ); 2154 } 2155 break; 2156 2157 case META_POLYPOLYGON_ACTION: 2158 { 2159 ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() ); 2160 aPoly.transform( getState( rStates ).mapModeTransform ); 2161 createFillAndStroke( aPoly, 2162 rFactoryParms ); 2163 } 2164 break; 2165 2166 case META_BMP_ACTION: 2167 { 2168 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct); 2169 2170 ActionSharedPtr pBmpAction( 2171 internal::BitmapActionFactory::createBitmapAction( 2172 pAct->GetBitmap(), 2173 getState( rStates ).mapModeTransform * 2174 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2175 rCanvas, 2176 getState( rStates ) ) ); 2177 2178 if( pBmpAction ) 2179 { 2180 maActions.push_back( 2181 MtfAction( 2182 pBmpAction, 2183 io_rCurrActionIndex ) ); 2184 2185 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2186 } 2187 } 2188 break; 2189 2190 case META_BMPSCALE_ACTION: 2191 { 2192 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct); 2193 2194 ActionSharedPtr pBmpAction( 2195 internal::BitmapActionFactory::createBitmapAction( 2196 pAct->GetBitmap(), 2197 getState( rStates ).mapModeTransform * 2198 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2199 getState( rStates ).mapModeTransform * 2200 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2201 rCanvas, 2202 getState( rStates ) ) ); 2203 2204 if( pBmpAction ) 2205 { 2206 maActions.push_back( 2207 MtfAction( 2208 pBmpAction, 2209 io_rCurrActionIndex ) ); 2210 2211 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2212 } 2213 } 2214 break; 2215 2216 case META_BMPSCALEPART_ACTION: 2217 { 2218 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct); 2219 2220 // crop bitmap to given source rectangle (no 2221 // need to copy and convert the whole bitmap) 2222 Bitmap aBmp( pAct->GetBitmap() ); 2223 const Rectangle aCropRect( pAct->GetSrcPoint(), 2224 pAct->GetSrcSize() ); 2225 aBmp.Crop( aCropRect ); 2226 2227 ActionSharedPtr pBmpAction( 2228 internal::BitmapActionFactory::createBitmapAction( 2229 aBmp, 2230 getState( rStates ).mapModeTransform * 2231 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2232 getState( rStates ).mapModeTransform * 2233 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2234 rCanvas, 2235 getState( rStates ) ) ); 2236 2237 if( pBmpAction ) 2238 { 2239 maActions.push_back( 2240 MtfAction( 2241 pBmpAction, 2242 io_rCurrActionIndex ) ); 2243 2244 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2245 } 2246 } 2247 break; 2248 2249 case META_BMPEX_ACTION: 2250 { 2251 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct); 2252 2253 ActionSharedPtr pBmpAction( 2254 internal::BitmapActionFactory::createBitmapAction( 2255 pAct->GetBitmapEx(), 2256 getState( rStates ).mapModeTransform * 2257 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2258 rCanvas, 2259 getState( rStates ) ) ); 2260 2261 if( pBmpAction ) 2262 { 2263 maActions.push_back( 2264 MtfAction( 2265 pBmpAction, 2266 io_rCurrActionIndex ) ); 2267 2268 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2269 } 2270 } 2271 break; 2272 2273 case META_BMPEXSCALE_ACTION: 2274 { 2275 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct); 2276 2277 ActionSharedPtr pBmpAction( 2278 internal::BitmapActionFactory::createBitmapAction( 2279 pAct->GetBitmapEx(), 2280 getState( rStates ).mapModeTransform * 2281 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2282 getState( rStates ).mapModeTransform * 2283 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2284 rCanvas, 2285 getState( rStates ) ) ); 2286 2287 if( pBmpAction ) 2288 { 2289 maActions.push_back( 2290 MtfAction( 2291 pBmpAction, 2292 io_rCurrActionIndex ) ); 2293 2294 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2295 } 2296 } 2297 break; 2298 2299 case META_BMPEXSCALEPART_ACTION: 2300 { 2301 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct); 2302 2303 // crop bitmap to given source rectangle (no 2304 // need to copy and convert the whole bitmap) 2305 BitmapEx aBmp( pAct->GetBitmapEx() ); 2306 const Rectangle aCropRect( pAct->GetSrcPoint(), 2307 pAct->GetSrcSize() ); 2308 aBmp.Crop( aCropRect ); 2309 2310 ActionSharedPtr pBmpAction( 2311 internal::BitmapActionFactory::createBitmapAction( 2312 aBmp, 2313 getState( rStates ).mapModeTransform * 2314 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2315 getState( rStates ).mapModeTransform * 2316 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2317 rCanvas, 2318 getState( rStates ) ) ); 2319 2320 if( pBmpAction ) 2321 { 2322 maActions.push_back( 2323 MtfAction( 2324 pBmpAction, 2325 io_rCurrActionIndex ) ); 2326 2327 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2328 } 2329 } 2330 break; 2331 2332 case META_MASK_ACTION: 2333 { 2334 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct); 2335 2336 // create masked BitmapEx right here, as the 2337 // canvas does not provide equivalent 2338 // functionality 2339 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2340 pAct->GetColor() )); 2341 2342 ActionSharedPtr pBmpAction( 2343 internal::BitmapActionFactory::createBitmapAction( 2344 aBmp, 2345 getState( rStates ).mapModeTransform * 2346 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2347 rCanvas, 2348 getState( rStates ) ) ); 2349 2350 if( pBmpAction ) 2351 { 2352 maActions.push_back( 2353 MtfAction( 2354 pBmpAction, 2355 io_rCurrActionIndex ) ); 2356 2357 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2358 } 2359 } 2360 break; 2361 2362 case META_MASKSCALE_ACTION: 2363 { 2364 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct); 2365 2366 // create masked BitmapEx right here, as the 2367 // canvas does not provide equivalent 2368 // functionality 2369 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2370 pAct->GetColor() )); 2371 2372 ActionSharedPtr pBmpAction( 2373 internal::BitmapActionFactory::createBitmapAction( 2374 aBmp, 2375 getState( rStates ).mapModeTransform * 2376 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2377 getState( rStates ).mapModeTransform * 2378 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2379 rCanvas, 2380 getState( rStates ) ) ); 2381 2382 if( pBmpAction ) 2383 { 2384 maActions.push_back( 2385 MtfAction( 2386 pBmpAction, 2387 io_rCurrActionIndex ) ); 2388 2389 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2390 } 2391 } 2392 break; 2393 2394 case META_MASKSCALEPART_ACTION: 2395 { 2396 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct); 2397 2398 // create masked BitmapEx right here, as the 2399 // canvas does not provide equivalent 2400 // functionality 2401 BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(), 2402 pAct->GetColor() )); 2403 2404 // crop bitmap to given source rectangle (no 2405 // need to copy and convert the whole bitmap) 2406 const Rectangle aCropRect( pAct->GetSrcPoint(), 2407 pAct->GetSrcSize() ); 2408 aBmp.Crop( aCropRect ); 2409 2410 ActionSharedPtr pBmpAction( 2411 internal::BitmapActionFactory::createBitmapAction( 2412 aBmp, 2413 getState( rStates ).mapModeTransform * 2414 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ), 2415 getState( rStates ).mapModeTransform * 2416 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ), 2417 rCanvas, 2418 getState( rStates ) ) ); 2419 2420 if( pBmpAction ) 2421 { 2422 maActions.push_back( 2423 MtfAction( 2424 pBmpAction, 2425 io_rCurrActionIndex ) ); 2426 2427 io_rCurrActionIndex += pBmpAction->getActionCount()-1; 2428 } 2429 } 2430 break; 2431 2432 case META_GRADIENTEX_ACTION: 2433 // TODO(F1): use native Canvas gradients here 2434 // action is ignored here, because redundant to META_GRADIENT_ACTION 2435 break; 2436 2437 case META_WALLPAPER_ACTION: 2438 // TODO(F2): NYI 2439 break; 2440 2441 case META_TRANSPARENT_ACTION: 2442 { 2443 const OutDevState& rState( getState( rStates ) ); 2444 if( rState.lineColor.getLength() || 2445 rState.fillColor.getLength() ) 2446 { 2447 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct); 2448 ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() ); 2449 aPoly.transform( rState.mapModeTransform ); 2450 2451 ActionSharedPtr pPolyAction( 2452 internal::PolyPolyActionFactory::createPolyPolyAction( 2453 aPoly, 2454 rCanvas, 2455 rState, 2456 pAct->GetTransparence() ) ); 2457 2458 if( pPolyAction ) 2459 { 2460 maActions.push_back( 2461 MtfAction( 2462 pPolyAction, 2463 io_rCurrActionIndex ) ); 2464 2465 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 2466 } 2467 } 2468 } 2469 break; 2470 2471 case META_FLOATTRANSPARENT_ACTION: 2472 { 2473 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct); 2474 2475 internal::MtfAutoPtr pMtf( 2476 new ::GDIMetaFile( pAct->GetGDIMetaFile() ) ); 2477 2478 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls) 2479 internal::GradientAutoPtr pGradient( 2480 new Gradient( pAct->GetGradient() ) ); 2481 2482 DBG_TESTSOLARMUTEX(); 2483 2484 ActionSharedPtr pFloatTransAction( 2485 internal::TransparencyGroupActionFactory::createTransparencyGroupAction( 2486 pMtf, 2487 pGradient, 2488 rParms, 2489 getState( rStates ).mapModeTransform * 2490 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2491 getState( rStates ).mapModeTransform * 2492 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2493 rCanvas, 2494 getState( rStates ) ) ); 2495 2496 if( pFloatTransAction ) 2497 { 2498 maActions.push_back( 2499 MtfAction( 2500 pFloatTransAction, 2501 io_rCurrActionIndex ) ); 2502 2503 io_rCurrActionIndex += pFloatTransAction->getActionCount()-1; 2504 } 2505 } 2506 break; 2507 2508 case META_TEXT_ACTION: 2509 { 2510 MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct); 2511 XubString sText = XubString( pAct->GetText() ); 2512 2513 if( rVDev.GetDigitLanguage()) 2514 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2515 2516 createTextAction( 2517 pAct->GetPoint(), 2518 sText, 2519 pAct->GetIndex(), 2520 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2521 NULL, 2522 rFactoryParms, 2523 bSubsettableActions ); 2524 } 2525 break; 2526 2527 case META_TEXTARRAY_ACTION: 2528 { 2529 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct); 2530 XubString sText = XubString( pAct->GetText() ); 2531 2532 if( rVDev.GetDigitLanguage()) 2533 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2534 2535 createTextAction( 2536 pAct->GetPoint(), 2537 sText, 2538 pAct->GetIndex(), 2539 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2540 pAct->GetDXArray(), 2541 rFactoryParms, 2542 bSubsettableActions ); 2543 } 2544 break; 2545 2546 case META_TEXTLINE_ACTION: 2547 { 2548 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct); 2549 2550 const OutDevState& rState( getState( rStates ) ); 2551 const ::Size aBaselineOffset( tools::getBaselineOffset( rState, 2552 rVDev ) ); 2553 const ::Point aStartPoint( pAct->GetStartPoint() ); 2554 const ::basegfx::B2DSize aSize( rState.mapModeTransform * 2555 ::basegfx::B2DSize(pAct->GetWidth(), 2556 0 )); 2557 2558 ActionSharedPtr pPolyAction( 2559 PolyPolyActionFactory::createPolyPolyAction( 2560 tools::createTextLinesPolyPolygon( 2561 rState.mapModeTransform * 2562 ::basegfx::B2DPoint( 2563 ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) + 2564 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)), 2565 aSize.getX(), 2566 tools::createTextLineInfo( rVDev, 2567 rState )), 2568 rCanvas, 2569 rState ) ); 2570 2571 if( pPolyAction.get() ) 2572 { 2573 maActions.push_back( 2574 MtfAction( 2575 pPolyAction, 2576 io_rCurrActionIndex ) ); 2577 2578 io_rCurrActionIndex += pPolyAction->getActionCount()-1; 2579 } 2580 } 2581 break; 2582 2583 case META_TEXTRECT_ACTION: 2584 { 2585 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct); 2586 2587 pushState( rStates, PUSH_ALL ); 2588 2589 // use the VDev to break up the text rect 2590 // action into readily formatted lines 2591 GDIMetaFile aTmpMtf; 2592 rVDev.AddTextRectActions( pAct->GetRect(), 2593 pAct->GetText(), 2594 pAct->GetStyle(), 2595 aTmpMtf ); 2596 2597 createActions( aTmpMtf, 2598 rFactoryParms, 2599 bSubsettableActions ); 2600 2601 popState( rStates ); 2602 2603 break; 2604 } 2605 2606 case META_STRETCHTEXT_ACTION: 2607 { 2608 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct); 2609 XubString sText = XubString( pAct->GetText() ); 2610 2611 if( rVDev.GetDigitLanguage()) 2612 convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() ); 2613 2614 const sal_uInt16 nLen( pAct->GetLen() == (sal_uInt16)STRING_LEN ? 2615 pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen() ); 2616 2617 // #i70897# Nothing to do, actually... 2618 if( nLen == 0 ) 2619 break; 2620 2621 // have to fit the text into the given 2622 // width. This is achieved by internally 2623 // generating a DX array, and uniformly 2624 // distributing the excess/insufficient width 2625 // to every logical character. 2626 ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] ); 2627 2628 rVDev.GetTextArray( pAct->GetText(), pDXArray.get(), 2629 pAct->GetIndex(), pAct->GetLen() ); 2630 2631 const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] ); 2632 2633 // Last entry of pDXArray contains total width of the text 2634 sal_Int32* p=pDXArray.get(); 2635 for( sal_uInt16 i=1; i<=nLen; ++i ) 2636 { 2637 // calc ratio for every array entry, to 2638 // distribute rounding errors 'evenly' 2639 // across the characters. Note that each 2640 // entry represents the 'end' position of 2641 // the corresponding character, thus, we 2642 // let i run from 1 to nLen. 2643 *p++ += (sal_Int32)i*nWidthDifference/nLen; 2644 } 2645 2646 createTextAction( 2647 pAct->GetPoint(), 2648 sText, 2649 pAct->GetIndex(), 2650 pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), 2651 pDXArray.get(), 2652 rFactoryParms, 2653 bSubsettableActions ); 2654 } 2655 break; 2656 2657 case META_RENDERGRAPHIC_ACTION: 2658 { 2659 MetaRenderGraphicAction* pAct = static_cast<MetaRenderGraphicAction*>(pCurrAct); 2660 2661 ActionSharedPtr pRenderGraphicAction( 2662 internal::RenderGraphicActionFactory::createRenderGraphicAction( 2663 pAct->GetRenderGraphic(), 2664 getState( rStates ).mapModeTransform * 2665 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ), 2666 getState( rStates ).mapModeTransform * 2667 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ), 2668 rCanvas, 2669 getState( rStates ) ) ); 2670 2671 if( pRenderGraphicAction ) 2672 { 2673 maActions.push_back( 2674 MtfAction( 2675 pRenderGraphicAction, 2676 io_rCurrActionIndex ) ); 2677 2678 io_rCurrActionIndex += pRenderGraphicAction->getActionCount()-1; 2679 } 2680 } 2681 break; 2682 2683 default: 2684 OSL_ENSURE( false, 2685 "Unknown meta action type encountered" ); 2686 break; 2687 } 2688 2689 // increment action index (each mtf action counts _at 2690 // least_ one. Some count for more, therefore, 2691 // io_rCurrActionIndex is sometimes incremented by 2692 // pAct->getActionCount()-1 above, the -1 being the 2693 // correction for the unconditional increment here). 2694 ++io_rCurrActionIndex; 2695 } 2696 2697 return true; 2698 } 2699 2700 2701 namespace 2702 { 2703 class ActionRenderer 2704 { 2705 public: 2706 ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) : 2707 maTransformation( rTransformation ), 2708 mbRet( true ) 2709 { 2710 } 2711 2712 bool result() 2713 { 2714 return mbRet; 2715 } 2716 2717 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction ) 2718 { 2719 // ANDing the result. We want to fail if at least 2720 // one action failed. 2721 mbRet &= rAction.mpAction->render( maTransformation ); 2722 } 2723 2724 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction, 2725 const Action::Subset& rSubset ) 2726 { 2727 // ANDing the result. We want to fail if at least 2728 // one action failed. 2729 mbRet &= rAction.mpAction->render( maTransformation, 2730 rSubset ); 2731 } 2732 2733 private: 2734 ::basegfx::B2DHomMatrix maTransformation; 2735 bool mbRet; 2736 }; 2737 2738 class AreaQuery 2739 { 2740 public: 2741 AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) : 2742 maTransformation( rTransformation ), 2743 maBounds() 2744 { 2745 } 2746 2747 bool result() 2748 { 2749 return true; // nothing can fail here 2750 } 2751 2752 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction ) 2753 { 2754 maBounds.expand( rAction.mpAction->getBounds( maTransformation ) ); 2755 } 2756 2757 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction, 2758 const Action::Subset& rSubset ) 2759 { 2760 maBounds.expand( rAction.mpAction->getBounds( maTransformation, 2761 rSubset ) ); 2762 } 2763 2764 ::basegfx::B2DRange getBounds() const 2765 { 2766 return maBounds; 2767 } 2768 2769 private: 2770 ::basegfx::B2DHomMatrix maTransformation; 2771 ::basegfx::B2DRange maBounds; 2772 }; 2773 2774 // Doing that via inline class. Compilers tend to not inline free 2775 // functions. 2776 struct UpperBoundActionIndexComparator 2777 { 2778 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS, 2779 const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS ) 2780 { 2781 const sal_Int32 nLHSCount( rLHS.mpAction ? 2782 rLHS.mpAction->getActionCount() : 0 ); 2783 const sal_Int32 nRHSCount( rRHS.mpAction ? 2784 rRHS.mpAction->getActionCount() : 0 ); 2785 2786 // compare end of action range, to have an action selected 2787 // by lower_bound even if the requested index points in 2788 // the middle of the action's range 2789 return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount; 2790 } 2791 }; 2792 2793 /** Algorithm to apply given functor to a subset range 2794 2795 @tpl Functor 2796 2797 Functor to call for each element of the subset 2798 range. Must provide the following method signatures: 2799 bool result() (returning false if operation failed) 2800 2801 */ 2802 template< typename Functor > bool 2803 forSubsetRange( Functor& rFunctor, 2804 ImplRenderer::ActionVector::const_iterator aRangeBegin, 2805 ImplRenderer::ActionVector::const_iterator aRangeEnd, 2806 sal_Int32 nStartIndex, 2807 sal_Int32 nEndIndex, 2808 const ImplRenderer::ActionVector::const_iterator& rEnd ) 2809 { 2810 if( aRangeBegin == aRangeEnd ) 2811 { 2812 // only a single action. Setup subset, and call functor 2813 Action::Subset aSubset; 2814 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ), 2815 nStartIndex - aRangeBegin->mnOrigIndex ); 2816 aSubset.mnSubsetEnd = ::std::min( aRangeBegin->mpAction->getActionCount(), 2817 nEndIndex - aRangeBegin->mnOrigIndex ); 2818 2819 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2820 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2821 2822 rFunctor( *aRangeBegin, aSubset ); 2823 } 2824 else 2825 { 2826 // more than one action. 2827 2828 // render partial first, full intermediate, and 2829 // partial last action 2830 Action::Subset aSubset; 2831 aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ), 2832 nStartIndex - aRangeBegin->mnOrigIndex ); 2833 aSubset.mnSubsetEnd = aRangeBegin->mpAction->getActionCount(); 2834 2835 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2836 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2837 2838 rFunctor( *aRangeBegin, aSubset ); 2839 2840 // first action rendered, skip to next 2841 ++aRangeBegin; 2842 2843 // render full middle actions 2844 while( aRangeBegin != aRangeEnd ) 2845 rFunctor( *aRangeBegin++ ); 2846 2847 if( aRangeEnd == rEnd || 2848 aRangeEnd->mnOrigIndex > nEndIndex ) 2849 { 2850 // aRangeEnd denotes end of action vector, 2851 // 2852 // or 2853 // 2854 // nEndIndex references something _after_ 2855 // aRangeBegin, but _before_ aRangeEnd 2856 // 2857 // either way: no partial action left 2858 return rFunctor.result(); 2859 } 2860 2861 aSubset.mnSubsetBegin = 0; 2862 aSubset.mnSubsetEnd = nEndIndex - aRangeEnd->mnOrigIndex; 2863 2864 ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0, 2865 "ImplRenderer::forSubsetRange(): Invalid indices" ); 2866 2867 rFunctor( *aRangeEnd, aSubset ); 2868 } 2869 2870 return rFunctor.result(); 2871 } 2872 } 2873 2874 bool ImplRenderer::getSubsetIndices( sal_Int32& io_rStartIndex, 2875 sal_Int32& io_rEndIndex, 2876 ActionVector::const_iterator& o_rRangeBegin, 2877 ActionVector::const_iterator& o_rRangeEnd ) const 2878 { 2879 ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex, 2880 "ImplRenderer::getSubsetIndices(): invalid action range" ); 2881 2882 ENSURE_OR_RETURN_FALSE( !maActions.empty(), 2883 "ImplRenderer::getSubsetIndices(): no actions to render" ); 2884 2885 const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex ); 2886 const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex + 2887 maActions.back().mpAction->getActionCount() ); 2888 2889 // clip given range to permissible values (there might be 2890 // ranges before and behind the valid indices) 2891 io_rStartIndex = ::std::max( nMinActionIndex, 2892 io_rStartIndex ); 2893 io_rEndIndex = ::std::min( nMaxActionIndex, 2894 io_rEndIndex ); 2895 2896 if( io_rStartIndex == io_rEndIndex || 2897 io_rStartIndex > io_rEndIndex ) 2898 { 2899 // empty range, don't render anything. The second 2900 // condition e.g. happens if the requested range lies 2901 // fully before or behind the valid action indices. 2902 return false; 2903 } 2904 2905 2906 const ActionVector::const_iterator aBegin( maActions.begin() ); 2907 const ActionVector::const_iterator aEnd( maActions.end() ); 2908 2909 2910 // find start and end action 2911 // ========================= 2912 o_rRangeBegin = ::std::lower_bound( aBegin, aEnd, 2913 MtfAction( ActionSharedPtr(), io_rStartIndex ), 2914 UpperBoundActionIndexComparator() ); 2915 o_rRangeEnd = ::std::lower_bound( aBegin, aEnd, 2916 MtfAction( ActionSharedPtr(), io_rEndIndex ), 2917 UpperBoundActionIndexComparator() ); 2918 return true; 2919 } 2920 2921 2922 // Public methods 2923 // ==================================================================== 2924 2925 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, 2926 const GDIMetaFile& rMtf, 2927 const Parameters& rParams ) : 2928 CanvasGraphicHelper( rCanvas ), 2929 maActions() 2930 { 2931 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" ); 2932 2933 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), 2934 "ImplRenderer::ImplRenderer(): Invalid canvas" ); 2935 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), 2936 "ImplRenderer::ImplRenderer(): Invalid graphic device" ); 2937 2938 // make sure canvas and graphic device are valid; action 2939 // creation don't check that every time 2940 if( rCanvas.get() == NULL || 2941 !rCanvas->getUNOCanvas().is() || 2942 !rCanvas->getUNOCanvas()->getDevice().is() ) 2943 { 2944 // leave actions empty 2945 return; 2946 } 2947 2948 VectorOfOutDevStates aStateStack; 2949 2950 VirtualDevice aVDev; 2951 aVDev.EnableOutput( sal_False ); 2952 2953 // Setup VDev for state tracking and mapping 2954 // ========================================= 2955 2956 aVDev.SetMapMode( rMtf.GetPrefMapMode() ); 2957 2958 const Size aMtfSize( rMtf.GetPrefSize() ); 2959 const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize, 2960 rMtf.GetPrefMapMode() ) ); 2961 const Point aEmptyPt; 2962 const Point aMtfOriginPix( aVDev.LogicToPixel( aEmptyPt ) ); 2963 2964 // #i44110# correct null-sized output - there are shapes 2965 // which have zero size in at least one dimension 2966 const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ), 2967 ::std::max( aMtfSizePixPre.Height(), 1L ) ); 2968 2969 sal_Int32 nCurrActions(0); 2970 ActionFactoryParameters aParms(aStateStack, 2971 rCanvas, 2972 aVDev, 2973 rParams, 2974 nCurrActions ); 2975 2976 // init state stack 2977 clearStateStack( aStateStack ); 2978 2979 // Setup local state, such that the metafile renders 2980 // itself into a one-by-one square at the origin for 2981 // identity view and render transformations 2982 getState( aStateStack ).transform.scale( 1.0 / aMtfSizePix.Width(), 2983 1.0 / aMtfSizePix.Height() ); 2984 2985 tools::calcLogic2PixelAffineTransform( getState( aStateStack ).mapModeTransform, 2986 aVDev ); 2987 2988 ColorSharedPtr pColor( getCanvas()->createColor() ); 2989 2990 // setup default text color to black 2991 getState( aStateStack ).textColor = 2992 getState( aStateStack ).textFillColor = 2993 getState( aStateStack ).textLineColor = pColor->getDeviceColor( 0x000000FF ); 2994 2995 // apply overrides from the Parameters struct 2996 if( rParams.maFillColor.is_initialized() ) 2997 { 2998 getState( aStateStack ).isFillColorSet = true; 2999 getState( aStateStack ).fillColor = pColor->getDeviceColor( *rParams.maFillColor ); 3000 } 3001 if( rParams.maLineColor.is_initialized() ) 3002 { 3003 getState( aStateStack ).isLineColorSet = true; 3004 getState( aStateStack ).lineColor = pColor->getDeviceColor( *rParams.maLineColor ); 3005 } 3006 if( rParams.maTextColor.is_initialized() ) 3007 { 3008 getState( aStateStack ).isTextFillColorSet = true; 3009 getState( aStateStack ).isTextLineColorSet = true; 3010 getState( aStateStack ).textColor = 3011 getState( aStateStack ).textFillColor = 3012 getState( aStateStack ).textLineColor = pColor->getDeviceColor( *rParams.maTextColor ); 3013 } 3014 if( rParams.maFontName.is_initialized() || 3015 rParams.maFontWeight.is_initialized() || 3016 rParams.maFontLetterForm.is_initialized() || 3017 rParams.maFontUnderline.is_initialized() || 3018 rParams.maFontProportion.is_initialized() ) 3019 { 3020 ::cppcanvas::internal::OutDevState& rState = getState( aStateStack ); 3021 3022 rState.xFont = createFont( rState.fontRotation, 3023 ::Font(), // default font 3024 aParms ); 3025 } 3026 3027 createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2): 3028 // we're 3029 // changing 3030 // the 3031 // current 3032 // action 3033 // in 3034 // createActions! 3035 aParms, 3036 true // TODO(P1): make subsettability configurable 3037 ); 3038 } 3039 3040 ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, 3041 const BitmapEx& rBmpEx, 3042 const Parameters& rParams ) : 3043 CanvasGraphicHelper( rCanvas ), 3044 maActions() 3045 { 3046 // TODO(F3): property modification parameters are 3047 // currently ignored for Bitmaps 3048 (void)rParams; 3049 3050 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(bitmap)" ); 3051 3052 OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), 3053 "ImplRenderer::ImplRenderer(): Invalid canvas" ); 3054 OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), 3055 "ImplRenderer::ImplRenderer(): Invalid graphic device" ); 3056 3057 // make sure canvas and graphic device are valid; action 3058 // creation don't check that every time 3059 if( rCanvas.get() == NULL || 3060 !rCanvas->getUNOCanvas().is() || 3061 !rCanvas->getUNOCanvas()->getDevice().is() ) 3062 { 3063 // leave actions empty 3064 return; 3065 } 3066 3067 OutDevState aState; 3068 3069 const Size aBmpSize( rBmpEx.GetSizePixel() ); 3070 3071 // Setup local state, such that the bitmap renders itself 3072 // into a one-by-one square for identity view and render 3073 // transformations 3074 aState.transform.scale( 1.0 / aBmpSize.Width(), 3075 1.0 / aBmpSize.Height() ); 3076 3077 // create a single action for the provided BitmapEx 3078 maActions.push_back( 3079 MtfAction( 3080 BitmapActionFactory::createBitmapAction( 3081 rBmpEx, 3082 ::basegfx::B2DPoint(), 3083 rCanvas, 3084 aState), 3085 0 ) ); 3086 } 3087 3088 ImplRenderer::~ImplRenderer() 3089 { 3090 } 3091 3092 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex, 3093 sal_Int32 nEndIndex ) const 3094 { 3095 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::drawSubset()" ); 3096 3097 ActionVector::const_iterator aRangeBegin; 3098 ActionVector::const_iterator aRangeEnd; 3099 3100 try 3101 { 3102 if( !getSubsetIndices( nStartIndex, nEndIndex, 3103 aRangeBegin, aRangeEnd ) ) 3104 return true; // nothing to render (but _that_ was successful) 3105 3106 // now, aRangeBegin references the action in which the 3107 // subset rendering must start, and aRangeEnd references 3108 // the action in which the subset rendering must end (it 3109 // might also end right at the start of the referenced 3110 // action, such that zero of that action needs to be 3111 // rendered). 3112 3113 3114 // render subset of actions 3115 // ======================== 3116 3117 ::basegfx::B2DHomMatrix aMatrix; 3118 ::canvas::tools::getRenderStateTransform( aMatrix, 3119 getRenderState() ); 3120 3121 ActionRenderer aRenderer( aMatrix ); 3122 3123 return forSubsetRange( aRenderer, 3124 aRangeBegin, 3125 aRangeEnd, 3126 nStartIndex, 3127 nEndIndex, 3128 maActions.end() ); 3129 } 3130 catch( uno::Exception& ) 3131 { 3132 OSL_ENSURE( false, 3133 rtl::OUStringToOString( 3134 comphelper::anyToString( cppu::getCaughtException() ), 3135 RTL_TEXTENCODING_UTF8 ).getStr() ); 3136 3137 // convert error to return value 3138 return false; 3139 } 3140 } 3141 3142 ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32 nStartIndex, 3143 sal_Int32 nEndIndex ) const 3144 { 3145 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::getSubsetArea()" ); 3146 3147 ActionVector::const_iterator aRangeBegin; 3148 ActionVector::const_iterator aRangeEnd; 3149 3150 if( !getSubsetIndices( nStartIndex, nEndIndex, 3151 aRangeBegin, aRangeEnd ) ) 3152 return ::basegfx::B2DRange(); // nothing to render -> empty range 3153 3154 // now, aRangeBegin references the action in which the 3155 // subset querying must start, and aRangeEnd references 3156 // the action in which the subset querying must end (it 3157 // might also end right at the start of the referenced 3158 // action, such that zero of that action needs to be 3159 // queried). 3160 3161 3162 // query bounds for subset of actions 3163 // ================================== 3164 3165 ::basegfx::B2DHomMatrix aMatrix; 3166 ::canvas::tools::getRenderStateTransform( aMatrix, 3167 getRenderState() ); 3168 3169 AreaQuery aQuery( aMatrix ); 3170 forSubsetRange( aQuery, 3171 aRangeBegin, 3172 aRangeEnd, 3173 nStartIndex, 3174 nEndIndex, 3175 maActions.end() ); 3176 3177 return aQuery.getBounds(); 3178 } 3179 3180 bool ImplRenderer::draw() const 3181 { 3182 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::draw()" ); 3183 3184 ::basegfx::B2DHomMatrix aMatrix; 3185 ::canvas::tools::getRenderStateTransform( aMatrix, 3186 getRenderState() ); 3187 3188 try 3189 { 3190 return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result(); 3191 } 3192 catch( uno::Exception& ) 3193 { 3194 OSL_ENSURE( false, 3195 rtl::OUStringToOString( 3196 comphelper::anyToString( cppu::getCaughtException() ), 3197 RTL_TEXTENCODING_UTF8 ).getStr() ); 3198 3199 return false; 3200 } 3201 } 3202 } 3203 } 3204