summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2017-06-21 12:19:52 +0200
committerThorsten Behrens <Thorsten.Behrens@CIB.de>2017-07-15 11:01:30 +0200
commit044dc9fcca4df6ca21df790bdb14660f951e4598 (patch)
treedb0be62abb97a6225b90880bfc543bec0b2377f1
parentemfplus: completed isolation/migration of Emf/Wmf (diff)
downloadcore-044dc9fcca4df6ca21df790bdb14660f951e4598.tar.gz
core-044dc9fcca4df6ca21df790bdb14660f951e4598.zip
emfplus: migrate gdiplus reader to drawinglayer
In the current state interpretation of the GDI+ data is needed in the MetafilePrimitive in the drawinglayer project. Migrate helper stuff and reader from cppcanvas to drawinglayer as tooling, prepare tooling, prepare changing from direct canvas rendering, isolate and migrate existing tooling from MetafilePrimitive from reading emf/wmf to places where it can be commonly used by both, prepare cange of different graphic statue usages, start changing XForm to B2DHomMatrix conversions, ... Change-Id: I2d6954f69464653d111abb316fd9654ad3499c3f
-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: */