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