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