LibreOffice Module vcl (master)  1
gdimetafiletools.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 <vcl/gdimetafiletools.hxx>
21 #include <vcl/metaact.hxx>
22 #include <vcl/canvastools.hxx>
27 #include <vcl/virdev.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/graphictools.hxx>
30 #include <osl/diagnose.h>
31 #include <tools/stream.hxx>
32 
33 // helpers
34 
35 namespace
36 {
37  bool handleGeometricContent(
38  const basegfx::B2DPolyPolygon& rClip,
39  const basegfx::B2DPolyPolygon& rSource,
40  GDIMetaFile& rTarget,
41  bool bStroke)
42  {
43  if(rSource.count() && rClip.count())
44  {
45  const basegfx::B2DPolyPolygon aResult(
47  rSource,
48  rClip,
49  true, // inside
50  bStroke));
51 
52  if(aResult.count())
53  {
54  if(aResult == rSource)
55  {
56  // not clipped, but inside. Add original
57  return false;
58  }
59  else
60  {
61  // add clipped geometry
62  if(bStroke)
63  {
64  for(auto const& rB2DPolygon : aResult)
65  {
66  rTarget.AddAction(
68  tools::Polygon(rB2DPolygon)));
69  }
70  }
71  else
72  {
73  rTarget.AddAction(
75  tools::PolyPolygon(aResult)));
76  }
77  }
78  }
79  }
80 
81  return true;
82  }
83 
84  bool handleGradientContent(
85  const basegfx::B2DPolyPolygon& rClip,
86  const basegfx::B2DPolyPolygon& rSource,
87  const Gradient& rGradient,
88  GDIMetaFile& rTarget)
89  {
90  if(rSource.count() && rClip.count())
91  {
92  const basegfx::B2DPolyPolygon aResult(
94  rSource,
95  rClip,
96  true, // inside
97  false)); // stroke
98 
99  if(aResult.count())
100  {
101  if(aResult == rSource)
102  {
103  // not clipped, but inside. Add original
104  return false;
105  }
106  else
107  {
108  // add clipped geometry
109  rTarget.AddAction(
111  tools::PolyPolygon(aResult),
112  rGradient));
113  }
114  }
115  }
116 
117  return true;
118  }
119 
120  bool handleBitmapContent(
121  const basegfx::B2DPolyPolygon& rClip,
122  const Point& rPoint,
123  const Size& rSize,
124  const BitmapEx& rBitmapEx,
125  GDIMetaFile& rTarget)
126  {
127  if(!rSize.Width() || !rSize.Height() || rBitmapEx.IsEmpty())
128  {
129  // bitmap or size is empty
130  return true;
131  }
132 
133  const basegfx::B2DRange aLogicBitmapRange(
134  rPoint.X(), rPoint.Y(),
135  rPoint.X() + rSize.Width(), rPoint.Y() + rSize.Height());
136  const basegfx::B2DPolyPolygon aClipOfBitmap(
138  rClip,
139  aLogicBitmapRange,
140  true,
141  false)); // stroke
142 
143  if(!aClipOfBitmap.count())
144  {
145  // outside clip region
146  return true;
147  }
148 
149  // inside or overlapping. Use area to find out if it is completely
150  // covering (inside) or overlapping
151  const double fClipArea(basegfx::utils::getArea(aClipOfBitmap));
152  const double fBitmapArea(
153  aLogicBitmapRange.getWidth() * aLogicBitmapRange.getWidth() +
154  aLogicBitmapRange.getHeight() * aLogicBitmapRange.getHeight());
155  const double fFactor(fClipArea / fBitmapArea);
156 
157  if(basegfx::fTools::more(fFactor, 1.0 - 0.001))
158  {
159  // completely covering (with 0.1% tolerance)
160  return false;
161  }
162 
163  // needs clipping (with 0.1% tolerance). Prepare VirtualDevice
164  // in pixel mode for alpha channel painting (black is transparent,
165  // white to paint 100% opacity)
166  const Size aSizePixel(rBitmapEx.GetSizePixel());
168 
169  aVDev->SetOutputSizePixel(aSizePixel);
170  aVDev->EnableMapMode(false);
171  aVDev->SetFillColor( COL_WHITE);
172  aVDev->SetLineColor();
173 
174  if(rBitmapEx.IsTransparent())
175  {
176  // use given alpha channel
177  aVDev->DrawBitmap(Point(0, 0), rBitmapEx.GetAlpha().GetBitmap());
178  }
179  else
180  {
181  // reset alpha channel
182  aVDev->SetBackground(Wallpaper(COL_BLACK));
183  aVDev->Erase();
184  }
185 
186  // transform polygon from clipping to pixel coordinates
187  basegfx::B2DPolyPolygon aPixelPoly(aClipOfBitmap);
188  basegfx::B2DHomMatrix aTransform;
189 
190  aTransform.translate(-aLogicBitmapRange.getMinX(), -aLogicBitmapRange.getMinY());
191  aTransform.scale(
192  static_cast< double >(aSizePixel.Width()) / aLogicBitmapRange.getWidth(),
193  static_cast< double >(aSizePixel.Height()) / aLogicBitmapRange.getHeight());
194  aPixelPoly.transform(aTransform);
195 
196  // to fill the non-covered parts, use the Xor fill rule of
197  // tools::PolyPolygon painting. Start with an all-covering polygon and
198  // add the clip polygon one
199  basegfx::B2DPolyPolygon aInvertPixelPoly;
200 
201  aInvertPixelPoly.append(
204  0.0, 0.0,
205  aSizePixel.Width(), aSizePixel.Height())));
206  aInvertPixelPoly.append(aPixelPoly);
207 
208  // paint as alpha
209  aVDev->DrawPolyPolygon(aInvertPixelPoly);
210 
211  // get created alpha mask and set defaults
212  AlphaMask aAlpha(
213  aVDev->GetBitmap(
214  Point(0, 0),
215  aSizePixel));
216 
217  aAlpha.SetPrefSize(rBitmapEx.GetPrefSize());
218  aAlpha.SetPrefMapMode(rBitmapEx.GetPrefMapMode());
219 
220  // add new action replacing the old one
221  rTarget.AddAction(
223  Point(
224  basegfx::fround(aLogicBitmapRange.getMinX()),
225  basegfx::fround(aLogicBitmapRange.getMinY())),
226  Size(
227  basegfx::fround(aLogicBitmapRange.getWidth()),
228  basegfx::fround(aLogicBitmapRange.getHeight())),
229  BitmapEx(rBitmapEx.GetBitmap(), aAlpha)));
230 
231  return true;
232  }
233 
234  void addSvtGraphicStroke(const SvtGraphicStroke& rStroke, GDIMetaFile& rTarget)
235  {
236  // write SvtGraphicFill
237  SvMemoryStream aMemStm;
238  WriteSvtGraphicStroke( aMemStm, rStroke );
239  rTarget.AddAction(
240  new MetaCommentAction(
241  "XPATHSTROKE_SEQ_BEGIN",
242  0,
243  static_cast< const sal_uInt8* >(aMemStm.GetData()),
244  aMemStm.TellEnd()));
245  }
246 
247  void addSvtGraphicFill(const SvtGraphicFill &rFilling, GDIMetaFile& rTarget)
248  {
249  // write SvtGraphicFill
250  SvMemoryStream aMemStm;
251  WriteSvtGraphicFill( aMemStm, rFilling );
252  rTarget.AddAction(
253  new MetaCommentAction(
254  "XPATHFILL_SEQ_BEGIN",
255  0,
256  static_cast< const sal_uInt8* >(aMemStm.GetData()),
257  aMemStm.TellEnd()));
258  }
259 } // end of anonymous namespace
260 
261 // #i121267# Tooling to internally clip geometry against internal clip regions
262 
264 {
265  const sal_uLong nObjCount(rSource.GetActionSize());
266 
267  if(!nObjCount)
268  {
269  return;
270  }
271 
272  // prepare target data container and push/pop stack data
273  GDIMetaFile aTarget;
274  bool bChanged(false);
275  std::vector< basegfx::B2DPolyPolygon > aClips;
276  std::vector< PushFlags > aPushFlags;
277  std::vector< MapMode > aMapModes;
278 
279  // start with empty region
280  aClips.emplace_back();
281 
282  // start with default MapMode (MapUnit::MapPixel)
283  aMapModes.emplace_back();
284 
285  for(sal_uLong i(0); i < nObjCount; ++i)
286  {
287  const MetaAction* pAction(rSource.GetAction(i));
288  const MetaActionType nType(pAction->GetType());
289  bool bDone(false);
290 
291  // basic operation takes care of clipregion actions (four) and push/pop of these
292  // to steer the currently set clip region. There *is* an active
293  // clip region when (aClips.size() && aClips.back().count()), see
294  // below
295  switch(nType)
296  {
298  {
299  const MetaClipRegionAction* pA = static_cast< const MetaClipRegionAction* >(pAction);
300 
301  if(pA->IsClipping())
302  {
303  const vcl::Region& rRegion = pA->GetRegion();
304  const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon());
305 
306  aClips.back() = aNewClip;
307  }
308  else
309  {
310  aClips.back() = basegfx::B2DPolyPolygon();
311  }
312 
313  break;
314  }
315 
317  {
318  const MetaISectRectClipRegionAction* pA = static_cast< const MetaISectRectClipRegionAction* >(pAction);
319  const tools::Rectangle& rRect = pA->GetRect();
320 
321  if(!rRect.IsEmpty() && !aClips.empty() && aClips.back().count())
322  {
324 
326  aClips.back(),
327  aClipRange,
328  true, // inside
329  false); // stroke
330  }
331  break;
332  }
333 
335  {
336  const MetaISectRegionClipRegionAction* pA = static_cast< const MetaISectRegionClipRegionAction* >(pAction);
337  const vcl::Region& rRegion = pA->GetRegion();
338 
339  if(!rRegion.IsEmpty() && !aClips.empty() && aClips.back().count())
340  {
341  const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon());
342 
344  aClips.back(),
345  aNewClip,
346  true, // inside
347  false); // stroke
348  }
349  break;
350  }
351 
353  {
354  const MetaMoveClipRegionAction* pA = static_cast< const MetaMoveClipRegionAction* >(pAction);
355  const long aHorMove(pA->GetHorzMove());
356  const long aVerMove(pA->GetVertMove());
357 
358  if((aHorMove || aVerMove) && !aClips.empty() && aClips.back().count())
359  {
360  aClips.back().transform(
362  aHorMove,
363  aVerMove));
364  }
365  break;
366  }
367 
368  case MetaActionType::PUSH :
369  {
370  const MetaPushAction* pA = static_cast< const MetaPushAction* >(pAction);
371  const PushFlags nFlags(pA->GetFlags());
372 
373  aPushFlags.push_back(nFlags);
374 
375  if(nFlags & PushFlags::CLIPREGION)
376  {
377  aClips.push_back(aClips.back());
378  }
379 
380  if(nFlags & PushFlags::MAPMODE)
381  {
382  aMapModes.push_back(aMapModes.back());
383  }
384  break;
385  }
386 
387  case MetaActionType::POP :
388  {
389 
390  if(!aPushFlags.empty())
391  {
392  const PushFlags nFlags(aPushFlags.back());
393  aPushFlags.pop_back();
394 
395  if(nFlags & PushFlags::CLIPREGION)
396  {
397  if(aClips.size() > 1)
398  {
399  aClips.pop_back();
400  }
401  else
402  {
403  OSL_ENSURE(false, "Wrong POP() in ClipRegions (!)");
404  }
405  }
406 
407  if(nFlags & PushFlags::MAPMODE)
408  {
409  if(aMapModes.size() > 1)
410  {
411  aMapModes.pop_back();
412  }
413  else
414  {
415  OSL_ENSURE(false, "Wrong POP() in MapModes (!)");
416  }
417  }
418  }
419  else
420  {
421  OSL_ENSURE(false, "Invalid pop() without push() (!)");
422  }
423 
424  break;
425  }
426 
428  {
429  const MetaMapModeAction* pA = static_cast< const MetaMapModeAction* >(pAction);
430 
431  aMapModes.back() = pA->GetMapMode();
432  break;
433  }
434 
435  default:
436  {
437  break;
438  }
439  }
440 
441  // this area contains all actions which could potentially be clipped. Since
442  // this tooling is only a fallback (see comments in header), only the needed
443  // actions will be implemented. Extend using the pattern for the already
444  // implemented actions.
445  if(!aClips.empty() && aClips.back().count())
446  {
447  switch(nType)
448  {
449 
450  // pixel actions, just check on inside
451 
452  case MetaActionType::PIXEL :
453  {
454  const MetaPixelAction* pA = static_cast< const MetaPixelAction* >(pAction);
455  const Point& rPoint = pA->GetPoint();
456 
458  aClips.back(),
459  basegfx::B2DPoint(rPoint.X(), rPoint.Y())))
460  {
461  // when not inside, do not add original
462  bDone = true;
463  }
464  break;
465  }
466 
467  case MetaActionType::POINT :
468  {
469  const MetaPointAction* pA = static_cast< const MetaPointAction* >(pAction);
470  const Point& rPoint = pA->GetPoint();
471 
473  aClips.back(),
474  basegfx::B2DPoint(rPoint.X(), rPoint.Y())))
475  {
476  // when not inside, do not add original
477  bDone = true;
478  }
479  break;
480  }
481 
482  // geometry actions
483 
484  case MetaActionType::LINE :
485  {
486  const MetaLineAction* pA = static_cast< const MetaLineAction* >(pAction);
487  const Point& rStart(pA->GetStartPoint());
488  const Point& rEnd(pA->GetEndPoint());
489  basegfx::B2DPolygon aLine;
490 
491  aLine.append(basegfx::B2DPoint(rStart.X(), rStart.Y()));
492  aLine.append(basegfx::B2DPoint(rEnd.X(), rEnd.Y()));
493 
494  bDone = handleGeometricContent(
495  aClips.back(),
497  aTarget,
498  true); // stroke
499  break;
500  }
501 
502  case MetaActionType::RECT :
503  {
504  const MetaRectAction* pA = static_cast< const MetaRectAction* >(pAction);
505  const tools::Rectangle& rRect = pA->GetRect();
506 
507  if(rRect.IsEmpty())
508  {
509  bDone = true;
510  }
511  else
512  {
513 
514  bDone = handleGeometricContent(
515  aClips.back(),
519  aTarget,
520  false); // stroke
521  }
522  break;
523  }
524 
526  {
527  const MetaRoundRectAction* pA = static_cast< const MetaRoundRectAction* >(pAction);
528  const tools::Rectangle& rRect = pA->GetRect();
529 
530  if(rRect.IsEmpty())
531  {
532  bDone = true;
533  }
534  else
535  {
536  const sal_uInt32 nHor(pA->GetHorzRound());
537  const sal_uInt32 nVer(pA->GetVertRound());
539  basegfx::B2DPolygon aOutline;
540 
541  if(nHor || nVer)
542  {
543  double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0));
544  double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0));
545  fRadiusX = std::max(0.0, std::min(1.0, fRadiusX));
546  fRadiusY = std::max(0.0, std::min(1.0, fRadiusY));
547 
548  aOutline = basegfx::utils::createPolygonFromRect(aRange, fRadiusX, fRadiusY);
549  }
550  else
551  {
552  aOutline = basegfx::utils::createPolygonFromRect(aRange);
553  }
554 
555  bDone = handleGeometricContent(
556  aClips.back(),
557  basegfx::B2DPolyPolygon(aOutline),
558  aTarget,
559  false); // stroke
560  }
561  break;
562  }
563 
565  {
566  const MetaEllipseAction* pA = static_cast< const MetaEllipseAction* >(pAction);
567  const tools::Rectangle& rRect = pA->GetRect();
568 
569  if(rRect.IsEmpty())
570  {
571  bDone = true;
572  }
573  else
574  {
576 
577  bDone = handleGeometricContent(
578  aClips.back(),
581  aRange.getCenter(),
582  aRange.getWidth() * 0.5,
583  aRange.getHeight() * 0.5)),
584  aTarget,
585  false); // stroke
586  }
587  break;
588  }
589 
590  case MetaActionType::ARC :
591  {
592  const MetaArcAction* pA = static_cast< const MetaArcAction* >(pAction);
593  const tools::Rectangle& rRect = pA->GetRect();
594 
595  if(rRect.IsEmpty())
596  {
597  bDone = true;
598  }
599  else
600  {
601  const tools::Polygon aToolsPoly(
602  rRect,
603  pA->GetStartPoint(),
604  pA->GetEndPoint(),
605  PolyStyle::Arc);
606 
607  bDone = handleGeometricContent(
608  aClips.back(),
609  basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
610  aTarget,
611  true); // stroke
612  }
613  break;
614  }
615 
616  case MetaActionType::PIE :
617  {
618  const MetaPieAction* pA = static_cast< const MetaPieAction* >(pAction);
619  const tools::Rectangle& rRect = pA->GetRect();
620 
621  if(rRect.IsEmpty())
622  {
623  bDone = true;
624  }
625  else
626  {
627  const tools::Polygon aToolsPoly(
628  rRect,
629  pA->GetStartPoint(),
630  pA->GetEndPoint(),
631  PolyStyle::Pie);
632 
633  bDone = handleGeometricContent(
634  aClips.back(),
635  basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
636  aTarget,
637  false); // stroke
638  }
639  break;
640  }
641 
642  case MetaActionType::CHORD :
643  {
644  const MetaChordAction* pA = static_cast< const MetaChordAction* >(pAction);
645  const tools::Rectangle& rRect = pA->GetRect();
646 
647  if(rRect.IsEmpty())
648  {
649  bDone = true;
650  }
651  else
652  {
653  const tools::Polygon aToolsPoly(
654  rRect,
655  pA->GetStartPoint(),
656  pA->GetEndPoint(),
657  PolyStyle::Chord);
658 
659  bDone = handleGeometricContent(
660  aClips.back(),
661  basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
662  aTarget,
663  false); // stroke
664  }
665  break;
666  }
667 
669  {
670  const MetaPolyLineAction* pA = static_cast< const MetaPolyLineAction* >(pAction);
671 
672  bDone = handleGeometricContent(
673  aClips.back(),
675  aTarget,
676  true); // stroke
677  break;
678  }
679 
681  {
682  const MetaPolygonAction* pA = static_cast< const MetaPolygonAction* >(pAction);
683 
684  bDone = handleGeometricContent(
685  aClips.back(),
687  aTarget,
688  false); // stroke
689  break;
690  }
691 
693  {
694  const MetaPolyPolygonAction* pA = static_cast< const MetaPolyPolygonAction* >(pAction);
695  const tools::PolyPolygon& rPoly = pA->GetPolyPolygon();
696 
697  bDone = handleGeometricContent(
698  aClips.back(),
699  rPoly.getB2DPolyPolygon(),
700  aTarget,
701  false); // stroke
702  break;
703  }
704 
705  // bitmap actions, create BitmapEx with alpha channel derived
706  // from clipping
707 
708  case MetaActionType::BMPEX :
709  {
710  const MetaBmpExAction* pA = static_cast< const MetaBmpExAction* >(pAction);
711  const BitmapEx& rBitmapEx = pA->GetBitmapEx();
712 
713  // the logical size depends on the PrefSize of the given bitmap in
714  // combination with the current MapMode
715  Size aLogicalSize(rBitmapEx.GetPrefSize());
716 
717  if(MapUnit::MapPixel == rBitmapEx.GetPrefMapMode().GetMapUnit())
718  {
719  aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back());
720  }
721  else
722  {
723  aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmapEx.GetPrefMapMode(), aMapModes.back());
724  }
725 
726  bDone = handleBitmapContent(
727  aClips.back(),
728  pA->GetPoint(),
729  aLogicalSize,
730  rBitmapEx,
731  aTarget);
732  break;
733  }
734 
735  case MetaActionType::BMP :
736  {
737  const MetaBmpAction* pA = static_cast< const MetaBmpAction* >(pAction);
738  const Bitmap& rBitmap = pA->GetBitmap();
739 
740  // the logical size depends on the PrefSize of the given bitmap in
741  // combination with the current MapMode
742  Size aLogicalSize(rBitmap.GetPrefSize());
743 
744  if(MapUnit::MapPixel == rBitmap.GetPrefMapMode().GetMapUnit())
745  {
746  aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back());
747  }
748  else
749  {
750  aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmap.GetPrefMapMode(), aMapModes.back());
751  }
752 
753  bDone = handleBitmapContent(
754  aClips.back(),
755  pA->GetPoint(),
756  aLogicalSize,
757  BitmapEx(rBitmap),
758  aTarget);
759  break;
760  }
761 
763  {
764  const MetaBmpExScaleAction* pA = static_cast< const MetaBmpExScaleAction* >(pAction);
765 
766  bDone = handleBitmapContent(
767  aClips.back(),
768  pA->GetPoint(),
769  pA->GetSize(),
770  pA->GetBitmapEx(),
771  aTarget);
772  break;
773  }
774 
776  {
777  const MetaBmpScaleAction* pA = static_cast< const MetaBmpScaleAction* >(pAction);
778 
779  bDone = handleBitmapContent(
780  aClips.back(),
781  pA->GetPoint(),
782  pA->GetSize(),
783  BitmapEx(pA->GetBitmap()),
784  aTarget);
785  break;
786  }
787 
789  {
790  const MetaBmpExScalePartAction* pA = static_cast< const MetaBmpExScalePartAction* >(pAction);
791  const BitmapEx& rBitmapEx = pA->GetBitmapEx();
792 
793  if(rBitmapEx.IsEmpty())
794  {
795  // empty content
796  bDone = true;
797  }
798  else
799  {
800  BitmapEx aCroppedBitmapEx(rBitmapEx);
801  const tools::Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
802 
803  if(aCropRectangle.IsEmpty())
804  {
805  // empty content
806  bDone = true;
807  }
808  else
809  {
810  aCroppedBitmapEx.Crop(aCropRectangle);
811  bDone = handleBitmapContent(
812  aClips.back(),
813  pA->GetDestPoint(),
814  pA->GetDestSize(),
815  aCroppedBitmapEx,
816  aTarget);
817  }
818  }
819  break;
820  }
821 
823  {
824  const MetaBmpScalePartAction* pA = static_cast< const MetaBmpScalePartAction* >(pAction);
825  const Bitmap& rBitmap = pA->GetBitmap();
826 
827  if(rBitmap.IsEmpty())
828  {
829  // empty content
830  bDone = true;
831  }
832  else
833  {
834  Bitmap aCroppedBitmap(rBitmap);
835  const tools::Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
836 
837  if(aCropRectangle.IsEmpty())
838  {
839  // empty content
840  bDone = true;
841  }
842  else
843  {
844  aCroppedBitmap.Crop(aCropRectangle);
845  bDone = handleBitmapContent(
846  aClips.back(),
847  pA->GetDestPoint(),
848  pA->GetDestSize(),
849  BitmapEx(aCroppedBitmap),
850  aTarget);
851  }
852  }
853  break;
854  }
855 
856  // need to handle all those 'hacks' which hide data in comments
857 
859  {
860  const MetaCommentAction* pA = static_cast< const MetaCommentAction* >(pAction);
861  const OString& rComment = pA->GetComment();
862 
863  if(rComment.equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
864  {
865  // nothing to do; this just means that between here and XGRAD_SEQ_END
866  // exists a MetaActionType::GRADIENTEX mixed with Xor-tricked painting
867  // commands. This comment is used to scan over these and filter for
868  // the gradient action. It is needed to support MetaActionType::GRADIENTEX
869  // in this processor to solve usages.
870  }
871  else if(rComment.equalsIgnoreAsciiCase("XPATHFILL_SEQ_BEGIN"))
872  {
873  SvtGraphicFill aFilling;
874  tools::PolyPolygon aPath;
875 
876  { // read SvtGraphicFill
877  SvMemoryStream aMemStm(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(),StreamMode::READ);
878  ReadSvtGraphicFill( aMemStm, aFilling );
879  }
880 
881  aFilling.getPath(aPath);
882 
883  if(aPath.Count())
884  {
885  const basegfx::B2DPolyPolygon aSource(aPath.getB2DPolyPolygon());
886  const basegfx::B2DPolyPolygon aResult(
888  aSource,
889  aClips.back(),
890  true, // inside
891  false)); // stroke
892 
893  if(aResult.count())
894  {
895  if(aResult != aSource)
896  {
897  // add clipped geometry
898  aFilling.setPath(tools::PolyPolygon(aResult));
899  addSvtGraphicFill(aFilling, aTarget);
900  bDone = true;
901  }
902  }
903  else
904  {
905  // exchange with empty polygon
906  aFilling.setPath(tools::PolyPolygon());
907  addSvtGraphicFill(aFilling, aTarget);
908  bDone = true;
909  }
910  }
911  }
912  else if(rComment.equalsIgnoreAsciiCase("XPATHSTROKE_SEQ_BEGIN"))
913  {
914  SvtGraphicStroke aStroke;
915  tools::Polygon aPath;
916 
917  { // read SvtGraphicFill
918  SvMemoryStream aMemStm(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(),StreamMode::READ);
919  ReadSvtGraphicStroke( aMemStm, aStroke );
920  }
921 
922  aStroke.getPath(aPath);
923 
924  if(aPath.GetSize())
925  {
926  const basegfx::B2DPolygon aSource(aPath.getB2DPolygon());
927  const basegfx::B2DPolyPolygon aResult(
929  aSource,
930  aClips.back(),
931  true, // inside
932  true)); // stroke
933 
934  if(aResult.count())
935  {
936  if(aResult.count() > 1 || aResult.getB2DPolygon(0) != aSource)
937  {
938  // add clipped geometry
939  for(auto const& rB2DPolygon : aResult)
940  {
941  aStroke.setPath(tools::Polygon(rB2DPolygon));
942  addSvtGraphicStroke(aStroke, aTarget);
943  }
944 
945  bDone = true;
946  }
947  }
948  else
949  {
950  // exchange with empty polygon
951  aStroke.setPath(tools::Polygon());
952  addSvtGraphicStroke(aStroke, aTarget);
953  bDone = true;
954  }
955 
956  }
957  }
958  break;
959  }
960 
961  // need to handle gradient fills (hopefully only unrotated ones)
962 
964  {
965  const MetaGradientAction* pA = static_cast< const MetaGradientAction* >(pAction);
966  const tools::Rectangle& rRect = pA->GetRect();
967 
968  if(rRect.IsEmpty())
969  {
970  bDone = true;
971  }
972  else
973  {
974  bDone = handleGradientContent(
975  aClips.back(),
979  pA->GetGradient(),
980  aTarget);
981  }
982 
983  break;
984  }
985 
987  {
988  const MetaGradientExAction* pA = static_cast< const MetaGradientExAction* >(pAction);
989  const tools::PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
990 
991  bDone = handleGradientContent(
992  aClips.back(),
993  rPolyPoly.getB2DPolyPolygon(),
994  pA->GetGradient(),
995  aTarget);
996  break;
997  }
998 
999  // not (yet) supported actions
1000 
1001  // MetaActionType::NONE
1002  // MetaActionType::TEXT
1003  // MetaActionType::TEXTARRAY
1004  // MetaActionType::STRETCHTEXT
1005  // MetaActionType::TEXTRECT
1006  // MetaActionType::MASK
1007  // MetaActionType::MASKSCALE
1008  // MetaActionType::MASKSCALEPART
1009  // MetaActionType::HATCH
1010  // MetaActionType::WALLPAPER
1011  // MetaActionType::FILLCOLOR
1012  // MetaActionType::TEXTCOLOR
1013  // MetaActionType::TEXTFILLCOLOR
1014  // MetaActionType::TEXTALIGN
1015  // MetaActionType::MAPMODE
1016  // MetaActionType::FONT
1017  // MetaActionType::Transparent
1018  // MetaActionType::EPS
1019  // MetaActionType::REFPOINT
1020  // MetaActionType::TEXTLINECOLOR
1021  // MetaActionType::TEXTLINE
1022  // MetaActionType::FLOATTRANSPARENT
1023  // MetaActionType::LAYOUTMODE
1024  // MetaActionType::TEXTLANGUAGE
1025  // MetaActionType::OVERLINECOLOR
1026 
1027  // if an action is not handled at all, it will simply get copied to the
1028  // target (see below). This is the default for all non-implemented actions
1029  default:
1030  {
1031  break;
1032  }
1033  }
1034  }
1035 
1036  if(bDone)
1037  {
1038  bChanged = true;
1039  }
1040  else
1041  {
1042  aTarget.AddAction(const_cast< MetaAction* >(pAction));
1043  }
1044  }
1045 
1046  if(bChanged)
1047  {
1048  // when changed, copy back and do not forget to set MapMode
1049  // and PrefSize
1050  aTarget.SetPrefMapMode(rSource.GetPrefMapMode());
1051  aTarget.SetPrefSize(rSource.GetPrefSize());
1052  rSource = aTarget;
1053  }
1054 }
1055 
1056 bool usesClipActions(const GDIMetaFile& rSource)
1057 {
1058  const sal_uLong nObjCount(rSource.GetActionSize());
1059 
1060  for(sal_uLong i(0); i < nObjCount; ++i)
1061  {
1062  const MetaAction* pAction(rSource.GetAction(i));
1063  const MetaActionType nType(pAction->GetType());
1064 
1065  switch(nType)
1066  {
1071  {
1072  return true;
1073  }
1074 
1075  default: break;
1076  }
1077  }
1078 
1079  return false;
1080 }
1081 
1083 {
1084 }
1085 
1086 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt16 Count() const
long Width() const
B2DPoint getCenter() const
bool IsClipping() const
Definition: metaact.hxx:1103
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
const Point & GetPoint() const
Definition: metaact.hxx:701
const Point & GetEndPoint() const
Definition: metaact.hxx:337
double getHeight() const
const Point & GetSrcPoint() const
Definition: metaact.hxx:739
sal_uInt32 GetVertRound() const
Definition: metaact.hxx:243
const tools::PolyPolygon & GetPolyPolygon() const
Definition: metaact.hxx:1011
double getArea(const B2DPolygon &rCandidate)
const MapMode & GetPrefMapMode() const
Definition: bitmapex.hxx:89
Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
Definition: map.cxx:1678
static bool more(const double &rfValA, const double &rfValB)
long Height() const
SvStream & WriteSvtGraphicStroke(SvStream &rOStm, const SvtGraphicStroke &rClass)
const tools::Rectangle & GetRect() const
Definition: metaact.hxx:241
const MapMode & GetPrefMapMode() const
Definition: gdimtf.hxx:176
Bitmap const & GetBitmap() const
Definition: alpha.cxx:70
sal_uIntPtr sal_uLong
Encapsulates geometry and associated attributes of a graphical 'pen stroke'.
bool usesClipActions(const GDIMetaFile &rSource)
void SetPrefSize(const Size &rSize)
Definition: gdimtf.hxx:174
const Size & GetSize() const
Definition: metaact.hxx:702
const Point & GetEndPoint() const
Definition: metaact.hxx:181
SvStream & ReadSvtGraphicStroke(SvStream &rIStm, SvtGraphicStroke &rClass)
const Size & GetSrcSize() const
Definition: metaact.hxx:740
SvStream & ReadSvtGraphicFill(SvStream &rIStm, SvtGraphicFill &rClass)
B2DPolyPolygon clipPolyPolygonOnPolyPolygon(const B2DPolyPolygon &rCandidate, const B2DPolyPolygon &rClip, bool bInside, bool bStroke)
virtual sal_uInt64 TellEnd() override
const sal_uInt8 * GetData() const
Definition: metaact.hxx:1656
const Size & GetPrefSize() const
Definition: bitmap.hxx:566
static OutputDevice * GetDefaultDevice()
Get the default "device" (in this case the default window).
Definition: svapp.cxx:1055
bool IsEmpty() const
bool Crop(const tools::Rectangle &rRectPixel)
Crop the bitmap.
double getWidth() const
const Point & GetPoint() const
Definition: metaact.hxx:802
FUNC_TYPE const nType
const Bitmap & GetBitmap() const
Definition: metaact.hxx:668
const Size & GetPrefSize() const
Definition: bitmapex.hxx:86
const vcl::Region & GetRegion() const
Definition: metaact.hxx:1102
const Gradient & GetGradient() const
Definition: metaact.hxx:982
const Point & GetStartPoint() const
Definition: metaact.hxx:303
PushFlags
Definition: outdevstate.hxx:38
MetaActionType
void clipMetafileContentAgainstOwnRegions(GDIMetaFile &rSource)
const tools::Rectangle & GetRect() const
Definition: metaact.hxx:1131
const tools::PolyPolygon & GetPolyPolygon() const
Definition: metaact.hxx:457
const Size & GetSize() const
Definition: metaact.hxx:803
const tools::Rectangle & GetRect() const
Definition: metaact.hxx:271
B2IRange fround(const B2DRange &rRange)
const tools::Polygon & GetPolygon() const
Definition: metaact.hxx:429
sal_uInt32 GetHorzRound() const
Definition: metaact.hxx:242
void setPath(const tools::PolyPolygon &rPath)
Set path to fill.
const vcl::Region & GetRegion() const
Definition: metaact.hxx:1159
Bitmap GetBitmap(const Color *pTransReplaceColor=nullptr) const
Definition: bitmapex.cxx:236
bool IsEmpty() const
Definition: bitmapex.cxx:203
bool IsEmpty() const
Definition: region.cxx:228
const MapMode & GetPrefMapMode() const
Definition: bitmap.hxx:556
const tools::Rectangle & GetRect() const
Definition: metaact.hxx:210
const Size & GetPrefSize() const
Definition: gdimtf.hxx:173
const tools::Polygon & GetPolygon() const
Definition: metaact.hxx:400
const BitmapEx & GetBitmapEx() const
Definition: metaact.hxx:837
const Point & GetDestPoint() const
Definition: metaact.hxx:838
const MapMode & GetMapMode() const
Definition: metaact.hxx:1400
const BitmapEx & GetBitmapEx() const
Definition: metaact.hxx:769
const tools::Rectangle & GetRect() const
Definition: metaact.hxx:981
int i
const Bitmap & GetBitmap() const
Definition: metaact.hxx:736
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
void scale(double fX, double fY)
const Point & GetEndPoint() const
Definition: metaact.hxx:304
void getPath(tools::Polygon &) const
Query path to stroke.
const Point & GetStartPoint() const
Definition: metaact.hxx:369
sal_uInt16 GetSize() const
const Point & GetSrcPoint() const
Definition: metaact.hxx:840
const Point & GetPoint() const
Definition: metaact.hxx:770
MapUnit GetMapUnit() const
Definition: mapmod.cxx:163
::basegfx::B2DPolygon getB2DPolygon() const
bool IsTransparent() const
Definition: bitmapex.cxx:221
long X() const
const Bitmap & GetBitmap() const
Definition: metaact.hxx:700
const Point & GetDestPoint() const
Definition: metaact.hxx:737
const tools::Rectangle & GetRect() const
Definition: metaact.hxx:302
const Size & GetDestSize() const
Definition: metaact.hxx:738
basegfx::B2DRange b2DRectangleFromRectangle(const ::tools::Rectangle &rRect)
PushFlags GetFlags() const
Definition: metaact.hxx:1452
const tools::Rectangle & GetRect() const
Definition: metaact.hxx:368
Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1185
SvStream & WriteSvtGraphicFill(SvStream &rOStm, const SvtGraphicFill &rClass)
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
const Point & GetStartPoint() const
Definition: metaact.hxx:180
sal_uInt32 GetDataSize() const
Definition: metaact.hxx:1655
const BitmapEx & GetBitmapEx() const
Definition: metaact.hxx:801
const Point & GetStartPoint() const
Definition: metaact.hxx:336
sal_uInt32 count() const
AlphaMask GetAlpha() const
Definition: bitmapex.cxx:268
const Point & GetPoint() const
Definition: metaact.hxx:120
::basegfx::B2DPolyPolygon getB2DPolyPolygon() const
const Size & GetSrcSize() const
Definition: metaact.hxx:841
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:539
MetaAction * GetAction(size_t nAction) const
Definition: gdimtf.cxx:157
B2DPolyPolygon clipPolygonOnPolyPolygon(const B2DPolygon &rCandidate, const B2DPolyPolygon &rClip, bool bInside, bool bStroke)
const Gradient & GetGradient() const
Definition: metaact.hxx:1012
const Point & GetPoint() const
Definition: metaact.hxx:148
const Point & GetPoint() const
Definition: metaact.hxx:669
bool isInside(const B2DPolygon &rCandidate, const B2DPoint &rPoint, bool bWithBorder)
void SetPrefSize(const Size &rSize)
Definition: alpha.hxx:52
bool IsEmpty() const
Definition: bitmap.hxx:551
size_t GetActionSize() const
Definition: gdimtf.cxx:152
void translate(double fX, double fY)
void setPath(const tools::Polygon &)
Set path to stroke.
long GetHorzMove() const
Definition: metaact.hxx:1187
const Point & GetEndPoint() const
Definition: metaact.hxx:370
basegfx::B2DPolyPolygon GetAsB2DPolyPolygon() const
Definition: region.cxx:1289
const tools::Rectangle & GetRect() const
Definition: metaact.hxx:335
long GetVertMove() const
Definition: metaact.hxx:1188
const OString & GetComment() const
Definition: metaact.hxx:1653
Encapsulates geometry and associated attributes of a filled area.
B2DPolyPolygon clipPolyPolygonOnRange(const B2DPolyPolygon &rCandidate, const B2DRange &rRange, bool bInside, bool bStroke)
void getPath(tools::PolyPolygon &) const
Query path to fill.
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
const Size & GetDestSize() const
Definition: metaact.hxx:839
const Size & GetSizePixel() const
Definition: bitmapex.hxx:83
bool Crop(const tools::Rectangle &rRectPixel)
Crop the bitmap.
Definition: bitmapex.cxx:434
B2DPolygon createPolygonFromEllipse(const B2DPoint &rCenter, double fRadiusX, double fRadiusY, sal_uInt32 nStartQuadrant=0)
long Y() const
const void * GetData()
void SetPrefMapMode(const MapMode &rMapMode)
Definition: gdimtf.hxx:177