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