summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drawinglayer/Library_drawinglayer.mk34
-rw-r--r--drawinglayer/inc/emfplushelper.hxx52
-rw-r--r--drawinglayer/inc/wmfemfhelper.hxx215
-rw-r--r--drawinglayer/source/primitive2d/metafileprimitive2d.cxx3196
-rw-r--r--drawinglayer/source/tools/emfpbrush.cxx328
-rw-r--r--drawinglayer/source/tools/emfpbrush.hxx130
-rw-r--r--drawinglayer/source/tools/emfpcustomlinecap.cxx158
-rw-r--r--drawinglayer/source/tools/emfpcustomlinecap.hxx45
-rw-r--r--drawinglayer/source/tools/emfpfont.cxx69
-rw-r--r--drawinglayer/source/tools/emfpfont.hxx40
-rw-r--r--drawinglayer/source/tools/emfphelperdata.cxx1232
-rw-r--r--drawinglayer/source/tools/emfphelperdata.hxx252
-rw-r--r--drawinglayer/source/tools/emfpimage.cxx95
-rw-r--r--drawinglayer/source/tools/emfpimage.hxx42
-rw-r--r--drawinglayer/source/tools/emfplushelper.cxx47
-rw-r--r--drawinglayer/source/tools/emfppath.cxx192
-rw-r--r--drawinglayer/source/tools/emfppath.hxx47
-rw-r--r--drawinglayer/source/tools/emfppen.cxx301
-rw-r--r--drawinglayer/source/tools/emfppen.hxx77
-rw-r--r--drawinglayer/source/tools/emfpregion.cxx90
-rw-r--r--drawinglayer/source/tools/emfpregion.hxx42
-rw-r--r--drawinglayer/source/tools/emfpstringformat.cxx61
-rw-r--r--drawinglayer/source/tools/emfpstringformat.hxx52
-rw-r--r--drawinglayer/source/tools/wmfemfhelper.cxx3119
24 files changed, 6752 insertions, 3164 deletions
diff --git a/drawinglayer/Library_drawinglayer.mk b/drawinglayer/Library_drawinglayer.mk
index 32daccd9d43e..f9e61604fce7 100644
--- a/drawinglayer/Library_drawinglayer.mk
+++ b/drawinglayer/Library_drawinglayer.mk
@@ -9,6 +9,11 @@
$(eval $(call gb_Library_Library,drawinglayer))
+$(eval $(call gb_Library_set_include,drawinglayer,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/drawinglayer/inc \
+))
+
$(eval $(call gb_Library_add_defs,drawinglayer,\
-DDRAWINGLAYER_DLLIMPLEMENTATION \
))
@@ -68,7 +73,7 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\
drawinglayer/source/primitive2d/bitmapprimitive2d \
drawinglayer/source/primitive2d/borderlineprimitive2d \
drawinglayer/source/primitive2d/controlprimitive2d \
- drawinglayer/source/primitive2d/cropprimitive2d \
+ drawinglayer/source/primitive2d/cropprimitive2d \
drawinglayer/source/primitive2d/discretebitmapprimitive2d \
drawinglayer/source/primitive2d/discreteshadowprimitive2d \
drawinglayer/source/primitive2d/embedded3dprimitive2d \
@@ -76,7 +81,7 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\
drawinglayer/source/primitive2d/fillgraphicprimitive2d \
drawinglayer/source/primitive2d/fillgradientprimitive2d \
drawinglayer/source/primitive2d/fillhatchprimitive2d \
- drawinglayer/source/primitive2d/graphicprimitivehelper2d \
+ drawinglayer/source/primitive2d/graphicprimitivehelper2d \
drawinglayer/source/primitive2d/graphicprimitive2d \
drawinglayer/source/primitive2d/gridprimitive2d \
drawinglayer/source/primitive2d/groupprimitive2d \
@@ -90,7 +95,7 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\
drawinglayer/source/primitive2d/modifiedcolorprimitive2d \
drawinglayer/source/primitive2d/objectinfoprimitive2d \
drawinglayer/source/primitive2d/pagepreviewprimitive2d \
- drawinglayer/source/primitive2d/patternfillprimitive2d \
+ drawinglayer/source/primitive2d/patternfillprimitive2d \
drawinglayer/source/primitive2d/pointarrayprimitive2d \
drawinglayer/source/primitive2d/polygonprimitive2d \
drawinglayer/source/primitive2d/polypolygonprimitive2d \
@@ -99,8 +104,8 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\
drawinglayer/source/primitive2d/sdrdecompositiontools2d \
drawinglayer/source/primitive2d/shadowprimitive2d \
drawinglayer/source/primitive2d/structuretagprimitive2d \
- drawinglayer/source/primitive2d/svggradientprimitive2d \
- drawinglayer/source/primitive2d/textbreakuphelper \
+ drawinglayer/source/primitive2d/svggradientprimitive2d \
+ drawinglayer/source/primitive2d/textbreakuphelper \
drawinglayer/source/primitive2d/textdecoratedprimitive2d \
drawinglayer/source/primitive2d/texteffectprimitive2d \
drawinglayer/source/primitive2d/textenumsprimitive2d \
@@ -134,7 +139,7 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\
drawinglayer/source/primitive3d/textureprimitive3d \
drawinglayer/source/primitive3d/transformprimitive3d \
drawinglayer/source/processor2d/baseprocessor2d \
- drawinglayer/source/processor2d/processor2dtools \
+ drawinglayer/source/processor2d/processor2dtools \
drawinglayer/source/processor2d/contourextractor2d \
drawinglayer/source/processor2d/getdigitlanguage \
drawinglayer/source/processor2d/helperwrongspellrenderer \
@@ -154,9 +159,20 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\
drawinglayer/source/processor3d/shadow3dextractor \
drawinglayer/source/processor3d/zbufferprocessor3d \
drawinglayer/source/texture/texture3d \
- drawinglayer/source/tools/converters \
- drawinglayer/source/drawinglayeruno/drawinglayeruno \
- drawinglayer/source/drawinglayeruno/xprimitive2drenderer \
+ drawinglayer/source/tools/converters \
+ drawinglayer/source/tools/emfplushelper \
+ drawinglayer/source/tools/emfphelperdata \
+ drawinglayer/source/tools/emfpbrush \
+ drawinglayer/source/tools/emfppath \
+ drawinglayer/source/tools/emfppen \
+ drawinglayer/source/tools/emfpregion \
+ drawinglayer/source/tools/emfpimage \
+ drawinglayer/source/tools/emfpfont \
+ drawinglayer/source/tools/emfpstringformat \
+ drawinglayer/source/tools/emfpcustomlinecap \
+ drawinglayer/source/tools/wmfemfhelper \
+ drawinglayer/source/drawinglayeruno/drawinglayeruno \
+ drawinglayer/source/drawinglayeruno/xprimitive2drenderer \
drawinglayer/source/texture/texture \
drawinglayer/source/dumper/XShapeDumper \
drawinglayer/source/dumper/EnhancedShapeDumper \
diff --git a/drawinglayer/inc/emfplushelper.hxx b/drawinglayer/inc/emfplushelper.hxx
new file mode 100644
index 000000000000..1a685a631abd
--- /dev/null
+++ b/drawinglayer/inc/emfplushelper.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DRAWINGLAYER_INC_EMFPLUSHELPER_HXX
+#define INCLUDED_DRAWINGLAYER_INC_EMFPLUSHELPER_HXX
+
+#include <sal/config.h>
+
+ /// predefines
+namespace emfplushelper { struct EmfPlusHelperData; }
+namespace wmfemfhelper { class TargetHolders; }
+namespace wmfemfhelper { class PropertyHolders; }
+
+namespace emfplushelper
+{
+ /// EMF+ data holder
+ class EmfPlusHelper
+ {
+ private:
+ EmfPlusHelperData* mpD;
+
+ public:
+ EmfPlusHelper(SvMemoryStream& rMemoryStream);
+ ~EmfPlusHelper();
+
+ void processEmfPlusData(
+ SvMemoryStream& rMemoryStream,
+ wmfemfhelper::TargetHolders& rTargetHolders,
+ wmfemfhelper::PropertyHolders& rPropertyHolders,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation);
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/inc/wmfemfhelper.hxx b/drawinglayer/inc/wmfemfhelper.hxx
new file mode 100644
index 000000000000..699b065795df
--- /dev/null
+++ b/drawinglayer/inc/wmfemfhelper.hxx
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DRAWINGLAYER_INC_WMFEMFHELPER_HXX
+#define INCLUDED_DRAWINGLAYER_INC_WMFEMFHELPER_HXX
+
+#include <sal/config.h>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+
+// predefines
+namespace drawinglayer { namespace geometry { class ViewInformation2D; }}
+class GDIMetaFile;
+namespace wmfemfhelper { class PropertyHolder; }
+
+namespace wmfemfhelper
+{
+ /** Helper class to buffer and hold a Primitive target vector. It
+ encapsulates the new/delete functionality and allows 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();
+ ~TargetHolder();
+ sal_uInt32 size() const;
+ void append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate);
+ drawinglayer::primitive2d::Primitive2DContainer getPrimitive2DSequence(const PropertyHolder& rPropertyHolder);
+ };
+}
+
+namespace wmfemfhelper
+{
+ /** Helper class which builds a stack on the TargetHolder class */
+ class TargetHolders
+ {
+ private:
+ std::vector< TargetHolder* > maTargetHolders;
+
+ public:
+ TargetHolders();
+ sal_uInt32 size() const;
+ void Push();
+ void Pop();
+ TargetHolder& Current();
+ ~TargetHolders();
+ };
+}
+
+namespace wmfemfhelper
+{
+ /** helper class for graphic context
+
+ This class allows to hold a complete representation of classic
+ VCL OutputDevice state. 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.
+ vcl::Font maFont;
+ RasterOp maRasterOp;
+ ComplexTextLayoutFlags mnLayoutMode;
+ LanguageType maLanguageType;
+ PushFlags mnPushFlags;
+
+ /// 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();
+
+ /// 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 vcl::Font& getFont() const { return maFont; }
+ void setFont(const vcl::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 (RasterOp::Xor == maRasterOp || RasterOp::Invert == maRasterOp); }
+ bool isRasterOpForceBlack() const { return RasterOp::N0 == maRasterOp; }
+ bool isRasterOpActive() const { return isRasterOpInvert() || isRasterOpForceBlack(); }
+
+ ComplexTextLayoutFlags getLayoutMode() const { return mnLayoutMode; }
+ void setLayoutMode(ComplexTextLayoutFlags nNew) { if (nNew != mnLayoutMode) mnLayoutMode = nNew; }
+
+ LanguageType getLanguageType() const { return maLanguageType; }
+ void setLanguageType(LanguageType aNew) { if (aNew != maLanguageType) maLanguageType = aNew; }
+
+ PushFlags getPushFlags() const { return mnPushFlags; }
+ void setPushFlags(PushFlags nNew) { if (nNew != mnPushFlags) mnPushFlags = nNew; }
+
+ bool getLineOrFillActive() const { return (mbLineColor || mbFillColor); }
+ };
+}
+
+namespace wmfemfhelper
+{
+ /** 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();
+ void PushDefault();
+ void Push(PushFlags nPushFlags);
+ void Pop();
+ PropertyHolder& Current();
+ ~PropertyHolders();
+ };
+}
+
+namespace wmfemfhelper
+{
+ drawinglayer::primitive2d::Primitive2DContainer interpretMetafile(
+ const GDIMetaFile& rMetaFile,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation);
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
index 4737f78c765b..65ec52c894de 100644
--- a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
@@ -18,3171 +18,57 @@
*/
#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/fillgraphicprimitive2d.hxx>
-#include <drawinglayer/primitive2d/wallpaperprimitive2d.hxx>
-#include <drawinglayer/primitive2d/textprimitive2d.hxx>
-#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
-#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
-#include <i18nlangtag/languagetag.hxx>
-#include <drawinglayer/primitive2d/textlineprimitive2d.hxx>
-#include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx>
-#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
-#include <tools/fract.hxx>
-#include <numeric>
-
+#include <wmfemfhelper.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/fillgraphicprimitive2d.hxx>
+//#include <drawinglayer/primitive2d/wallpaperprimitive2d.hxx>
+//#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+//#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+//#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+//#include <i18nlangtag/languagetag.hxx>
+//#include <drawinglayer/primitive2d/textlineprimitive2d.hxx>
+//#include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx>
+//#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
+//#include <tools/fract.hxx>
+//#include <numeric>
+//#include <emfplushelper.hxx>
using namespace com::sun::star;
-
-namespace
-{
- /** helper class for graphic context
-
- This class allows to hold a complete representation of classic
- VCL OutputDevice state. 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.
- vcl::Font maFont;
- RasterOp maRasterOp;
- ComplexTextLayoutFlags mnLayoutMode;
- LanguageType maLanguageType;
- PushFlags mnPushFlags;
-
- /// 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(MapUnit::Map100thMM),
- maLineColor(),
- maFillColor(),
- maTextColor(COL_BLACK),
- maTextFillColor(),
- maTextLineColor(),
- maOverlineColor(),
- maClipPolyPoygon(),
- maFont(),
- maRasterOp(RasterOp::OverPaint),
- mnLayoutMode(ComplexTextLayoutFlags::Default),
- maLanguageType(0),
- mnPushFlags(PushFlags::NONE),
- mbLineColor(false),
- mbFillColor(false),
- mbTextColor(true),
- mbTextFillColor(false),
- mbTextLineColor(false),
- mbOverlineColor(false),
- mbClipPolyPolygonActive(false)
- {
- }
-
- /// 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 vcl::Font& getFont() const { return maFont; }
- void setFont(const vcl::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 (RasterOp::Xor == maRasterOp || RasterOp::Invert == maRasterOp); }
- bool isRasterOpForceBlack() const { return RasterOp::N0 == maRasterOp; }
- bool isRasterOpActive() const { return isRasterOpInvert() || isRasterOpForceBlack(); }
-
- ComplexTextLayoutFlags getLayoutMode() const { return mnLayoutMode; }
- void setLayoutMode(ComplexTextLayoutFlags nNew) { if(nNew != mnLayoutMode) mnLayoutMode = nNew; }
-
- LanguageType getLanguageType() const { return maLanguageType; }
- void setLanguageType(LanguageType aNew) { if(aNew != maLanguageType) maLanguageType = aNew; }
-
- PushFlags getPushFlags() const { return mnPushFlags; }
- void setPushFlags(PushFlags 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());
- }
-
- void PushDefault()
- {
- PropertyHolder* pNew = new PropertyHolder();
- maPropertyHolders.push_back(pNew);
- }
-
- void Push(PushFlags nPushFlags)
- {
- if(bool(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 PushFlags nPushFlags(pTip->getPushFlags());
-
- if(nPushFlags != PushFlags::NONE)
- {
- if(nSize > 1)
- {
- // copy back content for all non-set flags
- PropertyHolder* pLast = maPropertyHolders[nSize - 2];
-
- if(PushFlags::ALL != nPushFlags)
- {
- if(!(nPushFlags & PushFlags::LINECOLOR ))
- {
- pLast->setLineColor(pTip->getLineColor());
- pLast->setLineColorActive(pTip->getLineColorActive());
- }
- if(!(nPushFlags & PushFlags::FILLCOLOR ))
- {
- pLast->setFillColor(pTip->getFillColor());
- pLast->setFillColorActive(pTip->getFillColorActive());
- }
- if(!(nPushFlags & PushFlags::FONT ))
- {
- pLast->setFont(pTip->getFont());
- }
- if(!(nPushFlags & PushFlags::TEXTCOLOR ))
- {
- pLast->setTextColor(pTip->getTextColor());
- pLast->setTextColorActive(pTip->getTextColorActive());
- }
- if(!(nPushFlags & PushFlags::MAPMODE ))
- {
- pLast->setTransformation(pTip->getTransformation());
- pLast->setMapUnit(pTip->getMapUnit());
- }
- if(!(nPushFlags & PushFlags::CLIPREGION ))
- {
- pLast->setClipPolyPolygon(pTip->getClipPolyPolygon());
- pLast->setClipPolyPolygonActive(pTip->getClipPolyPolygonActive());
- }
- if(!(nPushFlags & PushFlags::RASTEROP ))
- {
- pLast->setRasterOp(pTip->getRasterOp());
- }
- if(!(nPushFlags & PushFlags::TEXTFILLCOLOR ))
- {
- pLast->setTextFillColor(pTip->getTextFillColor());
- pLast->setTextFillColorActive(pTip->getTextFillColorActive());
- }
- if(!(nPushFlags & PushFlags::TEXTALIGN ))
- {
- if(pLast->getFont().GetAlignment() != pTip->getFont().GetAlignment())
- {
- vcl::Font aFont(pLast->getFont());
- aFont.SetAlignment(pTip->getFont().GetAlignment());
- pLast->setFont(aFont);
- }
- }
- if(!(nPushFlags & PushFlags::REFPOINT ))
- {
- // not supported
- }
- if(!(nPushFlags & PushFlags::TEXTLINECOLOR ))
- {
- pLast->setTextLineColor(pTip->getTextLineColor());
- pLast->setTextLineColorActive(pTip->getTextLineColorActive());
- }
- if(!(nPushFlags & PushFlags::TEXTLAYOUTMODE ))
- {
- pLast->setLayoutMode(pTip->getLayoutMode());
- }
- if(!(nPushFlags & PushFlags::TEXTLANGUAGE ))
- {
- pLast->setLanguageType(pTip->getLanguageType());
- }
- if(!(nPushFlags & PushFlags::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.empty())
- {
- delete maPropertyHolders.back();
- maPropertyHolders.pop_back();
- }
- }
- };
-} // end of anonymous namespace
-
-
-namespace
-{
- /** helper to convert a vcl::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 vcl::Region& rRegion)
- {
- basegfx::B2DPolyPolygon aRetval;
-
- if(!rRegion.IsEmpty())
- {
- aRetval = rRegion.GetAsB2DPolyPolygon();
- }
-
- return aRetval;
- }
-} // end of anonymous namespace
-
-
-namespace
-{
- /** Helper class to buffer and hold a Primitive target vector. It
- encapsulates the new/delete functionality and allows 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() const
- {
- return aTargets.size();
- }
-
- void append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate)
- {
- if(pCandidate)
- {
- aTargets.push_back(pCandidate);
- }
- }
-
- drawinglayer::primitive2d::Primitive2DContainer getPrimitive2DSequence(const PropertyHolder& rPropertyHolder)
- {
- const sal_uInt32 nCount(aTargets.size());
- drawinglayer::primitive2d::Primitive2DContainer 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.empty() && 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::Primitive2DContainer { xMask };
- }
- }
-
- 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() const
- {
- return maTargetHolders.size();
- }
-
- void Push()
- {
- maTargetHolders.push_back(new TargetHolder());
- }
-
- void Pop()
- {
- OSL_ENSURE(maTargetHolders.size(), "TargetHolders: POP with no property holders (!)");
- if(!maTargetHolders.empty())
- {
- delete maTargetHolders.back();
- maTargetHolders.pop_back();
- }
- }
-
- TargetHolder& Current()
- {
- static TargetHolder aDummy;
- OSL_ENSURE(maTargetHolders.size(), "TargetHolders: CURRENT with no property holders (!)");
- return maTargetHolders.empty() ? aDummy : *maTargetHolders.back();
- }
-
- ~TargetHolders()
- {
- while(!maTargetHolders.empty())
- {
- 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 overrides the decomposition of the C++
- implementation class to do an alternative decomposition.
- */
- class NonOverlappingFillGradientPrimitive2D : public FillGradientPrimitive2D
- {
- protected:
- /// local decomposition.
- virtual void create2DDecomposition(Primitive2DContainer& rContainer,
- const geometry::ViewInformation2D& rViewInformation) const override;
-
- public:
- /// constructor
- NonOverlappingFillGradientPrimitive2D(
- const basegfx::B2DRange& rObjectRange,
- const attribute::FillGradientAttribute& rFillGradient)
- : FillGradientPrimitive2D(rObjectRange, rFillGradient)
- {
- }
- };
-
- void NonOverlappingFillGradientPrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer,
- const geometry::ViewInformation2D& /*rViewInformation*/) const
- {
- if(!getFillGradient().isDefault())
- {
- createFill(rContainer, false);
- }
- }
- } // 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,
- const basegfx::BColor& rBColor)
- {
- if(!rPositions.empty())
- {
- if(rProperties.getTransformation().isIdentity())
- {
- rTarget.append(
- new drawinglayer::primitive2d::PointArrayPrimitive2D(
- rPositions,
- rBColor));
- }
- else
- {
- std::vector< basegfx::B2DPoint > aPositions(rPositions);
-
- for(basegfx::B2DPoint & aPosition : aPositions)
- {
- aPosition = rProperties.getTransformation() * aPosition;
- }
-
- rTarget.append(
- new drawinglayer::primitive2d::PointArrayPrimitive2D(
- aPositions,
- rBColor));
- }
- }
- }
-
- /** 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(LineStyle::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(),
- rLineInfo.GetLineCap());
-
- 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 GradientStyle::Linear :
- {
- aGradientStyle = drawinglayer::attribute::GradientStyle::Linear;
- break;
- }
- case GradientStyle::Axial :
- {
- aGradientStyle = drawinglayer::attribute::GradientStyle::Axial;
- break;
- }
- case GradientStyle::Radial :
- {
- aGradientStyle = drawinglayer::attribute::GradientStyle::Radial;
- break;
- }
- case GradientStyle::Elliptical :
- {
- aGradientStyle = drawinglayer::attribute::GradientStyle::Elliptical;
- break;
- }
- case GradientStyle::Square :
- {
- aGradientStyle = drawinglayer::attribute::GradientStyle::Square;
- break;
- }
- default : // GradientStyle::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 HatchStyle::Single :
- {
- aHatchStyle = drawinglayer::attribute::HatchStyle::Single;
- break;
- }
- case HatchStyle::Double :
- {
- aHatchStyle = drawinglayer::attribute::HatchStyle::Double;
- break;
- }
- case HatchStyle::Triple :
- {
- aHatchStyle = drawinglayer::attribute::HatchStyle::Triple;
- break;
- }
- }
-
- return drawinglayer::attribute::FillHatchAttribute(
- aHatchStyle,
- (double)rHatch.GetDistance(),
- (double)rHatch.GetAngle() * F_PI1800,
- rHatch.GetColor().getBColor(),
- 3, // same default as VCL, a minimum of three discrete units (pixels) offset
- false);
- }
-
- /** helper to take needed action on ClipRegion change. This method needs to be called
- on any vcl::Region change, e.g. at the obvious actions doing this, but also at pop-calls
- which change the vcl::Region of the current context. It takes care of creating the
- current embedded context, set the new vcl::Region at the context and possibly prepare
- a new target for including new geometry into the current region
- */
- void HandleNewClipRegion(
- const basegfx::B2DPolyPolygon& rClipPolyPolygon,
- TargetHolders& rTargetHolders,
- PropertyHolders& rPropertyHolders)
- {
- const bool bNewActive(rClipPolyPolygon.count());
-
- // #i108636# The handling 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 unnecessarily 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::Primitive2DContainer aSubContent;
-
- if(rPropertyHolders.Current().getClipPolyPolygon().count()
- && rTargetHolders.Current().size())
- {
- aSubContent = rTargetHolders.Current().getPrimitive2DSequence(
- rPropertyHolders.Current());
- }
-
- rTargetHolders.Pop();
-
- if(!aSubContent.empty())
- {
- 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 changing RasterOp is used. Currently, RasterOp::Xor and RasterOp::Invert are supported using
- InvertPrimitive2D, and RasterOp::N0 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::Primitive2DContainer aSubContent;
-
- if(rTargetHolders.Current().size())
- {
- aSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
- }
-
- rTargetHolders.Pop();
-
- if(!aSubContent.empty())
- {
- if(rPropertyHolders.Current().isRasterOpForceBlack())
- {
- // force content to black
- rTargetHolders.Current().append(
- new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
- aSubContent,
- basegfx::BColorModifierSharedPtr(
- new basegfx::BColorModifier_replace(
- 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::Primitive2DContainer xSeq { xPrim };
-
- 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 wanted 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()
- || (WallpaperStyle::Tile != eWallpaperStyle && WallpaperStyle::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::Primitive2DContainer { xPrim }));
- }
- }
-
- /** helper to decide UnderlineAbove for text primitives */
- bool isUnderlineAbove(const vcl::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 vcl::Font& rFont = rProperty.getFont();
- basegfx::B2DVector aFontScaling;
-
- rFontAttribute = drawinglayer::attribute::FontAttribute(
- drawinglayer::primitive2d::getFontAttributeFromVclFont(
- aFontScaling,
- rFont,
- bool(rProperty.getLayoutMode() & ComplexTextLayoutFlags::BiDiRtl),
- bool(rProperty.getLayoutMode() & ComplexTextLayoutFlags::BiDiStrong)));
-
- // add FontScaling
- rTextTransform.scale(aFontScaling.getX(), aFontScaling.getY());
-
- // take text align into account
- if(ALIGN_BASELINE != rFont.GetAlignment())
- {
- drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
- aTextLayouterDevice.setFont(rFont);
-
- if(ALIGN_TOP == rFont.GetAlignment())
- {
- 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 processMetaTextAction(
- const Point& rTextStartPosition,
- const OUString& rText,
- sal_uInt16 nTextStart,
- sal_uInt16 nTextLength,
- const std::vector< double >& rDXArray,
- TargetHolder& rTarget,
- PropertyHolder& rProperty)
- {
- drawinglayer::primitive2d::BasePrimitive2D* pResult = nullptr;
- const vcl::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 Color aFillColor(rFont.GetFillColor());
- const css::lang::Locale aLocale(LanguageTag(rProperty.getLanguageType()).getLocale());
- const bool bWordLineMode(rFont.IsWordLineMode());
-
- const bool bDecoratedIsNeeded(
- LINESTYLE_NONE != rFont.GetOverline()
- || LINESTYLE_NONE != rFont.GetUnderline()
- || STRIKEOUT_NONE != rFont.GetStrikeout()
- || FontEmphasisMark::NONE != (rFont.GetEmphasisMark() & FontEmphasisMark::Style)
- || FontRelief::NONE != rFont.GetRelief()
- || rFont.IsShadow()
- || bWordLineMode);
-
- if(bDecoratedIsNeeded)
- {
- // prepare overline, underline and strikeout data
- const drawinglayer::primitive2d::TextLine eFontOverline(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rFont.GetOverline()));
- const drawinglayer::primitive2d::TextLine eFontLineStyle(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rFont.GetUnderline()));
- const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rFont.GetStrikeout()));
-
- // check UndelineAbove
- const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE != eFontLineStyle && isUnderlineAbove(rFont));
-
- // prepare emphasis mark data
- drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE);
-
- switch(rFont.GetEmphasisMark() & FontEmphasisMark::Style)
- {
- case FontEmphasisMark::Dot : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT; break;
- case FontEmphasisMark::Circle : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE; break;
- case FontEmphasisMark::Disc : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC; break;
- case FontEmphasisMark::Accent : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT; break;
- default: break;
- }
-
- const bool bEmphasisMarkAbove(rFont.GetEmphasisMark() & FontEmphasisMark::PosAbove);
- const bool bEmphasisMarkBelow(rFont.GetEmphasisMark() & FontEmphasisMark::PosBelow);
-
- // prepare font relief data
- drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
-
- switch(rFont.GetRelief())
- {
- case FontRelief::Embossed : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
- case FontRelief::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,
- aFillColor,
-
- // attributes for TextDecoratedPortionPrimitive2D
- rProperty.getOverlineColorActive() ? rProperty.getOverlineColor() : aFontColor,
- rProperty.getTextLineColorActive() ? rProperty.getTextLineColor() : aFontColor,
- eFontOverline,
- eFontLineStyle,
- 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::Primitive2DContainer 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::Primitive2DContainer { aReference }));
- }
- }
- }
-
- /** 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::mapFontLineStyleToTextLine(rAction.GetOverline()));
- const drawinglayer::primitive2d::TextLine aUnderlineMode(drawinglayer::primitive2d::mapFontLineStyleToTextLine(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 css::lang::Locale aLocale(LanguageTag(
- rProperty.getLanguageType()).getLocale());
-
- 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.empty())
- {
- // add created text primitive to target
- if(rProperty.getTransformation().isIdentity())
- {
- for(drawinglayer::primitive2d::BasePrimitive2D* a : aTargetVector)
- {
- rTarget.append(a);
- }
- }
- else
- {
- // when a transformation is set, embed to it
- drawinglayer::primitive2d::Primitive2DContainer xTargets(aTargetVector.size());
-
- for(size_t 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 which adapted contexts and
- targets as e.g. needed for the MetaActionType::FLOATTRANSPARENT 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 vcl::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
- vcl::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 comments, see the single action implementations.
- */
- void interpretMetafile(
- const GDIMetaFile& rMetaFile,
- TargetHolders& rTargetHolders,
- PropertyHolders& rPropertyHolders,
- const drawinglayer::geometry::ViewInformation2D& rViewInformation)
- {
- const size_t nCount(rMetaFile.GetActionSize());
-
- for(size_t nAction(0); nAction < nCount; nAction++)
- {
- MetaAction* pAction = rMetaFile.GetAction(nAction);
-
- switch(pAction->GetType())
- {
- case MetaActionType::NONE :
- {
- /** SIMPLE, DONE */
- break;
- }
- case MetaActionType::PIXEL :
- {
- /** CHECKED, WORKS WELL */
- std::vector< basegfx::B2DPoint > aPositions;
- Color aLastColor(COL_BLACK);
-
- while(MetaActionType::PIXEL == pAction->GetType() && nAction < nCount)
- {
- const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);
-
- if(pA->GetColor() != aLastColor)
- {
- if(!aPositions.empty())
- {
- 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.empty())
- {
- createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
- }
-
- break;
- }
- case MetaActionType::POINT :
- {
- /** CHECKED, WORKS WELL */
- if(rPropertyHolders.Current().getLineColorActive())
- {
- std::vector< basegfx::B2DPoint > aPositions;
-
- while(MetaActionType::POINT == pAction->GetType() && nAction < nCount)
- {
- const MetaPointAction* pA = static_cast<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.empty())
- {
- createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor());
- }
- }
-
- break;
- }
- case MetaActionType::LINE :
- {
- /** CHECKED, WORKS WELL */
- if(rPropertyHolders.Current().getLineColorActive())
- {
- basegfx::B2DPolygon aLinePolygon;
- LineInfo aLineInfo;
-
- while(MetaActionType::LINE == pAction->GetType() && nAction < nCount)
- {
- const MetaLineAction* pA = static_cast<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 MetaActionType::RECT :
- {
- /** CHECKED, WORKS WELL */
- if(rPropertyHolders.Current().getLineOrFillActive())
- {
- const MetaRectAction* pA = static_cast<const MetaRectAction*>(pAction);
- const tools::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 MetaActionType::ROUNDRECT :
- {
- /** 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 = static_cast<const MetaRoundRectAction*>(pAction);
- const tools::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 MetaActionType::ELLIPSE :
- {
- /** CHECKED, WORKS WELL */
- if(rPropertyHolders.Current().getLineOrFillActive())
- {
- const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
- const tools::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 MetaActionType::ARC :
- {
- /** CHECKED, WORKS WELL */
- if(rPropertyHolders.Current().getLineColorActive())
- {
- const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
- const tools::Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Arc);
- const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
-
- createHairlinePrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
- }
-
- break;
- }
- case MetaActionType::PIE :
- {
- /** CHECKED, WORKS WELL */
- if(rPropertyHolders.Current().getLineOrFillActive())
- {
- const MetaPieAction* pA = static_cast<const MetaPieAction*>(pAction);
- const tools::Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Pie);
- const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
-
- createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
- }
-
- break;
- }
- case MetaActionType::CHORD :
- {
- /** CHECKED, WORKS WELL */
- if(rPropertyHolders.Current().getLineOrFillActive())
- {
- const MetaChordAction* pA = static_cast<const MetaChordAction*>(pAction);
- const tools::Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Chord);
- const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
-
- createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
- }
-
- break;
- }
- case MetaActionType::POLYLINE :
- {
- /** CHECKED, WORKS WELL */
- if(rPropertyHolders.Current().getLineColorActive())
- {
- const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
- createLinePrimitive(pA->GetPolygon().getB2DPolygon(), pA->GetLineInfo(), rTargetHolders.Current(), rPropertyHolders.Current());
- }
-
- break;
- }
- case MetaActionType::POLYGON :
- {
- /** CHECKED, WORKS WELL */
- if(rPropertyHolders.Current().getLineOrFillActive())
- {
- const MetaPolygonAction* pA = static_cast<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 MetaActionType::POLYPOLYGON :
- {
- /** CHECKED, WORKS WELL */
- if(rPropertyHolders.Current().getLineOrFillActive())
- {
- const MetaPolyPolygonAction* pA = static_cast<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 MetaActionType::TEXT :
- {
- /** CHECKED, WORKS WELL */
- const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
- sal_uInt32 nTextLength(pA->GetLen());
- const sal_uInt32 nTextIndex(pA->GetIndex());
- const sal_uInt32 nStringLength(pA->GetText().getLength());
-
- if(nTextLength + nTextIndex > nStringLength)
- {
- nTextLength = nStringLength - nTextIndex;
- }
-
- if(nTextLength && rPropertyHolders.Current().getTextColorActive())
- {
- const std::vector< double > aDXArray{};
- processMetaTextAction(
- pA->GetPoint(),
- pA->GetText(),
- nTextIndex,
- nTextLength,
- aDXArray,
- rTargetHolders.Current(),
- rPropertyHolders.Current());
- }
-
- break;
- }
- case MetaActionType::TEXTARRAY :
- {
- /** CHECKED, WORKS WELL */
- const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
- sal_uInt32 nTextLength(pA->GetLen());
- const sal_uInt32 nTextIndex(pA->GetIndex());
- const sal_uInt32 nStringLength(pA->GetText().getLength());
-
- if(nTextLength + nTextIndex > nStringLength)
- {
- nTextLength = nTextIndex > nStringLength ? 0 : nStringLength - nTextIndex;
- }
-
- if(nTextLength && rPropertyHolders.Current().getTextColorActive())
- {
- // preapare DXArray (if used)
- std::vector< double > aDXArray;
- long* pDXArray = pA->GetDXArray();
-
- if(pDXArray)
- {
- aDXArray.reserve(nTextLength);
-
- for(sal_uInt32 a(0); a < nTextLength; a++)
- {
- aDXArray.push_back((double)(*(pDXArray + a)));
- }
- }
-
- processMetaTextAction(
- pA->GetPoint(),
- pA->GetText(),
- nTextIndex,
- nTextLength,
- aDXArray,
- rTargetHolders.Current(),
- rPropertyHolders.Current());
- }
-
- break;
- }
- case MetaActionType::STRETCHTEXT :
- {
- // #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 = static_cast<const MetaStretchTextAction*>(pAction);
- sal_uInt32 nTextLength(pA->GetLen());
- const sal_uInt32 nTextIndex(pA->GetIndex());
- const sal_uInt32 nStringLength(pA->GetText().getLength());
-
- 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(double & a : aTextArray)
- {
- a *= fRelative;
- }
- }
- }
- }
-
- processMetaTextAction(
- pA->GetPoint(),
- pA->GetText(),
- nTextIndex,
- nTextLength,
- aTextArray,
- rTargetHolders.Current(),
- rPropertyHolders.Current());
- }
-
- break;
- }
- case MetaActionType::TEXTRECT :
- {
- /** CHECKED, WORKS WELL */
- // OSL_FAIL("MetaActionType::TEXTRECT requested (!)");
- const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
- const tools::Rectangle& rRectangle = pA->GetRect();
- const sal_uInt32 nStringLength(pA->GetText().getLength());
-
- 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 isolation.
- drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
- aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
- GDIMetaFile aGDIMetaFile;
-
- aTextLayouterDevice.addTextRectActions(
- rRectangle, pA->GetText(), pA->GetStyle(), aGDIMetaFile);
-
- if(aGDIMetaFile.GetActionSize())
- {
- // create sub-content
- drawinglayer::primitive2d::Primitive2DContainer xSubContent;
- {
- rTargetHolders.Push();
-
- // for sub-Mteafile contents, do start with new, default render state
- // #i124686# ...but copy font, this is already set accordingly
- vcl::Font aTargetFont = rPropertyHolders.Current().getFont();
- rPropertyHolders.PushDefault();
- rPropertyHolders.Current().setFont(aTargetFont);
-
- interpretMetafile(aGDIMetaFile, rTargetHolders, rPropertyHolders, rViewInformation);
- xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
- rPropertyHolders.Pop();
- rTargetHolders.Pop();
- }
-
- if(!xSubContent.empty())
- {
- // add with transformation
- rTargetHolders.Current().append(
- new drawinglayer::primitive2d::TransformPrimitive2D(
- rPropertyHolders.Current().getTransformation(),
- xSubContent));
- }
- }
- }
-
- break;
- }
- case MetaActionType::BMP :
- {
- /** CHECKED, WORKS WELL */
- const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction);
- const BitmapEx aBitmapEx(pA->GetBitmap());
-
- createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
-
- break;
- }
- case MetaActionType::BMPSCALE :
- {
- /** CHECKED, WORKS WELL */
- const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
- const Bitmap aBitmapEx(pA->GetBitmap());
-
- createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
-
- break;
- }
- case MetaActionType::BMPSCALEPART :
- {
- /** CHECKED, WORKS WELL */
- const MetaBmpScalePartAction* pA = static_cast<const MetaBmpScalePartAction*>(pAction);
- const Bitmap& rBitmap = pA->GetBitmap();
-
- if(!rBitmap.IsEmpty())
- {
- Bitmap aCroppedBitmap(rBitmap);
- const tools::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 MetaActionType::BMPEX :
- {
- /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */
- const MetaBmpExAction* pA = static_cast<const MetaBmpExAction*>(pAction);
- const BitmapEx& rBitmapEx = pA->GetBitmapEx();
-
- createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
-
- break;
- }
- case MetaActionType::BMPEXSCALE :
- {
- /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */
- const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
- const BitmapEx& rBitmapEx = pA->GetBitmapEx();
-
- createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
-
- break;
- }
- case MetaActionType::BMPEXSCALEPART :
- {
- /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALEPART */
- const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pAction);
- const BitmapEx& rBitmapEx = pA->GetBitmapEx();
-
- if(!rBitmapEx.IsEmpty())
- {
- BitmapEx aCroppedBitmapEx(rBitmapEx);
- const tools::Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
-
- if(!aCropRectangle.IsEmpty())
- {
- aCroppedBitmapEx.Crop(aCropRectangle);
- }
-
- createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
- }
-
- break;
- }
- case MetaActionType::MASK :
- {
- /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */
- /** Huh, no it isn't!? */
- const MetaMaskAction* pA = static_cast<const MetaMaskAction*>(pAction);
- const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
-
- createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
-
- break;
- }
- case MetaActionType::MASKSCALE :
- {
- /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */
- const MetaMaskScaleAction* pA = static_cast<const MetaMaskScaleAction*>(pAction);
- const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
-
- createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
-
- break;
- }
- case MetaActionType::MASKSCALEPART :
- {
- /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALEPART */
- const MetaMaskScalePartAction* pA = static_cast<const MetaMaskScalePartAction*>(pAction);
- const Bitmap& rBitmap = pA->GetBitmap();
-
- if(!rBitmap.IsEmpty())
- {
- Bitmap aCroppedBitmap(rBitmap);
- const tools::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 MetaActionType::GRADIENT :
- {
- /** CHECKED, WORKS WELL */
- const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction);
- const tools::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::Primitive2DContainer xGradient(1);
-
- if(rPropertyHolders.Current().isRasterOpInvert())
- {
- // use a special version of FillGradientPrimitive2D which creates
- // non-overlapping geometry on decomposition to make the 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 implicitly 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 MetaActionType::HATCH :
- {
- /** CHECKED, WORKS WELL */
- const MetaHatchAction* pA = static_cast<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::Primitive2DContainer { aFillHatch }));
- }
-
- break;
- }
- case MetaActionType::WALLPAPER :
- {
- /** CHECKED, WORKS WELL */
- const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pAction);
- tools::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(WallpaperStyle::NONE != 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 MetaActionType::CLIPREGION :
- {
- /** CHECKED, WORKS WELL */
- const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pAction);
-
- if(pA->IsClipping())
- {
- // new clipping. Get tools::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 MetaActionType::ISECTRECTCLIPREGION :
- {
- /** CHECKED, WORKS WELL */
- const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pAction);
- const tools::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 MetaActionType::ISECTREGIONCLIPREGION :
- {
- /** CHECKED, WORKS WELL */
- const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pAction);
- const vcl::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 MetaActionType::MOVECLIPREGION :
- {
- /** CHECKED, WORKS WELL */
- const MetaMoveClipRegionAction* pA = static_cast<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 MetaActionType::LINECOLOR :
- {
- /** CHECKED, WORKS WELL */
- const MetaLineColorAction* pA = static_cast<const MetaLineColorAction*>(pAction);
- const bool bActive(pA->IsSetting());
-
- rPropertyHolders.Current().setLineColorActive(bActive);
- if(bActive)
- rPropertyHolders.Current().setLineColor(pA->GetColor().getBColor());
-
- break;
- }
- case MetaActionType::FILLCOLOR :
- {
- /** CHECKED, WORKS WELL */
- const MetaFillColorAction* pA = static_cast<const MetaFillColorAction*>(pAction);
- const bool bActive(pA->IsSetting());
-
- rPropertyHolders.Current().setFillColorActive(bActive);
- if(bActive)
- rPropertyHolders.Current().setFillColor(pA->GetColor().getBColor());
-
- break;
- }
- case MetaActionType::TEXTCOLOR :
- {
- /** SIMPLE, DONE */
- const MetaTextColorAction* pA = static_cast<const MetaTextColorAction*>(pAction);
- const bool bActivate(COL_TRANSPARENT != pA->GetColor().GetColor());
-
- rPropertyHolders.Current().setTextColorActive(bActivate);
- rPropertyHolders.Current().setTextColor(pA->GetColor().getBColor());
-
- break;
- }
- case MetaActionType::TEXTFILLCOLOR :
- {
- /** SIMPLE, DONE */
- const MetaTextFillColorAction* pA = static_cast<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 MetaActionType::TEXTALIGN :
- {
- /** SIMPLE, DONE */
- const MetaTextAlignAction* pA = static_cast<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().GetAlignment() != aNewTextAlign)
- {
- vcl::Font aNewFont(rPropertyHolders.Current().getFont());
- aNewFont.SetAlignment(aNewTextAlign);
- rPropertyHolders.Current().setFont(aNewFont);
- }
-
- break;
- }
- case MetaActionType::MAPMODE :
- {
- /** CHECKED, WORKS WELL */
- // the most necessary MapMode to be interpreted is MapUnit::MapRelative,
- // but also the others may occur. Even not yet supported ones
- // may need to be added here later
- const MetaMapModeAction* pA = static_cast<const MetaMapModeAction*>(pAction);
- const MapMode& rMapMode = pA->GetMapMode();
- basegfx::B2DHomMatrix aMapping;
-
- if(MapUnit::MapRelative == rMapMode.GetMapUnit())
- {
- aMapping = getTransformFromMapMode(rMapMode);
- }
- else
- {
- switch(rMapMode.GetMapUnit())
- {
- case MapUnit::Map100thMM :
- {
- if(MapUnit::MapTwip == rPropertyHolders.Current().getMapUnit())
- {
- // MapUnit::MapTwip -> MapUnit::Map100thMM
- const double fTwipTo100thMm(127.0 / 72.0);
- aMapping.scale(fTwipTo100thMm, fTwipTo100thMm);
- }
- break;
- }
- case MapUnit::MapTwip :
- {
- if(MapUnit::Map100thMM == rPropertyHolders.Current().getMapUnit())
- {
- // MapUnit::Map100thMM -> MapUnit::MapTwip
- const double f100thMmToTwip(72.0 / 127.0);
- aMapping.scale(f100thMmToTwip, f100thMmToTwip);
- }
- break;
- }
- default :
- {
- OSL_FAIL("interpretMetafile: MetaActionType::MAPMODE 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 MetaActionType::FONT :
- {
- /** SIMPLE, DONE */
- const MetaFontAction* pA = static_cast<const MetaFontAction*>(pAction);
- rPropertyHolders.Current().setFont(pA->GetFont());
- Size aFontSize(pA->GetFont().GetFontSize());
-
- 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())
- vcl::Font aCorrectedFont(pA->GetFont());
-
- // guess 16 pixel (as in VCL)
- aFontSize = Size(0, 16);
-
- // convert to target MapUnit if not pixels
- aFontSize = OutputDevice::LogicToLogic(
- aFontSize, MapUnit::MapPixel, rPropertyHolders.Current().getMapUnit());
-
- aCorrectedFont.SetFontSize(aFontSize);
- rPropertyHolders.Current().setFont(aCorrectedFont);
- }
-
- // older Metafiles have no MetaActionType::TEXTCOLOR 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 deactivate here on transparent, 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 MetaActionType::PUSH :
- {
- /** CHECKED, WORKS WELL */
- const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction);
- rPropertyHolders.Push(pA->GetFlags());
-
- break;
- }
- case MetaActionType::POP :
- {
- /** CHECKED, WORKS WELL */
- const bool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & PushFlags::CLIPREGION);
- const bool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & PushFlags::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(RasterOp::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 MetaActionType::RASTEROP :
- {
- /** CHECKED, WORKS WELL */
- const MetaRasterOpAction* pA = static_cast<const MetaRasterOpAction*>(pAction);
- const RasterOp aRasterOp = pA->GetRasterOp();
-
- HandleNewRasterOp(aRasterOp, rTargetHolders, rPropertyHolders);
-
- break;
- }
- case MetaActionType::Transparent :
- {
- /** CHECKED, WORKS WELL */
- const MetaTransparentAction* pA = static_cast<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::Primitive2DContainer aSubContent(
- rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()));
-
- // back to old target
- rTargetHolders.Pop();
-
- if(!aSubContent.empty())
- {
- rTargetHolders.Current().append(
- new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
- aSubContent,
- nTransparence * 0.01));
- }
- }
- }
-
- break;
- }
- case MetaActionType::EPS :
- {
- /** 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 = static_cast<const MetaEPSAction*>(pAction);
- const tools::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 MetaActionType::REFPOINT :
- {
- /** SIMPLE, DONE */
- // only used for hatch and line pattern offsets, pretty much no longer
- // supported today
- // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction;
- break;
- }
- case MetaActionType::TEXTLINECOLOR :
- {
- /** SIMPLE, DONE */
- const MetaTextLineColorAction* pA = static_cast<const MetaTextLineColorAction*>(pAction);
- const bool bActive(pA->IsSetting());
-
- rPropertyHolders.Current().setTextLineColorActive(bActive);
- if(bActive)
- rPropertyHolders.Current().setTextLineColor(pA->GetColor().getBColor());
-
- break;
- }
- case MetaActionType::TEXTLINE :
- {
- /** 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 = static_cast<const MetaTextLineAction*>(pAction);
-
- proccessMetaTextLineAction(
- *pA,
- rTargetHolders.Current(),
- rPropertyHolders.Current());
-
- break;
- }
- case MetaActionType::FLOATTRANSPARENT :
- {
- /** CHECKED, WORKS WELL */
- const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pAction);
- const basegfx::B2DRange aTargetRange(
- pA->GetPoint().X(),
- pA->GetPoint().Y(),
- pA->GetPoint().X() + pA->GetSize().Width(),
- pA->GetPoint().Y() + pA->GetSize().Height());
-
- if(!aTargetRange.isEmpty())
- {
- const GDIMetaFile& rContent = pA->GetGDIMetaFile();
-
- if(rContent.GetActionSize())
- {
- // create the sub-content with no embedding specific to the
- // sub-metafile, this seems not to be used.
- drawinglayer::primitive2d::Primitive2DContainer 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.empty())
- {
- // prepare sub-content transform
- basegfx::B2DHomMatrix aSubTransform;
-
- // create SourceRange
- const basegfx::B2DRange aSourceRange(
- rContent.GetPrefMapMode().GetOrigin().X(),
- rContent.GetPrefMapMode().GetOrigin().Y(),
- rContent.GetPrefMapMode().GetOrigin().X() + rContent.GetPrefSize().Width(),
- rContent.GetPrefMapMode().GetOrigin().Y() + rContent.GetPrefSize().Height());
-
- // apply mapping if aTargetRange and aSourceRange are not equal
- if(!aSourceRange.equal(aTargetRange))
- {
- aSubTransform.translate(-aSourceRange.getMinX(), -aSourceRange.getMinY());
- aSubTransform.scale(
- aTargetRange.getWidth() / (basegfx::fTools::equalZero(aSourceRange.getWidth()) ? 1.0 : aSourceRange.getWidth()),
- aTargetRange.getHeight() / (basegfx::fTools::equalZero(aSourceRange.getHeight()) ? 1.0 : aSourceRange.getHeight()));
- aSubTransform.translate(aTargetRange.getMinX(), aTargetRange.getMinY());
- }
-
- // apply general current transformation
- aSubTransform = rPropertyHolders.Current().getTransformation() * aSubTransform;
-
- // evtl. embed sub-content to its transformation
- if(!aSubTransform.isIdentity())
- {
- const drawinglayer::primitive2d::Primitive2DReference aEmbeddedTransform(
- new drawinglayer::primitive2d::TransformPrimitive2D(
- aSubTransform,
- xSubContent));
-
- xSubContent = drawinglayer::primitive2d::Primitive2DContainer { aEmbeddedTransform };
- }
-
- // 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(aTargetRange);
- 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::Primitive2DContainer { xTransparence }));
- }
- }
- }
- }
-
- break;
- }
- case MetaActionType::GRADIENTEX :
- {
- /** SIMPLE, DONE */
- // This is only a data holder which is interpreted inside comment actions,
- // see MetaActionType::COMMENT for more info
- // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction;
- break;
- }
- case MetaActionType::LAYOUTMODE :
- {
- /** SIMPLE, DONE */
- const MetaLayoutModeAction* pA = static_cast<const MetaLayoutModeAction*>(pAction);
- rPropertyHolders.Current().setLayoutMode(pA->GetLayoutMode());
- break;
- }
- case MetaActionType::TEXTLANGUAGE :
- {
- /** SIMPLE, DONE */
- const MetaTextLanguageAction* pA = static_cast<const MetaTextLanguageAction*>(pAction);
- rPropertyHolders.Current().setLanguageType(pA->GetTextLanguage());
- break;
- }
- case MetaActionType::OVERLINECOLOR :
- {
- /** SIMPLE, DONE */
- const MetaOverlineColorAction* pA = static_cast<const MetaOverlineColorAction*>(pAction);
- const bool bActive(pA->IsSetting());
-
- rPropertyHolders.Current().setOverlineColorActive(bActive);
- if(bActive)
- rPropertyHolders.Current().setOverlineColor(pA->GetColor().getBColor());
-
- break;
- }
- case MetaActionType::COMMENT :
- {
- /** 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 = static_cast<const MetaCommentAction*>(pAction);
-
- if (pA->GetComment().equalsIgnoreAsciiCase("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) problematic with AntiAliasing, so it's
- // better to use this info
- const MetaGradientExAction* pMetaGradientExAction = nullptr;
- bool bDone(false);
- size_t b(nAction + 1);
-
- for(; !bDone && b < nCount; b++)
- {
- pAction = rMetaFile.GetAction(b);
-
- if(MetaActionType::GRADIENTEX == pAction->GetType())
- {
- pMetaGradientExAction = static_cast<const MetaGradientExAction*>(pAction);
- }
- else if(MetaActionType::COMMENT == pAction->GetType())
- {
- if (static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("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
{
void MetafilePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
{
- // prepare target and properties; 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 only one target, as in the start condition,
+ // Interpret the Metafile and get the content. There should be only one target, as in the start condition,
// but iterating will be the right thing to do when some push/pop is not closed
- Primitive2DContainer xRetval;
-
- while(aTargetHolders.size() > 1)
- {
- xRetval.append(
- aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
- aTargetHolders.Pop();
- }
-
- xRetval.append(
- aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
+ Primitive2DContainer xRetval(wmfemfhelper::interpretMetafile(getMetaFile(), rViewInformation));
if(!xRetval.empty())
{
diff --git a/drawinglayer/source/tools/emfpbrush.cxx b/drawinglayer/source/tools/emfpbrush.cxx
new file mode 100644
index 000000000000..d552bdfd5cd4
--- /dev/null
+++ b/drawinglayer/source/tools/emfpbrush.cxx
@@ -0,0 +1,328 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <basegfx/tools/canvastools.hxx>
+#include <basegfx/tools/gradienttools.hxx>
+#include <basegfx/tools/tools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <vcl/canvastools.hxx>
+#include <emfpbrush.hxx>
+#include <emfppath.hxx>
+
+namespace emfplushelper
+{
+ EMFPBrush::EMFPBrush()
+ : type(0)
+ , additionalFlags(0)
+ , wrapMode(0)
+ , areaX(0.0)
+ , areaY(0.0)
+ , areaWidth(0.0)
+ , areaHeight(0.0)
+ , hasTransformation(false)
+ , blendPoints(0)
+ , blendPositions(nullptr)
+ , blendFactors(nullptr)
+ , colorblendPoints(0)
+ , colorblendPositions(nullptr)
+ , colorblendColors(nullptr)
+ , surroundColorsNumber(0)
+ , surroundColors(nullptr)
+ , path(nullptr)
+ , hatchStyle(HatchStyleHorizontal)
+ {
+ }
+
+ EMFPBrush::~EMFPBrush()
+ {
+ if (blendPositions != nullptr) {
+ delete[] blendPositions;
+ blendPositions = nullptr;
+ }
+ if (colorblendPositions != nullptr) {
+ delete[] colorblendPositions;
+ colorblendPositions = nullptr;
+ }
+ if (colorblendColors != nullptr) {
+ delete[] colorblendColors;
+ colorblendColors = nullptr;
+ }
+ if (surroundColors != nullptr) {
+ delete[] surroundColors;
+ surroundColors = nullptr;
+ }
+ if (path) {
+ delete path;
+ path = nullptr;
+ }
+ }
+
+ void EMFPBrush::Read(SvStream& s, EmfPlusHelperData& rR)
+ {
+ sal_uInt32 header;
+
+ s.ReadUInt32(header).ReadUInt32(type);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\tbrush");
+ SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << header << " type: " << type << std::dec);
+
+ switch (type) {
+ case BrushTypeSolidColor:
+ {
+ sal_uInt32 color;
+
+ s.ReadUInt32(color);
+ solidColor = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("cppcanvas.emf", "EMF+\tsolid color: 0x" << std::hex << color << std::dec);
+ break;
+ }
+ case BrushTypeHatchFill:
+ {
+ sal_uInt32 style;
+ sal_uInt32 foregroundColor;
+ sal_uInt32 backgroundColor;
+ s.ReadUInt32(style);
+ s.ReadUInt32(foregroundColor);
+ s.ReadUInt32(backgroundColor);
+
+ hatchStyle = static_cast<EmfPlusHatchStyle>(style);
+ solidColor = ::Color(0xff - (foregroundColor >> 24), (foregroundColor >> 16) & 0xff, (foregroundColor >> 8) & 0xff, foregroundColor & 0xff);
+ secondColor = ::Color(0xff - (backgroundColor >> 24), (backgroundColor >> 16) & 0xff, (backgroundColor >> 8) & 0xff, backgroundColor & 0xff);
+ SAL_INFO("cppcanvas.emf", "EMF+\thatch style " << style << " foregroundcolor: 0x" << solidColor.AsRGBHexString() << " background 0x" << secondColor.AsRGBHexString());
+ break;
+ }
+ case BrushTypeTextureFill:
+ {
+ SAL_WARN("cppcanvas.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush");
+ break;
+ }
+ case BrushTypePathGradient:
+ {
+ s.ReadUInt32(additionalFlags).ReadInt32(wrapMode);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\tpath gradient, additional flags: 0x" << std::hex << additionalFlags << std::dec);
+
+ sal_uInt32 color;
+
+ s.ReadUInt32(color);
+ solidColor = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("cppcanvas.emf", "EMF+\tcenter color: 0x" << std::hex << color << std::dec);
+
+ s.ReadFloat(areaX).ReadFloat(areaY);
+ SAL_INFO("cppcanvas.emf", "EMF+\tcenter point: " << areaX << "," << areaY);
+
+ s.ReadInt32(surroundColorsNumber);
+ SAL_INFO("cppcanvas.emf", "EMF+\t number of surround colors: " << surroundColorsNumber);
+
+ if (surroundColorsNumber<0 || sal_uInt32(surroundColorsNumber)>SAL_MAX_INT32 / sizeof(::Color))
+ surroundColorsNumber = SAL_MAX_INT32 / sizeof(::Color);
+
+ surroundColors = new ::Color[surroundColorsNumber];
+ for (int i = 0; i < surroundColorsNumber; i++) {
+ s.ReadUInt32(color);
+ surroundColors[i] = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ if (i == 0)
+ secondColor = surroundColors[0];
+ SAL_INFO("cppcanvas.emf", "EMF+\tsurround color[" << i << "]: 0x" << std::hex << color << std::dec);
+ }
+
+ if (additionalFlags & 0x01) {
+ sal_Int32 pathLength;
+
+ s.ReadInt32(pathLength);
+ SAL_INFO("cppcanvas.emf", "EMF+\tpath length: " << pathLength);
+
+ sal_uInt64 const pos = s.Tell();
+
+ sal_uInt32 pathHeader;
+ sal_Int32 pathPoints, pathFlags;
+ s.ReadUInt32(pathHeader).ReadInt32(pathPoints).ReadInt32(pathFlags);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\tpath (brush path gradient)");
+ SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << pathHeader << " points: " << std::dec << pathPoints << " additional flags: 0x" << std::hex << pathFlags << std::dec);
+
+ path = new EMFPPath(pathPoints);
+ path->Read(s, pathFlags, rR);
+
+ s.Seek(pos + pathLength);
+
+ const ::basegfx::B2DRectangle aBounds(::basegfx::tools::getRange(path->GetPolygon(rR, false)));
+ areaWidth = aBounds.getWidth();
+ areaHeight = aBounds.getHeight();
+ SAL_INFO("cppcanvas.emf", "EMF+\t polygon bounding box: " << aBounds.getMinX() << "," << aBounds.getMinY() << " " << aBounds.getWidth() << "x" << aBounds.getHeight());
+ }
+ else
+ {
+ sal_Int32 boundaryPointCount;
+ s.ReadInt32(boundaryPointCount);
+
+ sal_uInt64 const pos = s.Tell();
+ SAL_INFO("cppcanvas.emf", "EMF+\t use boundary, points: " << boundaryPointCount);
+ path = new EMFPPath(boundaryPointCount);
+ path->Read(s, 0x0, rR);
+
+ s.Seek(pos + 8 * boundaryPointCount);
+
+ const ::basegfx::B2DRectangle aBounds(::basegfx::tools::getRange(path->GetPolygon(rR, false)));
+ areaWidth = aBounds.getWidth();
+ areaHeight = aBounds.getHeight();
+ SAL_INFO("cppcanvas.emf", "EMF+\t polygon bounding box: " << aBounds.getMinX() << "," << aBounds.getMinY() << " " << aBounds.getWidth() << "x" << aBounds.getHeight());
+ }
+
+ if (additionalFlags & 0x02) {
+ SAL_INFO("cppcanvas.emf", "EMF+\tuse transformation");
+ readXForm(s, brush_transformation);
+ hasTransformation = true;
+ SAL_INFO("cppcanvas.emf",
+ "EMF+\tm11: " << brush_transformation.get(0,0) << " m12: " << brush_transformation.get(1,0) <<
+ "\nEMF+\tm21: " << brush_transformation.get(0,1) << " m22: " << brush_transformation.get(1,1) <<
+ "\nEMF+\tdx: " << brush_transformation.get(0,2) << " dy: " << brush_transformation.get(1,2));
+
+ }
+ if (additionalFlags & 0x08) {
+ s.ReadInt32(blendPoints);
+ SAL_INFO("cppcanvas.emf", "EMF+\tuse blend, points: " << blendPoints);
+ if (blendPoints<0 || sal_uInt32(blendPoints)>SAL_MAX_INT32 / (2 * sizeof(float)))
+ blendPoints = SAL_MAX_INT32 / (2 * sizeof(float));
+ blendPositions = new float[2 * blendPoints];
+ blendFactors = blendPositions + blendPoints;
+ for (int i = 0; i < blendPoints; i++) {
+ s.ReadFloat(blendPositions[i]);
+ SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i << "]: " << blendPositions[i]);
+ }
+ for (int i = 0; i < blendPoints; i++) {
+ s.ReadFloat(blendFactors[i]);
+ SAL_INFO("cppcanvas.emf", "EMF+\tfactor[" << i << "]: " << blendFactors[i]);
+ }
+ }
+
+ if (additionalFlags & 0x04) {
+ s.ReadInt32(colorblendPoints);
+ SAL_INFO("cppcanvas.emf", "EMF+\tuse color blend, points: " << colorblendPoints);
+ if (colorblendPoints<0 || sal_uInt32(colorblendPoints)>SAL_MAX_INT32 / sizeof(float))
+ colorblendPoints = SAL_MAX_INT32 / sizeof(float);
+ if (sal_uInt32(colorblendPoints)>SAL_MAX_INT32 / sizeof(::Color))
+ colorblendPoints = SAL_MAX_INT32 / sizeof(::Color);
+ colorblendPositions = new float[colorblendPoints];
+ colorblendColors = new ::Color[colorblendPoints];
+ for (int i = 0; i < colorblendPoints; i++) {
+ s.ReadFloat(colorblendPositions[i]);
+ SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i << "]: " << colorblendPositions[i]);
+ }
+ for (int i = 0; i < colorblendPoints; i++) {
+ s.ReadUInt32(color);
+ colorblendColors[i] = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("cppcanvas.emf", "EMF+\tcolor[" << i << "]: 0x" << std::hex << color << std::dec);
+ }
+ }
+
+ break;
+ }
+ case BrushTypeLinearGradient:
+ {
+ s.ReadUInt32(additionalFlags).ReadInt32(wrapMode);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\tlinear gradient, additional flags: 0x" << std::hex << additionalFlags << std::dec);
+
+ s.ReadFloat(areaX).ReadFloat(areaY).ReadFloat(areaWidth).ReadFloat(areaHeight);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\tarea: " << areaX << "," << areaY << " - " << areaWidth << "x" << areaHeight);
+
+ sal_uInt32 color;
+
+ s.ReadUInt32(color);
+ solidColor = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("cppcanvas.emf", "EMF+\tfirst color: 0x" << std::hex << color << std::dec);
+
+ s.ReadUInt32(color);
+ secondColor = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("cppcanvas.emf", "EMF+\tsecond color: 0x" << std::hex << color << std::dec);
+
+ // repeated colors, unknown meaning, see http://www.aces.uiuc.edu/~jhtodd/Metafile/MetafileRecords/ObjectBrush.html
+ s.ReadUInt32(color);
+ s.ReadUInt32(color);
+
+ if (additionalFlags & 0x02) {
+ SAL_INFO("cppcanvas.emf", "EMF+\tuse transformation");
+ readXForm(s, brush_transformation);
+ hasTransformation = true;
+ SAL_INFO("cppcanvas.emf",
+ "EMF+\tm11: " << brush_transformation.get(0,0) << " m12: " << brush_transformation.get(1,0) <<
+ "\nEMF+\tm21: " << brush_transformation.get(0,1) << " m22: " << brush_transformation.get(1,1) <<
+ "\nEMF+\tdx: " << brush_transformation.get(0,2) << " dy: " << brush_transformation.get(1,2));
+ }
+ if (additionalFlags & 0x08) {
+ s.ReadInt32(blendPoints);
+ SAL_INFO("cppcanvas.emf", "EMF+\tuse blend, points: " << blendPoints);
+ if (blendPoints<0 || sal_uInt32(blendPoints)>SAL_MAX_INT32 / (2 * sizeof(float)))
+ blendPoints = SAL_MAX_INT32 / (2 * sizeof(float));
+ blendPositions = new float[2 * blendPoints];
+ blendFactors = blendPositions + blendPoints;
+ for (int i = 0; i < blendPoints; i++) {
+ s.ReadFloat(blendPositions[i]);
+ SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i << "]: " << blendPositions[i]);
+ }
+ for (int i = 0; i < blendPoints; i++) {
+ s.ReadFloat(blendFactors[i]);
+ SAL_INFO("cppcanvas.emf", "EMF+\tfactor[" << i << "]: " << blendFactors[i]);
+ }
+ }
+
+ if (additionalFlags & 0x04) {
+ s.ReadInt32(colorblendPoints);
+ SAL_INFO("cppcanvas.emf", "EMF+\tuse color blend, points: " << colorblendPoints);
+ if (colorblendPoints<0 || sal_uInt32(colorblendPoints)>SAL_MAX_INT32 / sizeof(float))
+ colorblendPoints = SAL_MAX_INT32 / sizeof(float);
+ if (sal_uInt32(colorblendPoints)>SAL_MAX_INT32 / sizeof(::Color))
+ colorblendPoints = sal_uInt32(SAL_MAX_INT32) / sizeof(::Color);
+ colorblendPositions = new float[colorblendPoints];
+ colorblendColors = new ::Color[colorblendPoints];
+ for (int i = 0; i < colorblendPoints; i++) {
+ s.ReadFloat(colorblendPositions[i]);
+ SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i << "]: " << colorblendPositions[i]);
+ }
+ for (int i = 0; i < colorblendPoints; i++) {
+ s.ReadUInt32(color);
+ colorblendColors[i] = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
+ SAL_INFO("cppcanvas.emf", "EMF+\tcolor[" << i << "]: 0x" << std::hex << color << std::dec);
+ }
+ }
+
+ break;
+ }
+ default:
+ SAL_INFO("cppcanvas.emf", "EMF+\tunhandled brush type: " << std::hex << type << std::dec);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpbrush.hxx b/drawinglayer/source/tools/emfpbrush.hxx
new file mode 100644
index 000000000000..852b2d469b4b
--- /dev/null
+++ b/drawinglayer/source/tools/emfpbrush.hxx
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPBRUSH_HXX
+#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPBRUSH_HXX
+
+#include <emfphelperdata.hxx>
+
+namespace emfplushelper
+{
+ enum EmfPlusHatchStyle
+ {
+ HatchStyleHorizontal = 0x00000000,
+ HatchStyleVertical = 0x00000001,
+ HatchStyleForwardDiagonal = 0x00000002,
+ HatchStyleBackwardDiagonal = 0x00000003,
+ HatchStyleLargeGrid = 0x00000004,
+ HatchStyleDiagonalCross = 0x00000005,
+ HatchStyle05Percent = 0x00000006,
+ HatchStyle10Percent = 0x00000007,
+ HatchStyle20Percent = 0x00000008,
+ HatchStyle25Percent = 0x00000009,
+ HatchStyle30Percent = 0x0000000A,
+ HatchStyle40Percent = 0x0000000B,
+ HatchStyle50Percent = 0x0000000C,
+ HatchStyle60Percent = 0x0000000D,
+ HatchStyle70Percent = 0x0000000E,
+ HatchStyle75Percent = 0x0000000F,
+ HatchStyle80Percent = 0x00000010,
+ HatchStyle90Percent = 0x00000011,
+ HatchStyleLightDownwardDiagonal = 0x00000012,
+ HatchStyleLightUpwardDiagonal = 0x00000013,
+ HatchStyleDarkDownwardDiagonal = 0x00000014,
+ HatchStyleDarkUpwardDiagonal = 0x00000015,
+ HatchStyleWideDownwardDiagonal = 0x00000016,
+ HatchStyleWideUpwardDiagonal = 0x00000017,
+ HatchStyleLightVertical = 0x00000018,
+ HatchStyleLightHorizontal = 0x00000019,
+ HatchStyleNarrowVertical = 0x0000001A,
+ HatchStyleNarrowHorizontal = 0x0000001B,
+ HatchStyleDarkVertical = 0x0000001C,
+ HatchStyleDarkHorizontal = 0x0000001D,
+ HatchStyleDashedDownwardDiagonal = 0x0000001E,
+ HatchStyleDashedUpwardDiagonal = 0x0000001F,
+ HatchStyleDashedHorizontal = 0x00000020,
+ HatchStyleDashedVertical = 0x00000021,
+ HatchStyleSmallConfetti = 0x00000022,
+ HatchStyleLargeConfetti = 0x00000023,
+ HatchStyleZigZag = 0x00000024,
+ HatchStyleWave = 0x00000025,
+ HatchStyleDiagonalBrick = 0x00000026,
+ HatchStyleHorizontalBrick = 0x00000027,
+ HatchStyleWeave = 0x00000028,
+ HatchStylePlaid = 0x00000029,
+ HatchStyleDivot = 0x0000002A,
+ HatchStyleDottedGrid = 0x0000002B,
+ HatchStyleDottedDiamond = 0x0000002C,
+ HatchStyleShingle = 0x0000002D,
+ HatchStyleTrellis = 0x0000002E,
+ HatchStyleSphere = 0x0000002F,
+ HatchStyleSmallGrid = 0x00000030,
+ HatchStyleSmallCheckerBoard = 0x00000031,
+ HatchStyleLargeCheckerBoard = 0x00000032,
+ HatchStyleOutlinedDiamond = 0x00000033,
+ HatchStyleSolidDiamond = 0x00000034
+ };
+
+ enum EmfPlusBrushType
+ {
+ BrushTypeSolidColor = 0x00000000,
+ BrushTypeHatchFill = 0x00000001,
+ BrushTypeTextureFill = 0x00000002,
+ BrushTypePathGradient = 0x00000003,
+ BrushTypeLinearGradient = 0x00000004
+ };
+
+ struct EMFPPath;
+
+ struct EMFPBrush : public EMFPObject
+ {
+ ::Color solidColor;
+ sal_uInt32 type;
+ sal_uInt32 additionalFlags;
+
+ /* linear gradient */
+ sal_Int32 wrapMode;
+ float areaX, areaY, areaWidth, areaHeight;
+ ::Color secondColor; // first color is stored in solidColor;
+ basegfx::B2DHomMatrix brush_transformation;
+ bool hasTransformation;
+ sal_Int32 blendPoints;
+ float* blendPositions;
+ float* blendFactors;
+ sal_Int32 colorblendPoints;
+ float* colorblendPositions;
+ ::Color* colorblendColors;
+ sal_Int32 surroundColorsNumber;
+ ::Color* surroundColors;
+ EMFPPath *path;
+ EmfPlusHatchStyle hatchStyle;
+
+ EMFPBrush();
+ virtual ~EMFPBrush() override;
+
+ sal_uInt32 GetType() const { return type; }
+ const ::Color& GetColor() const { return solidColor; }
+
+ void Read(SvStream& s, EmfPlusHelperData& rR);
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpcustomlinecap.cxx b/drawinglayer/source/tools/emfpcustomlinecap.cxx
new file mode 100644
index 000000000000..9024a11a564e
--- /dev/null
+++ b/drawinglayer/source/tools/emfpcustomlinecap.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <basegfx/tools/canvastools.hxx>
+#include <basegfx/tools/gradienttools.hxx>
+#include <basegfx/tools/tools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <vcl/canvastools.hxx>
+#include <emfpcustomlinecap.hxx>
+#include <emfppath.hxx>
+#include <emfppen.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::basegfx;
+
+namespace emfplushelper
+{
+ const sal_uInt32 EmfPlusCustomLineCapDataTypeDefault = 0x00000000;
+ const sal_uInt32 EmfPlusCustomLineCapDataTypeAdjustableArrow = 0x00000001;
+
+ const sal_uInt32 EmfPlusCustomLineCapDataFillPath = 0x00000001;
+ const sal_uInt32 EmfPlusCustomLineCapDataLinePath = 0x00000002;
+
+ EMFPCustomLineCap::EMFPCustomLineCap()
+ : EMFPObject()
+ , type(0)
+ , strokeStartCap(0)
+ , strokeEndCap(0)
+ , strokeJoin(0)
+ , miterLimit(0.0)
+ , mbIsFilled(false)
+ {
+ }
+
+ void EMFPCustomLineCap::SetAttributes(rendering::StrokeAttributes& aAttributes)
+ {
+ aAttributes.StartCapType = EMFPPen::lcl_convertStrokeCap(strokeStartCap);
+ aAttributes.EndCapType = EMFPPen::lcl_convertStrokeCap(strokeEndCap);
+ aAttributes.JoinType = EMFPPen::lcl_convertLineJoinType(strokeJoin);
+
+ aAttributes.MiterLimit = miterLimit;
+ }
+
+ void EMFPCustomLineCap::ReadPath(SvStream& s, EmfPlusHelperData& rR, bool bFill)
+ {
+ sal_Int32 pathLength;
+ s.ReadInt32(pathLength);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tpath length: " << pathLength);
+
+ sal_uInt32 pathHeader;
+ sal_Int32 pathPoints, pathFlags;
+ s.ReadUInt32(pathHeader).ReadInt32(pathPoints).ReadInt32(pathFlags);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tpath (custom cap line path)");
+ SAL_INFO("cppcanvas.emf", "EMF+\t\theader: 0x" << std::hex << pathHeader << " points: " << std::dec << pathPoints << " additional flags: 0x" << std::hex << pathFlags << std::dec);
+
+ EMFPPath path(pathPoints);
+ path.Read(s, pathFlags, rR);
+
+ polygon = path.GetPolygon(rR, false);
+ mbIsFilled = bFill;
+
+ // transformation to convert the path to what LibreOffice
+ // expects
+ B2DHomMatrix aMatrix;
+ aMatrix.scale(1.0, -1.0);
+
+ polygon.transform(aMatrix);
+ };
+
+ void EMFPCustomLineCap::Read(SvStream& s, EmfPlusHelperData& rR)
+ {
+ sal_uInt32 header;
+
+ s.ReadUInt32(header).ReadUInt32(type);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tcustom cap");
+ SAL_INFO("cppcanvas.emf", "EMF+\t\theader: 0x" << std::hex << header << " type: " << type << std::dec);
+
+ if (type == EmfPlusCustomLineCapDataTypeDefault)
+ {
+ sal_uInt32 customLineCapDataFlags, baseCap;
+ float baseInset;
+ float widthScale;
+ float fillHotSpotX, fillHotSpotY, strokeHotSpotX, strokeHotSpotY;
+
+ s.ReadUInt32(customLineCapDataFlags).ReadUInt32(baseCap).ReadFloat(baseInset)
+ .ReadUInt32(strokeStartCap).ReadUInt32(strokeEndCap).ReadUInt32(strokeJoin)
+ .ReadFloat(miterLimit).ReadFloat(widthScale)
+ .ReadFloat(fillHotSpotX).ReadFloat(fillHotSpotY).ReadFloat(strokeHotSpotX).ReadFloat(strokeHotSpotY);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tcustomLineCapDataFlags: 0x" << std::hex << customLineCapDataFlags);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tbaseCap: 0x" << std::hex << baseCap);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tbaseInset: " << baseInset);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tstrokeStartCap: 0x" << std::hex << strokeStartCap);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tstrokeEndCap: 0x" << std::hex << strokeEndCap);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tstrokeJoin: 0x" << std::hex << strokeJoin);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tmiterLimit: " << miterLimit);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\twidthScale: " << widthScale);
+
+ if (customLineCapDataFlags & EmfPlusCustomLineCapDataFillPath)
+ {
+ ReadPath(s, rR, true);
+ }
+
+ if (customLineCapDataFlags & EmfPlusCustomLineCapDataLinePath)
+ {
+ ReadPath(s, rR, false);
+ }
+ }
+ else if (type == EmfPlusCustomLineCapDataTypeAdjustableArrow)
+ {
+ // TODO only reads the data, does not use them [I've had
+ // no test document to be able to implement it]
+
+ sal_Int32 width, height, middleInset, fillState, lineStartCap;
+ sal_Int32 lineEndCap, lineJoin, widthScale;
+ float fillHotSpotX, fillHotSpotY, lineHotSpotX, lineHotSpotY;
+
+ s.ReadInt32(width).ReadInt32(height).ReadInt32(middleInset).ReadInt32(fillState).ReadInt32(lineStartCap)
+ .ReadInt32(lineEndCap).ReadInt32(lineJoin).ReadFloat(miterLimit).ReadInt32(widthScale)
+ .ReadFloat(fillHotSpotX).ReadFloat(fillHotSpotY).ReadFloat(lineHotSpotX).ReadFloat(lineHotSpotY);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tTODO - actually read EmfPlusCustomLineCapArrowData object (section 2.2.2.12)");
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpcustomlinecap.hxx b/drawinglayer/source/tools/emfpcustomlinecap.hxx
new file mode 100644
index 000000000000..67b21c85d81b
--- /dev/null
+++ b/drawinglayer/source/tools/emfpcustomlinecap.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPCUSTOMLINECAP_HXX
+#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPCUSTOMLINECAP_HXX
+
+#include <emfphelperdata.hxx>
+
+namespace emfplushelper
+{
+ struct EMFPCustomLineCap : public EMFPObject
+ {
+ sal_uInt32 type;
+ sal_uInt32 strokeStartCap, strokeEndCap, strokeJoin;
+ float miterLimit;
+ basegfx::B2DPolyPolygon polygon;
+ bool mbIsFilled;
+
+ EMFPCustomLineCap();
+
+ void SetAttributes(com::sun::star::rendering::StrokeAttributes& aAttributes);
+ void ReadPath(SvStream& s, EmfPlusHelperData& rR, bool bFill);
+ void Read(SvStream& s, EmfPlusHelperData& rR);
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpfont.cxx b/drawinglayer/source/tools/emfpfont.cxx
new file mode 100644
index 000000000000..b191a8f4c7e5
--- /dev/null
+++ b/drawinglayer/source/tools/emfpfont.cxx
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <basegfx/tools/canvastools.hxx>
+#include <basegfx/tools/gradienttools.hxx>
+#include <basegfx/tools/tools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <vcl/canvastools.hxx>
+#include <emfpfont.hxx>
+
+namespace emfplushelper
+{
+ void EMFPFont::Read(SvMemoryStream &s)
+ {
+ sal_uInt32 header;
+ sal_uInt32 reserved;
+ sal_uInt32 length;
+
+ s.ReadUInt32(header).ReadFloat(emSize).ReadUInt32(sizeUnit).ReadInt32(fontFlags).ReadUInt32(reserved).ReadUInt32(length);
+
+ SAL_WARN_IF((header >> 12) != 0xdbc01, "cppcanvas.emf", "Invalid header - not 0xdbc01");
+
+ SAL_INFO("cppcanvas.emf", "EMF+\tfont\nEMF+\theader: 0x" << std::hex << (header >> 12) << " version: 0x" << (header & 0x1fff) << " size: " << std::dec << emSize << " unit: 0x" << std::hex << sizeUnit << std::dec);
+ SAL_INFO("cppcanvas.emf", "EMF+\tflags: 0x" << std::hex << fontFlags << " reserved: 0x" << reserved << " length: 0x" << std::hex << length << std::dec);
+
+ if (length > 0 && length < 0x4000)
+ {
+ rtl_uString *pStr = rtl_uString_alloc(length);
+ sal_Unicode *chars = pStr->buffer;
+
+ for (sal_uInt32 i = 0; i < length; ++i)
+ s.ReadUtf16(chars[i]);
+
+ family = OUString(pStr, SAL_NO_ACQUIRE);
+ SAL_INFO("cppcanvas.emf", "EMF+\tfamily: " << family);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpfont.hxx b/drawinglayer/source/tools/emfpfont.hxx
new file mode 100644
index 000000000000..6c9f73fd4455
--- /dev/null
+++ b/drawinglayer/source/tools/emfpfont.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPFONT_HXX
+#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPFONT_HXX
+
+#include <emfphelperdata.hxx>
+
+namespace emfplushelper
+{
+ struct EMFPFont : public EMFPObject
+ {
+ float emSize;
+ sal_uInt32 sizeUnit;
+ sal_Int32 fontFlags;
+ OUString family;
+
+ void Read(SvMemoryStream &s);
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx
new file mode 100644
index 000000000000..cbce6d32efb0
--- /dev/null
+++ b/drawinglayer/source/tools/emfphelperdata.cxx
@@ -0,0 +1,1232 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <emfphelperdata.hxx>
+#include <emfpbrush.hxx>
+#include <emfppen.hxx>
+#include <emfppath.hxx>
+#include <emfpregion.hxx>
+#include <emfpimage.hxx>
+#include <emfpfont.hxx>
+#include <emfpstringformat.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+
+namespace emfplushelper
+{
+ const char* emfTypeToName(sal_uInt16 type)
+ {
+ switch (type)
+ {
+ case EmfPlusRecordTypeHeader: return "EmfPlusRecordTypeHeader";
+ case EmfPlusRecordTypeEndOfFile: return "EmfPlusRecordTypeEndOfFile";
+ case EmfPlusRecordTypeGetDC: return "EmfPlusRecordTypeGetDC";
+ case EmfPlusRecordTypeObject: return "EmfPlusRecordTypeObject";
+ case EmfPlusRecordTypeFillRects: return "EmfPlusRecordTypeFillRects";
+ case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects";
+ case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon";
+ case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines";
+ case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse";
+ case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse";
+ case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie";
+ case EmfPlusRecordTypeDrawPie: return "EmfPlusRecordTypeDrawPie";
+ case EmfPlusRecordTypeDrawArc: return "EmfPlusRecordTypeDrawArc";
+ case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath";
+ case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath";
+ case EmfPlusRecordTypeDrawBeziers: return "EmfPlusRecordTypeDrawBeziers";
+ case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage";
+ case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints";
+ case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString";
+ case EmfPlusRecordTypeSetRenderingOrigin: return "EmfPlusRecordTypeSetRenderingOrigin";
+ case EmfPlusRecordTypeSetAntiAliasMode: return "EmfPlusRecordTypeSetAntiAliasMode";
+ case EmfPlusRecordTypeSetTextRenderingHint: return "EmfPlusRecordTypeSetTextRenderingHint";
+ case EmfPlusRecordTypeSetInterpolationMode: return "EmfPlusRecordTypeSetInterpolationMode";
+ case EmfPlusRecordTypeSetPixelOffsetMode: return "EmfPlusRecordTypeSetPixelOffsetMode";
+ case EmfPlusRecordTypeSetCompositingQuality: return "EmfPlusRecordTypeSetCompositingQuality";
+ case EmfPlusRecordTypeSave: return "EmfPlusRecordTypeSave";
+ case EmfPlusRecordTypeRestore: return "EmfPlusRecordTypeRestore";
+ case EmfPlusRecordTypeBeginContainerNoParams: return "EmfPlusRecordTypeBeginContainerNoParams";
+ case EmfPlusRecordTypeEndContainer: return "EmfPlusRecordTypeEndContainer";
+ case EmfPlusRecordTypeSetWorldTransform: return "EmfPlusRecordTypeSetWorldTransform";
+ case EmfPlusRecordTypeResetWorldTransform: return "EmfPlusRecordTypeResetWorldTransform";
+ case EmfPlusRecordTypeMultiplyWorldTransform: return "EmfPlusRecordTypeMultiplyWorldTransform";
+ case EmfPlusRecordTypeTranslateWorldTransform: return "EmfPlusRecordTypeTranslateWorldTransform";
+ case EmfPlusRecordTypeScaleWorldTransform: return "EmfPlusRecordTypeScaleWorldTransform";
+ case EmfPlusRecordTypeSetPageTransform: return "EmfPlusRecordTypeSetPageTransform";
+ case EmfPlusRecordTypeSetClipRect: return "EmfPlusRecordTypeSetClipRect";
+ case EmfPlusRecordTypeSetClipPath: return "EmfPlusRecordTypeSetClipPath";
+ case EmfPlusRecordTypeSetClipRegion: return "EmfPlusRecordTypeSetClipRegion";
+ case EmfPlusRecordTypeDrawDriverString: return "EmfPlusRecordTypeDrawDriverString";
+ }
+ return "";
+ }
+
+ EMFPObject::~EMFPObject()
+ {
+ }
+
+ bool readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget)
+ {
+ rTarget.identity();
+
+ if (sizeof(float) != 4)
+ {
+ OSL_FAIL("EnhWMFReader::sizeof( float ) != 4");
+ return false;
+ }
+ else
+ {
+ float eM11(0.0);
+ float eM12(0.0);
+ float eM21(0.0);
+ float eM22(0.0);
+ float eDx(0.0);
+ float eDy(0.0);
+#ifdef OSL_BIGENDIAN
+ eM11 = GetSwapFloat(rIn);
+ eM12 = GetSwapFloat(rIn);
+ eM21 = GetSwapFloat(rIn);
+ eM22 = GetSwapFloat(rIn);
+ eDx = GetSwapFloat(rIn);
+ eDy = GetSwapFloat(rIn);
+#else
+ rIn.ReadFloat(eM11).ReadFloat(eM12).ReadFloat(eM21).ReadFloat(eM22).ReadFloat(eDx).ReadFloat(eDy);
+#endif
+ rTarget = basegfx::B2DHomMatrix(
+ eM11, eM21, eDx,
+ eM12, eM22, eDy);
+ }
+
+ return true;
+ }
+
+ OutDevState::OutDevState() :
+ clip(),
+ clipRect(),
+ xClipPoly(),
+
+ lineColor(),
+ fillColor(),
+ textColor(),
+ textFillColor(),
+ textLineColor(),
+
+ xFont(),
+ transform(),
+ mapModeTransform(),
+ fontRotation(0.0),
+
+ textEmphasisMarkStyle(FontEmphasisMark::NONE),
+ pushFlags(PushFlags::ALL),
+ textDirection(css::rendering::TextDirection::WEAK_LEFT_TO_RIGHT),
+ textAlignment(0), // TODO(Q2): Synchronize with implrenderer
+ // and possibly new rendering::TextAlignment
+ textReliefStyle(FontRelief::NONE),
+ textOverlineStyle(LINESTYLE_NONE),
+ textUnderlineStyle(LINESTYLE_NONE),
+ textStrikeoutStyle(STRIKEOUT_NONE),
+ textReferencePoint(ALIGN_BASELINE),
+
+ isTextOutlineModeSet(false),
+ isTextEffectShadowSet(false),
+ isTextWordUnderlineSet(false),
+
+ isLineColorSet(false),
+ isFillColorSet(false),
+ isTextFillColorSet(false),
+ isTextLineColorSet(false)
+ {
+ }
+
+ void EmfPlusHelperData::processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream)
+ {
+ sal_uInt32 index;
+ SAL_INFO("cppcanvas.emf", "EMF+ Object slot: " << (flags & 0xff) << " flags: " << (flags & 0xff00));
+ index = flags & 0xff;
+
+ if (aObjects[index] != nullptr)
+ {
+ delete aObjects[index];
+ aObjects[index] = nullptr;
+ }
+
+ switch (flags & 0x7f00) {
+ case EmfPlusObjectTypeBrush:
+ {
+ EMFPBrush *brush;
+ aObjects[index] = brush = new EMFPBrush();
+ brush->Read(rObjectStream, *this);
+ break;
+ }
+ case EmfPlusObjectTypePen:
+ {
+ EMFPPen *pen;
+ aObjects[index] = pen = new EMFPPen();
+ pen->Read(rObjectStream, *this);
+ break;
+ }
+ case EmfPlusObjectTypePath:
+ sal_uInt32 header, pathFlags;
+ sal_Int32 points;
+
+ rObjectStream.ReadUInt32(header).ReadInt32(points).ReadUInt32(pathFlags);
+ SAL_INFO("cppcanvas.emf", "EMF+\tpath");
+ SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << header << " points: " << std::dec << points << " additional flags: 0x" << std::hex << pathFlags << std::dec);
+ EMFPPath *path;
+ aObjects[index] = path = new EMFPPath(points);
+ path->Read(rObjectStream, pathFlags, *this);
+ break;
+ case EmfPlusObjectTypeRegion: {
+ EMFPRegion *region;
+ aObjects[index] = region = new EMFPRegion();
+ region->Read(rObjectStream);
+ break;
+ }
+ case EmfPlusObjectTypeImage:
+ {
+ EMFPImage *image;
+ aObjects[index] = image = new EMFPImage;
+ image->type = 0;
+ image->width = 0;
+ image->height = 0;
+ image->stride = 0;
+ image->pixelFormat = 0;
+ image->Read(rObjectStream, dataSize, bUseWholeStream);
+ break;
+ }
+ case EmfPlusObjectTypeFont:
+ {
+ EMFPFont *font;
+ aObjects[index] = font = new EMFPFont;
+ font->emSize = 0;
+ font->sizeUnit = 0;
+ font->fontFlags = 0;
+ font->Read(rObjectStream);
+ break;
+ }
+ case EmfPlusObjectTypeStringFormat:
+ {
+ EMFPStringFormat *stringFormat;
+ aObjects[index] = stringFormat = new EMFPStringFormat();
+ stringFormat->Read(rObjectStream);
+ break;
+ }
+ case EmfPlusObjectTypeImageAttributes:
+ {
+ SAL_INFO("cppcanvas.emf", "EMF+\t Object type 'image attributes' not yet implemented");
+ break;
+ }
+ case EmfPlusObjectTypeCustomLineCap:
+ {
+ SAL_INFO("cppcanvas.emf", "EMF+\t Object type 'custom line cap' not yet implemented");
+ break;
+ }
+ default:
+ SAL_INFO("cppcanvas.emf", "EMF+\tObject unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec);
+ break;
+ }
+ }
+
+ void EmfPlusHelperData::ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags)
+ {
+ if (flags & 0x800)
+ {
+ // specifies a location in the coordinate space that is relative to
+ // the location specified by the previous element in the array. In the case of the first element in
+ // PointData, a previous location at coordinates (0,0) is assumed.
+ SAL_WARN("cppcanvas.emf", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR");
+ }
+
+ if (flags & 0x4000)
+ {
+ sal_Int16 ix, iy;
+
+ s.ReadInt16(ix).ReadInt16(iy);
+
+ x = ix;
+ y = iy;
+ }
+ else
+ {
+ s.ReadFloat(x).ReadFloat(y);
+ }
+ }
+
+ void EmfPlusHelperData::ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed)
+ {
+ if (bCompressed)
+ {
+ sal_Int16 ix, iy, iw, ih;
+
+ s.ReadInt16(ix).ReadInt16(iy).ReadInt16(iw).ReadInt16(ih);
+
+ x = ix;
+ y = iy;
+ width = iw;
+ height = ih;
+ }
+ else
+ {
+ s.ReadFloat(x).ReadFloat(y).ReadFloat(width).ReadFloat(height);
+ }
+ }
+
+ void EmfPlusHelperData::MapToDevice(double& x, double& y)
+ {
+ // TODO: other units
+ x = 100 * mnMmX*x / mnPixX;
+ y = 100 * mnMmY*y / mnPixY;
+ }
+
+ ::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy)
+ {
+ double x, y;
+
+ x = ix*aWorldTransform.get(0,0) + iy*aWorldTransform.get(0,1) + aWorldTransform.get(0,2);
+ y = ix*aWorldTransform.get(1,0) + iy*aWorldTransform.get(1,1) + aWorldTransform.get(1,2);
+
+ MapToDevice(x, y);
+
+ x -= mnFrameLeft;
+ y -= mnFrameTop;
+
+ x *= aBaseTransform.get(0,0);
+ y *= aBaseTransform.get(1,1);
+
+ return ::basegfx::B2DPoint(x, y);
+ }
+
+ ::basegfx::B2DSize EmfPlusHelperData::MapSize(double iwidth, double iheight)
+ {
+ double w, h;
+
+ w = iwidth*aWorldTransform.get(0,0) + iheight*aWorldTransform.get(1,0);
+ h = iwidth*aWorldTransform.get(1,0) + iheight*aWorldTransform.get(1,1);
+
+ MapToDevice(w, h);
+
+ w *= aBaseTransform.get(0,0);
+ h *= aBaseTransform.get(1,1);
+
+ return ::basegfx::B2DSize(w, h);
+ }
+
+ EmfPlusHelperData::EmfPlusHelperData(SvMemoryStream& rMS)
+ : aBaseTransform(),
+ aWorldTransform(),
+ aObjects(),
+ fPageScale(0.0),
+ nOriginX(0),
+ nOriginY(0),
+ nHDPI(0),
+ nVDPI(0),
+ mnFrameLeft(0),
+ mnFrameTop(0),
+ mnFrameRight(0),
+ mnFrameBottom(0),
+ mnPixX(0),
+ mnPixY(0),
+ mnMmX(0),
+ mnMmY(0),
+ mbMultipart(false),
+ mMFlags(0),
+ mMStream(),
+ mGSStack(),
+ mGSContainerStack(),
+ mpTargetHolders(nullptr),
+ mpPropertyHolders(nullptr)
+ {
+ memset(aObjects, 0, sizeof(aObjects));
+
+ rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom);
+ SAL_INFO("cppcanvas.emf", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom);
+ rMS.ReadInt32(mnPixX).ReadInt32(mnPixY).ReadInt32(mnMmX).ReadInt32(mnMmY);
+ SAL_INFO("cppcanvas.emf", "EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY);
+ readXForm(rMS, aBaseTransform);
+ }
+
+ void EmfPlusHelperData::processEmfPlusData(
+ SvMemoryStream& rMS,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation)
+ {
+ sal_uInt64 length = rMS.GetSize();
+
+ if (length < 12)
+ {
+ SAL_INFO("cppcanvas.emf", "length is less than required header size");
+ }
+
+ // 12 is minimal valid EMF+ record size; remaining bytes are padding
+ while (length >= 12)
+ {
+ sal_uInt16 type, flags;
+ sal_uInt32 size, dataSize;
+ sal_uInt64 next;
+
+ rMS.ReadUInt16(type).ReadUInt16(flags).ReadUInt32(size).ReadUInt32(dataSize);
+
+ next = rMS.Tell() + (size - 12);
+
+ if (size < 12)
+ {
+ SAL_INFO("cppcanvas.emf", "Size field is less than 12 bytes");
+ }
+ else if (size > length)
+ {
+ SAL_INFO("cppcanvas.emf", "Size field is greater than bytes left");
+ }
+ if (dataSize > (size - 12))
+ {
+ SAL_INFO("cppcanvas.emf", "DataSize field is greater than Size-12");
+ }
+
+ SAL_INFO("cppcanvas.emf", "EMF+ record size: " << size << " type: " << emfTypeToName(type) << " flags: " << flags << " data size: " << dataSize);
+
+ if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000)))
+ {
+ if (!mbMultipart)
+ {
+ mbMultipart = true;
+ mMFlags = flags;
+ mMStream.Seek(0);
+ }
+
+ OSL_ENSURE(dataSize >= 4, "No room for TotalObjectSize in EmfPlusContinuedObjectRecord");
+
+ // 1st 4 bytes are TotalObjectSize
+ mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4);
+ SAL_INFO("cppcanvas.emf", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize);
+ }
+ else
+ {
+ if (mbMultipart)
+ {
+ SAL_INFO("cppcanvas.emf", "EMF+ multipart record flags: " << mMFlags);
+ mMStream.Seek(0);
+ processObjectRecord(mMStream, mMFlags, 0, true);
+ }
+
+ mbMultipart = false;
+ }
+
+ if (type != EmfPlusRecordTypeObject || !(flags & 0x8000))
+ {
+ switch (type) {
+ case EmfPlusRecordTypeHeader:
+ sal_uInt32 header, version;
+
+ rMS.ReadUInt32(header).ReadUInt32(version).ReadInt32(nHDPI).ReadInt32(nVDPI);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ Header");
+ SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << header << " version: " << std::dec << version << " horizontal DPI: " << nHDPI << " vertical DPI: " << nVDPI << " dual: " << (flags & 1));
+
+ break;
+ case EmfPlusRecordTypeEndOfFile:
+ SAL_INFO("cppcanvas.emf", "EMF+ EndOfFile");
+ break;
+ case EmfPlusRecordTypeGetDC:
+ SAL_INFO("cppcanvas.emf", "EMF+ GetDC");
+ SAL_INFO("cppcanvas.emf", "EMF+\talready used in svtools wmf/emf filter parser");
+ break;
+ case EmfPlusRecordTypeObject:
+ processObjectRecord(rMS, flags, dataSize);
+ break;
+ case EmfPlusRecordTypeFillPie:
+ case EmfPlusRecordTypeDrawPie:
+ case EmfPlusRecordTypeDrawArc:
+ {
+ float startAngle, sweepAngle;
+
+ // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
+ sal_uInt32 brushIndexOrColor = 999;
+
+ if (type == EmfPlusRecordTypeFillPie)
+ {
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("cppcanvas.emf", "EMF+ FillPie colorOrIndex: " << brushIndexOrColor);
+ }
+ else if (type == EmfPlusRecordTypeDrawPie)
+ {
+ SAL_INFO("cppcanvas.emf", "EMF+ DrawPie");
+ }
+ else
+ {
+ SAL_INFO("cppcanvas.emf", "EMF+ DrawArc");
+ }
+ rMS.ReadFloat(startAngle).ReadFloat(sweepAngle);
+
+ float dx, dy, dw, dh;
+
+ ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
+
+ SAL_INFO("cppcanvas.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+
+ startAngle = 2 * M_PI*startAngle / 360;
+ sweepAngle = 2 * M_PI*sweepAngle / 360;
+
+ ::basegfx::B2DPoint mappedCenter(Map(dx + dw / 2, dy + dh / 2));
+ ::basegfx::B2DSize mappedSize(MapSize(dw / 2, dh / 2));
+
+ float endAngle = startAngle + sweepAngle;
+ startAngle = fmodf(startAngle, static_cast<float>(M_PI * 2));
+ if (startAngle < 0)
+ startAngle += static_cast<float>(M_PI * 2);
+ endAngle = fmodf(endAngle, static_cast<float>(M_PI * 2));
+ if (endAngle < 0)
+ endAngle += static_cast<float>(M_PI * 2);
+
+ if (sweepAngle < 0)
+ std::swap(endAngle, startAngle);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\t adjusted angles: start " <<
+ (360.0*startAngle / M_PI) << ", end: " << (360.0*endAngle / M_PI) <<
+ " startAngle: " << startAngle << " sweepAngle: " << sweepAngle);
+
+ ::basegfx::B2DPolygon polygon = basegfx::tools::createPolygonFromEllipseSegment(mappedCenter, mappedSize.getX(), mappedSize.getY(), startAngle, endAngle);
+ if (type != EmfPlusRecordTypeDrawArc)
+ {
+ polygon.append(mappedCenter);
+ polygon.setClosed(true);
+ }
+
+ ::basegfx::B2DPolyPolygon polyPolygon(polygon);
+// if (type == EmfPlusRecordTypeFillPie)
+// EMFPPlusFillPolygon(polyPolygon,
+// rFactoryParms, rState, rCanvas, flags & 0x8000, brushIndexOrColor);
+// else
+// EMFPPlusDrawPolygon(polyPolygon,
+// rFactoryParms, rState, rCanvas, flags & 0xff);
+ }
+ break;
+ case EmfPlusRecordTypeFillPath:
+ {
+ sal_uInt32 index = flags & 0xff;
+ sal_uInt32 brushIndexOrColor;
+
+ rMS.ReadUInt32(brushIndexOrColor);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ FillPath slot: " << index);
+
+// EMFPPlusFillPolygon(static_cast<EMFPPath*>(aObjects[index])->GetPolygon(*this), rFactoryParms, rState, rCanvas, flags & 0x8000, brushIndexOrColor);
+ }
+ break;
+ case EmfPlusRecordTypeDrawEllipse:
+ case EmfPlusRecordTypeFillEllipse:
+ {
+ // Intentionally very bogus initial value to avoid MSVC complaining about potentially uninitialized local
+ // variable. As long as the code stays as intended, this variable will be assigned a (real) value in the case
+ // when it is later used.
+ sal_uInt32 brushIndexOrColor = 1234567;
+
+ if (type == EmfPlusRecordTypeFillEllipse)
+ rMS.ReadUInt32(brushIndexOrColor);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff));
+
+ float dx, dy, dw, dh;
+
+ ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
+
+ SAL_INFO("cppcanvas.emf", "EMF+ RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+
+ ::basegfx::B2DPoint mappedCenter(Map(dx + dw / 2, dy + dh / 2));
+ ::basegfx::B2DSize mappedSize(MapSize(dw / 2, dh / 2));
+ ::basegfx::B2DPolyPolygon polyPolygon(::basegfx::B2DPolygon(::basegfx::tools::createPolygonFromEllipse(mappedCenter, mappedSize.getX(), mappedSize.getY())));
+
+// if (type == EmfPlusRecordTypeFillEllipse)
+// EMFPPlusFillPolygon(polyPolygon,
+// rFactoryParms, rState, rCanvas, flags & 0x8000, brushIndexOrColor);
+// else
+// EMFPPlusDrawPolygon(polyPolygon,
+// rFactoryParms, rState, rCanvas, flags & 0xff);
+ }
+ break;
+ case EmfPlusRecordTypeFillRects:
+ case EmfPlusRecordTypeDrawRects:
+ {
+ // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
+ sal_uInt32 brushIndexOrColor = 999;
+ sal_Int32 rectangles;
+ bool isColor = (flags & 0x8000);
+ ::basegfx::B2DPolygon polygon;
+
+ if (type == EmfPlusRecordTypeFillRects)
+ {
+ SAL_INFO("cppcanvas.emf", "EMF+ FillRects");
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("cppcanvas.emf", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
+ }
+ else
+ {
+ SAL_INFO("cppcanvas.emf", "EMF+ DrawRects");
+ }
+
+ rMS.ReadInt32(rectangles);
+
+ for (int i = 0; i < rectangles; i++) {
+ float x, y, width, height;
+ ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000));
+
+ polygon.append(Map(x, y));
+ polygon.append(Map(x + width, y));
+ polygon.append(Map(x + width, y + height));
+ polygon.append(Map(x, y + height));
+ polygon.append(Map(x, y));
+
+ SAL_INFO("cppcanvas.emf", "EMF+\trectangle: " << x << ", " << width << "x" << height);
+
+ ::basegfx::B2DPolyPolygon polyPolygon(polygon);
+// if (type == EmfPlusRecordTypeFillRects)
+// EMFPPlusFillPolygon(polyPolygon,
+// rFactoryParms, rState, rCanvas, isColor, brushIndexOrColor);
+// else
+// EMFPPlusDrawPolygon(polyPolygon,
+// rFactoryParms, rState, rCanvas, flags & 0xff);
+ }
+ break;
+ }
+ case EmfPlusRecordTypeFillPolygon:
+ {
+ sal_uInt8 index = flags & 0xff;
+ sal_uInt32 brushIndexOrColor;
+ sal_Int32 points;
+
+ rMS.ReadUInt32(brushIndexOrColor);
+ rMS.ReadInt32(points);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ FillPolygon in slot: " << +index << " points: " << points);
+ SAL_INFO("cppcanvas.emf", "EMF+\t: " << ((flags & 0x8000) ? "color" : "brush index") << " 0x" << std::hex << brushIndexOrColor << std::dec);
+
+ EMFPPath path(points, true);
+ path.Read(rMS, flags, *this);
+
+// EMFPPlusFillPolygon(path.GetPolygon(*this), rFactoryParms, rState, rCanvas, flags & 0x8000, brushIndexOrColor);
+
+ break;
+ }
+ case EmfPlusRecordTypeDrawLines:
+ {
+ sal_uInt32 points;
+
+ rMS.ReadUInt32(points);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ DrawLines in slot: " << (flags & 0xff) << " points: " << points);
+
+ EMFPPath path(points, true);
+ path.Read(rMS, flags, *this);
+
+ // 0x2000 bit indicates whether to draw an extra line between the last point
+ // and the first point, to close the shape.
+// EMFPPlusDrawPolygon(path.GetPolygon(*this, true, (flags & 0x2000)), rFactoryParms, rState, rCanvas, flags);
+
+ break;
+ }
+ case EmfPlusRecordTypeDrawPath:
+ {
+ sal_uInt32 penIndex;
+
+ rMS.ReadUInt32(penIndex);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ DrawPath");
+ SAL_INFO("cppcanvas.emf", "EMF+\tpen: " << penIndex);
+
+ EMFPPath* path = static_cast<EMFPPath*>(aObjects[flags & 0xff]);
+ SAL_WARN_IF(!path, "cppcanvas.emf", "EmfPlusRecordTypeDrawPath missing path");
+
+// EMFPPlusDrawPolygon(path->GetPolygon(*this), rFactoryParms, rState, rCanvas, penIndex);
+
+ break;
+ }
+ case EmfPlusRecordTypeDrawBeziers:
+ {
+ sal_uInt32 aCount;
+ float x1, y1, x2, y2, x3, y3, x4, y4;
+ ::basegfx::B2DPoint aStartPoint, aControlPointA, aControlPointB, aEndPoint;
+ ::basegfx::B2DPolygon aPolygon;
+ rMS.ReadUInt32(aCount);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ DrawBeziers slot: " << (flags & 0xff) << "Number of points: " << aCount);
+
+ SAL_WARN_IF((aCount - 1) % 3 != 0, "cppcanvas.emf", "EMF+\t Bezier Draw not support number of points other than 4, 7, 10, 13, 16...");
+
+ if (aCount < 4)
+ {
+ SAL_WARN("cppcanvas.emf", "EMF+\t Bezier Draw does not support less than 4 points. Number of points: " << aCount);
+ break;
+ }
+
+ ReadPoint(rMS, x1, y1, flags);
+ // We need to add first starting point
+ aStartPoint = Map(x1, y1);
+ aPolygon.append(aStartPoint);
+
+ for (sal_uInt32 i = 4; i <= aCount; i += 3)
+ {
+ ReadPoint(rMS, x2, y2, flags);
+ ReadPoint(rMS, x3, y3, flags);
+ ReadPoint(rMS, x4, y4, flags);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\t Bezier points: " << x1 << "," << y1 << " " << x2 << "," << y2 << " " << x3 << "," << y3 << " " << x4 << "," << y4);
+
+ aStartPoint = Map(x1, y1);
+ aControlPointA = Map(x2, y2);
+ aControlPointB = Map(x3, y3);
+ aEndPoint = Map(x4, y4);
+
+ ::basegfx::B2DCubicBezier cubicBezier(aStartPoint, aControlPointA, aControlPointB, aEndPoint);
+ cubicBezier.adaptiveSubdivideByDistance(aPolygon, 10.0);
+// EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), rFactoryParms,
+// rState, rCanvas, flags & 0xff);
+ // The ending coordinate of one Bezier curve is the starting coordinate of the next.
+ x1 = x4;
+ y1 = y4;
+ }
+ break;
+ }
+ case EmfPlusRecordTypeDrawImage:
+ case EmfPlusRecordTypeDrawImagePoints:
+ {
+ sal_uInt32 attrIndex;
+ sal_Int32 sourceUnit;
+
+ rMS.ReadUInt32(attrIndex).ReadInt32(sourceUnit);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ " << (type == EmfPlusRecordTypeDrawImagePoints ? "DrawImagePoints" : "DrawImage") << "attributes index: " << attrIndex << "source unit: " << sourceUnit);
+ SAL_INFO("cppcanvas.emf", "EMF+\tTODO: use image attributes");
+
+ if (sourceUnit == 2 && aObjects[flags & 0xff]) { // we handle only GraphicsUnit.Pixel now
+ EMFPImage& image = *static_cast<EMFPImage *>(aObjects[flags & 0xff]);
+ float sx, sy, sw, sh;
+ sal_Int32 aCount;
+
+ ReadRectangle(rMS, sx, sy, sw, sh);
+ ::tools::Rectangle aSource(Point(sx, sy), Size(sw, sh));
+
+ SAL_INFO("cppcanvas.emf", "EMF+ " << (type == EmfPlusRecordTypeDrawImagePoints ? "DrawImagePoints" : "DrawImage") << " source rectangle: " << sx << "," << sy << " " << sw << "x" << sh);
+
+ ::basegfx::B2DPoint aDstPoint;
+ ::basegfx::B2DSize aDstSize;
+ bool bValid = false;
+
+ if (type == EmfPlusRecordTypeDrawImagePoints) {
+ rMS.ReadInt32(aCount);
+
+ if (aCount == 3) { // TODO: now that we now that this value is count we should support it better
+ float x1, y1, x2, y2, x3, y3;
+
+ ReadPoint(rMS, x1, y1, flags);
+ ReadPoint(rMS, x2, y2, flags);
+ ReadPoint(rMS, x3, y3, flags);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ destination points: " << x1 << "," << y1 << " " << x2 << "," << y2 << " " << x3 << "," << y3);
+ SAL_INFO("cppcanvas.emf", "EMF+ destination rectangle: " << x1 << "," << y1 << " " << x2 - x1 << "x" << y3 - y1);
+
+ aDstPoint = Map(x1, y1);
+ aDstSize = MapSize(x2 - x1, y3 - y1);
+
+ bValid = true;
+ }
+ }
+ else if (type == EmfPlusRecordTypeDrawImage) {
+ float dx, dy, dw, dh;
+
+ ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
+
+ SAL_INFO("cppcanvas.emf", "EMF+ destination rectangle: " << dx << "," << dy << " " << dw << "x" << dh);
+
+ aDstPoint = Map(dx, dy);
+ aDstSize = MapSize(dw, dh);
+
+ bValid = true;
+ }
+
+ if (bValid) {
+ BitmapEx aBmp(image.graphic.GetBitmapEx());
+ aBmp.Crop(aSource);
+
+ Size aSize(aBmp.GetSizePixel());
+ SAL_INFO("cppcanvas.emf", "EMF+ bitmap size: " << aSize.Width() << "x" << aSize.Height());
+ if (aSize.Width() > 0 && aSize.Height() > 0) {
+// ActionSharedPtr pBmpAction(
+// internal::BitmapActionFactory::createBitmapAction(
+// aBmp,
+// rState.mapModeTransform * aDstPoint,
+// rState.mapModeTransform * aDstSize,
+// rCanvas,
+// rState));
+//
+// if (pBmpAction) {
+// maActions.push_back(MtfAction(pBmpAction,
+// rFactoryParms.mrCurrActionIndex));
+//
+// rFactoryParms.mrCurrActionIndex += pBmpAction->getActionCount() - 1;
+// }
+ }
+ else {
+ SAL_INFO("cppcanvas.emf", "EMF+ warning: empty bitmap");
+ }
+ }
+ else {
+ SAL_WARN("cppcanvas.emf", "EMF+ DrawImage(Points) TODO (fixme)");
+ }
+ }
+ else {
+ SAL_WARN("cppcanvas.emf", "EMF+ DrawImage(Points) TODO (fixme) - possibly unsupported source units for crop rectangle");
+ }
+ break;
+ }
+ case EmfPlusRecordTypeDrawString:
+ {
+ SAL_INFO("cppcanvas.emf", "EMF+ DrawString");
+
+ sal_uInt32 brushId;
+ sal_uInt32 formatId;
+ sal_uInt32 stringLength;
+
+ rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength);
+ SAL_INFO("cppcanvas.emf", "EMF+ DrawString brushId: " << brushId << " formatId: " << formatId << " length: " << stringLength);
+
+ if (flags & 0x8000) {
+ float lx, ly, lw, lh;
+
+ rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh);
+
+ OUString text = read_uInt16s_ToOUString(rMS, stringLength);
+
+ EMFPStringFormat *stringFormat = static_cast< EMFPStringFormat* >(aObjects[formatId & 0xff]);
+ css::rendering::FontRequest aFontRequest;
+ if (stringFormat)
+ {
+ LanguageTag aLanguageTag(static_cast< LanguageType >(stringFormat->language));
+ aFontRequest.Locale = aLanguageTag.getLocale(false);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\t Font locale, Country:" << aLanguageTag.getCountry() << " Language:" << aLanguageTag.getLanguage());
+ }
+ SAL_INFO("cppcanvas.emf", "EMF+\t\t TODO Use all string formatting attributes during drawing");
+
+// double cellSize = setFont(aFontRequest, flags & 0xff, rFactoryParms, rState);
+// rState.textColor = COLOR(brushId);
+//
+// ::basegfx::B2DPoint point(Map(lx + 0.15*cellSize, ly + cellSize));
+//
+// ActionSharedPtr pTextAction(
+// TextActionFactory::createTextAction(
+// // position is just rough guess for now
+// // we should calculate it exactly from layoutRect or font
+// vcl::unotools::pointFromB2DPoint(point),
+// ::Size(),
+// ::Color(),
+// ::Size(),
+// ::Color(),
+// text,
+// 0,
+// stringLength,
+// nullptr,
+// rFactoryParms.mrVDev,
+// rFactoryParms.mrCanvas,
+// rState,
+// rFactoryParms.mrParms,
+// false));
+// if (pTextAction)
+// {
+// SAL_INFO("cppcanvas.emf", "EMF+\t\tadd text action");
+//
+// maActions.push_back(
+// MtfAction(
+// pTextAction,
+// rFactoryParms.mrCurrActionIndex));
+//
+// rFactoryParms.mrCurrActionIndex += pTextAction->getActionCount() - 1;
+// }
+ }
+ else {
+ SAL_WARN("cppcanvas.emf", "EMF+ DrawString TODO - drawing with brush not yet supported");
+ }
+ }
+ break;
+
+ case EmfPlusRecordTypeSetPageTransform:
+ {
+ rMS.ReadFloat(fPageScale);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ SetPageTransform");
+ SAL_INFO("cppcanvas.emf", "EMF+\tscale: " << fPageScale << " unit: " << flags);
+
+ if (flags != UnitTypePixel)
+ SAL_WARN("cppcanvas.emf", "EMF+\t TODO Only UnitTypePixel is supported. ");
+ else
+ {
+ mnMmX *= fPageScale;
+ mnMmY *= fPageScale;
+ }
+ }
+ break;
+ case EmfPlusRecordTypeSetRenderingOrigin:
+ rMS.ReadInt32(nOriginX).ReadInt32(nOriginY);
+ SAL_INFO("cppcanvas.emf", "EMF+ SetRenderingOrigin");
+ SAL_INFO("cppcanvas.emf", "EMF+\torigin [x,y]: " << nOriginX << "," << nOriginY);
+ break;
+ case EmfPlusRecordTypeSetTextRenderingHint:
+ SAL_INFO("cppcanvas.emf", "EMF+ SetTextRenderingHint");
+ SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
+ break;
+ case EmfPlusRecordTypeSetAntiAliasMode:
+ SAL_INFO("cppcanvas.emf", "EMF+ SetAntiAliasMode");
+ SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
+ break;
+ case EmfPlusRecordTypeSetInterpolationMode:
+ SAL_INFO("cppcanvas.emf", "EMF+ InterpolationMode");
+ SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
+ break;
+ case EmfPlusRecordTypeSetPixelOffsetMode:
+ SAL_INFO("cppcanvas.emf", "EMF+ SetPixelOffsetMode");
+ SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
+ break;
+ case EmfPlusRecordTypeSetCompositingQuality:
+ SAL_INFO("cppcanvas.emf", "EMF+ SetCompositingQuality");
+ SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
+ break;
+ case EmfPlusRecordTypeSave:
+ {
+ sal_uInt32 stackIndex;
+
+ rMS.ReadUInt32(stackIndex);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ Save stack index: " << stackIndex);
+
+// GraphicStatePush(mGSStack, stackIndex, rState);
+
+ break;
+ }
+ case EmfPlusRecordTypeRestore:
+ {
+ sal_uInt32 stackIndex;
+
+ rMS.ReadUInt32(stackIndex);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ Restore stack index: " << stackIndex);
+
+// GraphicStatePop(mGSStack, stackIndex, rState);
+
+ break;
+ }
+ case EmfPlusRecordTypeBeginContainerNoParams:
+ {
+ sal_uInt32 stackIndex;
+
+ rMS.ReadUInt32(stackIndex);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ Begin Container No Params stack index: " << stackIndex);
+
+// GraphicStatePush(mGSContainerStack, stackIndex, rState);
+ }
+ break;
+ case EmfPlusRecordTypeEndContainer:
+ {
+ sal_uInt32 stackIndex;
+
+ rMS.ReadUInt32(stackIndex);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ End Container stack index: " << stackIndex);
+
+// GraphicStatePop(mGSContainerStack, stackIndex, rState);
+ }
+ break;
+ case EmfPlusRecordTypeSetWorldTransform: {
+ SAL_INFO("cppcanvas.emf", "EMF+ SetWorldTransform");
+ basegfx::B2DHomMatrix transform;
+ readXForm(rMS, transform);
+ aWorldTransform = transform;
+ SAL_INFO("cppcanvas.emf",
+ "EMF+\tm11: " << aWorldTransform.get(0,0) << "\tm12: " << aWorldTransform.get(1,0) <<
+ "\tm21: " << aWorldTransform.get(0,1) << "\tm22: " << aWorldTransform.get(1,1) <<
+ "\tdx: " << aWorldTransform.get(0,2) << "\tdy: " << aWorldTransform.get(1,2));
+ break;
+ }
+ case EmfPlusRecordTypeResetWorldTransform:
+ SAL_INFO("cppcanvas.emf", "EMF+ ResetWorldTransform");
+ aWorldTransform.identity();
+ break;
+ case EmfPlusRecordTypeMultiplyWorldTransform: {
+ SAL_INFO("cppcanvas.emf", "EMF+ MultiplyWorldTransform");
+ basegfx::B2DHomMatrix transform;
+ readXForm(rMS, transform);
+
+ SAL_INFO("cppcanvas.emf",
+ "EMF+\tmatrix m11: " << transform.get(0,0) << "m12: " << transform.get(0,1) <<
+ "EMF+\tm21: " << transform.get(1,0) << "m22: " << transform.get(1,1) <<
+ "EMF+\tdx: " << transform.get(2,0) << "dy: " << transform.get(2,1));
+
+ if (flags & 0x2000)
+ {
+ // post multiply
+ aWorldTransform *= transform;
+ }
+ else
+ {
+ // pre multiply
+ transform *= aWorldTransform;
+ aWorldTransform = transform;
+ }
+
+ SAL_INFO("cppcanvas.emf",
+ "EMF+\tmatrix m11: " << aWorldTransform.get(0, 0) << "m12: " << aWorldTransform.get(0, 1) <<
+ "EMF+\tm21: " << aWorldTransform.get(1, 0) << "m22: " << aWorldTransform.get(1, 1) <<
+ "EMF+\tdx: " << aWorldTransform.get(2, 0) << "dy: " << aWorldTransform.get(2, 1));
+ break;
+ }
+ case EmfPlusRecordTypeTranslateWorldTransform:
+ {
+ SAL_INFO("cppcanvas.emf", "EMF+ TranslateWorldTransform");
+
+ basegfx::B2DHomMatrix transform;
+ float eDx, eDy;
+
+ rMS.ReadFloat(eDx).ReadFloat(eDy);
+ transform.set(0, 2, eDx);
+ transform.set(1, 2, eDy);
+
+ SAL_INFO("cppcanvas.emf",
+ "EMF+\tmatrix m11: " << transform.get(0, 0) << "m12: " << transform.get(0, 1) <<
+ "EMF+\tm21: " << transform.get(1, 0) << "m22: " << transform.get(1, 1) <<
+ "EMF+\tdx: " << transform.get(2, 0) << "dy: " << transform.get(2, 1));
+
+ if (flags & 0x2000)
+ {
+ // post multiply
+ aWorldTransform *= transform;
+ }
+ else
+ {
+ // pre multiply
+ transform *= aWorldTransform;
+ aWorldTransform = transform;
+ }
+
+ SAL_INFO("cppcanvas.emf",
+ "EMF+\tmatrix m11: " << aWorldTransform.get(0, 0) << "m12: " << aWorldTransform.get(0, 1) <<
+ "EMF+\tm21: " << aWorldTransform.get(1, 0) << "m22: " << aWorldTransform.get(1, 1) <<
+ "EMF+\tdx: " << aWorldTransform.get(2, 0) << "dy: " << aWorldTransform.get(2, 1));
+ break;
+ }
+ case EmfPlusRecordTypeScaleWorldTransform:
+ {
+ basegfx::B2DHomMatrix transform;
+ float eM11, eM22;
+
+ rMS.ReadFloat(eM11).ReadFloat(eM22);
+ transform.set(0, 0, eM11);
+ transform.set(1, 1, eM22);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ ScaleWorldTransform Sx: " << transform.get(0,0) << " Sy: " << transform.get(1,1));
+
+ SAL_INFO("cppcanvas.emf",
+ "EMF+\t m11: " << aWorldTransform.get(0,0) << ", m12: " << aWorldTransform.get(0,1) <<
+ "EMF+\t m21: " << aWorldTransform.get(1,0) << ", m22: " << aWorldTransform.get(1,1) <<
+ "EMF+\t dx: " << aWorldTransform.get(2,0) << ", dy: " << aWorldTransform.get(2,1));
+
+ if (flags & 0x2000)
+ {
+ // post multiply
+ aWorldTransform *= transform;
+ }
+ else
+ {
+ // pre multiply
+ transform *= aWorldTransform;
+ aWorldTransform = transform;
+ }
+
+ SAL_INFO("cppcanvas.emf",
+ "EMF+\t m11: " << aWorldTransform.get(0, 0) << ", m12: " << aWorldTransform.get(0, 1) <<
+ "EMF+\t m21: " << aWorldTransform.get(1, 0) << ", m22: " << aWorldTransform.get(1, 1) <<
+ "EMF+\t dx: " << aWorldTransform.get(2, 0) << ", dy: " << aWorldTransform.get(2, 1));
+ break;
+ }
+ case EmfPlusRecordTypeSetClipRect:
+ {
+ int combineMode = (flags >> 8) & 0xf;
+
+ SAL_INFO("cppcanvas.emf", "EMF+ SetClipRect combine mode: " << combineMode);
+#if OSL_DEBUG_LEVEL > 1
+ if (combineMode > 1) {
+ SAL_INFO("cppcanvas.emf", "EMF+ TODO combine mode > 1");
+ }
+#endif
+
+ float dx, dy, dw, dh;
+
+ ReadRectangle(rMS, dx, dy, dw, dh);
+
+ SAL_INFO("cppcanvas.emf", "EMF+ RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+
+ ::basegfx::B2DPoint mappedPoint(Map(dx, dy));
+ ::basegfx::B2DSize mappedSize(MapSize(dw, dh));
+
+ ::basegfx::B2DPolyPolygon polyPolygon(::basegfx::B2DPolygon(::basegfx::tools::createPolygonFromRect(::basegfx::B2DRectangle(mappedPoint.getX(), mappedPoint.getY(),
+ mappedPoint.getX() + mappedSize.getX(),
+ mappedPoint.getY() + mappedSize.getY()))));
+// polyPolygon.transform(rState.mapModeTransform);
+//
+// updateClipping(polyPolygon, rFactoryParms, combineMode == 1);
+
+ break;
+ }
+ case EmfPlusRecordTypeSetClipPath:
+ {
+ int combineMode = (flags >> 8) & 0xf;
+
+ SAL_INFO("cppcanvas.emf", "EMF+ SetClipPath combine mode: " << combineMode);
+ SAL_INFO("cppcanvas.emf", "EMF+\tpath in slot: " << (flags & 0xff));
+
+ EMFPPath& path = *static_cast<EMFPPath*>(aObjects[flags & 0xff]);
+ ::basegfx::B2DPolyPolygon& clipPoly(path.GetPolygon(*this));
+
+// clipPoly.transform(rState.mapModeTransform);
+ switch (combineMode)
+ {
+ case EmfPlusCombineModeReplace:
+ case EmfPlusCombineModeIntersect:
+ case EmfPlusCombineModeUnion: // Is this, EmfPlusCombineModeXOR and EmfPlusCombineModeComplement correct?
+ case EmfPlusCombineModeXOR:
+ case EmfPlusCombineModeComplement:
+// updateClipping(clipPoly, rFactoryParms, combineMode == 1);
+ break;
+ case EmfPlusCombineModeExclude:
+ // Not doing anything is better then including exactly what we wanted to exclude.
+ break;
+ }
+
+ break;
+ }
+ case EmfPlusRecordTypeSetClipRegion: {
+ int combineMode = (flags >> 8) & 0xf;
+
+ SAL_INFO("cppcanvas.emf", "EMF+ SetClipRegion");
+ SAL_INFO("cppcanvas.emf", "EMF+\tregion in slot: " << (flags & 0xff) << " combine mode: " << combineMode);
+ EMFPRegion *region = static_cast<EMFPRegion*>(aObjects[flags & 0xff]);
+
+ // reset clip
+ if (region && region->parts == 0 && region->initialState == EmfPlusRegionInitialStateInfinite) {
+// updateClipping(::basegfx::B2DPolyPolygon(), rFactoryParms, combineMode == 1);
+ }
+ else {
+ SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
+ }
+ break;
+ }
+ case EmfPlusRecordTypeDrawDriverString: {
+ SAL_INFO("cppcanvas.emf", "EMF+ DrawDriverString, flags: 0x" << std::hex << flags << std::dec);
+ sal_uInt32 brushIndexOrColor;
+ sal_uInt32 optionFlags;
+ sal_uInt32 hasMatrix;
+ sal_uInt32 glyphsCount;
+
+ rMS.ReadUInt32(brushIndexOrColor).ReadUInt32(optionFlags).ReadUInt32(hasMatrix).ReadUInt32(glyphsCount);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\t: " << ((flags & 0x8000) ? "color" : "brush index") << " 0x" << std::hex << brushIndexOrColor << std::dec);
+ SAL_INFO("cppcanvas.emf", "EMF+\toption flags: 0x" << std::hex << optionFlags << std::dec);
+ SAL_INFO("cppcanvas.emf", "EMF+\thas matrix: " << hasMatrix);
+ SAL_INFO("cppcanvas.emf", "EMF+\tglyphs: " << glyphsCount);
+
+ if ((optionFlags & 1) && glyphsCount > 0) {
+ std::unique_ptr<float[]> charsPosX(new float[glyphsCount]);
+ std::unique_ptr<float[]> charsPosY(new float[glyphsCount]);
+
+ OUString text = read_uInt16s_ToOUString(rMS, glyphsCount);
+
+ for (sal_uInt32 i = 0; i<glyphsCount; i++) {
+ rMS.ReadFloat(charsPosX[i]).ReadFloat(charsPosY[i]);
+ SAL_INFO("cppcanvas.emf", "EMF+\tglyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]);
+ }
+
+ basegfx::B2DHomMatrix transform;
+ if (hasMatrix) {
+ readXForm(rMS, transform);
+ SAL_INFO("cppcanvas.emf", "EMF+\tmatrix: " << transform.get(0,0) << ", " << transform.get(1,0) <<
+ ", " << transform.get(0,1) << ", " << transform.get(1,1) <<
+ ", " << transform.get(0,2) << ", " << transform.get(1,2));
+ }
+
+// rendering::FontRequest aFontRequest;
+// // add the text action
+// setFont(aFontRequest, flags & 0xff, rFactoryParms, rState);
+//
+// if (flags & 0x8000)
+// rState.textColor = COLOR(brushIndexOrColor);
+//
+// ::basegfx::B2DPoint point(Map(charsPosX[0], charsPosY[0]));
+//
+// ActionSharedPtr pTextAction(
+// TextActionFactory::createTextAction(
+// vcl::unotools::pointFromB2DPoint(point),
+// ::Size(),
+// ::Color(),
+// ::Size(),
+// ::Color(),
+// text,
+// 0,
+// glyphsCount,
+// nullptr,
+// rFactoryParms.mrVDev,
+// rFactoryParms.mrCanvas,
+// rState,
+// rFactoryParms.mrParms,
+// false));
+//
+// if (pTextAction)
+// {
+// SAL_INFO("cppcanvas.emf", "EMF+\t\tadd text action");
+//
+// maActions.push_back(
+// MtfAction(
+// pTextAction,
+// rFactoryParms.mrCurrActionIndex));
+//
+// rFactoryParms.mrCurrActionIndex += pTextAction->getActionCount() - 1;
+// }
+ }
+ else {
+ SAL_WARN("cppcanvas.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)");
+ }
+
+ break;
+ }
+ default:
+ SAL_WARN("cppcanvas.emf", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec);
+ }
+ }
+
+ rMS.Seek(next);
+
+ if (size <= length)
+ {
+ length -= size;
+ }
+ else
+ {
+ SAL_WARN("cppcanvas.emf", "ImplRenderer::processEMFPlus: "
+ "size " << size << " > length " << length);
+ length = 0;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfphelperdata.hxx b/drawinglayer/source/tools/emfphelperdata.hxx
new file mode 100644
index 000000000000..6b9084cbdd9d
--- /dev/null
+++ b/drawinglayer/source/tools/emfphelperdata.hxx
@@ -0,0 +1,252 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPHELPERDATA_HXX
+#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPHELPERDATA_HXX
+
+#include <emfplushelper.hxx>
+#include <com/sun/star/rendering/XCanvasFont.hpp>
+#include <com/sun/star/rendering/TextDirection.hpp>
+//#include <emfpbrush.hxx>
+
+namespace emfplushelper
+{
+ // EMF+ commands
+#define EmfPlusRecordTypeHeader 0x4001
+#define EmfPlusRecordTypeEndOfFile 0x4002
+ //TODO EmfPlusRecordTypeComment 0x4003
+#define EmfPlusRecordTypeGetDC 0x4004
+ //TODO EmfPlusRecordTypeMultiFormatStart 0x4005
+ //TODO EmfPlusRecordTypeMultiFormatSection 0x4006
+ //TODO EmfPlusRecordTypeMultiFormatEnd 0x4007
+#define EmfPlusRecordTypeObject 0x4008
+ //TODO EmfPlusRecordTypeClear 0x4009
+#define EmfPlusRecordTypeFillRects 0x400A
+#define EmfPlusRecordTypeDrawRects 0x400B
+#define EmfPlusRecordTypeFillPolygon 0x400C
+#define EmfPlusRecordTypeDrawLines 0x400D
+#define EmfPlusRecordTypeFillEllipse 0x400E
+#define EmfPlusRecordTypeDrawEllipse 0x400F
+#define EmfPlusRecordTypeFillPie 0x4010
+#define EmfPlusRecordTypeDrawPie 0x4011
+#define EmfPlusRecordTypeDrawArc 0x4012
+ //TODO EmfPlusRecordTypeFillRegion 0x4013
+#define EmfPlusRecordTypeFillPath 0x4014
+#define EmfPlusRecordTypeDrawPath 0x4015
+ //TODO EmfPlusRecordTypeFillClosedCurve 0x4016
+ //TODO EmfPlusRecordTypeDrawClosedCurve 0x4017
+ //TODO EmfPlusRecordTypeDrawCurve 0x4018
+#define EmfPlusRecordTypeDrawBeziers 0x4019
+#define EmfPlusRecordTypeDrawImage 0x401A
+#define EmfPlusRecordTypeDrawImagePoints 0x401B
+#define EmfPlusRecordTypeDrawString 0x401C
+#define EmfPlusRecordTypeSetRenderingOrigin 0x401D
+#define EmfPlusRecordTypeSetAntiAliasMode 0x401E
+#define EmfPlusRecordTypeSetTextRenderingHint 0x401F
+#define EmfPlusRecordTypeSetInterpolationMode 0x4021
+#define EmfPlusRecordTypeSetPixelOffsetMode 0x4022
+ //TODO EmfPlusRecordTypeSetCompositingMode 0x4023
+#define EmfPlusRecordTypeSetCompositingQuality 0x4024
+#define EmfPlusRecordTypeSave 0x4025
+#define EmfPlusRecordTypeRestore 0x4026
+ //TODO EmfPlusRecordTypeBeginContainer 0x4027
+#define EmfPlusRecordTypeBeginContainerNoParams 0x4028
+#define EmfPlusRecordTypeEndContainer 0x4029
+#define EmfPlusRecordTypeSetWorldTransform 0x402A
+#define EmfPlusRecordTypeResetWorldTransform 0x402B
+#define EmfPlusRecordTypeMultiplyWorldTransform 0x402C
+#define EmfPlusRecordTypeTranslateWorldTransform 0x402D
+#define EmfPlusRecordTypeScaleWorldTransform 0x402E
+ //TODO EmfPlusRecordTypeRotateWorldTransform 0x402F
+#define EmfPlusRecordTypeSetPageTransform 0x4030
+ //TODO EmfPlusRecordTypeResetClip 0x4031
+#define EmfPlusRecordTypeSetClipRect 0x4032
+#define EmfPlusRecordTypeSetClipPath 0x4033
+#define EmfPlusRecordTypeSetClipRegion 0x4034
+ //TODO EmfPlusRecordTypeOffsetClip 0x4035
+#define EmfPlusRecordTypeDrawDriverString 0x4036
+ //TODO EmfPlusRecordTypeStrokeFillPath 0x4037
+ //TODO EmfPlusRecordTypeSerializableObject 0x4038
+ //TODO EmfPlusRecordTypeSetTSGraphics 0x4039
+ //TODO EmfPlusRecordTypeSetTSClip 0x403A
+
+ // EMF+object types
+#define EmfPlusObjectTypeBrush 0x100
+#define EmfPlusObjectTypePen 0x200
+#define EmfPlusObjectTypePath 0x300
+#define EmfPlusObjectTypeRegion 0x400
+#define EmfPlusObjectTypeImage 0x500
+#define EmfPlusObjectTypeFont 0x600
+#define EmfPlusObjectTypeStringFormat 0x700
+#define EmfPlusObjectTypeImageAttributes 0x800
+#define EmfPlusObjectTypeCustomLineCap 0x900
+
+#define EmfPlusRegionInitialStateInfinite 0x10000003
+
+ enum UnitType
+ {
+ UnitTypeWorld = 0x00,
+ UnitTypeDisplay = 0x01,
+ UnitTypePixel = 0x02,
+ UnitTypePoint = 0x03,
+ UnitTypeInch = 0x04,
+ UnitTypeDocument = 0x05,
+ UnitTypeMillimeter = 0x06
+ };
+
+ enum EmfPlusCombineMode
+ {
+ EmfPlusCombineModeReplace = 0x00000000,
+ EmfPlusCombineModeIntersect = 0x00000001,
+ EmfPlusCombineModeUnion = 0x00000002,
+ EmfPlusCombineModeXOR = 0x00000003,
+ EmfPlusCombineModeExclude = 0x00000004,
+ EmfPlusCombineModeComplement = 0x00000005
+ };
+
+ const char* emfTypeToName(sal_uInt16 type);
+ struct EMFPObject
+ {
+ virtual ~EMFPObject();
+ };
+
+ bool readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget);
+ void readRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed);
+
+ struct OutDevState
+ {
+ OutDevState();
+
+ ::basegfx::B2DPolyPolygon clip;
+ ::tools::Rectangle clipRect;
+ css::uno::Reference< css::rendering::XPolyPolygon2D > xClipPoly;
+
+ css::uno::Sequence< double > lineColor;
+ css::uno::Sequence< double > fillColor;
+ css::uno::Sequence< double > textColor;
+ css::uno::Sequence< double > textFillColor;
+ css::uno::Sequence< double > textLineColor;
+
+ /** Current font.
+
+ @attention Beware, this member can be NULL, and
+ nevertheless text output is generated.
+ */
+ css::uno::Reference< css::rendering::XCanvasFont > xFont;
+ ::basegfx::B2DHomMatrix transform;
+ ::basegfx::B2DHomMatrix mapModeTransform;
+ double fontRotation;
+
+ FontEmphasisMark textEmphasisMarkStyle;
+ PushFlags pushFlags;
+ sal_Int8 textDirection;
+ sal_Int8 textAlignment;
+ FontRelief textReliefStyle;
+ sal_Int8 textOverlineStyle;
+ sal_Int8 textUnderlineStyle;
+ sal_Int8 textStrikeoutStyle;
+ TextAlign textReferencePoint;
+
+ bool isTextOutlineModeSet;
+ bool isTextEffectShadowSet;
+ bool isTextWordUnderlineSet;
+
+ bool isLineColorSet;
+ bool isFillColorSet;
+ bool isTextFillColorSet;
+ bool isTextLineColorSet;
+ };
+
+ typedef struct
+ {
+ basegfx::B2DHomMatrix aWorldTransform;
+ OutDevState aDevState;
+ } EmfPlusGraphicState;
+
+ typedef std::map<int, EmfPlusGraphicState> GraphicStateMap;
+
+ struct EmfPlusHelperData
+ {
+ private:
+ // allow setTargetHolders/setPropertyHolders call from there
+ friend class EmfPlusHelper;
+
+ /* EMF+ */
+ basegfx::B2DHomMatrix aBaseTransform;
+ basegfx::B2DHomMatrix aWorldTransform;
+ EMFPObject* aObjects[256];
+ float fPageScale;
+ sal_Int32 nOriginX;
+ sal_Int32 nOriginY;
+ sal_Int32 nHDPI;
+ sal_Int32 nVDPI;
+
+ /* EMF+ emf header info */
+ sal_Int32 mnFrameLeft;
+ sal_Int32 mnFrameTop;
+ sal_Int32 mnFrameRight;
+ sal_Int32 mnFrameBottom;
+ sal_Int32 mnPixX;
+ sal_Int32 mnPixY;
+ sal_Int32 mnMmX;
+ sal_Int32 mnMmY;
+
+ /* multipart object data */
+ bool mbMultipart;
+ sal_uInt16 mMFlags;
+ SvMemoryStream mMStream;
+
+ /* emf+ graphic state stack */
+ GraphicStateMap mGSStack;
+ GraphicStateMap mGSContainerStack;
+
+ /// data holders
+ wmfemfhelper::TargetHolders* mpTargetHolders;
+ wmfemfhelper::PropertyHolders* mpPropertyHolders;
+
+ void processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream = false);
+ void ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags);
+ void ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed = false);
+
+ void MapToDevice(double& x, double& y);
+ public:
+ ::basegfx::B2DPoint Map(double ix, double iy);
+ ::basegfx::B2DSize MapSize(double iwidth, double iheight);
+ private:
+
+ // to set data holders from EmfPlusHelper
+ void setTargetHolders(wmfemfhelper::TargetHolders& rTargetHolders) { mpTargetHolders = &rTargetHolders; }
+ void setPropertyHolders(wmfemfhelper::PropertyHolders& rPropertyHolders) { mpPropertyHolders = &rPropertyHolders; }
+
+ public:
+ EmfPlusHelperData(SvMemoryStream& rMS);
+
+ void processEmfPlusData(
+ SvMemoryStream& rMS,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation);
+
+ /// data holders access
+ wmfemfhelper::TargetHolders& getTargetHolders() const { return *mpTargetHolders; }
+ wmfemfhelper::PropertyHolders& getPropertyHolders() const { return *mpPropertyHolders; }
+ };
+}
+
+#endif // INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPBRUSH_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpimage.cxx b/drawinglayer/source/tools/emfpimage.cxx
new file mode 100644
index 000000000000..5b388c0da660
--- /dev/null
+++ b/drawinglayer/source/tools/emfpimage.cxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <basegfx/tools/canvastools.hxx>
+#include <basegfx/tools/gradienttools.hxx>
+#include <basegfx/tools/tools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <emfpimage.hxx>
+
+namespace emfplushelper
+{
+ void EMFPImage::Read(SvMemoryStream &s, sal_uInt32 dataSize, bool bUseWholeStream)
+ {
+ sal_uInt32 header, bitmapType;
+
+ s.ReadUInt32(header).ReadUInt32(type);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\timage\nEMF+\theader: 0x" << std::hex << header << " type: " << type << std::dec);
+
+ if (type == 1) { // bitmap
+ s.ReadInt32(width).ReadInt32(height).ReadInt32(stride).ReadInt32(pixelFormat).ReadUInt32(bitmapType);
+ SAL_INFO("cppcanvas.emf", "EMF+\tbitmap width: " << width << " height: " << height << " stride: " << stride << " pixelFormat: 0x" << std::hex << pixelFormat << std::dec);
+ if ((bitmapType != 0) || (width == 0)) { // non native formats
+ GraphicFilter filter;
+
+ filter.ImportGraphic(graphic, OUString(), s);
+ SAL_INFO("cppcanvas.emf", "EMF+\tbitmap width: " << graphic.GetBitmap().GetSizePixel().Width() << " height: " << graphic.GetBitmap().GetSizePixel().Height());
+ }
+
+ }
+ else if (type == 2) { // metafile
+ sal_Int32 mfType, mfSize;
+
+ s.ReadInt32(mfType).ReadInt32(mfSize);
+ if (bUseWholeStream)
+ dataSize = s.remainingSize();
+ else
+ dataSize -= 16;
+ SAL_INFO("cppcanvas.emf", "EMF+\tmetafile type: " << mfType << " dataSize: " << mfSize << " real size calculated from record dataSize: " << dataSize);
+
+ GraphicFilter filter;
+ // workaround buggy metafiles, which have wrong mfSize set (n#705956 for example)
+ SvMemoryStream mfStream(const_cast<char *>(static_cast<char const *>(s.GetData()) + s.Tell()), dataSize, StreamMode::READ);
+
+ filter.ImportGraphic(graphic, OUString(), mfStream);
+
+ // debug code - write the stream to debug file /tmp/emf-stream.emf
+#if OSL_DEBUG_LEVEL > 1
+ mfStream.Seek(0);
+ static sal_Int32 emfp_debug_stream_number = 0;
+ OUString emfp_debug_filename = "/tmp/emf-embedded-stream" +
+ OUString::number(emfp_debug_stream_number++) + ".emf";
+
+ SvFileStream file(emfp_debug_filename, StreamMode::WRITE | StreamMode::TRUNC);
+
+ mfStream.WriteStream(file);
+ file.Flush();
+ file.Close();
+#endif
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpimage.hxx b/drawinglayer/source/tools/emfpimage.hxx
new file mode 100644
index 000000000000..d5a06c5e56b9
--- /dev/null
+++ b/drawinglayer/source/tools/emfpimage.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPIMAGE_HXX
+#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPIMAGE_HXX
+
+#include <emfphelperdata.hxx>
+
+namespace emfplushelper
+{
+ struct EMFPImage : public EMFPObject
+ {
+ sal_uInt32 type;
+ sal_Int32 width;
+ sal_Int32 height;
+ sal_Int32 stride;
+ sal_Int32 pixelFormat;
+ Graphic graphic;
+
+ void Read(SvMemoryStream &s, sal_uInt32 dataSize, bool bUseWholeStream);
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfplushelper.cxx b/drawinglayer/source/tools/emfplushelper.cxx
new file mode 100644
index 000000000000..c6c472cf7b30
--- /dev/null
+++ b/drawinglayer/source/tools/emfplushelper.cxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <emfplushelper.hxx>
+#include <emfphelperdata.hxx>
+
+namespace emfplushelper
+{
+ EmfPlusHelper::EmfPlusHelper(SvMemoryStream& rMS)
+ : mpD(new EmfPlusHelperData(rMS))
+ {
+ }
+
+ EmfPlusHelper::~EmfPlusHelper()
+ {
+ delete mpD;
+ }
+
+ void EmfPlusHelper::processEmfPlusData(
+ SvMemoryStream& rMS,
+ wmfemfhelper::TargetHolders& rTargetHolders,
+ wmfemfhelper::PropertyHolders& rPropertyHolders,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation)
+ {
+ mpD->setTargetHolders(rTargetHolders);
+ mpD->setPropertyHolders(rPropertyHolders);
+ mpD->processEmfPlusData(rMS, rViewInformation);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfppath.cxx b/drawinglayer/source/tools/emfppath.cxx
new file mode 100644
index 000000000000..ae4cfa60149a
--- /dev/null
+++ b/drawinglayer/source/tools/emfppath.cxx
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <basegfx/tools/canvastools.hxx>
+#include <basegfx/tools/gradienttools.hxx>
+#include <basegfx/tools/tools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <vcl/canvastools.hxx>
+#include <emfppath.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::basegfx;
+
+namespace emfplushelper
+{
+ EMFPPath::EMFPPath (sal_Int32 _nPoints, bool bLines)
+ {
+ if( _nPoints<0 || sal_uInt32(_nPoints)>SAL_MAX_INT32/(2*sizeof(float)) )
+ _nPoints = SAL_MAX_INT32/(2*sizeof(float));
+ nPoints = _nPoints;
+ pPoints = new float [nPoints*2];
+ if (!bLines)
+ pPointTypes = new sal_uInt8 [_nPoints];
+ else
+ pPointTypes = nullptr;
+ }
+
+ EMFPPath::~EMFPPath ()
+ {
+ delete [] pPoints;
+ delete [] pPointTypes;
+ }
+
+ // TODO: remove rR argument when debug code is no longer needed
+ void EMFPPath::Read (SvStream& s, sal_uInt32 pathFlags, EmfPlusHelperData& rR)
+ {
+ for (int i = 0; i < nPoints; i ++) {
+ if (pathFlags & 0x800) {
+ // EMFPlusPointR: points are stored in EMFPlusInteger7 or
+ // EMFPlusInteger15 objects, see section 2.2.2.21/22
+ // If 0x800 bit is set, the 0x4000 bit is undefined and must be ignored
+ SAL_WARN("cppcanvas.emf", "EMF+\t\t TODO - parse EMFPlusPointR object (section 2.2.1.6)");
+ } else if (pathFlags & 0x4000) {
+ // EMFPlusPoint: stored in signed short 16bit integer format
+ sal_Int16 x, y;
+
+ s.ReadInt16( x ).ReadInt16( y );
+ SAL_INFO ("cppcanvas.emf", "EMF+\t EMFPlusPoint [x,y]: " << x << "," << y);
+ pPoints [i*2] = x;
+ pPoints [i*2 + 1] = y;
+ } else {
+ // EMFPlusPointF: stored in Single (float) format
+ s.ReadFloat( pPoints [i*2] ).ReadFloat( pPoints [i*2 + 1] );
+ SAL_INFO ("cppcanvas.emf", "EMF+\t EMFPlusPointF [x,y]: " << pPoints [i*2] << "," << pPoints [i*2 + 1]);
+ }
+
+ }
+
+ if (pPointTypes)
+ for (int i = 0; i < nPoints; i ++) {
+ s.ReadUChar( pPointTypes [i] );
+ SAL_INFO ("cppcanvas.emf", "EMF+\tpoint type: " << (int)pPointTypes [i]);
+ }
+
+ aPolygon.clear ();
+
+#if OSL_DEBUG_LEVEL > 1
+ const ::basegfx::B2DRectangle aBounds (::basegfx::tools::getRange (GetPolygon (rR)));
+
+ SAL_INFO ("cppcanvas.emf",
+ "EMF+\tpolygon bounding box: " << aBounds.getMinX () << "," << aBounds.getMinY () << aBounds.getWidth () << "x" << aBounds.getHeight () << " (mapped)");
+#else
+ (void) rR; // avoid warnings
+#endif
+ }
+
+ ::basegfx::B2DPolyPolygon& EMFPPath::GetPolygon (EmfPlusHelperData& rR, bool bMapIt, bool bAddLineToCloseShape)
+ {
+ ::basegfx::B2DPolygon polygon;
+
+ aPolygon.clear ();
+
+ int last_normal = 0, p = 0;
+ ::basegfx::B2DPoint prev, mapped;
+ bool hasPrev = false;
+ for (int i = 0; i < nPoints; i ++) {
+ if (p && pPointTypes && (pPointTypes [i] == 0)) {
+ aPolygon.append (polygon);
+ last_normal = i;
+ p = 0;
+ polygon.clear ();
+ }
+
+ if (bMapIt)
+ mapped = rR.Map (pPoints [i*2], pPoints [i*2 + 1]);
+ else
+ mapped = ::basegfx::B2DPoint (pPoints [i*2], pPoints [i*2 + 1]);
+ if (pPointTypes) {
+ if ((pPointTypes [i] & 0x07) == 3) {
+ if (((i - last_normal )% 3) == 1) {
+ polygon.setNextControlPoint (p - 1, mapped);
+ SAL_INFO ("cppcanvas.emf", "polygon append next: " << p - 1 << " mapped: " << mapped.getX () << "," << mapped.getY ());
+ continue;
+ } else if (((i - last_normal) % 3) == 2) {
+ prev = mapped;
+ hasPrev = true;
+ continue;
+ }
+ } else
+ last_normal = i;
+ }
+ polygon.append (mapped);
+ SAL_INFO ("cppcanvas.emf", "polygon append point: " << pPoints [i*2] << "," << pPoints [i*2 + 1] << " mapped: " << mapped.getX () << ":" << mapped.getY ());
+ if (hasPrev) {
+ polygon.setPrevControlPoint (p, prev);
+ SAL_INFO ("cppcanvas.emf", "polygon append prev: " << p << " mapped: " << prev.getX () << "," << prev.getY ());
+ hasPrev = false;
+ }
+ p ++;
+ if (pPointTypes && (pPointTypes [i] & 0x80)) { // closed polygon
+ polygon.setClosed (true);
+ aPolygon.append (polygon);
+ SAL_INFO ("cppcanvas.emf", "close polygon");
+ last_normal = i + 1;
+ p = 0;
+ polygon.clear ();
+ }
+ }
+ // Draw an extra line between the last point and the first point, to close the shape.
+ if (bAddLineToCloseShape) {
+ if (bMapIt)
+ polygon.append (rR.Map (pPoints [0], pPoints [1]) );
+ else
+ polygon.append (::basegfx::B2DPoint (pPoints [0], pPoints [1]) );
+ }
+ if (polygon.count ()) {
+ aPolygon.append (polygon);
+
+#if OSL_DEBUG_LEVEL > 1
+ for (unsigned int i=0; i<aPolygon.count(); i++) {
+ polygon = aPolygon.getB2DPolygon(i);
+ SAL_INFO ("cppcanvas.emf", "polygon: " << i);
+ for (unsigned int j=0; j<polygon.count(); j++) {
+ ::basegfx::B2DPoint point = polygon.getB2DPoint(j);
+ SAL_INFO ("cppcanvas.emf", "point: " << point.getX() << "," << point.getY());
+ if (polygon.isPrevControlPointUsed(j)) {
+ point = polygon.getPrevControlPoint(j);
+ SAL_INFO ("cppcanvas.emf", "prev: " << point.getX() << "," << point.getY());
+ }
+ if (polygon.isNextControlPointUsed(j)) {
+ point = polygon.getNextControlPoint(j);
+ SAL_INFO ("cppcanvas.emf", "next: " << point.getX() << "," << point.getY());
+ }
+ }
+ }
+#endif
+ }
+
+ return aPolygon;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfppath.hxx b/drawinglayer/source/tools/emfppath.hxx
new file mode 100644
index 000000000000..64c7c39c7438
--- /dev/null
+++ b/drawinglayer/source/tools/emfppath.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPPATH_HXX
+#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPPATH_HXX
+
+#include <emfphelperdata.hxx>
+
+namespace emfplushelper
+{
+ struct EMFPPath : public EMFPObject
+ {
+ ::basegfx::B2DPolyPolygon aPolygon;
+ sal_Int32 nPoints;
+ float* pPoints;
+ sal_uInt8* pPointTypes;
+
+ EMFPPath(sal_Int32 _nPoints, bool bLines = false);
+
+ virtual ~EMFPPath() override;
+
+ // TODO: remove rR argument when debug code is no longer needed
+ void Read(SvStream& s, sal_uInt32 pathFlags, EmfPlusHelperData& rR);
+
+ ::basegfx::B2DPolyPolygon& GetPolygon(EmfPlusHelperData& rR, bool bMapIt = true, bool bAddLineToCloseShape = false);
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfppen.cxx b/drawinglayer/source/tools/emfppen.cxx
new file mode 100644
index 000000000000..ef037b0d3157
--- /dev/null
+++ b/drawinglayer/source/tools/emfppen.cxx
@@ -0,0 +1,301 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <basegfx/tools/canvastools.hxx>
+#include <basegfx/tools/gradienttools.hxx>
+#include <basegfx/tools/tools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <vcl/canvastools.hxx>
+#include <emfppen.hxx>
+#include <emfpcustomlinecap.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::basegfx;
+
+namespace emfplushelper
+{
+ enum EmfPlusPenData
+ {
+ PenDataTransform = 0x00000001,
+ PenDataStartCap = 0x00000002,
+ PenDataEndCap = 0x00000004,
+ PenDataJoin = 0x00000008,
+ PenDataMiterLimit = 0x00000010,
+ PenDataLineStyle = 0x00000020,
+ PenDataDashedLineCap = 0x00000040,
+ PenDataDashedLineOffset = 0x00000080,
+ PenDataDashedLine = 0x00000100,
+ PenDataNonCenter = 0x00000200,
+ PenDataCompoundLine = 0x00000400,
+ PenDataCustomStartCap = 0x00000800,
+ PenDataCustomEndCap = 0x00001000
+ };
+
+ const sal_Int32 EmfPlusLineStyleSolid = 0x00000000;
+ const sal_Int32 EmfPlusLineStyleDash = 0x00000001;
+ const sal_Int32 EmfPlusLineStyleDot = 0x00000002;
+ const sal_Int32 EmfPlusLineStyleDashDot = 0x00000003;
+ const sal_Int32 EmfPlusLineStyleDashDotDot = 0x00000004;
+ const sal_Int32 EmfPlusLineStyleCustom = 0x00000005;
+
+ EMFPPen::EMFPPen()
+ : EMFPBrush()
+ , penWidth(0.0)
+ , startCap(0)
+ , endCap(0)
+ , lineJoin(0)
+ , mitterLimit(0.0)
+ , dashStyle(0)
+ , dashCap(0)
+ , dashOffset(0.0)
+ , dashPatternLen(0)
+ , dashPattern(nullptr)
+ , alignment(0)
+ , compoundArrayLen(0)
+ , compoundArray(nullptr)
+ , customStartCapLen(0)
+ , customStartCap(nullptr)
+ , customEndCapLen(0)
+ , customEndCap(nullptr)
+ {
+ }
+
+ EMFPPen::~EMFPPen()
+ {
+ delete[] dashPattern;
+ delete[] compoundArray;
+ delete customStartCap;
+ delete customEndCap;
+ }
+
+ void EMFPPen::SetStrokeWidth(rendering::StrokeAttributes& rStrokeAttributes, EmfPlusHelperData& rR, const OutDevState& rState)
+ {
+ // If a zero width is specified, a minimum value is used, which is determined by the units.
+ //TODO Add support for other units than Pixel
+ rStrokeAttributes.StrokeWidth = fabs((rState.mapModeTransform * rR.MapSize(penWidth == 0.0 ? 0.05 : penWidth, 0)).getLength());
+
+ // tdf#31814 Based on observation of different EMF+ files (eg. exported by ChemDraw),
+ // there is minimal value of line width
+ if (rStrokeAttributes.StrokeWidth < 1.0)
+ {
+ rStrokeAttributes.StrokeWidth = 1.0;
+ }
+ }
+
+ /// Convert stroke caps between EMF+ and rendering API
+ sal_Int8 EMFPPen::lcl_convertStrokeCap(sal_uInt32 nEmfStroke)
+ {
+ switch (nEmfStroke)
+ {
+ case EmfPlusLineCapTypeSquare: return rendering::PathCapType::SQUARE;
+ case EmfPlusLineCapTypeRound: return rendering::PathCapType::ROUND;
+ }
+
+ // we have no mapping for EmfPlusLineCapTypeTriangle = 0x00000003,
+ // so return BUTT always
+ return rendering::PathCapType::BUTT;
+ }
+
+ sal_Int8 EMFPPen::lcl_convertLineJoinType(sal_uInt32 nEmfLineJoin)
+ {
+ switch (nEmfLineJoin)
+ {
+ case EmfPlusLineJoinTypeMiter: // fall-through
+ case EmfPlusLineJoinTypeMiterClipped: return rendering::PathJoinType::MITER;
+ case EmfPlusLineJoinTypeBevel: return rendering::PathJoinType::BEVEL;
+ case EmfPlusLineJoinTypeRound: return rendering::PathJoinType::ROUND;
+ }
+ assert(false); // Line Join type isn't in specification.
+ return 0;
+ }
+
+
+ void EMFPPen::SetStrokeAttributes(rendering::StrokeAttributes& rStrokeAttributes)
+ {
+ rStrokeAttributes.JoinType = lcl_convertLineJoinType(lineJoin);
+
+ if (dashStyle != EmfPlusLineStyleSolid)
+ {
+ const float dash[] = { 3, 3 };
+ const float dot[] = { 1, 3 };
+ const float dashdot[] = { 3, 3, 1, 3 };
+ const float dashdotdot[] = { 3, 3, 1, 3, 1, 3 };
+
+ sal_Int32 nLen = 0;
+ const float *pPattern = nullptr;
+ switch (dashStyle)
+ {
+ case EmfPlusLineStyleDash: nLen = SAL_N_ELEMENTS(dash); pPattern = dash; break;
+ case EmfPlusLineStyleDot: nLen = SAL_N_ELEMENTS(dot); pPattern = dot; break;
+ case EmfPlusLineStyleDashDot: nLen = SAL_N_ELEMENTS(dashdot); pPattern = dashdot; break;
+ case EmfPlusLineStyleDashDotDot: nLen = SAL_N_ELEMENTS(dashdotdot); pPattern = dashdotdot; break;
+ case EmfPlusLineStyleCustom: nLen = dashPatternLen; pPattern = dashPattern; break;
+ }
+ if (nLen > 0)
+ {
+ uno::Sequence<double> aDashArray(nLen);
+ for (int i = 0; i < nLen; ++i)
+ aDashArray[i] = pPattern[i];
+
+ rStrokeAttributes.DashArray = aDashArray;
+ }
+ }
+ }
+
+ void EMFPPen::Read(SvStream& s, EmfPlusHelperData& rR)
+ {
+ sal_uInt32 graphicsVersion, penType, penDataFlags, penUnit;
+ int i;
+
+ s.ReadUInt32(graphicsVersion).ReadUInt32(penType).ReadUInt32(penDataFlags).ReadUInt32(penUnit).ReadFloat(penWidth);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\tpen");
+ SAL_INFO("cppcanvas.emf", "EMF+\t graphics version: 0x" << std::hex << graphicsVersion << " type (must be set to zero): " << penType <<
+ " pen data flags: 0x" << penDataFlags << " unit: " << penUnit << " width: " << std::dec << penWidth);
+
+ if (penDataFlags & PenDataTransform)
+ readXForm(s, pen_transformation);
+
+ if (penDataFlags & PenDataStartCap)
+ {
+ s.ReadInt32(startCap);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tstartCap: 0x" << std::hex << startCap);
+ }
+ else
+ startCap = 0;
+
+ if (penDataFlags & PenDataEndCap)
+ {
+ s.ReadInt32(endCap);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tendCap: 0x" << std::hex << endCap);
+ }
+ else
+ endCap = 0;
+
+ if (penDataFlags & PenDataJoin)
+ s.ReadInt32(lineJoin);
+ else
+ lineJoin = 0;
+
+ if (penDataFlags & PenDataMiterLimit)
+ s.ReadFloat(mitterLimit);
+ else
+ mitterLimit = 0;
+
+ if (penDataFlags & PenDataLineStyle)
+ {
+ s.ReadInt32(dashStyle);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tdashStyle: 0x" << std::hex << dashStyle);
+ }
+ else
+ dashStyle = 0;
+
+ if (penDataFlags & PenDataDashedLineCap)
+ s.ReadInt32(dashCap);
+ else
+ dashCap = 0;
+
+ if (penDataFlags & PenDataDashedLineOffset)
+ s.ReadFloat(dashOffset);
+ else
+ dashOffset = 0;
+
+ if (penDataFlags & PenDataDashedLine)
+ {
+ dashStyle = EmfPlusLineStyleCustom;
+
+ s.ReadInt32(dashPatternLen);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tdashPatternLen: " << dashPatternLen);
+
+ if (dashPatternLen<0 || sal_uInt32(dashPatternLen)>SAL_MAX_INT32 / sizeof(float))
+ dashPatternLen = SAL_MAX_INT32 / sizeof(float);
+ dashPattern = new float[dashPatternLen];
+ for (i = 0; i < dashPatternLen; i++)
+ {
+ s.ReadFloat(dashPattern[i]);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\t\tdashPattern[" << i << "]: " << dashPattern[i]);
+ }
+ }
+ else
+ dashPatternLen = 0;
+
+ if (penDataFlags & PenDataNonCenter)
+ s.ReadInt32(alignment);
+ else
+ alignment = 0;
+
+ if (penDataFlags & PenDataCompoundLine) {
+ s.ReadInt32(compoundArrayLen);
+ if (compoundArrayLen<0 || sal_uInt32(compoundArrayLen)>SAL_MAX_INT32 / sizeof(float))
+ compoundArrayLen = SAL_MAX_INT32 / sizeof(float);
+ compoundArray = new float[compoundArrayLen];
+ for (i = 0; i < compoundArrayLen; i++)
+ s.ReadFloat(compoundArray[i]);
+ }
+ else
+ compoundArrayLen = 0;
+
+ if (penDataFlags & PenDataCustomStartCap)
+ {
+ s.ReadInt32(customStartCapLen);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tcustomStartCapLen: " << customStartCapLen);
+ sal_uInt64 const pos = s.Tell();
+
+ customStartCap = new EMFPCustomLineCap();
+ customStartCap->Read(s, rR);
+
+ // maybe we don't read everything yet, play it safe ;-)
+ s.Seek(pos + customStartCapLen);
+ }
+ else
+ customStartCapLen = 0;
+
+ if (penDataFlags & PenDataCustomEndCap)
+ {
+ s.ReadInt32(customEndCapLen);
+ SAL_INFO("cppcanvas.emf", "EMF+\t\tcustomEndCapLen: " << customEndCapLen);
+ sal_uInt64 const pos = s.Tell();
+
+ customEndCap = new EMFPCustomLineCap();
+ customEndCap->Read(s, rR);
+
+ // maybe we don't read everything yet, play it safe ;-)
+ s.Seek(pos + customEndCapLen);
+ }
+ else
+ customEndCapLen = 0;
+
+ EMFPBrush::Read(s, rR);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfppen.hxx b/drawinglayer/source/tools/emfppen.hxx
new file mode 100644
index 000000000000..4e2dc6d38156
--- /dev/null
+++ b/drawinglayer/source/tools/emfppen.hxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPPEN_HXX
+#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPPEN_HXX
+
+#include <emfpbrush.hxx>
+#include <com/sun/star/rendering/StrokeAttributes.hpp>
+
+namespace emfplushelper
+{
+ const sal_uInt32 EmfPlusLineCapTypeSquare = 0x00000001;
+ const sal_uInt32 EmfPlusLineCapTypeRound = 0x00000002;
+
+ const sal_uInt32 EmfPlusLineJoinTypeMiter = 0x00000000;
+ const sal_uInt32 EmfPlusLineJoinTypeBevel = 0x00000001;
+ const sal_uInt32 EmfPlusLineJoinTypeRound = 0x00000002;
+ const sal_uInt32 EmfPlusLineJoinTypeMiterClipped = 0x00000003;
+
+ struct EMFPCustomLineCap;
+
+ struct EMFPPen : public EMFPBrush
+ {
+ basegfx::B2DHomMatrix pen_transformation; //TODO: This isn't used
+ float penWidth;
+ sal_Int32 startCap;
+ sal_Int32 endCap;
+ sal_Int32 lineJoin;
+ float mitterLimit;
+ sal_Int32 dashStyle;
+ sal_Int32 dashCap;
+ float dashOffset;
+ sal_Int32 dashPatternLen;
+ float *dashPattern;
+ sal_Int32 alignment;
+ sal_Int32 compoundArrayLen;
+ float *compoundArray;
+ sal_Int32 customStartCapLen;
+ EMFPCustomLineCap *customStartCap;
+ sal_Int32 customEndCapLen;
+ EMFPCustomLineCap *customEndCap;
+
+ EMFPPen();
+
+ virtual ~EMFPPen() override;
+
+ void SetStrokeWidth(com::sun::star::rendering::StrokeAttributes& rStrokeAttributes, EmfPlusHelperData& rR, const OutDevState& rState);
+
+ void SetStrokeAttributes(com::sun::star::rendering::StrokeAttributes& rStrokeAttributes);
+
+ void Read(SvStream& s, EmfPlusHelperData& rR);
+
+ static sal_Int8 lcl_convertStrokeCap(sal_uInt32 nEmfStroke);
+ static sal_Int8 lcl_convertLineJoinType(sal_uInt32 nEmfLineJoin);
+
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpregion.cxx b/drawinglayer/source/tools/emfpregion.cxx
new file mode 100644
index 000000000000..e4e58e70c683
--- /dev/null
+++ b/drawinglayer/source/tools/emfpregion.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <basegfx/tools/canvastools.hxx>
+#include <basegfx/tools/gradienttools.hxx>
+#include <basegfx/tools/tools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <vcl/canvastools.hxx>
+#include <emfpregion.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::basegfx;
+
+namespace emfplushelper
+{
+ EMFPRegion::EMFPRegion()
+ : parts(0)
+ , combineMode(nullptr)
+ , initialState(0)
+ , ix(0.0)
+ , iy(0.0)
+ , iw(0.0)
+ , ih(0.0)
+ {
+ }
+
+ EMFPRegion::~EMFPRegion()
+ {
+ if (combineMode) {
+ delete[] combineMode;
+ combineMode = nullptr;
+ }
+ }
+
+ void EMFPRegion::Read(SvStream& s)
+ {
+ sal_uInt32 header;
+
+ s.ReadUInt32(header).ReadInt32(parts);
+
+ SAL_INFO("cppcanvas.emf", "EMF+\tregion");
+ SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << header << " parts: " << parts << std::dec);
+
+ if (parts) {
+ if (parts<0 || sal_uInt32(parts)>SAL_MAX_INT32 / sizeof(sal_Int32))
+ parts = SAL_MAX_INT32 / sizeof(sal_Int32);
+
+ combineMode = new sal_Int32[parts];
+
+ for (int i = 0; i < parts; i++) {
+ s.ReadInt32(combineMode[i]);
+ SAL_INFO("cppcanvas.emf", "EMF+\tcombine mode [" << i << "]: 0x" << std::hex << combineMode[i] << std::dec);
+ }
+ }
+
+ s.ReadInt32(initialState);
+ SAL_INFO("cppcanvas.emf", "EMF+\tinitial state: 0x" << std::hex << initialState << std::dec);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpregion.hxx b/drawinglayer/source/tools/emfpregion.hxx
new file mode 100644
index 000000000000..b537c28b5214
--- /dev/null
+++ b/drawinglayer/source/tools/emfpregion.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPREGION_HXX
+#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPREGION_HXX
+
+#include <emfphelperdata.hxx>
+
+namespace emfplushelper
+{
+ struct EMFPRegion : public EMFPObject
+ {
+ sal_Int32 parts;
+ sal_Int32 *combineMode;
+ sal_Int32 initialState;
+ float ix, iy, iw, ih;
+
+ EMFPRegion();
+ virtual ~EMFPRegion() override;
+ void Read(SvStream& s);
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpstringformat.cxx b/drawinglayer/source/tools/emfpstringformat.cxx
new file mode 100644
index 000000000000..0af2409fa17f
--- /dev/null
+++ b/drawinglayer/source/tools/emfpstringformat.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <vcl/canvastools.hxx>
+#include <emfpstringformat.hxx>
+
+namespace emfplushelper
+{
+ EMFPStringFormat::EMFPStringFormat()
+ : header(0)
+ , stringFormatFlags(0)
+ , language(0)
+ , stringAlignment(0)
+ , lineAlign(0)
+ , digitSubstitution(0)
+ , digitLanguage(0)
+ , firstTabOffset(0.0)
+ , hotkeyPrefix(0)
+ , leadingMargin(0.0)
+ , trailingMargin(0.0)
+ , tracking(0.0)
+ , trimming(0)
+ , tabStopCount(0)
+ , rangeCount(0)
+ {
+ }
+
+ void EMFPStringFormat::Read(SvMemoryStream &s)
+ {
+ s.ReadUInt32(header).ReadUInt32(stringFormatFlags).ReadUInt32(language);
+ s.ReadUInt32(stringAlignment).ReadUInt32(lineAlign).ReadUInt32(digitSubstitution).ReadUInt32(digitLanguage);
+ s.ReadFloat(firstTabOffset).ReadInt32(hotkeyPrefix).ReadFloat(leadingMargin).ReadFloat(trailingMargin).ReadFloat(tracking);
+ s.ReadInt32(trimming).ReadInt32(tabStopCount).ReadInt32(rangeCount);
+
+ SAL_WARN_IF((header >> 12) != 0xdbc01, "cppcanvas.emf", "Invalid header - not 0xdbc01");
+
+ SAL_INFO("cppcanvas.emf", "EMF+\t string format\nEMF+\theader: 0x" << std::hex << (header >> 12) << " version: 0x" << (header & 0x1fff) << " StringFormatFlags: " << std::dec << stringFormatFlags << " Language: " << language);
+ SAL_INFO("cppcanvas.emf", "EMF+\t StringAlignment: " << stringAlignment << " LineAlign: " << lineAlign << " DigitSubstitution: " << digitSubstitution << " DigitLanguage: " << digitLanguage);
+ SAL_INFO("cppcanvas.emf", "EMF+\t FirstTabOffset: " << firstTabOffset << " HotkeyPrefix: " << hotkeyPrefix << " LeadingMargin: " << leadingMargin << " TrailingMargin: " << trailingMargin << " Tracking: " << tracking);
+ SAL_INFO("cppcanvas.emf", "EMF+\t Trimming: " << trimming << " TabStopCount: " << tabStopCount << " RangeCount: " << rangeCount);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpstringformat.hxx b/drawinglayer/source/tools/emfpstringformat.hxx
new file mode 100644
index 000000000000..58510850e880
--- /dev/null
+++ b/drawinglayer/source/tools/emfpstringformat.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPSTRINGFORMAT_HXX
+#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPSTRINGFORMAT_HXX
+
+#include <emfphelperdata.hxx>
+
+namespace emfplushelper
+{
+ struct EMFPStringFormat : public EMFPObject
+ {
+ sal_uInt32 header;
+ sal_uInt32 stringFormatFlags;
+ sal_uInt32 language;
+ sal_uInt32 stringAlignment;
+ sal_uInt32 lineAlign;
+ sal_uInt32 digitSubstitution;
+ sal_uInt32 digitLanguage;
+ float firstTabOffset;
+ sal_Int32 hotkeyPrefix;
+ float leadingMargin;
+ float trailingMargin;
+ float tracking;
+ sal_Int32 trimming;
+ sal_Int32 tabStopCount;
+ sal_Int32 rangeCount;
+
+ EMFPStringFormat();
+ void Read(SvMemoryStream &s);
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/wmfemfhelper.cxx b/drawinglayer/source/tools/wmfemfhelper.cxx
new file mode 100644
index 000000000000..f2231a1879fe
--- /dev/null
+++ b/drawinglayer/source/tools/wmfemfhelper.cxx
@@ -0,0 +1,3119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <wmfemfhelper.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/fillgraphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/wallpaperprimitive2d.hxx>
+//#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+//#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+//#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+//#include <i18nlangtag/languagetag.hxx>
+#include <drawinglayer/primitive2d/textlineprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx>
+//#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
+#include <tools/fract.hxx>
+//#include <numeric>
+#include <emfplushelper.hxx>
+
+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 overrides the decomposition of the C++
+ implementation class to do an alternative decomposition.
+ */
+ class NonOverlappingFillGradientPrimitive2D : public FillGradientPrimitive2D
+ {
+ protected:
+ /// local decomposition.
+ virtual void create2DDecomposition(Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& rViewInformation) const override;
+
+ public:
+ /// constructor
+ NonOverlappingFillGradientPrimitive2D(
+ const basegfx::B2DRange& rObjectRange,
+ const attribute::FillGradientAttribute& rFillGradient)
+ : FillGradientPrimitive2D(rObjectRange, rFillGradient)
+ {
+ }
+ };
+
+ void NonOverlappingFillGradientPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if (!getFillGradient().isDefault())
+ {
+ createFill(rContainer, false);
+ }
+ }
+ } // end of namespace primitive2d
+} // end of namespace drawinglayer
+
+namespace wmfemfhelper
+{
+ /** helper class for graphic context
+
+ This class allows to hold a complete representation of classic
+ VCL OutputDevice state. This data is needed for correct
+ interpretation of the MetaFile action flow.
+ */
+ PropertyHolder::PropertyHolder()
+ : maTransformation(),
+ maMapUnit(MapUnit::Map100thMM),
+ maLineColor(),
+ maFillColor(),
+ maTextColor(COL_BLACK),
+ maTextFillColor(),
+ maTextLineColor(),
+ maOverlineColor(),
+ maClipPolyPoygon(),
+ maFont(),
+ maRasterOp(RasterOp::OverPaint),
+ mnLayoutMode(ComplexTextLayoutFlags::Default),
+ maLanguageType(0),
+ mnPushFlags(PushFlags::NONE),
+ mbLineColor(false),
+ mbFillColor(false),
+ mbTextColor(true),
+ mbTextFillColor(false),
+ mbTextLineColor(false),
+ mbOverlineColor(false),
+ mbClipPolyPolygonActive(false)
+ {
+ }
+}
+
+namespace wmfemfhelper
+{
+ /** 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.
+ */
+ PropertyHolders::PropertyHolders()
+ {
+ maPropertyHolders.push_back(new PropertyHolder());
+ }
+
+ void PropertyHolders::PushDefault()
+ {
+ PropertyHolder* pNew = new PropertyHolder();
+ maPropertyHolders.push_back(pNew);
+ }
+
+ void PropertyHolders::Push(PushFlags nPushFlags)
+ {
+ if (bool(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 PropertyHolders::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 PushFlags nPushFlags(pTip->getPushFlags());
+
+ if (nPushFlags != PushFlags::NONE)
+ {
+ if (nSize > 1)
+ {
+ // copy back content for all non-set flags
+ PropertyHolder* pLast = maPropertyHolders[nSize - 2];
+
+ if (PushFlags::ALL != nPushFlags)
+ {
+ if (!(nPushFlags & PushFlags::LINECOLOR))
+ {
+ pLast->setLineColor(pTip->getLineColor());
+ pLast->setLineColorActive(pTip->getLineColorActive());
+ }
+ if (!(nPushFlags & PushFlags::FILLCOLOR))
+ {
+ pLast->setFillColor(pTip->getFillColor());
+ pLast->setFillColorActive(pTip->getFillColorActive());
+ }
+ if (!(nPushFlags & PushFlags::FONT))
+ {
+ pLast->setFont(pTip->getFont());
+ }
+ if (!(nPushFlags & PushFlags::TEXTCOLOR))
+ {
+ pLast->setTextColor(pTip->getTextColor());
+ pLast->setTextColorActive(pTip->getTextColorActive());
+ }
+ if (!(nPushFlags & PushFlags::MAPMODE))
+ {
+ pLast->setTransformation(pTip->getTransformation());
+ pLast->setMapUnit(pTip->getMapUnit());
+ }
+ if (!(nPushFlags & PushFlags::CLIPREGION))
+ {
+ pLast->setClipPolyPolygon(pTip->getClipPolyPolygon());
+ pLast->setClipPolyPolygonActive(pTip->getClipPolyPolygonActive());
+ }
+ if (!(nPushFlags & PushFlags::RASTEROP))
+ {
+ pLast->setRasterOp(pTip->getRasterOp());
+ }
+ if (!(nPushFlags & PushFlags::TEXTFILLCOLOR))
+ {
+ pLast->setTextFillColor(pTip->getTextFillColor());
+ pLast->setTextFillColorActive(pTip->getTextFillColorActive());
+ }
+ if (!(nPushFlags & PushFlags::TEXTALIGN))
+ {
+ if (pLast->getFont().GetAlignment() != pTip->getFont().GetAlignment())
+ {
+ vcl::Font aFont(pLast->getFont());
+ aFont.SetAlignment(pTip->getFont().GetAlignment());
+ pLast->setFont(aFont);
+ }
+ }
+ if (!(nPushFlags & PushFlags::REFPOINT))
+ {
+ // not supported
+ }
+ if (!(nPushFlags & PushFlags::TEXTLINECOLOR))
+ {
+ pLast->setTextLineColor(pTip->getTextLineColor());
+ pLast->setTextLineColorActive(pTip->getTextLineColorActive());
+ }
+ if (!(nPushFlags & PushFlags::TEXTLAYOUTMODE))
+ {
+ pLast->setLayoutMode(pTip->getLayoutMode());
+ }
+ if (!(nPushFlags & PushFlags::TEXTLANGUAGE))
+ {
+ pLast->setLanguageType(pTip->getLanguageType());
+ }
+ if (!(nPushFlags & PushFlags::OVERLINECOLOR))
+ {
+ pLast->setOverlineColor(pTip->getOverlineColor());
+ pLast->setOverlineColorActive(pTip->getOverlineColorActive());
+ }
+ }
+ }
+ }
+
+ // execute the pop
+ delete maPropertyHolders.back();
+ maPropertyHolders.pop_back();
+ }
+ }
+
+ PropertyHolder& PropertyHolders::Current()
+ {
+ static PropertyHolder aDummy;
+ OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: CURRENT with no property holders (!)");
+ return maPropertyHolders.empty() ? aDummy : *maPropertyHolders.back();
+ }
+
+ PropertyHolders::~PropertyHolders()
+ {
+ while (!maPropertyHolders.empty())
+ {
+ delete maPropertyHolders.back();
+ maPropertyHolders.pop_back();
+ }
+ }
+}
+
+namespace
+{
+ /** helper to convert a vcl::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 vcl::Region& rRegion)
+ {
+ basegfx::B2DPolyPolygon aRetval;
+
+ if (!rRegion.IsEmpty())
+ {
+ aRetval = rRegion.GetAsB2DPolyPolygon();
+ }
+
+ return aRetval;
+ }
+}
+
+namespace wmfemfhelper
+{
+ /** Helper class to buffer and hold a Primitive target vector. It
+ encapsulates the new/delete functionality and allows to work
+ on pointers of the implementation classes. All data will
+ be converted to uno sequences of uno references when accessing the
+ data.
+ */
+ TargetHolder::TargetHolder()
+ : aTargets()
+ {
+ }
+
+ TargetHolder::~TargetHolder()
+ {
+ const sal_uInt32 nCount(aTargets.size());
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ delete aTargets[a];
+ }
+ }
+
+ sal_uInt32 TargetHolder::size() const
+ {
+ return aTargets.size();
+ }
+
+ void TargetHolder::append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate)
+ {
+ if (pCandidate)
+ {
+ aTargets.push_back(pCandidate);
+ }
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer TargetHolder::getPrimitive2DSequence(const PropertyHolder& rPropertyHolder)
+ {
+ const sal_uInt32 nCount(aTargets.size());
+ drawinglayer::primitive2d::Primitive2DContainer 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.empty() && 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::Primitive2DContainer{ xMask };
+ }
+ }
+
+ return xRetval;
+ }
+}
+
+namespace wmfemfhelper
+{
+ /** Helper class which builds a stack on the TargetHolder class */
+ TargetHolders::TargetHolders()
+ {
+ maTargetHolders.push_back(new TargetHolder());
+ }
+
+ sal_uInt32 TargetHolders::size() const
+ {
+ return maTargetHolders.size();
+ }
+
+ void TargetHolders::Push()
+ {
+ maTargetHolders.push_back(new TargetHolder());
+ }
+
+ void TargetHolders::Pop()
+ {
+ OSL_ENSURE(maTargetHolders.size(), "TargetHolders: POP with no property holders (!)");
+ if (!maTargetHolders.empty())
+ {
+ delete maTargetHolders.back();
+ maTargetHolders.pop_back();
+ }
+ }
+
+ TargetHolder& TargetHolders::Current()
+ {
+ static TargetHolder aDummy;
+ OSL_ENSURE(maTargetHolders.size(), "TargetHolders: CURRENT with no property holders (!)");
+ return maTargetHolders.empty() ? aDummy : *maTargetHolders.back();
+ }
+
+ TargetHolders::~TargetHolders()
+ {
+ while (!maTargetHolders.empty())
+ {
+ delete maTargetHolders.back();
+ maTargetHolders.pop_back();
+ }
+ }
+}
+
+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;
+ }
+}
+
+namespace wmfemfhelper
+{
+ /** helper to create a PointArrayPrimitive2D based on current context */
+ void createPointArrayPrimitive(
+ const std::vector< basegfx::B2DPoint >& rPositions,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperties,
+ const basegfx::BColor& rBColor)
+ {
+ if(!rPositions.empty())
+ {
+ if(rProperties.getTransformation().isIdentity())
+ {
+ rTarget.append(
+ new drawinglayer::primitive2d::PointArrayPrimitive2D(
+ rPositions,
+ rBColor));
+ }
+ else
+ {
+ std::vector< basegfx::B2DPoint > aPositions(rPositions);
+
+ for(basegfx::B2DPoint & aPosition : aPositions)
+ {
+ aPosition = rProperties.getTransformation() * aPosition;
+ }
+
+ rTarget.append(
+ new drawinglayer::primitive2d::PointArrayPrimitive2D(
+ aPositions,
+ rBColor));
+ }
+ }
+ }
+
+ /** 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(LineStyle::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(),
+ rLineInfo.GetLineCap());
+
+ 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 GradientStyle::Linear :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Linear;
+ break;
+ }
+ case GradientStyle::Axial :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Axial;
+ break;
+ }
+ case GradientStyle::Radial :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Radial;
+ break;
+ }
+ case GradientStyle::Elliptical :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Elliptical;
+ break;
+ }
+ case GradientStyle::Square :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Square;
+ break;
+ }
+ default : // GradientStyle::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 HatchStyle::Single :
+ {
+ aHatchStyle = drawinglayer::attribute::HatchStyle::Single;
+ break;
+ }
+ case HatchStyle::Double :
+ {
+ aHatchStyle = drawinglayer::attribute::HatchStyle::Double;
+ break;
+ }
+ case HatchStyle::Triple :
+ {
+ aHatchStyle = drawinglayer::attribute::HatchStyle::Triple;
+ break;
+ }
+ }
+
+ return drawinglayer::attribute::FillHatchAttribute(
+ aHatchStyle,
+ (double)rHatch.GetDistance(),
+ (double)rHatch.GetAngle() * F_PI1800,
+ rHatch.GetColor().getBColor(),
+ 3, // same default as VCL, a minimum of three discrete units (pixels) offset
+ false);
+ }
+
+ /** helper to take needed action on ClipRegion change. This method needs to be called
+ on any vcl::Region change, e.g. at the obvious actions doing this, but also at pop-calls
+ which change the vcl::Region of the current context. It takes care of creating the
+ current embedded context, set the new vcl::Region at the context and possibly prepare
+ a new target for including new geometry into the current region
+ */
+ void HandleNewClipRegion(
+ const basegfx::B2DPolyPolygon& rClipPolyPolygon,
+ TargetHolders& rTargetHolders,
+ PropertyHolders& rPropertyHolders)
+ {
+ const bool bNewActive(rClipPolyPolygon.count());
+
+ // #i108636# The handling 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 unnecessarily 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::Primitive2DContainer aSubContent;
+
+ if(rPropertyHolders.Current().getClipPolyPolygon().count()
+ && rTargetHolders.Current().size())
+ {
+ aSubContent = rTargetHolders.Current().getPrimitive2DSequence(
+ rPropertyHolders.Current());
+ }
+
+ rTargetHolders.Pop();
+
+ if(!aSubContent.empty())
+ {
+ 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 changing RasterOp is used. Currently, RasterOp::Xor and RasterOp::Invert are supported using
+ InvertPrimitive2D, and RasterOp::N0 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::Primitive2DContainer aSubContent;
+
+ if(rTargetHolders.Current().size())
+ {
+ aSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
+ }
+
+ rTargetHolders.Pop();
+
+ if(!aSubContent.empty())
+ {
+ if(rPropertyHolders.Current().isRasterOpForceBlack())
+ {
+ // force content to black
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
+ aSubContent,
+ basegfx::BColorModifierSharedPtr(
+ new basegfx::BColorModifier_replace(
+ 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::Primitive2DContainer xSeq { xPrim };
+
+ 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 wanted 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()
+ || (WallpaperStyle::Tile != eWallpaperStyle && WallpaperStyle::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::Primitive2DContainer { xPrim }));
+ }
+ }
+
+ /** helper to decide UnderlineAbove for text primitives */
+ bool isUnderlineAbove(const vcl::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 vcl::Font& rFont = rProperty.getFont();
+ basegfx::B2DVector aFontScaling;
+
+ rFontAttribute = drawinglayer::attribute::FontAttribute(
+ drawinglayer::primitive2d::getFontAttributeFromVclFont(
+ aFontScaling,
+ rFont,
+ bool(rProperty.getLayoutMode() & ComplexTextLayoutFlags::BiDiRtl),
+ bool(rProperty.getLayoutMode() & ComplexTextLayoutFlags::BiDiStrong)));
+
+ // add FontScaling
+ rTextTransform.scale(aFontScaling.getX(), aFontScaling.getY());
+
+ // take text align into account
+ if(ALIGN_BASELINE != rFont.GetAlignment())
+ {
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(rFont);
+
+ if(ALIGN_TOP == rFont.GetAlignment())
+ {
+ 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 processMetaTextAction(
+ const Point& rTextStartPosition,
+ const OUString& rText,
+ sal_uInt16 nTextStart,
+ sal_uInt16 nTextLength,
+ const std::vector< double >& rDXArray,
+ TargetHolder& rTarget,
+ PropertyHolder& rProperty)
+ {
+ drawinglayer::primitive2d::BasePrimitive2D* pResult = nullptr;
+ const vcl::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 Color aFillColor(rFont.GetFillColor());
+ const css::lang::Locale aLocale(LanguageTag(rProperty.getLanguageType()).getLocale());
+ const bool bWordLineMode(rFont.IsWordLineMode());
+
+ const bool bDecoratedIsNeeded(
+ LINESTYLE_NONE != rFont.GetOverline()
+ || LINESTYLE_NONE != rFont.GetUnderline()
+ || STRIKEOUT_NONE != rFont.GetStrikeout()
+ || FontEmphasisMark::NONE != (rFont.GetEmphasisMark() & FontEmphasisMark::Style)
+ || FontRelief::NONE != rFont.GetRelief()
+ || rFont.IsShadow()
+ || bWordLineMode);
+
+ if(bDecoratedIsNeeded)
+ {
+ // prepare overline, underline and strikeout data
+ const drawinglayer::primitive2d::TextLine eFontOverline(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rFont.GetOverline()));
+ const drawinglayer::primitive2d::TextLine eFontLineStyle(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rFont.GetUnderline()));
+ const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rFont.GetStrikeout()));
+
+ // check UndelineAbove
+ const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE != eFontLineStyle && isUnderlineAbove(rFont));
+
+ // prepare emphasis mark data
+ drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE);
+
+ switch(rFont.GetEmphasisMark() & FontEmphasisMark::Style)
+ {
+ case FontEmphasisMark::Dot : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT; break;
+ case FontEmphasisMark::Circle : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE; break;
+ case FontEmphasisMark::Disc : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC; break;
+ case FontEmphasisMark::Accent : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT; break;
+ default: break;
+ }
+
+ const bool bEmphasisMarkAbove(rFont.GetEmphasisMark() & FontEmphasisMark::PosAbove);
+ const bool bEmphasisMarkBelow(rFont.GetEmphasisMark() & FontEmphasisMark::PosBelow);
+
+ // prepare font relief data
+ drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
+
+ switch(rFont.GetRelief())
+ {
+ case FontRelief::Embossed : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
+ case FontRelief::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,
+ aFillColor,
+
+ // attributes for TextDecoratedPortionPrimitive2D
+ rProperty.getOverlineColorActive() ? rProperty.getOverlineColor() : aFontColor,
+ rProperty.getTextLineColorActive() ? rProperty.getTextLineColor() : aFontColor,
+ eFontOverline,
+ eFontLineStyle,
+ 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::Primitive2DContainer 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::Primitive2DContainer { aReference }));
+ }
+ }
+ }
+
+ /** 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::mapFontLineStyleToTextLine(rAction.GetOverline()));
+ const drawinglayer::primitive2d::TextLine aUnderlineMode(drawinglayer::primitive2d::mapFontLineStyleToTextLine(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 css::lang::Locale aLocale(LanguageTag(
+ rProperty.getLanguageType()).getLocale());
+
+ 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.empty())
+ {
+ // add created text primitive to target
+ if(rProperty.getTransformation().isIdentity())
+ {
+ for(drawinglayer::primitive2d::BasePrimitive2D* a : aTargetVector)
+ {
+ rTarget.append(a);
+ }
+ }
+ else
+ {
+ // when a transformation is set, embed to it
+ drawinglayer::primitive2d::Primitive2DContainer xTargets(aTargetVector.size());
+
+ for(size_t 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 which adapted contexts and
+ targets as e.g. needed for the MetaActionType::FLOATTRANSPARENT 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 vcl::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
+ vcl::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 comments, see the single action implementations.
+ */
+ void implInterpretMetafile(
+ const GDIMetaFile& rMetaFile,
+ TargetHolders& rTargetHolders,
+ PropertyHolders& rPropertyHolders,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation)
+ {
+ const size_t nCount(rMetaFile.GetActionSize());
+ std::unique_ptr<emfplushelper::EmfPlusHelper> aEMFPlus;
+
+ for(size_t nAction(0); nAction < nCount; nAction++)
+ {
+ MetaAction* pAction = rMetaFile.GetAction(nAction);
+
+ switch(pAction->GetType())
+ {
+ case MetaActionType::NONE :
+ {
+ /** SIMPLE, DONE */
+ break;
+ }
+ case MetaActionType::PIXEL :
+ {
+ /** CHECKED, WORKS WELL */
+ std::vector< basegfx::B2DPoint > aPositions;
+ Color aLastColor(COL_BLACK);
+
+ while(MetaActionType::PIXEL == pAction->GetType() && nAction < nCount)
+ {
+ const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);
+
+ if(pA->GetColor() != aLastColor)
+ {
+ if(!aPositions.empty())
+ {
+ 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.empty())
+ {
+ createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
+ }
+
+ break;
+ }
+ case MetaActionType::POINT :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ std::vector< basegfx::B2DPoint > aPositions;
+
+ while(MetaActionType::POINT == pAction->GetType() && nAction < nCount)
+ {
+ const MetaPointAction* pA = static_cast<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.empty())
+ {
+ createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor());
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::LINE :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ basegfx::B2DPolygon aLinePolygon;
+ LineInfo aLineInfo;
+
+ while(MetaActionType::LINE == pAction->GetType() && nAction < nCount)
+ {
+ const MetaLineAction* pA = static_cast<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 MetaActionType::RECT :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaRectAction* pA = static_cast<const MetaRectAction*>(pAction);
+ const tools::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 MetaActionType::ROUNDRECT :
+ {
+ /** 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 = static_cast<const MetaRoundRectAction*>(pAction);
+ const tools::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 MetaActionType::ELLIPSE :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
+ const tools::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 MetaActionType::ARC :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
+ const tools::Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Arc);
+ const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
+
+ createHairlinePrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::PIE :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaPieAction* pA = static_cast<const MetaPieAction*>(pAction);
+ const tools::Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Pie);
+ const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::CHORD :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaChordAction* pA = static_cast<const MetaChordAction*>(pAction);
+ const tools::Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Chord);
+ const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
+
+ createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::POLYLINE :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineColorActive())
+ {
+ const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
+ createLinePrimitive(pA->GetPolygon().getB2DPolygon(), pA->GetLineInfo(), rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::POLYGON :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaPolygonAction* pA = static_cast<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 MetaActionType::POLYPOLYGON :
+ {
+ /** CHECKED, WORKS WELL */
+ if(rPropertyHolders.Current().getLineOrFillActive())
+ {
+ const MetaPolyPolygonAction* pA = static_cast<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 MetaActionType::TEXT :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
+ sal_uInt32 nTextLength(pA->GetLen());
+ const sal_uInt32 nTextIndex(pA->GetIndex());
+ const sal_uInt32 nStringLength(pA->GetText().getLength());
+
+ if(nTextLength + nTextIndex > nStringLength)
+ {
+ nTextLength = nStringLength - nTextIndex;
+ }
+
+ if(nTextLength && rPropertyHolders.Current().getTextColorActive())
+ {
+ const std::vector< double > aDXArray{};
+ processMetaTextAction(
+ pA->GetPoint(),
+ pA->GetText(),
+ nTextIndex,
+ nTextLength,
+ aDXArray,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::TEXTARRAY :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
+ sal_uInt32 nTextLength(pA->GetLen());
+ const sal_uInt32 nTextIndex(pA->GetIndex());
+ const sal_uInt32 nStringLength(pA->GetText().getLength());
+
+ if(nTextLength + nTextIndex > nStringLength)
+ {
+ nTextLength = nTextIndex > nStringLength ? 0 : nStringLength - nTextIndex;
+ }
+
+ if(nTextLength && rPropertyHolders.Current().getTextColorActive())
+ {
+ // preapare DXArray (if used)
+ std::vector< double > aDXArray;
+ long* pDXArray = pA->GetDXArray();
+
+ if(pDXArray)
+ {
+ aDXArray.reserve(nTextLength);
+
+ for(sal_uInt32 a(0); a < nTextLength; a++)
+ {
+ aDXArray.push_back((double)(*(pDXArray + a)));
+ }
+ }
+
+ processMetaTextAction(
+ pA->GetPoint(),
+ pA->GetText(),
+ nTextIndex,
+ nTextLength,
+ aDXArray,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::STRETCHTEXT :
+ {
+ // #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 = static_cast<const MetaStretchTextAction*>(pAction);
+ sal_uInt32 nTextLength(pA->GetLen());
+ const sal_uInt32 nTextIndex(pA->GetIndex());
+ const sal_uInt32 nStringLength(pA->GetText().getLength());
+
+ 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(double & a : aTextArray)
+ {
+ a *= fRelative;
+ }
+ }
+ }
+ }
+
+ processMetaTextAction(
+ pA->GetPoint(),
+ pA->GetText(),
+ nTextIndex,
+ nTextLength,
+ aTextArray,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::TEXTRECT :
+ {
+ /** CHECKED, WORKS WELL */
+ // OSL_FAIL("MetaActionType::TEXTRECT requested (!)");
+ const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
+ const tools::Rectangle& rRectangle = pA->GetRect();
+ const sal_uInt32 nStringLength(pA->GetText().getLength());
+
+ 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 isolation.
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
+ GDIMetaFile aGDIMetaFile;
+
+ aTextLayouterDevice.addTextRectActions(
+ rRectangle, pA->GetText(), pA->GetStyle(), aGDIMetaFile);
+
+ if(aGDIMetaFile.GetActionSize())
+ {
+ // create sub-content
+ drawinglayer::primitive2d::Primitive2DContainer xSubContent;
+ {
+ rTargetHolders.Push();
+
+ // for sub-Mteafile contents, do start with new, default render state
+ // #i124686# ...but copy font, this is already set accordingly
+ vcl::Font aTargetFont = rPropertyHolders.Current().getFont();
+ rPropertyHolders.PushDefault();
+ rPropertyHolders.Current().setFont(aTargetFont);
+
+ implInterpretMetafile(aGDIMetaFile, rTargetHolders, rPropertyHolders, rViewInformation);
+ xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
+ rPropertyHolders.Pop();
+ rTargetHolders.Pop();
+ }
+
+ if(!xSubContent.empty())
+ {
+ // add with transformation
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ rPropertyHolders.Current().getTransformation(),
+ xSubContent));
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::BMP :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction);
+ const BitmapEx aBitmapEx(pA->GetBitmap());
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::BMPSCALE :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
+ const Bitmap aBitmapEx(pA->GetBitmap());
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::BMPSCALEPART :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaBmpScalePartAction* pA = static_cast<const MetaBmpScalePartAction*>(pAction);
+ const Bitmap& rBitmap = pA->GetBitmap();
+
+ if(!rBitmap.IsEmpty())
+ {
+ Bitmap aCroppedBitmap(rBitmap);
+ const tools::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 MetaActionType::BMPEX :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */
+ const MetaBmpExAction* pA = static_cast<const MetaBmpExAction*>(pAction);
+ const BitmapEx& rBitmapEx = pA->GetBitmapEx();
+
+ createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::BMPEXSCALE :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */
+ const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
+ const BitmapEx& rBitmapEx = pA->GetBitmapEx();
+
+ createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::BMPEXSCALEPART :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALEPART */
+ const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pAction);
+ const BitmapEx& rBitmapEx = pA->GetBitmapEx();
+
+ if(!rBitmapEx.IsEmpty())
+ {
+ BitmapEx aCroppedBitmapEx(rBitmapEx);
+ const tools::Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
+
+ if(!aCropRectangle.IsEmpty())
+ {
+ aCroppedBitmapEx.Crop(aCropRectangle);
+ }
+
+ createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+ }
+
+ break;
+ }
+ case MetaActionType::MASK :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */
+ /** Huh, no it isn't!? */
+ const MetaMaskAction* pA = static_cast<const MetaMaskAction*>(pAction);
+ const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::MASKSCALE :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */
+ const MetaMaskScaleAction* pA = static_cast<const MetaMaskScaleAction*>(pAction);
+ const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
+
+ createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::MASKSCALEPART :
+ {
+ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALEPART */
+ const MetaMaskScalePartAction* pA = static_cast<const MetaMaskScalePartAction*>(pAction);
+ const Bitmap& rBitmap = pA->GetBitmap();
+
+ if(!rBitmap.IsEmpty())
+ {
+ Bitmap aCroppedBitmap(rBitmap);
+ const tools::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 MetaActionType::GRADIENT :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction);
+ const tools::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::Primitive2DContainer xGradient(1);
+
+ if(rPropertyHolders.Current().isRasterOpInvert())
+ {
+ // use a special version of FillGradientPrimitive2D which creates
+ // non-overlapping geometry on decomposition to make the 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 implicitly 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 MetaActionType::HATCH :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaHatchAction* pA = static_cast<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::Primitive2DContainer { aFillHatch }));
+ }
+
+ break;
+ }
+ case MetaActionType::WALLPAPER :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pAction);
+ tools::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(WallpaperStyle::NONE != 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 MetaActionType::CLIPREGION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pAction);
+
+ if(pA->IsClipping())
+ {
+ // new clipping. Get tools::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 MetaActionType::ISECTRECTCLIPREGION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pAction);
+ const tools::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 MetaActionType::ISECTREGIONCLIPREGION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pAction);
+ const vcl::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 MetaActionType::MOVECLIPREGION :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaMoveClipRegionAction* pA = static_cast<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 MetaActionType::LINECOLOR :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaLineColorAction* pA = static_cast<const MetaLineColorAction*>(pAction);
+ const bool bActive(pA->IsSetting());
+
+ rPropertyHolders.Current().setLineColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setLineColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case MetaActionType::FILLCOLOR :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaFillColorAction* pA = static_cast<const MetaFillColorAction*>(pAction);
+ const bool bActive(pA->IsSetting());
+
+ rPropertyHolders.Current().setFillColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setFillColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case MetaActionType::TEXTCOLOR :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextColorAction* pA = static_cast<const MetaTextColorAction*>(pAction);
+ const bool bActivate(COL_TRANSPARENT != pA->GetColor().GetColor());
+
+ rPropertyHolders.Current().setTextColorActive(bActivate);
+ rPropertyHolders.Current().setTextColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case MetaActionType::TEXTFILLCOLOR :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextFillColorAction* pA = static_cast<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 MetaActionType::TEXTALIGN :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextAlignAction* pA = static_cast<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().GetAlignment() != aNewTextAlign)
+ {
+ vcl::Font aNewFont(rPropertyHolders.Current().getFont());
+ aNewFont.SetAlignment(aNewTextAlign);
+ rPropertyHolders.Current().setFont(aNewFont);
+ }
+
+ break;
+ }
+ case MetaActionType::MAPMODE :
+ {
+ /** CHECKED, WORKS WELL */
+ // the most necessary MapMode to be interpreted is MapUnit::MapRelative,
+ // but also the others may occur. Even not yet supported ones
+ // may need to be added here later
+ const MetaMapModeAction* pA = static_cast<const MetaMapModeAction*>(pAction);
+ const MapMode& rMapMode = pA->GetMapMode();
+ basegfx::B2DHomMatrix aMapping;
+
+ if(MapUnit::MapRelative == rMapMode.GetMapUnit())
+ {
+ aMapping = getTransformFromMapMode(rMapMode);
+ }
+ else
+ {
+ switch(rMapMode.GetMapUnit())
+ {
+ case MapUnit::Map100thMM :
+ {
+ if(MapUnit::MapTwip == rPropertyHolders.Current().getMapUnit())
+ {
+ // MapUnit::MapTwip -> MapUnit::Map100thMM
+ const double fTwipTo100thMm(127.0 / 72.0);
+ aMapping.scale(fTwipTo100thMm, fTwipTo100thMm);
+ }
+ break;
+ }
+ case MapUnit::MapTwip :
+ {
+ if(MapUnit::Map100thMM == rPropertyHolders.Current().getMapUnit())
+ {
+ // MapUnit::Map100thMM -> MapUnit::MapTwip
+ const double f100thMmToTwip(72.0 / 127.0);
+ aMapping.scale(f100thMmToTwip, f100thMmToTwip);
+ }
+ break;
+ }
+ default :
+ {
+ OSL_FAIL("implInterpretMetafile: MetaActionType::MAPMODE 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 MetaActionType::FONT :
+ {
+ /** SIMPLE, DONE */
+ const MetaFontAction* pA = static_cast<const MetaFontAction*>(pAction);
+ rPropertyHolders.Current().setFont(pA->GetFont());
+ Size aFontSize(pA->GetFont().GetFontSize());
+
+ 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())
+ vcl::Font aCorrectedFont(pA->GetFont());
+
+ // guess 16 pixel (as in VCL)
+ aFontSize = Size(0, 16);
+
+ // convert to target MapUnit if not pixels
+ aFontSize = OutputDevice::LogicToLogic(
+ aFontSize, MapUnit::MapPixel, rPropertyHolders.Current().getMapUnit());
+
+ aCorrectedFont.SetFontSize(aFontSize);
+ rPropertyHolders.Current().setFont(aCorrectedFont);
+ }
+
+ // older Metafiles have no MetaActionType::TEXTCOLOR 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 deactivate here on transparent, 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 MetaActionType::PUSH :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction);
+ rPropertyHolders.Push(pA->GetFlags());
+
+ break;
+ }
+ case MetaActionType::POP :
+ {
+ /** CHECKED, WORKS WELL */
+ const bool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & PushFlags::CLIPREGION);
+ const bool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & PushFlags::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(RasterOp::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 MetaActionType::RASTEROP :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaRasterOpAction* pA = static_cast<const MetaRasterOpAction*>(pAction);
+ const RasterOp aRasterOp = pA->GetRasterOp();
+
+ HandleNewRasterOp(aRasterOp, rTargetHolders, rPropertyHolders);
+
+ break;
+ }
+ case MetaActionType::Transparent :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaTransparentAction* pA = static_cast<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::Primitive2DContainer aSubContent(
+ rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()));
+
+ // back to old target
+ rTargetHolders.Pop();
+
+ if(!aSubContent.empty())
+ {
+ rTargetHolders.Current().append(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ aSubContent,
+ nTransparence * 0.01));
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::EPS :
+ {
+ /** 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 = static_cast<const MetaEPSAction*>(pAction);
+ const tools::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 MetaActionType::REFPOINT :
+ {
+ /** SIMPLE, DONE */
+ // only used for hatch and line pattern offsets, pretty much no longer
+ // supported today
+ // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction;
+ break;
+ }
+ case MetaActionType::TEXTLINECOLOR :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextLineColorAction* pA = static_cast<const MetaTextLineColorAction*>(pAction);
+ const bool bActive(pA->IsSetting());
+
+ rPropertyHolders.Current().setTextLineColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setTextLineColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case MetaActionType::TEXTLINE :
+ {
+ /** 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 = static_cast<const MetaTextLineAction*>(pAction);
+
+ proccessMetaTextLineAction(
+ *pA,
+ rTargetHolders.Current(),
+ rPropertyHolders.Current());
+
+ break;
+ }
+ case MetaActionType::FLOATTRANSPARENT :
+ {
+ /** CHECKED, WORKS WELL */
+ const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pAction);
+ const basegfx::B2DRange aTargetRange(
+ pA->GetPoint().X(),
+ pA->GetPoint().Y(),
+ pA->GetPoint().X() + pA->GetSize().Width(),
+ pA->GetPoint().Y() + pA->GetSize().Height());
+
+ if(!aTargetRange.isEmpty())
+ {
+ const GDIMetaFile& rContent = pA->GetGDIMetaFile();
+
+ if(rContent.GetActionSize())
+ {
+ // create the sub-content with no embedding specific to the
+ // sub-metafile, this seems not to be used.
+ drawinglayer::primitive2d::Primitive2DContainer xSubContent;
+ {
+ rTargetHolders.Push();
+ // #i# for sub-Mteafile contents, do start with new, default render state
+ rPropertyHolders.PushDefault();
+ implInterpretMetafile(rContent, rTargetHolders, rPropertyHolders, rViewInformation);
+ xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
+ rPropertyHolders.Pop();
+ rTargetHolders.Pop();
+ }
+
+ if(!xSubContent.empty())
+ {
+ // prepare sub-content transform
+ basegfx::B2DHomMatrix aSubTransform;
+
+ // create SourceRange
+ const basegfx::B2DRange aSourceRange(
+ rContent.GetPrefMapMode().GetOrigin().X(),
+ rContent.GetPrefMapMode().GetOrigin().Y(),
+ rContent.GetPrefMapMode().GetOrigin().X() + rContent.GetPrefSize().Width(),
+ rContent.GetPrefMapMode().GetOrigin().Y() + rContent.GetPrefSize().Height());
+
+ // apply mapping if aTargetRange and aSourceRange are not equal
+ if(!aSourceRange.equal(aTargetRange))
+ {
+ aSubTransform.translate(-aSourceRange.getMinX(), -aSourceRange.getMinY());
+ aSubTransform.scale(
+ aTargetRange.getWidth() / (basegfx::fTools::equalZero(aSourceRange.getWidth()) ? 1.0 : aSourceRange.getWidth()),
+ aTargetRange.getHeight() / (basegfx::fTools::equalZero(aSourceRange.getHeight()) ? 1.0 : aSourceRange.getHeight()));
+ aSubTransform.translate(aTargetRange.getMinX(), aTargetRange.getMinY());
+ }
+
+ // apply general current transformation
+ aSubTransform = rPropertyHolders.Current().getTransformation() * aSubTransform;
+
+ // evtl. embed sub-content to its transformation
+ if(!aSubTransform.isIdentity())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference aEmbeddedTransform(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aSubTransform,
+ xSubContent));
+
+ xSubContent = drawinglayer::primitive2d::Primitive2DContainer { aEmbeddedTransform };
+ }
+
+ // 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(aTargetRange);
+ 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::Primitive2DContainer { xTransparence }));
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ case MetaActionType::GRADIENTEX :
+ {
+ /** SIMPLE, DONE */
+ // This is only a data holder which is interpreted inside comment actions,
+ // see MetaActionType::COMMENT for more info
+ // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction;
+ break;
+ }
+ case MetaActionType::LAYOUTMODE :
+ {
+ /** SIMPLE, DONE */
+ const MetaLayoutModeAction* pA = static_cast<const MetaLayoutModeAction*>(pAction);
+ rPropertyHolders.Current().setLayoutMode(pA->GetLayoutMode());
+ break;
+ }
+ case MetaActionType::TEXTLANGUAGE :
+ {
+ /** SIMPLE, DONE */
+ const MetaTextLanguageAction* pA = static_cast<const MetaTextLanguageAction*>(pAction);
+ rPropertyHolders.Current().setLanguageType(pA->GetTextLanguage());
+ break;
+ }
+ case MetaActionType::OVERLINECOLOR :
+ {
+ /** SIMPLE, DONE */
+ const MetaOverlineColorAction* pA = static_cast<const MetaOverlineColorAction*>(pAction);
+ const bool bActive(pA->IsSetting());
+
+ rPropertyHolders.Current().setOverlineColorActive(bActive);
+ if(bActive)
+ rPropertyHolders.Current().setOverlineColor(pA->GetColor().getBColor());
+
+ break;
+ }
+ case MetaActionType::COMMENT :
+ {
+ /** 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 = static_cast<const MetaCommentAction*>(pAction);
+
+ if (pA->GetComment().equalsIgnoreAsciiCase("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) problematic with AntiAliasing, so it's
+ // better to use this info
+ const MetaGradientExAction* pMetaGradientExAction = nullptr;
+ bool bDone(false);
+ size_t b(nAction + 1);
+
+ for(; !bDone && b < nCount; b++)
+ {
+ pAction = rMetaFile.GetAction(b);
+
+ if(MetaActionType::GRADIENTEX == pAction->GetType())
+ {
+ pMetaGradientExAction = static_cast<const MetaGradientExAction*>(pAction);
+ }
+ else if(MetaActionType::COMMENT == pAction->GetType())
+ {
+ if (static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("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));
+ }
+ }
+ }
+ }
+ else if (pA->GetComment().equalsIgnoreAsciiCase("EMF_PLUS_HEADER_INFO"))
+ {
+ if (aEMFPlus.get())
+ {
+ // error: should not yet exist
+ SAL_INFO("cppcanvas.emf", "Error: multiple EMF_PLUS_HEADER_INFO");
+ }
+ else
+ {
+ SAL_INFO("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pA->GetDataSize());
+ SvMemoryStream aMemoryStream(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(), StreamMode::READ);
+
+ aEMFPlus.reset(new emfplushelper::EmfPlusHelper(aMemoryStream));
+ }
+ }
+ else if (pA->GetComment().equalsIgnoreAsciiCase("EMF_PLUS"))
+ {
+ if (!aEMFPlus.get())
+ {
+ // error: should exist
+ SAL_INFO("cppcanvas.emf", "Error: EMF_PLUS before EMF_PLUS_HEADER_INFO");
+ }
+ else
+ {
+ static int count = -1, limit = 0x7fffffff;
+
+ if (count == -1)
+ {
+ count = 0;
+
+ if (char *env = getenv("EMF_PLUS_LIMIT"))
+ {
+ limit = atoi(env);
+ SAL_INFO("cppcanvas.emf", "EMF+ records limit: " << limit);
+ }
+ }
+
+ SAL_INFO("cppcanvas.emf", "EMF+ passed to canvas mtf renderer, size: " << pA->GetDataSize());
+
+ if (count < limit)
+ {
+ SvMemoryStream aMemoryStream(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(), StreamMode::READ);
+
+ aEMFPlus->processEmfPlusData(
+ aMemoryStream,
+ rTargetHolders,
+ rPropertyHolders,
+ rViewInformation);
+ }
+
+ count++;
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ OSL_FAIL("Unknown MetaFile Action (!)");
+ break;
+ }
+ }
+ }
+ }
+}
+
+namespace wmfemfhelper
+{
+ drawinglayer::primitive2d::Primitive2DContainer interpretMetafile(
+ const GDIMetaFile& rMetaFile,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation)
+ {
+ // prepare target and properties; each will have one default entry
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ TargetHolders aTargetHolders;
+ PropertyHolders aPropertyHolders;
+
+ // set target MapUnit at Properties
+ aPropertyHolders.Current().setMapUnit(rMetaFile.GetPrefMapMode().GetMapUnit());
+
+ // interpret the Metafile
+ implInterpretMetafile(rMetaFile, aTargetHolders, aPropertyHolders, rViewInformation);
+
+ // get the content. There should be only one target, as in the start condition,
+ // but iterating will be the right thing to do when some push/pop is not closed
+ while (aTargetHolders.size() > 1)
+ {
+ xRetval.append(
+ aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
+ aTargetHolders.Pop();
+ }
+
+ xRetval.append(
+ aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
+
+ return xRetval;
+ }
+
+} // end of namespace wmfemfhelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */