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 {
ConnectedComponentsConnectedComponents66 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 */
ImplIsActionSpecial(const MetaAction & rAct)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 */
checkRect(Rectangle & io_rPrevRect,Color & o_rBgColor,const Rectangle & rCurrRect,OutputDevice & rMapModeVDev)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 */
ImplConvertTransparentAction(GDIMetaFile & o_rMtf,const MetaAction & rAct,const OutputDevice & rStateOutDev,Color aBgColor)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
ImplIsNotTransparent(const MetaAction & rAct,const OutputDevice & rOut)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
ImplCalcActionBounds(const MetaAction & rAct,const OutputDevice & rOut)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
ImplIsActionHandlingTransparency(const MetaAction & rAct)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
RemoveTransparenciesFromMetaFile(const GDIMetaFile & rInMtf,GDIMetaFile & rOutMtf,long nMaxBmpDPIX,long nMaxBmpDPIY,bool bReduceTransparency,bool bTransparencyAutoMode,bool bDownsampleBitmaps,const Color & rBackground)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
GetDownsampledBitmap(const Size & rDstSz,const Point & rSrcPt,const Size & rSrcSz,const Bitmap & rBmp,long nMaxBmpDPIX,long nMaxBmpDPIY)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
GetDownsampledBitmapEx(const Size & rDstSz,const Point & rSrcPt,const Size & rSrcSz,const BitmapEx & rBmpEx,long nMaxBmpDPIX,long nMaxBmpDPIY)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
DrawGradientEx(OutputDevice * pOut,const Rectangle & rRect,const Gradient & rGradient)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
DrawGradientEx(OutputDevice * pOut,const PolyPolygon & rPolyPoly,const Gradient & rGradient)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