diff options
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: */ |