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_vcl.hxx" 26 27 #include <functional> 28 #include <algorithm> 29 #include <utility> 30 #include <list> 31 #include <vector> 32 33 #include <basegfx/polygon/b2dpolygon.hxx> 34 #include <basegfx/polygon/b2dpolygontools.hxx> 35 36 #include <tools/debug.hxx> 37 38 #include <vcl/virdev.hxx> 39 #include <vcl/metaact.hxx> 40 #include <vcl/gdimtf.hxx> 41 #include <vcl/salbtype.hxx> 42 #include <vcl/print.hxx> 43 #include <vcl/svapp.hxx> 44 #include <vcl/bmpacc.hxx> 45 46 #include <print.h> 47 48 #include "pdfwriter_impl.hxx" 49 50 // ----------- 51 // - Defines - 52 // ----------- 53 54 #define MAX_TILE_WIDTH 1024 55 #define MAX_TILE_HEIGHT 1024 56 57 // --------- 58 // - Types - 59 // --------- 60 61 typedef ::std::pair< MetaAction*, int > Component; // MetaAction plus index in metafile 62 63 typedef ::std::list< Component > ComponentList; 64 65 // List of (intersecting) actions, plus overall bounds 66 struct ConnectedComponents 67 { 68 ConnectedComponents() : 69 aComponentList(), 70 aBounds(), 71 aBgColor(COL_WHITE), 72 bIsSpecial(false), 73 bIsFullyTransparent(false) 74 {} 75 76 ComponentList aComponentList; 77 Rectangle aBounds; 78 Color aBgColor; 79 bool bIsSpecial; 80 bool bIsFullyTransparent; 81 }; 82 83 typedef ::std::list< ConnectedComponents > ConnectedComponentsList; 84 85 86 // ----------- 87 // - Printer - 88 // ----------- 89 90 /** #i10613# Extracted from Printer::GetPreparedMetaFile. Returns true 91 if given action requires special handling (usually because of 92 transparency) 93 */ 94 static bool ImplIsActionSpecial( const MetaAction& rAct ) 95 { 96 switch( rAct.GetType() ) 97 { 98 case META_TRANSPARENT_ACTION: 99 return true; 100 101 case META_FLOATTRANSPARENT_ACTION: 102 return true; 103 104 case META_BMPEX_ACTION: 105 return static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx().IsTransparent(); 106 107 case META_BMPEXSCALE_ACTION: 108 return static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx().IsTransparent(); 109 110 case META_BMPEXSCALEPART_ACTION: 111 return static_cast<const MetaBmpExScalePartAction&>(rAct).GetBitmapEx().IsTransparent(); 112 113 default: 114 return false; 115 } 116 } 117 118 /** Check whether rCurrRect rectangle fully covers io_rPrevRect - if 119 yes, return true and update o_rBgColor 120 */ 121 static bool checkRect( Rectangle& io_rPrevRect, 122 Color& o_rBgColor, 123 const Rectangle& rCurrRect, 124 OutputDevice& rMapModeVDev ) 125 { 126 // shape needs to fully cover previous content, and have uniform 127 // color 128 const bool bRet( 129 rMapModeVDev.LogicToPixel(rCurrRect).IsInside(io_rPrevRect) && 130 rMapModeVDev.IsFillColor() ); 131 132 if( bRet ) 133 { 134 io_rPrevRect = rCurrRect; 135 o_rBgColor = rMapModeVDev.GetFillColor(); 136 } 137 138 return bRet; 139 } 140 141 /** #107169# Convert BitmapEx to Bitmap with appropriately blended 142 color. Convert MetaTransparentAction to plain polygon, 143 appropriately colored 144 145 @param o_rMtf 146 Add converted actions to this metafile 147 */ 148 static void ImplConvertTransparentAction( GDIMetaFile& o_rMtf, 149 const MetaAction& rAct, 150 const OutputDevice& rStateOutDev, 151 Color aBgColor ) 152 { 153 if( rAct.GetType() == META_TRANSPARENT_ACTION ) 154 { 155 const MetaTransparentAction* pTransAct = static_cast<const MetaTransparentAction*>(&rAct); 156 sal_uInt16 nTransparency( pTransAct->GetTransparence() ); 157 158 // #i10613# Respect transparency for draw color 159 if( nTransparency ) 160 { 161 o_rMtf.AddAction( new MetaPushAction( PUSH_LINECOLOR|PUSH_FILLCOLOR ) ); 162 163 // assume white background for alpha blending 164 Color aLineColor( rStateOutDev.GetLineColor() ); 165 aLineColor.SetRed( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aLineColor.GetRed()) / 100L ) ); 166 aLineColor.SetGreen( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aLineColor.GetGreen()) / 100L ) ); 167 aLineColor.SetBlue( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aLineColor.GetBlue()) / 100L ) ); 168 o_rMtf.AddAction( new MetaLineColorAction(aLineColor, sal_True) ); 169 170 Color aFillColor( rStateOutDev.GetFillColor() ); 171 aFillColor.SetRed( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aFillColor.GetRed()) / 100L ) ); 172 aFillColor.SetGreen( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aFillColor.GetGreen()) / 100L ) ); 173 aFillColor.SetBlue( static_cast<sal_uInt8>( (255L*nTransparency + (100L - nTransparency)*aFillColor.GetBlue()) / 100L ) ); 174 o_rMtf.AddAction( new MetaFillColorAction(aFillColor, sal_True) ); 175 } 176 177 o_rMtf.AddAction( new MetaPolyPolygonAction(pTransAct->GetPolyPolygon()) ); 178 179 if( nTransparency ) 180 o_rMtf.AddAction( new MetaPopAction() ); 181 } 182 else 183 { 184 BitmapEx aBmpEx; 185 186 switch( rAct.GetType() ) 187 { 188 case META_BMPEX_ACTION: 189 aBmpEx = static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx(); 190 break; 191 192 case META_BMPEXSCALE_ACTION: 193 aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx(); 194 break; 195 196 case META_BMPEXSCALEPART_ACTION: 197 aBmpEx = static_cast<const MetaBmpExScaleAction&>(rAct).GetBitmapEx(); 198 break; 199 200 case META_TRANSPARENT_ACTION: 201 202 default: 203 DBG_ERROR("Printer::GetPreparedMetafile impossible state reached"); 204 break; 205 } 206 207 Bitmap aBmp( aBmpEx.GetBitmap() ); 208 if( !aBmpEx.IsAlpha() ) 209 { 210 // blend with mask 211 BitmapReadAccess* pRA = aBmp.AcquireReadAccess(); 212 213 if( !pRA ) 214 return; // what else should I do? 215 216 Color aActualColor( aBgColor ); 217 218 if( pRA->HasPalette() ) 219 aActualColor = pRA->GetBestPaletteColor( aBgColor ).operator Color(); 220 221 aBmp.ReleaseAccess(pRA); 222 223 // did we get true white? 224 if( aActualColor.GetColorError( aBgColor ) ) 225 { 226 // no, create truecolor bitmap, then 227 aBmp.Convert( BMP_CONVERSION_24BIT ); 228 229 // fill masked out areas white 230 aBmp.Replace( aBmpEx.GetMask(), aBgColor ); 231 } 232 else 233 { 234 // fill masked out areas white 235 aBmp.Replace( aBmpEx.GetMask(), aActualColor ); 236 } 237 } 238 else 239 { 240 // blend with alpha channel 241 aBmp.Convert( BMP_CONVERSION_24BIT ); 242 aBmp.Blend(aBmpEx.GetAlpha(),aBgColor); 243 } 244 245 // add corresponding action 246 switch( rAct.GetType() ) 247 { 248 case META_BMPEX_ACTION: 249 o_rMtf.AddAction( new MetaBmpAction( 250 static_cast<const MetaBmpExAction&>(rAct).GetPoint(), 251 aBmp )); 252 break; 253 case META_BMPEXSCALE_ACTION: 254 o_rMtf.AddAction( new MetaBmpScaleAction( 255 static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(), 256 static_cast<const MetaBmpExScaleAction&>(rAct).GetSize(), 257 aBmp )); 258 break; 259 case META_BMPEXSCALEPART_ACTION: 260 o_rMtf.AddAction( new MetaBmpScalePartAction( 261 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(), 262 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize(), 263 static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcPoint(), 264 static_cast<const MetaBmpExScalePartAction&>(rAct).GetSrcSize(), 265 aBmp )); 266 break; 267 default: 268 DBG_ERROR("Unexpected case"); 269 break; 270 } 271 } 272 } 273 274 // #i10613# Extracted from ImplCheckRect::ImplCreate 275 // Returns true, if given action creates visible (i.e. non-transparent) output 276 static bool ImplIsNotTransparent( const MetaAction& rAct, const OutputDevice& rOut ) 277 { 278 const bool bLineTransparency( rOut.IsLineColor() ? rOut.GetLineColor().GetTransparency() == 255 : true ); 279 const bool bFillTransparency( rOut.IsFillColor() ? rOut.GetFillColor().GetTransparency() == 255 : true ); 280 bool bRet( false ); 281 282 switch( rAct.GetType() ) 283 { 284 case META_POINT_ACTION: 285 if( !bLineTransparency ) 286 bRet = true; 287 break; 288 289 case META_LINE_ACTION: 290 if( !bLineTransparency ) 291 bRet = true; 292 break; 293 294 case META_RECT_ACTION: 295 if( !bLineTransparency || !bFillTransparency ) 296 bRet = true; 297 break; 298 299 case META_ROUNDRECT_ACTION: 300 if( !bLineTransparency || !bFillTransparency ) 301 bRet = true; 302 break; 303 304 case META_ELLIPSE_ACTION: 305 if( !bLineTransparency || !bFillTransparency ) 306 bRet = true; 307 break; 308 309 case META_ARC_ACTION: 310 if( !bLineTransparency || !bFillTransparency ) 311 bRet = true; 312 break; 313 314 case META_PIE_ACTION: 315 if( !bLineTransparency || !bFillTransparency ) 316 bRet = true; 317 break; 318 319 case META_CHORD_ACTION: 320 if( !bLineTransparency || !bFillTransparency ) 321 bRet = true; 322 break; 323 324 case META_POLYLINE_ACTION: 325 if( !bLineTransparency ) 326 bRet = true; 327 break; 328 329 case META_POLYGON_ACTION: 330 if( !bLineTransparency || !bFillTransparency ) 331 bRet = true; 332 break; 333 334 case META_POLYPOLYGON_ACTION: 335 if( !bLineTransparency || !bFillTransparency ) 336 bRet = true; 337 break; 338 339 case META_TEXT_ACTION: 340 { 341 const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct); 342 const XubString aString( rTextAct.GetText(), rTextAct.GetIndex(), rTextAct.GetLen() ); 343 344 if( aString.Len() ) 345 bRet = true; 346 } 347 break; 348 349 case META_TEXTARRAY_ACTION: 350 { 351 const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct); 352 const XubString aString( rTextAct.GetText(), rTextAct.GetIndex(), rTextAct.GetLen() ); 353 354 if( aString.Len() ) 355 bRet = true; 356 } 357 break; 358 359 case META_PIXEL_ACTION: 360 case META_BMP_ACTION: 361 case META_BMPSCALE_ACTION: 362 case META_BMPSCALEPART_ACTION: 363 case META_BMPEX_ACTION: 364 case META_BMPEXSCALE_ACTION: 365 case META_BMPEXSCALEPART_ACTION: 366 case META_MASK_ACTION: 367 case META_MASKSCALE_ACTION: 368 case META_MASKSCALEPART_ACTION: 369 case META_GRADIENT_ACTION: 370 case META_GRADIENTEX_ACTION: 371 case META_HATCH_ACTION: 372 case META_WALLPAPER_ACTION: 373 case META_TRANSPARENT_ACTION: 374 case META_FLOATTRANSPARENT_ACTION: 375 case META_EPS_ACTION: 376 case META_TEXTRECT_ACTION: 377 case META_STRETCHTEXT_ACTION: 378 case META_TEXTLINE_ACTION: 379 // all other actions: generate non-transparent output 380 bRet = true; 381 break; 382 383 default: 384 break; 385 } 386 387 return bRet; 388 } 389 390 // #i10613# Extracted from ImplCheckRect::ImplCreate 391 static Rectangle ImplCalcActionBounds( const MetaAction& rAct, const OutputDevice& rOut ) 392 { 393 Rectangle aActionBounds; 394 395 switch( rAct.GetType() ) 396 { 397 case META_PIXEL_ACTION: 398 aActionBounds = Rectangle( static_cast<const MetaPixelAction&>(rAct).GetPoint(), Size( 1, 1 ) ); 399 break; 400 401 case META_POINT_ACTION: 402 aActionBounds = Rectangle( static_cast<const MetaPointAction&>(rAct).GetPoint(), Size( 1, 1 ) ); 403 break; 404 405 case META_LINE_ACTION: 406 { 407 const MetaLineAction& rMetaLineAction = static_cast<const MetaLineAction&>(rAct); 408 aActionBounds = Rectangle( rMetaLineAction.GetStartPoint(), rMetaLineAction.GetEndPoint() ); 409 aActionBounds.Justify(); 410 const long nLineWidth(rMetaLineAction.GetLineInfo().GetWidth()); 411 if(nLineWidth) 412 { 413 const long nHalfLineWidth((nLineWidth + 1) / 2); 414 aActionBounds.Left() -= nHalfLineWidth; 415 aActionBounds.Top() -= nHalfLineWidth; 416 aActionBounds.Right() += nHalfLineWidth; 417 aActionBounds.Bottom() += nHalfLineWidth; 418 } 419 break; 420 } 421 422 case META_RECT_ACTION: 423 aActionBounds = static_cast<const MetaRectAction&>(rAct).GetRect(); 424 break; 425 426 case META_ROUNDRECT_ACTION: 427 aActionBounds = Polygon( static_cast<const MetaRoundRectAction&>(rAct).GetRect(), 428 static_cast<const MetaRoundRectAction&>(rAct).GetHorzRound(), 429 static_cast<const MetaRoundRectAction&>(rAct).GetVertRound() ).GetBoundRect(); 430 break; 431 432 case META_ELLIPSE_ACTION: 433 { 434 const Rectangle& rRect = static_cast<const MetaEllipseAction&>(rAct).GetRect(); 435 aActionBounds = Polygon( rRect.Center(), 436 rRect.GetWidth() >> 1, 437 rRect.GetHeight() >> 1 ).GetBoundRect(); 438 break; 439 } 440 441 case META_ARC_ACTION: 442 aActionBounds = Polygon( static_cast<const MetaArcAction&>(rAct).GetRect(), 443 static_cast<const MetaArcAction&>(rAct).GetStartPoint(), 444 static_cast<const MetaArcAction&>(rAct).GetEndPoint(), POLY_ARC ).GetBoundRect(); 445 break; 446 447 case META_PIE_ACTION: 448 aActionBounds = Polygon( static_cast<const MetaPieAction&>(rAct).GetRect(), 449 static_cast<const MetaPieAction&>(rAct).GetStartPoint(), 450 static_cast<const MetaPieAction&>(rAct).GetEndPoint(), POLY_PIE ).GetBoundRect(); 451 break; 452 453 case META_CHORD_ACTION: 454 aActionBounds = Polygon( static_cast<const MetaChordAction&>(rAct).GetRect(), 455 static_cast<const MetaChordAction&>(rAct).GetStartPoint(), 456 static_cast<const MetaChordAction&>(rAct).GetEndPoint(), POLY_CHORD ).GetBoundRect(); 457 break; 458 459 case META_POLYLINE_ACTION: 460 { 461 const MetaPolyLineAction& rMetaPolyLineAction = static_cast<const MetaPolyLineAction&>(rAct); 462 aActionBounds = rMetaPolyLineAction.GetPolygon().GetBoundRect(); 463 const long nLineWidth(rMetaPolyLineAction.GetLineInfo().GetWidth()); 464 if(nLineWidth) 465 { 466 const long nHalfLineWidth((nLineWidth + 1) / 2); 467 aActionBounds.Left() -= nHalfLineWidth; 468 aActionBounds.Top() -= nHalfLineWidth; 469 aActionBounds.Right() += nHalfLineWidth; 470 aActionBounds.Bottom() += nHalfLineWidth; 471 } 472 break; 473 } 474 475 case META_POLYGON_ACTION: 476 aActionBounds = static_cast<const MetaPolygonAction&>(rAct).GetPolygon().GetBoundRect(); 477 break; 478 479 case META_POLYPOLYGON_ACTION: 480 aActionBounds = static_cast<const MetaPolyPolygonAction&>(rAct).GetPolyPolygon().GetBoundRect(); 481 break; 482 483 case META_BMP_ACTION: 484 aActionBounds = Rectangle( static_cast<const MetaBmpAction&>(rAct).GetPoint(), 485 rOut.PixelToLogic( static_cast<const MetaBmpAction&>(rAct).GetBitmap().GetSizePixel() ) ); 486 break; 487 488 case META_BMPSCALE_ACTION: 489 aActionBounds = Rectangle( static_cast<const MetaBmpScaleAction&>(rAct).GetPoint(), 490 static_cast<const MetaBmpScaleAction&>(rAct).GetSize() ); 491 break; 492 493 case META_BMPSCALEPART_ACTION: 494 aActionBounds = Rectangle( static_cast<const MetaBmpScalePartAction&>(rAct).GetDestPoint(), 495 static_cast<const MetaBmpScalePartAction&>(rAct).GetDestSize() ); 496 break; 497 498 case META_BMPEX_ACTION: 499 aActionBounds = Rectangle( static_cast<const MetaBmpExAction&>(rAct).GetPoint(), 500 rOut.PixelToLogic( static_cast<const MetaBmpExAction&>(rAct).GetBitmapEx().GetSizePixel() ) ); 501 break; 502 503 case META_BMPEXSCALE_ACTION: 504 aActionBounds = Rectangle( static_cast<const MetaBmpExScaleAction&>(rAct).GetPoint(), 505 static_cast<const MetaBmpExScaleAction&>(rAct).GetSize() ); 506 break; 507 508 case META_BMPEXSCALEPART_ACTION: 509 aActionBounds = Rectangle( static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestPoint(), 510 static_cast<const MetaBmpExScalePartAction&>(rAct).GetDestSize() ); 511 break; 512 513 case META_MASK_ACTION: 514 aActionBounds = Rectangle( static_cast<const MetaMaskAction&>(rAct).GetPoint(), 515 rOut.PixelToLogic( static_cast<const MetaMaskAction&>(rAct).GetBitmap().GetSizePixel() ) ); 516 break; 517 518 case META_MASKSCALE_ACTION: 519 aActionBounds = Rectangle( static_cast<const MetaMaskScaleAction&>(rAct).GetPoint(), 520 static_cast<const MetaMaskScaleAction&>(rAct).GetSize() ); 521 break; 522 523 case META_MASKSCALEPART_ACTION: 524 aActionBounds = Rectangle( static_cast<const MetaMaskScalePartAction&>(rAct).GetDestPoint(), 525 static_cast<const MetaMaskScalePartAction&>(rAct).GetDestSize() ); 526 break; 527 528 case META_GRADIENT_ACTION: 529 aActionBounds = static_cast<const MetaGradientAction&>(rAct).GetRect(); 530 break; 531 532 case META_GRADIENTEX_ACTION: 533 aActionBounds = static_cast<const MetaGradientExAction&>(rAct).GetPolyPolygon().GetBoundRect(); 534 break; 535 536 case META_HATCH_ACTION: 537 aActionBounds = static_cast<const MetaHatchAction&>(rAct).GetPolyPolygon().GetBoundRect(); 538 break; 539 540 case META_WALLPAPER_ACTION: 541 aActionBounds = static_cast<const MetaWallpaperAction&>(rAct).GetRect(); 542 break; 543 544 case META_TRANSPARENT_ACTION: 545 aActionBounds = static_cast<const MetaTransparentAction&>(rAct).GetPolyPolygon().GetBoundRect(); 546 break; 547 548 case META_FLOATTRANSPARENT_ACTION: 549 aActionBounds = Rectangle( static_cast<const MetaFloatTransparentAction&>(rAct).GetPoint(), 550 static_cast<const MetaFloatTransparentAction&>(rAct).GetSize() ); 551 break; 552 553 case META_EPS_ACTION: 554 aActionBounds = Rectangle( static_cast<const MetaEPSAction&>(rAct).GetPoint(), 555 static_cast<const MetaEPSAction&>(rAct).GetSize() ); 556 break; 557 558 case META_TEXT_ACTION: 559 { 560 const MetaTextAction& rTextAct = static_cast<const MetaTextAction&>(rAct); 561 const XubString aString( rTextAct.GetText(), rTextAct.GetIndex(), rTextAct.GetLen() ); 562 563 if( aString.Len() ) 564 { 565 const Point aPtLog( rTextAct.GetPoint() ); 566 567 // #105987# Use API method instead of Impl* methods 568 // #107490# Set base parameter equal to index parameter 569 rOut.GetTextBoundRect( aActionBounds, rTextAct.GetText(), rTextAct.GetIndex(), 570 rTextAct.GetIndex(), rTextAct.GetLen() ); 571 aActionBounds.Move( aPtLog.X(), aPtLog.Y() ); 572 } 573 } 574 break; 575 576 case META_TEXTARRAY_ACTION: 577 { 578 const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct); 579 const XubString aString( rTextAct.GetText(), rTextAct.GetIndex(), rTextAct.GetLen() ); 580 const long nLen = aString.Len(); 581 582 if( nLen ) 583 { 584 // #105987# ImplLayout takes everything in logical coordinates 585 SalLayout* pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(), 586 rTextAct.GetLen(), rTextAct.GetPoint(), 587 0, rTextAct.GetDXArray() ); 588 if( pSalLayout ) 589 { 590 Rectangle aBoundRect( const_cast<OutputDevice&>(rOut).ImplGetTextBoundRect( *pSalLayout ) ); 591 aActionBounds = rOut.PixelToLogic( aBoundRect ); 592 pSalLayout->Release(); 593 } 594 } 595 } 596 break; 597 598 case META_TEXTRECT_ACTION: 599 aActionBounds = static_cast<const MetaTextRectAction&>(rAct).GetRect(); 600 break; 601 602 case META_STRETCHTEXT_ACTION: 603 { 604 const MetaStretchTextAction& rTextAct = static_cast<const MetaStretchTextAction&>(rAct); 605 const XubString aString( rTextAct.GetText(), rTextAct.GetIndex(), rTextAct.GetLen() ); 606 const long nLen = aString.Len(); 607 608 // #i16195# Literate copy from TextArray action, the 609 // semantics for the ImplLayout call are copied from the 610 // OutDev::DrawStretchText() code. Unfortunately, also in 611 // this case, public outdev methods such as GetTextWidth() 612 // don't provide enough info. 613 if( nLen ) 614 { 615 // #105987# ImplLayout takes everything in logical coordinates 616 SalLayout* pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(), 617 rTextAct.GetLen(), rTextAct.GetPoint(), 618 rTextAct.GetWidth() ); 619 if( pSalLayout ) 620 { 621 Rectangle aBoundRect( const_cast<OutputDevice&>(rOut).ImplGetTextBoundRect( *pSalLayout ) ); 622 aActionBounds = rOut.PixelToLogic( aBoundRect ); 623 pSalLayout->Release(); 624 } 625 } 626 } 627 break; 628 629 case META_TEXTLINE_ACTION: 630 DBG_ERROR("META_TEXTLINE_ACTION not supported"); 631 break; 632 633 default: 634 break; 635 } 636 637 if( !aActionBounds.IsEmpty() ) 638 return rOut.LogicToPixel( aActionBounds ); 639 else 640 return Rectangle(); 641 } 642 643 static bool ImplIsActionHandlingTransparency( const MetaAction& rAct ) 644 { 645 // META_FLOATTRANSPARENT_ACTION can contain a whole metafile, 646 // which is to be rendered with the given transparent gradient. We 647 // currently cannot emulate transparent painting on a white 648 // background reliably. 649 650 // the remainder can handle printing itself correctly on a uniform 651 // white background. 652 switch( rAct.GetType() ) 653 { 654 case META_TRANSPARENT_ACTION: 655 case META_BMPEX_ACTION: 656 case META_BMPEXSCALE_ACTION: 657 case META_BMPEXSCALEPART_ACTION: 658 return true; 659 660 default: 661 return false; 662 } 663 } 664 665 // remove comment to enable highlighting of generated output 666 bool OutputDevice::RemoveTransparenciesFromMetaFile( const GDIMetaFile& rInMtf, GDIMetaFile& rOutMtf, 667 long nMaxBmpDPIX, long nMaxBmpDPIY, 668 bool bReduceTransparency, bool bTransparencyAutoMode, 669 bool bDownsampleBitmaps, 670 const Color& rBackground 671 ) 672 { 673 MetaAction* pCurrAct; 674 bool bTransparent( false ); 675 676 rOutMtf.Clear(); 677 678 if( ! bReduceTransparency || bTransparencyAutoMode ) 679 { 680 // watch for transparent drawing actions 681 for( pCurrAct = ( (GDIMetaFile&) rInMtf ).FirstAction(); 682 pCurrAct && !bTransparent; 683 pCurrAct = ( (GDIMetaFile&) rInMtf ).NextAction() ) 684 { 685 // #i10613# Extracted "specialness" predicate into extra method 686 687 // #107169# Also examine metafiles with masked bitmaps in 688 // detail. Further down, this is optimized in such a way 689 // that there's no unnecessary painting of masked bitmaps 690 // (which are _always_ subdivided into rectangular regions 691 // of uniform opacity): if a masked bitmap is printed over 692 // empty background, we convert to a plain bitmap with 693 // white background. 694 if( ImplIsActionSpecial( *pCurrAct ) ) 695 { 696 bTransparent = true; 697 } 698 } 699 } 700 701 // #i10613# Determine set of connected components containing transparent objects. These are 702 // then processed as bitmaps, the original actions are removed from the metafile. 703 if( !bTransparent ) 704 { 705 // nothing transparent -> just copy 706 rOutMtf = rInMtf; 707 } 708 else 709 { 710 // #i10613# 711 // This works as follows: we want a number of distinct sets of 712 // connected components, where each set contains metafile 713 // actions that are intersecting (note: there are possibly 714 // more actions contained as are directly intersecting, 715 // because we can only produce rectangular bitmaps later 716 // on. Thus, each set of connected components is the smallest 717 // enclosing, axis-aligned rectangle that completely bounds a 718 // number of intersecting metafile actions, plus any action 719 // that would otherwise be cut in two). Therefore, we 720 // iteratively add metafile actions from the original metafile 721 // to this connected components list (aCCList), by checking 722 // each element's bounding box against intersection with the 723 // metaaction at hand. 724 // All those intersecting elements are removed from aCCList 725 // and collected in a temporary list (aCCMergeList). After all 726 // elements have been checked, the aCCMergeList elements are 727 // merged with the metaaction at hand into one resulting 728 // connected component, with one big bounding box, and 729 // inserted into aCCList again. 730 // The time complexity of this algorithm is O(n^3), where n is 731 // the number of metafile actions, and it finds all distinct 732 // regions of rectangle-bounded connected components. This 733 // algorithm was designed by AF. 734 // 735 736 // 737 // STAGE 1: Detect background 738 // ========================== 739 // 740 741 // Receives uniform background content, and is _not_ merged 742 // nor checked for intersection against other aCCList elements 743 ConnectedComponents aBackgroundComponent; 744 745 // create an OutputDevice to record mapmode changes and the like 746 VirtualDevice aMapModeVDev; 747 aMapModeVDev.mnDPIX = mnDPIX; 748 aMapModeVDev.mnDPIY = mnDPIY; 749 aMapModeVDev.EnableOutput(sal_False); 750 751 int nLastBgAction, nActionNum; 752 753 // weed out page-filling background objects (if they are 754 // uniformly coloured). Keeping them outside the other 755 // connected components often prevents whole-page bitmap 756 // generation. 757 bool bStillBackground=true; // true until first non-bg action 758 nActionNum=0; nLastBgAction=-1; 759 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(); 760 if( rBackground != Color( COL_TRANSPARENT ) ) 761 { 762 aBackgroundComponent.aBgColor = rBackground; 763 if( meOutDevType == OUTDEV_PRINTER ) 764 { 765 Printer* pThis = dynamic_cast<Printer*>(this); 766 Point aPageOffset = pThis->GetPageOffsetPixel(); 767 aPageOffset = Point( 0, 0 ) - aPageOffset; 768 Size aSize = pThis->GetPaperSizePixel(); 769 aBackgroundComponent.aBounds = Rectangle( aPageOffset, aSize ); 770 } 771 else 772 aBackgroundComponent.aBounds = Rectangle( Point( 0, 0 ), GetOutputSizePixel() ); 773 } 774 while( pCurrAct && bStillBackground ) 775 { 776 switch( pCurrAct->GetType() ) 777 { 778 case META_RECT_ACTION: 779 { 780 if( !checkRect( 781 aBackgroundComponent.aBounds, 782 aBackgroundComponent.aBgColor, 783 static_cast<const MetaRectAction*>(pCurrAct)->GetRect(), 784 aMapModeVDev) ) 785 bStillBackground=false; // incomplete occlusion of background 786 else 787 nLastBgAction=nActionNum; // this _is_ background 788 break; 789 } 790 case META_POLYGON_ACTION: 791 { 792 const Polygon aPoly( 793 static_cast<const MetaPolygonAction*>(pCurrAct)->GetPolygon()); 794 if( !basegfx::tools::isRectangle( 795 aPoly.getB2DPolygon()) || 796 !checkRect( 797 aBackgroundComponent.aBounds, 798 aBackgroundComponent.aBgColor, 799 aPoly.GetBoundRect(), 800 aMapModeVDev) ) 801 bStillBackground=false; // incomplete occlusion of background 802 else 803 nLastBgAction=nActionNum; // this _is_ background 804 break; 805 } 806 case META_POLYPOLYGON_ACTION: 807 { 808 const PolyPolygon aPoly( 809 static_cast<const MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon()); 810 if( aPoly.Count() != 1 || 811 !basegfx::tools::isRectangle( 812 aPoly[0].getB2DPolygon()) || 813 !checkRect( 814 aBackgroundComponent.aBounds, 815 aBackgroundComponent.aBgColor, 816 aPoly.GetBoundRect(), 817 aMapModeVDev) ) 818 bStillBackground=false; // incomplete occlusion of background 819 else 820 nLastBgAction=nActionNum; // this _is_ background 821 break; 822 } 823 case META_WALLPAPER_ACTION: 824 { 825 if( !checkRect( 826 aBackgroundComponent.aBounds, 827 aBackgroundComponent.aBgColor, 828 static_cast<const MetaWallpaperAction*>(pCurrAct)->GetRect(), 829 aMapModeVDev) ) 830 bStillBackground=false; // incomplete occlusion of background 831 else 832 nLastBgAction=nActionNum; // this _is_ background 833 break; 834 } 835 default: 836 { 837 if( ImplIsNotTransparent( *pCurrAct, 838 aMapModeVDev ) ) 839 bStillBackground=false; // non-transparent action, possibly 840 // not uniform 841 else 842 // extend current bounds (next uniform action 843 // needs to fully cover this area) 844 aBackgroundComponent.aBounds.Union( 845 ImplCalcActionBounds(*pCurrAct, aMapModeVDev) ); 846 break; 847 } 848 } 849 850 // execute action to get correct MapModes etc. 851 pCurrAct->Execute( &aMapModeVDev ); 852 853 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(); 854 ++nActionNum; 855 } 856 857 // clean up aMapModeVDev 858 sal_uInt32 nCount = aMapModeVDev.GetGCStackDepth(); 859 while( nCount-- ) 860 aMapModeVDev.Pop(); 861 862 ConnectedComponentsList aCCList; // list containing distinct sets of connected components as elements. 863 864 // fast-forward until one after the last background action 865 // (need to reconstruct map mode vdev state) 866 nActionNum=0; 867 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(); 868 while( pCurrAct && nActionNum<=nLastBgAction ) 869 { 870 // up to and including last ink-generating background 871 // action go to background component 872 aBackgroundComponent.aComponentList.push_back( 873 ::std::make_pair( 874 pCurrAct, nActionNum) ); 875 876 // execute action to get correct MapModes etc. 877 pCurrAct->Execute( &aMapModeVDev ); 878 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(); 879 ++nActionNum; 880 } 881 882 // 883 // STAGE 2: Generate connected components list 884 // =========================================== 885 // 886 887 // iterate over all actions (start where background action 888 // search left off) 889 for( ; 890 pCurrAct; 891 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum ) 892 { 893 // execute action to get correct MapModes etc. 894 pCurrAct->Execute( &aMapModeVDev ); 895 896 // cache bounds of current action 897 const Rectangle aBBCurrAct( ImplCalcActionBounds(*pCurrAct, aMapModeVDev) ); 898 899 // accumulate collected bounds here, initialize with current action 900 Rectangle aTotalBounds( aBBCurrAct ); // thus, 901 // aTotalComponents.aBounds 902 // is 903 // empty 904 // for 905 // non-output-generating 906 // actions 907 bool bTreatSpecial( false ); 908 ConnectedComponents aTotalComponents; 909 910 // 911 // STAGE 2.1: Search for intersecting cc entries 912 // ============================================= 913 // 914 915 // if aBBCurrAct is empty, it will intersect with no 916 // aCCList member. Thus, we can save the check. 917 // Furthermore, this ensures that non-output-generating 918 // actions get their own aCCList entry, which is necessary 919 // when copying them to the output metafile (see stage 4 920 // below). 921 922 // #107169# Wholly transparent objects need 923 // not be considered for connected components, 924 // too. Just put each of them into a separate 925 // component. 926 aTotalComponents.bIsFullyTransparent = !ImplIsNotTransparent(*pCurrAct, aMapModeVDev); 927 928 if( !aBBCurrAct.IsEmpty() && 929 !aTotalComponents.bIsFullyTransparent ) 930 { 931 if( !aBackgroundComponent.aComponentList.empty() && 932 !aBackgroundComponent.aBounds.IsInside(aTotalBounds) ) 933 { 934 // it seems the background is not large enough. to 935 // be on the safe side, combine with this component. 936 aTotalBounds.Union( aBackgroundComponent.aBounds ); 937 938 // extract all aCurr actions to aTotalComponents 939 aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(), 940 aBackgroundComponent.aComponentList ); 941 942 if( aBackgroundComponent.bIsSpecial ) 943 bTreatSpecial = true; 944 } 945 946 ConnectedComponentsList::iterator aCurrCC; 947 const ConnectedComponentsList::iterator aLastCC( aCCList.end() ); 948 bool bSomeComponentsChanged; 949 950 // now, this is unfortunate: since changing anyone of 951 // the aCCList elements (e.g. by merging or addition 952 // of an action) might generate new intersection with 953 // other aCCList elements, have to repeat the whole 954 // element scanning, until nothing changes anymore. 955 // Thus, this loop here makes us O(n^3) in the worst 956 // case. 957 do 958 { 959 // only loop here if 'intersects' branch below was hit 960 bSomeComponentsChanged = false; 961 962 // iterate over all current members of aCCList 963 for( aCurrCC=aCCList.begin(); aCurrCC != aLastCC; ) 964 { 965 // first check if current element's bounds are 966 // empty. This ensures that empty actions are not 967 // merged into one component, as a matter of fact, 968 // they have no position. 969 970 // #107169# Wholly transparent objects need 971 // not be considered for connected components, 972 // too. Just put each of them into a separate 973 // component. 974 if( !aCurrCC->aBounds.IsEmpty() && 975 !aCurrCC->bIsFullyTransparent && 976 aCurrCC->aBounds.IsOver( aTotalBounds ) ) 977 { 978 // union the intersecting aCCList element into aTotalComponents 979 980 // calc union bounding box 981 aTotalBounds.Union( aCurrCC->aBounds ); 982 983 // extract all aCurr actions to aTotalComponents 984 aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(), 985 aCurrCC->aComponentList ); 986 987 if( aCurrCC->bIsSpecial ) 988 bTreatSpecial = true; 989 990 // remove and delete aCurrCC element from list (we've now merged its content) 991 aCurrCC = aCCList.erase( aCurrCC ); 992 993 // at least one component changed, need to rescan everything 994 bSomeComponentsChanged = true; 995 } 996 else 997 { 998 ++aCurrCC; 999 } 1000 } 1001 } 1002 while( bSomeComponentsChanged ); 1003 } 1004 1005 // 1006 // STAGE 2.2: Determine special state for cc element 1007 // ================================================= 1008 // 1009 1010 // now test whether the whole connected component must be 1011 // treated specially (i.e. rendered as a bitmap): if the 1012 // added action is the very first action, or all actions 1013 // before it are completely transparent, the connected 1014 // component need not be treated specially, not even if 1015 // the added action contains transparency. This is because 1016 // painting of transparent objects on _white background_ 1017 // works without alpha compositing (you just calculate the 1018 // color). Note that for the test "all objects before me 1019 // are transparent" no sorting is necessary, since the 1020 // added metaaction pCurrAct is always in the order the 1021 // metafile is painted. Generally, the order of the 1022 // metaactions in the ConnectedComponents are not 1023 // guaranteed to be the same as in the metafile. 1024 if( bTreatSpecial ) 1025 { 1026 // prev component(s) special -> this one, too 1027 aTotalComponents.bIsSpecial = true; 1028 } 1029 else if( !ImplIsActionSpecial( *pCurrAct ) ) 1030 { 1031 // added action and none of prev components special -> 1032 // this one normal, too 1033 aTotalComponents.bIsSpecial = false; 1034 } 1035 else 1036 { 1037 // added action is special and none of prev components 1038 // special -> do the detailed tests 1039 1040 // can the action handle transparency correctly 1041 // (i.e. when painted on white background, does the 1042 // action still look correct)? 1043 if( !ImplIsActionHandlingTransparency( *pCurrAct ) ) 1044 { 1045 // no, action cannot handle its transparency on 1046 // a printer device, render to bitmap 1047 aTotalComponents.bIsSpecial = true; 1048 } 1049 else 1050 { 1051 // yes, action can handle its transparency, so 1052 // check whether we're on white background 1053 if( aTotalComponents.aComponentList.empty() ) 1054 { 1055 // nothing between pCurrAct and page 1056 // background -> don't be special 1057 aTotalComponents.bIsSpecial = false; 1058 } 1059 else 1060 { 1061 // #107169# Fixes abnove now ensure that _no_ 1062 // object in the list is fully transparent. Thus, 1063 // if the component list is not empty above, we 1064 // must assume that we have to treat this 1065 // component special. 1066 1067 // there are non-transparent objects between 1068 // pCurrAct and the empty sheet of paper -> be 1069 // special, then 1070 aTotalComponents.bIsSpecial = true; 1071 } 1072 } 1073 } 1074 1075 1076 // 1077 // STAGE 2.3: Add newly generated CC list element 1078 // ============================================== 1079 // 1080 1081 // set new bounds and add action to list 1082 aTotalComponents.aBounds = aTotalBounds; 1083 aTotalComponents.aComponentList.push_back( 1084 ::std::make_pair( 1085 pCurrAct, nActionNum) ); 1086 1087 // add aTotalComponents as a new entry to aCCList 1088 aCCList.push_back( aTotalComponents ); 1089 1090 DBG_ASSERT( !aTotalComponents.aComponentList.empty(), 1091 "Printer::GetPreparedMetaFile empty component" ); 1092 DBG_ASSERT( !aTotalComponents.aBounds.IsEmpty() || 1093 (aTotalComponents.aBounds.IsEmpty() && aTotalComponents.aComponentList.size() == 1), 1094 "Printer::GetPreparedMetaFile non-output generating actions must be solitary"); 1095 DBG_ASSERT( !aTotalComponents.bIsFullyTransparent || 1096 (aTotalComponents.bIsFullyTransparent && aTotalComponents.aComponentList.size() == 1), 1097 "Printer::GetPreparedMetaFile fully transparent actions must be solitary"); 1098 } 1099 1100 // well now, we've got the list of disjunct connected 1101 // components. Now we've got to create a map, which contains 1102 // the corresponding aCCList element for every 1103 // metaaction. Later on, we always process the complete 1104 // metafile for each bitmap to be generated, but switch on 1105 // output only for actions contained in the then current 1106 // aCCList element. This ensures correct mapmode and attribute 1107 // settings for all cases. 1108 1109 // maps mtf actions to CC list entries 1110 ::std::vector< const ConnectedComponents* > aCCList_MemberMap( rInMtf.GetActionCount() ); 1111 1112 // iterate over all aCCList members and their contained metaactions 1113 ConnectedComponentsList::iterator aCurr( aCCList.begin() ); 1114 const ConnectedComponentsList::iterator aLast( aCCList.end() ); 1115 for( ; aCurr != aLast; ++aCurr ) 1116 { 1117 ComponentList::iterator aCurrentAction( aCurr->aComponentList.begin() ); 1118 const ComponentList::iterator aLastAction( aCurr->aComponentList.end() ); 1119 for( ; aCurrentAction != aLastAction; ++aCurrentAction ) 1120 { 1121 // set pointer to aCCList element for corresponding index 1122 aCCList_MemberMap[ aCurrentAction->second ] = &(*aCurr); 1123 } 1124 } 1125 1126 // 1127 // STAGE 3.1: Output background mtf actions (if there are any) 1128 // =========================================================== 1129 // 1130 1131 ComponentList::iterator aCurrAct( aBackgroundComponent.aComponentList.begin() ); 1132 const ComponentList::iterator aLastAct( aBackgroundComponent.aComponentList.end() ); 1133 for( ; aCurrAct != aLastAct; ++aCurrAct ) 1134 { 1135 // simply add this action (above, we inserted the actions 1136 // starting at index 0 up to and including nLastBgAction) 1137 rOutMtf.AddAction( ( aCurrAct->first->Duplicate(), aCurrAct->first ) ); 1138 } 1139 1140 1141 // 1142 // STAGE 3.2: Generate banded bitmaps for special regions 1143 // ==================================================== 1144 // 1145 1146 Point aPageOffset; 1147 Size aTmpSize( GetOutputSizePixel() ); 1148 if( mpPDFWriter ) 1149 { 1150 aTmpSize = mpPDFWriter->getCurPageSize(); 1151 aTmpSize = LogicToPixel( aTmpSize, MapMode( MAP_POINT ) ); 1152 1153 // also add error code to PDFWriter 1154 mpPDFWriter->insertError( vcl::PDFWriter::Warning_Transparency_Converted ); 1155 } 1156 else if( meOutDevType == OUTDEV_PRINTER ) 1157 { 1158 Printer* pThis = dynamic_cast<Printer*>(this); 1159 aPageOffset = pThis->GetPageOffsetPixel(); 1160 aPageOffset = Point( 0, 0 ) - aPageOffset; 1161 aTmpSize = pThis->GetPaperSizePixel(); 1162 } 1163 const Rectangle aOutputRect( aPageOffset, aTmpSize ); 1164 bool bTiling = dynamic_cast<Printer*>(this) != NULL; 1165 1166 // iterate over all aCCList members and generate bitmaps for the special ones 1167 for( aCurr = aCCList.begin(); aCurr != aLast; ++aCurr ) 1168 { 1169 if( aCurr->bIsSpecial ) 1170 { 1171 Rectangle aBoundRect( aCurr->aBounds ); 1172 aBoundRect.Intersection( aOutputRect ); 1173 1174 const double fBmpArea( (double) aBoundRect.GetWidth() * aBoundRect.GetHeight() ); 1175 const double fOutArea( (double) aOutputRect.GetWidth() * aOutputRect.GetHeight() ); 1176 1177 // check if output doesn't exceed given size 1178 if( bReduceTransparency && bTransparencyAutoMode && ( fBmpArea > ( 0.25 * fOutArea ) ) ) 1179 { 1180 // output normally. Therefore, we simply clear the 1181 // special attribute, as everything non-special is 1182 // copied to rOutMtf further below. 1183 aCurr->bIsSpecial = false; 1184 } 1185 else 1186 { 1187 // create new bitmap action first 1188 if( aBoundRect.GetWidth() && aBoundRect.GetHeight() ) 1189 { 1190 Point aDstPtPix( aBoundRect.TopLeft() ); 1191 Size aDstSzPix; 1192 1193 VirtualDevice aMapVDev; // here, we record only mapmode information 1194 aMapVDev.EnableOutput(sal_False); 1195 1196 VirtualDevice aPaintVDev; // into this one, we render. 1197 aPaintVDev.SetBackground( aBackgroundComponent.aBgColor ); 1198 1199 rOutMtf.AddAction( new MetaPushAction( PUSH_MAPMODE ) ); 1200 rOutMtf.AddAction( new MetaMapModeAction() ); 1201 1202 aPaintVDev.SetDrawMode( GetDrawMode() ); 1203 1204 while( aDstPtPix.Y() <= aBoundRect.Bottom() ) 1205 { 1206 aDstPtPix.X() = aBoundRect.Left(); 1207 aDstSzPix = bTiling ? Size( MAX_TILE_WIDTH, MAX_TILE_HEIGHT ) : aBoundRect.GetSize(); 1208 1209 if( ( aDstPtPix.Y() + aDstSzPix.Height() - 1L ) > aBoundRect.Bottom() ) 1210 aDstSzPix.Height() = aBoundRect.Bottom() - aDstPtPix.Y() + 1L; 1211 1212 while( aDstPtPix.X() <= aBoundRect.Right() ) 1213 { 1214 if( ( aDstPtPix.X() + aDstSzPix.Width() - 1L ) > aBoundRect.Right() ) 1215 aDstSzPix.Width() = aBoundRect.Right() - aDstPtPix.X() + 1L; 1216 1217 if( !Rectangle( aDstPtPix, aDstSzPix ).Intersection( aBoundRect ).IsEmpty() && 1218 aPaintVDev.SetOutputSizePixel( aDstSzPix ) ) 1219 { 1220 aPaintVDev.Push(); 1221 aMapVDev.Push(); 1222 1223 aMapVDev.mnDPIX = aPaintVDev.mnDPIX = mnDPIX; 1224 aMapVDev.mnDPIY = aPaintVDev.mnDPIY = mnDPIY; 1225 1226 aPaintVDev.EnableOutput(sal_False); 1227 1228 // iterate over all actions 1229 for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0; 1230 pCurrAct; 1231 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum ) 1232 { 1233 // enable output only for 1234 // actions that are members of 1235 // the current aCCList element 1236 // (aCurr) 1237 if( aCCList_MemberMap[nActionNum] == &(*aCurr) ) 1238 aPaintVDev.EnableOutput(sal_True); 1239 1240 // but process every action 1241 const sal_uInt16 nType( pCurrAct->GetType() ); 1242 1243 if( META_MAPMODE_ACTION == nType ) 1244 { 1245 pCurrAct->Execute( &aMapVDev ); 1246 1247 MapMode aMtfMap( aMapVDev.GetMapMode() ); 1248 const Point aNewOrg( aMapVDev.PixelToLogic( aDstPtPix ) ); 1249 1250 aMtfMap.SetOrigin( Point( -aNewOrg.X(), -aNewOrg.Y() ) ); 1251 aPaintVDev.SetMapMode( aMtfMap ); 1252 } 1253 else if( ( META_PUSH_ACTION == nType ) || ( META_POP_ACTION ) == nType ) 1254 { 1255 pCurrAct->Execute( &aMapVDev ); 1256 pCurrAct->Execute( &aPaintVDev ); 1257 } 1258 else if( META_GRADIENT_ACTION == nType ) 1259 { 1260 MetaGradientAction* pGradientAction = static_cast<MetaGradientAction*>(pCurrAct); 1261 Printer* pPrinter = dynamic_cast< Printer* >(this); 1262 if( pPrinter ) 1263 pPrinter->DrawGradientEx( &aPaintVDev, pGradientAction->GetRect(), pGradientAction->GetGradient() ); 1264 else 1265 DrawGradient( pGradientAction->GetRect(), pGradientAction->GetGradient() ); 1266 } 1267 else 1268 { 1269 pCurrAct->Execute( &aPaintVDev ); 1270 } 1271 1272 if( !( nActionNum % 8 ) ) 1273 Application::Reschedule(); 1274 } 1275 1276 const sal_Bool bOldMap = mbMap; 1277 mbMap = aPaintVDev.mbMap = sal_False; 1278 1279 Bitmap aBandBmp( aPaintVDev.GetBitmap( Point(), aDstSzPix ) ); 1280 1281 // scale down bitmap, if requested 1282 if( bDownsampleBitmaps ) 1283 { 1284 aBandBmp = GetDownsampledBitmap( aDstSzPix, 1285 Point(), aBandBmp.GetSizePixel(), 1286 aBandBmp, nMaxBmpDPIX, nMaxBmpDPIY ); 1287 } 1288 1289 rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_BEGIN" ) ); 1290 rOutMtf.AddAction( new MetaBmpScaleAction( aDstPtPix, aDstSzPix, aBandBmp ) ); 1291 rOutMtf.AddAction( new MetaCommentAction( "PRNSPOOL_TRANSPARENTBITMAP_END" ) ); 1292 1293 aPaintVDev.mbMap = sal_True; 1294 mbMap = bOldMap; 1295 aMapVDev.Pop(); 1296 aPaintVDev.Pop(); 1297 } 1298 1299 // overlapping bands to avoid missing lines (e.g. PostScript) 1300 aDstPtPix.X() += aDstSzPix.Width(); 1301 } 1302 1303 // overlapping bands to avoid missing lines (e.g. PostScript) 1304 aDstPtPix.Y() += aDstSzPix.Height(); 1305 } 1306 1307 rOutMtf.AddAction( new MetaPopAction() ); 1308 } 1309 } 1310 } 1311 } 1312 1313 // clean up aMapModeVDev 1314 nCount = aMapModeVDev.GetGCStackDepth(); 1315 while( nCount-- ) 1316 aMapModeVDev.Pop(); 1317 1318 // 1319 // STAGE 4: Copy actions to output metafile 1320 // ======================================== 1321 // 1322 1323 // iterate over all actions and duplicate the ones not in a 1324 // special aCCList member into rOutMtf 1325 for( pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction(), nActionNum=0; 1326 pCurrAct; 1327 pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum ) 1328 { 1329 const ConnectedComponents* pCurrAssociatedComponent = aCCList_MemberMap[nActionNum]; 1330 1331 // NOTE: This relies on the fact that map-mode or draw 1332 // mode changing actions are solitary aCCList elements and 1333 // have empty bounding boxes, see comment on stage 2.1 1334 // above 1335 if( pCurrAssociatedComponent && 1336 (pCurrAssociatedComponent->aBounds.IsEmpty() || 1337 !pCurrAssociatedComponent->bIsSpecial) ) 1338 { 1339 // #107169# Treat transparent bitmaps special, if they 1340 // are the first (or sole) action in their bounds 1341 // list. Note that we previously ensured that no 1342 // fully-transparent objects are before us here. 1343 if( ImplIsActionHandlingTransparency( *pCurrAct ) && 1344 pCurrAssociatedComponent->aComponentList.begin()->first == pCurrAct ) 1345 { 1346 // convert actions, where masked-out parts are of 1347 // given background color 1348 ImplConvertTransparentAction(rOutMtf, 1349 *pCurrAct, 1350 aMapModeVDev, 1351 aBackgroundComponent.aBgColor); 1352 } 1353 else 1354 { 1355 // simply add this action 1356 rOutMtf.AddAction( ( pCurrAct->Duplicate(), pCurrAct ) ); 1357 } 1358 1359 pCurrAct->Execute(&aMapModeVDev); 1360 } 1361 } 1362 1363 rOutMtf.SetPrefMapMode( rInMtf.GetPrefMapMode() ); 1364 rOutMtf.SetPrefSize( rInMtf.GetPrefSize() ); 1365 } 1366 return bTransparent; 1367 } 1368 1369 // ----------------------------------------------------------------------------- 1370 1371 Bitmap OutputDevice::GetDownsampledBitmap( const Size& rDstSz, 1372 const Point& rSrcPt, const Size& rSrcSz, 1373 const Bitmap& rBmp, long nMaxBmpDPIX, long nMaxBmpDPIY ) 1374 { 1375 Bitmap aBmp( rBmp ); 1376 1377 if( !aBmp.IsEmpty() ) 1378 { 1379 Point aPoint; 1380 const Rectangle aBmpRect( aPoint, aBmp.GetSizePixel() ); 1381 Rectangle aSrcRect( rSrcPt, rSrcSz ); 1382 1383 // do cropping if neccessary 1384 if( aSrcRect.Intersection( aBmpRect ) != aBmpRect ) 1385 { 1386 if( !aSrcRect.IsEmpty() ) 1387 aBmp.Crop( aSrcRect ); 1388 else 1389 aBmp.SetEmpty(); 1390 } 1391 1392 if( !aBmp.IsEmpty() ) 1393 { 1394 // do downsampling if neccessary 1395 Size aDstSizeTwip( PixelToLogic( LogicToPixel( rDstSz ), MAP_TWIP ) ); 1396 1397 // #103209# Normalize size (mirroring has to happen outside of this method) 1398 aDstSizeTwip = Size( labs(aDstSizeTwip.Width()), labs(aDstSizeTwip.Height()) ); 1399 1400 const Size aBmpSize( aBmp.GetSizePixel() ); 1401 const double fBmpPixelX = aBmpSize.Width(); 1402 const double fBmpPixelY = aBmpSize.Height(); 1403 const double fMaxPixelX = aDstSizeTwip.Width() * nMaxBmpDPIX / 1440.0; 1404 const double fMaxPixelY = aDstSizeTwip.Height() * nMaxBmpDPIY / 1440.0; 1405 1406 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance) 1407 if( ( ( fBmpPixelX > ( fMaxPixelX + 4 ) ) || 1408 ( fBmpPixelY > ( fMaxPixelY + 4 ) ) ) && 1409 ( fBmpPixelY > 0.0 ) && ( fMaxPixelY > 0.0 ) ) 1410 { 1411 // do scaling 1412 Size aNewBmpSize; 1413 const double fBmpWH = fBmpPixelX / fBmpPixelY; 1414 const double fMaxWH = fMaxPixelX / fMaxPixelY; 1415 1416 if( fBmpWH < fMaxWH ) 1417 { 1418 aNewBmpSize.Width() = FRound( fMaxPixelY * fBmpWH ); 1419 aNewBmpSize.Height() = FRound( fMaxPixelY ); 1420 } 1421 else if( fBmpWH > 0.0 ) 1422 { 1423 aNewBmpSize.Width() = FRound( fMaxPixelX ); 1424 aNewBmpSize.Height() = FRound( fMaxPixelX / fBmpWH); 1425 } 1426 1427 if( aNewBmpSize.Width() && aNewBmpSize.Height() ) 1428 aBmp.Scale( aNewBmpSize ); 1429 else 1430 aBmp.SetEmpty(); 1431 } 1432 } 1433 } 1434 1435 return aBmp; 1436 } 1437 1438 // ----------------------------------------------------------------------------- 1439 1440 BitmapEx OutputDevice::GetDownsampledBitmapEx( const Size& rDstSz, 1441 const Point& rSrcPt, const Size& rSrcSz, 1442 const BitmapEx& rBmpEx, long nMaxBmpDPIX, long nMaxBmpDPIY ) 1443 { 1444 BitmapEx aBmpEx( rBmpEx ); 1445 1446 if( !aBmpEx.IsEmpty() ) 1447 { 1448 Point aPoint; 1449 const Rectangle aBmpRect( aPoint, aBmpEx.GetSizePixel() ); 1450 Rectangle aSrcRect( rSrcPt, rSrcSz ); 1451 1452 // do cropping if neccessary 1453 if( aSrcRect.Intersection( aBmpRect ) != aBmpRect ) 1454 { 1455 if( !aSrcRect.IsEmpty() ) 1456 aBmpEx.Crop( aSrcRect ); 1457 else 1458 aBmpEx.SetEmpty(); 1459 } 1460 1461 if( !aBmpEx.IsEmpty() ) 1462 { 1463 // do downsampling if neccessary 1464 Size aDstSizeTwip( PixelToLogic( LogicToPixel( rDstSz ), MAP_TWIP ) ); 1465 1466 // #103209# Normalize size (mirroring has to happen outside of this method) 1467 aDstSizeTwip = Size( labs(aDstSizeTwip.Width()), labs(aDstSizeTwip.Height()) ); 1468 1469 const Size aBmpSize( aBmpEx.GetSizePixel() ); 1470 const double fBmpPixelX = aBmpSize.Width(); 1471 const double fBmpPixelY = aBmpSize.Height(); 1472 const double fMaxPixelX = aDstSizeTwip.Width() * nMaxBmpDPIX / 1440.0; 1473 const double fMaxPixelY = aDstSizeTwip.Height() * nMaxBmpDPIY / 1440.0; 1474 1475 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance) 1476 if( ( ( fBmpPixelX > ( fMaxPixelX + 4 ) ) || 1477 ( fBmpPixelY > ( fMaxPixelY + 4 ) ) ) && 1478 ( fBmpPixelY > 0.0 ) && ( fMaxPixelY > 0.0 ) ) 1479 { 1480 // do scaling 1481 Size aNewBmpSize; 1482 const double fBmpWH = fBmpPixelX / fBmpPixelY; 1483 const double fMaxWH = fMaxPixelX / fMaxPixelY; 1484 1485 if( fBmpWH < fMaxWH ) 1486 { 1487 aNewBmpSize.Width() = FRound( fMaxPixelY * fBmpWH ); 1488 aNewBmpSize.Height() = FRound( fMaxPixelY ); 1489 } 1490 else if( fBmpWH > 0.0 ) 1491 { 1492 aNewBmpSize.Width() = FRound( fMaxPixelX ); 1493 aNewBmpSize.Height() = FRound( fMaxPixelX / fBmpWH); 1494 } 1495 1496 if( aNewBmpSize.Width() && aNewBmpSize.Height() ) 1497 aBmpEx.Scale( aNewBmpSize ); 1498 else 1499 aBmpEx.SetEmpty(); 1500 } 1501 } 1502 } 1503 1504 return aBmpEx; 1505 } 1506 1507 // ----------------------------------------------------------------------------- 1508 1509 void Printer::DrawGradientEx( OutputDevice* pOut, const Rectangle& rRect, const Gradient& rGradient ) 1510 { 1511 const PrinterOptions& rPrinterOptions = GetPrinterOptions(); 1512 1513 if( rPrinterOptions.IsReduceGradients() ) 1514 { 1515 if( PRINTER_GRADIENT_STRIPES == rPrinterOptions.GetReducedGradientMode() ) 1516 { 1517 if( !rGradient.GetSteps() || ( rGradient.GetSteps() > rPrinterOptions.GetReducedGradientStepCount() ) ) 1518 { 1519 Gradient aNewGradient( rGradient ); 1520 1521 aNewGradient.SetSteps( rPrinterOptions.GetReducedGradientStepCount() ); 1522 pOut->DrawGradient( rRect, aNewGradient ); 1523 } 1524 else 1525 pOut->DrawGradient( rRect, rGradient ); 1526 } 1527 else 1528 { 1529 const Color& rStartColor = rGradient.GetStartColor(); 1530 const Color& rEndColor = rGradient.GetEndColor(); 1531 const long nR = ( ( (long) rStartColor.GetRed() * rGradient.GetStartIntensity() ) / 100L + 1532 ( (long) rEndColor.GetRed() * rGradient.GetEndIntensity() ) / 100L ) >> 1; 1533 const long nG = ( ( (long) rStartColor.GetGreen() * rGradient.GetStartIntensity() ) / 100L + 1534 ( (long) rEndColor.GetGreen() * rGradient.GetEndIntensity() ) / 100L ) >> 1; 1535 const long nB = ( ( (long) rStartColor.GetBlue() * rGradient.GetStartIntensity() ) / 100L + 1536 ( (long) rEndColor.GetBlue() * rGradient.GetEndIntensity() ) / 100L ) >> 1; 1537 const Color aColor( (sal_uInt8) nR, (sal_uInt8) nG, (sal_uInt8) nB ); 1538 1539 pOut->Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); 1540 pOut->SetLineColor( aColor ); 1541 pOut->SetFillColor( aColor ); 1542 pOut->DrawRect( rRect ); 1543 pOut->Pop(); 1544 } 1545 } 1546 else 1547 pOut->DrawGradient( rRect, rGradient ); 1548 } 1549 1550 // ----------------------------------------------------------------------------- 1551 1552 void Printer::DrawGradientEx( OutputDevice* pOut, const PolyPolygon& rPolyPoly, const Gradient& rGradient ) 1553 { 1554 const PrinterOptions& rPrinterOptions = GetPrinterOptions(); 1555 1556 if( rPrinterOptions.IsReduceGradients() ) 1557 { 1558 if( PRINTER_GRADIENT_STRIPES == rPrinterOptions.GetReducedGradientMode() ) 1559 { 1560 if( !rGradient.GetSteps() || ( rGradient.GetSteps() > rPrinterOptions.GetReducedGradientStepCount() ) ) 1561 { 1562 Gradient aNewGradient( rGradient ); 1563 1564 aNewGradient.SetSteps( rPrinterOptions.GetReducedGradientStepCount() ); 1565 pOut->DrawGradient( rPolyPoly, aNewGradient ); 1566 } 1567 else 1568 pOut->DrawGradient( rPolyPoly, rGradient ); 1569 } 1570 else 1571 { 1572 const Color& rStartColor = rGradient.GetStartColor(); 1573 const Color& rEndColor = rGradient.GetEndColor(); 1574 const long nR = ( ( (long) rStartColor.GetRed() * rGradient.GetStartIntensity() ) / 100L + 1575 ( (long) rEndColor.GetRed() * rGradient.GetEndIntensity() ) / 100L ) >> 1; 1576 const long nG = ( ( (long) rStartColor.GetGreen() * rGradient.GetStartIntensity() ) / 100L + 1577 ( (long) rEndColor.GetGreen() * rGradient.GetEndIntensity() ) / 100L ) >> 1; 1578 const long nB = ( ( (long) rStartColor.GetBlue() * rGradient.GetStartIntensity() ) / 100L + 1579 ( (long) rEndColor.GetBlue() * rGradient.GetEndIntensity() ) / 100L ) >> 1; 1580 const Color aColor( (sal_uInt8) nR, (sal_uInt8) nG, (sal_uInt8) nB ); 1581 1582 pOut->Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); 1583 pOut->SetLineColor( aColor ); 1584 pOut->SetFillColor( aColor ); 1585 pOut->DrawPolyPolygon( rPolyPoly ); 1586 pOut->Pop(); 1587 } 1588 } 1589 else 1590 pOut->DrawGradient( rPolyPoly, rGradient ); 1591 } 1592