[Libreoffice-commits] core.git: basegfx/source drawinglayer/source filter/source include/basegfx include/vcl vcl/Library_vcl.mk vcl/source

Armin Le Grand alg at apache.org
Wed Jun 12 07:39:12 PDT 2013


 basegfx/source/polygon/b2dpolygontools.cxx                 |    2 
 basegfx/source/polygon/b2dpolypolygontools.cxx             |   20 
 drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx |   76 
 drawinglayer/source/processor2d/vclprocessor2d.cxx         |   36 
 filter/source/flash/swfexporter.cxx                        |   11 
 filter/source/graphicfilter/eos2met/eos2met.cxx            |   19 
 include/basegfx/polygon/b2dpolypolygontools.hxx            |    6 
 include/vcl/gdimetafiletools.hxx                           |   43 
 vcl/Library_vcl.mk                                         |    1 
 vcl/source/filter/wmf/wmf.cxx                              |   25 
 vcl/source/gdi/gdimetafiletools.cxx                        | 1095 +++++++++++++
 11 files changed, 1297 insertions(+), 37 deletions(-)

New commits:
commit 70e3eb2c1762fb1ca097cf671e3c7ce3d0dfd1b7
Author: Armin Le Grand <alg at apache.org>
Date:   Fri Oct 26 11:00:48 2012 +0000

    Resolves: #i121267# added support for taking clipping into account...
    
    for metafile-based exporters to vector formats
    
    (cherry picked from commit 229a79b97110c11ef21b7b99ffc768254ca01d96)
    
    Conflicts:
    	basegfx/inc/basegfx/polygon/b2dpolypolygontools.hxx
    	filter/source/flash/swfexporter.cxx
    	filter/source/graphicfilter/eos2met/eos2met.cxx
    	svtools/source/filter/wmf/wmf.cxx
    	vcl/Package_inc.mk
    
    Remove unused variable to prevent compile warnings.
    
    (cherry picked from commit 60d19c1602e4fec740624fd20a7bfd39707297e4)
    
    Change-Id: Iaeb02d7321bea2890cf4e969f1d45d58deef5584
    106285f06a21f4f39f34c6755d12ac6cd4196a7f

diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx
index 24affdf..59b2813 100644
--- a/basegfx/source/polygon/b2dpolygontools.cxx
+++ b/basegfx/source/polygon/b2dpolygontools.cxx
@@ -489,8 +489,6 @@ namespace basegfx
                     fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX();
                 }
 
-                fRetval /= 2.0;
-
                 // correct to zero if small enough. Also test the quadratic
                 // of the result since the precision is near quadratic due to
                 // the algorithm
diff --git a/basegfx/source/polygon/b2dpolypolygontools.cxx b/basegfx/source/polygon/b2dpolypolygontools.cxx
index 8a5337b..c2b18ef 100644
--- a/basegfx/source/polygon/b2dpolypolygontools.cxx
+++ b/basegfx/source/polygon/b2dpolypolygontools.cxx
@@ -243,6 +243,26 @@ namespace basegfx
             return aRetval;
         }
 
+        double getSignedArea(const B2DPolyPolygon& rCandidate)
+        {
+            double fRetval(0.0);
+            const sal_uInt32 nPolygonCount(rCandidate.count());
+
+            for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+            {
+                const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+                fRetval += tools::getSignedArea(aCandidate);
+            }
+
+            return fRetval;
+        }
+
+        double getArea(const B2DPolyPolygon& rCandidate)
+        {
+            return fabs(getSignedArea(rCandidate));
+        }
+
         void applyLineDashing(const B2DPolyPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, B2DPolyPolygon* pGapTarget, double fFullDashDotLen)
         {
             if(0.0 == fFullDashDotLen && rDotDashArray.size())
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index cf3c79b..3511e57 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -1177,14 +1177,29 @@ namespace drawinglayer
                         // direct draw of hairline; use default processing
                         // support SvtGraphicStroke MetaCommentAction
                         const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rHairlinePrimitive.getBColor()));
-                        SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
-                            rHairlinePrimitive.getB2DPolygon(),
-                            &aLineColor,
-                            0, 0, 0, 0);
+                        SvtGraphicStroke* pSvtGraphicStroke = 0;
+
+                        // #i121267# Not needed, does not give better quality compared with
+                        // the META_POLYPOLYGON_ACTION written by RenderPolygonHairlinePrimitive2D
+                        // below
+                        bool bSupportSvtGraphicStroke(false);
+
+                        if(bSupportSvtGraphicStroke)
+                        {
+                            pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
+                                rHairlinePrimitive.getB2DPolygon(),
+                                &aLineColor,
+                                0, 0, 0, 0);
+
+                            impStartSvtGraphicStroke(pSvtGraphicStroke);
+                        }
 
-                        impStartSvtGraphicStroke(pSvtGraphicStroke);
                         RenderPolygonHairlinePrimitive2D(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate), false);
-                        impEndSvtGraphicStroke(pSvtGraphicStroke);
+
+                        if(bSupportSvtGraphicStroke)
+                        {
+                            impEndSvtGraphicStroke(pSvtGraphicStroke);
+                        }
                     }
                     break;
                 }
@@ -1653,7 +1668,12 @@ namespace drawinglayer
                     // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
                     SvtGraphicFill* pSvtGraphicFill = 0;
 
-                    if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
+                    // #i121267# Not needed, does not give better quality compared with
+                    // the META_POLYPOLYGON_ACTION written by the DrawPolyPolygon command
+                    // below
+                    bool bSupportSvtGraphicFill(false);
+
+                    if(bSupportSvtGraphicFill && !mnSvtGraphicFillCount && aLocalPolyPolygon.count())
                     {
                         // setup simple color fill stuff like in impgrfll
                         pSvtGraphicFill = new SvtGraphicFill(
@@ -1678,9 +1698,17 @@ namespace drawinglayer
                     mpOutputDevice->SetLineColor();
 
                     // call VCL directly; encapsulate with SvtGraphicFill
-                    impStartSvtGraphicFill(pSvtGraphicFill);
+                    if(bSupportSvtGraphicFill)
+                    {
+                            impStartSvtGraphicFill(pSvtGraphicFill);
+                    }
+
                     mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
-                    impEndSvtGraphicFill(pSvtGraphicFill);
+
+                    if(bSupportSvtGraphicFill)
+                    {
+                        impEndSvtGraphicFill(pSvtGraphicFill);
+                    }
 
                     break;
                 }
@@ -1739,16 +1767,13 @@ namespace drawinglayer
                                 // Removed subdivision and fixed in Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where
                                 // the ClipRegion is built from the Polygon. A AdaptiveSubdivide on the source polygon was missing there
                                 mpOutputDevice->Push(PUSH_CLIPREGION);
-                                //mpOutputDevice->SetClipRegion(Region(PolyPolygon(basegfx::tools::adaptiveSubdivideByAngle(maClipPolyPolygon))));
-                                //mpOutputDevice->SetClipRegion(Region(PolyPolygon(maClipPolyPolygon)));
                                 mpOutputDevice->SetClipRegion(Region(maClipPolyPolygon));
-                            }
 
-                            // recursively paint content
-                            process(rMaskCandidate.getChildren());
+                                // recursively paint content
+                                // #i121267# Only need to process sub-content when clip polygon is *not* empty.
+                                // If it is empty, the clip is empty and there can be nothing inside.
+                                process(rMaskCandidate.getChildren());
 
-                            if(maClipPolyPolygon.count())
-                            {
                                 // restore VCL clip region
                                 mpOutputDevice->Pop();
                             }
@@ -1820,7 +1845,12 @@ namespace drawinglayer
                                 // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
                                 SvtGraphicFill* pSvtGraphicFill = 0;
 
-                                if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
+                                // #i121267# Not needed, does not give better quality compared with
+                                // the META_POLYPOLYGON_ACTION written by the DrawPolyPolygon command
+                                // below
+                                bool bSupportSvtGraphicFill(false);
+
+                                if(bSupportSvtGraphicFill && !mnSvtGraphicFillCount && aLocalPolyPolygon.count())
                                 {
                                     // setup simple color with transparence fill stuff like in impgrfll
                                     pSvtGraphicFill = new SvtGraphicFill(
@@ -1846,11 +1876,19 @@ namespace drawinglayer
                                 mpOutputDevice->SetLineColor();
 
                                 // call VCL directly; encapsulate with SvtGraphicFill
-                                impStartSvtGraphicFill(pSvtGraphicFill);
+                                if(bSupportSvtGraphicFill)
+                                {
+                                    impStartSvtGraphicFill(pSvtGraphicFill);
+                                }
+
                                 mpOutputDevice->DrawTransparent(
                                     PolyPolygon(aLocalPolyPolygon),
                                     nTransPercentVcl);
-                                impEndSvtGraphicFill(pSvtGraphicFill);
+
+                                if(bSupportSvtGraphicFill)
+                                {
+                                    impEndSvtGraphicFill(pSvtGraphicFill);
+                                }
                             }
                             else
                             {
diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx
index d129251..9741bc8 100644
--- a/drawinglayer/source/processor2d/vclprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx
@@ -538,14 +538,16 @@ namespace drawinglayer
                                 // nBWidth, nBHeight is the pixel size of the neede bitmap. To not need to scale it
                                 // in vcl many times, create a size-optimized version
                                 const Size aNeededBitmapSizePixel(nBWidth, nBHeight);
-                                BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx(
-                                    GraphicConversionParameters(
-                                        aNeededBitmapSizePixel, // get the correct size immediately
-                                        false, // no unlimited size
-                                        false, // Use AntiAliasing
-                                        false, //SnapHorVerLines
-                                        true // ScaleHighQuality
-                                        )));
+                                BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx());
+                                static bool bEnablePreScaling(true);
+                                const bool bPreScaled(bEnablePreScaling && nBWidth * nBHeight < (250 * 250));
+
+                                if(bPreScaled)
+                                {
+                                    // ... but only up to a maximum size, else it gets too expensive
+                                    aBitmapEx.Scale(aNeededBitmapSizePixel, BMP_SCALE_INTERPOLATE);
+                                }
+
                                 bool bPainted(false);
 
                                 if(maBColorModifierStack.count())
@@ -632,7 +634,14 @@ namespace drawinglayer
 
                                                 if(aOutRectPixel.IsOver(aVisiblePixel))
                                                 {
-                                                    mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
+                                                    if(bPreScaled)
+                                                    {
+                                                        mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
+                                                    }
+                                                    else
+                                                    {
+                                                        mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, aBitmapEx);
+                                                    }
                                                 }
                                             }
                                         }
@@ -652,7 +661,14 @@ namespace drawinglayer
 
                                                 if(aOutRectPixel.IsOver(aVisiblePixel))
                                                 {
-                                                    mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
+                                                    if(bPreScaled)
+                                                    {
+                                                        mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
+                                                    }
+                                                    else
+                                                    {
+                                                        mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, aBitmapEx);
+                                                    }
                                                 }
                                             }
                                         }
diff --git a/filter/source/flash/swfexporter.cxx b/filter/source/flash/swfexporter.cxx
index 8bc51b6..3e5e364 100644
--- a/filter/source/flash/swfexporter.cxx
+++ b/filter/source/flash/swfexporter.cxx
@@ -35,6 +35,7 @@
 #include <vcl/metaact.hxx>
 #include <vcl/wmf.hxx>
 #include <vcl/graphicfilter.hxx>
+#include <vcl/gdimetafiletools.hxx>
 
 #include "swfexporter.hxx"
 #include "swfwriter.hxx"
@@ -707,8 +708,18 @@ bool FlashExporter::getMetaFile( Reference< XComponent >&xComponent, GDIMetaFile
 
     }
     else
+    {
         rMtf.Read( *aFile.GetStream( STREAM_READ ) );
 
+        if(usesClipActions(rMtf))
+        {
+            // #i121267# It is necessary to prepare the metafile since the export does *not* support
+            // clip regions. This tooling method clips the geometry content of the metafile internally
+            // against it's own clip regions, so that the export is safe to ignore clip regions
+            clipMetafileContentAgainstOwnRegions(rMtf);
+        }
+    }
+
     return rMtf.GetActionSize() != 0;
 }
 
diff --git a/filter/source/graphicfilter/eos2met/eos2met.cxx b/filter/source/graphicfilter/eos2met/eos2met.cxx
index f5a5a23..761c456 100644
--- a/filter/source/graphicfilter/eos2met/eos2met.cxx
+++ b/filter/source/graphicfilter/eos2met/eos2met.cxx
@@ -34,7 +34,7 @@
 #include <vcl/svapp.hxx>
 #include <vcl/msgbox.hxx>
 #include <svl/solar.hrc>
-
+#include <vcl/gdimetafiletools.hxx>
 
 // -----------------------------Field Types-------------------------------
 
@@ -2558,10 +2558,21 @@ sal_Bool METWriter::WriteMET( const GDIMetaFile& rMTF, SvStream& rTargetStream,
 
 extern "C" SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL
 GraphicExport( SvStream & rStream, Graphic & rGraphic, FilterConfigItem* pFilterConfigItem, sal_Bool )
-{   METWriter aMETWriter;
+{
+    METWriter aMETWriter;
+
+    // #119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+    GDIMetaFile aMetafile(rGraphic.GetGDIMetaFile());
+
+    if(usesClipActions(aMetafile))
+    {
+        // #i121267# It is necessary to prepare the metafile since the export does *not* support
+        // clip regions. This tooling method clips the geometry content of the metafile internally
+        // against it's own clip regions, so that the export is safe to ignore clip regions
+        clipMetafileContentAgainstOwnRegions(aMetafile);
+    }
 
-    // #119735# just use GetGDIMetaFile, it will create a bufferd version of contained bitmap now automatically
-    return aMETWriter.WriteMET( rGraphic.GetGDIMetaFile(), rStream, pFilterConfigItem );
+    return aMETWriter.WriteMET( aMetafile, rStream, pFilterConfigItem );
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/basegfx/polygon/b2dpolypolygontools.hxx b/include/basegfx/polygon/b2dpolypolygontools.hxx
index 29ae3e0..01c1306 100644
--- a/include/basegfx/polygon/b2dpolypolygontools.hxx
+++ b/include/basegfx/polygon/b2dpolypolygontools.hxx
@@ -78,6 +78,12 @@ namespace basegfx
         */
         BASEGFX_DLLPUBLIC B2DRange getRange(const B2DPolyPolygon& rCandidate);
 
+        // get signed area of polygon
+        BASEGFX_DLLPUBLIC double getSignedArea(const B2DPolyPolygon& rCandidate);
+
+        // get area of polygon
+        BASEGFX_DLLPUBLIC double getArea(const B2DPolyPolygon& rCandidate);
+
         /** Apply given LineDashing to given polyPolygon
 
             For a description see applyLineDashing in b2dpolygontoos.hxx
diff --git a/include/vcl/gdimetafiletools.hxx b/include/vcl/gdimetafiletools.hxx
new file mode 100644
index 0000000..f6f3cfd
--- /dev/null
+++ b/include/vcl/gdimetafiletools.hxx
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef _SV_GDIMETAFILETOOLS_HXX
+#define _SV_GDIMETAFILETOOLS_HXX
+
+#include <vcl/gdimtf.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+// #i121267# Added tooling to be able to support old exporters which are based on
+// metafiles as graphic content, but do not implement using the contained clip
+// regions.
+// The given metafile will internall yclip it's graphic content against the
+// included clip regions so that it is safe to ignore clip actions there. This
+// is not done completely, but implemented and extended as needed (on demand)
+// since all this is a workarund; the better and long term solution will be to
+// reimplement these im/exports to use primitives and not metafiles as bese
+// information.
+
+void VCL_DLLPUBLIC clipMetafileContentAgainstOwnRegions(GDIMetaFile& rSource);
+
+//////////////////////////////////////////////////////////////////////////////
+// Allow to check if a Metafile contains clipping or not
+
+bool VCL_DLLPUBLIC usesClipActions(const GDIMetaFile& rSource);
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif // _SV_GDIMETAFILETOOLS_HXX
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index d9b19fa..761f497 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -206,6 +206,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/gdi/extoutdevdata \
     vcl/source/gdi/font \
     vcl/source/gdi/gdimtf \
+    vcl/source/gdi/gdimetafiletools \
     vcl/source/gdi/gfxlink \
     vcl/source/gdi/gradient \
     vcl/source/gdi/graph \
diff --git a/vcl/source/filter/wmf/wmf.cxx b/vcl/source/filter/wmf/wmf.cxx
index 91cf24f..3c06d55 100644
--- a/vcl/source/filter/wmf/wmf.cxx
+++ b/vcl/source/filter/wmf/wmf.cxx
@@ -22,6 +22,7 @@
 #include "emfwr.hxx"
 #include "wmfwr.hxx"
 #include <vcl/wmf.hxx>
+#include <vcl/gdimetafiletools.hxx>
 #include <comphelper/scopeguard.hxx>
 
 // -----------------------------------------------------------------------------
@@ -88,7 +89,17 @@ bool ConvertGDIMetaFileToWMF( const GDIMetaFile & rMTF, SvStream & rTargetStream
                               FilterConfigItem* pConfigItem, bool bPlaceable)
 {
     WMFWriter aWMFWriter;
-    return aWMFWriter.WriteWMF( rMTF, rTargetStream, pConfigItem, bPlaceable );
+    GDIMetaFile aGdiMetaFile(rMTF);
+
+    if(usesClipActions(aGdiMetaFile))
+    {
+        // #i121267# It is necessary to prepare the metafile since the export does *not* support
+        // clip regions. This tooling method clips the geometry content of the metafile internally
+        // against it's own clip regions, so that the export is safe to ignore clip regions
+        clipMetafileContentAgainstOwnRegions(aGdiMetaFile);
+    }
+
+    return aWMFWriter.WriteWMF( aGdiMetaFile, rTargetStream, pConfigItem, bPlaceable );
 }
 
 // -----------------------------------------------------------------------------
@@ -97,7 +108,17 @@ sal_Bool ConvertGDIMetaFileToEMF( const GDIMetaFile & rMTF, SvStream & rTargetSt
                               FilterConfigItem* pConfigItem )
 {
     EMFWriter aEMFWriter(rTargetStream);
-    return aEMFWriter.WriteEMF( rMTF, pConfigItem );
+    GDIMetaFile aGdiMetaFile(rMTF);
+
+    if(usesClipActions(aGdiMetaFile))
+    {
+        // #i121267# It is necessary to prepare the metafile since the export does *not* support
+        // clip regions. This tooling method clips the geometry content of the metafile internally
+        // against it's own clip regions, so that the export is safe to ignore clip regions
+        clipMetafileContentAgainstOwnRegions(aGdiMetaFile);
+    }
+
+    return aEMFWriter.WriteEMF( aGdiMetaFile, pConfigItem );
 }
 
 // -----------------------------------------------------------------------------
diff --git a/vcl/source/gdi/gdimetafiletools.cxx b/vcl/source/gdi/gdimetafiletools.cxx
new file mode 100644
index 0000000..ec2c947
--- /dev/null
+++ b/vcl/source/gdi/gdimetafiletools.cxx
@@ -0,0 +1,1095 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/gdimetafiletools.hxx>
+#include <vcl/metaact.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/graphictools.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+// helpers
+
+namespace
+{
+    bool handleGeometricContent(
+        const basegfx::B2DPolyPolygon& rClip,
+        const basegfx::B2DPolyPolygon& rSource,
+        GDIMetaFile& rTarget,
+        bool bStroke)
+    {
+        if(rSource.count() && rClip.count())
+        {
+            const basegfx::B2DPolyPolygon aResult(
+                basegfx::tools::clipPolyPolygonOnPolyPolygon(
+                    rSource,
+                    rClip,
+                    true, // inside
+                    bStroke));
+
+            if(aResult.count())
+            {
+                if(aResult == rSource)
+                {
+                    // not clipped, but inside. Add original
+                    return false;
+                }
+                else
+                {
+                    // add clipped geometry
+                    if(bStroke)
+                    {
+                        for(sal_uInt32 a(0); a < aResult.count(); a++)
+                        {
+                            rTarget.AddAction(
+                                new MetaPolyLineAction(
+                                    Polygon(aResult.getB2DPolygon(a))));
+                        }
+                    }
+                    else
+                    {
+                        rTarget.AddAction(
+                            new MetaPolyPolygonAction(
+                                PolyPolygon(aResult)));
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    bool handleGradientContent(
+        const basegfx::B2DPolyPolygon& rClip,
+        const basegfx::B2DPolyPolygon& rSource,
+        const Gradient& rGradient,
+        GDIMetaFile& rTarget)
+    {
+        if(rSource.count() && rClip.count())
+        {
+            const basegfx::B2DPolyPolygon aResult(
+                basegfx::tools::clipPolyPolygonOnPolyPolygon(
+                    rSource,
+                    rClip,
+                    true, // inside
+                    false)); // stroke
+
+            if(aResult.count())
+            {
+                if(aResult == rSource)
+                {
+                    // not clipped, but inside. Add original
+                    return false;
+                }
+                else
+                {
+                    // add clipped geometry
+                    rTarget.AddAction(
+                        new MetaGradientExAction(
+                            PolyPolygon(aResult),
+                            rGradient));
+                }
+            }
+        }
+
+        return true;
+    }
+
+    bool handleBitmapContent(
+        const basegfx::B2DPolyPolygon& rClip,
+        const Point& rPoint,
+        const Size& rSize,
+        const BitmapEx& rBitmapEx,
+        GDIMetaFile& rTarget)
+    {
+        if(!rSize.Width() || !rSize.Height() || rBitmapEx.IsEmpty())
+        {
+            // bitmap or size is empty
+            return true;
+        }
+
+        const basegfx::B2DRange aLogicBitmapRange(
+            rPoint.X(), rPoint.Y(),
+            rPoint.X() + rSize.Width(), rPoint.Y() + rSize.Height());
+        const basegfx::B2DPolyPolygon aClipOfBitmap(
+            basegfx::tools::clipPolyPolygonOnRange(
+                rClip,
+                aLogicBitmapRange,
+                true,
+                false)); // stroke
+
+        if(!aClipOfBitmap.count())
+        {
+            // outside clip region
+            return true;
+        }
+
+        // inside or overlapping. Use area to find out if it is completely
+        // covering (inside) or overlapping
+        const double fClipArea(basegfx::tools::getArea(aClipOfBitmap));
+        const double fBitmapArea(
+            aLogicBitmapRange.getWidth() * aLogicBitmapRange.getWidth() +
+            aLogicBitmapRange.getHeight() * aLogicBitmapRange.getHeight());
+        const double fFactor(fClipArea / fBitmapArea);
+
+        if(basegfx::fTools::more(fFactor, 1.0 - 0.001))
+        {
+            // completely covering (with 0.1% tolerance)
+            return false;
+        }
+
+        // needs clipping (with 0.1% tolerance). Prepare VirtualDevice
+        // in pixel mode for alpha channel painting (black is transparent,
+        // white to paint 100% opacity)
+        const Size aSizePixel(rBitmapEx.GetSizePixel());
+        VirtualDevice aVDev;
+
+        aVDev.SetOutputSizePixel(aSizePixel);
+        aVDev.EnableMapMode(false);
+        aVDev.SetFillColor(COL_WHITE);
+        aVDev.SetLineColor();
+
+        if(rBitmapEx.IsTransparent())
+        {
+            // use given alpha channel
+            aVDev.DrawBitmap(Point(0, 0), rBitmapEx.GetAlpha().GetBitmap());
+        }
+        else
+        {
+            // reset alpha channel
+            aVDev.SetBackground(Wallpaper(Color(COL_BLACK)));
+            aVDev.Erase();
+        }
+
+        // transform polygon from clipping to pixel coordinates
+        basegfx::B2DPolyPolygon aPixelPoly(aClipOfBitmap);
+        basegfx::B2DHomMatrix aTransform;
+
+        aTransform.translate(-aLogicBitmapRange.getMinX(), -aLogicBitmapRange.getMinY());
+        aTransform.scale(
+            static_cast< double >(aSizePixel.Width()) / aLogicBitmapRange.getWidth(),
+            static_cast< double >(aSizePixel.Height()) / aLogicBitmapRange.getHeight());
+        aPixelPoly.transform(aTransform);
+
+        // to fill the non-covered parts, use the Xor fill rule of
+        // PolyPolygon painting. Start with a all-covering polygon and
+        // add the clip polygon one
+        basegfx::B2DPolyPolygon aInvertPixelPoly;
+
+        aInvertPixelPoly.append(
+            basegfx::tools::createPolygonFromRect(
+                basegfx::B2DRange(
+                    0.0, 0.0,
+                    aSizePixel.Width(), aSizePixel.Height())));
+        aInvertPixelPoly.append(aPixelPoly);
+
+        // paint as alpha
+        aVDev.DrawPolyPolygon(aInvertPixelPoly);
+
+        // get created alpha mask and set defaults
+        AlphaMask aAlpha(
+            aVDev.GetBitmap(
+                Point(0, 0),
+                aSizePixel));
+
+        aAlpha.SetPrefSize(rBitmapEx.GetPrefSize());
+        aAlpha.SetPrefMapMode(rBitmapEx.GetPrefMapMode());
+
+        // add new action replacing the old one
+        rTarget.AddAction(
+            new MetaBmpExScaleAction(
+                Point(
+                    basegfx::fround(aLogicBitmapRange.getMinX()),
+                    basegfx::fround(aLogicBitmapRange.getMinY())),
+                Size(
+                    basegfx::fround(aLogicBitmapRange.getWidth()),
+                    basegfx::fround(aLogicBitmapRange.getHeight())),
+                BitmapEx(rBitmapEx.GetBitmap(), aAlpha)));
+
+        return true;
+    }
+
+    void addSvtGraphicStroke(const SvtGraphicStroke& rStroke, GDIMetaFile& rTarget)
+    {
+        // write SvtGraphicFill
+        SvMemoryStream aMemStm;
+        aMemStm << rStroke;
+        rTarget.AddAction(
+            new MetaCommentAction(
+                "XPATHSTROKE_SEQ_BEGIN",
+                0,
+                static_cast< const sal_uInt8* >(aMemStm.GetData()),
+                aMemStm.Seek(STREAM_SEEK_TO_END)));
+    }
+
+    void addSvtGraphicFill(const SvtGraphicFill &rFilling, GDIMetaFile& rTarget)
+    {
+        // write SvtGraphicFill
+        SvMemoryStream aMemStm;
+        aMemStm << rFilling;
+        rTarget.AddAction(
+            new MetaCommentAction(
+                "XPATHFILL_SEQ_BEGIN",
+                0,
+                static_cast< const sal_uInt8* >(aMemStm.GetData()),
+                aMemStm.Seek(STREAM_SEEK_TO_END)));
+    }
+} // end of anonymous namespace
+
+//////////////////////////////////////////////////////////////////////////////
+// #i121267# Tooling to internally clip geometry against internal clip regions
+
+void clipMetafileContentAgainstOwnRegions(GDIMetaFile& rSource)
+{
+    const sal_uLong nObjCount(rSource.GetActionSize());
+
+    if(!nObjCount)
+    {
+        return;
+    }
+
+    // prepare target data container and push/pop stack data
+    GDIMetaFile aTarget;
+    bool bChanged(false);
+    std::vector< basegfx::B2DPolyPolygon > aClips;
+    std::vector< sal_uInt16 > aPushFlags;
+    std::vector< MapMode > aMapModes;
+
+    // start with empty region
+    aClips.push_back(basegfx::B2DPolyPolygon());
+
+    // start with default MapMode (MAP_PIXEL)
+    aMapModes.push_back(MapMode());
+
+    for(sal_uLong i(0); i < nObjCount; ++i)
+    {
+        const MetaAction* pAction(rSource.GetAction(i));
+        const sal_uInt16 nType(pAction->GetType());
+        bool bDone(false);
+
+        // basic operation takes care of clipregion actions (four) and push/pop of these
+        // to steer the currently set clip region. There *is* an active
+        // clip region when (aClips.size() && aClips.back().count()), see
+        // below
+        switch(nType)
+        {
+            case META_CLIPREGION_ACTION :
+            {
+                const MetaClipRegionAction* pA = static_cast< const MetaClipRegionAction* >(pAction);
+
+                if(pA->IsClipping())
+                {
+                    const Region& rRegion = pA->GetRegion();
+                    const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon());
+
+                    aClips.back() = aNewClip;
+                }
+                else
+                {
+                    aClips.back() = basegfx::B2DPolyPolygon();
+                }
+
+                break;
+            }
+
+            case META_ISECTRECTCLIPREGION_ACTION :
+            {
+                const MetaISectRectClipRegionAction* pA = static_cast< const MetaISectRectClipRegionAction* >(pAction);
+                const Rectangle& rRect = pA->GetRect();
+
+                if(!rRect.IsEmpty() && aClips.size() && aClips.back().count())
+                {
+                    const basegfx::B2DRange aClipRange(
+                        rRect.Left(), rRect.Top(),
+                        rRect.Right(), rRect.Bottom());
+
+                    aClips.back() = basegfx::tools::clipPolyPolygonOnRange(
+                        aClips.back(),
+                        aClipRange,
+                        true, // inside
+                        false); // stroke
+                }
+                break;
+            }
+
+            case META_ISECTREGIONCLIPREGION_ACTION :
+            {
+                const MetaISectRegionClipRegionAction* pA = static_cast< const MetaISectRegionClipRegionAction* >(pAction);
+                const Region& rRegion = pA->GetRegion();
+
+                if(!rRegion.IsEmpty() && aClips.size() && aClips.back().count())
+                {
+                    const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon());
+
+                    aClips.back() = basegfx::tools::clipPolyPolygonOnPolyPolygon(
+                        aClips.back(),
+                        aNewClip,
+                        true,  // inside
+                        false); // stroke
+                }
+                break;
+            }
+
+            case META_MOVECLIPREGION_ACTION :
+            {
+                const MetaMoveClipRegionAction* pA = static_cast< const MetaMoveClipRegionAction* >(pAction);
+                const long aHorMove(pA->GetHorzMove());
+                const long aVerMove(pA->GetVertMove());
+
+                if((aHorMove || aVerMove) && aClips.size() && aClips.back().count())
+                {
+                    aClips.back().transform(
+                        basegfx::tools::createTranslateB2DHomMatrix(
+                            aHorMove,
+                            aVerMove));
+                }
+                break;
+            }
+
+            case META_PUSH_ACTION :
+            {
+                const MetaPushAction* pA = static_cast< const MetaPushAction* >(pAction);
+                const sal_uInt16 nFlags(pA->GetFlags());
+
+                aPushFlags.push_back(nFlags);
+
+                if(nFlags & PUSH_CLIPREGION)
+                {
+                    aClips.push_back(aClips.back());
+                }
+
+                if(nFlags & PUSH_MAPMODE)
+                {
+                    aMapModes.push_back(aMapModes.back());
+                }
+                break;
+            }
+
+            case META_POP_ACTION :
+            {
+
+                if(aPushFlags.size())
+                {
+                    const sal_uInt16 nFlags(aPushFlags.back());
+                    aPushFlags.pop_back();
+
+                    if(nFlags & PUSH_CLIPREGION)
+                    {
+                        if(aClips.size() > 1)
+                        {
+                            aClips.pop_back();
+                        }
+                        else
+                        {
+                            OSL_ENSURE(false, "Wrong POP() in ClipRegions (!)");
+                        }
+                    }
+
+                    if(nFlags & PUSH_MAPMODE)
+                    {
+                        if(aMapModes.size() > 1)
+                        {
+                            aMapModes.pop_back();
+                        }
+                        else
+                        {
+                            OSL_ENSURE(false, "Wrong POP() in MapModes (!)");
+                        }
+                    }
+                }
+                else
+                {
+                    OSL_ENSURE(false, "Invalid pop() without push() (!)");
+                }
+
+                break;
+            }
+
+            case META_MAPMODE_ACTION :
+            {
+                const MetaMapModeAction* pA = static_cast< const MetaMapModeAction* >(pAction);
+
+                aMapModes.back() = pA->GetMapMode();
+                break;
+            }
+
+            default:
+            {
+                break;
+            }
+        }
+
+        // this area contains all actions which could potentially be clipped. Since
+        // this tooling is only a fallback (see comments in header), only the needed
+        // actions will be implemented. Extend using the pattern for the already
+        // implemented actions.
+        if(aClips.size() && aClips.back().count())
+        {
+            switch(nType)
+            {
+                //
+                // pixel actions, just check on inside
+                //
+                case META_PIXEL_ACTION :
+                {
+                    const MetaPixelAction* pA = static_cast< const MetaPixelAction* >(pAction);
+                    const Point& rPoint = pA->GetPoint();
+
+                    if(!basegfx::tools::isInside(
+                        aClips.back(),
+                        basegfx::B2DPoint(rPoint.X(), rPoint.Y())))
+                    {
+                        // when not inside, do not add original
+                        bDone = true;
+                    }
+                    break;
+                }
+
+                case META_POINT_ACTION :
+                {
+                    const MetaPointAction* pA = static_cast< const MetaPointAction* >(pAction);
+                    const Point& rPoint = pA->GetPoint();
+
+                    if(!basegfx::tools::isInside(
+                        aClips.back(),
+                        basegfx::B2DPoint(rPoint.X(), rPoint.Y())))
+                    {
+                        // when not inside, do not add original
+                        bDone = true;
+                    }
+                    break;
+                }
+
+                //
+                // geometry actions
+                //
+                case META_LINE_ACTION :
+                {
+                    const MetaLineAction* pA = static_cast< const MetaLineAction* >(pAction);
+                    const Point& rStart(pA->GetStartPoint());
+                    const Point& rEnd(pA->GetEndPoint());
+                    basegfx::B2DPolygon aLine;
+
+                    aLine.append(basegfx::B2DPoint(rStart.X(), rStart.Y()));
+                    aLine.append(basegfx::B2DPoint(rEnd.X(), rEnd.Y()));
+
+                    bDone = handleGeometricContent(
+                        aClips.back(),
+                        basegfx::B2DPolyPolygon(aLine),
+                        aTarget,
+                        true); // stroke
+                    break;
+                }
+
+                case META_RECT_ACTION :
+                {
+                    const MetaRectAction* pA = static_cast< const MetaRectAction* >(pAction);
+                    const Rectangle& rRect = pA->GetRect();
+
+                    if(rRect.IsEmpty())
+                    {
+                        bDone = true;
+                    }
+                    else
+                    {
+
+                        bDone = handleGeometricContent(
+                            aClips.back(),
+                            basegfx::B2DPolyPolygon(
+                                basegfx::tools::createPolygonFromRect(
+                                    basegfx::B2DRange(
+                                        rRect.Left(), rRect.Top(),
+                                        rRect.Right(), rRect.Bottom()))),
+                            aTarget,
+                            false); // stroke
+                    }
+                    break;
+                }
+
+                case META_ROUNDRECT_ACTION :
+                {
+                    const MetaRoundRectAction* pA = static_cast< const MetaRoundRectAction* >(pAction);
+                    const Rectangle& rRect = pA->GetRect();
+
+                    if(rRect.IsEmpty())
+                    {
+                        bDone = true;
+                    }
+                    else
+                    {
+                        const sal_uInt32 nHor(pA->GetHorzRound());
+                        const sal_uInt32 nVer(pA->GetVertRound());
+                        const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom());
+                        basegfx::B2DPolygon aOutline;
+
+                        if(nHor || nVer)
+                        {
+                            double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0));
+                            double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0));
+                            fRadiusX = std::max(0.0, std::min(1.0, fRadiusX));
+                            fRadiusY = std::max(0.0, std::min(1.0, fRadiusY));
+
+                            aOutline = basegfx::tools::createPolygonFromRect(aRange, fRadiusX, fRadiusY);
+                        }
+                        else
+                        {
+                            aOutline = basegfx::tools::createPolygonFromRect(aRange);
+                        }
+
+                        bDone = handleGeometricContent(
+                            aClips.back(),
+                            basegfx::B2DPolyPolygon(aOutline),
+                            aTarget,
+                            false); // stroke
+                    }
+                    break;
+                }
+
+                case META_ELLIPSE_ACTION :
+                {
+                    const MetaEllipseAction* pA = static_cast< const MetaEllipseAction* >(pAction);
+                    const Rectangle& rRect = pA->GetRect();
+
+                    if(rRect.IsEmpty())
+                    {
+                        bDone = true;
+                    }
+                    else
+                    {
+                        const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom());
+
+                        bDone = handleGeometricContent(
+                            aClips.back(),
+                            basegfx::B2DPolyPolygon(
+                                basegfx::tools::createPolygonFromEllipse(
+                                    aRange.getCenter(),
+                                    aRange.getWidth() * 0.5,
+                                    aRange.getHeight() * 0.5)),
+                            aTarget,
+                            false); // stroke
+                    }
+                    break;
+                }
+
+                case META_ARC_ACTION :
+                {
+                    const MetaArcAction* pA = static_cast< const MetaArcAction* >(pAction);
+                    const Rectangle& rRect = pA->GetRect();
+
+                    if(rRect.IsEmpty())
+                    {
+                        bDone = true;
+                    }
+                    else
+                    {
+                        const Polygon aToolsPoly(
+                            rRect,
+                            pA->GetStartPoint(),
+                            pA->GetEndPoint(),
+                            POLY_ARC);
+
+                        bDone = handleGeometricContent(
+                            aClips.back(),
+                            basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
+                            aTarget,
+                            true); // stroke
+                    }
+                    break;
+                }
+
+                case META_PIE_ACTION :
+                {
+                    const MetaPieAction* pA = static_cast< const MetaPieAction* >(pAction);
+                    const Rectangle& rRect = pA->GetRect();
+
+                    if(rRect.IsEmpty())
+                    {
+                        bDone = true;
+                    }
+                    else
+                    {
+                        const Polygon aToolsPoly(
+                            rRect,
+                            pA->GetStartPoint(),
+                            pA->GetEndPoint(),
+                            POLY_PIE);
+
+                        bDone = handleGeometricContent(
+                            aClips.back(),
+                            basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
+                            aTarget,
+                            false); // stroke
+                    }
+                    break;
+                }
+
+                case META_CHORD_ACTION :
+                {
+                    const MetaChordAction* pA = static_cast< const MetaChordAction* >(pAction);
+                    const Rectangle& rRect = pA->GetRect();
+
+                    if(rRect.IsEmpty())
+                    {
+                        bDone = true;
+                    }
+                    else
+                    {
+                        const Polygon aToolsPoly(
+                            rRect,
+                            pA->GetStartPoint(),
+                            pA->GetEndPoint(),
+                            POLY_CHORD);
+
+                        bDone = handleGeometricContent(
+                            aClips.back(),
+                            basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
+                            aTarget,
+                            false); // stroke
+                    }
+                    break;
+                }
+
+                case META_POLYLINE_ACTION :
+                {
+                    const MetaPolyLineAction* pA = static_cast< const MetaPolyLineAction* >(pAction);
+
+                    bDone = handleGeometricContent(
+                        aClips.back(),
+                        basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()),
+                        aTarget,
+                        true); // stroke
+                    break;
+                }
+
+                case META_POLYGON_ACTION :
+                {
+                    const MetaPolygonAction* pA = static_cast< const MetaPolygonAction* >(pAction);
+
+                    bDone = handleGeometricContent(
+                        aClips.back(),
+                        basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()),
+                        aTarget,
+                        false); // stroke
+                    break;
+                }
+
+                case META_POLYPOLYGON_ACTION :
+                {
+                    const MetaPolyPolygonAction* pA = static_cast< const MetaPolyPolygonAction* >(pAction);
+                    const PolyPolygon& rPoly = pA->GetPolyPolygon();
+
+                    bDone = handleGeometricContent(
+                        aClips.back(),
+                        rPoly.getB2DPolyPolygon(),
+                        aTarget,
+                        false); // stroke
+                    break;
+                }
+
+                //
+                // bitmap actions, create BitmapEx with alpha channel derived
+                // from clipping
+                //
+                case META_BMPEX_ACTION :
+                {
+                    const MetaBmpExAction* pA = static_cast< const MetaBmpExAction* >(pAction);
+                    const BitmapEx& rBitmapEx = pA->GetBitmapEx();
+
+                    // the logical size depends on the PrefSize of the given bitmap in
+                    // combination with the current MapMode
+                    Size aLogicalSize(rBitmapEx.GetPrefSize());
+
+                    if(MAP_PIXEL == rBitmapEx.GetPrefMapMode().GetMapUnit())
+                    {
+                        aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit());
+                    }
+                    else
+                    {
+                        aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmapEx.GetPrefMapMode(), aMapModes.back().GetMapUnit());
+                    }
+
+                    bDone = handleBitmapContent(
+                        aClips.back(),
+                        pA->GetPoint(),
+                        aLogicalSize,
+                        rBitmapEx,
+                        aTarget);
+                    break;
+                }
+
+                case META_BMP_ACTION :
+                {
+                    const MetaBmpAction* pA = static_cast< const MetaBmpAction* >(pAction);
+                    const Bitmap& rBitmap = pA->GetBitmap();
+
+                    // the logical size depends on the PrefSize of the given bitmap in
+                    // combination with the current MapMode
+                    Size aLogicalSize(rBitmap.GetPrefSize());
+
+                    if(MAP_PIXEL == rBitmap.GetPrefMapMode().GetMapUnit())
+                    {
+                        aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit());
+                    }
+                    else
+                    {
+                        aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmap.GetPrefMapMode(), aMapModes.back().GetMapUnit());
+                    }
+
+                    bDone = handleBitmapContent(
+                        aClips.back(),
+                        pA->GetPoint(),
+                        aLogicalSize,
+                        BitmapEx(rBitmap),
+                        aTarget);
+                    break;
+                }
+
+                case META_BMPEXSCALE_ACTION :
+                {
+                    const MetaBmpExScaleAction* pA = static_cast< const MetaBmpExScaleAction* >(pAction);
+
+                    bDone = handleBitmapContent(
+                        aClips.back(),
+                        pA->GetPoint(),
+                        pA->GetSize(),
+                        pA->GetBitmapEx(),
+                        aTarget);
+                    break;
+                }
+
+                case META_BMPSCALE_ACTION :
+                {
+                    const MetaBmpScaleAction* pA = static_cast< const MetaBmpScaleAction* >(pAction);
+
+                    bDone = handleBitmapContent(
+                        aClips.back(),
+                        pA->GetPoint(),
+                        pA->GetSize(),
+                        BitmapEx(pA->GetBitmap()),
+                        aTarget);
+                    break;
+                }
+
+                case META_BMPEXSCALEPART_ACTION :
+                {
+                    const MetaBmpExScalePartAction* pA = static_cast< const MetaBmpExScalePartAction* >(pAction);
+                    const BitmapEx& rBitmapEx = pA->GetBitmapEx();
+
+                    if(rBitmapEx.IsEmpty())
+                    {
+                        // empty content
+                        bDone = true;
+                    }
+                    else
+                    {
+                        BitmapEx aCroppedBitmapEx(rBitmapEx);
+                        const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
+
+                        if(aCropRectangle.IsEmpty())
+                        {
+                            // empty content
+                            bDone = true;
+                        }
+                        else
+                        {
+                            aCroppedBitmapEx.Crop(aCropRectangle);
+                            bDone = handleBitmapContent(
+                                aClips.back(),
+                                pA->GetDestPoint(),
+                                pA->GetDestSize(),
+                                aCroppedBitmapEx,
+                                aTarget);
+                        }
+                    }
+                    break;
+                }
+
+                case META_BMPSCALEPART_ACTION :
+                {
+                    const MetaBmpScalePartAction* pA = static_cast< const MetaBmpScalePartAction* >(pAction);
+                    const Bitmap& rBitmap = pA->GetBitmap();
+
+                    if(rBitmap.IsEmpty())
+                    {
+                        // empty content
+                        bDone = true;
+                    }
+                    else
+                    {
+                        Bitmap aCroppedBitmap(rBitmap);
+                        const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
+
+                        if(aCropRectangle.IsEmpty())
+                        {
+                            // empty content
+                            bDone = true;
+                        }
+                        else
+                        {
+                            aCroppedBitmap.Crop(aCropRectangle);
+                            bDone = handleBitmapContent(
+                                aClips.back(),
+                                pA->GetDestPoint(),
+                                pA->GetDestSize(),
+                                BitmapEx(aCroppedBitmap),
+                                aTarget);
+                        }
+                    }
+                    break;
+                }
+
+                //
+                // need to handle all those 'hacks' which hide data in comments
+                //
+                case META_COMMENT_ACTION :
+                {
+                    const MetaCommentAction* pA = static_cast< const MetaCommentAction* >(pAction);
+                    const OString& rComment = pA->GetComment();
+
+                    if(rComment.equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
+                    {
+                        // nothing to do; this just means that between here and XGRAD_SEQ_END
+                        // exists a META_GRADIENTEX_ACTION mixed with Xor-tricked painiting
+                        // commands. This comment is used to scan over these and filter for
+                        // the gradient action. It is needed to support META_GRADIENTEX_ACTION
+                        // in this processor to solve usages.
+                    }
+                    else if(rComment.equalsIgnoreAsciiCase("XPATHFILL_SEQ_BEGIN"))
+                    {
+                        SvtGraphicFill aFilling;
+                        PolyPolygon aPath;
+
+                        {   // read SvtGraphicFill
+                            SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ);
+                            aMemStm >> aFilling;
+                        }
+
+                        aFilling.getPath(aPath);
+
+                        if(aPath.Count())
+                        {
+                            const basegfx::B2DPolyPolygon aSource(aPath.getB2DPolyPolygon());
+                            const basegfx::B2DPolyPolygon aResult(
+                                basegfx::tools::clipPolyPolygonOnPolyPolygon(
+                                    aSource,
+                                    aClips.back(),
+                                    true, // inside
+                                    false)); // stroke
+
+                            if(aResult.count())
+                            {
+                                if(aResult != aSource)
+                                {
+                                    // add clipped geometry
+                                    aFilling.setPath(PolyPolygon(aResult));
+                                    addSvtGraphicFill(aFilling, aTarget);
+                                    bDone = true;
+                                }
+                            }
+                            else
+                            {
+                                // exchange with empty polygon
+                                aFilling.setPath(PolyPolygon());
+                                addSvtGraphicFill(aFilling, aTarget);
+                                bDone = true;
+                            }
+                        }
+                    }
+                    else if(rComment.equalsIgnoreAsciiCase("XPATHSTROKE_SEQ_BEGIN"))
+                    {
+                        SvtGraphicStroke aStroke;
+                        Polygon aPath;
+
+                        {   // read SvtGraphicFill
+                            SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ);
+                            aMemStm >> aStroke;
+                        }
+
+                        aStroke.getPath(aPath);
+
+                        if(aPath.GetSize())
+                        {
+                            const basegfx::B2DPolygon aSource(aPath.getB2DPolygon());
+                            const basegfx::B2DPolyPolygon aResult(
+                                basegfx::tools::clipPolygonOnPolyPolygon(
+                                    aSource,
+                                    aClips.back(),
+                                    true, // inside
+                                    true)); // stroke
+
+                            if(aResult.count())
+                            {
+                                if(aResult.count() > 1 || aResult.getB2DPolygon(0) != aSource)
+                                {
+                                    // add clipped geometry
+                                    for(sal_uInt32 a(0); a < aResult.count(); a++)
+                                    {
+                                        aStroke.setPath(Polygon(aResult.getB2DPolygon(a)));
+                                        addSvtGraphicStroke(aStroke, aTarget);
+                                    }
+
+                                    bDone = true;
+                                }
+                            }
+                            else
+                            {
+                                // exchange with empty polygon
+                                aStroke.setPath(Polygon());
+                                addSvtGraphicStroke(aStroke, aTarget);
+                                bDone = true;
+                            }
+
+                        }
+                    }
+                    break;
+                }
+
+                //
+                // need to handle gradient fills (hopefully only unroated ones)
+                //
+
+                case META_GRADIENT_ACTION :
+                {
+                    const MetaGradientAction* pA = static_cast< const MetaGradientAction* >(pAction);
+                    const Rectangle& rRect = pA->GetRect();
+
+                    if(rRect.IsEmpty())
+                    {
+                        bDone = true;
+                    }
+                    else
+                    {
+                        bDone = handleGradientContent(
+                            aClips.back(),
+                            basegfx::B2DPolyPolygon(
+                                basegfx::tools::createPolygonFromRect(
+                                    basegfx::B2DRange(
+                                        rRect.Left(), rRect.Top(),
+                                        rRect.Right(), rRect.Bottom()))),
+                            pA->GetGradient(),
+                            aTarget);
+                    }
+
+
+                    break;
+                }
+
+                case META_GRADIENTEX_ACTION :
+                {
+                    const MetaGradientExAction* pA = static_cast< const MetaGradientExAction* >(pAction);
+                    const PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
+
+                    bDone = handleGradientContent(
+                        aClips.back(),
+                        rPolyPoly.getB2DPolyPolygon(),
+                        pA->GetGradient(),
+                        aTarget);
+                    break;
+                }
+
+                // not (yet) supported actions
+                //
+                // META_NULL_ACTION
+                // META_TEXT_ACTION
+                // META_TEXTARRAY_ACTION
+                // META_STRETCHTEXT_ACTION
+                // META_TEXTRECT_ACTION
+                // META_MASK_ACTION
+                // META_MASKSCALE_ACTION
+                // META_MASKSCALEPART_ACTION
+                // META_HATCH_ACTION
+                // META_WALLPAPER_ACTION
+                // META_FILLCOLOR_ACTION
+                // META_TEXTCOLOR_ACTION
+                // META_TEXTFILLCOLOR_ACTION
+                // META_TEXTALIGN_ACTION
+                // META_MAPMODE_ACTION
+                // META_FONT_ACTION
+                // META_TRANSPARENT_ACTION
+                // META_EPS_ACTION
+                // META_REFPOINT_ACTION
+                // META_TEXTLINECOLOR_ACTION
+                // META_TEXTLINE_ACTION
+                // META_FLOATTRANSPARENT_ACTION
+                // META_LAYOUTMODE_ACTION
+                // META_TEXTLANGUAGE_ACTION
+                // META_OVERLINECOLOR_ACTION
+
+                // if an action is not handled at all, it will simply get copied to the
+                // target (see below). This is the default for all non-implemented actions
+                default:
+                {
+                    break;
+                }
+            }
+        }
+
+        if(bDone)
+        {
+            bChanged = true;
+        }
+        else
+        {
+            const_cast< MetaAction* >(pAction)->Duplicate();
+            aTarget.AddAction(const_cast< MetaAction* >(pAction));
+        }
+    }
+
+    if(bChanged)
+    {
+        // when changed, copy back and do not forget to set MapMode
+        // and PrefSize
+        aTarget.SetPrefMapMode(rSource.GetPrefMapMode());
+        aTarget.SetPrefSize(rSource.GetPrefSize());
+        rSource = aTarget;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool VCL_DLLPUBLIC usesClipActions(const GDIMetaFile& rSource)
+{
+    const sal_uLong nObjCount(rSource.GetActionSize());
+
+    for(sal_uLong i(0); i < nObjCount; ++i)
+    {
+        const MetaAction* pAction(rSource.GetAction(i));
+        const sal_uInt16 nType(pAction->GetType());
+
+        switch(nType)
+        {
+            case META_CLIPREGION_ACTION :
+            case META_ISECTRECTCLIPREGION_ACTION :
+            case META_ISECTREGIONCLIPREGION_ACTION :
+            case META_MOVECLIPREGION_ACTION :
+            {
+                return true;
+                break;
+            }
+
+            default: break;
+        }
+    }
+
+    return false;
+}
+


More information about the Libreoffice-commits mailing list