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