tdf#111486 Removed not used EMF+ code

Change-Id: I708876fecab1bd6b0c37892efa9c4ab180457d6b
Reviewed-on: https://gerrit.libreoffice.org/64366
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
diff --git a/cppcanvas/Library_cppcanvas.mk b/cppcanvas/Library_cppcanvas.mk
index 8a374b5..6aa698d 100644
--- a/cppcanvas/Library_cppcanvas.mk
+++ b/cppcanvas/Library_cppcanvas.mk
@@ -50,20 +50,11 @@
$(eval $(call gb_Library_add_exception_objects,cppcanvas,\
	cppcanvas/source/mtfrenderer/bitmapaction \
	cppcanvas/source/mtfrenderer/cachedprimitivebase \
	cppcanvas/source/mtfrenderer/emfpbrush \
	cppcanvas/source/mtfrenderer/emfpcustomlinecap \
	cppcanvas/source/mtfrenderer/emfpfont \
	cppcanvas/source/mtfrenderer/emfpimage \
	cppcanvas/source/mtfrenderer/emfplus \
	cppcanvas/source/mtfrenderer/emfppen \
	cppcanvas/source/mtfrenderer/emfppath \
	cppcanvas/source/mtfrenderer/emfpregion \
	cppcanvas/source/mtfrenderer/implrenderer \
	cppcanvas/source/mtfrenderer/lineaction \
	cppcanvas/source/mtfrenderer/mtftools \
	cppcanvas/source/mtfrenderer/pointaction \
	cppcanvas/source/mtfrenderer/polypolyaction \
	cppcanvas/source/mtfrenderer/emfpstringformat \
	cppcanvas/source/mtfrenderer/textaction \
	cppcanvas/source/mtfrenderer/textlineshelper \
	cppcanvas/source/mtfrenderer/transparencygroupaction \
diff --git a/cppcanvas/source/inc/implrenderer.hxx b/cppcanvas/source/inc/implrenderer.hxx
index 6fec26e..26fc504 100644
--- a/cppcanvas/source/inc/implrenderer.hxx
+++ b/cppcanvas/source/inc/implrenderer.hxx
@@ -196,15 +196,6 @@
            // (externally not visible)
            typedef std::vector< MtfAction >      ActionVector;

            /* EMF+ */
            static void ReadRectangle (SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed = false);
            static void ReadPoint (SvStream& s, float& x, float& y, sal_uInt32 flags);
            void MapToDevice (double &x, double &y) const;
            ::basegfx::B2DPoint Map (double ix, double iy) const;
            ::basegfx::B2DSize MapSize (double iwidth, double iheight) const;
            void GraphicStatePush (GraphicStateMap& map, sal_Int32 index, OutDevState const & rState);
            void GraphicStatePop (GraphicStateMap& map, sal_Int32 index, OutDevState& rState);

        private:
            ImplRenderer(const ImplRenderer&) = delete;
            ImplRenderer& operator=( const ImplRenderer& ) = delete;
@@ -257,20 +248,6 @@

            void processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream = false);

            /* EMF+ */
            void processEMFPlus( MetaCommentAction const * pAct, const ActionFactoryParameters& rFactoryParms, OutDevState& rState, const CanvasSharedPtr& rCanvas );
            double setFont(css::rendering::FontRequest& aFontRequest, sal_uInt8 fontObjectId, const ActionFactoryParameters& rParms, OutDevState& rState );

            /// Render LineCap, like the start or end arrow of a polygon.
            /// @return how much we should shorten the original polygon.
            double EMFPPlusDrawLineCap(const ::basegfx::B2DPolygon& rPolygon, double fPolyLength,
                    const ::basegfx::B2DPolyPolygon& rLineCap, bool isFilled, bool bStart,
                    const css::rendering::StrokeAttributes& rAttributes,
                    const ActionFactoryParameters& rParms, OutDevState& rState);

            void EMFPPlusDrawPolygon (const ::basegfx::B2DPolyPolygon & polygon, const ActionFactoryParameters& rParms, OutDevState& rState, const CanvasSharedPtr& rCanvas, sal_uInt32 penIndex);
            void EMFPPlusFillPolygon (::basegfx::B2DPolyPolygon const & polygon, const ActionFactoryParameters& rParms, OutDevState& rState, const CanvasSharedPtr& rCanvas, bool isColor, sal_uInt32 brushIndexOrColor);

            ActionVector maActions;

            /* EMF+ */
diff --git a/cppcanvas/source/mtfrenderer/emfpbrush.cxx b/cppcanvas/source/mtfrenderer/emfpbrush.cxx
deleted file mode 100644
index dc676c3..0000000
--- a/cppcanvas/source/mtfrenderer/emfpbrush.cxx
+++ /dev/null
@@ -1,309 +0,0 @@
/* -*- 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/utils/canvastools.hxx>
#include <basegfx/utils/gradienttools.hxx>
#include <basegfx/utils/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 <implrenderer.hxx>
#include "emfpbrush.hxx"
#include "emfppath.hxx"
#include <sal/log.hxx>

namespace cppcanvas
{
    namespace internal
    {
        EMFPBrush::EMFPBrush()
            : type(0)
            , additionalFlags(0)
            , wrapMode(0)
            , areaX(0.0)
            , areaY(0.0)
            , areaWidth(0.0)
            , areaHeight(0.0)
            , hasTransformation(false)
            , blendPoints(0)
            , blendFactors(nullptr)
            , colorblendPoints(0)
            , surroundColorsNumber(0)
            , hatchStyle(HatchStyleHorizontal)
        {
        }

        EMFPBrush::~EMFPBrush()
        {
            path.reset();
        }

        void EMFPBrush::Read(SvStream& s, ImplRenderer const & 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.reset( 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.reset( new EMFPPath(pathPoints) );
                    path->Read(s, pathFlags, rR);

                    s.Seek(pos + pathLength);

                    const ::basegfx::B2DRectangle aBounds(::basegfx::utils::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.reset( new EMFPPath(boundaryPointCount) );
                    path->Read(s, 0x0, rR);

                    s.Seek(pos + 8 * boundaryPointCount);

                    const ::basegfx::B2DRectangle aBounds(::basegfx::utils::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.eM11 << " m12: " << brush_transformation.eM12 <<
                             "\nEMF+\tm21: " << brush_transformation.eM21 << " m22: " << brush_transformation.eM22 <<
                             "\nEMF+\tdx: " << brush_transformation.eDx << " dy: " << brush_transformation.eDy);

                }
                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.reset( new float[2 * blendPoints] );
                    blendFactors = blendPositions.get() + 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.reset( new float[colorblendPoints] );
                    colorblendColors.reset( 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.eM11 << " m12: " << brush_transformation.eM12 <<
                        "\nEMF+\tm21: " << brush_transformation.eM21 << " m22: " << brush_transformation.eM22 <<
                        "\nEMF+\tdx: " << brush_transformation.eDx << " dy: " << brush_transformation.eDy);
                }
                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.reset( new float[2 * blendPoints] );
                    blendFactors = blendPositions.get() + 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.reset(new float[colorblendPoints] );
                    colorblendColors.reset( 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/cppcanvas/source/mtfrenderer/emfpbrush.hxx b/cppcanvas/source/mtfrenderer/emfpbrush.hxx
deleted file mode 100644
index f651f83..0000000
--- a/cppcanvas/source/mtfrenderer/emfpbrush.hxx
+++ /dev/null
@@ -1,131 +0,0 @@
/* -*- 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_CPPCANVAS_SOURCE_MTFRENDERER_EMFPBRUSH_HXX
#define INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPBRUSH_HXX

namespace cppcanvas
{
    namespace internal
    {
        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;
            XForm brush_transformation;
            bool hasTransformation;
            sal_Int32 blendPoints;
            std::unique_ptr<float[]> blendPositions;
            float* blendFactors;
            sal_Int32 colorblendPoints;
            std::unique_ptr<float[]> colorblendPositions;
            std::unique_ptr<::Color[]> colorblendColors;
            sal_Int32 surroundColorsNumber;
            std::unique_ptr<::Color[]> surroundColors;
            std::unique_ptr<EMFPPath> path;
            EmfPlusHatchStyle hatchStyle;

            EMFPBrush();
            virtual ~EMFPBrush() override;

            sal_uInt32 GetType() const { return type; }
            const ::Color& GetColor() const { return solidColor; }

            void Read(SvStream& s, ImplRenderer const & rR);
        };
    }
}

#endif // INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPBRUSH_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cppcanvas/source/mtfrenderer/emfpcustomlinecap.cxx b/cppcanvas/source/mtfrenderer/emfpcustomlinecap.cxx
deleted file mode 100644
index 8cb3c56..0000000
--- a/cppcanvas/source/mtfrenderer/emfpcustomlinecap.cxx
+++ /dev/null
@@ -1,164 +0,0 @@
/* -*- 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/utils/canvastools.hxx>
#include <basegfx/utils/gradienttools.hxx>
#include <basegfx/utils/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 <sal/log.hxx>
#include <vcl/canvastools.hxx>
#include <implrenderer.hxx>

#include "emfpcustomlinecap.hxx"
#include "emfppath.hxx"
#include "emfppen.hxx"

using namespace ::com::sun::star;
using namespace ::basegfx;

namespace cppcanvas
{
    namespace internal
    {
        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, ImplRenderer const & 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, ImplRenderer const & 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/cppcanvas/source/mtfrenderer/emfpcustomlinecap.hxx b/cppcanvas/source/mtfrenderer/emfpcustomlinecap.hxx
deleted file mode 100644
index cd5a45e..0000000
--- a/cppcanvas/source/mtfrenderer/emfpcustomlinecap.hxx
+++ /dev/null
@@ -1,47 +0,0 @@
/* -*- 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_CPPCANVAS_SOURCE_MTFRENDERER_EMFPCUSTOMLINECAP_HXX
#define INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPCUSTOMLINECAP_HXX

namespace cppcanvas
{
    namespace internal
    {
        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, ImplRenderer const & rR, bool bFill);
            void Read(SvStream& s, ImplRenderer const & rR);
        };
    }
}


#endif // INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPCUSTOMLINECAP_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cppcanvas/source/mtfrenderer/emfpfont.cxx b/cppcanvas/source/mtfrenderer/emfpfont.cxx
deleted file mode 100644
index 67c06a1..0000000
--- a/cppcanvas/source/mtfrenderer/emfpfont.cxx
+++ /dev/null
@@ -1,75 +0,0 @@
/* -*- 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/utils/canvastools.hxx>
#include <basegfx/utils/gradienttools.hxx>
#include <basegfx/utils/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 <sal/log.hxx>
#include <vcl/canvastools.hxx>
#include <implrenderer.hxx>
#include "emfpfont.hxx"


namespace cppcanvas
{
    namespace internal
    {
        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/cppcanvas/source/mtfrenderer/emfpfont.hxx b/cppcanvas/source/mtfrenderer/emfpfont.hxx
deleted file mode 100644
index aa9d0c4..0000000
--- a/cppcanvas/source/mtfrenderer/emfpfont.hxx
+++ /dev/null
@@ -1,42 +0,0 @@
/* -*- 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_CPPCANVAS_SOURCE_MTFRENDERER_EMFPFONT_HXX
#define INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPFONT_HXX

namespace cppcanvas
{
    namespace internal
    {
        struct EMFPFont : public EMFPObject
        {
            float emSize;
            sal_uInt32 sizeUnit;
            sal_Int32 fontFlags;
            OUString family;

            void Read(SvMemoryStream &s);
        };
    }
}


#endif // INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPFONT_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cppcanvas/source/mtfrenderer/emfpimage.cxx b/cppcanvas/source/mtfrenderer/emfpimage.cxx
deleted file mode 100644
index 0e1f3538..0000000
--- a/cppcanvas/source/mtfrenderer/emfpimage.cxx
+++ /dev/null
@@ -1,101 +0,0 @@
/* -*- 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/utils/canvastools.hxx>
#include <basegfx/utils/gradienttools.hxx>
#include <basegfx/utils/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 <implrenderer.hxx>
#include <vcl/graphicfilter.hxx>
#include "emfpimage.hxx"
#include <sal/log.hxx>


namespace cppcanvas
{
    namespace internal
    {
        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.GetSizePixel().Width() << " height: " << graphic.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/cppcanvas/source/mtfrenderer/emfpimage.hxx b/cppcanvas/source/mtfrenderer/emfpimage.hxx
deleted file mode 100644
index a30c73e..0000000
--- a/cppcanvas/source/mtfrenderer/emfpimage.hxx
+++ /dev/null
@@ -1,44 +0,0 @@
/* -*- 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_CPPCANVAS_SOURCE_MTFRENDERER_EMFPIMAGE_HXX
#define INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPIMAGE_HXX

namespace cppcanvas
{
    namespace internal
    {
        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 // INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPIMAGE_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cppcanvas/source/mtfrenderer/emfplus.cxx b/cppcanvas/source/mtfrenderer/emfplus.cxx
deleted file mode 100644
index a36d37b..0000000
--- a/cppcanvas/source/mtfrenderer/emfplus.cxx
+++ /dev/null
@@ -1,1666 +0,0 @@
/* -*- 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 <memory>
#include <tools/stream.hxx>
#include <vcl/metaact.hxx>
#include <vcl/graphicfilter.hxx>
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <basegfx/utils/canvastools.hxx>
#include <basegfx/utils/gradienttools.hxx>
#include <basegfx/utils/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 <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <i18nlangtag/languagetag.hxx>

#include <com/sun/star/rendering/PanoseWeight.hpp>
#include <com/sun/star/rendering/TexturingMode.hpp>
#include <com/sun/star/rendering/XCanvas.hpp>
#include <comphelper/propertysequence.hxx>

#include "bitmapaction.hxx"
#include <implrenderer.hxx>
#include <outdevstate.hxx>
#include "polypolyaction.hxx"
#include "textaction.hxx"

#include "emfpbrush.hxx"
#include "emfpcustomlinecap.hxx"
#include "emfpfont.hxx"
#include "emfpimage.hxx"
#include "emfppath.hxx"
#include "emfppen.hxx"
#include "emfpregion.hxx"
#include "emfpstringformat.hxx"

namespace
{

#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

#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 EmfPlusCombineMode
{
    EmfPlusCombineModeReplace = 0x00000000,
    EmfPlusCombineModeIntersect = 0x00000001,
    EmfPlusCombineModeUnion = 0x00000002,
    EmfPlusCombineModeXOR = 0x00000003,
    EmfPlusCombineModeExclude = 0x00000004,
    EmfPlusCombineModeComplement = 0x00000005
};

enum EmfPlusFontStyle
{
    FontStyleBold = 0x00000001,
    FontStyleItalic = 0x00000002,
    FontStyleUnderline = 0x00000004,
    FontStyleStrikeout = 0x00000008
};

enum UnitType
{
    UnitTypeWorld = 0x00,
    UnitTypeDisplay = 0x01,
    UnitTypePixel = 0x02,
    UnitTypePoint = 0x03,
    UnitTypeInch = 0x04,
    UnitTypeDocument = 0x05,
    UnitTypeMillimeter = 0x06
};

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 "";
}

} // anonymous namespace

using namespace ::com::sun::star;
using namespace ::basegfx;

namespace cppcanvas
{
    namespace internal
    {
        void ImplRenderer::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 ImplRenderer::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 ImplRenderer::MapToDevice (double& x, double& y) const
        {
            // TODO: other units
            x = 100*nMmX*x/nPixX;
            y = 100*nMmY*y/nPixY;
        }

        ::basegfx::B2DPoint ImplRenderer::Map (double ix, double iy) const
        {
            double x, y;

            x = ix*aWorldTransform.eM11 + iy*aWorldTransform.eM21 + aWorldTransform.eDx;
            y = ix*aWorldTransform.eM12 + iy*aWorldTransform.eM22 + aWorldTransform.eDy;

            MapToDevice (x, y);

            x -= nFrameLeft;
            y -= nFrameTop;

            x *= aBaseTransform.eM11;
            y *= aBaseTransform.eM22;

            return ::basegfx::B2DPoint (x, y);
        }

        ::basegfx::B2DSize ImplRenderer::MapSize (double iwidth, double iheight) const
        {
            double w, h;

            w = iwidth*aWorldTransform.eM11 + iheight*aWorldTransform.eM21;
            h = iwidth*aWorldTransform.eM12 + iheight*aWorldTransform.eM22;

            MapToDevice (w, h);

            w *= aBaseTransform.eM11;
            h *= aBaseTransform.eM22;

            return ::basegfx::B2DSize (w, h);
        }

#define COLOR(x) \
    vcl::unotools::colorToDoubleSequence( ::Color (0xff - (x >> 24), \
                             (x >> 16) & 0xff, \
                             (x >> 8) & 0xff, \
                             x & 0xff), \
                        rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());

        void ImplRenderer::EMFPPlusFillPolygon (::basegfx::B2DPolyPolygon const & polygon, const ActionFactoryParameters& rParms,
                                                OutDevState& rState, const CanvasSharedPtr& rCanvas, bool isColor, sal_uInt32 brushIndexOrColor)
        {
            ::basegfx::B2DPolyPolygon localPolygon (polygon);

            SAL_INFO("cppcanvas.emf", "EMF+\tfill polygon");

            localPolygon.transform( rState.mapModeTransform );

            std::shared_ptr<Action> pPolyAction;

            if (isColor) {
                SAL_INFO("cppcanvas.emf", "EMF+\t\tcolor fill:0x" << std::hex << brushIndexOrColor << std::dec);
                rState.isFillColorSet = true;
                rState.isLineColorSet = false;

                rState.fillColor = COLOR(brushIndexOrColor);

                pPolyAction = internal::PolyPolyActionFactory::createPolyPolyAction( localPolygon, rParms.mrCanvas, rState );

            } else {
                rState.isFillColorSet = true;
                // extract UseBrush
                EMFPBrush* brush = static_cast<EMFPBrush*>( aObjects [brushIndexOrColor & 0xff].get() );
                SAL_INFO("cppcanvas.emf", "EMF+\tbrush fill slot: " << brushIndexOrColor << " (type: " << (brush ? brush->GetType() : -1) << ")");

                // give up in case something wrong happened
                if( !brush )
                    return;

                rState.isFillColorSet = false;
                rState.isLineColorSet = false;

                if (brush->type == BrushTypeHatchFill)
                {
                    // EMF+ like hatching is currently not supported. These are just color blends which serve as an approximation for some of them
                    // for the others the hatch "background" color (secondColor in brush) is used.

                    bool isHatchBlend = true;
                    double blendFactor = 0.0;

                    switch (brush->hatchStyle)
                    {
                        case HatchStyle05Percent: blendFactor = 0.05; break;
                        case HatchStyle10Percent: blendFactor = 0.10; break;
                        case HatchStyle20Percent: blendFactor = 0.20; break;
                        case HatchStyle25Percent: blendFactor = 0.25; break;
                        case HatchStyle30Percent: blendFactor = 0.30; break;
                        case HatchStyle40Percent: blendFactor = 0.40; break;
                        case HatchStyle50Percent: blendFactor = 0.50; break;
                        case HatchStyle60Percent: blendFactor = 0.60; break;
                        case HatchStyle70Percent: blendFactor = 0.70; break;
                        case HatchStyle75Percent: blendFactor = 0.75; break;
                        case HatchStyle80Percent: blendFactor = 0.80; break;
                        case HatchStyle90Percent: blendFactor = 0.90; break;
                        default:
                            isHatchBlend = false;
                            break;
                    }
                    rState.isFillColorSet = true;
                    rState.isLineColorSet = false;
                    ::Color fillColor;
                    if (isHatchBlend)
                    {
                        fillColor = brush->solidColor;
                        fillColor.Merge(brush->secondColor, static_cast<sal_uInt8>(255 * blendFactor));
                    }
                    else
                    {
                        fillColor = brush->secondColor;
                    }
                    rState.fillColor = vcl::unotools::colorToDoubleSequence(fillColor, rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
                    pPolyAction = internal::PolyPolyActionFactory::createPolyPolyAction( localPolygon, rParms.mrCanvas, rState );
                }
                else if (brush->type == BrushTypeTextureFill)
                {
                    SAL_WARN("cppcanvas.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush");
                }
                else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient)
                {
                    if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1))
                    {
                        SAL_WARN("cppcanvas.emf", "EMF+\t TODO Verify proper displaying of BrushTypePathGradient with flags: " <<  std::hex << brush->additionalFlags << std::dec);
                    }
                    ::basegfx::B2DHomMatrix aTextureTransformation;
                    ::basegfx::B2DHomMatrix aWorldTransformation;
                    ::basegfx::B2DHomMatrix aBaseTransformation;
                    rendering::Texture aTexture;

                    aWorldTransformation.set (0, 0, aWorldTransform.eM11);
                    aWorldTransformation.set (0, 1, aWorldTransform.eM21);
                    aWorldTransformation.set (0, 2, aWorldTransform.eDx);
                    aWorldTransformation.set (1, 0, aWorldTransform.eM12);
                    aWorldTransformation.set (1, 1, aWorldTransform.eM22);
                    aWorldTransformation.set (1, 2, aWorldTransform.eDy);

                    aBaseTransformation.set (0, 0, aBaseTransform.eM11);
                    aBaseTransformation.set (0, 1, aBaseTransform.eM21);
                    aBaseTransformation.set (0, 2, aBaseTransform.eDx);
                    aBaseTransformation.set (1, 0, aBaseTransform.eM12);
                    aBaseTransformation.set (1, 1, aBaseTransform.eM22);
                    aBaseTransformation.set (1, 2, aBaseTransform.eDy);

                    // TODO Verify on example image, why there is shift (-0.5, -0.5)
                    if (brush->type == BrushTypePathGradient && (brush->additionalFlags & 0x1))
                    {
                        aTextureTransformation.translate (-0.5, -0.5);
                    }
                    aTextureTransformation.scale (brush->areaWidth, brush->areaHeight);
                    aTextureTransformation.translate (brush->areaX, brush->areaY);

                    if (brush->hasTransformation) {
                        ::basegfx::B2DHomMatrix aTransformation;

                        aTransformation.set (0, 0, brush->brush_transformation.eM11);
                        aTransformation.set (0, 1, brush->brush_transformation.eM21);
                        aTransformation.set (0, 2, brush->brush_transformation.eDx);
                        aTransformation.set (1, 0, brush->brush_transformation.eM12);
                        aTransformation.set (1, 1, brush->brush_transformation.eM22);
                        aTransformation.set (1, 2, brush->brush_transformation.eDy);

                        aTextureTransformation *= aTransformation;
                    }

                    aTextureTransformation *= aWorldTransformation;
                    aTextureTransformation.scale (100.0*nMmX/nPixX, 100.0*nMmY/nPixY);
                    aTextureTransformation.translate (-nFrameLeft, -nFrameTop);
                    aTextureTransformation *= rState.mapModeTransform;
                    aTextureTransformation *= aBaseTransformation;

                    aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
                    aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
                    aTexture.Alpha = 1.0;

                    basegfx::ODFGradientInfo aGradInfo;
                    OUString aGradientService;

                    const uno::Sequence< double > aStartColor(
                            vcl::unotools::colorToDoubleSequence( brush->solidColor,
                                rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ) );
                    const uno::Sequence< double > aEndColor(
                            vcl::unotools::colorToDoubleSequence( brush->secondColor,
                                rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ) );
                    uno::Sequence< uno::Sequence < double > > aColors (2);
                    uno::Sequence< double > aStops (2);

                    if (brush->blendPositions) {
                        SAL_INFO("cppcanvas.emf", "EMF+\t\tuse blend");
                        aColors.realloc (brush->blendPoints);
                        aStops.realloc (brush->blendPoints);
                        int length = aStartColor.getLength ();
                        uno::Sequence< double > aColor (length);

                        assert(length == aEndColor.getLength());
                        for (int i = 0; i < brush->blendPoints; i++) {
                            aStops[i] = brush->blendPositions [i];

                            for (int j = 0; j < length; j++) {
                                if (brush->type == BrushTypeLinearGradient) {
                                    aColor [j] = aStartColor [j]*(1 - brush->blendFactors[i]) + aEndColor [j]*brush->blendFactors[i];
                                } else
                                    aColor [j] = aStartColor [j]*brush->blendFactors[i] + aEndColor [j]*(1 - brush->blendFactors[i]);
                            }

                            aColors[i] = aColor;
                        }
                    } else if (brush->colorblendPositions) {
                        SAL_INFO("cppcanvas.emf", "EMF+\t\tuse color blend");
                        aColors.realloc (brush->colorblendPoints);
                        aStops.realloc (brush->colorblendPoints);

                        for (int i = 0; i < brush->colorblendPoints; i++) {
                            aStops[i] = brush->colorblendPositions [i];
                            aColors[(brush->type == BrushTypeLinearGradient) ? i : brush->colorblendPoints - 1 - i] = vcl::unotools::colorToDoubleSequence( brush->colorblendColors [i],
                                    rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
                        }
                    } else {
                        aStops[0] = 0.0;
                        aStops[1] = 1.0;

                        if (brush->type == BrushTypeLinearGradient) {
                            aColors[0] = aStartColor;
                            aColors[1] = aEndColor;
                        } else {
                            aColors[1] = aStartColor;
                            aColors[0] = aEndColor;
                        }
                    }

                    SAL_INFO("cppcanvas.emf", "EMF+\t\tset gradient");
                    basegfx::B2DRange aBoundsRectangle (0, 0, 1, 1);
                    if (brush->type == BrushTypeLinearGradient) {
                        aGradientService = "LinearGradient";
                        aGradInfo = basegfx::utils::createLinearODFGradientInfo(
                                aBoundsRectangle,
                                aStops.getLength(),
                                0,
                                0);

                    } else {
                        aGradientService = "EllipticalGradient";
                        aGradInfo = basegfx::utils::createEllipticalODFGradientInfo(
                                aBoundsRectangle,
                                ::basegfx::B2DVector( 0, 0 ),
                                aStops.getLength(),
                                0,
                                0);
                    }

                    uno::Reference< lang::XMultiServiceFactory > xFactory(
                            rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );

                    if( xFactory.is() ) {
                        uno::Sequence<uno::Any> args(comphelper::InitAnyPropertySequence(
                        {
                            {"Colors", uno::Any(aColors)},
                            {"Stops", uno::Any(aStops)},
                            {"AspectRatio", uno::Any(static_cast<sal_Int32>(1))},
                        }));
                        aTexture.Gradient.set(
                                xFactory->createInstanceWithArguments( aGradientService,
                                    args ),
                                uno::UNO_QUERY);
                    }

                    ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
                            aTextureTransformation );

                    if( aTexture.Gradient.is() )
                        pPolyAction = internal::PolyPolyActionFactory::createPolyPolyAction( localPolygon,
                                        rParms.mrCanvas,
                                        rState,
                                        aTexture );
                }
            }

            if( pPolyAction )
            {
                SAL_INFO("cppcanvas.emf", "EMF+\t\tadd poly action");

                maActions.emplace_back(
                        pPolyAction,
                        rParms.mrCurrActionIndex );

                rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
            }
        }

        double ImplRenderer::EMFPPlusDrawLineCap(const ::basegfx::B2DPolygon& rPolygon, double fPolyLength,
                const ::basegfx::B2DPolyPolygon& rLineCap, bool bIsFilled, bool bStart, const rendering::StrokeAttributes& rAttributes,
                const ActionFactoryParameters& rParms, OutDevState& rState)
        {
            if (!rLineCap.count())
                return 0.0;

            // createAreaGeometryForLineStartEnd normalises the arrows height
            // before scaling (i.e. scales down by rPolygon.height), hence
            // we pre-scale it (which means we can avoid changing the logic
            // that would affect arrows rendered outside of EMF+).
            const double fWidth = rAttributes.StrokeWidth*rLineCap.getB2DRange().getWidth();

            // When drawing an outline (as opposed to a filled endCap), we also
            // need to take account that the brush width also adds to the area
            // of the polygon.
            const double fShift = bIsFilled ? 0 : rAttributes.StrokeWidth;
            double fConsumed = 0;
            basegfx::B2DPolyPolygon aArrow(basegfx::utils::createAreaGeometryForLineStartEnd(
                        rPolygon, rLineCap, bStart,
                        fWidth, fPolyLength, 0, &fConsumed, fShift));

            // createAreaGeometryForLineStartEnd from some reason always sets
            // the path as closed, correct it
            aArrow.setClosed(rLineCap.isClosed());

            // If the endcap is filled, we draw ONLY the filling, if it isn't
            // filled we draw ONLY the outline, but never both.
            if (bIsFilled)
            {
                bool bWasFillColorSet = rState.isFillColorSet;
                rState.isFillColorSet = true;
                rState.fillColor = rState.lineColor;
                std::shared_ptr<Action> pAction2(internal::PolyPolyActionFactory::createPolyPolyAction(aArrow, rParms.mrCanvas, rState));
                if (pAction2)
                {
                    maActions.emplace_back(pAction2, rParms.mrCurrActionIndex);
                    rParms.mrCurrActionIndex += pAction2->getActionCount()-1;
                }
                rState.isFillColorSet = bWasFillColorSet;
            }
            else
            {
                std::shared_ptr<Action> pAction(internal::PolyPolyActionFactory::createPolyPolyAction(aArrow, rParms.mrCanvas, rState, rAttributes));
                if (pAction)
                {
                    maActions.emplace_back(pAction, rParms.mrCurrActionIndex);
                    rParms.mrCurrActionIndex += pAction->getActionCount()-1;
                }
            }

            // There isn't any clear definition of how far the line should extend
            // for arrows, however the following values seem to give best results
            // (fConsumed/2 draws the line to the center-point of the endcap
            // for filled caps -- however it is likely this will need to be
            // changed once we start taking baseInset into account).
            if (bIsFilled)
                return fConsumed/2;
            else
                return rAttributes.StrokeWidth;
        }

        void ImplRenderer::EMFPPlusDrawPolygon (const ::basegfx::B2DPolyPolygon& polygon, const ActionFactoryParameters& rParms,
                                                OutDevState& rState, const CanvasSharedPtr& rCanvas, sal_uInt32 penIndex)
        {
            EMFPPen* pen = static_cast<EMFPPen*>( aObjects [penIndex & 0xff].get() );

            SAL_WARN_IF( !pen, "cppcanvas.emf", "emf+ missing pen" );

            if (pen)
            {
                rState.isFillColorSet = false;
                rState.isLineColorSet = true;
                rState.lineColor = vcl::unotools::colorToDoubleSequence (pen->GetColor (),
                                                                           rCanvas->getUNOCanvas ()->getDevice()->getDeviceColorSpace());

                basegfx::B2DPolyPolygon aPolyPolygon(polygon);
                aPolyPolygon.transform(rState.mapModeTransform);
                rendering::StrokeAttributes aCommonAttributes;

                // some attributes are common for the polygon, and the line
                // starts & ends - like the stroke width
                pen->SetStrokeWidth(aCommonAttributes, *this, rState);

                // but eg. dashing has to be additionally set only on the
                // polygon
                rendering::StrokeAttributes aPolygonAttributes(aCommonAttributes);
                pen->SetStrokeAttributes(aPolygonAttributes);

                basegfx::B2DPolyPolygon aFinalPolyPolygon;

                // render line starts & ends if present
                if (!pen->customStartCap && !pen->customEndCap)
                    aFinalPolyPolygon = aPolyPolygon;
                else
                {
                    for (sal_uInt32 i = 0; i < aPolyPolygon.count(); ++i)
                    {
                        basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(i));

                        if (!aPolygon.isClosed())
                        {
                            double fStart = 0.0;
                            double fEnd = 0.0;
                            double fPolyLength = basegfx::utils::getLength(aPolygon);

                            // line start
                            if (pen->customStartCap)
                            {
                                rendering::StrokeAttributes aAttributes(aCommonAttributes);
                                pen->customStartCap->SetAttributes(aAttributes);

                                fStart = EMFPPlusDrawLineCap(aPolygon, fPolyLength, pen->customStartCap->polygon,
                                        pen->customStartCap->mbIsFilled,
                                        true, aAttributes, rParms, rState);
                            }

                            // line end
                            if (pen->customEndCap)
                            {
                                rendering::StrokeAttributes aAttributes(aCommonAttributes);
                                pen->customEndCap->SetAttributes(aAttributes);

                                fEnd = EMFPPlusDrawLineCap(aPolygon, fPolyLength, pen->customEndCap->polygon,
                                        pen->customEndCap->mbIsFilled,
                                        false, aAttributes, rParms, rState);
                            }

                            // build new poly, consume something from the old poly
                            if (fStart != 0.0 || fEnd != 0.0)
                                aPolygon = basegfx::utils::getSnippetAbsolute(aPolygon, fStart, fPolyLength - fEnd, fPolyLength);
                        }

                        aFinalPolyPolygon.append(aPolygon);
                    }
                }

                // finally render the polygon
                std::shared_ptr<Action> pPolyAction(internal::PolyPolyActionFactory::createPolyPolyAction(aFinalPolyPolygon, rParms.mrCanvas, rState, aPolygonAttributes));
                if( pPolyAction )
                {
                    maActions.emplace_back(pPolyAction, rParms.mrCurrActionIndex);
                    rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
                }
            }
        }

        void ImplRenderer::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) {
                aObjects [index].reset();
            }

            switch (flags & 0x7f00) {
            case EmfPlusObjectTypeBrush:
                {
                    std::unique_ptr<EMFPBrush> brush(new EMFPBrush());
                    brush->Read (rObjectStream, *this);
                    aObjects [index] = std::move(brush);

                    break;
                }
            case EmfPlusObjectTypePen:
                {
                    std::unique_ptr<EMFPPen> pen(new EMFPPen ());
                    pen->Read (rObjectStream, *this);
                    aObjects [index] = std::move(pen);

                    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);

                std::unique_ptr<EMFPPath> path(new EMFPPath (points));
                path->Read (rObjectStream, pathFlags, *this);
                aObjects [index] = std::move(path);

                break;
            }
            case EmfPlusObjectTypeRegion: {
                std::unique_ptr<EMFPRegion> region(new EMFPRegion ());
                region->Read (rObjectStream);
                aObjects [index] = std::move(region);
                break;
            }
            case EmfPlusObjectTypeImage:
                {
                    std::unique_ptr<EMFPImage> image(new EMFPImage);
                    image->type = 0;
                    image->width = 0;
                    image->height = 0;
                    image->stride = 0;
                    image->pixelFormat = 0;
                    image->Read (rObjectStream, dataSize, bUseWholeStream);
                    aObjects [index] = std::move(image);

                    break;
                }
            case EmfPlusObjectTypeFont:
                {
                    std::unique_ptr<EMFPFont> font(new EMFPFont);
                    font->emSize = 0;
                    font->sizeUnit = 0;
                    font->fontFlags = 0;
                    font->Read (rObjectStream);
                    aObjects [index] = std::move(font);

                    break;
                }
            case EmfPlusObjectTypeStringFormat:
                {
                    std::unique_ptr<EMFPStringFormat> stringFormat(new EMFPStringFormat());
                    stringFormat->Read (rObjectStream);
                    aObjects [index] = std::move(stringFormat);
                    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;
            }
        }

        double ImplRenderer::setFont (css::rendering::FontRequest& aFontRequest, sal_uInt8 fontObjectId, const ActionFactoryParameters& rParms, OutDevState& rState)
        {
            EMFPFont *font = static_cast< EMFPFont* >( aObjects[ fontObjectId ].get() );

            aFontRequest.FontDescription.FamilyName = font->family;
            double cellSize = font->emSize;
            if( font->fontFlags & FontStyleBold )
            {
                aFontRequest.FontDescription.FontDescription.Weight = rendering::PanoseWeight::BOLD;
            }
            aFontRequest.CellSize = (rState.mapModeTransform*MapSize( cellSize, 0 )).getX();
            rState.xFont = rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
                                               uno::Sequence< beans::PropertyValue >(),
                                               geometry::Matrix2D() );

            return cellSize;
        }

        void ImplRenderer::GraphicStatePush(GraphicStateMap& map, sal_Int32 index, OutDevState const & rState)
        {
            GraphicStateMap::iterator iter = map.find( index );

            if ( iter != map.end() )
            {
                EmfPlusGraphicState state = iter->second;
                map.erase( iter );

                SAL_INFO("cppcanvas.emf", "stack index: " << index << " found and erased");
            }

            EmfPlusGraphicState state;

            state.aWorldTransform = aWorldTransform;
            state.aDevState = rState;

            map[ index ] = state;
        }

        void ImplRenderer::GraphicStatePop(GraphicStateMap& map, sal_Int32 index, OutDevState& rState)
        {
            GraphicStateMap::iterator iter = map.find( index );

            if ( iter != map.end() )
            {
                SAL_INFO("cppcanvas.emf", "stack index: " << index << " found");

                EmfPlusGraphicState state = iter->second;

                aWorldTransform = state.aWorldTransform;
                rState.clip = state.aDevState.clip;
                rState.clipRect = state.aDevState.clipRect;
                rState.xClipPoly = state.aDevState.xClipPoly;
            }
        }

        void ImplRenderer::processEMFPlus( MetaCommentAction const * pAct, const ActionFactoryParameters& rFactoryParms,
                                           OutDevState& rState, const CanvasSharedPtr& rCanvas )
        {
            sal_uInt32 length = pAct->GetDataSize ();
            SvMemoryStream rMF (const_cast<sal_uInt8 *>(pAct->GetData ()), length, StreamMode::READ);

            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;

                rMF.ReadUInt16( type ).ReadUInt16( flags ).ReadUInt32( size ).ReadUInt32( dataSize );

                next = rMF.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 *>(rMF.GetData()) + rMF.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;

                        rMF.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 (rMF, 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 )
                            {
                                rMF.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");
                            }
                            rMF.ReadFloat( startAngle ).ReadFloat( sweepAngle );

                            float dx, dy, dw, dh;

                            ReadRectangle (rMF, dx, dy, dw, dh, bool(flags & 0x4000));

                            SAL_INFO("cppcanvas.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);

                            startAngle = basegfx::deg2rad(startAngle);
                            sweepAngle = basegfx::deg2rad(sweepAngle);

                            B2DPoint mappedCenter (Map (dx + dw/2, dy + dh/2));
                            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 " <<
                                     basegfx::rad2deg(startAngle) << ", end: " << basegfx::rad2deg(endAngle) <<
                                     " startAngle: " << startAngle << " sweepAngle: " << sweepAngle);

                            B2DPolygon polygon = basegfx::utils::createPolygonFromEllipseSegment (mappedCenter, mappedSize.getX (), mappedSize.getY (), startAngle, endAngle);
                            if ( type != EmfPlusRecordTypeDrawArc )
                            {
                                polygon.append (mappedCenter);
                                polygon.setClosed (true);
                            }

                            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;

                            rMF.ReadUInt32( brushIndexOrColor );

                            SAL_INFO("cppcanvas.emf", "EMF+ FillPath slot: " << index);

                            EMFPPlusFillPolygon( static_cast<EMFPPath*>( aObjects [index].get())->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 )
                                rMF.ReadUInt32( brushIndexOrColor );

                            SAL_INFO("cppcanvas.emf", "EMF+ " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff));

                            float dx, dy, dw, dh;

                            ReadRectangle (rMF, dx, dy, dw, dh, bool(flags & 0x4000));

                            SAL_INFO("cppcanvas.emf", "EMF+ RectData: " << dx << "," << dy << " " << dw << "x" << dh);

                            B2DPoint mappedCenter (Map (dx + dw/2, dy + dh/2));
                            B2DSize mappedSize( MapSize (dw/2, dh/2));

                            ::basegfx::B2DPolyPolygon polyPolygon( ::basegfx::utils::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");
                                rMF.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");
                            }

                            rMF.ReadInt32( rectangles );

                            for (int i=0; i < rectangles; i++) {
                                float x, y, width, height;
                                ReadRectangle (rMF, 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;

                            rMF.ReadUInt32( brushIndexOrColor );
                            rMF.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 (rMF, flags, *this);

                            EMFPPlusFillPolygon (path.GetPolygon (*this), rFactoryParms, rState, rCanvas, flags & 0x8000, brushIndexOrColor);

                            break;
                        }
                    case EmfPlusRecordTypeDrawLines:
                        {
                            sal_uInt32 points;

                            rMF.ReadUInt32( points );

                            SAL_INFO("cppcanvas.emf", "EMF+ DrawLines in slot: " << (flags & 0xff) << " points: " << points);

                            EMFPPath path (points, true);
                            path.Read (rMF, 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;

                            rMF.ReadUInt32( penIndex );

                            SAL_INFO("cppcanvas.emf", "EMF+ DrawPath");
                            SAL_INFO("cppcanvas.emf", "EMF+\tpen: " << penIndex);

                            EMFPPath* path = static_cast<EMFPPath*>( aObjects [flags & 0xff].get() );
                            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;
                            rMF.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 (rMF, 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 (rMF, x2, y2, flags);
                                ReadPoint (rMF, x3, y3, flags);
                                ReadPoint (rMF, 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;

                            rMF.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");

                            // For DrawImage and DrawImagePoints, source unit of measurement type must be 1 pixel
                            if (sourceUnit == UnitTypePixel && aObjects [flags & 0xff]) {
                                EMFPImage& image = *static_cast<EMFPImage *>( aObjects [flags & 0xff].get());
                                float sx, sy, sw, sh;

                                ReadRectangle (rMF, 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;

                                if (type == EmfPlusRecordTypeDrawImagePoints) {
                                    sal_Int32 aCount;
                                    rMF.ReadInt32( aCount );

                                    // Number of points used by DrawImagePoints. Exactly 3 points must be specified.
                                    if( aCount == 3 ) {
                                        float x1, y1, x2, y2, x3, y3;

                                        ReadPoint (rMF, x1, y1, flags);
                                        ReadPoint (rMF, x2, y2, flags);
                                        ReadPoint (rMF, 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);
                                    } else {
                                        SAL_WARN("cppcanvas.emf", "EMF+ DrawImagePoints Wrong EMF+ file. Expected 3 points, received: "<< aCount);
                                        break;
                                    }
                                } else if (type == EmfPlusRecordTypeDrawImage) {
                                    float dx, dy, dw, dh;

                                    ReadRectangle (rMF, 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);
                                }

                                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 ) {
                                    std::shared_ptr<Action> pBmpAction (
                                        internal::BitmapActionFactory::createBitmapAction (
                                            aBmp,
                                            rState.mapModeTransform * aDstPoint,
                                            rState.mapModeTransform * aDstSize,
                                            rCanvas,
                                            rState));

                                    if( pBmpAction ) {
                                        maActions.emplace_back( pBmpAction,
                                                                        rFactoryParms.mrCurrActionIndex );

                                        rFactoryParms.mrCurrActionIndex += pBmpAction->getActionCount()-1;
                                    }
                                } else {
                                    SAL_WARN("cppcanvas.emf", "EMF+ warning: empty bitmap");
                                }
                            } else {
                                SAL_WARN("cppcanvas.emf", "EMF+ DrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is support by EMF+ standard in DrawImage(Points)");
                            }
                            break;
                        }
                    case EmfPlusRecordTypeDrawString:
                        {
                            SAL_INFO("cppcanvas.emf", "EMF+ DrawString");

                            sal_uInt32 brushId;
                            sal_uInt32 formatId;
                            sal_uInt32 stringLength;

                            rMF.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;

                                rMF.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(rMF, stringLength);

                                EMFPStringFormat *stringFormat = static_cast< EMFPStringFormat* >( aObjects[ formatId & 0xff ].get() );
                                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 ) );

                                std::shared_ptr<Action> 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(),
                                        ::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.emplace_back(
                                                                  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:
                        {
                            rMF.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
                            {
                                nMmX *= fPageScale;
                                nMmY *= fPageScale;
                            }
                        }
                        break;
                    case EmfPlusRecordTypeSetRenderingOrigin:
                        rMF.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;

                        rMF.ReadUInt32( stackIndex );

                        SAL_INFO("cppcanvas.emf", "EMF+ Save stack index: " << stackIndex);

                        GraphicStatePush( mGSStack, stackIndex, rState );

                        break;
                    }
                    case EmfPlusRecordTypeRestore:
                    {
                        sal_uInt32 stackIndex;

                        rMF.ReadUInt32( stackIndex );

                        SAL_INFO("cppcanvas.emf", "EMF+ Restore stack index: " << stackIndex);

                        GraphicStatePop( mGSStack, stackIndex, rState );

                        break;
                    }
                    case EmfPlusRecordTypeBeginContainerNoParams:
                    {
                        sal_uInt32 stackIndex;

                        rMF.ReadUInt32( stackIndex );

                        SAL_INFO("cppcanvas.emf", "EMF+ Begin Container No Params stack index: " << stackIndex);

                        GraphicStatePush( mGSContainerStack, stackIndex, rState );
                    }
                    break;
                    case EmfPlusRecordTypeEndContainer:
                    {
                        sal_uInt32 stackIndex;

                        rMF.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");
                        XForm transform;
                        ReadXForm( rMF, transform );
                        aWorldTransform.Set (transform);
                        SAL_INFO("cppcanvas.emf",
                                "EMF+\tm11: " << aWorldTransform.eM11 << "\tm12: " << aWorldTransform.eM12 <<
                                "\tm21: " << aWorldTransform.eM21 << "\tm22: " << aWorldTransform.eM22 <<
                                "\tdx: "  << aWorldTransform.eDx  << "\tdy: "  << aWorldTransform.eDy);
                        break;
                    }
                    case EmfPlusRecordTypeResetWorldTransform:
                        SAL_INFO("cppcanvas.emf", "EMF+ ResetWorldTransform");
                        aWorldTransform.SetIdentity ();
                        break;
                    case EmfPlusRecordTypeMultiplyWorldTransform: {
                        SAL_INFO("cppcanvas.emf", "EMF+ MultiplyWorldTransform");
                        XForm transform;
                        ReadXForm( rMF, transform );

                        SAL_INFO("cppcanvas.emf",
                                "EMF+\tmatrix m11: " << transform.eM11 << "m12: " << transform.eM12 <<
                                "EMF+\tm21: "        << transform.eM21 << "m22: " << transform.eM22 <<
                                "EMF+\tdx: "         << transform.eDx  << "dy: "  << transform.eDy);

                        if (flags & 0x2000)  // post multiply
                            aWorldTransform.Multiply (transform);
                        else {               // pre multiply
                            transform.Multiply (aWorldTransform);
                            aWorldTransform.Set (transform);
                        }
                        SAL_INFO("cppcanvas.emf",
                                "EMF+\tm11: " << aWorldTransform.eM11 << "m12: " << aWorldTransform.eM12 <<
                                "EMF+\tm21: " << aWorldTransform.eM21 << "m22: " << aWorldTransform.eM22 <<
                                "EMF+\tdx: "  << aWorldTransform.eDx  << "dy: "  << aWorldTransform.eDy);
                        break;
                    }
                    case EmfPlusRecordTypeTranslateWorldTransform:
                        {
                            SAL_INFO("cppcanvas.emf", "EMF+ TranslateWorldTransform");

                            XForm transform = XForm();
                            rMF.ReadFloat( transform.eDx ).ReadFloat( transform.eDy );

                            SAL_INFO("cppcanvas.emf",
                                     "EMF+\t m11: " << transform.eM11 << ", m12: " << transform.eM12 <<
                                     "EMF+\t m21: " << transform.eM21 << ", m22: " << transform.eM22 <<
                                     "EMF+\t dx: "  << transform.eDx  << ", dy: "  << transform.eDy);

                            if (flags & 0x2000)  // post multiply
                                aWorldTransform.Multiply (transform);
                            else {               // pre multiply
                                transform.Multiply (aWorldTransform);
                                aWorldTransform.Set (transform);
                            }
                            SAL_INFO("cppcanvas.emf",
                                     "EMF+\t m11: " << aWorldTransform.eM11 << ", m12: " << aWorldTransform.eM12 <<
                                     "EMF+\t m21: " << aWorldTransform.eM21 << ", m22: " << aWorldTransform.eM22 <<
                                     "EMF+\t dx: "  << aWorldTransform.eDx  << ", dy: "  << aWorldTransform.eDy);
                            break;
                        }
                    case EmfPlusRecordTypeScaleWorldTransform:
                        {
                            XForm transform = XForm();
                            rMF.ReadFloat( transform.eM11 ).ReadFloat( transform.eM22 );
                            SAL_INFO("cppcanvas.emf", "EMF+ ScaleWorldTransform Sx: " << transform.eM11 << " Sy: " << transform.eM22);

                            SAL_INFO("cppcanvas.emf",
                                     "EMF+\t m11: " << aWorldTransform.eM11 << ", m12: " << aWorldTransform.eM12 <<
                                     "EMF+\t m21: " << aWorldTransform.eM21 << ", m22: " << aWorldTransform.eM22 <<
                                     "EMF+\t dx: "  << aWorldTransform.eDx  << ", dy: "  << aWorldTransform.eDy);

                            if (flags & 0x2000)  // post multiply
                                aWorldTransform.Multiply (transform);
                            else {               // pre multiply
                                transform.Multiply (aWorldTransform);
                                aWorldTransform.Set (transform);
                            }
                            SAL_INFO("cppcanvas.emf",
                                     "EMF+\t m11: " << aWorldTransform.eM11 << ", m12: " << aWorldTransform.eM12 <<
                                     "EMF+\t m21: " << aWorldTransform.eM21 << ", m22: " << aWorldTransform.eM22 <<
                                     "EMF+\t dx: "  << aWorldTransform.eDx  << ", dy: "  << aWorldTransform.eDy);
                            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 (rMF, dx, dy, dw, dh);

                            SAL_INFO("cppcanvas.emf", "EMF+ RectData: " << dx << "," << dy << " " << dw << "x" << dh);

                            B2DPoint mappedPoint (Map (dx, dy));
                            B2DSize mappedSize( MapSize (dw, dh));

                            ::basegfx::B2DPolyPolygon polyPolygon( ::basegfx::utils::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].get() );
                            ::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].get());

                        // 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;

                        rMF.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(rMF, glyphsCount);

                            for( sal_uInt32 i=0; i<glyphsCount; i++) {
                                rMF.ReadFloat( charsPosX[i] ).ReadFloat( charsPosY[i] );
                                SAL_INFO("cppcanvas.emf", "EMF+\tglyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]);
                            }

                            XForm transform;
                            if( hasMatrix ) {
                                ReadXForm( rMF, transform );
                                SAL_INFO("cppcanvas.emf", "EMF+\tmatrix: " << transform.eM11 << ", " << transform.eM12 << ", " << transform.eM21 << ", " << transform.eM22 << ", " << transform.eDx << ", " << transform.eDy);
                            }

                            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] ) );

                            std::shared_ptr<Action> pTextAction(
                                    TextActionFactory::createTextAction(
                                        vcl::unotools::pointFromB2DPoint ( point ),
                                        ::Size(),
                                        ::Color(),
                                        ::Size(),
                                        ::Color(),
                                        ::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.emplace_back(
                                            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);
                    }
                }

                rMF.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/cppcanvas/source/mtfrenderer/emfppath.cxx b/cppcanvas/source/mtfrenderer/emfppath.cxx
deleted file mode 100644
index 82e99a9..0000000
--- a/cppcanvas/source/mtfrenderer/emfppath.cxx
+++ /dev/null
@@ -1,193 +0,0 @@
/* -*- 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/utils/canvastools.hxx>
#include <basegfx/utils/gradienttools.hxx>
#include <basegfx/utils/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 <sal/log.hxx>
#include <vcl/canvastools.hxx>
#include <implrenderer.hxx>
#include "emfppath.hxx"

using namespace ::com::sun::star;
using namespace ::basegfx;

namespace cppcanvas
{
    namespace internal
    {
        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.reset( new float [nPoints*2] );
            if (!bLines)
                pPointTypes.reset( new sal_uInt8 [_nPoints] );
        }

        EMFPPath::~EMFPPath ()
        {
        }

        // TODO: remove rR argument when debug code is no longer needed
        void EMFPPath::Read (SvStream& s, sal_uInt32 pathFlags, ImplRenderer const & 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: " << static_cast<int>(pPointTypes [i]));
                }

            aPolygon.clear ();

#if OSL_DEBUG_LEVEL > 1
            const ::basegfx::B2DRectangle aBounds (::basegfx::utils::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 (ImplRenderer const & 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/cppcanvas/source/mtfrenderer/emfppath.hxx b/cppcanvas/source/mtfrenderer/emfppath.hxx
deleted file mode 100644
index 47fb32a..0000000
--- a/cppcanvas/source/mtfrenderer/emfppath.hxx
+++ /dev/null
@@ -1,50 +0,0 @@
/* -*- 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_CPPCANVAS_SOURCE_MTFRENDERER_EMFPPATH_HXX
#define INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPPATH_HXX

#include <memory>

namespace cppcanvas
{
    namespace internal
    {
        struct EMFPPath : public EMFPObject
        {
            ::basegfx::B2DPolyPolygon    aPolygon;
            sal_Int32                    nPoints;
            std::unique_ptr<float[]>     pPoints;
            std::unique_ptr<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, ImplRenderer const & rR);

            ::basegfx::B2DPolyPolygon& GetPolygon(ImplRenderer const & rR, bool bMapIt = true, bool bAddLineToCloseShape = false);
        };
    }
}

#endif // INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPPATH_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cppcanvas/source/mtfrenderer/emfppen.cxx b/cppcanvas/source/mtfrenderer/emfppen.cxx
deleted file mode 100644
index adf578a..0000000
--- a/cppcanvas/source/mtfrenderer/emfppen.cxx
+++ /dev/null
@@ -1,296 +0,0 @@
/* -*- 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/utils/canvastools.hxx>
#include <basegfx/utils/gradienttools.hxx>
#include <basegfx/utils/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 <sal/log.hxx>
#include <vcl/canvastools.hxx>
#include <implrenderer.hxx>
#include "emfppen.hxx"
#include "emfpcustomlinecap.hxx"

using namespace ::com::sun::star;
using namespace ::basegfx;

namespace cppcanvas
{
    namespace internal
    {

        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)
            , alignment(0)
            , customStartCapLen(0)
            , customEndCapLen(0)
        {
        }

        EMFPPen::~EMFPPen()
        {
        }

        void EMFPPen::SetStrokeWidth(rendering::StrokeAttributes& rStrokeAttributes, ImplRenderer const & 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 = dashPattern.size(); pPattern = dashPattern.data(); 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, ImplRenderer const & 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;
                sal_Int32 dashPatternLen;

                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.resize( dashPatternLen );
                for (i = 0; i < dashPatternLen; i++)
                {
                    s.ReadFloat(dashPattern[i]);
                    SAL_INFO("cppcanvas.emf", "EMF+\t\t\tdashPattern[" << i << "]: " << dashPattern[i]);
                }
            }

            if (penDataFlags & PenDataNonCenter)
                s.ReadInt32(alignment);
            else
                alignment = 0;

            if (penDataFlags & PenDataCompoundLine)
            {
                sal_Int32 compoundArrayLen;
                s.ReadInt32(compoundArrayLen);
                if (compoundArrayLen<0 || sal_uInt32(compoundArrayLen)>SAL_MAX_INT32 / sizeof(float))
                    compoundArrayLen = SAL_MAX_INT32 / sizeof(float);
                compoundArray.resize(compoundArrayLen);
                for (i = 0; i < compoundArrayLen; i++)
                    s.ReadFloat(compoundArray[i]);
            }

            if (penDataFlags & PenDataCustomStartCap)
            {
                s.ReadInt32(customStartCapLen);
                SAL_INFO("cppcanvas.emf", "EMF+\t\tcustomStartCapLen: " << customStartCapLen);
                sal_uInt64 const pos = s.Tell();

                customStartCap.reset( 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.reset( 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/cppcanvas/source/mtfrenderer/emfppen.hxx b/cppcanvas/source/mtfrenderer/emfppen.hxx
deleted file mode 100644
index 75878df..0000000
--- a/cppcanvas/source/mtfrenderer/emfppen.hxx
+++ /dev/null
@@ -1,79 +0,0 @@
/* -*- 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_CPPCANVAS_SOURCE_MTFRENDERER_EMFPPEN_HXX
#define INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPPEN_HXX

#include "emfpbrush.hxx"
#include <vector>

namespace cppcanvas
{
    namespace internal
    {
        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
        {
            XForm 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;
            std::vector<float> dashPattern;
            sal_Int32 alignment;
            std::vector<float> compoundArray;
            sal_Int32 customStartCapLen;
            std::unique_ptr<EMFPCustomLineCap> customStartCap;
            sal_Int32 customEndCapLen;
            std::unique_ptr<EMFPCustomLineCap> customEndCap;

            EMFPPen();

            virtual ~EMFPPen() override;

            void SetStrokeWidth(com::sun::star::rendering::StrokeAttributes& rStrokeAttributes, ImplRenderer const & rR, const OutDevState& rState);

            void SetStrokeAttributes(com::sun::star::rendering::StrokeAttributes& rStrokeAttributes);

            void Read(SvStream& s, ImplRenderer const & rR);

            static sal_Int8 lcl_convertStrokeCap(sal_uInt32 nEmfStroke);
            static sal_Int8 lcl_convertLineJoinType(sal_uInt32 nEmfLineJoin);

        };
    }
}


#endif // INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPPEN_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cppcanvas/source/mtfrenderer/emfpregion.cxx b/cppcanvas/source/mtfrenderer/emfpregion.cxx
deleted file mode 100644
index 4a45d4a..0000000
--- a/cppcanvas/source/mtfrenderer/emfpregion.cxx
+++ /dev/null
@@ -1,86 +0,0 @@
/* -*- 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/utils/canvastools.hxx>
#include <basegfx/utils/gradienttools.hxx>
#include <basegfx/utils/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 <sal/log.hxx>
#include <vcl/canvastools.hxx>
#include <implrenderer.hxx>
#include "emfpregion.hxx"

using namespace ::com::sun::star;
using namespace ::basegfx;

namespace cppcanvas
{
    namespace internal
    {
        EMFPRegion::EMFPRegion()
            : parts(0)
            , initialState(0)
        {
        }

        EMFPRegion::~EMFPRegion()
        {
        }

        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.reset( 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/cppcanvas/source/mtfrenderer/emfpregion.hxx b/cppcanvas/source/mtfrenderer/emfpregion.hxx
deleted file mode 100644
index 8d929d6..0000000
--- a/cppcanvas/source/mtfrenderer/emfpregion.hxx
+++ /dev/null
@@ -1,42 +0,0 @@
/* -*- 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_CPPCANVAS_SOURCE_MTFRENDERER_EMFPREGION_HXX
#define INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPREGION_HXX

namespace cppcanvas
{
    namespace internal
    {
        struct EMFPRegion : public EMFPObject
        {
            sal_Int32 parts;
            std::unique_ptr<sal_Int32[]> combineMode;
            sal_Int32 initialState;

            EMFPRegion();
            virtual ~EMFPRegion() override;
            void Read(SvStream& s);
        };
    }
}

#endif // INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPREGION_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cppcanvas/source/mtfrenderer/emfpstringformat.cxx b/cppcanvas/source/mtfrenderer/emfpstringformat.cxx
deleted file mode 100644
index 3198701..0000000
--- a/cppcanvas/source/mtfrenderer/emfpstringformat.cxx
+++ /dev/null
@@ -1,66 +0,0 @@
/* -*- 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 <implrenderer.hxx>
#include <sal/log.hxx>
#include "emfpstringformat.hxx"

namespace cppcanvas
{
    namespace internal
    {
        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/cppcanvas/source/mtfrenderer/emfpstringformat.hxx b/cppcanvas/source/mtfrenderer/emfpstringformat.hxx
deleted file mode 100644
index 6357fdb..0000000
--- a/cppcanvas/source/mtfrenderer/emfpstringformat.hxx
+++ /dev/null
@@ -1,54 +0,0 @@
/* -*- 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_CPPCANVAS_SOURCE_MTFRENDERER_EMFPSTRINGFORMAT_HXX
#define INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPSTRINGFORMAT_HXX

namespace cppcanvas
{
    namespace internal
    {
        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 // INCLUDED_CPPCANVAS_SOURCE_MTFRENDERER_EMFPSTRINGFORMAT_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cppcanvas/source/mtfrenderer/implrenderer.cxx b/cppcanvas/source/mtfrenderer/implrenderer.cxx
index 84b46e0..999a965 100644
--- a/cppcanvas/source/mtfrenderer/implrenderer.cxx
+++ b/cppcanvas/source/mtfrenderer/implrenderer.cxx
@@ -1789,18 +1789,7 @@
                        }
                        // Handle drawing layer fills
                        else if( pAct->GetComment() == "EMF_PLUS" ) {
                            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: " << pAct->GetDataSize ());
                            if (count < limit)
                                processEMFPlus( pAct, rFactoryParms, rStates.getState(), rCanvas );
                            count ++;
                            SAL_WARN ("cppcanvas.emf", "EMF+ code was refactored and removed");
                        } else if( pAct->GetComment() == "EMF_PLUS_HEADER_INFO" ) {
                            SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct->GetDataSize ());