From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001
From: Daniel Baumann <daniel.baumann@progress-linux.org>
Date: Sun, 7 Apr 2024 11:06:44 +0200
Subject: Adding upstream version 4:7.4.7.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
---
 emfio/source/emfuno/xemfparser.cxx |  238 ++++
 emfio/source/reader/emfreader.cxx  | 2230 +++++++++++++++++++++++++++++++
 emfio/source/reader/mtftools.cxx   | 2528 ++++++++++++++++++++++++++++++++++++
 emfio/source/reader/wmfreader.cxx  | 2015 ++++++++++++++++++++++++++++
 4 files changed, 7011 insertions(+)
 create mode 100644 emfio/source/emfuno/xemfparser.cxx
 create mode 100644 emfio/source/reader/emfreader.cxx
 create mode 100644 emfio/source/reader/mtftools.cxx
 create mode 100644 emfio/source/reader/wmfreader.cxx

(limited to 'emfio/source')

diff --git a/emfio/source/emfuno/xemfparser.cxx b/emfio/source/emfuno/xemfparser.cxx
new file mode 100644
index 000000000..d5342edf7
--- /dev/null
+++ b/emfio/source/emfuno/xemfparser.cxx
@@ -0,0 +1,238 @@
+/* -*- 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 <sal/config.h>
+
+#include <com/sun/star/graphic/XEmfParser.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase2.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/wmfexternal.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <sal/log.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+
+#include <wmfreader.hxx>
+#include <emfreader.hxx>
+
+using namespace ::com::sun::star;
+
+namespace emfio::emfreader
+{
+        namespace {
+
+        class XEmfParser : public ::cppu::WeakAggImplHelper2< graphic::XEmfParser, lang::XServiceInfo >
+        {
+        private:
+            uno::Reference< uno::XComponentContext > context_;
+            basegfx::B2DTuple maSizeHint;
+
+        public:
+            explicit XEmfParser(
+                uno::Reference< uno::XComponentContext > const & context);
+            XEmfParser(const XEmfParser&) = delete;
+            XEmfParser& operator=(const XEmfParser&) = delete;
+
+            // XEmfParser
+            virtual uno::Sequence< uno::Reference< ::graphic::XPrimitive2D > > SAL_CALL getDecomposition(
+                const uno::Reference< ::io::XInputStream >& xEmfStream,
+                const OUString& aAbsolutePath,
+                const uno::Sequence< ::beans::PropertyValue >& rProperties) override;
+            void SAL_CALL setSizeHint(const geometry::RealPoint2D& rSize) override;
+
+            // XServiceInfo
+            virtual OUString SAL_CALL getImplementationName() override;
+            virtual sal_Bool SAL_CALL supportsService(const OUString&) override;
+            virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+        };
+
+        }
+
+        XEmfParser::XEmfParser(
+            uno::Reference< uno::XComponentContext > const & context):
+            context_(context)
+        {
+        }
+
+        uno::Sequence< uno::Reference< ::graphic::XPrimitive2D > > XEmfParser::getDecomposition(
+            const uno::Reference< ::io::XInputStream >& xEmfStream,
+            const OUString& /*aAbsolutePath*/,
+            const uno::Sequence< ::beans::PropertyValue >& rProperties)
+        {
+            drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+            if (xEmfStream.is())
+            {
+                WmfExternal aExternalHeader;
+                const bool bExternalHeaderUsed(aExternalHeader.setSequence(rProperties));
+                bool bEnableEMFPlus = true;
+                comphelper::SequenceAsHashMap aMap(rProperties);
+                auto it = aMap.find("EMFPlusEnable");
+                if (it != aMap.end())
+                {
+                    bool bValue;
+                    if (it->second >>= bValue)
+                    {
+                        bEnableEMFPlus = bValue;
+                    }
+                }
+
+                // rough check - import and conv to primitive
+                GDIMetaFile aMtf;
+                std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream(xEmfStream));
+                sal_uInt32 nOrgPos = pStream->Tell();
+
+                SvStreamEndian nOrigNumberFormat = pStream->GetEndian();
+                pStream->SetEndian(SvStreamEndian::LITTLE);
+
+                sal_uInt32 nMetaType(0);
+                if (checkSeek(*pStream, 0x28))
+                    pStream->ReadUInt32(nMetaType);
+                pStream->Seek(nOrgPos);
+
+                bool bReadError(false);
+
+                try
+                {
+                    if (nMetaType == 0x464d4520)
+                    {
+                        // read and get possible failure/error, ReadEnhWMF returns success
+                        emfio::EmfReader aReader(*pStream, aMtf);
+                        aReader.SetSizeHint(maSizeHint);
+                        if (!bEnableEMFPlus)
+                        {
+                            aReader.SetEnableEMFPlus(bEnableEMFPlus);
+                        }
+                        bReadError = !aReader.ReadEnhWMF();
+                    }
+                    else
+                    {
+                        emfio::WmfReader aReader(*pStream, aMtf, bExternalHeaderUsed ? &aExternalHeader : nullptr);
+                        if (!bEnableEMFPlus)
+                            aReader.SetEnableEMFPlus(bEnableEMFPlus);
+                        aReader.ReadWMF();
+
+                        // Need to check for ErrCode at stream to not lose former work.
+                        // This may contain important information and will behave the
+                        // same as before. When we have an error, do not create content
+                        ErrCode aErrCode(pStream->GetError());
+
+                        bReadError = aErrCode.IsError();
+                    }
+                }
+                catch (...)
+                {
+                    bReadError = true;
+                }
+
+                pStream->SetEndian(nOrigNumberFormat);
+
+                if (!bReadError)
+                {
+                    Size aSize(aMtf.GetPrefSize());
+
+                    if (aMtf.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
+                    {
+                        aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, MapMode(MapUnit::Map100thMM));
+                    }
+                    else
+                    {
+                        aSize = OutputDevice::LogicToLogic(aSize, aMtf.GetPrefMapMode(), MapMode(MapUnit::Map100thMM));
+                    }
+
+                    // use size
+                    const basegfx::B2DHomMatrix aMetafileTransform(
+                        basegfx::utils::createScaleB2DHomMatrix(
+                            aSize.Width(),
+                            aSize.Height()));
+
+                    // ...and create a single MetafilePrimitive2D containing the Metafile.
+                    // CAUTION: Currently, ReadWindowMetafile uses the local VectorGraphicData
+                    // and a MetafileAccessor hook at the MetafilePrimitive2D inside of
+                    // ImpGraphic::ImplGetGDIMetaFile to get the Metafile. Thus, the first
+                    // and only primitive in this case *has to be* a MetafilePrimitive2D.
+                    aRetval.push_back(
+                        new drawinglayer::primitive2d::MetafilePrimitive2D(
+                            aMetafileTransform,
+                            aMtf));
+
+                    // // force to use decomposition directly to get rid of the metafile
+                    // const css::uno::Sequence< css::beans::PropertyValue > aViewParameters;
+                    // drawinglayer::primitive2d::MetafilePrimitive2D aMetafilePrimitive2D(
+                    //     aMetafileTransform,
+                    //     aMtf);
+                    // aRetval.append(aMetafilePrimitive2D.getDecomposition(aViewParameters));
+
+                    // if (aRetval.empty())
+                    // {
+                    //     // for test, just create some graphic data that will get visualized
+                    //     const basegfx::B2DRange aRange(1000, 1000, 5000, 5000);
+                    //     const basegfx::BColor aColor(1.0, 0.0, 0.0);
+                    //     const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange));
+                    //
+                    //     aRetval.push_back(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aOutline), aColor));
+                    // }
+                }
+            }
+            else
+            {
+                SAL_WARN("emfio", "Invalid stream (!)");
+            }
+
+            return aRetval.toSequence();
+        }
+
+        void XEmfParser::setSizeHint(const geometry::RealPoint2D& rSize)
+        {
+            maSizeHint.setX(rSize.X);
+            maSizeHint.setY(rSize.Y);
+        }
+
+        OUString SAL_CALL XEmfParser::getImplementationName()
+        {
+            return "emfio::emfreader::XEmfParser";
+        }
+
+        sal_Bool SAL_CALL XEmfParser::supportsService(const OUString& rServiceName)
+        {
+            return cppu::supportsService(this, rServiceName);
+        }
+
+        uno::Sequence< OUString > SAL_CALL XEmfParser::getSupportedServiceNames()
+        {
+            return { "com.sun.star.graphic.EmfTools" };
+        }
+
+} // end of namespace emfio::emfreader
+
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+emfio_emfreader_XEmfParser_get_implementation(
+    css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+    return cppu::acquire(new emfio::emfreader::XEmfParser(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/emfio/source/reader/emfreader.cxx b/emfio/source/reader/emfreader.cxx
new file mode 100644
index 000000000..1613fd859
--- /dev/null
+++ b/emfio/source/reader/emfreader.cxx
@@ -0,0 +1,2230 @@
+/* -*- 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 <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <emfreader.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <vcl/dibtools.hxx>
+#include <o3tl/safeint.hxx>
+#include <tools/stream.hxx>
+#include <memory>
+#include <unotools/configmgr.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/pdfread.hxx>
+#include <rtl/bootstrap.hxx>
+
+#ifdef DBG_UTIL
+#include <vcl/pngwrite.hxx>
+#endif
+
+// GDI-Array
+
+#define EMR_HEADER                      1
+#define EMR_POLYBEZIER                  2
+#define EMR_POLYGON                     3
+#define EMR_POLYLINE                    4
+#define EMR_POLYBEZIERTO                5
+#define EMR_POLYLINETO                  6
+#define EMR_POLYPOLYLINE                7
+#define EMR_POLYPOLYGON                 8
+#define EMR_SETWINDOWEXTEX              9
+#define EMR_SETWINDOWORGEX              10
+#define EMR_SETVIEWPORTEXTEX            11
+#define EMR_SETVIEWPORTORGEX            12
+#define EMR_SETBRUSHORGEX               13
+#define EMR_EOF                         14
+#define EMR_SETPIXELV                   15
+#define EMR_SETMAPPERFLAGS              16
+#define EMR_SETMAPMODE                  17
+#define EMR_SETBKMODE                   18
+#define EMR_SETPOLYFILLMODE             19
+#define EMR_SETROP2                     20
+#define EMR_SETSTRETCHBLTMODE           21
+#define EMR_SETTEXTALIGN                22
+#define EMR_SETCOLORADJUSTMENT          23
+#define EMR_SETTEXTCOLOR                24
+#define EMR_SETBKCOLOR                  25
+#define EMR_OFFSETCLIPRGN               26
+#define EMR_MOVETOEX                    27
+#define EMR_SETMETARGN                  28
+#define EMR_EXCLUDECLIPRECT             29
+#define EMR_INTERSECTCLIPRECT           30
+#define EMR_SCALEVIEWPORTEXTEX          31
+#define EMR_SCALEWINDOWEXTEX            32
+#define EMR_SAVEDC                      33
+#define EMR_RESTOREDC                   34
+#define EMR_SETWORLDTRANSFORM           35
+#define EMR_MODIFYWORLDTRANSFORM        36
+#define EMR_SELECTOBJECT                37
+#define EMR_CREATEPEN                   38
+#define EMR_CREATEBRUSHINDIRECT         39
+#define EMR_DELETEOBJECT                40
+#define EMR_ANGLEARC                    41
+#define EMR_ELLIPSE                     42
+#define EMR_RECTANGLE                   43
+#define EMR_ROUNDRECT                   44
+#define EMR_ARC                         45
+#define EMR_CHORD                       46
+#define EMR_PIE                         47
+#define EMR_SELECTPALETTE               48
+#define EMR_CREATEPALETTE               49
+#define EMR_SETPALETTEENTRIES           50
+#define EMR_RESIZEPALETTE               51
+#define EMR_REALIZEPALETTE              52
+#define EMR_EXTFLOODFILL                53
+#define EMR_LINETO                      54
+#define EMR_ARCTO                       55
+#define EMR_POLYDRAW                    56
+#define EMR_SETARCDIRECTION             57
+#define EMR_SETMITERLIMIT               58
+#define EMR_BEGINPATH                   59
+#define EMR_ENDPATH                     60
+#define EMR_CLOSEFIGURE                 61
+#define EMR_FILLPATH                    62
+#define EMR_STROKEANDFILLPATH           63
+#define EMR_STROKEPATH                  64
+#define EMR_FLATTENPATH                 65
+#define EMR_WIDENPATH                   66
+#define EMR_SELECTCLIPPATH              67
+#define EMR_ABORTPATH                   68
+
+#define EMR_COMMENT                     70          // Contains arbitrary private data.
+// Comment Identifiers:
+#define EMR_COMMENT_EMFPLUS             0x2B464D45  // Contains embedded EMF+ records.
+#define EMR_COMMENT_EMFSPOOL            0x00000000  // Contains embedded EMFSPOOL records.
+#define EMR_COMMENT_PUBLIC              0x43494447  // Specify extensions to EMF processing.
+
+#define EMR_FILLRGN                     71
+#define EMR_FRAMERGN                    72
+#define EMR_INVERTRGN                   73
+#define EMR_PAINTRGN                    74
+#define EMR_EXTSELECTCLIPRGN            75
+#define EMR_BITBLT                      76
+#define EMR_STRETCHBLT                  77
+#define EMR_MASKBLT                     78
+#define EMR_PLGBLT                      79
+#define EMR_SETDIBITSTODEVICE           80
+#define EMR_STRETCHDIBITS               81
+#define EMR_EXTCREATEFONTINDIRECTW      82
+#define EMR_EXTTEXTOUTA                 83
+#define EMR_EXTTEXTOUTW                 84
+#define EMR_POLYBEZIER16                85
+#define EMR_POLYGON16                   86
+#define EMR_POLYLINE16                  87
+#define EMR_POLYBEZIERTO16              88
+#define EMR_POLYLINETO16                89
+#define EMR_POLYPOLYLINE16              90
+#define EMR_POLYPOLYGON16               91
+#define EMR_POLYDRAW16                  92
+#define EMR_CREATEMONOBRUSH             93
+#define EMR_CREATEDIBPATTERNBRUSHPT     94
+#define EMR_EXTCREATEPEN                95
+#define EMR_POLYTEXTOUTA                96
+#define EMR_POLYTEXTOUTW                97
+
+// WINDOWS VERSION >= 0x400
+#define EMR_SETICMMODE                  98
+#define EMR_CREATECOLORSPACE            99
+#define EMR_SETCOLORSPACE              100
+#define EMR_DELETECOLORSPACE           101
+#define EMR_GLSRECORD                  102
+#define EMR_GLSBOUNDEDRECORD           103
+#define EMR_PIXELFORMAT                104
+
+// WINDOWS VERSION >= 0x500
+#define EMR_DRAWESCAPE                 105
+#define EMR_EXTESCAPE                  106
+#define EMR_STARTDOC                   107
+#define EMR_SMALLTEXTOUT               108
+#define EMR_FORCEUFIMAPPING            109
+#define EMR_NAMEDESCAPE                110
+#define EMR_COLORCORRECTPALETTE        111
+#define EMR_SETICMPROFILEA             112
+#define EMR_SETICMPROFILEW             113
+#define EMR_ALPHABLEND                 114
+#define EMR_ALPHADIBBLEND              115
+#define EMR_TRANSPARENTBLT             116
+#define EMR_TRANSPARENTDIB             117
+#define EMR_GRADIENTFILL               118
+#define EMR_SETLINKEDUFIS              119
+#define EMR_SETTEXTJUSTIFICATION       120
+
+#define PDF_SIGNATURE 0x50444620 // "PDF "
+
+/* [MS-EMF] - v20210625 - page 28 */
+constexpr sal_Int32 ARCDIRECTION_CLOCKWISE = 0x00000002;
+
+namespace
+{
+
+const char *
+record_type_name(sal_uInt32 nRecType)
+{
+#ifndef SAL_LOG_INFO
+    (void) nRecType;
+    return "";
+#else
+    switch( nRecType )
+    {
+    case EMR_HEADER: return "HEADER";
+    case EMR_POLYBEZIER: return "POLYBEZIER";
+    case EMR_POLYGON: return "POLYGON";
+    case EMR_POLYLINE: return "POLYLINE";
+    case EMR_POLYBEZIERTO: return "POLYBEZIERTO";
+    case EMR_POLYLINETO: return "POLYLINETO";
+    case EMR_POLYPOLYLINE: return "POLYPOLYLINE";
+    case EMR_POLYPOLYGON: return "POLYPOLYGON";
+    case EMR_SETWINDOWEXTEX: return "SETWINDOWEXTEX";
+    case EMR_SETWINDOWORGEX: return "SETWINDOWORGEX";
+    case EMR_SETVIEWPORTEXTEX: return "SETVIEWPORTEXTEX";
+    case EMR_SETVIEWPORTORGEX: return "SETVIEWPORTORGEX";
+    case EMR_SETBRUSHORGEX: return "SETBRUSHORGEX";
+    case EMR_EOF: return "EOF";
+    case EMR_SETPIXELV: return "SETPIXELV";
+    case EMR_SETMAPPERFLAGS: return "SETMAPPERFLAGS";
+    case EMR_SETMAPMODE: return "SETMAPMODE";
+    case EMR_SETBKMODE: return "SETBKMODE";
+    case EMR_SETPOLYFILLMODE: return "SETPOLYFILLMODE";
+    case EMR_SETROP2: return "SETROP2";
+    case EMR_SETSTRETCHBLTMODE: return "SETSTRETCHBLTMODE";
+    case EMR_SETTEXTALIGN: return "SETTEXTALIGN";
+    case EMR_SETCOLORADJUSTMENT: return "SETCOLORADJUSTMENT";
+    case EMR_SETTEXTCOLOR: return "SETTEXTCOLOR";
+    case EMR_SETBKCOLOR: return "SETBKCOLOR";
+    case EMR_OFFSETCLIPRGN: return "OFFSETCLIPRGN";
+    case EMR_MOVETOEX: return "MOVETOEX";
+    case EMR_SETMETARGN: return "SETMETARGN";
+    case EMR_EXCLUDECLIPRECT: return "EXCLUDECLIPRECT";
+    case EMR_INTERSECTCLIPRECT: return "INTERSECTCLIPRECT";
+    case EMR_SCALEVIEWPORTEXTEX: return "SCALEVIEWPORTEXTEX";
+    case EMR_SCALEWINDOWEXTEX: return "SCALEWINDOWEXTEX";
+    case EMR_SAVEDC: return "SAVEDC";
+    case EMR_RESTOREDC: return "RESTOREDC";
+    case EMR_SETWORLDTRANSFORM: return "SETWORLDTRANSFORM";
+    case EMR_MODIFYWORLDTRANSFORM: return "MODIFYWORLDTRANSFORM";
+    case EMR_SELECTOBJECT: return "SELECTOBJECT";
+    case EMR_CREATEPEN: return "CREATEPEN";
+    case EMR_CREATEBRUSHINDIRECT: return "CREATEBRUSHINDIRECT";
+    case EMR_DELETEOBJECT: return "DELETEOBJECT";
+    case EMR_ANGLEARC: return "ANGLEARC";
+    case EMR_ELLIPSE: return "ELLIPSE";
+    case EMR_RECTANGLE: return "RECTANGLE";
+    case EMR_ROUNDRECT: return "ROUNDRECT";
+    case EMR_ARC: return "ARC";
+    case EMR_CHORD: return "CHORD";
+    case EMR_PIE: return "PIE";
+    case EMR_SELECTPALETTE: return "SELECTPALETTE";
+    case EMR_CREATEPALETTE: return "CREATEPALETTE";
+    case EMR_SETPALETTEENTRIES: return "SETPALETTEENTRIES";
+    case EMR_RESIZEPALETTE: return "RESIZEPALETTE";
+    case EMR_REALIZEPALETTE: return "REALIZEPALETTE";
+    case EMR_EXTFLOODFILL: return "EXTFLOODFILL";
+    case EMR_LINETO: return "LINETO";
+    case EMR_ARCTO: return "ARCTO";
+    case EMR_POLYDRAW: return "POLYDRAW";
+    case EMR_SETARCDIRECTION: return "SETARCDIRECTION";
+    case EMR_SETMITERLIMIT: return "SETMITERLIMIT";
+    case EMR_BEGINPATH: return "BEGINPATH";
+    case EMR_ENDPATH: return "ENDPATH";
+    case EMR_CLOSEFIGURE: return "CLOSEFIGURE";
+    case EMR_FILLPATH: return "FILLPATH";
+    case EMR_STROKEANDFILLPATH: return "STROKEANDFILLPATH";
+    case EMR_STROKEPATH: return "STROKEPATH";
+    case EMR_FLATTENPATH: return "FLATTENPATH";
+    case EMR_WIDENPATH: return "WIDENPATH";
+    case EMR_SELECTCLIPPATH: return "SELECTCLIPPATH";
+    case EMR_ABORTPATH: return "ABORTPATH";
+    case EMR_COMMENT: return "COMMENT";
+    case EMR_FILLRGN: return "FILLRGN";
+    case EMR_FRAMERGN: return "FRAMERGN";
+    case EMR_INVERTRGN: return "INVERTRGN";
+    case EMR_PAINTRGN: return "PAINTRGN";
+    case EMR_EXTSELECTCLIPRGN: return "EXTSELECTCLIPRGN";
+    case EMR_BITBLT: return "BITBLT";
+    case EMR_STRETCHBLT: return "STRETCHBLT";
+    case EMR_MASKBLT: return "MASKBLT";
+    case EMR_PLGBLT: return "PLGBLT";
+    case EMR_SETDIBITSTODEVICE: return "SETDIBITSTODEVICE";
+    case EMR_STRETCHDIBITS: return "STRETCHDIBITS";
+    case EMR_EXTCREATEFONTINDIRECTW: return "EXTCREATEFONTINDIRECTW";
+    case EMR_EXTTEXTOUTA: return "EXTTEXTOUTA";
+    case EMR_EXTTEXTOUTW: return "EXTTEXTOUTW";
+    case EMR_POLYBEZIER16: return "POLYBEZIER16";
+    case EMR_POLYGON16: return "POLYGON16";
+    case EMR_POLYLINE16: return "POLYLINE16";
+    case EMR_POLYBEZIERTO16: return "POLYBEZIERTO16";
+    case EMR_POLYLINETO16: return "POLYLINETO16";
+    case EMR_POLYPOLYLINE16: return "POLYPOLYLINE16";
+    case EMR_POLYPOLYGON16: return "POLYPOLYGON16";
+    case EMR_POLYDRAW16: return "POLYDRAW16";
+    case EMR_CREATEMONOBRUSH: return "CREATEMONOBRUSH";
+    case EMR_CREATEDIBPATTERNBRUSHPT: return "CREATEDIBPATTERNBRUSHPT";
+    case EMR_EXTCREATEPEN: return "EXTCREATEPEN";
+    case EMR_POLYTEXTOUTA: return "POLYTEXTOUTA";
+    case EMR_POLYTEXTOUTW: return "POLYTEXTOUTW";
+    case EMR_SETICMMODE: return "SETICMMODE";
+    case EMR_CREATECOLORSPACE: return "CREATECOLORSPACE";
+    case EMR_SETCOLORSPACE: return "SETCOLORSPACE";
+    case EMR_DELETECOLORSPACE: return "DELETECOLORSPACE";
+    case EMR_GLSRECORD: return "GLSRECORD";
+    case EMR_GLSBOUNDEDRECORD: return "GLSBOUNDEDRECORD";
+    case EMR_PIXELFORMAT: return "PIXELFORMAT";
+    case EMR_DRAWESCAPE: return "DRAWESCAPE";
+    case EMR_EXTESCAPE: return "EXTESCAPE";
+    case EMR_STARTDOC: return "STARTDOC";
+    case EMR_SMALLTEXTOUT: return "SMALLTEXTOUT";
+    case EMR_FORCEUFIMAPPING: return "FORCEUFIMAPPING";
+    case EMR_NAMEDESCAPE: return "NAMEDESCAPE";
+    case EMR_COLORCORRECTPALETTE: return "COLORCORRECTPALETTE";
+    case EMR_SETICMPROFILEA: return "SETICMPROFILEA";
+    case EMR_SETICMPROFILEW: return "SETICMPROFILEW";
+    case EMR_ALPHABLEND: return "ALPHABLEND";
+    case EMR_ALPHADIBBLEND: return "ALPHADIBBLEND";
+    case EMR_TRANSPARENTBLT: return "TRANSPARENTBLT";
+    case EMR_TRANSPARENTDIB: return "TRANSPARENTDIB";
+    case EMR_GRADIENTFILL: return "GRADIENTFILL";
+    case EMR_SETLINKEDUFIS: return "SETLINKEDUFIS";
+    case EMR_SETTEXTJUSTIFICATION: return "SETTEXTJUSTIFICATION";
+    default:
+        // Yes, return a pointer to a static buffer. This is a very
+        // local debugging output function, so no big deal.
+        static char buffer[11];
+        sprintf(buffer, "0x%08" SAL_PRIxUINT32, nRecType);
+        return buffer;
+    }
+#endif
+}
+
+struct BLENDFUNCTION
+{
+    unsigned char aBlendOperation;
+    unsigned char aBlendFlags;
+    unsigned char aSrcConstantAlpha;
+    unsigned char aAlphaFormat;
+
+    friend SvStream& operator>>(SvStream& rInStream, BLENDFUNCTION& rBlendFun);
+};
+
+SvStream& operator>>(SvStream& rInStream, BLENDFUNCTION& rBlendFun)
+{
+    rInStream.ReadUChar(rBlendFun.aBlendOperation);
+    rInStream.ReadUChar(rBlendFun.aBlendFlags);
+    rInStream.ReadUChar(rBlendFun.aSrcConstantAlpha);
+    rInStream.ReadUChar(rBlendFun.aAlphaFormat);
+    return rInStream;
+}
+
+bool ImplReadRegion( basegfx::B2DPolyPolygon& rPolyPoly, SvStream& rStream, sal_uInt32 nLen )
+{
+    if (nLen < 32) // 32 bytes - Size of RegionDataHeader
+        return false;
+
+    sal_uInt32 nHdSize, nType, nCountRects, nRgnSize;
+    rStream.ReadUInt32(nHdSize);
+    rStream.ReadUInt32(nType);
+    rStream.ReadUInt32(nCountRects);
+    rStream.ReadUInt32(nRgnSize);
+
+    sal_Int32 nLeft, nTop, nRight, nBottom;
+    //bounds of the region
+    rStream.ReadInt32(nLeft);
+    rStream.ReadInt32(nTop);
+    rStream.ReadInt32(nRight);
+    rStream.ReadInt32(nBottom);
+
+    if (!rStream.good() || nCountRects == 0 || nType != emfio::RDH_RECTANGLES)
+        return false;
+
+    SAL_INFO("emfio", "\t\tBounds Left: " << nLeft << ", top: " << nTop << ", right: " << nRight << ", bottom: " << nBottom);
+
+    nLen -= 32;
+
+    sal_uInt32 nSize;
+    if (o3tl::checked_multiply<sal_uInt32>(nCountRects, 16, nSize))
+        return false;
+    if (nLen < nSize)
+        return false;
+
+    for (sal_uInt32 i = 0; i < nCountRects; ++i)
+    {
+        rStream.ReadInt32(nLeft);
+        rStream.ReadInt32(nTop);
+        rStream.ReadInt32(nRight);
+        rStream.ReadInt32(nBottom);
+        rPolyPoly.append( basegfx::utils::createPolygonFromRect( ::basegfx::B2DRectangle( nLeft, nTop, nRight, nBottom ) ) );
+        SAL_INFO("emfio", "\t\t" << i << " Left: " << nLeft << ", top: " << nTop << ", right: " << nRight << ", bottom: " << nBottom);
+    }
+    if (!utl::ConfigManager::IsFuzzing())
+    {
+        rPolyPoly = basegfx::utils::solveCrossovers(rPolyPoly);
+        rPolyPoly = basegfx::utils::stripNeutralPolygons(rPolyPoly);
+        rPolyPoly = basegfx::utils::stripDispensablePolygons(rPolyPoly);
+    }
+    return true;
+}
+
+} // anonymous namespace
+
+namespace emfio
+{
+    EmfReader::EmfReader(SvStream& rStream,GDIMetaFile& rGDIMetaFile)
+        : MtfTools(rGDIMetaFile, rStream)
+        , mnRecordCount(0)
+        , mbRecordPath(false)
+        , mbEMFPlus(false)
+        , mbEMFPlusDualMode(false)
+    {
+    }
+
+    EmfReader::~EmfReader()
+    {
+    }
+
+    const sal_uInt32 EMR_COMMENT_BEGINGROUP = 0x00000002;
+    const sal_uInt32 EMR_COMMENT_ENDGROUP = 0x00000003;
+    const sal_uInt32 EMR_COMMENT_MULTIFORMATS = 0x40000004;
+    const sal_uInt32 EMR_COMMENT_WINDOWS_METAFILE = 0x80000001;
+
+    void EmfReader::ReadGDIComment(sal_uInt32 nCommentId)
+    {
+        sal_uInt32 nPublicCommentIdentifier(0);
+        mpInputStream->ReadUInt32(nPublicCommentIdentifier);
+
+        SAL_INFO("emfio", "\t\tEMR_COMMENT_PUBLIC, id: 0x" << std::hex << nCommentId << std::dec);
+        switch (nPublicCommentIdentifier)
+        {
+            case EMR_COMMENT_BEGINGROUP:
+            {
+                SAL_INFO("emfio", "\t\t\tEMR_COMMENT_BEGINGROUP");
+                sal_uInt32 left, top, right, bottom;
+                mpInputStream->ReadUInt32(left).ReadUInt32(top).ReadUInt32(right).ReadUInt32(bottom);
+
+                SAL_INFO("emfio", "\t\t\t\tBounding rect");
+                SAL_INFO("emfio", "\t\t\t\t\tLeft: " << left);
+                SAL_INFO("emfio", "\t\t\t\t\tTop: " << top);
+                SAL_INFO("emfio", "\t\t\t\t\tRight: " << right);
+                SAL_INFO("emfio", "\t\t\t\t\tBottom: " << bottom);
+
+                sal_uInt32 nDescChars(0);
+                mpInputStream->ReadUInt32(nDescChars);
+
+                OUString aDesc;
+                for (sal_uInt32 i=0; i < nDescChars; i++)
+                {
+                    sal_uInt16 cChar(0);
+                    mpInputStream->ReadUInt16(cChar);
+                    if (cChar == '\0')
+                        break;
+
+                    sal_Unicode cUniChar = static_cast<sal_Unicode>(cChar);
+                    aDesc = aDesc + OUStringChar(cUniChar);
+                }
+
+                SAL_INFO("emfio", "\t\tDescription: " << aDesc);
+            }
+            break;
+
+            case EMR_COMMENT_ENDGROUP:
+                SAL_INFO("emfio", "\t\t\tEMR_COMMENT_ENDGROUP");
+                break;
+
+            case EMR_COMMENT_MULTIFORMATS:
+                ReadMultiformatsComment();
+                break;
+
+            case EMR_COMMENT_WINDOWS_METAFILE:
+                SAL_WARN("emfio", "\t\tEMR_COMMENT_WINDOWS_METAFILE not implemented");
+                break;
+
+            default:
+                SAL_WARN("emfio", "\t\tEMR_COMMENT_PUBLIC not implemented, id: 0x" << std::hex << nCommentId << std::dec);
+                break;
+        }
+    }
+
+    void EmfReader::ReadMultiformatsComment()
+    {
+        tools::Rectangle aOutputRect = EmfReader::ReadRectangle();
+
+        sal_uInt32 nCountFormats(0);
+        mpInputStream->ReadUInt32(nCountFormats);
+        if (nCountFormats < 1)
+        {
+            return;
+        }
+
+        // Read the first EmrFormat.
+        sal_uInt32 nSignature(0);
+        mpInputStream->ReadUInt32(nSignature);
+        if (nSignature != PDF_SIGNATURE)
+        {
+            return;
+        }
+
+        sal_uInt32 nVersion(0);
+        mpInputStream->ReadUInt32(nVersion);
+        if (nVersion != 1)
+        {
+            return;
+        }
+
+        sal_uInt32 nSizeData(0);
+        mpInputStream->ReadUInt32(nSizeData);
+        if (!nSizeData || nSizeData > mpInputStream->remainingSize())
+        {
+            return;
+        }
+
+        sal_uInt32 nOffData(0);
+        mpInputStream->ReadUInt32(nOffData);
+        if (!nOffData)
+        {
+            return;
+        }
+
+        std::vector<char> aPdfData(nSizeData);
+        mpInputStream->ReadBytes(aPdfData.data(), aPdfData.size());
+        if (!mpInputStream->good())
+        {
+            return;
+        }
+
+        SvMemoryStream aPdfStream;
+        aPdfStream.WriteBytes(aPdfData.data(), aPdfData.size());
+        aPdfStream.Seek(0);
+        Graphic aGraphic;
+        if (!vcl::ImportPDF(aPdfStream, aGraphic))
+        {
+            return;
+        }
+
+        // aGraphic will be the only output of the EMF parser, so its size hint can be the same as
+        // ours.
+        aGraphic.getVectorGraphicData()->setSizeHint(maSizeHint);
+
+        maBmpSaveList.emplace_back(
+            aGraphic.GetBitmapEx(), aOutputRect, SRCCOPY, /*bForceAlpha=*/true);
+        const std::shared_ptr<VectorGraphicData> pVectorGraphicData
+            = aGraphic.getVectorGraphicData();
+        if (!pVectorGraphicData)
+        {
+            return;
+        }
+
+        if (pVectorGraphicData->getType() != VectorGraphicDataType::Pdf)
+        {
+            return;
+        }
+
+        mbReadOtherGraphicFormat = true;
+    }
+
+    void EmfReader::ReadEMFPlusComment(sal_uInt32 length, bool& bHaveDC)
+    {
+        if (!mbEMFPlus)
+        {
+            PassEMFPlusHeaderInfo();
+
+    #if OSL_DEBUG_LEVEL > 1
+            // debug code - write the stream to debug file /tmp/emf-stream.emf
+            sal_uInt64 const pos = mpInputStream->Tell();
+            mpInputStream->Seek(0);
+            SvFileStream file( OUString( "/tmp/emf-stream.emf" ), StreamMode::WRITE | StreamMode::TRUNC );
+
+            mpInputStream->WriteStream(file);
+            file.Flush();
+            file.Close();
+
+            mpInputStream->Seek( pos );
+    #endif
+
+        }
+
+        mbEMFPlus = true;
+        sal_uInt64 const pos = mpInputStream->Tell();
+        auto buffer = std::make_unique<char[]>( length );
+        PassEMFPlus( buffer.get(), mpInputStream->ReadBytes(buffer.get(), length) );
+        buffer.reset();
+        mpInputStream->Seek( pos );
+
+        bHaveDC = false;
+
+        // skip in SeekRel if impossibly unavailable
+        sal_uInt32 nRemainder = length;
+
+        const size_t nRequiredHeaderSize = 12;
+        while (nRemainder >= nRequiredHeaderSize)
+        {
+            sal_uInt16 type(0), flags(0);
+            sal_uInt32 size(0), dataSize(0);
+
+            mpInputStream->ReadUInt16( type ).ReadUInt16( flags ).ReadUInt32( size ).ReadUInt32( dataSize );
+            nRemainder -= nRequiredHeaderSize;
+
+            SAL_INFO("emfio", "\t\tEMF+ record type: 0x" << std::hex << type << std::dec);
+
+            // Get Device Context
+            // TODO We should use  EmfPlusRecordType::GetDC instead
+            if( type == 0x4004 )
+            {
+                bHaveDC = true;
+                SAL_INFO("emfio", "\t\tEMF+ lock DC (device context)");
+            }
+
+            // look for the "dual mode" in header
+            // it indicates that either EMF or EMF+ records should be processed
+            // 0x4001    = EMF+ header
+            // flags & 1 = dual mode active
+            if ( type == 0x4001 && flags & 1 )
+            {
+                mbEMFPlusDualMode = true;
+                SAL_INFO ("emfio", "\t\tEMF+ dual mode detected");
+            }
+
+            // Get the length of the remaining data of this record based
+            // on the alleged size
+            sal_uInt32 nRemainingRecordData = size >= nRequiredHeaderSize ?
+                size-nRequiredHeaderSize : 0;
+            // clip to available size
+            nRemainingRecordData = std::min(nRemainingRecordData, nRemainder);
+            mpInputStream->SeekRel(nRemainingRecordData);
+            nRemainder -= nRemainingRecordData;
+        }
+        mpInputStream->SeekRel(nRemainder);
+    }
+
+    // these are referenced from inside the templates
+    static SvStream& operator >> (SvStream& rStream, sal_Int16 &n)
+    {
+        return rStream.ReadInt16(n);
+    }
+
+    static SvStream& operator >> (SvStream& rStream, sal_Int32 &n)
+    {
+        return rStream.ReadInt32(n);
+    }
+
+    /**
+     * Reads polygons from the stream.
+     * The \<class T> parameter is for the type of the points (sal_uInt32 or sal_uInt16).
+     * skipFirst: if the first point read is the 0th point or the 1st point in the array.
+     * */
+    template <class T>
+    tools::Polygon EmfReader::ReadPolygonWithSkip(const bool skipFirst, sal_uInt32 nNextPos)
+    {
+        sal_uInt32 nPoints(0), nStartIndex(0);
+        mpInputStream->SeekRel( 16 );
+        mpInputStream->ReadUInt32( nPoints );
+        if (skipFirst)
+        {
+            nPoints ++;
+            nStartIndex ++;
+        }
+
+        return ReadPolygon<T>(nStartIndex, nPoints, nNextPos);
+    }
+
+    /**
+     * Reads polygons from the stream.
+     * The \<class T> parameter is for the type of the points
+     * nStartIndex: which is the starting index in the polygon of the first point read
+     * nPoints: number of points
+     * mpInputStream: the stream containing the polygons
+     * */
+    template <class T>
+    tools::Polygon EmfReader::ReadPolygon(sal_uInt32 nStartIndex, sal_uInt32 nPoints, sal_uInt32 nNextPos)
+    {
+        SAL_INFO ("emfio", "\t\tPolygon:");
+
+        bool bRecordOk = nPoints <= SAL_MAX_UINT16;
+        SAL_WARN_IF(!bRecordOk, "emfio", "polygon record has more polygons than we can handle");
+        if (!bRecordOk || !nPoints)
+            return tools::Polygon();
+
+        auto nRemainingSize = std::min(nNextPos - mpInputStream->Tell(), mpInputStream->remainingSize());
+        auto nMaxPossiblePoints = nRemainingSize / (sizeof(T) * 2);
+        auto nPointCount = nPoints - nStartIndex;
+        if (nPointCount > nMaxPossiblePoints)
+        {
+            SAL_WARN("emfio", "polygon claims more points than record can provide, truncating");
+            nPoints = nMaxPossiblePoints + nStartIndex;
+        }
+
+        tools::Polygon aPolygon(nPoints);
+        for (sal_uInt32 i = nStartIndex ; i < nPoints && mpInputStream->good(); i++ )
+        {
+            T nX, nY;
+            *mpInputStream >> nX >> nY;
+
+            SAL_INFO("emfio", "\t\t\tPoint " << i << " of " << nPoints - 1 << ": " << nX << ", " << nY);
+
+            if (!mpInputStream->good())
+            {
+                SAL_WARN("emfio", "short read on polygon, truncating");
+                aPolygon.SetSize(i);
+                break;
+            }
+            aPolygon[ i ] = Point( nX, nY );
+        }
+
+        return aPolygon;
+    }
+
+    /**
+     * Reads a polyline from the WMF file and draws it
+     * The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
+     * */
+    template <class T>
+    void EmfReader::ReadAndDrawPolyLine(sal_uInt32 nNextPos)
+    {
+        SAL_INFO("emfio", "\t\tPolyline: ");
+
+        mpInputStream->SeekRel( 0x10 ); // TODO Skipping Bounds. A 128-bit WMF RectL object (specifies the bounding rectangle in device units.)
+
+        sal_uInt32 nNumberOfPolylines = 0;
+        mpInputStream->ReadUInt32( nNumberOfPolylines );
+        SAL_INFO("emfio", "\t\t\tPolylines: " << nNumberOfPolylines);
+
+        sal_uInt32 nCount = 0;
+        mpInputStream->ReadUInt32( nCount ); // total number of points in all polylines
+        SAL_INFO("emfio", "\t\t\tPoints: " << nCount);
+
+        const auto nEndPos = std::min(nNextPos, mnEndPos);
+        if (mpInputStream->Tell() >= nEndPos)
+            return;
+
+        // taking the amount of points of each polygon, retrieving the total number of points
+        if ( !(mpInputStream->good() &&
+             ( nNumberOfPolylines < SAL_MAX_UINT32 / sizeof( sal_uInt16 ) ) &&
+             ( nNumberOfPolylines * sizeof( sal_uInt16 ) ) <= ( nEndPos - mpInputStream->Tell() ))
+           )
+            return;
+
+        std::unique_ptr< sal_uInt32[] > pnPolylinePointCount( new sal_uInt32[ nNumberOfPolylines ] );
+        for ( sal_uInt32 i = 0; i < nNumberOfPolylines && mpInputStream->good(); i++ )
+        {
+            sal_uInt32 nPoints;
+            mpInputStream->ReadUInt32( nPoints );
+            SAL_INFO("emfio", "\t\t\tPoint " << i << " of " << nNumberOfPolylines << ": " << nPoints);
+            pnPolylinePointCount[ i ] = nPoints;
+        }
+
+        // Get polyline points:
+        for ( sal_uInt32 i = 0; ( i < nNumberOfPolylines ) && mpInputStream->good(); i++ )
+        {
+            tools::Polygon aPolygon = ReadPolygon<T>(0, pnPolylinePointCount[i], nNextPos);
+            DrawPolyLine(std::move(aPolygon), false, mbRecordPath);
+        }
+    }
+
+    /**
+     * Reads a poly polygon from the WMF file and draws it.
+     * The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
+     * */
+    template <class T>
+    void EmfReader::ReadAndDrawPolyPolygon(sal_uInt32 nNextPos)
+    {
+        SAL_INFO("emfio", "\t\tPolygon: ");
+        mpInputStream->SeekRel( 0x10 ); // RectL bounds
+
+        sal_uInt32 nPoly(0), nGesPoints(0), nReadPoints(0);
+        // Number of polygons
+        mpInputStream->ReadUInt32( nPoly ).ReadUInt32( nGesPoints );
+        SAL_INFO("emfio", "\t\t\tPolygons: " << nPoly);
+        SAL_INFO("emfio", "\t\t\tPoints: " << nGesPoints);
+
+        const auto nEndPos = std::min(nNextPos, mnEndPos);
+        if (mpInputStream->Tell() >= nEndPos)
+            return;
+        if (!mpInputStream->good())
+            return;
+        //check against numeric overflowing
+        if (nGesPoints >= SAL_MAX_UINT32 / sizeof(Point))
+            return;
+        if (nPoly >= SAL_MAX_UINT32 / sizeof(sal_uInt16))
+            return;
+        if (nPoly * sizeof(sal_uInt16) > nEndPos - mpInputStream->Tell())
+            return;
+
+        // Get number of points in each polygon
+        std::vector<sal_uInt16> aPoints(nPoly);
+        for (sal_uInt32 i = 0; i < nPoly && mpInputStream->good(); ++i)
+        {
+            sal_uInt32 nPoints(0);
+            mpInputStream->ReadUInt32( nPoints );
+
+            SAL_INFO("emfio", "\t\t\t\tPolygon " << i << " points: " << nPoints);
+
+            aPoints[i] = static_cast<sal_uInt16>(nPoints);
+        }
+
+        if ( mpInputStream->good() && ( nGesPoints * (sizeof(T)+sizeof(T)) ) <= ( nEndPos - mpInputStream->Tell() ) )
+        {
+            // Get polygon points
+            tools::PolyPolygon aPolyPoly(nPoly);
+            for (sal_uInt32 i = 0; i < nPoly && mpInputStream->good(); ++i)
+            {
+                const sal_uInt16 nPointCount(aPoints[i]);
+                std::vector<Point> aPtAry(nPointCount);
+                for (sal_uInt16 j = 0; j < nPointCount && mpInputStream->good(); ++j)
+                {
+                    T nX(0), nY(0);
+                    *mpInputStream >> nX >> nY;
+                    aPtAry[j] = Point( nX, nY );
+                    ++nReadPoints;
+                }
+
+                aPolyPoly.Insert(tools::Polygon(aPtAry.size(), aPtAry.data()));
+            }
+
+            DrawPolyPolygon(aPolyPoly, mbRecordPath);
+        }
+
+        OSL_ENSURE(nReadPoints == nGesPoints, "The number Points processed from EMR_POLYPOLYGON is unequal imported number (!)");
+    }
+
+    bool EmfReader::ReadEnhWMF()
+    {
+        sal_uInt32  nStretchBltMode = 0;
+        sal_uInt32  nNextPos(0),
+                    nW(0), nH(0), nColor(0), nIndex(0),
+                    nDat32(0), nNom1(0), nDen1(0), nNom2(0), nDen2(0);
+        sal_Int32   nX32(0), nY32(0), nx32(0), ny32(0);
+
+        bool    bStatus = ReadHeader();
+        bool    bHaveDC = false;
+
+        OUString aEMFPlusDisable;
+        rtl::Bootstrap::get("EMF_PLUS_DISABLE", aEMFPlusDisable);
+        bool bEnableEMFPlus = aEMFPlusDisable.isEmpty();
+        if (!mbEnableEMFPlus)
+        {
+            // EMF+ is enabled if neither the bootstrap variable, not the member variable disables
+            // it.
+            bEnableEMFPlus = mbEnableEMFPlus;
+        }
+
+        SAL_INFO("emfio", "EMF+ reading is " << (bEnableEMFPlus ? "enabled" : "disabled"));
+
+        while (bStatus && mnRecordCount-- && mpInputStream->good() && !mbReadOtherGraphicFormat)
+        {
+            sal_uInt32  nRecType(0), nRecSize(0);
+            mpInputStream->ReadUInt32(nRecType).ReadUInt32(nRecSize);
+
+            if ( !mpInputStream->good() || ( nRecSize < 8 ) || ( nRecSize & 3 ) )     // Parameters are always divisible by 4
+            {
+                bStatus = false;
+                break;
+            }
+
+            auto nCurPos = mpInputStream->Tell();
+
+            if (mnEndPos < nCurPos - 8)
+            {
+                bStatus = false;
+                break;
+            }
+
+            const sal_uInt32 nMaxPossibleRecSize = mnEndPos - (nCurPos - 8);
+            if (nRecSize > nMaxPossibleRecSize)
+            {
+                bStatus = false;
+                break;
+            }
+
+            nNextPos = nCurPos + (nRecSize - 8);
+
+            if(  !maBmpSaveList.empty()
+              && ( nRecType != EMR_STRETCHBLT )
+              && ( nRecType != EMR_STRETCHDIBITS )
+              ) {
+                ResolveBitmapActions( maBmpSaveList );
+            }
+
+            bool bFlag = false;
+
+            SAL_INFO("emfio", "0x" << std::hex << (nNextPos - nRecSize) <<  "-0x" << nNextPos << " " << record_type_name(nRecType) << " size: "
+                                    << std::dec << nRecSize);
+
+            if( bEnableEMFPlus && nRecType == EMR_COMMENT )
+            {
+                sal_uInt32 length;
+
+                mpInputStream->ReadUInt32( length );
+
+                SAL_INFO("emfio", "\tGDI comment, length: " << length);
+
+                if( mpInputStream->good() && length >= 4 && length <= mpInputStream->remainingSize() ) {
+                    sal_uInt32 nCommentId;
+
+                    mpInputStream->ReadUInt32( nCommentId );
+
+                    SAL_INFO("emfio", "\t\tbegin " << static_cast<char>(nCommentId & 0xff) << static_cast<char>((nCommentId & 0xff00) >> 8) << static_cast<char>((nCommentId & 0xff0000) >> 16) << static_cast<char>((nCommentId & 0xff000000) >> 24) << " id: 0x" << std::hex << nCommentId << std::dec);
+
+                    if( nCommentId == EMR_COMMENT_EMFPLUS && nRecSize >= 12 )
+                    {
+                        // [MS-EMF] 2.3.3: DataSize includes both CommentIdentifier and CommentRecordParm fields.
+                        // We have already read 4-byte CommentIdentifier, so reduce length appropriately
+                        ReadEMFPlusComment( length-4, bHaveDC );
+                    }
+                    else if( nCommentId == EMR_COMMENT_PUBLIC && nRecSize >= 12 )
+                    {
+                        ReadGDIComment(nCommentId);
+                    }
+                    else if( nCommentId == EMR_COMMENT_EMFSPOOL && nRecSize >= 12 )
+                    {
+                        SAL_WARN("emfio", "\t\tEMFSPOOL not implemented, id: 0x" << std::hex << nCommentId << std::dec);
+                        // TODO Implement reading EMFSPOOL comment
+
+                    }
+                    else
+                    {
+                        SAL_WARN("emfio", "\t\tunknown id: 0x" << std::hex << nCommentId << std::dec);
+                    }
+                }
+            }
+            else if ( !bHaveDC && mbEMFPlusDualMode && nRecType != EMR_HEADER && nRecType != EMR_EOF )
+            {
+                // skip content (EMF record) in dual mode
+                // we process only EMR_COMMENT (see above) to access EMF+ data
+                // with 2 exceptions, according to EMF+ specification:
+                // EMR_HEADER and EMR_EOF
+                // if a device context is given (bHaveDC) process the following EMF record, too.
+            }
+            else if( !mbEMFPlus || bHaveDC || nRecType == EMR_EOF )
+            {
+                switch( nRecType )
+                {
+                    case EMR_POLYBEZIERTO :
+                        DrawPolyBezier(ReadPolygonWithSkip<sal_Int32>(true, nNextPos), true, mbRecordPath);
+                    break;
+                    case EMR_POLYBEZIER :
+                        DrawPolyBezier(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), false, mbRecordPath);
+                    break;
+
+                    case EMR_POLYGON :
+                        DrawPolygon(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), mbRecordPath);
+                    break;
+
+                    case EMR_POLYLINETO :
+                        DrawPolyLine(ReadPolygonWithSkip<sal_Int32>(true, nNextPos), true, mbRecordPath);
+                    break;
+
+                    case EMR_POLYLINE :
+                        DrawPolyLine(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), false, mbRecordPath);
+                    break;
+
+                    case EMR_POLYPOLYLINE :
+                        ReadAndDrawPolyLine<sal_Int32>(nNextPos);
+                    break;
+
+                    case EMR_POLYPOLYGON :
+                        ReadAndDrawPolyPolygon<sal_Int32>(nNextPos);
+                    break;
+
+                    case EMR_SETWINDOWEXTEX :
+                    {
+                        sal_Int32 w = 0, h = 0;
+                        mpInputStream->ReadInt32( w ).ReadInt32( h );
+                        SAL_INFO("emfio", "\t\tWidth: " << w);
+                        SAL_INFO("emfio", "\t\tHeight: " << h);
+                        SetWinExt( Size( w, h ), true);
+                    }
+                    break;
+
+                    case EMR_SETWINDOWORGEX :
+                    {
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
+                        SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
+                        SetWinOrg( Point( nX32, nY32 ), true);
+                    }
+                    break;
+
+                    case EMR_SCALEWINDOWEXTEX :
+                    {
+                        mpInputStream->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
+                        SAL_INFO("emfio", "\t\tHorizontal scale: " << nNom1 << " / " << nDen1);
+                        SAL_INFO("emfio", "\t\tVertical scale: " << nNom2 << " / " << nDen2);
+
+                        if (nDen1 != 0 && nDen2 != 0)
+                            ScaleWinExt( static_cast<double>(nNom1) / nDen1, static_cast<double>(nNom2) / nDen2 );
+                        else
+                            SAL_WARN("vcl.emf", "ignoring bogus divide by zero");
+                    }
+                    break;
+
+                    case EMR_SETVIEWPORTORGEX :
+                    {
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
+                        SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
+                        SetDevOrg( Point( nX32, nY32 ) );
+                    }
+                    break;
+
+                    case EMR_SCALEVIEWPORTEXTEX :
+                    {
+                        mpInputStream->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
+                        SAL_INFO("emfio", "\t\tHorizontal scale: " << nNom1 << " / " << nDen1);
+                        SAL_INFO("emfio", "\t\tVertical scale: " << nNom2 << " / " << nDen2);
+
+                        if (nDen1 != 0 && nDen2 != 0)
+                            ScaleDevExt( static_cast<double>(nNom1) / nDen1, static_cast<double>(nNom2) / nDen2 );
+                        else
+                            SAL_WARN("vcl.emf", "ignoring bogus divide by zero");
+                    }
+                    break;
+
+                    case EMR_SETVIEWPORTEXTEX :
+                    {
+                        sal_Int32 w = 0, h = 0;
+                        mpInputStream->ReadInt32( w ).ReadInt32( h );
+                        SAL_INFO("emfio", "\t\tWidth: " << w);
+                        SAL_INFO("emfio", "\t\tHeight: " << h);
+
+                        SetDevExt( Size( w, h ) );
+                    }
+                    break;
+
+                    case EMR_EOF :
+                        mnRecordCount = 0;
+                    break;
+
+                    case EMR_SETPIXELV :
+                    {
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
+                        SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
+                        DrawPixel( Point( nX32, nY32 ), ReadColor() );
+                    }
+                    break;
+
+                    case EMR_SETMAPMODE :
+                    {
+                        sal_uInt32 nMapMode(0);
+                        mpInputStream->ReadUInt32( nMapMode );
+                        SAL_INFO("emfio", "\t\tMapMode: 0x" << std::hex << nMapMode << std::dec);
+                        SetMapMode( static_cast<MappingMode>(nMapMode) );
+                    }
+                    break;
+
+                    case EMR_SETBKMODE :
+                    {
+                        mpInputStream->ReadUInt32( nDat32 );
+                        SAL_INFO("emfio", "\t\tBkMode: 0x" << std::hex << nDat32 << std::dec);
+                        SetBkMode( static_cast<BackgroundMode>(nDat32) );
+                    }
+                    break;
+
+                    case EMR_SETPOLYFILLMODE :
+                    break;
+
+                    case EMR_SETROP2 :
+                    {
+                        mpInputStream->ReadUInt32( nDat32 );
+                        SAL_INFO("emfio", "\t\tROP2: 0x" << std::hex << nDat32 << std::dec);
+                        SetRasterOp( static_cast<WMFRasterOp>(nDat32) );
+                    }
+                    break;
+
+                    case EMR_SETSTRETCHBLTMODE :
+                    {
+                        mpInputStream->ReadUInt32( nStretchBltMode );
+                        SAL_INFO("emfio", "\t\tStretchBltMode: 0x" << std::hex << nDat32 << std::dec);
+                    }
+                    break;
+
+                    case EMR_SETTEXTALIGN :
+                    {
+                        mpInputStream->ReadUInt32( nDat32 );
+                        SAL_INFO("emfio", "\t\tTextAlign: 0x" << std::hex << nDat32 << std::dec);
+                        SetTextAlign( nDat32 );
+                    }
+                    break;
+
+                    case EMR_SETTEXTCOLOR :
+                    {
+                        SetTextColor( ReadColor() );
+                    }
+                    break;
+
+                    case EMR_SETARCDIRECTION:
+                    {
+                        mpInputStream->ReadUInt32(nIndex);
+                        SetArcDirection(nIndex == ARCDIRECTION_CLOCKWISE);
+                    }
+                    break;
+
+                    case EMR_SETBKCOLOR :
+                    {
+                        SetBkColor( ReadColor() );
+                    }
+                    break;
+
+                    case EMR_OFFSETCLIPRGN :
+                    {
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
+                        SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
+                        MoveClipRegion( Size( nX32, nY32 ) );
+                    }
+                    break;
+
+                    case EMR_MOVETOEX :
+                    {
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
+                        SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
+                        MoveTo( Point( nX32, nY32 ), mbRecordPath);
+                    }
+                    break;
+
+                    case EMR_INTERSECTCLIPRECT :
+                    {
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
+                        IntersectClipRect( ReadRectangle( nX32, nY32, nx32, ny32 ) );
+                        SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
+                        SAL_INFO("emfio", "\t\tPoint: (" << nx32 << ", " << ny32 << ")");
+                    }
+                    break;
+
+                    case EMR_SAVEDC :
+                    {
+                        Push();
+                    }
+                    break;
+
+                    case EMR_RESTOREDC :
+                    {
+                        sal_Int32 nSavedDC(0);
+                        mpInputStream->ReadInt32( nSavedDC );
+                        SAL_INFO( "emfio", "\t\t SavedDC Index: " << nSavedDC );
+                        if ( nSavedDC < 0 )
+                            Pop( nSavedDC );
+                        else
+                            Pop( -1 ); // For RestoreDC values above -1, treat as get last element
+                    }
+                    break;
+
+                    case EMR_SETWORLDTRANSFORM :
+                    {
+                        XForm aTempXForm;
+                        *mpInputStream >> aTempXForm;
+                        SetWorldTransform( aTempXForm );
+                    }
+                    break;
+
+                    case EMR_MODIFYWORLDTRANSFORM :
+                    {
+                        sal_uInt32 nMode(0);
+                        XForm aTempXForm;
+                        *mpInputStream >> aTempXForm;
+                        mpInputStream->ReadUInt32( nMode );
+                        ModifyWorldTransform( aTempXForm, static_cast<ModifyWorldTransformMode>(nMode) );
+                    }
+                    break;
+
+                    case EMR_SELECTOBJECT :
+                    {
+                        mpInputStream->ReadUInt32( nIndex );
+                        SelectObject( nIndex );
+                    }
+                    break;
+
+                    case EMR_CREATEPEN:
+                    {
+                        mpInputStream->ReadUInt32(nIndex);
+                        if ((nIndex & ENHMETA_STOCK_OBJECT) == 0)
+                        {
+                            sal_uInt32 nStyle(0);
+                            sal_Int32 nPenWidth(0), nIgnored;
+                            mpInputStream->ReadUInt32(nStyle).ReadInt32(nPenWidth).ReadInt32(nIgnored);
+                            SAL_INFO("emfio", "\t\tIndex: " << nIndex << " Style: 0x" << std::hex
+                                                            << nStyle << std::dec
+                                                            << " PenWidth: " << nPenWidth);
+                            CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>(ReadColor(), nStyle, nPenWidth));
+                        }
+                    }
+                    break;
+
+                    case EMR_EXTCREATEPEN:
+                    {
+                        mpInputStream->ReadUInt32(nIndex);
+                        if ((nIndex & ENHMETA_STOCK_OBJECT) == 0)
+                        {
+                            sal_uInt32 offBmi, cbBmi, offBits, cbBits, nStyle, nWidth, nBrushStyle, elpNumEntries;
+                            sal_Int32 elpHatch;
+                            mpInputStream->ReadUInt32(offBmi).ReadUInt32(cbBmi).ReadUInt32(offBits).ReadUInt32(cbBits);
+                            mpInputStream->ReadUInt32(nStyle).ReadUInt32(nWidth).ReadUInt32(nBrushStyle);
+
+                            SAL_INFO("emfio", "\t\tStyle: 0x" << std::hex << nStyle << std::dec);
+                            SAL_INFO("emfio", "\t\tWidth: " << nWidth);
+                            Color aColorRef = ReadColor();
+                            mpInputStream->ReadInt32(elpHatch).ReadUInt32(elpNumEntries);
+
+                            if (!mpInputStream->good())
+                                bStatus = false;
+                            else
+                                CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>(aColorRef, nStyle, nWidth));
+                        }
+                    }
+                    break;
+
+                    case EMR_CREATEBRUSHINDIRECT :
+                    {
+                        mpInputStream->ReadUInt32( nIndex );
+                        if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+                        {
+                            sal_uInt32  nStyle;
+                            mpInputStream->ReadUInt32( nStyle );
+                            BrushStyle eStyle = static_cast<BrushStyle>(nStyle);
+                            CreateObjectIndexed(nIndex, std::make_unique<WinMtfFillStyle>( ReadColor(), ( eStyle == BrushStyle::BS_HOLLOW ) ));
+                        }
+                    }
+                    break;
+
+                    case EMR_DELETEOBJECT :
+                    {
+                        mpInputStream->ReadUInt32( nIndex );
+                        if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+                            DeleteObject( nIndex );
+                    }
+                    break;
+
+                    case EMR_ELLIPSE :
+                    {
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
+                        SAL_INFO("emfio", "\t\t Rectangle, left: " << nX32 << ", top: " << nY32 << ", right: " << nx32 << ", bottom: " << ny32);
+
+                        sal_Int32 w(0), h(0);
+                        if (o3tl::checked_sub(nx32, nX32, w) || o3tl::checked_sub(ny32, nY32, h))
+                            SAL_WARN("emfio", "broken ellipse");
+                        else
+                        {
+                            tools::Long dw = w / 2;
+                            tools::Long dh = h / 2;
+                            Point aCenter( nX32 + dw, nY32 + dh );
+                            tools::Polygon aPoly( aCenter, dw, dh );
+                            DrawPolygon( std::move(aPoly), mbRecordPath );
+                        }
+                    }
+                    break;
+
+                    case EMR_RECTANGLE :
+                    {
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
+                        SAL_INFO("emfio", "\t\t Rectangle, left: " << nX32 << ", top: " << nY32 << ", right: " << nx32 << ", bottom: " << ny32);
+                        Point aPoints[] { Point(nX32, nY32),
+                                          Point(nx32, nY32),
+                                          Point(nx32, ny32),
+                                          Point(nX32, ny32) };
+                        tools::Polygon aPoly(4, aPoints);
+                        aPoly.Optimize( PolyOptimizeFlags::CLOSE );
+                        DrawPolygon( std::move(aPoly), mbRecordPath );
+                    }
+                    break;
+
+                    case EMR_ROUNDRECT :
+                    {
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nW ).ReadUInt32( nH );
+                        tools::Polygon aRoundRectPoly( ReadRectangle( nX32, nY32, nx32, ny32 ), nW, nH );
+                        DrawPolygon( std::move(aRoundRectPoly), mbRecordPath );
+                    }
+                    break;
+
+                    case EMR_ARC :
+                    case EMR_ARCTO :
+                    case EMR_CHORD :
+                    {
+                        sal_Int32 nStartX, nStartY, nEndX, nEndY;
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadInt32( nStartX ).ReadInt32( nStartY ).ReadInt32( nEndX ).ReadInt32( nEndY );
+                        if (!mpInputStream->good())
+                            bStatus = false;
+                        else
+                        {
+                            SAL_INFO( "emfio", "\t\t Bounds: " << nX32 << ":" << nY32 << ", " << nx32 << ":" << ny32 << ", Start: " << nStartX << ":" << nStartY << ", End: " << nEndX << ":" << nEndY );
+                            tools::Polygon aPoly(ReadRectangle(nX32, nY32, nx32, ny32), Point(nStartX, nStartY), Point(nEndX, nEndY), PolyStyle::Arc, IsArcDirectionClockWise());
+
+                            if ( nRecType == EMR_CHORD )
+                                DrawPolygon( std::move(aPoly), mbRecordPath );
+                            else
+                                DrawPolyLine( std::move(aPoly), nRecType == EMR_ARCTO, mbRecordPath );
+                        }
+                    }
+                    break;
+
+                    case EMR_PIE :
+                    {
+                        sal_Int32 nStartX, nStartY, nEndX, nEndY;
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadInt32( nStartX ).ReadInt32( nStartY ).ReadInt32( nEndX ).ReadInt32( nEndY );
+                        if (!mpInputStream->good())
+                            bStatus = false;
+                        else
+                        {
+                            tools::Polygon aPoly(ReadRectangle(nX32, nY32, nx32, ny32), Point(nStartX, nStartY), Point(nEndX, nEndY), PolyStyle::Pie, IsArcDirectionClockWise());
+                            DrawPolygon( std::move(aPoly), mbRecordPath );
+                        }
+                    }
+                    break;
+
+                    case EMR_LINETO :
+                    {
+                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
+                        LineTo( Point( nX32, nY32 ), mbRecordPath);
+                    }
+                    break;
+
+                    case EMR_BEGINPATH :
+                    {
+                        ClearPath();
+                        mbRecordPath = true;
+                    }
+                    break;
+
+                    case EMR_ABORTPATH :
+                        ClearPath();
+                        [[fallthrough]];
+                    case EMR_ENDPATH :
+                        mbRecordPath = false;
+                    break;
+
+                    case EMR_CLOSEFIGURE :
+                        ClosePath();
+                    break;
+
+                    case EMR_FILLPATH :
+                        StrokeAndFillPath( false, true );
+                    break;
+
+                    case EMR_STROKEANDFILLPATH :
+                        StrokeAndFillPath( true, true );
+                    break;
+
+                    case EMR_STROKEPATH :
+                        StrokeAndFillPath( true, false );
+                    break;
+
+                    case EMR_SELECTCLIPPATH :
+                    {
+                        sal_Int32 nClippingMode(0);
+                        mpInputStream->ReadInt32(nClippingMode);
+                        SetClipPath(GetPathObj(), static_cast<RegionMode>(nClippingMode), true);
+                    }
+                    break;
+
+                    case EMR_EXTSELECTCLIPRGN :
+                    {
+                        sal_uInt32 nRemainingRecSize = nRecSize - 8;
+                        if (nRemainingRecSize < 8)
+                            bStatus = false;
+                        else
+                        {
+                            sal_Int32 nClippingMode(0), cbRgnData(0);
+                            mpInputStream->ReadInt32(cbRgnData);
+                            mpInputStream->ReadInt32(nClippingMode);
+                            nRemainingRecSize -= 8;
+
+                            // This record's region data should be ignored if mode
+                            // is RGN_COPY - see EMF spec section 2.3.2.2
+                            if (static_cast<RegionMode>(nClippingMode) == RegionMode::RGN_COPY)
+                            {
+                                SetDefaultClipPath();
+                            }
+                            else
+                            {
+                                basegfx::B2DPolyPolygon aPolyPoly;
+                                if (cbRgnData)
+                                    ImplReadRegion(aPolyPoly, *mpInputStream, nRemainingRecSize);
+                                const tools::PolyPolygon aPolyPolygon(aPolyPoly);
+                                SetClipPath(aPolyPolygon, static_cast<RegionMode>(nClippingMode), false);
+                            }
+                        }
+                    }
+                    break;
+
+                    case EMR_ALPHABLEND:
+                    {
+                        sal_Int32 xDest(0), yDest(0), cxDest(0), cyDest(0);
+
+                        BLENDFUNCTION aFunc;
+                        sal_Int32 xSrc(0), ySrc(0), cxSrc(0), cySrc(0);
+                        XForm xformSrc;
+                        sal_uInt32 BkColorSrc(0), iUsageSrc(0), offBmiSrc(0);
+                        sal_uInt32 cbBmiSrc(0), offBitsSrc(0), cbBitsSrc(0);
+
+                        sal_uInt32   nStart = mpInputStream->Tell() - 8;
+                        mpInputStream->SeekRel( 0x10 );
+
+                        mpInputStream->ReadInt32( xDest ).ReadInt32( yDest ).ReadInt32( cxDest ).ReadInt32( cyDest );
+                        *mpInputStream >> aFunc;
+                        mpInputStream->ReadInt32( xSrc ).ReadInt32( ySrc );
+                        *mpInputStream >> xformSrc;
+                        mpInputStream->ReadUInt32( BkColorSrc ).ReadUInt32( iUsageSrc ).ReadUInt32( offBmiSrc ).ReadUInt32( cbBmiSrc )
+                                   .ReadUInt32( offBitsSrc ).ReadUInt32( cbBitsSrc ).ReadInt32( cxSrc ).ReadInt32( cySrc ) ;
+
+                        if ( !mpInputStream->good() ||
+                             (cbBitsSrc > (SAL_MAX_UINT32 - 14)) ||
+                             ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc) ||
+                             cxDest == SAL_MAX_INT32 || cyDest == SAL_MAX_INT32 )
+                        {
+                            bStatus = false;
+                        }
+                        else
+                        {
+                            tools::Rectangle aRect(Point(xDest, yDest), Size(cxDest + 1, cyDest + 1));
+
+                            const sal_uInt32 nSourceSize = cbBmiSrc + cbBitsSrc + 14;
+                            bool bSafeRead = nSourceSize <= (mnEndPos - mnStartPos);
+                            sal_uInt32 nDeltaToDIB5HeaderSize(0);
+                            const bool bReadAlpha(0x01 == aFunc.aAlphaFormat);
+                            if (bSafeRead && bReadAlpha)
+                            {
+                                // we need to read alpha channel data if AlphaFormat of BLENDFUNCTION is
+                                // AC_SRC_ALPHA (==0x01). To read it, create a temp DIB-File which is ready
+                                // for DIB-5 format
+                                const sal_uInt32 nHeaderSize = getDIBV5HeaderSize();
+                                if (cbBmiSrc > nHeaderSize)
+                                    bSafeRead = false;
+                                else
+                                    nDeltaToDIB5HeaderSize = nHeaderSize - cbBmiSrc;
+                            }
+                            if (bSafeRead)
+                            {
+                                const sal_uInt32 nTargetSize(cbBmiSrc + nDeltaToDIB5HeaderSize + cbBitsSrc + 14);
+                                char* pBuf = new char[ nTargetSize ];
+                                SvMemoryStream aTmp( pBuf, nTargetSize, StreamMode::READ | StreamMode::WRITE );
+
+                                aTmp.ObjectOwnsMemory( true );
+
+                                // write BM-Header (14 bytes)
+                                aTmp.WriteUChar( 'B' )
+                                    .WriteUChar( 'M' )
+                                    .WriteUInt32( cbBitsSrc )
+                                    .WriteUInt16( 0 )
+                                    .WriteUInt16( 0 )
+                                    .WriteUInt32( cbBmiSrc + nDeltaToDIB5HeaderSize + 14 );
+
+                                // copy DIBInfoHeader from source (cbBmiSrc bytes)
+                                mpInputStream->Seek( nStart + offBmiSrc );
+                                char* pWritePos = pBuf + 14;
+                                auto nRead = mpInputStream->ReadBytes(pWritePos, cbBmiSrc);
+                                if (nRead != cbBmiSrc)
+                                {
+                                    // zero remainder if short read
+                                    memset(pWritePos + nRead, 0, cbBmiSrc - nRead);
+                                }
+
+                                if (bReadAlpha)
+                                {
+                                    // need to add values for all stuff that DIBV5Header is bigger
+                                    // than DIBInfoHeader, all values are correctly initialized to zero,
+                                    // so we can use memset here
+                                    memset(pBuf + cbBmiSrc + 14, 0, nDeltaToDIB5HeaderSize);
+                                }
+
+                                // copy bitmap data from source (offBitsSrc bytes)
+                                mpInputStream->Seek( nStart + offBitsSrc );
+                                pWritePos = pBuf + 14 + nDeltaToDIB5HeaderSize + cbBmiSrc;
+                                nRead = mpInputStream->ReadBytes(pWritePos, cbBitsSrc);
+                                if (nRead != cbBitsSrc)
+                                {
+                                    // zero remainder if short read
+                                    memset(pWritePos + nRead, 0, cbBitsSrc - nRead);
+                                }
+
+                                aTmp.Seek( 0 );
+
+                                // prepare to read and fill BitmapEx
+                                BitmapEx aBitmapEx;
+
+                                if(bReadAlpha)
+                                {
+                                    Bitmap aBitmap;
+                                    AlphaMask aAlpha;
+
+                                    if(ReadDIBV5(aBitmap, aAlpha, aTmp))
+                                    {
+                                        aBitmapEx = BitmapEx(aBitmap, aAlpha);
+                                    }
+                                }
+                                else
+                                {
+                                    Bitmap aBitmap;
+
+                                    if(ReadDIB(aBitmap, aTmp, true))
+                                    {
+                                        if(0xff != aFunc.aSrcConstantAlpha)
+                                        {
+                                            // add const alpha channel
+                                            aBitmapEx = BitmapEx(
+                                                aBitmap,
+                                                AlphaMask(aBitmap.GetSizePixel(), &aFunc.aSrcConstantAlpha));
+                                        }
+                                        else
+                                        {
+                                            // just use Bitmap
+                                            aBitmapEx = BitmapEx(aBitmap);
+                                        }
+                                    }
+                                }
+
+                                if(!aBitmapEx.IsEmpty())
+                                {
+                                    // test if it is sensible to crop
+                                    if (cxSrc > 0 && cySrc > 0 && xSrc >= 0 && ySrc >= 0)
+                                    {
+                                        sal_Int32 xEndSrc;
+                                        sal_Int32 yEndSrc;
+                                        if (!o3tl::checked_add(xSrc, cxSrc, xEndSrc) && xEndSrc < aBitmapEx.GetSizePixel().Width() &&
+                                            !o3tl::checked_add(ySrc, cySrc, yEndSrc) && yEndSrc < aBitmapEx.GetSizePixel().Height())
+                                        {
+                                            const tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
+                                            aBitmapEx.Crop( aCropRect );
+                                        }
+                                    }
+
+    #ifdef DBG_UTIL
+                                    static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
+
+                                    if(bDoSaveForVisualControl)
+                                    {
+                                        SvFileStream aNew("c:\\metafile_content.png", StreamMode::WRITE|StreamMode::TRUNC);
+                                        vcl::PNGWriter aPNGWriter(aBitmapEx);
+                                        aPNGWriter.Write(aNew);
+                                    }
+    #endif
+                                    maBmpSaveList.emplace_back(aBitmapEx, aRect, SRCAND|SRCINVERT);
+                                }
+                            }
+                        }
+                    }
+                    break;
+
+                    case EMR_BITBLT :   // PASSTHROUGH INTENDED
+                    case EMR_STRETCHBLT :
+                    {
+                        sal_Int32   xDest, yDest, cxDest, cyDest, xSrc, ySrc, cxSrc, cySrc;
+                        sal_uInt32  dwRop, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc;
+                        XForm   xformSrc;
+
+                        sal_uInt32  nStart = mpInputStream->Tell() - 8;
+
+                        mpInputStream->SeekRel( 0x10 );
+                        mpInputStream->ReadInt32( xDest ).ReadInt32( yDest ).ReadInt32( cxDest ).ReadInt32( cyDest ).ReadUInt32( dwRop ).ReadInt32( xSrc ).ReadInt32( ySrc )
+                                >> xformSrc;
+                        mpInputStream->ReadUInt32( nColor ).ReadUInt32( iUsageSrc ).ReadUInt32( offBmiSrc ).ReadUInt32( cbBmiSrc )
+                                   .ReadUInt32( offBitsSrc ).ReadUInt32( cbBitsSrc );
+
+                        if ( nRecType == EMR_STRETCHBLT )
+                            mpInputStream->ReadInt32( cxSrc ).ReadInt32( cySrc );
+                        else
+                            cxSrc = cySrc = 0;
+
+                        if (!mpInputStream->good() || (cbBitsSrc > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc))
+                            bStatus = false;
+                        else
+                        {
+                            Bitmap aBitmap;
+                            tools::Rectangle aRect(Point(xDest, yDest), Size(cxDest, cyDest));
+
+                            sal_uInt32 nSize = cbBmiSrc + cbBitsSrc + 14;
+                            if ( nSize <= ( mnEndPos - mnStartPos ) )
+                            {
+                                char* pBuf = new char[ nSize ];
+                                SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
+                                aTmp.ObjectOwnsMemory( true );
+                                aTmp.WriteUChar( 'B' )
+                                    .WriteUChar( 'M' )
+                                    .WriteUInt32( cbBitsSrc )
+                                    .WriteUInt16( 0 )
+                                    .WriteUInt16( 0 )
+                                    .WriteUInt32( cbBmiSrc + 14 );
+
+                                mpInputStream->Seek( nStart + offBmiSrc );
+                                char* pWritePos = pBuf + 14;
+                                auto nRead = mpInputStream->ReadBytes(pWritePos, cbBmiSrc);
+                                if (nRead != cbBmiSrc)
+                                {
+                                    // zero remainder if short read
+                                    memset(pWritePos + nRead, 0, cbBmiSrc - nRead);
+                                }
+
+                                mpInputStream->Seek( nStart + offBitsSrc );
+                                pWritePos = pBuf + 14 + cbBmiSrc;
+                                nRead = mpInputStream->ReadBytes(pWritePos, cbBitsSrc);
+                                if (nRead != cbBitsSrc)
+                                {
+                                    // zero remainder if short read
+                                    memset(pWritePos + nRead, 0, cbBitsSrc - nRead);
+                                }
+
+                                aTmp.Seek( 0 );
+                                ReadDIB(aBitmap, aTmp, true);
+
+                                // test if it is sensible to crop
+                                if ( (cxSrc > 0) && (cySrc > 0) &&
+                                     (xSrc >= 0) && (ySrc >= 0) &&
+                                     (aBitmap.GetSizePixel().Width() >= cxSrc) &&
+                                     (xSrc <= aBitmap.GetSizePixel().Width() - cxSrc) &&
+                                     (aBitmap.GetSizePixel().Height() >= cySrc) &&
+                                     (ySrc <= aBitmap.GetSizePixel().Height() - cySrc) )
+                                {
+                                    tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
+                                    aBitmap.Crop( aCropRect );
+                                }
+
+                                maBmpSaveList.emplace_back(aBitmap, aRect, dwRop);
+                            }
+                        }
+                    }
+                    break;
+
+                    case EMR_STRETCHDIBITS :
+                    {
+                        sal_Int32   xDest, yDest, xSrc, ySrc, cxSrc, cySrc, cxDest, cyDest;
+                        sal_uInt32  offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, iUsageSrc, dwRop;
+                        sal_uInt32  nStart = mpInputStream->Tell() - 8;
+
+                        mpInputStream->SeekRel( 0x10 );
+                        mpInputStream->ReadInt32( xDest )
+                             .ReadInt32( yDest )
+                             .ReadInt32( xSrc )
+                             .ReadInt32( ySrc )
+                             .ReadInt32( cxSrc )
+                             .ReadInt32( cySrc )
+                             .ReadUInt32( offBmiSrc )
+                             .ReadUInt32( cbBmiSrc )
+                             .ReadUInt32( offBitsSrc )
+                             .ReadUInt32( cbBitsSrc )
+                             .ReadUInt32( iUsageSrc )
+                             .ReadUInt32( dwRop )
+                             .ReadInt32( cxDest )
+                             .ReadInt32( cyDest );
+
+                        if (!mpInputStream->good() ||
+                            ((SAL_MAX_UINT32 - 14) < cbBitsSrc) ||
+                            ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc))
+                        {
+                            bStatus = false;
+                        }
+                        else
+                        {
+                            Bitmap aBitmap;
+                            tools::Rectangle aRect(xDest, yDest);
+                            aRect.SaturatingSetSize(Size(cxDest, cyDest));
+
+                            sal_uInt32 nSize = cbBmiSrc + cbBitsSrc + 14;
+                            if ( nSize <= ( mnEndPos - mnStartPos ) )
+                            {
+                                char* pBuf = new char[ nSize ];
+                                SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
+                                aTmp.ObjectOwnsMemory( true );
+                                aTmp.WriteUChar( 'B' )
+                                   .WriteUChar( 'M' )
+                                   .WriteUInt32( cbBitsSrc )
+                                   .WriteUInt16( 0 )
+                                   .WriteUInt16( 0 )
+                                   .WriteUInt32( cbBmiSrc + 14 );
+
+                                mpInputStream->Seek( nStart + offBmiSrc );
+                                char* pWritePos = pBuf + 14;
+                                auto nRead = mpInputStream->ReadBytes(pWritePos, cbBmiSrc);
+                                if (nRead != cbBmiSrc)
+                                {
+                                    // zero remainder if short read
+                                    memset(pWritePos + nRead, 0, cbBmiSrc - nRead);
+                                }
+
+                                mpInputStream->Seek( nStart + offBitsSrc );
+                                pWritePos = pBuf + 14 + cbBmiSrc;
+                                nRead = mpInputStream->ReadBytes(pWritePos, cbBitsSrc);
+                                if (nRead != cbBitsSrc)
+                                {
+                                    // zero remainder if short read
+                                    memset(pWritePos + nRead, 0, cbBitsSrc - nRead);
+                                }
+
+                                aTmp.Seek( 0 );
+                                ReadDIB(aBitmap, aTmp, true);
+
+                                const tools::Long nWidthDiff = aBitmap.GetSizePixel().Width() - cxSrc;
+                                const tools::Long nHeightDiff = aBitmap.GetSizePixel().Height() - cySrc;
+
+                                // test if it is sensible to crop
+                                if ( (cxSrc > 0) && (cySrc > 0) &&
+                                     (xSrc >= 0) && (ySrc >= 0) &&
+                                     (xSrc <= nWidthDiff) && (ySrc <= nHeightDiff) )
+                                {
+                                    tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
+                                    aBitmap.Crop( aCropRect );
+                                }
+                                maBmpSaveList.emplace_back(aBitmap, aRect, dwRop);
+                            }
+                        }
+                    }
+                    break;
+
+                    case EMR_EXTCREATEFONTINDIRECTW :
+                    {
+                        mpInputStream->ReadUInt32( nIndex );
+                        if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+                        {
+                            LOGFONTW aLogFont;
+                            mpInputStream->ReadInt32( aLogFont.lfHeight )
+                                 .ReadInt32( aLogFont.lfWidth )
+                                 .ReadInt32( aLogFont.lfEscapement )
+                                 .ReadInt32( aLogFont.lfOrientation )
+                                 .ReadInt32( aLogFont.lfWeight )
+                                 .ReadUChar( aLogFont.lfItalic )
+                                 .ReadUChar( aLogFont.lfUnderline )
+                                 .ReadUChar( aLogFont.lfStrikeOut )
+                                 .ReadUChar( aLogFont.lfCharSet )
+                                 .ReadUChar( aLogFont.lfOutPrecision )
+                                 .ReadUChar( aLogFont.lfClipPrecision )
+                                 .ReadUChar( aLogFont.lfQuality )
+                                 .ReadUChar( aLogFont.lfPitchAndFamily );
+
+                            sal_Unicode lfFaceName[LF_FACESIZE+1];
+                            lfFaceName[LF_FACESIZE] = 0;
+                            for (int i = 0; i < LF_FACESIZE; ++i)
+                            {
+                                sal_uInt16 nChar(0);
+                                mpInputStream->ReadUInt16(nChar);
+                                lfFaceName[i] = nChar;
+                            }
+                            aLogFont.alfFaceName = OUString( lfFaceName );
+
+                            // #i123216# Not used in the test case of #121382# (always identity in XForm), also
+                            // no hints in ms docu if FontSize should be scaled with WT. Using with the example
+                            // from #i123216# creates errors, so removing.
+
+                            // // #i121382# Need to apply WorldTransform to FontHeight/Width; this should be completely
+                            // // changed to basegfx::B2DHomMatrix instead of 'struct XForm', but not now due to time
+                            // // constraints and dangers
+                            // const XForm& rXF = GetWorldTransform();
+                            // const basegfx::B2DHomMatrix aWT(rXF.eM11, rXF.eM21, rXF.eDx, rXF.eM12, rXF.eM22, rXF.eDy);
+                            // const basegfx::B2DVector aTransVec(aWT * basegfx::B2DVector(aLogFont.lfWidth, aLogFont.lfHeight));
+                            // aLogFont.lfWidth = aTransVec.getX();
+                            // aLogFont.lfHeight = aTransVec.getY();
+                            if (mpInputStream->good() && aLogFont.lfHeight != SAL_MIN_INT32 && aLogFont.lfWidth != SAL_MIN_INT32)
+                            {
+                                CreateObjectIndexed(nIndex, std::make_unique<WinMtfFontStyle>( aLogFont ));
+                            }
+                        }
+                    }
+                    break;
+
+                    case EMR_POLYTEXTOUTA :
+                    case EMR_EXTTEXTOUTA :
+                        bFlag = true;
+                        [[fallthrough]];
+                    case EMR_POLYTEXTOUTW :
+                    case EMR_EXTTEXTOUTW :
+                    {
+                        sal_Int32   nLeft, nTop, nRight, nBottom;
+                        sal_uInt32  nGfxMode;
+                        float       nXScale, nYScale;
+                        sal_uInt32  ncStrings( 1 );
+                        sal_Int32   ptlReferenceX, ptlReferenceY;
+                        sal_uInt32  nLen, nOffString, nOptions, offDx;
+                        sal_Int32   nLeftRect, nTopRect, nRightRect, nBottomRect;
+
+                        nCurPos = mpInputStream->Tell() - 8;
+
+                        mpInputStream->ReadInt32( nLeft ).ReadInt32( nTop ).ReadInt32( nRight ).ReadInt32( nBottom )
+                           .ReadUInt32( nGfxMode ).ReadFloat( nXScale ).ReadFloat( nYScale );
+                        SAL_INFO("emfio", "\t\tBounds: " << nLeft << ", " << nTop << ", " << nRight << ", " << nBottom);
+                        SAL_INFO("emfio", "\t\tiGraphicsMode: 0x" << std::hex << nGfxMode << std::dec);
+                        SAL_INFO("emfio", "\t\t Scale: " << nXScale << " x " << nYScale);
+                        if ( ( nRecType == EMR_POLYTEXTOUTA ) || ( nRecType == EMR_POLYTEXTOUTW ) )
+                        {
+                            mpInputStream->ReadUInt32( ncStrings );
+                            SAL_INFO("emfio", "\t\t Number of Text objects: " << ncStrings);
+                            if ( ncStrings == 0 )
+                                break;
+                        }
+                        mpInputStream->ReadInt32( ptlReferenceX ).ReadInt32( ptlReferenceY ).ReadUInt32( nLen ).ReadUInt32( nOffString ).ReadUInt32( nOptions );
+                        SAL_INFO("emfio", "\t\tReference: (" << ptlReferenceX << ", " << ptlReferenceY << ")");
+
+                        mpInputStream->ReadInt32( nLeftRect ).ReadInt32( nTopRect ).ReadInt32( nRightRect ).ReadInt32( nBottomRect );
+                        mpInputStream->ReadUInt32( offDx );
+
+                        if (!mpInputStream->good())
+                            bStatus = false;
+                        else
+                        {
+                            const tools::Rectangle aRect( nLeftRect, nTopRect, nRightRect, nBottomRect );
+                            const BackgroundMode mnBkModeBackup = mnBkMode;
+                            if ( nOptions & ETO_NO_RECT ) // Don't draw the background rectangle and text background
+                                mnBkMode = BackgroundMode::Transparent;
+                            else if ( nOptions & ETO_OPAQUE )
+                                DrawRectWithBGColor( aRect );
+
+                            // ETO_RTLREADING indicates that the characters are laid from right to left
+                            vcl::text::ComplexTextLayoutFlags nTextLayoutMode = vcl::text::ComplexTextLayoutFlags::Default;
+                            if ( nOptions & ETO_RTLREADING )
+                                nTextLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
+                            SetTextLayoutMode( nTextLayoutMode );
+                            SAL_WARN_IF( ( nOptions & ( ETO_PDY | ETO_GLYPH_INDEX ) ) != 0, "emfio", "SJ: ETO_PDY || ETO_GLYPH_INDEX in EMF" );
+
+                            Point aPos( ptlReferenceX, ptlReferenceY );
+                            bool bOffStringSane = nOffString <= mnEndPos - nCurPos;
+                            if ( bOffStringSane )
+                            {
+                                mpInputStream->Seek( nCurPos + nOffString );
+                                OUString aText;
+                                if ( bFlag )
+                                {
+                                    if ( nLen <= ( mnEndPos - mpInputStream->Tell() ) )
+                                    {
+                                        std::vector<char> pBuf( nLen );
+                                        mpInputStream->ReadBytes(pBuf.data(), nLen);
+                                        aText = OUString(pBuf.data(), nLen, GetCharSet());
+                                    }
+                                }
+                                else
+                                {
+                                    if ( ( nLen * sizeof(sal_Unicode) ) <= ( mnEndPos - mpInputStream->Tell() ) )
+                                    {
+                                        aText = read_uInt16s_ToOUString(*mpInputStream, nLen);
+                                    }
+                                }
+
+                                SAL_INFO("emfio", "\t\tText: " << aText);
+                                SAL_INFO("emfio", "\t\tDxBuffer:");
+
+                                std::vector<sal_Int32> aDXAry;
+                                std::unique_ptr<tools::Long[]> pDYAry;
+
+                                sal_Int32 nDxSize;
+                                sal_Int32 nBytesEach;
+
+                                // Reading OutputDx
+                                // ETO_PDY flag indicates that we should read twice values
+                                // compared to the number of characters in the output string.
+                                // Values are stored in an array of 32-bit unsigned integers
+                                // named OutputDx, so there will be either 8 bytes or 4 bytes
+                                // each depending on ETO_PDY is set or not.
+                                if (nOptions & ETO_PDY)
+                                    nBytesEach = 8;
+                                else
+                                    nBytesEach = 4;
+
+                                bool bOverflow = o3tl::checked_multiply<sal_Int32>(nLen, nBytesEach, nDxSize);
+                                if (!bOverflow && offDx && ((nCurPos + offDx + nDxSize) <= nNextPos ) && nNextPos <= mnEndPos)
+                                {
+                                    mpInputStream->Seek( nCurPos + offDx );
+                                    aDXAry.resize(aText.getLength());
+                                    if (nOptions & ETO_PDY)
+                                    {
+                                        pDYAry.reset( new tools::Long[aText.getLength()] );
+                                    }
+
+                                    for (sal_Int32 i = 0; i < aText.getLength(); ++i)
+                                    {
+                                        sal_Int32 nDxCount = 1;
+                                        if ( static_cast<sal_uInt32>( aText.getLength() ) !=  nLen )
+                                        {
+                                            sal_Unicode cUniChar = aText[i];
+                                            OString aTmp(&cUniChar, 1, GetCharSet());
+                                            if (aTmp.getLength() > 1)
+                                            {
+                                                nDxCount = aTmp.getLength();
+                                            }
+                                        }
+
+                                        aDXAry[i] = 0;
+                                        if (nOptions & ETO_PDY)
+                                        {
+                                            pDYAry[i] = 0;
+                                        }
+
+                                        while (nDxCount--)
+                                        {
+                                            sal_Int32 nDxTmp = 0;
+                                            mpInputStream->ReadInt32(nDxTmp);
+                                            aDXAry[i] = o3tl::saturating_add(aDXAry[i], nDxTmp);
+                                            if (nOptions & ETO_PDY)
+                                            {
+                                                sal_Int32 nDyTmp = 0;
+                                                mpInputStream->ReadInt32(nDyTmp);
+                                                pDYAry[i] += nDyTmp;
+                                            }
+                                        }
+
+                                        SAL_INFO("emfio", "\t\t\tSpacing " << i << ": " << aDXAry[i]);
+                                    }
+                                }
+                                if ( nOptions & ETO_CLIPPED )
+                                {
+                                    Push(); // Save the current clip. It will be restored after text drawing
+                                    IntersectClipRect( aRect );
+                                }
+                                DrawText(aPos, aText, aDXAry.empty() ? nullptr : &aDXAry, pDYAry.get(), mbRecordPath, static_cast<GraphicsMode>(nGfxMode));
+                                if ( nOptions & ETO_CLIPPED )
+                                    Pop();
+                            }
+                            mnBkMode = mnBkModeBackup;
+                        }
+                    }
+                    break;
+
+                    case EMR_POLYBEZIERTO16 :
+                        DrawPolyBezier(ReadPolygonWithSkip<sal_Int16>(true, nNextPos), true, mbRecordPath);
+                    break;
+
+                    case EMR_POLYBEZIER16 :
+                        DrawPolyBezier(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), false, mbRecordPath);
+                    break;
+
+                    case EMR_POLYGON16 :
+                        DrawPolygon(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), mbRecordPath);
+                    break;
+
+                    case EMR_POLYLINETO16 :
+                        DrawPolyLine(ReadPolygonWithSkip<sal_Int16>(true, nNextPos), true, mbRecordPath);
+                    break;
+
+                    case EMR_POLYLINE16 :
+                        DrawPolyLine(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), false, mbRecordPath);
+                    break;
+
+                    case EMR_POLYPOLYLINE16 :
+                        ReadAndDrawPolyLine<sal_Int16>(nNextPos);
+                    break;
+
+                    case EMR_POLYPOLYGON16 :
+                        ReadAndDrawPolyPolygon<sal_Int16>(nNextPos);
+                    break;
+
+                    case EMR_FILLRGN :
+                    {
+                        sal_uInt32 nRemainingRecSize = nRecSize - 8;
+                        if (nRemainingRecSize < 24)
+                            bStatus = false;
+                        else
+                        {
+                            sal_uInt32 nRgnDataSize;
+                            basegfx::B2DPolyPolygon aPolyPoly;
+                            mpInputStream->SeekRel(16);  // RectL bounds
+                            mpInputStream->ReadUInt32( nRgnDataSize ).ReadUInt32( nIndex );
+                            nRemainingRecSize -= 24;
+
+                            if (ImplReadRegion(aPolyPoly, *mpInputStream, nRemainingRecSize))
+                            {
+                                Push();
+                                SelectObject( nIndex );
+                                tools::PolyPolygon aPolyPolygon(aPolyPoly);
+                                DrawPolyPolygon( aPolyPolygon );
+                                Pop();
+                            }
+                        }
+                    }
+                    break;
+
+                    case EMR_PAINTRGN :
+                    {
+                        sal_uInt32 nRemainingRecSize = nRecSize - 8;
+                        if (nRemainingRecSize < 20)
+                            bStatus = false;
+                        else
+                        {
+                            sal_uInt32 nRgnDataSize;
+                            basegfx::B2DPolyPolygon aPolyPoly;
+                            mpInputStream->SeekRel(16); // Skipping RectL bounds
+                            mpInputStream->ReadUInt32( nRgnDataSize );
+                            nRemainingRecSize -= 20;
+
+                            if (ImplReadRegion(aPolyPoly, *mpInputStream, nRemainingRecSize))
+                            {
+                                tools::PolyPolygon aPolyPolygon(aPolyPoly);
+                                DrawPolyPolygon( aPolyPolygon );
+                            }
+                        }
+                    }
+                    break;
+
+                    case EMR_CREATEDIBPATTERNBRUSHPT :
+                    {
+                        sal_uInt32  nStart = mpInputStream->Tell() - 8;
+                        Bitmap aBitmap;
+
+                        mpInputStream->ReadUInt32( nIndex );
+
+                        if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+                        {
+                            sal_uInt32 usage, offBmi, cbBmi, offBits, cbBits;
+
+                            mpInputStream->ReadUInt32( usage );
+                            mpInputStream->ReadUInt32( offBmi );
+                            mpInputStream->ReadUInt32( cbBmi );
+                            mpInputStream->ReadUInt32( offBits );
+                            mpInputStream->ReadUInt32( cbBits );
+
+                            if ( !mpInputStream->good() || (cbBits > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBits < cbBmi) )
+                               bStatus = false;
+                            else if ( offBmi )
+                            {
+                                sal_uInt32  nSize = cbBmi + cbBits + 14;
+                                if ( nSize <= ( mnEndPos - mnStartPos ) )
+                                {
+                                    char*   pBuf = new char[ nSize ];
+
+                                    SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
+                                    aTmp.ObjectOwnsMemory( true );
+                                    aTmp.WriteUChar( 'B' )
+                                        .WriteUChar( 'M' )
+                                        .WriteUInt32( cbBits )
+                                        .WriteUInt16( 0 )
+                                        .WriteUInt16( 0 )
+                                        .WriteUInt32( cbBmi + 14 );
+
+                                    mpInputStream->Seek( nStart + offBmi );
+                                    char* pWritePos = pBuf + 14;
+                                    auto nRead = mpInputStream->ReadBytes(pWritePos, cbBmi);
+                                    if (nRead != cbBmi)
+                                    {
+                                        // zero remainder if short read
+                                        memset(pWritePos + nRead, 0, cbBmi - nRead);
+                                    }
+
+                                    mpInputStream->Seek( nStart + offBits );
+                                    pWritePos = pBuf + 14 + cbBmi;
+                                    nRead = mpInputStream->ReadBytes(pWritePos, cbBits);
+                                    if (nRead != cbBits)
+                                    {
+                                        // zero remainder if short read
+                                        memset(pWritePos + nRead, 0, cbBits - nRead);
+                                    }
+
+                                    aTmp.Seek( 0 );
+                                    ReadDIB(aBitmap, aTmp, true);
+                                }
+                            }
+                        }
+
+                        CreateObjectIndexed(nIndex, std::make_unique<WinMtfFillStyle>( aBitmap ));
+                    }
+                    break;
+
+                    case EMR_MASKBLT :
+                    case EMR_PLGBLT :
+                    case EMR_SETDIBITSTODEVICE :
+                    case EMR_FRAMERGN :
+                    case EMR_INVERTRGN :
+                    case EMR_FLATTENPATH :
+                    case EMR_WIDENPATH :
+                    case EMR_POLYDRAW :
+                    case EMR_SETPALETTEENTRIES :
+                    case EMR_RESIZEPALETTE :
+                    case EMR_EXTFLOODFILL :
+                    case EMR_ANGLEARC :
+                    case EMR_SETCOLORADJUSTMENT :
+                    case EMR_POLYDRAW16 :
+                    case EMR_CREATECOLORSPACE :
+                    case EMR_SETCOLORSPACE :
+                    case EMR_DELETECOLORSPACE :
+                    case EMR_GLSRECORD :
+                    case EMR_GLSBOUNDEDRECORD :
+                    case EMR_PIXELFORMAT :
+                    case EMR_DRAWESCAPE :
+                    case EMR_EXTESCAPE :
+                    case EMR_STARTDOC :
+                    case EMR_SMALLTEXTOUT :
+                    case EMR_FORCEUFIMAPPING :
+                    case EMR_NAMEDESCAPE :
+                    case EMR_COLORCORRECTPALETTE :
+                    case EMR_SETICMPROFILEA :
+                    case EMR_SETICMPROFILEW :
+                    case EMR_TRANSPARENTBLT :
+                    case EMR_TRANSPARENTDIB :
+                    case EMR_GRADIENTFILL :
+                    case EMR_SETLINKEDUFIS :
+                    case EMR_SETMAPPERFLAGS :
+                    case EMR_SETICMMODE :
+                    case EMR_CREATEMONOBRUSH :
+                    case EMR_SETBRUSHORGEX :
+                    case EMR_SETMETARGN :
+                    case EMR_SETMITERLIMIT :
+                    case EMR_EXCLUDECLIPRECT :
+                    case EMR_REALIZEPALETTE :
+                    case EMR_SELECTPALETTE :
+                    case EMR_CREATEPALETTE :
+                    case EMR_ALPHADIBBLEND :
+                    case EMR_SETTEXTJUSTIFICATION :
+                    {
+                        SAL_WARN("emfio", "TODO: EMF record not implemented: " << record_type_name(nRecType));
+                    }
+                    break;
+
+                    case EMR_COMMENT :
+                    case EMR_HEADER :               // has already been read at ReadHeader()
+                    break;
+
+                    default :                           SAL_INFO("emfio", "Unknown Meta Action");                                     break;
+                }
+            }
+            mpInputStream->Seek( nNextPos );
+        }
+
+        // tdf#127471
+        maScaledFontHelper.applyAlternativeFontScale();
+
+        if( !maBmpSaveList.empty() )
+            ResolveBitmapActions( maBmpSaveList );
+
+        if ( bStatus )
+            mpInputStream->Seek(mnEndPos);
+
+        return bStatus;
+    };
+
+    bool EmfReader::ReadHeader()
+    {
+        // Spare me the METAFILEHEADER here
+        // Reading the METAHEADER - EMR_HEADER ([MS-EMF] section 2.3.4.2 EMR_HEADER Record Types)
+        sal_uInt32 nType(0), nHeaderSize(0);
+        mpInputStream->ReadUInt32(nType).ReadUInt32(nHeaderSize);
+        SAL_INFO ("emfio", "0x0-0x" << std::hex << nHeaderSize << " " << record_type_name(nType) << " size: " << std::dec <<  nHeaderSize);
+        if (nType != 0x00000001)
+        {
+            // per [MS-EMF] 2.3.4.2 EMF Header Record Types, type MUST be 0x00000001
+            SAL_WARN("emfio", "EMF header type is not set to 0x00000001 - possibly corrupted file?");
+            return false;
+        }
+
+        // Start reading the EMR_HEADER Header object
+
+        // bound size (RectL object, see [MS-WMF] section 2.2.2.19)
+        SAL_INFO("emfio", "\tBounding rectangle");
+        tools::Rectangle rclBounds = ReadRectangle(); // rectangle in logical units
+
+        // picture frame size (RectL object)
+        SAL_INFO("emfio", "\tPicture frame");
+        tools::Rectangle rclFrame = ReadRectangle(); // rectangle in device units 1/100th mm
+
+        sal_uInt32 nSignature(0);
+        mpInputStream->ReadUInt32(nSignature);
+        SAL_INFO("emfio", "\tSignature: 0x" << std::hex << nSignature << std::dec);
+
+        // nSignature MUST be the ASCII characters "FME", see [WS-EMF] 2.2.9 Header Object
+        // and 2.1.14 FormatSignature Enumeration
+        if (nSignature != 0x464d4520)
+        {
+            SAL_WARN("emfio", "EMF\t\tSignature is not 0x464d4520 (\"FME\") - possibly corrupted file?");
+            return false;
+        }
+
+        sal_uInt32 nVersion(0);
+        mpInputStream->ReadUInt32(nVersion);  // according to [WS-EMF] 2.2.9, this SHOULD be 0x0001000, however
+                                       // Microsoft note that not even Windows checks this...
+        SAL_INFO("emfio", "\tVersion: 0x" << std::hex << nVersion << std::dec);
+        if (nVersion != 0x00010000)
+        {
+            SAL_WARN("emfio", "EMF\t\tThis really should be 0x00010000, though not absolutely essential...");
+        }
+
+        mpInputStream->ReadUInt32(mnEndPos); // size of metafile
+        SAL_INFO("emfio", "\tMetafile size: " << mnEndPos);
+        mnEndPos += mnStartPos;
+
+        sal_uInt32 nStrmPos = mpInputStream->Tell(); // checking if mnEndPos is valid
+        sal_uInt32 nActualFileSize = nStrmPos + mpInputStream->remainingSize();
+
+        if ( nActualFileSize < mnEndPos )
+        {
+            SAL_WARN("emfio", "EMF\t\tEMF Header object records number of bytes as " << mnEndPos
+                                << ", however the file size is actually " << nActualFileSize
+                                << " bytes. Possible file corruption?");
+            mnEndPos = nActualFileSize;
+        }
+
+        mpInputStream->ReadUInt32(mnRecordCount);
+        SAL_INFO("emfio", "\tRecords: " << mnRecordCount);
+
+        // the number of "handles", or graphics objects used in the metafile
+
+        sal_uInt16 nHandlesCount;
+        mpInputStream->ReadUInt16(nHandlesCount);
+        SAL_INFO("emfio", "\tGraphics: " << nHandlesCount);
+
+        // the next 2 bytes are reserved, but according to [MS-EMF] section 2.2.9
+        // it MUST be 0x000 and MUST be ignored... the thing is, having such a specific
+        // value is actually pretty useful in checking if there is possible corruption
+
+        sal_uInt16 nReserved(0);
+        mpInputStream->ReadUInt16(nReserved);
+        SAL_INFO("emfio", "\tReserved: 0x" << std::hex << nReserved << std::dec);
+
+        if ( nReserved != 0x0000 )
+        {
+            SAL_WARN("emfio", "EMF\t\tEMF Header object's reserved field is NOT 0x0000... possible "
+                                "corruption?");
+        }
+
+        // The next 4 bytes specifies the number of characters in the metafile description.
+        // The 4 bytes after that specific the offset from this record that contains the
+        // metafile description... zero means no description string.
+        // For now, we ignore it.
+
+        mpInputStream->SeekRel(0x8);
+
+        sal_uInt32 nPalEntries(0);
+        mpInputStream->ReadUInt32(nPalEntries);
+        SAL_INFO("emfio", "\tPalette entries: " << nPalEntries);
+        sal_Int32 nPixX(0), nPixY(0), nMillX(0), nMillY(0);
+        mpInputStream->ReadInt32(nPixX);
+        mpInputStream->ReadInt32(nPixY);
+        SAL_INFO("emfio", "\tRef (pixels): " << nPixX << ", " << nPixY);
+        mpInputStream->ReadInt32(nMillX);
+        mpInputStream->ReadInt32(nMillY);
+        SAL_INFO("emfio", "\tRef (mm): " << nMillX << ", " << nMillY);
+
+        SetrclFrame(rclFrame);
+        SetrclBounds(rclBounds);
+        SetRefPix(Size( nPixX, nPixY ) );
+        SetRefMill(Size( nMillX, nMillY ) );
+
+        return checkSeek(*mpInputStream, mnStartPos + nHeaderSize);
+    }
+
+    tools::Rectangle EmfReader::ReadRectangle()
+    {
+        sal_Int32 nLeft(0), nTop(0), nRight(0), nBottom(0);
+        mpInputStream->ReadInt32(nLeft);
+        mpInputStream->ReadInt32(nTop);
+        mpInputStream->ReadInt32(nRight);
+        mpInputStream->ReadInt32(nBottom);
+
+        SAL_INFO("emfio", "\t\tLeft: " << nLeft << ", top: " << nTop << ", right: " << nRight << ", bottom: " << nBottom);
+        if (nLeft > nRight || nTop > nBottom)
+        {
+            SAL_WARN("emfio", "broken rectangle");
+            return tools::Rectangle::Justify(Point(nLeft, nTop), Point(nRight, nBottom));
+        }
+
+        return tools::Rectangle(nLeft, nTop, nRight, nBottom);
+    }
+
+    tools::Rectangle EmfReader::ReadRectangle( sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 )
+    {
+        Point aTL(x1, y1);
+        Point aBR(o3tl::saturating_add<sal_Int32>(x2, -1), o3tl::saturating_add<sal_Int32>(y2, -1));
+        return tools::Rectangle(aTL, aBR);
+    }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/emfio/source/reader/mtftools.cxx b/emfio/source/reader/mtftools.cxx
new file mode 100644
index 000000000..85429a501
--- /dev/null
+++ b/emfio/source/reader/mtftools.cxx
@@ -0,0 +1,2528 @@
+/* -*- 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 <mtftools.hxx>
+
+#include <cstdlib>
+#include <memory>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/graphictools.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/stream.hxx>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <vcl/virdev.hxx>
+#include <o3tl/safeint.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/defaultencoding.hxx>
+#include <unotools/wincodepage.hxx>
+
+#if OSL_DEBUG_LEVEL > 1
+#define EMFP_DEBUG(x) x
+#else
+#define EMFP_DEBUG(x)
+#endif
+
+namespace emfio
+{
+    SvStream& operator >> (SvStream& rInStream, XForm& rXForm)
+    {
+        if (sizeof(float) != 4)
+        {
+            OSL_FAIL("EmfReader::sizeof( float ) != 4");
+            rXForm = XForm();
+        }
+        else
+        {
+            rInStream.ReadFloat(rXForm.eM11);
+            rInStream.ReadFloat(rXForm.eM12);
+            rInStream.ReadFloat(rXForm.eM21);
+            rInStream.ReadFloat(rXForm.eM22);
+            rInStream.ReadFloat(rXForm.eDx);
+            rInStream.ReadFloat(rXForm.eDy);
+        }
+        return rInStream;
+    }
+
+    void WinMtfClipPath::intersectClip( const basegfx::B2DPolyPolygon& rPolyPolygon )
+    {
+        maClip.intersectPolyPolygon(rPolyPolygon);
+    }
+
+    void WinMtfClipPath::excludeClip( const basegfx::B2DPolyPolygon& rPolyPolygon )
+    {
+        maClip.subtractPolyPolygon(rPolyPolygon);
+    }
+
+    void WinMtfClipPath::setClipPath( const basegfx::B2DPolyPolygon& rB2DPoly, RegionMode nClippingMode )
+    {
+        switch ( nClippingMode )
+        {
+            case RegionMode::RGN_OR :
+                maClip.unionPolyPolygon(rB2DPoly);
+                break;
+            case RegionMode::RGN_XOR :
+                maClip.xorPolyPolygon(rB2DPoly);
+                break;
+            case RegionMode::RGN_DIFF :
+                maClip.subtractPolyPolygon(rB2DPoly);
+                break;
+            case RegionMode::RGN_AND :
+                maClip.intersectPolyPolygon(rB2DPoly);
+                break;
+            case RegionMode::RGN_COPY :
+                maClip = basegfx::utils::B2DClipState(rB2DPoly);
+                break;
+        }
+    }
+
+    void WinMtfClipPath::moveClipRegion( const Size& rSize )
+    {
+        basegfx::B2DHomMatrix aTranslate;
+        aTranslate.translate(rSize.Width(), rSize.Height());
+        maClip.transform(aTranslate);
+    }
+
+    void WinMtfClipPath::setDefaultClipPath()
+    {
+        // Empty clip region - everything visible
+        maClip = basegfx::utils::B2DClipState();
+    }
+
+    basegfx::B2DPolyPolygon const & WinMtfClipPath::getClipPath() const
+    {
+        return maClip.getClipPoly();
+    }
+
+    void WinMtfPathObj::AddPoint( const Point& rPoint )
+    {
+        if ( bClosed )
+            Insert( tools::Polygon() );
+        tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ];
+        rPoly.Insert( rPoly.GetSize(), rPoint );
+        bClosed = false;
+    }
+
+    void WinMtfPathObj::AddPolyLine( const tools::Polygon& rPolyLine )
+    {
+        if ( bClosed )
+            Insert( tools::Polygon() );
+        tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ];
+        rPoly.Insert( rPoly.GetSize(), rPolyLine );
+        bClosed = false;
+    }
+
+    void WinMtfPathObj::AddPolygon( const tools::Polygon& rPoly )
+    {
+        Insert( rPoly );
+        bClosed = true;
+    }
+
+    void WinMtfPathObj::AddPolyPolygon( const tools::PolyPolygon& rPolyPoly )
+    {
+        sal_uInt16 i, nCount = rPolyPoly.Count();
+        for ( i = 0; i < nCount; i++ )
+            Insert( rPolyPoly[ i ] );
+        bClosed = true;
+    }
+
+    void WinMtfPathObj::ClosePath()
+    {
+        if ( Count() )
+        {
+            tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ];
+            if ( rPoly.GetSize() > 2 )
+            {
+                Point aFirst( rPoly[ 0 ] );
+                if ( aFirst != rPoly[ rPoly.GetSize() - 1 ] )
+                    rPoly.Insert( rPoly.GetSize(), aFirst );
+            }
+        }
+        bClosed = true;
+    }
+
+    WinMtfFontStyle::WinMtfFontStyle( LOGFONTW const & rFont )
+    {
+        rtl_TextEncoding eCharSet;
+        if ((rFont.alfFaceName == "Symbol")
+         || (rFont.alfFaceName == "MT Extra"))
+            eCharSet = RTL_TEXTENCODING_SYMBOL;
+        else if ((rFont.lfCharSet == DEFAULT_CHARSET) || (rFont.lfCharSet == OEM_CHARSET))
+            eCharSet = utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding(),
+                                                         rFont.lfCharSet == OEM_CHARSET);
+        else
+            eCharSet = rtl_getTextEncodingFromWindowsCharset( rFont.lfCharSet );
+        if ( eCharSet == RTL_TEXTENCODING_DONTKNOW )
+            eCharSet = RTL_TEXTENCODING_MS_1252;
+        aFont.SetCharSet( eCharSet );
+        aFont.SetFamilyName( rFont.alfFaceName );
+        FontFamily eFamily;
+        switch ( rFont.lfPitchAndFamily >> 4 & 0x0f )
+        {
+            case FamilyFont::FF_ROMAN:
+                eFamily = FAMILY_ROMAN;
+            break;
+
+            case FamilyFont::FF_SWISS:
+                eFamily = FAMILY_SWISS;
+            break;
+
+            case FamilyFont::FF_MODERN:
+                eFamily = FAMILY_MODERN;
+            break;
+
+            case FamilyFont::FF_SCRIPT:
+                eFamily = FAMILY_SCRIPT;
+            break;
+
+            case FamilyFont::FF_DECORATIVE:
+                 eFamily = FAMILY_DECORATIVE;
+            break;
+
+            default:
+                eFamily = FAMILY_DONTKNOW;
+            break;
+        }
+        aFont.SetFamily( eFamily );
+
+        FontPitch ePitch;
+        switch ( rFont.lfPitchAndFamily & 0x0f )
+        {
+            case FIXED_PITCH:
+                ePitch = PITCH_FIXED;
+            break;
+
+            case DEFAULT_PITCH:
+            case VARIABLE_PITCH:
+            default:
+                ePitch = PITCH_VARIABLE;
+            break;
+        }
+        aFont.SetPitch( ePitch );
+
+        FontWeight eWeight;
+        if (rFont.lfWeight == 0) // default weight SHOULD be used
+            eWeight = WEIGHT_DONTKNOW;
+        else if (rFont.lfWeight <= FW_THIN)
+            eWeight = WEIGHT_THIN;
+        else if( rFont.lfWeight <= FW_ULTRALIGHT )
+            eWeight = WEIGHT_ULTRALIGHT;
+        else if( rFont.lfWeight <= FW_LIGHT )
+            eWeight = WEIGHT_LIGHT;
+        else if( rFont.lfWeight <  FW_MEDIUM )
+            eWeight = WEIGHT_NORMAL;
+        else if( rFont.lfWeight == FW_MEDIUM )
+            eWeight = WEIGHT_MEDIUM;
+        else if( rFont.lfWeight <= FW_SEMIBOLD )
+            eWeight = WEIGHT_SEMIBOLD;
+        else if( rFont.lfWeight <= FW_BOLD )
+            eWeight = WEIGHT_BOLD;
+        else if( rFont.lfWeight <= FW_ULTRABOLD )
+            eWeight = WEIGHT_ULTRABOLD;
+        else
+            eWeight = WEIGHT_BLACK;
+        aFont.SetWeight( eWeight );
+
+        if( rFont.lfItalic )
+            aFont.SetItalic( ITALIC_NORMAL );
+
+        if( rFont.lfUnderline )
+            aFont.SetUnderline( LINESTYLE_SINGLE );
+
+        if( rFont.lfStrikeOut )
+            aFont.SetStrikeout( STRIKEOUT_SINGLE );
+
+        aFont.SetOrientation( Degree10(static_cast<sal_Int16>(rFont.lfEscapement)) );
+
+        Size aFontSize( Size( rFont.lfWidth, rFont.lfHeight ) );
+        if ( rFont.lfHeight > 0 )
+        {
+            // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
+            SolarMutexGuard aGuard;
+            ScopedVclPtrInstance< VirtualDevice > pVDev;
+            // converting the cell height into a font height
+            aFont.SetFontSize( aFontSize );
+            pVDev->SetFont( aFont );
+            FontMetric aMetric( pVDev->GetFontMetric() );
+            tools::Long nHeight = aMetric.GetAscent() + aMetric.GetDescent();
+            if (nHeight)
+            {
+                double fHeight = (static_cast<double>(aFontSize.Height()) * rFont.lfHeight ) / nHeight;
+                aFontSize.setHeight( static_cast<sal_Int32>( fHeight + 0.5 ) );
+            }
+        }
+
+        // Convert height to positive
+        aFontSize.setHeight( std::abs(aFontSize.Height()) );
+        aFont.SetFontSize(aFontSize);
+
+        // tdf#127471 adapt nFontWidth from Windows-like notation to
+        // NormedFontScaling if used for text scaling
+#ifndef _WIN32
+        const bool bFontScaledHorizontally(aFontSize.Width() != 0 && aFontSize.Width() != aFontSize.Height());
+
+        if(bFontScaledHorizontally)
+        {
+            // tdf#127471 nFontWidth is the Windows FontScaling, need to convert to
+            // Non-Windowslike notation relative to FontHeight.
+            const tools::Long nAverageFontWidth(aFont.GetOrCalculateAverageFontWidth());
+
+            if(nAverageFontWidth > 0)
+            {
+                const double fScaleFactor(static_cast<double>(aFontSize.Height()) / static_cast<double>(nAverageFontWidth));
+                aFont.SetAverageFontWidth(static_cast<tools::Long>(static_cast<double>(aFontSize.Width()) * fScaleFactor));
+            }
+        }
+#endif
+    };
+
+    // tdf#127471
+    ScaledFontDetectCorrectHelper::ScaledFontDetectCorrectHelper()
+    {
+    }
+
+    void ScaledFontDetectCorrectHelper::endCurrentMetaFontAction()
+    {
+        if(maCurrentMetaFontAction.is() && !maAlternativeFontScales.empty())
+        {
+            // create average corrected FontScale value and count
+            // positive/negative hits
+            sal_uInt32 nPositive(0);
+            sal_uInt32 nNegative(0);
+            double fAverage(0.0);
+
+            for(double fPart : maAlternativeFontScales)
+            {
+                if(fPart < 0.0)
+                {
+                    nNegative++;
+                    fAverage += -fPart;
+                }
+                else
+                {
+                    nPositive++;
+                    fAverage += fPart;
+                }
+            }
+
+            fAverage /= static_cast<double>(maAlternativeFontScales.size());
+
+            if(nPositive >= nNegative)
+            {
+                // correction intended, it is probably an old imported file
+                maPositiveIdentifiedCases.push_back(std::pair<rtl::Reference<MetaFontAction>, double>(maCurrentMetaFontAction, fAverage));
+            }
+            else
+            {
+                // correction not favorable in the majority of cases for this Font, still
+                // remember to have a weight in the last decision for correction
+                maNegativeIdentifiedCases.push_back(std::pair<rtl::Reference<MetaFontAction>, double>(maCurrentMetaFontAction, fAverage));
+            }
+        }
+
+        maCurrentMetaFontAction.clear();
+        maAlternativeFontScales.clear();
+    }
+
+    void ScaledFontDetectCorrectHelper::newCurrentMetaFontAction(const rtl::Reference<MetaFontAction>& rNewMetaFontAction)
+    {
+        maCurrentMetaFontAction.clear();
+        maAlternativeFontScales.clear();
+
+        if(!rNewMetaFontAction.is())
+            return;
+
+        // check 1st criteria for FontScale active. We usually write this,
+        // so this will already sort out most situations
+        const vcl::Font& rCandidate(rNewMetaFontAction->GetFont());
+
+        if(0 != rCandidate.GetAverageFontWidth())
+        {
+            const tools::Long nUnscaledAverageFontWidth(rCandidate.GetOrCalculateAverageFontWidth());
+
+            // check 2nd (system-dependent) criteria for FontScale
+            if(nUnscaledAverageFontWidth != rCandidate.GetFontHeight())
+            {
+                // FontScale is active, remember and use as current
+                maCurrentMetaFontAction = rNewMetaFontAction;
+            }
+        }
+    }
+
+    void ScaledFontDetectCorrectHelper::evaluateAlternativeFontScale(OUString const & rText, tools::Long nImportedTextLength)
+    {
+        if(!maCurrentMetaFontAction.is())
+            return;
+
+        SolarMutexGuard aGuard; // VirtualDevice is not thread-safe
+        ScopedVclPtrInstance< VirtualDevice > pTempVirtualDevice;
+
+        // calculate measured TextLength
+        const vcl::Font& rFontCandidate(maCurrentMetaFontAction->GetFont());
+        pTempVirtualDevice->SetFont(rFontCandidate);
+        tools::Long nMeasuredTextLength(pTempVirtualDevice->GetTextWidth(rText));
+        // on failure, use original length
+        if (!nMeasuredTextLength)
+            nMeasuredTextLength = nImportedTextLength;
+
+        // compare expected and imported TextLengths
+        if (nImportedTextLength == nMeasuredTextLength)
+            return;
+
+        const double fFactorText(static_cast<double>(nImportedTextLength) / static_cast<double>(nMeasuredTextLength));
+        const double fFactorTextPercent(fabs(1.0 - fFactorText) * 100.0);
+
+        // if we assume that loaded file was written on old linux, we have to
+        // back-convert the scale value depending on which system we run
+#ifdef _WIN32
+        // When running on Windows the value was not adapted at font import (see WinMtfFontStyle
+        // constructor), so it is still NormedFontScaling and we need to convert to Windows-style
+        // scaling
+#else
+        // When running on unx (non-Windows) the value was already adapted at font import (see WinMtfFontStyle
+        // constructor). It was wrongly assumed to be Windows-style FontScaling, so we need to revert that
+        // to get back to the needed unx-style FontScale
+#endif
+        // Interestingly this leads to the *same* correction, so no need to make this
+        // system-dependent (!)
+        const tools::Long nUnscaledAverageFontWidth(rFontCandidate.GetOrCalculateAverageFontWidth());
+        const tools::Long nScaledAverageFontWidth(rFontCandidate.GetAverageFontWidth());
+        const double fScaleFactor(static_cast<double>(nUnscaledAverageFontWidth) / static_cast<double>(rFontCandidate.GetFontHeight()));
+        const double fCorrectedAverageFontWidth(static_cast<double>(nScaledAverageFontWidth) * fScaleFactor);
+        tools::Long nCorrectedTextLength(0);
+
+        { // do in own scope, only need nUnscaledAverageFontWidth
+            vcl::Font rFontCandidate2(rFontCandidate);
+            rFontCandidate2.SetAverageFontWidth(static_cast<tools::Long>(fCorrectedAverageFontWidth));
+            pTempVirtualDevice->SetFont(rFontCandidate2);
+            nCorrectedTextLength = pTempVirtualDevice->GetTextWidth(rText);
+            // on failure, use original length
+            if (!nCorrectedTextLength)
+                nCorrectedTextLength = nImportedTextLength;
+        }
+
+        const double fFactorCorrectedText(static_cast<double>(nImportedTextLength) / static_cast<double>(nCorrectedTextLength));
+        const double fFactorCorrectedTextPercent(fabs(1.0 - fFactorCorrectedText) * 100.0);
+
+        // If FactorCorrectedText fits better than FactorText this is probably
+        // an import of an old EMF/WMF written by LibreOffice on a non-Windows (unx) system
+        // and should be corrected.
+        // Usually in tested cases this lies inside 5% of range, so detecting this just using
+        //  fFactorTextPercent inside 5% -> no old file
+        //  fFactorCorrectedTextPercent inside 5% -> is old file
+        // works not too bad, but there are some strange not so often used fonts where that
+        // values do deviate, so better just compare if old corrected would fit better than
+        // the uncorrected case, that is usually safe.
+        if(fFactorCorrectedTextPercent < fFactorTextPercent)
+        {
+            maAlternativeFontScales.push_back(fCorrectedAverageFontWidth);
+        }
+        else
+        {
+            // also push, but negative to remember non-fitting case
+            maAlternativeFontScales.push_back(-fCorrectedAverageFontWidth);
+        }
+    }
+
+    void ScaledFontDetectCorrectHelper::applyAlternativeFontScale()
+    {
+        // make sure last evtl. detected current FontAction gets added to identified cases
+        endCurrentMetaFontAction();
+
+        // Take final decision to correct FontScaling for this imported Metafile or not.
+        // It is possible to weight positive against negative cases, so to only finally
+        // correct when more positive cases were detected.
+        // But that would be inconsequent and wrong. *If* the detected case is an old import
+        // the whole file was written with wrong FontScale values and all Font actions
+        // need to be corrected. Thus, for now, correct all when there are/is positive
+        // cases detected.
+        // On the other hand it *may* be that for some strange fonts there is a false-positive
+        // in the positive cases, so at least insist on positive cases being more than negative.
+        // Still, do then correct *all* cases.
+        if(!maPositiveIdentifiedCases.empty()
+            && maPositiveIdentifiedCases.size() >= maNegativeIdentifiedCases.size())
+        {
+            for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maPositiveIdentifiedCases)
+            {
+                rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second));
+            }
+            for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maNegativeIdentifiedCases)
+            {
+                rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second));
+            }
+        }
+
+        maPositiveIdentifiedCases.clear();
+        maNegativeIdentifiedCases.clear();
+    }
+
+    Color MtfTools::ReadColor()
+    {
+        sal_uInt32 nColor(0);
+        mpInputStream->ReadUInt32( nColor );
+        Color aColor( COL_BLACK );
+        if ( ( nColor & 0xFFFF0000 ) == 0x01000000 )
+        {
+            size_t index = nColor & 0x0000FFFF;
+            if ( index < maPalette.aPaletteColors.size() )
+                aColor = maPalette.aPaletteColors[ index ];
+            else
+                SAL_INFO( "emfio", "\t\t Palette index out of range: " << index );
+        }
+        else
+            aColor = Color( static_cast<sal_uInt8>( nColor ), static_cast<sal_uInt8>( nColor >> 8 ), static_cast<sal_uInt8>( nColor >> 16 ) );
+
+        SAL_INFO("emfio", "\t\tColor: " << aColor);
+        return aColor;
+    };
+
+    Point MtfTools::ImplScale(const Point& rPoint) // Hack to set varying defaults for incompletely defined files.
+    {
+        if (!mbIsMapDevSet)
+            return Point(rPoint.X() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Left(),
+                         rPoint.Y() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Top());
+        else
+            return rPoint;
+    }
+
+    Point MtfTools::ImplMap( const Point& rPt )
+    {
+        if ( mnWinExtX && mnWinExtY )
+        {
+            double fX = rPt.X();
+            double fY = rPt.Y();
+
+            double fX2 = fX * maXForm.eM11 + fY * maXForm.eM21 + maXForm.eDx;
+            double fY2 = fX * maXForm.eM12 + fY * maXForm.eM22 + maXForm.eDy;
+
+            if ( meGfxMode == GraphicsMode::GM_COMPATIBLE )
+            {
+                fX2 -= mnWinOrgX;
+                fY2 -= mnWinOrgY;
+
+                switch( meMapMode )
+                {
+                    case MappingMode::MM_LOENGLISH :
+                    {
+                        fX2 = o3tl::convert(fX2, o3tl::Length::in100, o3tl::Length::mm100);
+                        fY2 = o3tl::convert(-fY2, o3tl::Length::in100, o3tl::Length::mm100);
+                    }
+                    break;
+                    case MappingMode::MM_HIENGLISH :
+                    {
+                        fX2 = o3tl::convert(fX2, o3tl::Length::in1000, o3tl::Length::mm100);
+                        fY2 = o3tl::convert(-fY2, o3tl::Length::in1000, o3tl::Length::mm100);
+                    }
+                    break;
+                    case MappingMode::MM_TWIPS:
+                    {
+                        fX2 = o3tl::convert(fX2, o3tl::Length::twip, o3tl::Length::mm100);
+                        fY2 = o3tl::convert(-fY2, o3tl::Length::twip, o3tl::Length::mm100);
+                    }
+                    break;
+                    case MappingMode::MM_LOMETRIC :
+                    {
+                        fX2 = o3tl::convert(fX2, o3tl::Length::mm10, o3tl::Length::mm100);
+                        fY2 = o3tl::convert(-fY2, o3tl::Length::mm10, o3tl::Length::mm100);
+                    }
+                    break;
+                    case MappingMode::MM_HIMETRIC : // in hundredth of a millimeter
+                    {
+                        fY2 *= -1;
+                    }
+                    break;
+                    default :
+                    {
+                        if (mnPixX == 0 || mnPixY == 0)
+                        {
+                            SAL_WARN("emfio", "invalid scaling factor");
+                            return Point();
+                        }
+                        else
+                        {
+                            if ( meMapMode != MappingMode::MM_TEXT )
+                            {
+                                fX2 /= mnWinExtX;
+                                fY2 /= mnWinExtY;
+                                fX2 *= mnDevWidth;
+                                fY2 *= mnDevHeight;
+                            }
+                            fX2 *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
+                            fY2 *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
+                        }
+                    }
+                    break;
+                }
+
+                double nDevOrgX = mnDevOrgX;
+                if (mnPixX)
+                    nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
+                fX2 += nDevOrgX;
+                double nDevOrgY = mnDevOrgY;
+                if (mnPixY)
+                    nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
+                fY2 += nDevOrgY;
+
+                fX2 -= mrclFrame.Left();
+                fY2 -= mrclFrame.Top();
+            }
+            return Point(basegfx::fround(fX2), basegfx::fround(fY2));
+        }
+        else
+            return Point();
+    };
+
+    Size MtfTools::ImplMap(const Size& rSz, bool bDoWorldTransform)
+    {
+        if ( mnWinExtX && mnWinExtY )
+        {
+            // #i121382# apply the whole WorldTransform, else a rotation will be misinterpreted
+            double fWidth, fHeight;
+            if (bDoWorldTransform)
+            {
+                fWidth = rSz.Width() * maXForm.eM11 + rSz.Height() * maXForm.eM21;
+                fHeight = rSz.Width() * maXForm.eM12 + rSz.Height() * maXForm.eM22;
+            }
+            else
+            {
+                //take the scale, but not the rotation
+                basegfx::B2DHomMatrix aMatrix(maXForm.eM11, maXForm.eM12, 0,
+                                              maXForm.eM21, maXForm.eM22, 0);
+                basegfx::B2DTuple aScale, aTranslate;
+                double fRotate, fShearX;
+                if (!aMatrix.decompose(aScale, aTranslate, fRotate, fShearX))
+                {
+                    aScale.setX(1.0);
+                    aScale.setY(1.0);
+                }
+                fWidth = rSz.Width() * aScale.getX();
+                fHeight = rSz.Height() * aScale.getY();
+            }
+
+            if ( meGfxMode == GraphicsMode::GM_COMPATIBLE )
+            {
+                switch( meMapMode )
+                {
+                    case MappingMode::MM_LOENGLISH :
+                    {
+                        fWidth = o3tl::convert(fWidth, o3tl::Length::in100, o3tl::Length::mm100);
+                        fHeight = o3tl::convert(-fHeight, o3tl::Length::in100, o3tl::Length::mm100);
+                    }
+                    break;
+                    case MappingMode::MM_HIENGLISH :
+                    {
+                        fWidth = o3tl::convert(fWidth, o3tl::Length::in1000, o3tl::Length::mm100);
+                        fHeight = o3tl::convert(-fHeight, o3tl::Length::in1000, o3tl::Length::mm100);
+                    }
+                    break;
+                    case MappingMode::MM_LOMETRIC :
+                    {
+                        fWidth = o3tl::convert(fWidth, o3tl::Length::mm10, o3tl::Length::mm100);
+                        fHeight = o3tl::convert(-fHeight, o3tl::Length::mm10, o3tl::Length::mm100);
+                    }
+                    break;
+                    case MappingMode::MM_HIMETRIC : // in hundredth of millimeters
+                    {
+                        fHeight *= -1;
+                    }
+                    break;
+                    case MappingMode::MM_TWIPS:
+                    {
+                        fWidth = o3tl::convert(fWidth, o3tl::Length::twip, o3tl::Length::mm100);
+                        fHeight = o3tl::convert(-fHeight, o3tl::Length::twip, o3tl::Length::mm100);
+                    }
+                    break;
+                    default :
+                    {
+                        if (mnPixX == 0 || mnPixY == 0)
+                        {
+                            SAL_WARN("emfio", "invalid scaling factor");
+                            return Size();
+                        }
+                        else
+                        {
+                            if ( meMapMode != MappingMode::MM_TEXT )
+                            {
+                                fWidth /= mnWinExtX;
+                                fHeight /= mnWinExtY;
+                                fWidth *= mnDevWidth;
+                                fHeight *= mnDevHeight;
+                            }
+                            fWidth *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
+                            fHeight *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
+                        }
+                    }
+                    break;
+                }
+            }
+            return Size(basegfx::fround(fWidth), basegfx::fround(fHeight));
+        }
+        else
+            return Size();
+    }
+
+    tools::Rectangle MtfTools::ImplMap( const tools::Rectangle& rRect )
+    {
+        tools::Rectangle aRect;
+        aRect.SetPos(ImplMap(rRect.TopLeft()));
+        aRect.SaturatingSetSize(ImplMap(rRect.GetSize()));
+        return aRect;
+    }
+
+    void MtfTools::ImplMap( vcl::Font& rFont )
+    {
+        // !!! HACK: we now always set the width to zero because the OS width is interpreted differently;
+        // must later be made portable in SV (KA 1996-02-08)
+        Size  aFontSize = ImplMap (rFont.GetFontSize(), false);
+
+        const auto nHeight = aFontSize.Height();
+        if (nHeight < 0)
+            aFontSize.setHeight( o3tl::saturating_toggle_sign(nHeight) );
+
+        rFont.SetFontSize( aFontSize );
+
+        sal_Int32 nResult;
+        const bool bFail = o3tl::checked_multiply(mnWinExtX, mnWinExtY, nResult);
+        if (!bFail && nResult < 0)
+            rFont.SetOrientation( 3600_deg10 - rFont.GetOrientation() );
+    }
+
+    tools::Polygon& MtfTools::ImplMap( tools::Polygon& rPolygon )
+    {
+        sal_uInt16 nPoints = rPolygon.GetSize();
+        for ( sal_uInt16 i = 0; i < nPoints; i++ )
+        {
+            rPolygon[ i ] = ImplMap( rPolygon[ i ] );
+        }
+        return rPolygon;
+    }
+
+    void MtfTools::ImplScale( tools::Polygon& rPolygon )
+    {
+        sal_uInt16 nPoints = rPolygon.GetSize();
+        for ( sal_uInt16 i = 0; i < nPoints; i++ )
+        {
+            rPolygon[ i ] = ImplScale( rPolygon[ i ] );
+        }
+    }
+
+    tools::PolyPolygon& MtfTools::ImplScale( tools::PolyPolygon& rPolyPolygon )
+    {
+        sal_uInt16 nPolys = rPolyPolygon.Count();
+        for (sal_uInt16 i = 0; i < nPolys; ++i)
+        {
+            ImplScale(rPolyPolygon[i]);
+        }
+        return rPolyPolygon;
+    }
+
+    tools::PolyPolygon& MtfTools::ImplMap( tools::PolyPolygon& rPolyPolygon )
+    {
+        sal_uInt16 nPolys = rPolyPolygon.Count();
+        for ( sal_uInt16 i = 0; i < nPolys; ImplMap( rPolyPolygon[ i++ ] ) ) ;
+        return rPolyPolygon;
+    }
+
+    void MtfTools::SelectObject( sal_uInt32 nIndex )
+    {
+        if ( nIndex & ENHMETA_STOCK_OBJECT )
+        {
+            SAL_INFO ( "emfio", "\t\t ENHMETA_STOCK_OBJECT, StockObject Enumeration: 0x" << std::hex  << nIndex );
+            StockObject nStockId = static_cast<StockObject>(nIndex & 0xFF);
+            switch( nStockId )
+            {
+                case StockObject::WHITE_BRUSH :
+                {
+                    maFillStyle = WinMtfFillStyle( COL_WHITE );
+                    mbFillStyleSelected = true;
+                }
+                break;
+                case StockObject::LTGRAY_BRUSH :
+                {
+                    maFillStyle = WinMtfFillStyle( COL_LIGHTGRAY );
+                    mbFillStyleSelected = true;
+                }
+                break;
+                case StockObject::GRAY_BRUSH :
+                {
+                    maFillStyle = WinMtfFillStyle( COL_GRAY );
+                    mbFillStyleSelected = true;
+                }
+                break;
+                case StockObject::DKGRAY_BRUSH :
+                {
+                    maFillStyle = WinMtfFillStyle( COL_GRAY7 );
+                    mbFillStyleSelected = true;
+                }
+                break;
+                case StockObject::BLACK_BRUSH :
+                {
+                    maFillStyle = WinMtfFillStyle( COL_BLACK );
+                    mbFillStyleSelected = true;
+                }
+                break;
+                case StockObject::NULL_BRUSH :
+                {
+                   maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true );
+                   mbFillStyleSelected = true;
+                }
+                break;
+                case StockObject::WHITE_PEN :
+                {
+                    maLineStyle = WinMtfLineStyle( COL_WHITE );
+                }
+                break;
+                case StockObject::BLACK_PEN :
+                {
+                    maLineStyle = WinMtfLineStyle( COL_BLACK );
+                }
+                break;
+                case StockObject::NULL_PEN :
+                {
+                    maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true );
+                }
+                break;
+                default:
+                break;
+            }
+        }
+        else
+        {
+            nIndex &= 0xffff;       // safety check: don't allow index to be > 65535
+
+            GDIObj *pGDIObj = nullptr;
+
+            if ( nIndex < mvGDIObj.size() )
+                pGDIObj = mvGDIObj[ nIndex ].get();
+
+            if ( pGDIObj )
+            {
+
+                SAL_INFO ( "emfio", "\t\t Index: " << nIndex );
+                if (const auto pen = dynamic_cast<WinMtfLineStyle*>(pGDIObj))
+                {
+                    maLineStyle = *pen;
+                    SAL_INFO ( "emfio", "\t Line Style, Color: 0x" << std::hex << maLineStyle.aLineColor
+                                        << ", Weight: " << maLineStyle.aLineInfo.GetWidth() );
+                }
+                else if (const auto brush = dynamic_cast<WinMtfFillStyle*>(
+                             pGDIObj))
+                {
+                    maFillStyle = *brush;
+                    mbFillStyleSelected = true;
+                    SAL_INFO("emfio", "\t\tBrush Object, Index: " << nIndex << ", Color: " << maFillStyle.aFillColor);
+                }
+                else if (const auto font = dynamic_cast<WinMtfFontStyle*>(
+                             pGDIObj))
+                {
+                    maFont = font->aFont;
+                    SAL_INFO("emfio", "\t\tFont Object, Index: " << nIndex << ", Font: " << maFont.GetFamilyName() << " " << maFont.GetStyleName());
+                }
+                else if (const auto palette = dynamic_cast<WinMtfPalette*>(
+                             pGDIObj))
+                {
+                    maPalette = palette->aPaletteColors;
+                    SAL_INFO("emfio", "\t\tPalette Object, Index: " << nIndex << ", Number of colours: " << maPalette.aPaletteColors.size() );
+                }
+            }
+            else
+            {
+                SAL_WARN("emfio", "Warning: Unable to find Object with index:" << nIndex);
+            }
+        }
+    }
+
+    void MtfTools::SetTextLayoutMode( vcl::text::ComplexTextLayoutFlags nTextLayoutMode )
+    {
+        mnTextLayoutMode = nTextLayoutMode;
+    }
+
+    void MtfTools::SetArcDirection(bool bClockWise)
+    {
+        SAL_INFO("emfio", "\t\t Arc direction: " << (bClockWise ? "ClockWise" : "CounterClockWise"));
+        mbClockWiseArcDirection = bClockWise;
+    }
+
+    void MtfTools::SetBkMode( BackgroundMode nMode )
+    {
+        mnBkMode = nMode;
+    }
+
+    void MtfTools::SetBkColor( const Color& rColor )
+    {
+        maBkColor = rColor;
+    }
+
+    void MtfTools::SetTextColor( const Color& rColor )
+    {
+        maTextColor = rColor;
+    }
+
+    void MtfTools::SetTextAlign( sal_uInt32 nAlign )
+    {
+        mnTextAlign = nAlign;
+    }
+
+    void MtfTools::ImplResizeObjectArry( sal_uInt32 nNewEntrys )
+    {
+        mvGDIObj.resize(nNewEntrys);
+    }
+
+    void MtfTools::ImplDrawClippedPolyPolygon( const tools::PolyPolygon& rPolyPoly )
+    {
+        if ( !rPolyPoly.Count() )
+            return;
+
+        ImplSetNonPersistentLineColorTransparenz();
+        if ( rPolyPoly.Count() == 1 )
+        {
+            if ( rPolyPoly.IsRect() )
+                mpGDIMetaFile->AddAction( new MetaRectAction( rPolyPoly.GetBoundRect() ) );
+            else
+            {
+                tools::Polygon aPoly( rPolyPoly[ 0 ] );
+                sal_uInt16 nCount = aPoly.GetSize();
+                if ( nCount )
+                {
+                    if ( aPoly[ nCount - 1 ] != aPoly[ 0 ] )
+                    {
+                        Point aPoint( aPoly[ 0 ] );
+                        aPoly.Insert( nCount, aPoint );
+                    }
+                    mpGDIMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
+                }
+            }
+        }
+        else
+            mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPoly ) );
+    }
+
+    void MtfTools::CreateObject( std::unique_ptr<GDIObj> pObject )
+    {
+        if ( pObject )
+        {
+            const auto pLineStyle = dynamic_cast<WinMtfLineStyle*>(pObject.get());
+            const auto pFontStyle = dynamic_cast<WinMtfFontStyle*>(pObject.get());
+
+            if ( pFontStyle )
+            {
+                if (pFontStyle->aFont.GetFontHeight() == 0)
+                    pFontStyle->aFont.SetFontHeight(423);
+                ImplMap(pFontStyle->aFont); // defaulting to 12pt
+            }
+            else if ( pLineStyle )
+            {
+                Size aSize(pLineStyle->aLineInfo.GetWidth(), 0);
+                aSize = ImplMap(aSize);
+                pLineStyle->aLineInfo.SetWidth(aSize.Width());
+            }
+        }
+        std::vector<std::unique_ptr<GDIObj>>::size_type nIndex;
+        for ( nIndex = 0; nIndex < mvGDIObj.size(); nIndex++ )
+        {
+            if ( !mvGDIObj[ nIndex ] )
+                break;
+        }
+        if ( nIndex == mvGDIObj.size() )
+            ImplResizeObjectArry( mvGDIObj.size() + 16 );
+
+        mvGDIObj[ nIndex ] = std::move(pObject);
+    }
+
+    void MtfTools::CreateObjectIndexed( sal_uInt32 nIndex, std::unique_ptr<GDIObj> pObject )
+    {
+        if ( ( nIndex & ENHMETA_STOCK_OBJECT ) != 0 )
+            return;
+
+        nIndex &= 0xffff;       // safety check: do not allow index to be > 65535
+        if ( pObject )
+        {
+            const auto pLineStyle = dynamic_cast<WinMtfLineStyle*>(pObject.get());
+            const auto pFontStyle = dynamic_cast<WinMtfFontStyle*>(pObject.get());
+            if ( pFontStyle )
+            {
+                if (pFontStyle->aFont.GetFontHeight() == 0)
+                    pFontStyle->aFont.SetFontHeight(423);
+                ImplMap(pFontStyle->aFont);
+            }
+            else if ( pLineStyle )
+            {
+                Size aSize(pLineStyle->aLineInfo.GetWidth(), 0);
+                pLineStyle->aLineInfo.SetWidth( ImplMap(aSize).Width() );
+
+                if ( pLineStyle->aLineInfo.GetStyle() == LineStyle::Dash )
+                {
+                    aSize.AdjustWidth(1 );
+                    tools::Long nDotLen = ImplMap( aSize ).Width();
+                    pLineStyle->aLineInfo.SetDistance( nDotLen );
+                    pLineStyle->aLineInfo.SetDotLen( nDotLen );
+                    pLineStyle->aLineInfo.SetDashLen( nDotLen * 3 );
+                }
+            }
+        }
+        if ( nIndex >= mvGDIObj.size() )
+            ImplResizeObjectArry( nIndex + 16 );
+
+        mvGDIObj[ nIndex ] = std::move(pObject);
+    }
+
+    void MtfTools::CreateObject()
+    {
+        CreateObject(std::make_unique<GDIObj>());
+    }
+
+    void MtfTools::DeleteObject( sal_uInt32 nIndex )
+    {
+        if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+        {
+            if ( nIndex < mvGDIObj.size() )
+            {
+                mvGDIObj[ nIndex ].reset();
+            }
+        }
+    }
+
+    void MtfTools::IntersectClipRect( const tools::Rectangle& rRect )
+    {
+        if (utl::ConfigManager::IsFuzzing())
+            return;
+        mbClipNeedsUpdate=true;
+        if ((rRect.Left()-rRect.Right()==0) && (rRect.Top()-rRect.Bottom()==0))
+        {
+            return; // empty rectangles cause trouble
+        }
+        tools::Polygon aPoly( rRect );
+        const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) );
+        maClipPath.intersectClip( aPolyPolyRect.getB2DPolyPolygon() );
+    }
+
+    void MtfTools::ExcludeClipRect( const tools::Rectangle& rRect )
+    {
+        if (utl::ConfigManager::IsFuzzing())
+            return;
+        mbClipNeedsUpdate=true;
+        tools::Polygon aPoly( rRect );
+        const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) );
+        maClipPath.excludeClip( aPolyPolyRect.getB2DPolyPolygon() );
+    }
+
+    void MtfTools::MoveClipRegion( const Size& rSize )
+    {
+        if (utl::ConfigManager::IsFuzzing())
+            return;
+        mbClipNeedsUpdate=true;
+        maClipPath.moveClipRegion( ImplMap( rSize ) );
+    }
+
+    void MtfTools::SetClipPath( const tools::PolyPolygon& rPolyPolygon, RegionMode eClippingMode, bool bIsMapped )
+    {
+        if (utl::ConfigManager::IsFuzzing())
+            return;
+        mbClipNeedsUpdate = true;
+        tools::PolyPolygon aPolyPolygon(rPolyPolygon);
+
+        if (!bIsMapped)
+        {
+            if (!mbIsMapDevSet && (meMapMode == MappingMode::MM_ISOTROPIC || meMapMode == MappingMode::MM_ANISOTROPIC))
+                aPolyPolygon = ImplScale(aPolyPolygon);
+            else
+                aPolyPolygon = ImplMap(aPolyPolygon);
+        }
+        maClipPath.setClipPath(aPolyPolygon.getB2DPolyPolygon(), eClippingMode);
+    }
+
+    void MtfTools::SetDefaultClipPath()
+    {
+        mbClipNeedsUpdate = true;
+        maClipPath.setDefaultClipPath();
+    }
+
+    MtfTools::MtfTools( GDIMetaFile& rGDIMetaFile, SvStream& rStreamWMF)
+    :   mnLatestTextAlign(90),
+        mnTextAlign(TextAlignmentMode::TA_LEFT | TextAlignmentMode::TA_TOP | TextAlignmentMode::TA_NOUPDATECP),
+        maLatestBkColor(ColorTransparency, 0x12345678),
+        maBkColor(COL_WHITE),
+        mnLatestTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default),
+        mnTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default),
+        mnLatestBkMode(BackgroundMode::NONE),
+        mnBkMode(BackgroundMode::OPAQUE),
+        meLatestRasterOp(RasterOp::Invert),
+        meRasterOp(RasterOp::OverPaint),
+        mnRop(),
+        meGfxMode(GraphicsMode::GM_COMPATIBLE),
+        meMapMode(MappingMode::MM_TEXT),
+        mnDevOrgX(0),
+        mnDevOrgY(0),
+        mnDevWidth(1),
+        mnDevHeight(1),
+        mnWinOrgX(0),
+        mnWinOrgY(0),
+        mnWinExtX(1),
+        mnWinExtY(1),
+        mnPixX(100),
+        mnPixY(100),
+        mnMillX(1),
+        mnMillY(1),
+        mpGDIMetaFile(&rGDIMetaFile),
+        mpInputStream(&rStreamWMF),
+        mnStartPos(0),
+        mnEndPos(0),
+        mbNopMode(false),
+        mbClockWiseArcDirection(false),
+        mbFillStyleSelected(false),
+        mbClipNeedsUpdate(true),
+        mbComplexClip(false),
+        mbIsMapWinSet(false),
+        mbIsMapDevSet(false)
+    {
+        SvLockBytes *pLB = mpInputStream->GetLockBytes();
+
+        if (pLB)
+        {
+            pLB->SetSynchronMode();
+        }
+
+        mnStartPos = mpInputStream->Tell();
+        SetDevOrg(Point());
+
+        mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::CLIPREGION ) ); // The original clipregion has to be on top
+                                                                                 // of the stack so it can always be restored
+                                                                                 // this is necessary to be able to support
+                                                                                 // SetClipRgn( NULL ) and similar ClipRgn actions (SJ)
+
+        maFont.SetFamilyName( "Arial" );                                         // sj: #i57205#, we do have some scaling problems if using
+        maFont.SetCharSet( RTL_TEXTENCODING_MS_1252 );                           // the default font then most times a x11 font is used, we
+        maFont.SetFontHeight( 423 );                                      // will prevent this defining a font
+
+        maLatestLineStyle.aLineColor = Color( 0x12, 0x34, 0x56 );
+        maLatestFillStyle.aFillColor = Color( 0x12, 0x34, 0x56 );
+
+        mnRop = WMFRasterOp::Black;
+        meRasterOp = RasterOp::OverPaint;
+        mpGDIMetaFile->AddAction( new MetaRasterOpAction( RasterOp::OverPaint ) );
+    }
+
+    MtfTools::~MtfTools() COVERITY_NOEXCEPT_FALSE
+    {
+        mpGDIMetaFile->AddAction( new MetaPopAction() );
+        mpGDIMetaFile->SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+        if ( mrclFrame.IsEmpty() )
+            mpGDIMetaFile->SetPrefSize( Size( mnDevWidth, mnDevHeight ) );
+        else
+            mpGDIMetaFile->SetPrefSize( mrclFrame.GetSize() );
+    }
+
+    void MtfTools::UpdateClipRegion()
+    {
+        if (!mbClipNeedsUpdate)
+            return;
+
+        mbClipNeedsUpdate = false;
+        mbComplexClip = false;
+
+        mpGDIMetaFile->AddAction( new MetaPopAction() );                    // taking the original clipregion
+        mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::CLIPREGION ) );
+
+        // skip for 'no clipping at all' case
+        if( maClipPath.isEmpty() )
+            return;
+
+        const basegfx::B2DPolyPolygon& rClipPoly( maClipPath.getClipPath() );
+
+        mbComplexClip = rClipPoly.count() > 1
+            || !basegfx::utils::isRectangle(rClipPoly);
+
+        // This makes cases like tdf#45820 work in reasonable time.
+        if (mbComplexClip)
+        {
+            mpGDIMetaFile->AddAction(
+                new MetaISectRegionClipRegionAction(
+                    vcl::Region(rClipPoly)));
+            mbComplexClip = false;
+        }
+        else
+        {
+            mpGDIMetaFile->AddAction(
+                new MetaISectRectClipRegionAction(
+                    vcl::unotools::rectangleFromB2DRectangle(
+                        rClipPoly.getB2DRange())));
+        }
+    }
+
+    void MtfTools::ImplSetNonPersistentLineColorTransparenz()
+    {
+        WinMtfLineStyle aTransparentLine( COL_TRANSPARENT, true );
+        if ( ! ( maLatestLineStyle == aTransparentLine ) )
+        {
+            maLatestLineStyle = aTransparentLine;
+            mpGDIMetaFile->AddAction( new MetaLineColorAction( aTransparentLine.aLineColor, !aTransparentLine.bTransparent ) );
+        }
+    }
+
+    void MtfTools::UpdateLineStyle()
+    {
+        if (!( maLatestLineStyle == maLineStyle ) )
+        {
+            maLatestLineStyle = maLineStyle;
+            mpGDIMetaFile->AddAction( new MetaLineColorAction( maLineStyle.aLineColor, !maLineStyle.bTransparent ) );
+        }
+    }
+
+    void MtfTools::UpdateFillStyle()
+    {
+        if ( !mbFillStyleSelected )     // SJ: #i57205# taking care of bkcolor if no brush is selected
+            maFillStyle = WinMtfFillStyle( maBkColor, mnBkMode == BackgroundMode::Transparent );
+        if (!( maLatestFillStyle == maFillStyle ) )
+        {
+            maLatestFillStyle = maFillStyle;
+            if (maFillStyle.aType == WinMtfFillStyleType::Solid)
+                mpGDIMetaFile->AddAction( new MetaFillColorAction( maFillStyle.aFillColor, !maFillStyle.bTransparent ) );
+        }
+    }
+
+    WMFRasterOp MtfTools::SetRasterOp( WMFRasterOp nRasterOp )
+    {
+        WMFRasterOp nRetROP = mnRop;
+        if ( nRasterOp != mnRop )
+        {
+            mnRop = nRasterOp;
+
+            if ( mbNopMode && ( nRasterOp != WMFRasterOp::Nop ) )
+            {   // changing modes from WMFRasterOp::Nop so set pen and brush
+                maFillStyle = maNopFillStyle;
+                maLineStyle = maNopLineStyle;
+                mbNopMode = false;
+            }
+            switch( nRasterOp )
+            {
+                case WMFRasterOp::Not:
+                    meRasterOp = RasterOp::Invert;
+                break;
+
+                case WMFRasterOp::XorPen:
+                    meRasterOp = RasterOp::Xor;
+                break;
+
+                case WMFRasterOp::Nop:
+                {
+                    meRasterOp = RasterOp::OverPaint;
+                    if( !mbNopMode )
+                    {
+                        maNopFillStyle = maFillStyle;
+                        maNopLineStyle = maLineStyle;
+                        maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true );
+                        maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true );
+                        mbNopMode = true;
+                    }
+                }
+                break;
+
+                default:
+                    meRasterOp = RasterOp::OverPaint;
+                break;
+            }
+        }
+        if ( nRetROP != nRasterOp )
+            mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) );
+        return nRetROP;
+    };
+
+    void MtfTools::StrokeAndFillPath( bool bStroke, bool bFill )
+    {
+        if ( !maPathObj.Count() )
+            return;
+
+        UpdateClipRegion();
+        UpdateLineStyle();
+        UpdateFillStyle();
+        if ( bFill )
+        {
+            if ( !bStroke )
+            {
+                mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) );
+                mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
+            }
+            if ( maPathObj.Count() == 1 )
+                mpGDIMetaFile->AddAction( new MetaPolygonAction( maPathObj.GetObject( 0 ) ) );
+            else
+                mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( maPathObj ) );
+
+            if ( !bStroke )
+                mpGDIMetaFile->AddAction( new MetaPopAction() );
+        }
+        // tdf#142014 By default the stroke is made with hairline. If width is bigger, we need to use PolyLineAction
+        if ( bStroke )
+        {
+            // bFill is drawing hairstyle line. So we need to draw it only when the width is different than 0
+            if ( !bFill || maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
+            {
+                sal_uInt16 i, nCount = maPathObj.Count();
+                for ( i = 0; i < nCount; i++ )
+                    mpGDIMetaFile->AddAction( new MetaPolyLineAction( maPathObj[ i ], maLineStyle.aLineInfo ) );
+            }
+        }
+        ClearPath();
+    }
+
+    void MtfTools::DrawPixel( const Point& rSource, const Color& rColor )
+    {
+        mpGDIMetaFile->AddAction( new MetaPixelAction( ImplMap( rSource), rColor ) );
+    }
+
+    void MtfTools::MoveTo( const Point& rPoint, bool bRecordPath )
+    {
+        Point aDest( ImplMap( rPoint ) );
+        if ( bRecordPath )
+        {
+            // fdo#57353 create new subpath for subsequent moves
+            if ( maPathObj.Count() )
+                if ( maPathObj[ maPathObj.Count() - 1 ].GetSize() )
+                    maPathObj.Insert( tools::Polygon() );
+            maPathObj.AddPoint( aDest );
+        }
+        maActPos = aDest;
+    }
+
+    void MtfTools::LineTo( const Point& rPoint, bool bRecordPath )
+    {
+        UpdateClipRegion();
+        Point aDest( ImplMap( rPoint ) );
+        if ( bRecordPath )
+            maPathObj.AddPoint( aDest );
+        else
+        {
+            UpdateLineStyle();
+            mpGDIMetaFile->AddAction( new MetaLineAction( maActPos, aDest, maLineStyle.aLineInfo ) );
+        }
+        maActPos = aDest;
+    }
+
+    void MtfTools::DrawRectWithBGColor(const tools::Rectangle& rRect)
+    {
+        WinMtfFillStyle aFillStyleBackup = maFillStyle;
+        bool            aTransparentBackup = maLineStyle.bTransparent;
+        BackgroundMode  mnBkModeBackup = mnBkMode;
+
+        const tools::Polygon aPoly( rRect );
+        maLineStyle.bTransparent = true;
+        maFillStyle = maBkColor;
+        mnBkMode = BackgroundMode::OPAQUE;
+        ImplSetNonPersistentLineColorTransparenz();
+        DrawPolygon(std::move(aPoly), false);
+        mnBkMode = mnBkModeBackup; // The rectangle needs to be always drawned even if mode is transparent
+        maFillStyle = aFillStyleBackup;
+        maLineStyle.bTransparent = aTransparentBackup;
+    }
+
+    void MtfTools::DrawRect( const tools::Rectangle& rRect, bool bEdge )
+    {
+        UpdateClipRegion();
+        UpdateFillStyle();
+
+        if ( mbComplexClip )
+        {
+            tools::Polygon aPoly( ImplMap( rRect ) );
+            tools::PolyPolygon aPolyPolyRect( aPoly );
+            tools::PolyPolygon aDest;
+            tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( aPolyPolyRect, aDest );
+            ImplDrawClippedPolyPolygon( aDest );
+        }
+        else
+        {
+            if ( bEdge )
+            {
+                if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
+                {
+                    ImplSetNonPersistentLineColorTransparenz();
+                    mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
+                    UpdateLineStyle();
+                    mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( ImplMap( rRect ) ),maLineStyle.aLineInfo ) );
+                }
+                else
+                {
+                    UpdateLineStyle();
+                    mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
+                }
+            }
+            else
+            {
+                ImplSetNonPersistentLineColorTransparenz();
+                mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
+            }
+        }
+    }
+
+    void MtfTools::DrawRoundRect( const tools::Rectangle& rRect, const Size& rSize )
+    {
+        UpdateClipRegion();
+        UpdateLineStyle();
+        UpdateFillStyle();
+        mpGDIMetaFile->AddAction( new MetaRoundRectAction( ImplMap( rRect ), std::abs( ImplMap( rSize ).Width() ), std::abs( ImplMap( rSize ).Height() ) ) );
+        // tdf#142139 Wrong line width during WMF import
+        if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
+        {
+            tools::Polygon aRoundRectPoly( rRect, rSize.Width(), rSize.Height() );
+            mpGDIMetaFile->AddAction( new MetaPolyLineAction( ImplMap( aRoundRectPoly ), maLineStyle.aLineInfo ) );
+        }
+    }
+
+    void MtfTools::DrawEllipse( const tools::Rectangle& rRect )
+    {
+        UpdateClipRegion();
+        UpdateFillStyle();
+
+        if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
+        {
+            Point aCenter( ImplMap( rRect.Center() ) );
+            Size  aRad( ImplMap( Size( rRect.GetWidth() / 2, rRect.GetHeight() / 2 ) ) );
+
+            ImplSetNonPersistentLineColorTransparenz();
+            mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) );
+            UpdateLineStyle();
+            mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ), maLineStyle.aLineInfo ) );
+        }
+        else
+        {
+            UpdateLineStyle();
+            mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) );
+        }
+    }
+
+    void MtfTools::DrawArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd, bool bTo )
+    {
+        UpdateClipRegion();
+        UpdateLineStyle();
+        UpdateFillStyle();
+
+        tools::Rectangle   aRect( ImplMap( rRect ) );
+        Point       aStart( ImplMap( rStart ) );
+        Point       aEnd( ImplMap( rEnd ) );
+
+        if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
+        {
+            if ( aStart == aEnd )
+            {   // SJ: #i53768# if start & end is identical, then we have to draw a full ellipse
+                Point aCenter( aRect.Center() );
+                Size  aRad( aRect.GetWidth() / 2, aRect.GetHeight() / 2 );
+
+                mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ), maLineStyle.aLineInfo ) );
+            }
+            else
+                mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Arc ), maLineStyle.aLineInfo ) );
+        }
+        else
+            mpGDIMetaFile->AddAction( new MetaArcAction( aRect, aStart, aEnd ) );
+
+        if ( bTo )
+            maActPos = aEnd;
+    }
+
+    void MtfTools::DrawPie( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd )
+    {
+        UpdateClipRegion();
+        UpdateFillStyle();
+
+        tools::Rectangle   aRect( ImplMap( rRect ) );
+        Point       aStart( ImplMap( rStart ) );
+        Point       aEnd( ImplMap( rEnd ) );
+
+        if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
+        {
+            ImplSetNonPersistentLineColorTransparenz();
+            mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) );
+            UpdateLineStyle();
+            mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Pie ), maLineStyle.aLineInfo ) );
+        }
+        else
+        {
+            UpdateLineStyle();
+            mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) );
+        }
+    }
+
+    void MtfTools::DrawChord( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd )
+    {
+        UpdateClipRegion();
+        UpdateFillStyle();
+
+        tools::Rectangle   aRect( ImplMap( rRect ) );
+        Point       aStart( ImplMap( rStart ) );
+        Point       aEnd( ImplMap( rEnd ) );
+
+        if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
+        {
+            ImplSetNonPersistentLineColorTransparenz();
+            mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) );
+            UpdateLineStyle();
+            mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Chord ), maLineStyle.aLineInfo ) );
+        }
+        else
+        {
+            UpdateLineStyle();
+            mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) );
+        }
+    }
+
+    void MtfTools::DrawPolygon( tools::Polygon rPolygon, bool bRecordPath )
+    {
+        UpdateClipRegion();
+        ImplMap( rPolygon );
+        if ( bRecordPath )
+            maPathObj.AddPolygon( rPolygon );
+        else
+        {
+            UpdateFillStyle();
+
+            if ( mbComplexClip )
+            {
+                tools::PolyPolygon aPolyPoly( rPolygon );
+                auto tmp = maClipPath.getClip();
+                tmp.intersectPolyPolygon(aPolyPoly.getB2DPolyPolygon());
+                tools::PolyPolygon aDest(tmp.getClipPoly());
+                ImplDrawClippedPolyPolygon( aDest );
+            }
+            else
+            {
+                if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
+                {
+                    sal_uInt16 nCount = rPolygon.GetSize();
+                    if ( nCount )
+                    {
+                        if ( rPolygon[ nCount - 1 ] != rPolygon[ 0 ] )
+                        {
+                            Point aPoint( rPolygon[ 0 ] );
+                            rPolygon.Insert( nCount, aPoint );
+                        }
+                    }
+                    ImplSetNonPersistentLineColorTransparenz();
+                    mpGDIMetaFile->AddAction( new MetaPolygonAction( rPolygon ) );
+                    UpdateLineStyle();
+                    mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) );
+                }
+                else
+                {
+                    UpdateLineStyle();
+
+                    if (maLatestFillStyle.aType != WinMtfFillStyleType::Pattern)
+                        mpGDIMetaFile->AddAction( new MetaPolygonAction( rPolygon ) );
+                    else {
+                        SvtGraphicFill aFill( tools::PolyPolygon( rPolygon ),
+                                              Color(),
+                                              0.0,
+                                              SvtGraphicFill::fillNonZero,
+                                              SvtGraphicFill::fillTexture,
+                                              SvtGraphicFill::Transform(),
+                                              true,
+                                              SvtGraphicFill::hatchSingle,
+                                              Color(),
+                                              SvtGraphicFill::GradientType::Linear,
+                                              Color(),
+                                              Color(),
+                                              0,
+                                              Graphic (BitmapEx(maLatestFillStyle.aBmp)));
+
+                        SvMemoryStream  aMemStm;
+
+                        WriteSvtGraphicFill( aMemStm, aFill );
+
+                        mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0,
+                                                                static_cast<const sal_uInt8*>(aMemStm.GetData()),
+                                                                aMemStm.TellEnd() ) );
+                        mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_END" ) );
+                    }
+
+                }
+            }
+        }
+    }
+
+    void MtfTools::DrawPolyPolygon( tools::PolyPolygon& rPolyPolygon, bool bRecordPath )
+    {
+        UpdateClipRegion();
+
+        ImplMap( rPolyPolygon );
+
+        if ( bRecordPath )
+            maPathObj.AddPolyPolygon( rPolyPolygon );
+        else
+        {
+            UpdateFillStyle();
+
+            if ( mbComplexClip )
+            {
+                tools::PolyPolygon aDest;
+                tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( rPolyPolygon, aDest );
+                ImplDrawClippedPolyPolygon( aDest );
+            }
+            else
+            {
+                UpdateLineStyle();
+                mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPolygon ) );
+                if (maLineStyle.aLineInfo.GetWidth() > 0 || maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash)
+                {
+                    for (sal_uInt16 nPoly = 0; nPoly < rPolyPolygon.Count(); ++nPoly)
+                    {
+                        mpGDIMetaFile->AddAction(new MetaPolyLineAction(rPolyPolygon[nPoly], maLineStyle.aLineInfo));
+                    }
+                }
+            }
+        }
+    }
+
+    void MtfTools::DrawPolyLine( tools::Polygon rPolygon, bool bTo, bool bRecordPath )
+    {
+        UpdateClipRegion();
+
+        sal_uInt16 nPoints = rPolygon.GetSize();
+        if (nPoints < 1)
+            return;
+
+        ImplMap( rPolygon );
+        if ( bTo )
+        {
+            rPolygon[ 0 ] = maActPos;
+            maActPos = rPolygon[ rPolygon.GetSize() - 1 ];
+        }
+        if ( bRecordPath )
+            maPathObj.AddPolyLine( rPolygon );
+        else
+        {
+            UpdateLineStyle();
+            mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) );
+        }
+    }
+
+    void MtfTools::DrawPolyBezier( tools::Polygon rPolygon, bool bTo, bool bRecordPath )
+    {
+        sal_uInt16 nPoints = rPolygon.GetSize();
+        if ( ( nPoints < 4 ) || ( ( ( nPoints - 4 ) % 3 ) != 0 ) )
+            return;
+
+        UpdateClipRegion();
+
+        ImplMap( rPolygon );
+        if ( bTo )
+        {
+            rPolygon[ 0 ] = maActPos;
+            maActPos = rPolygon[ nPoints - 1 ];
+        }
+        sal_uInt16 i;
+        for ( i = 0; ( i + 2 ) < nPoints; )
+        {
+            rPolygon.SetFlags( i++, PolyFlags::Normal );
+            rPolygon.SetFlags( i++, PolyFlags::Control );
+            rPolygon.SetFlags( i++, PolyFlags::Control );
+        }
+        if ( bRecordPath )
+            maPathObj.AddPolyLine( rPolygon );
+        else
+        {
+            UpdateLineStyle();
+            mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) );
+        }
+    }
+
+    void MtfTools::DrawText( Point& rPosition, OUString const & rText, std::vector<sal_Int32>* pDXArry, tools::Long* pDYArry, bool bRecordPath, GraphicsMode nGfxMode )
+    {
+        UpdateClipRegion();
+        rPosition = ImplMap( rPosition );
+        GraphicsMode nOldGfxMode = GetGfxMode();
+        SetGfxMode( GraphicsMode::GM_COMPATIBLE );
+
+        if (pDXArry)
+        {
+            sal_Int64 nSumX = 0, nSumY = 0;
+            for (sal_Int32 i = 0; i < rText.getLength(); i++ )
+            {
+                nSumX += (*pDXArry)[i];
+
+                // #i121382# Map DXArray using WorldTransform
+                const Size aSizeX(ImplMap(Size(nSumX, 0)));
+                const basegfx::B2DVector aVectorX(aSizeX.Width(), aSizeX.Height());
+                (*pDXArry)[i] = basegfx::fround(aVectorX.getLength());
+                (*pDXArry)[i] *= (nSumX >= 0 ? 1 : -1);
+
+                if (pDYArry)
+                {
+                    nSumY += pDYArry[i];
+
+                    const Size aSizeY(ImplMap(Size(0, nSumY)));
+                    const basegfx::B2DVector aVectorY(aSizeY.Width(), aSizeY.Height());
+                    // Reverse Y
+                    pDYArry[i] = basegfx::fround(aVectorY.getLength());
+                    pDYArry[i] *= (nSumY >= 0 ? -1 : 1);
+                }
+            }
+        }
+        if ( mnLatestTextLayoutMode != mnTextLayoutMode )
+        {
+            mnLatestTextLayoutMode = mnTextLayoutMode;
+            mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) );
+        }
+        SetGfxMode( nGfxMode );
+        TextAlign eTextAlign;
+        if ( ( mnTextAlign & TA_BASELINE) == TA_BASELINE )
+            eTextAlign = ALIGN_BASELINE;
+        else if( ( mnTextAlign & TA_BOTTOM) == TA_BOTTOM )
+            eTextAlign = ALIGN_BOTTOM;
+        else
+            eTextAlign = ALIGN_TOP;
+        bool bChangeFont = false;
+        if ( mnLatestTextAlign != mnTextAlign )
+        {
+            bChangeFont = true;
+            mnLatestTextAlign = mnTextAlign;
+            mpGDIMetaFile->AddAction( new MetaTextAlignAction( eTextAlign ) );
+        }
+        if ( maLatestTextColor != maTextColor )
+        {
+            bChangeFont = true;
+            maLatestTextColor = maTextColor;
+            mpGDIMetaFile->AddAction( new MetaTextColorAction( maTextColor ) );
+        }
+        bool bChangeFillColor = false;
+        if ( maLatestBkColor != maBkColor )
+        {
+            bChangeFillColor = true;
+            maLatestBkColor = maBkColor;
+        }
+        if ( mnLatestBkMode != mnBkMode )
+        {
+            bChangeFillColor = true;
+            mnLatestBkMode = mnBkMode;
+        }
+        if ( bChangeFillColor )
+        {
+            bChangeFont = true;
+            mpGDIMetaFile->AddAction( new MetaTextFillColorAction( maFont.GetFillColor(), !maFont.IsTransparent() ) );
+        }
+        vcl::Font aTmp( maFont );
+        aTmp.SetColor( maTextColor );
+        aTmp.SetFillColor( maBkColor );
+
+        if( mnBkMode == BackgroundMode::Transparent )
+            aTmp.SetTransparent( true );
+        else
+            aTmp.SetTransparent( false );
+
+        aTmp.SetAlignment( eTextAlign );
+
+        if ( nGfxMode == GraphicsMode::GM_ADVANCED )
+        {
+            // check whether there is a font rotation applied via transformation
+            Point aP1( ImplMap( Point() ) );
+            Point aP2( ImplMap( Point( 0, 100 ) ) );
+            aP2.AdjustX( -(aP1.X()) );
+            aP2.AdjustY( -(aP1.Y()) );
+            double fX = aP2.X();
+            double fY = aP2.Y();
+            if ( fX )
+            {
+                double fOrientation = basegfx::rad2deg(acos(fX / std::hypot(fX, fY)));
+                if ( fY > 0 )
+                    fOrientation = 360 - fOrientation;
+                fOrientation += 90;
+                fOrientation *= 10;
+                aTmp.SetOrientation( aTmp.GetOrientation() + Degree10( static_cast<sal_Int16>(fOrientation) ) );
+            }
+        }
+
+        if( mnTextAlign & ( TA_UPDATECP | TA_RIGHT_CENTER ) )
+        {
+            // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
+            SolarMutexGuard aGuard;
+            ScopedVclPtrInstance< VirtualDevice > pVDev;
+            sal_Int32 nTextWidth;
+            Point aActPosDelta;
+            pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) );
+            pVDev->SetFont( maFont );
+            const sal_uInt32 nLen = pDXArry ? rText.getLength() : 0;
+            if (nLen)
+            {
+                nTextWidth = pVDev->GetTextWidth( OUString(rText[ nLen - 1 ]) );
+                if( nLen > 1 )
+                    nTextWidth += (*pDXArry)[ nLen - 2 ];
+                // tdf#39894: We should consider the distance to next character cell origin
+                aActPosDelta.setX( (*pDXArry)[ nLen - 1 ] );
+                if ( pDYArry )
+                {
+                    aActPosDelta.setY( pDYArry[ nLen - 1 ] );
+                }
+            }
+            else
+            {
+                nTextWidth = pVDev->GetTextWidth( rText );
+                aActPosDelta.setX( nTextWidth );
+            }
+
+            if( mnTextAlign & TA_UPDATECP )
+                rPosition = maActPos;
+
+            if ( mnTextAlign & TA_RIGHT_CENTER )
+            {
+                Point aDisplacement( ( ( mnTextAlign & TA_RIGHT_CENTER ) == TA_RIGHT ) ? nTextWidth : nTextWidth >> 1, 0 );
+                Point().RotateAround(aDisplacement, maFont.GetOrientation());
+                rPosition -= aDisplacement;
+            }
+
+            if( mnTextAlign & TA_UPDATECP )
+            {
+                Point().RotateAround(aActPosDelta, maFont.GetOrientation());
+                maActPos = rPosition + aActPosDelta;
+            }
+        }
+
+        if(bChangeFont || (maLatestFont != aTmp))
+        {
+            maLatestFont = aTmp;
+            rtl::Reference<MetaFontAction> aNewMetaFontAction(new MetaFontAction(aTmp));
+
+            // tdf#127471 end evtl active MetaFontAction scale corrector detector/collector
+            maScaledFontHelper.endCurrentMetaFontAction();
+
+            // !bRecordPath: else no MetaTextArrayAction will be created
+            // nullptr != pDXArry: detection only possible when text size is given
+            // rText.getLength(): no useful check without text
+            if(!bRecordPath && nullptr != pDXArry && 0 != rText.getLength())
+            {
+                maScaledFontHelper.newCurrentMetaFontAction(aNewMetaFontAction);
+            }
+
+            mpGDIMetaFile->AddAction( aNewMetaFontAction );
+            mpGDIMetaFile->AddAction( new MetaTextAlignAction( aTmp.GetAlignment() ) );
+            mpGDIMetaFile->AddAction( new MetaTextColorAction( aTmp.GetColor() ) );
+            mpGDIMetaFile->AddAction( new MetaTextFillColorAction( aTmp.GetFillColor(), !aTmp.IsTransparent() ) );
+        }
+
+        if ( bRecordPath )
+        {
+            // TODO
+        }
+        else
+        {
+            if ( pDXArry && pDYArry )
+            {
+                for (sal_Int32 i = 0; i < rText.getLength(); ++i)
+                {
+                    Point aCharDisplacement( i ? (*pDXArry)[i-1] : 0, i ? pDYArry[i-1] : 0 );
+                    Point().RotateAround(aCharDisplacement, maFont.GetOrientation());
+                    mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition + aCharDisplacement, OUString( rText[i] ), o3tl::span<const sal_Int32>{}, 0, 1 ) );
+                }
+            }
+            else
+            {
+                /* because text without dx array is badly scaled, we
+                   will create such an array if necessary */
+                o3tl::span<const sal_Int32> pDX;
+                std::vector<sal_Int32> aMyDXArray;
+                if (pDXArry)
+                {
+                    pDX = { pDXArry->data(), pDXArry->size() };
+                    // only useful when we have an imported DXArray
+                    if(!rText.isEmpty())
+                    {
+                        maScaledFontHelper.evaluateAlternativeFontScale(
+                            rText,
+                            (*pDXArry)[rText.getLength() - 1] // extract imported TextLength
+                        );
+                    }
+                }
+                else
+                {
+                    // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
+                    SolarMutexGuard aGuard;
+                    ScopedVclPtrInstance< VirtualDevice > pVDev;
+                    pVDev->SetMapMode(MapMode(MapUnit::Map100thMM));
+                    pVDev->SetFont( maLatestFont );
+                    pVDev->GetTextArray( rText, &aMyDXArray, 0, rText.getLength());
+                    pDX = aMyDXArray;
+                }
+                mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, rText, pDX, 0, rText.getLength() ) );
+            }
+        }
+        SetGfxMode( nOldGfxMode );
+    }
+
+    void MtfTools::ImplDrawBitmap( const Point& rPos, const Size& rSize, const BitmapEx& rBitmap )
+    {
+        BitmapEx aBmpEx( rBitmap );
+        if ( mbComplexClip )
+        {
+            vcl::bitmap::DrawAndClipBitmap(rPos, rSize, rBitmap, aBmpEx, maClipPath.getClipPath());
+        }
+
+        if ( aBmpEx.IsAlpha() )
+            mpGDIMetaFile->AddAction( new MetaBmpExScaleAction( rPos, rSize, aBmpEx ) );
+        else
+            mpGDIMetaFile->AddAction( new MetaBmpScaleAction( rPos, rSize, aBmpEx.GetBitmap() ) );
+    }
+
+    void MtfTools::ResolveBitmapActions( std::vector<BSaveStruct>& rSaveList )
+    {
+        UpdateClipRegion();
+
+        size_t nObjects     = rSaveList.size();
+        size_t nObjectsLeft = nObjects;
+
+        while ( nObjectsLeft )
+        {
+            size_t          i;
+            size_t          nObjectsOfSameSize = 0;
+            size_t          nObjectStartIndex = nObjects - nObjectsLeft;
+
+            BSaveStruct*    pSave = &rSaveList[nObjectStartIndex];
+            tools::Rectangle       aRect( pSave->aOutRect );
+
+            for ( i = nObjectStartIndex; i < nObjects; )
+            {
+                nObjectsOfSameSize++;
+                if ( ++i < nObjects )
+                {
+                    pSave = &rSaveList[i];
+                    if ( pSave->aOutRect != aRect )
+                        break;
+                }
+            }
+            Point   aPos( ImplMap( aRect.TopLeft() ) );
+            Size    aSize( ImplMap( aRect.GetSize() ) );
+
+            for ( i = nObjectStartIndex; i < ( nObjectStartIndex + nObjectsOfSameSize ); i++ )
+            {
+                pSave = &rSaveList[i];
+
+                sal_uInt32  nWinRop = pSave->nWinRop;
+                sal_uInt8   nRasterOperation = static_cast<sal_uInt8>( nWinRop >> 16 );
+
+                sal_uInt32  nUsed =  0;
+                if ( ( nRasterOperation & 0xf )  != ( nRasterOperation >> 4 ) )
+                    nUsed |= 1;     // pattern is used
+                if ( ( nRasterOperation & 0x33 ) != ( ( nRasterOperation & 0xcc ) >> 2 ) )
+                    nUsed |= 2;     // source is used
+                if ( ( nRasterOperation & 0xaa ) != ( ( nRasterOperation & 0x55 ) << 1 ) )
+                    nUsed |= 4;     // destination is used
+
+                if ( (nUsed & 1) && (( nUsed & 2 ) == 0) && nWinRop != PATINVERT )
+                {   // patterns aren't well supported yet
+                    WMFRasterOp nOldRop = SetRasterOp( WMFRasterOp::NONE );  // in this case nRasterOperation is either 0 or 0xff
+                    UpdateFillStyle();
+                    DrawRect( aRect, false );
+                    SetRasterOp( nOldRop );
+                }
+                else
+                {
+                    bool bDrawn = false;
+
+                    if ( i == nObjectStartIndex )   // optimizing, sometimes it is possible to create just one transparent bitmap
+                    {
+                        if ( nObjectsOfSameSize == 2 )
+                        {
+                            BSaveStruct* pSave2 = &rSaveList[i + 1];
+                            if ( ( pSave->aBmpEx.GetPrefSize() == pSave2->aBmpEx.GetPrefSize() ) &&
+                                 ( pSave->aBmpEx.GetPrefMapMode() == pSave2->aBmpEx.GetPrefMapMode() ) )
+                            {
+                                // TODO: Strictly speaking, we should
+                                // check whether mask is monochrome, and
+                                // whether image is black (upper branch)
+                                // or white (lower branch). Otherwise, the
+                                // effect is not the same as a masked
+                                // bitmap.
+                                if ( ( nWinRop == SRCPAINT ) && ( pSave2->nWinRop == SRCAND ) )
+                                {
+                                    Bitmap aMask( pSave->aBmpEx.GetBitmap() ); aMask.Invert();
+                                    BitmapEx aBmpEx( pSave2->aBmpEx.GetBitmap(), aMask );
+                                    ImplDrawBitmap( aPos, aSize, aBmpEx );
+                                    bDrawn = true;
+                                    i++;
+                                }
+                                // #i20085# This is just the other way
+                                // around as above. Only difference: mask
+                                // is inverted
+                                else if ( ( nWinRop == SRCAND ) && ( pSave2->nWinRop == SRCPAINT ) )
+                                {
+                                    const Bitmap & rMask( pSave->aBmpEx.GetBitmap() );
+                                    BitmapEx aBmpEx( pSave2->aBmpEx.GetBitmap(), rMask );
+                                    ImplDrawBitmap( aPos, aSize, aBmpEx );
+                                    bDrawn = true;
+                                    i++;
+                                }
+                                // tdf#90539
+                                else if ( ( nWinRop == SRCAND ) && ( pSave2->nWinRop == SRCINVERT ) )
+                                {
+                                    const Bitmap & rMask( pSave->aBmpEx.GetBitmap() );
+                                    BitmapEx aBmpEx( pSave2->aBmpEx.GetBitmap(), rMask );
+                                    ImplDrawBitmap( aPos, aSize, aBmpEx );
+                                    bDrawn = true;
+                                    i++;
+                                }
+                            }
+                        }
+                    }
+
+                    if ( !bDrawn )
+                    {
+                        Push();
+                        WMFRasterOp nOldRop = SetRasterOp( WMFRasterOp::CopyPen );
+                        Bitmap      aBitmap( pSave->aBmpEx.GetBitmap() );
+                        sal_uInt32  nOperation = ( nRasterOperation & 0xf );
+                        switch( nOperation )
+                        {
+                            case 0x1 :
+                            case 0xe :
+                            {
+                                if(pSave->aBmpEx.IsAlpha())
+                                {
+                                    ImplDrawBitmap( aPos, aSize, pSave->aBmpEx );
+                                }
+                                else
+                                {
+                                    SetRasterOp( WMFRasterOp::XorPen );
+                                    ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) );
+                                    SetRasterOp( WMFRasterOp::CopyPen );
+                                    Bitmap  aMask( aBitmap );
+                                    aMask.Invert();
+                                    BitmapEx aBmpEx( aBitmap, aMask );
+                                    ImplDrawBitmap( aPos, aSize, aBmpEx );
+                                    if ( nOperation == 0x1 )
+                                    {
+                                        SetRasterOp( WMFRasterOp::Not );
+                                        DrawRect( aRect, false );
+                                    }
+                                }
+                            }
+                            break;
+                            case 0x7 :
+                            case 0x8 :
+                            {
+                                Bitmap  aMask( aBitmap );
+                                if ( ( nUsed & 1 ) && ( nRasterOperation & 0xb0 ) == 0xb0 )     // pattern used
+                                {
+                                    aBitmap.Convert( BmpConversion::N24Bit );
+                                    aBitmap.Erase( maFillStyle.aFillColor );
+                                }
+                                BitmapEx aBmpEx( aBitmap, aMask );
+                                ImplDrawBitmap( aPos, aSize, aBmpEx );
+                                if ( nOperation == 0x7 )
+                                {
+                                    SetRasterOp( WMFRasterOp::Not );
+                                    DrawRect( aRect, false );
+                                }
+                            }
+                            break;
+
+                            case 0x4 :
+                            case 0xb :
+                            {
+                                SetRasterOp( WMFRasterOp::Not );
+                                DrawRect( aRect, false );
+                                SetRasterOp( WMFRasterOp::CopyPen );
+                                Bitmap  aMask( aBitmap );
+                                aBitmap.Invert();
+                                BitmapEx aBmpEx( aBitmap, aMask );
+                                ImplDrawBitmap( aPos, aSize, aBmpEx );
+                                SetRasterOp( WMFRasterOp::XorPen );
+                                ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) );
+                                if ( nOperation == 0xb )
+                                {
+                                    SetRasterOp( WMFRasterOp::Not );
+                                    DrawRect( aRect, false );
+                                }
+                            }
+                            break;
+
+                            case 0x2 :
+                            case 0xd :
+                            {
+                                Bitmap  aMask( aBitmap );
+                                aMask.Invert();
+                                BitmapEx aBmpEx( aBitmap, aMask );
+                                ImplDrawBitmap( aPos, aSize, aBmpEx );
+                                SetRasterOp( WMFRasterOp::XorPen );
+                                ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) );
+                                if ( nOperation == 0xd )
+                                {
+                                    SetRasterOp( WMFRasterOp::Not );
+                                    DrawRect( aRect, false );
+                                }
+                            }
+                            break;
+                            case 0x6 :
+                            case 0x9 :
+                            {
+                                SetRasterOp( WMFRasterOp::XorPen );
+                                ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) );
+                                if ( nOperation == 0x9 )
+                                {
+                                    SetRasterOp( WMFRasterOp::Not );
+                                    DrawRect( aRect, false );
+                                }
+                            }
+                            break;
+
+                            case 0x0 :  // WHITENESS
+                            case 0xf :  // BLACKNESS
+                            {                                                   // in this case nRasterOperation is either 0 or 0xff
+                                maFillStyle = WinMtfFillStyle( Color( nRasterOperation, nRasterOperation, nRasterOperation ) );
+                                UpdateFillStyle();
+                                DrawRect( aRect, false );
+                            }
+                            break;
+
+                            case 0x3 :  // only source is used
+                            case 0xc :
+                            {
+                                if ( nRasterOperation == 0x33 )
+                                    aBitmap.Invert();
+                                if (pSave->m_bForceAlpha)
+                                {
+                                    ImplDrawBitmap(aPos, aSize, pSave->aBmpEx);
+                                }
+                                else
+                                {
+                                    ImplDrawBitmap(aPos, aSize, BitmapEx(aBitmap));
+                                }
+                            }
+                            break;
+
+                            case 0x5 :  // only destination is used
+                            {
+                                SetRasterOp( WMFRasterOp::Not );
+                                DrawRect( aRect, false );
+                            }
+                            break;
+
+                            case 0xa :  // no operation
+                            break;
+                        }
+                        SetRasterOp( nOldRop );
+                        Pop();
+                    }
+                }
+            }
+            nObjectsLeft -= nObjectsOfSameSize;
+        }
+
+        rSaveList.clear();
+    }
+
+    void MtfTools::SetDevOrg( const Point& rPoint )
+    {
+        mnDevOrgX = rPoint.X();
+        mnDevOrgY = rPoint.Y();
+    }
+
+    void MtfTools::SetDevOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd )
+    {
+        mnDevOrgX += nXAdd;
+        mnDevOrgY += nYAdd;
+    }
+
+    void MtfTools::SetDevExt( const Size& rSize ,bool regular)
+    {
+        if ( !(rSize.Width() && rSize.Height()) )
+            return;
+
+        switch( meMapMode )
+        {
+            case MappingMode::MM_ISOTROPIC :
+            case MappingMode::MM_ANISOTROPIC :
+            {
+                mnDevWidth = rSize.Width();
+                mnDevHeight = rSize.Height();
+                break;
+            }
+
+            //do nothing
+            default:
+                break;
+        }
+        if (regular)
+        {
+            mbIsMapDevSet=true;
+        }
+    }
+
+    void MtfTools::ScaleDevExt(double fX, double fY)
+    {
+        mnDevWidth = basegfx::fround(mnDevWidth * fX);
+        mnDevHeight = basegfx::fround(mnDevHeight * fY);
+    }
+
+    void MtfTools::SetWinOrg( const Point& rPoint , bool bIsEMF)
+    {
+        mnWinOrgX = rPoint.X();
+        mnWinOrgY = rPoint.Y();
+        if (bIsEMF)
+        {
+            SetDevByWin();
+        }
+        mbIsMapWinSet=true;
+    }
+
+    void MtfTools::SetWinOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd )
+    {
+        mnWinOrgX += nXAdd;
+        mnWinOrgY += nYAdd;
+    }
+
+    void MtfTools::SetDevByWin() //mnWinExt...-stuff has to be assigned before.
+    {
+        if (!mbIsMapDevSet)
+        {
+            if ( meMapMode == MappingMode::MM_ISOTROPIC ) //TODO: WHAT ABOUT ANISOTROPIC???
+            {
+                sal_Int32 nX, nY;
+                if (o3tl::checked_add(mnWinExtX, mnWinOrgX, nX) || o3tl::checked_sub(mnWinExtY, mnWinOrgY, nY))
+                    return;
+                Size aSize(nX >> MS_FIXPOINT_BITCOUNT_28_4, -(nY >> MS_FIXPOINT_BITCOUNT_28_4));
+                SetDevExt(aSize, false);
+            }
+        }
+    }
+
+    void MtfTools::SetWinExt(const Size& rSize, bool bIsEMF)
+    {
+        if (!(rSize.Width() && rSize.Height()))
+            return;
+
+        switch( meMapMode )
+        {
+            case MappingMode::MM_ISOTROPIC :
+            case MappingMode::MM_ANISOTROPIC :
+            {
+                mnWinExtX = rSize.Width();
+                mnWinExtY = rSize.Height();
+                if (bIsEMF)
+                {
+                    SetDevByWin();
+                }
+                mbIsMapWinSet = true;
+                break;
+            }
+
+            default:
+                //do nothing
+                break;
+        }
+    }
+
+    void MtfTools::ScaleWinExt(double fX, double fY)
+    {
+        mnWinExtX = basegfx::fround(mnWinExtX * fX);
+        mnWinExtY = basegfx::fround(mnWinExtY * fY);
+    }
+
+    void MtfTools::SetrclBounds( const tools::Rectangle& rRect )
+    {
+        mrclBounds = rRect;
+    }
+
+    void MtfTools::SetrclFrame( const tools::Rectangle& rRect )
+    {
+        mrclFrame = rRect;
+    }
+
+    void MtfTools::SetRefPix( const Size& rSize )
+    {
+        mnPixX = rSize.Width();
+        mnPixY = rSize.Height();
+    }
+
+    void MtfTools::SetRefMill( const Size& rSize )
+    {
+        mnMillX = rSize.Width();
+        mnMillY = rSize.Height();
+    }
+
+    void MtfTools::SetMapMode( MappingMode nMapMode )
+    {
+        meMapMode = nMapMode;
+        if ( nMapMode == MappingMode::MM_TEXT && !mbIsMapWinSet )
+        {
+            mnWinExtX = mnDevWidth;
+            mnWinExtY = mnDevHeight;
+        }
+        else if ( meMapMode == MappingMode::MM_HIMETRIC )
+        {
+            sal_Int32 nWinExtX, nWinExtY;
+            if (o3tl::checked_multiply<sal_Int32>(mnMillX, 100, nWinExtX) ||
+                o3tl::checked_multiply<sal_Int32>(mnMillY, 100, nWinExtY))
+            {
+                return;
+            }
+            mnWinExtX = nWinExtX;
+            mnWinExtY = nWinExtY;
+        }
+    }
+
+    void MtfTools::SetWorldTransform( const XForm& rXForm )
+    {
+        maXForm.eM11 = rXForm.eM11;
+        maXForm.eM12 = rXForm.eM12;
+        maXForm.eM21 = rXForm.eM21;
+        maXForm.eM22 = rXForm.eM22;
+        maXForm.eDx = rXForm.eDx;
+        maXForm.eDy = rXForm.eDy;
+    }
+
+    void MtfTools::ModifyWorldTransform( const XForm& rXForm, ModifyWorldTransformMode nMode )
+    {
+        switch( nMode )
+        {
+            case ModifyWorldTransformMode::MWT_IDENTITY :
+            {
+                maXForm.eM11 = maXForm.eM22 = 1.0f;
+                maXForm.eM12 = maXForm.eM21 = maXForm.eDx = maXForm.eDy = 0.0f;
+                break;
+            }
+
+            case ModifyWorldTransformMode::MWT_RIGHTMULTIPLY :
+            case ModifyWorldTransformMode::MWT_LEFTMULTIPLY :
+            {
+                const XForm* pLeft;
+                const XForm* pRight;
+
+                if ( nMode == ModifyWorldTransformMode::MWT_LEFTMULTIPLY )
+                {
+                    pLeft = &rXForm;
+                    pRight = &maXForm;
+                }
+                else
+                {
+                    pLeft = &maXForm;
+                    pRight = &rXForm;
+                }
+
+                float aF[3][3];
+                float bF[3][3];
+                float cF[3][3];
+
+                aF[0][0] = pLeft->eM11;
+                aF[0][1] = pLeft->eM12;
+                aF[0][2] = 0;
+                aF[1][0] = pLeft->eM21;
+                aF[1][1] = pLeft->eM22;
+                aF[1][2] = 0;
+                aF[2][0] = pLeft->eDx;
+                aF[2][1] = pLeft->eDy;
+                aF[2][2] = 1;
+
+                bF[0][0] = pRight->eM11;
+                bF[0][1] = pRight->eM12;
+                bF[0][2] = 0;
+                bF[1][0] = pRight->eM21;
+                bF[1][1] = pRight->eM22;
+                bF[1][2] = 0;
+                bF[2][0] = pRight->eDx;
+                bF[2][1] = pRight->eDy;
+                bF[2][2] = 1;
+
+                int i, j, k;
+                for ( i = 0; i < 3; i++ )
+                {
+                  for ( j = 0; j < 3; j++ )
+                  {
+                     cF[i][j] = 0;
+                     for ( k = 0; k < 3; k++ )
+                        cF[i][j] += aF[i][k] * bF[k][j];
+                  }
+                }
+                maXForm.eM11 = cF[0][0];
+                maXForm.eM12 = cF[0][1];
+                maXForm.eM21 = cF[1][0];
+                maXForm.eM22 = cF[1][1];
+                maXForm.eDx = cF[2][0];
+                maXForm.eDy = cF[2][1];
+                break;
+            }
+            case ModifyWorldTransformMode::MWT_SET:
+            {
+                SetWorldTransform(rXForm);
+                break;
+            }
+        }
+    }
+
+    void MtfTools::Push()                       // !! to be able to access the original ClipRegion it
+    {                                               // is not allowed to use the MetaPushAction()
+        UpdateClipRegion();                         // (the original clip region is on top of the stack) (SJ)
+        auto pSave = std::make_shared<SaveStruct>();
+
+        pSave->aLineStyle = maLineStyle;
+        pSave->aFillStyle = maFillStyle;
+
+        pSave->aFont = maFont;
+        pSave->aTextColor = maTextColor;
+        pSave->nTextAlign = mnTextAlign;
+        pSave->nTextLayoutMode = mnTextLayoutMode;
+        pSave->eMapMode = meMapMode;
+        pSave->eGfxMode = meGfxMode;
+        pSave->nBkMode = mnBkMode;
+        pSave->aBkColor = maBkColor;
+        pSave->bClockWiseArcDirection = mbClockWiseArcDirection;
+        pSave->bFillStyleSelected = mbFillStyleSelected;
+
+        pSave->aActPos = maActPos;
+        pSave->aXForm = maXForm;
+        pSave->eRasterOp = meRasterOp;
+
+        pSave->nWinOrgX = mnWinOrgX;
+        pSave->nWinOrgY = mnWinOrgY;
+        pSave->nWinExtX = mnWinExtX;
+        pSave->nWinExtY = mnWinExtY;
+        pSave->nDevOrgX = mnDevOrgX;
+        pSave->nDevOrgY = mnDevOrgY;
+        pSave->nDevWidth = mnDevWidth;
+        pSave->nDevHeight = mnDevHeight;
+
+        pSave->maPathObj = maPathObj;
+        pSave->maClipPath = maClipPath;
+
+        SAL_INFO("emfio", "\t\t GfxMode: " << static_cast<sal_uInt32>(meGfxMode));
+        SAL_INFO("emfio", "\t\t MapMode: " << static_cast<sal_uInt32>(meMapMode));
+        SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY);
+        SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY);
+        SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY);
+        SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight);
+        SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor );
+        mvSaveStack.push_back( pSave );
+    }
+
+    void MtfTools::Pop( const sal_Int32 nSavedDC )
+    {
+        if ( nSavedDC == 0 )
+            return;
+
+        sal_Int32 aIndex;
+        if ( nSavedDC < 0 ) // WMF/EMF, if negative, nSavedDC represents an instance relative to the current state.
+            aIndex = static_cast< sal_Int32 >( mvSaveStack.size() ) + nSavedDC;
+        else
+            aIndex = nSavedDC; // WMF, if positive, nSavedDC represents a specific instance of the state to be restored.
+        if( aIndex < 0 )
+        {
+            mvSaveStack.clear();
+            return;
+        }
+        if( mvSaveStack.empty() || ( aIndex >= static_cast< sal_Int32 >( mvSaveStack.size() ) ) )
+            return;
+
+        mvSaveStack.resize( aIndex + 1 );
+        // Backup the current data on the stack
+        std::shared_ptr<SaveStruct>& pSave( mvSaveStack.back() );
+
+        maLineStyle = pSave->aLineStyle;
+        maFillStyle = pSave->aFillStyle;
+
+        maFont = pSave->aFont;
+        maTextColor = pSave->aTextColor;
+        mnTextAlign = pSave->nTextAlign;
+        mnTextLayoutMode = pSave->nTextLayoutMode;
+        mnBkMode = pSave->nBkMode;
+        meGfxMode = pSave->eGfxMode;
+        meMapMode = pSave->eMapMode;
+        maBkColor = pSave->aBkColor;
+        mbClockWiseArcDirection = pSave->bClockWiseArcDirection;
+        mbFillStyleSelected = pSave->bFillStyleSelected;
+
+        maActPos = pSave->aActPos;
+        maXForm = pSave->aXForm;
+        meRasterOp = pSave->eRasterOp;
+
+        mnWinOrgX = pSave->nWinOrgX;
+        mnWinOrgY = pSave->nWinOrgY;
+        mnWinExtX = pSave->nWinExtX;
+        mnWinExtY = pSave->nWinExtY;
+        mnDevOrgX = pSave->nDevOrgX;
+        mnDevOrgY = pSave->nDevOrgY;
+        mnDevWidth = pSave->nDevWidth;
+        mnDevHeight = pSave->nDevHeight;
+
+        maPathObj = pSave->maPathObj;
+        if ( ! ( maClipPath == pSave->maClipPath ) )
+        {
+            maClipPath = pSave->maClipPath;
+            mbClipNeedsUpdate = true;
+        }
+        if ( meLatestRasterOp != meRasterOp )
+        {
+            mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) );
+            meLatestRasterOp = meRasterOp;
+        }
+
+        SAL_INFO("emfio", "\t\t GfxMode: " << static_cast<sal_uInt32>(meGfxMode));
+        SAL_INFO("emfio", "\t\t MapMode: " << static_cast<sal_uInt32>(meMapMode));
+        SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY);
+        SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY);
+        SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY);
+        SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight);
+        SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor );
+        mvSaveStack.pop_back();
+    }
+
+    void MtfTools::AddFromGDIMetaFile( GDIMetaFile& rGDIMetaFile )
+    {
+       rGDIMetaFile.Play( *mpGDIMetaFile );
+    }
+
+    void MtfTools::PassEMFPlusHeaderInfo()
+    {
+        EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS header info\n"));
+
+        SvMemoryStream mem;
+        sal_Int32 nLeft, nRight, nTop, nBottom;
+
+        nLeft = mrclFrame.Left();
+        nTop = mrclFrame.Top();
+        nRight = mrclFrame.Right();
+        nBottom = mrclFrame.Bottom();
+
+        // emf header info
+        mem.WriteInt32( nLeft ).WriteInt32( nTop ).WriteInt32( nRight ).WriteInt32( nBottom );
+        mem.WriteInt32( mnPixX ).WriteInt32( mnPixY ).WriteInt32( mnMillX ).WriteInt32( mnMillY );
+
+        float one, zero;
+
+        one = 1;
+        zero = 0;
+
+        // add transformation matrix to be used in vcl's metaact.cxx for
+        // rotate and scale operations
+        mem.WriteFloat( one ).WriteFloat( zero ).WriteFloat( zero ).WriteFloat( one ).WriteFloat( zero ).WriteFloat( zero );
+
+        // need to flush the stream, otherwise GetEndOfData will return 0
+        // on windows where the function parameters are probably resolved in reverse order
+        mem.Flush();
+
+        mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS_HEADER_INFO", 0, static_cast<const sal_uInt8*>(mem.GetData()), mem.GetEndOfData() ) );
+        mpGDIMetaFile->UseCanvas( true );
+    }
+
+    void MtfTools::PassEMFPlus( void const * pBuffer, sal_uInt32 nLength )
+    {
+        EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS comment length %04x\n",(unsigned int) nLength));
+        mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS", 0, static_cast<const sal_uInt8*>(pBuffer), nLength ) );
+    }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/emfio/source/reader/wmfreader.cxx b/emfio/source/reader/wmfreader.cxx
new file mode 100644
index 000000000..bb79033a3
--- /dev/null
+++ b/emfio/source/reader/wmfreader.cxx
@@ -0,0 +1,2015 @@
+/* -*- 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 <wmfreader.hxx>
+#include <emfreader.hxx>
+
+#include <cstdlib>
+#include <memory>
+#include <optional>
+#include <o3tl/safeint.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <rtl/crc.h>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <osl/endian.h>
+#include <vcl/gdimtf.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/wmfexternal.hxx>
+#include <tools/fract.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <osl/thread.h>
+
+namespace
+{
+    // MS Windows defines
+    enum WMFRecords
+    {
+        W_META_EOF                  = 0x0000,
+        W_META_SETBKCOLOR           = 0x0201,
+        W_META_SETBKMODE            = 0x0102,
+        W_META_SETMAPMODE           = 0x0103,
+        W_META_SETROP2              = 0x0104,
+        W_META_SETRELABS            = 0x0105,
+        W_META_SETPOLYFILLMODE      = 0x0106,
+        W_META_SETSTRETCHBLTMODE    = 0x0107,
+        W_META_SETTEXTCHAREXTRA     = 0x0108,
+        W_META_SETTEXTCOLOR         = 0x0209,
+        W_META_SETTEXTJUSTIFICATION = 0x020A,
+        W_META_SETWINDOWORG         = 0x020B,
+        W_META_SETWINDOWEXT         = 0x020C,
+        W_META_SETVIEWPORTORG       = 0x020D,
+        W_META_SETVIEWPORTEXT       = 0x020E,
+        W_META_OFFSETWINDOWORG      = 0x020F,
+        W_META_SCALEWINDOWEXT       = 0x0410,
+        W_META_OFFSETVIEWPORTORG    = 0x0211,
+        W_META_SCALEVIEWPORTEXT     = 0x0412,
+        W_META_LINETO               = 0x0213,
+        W_META_MOVETO               = 0x0214,
+        W_META_EXCLUDECLIPRECT      = 0x0415,
+        W_META_INTERSECTCLIPRECT    = 0x0416,
+        W_META_ARC                  = 0x0817,
+        W_META_ELLIPSE              = 0x0418,
+        W_META_FLOODFILL            = 0x0419,
+        W_META_PIE                  = 0x081A,
+        W_META_RECTANGLE            = 0x041B,
+        W_META_ROUNDRECT            = 0x061C,
+        W_META_PATBLT               = 0x061D,
+        W_META_SAVEDC               = 0x001E,
+        W_META_SETPIXEL             = 0x041F,
+        W_META_OFFSETCLIPRGN        = 0x0220,
+        W_META_TEXTOUT              = 0x0521,
+        W_META_BITBLT               = 0x0922,
+        W_META_STRETCHBLT           = 0x0B23,
+        W_META_POLYGON              = 0x0324,
+        W_META_POLYLINE             = 0x0325,
+        W_META_ESCAPE               = 0x0626,
+        W_META_RESTOREDC            = 0x0127,
+        W_META_FILLREGION           = 0x0228,
+        W_META_FRAMEREGION          = 0x0429,
+        W_META_INVERTREGION         = 0x012A,
+        W_META_PAINTREGION          = 0x012B,
+        W_META_SELECTCLIPREGION     = 0x012C,
+        W_META_SELECTOBJECT         = 0x012D,
+        W_META_SETTEXTALIGN         = 0x012E,
+        W_META_DRAWTEXT             = 0x062F,
+        W_META_CHORD                = 0x0830,
+        W_META_SETMAPPERFLAGS       = 0x0231,
+        W_META_EXTTEXTOUT           = 0x0a32,
+        W_META_SETDIBTODEV          = 0x0d33,
+        W_META_SELECTPALETTE        = 0x0234,
+        W_META_REALIZEPALETTE       = 0x0035,
+        W_META_ANIMATEPALETTE       = 0x0436,
+        W_META_SETPALENTRIES        = 0x0037,
+        W_META_POLYPOLYGON          = 0x0538,
+        W_META_RESIZEPALETTE        = 0x0139,
+        W_META_DIBBITBLT            = 0x0940,
+        W_META_DIBSTRETCHBLT        = 0x0b41,
+        W_META_DIBCREATEPATTERNBRUSH = 0x0142,
+        W_META_STRETCHDIB           = 0x0f43,
+        W_META_EXTFLOODFILL         = 0x0548,
+        W_META_RESETDC              = 0x014C,
+        W_META_STARTDOC             = 0x014D,
+        W_META_STARTPAGE            = 0x004F,
+        W_META_ENDPAGE              = 0x0050,
+        W_META_ABORTDOC             = 0x0052,
+        W_META_ENDDOC               = 0x005E,
+        W_META_DELETEOBJECT         = 0x01f0,
+        W_META_CREATEPALETTE        = 0x00f7,
+        W_META_CREATEBRUSH          = 0x00F8,
+        W_META_CREATEPATTERNBRUSH   = 0x01F9,
+        W_META_CREATEPENINDIRECT    = 0x02FA,
+        W_META_CREATEFONTINDIRECT   = 0x02FB,
+        W_META_CREATEBRUSHINDIRECT  = 0x02FC,
+        W_META_CREATEBITMAPINDIRECT = 0x02FD,
+        W_META_CREATEBITMAP         = 0x06FE,
+        W_META_CREATEREGION         = 0x06FF
+    };
+
+    void GetWinExtMax(const Point& rSource, tools::Rectangle& rPlaceableBound, emfio::MappingMode eMapMode)
+    {
+        Point aSource(rSource);
+        if (eMapMode == emfio::MappingMode::MM_HIMETRIC)
+            aSource.setY( -rSource.Y() );
+        if (aSource.X() < rPlaceableBound.Left())
+            rPlaceableBound.SetLeft( aSource.X() );
+        if (aSource.X() > rPlaceableBound.Right())
+            rPlaceableBound.SetRight( aSource.X() );
+        if (aSource.Y() < rPlaceableBound.Top())
+            rPlaceableBound.SetTop( aSource.Y() );
+        if (aSource.Y() > rPlaceableBound.Bottom())
+            rPlaceableBound.SetBottom( aSource.Y() );
+    }
+
+    void GetWinExtMax(const tools::Rectangle& rSource, tools::Rectangle& rPlaceableBound, emfio::MappingMode nMapMode)
+    {
+        GetWinExtMax(rSource.TopLeft(), rPlaceableBound, nMapMode);
+        GetWinExtMax(rSource.BottomRight(), rPlaceableBound, nMapMode);
+    }
+
+    const char *
+    record_type_name(sal_uInt16 nRecType)
+    {
+    #ifndef SAL_LOG_INFO
+        (void) nRecType;
+        return "";
+    #else
+        switch( nRecType )
+        {
+        case W_META_SETBKCOLOR: return "META_SETBKCOLOR";
+        case W_META_SETBKMODE: return "META_SETBKMODE";
+        case W_META_SETMAPMODE: return "META_SETMAPMODE";
+        case W_META_SETROP2: return "META_SETROP2";
+        case W_META_SETRELABS: return "META_SETRELABS";
+        case W_META_SETPOLYFILLMODE: return "META_SETPOLYFILLMODE";
+        case W_META_SETSTRETCHBLTMODE: return "META_SETSTRETCHBLTMODE";
+        case W_META_SETTEXTCHAREXTRA: return "META_SETTEXTCHAREXTRA";
+        case W_META_SETTEXTCOLOR: return "META_SETTEXTCOLOR";
+        case W_META_SETTEXTJUSTIFICATION: return "META_SETTEXTJUSTIFICATION";
+        case W_META_SETWINDOWORG: return "META_SETWINDOWORG";
+        case W_META_SETWINDOWEXT: return "META_SETWINDOWEXT";
+        case W_META_SETVIEWPORTORG: return "META_SETVIEWPORTORG";
+        case W_META_SETVIEWPORTEXT: return "META_SETVIEWPORTEXT";
+        case W_META_OFFSETWINDOWORG: return "META_OFFSETWINDOWORG";
+        case W_META_SCALEWINDOWEXT: return "META_SCALEWINDOWEXT";
+        case W_META_OFFSETVIEWPORTORG: return "META_OFFSETVIEWPORTORG";
+        case W_META_SCALEVIEWPORTEXT: return "META_SCALEVIEWPORTEXT";
+        case W_META_LINETO: return "META_LINETO";
+        case W_META_MOVETO: return "META_MOVETO";
+        case W_META_EXCLUDECLIPRECT: return "META_EXCLUDECLIPRECT";
+        case W_META_INTERSECTCLIPRECT: return "META_INTERSECTCLIPRECT";
+        case W_META_ARC: return "META_ARC";
+        case W_META_ELLIPSE: return "META_ELLIPSE";
+        case W_META_FLOODFILL: return "META_FLOODFILL";
+        case W_META_PIE: return "META_PIE";
+        case W_META_RECTANGLE: return "META_RECTANGLE";
+        case W_META_ROUNDRECT: return "META_ROUNDRECT";
+        case W_META_PATBLT: return "META_PATBLT";
+        case W_META_SAVEDC: return "META_SAVEDC";
+        case W_META_SETPIXEL: return "META_SETPIXEL";
+        case W_META_OFFSETCLIPRGN: return "META_OFFSETCLIPRGN";
+        case W_META_TEXTOUT: return "META_TEXTOUT";
+        case W_META_BITBLT: return "META_BITBLT";
+        case W_META_STRETCHBLT: return "META_STRETCHBLT";
+        case W_META_POLYGON: return "META_POLYGON";
+        case W_META_POLYLINE: return "META_POLYLINE";
+        case W_META_ESCAPE: return "META_ESCAPE";
+        case W_META_RESTOREDC: return "META_RESTOREDC";
+        case W_META_FILLREGION: return "META_FILLREGION";
+        case W_META_FRAMEREGION: return "META_FRAMEREGION";
+        case W_META_INVERTREGION: return "META_INVERTREGION";
+        case W_META_PAINTREGION: return "META_PAINTREGION";
+        case W_META_SELECTCLIPREGION: return "META_SELECTCLIPREGION";
+        case W_META_SELECTOBJECT: return "META_SELECTOBJECT";
+        case W_META_SETTEXTALIGN: return "META_SETTEXTALIGN";
+        case W_META_DRAWTEXT: return "META_DRAWTEXT";
+        case W_META_CHORD: return "META_CHORD";
+        case W_META_SETMAPPERFLAGS: return "META_SETMAPPERFLAGS";
+        case W_META_EXTTEXTOUT: return "META_EXTTEXTOUT";
+        case W_META_SETDIBTODEV: return "META_SETDIBTODEV";
+        case W_META_SELECTPALETTE: return "META_SELECTPALETTE";
+        case W_META_REALIZEPALETTE: return "META_REALIZEPALETTE";
+        case W_META_ANIMATEPALETTE: return "META_ANIMATEPALETTE";
+        case W_META_SETPALENTRIES: return "META_SETPALENTRIES";
+        case W_META_POLYPOLYGON: return "META_POLYPOLYGON";
+        case W_META_RESIZEPALETTE: return "META_RESIZEPALETTE";
+        case W_META_DIBBITBLT: return "META_DIBBITBLT";
+        case W_META_DIBSTRETCHBLT: return "META_DIBSTRETCHBLT";
+        case W_META_DIBCREATEPATTERNBRUSH: return "META_DIBCREATEPATTERNBRUSH";
+        case W_META_STRETCHDIB: return "META_STRETCHDIB";
+        case W_META_EXTFLOODFILL: return "META_EXTFLOODFILL";
+        case W_META_RESETDC: return "META_RESETDC";
+        case W_META_STARTDOC: return "META_STARTDOC";
+        case W_META_STARTPAGE: return "META_STARTPAGE";
+        case W_META_ENDPAGE: return "META_ENDPAGE";
+        case W_META_ABORTDOC: return "META_ABORTDOC";
+        case W_META_ENDDOC: return "META_ENDDOC";
+        case W_META_DELETEOBJECT: return "META_DELETEOBJECT";
+        case W_META_CREATEPALETTE: return "META_CREATEPALETTE";
+        case W_META_CREATEBRUSH: return "META_CREATEBRUSH";
+        case W_META_CREATEPATTERNBRUSH: return "META_CREATEPATTERNBRUSH";
+        case W_META_CREATEPENINDIRECT: return "META_CREATEPENINDIRECT";
+        case W_META_CREATEFONTINDIRECT: return "META_CREATEFONTINDIRECT";
+        case W_META_CREATEBRUSHINDIRECT: return "META_CREATEBRUSHINDIRECT";
+        case W_META_CREATEBITMAPINDIRECT: return "META_CREATEBITMAPINDIRECT";
+        case W_META_CREATEBITMAP: return "META_CREATEBITMAP";
+        case W_META_CREATEREGION: return "META_CREATEREGION";
+        default:
+            // Yes, return a pointer to a static buffer. This is a very
+            // local debugging output function, so no big deal.
+            static char buffer[11];
+            sprintf(buffer, "0x%08" SAL_PRIxUINT32, sal_uInt32(nRecType));
+            return buffer;
+        }
+    #endif
+    }
+
+}
+
+namespace emfio
+{
+    inline Point WmfReader::ReadPoint()
+    {
+        short nX = 0, nY = 0;
+        mpInputStream->ReadInt16( nX ).ReadInt16( nY );
+        return Point( nX, nY );
+    }
+
+    inline Point WmfReader::ReadYX()
+    {
+        short nX = 0, nY = 0;
+        mpInputStream->ReadInt16( nY ).ReadInt16( nX );
+        return Point( nX, nY );
+    }
+
+    tools::Rectangle WmfReader::ReadRectangle()
+    {
+        Point aBR, aTL;
+        aBR = ReadYX();
+        aTL = ReadYX();
+        aBR.AdjustX( -1 );
+        aBR.AdjustY( -1 );
+        if (aTL.X() > aBR.X() || aTL.Y() > aBR.Y())
+        {
+            SAL_WARN("emfio", "broken rectangle");
+            return tools::Rectangle::Justify(aTL, aBR);
+        }
+        return tools::Rectangle( aTL, aBR );
+    }
+
+    Size WmfReader::ReadYXExt()
+    {
+        short nW=0, nH=0;
+        mpInputStream->ReadInt16( nH ).ReadInt16( nW );
+        return Size( nW, nH );
+    }
+
+    void WmfReader::ReadRecordParams( sal_uInt32 nRecordSize, sal_uInt16 nFunc )
+    {
+        SAL_INFO("emfio", "\t" << record_type_name(nFunc));
+        switch( nFunc )
+        {
+            case W_META_SETBKCOLOR:
+            {
+                SetBkColor( ReadColor() );
+            }
+            break;
+
+            case W_META_SETBKMODE:
+            {
+                sal_uInt16 nDat = 0;
+                mpInputStream->ReadUInt16( nDat );
+                SetBkMode( static_cast<BackgroundMode>(nDat) );
+            }
+            break;
+
+            // !!!
+            case W_META_SETMAPMODE:
+            {
+                sal_Int16 nMapMode = 0;
+                mpInputStream->ReadInt16( nMapMode );
+                SetMapMode( static_cast<MappingMode>(nMapMode) );
+            }
+            break;
+
+            case W_META_SETROP2:
+            {
+                sal_uInt16 nROP2 = 0;
+                mpInputStream->ReadUInt16( nROP2 );
+                SetRasterOp( static_cast<WMFRasterOp>(nROP2) );
+            }
+            break;
+
+            case W_META_SETTEXTCOLOR:
+            {
+                SetTextColor( ReadColor() );
+            }
+            break;
+
+            case W_META_SETWINDOWORG:
+            {
+                SetWinOrg( ReadYX() );
+            }
+            break;
+
+            case W_META_SETWINDOWEXT:
+            {
+                short nWidth = 0, nHeight = 0;
+                mpInputStream->ReadInt16( nHeight ).ReadInt16( nWidth );
+                SetWinExt( Size( nWidth, nHeight ) );
+            }
+            break;
+
+            case W_META_OFFSETWINDOWORG:
+            {
+                short nXAdd = 0, nYAdd = 0;
+                mpInputStream->ReadInt16( nYAdd ).ReadInt16( nXAdd );
+                SetWinOrgOffset( nXAdd, nYAdd );
+            }
+            break;
+
+            case W_META_SCALEWINDOWEXT:
+            {
+                short nXNum = 0, nXDenom = 0, nYNum = 0, nYDenom = 0;
+                mpInputStream->ReadInt16( nYDenom ).ReadInt16( nYNum ).ReadInt16( nXDenom ).ReadInt16( nXNum );
+                if (!nYDenom || !nXDenom)
+                {
+                    mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                    break;
+                }
+                ScaleWinExt( static_cast<double>(nXNum) / nXDenom, static_cast<double>(nYNum) / nYDenom );
+            }
+            break;
+
+            case W_META_SETVIEWPORTORG:
+            case W_META_SETVIEWPORTEXT:
+            break;
+
+            case W_META_OFFSETVIEWPORTORG:
+            {
+                short nXAdd = 0, nYAdd = 0;
+                mpInputStream->ReadInt16( nYAdd ).ReadInt16( nXAdd );
+                SetDevOrgOffset( nXAdd, nYAdd );
+            }
+            break;
+
+            case W_META_SCALEVIEWPORTEXT:
+            {
+                short nXNum = 0, nXDenom = 0, nYNum = 0, nYDenom = 0;
+                mpInputStream->ReadInt16( nYDenom ).ReadInt16( nYNum ).ReadInt16( nXDenom ).ReadInt16( nXNum );
+                if (!nYDenom || !nXDenom)
+                {
+                    mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                    break;
+                }
+                ScaleDevExt( static_cast<double>(nXNum) / nXDenom, static_cast<double>(nYNum) / nYDenom );
+            }
+            break;
+
+            case W_META_LINETO:
+            {
+                LineTo( ReadYX() );
+            }
+            break;
+
+            case W_META_MOVETO:
+            {
+                MoveTo( ReadYX() );
+            }
+            break;
+
+            case W_META_INTERSECTCLIPRECT:
+            {
+                IntersectClipRect( ReadRectangle() );
+            }
+            break;
+
+            case W_META_RECTANGLE:
+            {
+                DrawRect( ReadRectangle() );
+            }
+            break;
+
+            case W_META_ROUNDRECT:
+            {
+                Size aSize( ReadYXExt() );
+                DrawRoundRect( ReadRectangle(), Size( aSize.Width() / 2, aSize.Height() / 2 ) );
+            }
+            break;
+
+            case W_META_ELLIPSE:
+            {
+                DrawEllipse( ReadRectangle() );
+            }
+            break;
+
+            case W_META_ARC:
+            {
+                Point aEnd( ReadYX() );
+                Point aStart( ReadYX() );
+                tools::Rectangle aRect( ReadRectangle() );
+                aRect.Justify();
+                DrawArc( aRect, aStart, aEnd );
+            }
+            break;
+
+            case W_META_PIE:
+            {
+                Point     aEnd( ReadYX() );
+                Point     aStart( ReadYX() );
+                tools::Rectangle aRect( ReadRectangle() );
+                aRect.Justify();
+
+                // #i73608# OutputDevice deviates from WMF
+                // semantics. start==end means full ellipse here.
+                if( aStart == aEnd )
+                    DrawEllipse( aRect );
+                else
+                    DrawPie( aRect, aStart, aEnd );
+            }
+            break;
+
+            case W_META_CHORD:
+            {
+                Point aEnd( ReadYX() );
+                Point aStart( ReadYX() );
+                tools::Rectangle aRect( ReadRectangle() );
+                aRect.Justify();
+                DrawChord( aRect, aStart, aEnd );
+            }
+            break;
+
+            case W_META_POLYGON:
+            {
+                bool bRecordOk = true;
+
+                sal_uInt16 nPoints(0);
+                mpInputStream->ReadUInt16(nPoints);
+
+                if (nPoints > mpInputStream->remainingSize() / (2 * sizeof(sal_uInt16)))
+                {
+                    bRecordOk = false;
+                }
+                else
+                {
+                    tools::Polygon aPoly(nPoints);
+                    for (sal_uInt16 i(0); i < nPoints && mpInputStream->good(); ++i)
+                        aPoly[ i ] = ReadPoint();
+                    DrawPolygon(std::move(aPoly), false/*bRecordPath*/);
+                }
+
+                SAL_WARN_IF(!bRecordOk, "emfio", "polygon record has more points than we can handle");
+
+                bRecordOk &= mpInputStream->good();
+
+                if (!bRecordOk)
+                {
+                    mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                    break;
+                }
+            }
+            break;
+
+            case W_META_POLYPOLYGON:
+            {
+                sal_uInt16 nPolyCount(0);
+                // Number of polygons:
+                mpInputStream->ReadUInt16( nPolyCount );
+                if (nPolyCount && mpInputStream->good())
+                {
+                    bool bRecordOk = true;
+                    if (nPolyCount > mpInputStream->remainingSize() / sizeof(sal_uInt16))
+                    {
+                        break;
+                    }
+
+                    // Number of points of each polygon. Determine total number of points
+                    std::unique_ptr<sal_uInt16[]> xPolygonPointCounts(new sal_uInt16[nPolyCount]);
+                    sal_uInt16* pnPoints = xPolygonPointCounts.get();
+                    tools::PolyPolygon aPolyPoly(nPolyCount);
+                    sal_uInt16 nPoints = 0;
+                    for (sal_uInt16 a = 0; a < nPolyCount && mpInputStream->good(); ++a)
+                    {
+                        mpInputStream->ReadUInt16( pnPoints[a] );
+
+                        if (pnPoints[a] > SAL_MAX_UINT16 - nPoints)
+                        {
+                            bRecordOk = false;
+                            break;
+                        }
+
+                        nPoints += pnPoints[a];
+                    }
+
+                    SAL_WARN_IF(!bRecordOk, "emfio", "polypolygon record has more polygons than we can handle");
+
+                    bRecordOk &= mpInputStream->good();
+
+                    if (!bRecordOk)
+                    {
+                        mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                        break;
+                    }
+
+                    // Polygon points are:
+                    for (sal_uInt16 a = 0; a < nPolyCount && mpInputStream->good(); ++a)
+                    {
+                        const sal_uInt16 nPointCount(pnPoints[a]);
+
+                        if (nPointCount > mpInputStream->remainingSize() / (2 * sizeof(sal_uInt16)))
+                        {
+                            bRecordOk = false;
+                            break;
+                        }
+
+                        std::unique_ptr<Point[]> xPolygonPoints(new Point[nPointCount]);
+                        Point* pPtAry = xPolygonPoints.get();
+
+                        for(sal_uInt16 b(0); b < nPointCount && mpInputStream->good(); ++b)
+                        {
+                            pPtAry[b] = ReadPoint();
+                        }
+
+                        aPolyPoly.Insert( tools::Polygon(nPointCount, pPtAry) );
+                    }
+
+                    bRecordOk &= mpInputStream->good();
+
+                    if (!bRecordOk)
+                    {
+                        mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                        break;
+                    }
+
+                    DrawPolyPolygon( aPolyPoly );
+                }
+            }
+            break;
+
+            case W_META_POLYLINE:
+            {
+                bool bRecordOk = true;
+
+                sal_uInt16 nPoints(0);
+                mpInputStream->ReadUInt16(nPoints);
+
+                if (nPoints > mpInputStream->remainingSize() / (2 * sizeof(sal_uInt16)))
+                {
+                    bRecordOk = false;
+                }
+                else
+                {
+                    tools::Polygon aPoly(nPoints);
+                    for (sal_uInt16 i(0); i < nPoints && mpInputStream->good(); ++i)
+                        aPoly[ i ] = ReadPoint();
+                    DrawPolyLine( std::move(aPoly) );
+                }
+
+                SAL_WARN_IF(!bRecordOk, "emfio", "polyline record has more points than we can handle");
+
+                bRecordOk &= mpInputStream->good();
+
+                if (!bRecordOk)
+                {
+                    mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                    break;
+                }
+            }
+            break;
+
+            case W_META_SAVEDC:
+            {
+                Push();
+            }
+            break;
+
+            case W_META_RESTOREDC:
+            {
+                sal_Int16 nSavedDC(0);
+                mpInputStream->ReadInt16( nSavedDC );
+                SAL_INFO( "emfio", "\t\t SavedDC: " << nSavedDC );
+                Pop( nSavedDC );
+            }
+            break;
+
+            case W_META_SETPIXEL:
+            {
+                const Color aColor = ReadColor();
+                DrawPixel( ReadYX(), aColor );
+            }
+            break;
+
+            case W_META_OFFSETCLIPRGN:
+            {
+                MoveClipRegion( ReadYXExt() );
+            }
+            break;
+
+            case W_META_TEXTOUT:
+            {
+                //record is Recordsize, RecordFunction, StringLength, <String>, YStart, XStart
+                const sal_uInt32 nNonStringLen = sizeof(sal_uInt32) + 4 * sizeof(sal_uInt16);
+                const sal_uInt32 nRecSize = mnRecSize * 2;
+
+                if (nRecSize < nNonStringLen)
+                {
+                    SAL_WARN("emfio", "W_META_TEXTOUT too short");
+                    break;
+                }
+
+                sal_uInt16 nLength = 0;
+                mpInputStream->ReadUInt16(nLength);
+                sal_uInt16 nStoredLength = (nLength + 1) &~ 1;
+
+                if (nRecSize - nNonStringLen < nStoredLength)
+                {
+                    SAL_WARN("emfio", "W_META_TEXTOUT too short, truncating string");
+                    nLength = nStoredLength = nRecSize - nNonStringLen;
+                }
+
+                if (nLength)
+                {
+                    std::vector<char> aChars(nStoredLength);
+                    nLength = std::min<sal_uInt16>(nLength, mpInputStream->ReadBytes(aChars.data(), aChars.size()));
+                    OUString aText(aChars.data(), nLength, GetCharSet());
+                    Point aPosition( ReadYX() );
+                    DrawText( aPosition, aText );
+                }
+            }
+            break;
+
+            case W_META_EXTTEXTOUT:
+            {
+                //record is Recordsize, RecordFunction, Y, X, StringLength, options, maybe rectangle, <String>
+                sal_uInt32 nNonStringLen = sizeof(sal_uInt32) + 5 * sizeof(sal_uInt16);
+                const sal_uInt32 nRecSize = mnRecSize * 2;
+
+                if (nRecSize < nNonStringLen)
+                {
+                    SAL_WARN("emfio", "W_META_EXTTEXTOUT too short");
+                    break;
+                }
+
+                auto nRecordPos = mpInputStream->Tell() - 6;
+                Point aPosition = ReadYX();
+                sal_uInt16 nLen = 0, nOptions = 0;
+                mpInputStream->ReadUInt16( nLen ).ReadUInt16( nOptions );
+                SAL_INFO( "emfio", "\t\t\t Pos: " << aPosition.getX() << ":" << aPosition.getY() << " String Length: " << nLen << " Options: " << nOptions );
+                tools::Rectangle aRect;
+                if ( ( nOptions & ETO_CLIPPED ) || ( nOptions & ETO_OPAQUE ) )
+                {
+                    nNonStringLen += 2 * sizeof(sal_uInt16);
+
+                    if (nRecSize < nNonStringLen)
+                    {
+                        SAL_WARN("emfio", "W_META_TEXTOUT too short");
+                        break;
+                    }
+                    const Point aTopLeft = ReadPoint();
+                    const Point aBottomRight = ReadPoint();
+                    aRect = tools::Rectangle( aTopLeft, aBottomRight );
+                    if ( nOptions & ETO_OPAQUE )
+                        DrawRectWithBGColor( aRect );
+                    SAL_INFO( "emfio", "\t\t\t Rectangle : " << aTopLeft.getX() << ":" << aTopLeft.getY() << ", " << aBottomRight.getX() << ":" << aBottomRight.getY() );
+                }
+
+                vcl::text::ComplexTextLayoutFlags nTextLayoutMode = vcl::text::ComplexTextLayoutFlags::Default;
+                if ( nOptions & ETO_RTLREADING )
+                    nTextLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
+                SetTextLayoutMode( nTextLayoutMode );
+                SAL_WARN_IF( ( nOptions & ( ETO_PDY | ETO_GLYPH_INDEX ) ) != 0, "emfio", "SJ: ETO_PDY || ETO_GLYPH_INDEX in WMF" );
+
+                // output only makes sense if the text contains characters
+                if (nLen)
+                {
+                    sal_Int32 nOriginalTextLen = nLen;
+                    sal_Int32 nOriginalBlockLen = ( nOriginalTextLen + 1 ) &~ 1;
+
+                    auto nMaxStreamPos = nRecordPos + nRecSize;
+                    auto nRemainingSize = std::min(mpInputStream->remainingSize(), nMaxStreamPos - mpInputStream->Tell());
+                    if (nRemainingSize < o3tl::make_unsigned(nOriginalBlockLen))
+                    {
+                        SAL_WARN("emfio", "exttextout record claimed more data than the stream can provide");
+                        nOriginalTextLen = nOriginalBlockLen = nRemainingSize;
+                    }
+
+                    std::vector<char> pChar(nOriginalBlockLen);
+                    mpInputStream->ReadBytes(pChar.data(), nOriginalBlockLen);
+                    OUString aText(pChar.data(), nOriginalTextLen, GetCharSet()); // after this conversion the text may contain
+                    sal_Int32 nNewTextLen = aText.getLength();                         // less character (japanese version), so the
+                                                                                       // dxAry will not fit
+                    if ( nNewTextLen )
+                    {
+                        if ( nOptions & ETO_CLIPPED )
+                        {
+                            Push(); // Save the current clip. It will be restored after text drawing
+                            IntersectClipRect( aRect );
+                        }
+                        SAL_INFO( "emfio", "\t\t\t Text : " << aText );
+                        std::vector<sal_Int32> aDXAry;
+                        std::unique_ptr<tools::Long[]> pDYAry;
+                        auto nDxArySize =  nMaxStreamPos - mpInputStream->Tell();
+                        auto nDxAryEntries = nDxArySize >> 1;
+                        bool        bUseDXAry = false;
+
+                        if ( ( ( nDxAryEntries % nOriginalTextLen ) == 0 ) && ( nNewTextLen <= nOriginalTextLen ) )
+                        {
+                            sal_Int32 i; // needed just outside the for
+                            aDXAry.resize( nNewTextLen );
+                            if ( nOptions & ETO_PDY )
+                            {
+                                pDYAry.reset(new tools::Long[ nNewTextLen ]);
+                            }
+                            for (i = 0; i < nNewTextLen; i++ )
+                            {
+                                if ( mpInputStream->Tell() >= nMaxStreamPos )
+                                    break;
+                                sal_Int32 nDxCount = 1;
+                                if ( nNewTextLen != nOriginalTextLen )
+                                {
+                                    sal_Unicode cUniChar = aText[i];
+                                    OString aTmp(&cUniChar, 1, GetCharSet());
+                                    if ( aTmp.getLength() > 1 )
+                                    {
+                                        nDxCount = aTmp.getLength();
+                                    }
+                                }
+
+                                sal_Int16 nDx = 0, nDy = 0;
+                                while ( nDxCount-- )
+                                {
+                                    if ( ( mpInputStream->Tell() + 2 ) > nMaxStreamPos )
+                                        break;
+                                    sal_Int16 nDxTmp = 0;
+                                    mpInputStream->ReadInt16(nDxTmp);
+                                    nDx += nDxTmp;
+                                    if ( nOptions & ETO_PDY )
+                                    {
+                                        if ( ( mpInputStream->Tell() + 2 ) > nMaxStreamPos )
+                                            break;
+                                        sal_Int16 nDyTmp = 0;
+                                        mpInputStream->ReadInt16(nDyTmp);
+                                        nDy += nDyTmp;
+                                    }
+                                }
+
+                                aDXAry[ i ] = nDx;
+                                if ( nOptions & ETO_PDY )
+                                {
+                                    pDYAry[i] = nDy;
+                                }
+                            }
+                            if ( i == nNewTextLen )
+                                bUseDXAry = true;
+                        }
+                        if ( bUseDXAry )
+                            DrawText( aPosition, aText, &aDXAry, pDYAry.get() );
+                        else
+                            DrawText( aPosition, aText );
+                        if ( nOptions & ETO_CLIPPED )
+                            Pop();
+                    }
+                }
+            }
+            break;
+
+            case W_META_SELECTOBJECT:
+            case W_META_SELECTPALETTE:
+            {
+                sal_uInt16   nObjIndex = 0;
+                mpInputStream->ReadUInt16( nObjIndex );
+                SelectObject( nObjIndex );
+            }
+            break;
+
+            case W_META_SETTEXTALIGN:
+            {
+                sal_uInt16  nAlign = 0;
+                mpInputStream->ReadUInt16( nAlign );
+                SetTextAlign( nAlign );
+            }
+            break;
+
+            case W_META_BITBLT:
+            case W_META_STRETCHBLT:
+            {
+                sal_uInt32  nRasterOperation = 0;
+                sal_Int16   nSrcHeight = 0, nSrcWidth = 0, nYSrc, nXSrc, nSye, nSxe, nBitmapType, nWidth, nHeight, nBytesPerScan;
+                sal_uInt8   nPlanes, nBitCount;
+                const bool bNoSourceBitmap = ( nRecordSize == ( static_cast< sal_uInt32 >( nFunc ) >> 8 ) + 3 );
+
+                mpInputStream->ReadUInt32( nRasterOperation );
+                SAL_INFO("emfio", "\t\t Raster operation: 0x" << std::hex << nRasterOperation << std::dec << ", No source bitmap: " << bNoSourceBitmap);
+
+                if( nFunc == W_META_STRETCHBLT )
+                    mpInputStream->ReadInt16( nSrcHeight ).ReadInt16( nSrcWidth );
+
+                mpInputStream->ReadInt16( nYSrc ).ReadInt16( nXSrc );
+                if ( bNoSourceBitmap )
+                    mpInputStream->SeekRel( 2 ); // Skip Reserved 2 bytes (it must be ignored)
+                mpInputStream->ReadInt16( nSye ).ReadInt16( nSxe );
+                Point aPoint( ReadYX() ); // The upper-left corner of the destination rectangle.
+                mpInputStream->ReadInt16( nBitmapType ).ReadInt16( nWidth ).ReadInt16( nHeight ).ReadInt16( nBytesPerScan ).ReadUChar( nPlanes ).ReadUChar( nBitCount );
+
+                SAL_INFO("emfio", "\t\t Bitmap type:" << nBitmapType << " Width:" << nWidth << " Height:" << nHeight << " WidthBytes:" << nBytesPerScan << " Planes: " << static_cast< sal_uInt16 >( nPlanes ) << " BitCount: " << static_cast< sal_uInt16 >( nBitCount ) );
+                if (!mpInputStream->good() || bNoSourceBitmap || nBitCount == 4 || nBitCount == 8 || nPlanes != 1)
+                {
+                    SAL_WARN("emfio", "\t\t TODO The unsupported Bitmap record. Please fill a bug.");
+                    break;
+                }
+                const vcl::PixelFormat ePixelFormat = vcl::bitDepthToPixelFormat( nBitCount );
+                bool bOk = nWidth > 0 && nHeight > 0 && nBytesPerScan > 0 && ePixelFormat != vcl::PixelFormat::INVALID;
+                if (bOk)
+                {
+                    // must be enough data to fulfil the request
+                    bOk = o3tl::make_unsigned( nBytesPerScan ) <= mpInputStream->remainingSize() / nHeight;
+                }
+                if (bOk)
+                {
+                    // scanline must be large enough to provide all pixels
+                    bOk = nBytesPerScan >= nWidth * nBitCount / 8;
+                }
+                if (bOk)
+                {
+                    std::unique_ptr< sal_uInt8[] > pData;
+                    pData.reset( new sal_uInt8[ nHeight * nBytesPerScan ] );
+                    mpInputStream->ReadBytes( pData.get(), nHeight * nBytesPerScan );
+                    BitmapEx aBitmap = vcl::bitmap::CreateFromData( pData.get(), nWidth, nHeight, nBytesPerScan, ePixelFormat, true );
+                    if ( nSye && nSxe &&
+                         ( nXSrc + nSxe <= nWidth ) &&
+                         ( nYSrc + nSye <= nHeight ) )
+                    {
+                        tools::Rectangle aCropRect( Point( nXSrc, nYSrc ), Size( nSxe, nSye ) );
+                        aBitmap.Crop( aCropRect );
+                    }
+                    tools::Rectangle aDestRect( aPoint, Size( nSxe, nSye ) );
+                    maBmpSaveList.emplace_back(aBitmap, aDestRect, nRasterOperation);
+                }
+            }
+            break;
+
+            case W_META_DIBBITBLT:
+            case W_META_DIBSTRETCHBLT:
+            case W_META_STRETCHDIB:
+            {
+                sal_uInt32  nRasterOperation = 0;
+                sal_uInt16  nColorUsage = 0;
+                sal_Int16   nSrcHeight = 0, nSrcWidth = 0, nYSrc = 0, nXSrc = 0;
+                Bitmap      aBmp;
+                const bool bNoSourceBitmap = ( nFunc != W_META_STRETCHDIB ) && ( nRecordSize == ( ( static_cast< sal_uInt32 >( nFunc ) >> 8 ) + 3 ) );
+
+                mpInputStream->ReadUInt32( nRasterOperation );
+                SAL_INFO("emfio", "\t\t Raster operation: 0x" << std::hex << nRasterOperation << std::dec << ", No source bitmap: " << bNoSourceBitmap);
+                if( nFunc == W_META_STRETCHDIB )
+                    mpInputStream->ReadUInt16( nColorUsage );
+
+                // nSrcHeight and nSrcWidth is the number of pixels that has to been used
+                // If they are set to zero, it is as indicator not to scale the bitmap later
+                if( nFunc == W_META_DIBSTRETCHBLT ||
+                    nFunc == W_META_STRETCHDIB )
+                    mpInputStream->ReadInt16( nSrcHeight ).ReadInt16( nSrcWidth );
+
+                // nYSrc and nXSrc is the offset of the first pixel
+                mpInputStream->ReadInt16( nYSrc ).ReadInt16( nXSrc );
+
+                if ( bNoSourceBitmap )
+                    mpInputStream->SeekRel( 2 ); // Skip Reserved 2 bytes (it must be ignored)
+
+                Size aDestSize( ReadYXExt() );
+                if ( aDestSize.Width() && aDestSize.Height() )  // #92623# do not try to read buggy bitmaps
+                {
+                    tools::Rectangle aDestRect( ReadYX(), aDestSize );
+                    if ( !bNoSourceBitmap )
+                    {
+                        // tdf#142625 Read the DIBHeader and check if bitmap is supported
+                        // If bitmap is not supported don't run ReadDIB, as it will interrupt image processing
+                        const auto nOldPos(mpInputStream->Tell());
+                        sal_uInt32 nHeaderSize(0);
+                        mpInputStream->ReadUInt32( nHeaderSize );
+                        if ( nHeaderSize == 0xC ) // BitmapCoreHeader
+                            mpInputStream->SeekRel( 6 ); // skip Width (16), Height (16), Planes (16)
+                        else
+                            mpInputStream->SeekRel( 10 ); // skip Width (32), Height (32), Planes (16)
+                        sal_uInt16 nBitCount(0);
+                        mpInputStream->ReadUInt16( nBitCount );
+                        if ( nBitCount == 0 ) // TODO Undefined BitCount (JPEG/PNG), which are not supported
+                            break;
+                        mpInputStream->Seek(nOldPos);
+
+                        if ( !ReadDIB( aBmp, *mpInputStream, false ) )
+                            SAL_WARN( "emfio", "\tTODO Read DIB failed. Interrupting processing whole image. Please report bug report." );
+                    }
+                    // test if it is sensible to crop
+                    if ( nSrcHeight && nSrcWidth &&
+                            ( nXSrc + nSrcWidth <= aBmp.GetSizePixel().Width() ) &&
+                            ( nYSrc + nSrcHeight <= aBmp.GetSizePixel().Height() ) )
+                    {
+                        tools::Rectangle aCropRect( Point( nXSrc, nYSrc ), Size( nSrcWidth, nSrcHeight ) );
+                        aBmp.Crop( aCropRect );
+                    }
+
+                    maBmpSaveList.emplace_back(aBmp, aDestRect, nRasterOperation);
+                }
+            }
+            break;
+
+            case W_META_DIBCREATEPATTERNBRUSH:
+            {
+                Bitmap  aBmp;
+                sal_uInt32 nRed(0), nGreen(0), nBlue(0), nCount(1);
+                sal_uInt16 nStyle(0), nColorUsage(0);
+
+                mpInputStream->ReadUInt16( nStyle ).ReadUInt16( nColorUsage );
+                BrushStyle eStyle = static_cast<BrushStyle>(nStyle);
+                SAL_INFO( "emfio", "\t\t Style:" << nStyle << ", ColorUsage: " << nColorUsage );
+                if ( eStyle == BrushStyle::BS_PATTERN ) // TODO tdf#142625 Add support for pattern
+                {
+                    SAL_WARN( "emfio", "\tTODO: Pattern brush style is not supported." );
+                    CreateObject();
+                    break;
+                }
+                if ( !ReadDIB( aBmp, *mpInputStream, false ) )
+                    SAL_WARN( "emfio", "\tTODO Read DIB failed. Interrupting processing whole image. Please report bug report." );
+                if ( !aBmp.IsEmpty() )
+                {
+                    Bitmap::ScopedReadAccess pBmp(aBmp);
+                    for ( tools::Long y = 0; y < pBmp->Height(); y++ )
+                    {
+                        for ( tools::Long x = 0; x < pBmp->Width(); x++ )
+                        {
+                            const BitmapColor aColor( pBmp->GetColor( y, x ) );
+
+                            nRed += aColor.GetRed();
+                            nGreen += aColor.GetGreen();
+                            nBlue += aColor.GetBlue();
+                        }
+                    }
+                    nCount = pBmp->Height() * pBmp->Width();
+                    if ( !nCount )
+                        nCount++;
+                }
+                Color aColor( static_cast<sal_uInt8>( nRed / nCount ), static_cast<sal_uInt8>( nGreen / nCount ), static_cast<sal_uInt8>( nBlue / nCount ) );
+                CreateObject(std::make_unique<WinMtfFillStyle>( aColor, false ));
+            }
+            break;
+
+            case W_META_DELETEOBJECT:
+            {
+                sal_uInt16 nIndex = 0;
+                mpInputStream->ReadUInt16( nIndex );
+                DeleteObject( nIndex );
+            }
+            break;
+
+            case W_META_CREATEPALETTE:
+            {
+                sal_uInt16 nStart = 0;
+                sal_uInt16 nNumberOfEntries = 0;
+                mpInputStream->ReadUInt16( nStart );
+                mpInputStream->ReadUInt16( nNumberOfEntries );
+
+                SAL_INFO("emfio", "\t\t Start 0x" << std::hex << nStart << std::dec << ", Number of entries: " << nNumberOfEntries);
+                sal_uInt32 nPalleteEntry;
+                std::vector< Color > aPaletteColors;
+                for (sal_uInt16 i = 0; i < nNumberOfEntries; ++i)
+                {
+                    //PALETTEENTRY: Values, Blue, Green, Red
+                    mpInputStream->ReadUInt32( nPalleteEntry );
+                    SAL_INFO("emfio", "\t\t " << i << ". Palette entry: " << std::setw(10) << std::showbase <<std::hex << nPalleteEntry << std::dec );
+                    aPaletteColors.push_back(Color(static_cast<sal_uInt8>(nPalleteEntry), static_cast<sal_uInt8>(nPalleteEntry >> 8), static_cast<sal_uInt8>(nPalleteEntry >> 16)));
+                }
+                CreateObject(std::make_unique<WinMtfPalette>( aPaletteColors ));
+            }
+            break;
+
+            case W_META_CREATEBRUSH:
+            {
+                SAL_WARN( "emfio", "TODO: Not implemented. Please fill the bug report" );
+                CreateObject(std::make_unique<WinMtfFillStyle>( COL_WHITE, false ));
+            }
+            break;
+
+            case W_META_CREATEPATTERNBRUSH:
+            {
+                SAL_WARN( "emfio", "TODO: Not implemented. Please fill the bug report" );
+                CreateObject(std::make_unique<WinMtfFillStyle>( COL_WHITE, false ));
+            }
+            break;
+
+            case W_META_CREATEPENINDIRECT:
+            {
+                LineInfo   aLineInfo;
+                sal_uInt16 nStyle = 0;
+                sal_uInt16 nWidth = 0;
+                sal_uInt16 nHeight = 0;
+
+                mpInputStream->ReadUInt16(nStyle);
+                mpInputStream->ReadUInt16(nWidth);
+                mpInputStream->ReadUInt16(nHeight);
+                CreateObject(std::make_unique<WinMtfLineStyle>(ReadColor(), nStyle, nWidth));
+            }
+            break;
+
+            case W_META_CREATEBRUSHINDIRECT:
+            {
+                sal_uInt16  nBrushStyle = 0;
+                mpInputStream->ReadUInt16( nBrushStyle );
+                BrushStyle eBrushStyle = static_cast<BrushStyle>(nBrushStyle);
+                CreateObject(std::make_unique<WinMtfFillStyle>( ReadColor(), ( eBrushStyle == BrushStyle::BS_NULL ) ));
+                SAL_WARN_IF( (eBrushStyle != BrushStyle::BS_SOLID) && (eBrushStyle != BrushStyle::BS_NULL), "emfio", "TODO: Brush style not implemented. Please fill the bug report" );
+            }
+            break;
+
+            case W_META_CREATEFONTINDIRECT:
+            {
+                Size aFontSize;
+                char lfFaceName[LF_FACESIZE+1];
+                sal_Int16 lfEscapement = 0;
+                sal_Int16 lfOrientation = 0;
+                sal_Int16 lfWeight = 0;
+
+                LOGFONTW aLogFont;
+                aFontSize = ReadYXExt();
+                mpInputStream->ReadInt16( lfEscapement );
+                mpInputStream->ReadInt16( lfOrientation );
+                mpInputStream->ReadInt16( lfWeight );
+                mpInputStream->ReadUChar( aLogFont.lfItalic );
+                mpInputStream->ReadUChar( aLogFont.lfUnderline );
+                mpInputStream->ReadUChar( aLogFont.lfStrikeOut );
+                mpInputStream->ReadUChar( aLogFont.lfCharSet );
+                mpInputStream->ReadUChar( aLogFont.lfOutPrecision );
+                mpInputStream->ReadUChar( aLogFont.lfClipPrecision );
+                mpInputStream->ReadUChar( aLogFont.lfQuality );
+                mpInputStream->ReadUChar( aLogFont.lfPitchAndFamily );
+                size_t nRet = mpInputStream->ReadBytes( lfFaceName, LF_FACESIZE );
+                lfFaceName[nRet] = 0;
+                aLogFont.lfWidth = aFontSize.Width();
+                aLogFont.lfHeight = aFontSize.Height();
+                aLogFont.lfEscapement = lfEscapement;
+                aLogFont.lfOrientation = lfOrientation;
+                aLogFont.lfWeight = lfWeight;
+
+                rtl_TextEncoding eCharSet;
+                if ( ( aLogFont.lfCharSet == OEM_CHARSET ) || ( aLogFont.lfCharSet == DEFAULT_CHARSET ) )
+                    eCharSet = osl_getThreadTextEncoding();
+                else
+                    eCharSet = rtl_getTextEncodingFromWindowsCharset( aLogFont.lfCharSet );
+                if ( eCharSet == RTL_TEXTENCODING_DONTKNOW )
+                    eCharSet = osl_getThreadTextEncoding();
+                if ( eCharSet == RTL_TEXTENCODING_SYMBOL )
+                    eCharSet = RTL_TEXTENCODING_MS_1252;
+                aLogFont.alfFaceName = OUString( lfFaceName, strlen(lfFaceName), eCharSet );
+
+                CreateObject(std::make_unique<WinMtfFontStyle>( aLogFont ));
+            }
+            break;
+
+            case W_META_CREATEBITMAPINDIRECT:
+            {
+                SAL_WARN( "emfio", "TODO: W_META_CREATEBITMAPINDIRECT is not implemented. Please fill the bug report" );
+                CreateObject();
+            }
+            break;
+
+            case W_META_CREATEBITMAP:
+            {
+                SAL_WARN( "emfio", "TODO: W_META_CREATEBITMAP is not implemented. Please fill the bug report" );
+                CreateObject();
+            }
+            break;
+
+            case W_META_CREATEREGION:
+            {
+                SAL_WARN( "emfio", "TODO: W_META_CREATEREGION is not implemented. Please fill the bug report" );
+                CreateObject();
+            }
+            break;
+
+            case W_META_EXCLUDECLIPRECT :
+            {
+                SAL_WARN( "emfio", "TODO:  Not working correctly. Please fill the bug report" );
+                ExcludeClipRect( ReadRectangle() );
+            }
+            break;
+
+            case W_META_PATBLT:
+            {
+                sal_uInt32 nROP = 0;
+                mpInputStream->ReadUInt32( nROP );
+                Size aSize = ReadYXExt();
+                WMFRasterOp nOldROP = SetRasterOp( static_cast<WMFRasterOp>(nROP) );
+                DrawRect( tools::Rectangle( ReadYX(), aSize ), false );
+                SetRasterOp( nOldROP );
+            }
+            break;
+
+            case W_META_SELECTCLIPREGION:
+            {
+                sal_uInt16 nObjIndex = 0;
+                mpInputStream->ReadUInt16( nObjIndex );
+                SAL_WARN( "emfio", "TODO: W_META_SELECTCLIPREGION is not implemented. Please fill the bug report" );
+                if ( !nObjIndex )
+                {
+                    tools::PolyPolygon aEmptyPolyPoly;
+                    SetClipPath( aEmptyPolyPoly, RegionMode::RGN_COPY, true );
+                }
+            }
+            break;
+
+            case W_META_ESCAPE :
+            {
+                // mnRecSize has been checked previously to be greater than 3
+                sal_uInt64 nMetaRecSize = static_cast< sal_uInt64 >(mnRecSize - 2 ) * 2;
+                sal_uInt64 nMetaRecEndPos = mpInputStream->Tell() + nMetaRecSize;
+
+                // taking care that mnRecSize does not exceed the maximal stream position
+                if ( nMetaRecEndPos > mnEndPos )
+                {
+                    mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                    break;
+                }
+                if (mnRecSize >= 4 )    // minimal escape length
+                {
+                    sal_uInt16  nMode = 0, nLen = 0;
+                    mpInputStream->ReadUInt16( nMode )
+                         .ReadUInt16( nLen );
+                    if ( ( nMode == W_MFCOMMENT ) && ( nLen >= 4 ) )
+                    {
+                        sal_uInt32 nNewMagic = 0; // we have to read int32 for
+                        mpInputStream->ReadUInt32( nNewMagic );   // META_ESCAPE_ENHANCED_METAFILE CommentIdentifier
+
+                        if( nNewMagic == 0x2c2a4f4f &&  nLen >= 14 )
+                        {
+                            sal_uInt16 nMagic2 = 0;
+                            mpInputStream->ReadUInt16( nMagic2 );
+                            if( nMagic2 == 0x0a ) // 2nd half of magic
+                            {                     // continue with private escape
+                                sal_uInt32 nCheck = 0, nEsc = 0;
+                                mpInputStream->ReadUInt32( nCheck )
+                                     .ReadUInt32( nEsc );
+
+                                sal_uInt32 nEscLen = nLen - 14;
+                                if ( nEscLen <= (mnRecSize * 2 ) )
+                                {
+    #ifdef OSL_BIGENDIAN
+                                    sal_uInt32 nTmp = OSL_SWAPDWORD( nEsc );
+                                    sal_uInt32 nCheckSum = rtl_crc32( 0, &nTmp, 4 );
+    #else
+                                    sal_uInt32 nCheckSum = rtl_crc32( 0, &nEsc, 4 );
+    #endif
+                                    std::unique_ptr<sal_Int8[]> pData;
+
+                                    if ( ( static_cast< sal_uInt64 >( nEscLen ) + mpInputStream->Tell() ) > nMetaRecEndPos )
+                                    {
+                                        mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                                        break;
+                                    }
+                                    if ( nEscLen > 0 )
+                                    {
+                                        pData.reset(new sal_Int8[ nEscLen ]);
+                                        mpInputStream->ReadBytes(pData.get(), nEscLen);
+                                        nCheckSum = rtl_crc32( nCheckSum, pData.get(), nEscLen );
+                                    }
+                                    if ( nCheck == nCheckSum )
+                                    {
+                                        switch( nEsc )
+                                        {
+                                            case PRIVATE_ESCAPE_UNICODE :
+                                            {
+                                                // we will use text instead of polygons only if we have the correct font
+                                                if ( Application::GetDefaultDevice()->IsFontAvailable( GetFont().GetFamilyName() ) )
+                                                {
+                                                    Point  aPt;
+                                                    sal_uInt32  nStringLen, nDXCount;
+                                                    std::vector<sal_Int32> aDXAry;
+                                                    SvMemoryStream aMemoryStream( nEscLen );
+                                                    aMemoryStream.WriteBytes(pData.get(), nEscLen);
+                                                    aMemoryStream.Seek( STREAM_SEEK_TO_BEGIN );
+                                                    sal_Int32 nTmpX(0), nTmpY(0);
+                                                    aMemoryStream.ReadInt32( nTmpX )
+                                                                 .ReadInt32( nTmpY )
+                                                                 .ReadUInt32( nStringLen );
+                                                    aPt.setX( nTmpX );
+                                                    aPt.setY( nTmpY );
+
+                                                    if ( ( static_cast< sal_uInt64 >( nStringLen ) * sizeof( sal_Unicode ) ) < ( nEscLen - aMemoryStream.Tell() ) )
+                                                    {
+                                                        OUString aString = read_uInt16s_ToOUString(aMemoryStream, nStringLen);
+                                                        aMemoryStream.ReadUInt32( nDXCount );
+                                                        if ( ( static_cast< sal_uInt64 >( nDXCount ) * sizeof( sal_Int32 ) ) >= ( nEscLen - aMemoryStream.Tell() ) )
+                                                            nDXCount = 0;
+                                                        if ( nDXCount )
+                                                            aDXAry.resize(nDXCount);
+                                                        for  (sal_uInt32 i = 0; i < nDXCount; i++ )
+                                                        {
+                                                            sal_Int32 val;
+                                                            aMemoryStream.ReadInt32( val);
+                                                            aDXAry[ i ] = val;
+                                                        }
+                                                        aMemoryStream.ReadUInt32(mnSkipActions);
+                                                        DrawText( aPt, aString, aDXAry.empty() ? nullptr : &aDXAry );
+                                                    }
+                                                }
+                                            }
+                                            break;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        else if ( (nNewMagic == static_cast< sal_uInt32 >(0x43464D57)) && (nLen >= 34) && ( static_cast<sal_Int32>(nLen + 10) <= static_cast<sal_Int32>(mnRecSize * 2) ))
+                        {
+                            sal_uInt32 nComType = 0, nVersion = 0, nFlags = 0, nComRecCount = 0,
+                                       nCurRecSize = 0, nRemainingSize = 0, nEMFTotalSize = 0;
+                            sal_uInt16 nCheck = 0;
+
+                            mpInputStream->ReadUInt32( nComType ).ReadUInt32( nVersion ).ReadUInt16( nCheck ).ReadUInt32( nFlags )
+                                 .ReadUInt32( nComRecCount ).ReadUInt32( nCurRecSize )
+                                 .ReadUInt32( nRemainingSize ).ReadUInt32( nEMFTotalSize ); // the nRemainingSize is not mentioned in MSDN documentation
+                                                                      // but it seems to be required to read in data produced by OLE
+
+                            if( nComType == 0x01 && nVersion == 0x10000 && nComRecCount )
+                            {
+                                if( !mnEMFRec)
+                                {   // first EMF comment
+                                    mnEMFRecCount = nComRecCount;
+                                    mnEMFSize = nEMFTotalSize;
+                                    if (mnEMFSize > mpInputStream->remainingSize())
+                                    {
+                                        SAL_WARN("emfio", "emf size claims to be larger than remaining data");
+                                        mpEMFStream.reset();
+                                    }
+                                    else
+                                        mpEMFStream = std::vector<sal_uInt8>();
+                                }
+                                else if( (mnEMFRecCount != nComRecCount ) || (mnEMFSize != nEMFTotalSize ) ) // add additional checks here
+                                {
+                                    // total records should be the same as in previous comments
+                                    mnEMFRecCount = 0xFFFFFFFF;
+                                    mpEMFStream.reset();
+                                }
+                                mnEMFRec++;
+
+                                if (mpEMFStream && nCurRecSize + 34 > nLen)
+                                {
+                                    mnEMFRecCount = 0xFFFFFFFF;
+                                    mpEMFStream.reset();
+                                }
+
+                                if (mpEMFStream && nCurRecSize > mpInputStream->remainingSize())
+                                {
+                                    SAL_WARN("emfio", "emf record size claims to be larger than remaining data");
+                                    mnEMFRecCount = 0xFFFFFFFF;
+                                    mpEMFStream.reset();
+                                }
+
+                                if (mpEMFStream)
+                                {
+                                    std::vector<sal_Int8> aBuf(nCurRecSize);
+                                    sal_uInt32 nCount = mpInputStream->ReadBytes(aBuf.data(), nCurRecSize);
+                                    if( nCount == nCurRecSize )
+                                    {
+                                        mpEMFStream->insert(mpEMFStream->end(), aBuf.begin(), aBuf.end());
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            break;
+
+            case W_META_SETRELABS:
+            case W_META_SETPOLYFILLMODE:
+            case W_META_SETSTRETCHBLTMODE:
+            case W_META_SETTEXTCHAREXTRA:
+            case W_META_SETTEXTJUSTIFICATION:
+            case W_META_FLOODFILL :
+            case W_META_FILLREGION:
+            case W_META_FRAMEREGION:
+            case W_META_INVERTREGION:
+            case W_META_PAINTREGION:
+            case W_META_DRAWTEXT:
+            case W_META_SETMAPPERFLAGS:
+            case W_META_SETDIBTODEV:
+            case W_META_REALIZEPALETTE:
+            case W_META_ANIMATEPALETTE:
+            case W_META_SETPALENTRIES:
+            case W_META_RESIZEPALETTE:
+            case W_META_EXTFLOODFILL:
+            case W_META_RESETDC:
+            case W_META_STARTDOC:
+            case W_META_STARTPAGE:
+            case W_META_ENDPAGE:
+            case W_META_ABORTDOC:
+            case W_META_ENDDOC:
+            {
+                SAL_WARN("emfio", "TODO: WMF record not implemented: " << record_type_name(nFunc));
+            }
+            break;
+
+            default:
+            {
+                SAL_WARN("emfio", "Unknown Meta Action: 0x" << std::hex << nFunc << std::dec);
+            }
+        }
+
+        // tdf#127471
+        maScaledFontHelper.applyAlternativeFontScale();
+    }
+
+    const tools::Long   aMaxWidth = 1024;
+
+    bool WmfReader::ReadHeader()
+    {
+        sal_uInt64 const nStrmPos = mpInputStream->Tell();
+
+        sal_uInt32 nPlaceableMetaKey(0);
+        // if available read the METAFILEHEADER
+        mpInputStream->ReadUInt32( nPlaceableMetaKey );
+        if (!mpInputStream->good())
+            return false;
+
+        tools::Rectangle aPlaceableBound;
+
+        mbPlaceable = nPlaceableMetaKey == 0x9ac6cdd7L;
+
+        SAL_INFO("emfio", "Placeable: \"" << (mbPlaceable ? "yes" : "no") << "\"");
+
+        if (mbPlaceable)
+        {
+            //TODO do some real error handling here
+            sal_Int16 nVal(0);
+
+            // Skip reserved bytes
+            mpInputStream->SeekRel(2);
+
+            // BoundRect
+            // These are simply ignored for now
+            mpInputStream->ReadInt16( nVal );
+            aPlaceableBound.SetLeft( nVal );
+            mpInputStream->ReadInt16( nVal );
+            aPlaceableBound.SetTop( nVal );
+            mpInputStream->ReadInt16( nVal );
+            aPlaceableBound.SetRight( nVal );
+            mpInputStream->ReadInt16( nVal );
+            aPlaceableBound.SetBottom( nVal );
+
+            // inch
+            mpInputStream->ReadUInt16( mnUnitsPerInch );
+
+            // reserved
+            mpInputStream->SeekRel( 4 );
+
+            // Skip and don't check the checksum
+            mpInputStream->SeekRel( 2 );
+
+            // Skip wmf header
+            mpInputStream->Seek( nStrmPos + 40 );    // set the streampos to the start of the metaactions
+            GetPlaceableBound( aPlaceableBound, mpInputStream );
+            // Go back to the place after placeable header
+            mpInputStream->Seek( nStrmPos + 22);
+        }
+        else
+        {
+            // Default is 1440, but it is set to 96 to show the wmf larger
+            mnUnitsPerInch = 96;
+
+            if (mpExternalHeader != nullptr
+                && mpExternalHeader->xExt > 0
+                && mpExternalHeader->yExt > 0
+                && (mpExternalHeader->mapMode == MappingMode::MM_ISOTROPIC || mpExternalHeader->mapMode == MappingMode::MM_ANISOTROPIC))
+            {
+                // #n417818#: If we have an external header then overwrite the bounds!
+                tools::Rectangle aExtRect(0, 0,
+                    o3tl::convert(mpExternalHeader->xExt, o3tl::Length::mm100, o3tl::Length::px),
+                    o3tl::convert(mpExternalHeader->yExt, o3tl::Length::mm100, o3tl::Length::px));
+                aPlaceableBound = aExtRect;
+
+                SAL_INFO("emfio", "External header size "
+                    " left: " << aPlaceableBound.Left() << " top: " << aPlaceableBound.Top()
+                    << " right: " << aPlaceableBound.Right() << " bottom: " << aPlaceableBound.Bottom());
+
+                SetMapMode(static_cast<MappingMode>(mpExternalHeader->mapMode));;
+            }
+            else
+            {
+                mpInputStream->Seek(nStrmPos + 18);    // set the streampos to the start of the metaactions
+                GetPlaceableBound(aPlaceableBound, mpInputStream);
+
+                // The image size is not known so normalize the calculated bounds so that the
+                // resulting image is not too big
+                if (aPlaceableBound.GetWidth() > aMaxWidth)
+                {
+                    const double fMaxWidth = static_cast<double>(aMaxWidth);
+                    double fRatio = aPlaceableBound.GetWidth() / fMaxWidth;
+
+                    // changing mnUnitsPerInch as a tool to scale wmf
+                    mnUnitsPerInch *= fRatio;
+
+                    SAL_INFO("emfio", "Placeable bounds "
+                        " left: " << aPlaceableBound.Left() << " top: " << aPlaceableBound.Top()
+                        << " right: " << aPlaceableBound.Right() << " bottom: " << aPlaceableBound.Bottom());
+                }
+            }
+
+            mpInputStream->Seek( nStrmPos );
+        }
+
+        SetWinOrg( aPlaceableBound.TopLeft() );
+        Size aWMFSize(
+            std::abs( aPlaceableBound.GetWidth() ), std::abs( aPlaceableBound.GetHeight() ) );
+        SetWinExt( aWMFSize );
+
+        SAL_INFO("emfio", "WMF size  w: " << aWMFSize.Width()    << " h: " << aWMFSize.Height());
+
+        Size aDevExt( 10000, 10000 );
+        if( ( std::abs( aWMFSize.Width() ) > 1 ) && ( std::abs( aWMFSize.Height() ) > 1 ) )
+        {
+            const Fraction  aFrac( 1, mnUnitsPerInch);
+            MapMode         aWMFMap( MapUnit::MapInch, Point(), aFrac, aFrac );
+            Size            aSize100(OutputDevice::LogicToLogic(aWMFSize, aWMFMap, MapMode(MapUnit::Map100thMM)));
+            aDevExt = Size( std::abs( aSize100.Width() ), std::abs( aSize100.Height() ) );
+        }
+        SetDevExt( aDevExt );
+
+        SAL_INFO("emfio", "Dev size  w: " << aDevExt.Width()    << " h: " << aDevExt.Height());
+
+        // read the METAHEADER
+        sal_uInt32 nMetaKey(0);
+        mpInputStream->ReadUInt32( nMetaKey ); // type and headersize
+        if (!mpInputStream->good())
+            return false;
+        if (nMetaKey != 0x00090001)
+        {
+            sal_uInt16 aNextWord(0);
+            mpInputStream->ReadUInt16( aNextWord );
+            if (nMetaKey != 0x10000 || aNextWord != 0x09)
+            {
+                mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                return false;
+            }
+        }
+
+        mpInputStream->SeekRel( 2 ); // Version (of Windows)
+        mpInputStream->SeekRel( 4 ); // Size (of file in words)
+        mpInputStream->SeekRel( 2 ); // NoObjects (maximum number of simultaneous objects)
+        mpInputStream->SeekRel( 4 ); // MaxRecord (size of largest record in words)
+        mpInputStream->SeekRel( 2 ); // NoParameters (Unused
+
+        return mpInputStream->good();
+    }
+
+    void WmfReader::ReadWMF()
+    {
+        sal_uInt16  nFunction;
+
+        mnSkipActions = 0;
+
+        mpEMFStream.reset();
+        mnEMFRecCount = 0;
+        mnEMFRec = 0;
+        mnEMFSize = 0;
+
+        SetMapMode( MappingMode::MM_ANISOTROPIC );
+        SetWinOrg( Point() );
+        SetWinExt( Size( 1, 1 ) );
+        SetDevExt( Size( 10000, 10000 ) );
+
+        mnEndPos=mpInputStream->TellEnd();
+        mpInputStream->Seek( mnStartPos );
+
+        if ( ReadHeader( ) )
+        {
+            auto nPos = mpInputStream->Tell();
+
+            if( mnEndPos - mnStartPos )
+            {
+                bool bEMFAvailable = false;
+                while( !mpInputStream->eof() )
+                {
+                    mpInputStream->ReadUInt32(mnRecSize).ReadUInt16( nFunction );
+
+                    if (
+                         !mpInputStream->good() ||
+                         (mnRecSize < 3) ||
+                         (mnRecSize == 3 && nFunction == W_META_EOF)
+                       )
+                    {
+                        if( mpInputStream->eof() )
+                            mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
+
+                        break;
+                    }
+
+                    const sal_uInt32 nAvailableBytes = mnEndPos - nPos;
+                    const sal_uInt32 nMaxPossibleRecordSize = nAvailableBytes/2;
+                    if (mnRecSize > nMaxPossibleRecordSize)
+                    {
+                        mpInputStream->SetError(SVSTREAM_FILEFORMAT_ERROR);
+                        break;
+                    }
+
+                    if ( !bEMFAvailable )
+                    {
+                        if(   !maBmpSaveList.empty()
+                          && ( nFunction != W_META_STRETCHDIB    )
+                          && ( nFunction != W_META_DIBBITBLT     )
+                          && ( nFunction != W_META_DIBSTRETCHBLT )
+                          )
+                        {
+                            ResolveBitmapActions( maBmpSaveList );
+                        }
+
+                        if ( !mnSkipActions)
+                            ReadRecordParams( mnRecSize, nFunction );
+                        else
+                            mnSkipActions--;
+
+                        if(mpEMFStream && mnEMFRecCount == mnEMFRec)
+                        {
+                            GDIMetaFile aMeta;
+                            SvMemoryStream aStream(mpEMFStream->data(), mpEMFStream->size(), StreamMode::STD_READ);
+                            std::unique_ptr<EmfReader> pEMFReader(std::make_unique<EmfReader>(aStream, aMeta));
+                            pEMFReader->SetEnableEMFPlus(mbEnableEMFPlus);
+                            bEMFAvailable = pEMFReader->ReadEnhWMF();
+                            pEMFReader.reset(); // destroy first!!!
+
+                            if( bEMFAvailable )
+                            {
+                                AddFromGDIMetaFile( aMeta );
+                                SetrclFrame( tools::Rectangle( Point(0, 0), aMeta.GetPrefSize()));
+
+                                // the stream needs to be set to the wmf end position,
+                                // otherwise the GfxLink that is created will be incorrect
+                                // (leading to graphic loss after swapout/swapin).
+                                // so we will proceed normally, but are ignoring further wmf
+                                // records
+                            }
+                            else
+                            {
+                                // something went wrong
+                                // continue with WMF, don't try this again
+                                mpEMFStream.reset();
+                            }
+                        }
+                    }
+
+                    nPos += mnRecSize * 2;
+                    mpInputStream->Seek(nPos);
+                }
+            }
+            else
+                mpInputStream->SetError( SVSTREAM_GENERALERROR );
+
+            if( !mpInputStream->GetError() && !maBmpSaveList.empty() )
+                ResolveBitmapActions( maBmpSaveList );
+        }
+        if ( mpInputStream->GetError() )
+            mpInputStream->Seek( mnStartPos );
+    }
+
+    void WmfReader::GetPlaceableBound( tools::Rectangle& rPlaceableBound, SvStream* pStm )
+    {
+        bool bRet = true;
+
+        tools::Rectangle aBound;
+        aBound.SetLeft( RECT_MAX );
+        aBound.SetTop( RECT_MAX );
+        aBound.SetRight( RECT_MIN );
+        aBound.SetBottom( RECT_MIN );
+        bool bBoundsDetermined = false;
+
+        auto nPos = pStm->Tell();
+        auto nEnd = nPos + pStm->remainingSize();
+
+        Point aWinOrg(0,0);
+        std::optional<Size>  aWinExt;
+
+        Point aViewportOrg(0,0);
+        std::optional<Size>  aViewportExt;
+
+        MappingMode eMapMode = MappingMode::MM_ANISOTROPIC;
+
+        if (nEnd - nPos)
+        {
+            sal_uInt16 nFunction;
+            sal_uInt32 nRSize;
+
+            while( bRet )
+            {
+                pStm->ReadUInt32( nRSize ).ReadUInt16( nFunction );
+
+                if( pStm->GetError() )
+                {
+                    bRet = false;
+                    break;
+                }
+                else if (pStm->eof() || nRSize < 3)
+                {
+                    pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                    bRet = false;
+                    break;
+                }
+                else if ( nRSize == 3 && nFunction == W_META_EOF )
+                {
+                    break;
+                }
+                switch( nFunction )
+                {
+                    case W_META_EOF:
+                    {
+                        return;
+                    }
+
+                    case W_META_SETWINDOWORG:
+                    {
+                        aWinOrg = ReadYX();
+                    }
+                    break;
+
+                    case W_META_SETWINDOWEXT:
+                    {
+                        sal_Int16 nWidth(0), nHeight(0);
+                        pStm->ReadInt16(nHeight);
+                        pStm->ReadInt16(nWidth);
+                        aWinExt = Size(nWidth, nHeight);
+                    }
+                    break;
+
+                    case W_META_SETVIEWPORTORG:
+                    {
+                        aViewportOrg = ReadYX();
+                    }
+                    break;
+
+                    case W_META_SETVIEWPORTEXT:
+                    {
+                        sal_Int16 nWidth(0), nHeight(0);
+                        pStm->ReadInt16(nHeight);
+                        pStm->ReadInt16(nWidth);
+                        aViewportExt = Size(nWidth, nHeight);
+                    }
+                    break;
+
+                    case W_META_SETMAPMODE :
+                    {
+                        sal_Int16 nMapMode(0);
+                        pStm->ReadInt16( nMapMode );
+                        eMapMode = static_cast<MappingMode>(nMapMode);
+                    }
+                    break;
+
+                    case W_META_MOVETO:
+                    case W_META_LINETO:
+                    {
+                        GetWinExtMax( ReadYX(), aBound, eMapMode );
+                        bBoundsDetermined = true;
+                    }
+                    break;
+
+                    case W_META_RECTANGLE:
+                    case W_META_INTERSECTCLIPRECT:
+                    case W_META_EXCLUDECLIPRECT :
+                    case W_META_ELLIPSE:
+                    {
+                        GetWinExtMax( ReadRectangle(), aBound, eMapMode );
+                        bBoundsDetermined = true;
+                    }
+                    break;
+
+                    case W_META_ROUNDRECT:
+                    {
+                        ReadYXExt(); // size
+                        GetWinExtMax( ReadRectangle(), aBound, eMapMode );
+                        bBoundsDetermined = true;
+                    }
+                    break;
+
+                    case W_META_ARC:
+                    case W_META_PIE:
+                    case W_META_CHORD:
+                    {
+                        ReadYX(); // end
+                        ReadYX(); // start
+                        GetWinExtMax( ReadRectangle(), aBound, eMapMode );
+                        bBoundsDetermined = true;
+                    }
+                    break;
+
+                    case W_META_POLYGON:
+                    {
+                        bool bRecordOk = true;
+
+                        sal_uInt16 nPoints(0);
+                        pStm->ReadUInt16( nPoints );
+
+                        if (nPoints > pStm->remainingSize() / (2 * sizeof(sal_uInt16)))
+                        {
+                            bRecordOk = false;
+                        }
+                        else
+                        {
+                            for(sal_uInt16 i = 0; i < nPoints; i++ )
+                            {
+                                GetWinExtMax( ReadPoint(), aBound, eMapMode );
+                                bBoundsDetermined = true;
+                            }
+                        }
+
+                        bRecordOk &= pStm->good();
+
+                        SAL_WARN_IF(!bRecordOk, "emfio", "polyline record claimed more points than the stream can provide");
+
+                        if (!bRecordOk)
+                        {
+                            pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                            bRet = false;
+                            break;
+                        }
+                    }
+                    break;
+
+                    case W_META_POLYPOLYGON:
+                    {
+                        bool bRecordOk = true;
+                        sal_uInt16 nPoly(0), nPoints(0);
+                        pStm->ReadUInt16(nPoly);
+                        if (nPoly > pStm->remainingSize() / sizeof(sal_uInt16))
+                        {
+                            bRecordOk = false;
+                        }
+                        else
+                        {
+                            for(sal_uInt16 i = 0; i < nPoly; i++ )
+                            {
+                                sal_uInt16 nP = 0;
+                                pStm->ReadUInt16( nP );
+                                if (nP > SAL_MAX_UINT16 - nPoints)
+                                {
+                                    bRecordOk = false;
+                                    break;
+                                }
+                                nPoints += nP;
+                            }
+                        }
+
+                        SAL_WARN_IF(!bRecordOk, "emfio", "polypolygon record has more polygons than we can handle");
+
+                        bRecordOk &= pStm->good();
+
+                        if (!bRecordOk)
+                        {
+                            pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                            bRet = false;
+                            break;
+                        }
+
+                        if (nPoints > pStm->remainingSize() / (2 * sizeof(sal_uInt16)))
+                        {
+                            bRecordOk = false;
+                        }
+                        else
+                        {
+                            for (sal_uInt16 i = 0; i < nPoints; i++ )
+                            {
+                                GetWinExtMax( ReadPoint(), aBound, eMapMode );
+                                bBoundsDetermined = true;
+                            }
+                        }
+
+                        SAL_WARN_IF(!bRecordOk, "emfio", "polypolygon record claimed more points than the stream can provide");
+
+                        bRecordOk &= pStm->good();
+
+                        if (!bRecordOk)
+                        {
+                            pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                            bRet = false;
+                            break;
+                        }
+                    }
+                    break;
+
+                    case W_META_POLYLINE:
+                    {
+                        bool bRecordOk = true;
+
+                        sal_uInt16 nPoints(0);
+                        pStm->ReadUInt16(nPoints);
+                        if (nPoints > pStm->remainingSize() / (2 * sizeof(sal_uInt16)))
+                        {
+                            bRecordOk = false;
+                        }
+                        else
+                        {
+                            for (sal_uInt16 i = 0; i < nPoints; ++i)
+                            {
+                                GetWinExtMax( ReadPoint(), aBound, eMapMode );
+                                bBoundsDetermined = true;
+                            }
+                        }
+
+                        SAL_WARN_IF(!bRecordOk, "emfio", "polyline record claimed more points than the stream can provide");
+
+                        bRecordOk &= pStm->good();
+
+                        if (!bRecordOk)
+                        {
+                            pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                            bRet = false;
+                            break;
+                        }
+                    }
+                    break;
+
+                    case W_META_SETPIXEL:
+                    {
+                        ReadColor();
+                        GetWinExtMax( ReadYX(), aBound, eMapMode );
+                        bBoundsDetermined = true;
+                    }
+                    break;
+
+                    case W_META_TEXTOUT:
+                    {
+                        sal_uInt16 nLength(0);
+                        pStm->ReadUInt16( nLength );
+                        // todo: we also have to take care of the text width
+                        if ( nLength )
+                        {
+                            pStm->SeekRel( ( nLength + 1 ) &~ 1 );
+                            GetWinExtMax( ReadYX(), aBound, eMapMode );
+                            bBoundsDetermined = true;
+                        }
+                    }
+                    break;
+
+                    case W_META_EXTTEXTOUT:
+                    {
+                        sal_uInt16 nLen(0), nOptions;
+                        Point aPosition = ReadYX();
+                        pStm->ReadUInt16( nLen ).ReadUInt16( nOptions );
+                        // todo: we also have to take care of the text width
+                        if( nLen )
+                        {
+                            GetWinExtMax( aPosition, aBound, eMapMode );
+                            bBoundsDetermined = true;
+                        }
+                    }
+                    break;
+                    case W_META_BITBLT:
+                    case W_META_DIBBITBLT:
+                    case W_META_DIBSTRETCHBLT:
+                    case W_META_STRETCHBLT:
+                    case W_META_STRETCHDIB:
+                    {
+                        sal_uInt32 nRasterOperation;
+                        sal_Int16 nYSrc, nXSrc;
+                        sal_uInt16 nColorUsage;
+                        pStm->ReadUInt32( nRasterOperation );
+
+                        if( nFunction == W_META_STRETCHDIB )
+                            pStm->ReadUInt16( nColorUsage );
+
+                        if( nFunction == W_META_DIBSTRETCHBLT ||
+                            nFunction == W_META_STRETCHBLT ||
+                            nFunction == W_META_STRETCHDIB )
+                        {
+                            sal_Int16 nSrcHeight, nSrcWidth;
+                            pStm->ReadInt16( nSrcHeight ).ReadInt16( nSrcWidth );
+                        }
+
+                        // nYSrc and nXSrc is the offset of the first pixel
+                        pStm->ReadInt16( nYSrc ).ReadInt16( nXSrc );
+
+                        const bool bNoSourceBitmap = ( nFunction != W_META_STRETCHDIB ) && ( nRSize == ( ( static_cast< sal_uInt32 >( nFunction ) >> 8 ) + 3 ) );
+                        if ( bNoSourceBitmap )
+                            mpInputStream->SeekRel( 2 ); // Skip Reserved 2 bytes (it must be ignored)
+
+                        Size aDestSize( ReadYXExt() );
+                        if ( aDestSize.Width() && aDestSize.Height() )  // #92623# do not try to read buggy bitmaps
+                        {
+                            tools::Rectangle aDestRect( ReadYX(), aDestSize );
+                            GetWinExtMax( aDestRect, aBound, eMapMode );
+                            bBoundsDetermined = true;
+                        }
+                    }
+                    break;
+
+                    case W_META_PATBLT:
+                    {
+                        sal_uInt32 nROP(0);
+                        pStm->ReadUInt32( nROP );
+                        Size aSize = ReadYXExt();
+                        GetWinExtMax( tools::Rectangle( ReadYX(), aSize ), aBound, eMapMode );
+                        bBoundsDetermined = true;
+                    }
+                    break;
+                }
+
+                const auto nAvailableBytes = nEnd - nPos;
+                const auto nMaxPossibleRecordSize = nAvailableBytes/2;
+                if (nRSize <= nMaxPossibleRecordSize)
+                {
+                    nPos += nRSize * 2;
+                    pStm->Seek(nPos);
+                }
+                else
+                {
+                    pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
+                    bRet = false;
+                }
+            }
+        }
+        else
+        {
+            pStm->SetError( SVSTREAM_GENERALERROR );
+            bRet = false;
+        }
+
+        if (!bRet)
+            return;
+
+        if (aWinExt)
+        {
+            rPlaceableBound = tools::Rectangle(aWinOrg, *aWinExt);
+            if (mbPlaceable && eMapMode == MM_ANISOTROPIC)
+            {
+                // It seems that (in MM_ANISOTROPIC WMFs) the "inch" field (PPI) in META_PLACEABLE is
+                // ignored and instead competitor office suites decide what it should be arbitrarily
+                // Could have to do with MM_ANISOTROPICs definition:
+                // Logical units are mapped to arbitrary units with arbitrarily scaled axes.
+                // The issue is that when PPI is bigger than the window size, the image appears
+                // tiny (smaller than an inch squared).
+                // A solution is to scale PPI down in such images to an arbitrary amount that makes
+                // the image visible:
+                auto nWidth = rPlaceableBound.GetWidth();
+                auto nHeight = rPlaceableBound.GetHeight();
+                if (mnUnitsPerInch > nWidth && mnUnitsPerInch > nHeight)
+                    mnUnitsPerInch = std::max(nWidth, nHeight);
+            }
+            SAL_INFO("emfio", "Window dimension "
+                       " left: " << rPlaceableBound.Left()  << " top: " << rPlaceableBound.Top()
+                    << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom());
+        }
+        else if (aViewportExt)
+        {
+            rPlaceableBound = tools::Rectangle(aViewportOrg, *aViewportExt);
+            SAL_INFO("emfio", "Viewport dimension "
+                       " left: " << rPlaceableBound.Left()  << " top: " << rPlaceableBound.Top()
+                    << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom());
+        }
+        else if (bBoundsDetermined)
+        {
+            rPlaceableBound = aBound;
+            SAL_INFO("emfio", "Determined dimension "
+                       " left: " << rPlaceableBound.Left()  << " top: " << rPlaceableBound.Top()
+                    << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom());
+        }
+        else
+        {
+            rPlaceableBound.SetLeft( 0 );
+            rPlaceableBound.SetTop( 0 );
+            rPlaceableBound.SetRight( aMaxWidth );
+            rPlaceableBound.SetBottom( aMaxWidth );
+            SAL_INFO("emfio", "Default dimension "
+                       " left: " << rPlaceableBound.Left()  << " top: " << rPlaceableBound.Top()
+                    << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom());
+        }
+    }
+
+    WmfReader::WmfReader(SvStream& rStreamWMF, GDIMetaFile& rGDIMetaFile, const WmfExternal* pExternalHeader)
+        : MtfTools(rGDIMetaFile, rStreamWMF)
+        , mnUnitsPerInch(96)
+        , mnRecSize(0)
+        , mbPlaceable(false)
+        , mnEMFRecCount(0)
+        , mnEMFRec(0)
+        , mnEMFSize(0)
+        , mnSkipActions(0)
+        , mpExternalHeader(pExternalHeader)
+    {
+    }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
-- 
cgit v1.2.3