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