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