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