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