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