Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / source / gdi / gdimetafiletools.cxx
blob6a74c4d69871dfc2bc0415de58baf906731376fb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <vcl/gdimetafiletools.hxx>
21 #include <vcl/metaact.hxx>
22 #include <vcl/canvastools.hxx>
23 #include <basegfx/polygon/b2dpolygonclipper.hxx>
24 #include <basegfx/matrix/b2dhommatrixtools.hxx>
25 #include <basegfx/polygon/b2dpolypolygontools.hxx>
26 #include <basegfx/polygon/b2dpolygontools.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>
33 // helpers
35 namespace
37 bool handleGeometricContent(
38 const basegfx::B2DPolyPolygon& rClip,
39 const basegfx::B2DPolyPolygon& rSource,
40 GDIMetaFile& rTarget,
41 bool bStroke)
43 if(rSource.count() && rClip.count())
45 const basegfx::B2DPolyPolygon aResult(
46 basegfx::utils::clipPolyPolygonOnPolyPolygon(
47 rSource,
48 rClip,
49 true, // inside
50 bStroke));
52 if(aResult.count())
54 if(aResult == rSource)
56 // not clipped, but inside. Add original
57 return false;
59 else
61 // add clipped geometry
62 if(bStroke)
64 for(auto const& rB2DPolygon : aResult)
66 rTarget.AddAction(
67 new MetaPolyLineAction(
68 tools::Polygon(rB2DPolygon)));
71 else
73 rTarget.AddAction(
74 new MetaPolyPolygonAction(
75 tools::PolyPolygon(aResult)));
81 return true;
84 bool handleGradientContent(
85 const basegfx::B2DPolyPolygon& rClip,
86 const basegfx::B2DPolyPolygon& rSource,
87 const Gradient& rGradient,
88 GDIMetaFile& rTarget)
90 if(rSource.count() && rClip.count())
92 const basegfx::B2DPolyPolygon aResult(
93 basegfx::utils::clipPolyPolygonOnPolyPolygon(
94 rSource,
95 rClip,
96 true, // inside
97 false)); // stroke
99 if(aResult.count())
101 if(aResult == rSource)
103 // not clipped, but inside. Add original
104 return false;
106 else
108 // add clipped geometry
109 rTarget.AddAction(
110 new MetaGradientExAction(
111 tools::PolyPolygon(aResult),
112 rGradient));
117 return true;
120 bool handleBitmapContent(
121 const basegfx::B2DPolyPolygon& rClip,
122 const Point& rPoint,
123 const Size& rSize,
124 const BitmapEx& rBitmapEx,
125 GDIMetaFile& rTarget)
127 if(!rSize.Width() || !rSize.Height() || rBitmapEx.IsEmpty())
129 // bitmap or size is empty
130 return true;
133 const basegfx::B2DRange aLogicBitmapRange(
134 rPoint.X(), rPoint.Y(),
135 rPoint.X() + rSize.Width(), rPoint.Y() + rSize.Height());
136 const basegfx::B2DPolyPolygon aClipOfBitmap(
137 basegfx::utils::clipPolyPolygonOnRange(
138 rClip,
139 aLogicBitmapRange,
140 true,
141 false)); // stroke
143 if(!aClipOfBitmap.count())
145 // outside clip region
146 return true;
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);
157 if(basegfx::fTools::more(fFactor, 1.0 - 0.001))
159 // completely covering (with 0.1% tolerance)
160 return false;
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());
167 ScopedVclPtrInstance< VirtualDevice > aVDev;
169 aVDev->SetOutputSizePixel(aSizePixel);
170 aVDev->EnableMapMode(false);
171 aVDev->SetFillColor( COL_WHITE);
172 aVDev->SetLineColor();
174 if(rBitmapEx.IsTransparent())
176 // use given alpha channel
177 aVDev->DrawBitmap(Point(0, 0), rBitmapEx.GetAlpha().GetBitmap());
179 else
181 // reset alpha channel
182 aVDev->SetBackground(Wallpaper(COL_BLACK));
183 aVDev->Erase();
186 // transform polygon from clipping to pixel coordinates
187 basegfx::B2DPolyPolygon aPixelPoly(aClipOfBitmap);
188 basegfx::B2DHomMatrix aTransform;
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);
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;
201 aInvertPixelPoly.append(
202 basegfx::utils::createPolygonFromRect(
203 basegfx::B2DRange(
204 0.0, 0.0,
205 aSizePixel.Width(), aSizePixel.Height())));
206 aInvertPixelPoly.append(aPixelPoly);
208 // paint as alpha
209 aVDev->DrawPolyPolygon(aInvertPixelPoly);
211 // get created alpha mask and set defaults
212 AlphaMask aAlpha(
213 aVDev->GetBitmap(
214 Point(0, 0),
215 aSizePixel));
217 aAlpha.SetPrefSize(rBitmapEx.GetPrefSize());
218 aAlpha.SetPrefMapMode(rBitmapEx.GetPrefMapMode());
220 // add new action replacing the old one
221 rTarget.AddAction(
222 new MetaBmpExScaleAction(
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)));
231 return true;
234 void addSvtGraphicStroke(const SvtGraphicStroke& rStroke, GDIMetaFile& rTarget)
236 // write SvtGraphicFill
237 SvMemoryStream aMemStm;
238 WriteSvtGraphicStroke( aMemStm, rStroke );
239 rTarget.AddAction(
240 new MetaCommentAction(
241 "XPATHSTROKE_SEQ_BEGIN",
243 static_cast< const sal_uInt8* >(aMemStm.GetData()),
244 aMemStm.TellEnd()));
247 void addSvtGraphicFill(const SvtGraphicFill &rFilling, GDIMetaFile& rTarget)
249 // write SvtGraphicFill
250 SvMemoryStream aMemStm;
251 WriteSvtGraphicFill( aMemStm, rFilling );
252 rTarget.AddAction(
253 new MetaCommentAction(
254 "XPATHFILL_SEQ_BEGIN",
256 static_cast< const sal_uInt8* >(aMemStm.GetData()),
257 aMemStm.TellEnd()));
259 } // end of anonymous namespace
261 // #i121267# Tooling to internally clip geometry against internal clip regions
263 void clipMetafileContentAgainstOwnRegions(GDIMetaFile& rSource)
265 const sal_uLong nObjCount(rSource.GetActionSize());
267 if(!nObjCount)
269 return;
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;
279 // start with empty region
280 aClips.emplace_back();
282 // start with default MapMode (MapUnit::MapPixel)
283 aMapModes.emplace_back();
285 for(sal_uLong i(0); i < nObjCount; ++i)
287 const MetaAction* pAction(rSource.GetAction(i));
288 const MetaActionType nType(pAction->GetType());
289 bool bDone(false);
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)
297 case MetaActionType::CLIPREGION :
299 const MetaClipRegionAction* pA = static_cast< const MetaClipRegionAction* >(pAction);
301 if(pA->IsClipping())
303 const vcl::Region& rRegion = pA->GetRegion();
304 const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon());
306 aClips.back() = aNewClip;
308 else
310 aClips.back() = basegfx::B2DPolyPolygon();
313 break;
316 case MetaActionType::ISECTRECTCLIPREGION :
318 const MetaISectRectClipRegionAction* pA = static_cast< const MetaISectRectClipRegionAction* >(pAction);
319 const tools::Rectangle& rRect = pA->GetRect();
321 if(!rRect.IsEmpty() && !aClips.empty() && aClips.back().count())
323 const basegfx::B2DRange aClipRange(vcl::unotools::b2DRectangleFromRectangle(rRect));
325 aClips.back() = basegfx::utils::clipPolyPolygonOnRange(
326 aClips.back(),
327 aClipRange,
328 true, // inside
329 false); // stroke
331 break;
334 case MetaActionType::ISECTREGIONCLIPREGION :
336 const MetaISectRegionClipRegionAction* pA = static_cast< const MetaISectRegionClipRegionAction* >(pAction);
337 const vcl::Region& rRegion = pA->GetRegion();
339 if(!rRegion.IsEmpty() && !aClips.empty() && aClips.back().count())
341 const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon());
343 aClips.back() = basegfx::utils::clipPolyPolygonOnPolyPolygon(
344 aClips.back(),
345 aNewClip,
346 true, // inside
347 false); // stroke
349 break;
352 case MetaActionType::MOVECLIPREGION :
354 const MetaMoveClipRegionAction* pA = static_cast< const MetaMoveClipRegionAction* >(pAction);
355 const long aHorMove(pA->GetHorzMove());
356 const long aVerMove(pA->GetVertMove());
358 if((aHorMove || aVerMove) && !aClips.empty() && aClips.back().count())
360 aClips.back().transform(
361 basegfx::utils::createTranslateB2DHomMatrix(
362 aHorMove,
363 aVerMove));
365 break;
368 case MetaActionType::PUSH :
370 const MetaPushAction* pA = static_cast< const MetaPushAction* >(pAction);
371 const PushFlags nFlags(pA->GetFlags());
373 aPushFlags.push_back(nFlags);
375 if(nFlags & PushFlags::CLIPREGION)
377 aClips.push_back(aClips.back());
380 if(nFlags & PushFlags::MAPMODE)
382 aMapModes.push_back(aMapModes.back());
384 break;
387 case MetaActionType::POP :
390 if(!aPushFlags.empty())
392 const PushFlags nFlags(aPushFlags.back());
393 aPushFlags.pop_back();
395 if(nFlags & PushFlags::CLIPREGION)
397 if(aClips.size() > 1)
399 aClips.pop_back();
401 else
403 OSL_ENSURE(false, "Wrong POP() in ClipRegions (!)");
407 if(nFlags & PushFlags::MAPMODE)
409 if(aMapModes.size() > 1)
411 aMapModes.pop_back();
413 else
415 OSL_ENSURE(false, "Wrong POP() in MapModes (!)");
419 else
421 OSL_ENSURE(false, "Invalid pop() without push() (!)");
424 break;
427 case MetaActionType::MAPMODE :
429 const MetaMapModeAction* pA = static_cast< const MetaMapModeAction* >(pAction);
431 aMapModes.back() = pA->GetMapMode();
432 break;
435 default:
437 break;
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())
447 switch(nType)
450 // pixel actions, just check on inside
452 case MetaActionType::PIXEL :
454 const MetaPixelAction* pA = static_cast< const MetaPixelAction* >(pAction);
455 const Point& rPoint = pA->GetPoint();
457 if(!basegfx::utils::isInside(
458 aClips.back(),
459 basegfx::B2DPoint(rPoint.X(), rPoint.Y())))
461 // when not inside, do not add original
462 bDone = true;
464 break;
467 case MetaActionType::POINT :
469 const MetaPointAction* pA = static_cast< const MetaPointAction* >(pAction);
470 const Point& rPoint = pA->GetPoint();
472 if(!basegfx::utils::isInside(
473 aClips.back(),
474 basegfx::B2DPoint(rPoint.X(), rPoint.Y())))
476 // when not inside, do not add original
477 bDone = true;
479 break;
482 // geometry actions
484 case MetaActionType::LINE :
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;
491 aLine.append(basegfx::B2DPoint(rStart.X(), rStart.Y()));
492 aLine.append(basegfx::B2DPoint(rEnd.X(), rEnd.Y()));
494 bDone = handleGeometricContent(
495 aClips.back(),
496 basegfx::B2DPolyPolygon(aLine),
497 aTarget,
498 true); // stroke
499 break;
502 case MetaActionType::RECT :
504 const MetaRectAction* pA = static_cast< const MetaRectAction* >(pAction);
505 const tools::Rectangle& rRect = pA->GetRect();
507 if(rRect.IsEmpty())
509 bDone = true;
511 else
514 bDone = handleGeometricContent(
515 aClips.back(),
516 basegfx::B2DPolyPolygon(
517 basegfx::utils::createPolygonFromRect(
518 vcl::unotools::b2DRectangleFromRectangle(rRect))),
519 aTarget,
520 false); // stroke
522 break;
525 case MetaActionType::ROUNDRECT :
527 const MetaRoundRectAction* pA = static_cast< const MetaRoundRectAction* >(pAction);
528 const tools::Rectangle& rRect = pA->GetRect();
530 if(rRect.IsEmpty())
532 bDone = true;
534 else
536 const sal_uInt32 nHor(pA->GetHorzRound());
537 const sal_uInt32 nVer(pA->GetVertRound());
538 const basegfx::B2DRange aRange(vcl::unotools::b2DRectangleFromRectangle(rRect));
539 basegfx::B2DPolygon aOutline;
541 if(nHor || nVer)
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));
548 aOutline = basegfx::utils::createPolygonFromRect(aRange, fRadiusX, fRadiusY);
550 else
552 aOutline = basegfx::utils::createPolygonFromRect(aRange);
555 bDone = handleGeometricContent(
556 aClips.back(),
557 basegfx::B2DPolyPolygon(aOutline),
558 aTarget,
559 false); // stroke
561 break;
564 case MetaActionType::ELLIPSE :
566 const MetaEllipseAction* pA = static_cast< const MetaEllipseAction* >(pAction);
567 const tools::Rectangle& rRect = pA->GetRect();
569 if(rRect.IsEmpty())
571 bDone = true;
573 else
575 const basegfx::B2DRange aRange(vcl::unotools::b2DRectangleFromRectangle(rRect));
577 bDone = handleGeometricContent(
578 aClips.back(),
579 basegfx::B2DPolyPolygon(
580 basegfx::utils::createPolygonFromEllipse(
581 aRange.getCenter(),
582 aRange.getWidth() * 0.5,
583 aRange.getHeight() * 0.5)),
584 aTarget,
585 false); // stroke
587 break;
590 case MetaActionType::ARC :
592 const MetaArcAction* pA = static_cast< const MetaArcAction* >(pAction);
593 const tools::Rectangle& rRect = pA->GetRect();
595 if(rRect.IsEmpty())
597 bDone = true;
599 else
601 const tools::Polygon aToolsPoly(
602 rRect,
603 pA->GetStartPoint(),
604 pA->GetEndPoint(),
605 PolyStyle::Arc);
607 bDone = handleGeometricContent(
608 aClips.back(),
609 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
610 aTarget,
611 true); // stroke
613 break;
616 case MetaActionType::PIE :
618 const MetaPieAction* pA = static_cast< const MetaPieAction* >(pAction);
619 const tools::Rectangle& rRect = pA->GetRect();
621 if(rRect.IsEmpty())
623 bDone = true;
625 else
627 const tools::Polygon aToolsPoly(
628 rRect,
629 pA->GetStartPoint(),
630 pA->GetEndPoint(),
631 PolyStyle::Pie);
633 bDone = handleGeometricContent(
634 aClips.back(),
635 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
636 aTarget,
637 false); // stroke
639 break;
642 case MetaActionType::CHORD :
644 const MetaChordAction* pA = static_cast< const MetaChordAction* >(pAction);
645 const tools::Rectangle& rRect = pA->GetRect();
647 if(rRect.IsEmpty())
649 bDone = true;
651 else
653 const tools::Polygon aToolsPoly(
654 rRect,
655 pA->GetStartPoint(),
656 pA->GetEndPoint(),
657 PolyStyle::Chord);
659 bDone = handleGeometricContent(
660 aClips.back(),
661 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
662 aTarget,
663 false); // stroke
665 break;
668 case MetaActionType::POLYLINE :
670 const MetaPolyLineAction* pA = static_cast< const MetaPolyLineAction* >(pAction);
672 bDone = handleGeometricContent(
673 aClips.back(),
674 basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()),
675 aTarget,
676 true); // stroke
677 break;
680 case MetaActionType::POLYGON :
682 const MetaPolygonAction* pA = static_cast< const MetaPolygonAction* >(pAction);
684 bDone = handleGeometricContent(
685 aClips.back(),
686 basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()),
687 aTarget,
688 false); // stroke
689 break;
692 case MetaActionType::POLYPOLYGON :
694 const MetaPolyPolygonAction* pA = static_cast< const MetaPolyPolygonAction* >(pAction);
695 const tools::PolyPolygon& rPoly = pA->GetPolyPolygon();
697 bDone = handleGeometricContent(
698 aClips.back(),
699 rPoly.getB2DPolyPolygon(),
700 aTarget,
701 false); // stroke
702 break;
705 // bitmap actions, create BitmapEx with alpha channel derived
706 // from clipping
708 case MetaActionType::BMPEX :
710 const MetaBmpExAction* pA = static_cast< const MetaBmpExAction* >(pAction);
711 const BitmapEx& rBitmapEx = pA->GetBitmapEx();
713 // the logical size depends on the PrefSize of the given bitmap in
714 // combination with the current MapMode
715 Size aLogicalSize(rBitmapEx.GetPrefSize());
717 if(MapUnit::MapPixel == rBitmapEx.GetPrefMapMode().GetMapUnit())
719 aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back());
721 else
723 aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmapEx.GetPrefMapMode(), aMapModes.back());
726 bDone = handleBitmapContent(
727 aClips.back(),
728 pA->GetPoint(),
729 aLogicalSize,
730 rBitmapEx,
731 aTarget);
732 break;
735 case MetaActionType::BMP :
737 const MetaBmpAction* pA = static_cast< const MetaBmpAction* >(pAction);
738 const Bitmap& rBitmap = pA->GetBitmap();
740 // the logical size depends on the PrefSize of the given bitmap in
741 // combination with the current MapMode
742 Size aLogicalSize(rBitmap.GetPrefSize());
744 if(MapUnit::MapPixel == rBitmap.GetPrefMapMode().GetMapUnit())
746 aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back());
748 else
750 aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmap.GetPrefMapMode(), aMapModes.back());
753 bDone = handleBitmapContent(
754 aClips.back(),
755 pA->GetPoint(),
756 aLogicalSize,
757 BitmapEx(rBitmap),
758 aTarget);
759 break;
762 case MetaActionType::BMPEXSCALE :
764 const MetaBmpExScaleAction* pA = static_cast< const MetaBmpExScaleAction* >(pAction);
766 bDone = handleBitmapContent(
767 aClips.back(),
768 pA->GetPoint(),
769 pA->GetSize(),
770 pA->GetBitmapEx(),
771 aTarget);
772 break;
775 case MetaActionType::BMPSCALE :
777 const MetaBmpScaleAction* pA = static_cast< const MetaBmpScaleAction* >(pAction);
779 bDone = handleBitmapContent(
780 aClips.back(),
781 pA->GetPoint(),
782 pA->GetSize(),
783 BitmapEx(pA->GetBitmap()),
784 aTarget);
785 break;
788 case MetaActionType::BMPEXSCALEPART :
790 const MetaBmpExScalePartAction* pA = static_cast< const MetaBmpExScalePartAction* >(pAction);
791 const BitmapEx& rBitmapEx = pA->GetBitmapEx();
793 if(rBitmapEx.IsEmpty())
795 // empty content
796 bDone = true;
798 else
800 BitmapEx aCroppedBitmapEx(rBitmapEx);
801 const tools::Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
803 if(aCropRectangle.IsEmpty())
805 // empty content
806 bDone = true;
808 else
810 aCroppedBitmapEx.Crop(aCropRectangle);
811 bDone = handleBitmapContent(
812 aClips.back(),
813 pA->GetDestPoint(),
814 pA->GetDestSize(),
815 aCroppedBitmapEx,
816 aTarget);
819 break;
822 case MetaActionType::BMPSCALEPART :
824 const MetaBmpScalePartAction* pA = static_cast< const MetaBmpScalePartAction* >(pAction);
825 const Bitmap& rBitmap = pA->GetBitmap();
827 if(rBitmap.IsEmpty())
829 // empty content
830 bDone = true;
832 else
834 Bitmap aCroppedBitmap(rBitmap);
835 const tools::Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
837 if(aCropRectangle.IsEmpty())
839 // empty content
840 bDone = true;
842 else
844 aCroppedBitmap.Crop(aCropRectangle);
845 bDone = handleBitmapContent(
846 aClips.back(),
847 pA->GetDestPoint(),
848 pA->GetDestSize(),
849 BitmapEx(aCroppedBitmap),
850 aTarget);
853 break;
856 // need to handle all those 'hacks' which hide data in comments
858 case MetaActionType::COMMENT :
860 const MetaCommentAction* pA = static_cast< const MetaCommentAction* >(pAction);
861 const OString& rComment = pA->GetComment();
863 if(rComment.equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
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.
871 else if(rComment.equalsIgnoreAsciiCase("XPATHFILL_SEQ_BEGIN"))
873 SvtGraphicFill aFilling;
874 tools::PolyPolygon aPath;
876 { // read SvtGraphicFill
877 SvMemoryStream aMemStm(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(),StreamMode::READ);
878 ReadSvtGraphicFill( aMemStm, aFilling );
881 aFilling.getPath(aPath);
883 if(aPath.Count())
885 const basegfx::B2DPolyPolygon aSource(aPath.getB2DPolyPolygon());
886 const basegfx::B2DPolyPolygon aResult(
887 basegfx::utils::clipPolyPolygonOnPolyPolygon(
888 aSource,
889 aClips.back(),
890 true, // inside
891 false)); // stroke
893 if(aResult.count())
895 if(aResult != aSource)
897 // add clipped geometry
898 aFilling.setPath(tools::PolyPolygon(aResult));
899 addSvtGraphicFill(aFilling, aTarget);
900 bDone = true;
903 else
905 // exchange with empty polygon
906 aFilling.setPath(tools::PolyPolygon());
907 addSvtGraphicFill(aFilling, aTarget);
908 bDone = true;
912 else if(rComment.equalsIgnoreAsciiCase("XPATHSTROKE_SEQ_BEGIN"))
914 SvtGraphicStroke aStroke;
915 tools::Polygon aPath;
917 { // read SvtGraphicFill
918 SvMemoryStream aMemStm(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(),StreamMode::READ);
919 ReadSvtGraphicStroke( aMemStm, aStroke );
922 aStroke.getPath(aPath);
924 if(aPath.GetSize())
926 const basegfx::B2DPolygon aSource(aPath.getB2DPolygon());
927 const basegfx::B2DPolyPolygon aResult(
928 basegfx::utils::clipPolygonOnPolyPolygon(
929 aSource,
930 aClips.back(),
931 true, // inside
932 true)); // stroke
934 if(aResult.count())
936 if(aResult.count() > 1 || aResult.getB2DPolygon(0) != aSource)
938 // add clipped geometry
939 for(auto const& rB2DPolygon : aResult)
941 aStroke.setPath(tools::Polygon(rB2DPolygon));
942 addSvtGraphicStroke(aStroke, aTarget);
945 bDone = true;
948 else
950 // exchange with empty polygon
951 aStroke.setPath(tools::Polygon());
952 addSvtGraphicStroke(aStroke, aTarget);
953 bDone = true;
958 break;
961 // need to handle gradient fills (hopefully only unrotated ones)
963 case MetaActionType::GRADIENT :
965 const MetaGradientAction* pA = static_cast< const MetaGradientAction* >(pAction);
966 const tools::Rectangle& rRect = pA->GetRect();
968 if(rRect.IsEmpty())
970 bDone = true;
972 else
974 bDone = handleGradientContent(
975 aClips.back(),
976 basegfx::B2DPolyPolygon(
977 basegfx::utils::createPolygonFromRect(
978 vcl::unotools::b2DRectangleFromRectangle(rRect))),
979 pA->GetGradient(),
980 aTarget);
983 break;
986 case MetaActionType::GRADIENTEX :
988 const MetaGradientExAction* pA = static_cast< const MetaGradientExAction* >(pAction);
989 const tools::PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
991 bDone = handleGradientContent(
992 aClips.back(),
993 rPolyPoly.getB2DPolyPolygon(),
994 pA->GetGradient(),
995 aTarget);
996 break;
999 // not (yet) supported actions
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
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:
1031 break;
1036 if(bDone)
1038 bChanged = true;
1040 else
1042 aTarget.AddAction(const_cast< MetaAction* >(pAction));
1046 if(bChanged)
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;
1056 bool usesClipActions(const GDIMetaFile& rSource)
1058 const sal_uLong nObjCount(rSource.GetActionSize());
1060 for(sal_uLong i(0); i < nObjCount; ++i)
1062 const MetaAction* pAction(rSource.GetAction(i));
1063 const MetaActionType nType(pAction->GetType());
1065 switch(nType)
1067 case MetaActionType::CLIPREGION :
1068 case MetaActionType::ISECTRECTCLIPREGION :
1069 case MetaActionType::ISECTREGIONCLIPREGION :
1070 case MetaActionType::MOVECLIPREGION :
1072 return true;
1075 default: break;
1079 return false;
1082 MetafileAccessor::~MetafileAccessor()
1086 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */