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