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