summaryrefslogtreecommitdiff
path: root/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'drawinglayer/source/primitive2d/metafileprimitive2d.cxx')
-rw-r--r--drawinglayer/source/primitive2d/metafileprimitive2d.cxx3259
1 files changed, 3259 insertions, 0 deletions
diff --git a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
new file mode 100644
index 000000000000..5d3e5181855d
--- /dev/null
+++ b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
@@ -0,0 +1,3259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_drawinglayer.hxx"
+
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <basegfx/tools/canvastools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <vcl/lineinfo.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <vcl/metaact.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <vcl/salbtype.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <vcl/svapp.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <drawinglayer/primitive2d/invertprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillbitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/wallpaperprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <i18npool/mslangid.hxx>
+#include <drawinglayer/primitive2d/textlineprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx>
+#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
+#include <numeric>
+
+//////////////////////////////////////////////////////////////////////////////
+
+using namespace com::sun::star;
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+ /** helper class for graphic context
+
+ This class allows to hold a complete status of classic
+ VCL OutputDevice stati. This data is needed for correct
+ interpretation of the MetaFile action flow.
+ */
+ class PropertyHolder
+ {
+ private:
+ /// current transformation (aka MapMode)
+ basegfx::B2DHomMatrix maTransformation;
+ MapUnit maMapUnit;
+
+ /// current colors
+ basegfx::BColor maLineColor;
+ basegfx::BColor maFillColor;
+ basegfx::BColor maTextColor;
+ basegfx::BColor maTextFillColor;
+ basegfx::BColor maTextLineColor;
+ basegfx::BColor maOverlineColor;
+
+ /// clipping
+ basegfx::B2DPolyPolygon maClipPolyPoygon;
+
+ /// font, etc.
+ Font maFont;
+ RasterOp maRasterOp;
+ sal_uInt32 mnLayoutMode;
+ LanguageType maLanguageType;
+ sal_uInt16 mnPushFlags;
+
+ /// bitfield
+ /// contains all active markers
+ bool mbLineColor : 1;
+ bool mbFillColor : 1;
+ bool mbTextColor : 1;
+ bool mbTextFillColor : 1;
+ bool mbTextLineColor : 1;
+ bool mbOverlineColor : 1;
+ bool mbClipPolyPolygonActive : 1;
+
+ public:
+ PropertyHolder()
+ : maTransformation(),
+ maMapUnit(MAP_100TH_MM),
+ maLineColor(),
+ maFillColor(),
+ maTextColor(COL_BLACK),
+ maTextFillColor(),
+ maTextLineColor(),
+ maOverlineColor(),
+ maClipPolyPoygon(),
+ maFont(),
+ maRasterOp(ROP_OVERPAINT),
+ mnLayoutMode(0),
+ maLanguageType(0),
+ mnPushFlags(0),
+ mbLineColor(false),
+ mbFillColor(false),
+ mbTextColor(true),
+ mbTextFillColor(false),
+ mbTextLineColor(false),
+ mbOverlineColor(false),
+ mbClipPolyPolygonActive(false)
+ {
+ }
+
+ ~PropertyHolder()
+ {
+ }
+
+ /// read/write accesses
+ const basegfx::B2DHomMatrix& getTransformation() const { return maTransformation; }
+ void setTransformation(const basegfx::B2DHomMatrix& rNew) { if(rNew != maTransformation) maTransformation = rNew; }
+
+ MapUnit getMapUnit() const { return maMapUnit; }
+ void setMapUnit(MapUnit eNew) { if(eNew != maMapUnit) maMapUnit = eNew; }
+
+ const basegfx::BColor& getLineColor() const { return maLineColor; }
+ void setLineColor(const basegfx::BColor& rNew) { if(rNew != maLineColor) maLineColor = rNew; }
+ bool getLineColorActive() const { return mbLineColor; }
+ void setLineColorActive(bool bNew) { if(bNew != mbLineColor) mbLineColor = bNew; }
+
+ const basegfx::BColor& getFillColor() const { return maFillColor; }
+ void setFillColor(const basegfx::BColor& rNew) { if(rNew != maFillColor) maFillColor = rNew; }
+ bool getFillColorActive() const { return mbFillColor; }
+ void setFillColorActive(bool bNew) { if(bNew != mbFillColor) mbFillColor = bNew; }
+
+ const basegfx::BColor& getTextColor() const { return maTextColor; }
+ void setTextColor(const basegfx::BColor& rNew) { if(rNew != maTextColor) maTextColor = rNew; }
+ bool getTextColorActive() const { return mbTextColor; }
+ void setTextColorActive(bool bNew) { if(bNew != mbTextColor) mbTextColor = bNew; }
+
+ const basegfx::BColor& getTextFillColor() const { return maTextFillColor; }
+ void setTextFillColor(const basegfx::BColor& rNew) { if(rNew != maTextFillColor) maTextFillColor = rNew; }
+ bool getTextFillColorActive() const { return mbTextFillColor; }
+ void setTextFillColorActive(bool bNew) { if(bNew != mbTextFillColor) mbTextFillColor = bNew; }
+
+ const basegfx::BColor& getTextLineColor() const { return maTextLineColor; }
+ void setTextLineColor(const basegfx::BColor& rNew) { if(rNew != maTextLineColor) maTextLineColor = rNew; }
+ bool getTextLineColorActive() const { return mbTextLineColor; }
+ void setTextLineColorActive(bool bNew) { if(bNew != mbTextLineColor) mbTextLineColor = bNew; }
+
+ const basegfx::BColor& getOverlineColor() const { return maOverlineColor; }
+ void setOverlineColor(const basegfx::BColor& rNew) { if(rNew != maOverlineColor) maOverlineColor = rNew; }
+ bool getOverlineColorActive() const { return mbOverlineColor; }
+ void setOverlineColorActive(bool bNew) { if(bNew != mbOverlineColor) mbOverlineColor = bNew; }
+
+ const basegfx::B2DPolyPolygon& getClipPolyPolygon() const { return maClipPolyPoygon; }
+ void setClipPolyPolygon(const basegfx::B2DPolyPolygon& rNew) { if(rNew != maClipPolyPoygon) maClipPolyPoygon = rNew; }
+ bool getClipPolyPolygonActive() const { return mbClipPolyPolygonActive; }
+ void setClipPolyPolygonActive(bool bNew) { if(bNew != mbClipPolyPolygonActive) mbClipPolyPolygonActive = bNew; }
+
+ const Font& getFont() const { return maFont; }
+ void setFont(const Font& rFont) { if(rFont != maFont) maFont = rFont; }
+
+ const RasterOp& getRasterOp() const { return maRasterOp; }
+ void setRasterOp(const RasterOp& rRasterOp) { if(rRasterOp != maRasterOp) maRasterOp = rRasterOp; }
+ bool isRasterOpInvert() const { return (ROP_XOR == maRasterOp || ROP_INVERT == maRasterOp); }
+ bool isRasterOpForceBlack() const { return ROP_0 == maRasterOp; }
+ bool isRasterOpActive() const { return isRasterOpInvert() || isRasterOpForceBlack(); }
+
+ sal_uInt32 getLayoutMode() const { return mnLayoutMode; }
+ void setLayoutMode(sal_uInt32 nNew) { if(nNew != mnLayoutMode) mnLayoutMode = nNew; }
+
+ LanguageType getLanguageType() const { return maLanguageType; }
+ void setLanguageType(LanguageType aNew) { if(aNew != maLanguageType) maLanguageType = aNew; }
+
+ sal_uInt16 getPushFlags() const { return mnPushFlags; }
+ void setPushFlags(sal_uInt16 nNew) { if(nNew != mnPushFlags) mnPushFlags = nNew; }
+
+ bool getLineOrFillActive() const { return (mbLineColor || mbFillColor); }
+ };
+} // end of anonymous namespace
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+ /** stack for properites
+
+ This class builds a stack based on the PropertyHolder
+ class. It encapsulates the pointer/new/delete usage to
+ make it safe and implements the push/pop as needed by a
+ VCL Metafile interpreter. The critical part here are the
+ flag values VCL OutputDevice uses here; not all stuff is
+ pushed and thus needs to be copied at pop.
+ */
+ class PropertyHolders
+ {
+ private:
+ std::vector< PropertyHolder* > maPropertyHolders;
+
+ public:
+ PropertyHolders()
+ {
+ maPropertyHolders.push_back(new PropertyHolder());
+ }
+
+ sal_uInt32 size()
+ {
+ return maPropertyHolders.size();
+ }
+
+ void PushDefault()
+ {
+ PropertyHolder* pNew = new PropertyHolder();
+ maPropertyHolders.push_back(pNew);
+ }
+
+ void Push(sal_uInt16 nPushFlags)
+ {
+ if(nPushFlags)
+ {
+ OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: PUSH with no property holders (!)");
+ if ( !maPropertyHolders.empty() )
+ {
+ PropertyHolder* pNew = new PropertyHolder(*maPropertyHolders.back());
+ pNew->setPushFlags(nPushFlags);
+ maPropertyHolders.push_back(pNew);
+ }
+ }
+ }
+
+ void Pop()
+ {
+ OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: POP with no property holders (!)");
+ const sal_uInt32 nSize(maPropertyHolders.size());
+
+ if(nSize)
+ {
+ const PropertyHolder* pTip = maPropertyHolders.back();
+ const sal_uInt16 nPushFlags(pTip->getPushFlags());
+
+ if(nPushFlags)
+ {
+ if(nSize > 1)
+ {
+ // copy back content for all non-set flags
+ PropertyHolder* pLast = maPropertyHolders[nSize - 2];
+
+ if(PUSH_ALL != nPushFlags)
+ {
+ if(!(nPushFlags & PUSH_LINECOLOR ))
+ {
+ pLast->setLineColor(pTip->getLineColor());
+ pLast->setLineColorActive(pTip->getLineColorActive());
+ }
+ if(!(nPushFlags & PUSH_FILLCOLOR ))
+ {
+ pLast->setFillColor(pTip->getFillColor());
+ pLast->setFillColorActive(pTip->getFillColorActive());
+ }
+ if(!(nPushFlags & PUSH_FONT ))
+ {
+ pLast->setFont(pTip->getFont());
+ }
+ if(!(nPushFlags & PUSH_TEXTCOLOR ))
+ {
+ pLast->setTextColor(pTip->getTextColor());
+ pLast->setTextColorActive(pTip->getTextColorActive());
+ }
+ if(!(nPushFlags & PUSH_MAPMODE ))
+ {
+ pLast->setTransformation(pTip->getTransformation());
+ pLast->setMapUnit(pTip->getMapUnit());
+ }
+ if(!(nPushFlags & PUSH_CLIPREGION ))
+ {
+ pLast->setClipPolyPolygon(pTip->getClipPolyPolygon());
+ pLast->setClipPolyPolygonActive(pTip->getClipPolyPolygonActive());
+ }
+ if(!(nPushFlags & PUSH_RASTEROP ))
+ {
+ pLast->setRasterOp(pTip->getRasterOp());
+ }
+ if(!(nPushFlags & PUSH_TEXTFILLCOLOR ))
+ {
+ pLast->setTextFillColor(pTip->getTextFillColor());
+ pLast->setTextFillColorActive(pTip->getTextFillColorActive());
+ }
+ if(!(nPushFlags & PUSH_TEXTALIGN ))
+ {
+ if(pLast->getFont().GetAlign() != pTip->getFont().GetAlign())
+ {
+ Font aFont(pLast->getFont());
+ aFont.SetAlign(pTip->getFont().GetAlign());
+ pLast->setFont(aFont);
+ }
+ }
+ if(!(nPushFlags & PUSH_REFPOINT ))
+ {
+ // not supported
+ }
+ if(!(nPushFlags & PUSH_TEXTLINECOLOR ))
+ {
+ pLast->setTextLineColor(pTip->getTextLineColor());
+ pLast->setTextLineColorActive(pTip->getTextLineColorActive());
+ }
+ if(!(nPushFlags & PUSH_TEXTLAYOUTMODE ))
+ {
+ pLast->setLayoutMode(pTip->getLayoutMode());
+ }
+ if(!(nPushFlags & PUSH_TEXTLANGUAGE ))
+ {
+ pLast->setLanguageType(pTip->getLanguageType());
+ }
+ if(!(nPushFlags & PUSH_OVERLINECOLOR ))
+ {
+ pLast->setOverlineColor(pTip->getOverlineColor());
+ pLast->setOverlineColorActive(pTip->getOverlineColorActive());
+ }
+ }
+ }
+ }
+
+ // execute the pop
+ delete maPropertyHolders.back();
+ maPropertyHolders.pop_back();
+ }
+ }
+
+ PropertyHolder& Current()
+ {
+ static PropertyHolder aDummy;
+ OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: CURRENT with no property holders (!)");
+ return maPropertyHolders.empty() ? aDummy : *maPropertyHolders.back();
+ }
+
+ ~PropertyHolders()
+ {
+ while(maPropertyHolders.size())
+ {
+ delete maPropertyHolders.back();
+ maPropertyHolders.pop_back();
+ }
+ }
+ };
+} // end of anonymous namespace
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+ /** helper to convert a Region to a B2DPolyPolygon
+ when it does not yet contain one. In the future
+ this may be expanded to merge the polygons created
+ from rectangles or use a special algo to directly turn
+ the spans of regions to a single, already merged
+ PolyPolygon.
+ */
+ basegfx::B2DPolyPolygon getB2DPolyPolygonFromRegion(const Region& rRegion)
+ {
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(!rRegion.IsEmpty())
+ {
+ Region aRegion(rRegion);
+ aRetval = aRegion.GetB2DPolyPolygon();
+
+ if(!aRetval.count())
+ {
+ RegionHandle aRegionHandle(aRegion.BeginEnumRects());
+ Rectangle aRegionRectangle;
+
+ while(aRegion.GetEnumRects(aRegionHandle, aRegionRectangle))
+ {
+ if(!aRegionRectangle.IsEmpty())
+ {
+ const basegfx::B2DRange aRegionRange(
+ aRegionRectangle.Left(), aRegionRectangle.Top(),
+ aRegionRectangle.Right(), aRegionRectangle.Bottom());
+ aRetval.append(basegfx::tools::createPolygonFromRect(aRegionRange));
+ }
+ }
+
+ aRegion.EndEnumRects(aRegionHandle);
+ }
+ }
+
+ return aRetval;
+ }
+} // end of anonymous namespace
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+ /** Helper class to buffer and hold a Primive target vector. It
+ encapsulates the new/delete functionality and aloows to work
+ on pointers of the implementation classes. All data will
+ be converted to uno sequences of uno references when accessing the
+ data.
+ */
+ class TargetHolder
+ {
+ private:
+ std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargets;
+
+ public:
+ TargetHolder()
+ : aTargets()
+ {
+ }
+
+ ~TargetHolder()
+ {
+ const sal_uInt32 nCount(aTargets.size());
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ delete aTargets[a];
+ }
+ }
+
+ sal_uInt32 size()
+ {
+ return aTargets.size();
+ }
+
+ void append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate)
+ {
+ if(pCandidate)
+ {
+ aTargets.push_back(pCandidate);
+ }
+ }
+
+ drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence(const PropertyHolder& rPropertyHolder)
+ {
+ const sal_uInt32 nCount(aTargets.size());
+ drawinglayer::primitive2d::Primitive2DSequence xRetval(nCount);
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ xRetval[a] = aTargets[a];
+ }
+
+ // All Targets were pointers, but do not need to be deleted since they
+ // were converted to UNO API references now, so they stay as long as
+ // referenced. Do NOT delete the C++ implementation classes here, but clear
+ // the buffer to not delete them in the destructor.
+ aTargets.clear();
+
+ if(xRetval.hasElements() && rPropertyHolder.getClipPolyPolygonActive())
+ {
+ const basegfx::B2DPolyPolygon& rClipPolyPolygon = rPropertyHolder.getClipPolyPolygon();
+
+ if(rClipPolyPolygon.count())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xMask(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ rClipPolyPolygon,
+ xRetval));
+
+ xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
+ }
+ }
+
+ return xRetval;
+ }
+ };
+} // end of anonymous namespace
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+ /** Helper class which builds a stack on the TargetHolder class */
+ class TargetHolders
+ {
+ private:
+ std::vector< TargetHolder* > maTargetHolders;
+
+ public:
+ TargetHolders()
+ {
+ maTargetHolders.push_back(new TargetHolder());
+ }
+
+ sal_uInt32 size()
+ {
+ return maTargetHolders.size();
+ }
+
+ void Push()
+ {
+ maTargetHolders.push_back(new TargetHolder());
+ }
+
+ void Pop()
+ {
+ OSL_ENSURE(maTargetHolders.size(), "TargetHolders: POP with no property holders (!)");
+ if(maTargetHolders.size())
+ {
+ delete maTargetHolders.back();
+ maTargetHolders.pop_back();
+ }
+ }
+
+ TargetHolder& Current()
+ {
+ OSL_ENSURE(maTargetHolders.size(), "TargetHolders: CURRENT with no property holders (!)");
+ return *maTargetHolders.back();
+ }
+
+ ~TargetHolders()
+ {
+ while(maTargetHolders.size())
+ {
+ delete maTargetHolders.back();
+ maTargetHolders.pop_back();
+ }
+ }
+ };
+} // end of anonymous namespace
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace drawinglayer
+{
+ namespace primitive2d
+ {
+ /** NonOverlappingFillGradientPrimitive2D class
+
+ This is a special version of the FillGradientPrimitive2D which decomposes
+ to a non-overlapping geometry version of the gradient. This needs to be
+ used to support the old XOR paint-'trick'.
+
+ It does not need an own identifier since a renderer who wants to interpret
+ it itself may do so. It just overloads the decomposition of the C++
+ implementation class to do an alternative decomposition.
+ */
+ class NonOverlappingFillGradientPrimitive2D : public FillGradientPrimitive2D
+ {
+ protected:
+ /// local decomposition.
+ virtual Primitive2DSequence create2DDecomposition(
+ const geometry::ViewInformation2D& rViewInformation) const;
+
+ public:
+ /// constructor
+ NonOverlappingFillGradientPrimitive2D(
+ const basegfx::B2DRange& rObjectRange,
+ const attribute::FillGradientAttribute& rFillGradient)
+ : FillGradientPrimitive2D(rObjectRange, rFillGradient)
+ {
+ }
+ };
+
+ Primitive2DSequence NonOverlappingFillGradientPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(!getFillGradient().isDefault())
+ {
+ return createFill(false);
+ }
+ else
+ {
+ return Primitive2DSequence();
+ }
+ }
+ } // end of namespace primitive2d
+} // end of namespace drawinglayer
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+ /** helper to convert a MapMode to a transformation */
+ basegfx::B2DHomMatrix getTransformFromMapMode(const MapMode& rMapMode)
+ {
+ basegfx::B2DHomMatrix aMapping;
+ const Fraction aNoScale(1, 1);
+ const Point& rOrigin(rMapMode.GetOrigin());
+
+ if(0 != rOrigin.X() || 0 != rOrigin.Y())
+ {
+ aMapping.translate(rOrigin.X(), rOrigin.Y());
+ }
+
+ if(rMapMode.GetScaleX() != aNoScale || rMapMode.GetScaleY() != aNoScale)
+ {
+ aMapping.scale(
+ double(rMapMode.GetScaleX()),
+ double(rMapMode.GetScaleY()));
+ }
+
+ return aMapping;
+ }
+
+ /** helper to create a PointArrayPrimitive2D based on current context */
+ void createPointArrayPrimitive(
+ const std::vector< basegfx::B2DPoint >& rPositions,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperties,
+ basegfx::BColor aBColor)
+ {
+ if(rPositions.size())
+ {
+ if(rProperties.getTransformation().isIdentity())
+ {
+ rTarget.append(
+ new drawinglayer::primitive2d::PointArrayPrimitive2D(
+ rPositions,
+ aBColor));
+ }
+ else
+ {
+ std::vector< basegfx::B2DPoint > aPositions(rPositions);
+
+ for(sal_uInt32 a(0); a < aPositions.size(); a++)
+ {
+ aPositions[a] = rProperties.getTransformation() * aPositions[a];
+ }
+
+ rTarget.append(
+ new drawinglayer::primitive2d::PointArrayPrimitive2D(
+ aPositions,
+ aBColor));
+ }
+ }
+ }
+
+ /** helper to create a PolygonHairlinePrimitive2D based on current context */
+ void createHairlinePrimitive(
+ const basegfx::B2DPolygon& rLinePolygon,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperties)
+ {
+ if(rLinePolygon.count())
+ {
+ basegfx::B2DPolygon aLinePolygon(rLinePolygon);
+ aLinePolygon.transform(rProperties.getTransformation());
+ rTarget.append(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aLinePolygon,
+ rProperties.getLineColor()));
+ }
+ }
+
+ /** helper to create a PolyPolygonColorPrimitive2D based on current context */
+ void createFillPrimitive(
+ const basegfx::B2DPolyPolygon& rFillPolyPolygon,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperties)
+ {
+ if(rFillPolyPolygon.count())
+ {
+ basegfx::B2DPolyPolygon aFillPolyPolygon(rFillPolyPolygon);
+ aFillPolyPolygon.transform(rProperties.getTransformation());
+ rTarget.append(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ aFillPolyPolygon,
+ rProperties.getFillColor()));
+ }
+ }
+
+ /** helper to create a PolygonStrokePrimitive2D based on current context */
+ void createLinePrimitive(
+ const basegfx::B2DPolygon& rLinePolygon,
+ const LineInfo& rLineInfo,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperties)
+ {
+ if(rLinePolygon.count())
+ {
+ const bool bDashDotUsed(LINE_DASH == rLineInfo.GetStyle());
+ const bool bWidthUsed(rLineInfo.GetWidth() > 1);
+
+ if(bDashDotUsed || bWidthUsed)
+ {
+ basegfx::B2DPolygon aLinePolygon(rLinePolygon);
+ aLinePolygon.transform(rProperties.getTransformation());
+ const drawinglayer::attribute::LineAttribute aLineAttribute(
+ rProperties.getLineColor(),
+ bWidthUsed ? rLineInfo.GetWidth() : 0.0,
+ rLineInfo.GetLineJoin());
+
+ if(bDashDotUsed)
+ {
+ ::std::vector< double > fDotDashArray;
+ const double fDashLen(rLineInfo.GetDashLen());
+ const double fDotLen(rLineInfo.GetDotLen());
+ const double fDistance(rLineInfo.GetDistance());
+
+ for(sal_uInt16 a(0); a < rLineInfo.GetDashCount(); a++)
+ {
+ fDotDashArray.push_back(fDashLen);
+ fDotDashArray.push_back(fDistance);
+ }
+
+ for(sal_uInt16 b(0); b < rLineInfo.GetDotCount(); b++)
+ {
+ fDotDashArray.push_back(fDotLen);
+ fDotDashArray.push_back(fDistance);
+ }
+
+ const double fAccumulated(::std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0));
+ const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(
+ fDotDashArray,
+ fAccumulated);
+
+ rTarget.append(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ aLinePolygon,
+ aLineAttribute,
+ aStrokeAttribute));
+ }
+ else
+ {
+ rTarget.append(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ aLinePolygon,
+ aLineAttribute));
+ }
+ }
+ else
+ {
+ createHairlinePrimitive(rLinePolygon, rTarget, rProperties);
+ }
+ }
+ }
+
+ /** helper to create needed line and fill primitives based on current context */
+ void createHairlineAndFillPrimitive(
+ const basegfx::B2DPolygon& rPolygon,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperties)
+ {
+ if(rProperties.getFillColorActive())
+ {
+ createFillPrimitive(basegfx::B2DPolyPolygon(rPolygon), rTarget, rProperties);
+ }
+
+ if(rProperties.getLineColorActive())
+ {
+ createHairlinePrimitive(rPolygon, rTarget, rProperties);
+ }
+ }
+
+ /** helper to create needed line and fill primitives based on current context */
+ void createHairlineAndFillPrimitive(
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperties)
+ {
+ if(rProperties.getFillColorActive())
+ {
+ createFillPrimitive(rPolyPolygon, rTarget, rProperties);
+ }
+
+ if(rProperties.getLineColorActive())
+ {
+ for(sal_uInt32 a(0); a < rPolyPolygon.count(); a++)
+ {
+ createHairlinePrimitive(rPolyPolygon.getB2DPolygon(a), rTarget, rProperties);
+ }
+ }
+ }
+
+ /** helper to create DiscreteBitmapPrimitive2D based on current context.
+ The DiscreteBitmapPrimitive2D is especially created for this usage
+ since no other usage defines a bitmap visualisation based on top-left
+ position and size in pixels. At the end it will create a view-dependent
+ transformed embedding of a BitmapPrimitive2D.
+ */
+ void createBitmapExPrimitive(
+ const BitmapEx& rBitmapEx,
+ const Point& rPoint,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperties)
+ {
+ if(!rBitmapEx.IsEmpty())
+ {
+ basegfx::B2DPoint aPoint(rPoint.X(), rPoint.Y());
+ aPoint = rProperties.getTransformation() * aPoint;
+
+ rTarget.append(
+ new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D(
+ rBitmapEx,
+ aPoint));
+ }
+ }
+
+ /** helper to create BitmapPrimitive2D based on current context */
+ void createBitmapExPrimitive(
+ const BitmapEx& rBitmapEx,
+ const Point& rPoint,
+ const Size& rSize,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperties)
+ {
+ if(!rBitmapEx.IsEmpty())
+ {
+ basegfx::B2DHomMatrix aObjectTransform;
+
+ aObjectTransform.set(0, 0, rSize.Width());
+ aObjectTransform.set(1, 1, rSize.Height());
+ aObjectTransform.set(0, 2, rPoint.X());
+ aObjectTransform.set(1, 2, rPoint.Y());
+
+ aObjectTransform = rProperties.getTransformation() * aObjectTransform;
+
+ rTarget.append(
+ new drawinglayer::primitive2d::BitmapPrimitive2D(
+ rBitmapEx,
+ aObjectTransform));
+ }
+ }
+
+ /** helper to create a regular BotmapEx from a MaskAction (definitions
+ which use a bitmap without transparence but define one of the colors as
+ transparent)
+ */
+ BitmapEx createMaskBmpEx(const Bitmap& rBitmap, const Color& rMaskColor)
+ {
+ const Color aWhite(COL_WHITE);
+ BitmapPalette aBiLevelPalette(2);
+
+ aBiLevelPalette[0] = aWhite;
+ aBiLevelPalette[1] = rMaskColor;
+
+ Bitmap aMask(rBitmap.CreateMask(aWhite));
+ Bitmap aSolid(rBitmap.GetSizePixel(), 1, &aBiLevelPalette);
+
+ aSolid.Erase(rMaskColor);
+
+ return BitmapEx(aSolid, aMask);
+ }
+
+ /** helper to convert from a VCL Gradient definition to the corresponding
+ data for primitive representation
+ */
+ drawinglayer::attribute::FillGradientAttribute createFillGradientAttribute(const Gradient& rGradient)
+ {
+ const Color aStartColor(rGradient.GetStartColor());
+ const sal_uInt16 nStartIntens(rGradient.GetStartIntensity());
+ basegfx::BColor aStart(aStartColor.getBColor());
+
+ if(nStartIntens != 100)
+ {
+ const basegfx::BColor aBlack;
+ aStart = interpolate(aBlack, aStart, (double)nStartIntens * 0.01);
+ }
+
+ const Color aEndColor(rGradient.GetEndColor());
+ const sal_uInt16 nEndIntens(rGradient.GetEndIntensity());
+ basegfx::BColor aEnd(aEndColor.getBColor());
+
+ if(nEndIntens != 100)
+ {
+ const basegfx::BColor aBlack;
+ aEnd = interpolate(aBlack, aEnd, (double)nEndIntens * 0.01);
+ }
+
+ drawinglayer::attribute::GradientStyle aGradientStyle(drawinglayer::attribute::GRADIENTSTYLE_RECT);
+
+ switch(rGradient.GetStyle())
+ {
+ case GRADIENT_LINEAR :
+ {
+ aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_LINEAR;
+ break;
+ }
+ case GRADIENT_AXIAL :
+ {
+ aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_AXIAL;
+ break;
+ }
+ case GRADIENT_RADIAL :
+ {
+ aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_RADIAL;
+ break;
+ }
+ case GRADIENT_ELLIPTICAL :
+ {
+ aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_ELLIPTICAL;
+ break;
+ }
+ case GRADIENT_SQUARE :
+ {
+ aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_SQUARE;
+ break;
+ }
+ default : // GRADIENT_RECT
+ {
+ aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_RECT;
+ break;
+ }
+ }
+
+ return drawinglayer::attribute::FillGradientAttribute(
+ aGradientStyle,
+ (double)rGradient.GetBorder() * 0.01,
+ (double)rGradient.GetOfsX() * 0.01,
+ (double)rGradient.GetOfsY() * 0.01,
+ (double)rGradient.GetAngle() * F_PI1800,
+ aStart,
+ aEnd,
+ rGradient.GetSteps());
+ }
+
+ /** helper to convert from a VCL Hatch definition to the corresponding
+ data for primitive representation
+ */
+ drawinglayer::attribute::FillHatchAttribute createFillHatchAttribute(const Hatch& rHatch)
+ {
+ drawinglayer::attribute::HatchStyle aHatchStyle(drawinglayer::attribute::HATCHSTYLE_SINGLE);
+
+ switch(rHatch.GetStyle())
+ {
+ default : // case HATCH_SINGLE :
+ {
+ aHatchStyle = drawinglayer::attribute::HATCHSTYLE_SINGLE;
+ break;
+ }
+ case HATCH_DOUBLE :
+ {
+ aHatchStyle = drawinglayer::attribute::HATCHSTYLE_DOUBLE;
+ break;
+ }
+ case HATCH_TRIPLE :
+ {
+ aHatchStyle = drawinglayer::attribute::HATCHSTYLE_TRIPLE;
+ break;
+ }
+ }
+
+ return drawinglayer::attribute::FillHatchAttribute(
+ aHatchStyle,
+ (double)rHatch.GetDistance(),
+ (double)rHatch.GetAngle() * F_PI1800,
+ rHatch.GetColor().getBColor(),
+ false);
+ }
+
+ /** helper to take needed action on ClipRegion change. This method needs to be called
+ on any Region change, e.g. at the obvious actions doing this, but also at pop-calls
+ whcih change the Region of the current context. It takes care of creating the
+ current embeddec context, set the new Region at the context and eventually prepare
+ a new target for embracing new geometry to the current region
+ */
+ void HandleNewClipRegion(
+ const basegfx::B2DPolyPolygon& rClipPolyPolygon,
+ TargetHolders& rTargetHolders,
+ PropertyHolders& rPropertyHolders)
+ {
+ const bool bNewActive(rClipPolyPolygon.count());
+
+ // #i108636# The handlig of new ClipPolyPolygons was not done as good as possible
+ // in the first version of this interpreter; e.g. when a ClipPolyPolygon was set
+ // initially and then using a lot of push/pop actions, the pop always leads
+ // to setting a 'new' ClipPolyPolygon which indeed is the return to the ClipPolyPolygon
+ // of the properties next on the stack.
+ //
+ // This ClipPolyPolygon is identical to the current one, so there is no need to
+ // create a MaskPrimitive2D containing the up-to-now created primitives, but
+ // this was done before. While this does not lead to wrong primitive
+ // representations of the metafile data, it creates unneccesarily expensive
+ // representations. Just detecting when no really 'new' ClipPolyPolygon gets set
+ // solves the problem.
+
+ if(!rPropertyHolders.Current().getClipPolyPolygonActive() && !bNewActive)
+ {
+ // no active ClipPolyPolygon exchanged by no new one, done
+ return;
+ }
+
+ if(rPropertyHolders.Current().getClipPolyPolygonActive() && bNewActive)
+ {
+ // active ClipPolyPolygon and new active ClipPolyPolygon
+ if(rPropertyHolders.Current().getClipPolyPolygon() == rClipPolyPolygon)
+ {
+ // new is the same as old, done
+ return;
+ }
+ }
+
+ // Here the old and the new are definitively different, maybe
+ // old one and/or new one is not active.
+
+ // Handle deletion of old ClipPolyPolygon. The process evtl. created primitives which
+ // belong to this active ClipPolyPolygon. These need to be embedded to a
+ // MaskPrimitive2D accordingly.
+ if(rPropertyHolders.Current().getClipPolyPolygonActive() && rTargetHolders.size() > 1)
+ {
+ drawinglayer::primitive2d::Primitive2DSequence aSubContent;
+
+ if(rPropertyHolders.Current().getClipPolyPolygon().count()
+ && rTargetHolders.Current().size())
+ {
+ aSubContent = rTargetHolders.Current().getPrimitive2DSequence(
+ rPropertyHolders.Current());
+ }
+
+ rTargetHolders.Pop();
+
+ if(aSubContent.hasElements())
+ {
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::GroupPrimitive2D(
+ aSubContent));
+ }
+ }
+
+ // apply new settings to current properties by setting
+ // the new region now
+ rPropertyHolders.Current().setClipPolyPolygonActive(bNewActive);
+
+ if(bNewActive)
+ {
+ rPropertyHolders.Current().setClipPolyPolygon(rClipPolyPolygon);
+
+ // prepare new content holder for new active region
+ rTargetHolders.Push();
+ }
+ }
+
+ /** helper to handle the change of RasterOp. It takes care of encapsulating all current
+ geometry to the current RasterOp (if changed) and needs to be called on any RasterOp
+ change. It will also start a new geometry target to embrace to the new RasterOp if
+ a changuing RasterOp is used. Currently, ROP_XOR and ROP_INVERT are supported using
+ InvertPrimitive2D, and ROP_0 by using a ModifiedColorPrimitive2D to force to black paint
+ */
+ void HandleNewRasterOp(
+ RasterOp aRasterOp,
+ TargetHolders& rTargetHolders,
+ PropertyHolders& rPropertyHolders)
+ {
+ // check if currently active
+ if(rPropertyHolders.Current().isRasterOpActive() && rTargetHolders.size() > 1)
+ {
+ drawinglayer::primitive2d::Primitive2DSequence aSubContent;
+
+ if(rTargetHolders.Current().size())
+ {
+ aSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
+ }
+
+ rTargetHolders.Pop();
+
+ if(aSubContent.hasElements())
+ {
+ if(rPropertyHolders.Current().isRasterOpForceBlack())
+ {
+ // force content to black
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
+ aSubContent,
+ basegfx::BColorModifier(basegfx::BColor(0.0, 0.0, 0.0))));
+ }
+ else // if(rPropertyHolders.Current().isRasterOpInvert())
+ {
+ // invert content
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::InvertPrimitive2D(
+ aSubContent));
+ }
+ }
+ }
+
+ // apply new settings
+ rPropertyHolders.Current().setRasterOp(aRasterOp);
+
+ // check if now active
+ if(rPropertyHolders.Current().isRasterOpActive())
+ {
+ // prepare new content holder for new invert
+ rTargetHolders.Push();
+ }
+ }
+
+ /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
+ It is a quite mighty action. This helper is for simple color filled background.
+ */
+ drawinglayer::primitive2d::BasePrimitive2D* CreateColorWallpaper(
+ const basegfx::B2DRange& rRange,
+ const basegfx::BColor& rColor,
+ PropertyHolder& rPropertyHolder)
+ {
+ basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(rRange));
+ aOutline.transform(rPropertyHolder.getTransformation());
+
+ return new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aOutline),
+ rColor);
+ }
+
+ /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
+ It is a quite mighty action. This helper is for gradient filled background.
+ */
+ drawinglayer::primitive2d::BasePrimitive2D* CreateGradientWallpaper(
+ const basegfx::B2DRange& rRange,
+ const Gradient& rGradient,
+ PropertyHolder& rPropertyHolder)
+ {
+ const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+
+ if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ {
+ // not really a gradient. Create filled rectangle
+ return CreateColorWallpaper(rRange, aAttribute.getStartColor(), rPropertyHolder);
+ }
+ else
+ {
+ // really a gradient
+ drawinglayer::primitive2d::BasePrimitive2D* pRetval =
+ new drawinglayer::primitive2d::FillGradientPrimitive2D(
+ rRange,
+ aAttribute);
+
+ if(!rPropertyHolder.getTransformation().isIdentity())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xPrim(pRetval);
+ const drawinglayer::primitive2d::Primitive2DSequence xSeq(&xPrim, 1);
+
+ pRetval = new drawinglayer::primitive2d::TransformPrimitive2D(
+ rPropertyHolder.getTransformation(),
+ xSeq);
+ }
+
+ return pRetval;
+ }
+ }
+
+ /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
+ It is a quite mighty action. This helper decides if color and/or gradient
+ background is needed for the wnated bitmap fill and then creates the needed
+ WallpaperBitmapPrimitive2D. This primitive was created for this purpose and
+ takes over all needed logic of orientations and tiling.
+ */
+ void CreateAndAppendBitmapWallpaper(
+ basegfx::B2DRange aWallpaperRange,
+ const Wallpaper& rWallpaper,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperty)
+ {
+ const BitmapEx aBitmapEx(rWallpaper.GetBitmap());
+ const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle());
+
+ // if bitmap visualisation is transparent, maybe background
+ // needs to be filled. Create background
+ if(aBitmapEx.IsTransparent()
+ || (WALLPAPER_TILE != eWallpaperStyle && WALLPAPER_SCALE != eWallpaperStyle))
+ {
+ if(rWallpaper.IsGradient())
+ {
+ rTarget.append(
+ CreateGradientWallpaper(
+ aWallpaperRange,
+ rWallpaper.GetGradient(),
+ rProperty));
+ }
+ else if(!rWallpaper.GetColor().GetTransparency())
+ {
+ rTarget.append(
+ CreateColorWallpaper(
+ aWallpaperRange,
+ rWallpaper.GetColor().getBColor(),
+ rProperty));
+ }
+ }
+
+ // use wallpaper rect if set
+ if(rWallpaper.IsRect() && !rWallpaper.GetRect().IsEmpty())
+ {
+ aWallpaperRange = basegfx::B2DRange(
+ rWallpaper.GetRect().Left(), rWallpaper.GetRect().Top(),
+ rWallpaper.GetRect().Right(), rWallpaper.GetRect().Bottom());
+ }
+
+ drawinglayer::primitive2d::BasePrimitive2D* pBitmapWallpaperFill =
+ new drawinglayer::primitive2d::WallpaperBitmapPrimitive2D(
+ aWallpaperRange,
+ aBitmapEx,
+ eWallpaperStyle);
+
+ if(rProperty.getTransformation().isIdentity())
+ {
+ // add directly
+ rTarget.append(pBitmapWallpaperFill);
+ }
+ else
+ {
+ // when a transformation is set, embed to it
+ const drawinglayer::primitive2d::Primitive2DReference xPrim(pBitmapWallpaperFill);
+
+ rTarget.append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ rProperty.getTransformation(),
+ drawinglayer::primitive2d::Primitive2DSequence(&xPrim, 1)));
+ }
+ }
+
+ /** helper to decide UnderlineAbove for text primitives */
+ bool isUnderlineAbove(const Font& rFont)
+ {
+ if(!rFont.IsVertical())
+ {
+ return false;
+ }
+
+ if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()))
+ {
+ // the underline is right for Japanese only
+ return true;
+ }
+
+ return false;
+ }
+
+ void createFontAttributeTransformAndAlignment(
+ drawinglayer::attribute::FontAttribute& rFontAttribute,
+ basegfx::B2DHomMatrix& rTextTransform,
+ basegfx::B2DVector& rAlignmentOffset,
+ PropertyHolder& rProperty)
+ {
+ const Font& rFont = rProperty.getFont();
+ basegfx::B2DVector aFontScaling;
+
+ rFontAttribute = drawinglayer::attribute::FontAttribute(
+ drawinglayer::primitive2d::getFontAttributeFromVclFont(
+ aFontScaling,
+ rFont,
+ 0 != (rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_RTL),
+ 0 != (rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_STRONG)));
+
+ // add FontScaling
+ rTextTransform.scale(aFontScaling.getX(), aFontScaling.getY());
+
+ // take text align into account
+ if(ALIGN_BASELINE != rFont.GetAlign())
+ {
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(rFont);
+
+ if(ALIGN_TOP == rFont.GetAlign())
+ {
+ rAlignmentOffset.setY(aTextLayouterDevice.getFontAscent());
+ }
+ else // ALIGN_BOTTOM
+ {
+ rAlignmentOffset.setY(-aTextLayouterDevice.getFontDescent());
+ }
+
+ rTextTransform.translate(rAlignmentOffset.getX(), rAlignmentOffset.getY());
+ }
+
+ // add FontRotation (if used)
+ if(rFont.GetOrientation())
+ {
+ rTextTransform.rotate(-rFont.GetOrientation() * F_PI1800);
+ }
+ }
+
+ /** helper which takes complete care for creating the needed text primitives. It
+ takes care of decorated stuff and all the geometry adaptions needed
+ */
+ void proccessMetaTextAction(
+ const Point& rTextStartPosition,
+ const XubString& rText,
+ sal_uInt16 nTextStart,
+ sal_uInt16 nTextLength,
+ const ::std::vector< double >& rDXArray,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperty)
+ {
+ drawinglayer::primitive2d::BasePrimitive2D* pResult = 0;
+ const Font& rFont = rProperty.getFont();
+ basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
+
+ if(nTextLength)
+ {
+ drawinglayer::attribute::FontAttribute aFontAttribute;
+ basegfx::B2DHomMatrix aTextTransform;
+
+ // fill parameters derived from current font
+ createFontAttributeTransformAndAlignment(
+ aFontAttribute,
+ aTextTransform,
+ aAlignmentOffset,
+ rProperty);
+
+ // add TextStartPosition
+ aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());
+
+ // prepare FontColor and Locale
+ const basegfx::BColor aFontColor(rProperty.getTextColor());
+ const com::sun::star::lang::Locale aLocale(MsLangId::convertLanguageToLocale(rProperty.getLanguageType()));
+ const bool bWordLineMode(rFont.IsWordLineMode());
+
+ const bool bDecoratedIsNeeded(
+ UNDERLINE_NONE != rFont.GetOverline()
+ || UNDERLINE_NONE != rFont.GetUnderline()
+ || STRIKEOUT_NONE != rFont.GetStrikeout()
+ || EMPHASISMARK_NONE != (rFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
+ || RELIEF_NONE != rFont.GetRelief()
+ || rFont.IsShadow()
+ || bWordLineMode);
+
+ if(bDecoratedIsNeeded)
+ {
+ // prepare overline, underline and srikeout data
+ const drawinglayer::primitive2d::TextLine eFontOverline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont.GetOverline()));
+ const drawinglayer::primitive2d::TextLine eFontUnderline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont.GetUnderline()));
+ const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rFont.GetStrikeout()));
+
+ // check UndelineAbove
+ const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE != eFontUnderline && isUnderlineAbove(rFont));
+
+ // prepare emphasis mark data
+ drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE);
+
+ switch(rFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
+ {
+ case EMPHASISMARK_DOT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT; break;
+ case EMPHASISMARK_CIRCLE : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE; break;
+ case EMPHASISMARK_DISC : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC; break;
+ case EMPHASISMARK_ACCENT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT; break;
+ }
+
+ const bool bEmphasisMarkAbove(rFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE);
+ const bool bEmphasisMarkBelow(rFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW);
+
+ // prepare font relief data
+ drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
+
+ switch(rFont.GetRelief())
+ {
+ case RELIEF_EMBOSSED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
+ case RELIEF_ENGRAVED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
+ default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
+ }
+
+ // prepare shadow/outline data
+ const bool bShadow(rFont.IsShadow());
+
+ // TextDecoratedPortionPrimitive2D is needed, create one
+ pResult = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
+
+ // attributes for TextSimplePortionPrimitive2D
+ aTextTransform,
+ rText,
+ nTextStart,
+ nTextLength,
+ rDXArray,
+ aFontAttribute,
+ aLocale,
+ aFontColor,
+
+ // attributes for TextDecoratedPortionPrimitive2D
+ rProperty.getOverlineColorActive() ? rProperty.getOverlineColor() : aFontColor,
+ rProperty.getTextLineColorActive() ? rProperty.getTextLineColor() : aFontColor,
+ eFontOverline,
+ eFontUnderline,
+ bUnderlineAbove,
+ eTextStrikeout,
+ bWordLineMode,
+ eTextEmphasisMark,
+ bEmphasisMarkAbove,
+ bEmphasisMarkBelow,
+ eTextRelief,
+ bShadow);
+ }
+ else
+ {
+ // TextSimplePortionPrimitive2D is enough
+ pResult = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aTextTransform,
+ rText,
+ nTextStart,
+ nTextLength,
+ rDXArray,
+ aFontAttribute,
+ aLocale,
+ aFontColor);
+ }
+ }
+
+ if(pResult && rProperty.getTextFillColorActive())
+ {
+ // text background is requested, add and encapsulate both to new primitive
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(rFont);
+
+ // get text width
+ double fTextWidth(0.0);
+
+ if(rDXArray.empty())
+ {
+ fTextWidth = aTextLayouterDevice.getTextWidth(rText, nTextStart, nTextLength);
+ }
+ else
+ {
+ fTextWidth = rDXArray.back();
+ }
+
+ if(basegfx::fTools::more(fTextWidth, 0.0))
+ {
+ // build text range
+ const basegfx::B2DRange aTextRange(
+ 0.0, -aTextLayouterDevice.getFontAscent(),
+ fTextWidth, aTextLayouterDevice.getFontDescent());
+
+ // create Transform
+ basegfx::B2DHomMatrix aTextTransform;
+
+ aTextTransform.translate(aAlignmentOffset.getX(), aAlignmentOffset.getY());
+
+ if(rFont.GetOrientation())
+ {
+ aTextTransform.rotate(-rFont.GetOrientation() * F_PI1800);
+ }
+
+ aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());
+
+ // prepare Primitive2DSequence, put text in foreground
+ drawinglayer::primitive2d::Primitive2DSequence aSequence(2);
+ aSequence[1] = drawinglayer::primitive2d::Primitive2DReference(pResult);
+
+ // prepare filled polygon
+ basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aTextRange));
+ aOutline.transform(aTextTransform);
+
+ aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aOutline),
+ rProperty.getTextFillColor()));
+
+ // set as group at pResult
+ pResult = new drawinglayer::primitive2d::GroupPrimitive2D(aSequence);
+ }
+ }
+
+ if(pResult)
+ {
+ // add created text primitive to target
+ if(rProperty.getTransformation().isIdentity())
+ {
+ rTarget.append(pResult);
+ }
+ else
+ {
+ // when a transformation is set, embed to it
+ const drawinglayer::primitive2d::Primitive2DReference aReference(pResult);
+
+ rTarget.append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ rProperty.getTransformation(),
+ drawinglayer::primitive2d::Primitive2DSequence(&aReference, 1)));
+ }
+ }
+ }
+
+ /** helper which takes complete care for creating the needed textLine primitives */
+ void proccessMetaTextLineAction(
+ const MetaTextLineAction& rAction,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperty)
+ {
+ const double fLineWidth(fabs((double)rAction.GetWidth()));
+
+ if(fLineWidth > 0.0)
+ {
+ const drawinglayer::primitive2d::TextLine aOverlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction.GetOverline()));
+ const drawinglayer::primitive2d::TextLine aUnderlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction.GetUnderline()));
+ const drawinglayer::primitive2d::TextStrikeout aTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rAction.GetStrikeout()));
+
+ const bool bOverlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aOverlineMode);
+ const bool bUnderlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aUnderlineMode);
+ const bool bStrikeoutUsed(drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE != aTextStrikeout);
+
+ if(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed)
+ {
+ std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargetVector;
+ basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
+ drawinglayer::attribute::FontAttribute aFontAttribute;
+ basegfx::B2DHomMatrix aTextTransform;
+
+ // fill parameters derived from current font
+ createFontAttributeTransformAndAlignment(
+ aFontAttribute,
+ aTextTransform,
+ aAlignmentOffset,
+ rProperty);
+
+ // add TextStartPosition
+ aTextTransform.translate(rAction.GetStartPoint().X(), rAction.GetStartPoint().Y());
+
+ // prepare TextLayouter (used in most cases)
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+ aTextLayouter.setFont(rProperty.getFont());
+
+ if(bOverlineUsed)
+ {
+ // create primitive geometry for overline
+ aTargetVector.push_back(
+ new drawinglayer::primitive2d::TextLinePrimitive2D(
+ aTextTransform,
+ fLineWidth,
+ aTextLayouter.getOverlineOffset(),
+ aTextLayouter.getOverlineHeight(),
+ aOverlineMode,
+ rProperty.getOverlineColor()));
+ }
+
+ if(bUnderlineUsed)
+ {
+ // create primitive geometry for underline
+ aTargetVector.push_back(
+ new drawinglayer::primitive2d::TextLinePrimitive2D(
+ aTextTransform,
+ fLineWidth,
+ aTextLayouter.getUnderlineOffset(),
+ aTextLayouter.getUnderlineHeight(),
+ aUnderlineMode,
+ rProperty.getTextLineColor()));
+ }
+
+ if(bStrikeoutUsed)
+ {
+ // create primitive geometry for strikeout
+ if(drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout
+ || drawinglayer::primitive2d::TEXT_STRIKEOUT_X == aTextStrikeout)
+ {
+ // strikeout with character
+ const sal_Unicode aStrikeoutChar(
+ drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout ? '/' : 'X');
+ const com::sun::star::lang::Locale aLocale(MsLangId::convertLanguageToLocale(
+ rProperty.getLanguageType()));
+
+ aTargetVector.push_back(
+ new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D(
+ aTextTransform,
+ fLineWidth,
+ rProperty.getTextColor(),
+ aStrikeoutChar,
+ aFontAttribute,
+ aLocale));
+ }
+ else
+ {
+ // strikeout with geometry
+ aTargetVector.push_back(
+ new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D(
+ aTextTransform,
+ fLineWidth,
+ rProperty.getTextColor(),
+ aTextLayouter.getUnderlineHeight(),
+ aTextLayouter.getStrikeoutOffset(),
+ aTextStrikeout));
+ }
+ }
+
+ if(aTargetVector.size())
+ {
+ // add created text primitive to target
+ if(rProperty.getTransformation().isIdentity())
+ {
+ for(sal_uInt32 a(0); a < aTargetVector.size(); a++)
+ {
+ rTarget.append(aTargetVector[a]);
+ }
+ }
+ else
+ {
+ // when a transformation is set, embed to it
+ drawinglayer::primitive2d::Primitive2DSequence xTargets(aTargetVector.size());
+
+ for(sal_uInt32 a(0); a < aTargetVector.size(); a++)
+ {
+ xTargets[a] = drawinglayer::primitive2d::Primitive2DReference(aTargetVector[a]);
+ }
+
+ rTarget.append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ rProperty.getTransformation(),
+ xTargets));
+ }
+ }
+ }
+ }
+
+ }
+
+ /** This is the main interpreter method. It is designed to handle the given Metafile
+ completely inside the given context and target. It may use and modify the context and
+ target. This design allows to call itself recursively wich adapted contexts and
+ targets as e.g. needed for the META_FLOATTRANSPARENT_ACTION where the content is expressed
+ as a metafile as sub-content.
+
+ This interpreter is as free of VCL functionality as possible. It uses VCL data classes
+ (else reading the data would not be possible), but e.g. does NOT use a local OutputDevice
+ as most other MetaFile interpreters/exporters do to hold and work with the current context.
+ This is necessary to be able to get away from the strong internal VCL-binding.
+
+ It tries to combine e.g. pixel and/or point actions and to stitch together single line primitives
+ where possible (which is not trivial with the possible line geometry definitions).
+
+ It tries to handle clipping no longer as Regions and spans of Rectangles, but as PolyPolygon
+ ClipRegions with (where possible) high precision by using the best possible data quality
+ from the Region. The Region is unavoidable as data container, but nowadays allows the transport
+ of Polygon-based clip regions. Where this is not used, a Polygon is constructed from the
+ Region ranges. All primitive clipping uses the MaskPrimitive2D with Polygon-based clipping.
+
+ I have marked the single MetaActions with:
+
+ SIMPLE, DONE:
+ Simple, e.g nothing to do or value setting in the context
+
+ CHECKED, WORKS WELL:
+ Thoroughly tested with extra written test code which created a replacement
+ Metafile just to test this action in various combinations
+
+ NEEDS IMPLEMENTATION:
+ Not implemented and asserted, but also no usage found, neither in own Metafile
+ creations, nor in EMF/WMF imports (checked with a whole bunch of critical EMF/WMF
+ bugdocs)
+
+ For more commens, see the single action implementations.
+ */
+ void interpretMetafile(
+ const GDIMetaFile& rMetaFile,
+ TargetHolders& rTargetHolders,
+ PropertyHolders& rPropertyHolders,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation)
+ {
+ const sal_uInt32 nCount(rMetaFile.GetActionCount());
+
+ for(sal_uInt32 nAction(0); nAction < nCount; nAction++)
+ {
+ MetaAction* pAction = rMetaFile.GetAction(nAction);
+
+ switch(pAction->GetType())
+ {
+ case META_NULL_ACTION :
+ {
+ /** SIMPLE, DONE */
+ break;
+ }
+ case META_PIXEL_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ std::vector< basegfx::B2DPoint > aPositions;
+ Color aLastColor(COL_BLACK);
+
+ while(META_PIXEL_ACTION == pAction->GetType() && nAction < nCount)
+ {
+ const MetaPixelAction* pA = (const MetaPixelAction*)pAction;
+
+ if(pA->GetColor() != aLastColor)
+ {
+ if(aPositions.size())
+ {
+ createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
+ aPositions.clear();
+ }
+
+ aLastColor = pA->GetColor();
+ }
+
+ const Point& rPoint = pA->GetPoint();
+ aPositions.push_back(basegfx::B2DPoint(rPoint.X(), rPoint.Y()));
+ nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
+ }
+
+ nAction--;
+
+ if(aPositions.size())
+ {
+ createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
+ }
+
+ break;
+ }
+ case META_POINT_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ std::vector< basegfx::B2DPoint > aPositions;
+
+ while(META_POINT_ACTION == pAction->GetType() && nAction < nCount)
+ {
+ const MetaPointAction* pA = (const MetaPointAction*)pAction;
+ const Point& rPoint = pA->GetPoint();
+ aPositions.push_back(basegfx::B2DPoint(rPoint.X(), rPoint.Y()));
+ nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
+ }
+
+ nAction--;
+
+ if(aPositions.size())
+ {
+ createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor());
+ }
+ }
+
+ break;
+ }
+ case META_LINE_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ basegfx::B2DPolygon aLinePolygon;
+ LineInfo aLineInfo;
+
+ while(META_LINE_ACTION == pAction->GetType() && nAction < nCount)
+ {
+ const MetaLineAction* pA = (const MetaLineAction*)pAction;
+ const Point& rStartPoint = pA->GetStartPoint();
+ const Point& rEndPoint = pA->GetEndPoint();
+ const basegfx::B2DPoint aStart(rStartPoint.X(), rStartPoint.Y());
+ const basegfx::B2DPoint aEnd(rEndPoint.X(), rEndPoint.Y());
+
+ if(aLinePolygon.count())
+ {
+ if(pA->GetLineInfo() == aLineInfo
+ && aStart == aLinePolygon.getB2DPoint(aLinePolygon.count() - 1))
+ {
+ aLinePolygon.append(aEnd);
+ }
+ else
+ {
+ aLineInfo.SetLineJoin(basegfx::B2DLINEJOIN_NONE); // It were lines; force to NONE
+ createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
+ aLinePolygon.clear();
+ aLineInfo = pA->GetLineInfo();
+ aLinePolygon.append(aStart);
+ aLinePolygon.append(aEnd);
+ }
+ }
+ else
+ {
+ aLineInfo = pA->GetLineInfo();
+ aLinePolygon.append(aStart);
+ aLinePolygon.append(aEnd);
+ }
+
+ nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
+ }
+
+ nAction--;
+
+ if(aLinePolygon.count())
+ {
+ aLineInfo.SetLineJoin(basegfx::B2DLINEJOIN_NONE); // It were lines; force to NONE
+ createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+ }
+
+ break;
+ }
+ case META_RECT_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaRectAction* pA = (const MetaRectAction*)pAction;
+ const Rectangle& rRectangle = pA->GetRect();
+
+ if(!rRectangle.IsEmpty())
+ {
+ const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
+
+ if(!aRange.isEmpty())
+ {
+ const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+ }
+ }
+
+ break;
+ }
+ case META_ROUNDRECT_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ /** The original OutputDevice::DrawRect paints nothing when nHor or nVer is zero; but just
+ because the tools::Polygon operator creating the rounding does produce nonsense. I assume
+ this an error and create an unrounded rectangle in that case (implicit in
+ createPolygonFromRect)
+ */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaRoundRectAction* pA = (const MetaRoundRectAction*)pAction;
+ const Rectangle& rRectangle = pA->GetRect();
+
+ if(!rRectangle.IsEmpty())
+ {
+ const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
+
+ if(!aRange.isEmpty())
+ {
+ const sal_uInt32 nHor(pA->GetHorzRound());
+ const sal_uInt32 nVer(pA->GetVertRound());
+ 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);
+ }
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+ }
+ }
+
+ break;
+ }
+ case META_ELLIPSE_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaEllipseAction* pA = (const MetaEllipseAction*)pAction;
+ const Rectangle& rRectangle = pA->GetRect();
+
+ if(!rRectangle.IsEmpty())
+ {
+ const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
+
+ if(!aRange.isEmpty())
+ {
+ const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromEllipse(
+ aRange.getCenter(), aRange.getWidth() * 0.5, aRange.getHeight() * 0.5));
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+ }
+ }
+
+ break;
+ }
+ case META_ARC_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ const MetaArcAction* pA = (const MetaArcAction*)pAction;
+ const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC);
+ const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
+
+ createHairlinePrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_PIE_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaPieAction* pA = (const MetaPieAction*)pAction;
+ const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE);
+ const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_CHORD_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaChordAction* pA = (const MetaChordAction*)pAction;
+ const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD);
+ const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_POLYLINE_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ const MetaPolyLineAction* pA = (const MetaPolyLineAction*)pAction;
+ createLinePrimitive(pA->GetPolygon().getB2DPolygon(), pA->GetLineInfo(), rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_POLYGON_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaPolygonAction* pA = (const MetaPolygonAction*)pAction;
+ basegfx::B2DPolygon aOutline(pA->GetPolygon().getB2DPolygon());
+
+ // the metafile play interprets the polygons from MetaPolygonAction
+ // always as closed and always paints an edge from last to first point,
+ // so force to closed here to emulate that
+ if(aOutline.count() > 1 && !aOutline.isClosed())
+ {
+ aOutline.setClosed(true);
+ }
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_POLYPOLYGON_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaPolyPolygonAction* pA = (const MetaPolyPolygonAction*)pAction;
+ basegfx::B2DPolyPolygon aPolyPolygonOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
+
+ // the metafile play interprets the single polygons from MetaPolyPolygonAction
+ // always as closed and always paints an edge from last to first point,
+ // so force to closed here to emulate that
+ for(sal_uInt32 b(0); b < aPolyPolygonOutline.count(); b++)
+ {
+ basegfx::B2DPolygon aPolygonOutline(aPolyPolygonOutline.getB2DPolygon(b));
+
+ if(aPolygonOutline.count() > 1 && !aPolygonOutline.isClosed())
+ {
+ aPolygonOutline.setClosed(true);
+ aPolyPolygonOutline.setB2DPolygon(b, aPolygonOutline);
+ }
+ }
+
+ createHairlineAndFillPrimitive(aPolyPolygonOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_TEXT_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaTextAction* pA = (const MetaTextAction*)pAction;
+ sal_uInt32 nTextLength(pA->GetLen());
+ const sal_uInt32 nTextIndex(pA->GetIndex());
+ const sal_uInt32 nStringLength(pA->GetText().Len());
+
+ if(nTextLength + nTextIndex > nStringLength)
+ {
+ nTextLength = nStringLength - nTextIndex;
+ }
+
+ if(nTextLength && rPropertyHolders.Current().getTextColorActive())
+ {
+ const std::vector< double > aDXArray;
+ proccessMetaTextAction(
+ pA->GetPoint(),
+ pA->GetText(),
+ nTextIndex,
+ nTextLength,
+ aDXArray,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_TEXTARRAY_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaTextArrayAction* pA = (const MetaTextArrayAction*)pAction;
+ sal_uInt32 nTextLength(pA->GetLen());
+ const sal_uInt32 nTextIndex(pA->GetIndex());
+ const sal_uInt32 nStringLength(pA->GetText().Len());
+
+ if(nTextLength + nTextIndex > nStringLength)
+ {
+ nTextLength = nTextIndex > nStringLength ? 0 : nStringLength - nTextIndex;
+ }
+
+ if(nTextLength && rPropertyHolders.Current().getTextColorActive())
+ {
+ // preapare DXArray (if used)
+ std::vector< double > aDXArray;
+ sal_Int32* pDXArray = pA->GetDXArray();
+
+ if(pDXArray)
+ {
+ aDXArray.reserve(nTextLength);
+
+ for(sal_uInt32 a(0); a < nTextLength; a++)
+ {
+ aDXArray.push_back((double)(*(pDXArray + a)));
+ }
+ }
+
+ proccessMetaTextAction(
+ pA->GetPoint(),
+ pA->GetText(),
+ nTextIndex,
+ nTextLength,
+ aDXArray,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_STRETCHTEXT_ACTION :
+ {
+ // #i108440# StarMath uses MetaStretchTextAction, thus support is needed.
+ // It looks as if it pretty never really uses a width different from
+ // the default text-layout width, but it's not possible to be sure.
+ // Implemented getting the DXArray and checking for scale at all. If
+ // scale is more than 3.5% different, scale the DXArray before usage.
+ // New status:
+
+ /** CHECKED, WORKS WELL */
+ const MetaStretchTextAction* pA = (const MetaStretchTextAction*)pAction;
+ sal_uInt32 nTextLength(pA->GetLen());
+ const sal_uInt32 nTextIndex(pA->GetIndex());
+ const sal_uInt32 nStringLength(pA->GetText().Len());
+
+ if(nTextLength + nTextIndex > nStringLength)
+ {
+ nTextLength = nStringLength - nTextIndex;
+ }
+
+ if(nTextLength && rPropertyHolders.Current().getTextColorActive())
+ {
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
+
+ ::std::vector< double > aTextArray(
+ aTextLayouterDevice.getTextArray(
+ pA->GetText(),
+ nTextIndex,
+ nTextLength));
+
+ if(!aTextArray.empty())
+ {
+ const double fTextLength(aTextArray.back());
+
+ if(0.0 != fTextLength && pA->GetWidth())
+ {
+ const double fRelative(pA->GetWidth() / fTextLength);
+
+ if(fabs(fRelative - 1.0) >= 0.035)
+ {
+ // when derivation is more than 3,5% from default text size,
+ // scale the DXArray
+ for(sal_uInt32 a(0); a < aTextArray.size(); a++)
+ {
+ aTextArray[a] *= fRelative;
+ }
+ }
+ }
+ }
+
+ proccessMetaTextAction(
+ pA->GetPoint(),
+ pA->GetText(),
+ nTextIndex,
+ nTextLength,
+ aTextArray,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_TEXTRECT_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ // OSL_FAIL("META_TEXTRECT_ACTION requested (!)");
+ const MetaTextRectAction* pA = (const MetaTextRectAction*)pAction;
+ const Rectangle& rRectangle = pA->GetRect();
+ const sal_uInt32 nStringLength(pA->GetText().Len());
+
+ if(!rRectangle.IsEmpty() && 0 != nStringLength)
+ {
+ // The problem with this action is that it describes unlayouted text
+ // and the layout capabilities are in EditEngine/Outliner in SVX. The
+ // same problem is true for VCL which internally has implementations
+ // to layout text in this case. There exists even a call
+ // OutputDevice::AddTextRectActions(...) to create the needed actions
+ // as 'sub-content' of a Metafile. Unfortunately i do not have an
+ // OutputDevice here since this interpreter tries to work without
+ // VCL AFAP.
+ // Since AddTextRectActions is the only way as long as we do not have
+ // a simple text layouter available, i will try to add it to the
+ // TextLayouterDevice isloation.
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
+ GDIMetaFile aGDIMetaFile;
+
+ aTextLayouterDevice.addTextRectActions(
+ rRectangle, pA->GetText(), pA->GetStyle(), aGDIMetaFile);
+
+ if(aGDIMetaFile.GetActionCount())
+ {
+ // cerate sub-content
+ drawinglayer::primitive2d::Primitive2DSequence xSubContent;
+ {
+ rTargetHolders.Push();
+ // #i# for sub-Mteafile contents, do start with new, default render state
+ rPropertyHolders.PushDefault();
+ interpretMetafile(aGDIMetaFile, rTargetHolders, rPropertyHolders, rViewInformation);
+ xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
+ rPropertyHolders.Pop();
+ rTargetHolders.Pop();
+ }
+
+ if(xSubContent.hasElements())
+ {
+ // add with transformation
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ rPropertyHolders.Current().getTransformation(),
+ xSubContent));
+ }
+ }
+ }
+
+ break;
+ }
+ case META_BMP_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaBmpAction* pA = (const MetaBmpAction*)pAction;
+ const BitmapEx aBitmapEx(pA->GetBitmap());
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case META_BMPSCALE_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*)pAction;
+ const Bitmap aBitmapEx(pA->GetBitmap());
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case META_BMPSCALEPART_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaBmpScalePartAction* pA = (const MetaBmpScalePartAction*)pAction;
+ const Bitmap& rBitmap = pA->GetBitmap();
+
+ if(!rBitmap.IsEmpty())
+ {
+ Bitmap aCroppedBitmap(rBitmap);
+ const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
+
+ if(!aCropRectangle.IsEmpty())
+ {
+ aCroppedBitmap.Crop(aCropRectangle);
+ }
+
+ const BitmapEx aCroppedBitmapEx(aCroppedBitmap);
+ createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_BMPEX_ACTION :
+ {
+ /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */
+ const MetaBmpExAction* pA = (const MetaBmpExAction*)pAction;
+ const BitmapEx& rBitmapEx = pA->GetBitmapEx();
+
+ createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case META_BMPEXSCALE_ACTION :
+ {
+ /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */
+ const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*)pAction;
+ const BitmapEx& rBitmapEx = pA->GetBitmapEx();
+
+ createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case META_BMPEXSCALEPART_ACTION :
+ {
+ /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */
+ const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*)pAction;
+ const BitmapEx& rBitmapEx = pA->GetBitmapEx();
+
+ if(!rBitmapEx.IsEmpty())
+ {
+ BitmapEx aCroppedBitmapEx(rBitmapEx);
+ const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
+
+ if(!aCropRectangle.IsEmpty())
+ {
+ aCroppedBitmapEx.Crop(aCropRectangle);
+ }
+
+ createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_MASK_ACTION :
+ {
+ /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */
+ const MetaMaskAction* pA = (const MetaMaskAction*)pAction;
+ const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case META_MASKSCALE_ACTION :
+ {
+ /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */
+ const MetaMaskScaleAction* pA = (const MetaMaskScaleAction*)pAction;
+ const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case META_MASKSCALEPART_ACTION :
+ {
+ /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */
+ const MetaMaskScalePartAction* pA = (const MetaMaskScalePartAction*)pAction;
+ const Bitmap& rBitmap = pA->GetBitmap();
+
+ if(!rBitmap.IsEmpty())
+ {
+ Bitmap aCroppedBitmap(rBitmap);
+ const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
+
+ if(!aCropRectangle.IsEmpty())
+ {
+ aCroppedBitmap.Crop(aCropRectangle);
+ }
+
+ const BitmapEx aCroppedBitmapEx(createMaskBmpEx(aCroppedBitmap, pA->GetColor()));
+ createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case META_GRADIENT_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaGradientAction* pA = (const MetaGradientAction*)pAction;
+ const Rectangle& rRectangle = pA->GetRect();
+
+ if(!rRectangle.IsEmpty())
+ {
+ basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
+
+ if(!aRange.isEmpty())
+ {
+ const Gradient& rGradient = pA->GetGradient();
+ const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+ basegfx::B2DPolyPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
+
+ if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ {
+ // not really a gradient. Create filled rectangle
+ createFillPrimitive(
+ aOutline,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+ else
+ {
+ // really a gradient
+ aRange.transform(rPropertyHolders.Current().getTransformation());
+ drawinglayer::primitive2d::Primitive2DSequence xGradient(1);
+
+ if(rPropertyHolders.Current().isRasterOpInvert())
+ {
+ // use a special version of FillGradientPrimitive2D which creates
+ // non-overlapping geometry on decomposition to makethe old XOR
+ // paint 'trick' work.
+ xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::NonOverlappingFillGradientPrimitive2D(
+ aRange,
+ aAttribute));
+ }
+ else
+ {
+ xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::FillGradientPrimitive2D(
+ aRange,
+ aAttribute));
+ }
+
+ // #i112300# clip against polygon representing the rectangle from
+ // the action. This is implicitely done using a temp Clipping in VCL
+ // when a MetaGradientAction is executed
+ aOutline.transform(rPropertyHolders.Current().getTransformation());
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ aOutline,
+ xGradient));
+ }
+ }
+ }
+
+ break;
+ }
+ case META_HATCH_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaHatchAction* pA = (const MetaHatchAction*)pAction;
+ basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
+
+ if(aOutline.count())
+ {
+ const Hatch& rHatch = pA->GetHatch();
+ const drawinglayer::attribute::FillHatchAttribute aAttribute(createFillHatchAttribute(rHatch));
+
+ aOutline.transform(rPropertyHolders.Current().getTransformation());
+
+ const basegfx::B2DRange aObjectRange(aOutline.getB2DRange());
+ const drawinglayer::primitive2d::Primitive2DReference aFillHatch(
+ new drawinglayer::primitive2d::FillHatchPrimitive2D(
+ aObjectRange,
+ basegfx::BColor(),
+ aAttribute));
+
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ aOutline,
+ drawinglayer::primitive2d::Primitive2DSequence(&aFillHatch, 1)));
+ }
+
+ break;
+ }
+ case META_WALLPAPER_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaWallpaperAction* pA = (const MetaWallpaperAction*)pAction;
+ Rectangle aWallpaperRectangle(pA->GetRect());
+
+ if(!aWallpaperRectangle.IsEmpty())
+ {
+ const Wallpaper& rWallpaper = pA->GetWallpaper();
+ const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle());
+ basegfx::B2DRange aWallpaperRange(
+ aWallpaperRectangle.Left(), aWallpaperRectangle.Top(),
+ aWallpaperRectangle.Right(), aWallpaperRectangle.Bottom());
+
+ if(WALLPAPER_NULL != eWallpaperStyle)
+ {
+ if(rWallpaper.IsBitmap())
+ {
+ // create bitmap background. Caution: This
+ // also will create gradient/color background(s)
+ // when the bitmap is transparent or not tiled
+ CreateAndAppendBitmapWallpaper(
+ aWallpaperRange,
+ rWallpaper,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+ else if(rWallpaper.IsGradient())
+ {
+ // create gradient background
+ rTargetHolders.Current().append(
+ CreateGradientWallpaper(
+ aWallpaperRange,
+ rWallpaper.GetGradient(),
+ rPropertyHolders.Current()));
+ }
+ else if(!rWallpaper.GetColor().GetTransparency())
+ {
+ // create color background
+ rTargetHolders.Current().append(
+ CreateColorWallpaper(
+ aWallpaperRange,
+ rWallpaper.GetColor().getBColor(),
+ rPropertyHolders.Current()));
+ }
+ }
+ }
+
+ break;
+ }
+ case META_CLIPREGION_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaClipRegionAction* pA = (const MetaClipRegionAction*)pAction;
+
+ if(pA->IsClipping())
+ {
+ // new clipping. Get PolyPolygon and transform with current transformation
+ basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(pA->GetRegion()));
+
+ aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
+ HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ else
+ {
+ // end clipping
+ const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
+
+ HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+
+ break;
+ }
+ case META_ISECTRECTCLIPREGION_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaISectRectClipRegionAction* pA = (const MetaISectRectClipRegionAction*)pAction;
+ const Rectangle& rRectangle = pA->GetRect();
+
+ if(rRectangle.IsEmpty())
+ {
+ // intersect with empty rectangle will always give empty
+ // ClipPolyPolygon; start new clipping with empty PolyPolygon
+ const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
+
+ HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ else
+ {
+ // create transformed ClipRange
+ basegfx::B2DRange aClipRange(
+ rRectangle.Left(), rRectangle.Top(),
+ rRectangle.Right(), rRectangle.Bottom());
+
+ aClipRange.transform(rPropertyHolders.Current().getTransformation());
+
+ if(rPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
+ {
+ // nothing to do, empty active clipPolyPolygon will stay
+ // empty when intersecting
+ }
+ else
+ {
+ // AND existing region and new ClipRange
+ const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
+ rPropertyHolders.Current().getClipPolyPolygon());
+ basegfx::B2DPolyPolygon aClippedPolyPolygon;
+
+ if(aOriginalPolyPolygon.count())
+ {
+ aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnRange(
+ aOriginalPolyPolygon,
+ aClipRange,
+ true,
+ false);
+ }
+
+ if(aClippedPolyPolygon != aOriginalPolyPolygon)
+ {
+ // start new clipping with intersected region
+ HandleNewClipRegion(
+ aClippedPolyPolygon,
+ rTargetHolders,
+ rPropertyHolders);
+ }
+ }
+ }
+ else
+ {
+ // start new clipping with ClipRange
+ const basegfx::B2DPolyPolygon aNewClipPolyPolygon(
+ basegfx::tools::createPolygonFromRect(aClipRange));
+
+ HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ }
+
+ break;
+ }
+ case META_ISECTREGIONCLIPREGION_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaISectRegionClipRegionAction* pA = (const MetaISectRegionClipRegionAction*)pAction;
+ const Region& rNewRegion = pA->GetRegion();
+
+ if(rNewRegion.IsEmpty())
+ {
+ // intersect with empty region will always give empty
+ // region; start new clipping with empty PolyPolygon
+ const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
+
+ HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ else
+ {
+ // get new ClipPolyPolygon, transform it with current transformation
+ basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(rNewRegion));
+ aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
+
+ if(rPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
+ {
+ // nothing to do, empty active clipPolyPolygon will stay empty
+ // when intersecting with any region
+ }
+ else
+ {
+ // AND existing and new region
+ const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
+ rPropertyHolders.Current().getClipPolyPolygon());
+ basegfx::B2DPolyPolygon aClippedPolyPolygon;
+
+ if(aOriginalPolyPolygon.count())
+ {
+ aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon(
+ aOriginalPolyPolygon, aNewClipPolyPolygon, true, false);
+ }
+
+ if(aClippedPolyPolygon != aOriginalPolyPolygon)
+ {
+ // start new clipping with intersected ClipPolyPolygon
+ HandleNewClipRegion(aClippedPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ }
+ }
+ else
+ {
+ // start new clipping with new ClipPolyPolygon
+ HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ }
+
+ break;
+ }
+ case META_MOVECLIPREGION_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaMoveClipRegionAction* pA = (const MetaMoveClipRegionAction*)pAction;
+
+ if(rPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
+ {
+ // nothing to do
+ }
+ else
+ {
+ const sal_Int32 nHor(pA->GetHorzMove());
+ const sal_Int32 nVer(pA->GetVertMove());
+
+ if(0 != nHor || 0 != nVer)
+ {
+ // prepare translation, add current transformation
+ basegfx::B2DVector aVector(pA->GetHorzMove(), pA->GetVertMove());
+ aVector *= rPropertyHolders.Current().getTransformation();
+ basegfx::B2DHomMatrix aTransform(
+ basegfx::tools::createTranslateB2DHomMatrix(aVector));
+
+ // transform existing region
+ basegfx::B2DPolyPolygon aClipPolyPolygon(
+ rPropertyHolders.Current().getClipPolyPolygon());
+
+ aClipPolyPolygon.transform(aTransform);
+ HandleNewClipRegion(aClipPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+ }
+ }
+
+ break;
+ }
+ case META_LINECOLOR_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaLineColorAction* pA = (const MetaLineColorAction*)pAction;
+ const bool bActive(pA->IsSetting());
+
+ rPropertyHolders.Current().setLineColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setLineColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case META_FILLCOLOR_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaFillColorAction* pA = (const MetaFillColorAction*)pAction;
+ const bool bActive(pA->IsSetting());
+
+ rPropertyHolders.Current().setFillColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setFillColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case META_TEXTCOLOR_ACTION :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextColorAction* pA = (const MetaTextColorAction*)pAction;
+ const bool bActivate(COL_TRANSPARENT != pA->GetColor().GetColor());
+
+ rPropertyHolders.Current().setTextColorActive(bActivate);
+ rPropertyHolders.Current().setTextColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case META_TEXTFILLCOLOR_ACTION :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextFillColorAction* pA = (const MetaTextFillColorAction*)pAction;
+ const bool bWithColorArgument(pA->IsSetting());
+
+ if(bWithColorArgument)
+ {
+ // emulate OutputDevice::SetTextFillColor(...) WITH argument
+ const Color& rFontFillColor = pA->GetColor();
+ rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
+ rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor());
+ }
+ else
+ {
+ // emulate SetFillColor() <- NO argument (!)
+ rPropertyHolders.Current().setTextFillColorActive(false);
+ }
+
+ break;
+ }
+ case META_TEXTALIGN_ACTION :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextAlignAction* pA = (const MetaTextAlignAction*)pAction;
+ const TextAlign aNewTextAlign = pA->GetTextAlign();
+
+ // TextAlign is applied to the current font (as in
+ // OutputDevice::SetTextAlign which would be used when
+ // playing the Metafile)
+ if(rPropertyHolders.Current().getFont().GetAlign() != aNewTextAlign)
+ {
+ Font aNewFont(rPropertyHolders.Current().getFont());
+ aNewFont.SetAlign(aNewTextAlign);
+ rPropertyHolders.Current().setFont(aNewFont);
+ }
+
+ break;
+ }
+ case META_MAPMODE_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ // the most necessary MapMode to be interpreted is MAP_RELATIVE,
+ // but also the others may occur. Even not yet supported ones
+ // may need to be added here later
+ const MetaMapModeAction* pA = (const MetaMapModeAction*)pAction;
+ const MapMode& rMapMode = pA->GetMapMode();
+ basegfx::B2DHomMatrix aMapping;
+
+ if(MAP_RELATIVE == rMapMode.GetMapUnit())
+ {
+ aMapping = getTransformFromMapMode(rMapMode);
+ }
+ else
+ {
+ switch(rMapMode.GetMapUnit())
+ {
+ case MAP_100TH_MM :
+ {
+ if(MAP_TWIP == rPropertyHolders.Current().getMapUnit())
+ {
+ // MAP_TWIP -> MAP_100TH_MM
+ const double fTwipTo100thMm(127.0 / 72.0);
+ aMapping.scale(fTwipTo100thMm, fTwipTo100thMm);
+ }
+ break;
+ }
+ case MAP_TWIP :
+ {
+ if(MAP_100TH_MM == rPropertyHolders.Current().getMapUnit())
+ {
+ // MAP_100TH_MM -> MAP_TWIP
+ const double f100thMmToTwip(72.0 / 127.0);
+ aMapping.scale(f100thMmToTwip, f100thMmToTwip);
+ }
+ break;
+ }
+ default :
+ {
+ OSL_FAIL("interpretMetafile: META_MAPMODE_ACTION with unsupported MapUnit (!)");
+ break;
+ }
+ }
+
+ aMapping = getTransformFromMapMode(rMapMode) * aMapping;
+ rPropertyHolders.Current().setMapUnit(rMapMode.GetMapUnit());
+ }
+
+ if(!aMapping.isIdentity())
+ {
+ aMapping = aMapping * rPropertyHolders.Current().getTransformation();
+ rPropertyHolders.Current().setTransformation(aMapping);
+ }
+
+ break;
+ }
+ case META_FONT_ACTION :
+ {
+ /** SIMPLE, DONE */
+ const MetaFontAction* pA = (const MetaFontAction*)pAction;
+ rPropertyHolders.Current().setFont(pA->GetFont());
+ Size aFontSize(pA->GetFont().GetSize());
+
+ if(0 == aFontSize.Height())
+ {
+ // this should not happen but i got Metafiles where this was the
+ // case. A height needs to be guessed (similar to OutputDevice::ImplNewFont())
+ Font aCorrectedFont(pA->GetFont());
+
+ // guess 16 pixel (as in VCL)
+ aFontSize = Size(0, 16);
+
+ // convert to target MapUnit if not pixels
+ aFontSize = Application::GetDefaultDevice()->LogicToLogic(
+ aFontSize, MAP_PIXEL, rPropertyHolders.Current().getMapUnit());
+
+ aCorrectedFont.SetSize(aFontSize);
+ rPropertyHolders.Current().setFont(aCorrectedFont);
+ }
+
+ // older Metafiles have no META_TEXTCOLOR_ACTION which defines
+ // the FontColor now, so use the Font's color when not transparent
+ const Color& rFontColor = pA->GetFont().GetColor();
+ const bool bActivate(COL_TRANSPARENT != rFontColor.GetColor());
+
+ if(bActivate)
+ {
+ rPropertyHolders.Current().setTextColor(rFontColor.getBColor());
+ }
+
+ // caution: do NOT decativate here on transparet, see
+ // OutputDevice::SetFont(..) for more info
+ // rPropertyHolders.Current().setTextColorActive(bActivate);
+
+ // for fill color emulate a MetaTextFillColorAction with !transparent as bool,
+ // see OutputDevice::SetFont(..) the if(mpMetaFile) case
+ if(bActivate)
+ {
+ const Color& rFontFillColor = pA->GetFont().GetFillColor();
+ rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
+ rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor());
+ }
+ else
+ {
+ rPropertyHolders.Current().setTextFillColorActive(false);
+ }
+
+ break;
+ }
+ case META_PUSH_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaPushAction* pA = (const MetaPushAction*)pAction;
+ rPropertyHolders.Push(pA->GetFlags());
+
+ break;
+ }
+ case META_POP_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const bool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & PUSH_CLIPREGION);
+ const bool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & PUSH_RASTEROP);
+
+ if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ // end evtl. clipping
+ const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
+
+ HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
+ }
+
+ if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive())
+ {
+ // end evtl. RasterOp
+ HandleNewRasterOp(ROP_OVERPAINT, rTargetHolders, rPropertyHolders);
+ }
+
+ rPropertyHolders.Pop();
+
+ if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive())
+ {
+ // start evtl. RasterOp
+ HandleNewRasterOp(rPropertyHolders.Current().getRasterOp(), rTargetHolders, rPropertyHolders);
+ }
+
+ if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ // start evtl. clipping
+ HandleNewClipRegion(
+ rPropertyHolders.Current().getClipPolyPolygon(), rTargetHolders, rPropertyHolders);
+ }
+
+ break;
+ }
+ case META_RASTEROP_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaRasterOpAction* pA = (const MetaRasterOpAction*)pAction;
+ const RasterOp aRasterOp = pA->GetRasterOp();
+
+ HandleNewRasterOp(aRasterOp, rTargetHolders, rPropertyHolders);
+
+ break;
+ }
+ case META_TRANSPARENT_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaTransparentAction* pA = (const MetaTransparentAction*)pAction;
+ const basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
+
+ if(aOutline.count())
+ {
+ const sal_uInt16 nTransparence(pA->GetTransparence());
+
+ if(0 == nTransparence)
+ {
+ // not transparent
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+ else if(nTransparence >= 100)
+ {
+ // fully or more than transparent
+ }
+ else
+ {
+ // transparent. Create new target
+ rTargetHolders.Push();
+
+ // create primitives there and get them
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ const drawinglayer::primitive2d::Primitive2DSequence aSubContent(
+ rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()));
+
+ // back to old target
+ rTargetHolders.Pop();
+
+ if(aSubContent.hasElements())
+ {
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ aSubContent,
+ nTransparence * 0.01));
+ }
+ }
+ }
+
+ break;
+ }
+ case META_EPS_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ // To support this action, i have added a EpsPrimitive2D which will
+ // by default decompose to the Metafile replacement data. To support
+ // this EPS on screen, the renderer visualizing this has to support
+ // that primitive and visualize the Eps file (e.g. printing)
+ const MetaEPSAction* pA = (const MetaEPSAction*)pAction;
+ const Rectangle aRectangle(pA->GetPoint(), pA->GetSize());
+
+ if(!aRectangle.IsEmpty())
+ {
+ // create object transform
+ basegfx::B2DHomMatrix aObjectTransform;
+
+ aObjectTransform.set(0, 0, aRectangle.GetWidth());
+ aObjectTransform.set(1, 1, aRectangle.GetHeight());
+ aObjectTransform.set(0, 2, aRectangle.Left());
+ aObjectTransform.set(1, 2, aRectangle.Top());
+
+ // add current transformation
+ aObjectTransform = rPropertyHolders.Current().getTransformation() * aObjectTransform;
+
+ // embed using EpsPrimitive
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::EpsPrimitive2D(
+ aObjectTransform,
+ pA->GetLink(),
+ pA->GetSubstitute()));
+ }
+
+ break;
+ }
+ case META_REFPOINT_ACTION :
+ {
+ /** SIMPLE, DONE */
+ // only used for hatch and line pattern offsets, pretty much no longer
+ // supported today
+ // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction;
+ break;
+ }
+ case META_TEXTLINECOLOR_ACTION :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextLineColorAction* pA = (const MetaTextLineColorAction*)pAction;
+ const bool bActive(pA->IsSetting());
+
+ rPropertyHolders.Current().setTextLineColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setTextLineColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case META_TEXTLINE_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ // actually creates overline, underline and strikeouts, so
+ // these should be isolated from TextDecoratedPortionPrimitive2D
+ // to own primitives. Done, available now.
+ //
+ // This Metaaction seems not to be used (was not used in any
+ // checked files). It's used in combination with the current
+ // Font.
+ const MetaTextLineAction* pA = (const MetaTextLineAction*)pAction;
+
+ proccessMetaTextLineAction(
+ *pA,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+
+ break;
+ }
+ case META_FLOATTRANSPARENT_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*)pAction;
+ const Rectangle aTargetRectangle(pA->GetPoint(), pA->GetSize());
+
+ if(!aTargetRectangle.IsEmpty())
+ {
+ const GDIMetaFile& rContent = pA->GetGDIMetaFile();
+
+ if(rContent.GetActionCount())
+ {
+ // create the sub-content with no embedding specific to the
+ // sub-metafile, this seems not to be used.
+ drawinglayer::primitive2d::Primitive2DSequence xSubContent;
+ {
+ rTargetHolders.Push();
+ // #i# for sub-Mteafile contents, do start with new, default render state
+ rPropertyHolders.PushDefault();
+ interpretMetafile(rContent, rTargetHolders, rPropertyHolders, rViewInformation);
+ xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
+ rPropertyHolders.Pop();
+ rTargetHolders.Pop();
+ }
+
+ if(xSubContent.hasElements())
+ {
+ // check if gradient is a real gradient
+ const Gradient& rGradient = pA->GetGradient();
+ const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+
+ if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ {
+ // not really a gradient; create UnifiedTransparencePrimitive2D
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ xSubContent,
+ aAttribute.getStartColor().luminance()));
+ }
+ else
+ {
+ // really a gradient. Create gradient sub-content (with correct scaling)
+ basegfx::B2DRange aRange(
+ aTargetRectangle.Left(), aTargetRectangle.Top(),
+ aTargetRectangle.Right(), aTargetRectangle.Bottom());
+ aRange.transform(rPropertyHolders.Current().getTransformation());
+
+ // prepare gradient for transparent content
+ const drawinglayer::primitive2d::Primitive2DReference xTransparence(
+ new drawinglayer::primitive2d::FillGradientPrimitive2D(
+ aRange,
+ aAttribute));
+
+ // create transparence primitive
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::TransparencePrimitive2D(
+ xSubContent,
+ drawinglayer::primitive2d::Primitive2DSequence(&xTransparence, 1)));
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ case META_GRADIENTEX_ACTION :
+ {
+ /** SIMPLE, DONE */
+ // This is only a data holder which is interpreted inside comment actions,
+ // see META_COMMENT_ACTION for more info
+ // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction;
+ break;
+ }
+ case META_LAYOUTMODE_ACTION :
+ {
+ /** SIMPLE, DONE */
+ const MetaLayoutModeAction* pA = (const MetaLayoutModeAction*)pAction;
+ rPropertyHolders.Current().setLayoutMode(pA->GetLayoutMode());
+ break;
+ }
+ case META_TEXTLANGUAGE_ACTION :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextLanguageAction* pA = (const MetaTextLanguageAction*)pAction;
+ rPropertyHolders.Current().setLanguageType(pA->GetTextLanguage());
+ break;
+ }
+ case META_OVERLINECOLOR_ACTION :
+ {
+ /** SIMPLE, DONE */
+ const MetaOverlineColorAction* pA = (const MetaOverlineColorAction*)pAction;
+ const bool bActive(pA->IsSetting());
+
+ rPropertyHolders.Current().setOverlineColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setOverlineColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case META_COMMENT_ACTION :
+ {
+ /** CHECKED, WORKS WELL */
+ // I already implemented
+ // XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END
+ // XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END,
+ // but opted to remove these again; it works well without them
+ // and makes the code less dependent from those Metafile Add-Ons
+ const MetaCommentAction* pA = (const MetaCommentAction*)pAction;
+
+ if(COMPARE_EQUAL == pA->GetComment().CompareIgnoreCaseToAscii("XGRAD_SEQ_BEGIN"))
+ {
+ // XGRAD_SEQ_BEGIN, XGRAD_SEQ_END should be supported since the
+ // pure recorded paint of the gradients uses the XOR paint functionality
+ // ('trick'). This is (and will be) broblematic with AntAliasing, so it's
+ // better to use this info
+ const MetaGradientExAction* pMetaGradientExAction = 0;
+ bool bDone(false);
+ sal_uInt32 b(nAction + 1);
+
+ for(; !bDone && b < nCount; b++)
+ {
+ pAction = rMetaFile.GetAction(b);
+
+ if(META_GRADIENTEX_ACTION == pAction->GetType())
+ {
+ pMetaGradientExAction = (const MetaGradientExAction*)pAction;
+ }
+ else if(META_COMMENT_ACTION == pAction->GetType())
+ {
+ if(COMPARE_EQUAL == ((const MetaCommentAction*)pAction)->GetComment().CompareIgnoreCaseToAscii("XGRAD_SEQ_END"))
+ {
+ bDone = true;
+ }
+ }
+ }
+
+ if(bDone && pMetaGradientExAction)
+ {
+ // consume actions and skip forward
+ nAction = b - 1;
+
+ // get geometry data
+ basegfx::B2DPolyPolygon aPolyPolygon(pMetaGradientExAction->GetPolyPolygon().getB2DPolyPolygon());
+
+ if(aPolyPolygon.count())
+ {
+ // transform geometry
+ aPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
+
+ // get and check if gradient is a real gradient
+ const Gradient& rGradient = pMetaGradientExAction->GetGradient();
+ const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+
+ if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ {
+ // not really a gradient
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ aPolyPolygon,
+ aAttribute.getStartColor()));
+ }
+ else
+ {
+ // really a gradient
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
+ aPolyPolygon,
+ aAttribute));
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ OSL_FAIL("Unknown MetaFile Action (!)");
+ break;
+ }
+ }
+ }
+ }
+} // end of anonymous namespace
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace drawinglayer
+{
+ namespace primitive2d
+ {
+ Primitive2DSequence MetafilePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // prepare target and porperties; each will have one default entry
+ TargetHolders aTargetHolders;
+ PropertyHolders aPropertyHolders;
+
+ // set target MapUnit at Properties
+ aPropertyHolders.Current().setMapUnit(getMetaFile().GetPrefMapMode().GetMapUnit());
+
+ // interpret the Metafile
+ interpretMetafile(getMetaFile(), aTargetHolders, aPropertyHolders, rViewInformation);
+
+ // get the content. There should be ony one target, as in the start condition,
+ // but iterating will be the right thing to do when some push/pop is not closed
+ Primitive2DSequence xRetval;
+
+ while(aTargetHolders.size() > 1)
+ {
+ appendPrimitive2DSequenceToPrimitive2DSequence(xRetval,
+ aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
+ aTargetHolders.Pop();
+ }
+
+ appendPrimitive2DSequenceToPrimitive2DSequence(xRetval,
+ aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
+
+ if(xRetval.hasElements())
+ {
+ // get target size
+ const Rectangle aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize());
+
+ // create transformation
+ basegfx::B2DHomMatrix aAdaptedTransform;
+
+ aAdaptedTransform.translate(-aMtfTarget.Left(), -aMtfTarget.Top());
+ aAdaptedTransform.scale(
+ aMtfTarget.getWidth() ? 1.0 / aMtfTarget.getWidth() : 1.0,
+ aMtfTarget.getHeight() ? 1.0 / aMtfTarget.getHeight() : 1.0);
+ aAdaptedTransform = getTransform() * aAdaptedTransform;
+
+ // embed to target transformation
+ const Primitive2DReference aEmbeddedTransform(
+ new TransformPrimitive2D(
+ aAdaptedTransform,
+ xRetval));
+
+ xRetval = Primitive2DSequence(&aEmbeddedTransform, 1);
+ }
+
+ return xRetval;
+ }
+
+ MetafilePrimitive2D::MetafilePrimitive2D(
+ const basegfx::B2DHomMatrix& rMetaFileTransform,
+ const GDIMetaFile& rMetaFile)
+ : BufferedDecompositionPrimitive2D(),
+ maMetaFileTransform(rMetaFileTransform),
+ maMetaFile(rMetaFile)
+ {
+ }
+
+ bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const MetafilePrimitive2D& rCompare = (MetafilePrimitive2D&)rPrimitive;
+
+ return (getTransform() == rCompare.getTransform()
+ && getMetaFile() == rCompare.getMetaFile());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange MetafilePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // use own implementation to quickly answer the getB2DRange question. The
+ // MetafilePrimitive2D assumes that all geometry is inside of the shape. If
+ // this is not the case (i have already seen some wrong Metafiles) it should
+ // be embedded to a MaskPrimitive2D
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+
+ return aRetval;
+ }
+
+ // provide unique ID
+ ImplPrimitrive2DIDBlock(MetafilePrimitive2D, PRIMITIVE2D_ID_METAFILEPRIMITIVE2D)
+
+ } // end of namespace primitive2d
+} // end of namespace drawinglayer
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */