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