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