summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2017-06-15 16:35:31 +0200
committerThorsten Behrens <Thorsten.Behrens@CIB.de>2017-07-15 11:01:29 +0200
commitc7470f5be441d8fe80155ff29605d74d5838be26 (patch)
tree1547af7f13f4d83d972f86115d0fc49a8a734105
parentb93d0cadb79f6652dec4d29ef20813f1d57cc708 (diff)
emfplus: add rough version of local reader
Complete redevelopent is too expensive, start with adding a copy of the existing vcl importer which will in the next steps be adapted to import primitives instead of MetaFile(Actions). Adapted namespace, made compile and added sample code to roughly use it Change-Id: I79e7ea0d78099fbbe18e2a595457b2ab353f58ea
-rw-r--r--emfio/Library_emfio.mk8
-rw-r--r--emfio/inc/emfreader.hxx55
-rw-r--r--emfio/inc/mtftools.hxx667
-rw-r--r--emfio/inc/wmfreader.hxx106
-rw-r--r--emfio/source/emfuno/xemfparser.cxx101
-rw-r--r--emfio/source/reader/emfreader.cxx1940
-rw-r--r--emfio/source/reader/mtftools.cxx2283
-rw-r--r--emfio/source/reader/wmfreader.cxx1846
-rw-r--r--include/vcl/dibtools.hxx3
9 files changed, 6957 insertions, 52 deletions
diff --git a/emfio/Library_emfio.mk b/emfio/Library_emfio.mk
index e9a14249271a..ff209f5da3bf 100644
--- a/emfio/Library_emfio.mk
+++ b/emfio/Library_emfio.mk
@@ -25,6 +25,10 @@ $(eval $(call gb_Library_set_include,emfio,\
-I$(SRCDIR)/emfio/inc \
))
+$(eval $(call gb_Library_use_custom_headers,emfio,\
+ officecfg/registry \
+))
+
$(eval $(call gb_Library_use_external,emfio,boost_headers))
$(eval $(call gb_Library_set_precompiled_header,emfio,$(SRCDIR)/emfio/inc/pch/precompiled_emfio))
@@ -37,6 +41,7 @@ $(eval $(call gb_Library_use_libraries,emfio,\
cppu \
cppuhelper \
sal \
+ comphelper \
tl \
sax \
vcl \
@@ -47,6 +52,9 @@ $(eval $(call gb_Library_use_libraries,emfio,\
$(eval $(call gb_Library_add_exception_objects,emfio,\
emfio/source/emfuno/emfuno \
emfio/source/emfuno/xemfparser \
+ emfio/source/reader/mtftools \
+ emfio/source/reader/wmfreader \
+ emfio/source/reader/emfreader \
))
# vim: set noet sw=4 ts=4:
diff --git a/emfio/inc/emfreader.hxx b/emfio/inc/emfreader.hxx
new file mode 100644
index 000000000000..c4f7e17e574d
--- /dev/null
+++ b/emfio/inc/emfreader.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_EMFIO_INC_EMFREADER_HXX
+#define INCLUDED_EMFIO_INC_EMFREADER_HXX
+
+#include <mtftools.hxx>
+
+namespace emfio
+{
+ class EmfReader : public MtfTools
+ {
+ bool bRecordPath;
+ sal_Int32 nRecordCount;
+ bool bEMFPlus;
+
+ bool ReadHeader();
+ // reads and converts the rectangle
+ static tools::Rectangle ReadRectangle(sal_Int32, sal_Int32, sal_Int32, sal_Int32);
+
+ public:
+ EmfReader(SvStream& rStreamWMF, GDIMetaFile& rGDIMetaFile, FilterConfigItem* pConfigItem = nullptr);
+ ~EmfReader();
+
+ bool ReadEnhWMF();
+ private:
+ template <class T> void ReadAndDrawPolyPolygon();
+ template <class T> void ReadAndDrawPolyLine();
+ template <class T> tools::Polygon ReadPolygon(sal_uInt32 nStartIndex, sal_uInt32 nPoints);
+ template <class T, class Drawer> void ReadAndDrawPolygon(Drawer drawer, const bool skipFirst);
+
+ tools::Rectangle ReadRectangle();
+ void ReadEMFPlusComment(sal_uInt32 length, bool& bHaveDC);
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/emfio/inc/mtftools.hxx b/emfio/inc/mtftools.hxx
new file mode 100644
index 000000000000..f20925f5a147
--- /dev/null
+++ b/emfio/inc/mtftools.hxx
@@ -0,0 +1,667 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_EMFIO_INC_MTFTOOLS_HXX
+#define INCLUDED_EMFIO_INC_MTFTOOLS_HXX
+
+#include <memory>
+#include <sal/config.h>
+
+//#include <vcl/graph.hxx>
+#include <basegfx/tools/b2dclipstate.hxx>
+#include <tools/poly.hxx>
+#include <vcl/font.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+//#include <vcl/bitmapaccess.hxx>
+#include <vcl/lineinfo.hxx>
+//#include <vcl/fltcall.hxx>
+#include <o3tl/make_unique.hxx>
+#include <vcl/outdevstate.hxx>
+#include <vcl/FilterConfigItem.hxx>
+
+#define ERROR 0
+#define NULLREGION 1
+#define COMPLEXREGION 3
+
+#define RGN_AND 1
+#define RGN_OR 2
+#define RGN_XOR 3
+#define RGN_DIFF 4
+#define RGN_COPY 5
+
+namespace emfio
+{
+ enum class BkMode
+ {
+ NONE = 0,
+ Transparent = 1,
+ OPAQUE = 2,
+ };
+}
+
+/* xform stuff */
+#define MWT_IDENTITY 1
+#define MWT_LEFTMULTIPLY 2
+#define MWT_RIGHTMULTIPLY 3
+#define MWT_SET 4
+
+#define ENHMETA_STOCK_OBJECT 0x80000000
+
+/* Stock Logical Objects */
+#define WHITE_BRUSH 0
+#define LTGRAY_BRUSH 1
+#define GRAY_BRUSH 2
+#define DKGRAY_BRUSH 3
+#define BLACK_BRUSH 4
+#define NULL_BRUSH 5
+#define WHITE_PEN 6
+#define BLACK_PEN 7
+#define NULL_PEN 8
+#define ANSI_FIXED_FONT 11
+#define ANSI_VAR_FONT 12
+#define SYSTEM_FIXED_FONT 16
+
+namespace emfio
+{
+ enum class WMFRasterOp {
+ NONE = 0,
+ Black = 1,
+ Not = 6,
+ XorPen = 7,
+ Nop = 11,
+ CopyPen = 13
+ };
+}
+
+/* Mapping modes */
+#define MM_TEXT 1
+#define MM_LOMETRIC 2
+#define MM_HIMETRIC 3
+#define MM_LOENGLISH 4
+#define MM_HIENGLISH 5
+#define MM_TWIPS 6
+#define MM_ISOTROPIC 7
+#define MM_ANISOTROPIC 8
+
+/* Graphics modes */
+#define GM_COMPATIBLE 1
+#define GM_ADVANCED 2
+
+/* StretchBlt() modes */
+#define BLACKONWHITE 1
+#define WHITEONBLACK 2
+#define COLORONCOLOR 3
+#define HALFTONE 4
+#define STRETCH_ANDSCANS BLACKONWHITE
+#define STRETCH_ORSCANS WHITEONBLACK
+#define STRETCH_DELETESCANS COLORONCOLOR
+
+#define LF_FACESIZE 32
+
+namespace emfio
+{
+ struct LOGFONTW
+ {
+ sal_Int32 lfHeight;
+ sal_Int32 lfWidth;
+ sal_Int32 lfEscapement;
+ sal_Int32 lfOrientation;
+ sal_Int32 lfWeight;
+ sal_uInt8 lfItalic;
+ sal_uInt8 lfUnderline;
+ sal_uInt8 lfStrikeOut;
+ sal_uInt8 lfCharSet;
+ sal_uInt8 lfOutPrecision;
+ sal_uInt8 lfClipPrecision;
+ sal_uInt8 lfQuality;
+ sal_uInt8 lfPitchAndFamily;
+ OUString alfFaceName;
+ };
+ struct WMF_EXTERNALHEADER;
+}
+
+#define TA_NOUPDATECP 0x0000
+#define TA_UPDATECP 0x0001
+#define TA_LEFT 0x0000
+#define TA_RIGHT 0x0002
+#define TA_CENTER 0x0006
+#define TA_RIGHT_CENTER (TA_RIGHT | TA_CENTER)
+#define TA_TOP 0x0000
+#define TA_BOTTOM 0x0008
+#define TA_BASELINE 0x0018
+
+#define SRCCOPY 0x00CC0020L
+#define SRCPAINT 0x00EE0086L
+#define SRCAND 0x008800C6L
+#define SRCINVERT 0x00660046L
+#define SRCERASE 0x00440328L
+#define PATCOPY 0x00F00021L
+#define PATINVERT 0x005A0049L
+#define BLACKNESS 0x00000042L
+#define WHITENESS 0x00FF0062L
+
+#define PS_SOLID 0
+#define PS_DASH 1
+#define PS_DOT 2
+#define PS_DASHDOT 3
+#define PS_DASHDOTDOT 4
+#define PS_NULL 5
+#define PS_INSIDEFRAME 6
+#define PS_STYLE_MASK 15
+
+#define PS_ENDCAP_ROUND 0x000
+#define PS_ENDCAP_SQUARE 0x100
+#define PS_ENDCAP_FLAT 0x200
+#define PS_ENDCAP_STYLE_MASK 0xF00
+
+#define PS_JOIN_ROUND 0x0000
+#define PS_JOIN_BEVEL 0x1000
+#define PS_JOIN_MITER 0x2000
+#define PS_JOIN_STYLE_MASK 0xF000
+
+#define ANSI_CHARSET 0
+#define DEFAULT_CHARSET 1
+#define SYMBOL_CHARSET 2
+#define SHIFTJIS_CHARSET 128
+#define HANGEUL_CHARSET 129
+#define GB2312_CHARSET 134
+#define CHINESEBIG5_CHARSET 136
+#define OEM_CHARSET 255
+/*WINVER >= 0x0400*/
+#define JOHAB_CHARSET 130
+#define HEBREW_CHARSET 177
+#define ARABIC_CHARSET 178
+#define GREEK_CHARSET 161
+#define TURKISH_CHARSET 162
+#define VIETNAMESE_CHARSET 163
+#define THAI_CHARSET 222
+#define EASTEUROPE_CHARSET 238
+#define RUSSIAN_CHARSET 204
+#define MAC_CHARSET 77
+#define BALTIC_CHARSET 186
+
+#define ETO_CLIPPED 0x0004
+/*WINVER >= 0x0400*/
+#define ETO_GLYPH_INDEX 0x0010
+#define ETO_RTLREADING 0x0080
+/*_WIN32_WINNT >= 0x0500*/
+#define ETO_PDY 0x2000
+
+#define DEFAULT_PITCH 0x00
+#define FIXED_PITCH 0x01
+#define VARIABLE_PITCH 0x02
+
+/* Font Families */
+#define FF_DONTCARE 0x00
+#define FF_ROMAN 0x10
+#define FF_SWISS 0x20
+#define FF_MODERN 0x30
+#define FF_SCRIPT 0x40
+#define FF_DECORATIVE 0x50
+
+#define FW_THIN 100
+#define FW_EXTRALIGHT 200
+#define FW_LIGHT 300
+#define FW_NORMAL 400
+#define FW_MEDIUM 500
+#define FW_SEMIBOLD 600
+#define FW_BOLD 700
+#define FW_EXTRABOLD 800
+#define FW_ULTRALIGHT 200
+#define FW_ULTRABOLD 800
+#define FW_BLACK 900
+
+#define BS_SOLID 0
+#define BS_NULL 1
+#define BS_HOLLOW 1
+#define BS_HATCHED 2
+#define BS_PATTERN 3
+#define BS_INDEXED 4
+#define BS_DIBPATTERN 5
+#define BS_DIBPATTERNPT 6
+#define BS_PATTERN8X8 7
+#define BS_DIBPATTERN8X8 8
+#define BS_MONOPATTERN 9
+
+
+#define RDH_RECTANGLES 1
+
+#define W_MFCOMMENT 15
+
+#define PRIVATE_ESCAPE_UNICODE 2
+
+//Scalar constants
+
+#define UNDOCUMENTED_WIN_RCL_RELATION 32
+#define MS_FIXPOINT_BITCOUNT_28_4 4
+#define HUNDREDTH_MILLIMETERS_PER_MILLIINCH 2.54
+#define MILLIINCH_PER_TWIPS 1.44
+
+//============================ WmfReader ==================================
+
+namespace emfio
+{
+ class WinMtfClipPath
+ {
+ basegfx::tools::B2DClipState maClip;
+
+ public:
+ WinMtfClipPath() : maClip() {};
+
+ void setClipPath(const tools::PolyPolygon& rPolyPolygon, sal_Int32 nClippingMode);
+ void intersectClipRect(const tools::Rectangle& rRect);
+ void excludeClipRect(const tools::Rectangle& rRect);
+ void moveClipRegion(const Size& rSize);
+ void setDefaultClipPath();
+
+ bool isEmpty() const { return maClip.isCleared(); }
+
+ basegfx::B2DPolyPolygon getClipPath() const;
+
+ bool operator==(const WinMtfClipPath& rPath) const
+ {
+ return maClip == rPath.maClip;
+ };
+ };
+
+ class WinMtfPathObj : public tools::PolyPolygon
+ {
+ bool bClosed;
+
+ public:
+
+ WinMtfPathObj() :
+ bClosed(true)
+ {}
+
+ void Init()
+ {
+ Clear();
+ bClosed = true;
+ }
+
+ void ClosePath();
+ void AddPoint(const Point& rPoint);
+ void AddPolygon(const tools::Polygon& rPoly);
+ void AddPolyLine(const tools::Polygon& rPoly);
+ void AddPolyPolygon(const tools::PolyPolygon& rPolyPolygon);
+ };
+
+ struct GDIObj
+ {
+ virtual ~GDIObj() = default; // Polymorphic base class
+ };
+
+ struct WinMtfFontStyle : GDIObj
+ {
+ vcl::Font aFont;
+
+ explicit WinMtfFontStyle(LOGFONTW& rLogFont);
+ };
+
+ enum class WinMtfFillStyleType
+ {
+ Solid, Pattern
+ };
+
+ struct WinMtfFillStyle : GDIObj
+ {
+ Color aFillColor;
+ bool bTransparent;
+ WinMtfFillStyleType aType;
+ Bitmap aBmp;
+
+ WinMtfFillStyle()
+ : aFillColor(Color(COL_BLACK))
+ , bTransparent(false)
+ , aType(WinMtfFillStyleType::Solid)
+ {}
+
+ WinMtfFillStyle(const Color& rColor, bool bTrans = false)
+ : aFillColor(rColor)
+ , bTransparent(bTrans)
+ , aType(WinMtfFillStyleType::Solid)
+ {}
+
+ explicit WinMtfFillStyle(Bitmap& rBmp)
+ : bTransparent(false)
+ , aType(WinMtfFillStyleType::Pattern)
+ , aBmp(rBmp)
+ {}
+
+ bool operator==(const WinMtfFillStyle& rStyle)
+ {
+ return aFillColor == rStyle.aFillColor
+ && bTransparent == rStyle.bTransparent
+ && aType == rStyle.aType;
+ }
+ };
+
+ struct WinMtfLineStyle : GDIObj
+ {
+ Color aLineColor;
+ LineInfo aLineInfo;
+ bool bTransparent;
+
+ WinMtfLineStyle()
+ : aLineColor(COL_BLACK)
+ , bTransparent(false)
+ {}
+
+ WinMtfLineStyle(const Color& rColor, bool bTrans = false)
+ : aLineColor(rColor)
+ , bTransparent(bTrans)
+ {}
+
+ WinMtfLineStyle(const Color& rColor, const LineInfo& rStyle, bool bTrans)
+ : aLineColor(rColor)
+ , aLineInfo(rStyle)
+ , bTransparent(bTrans)
+ {}
+
+ bool operator==(const WinMtfLineStyle& rStyle)
+ {
+ return aLineColor == rStyle.aLineColor
+ && bTransparent == rStyle.bTransparent
+ && aLineInfo == rStyle.aLineInfo;
+ }
+ };
+
+ struct XForm
+ {
+ float eM11;
+ float eM12;
+ float eM21;
+ float eM22;
+ float eDx;
+ float eDy;
+
+ XForm()
+ : eM11(1.0f)
+ , eM12(0.0f)
+ , eM21(0.0f)
+ , eM22(1.0f)
+ , eDx(0.0f)
+ , eDy(0.0f)
+ {}
+ };
+
+ SvStream& operator >> (SvStream& rInStream, XForm& rXForm);
+
+ struct SaveStruct
+ {
+ BkMode nBkMode;
+ sal_uInt32 nMapMode, nGfxMode;
+ ComplexTextLayoutFlags nTextLayoutMode;
+ sal_Int32 nWinOrgX, nWinOrgY, nWinExtX, nWinExtY;
+ sal_Int32 nDevOrgX, nDevOrgY, nDevWidth, nDevHeight;
+
+ WinMtfLineStyle aLineStyle;
+ WinMtfFillStyle aFillStyle;
+
+ vcl::Font aFont;
+ Color aBkColor;
+ Color aTextColor;
+ sal_uInt32 nTextAlign;
+ RasterOp eRasterOp;
+
+ Point aActPos;
+ WinMtfPathObj aPathObj;
+ WinMtfClipPath aClipPath;
+ XForm aXForm;
+
+ bool bFillStyleSelected;
+ };
+
+ struct BSaveStruct
+ {
+ BitmapEx aBmpEx;
+ tools::Rectangle aOutRect;
+ sal_uInt32 nWinRop;
+
+ BSaveStruct(const Bitmap& rBmp, const tools::Rectangle& rOutRect, sal_uInt32 nRop)
+ : aBmpEx(rBmp)
+ , aOutRect(rOutRect)
+ , nWinRop(nRop)
+ {}
+
+ BSaveStruct(const BitmapEx& rBmpEx, const tools::Rectangle& rOutRect, sal_uInt32 nRop)
+ : aBmpEx(rBmpEx)
+ , aOutRect(rOutRect)
+ , nWinRop(nRop)
+ {}
+ };
+
+ class MtfToolsWriter final
+ {
+ WinMtfPathObj aPathObj;
+ WinMtfClipPath aClipPath;
+
+ WinMtfLineStyle maLatestLineStyle;
+ WinMtfLineStyle maLineStyle;
+ WinMtfLineStyle m_NopLineStyle;
+ WinMtfFillStyle maLatestFillStyle;
+ WinMtfFillStyle maFillStyle;
+ WinMtfFillStyle m_NopFillStyle;
+ vcl::Font maLatestFont;
+ vcl::Font maFont;
+ sal_uInt32 mnLatestTextAlign;
+ sal_uInt32 mnTextAlign;
+ Color maLatestTextColor;
+ Color maTextColor;
+ Color maLatestBkColor;
+ Color maBkColor;
+ ComplexTextLayoutFlags mnLatestTextLayoutMode;
+ ComplexTextLayoutFlags mnTextLayoutMode;
+ BkMode mnLatestBkMode;
+ BkMode mnBkMode;
+ RasterOp meLatestRasterOp;
+ RasterOp meRasterOp;
+
+ std::vector< std::unique_ptr<GDIObj> > vGDIObj;
+
+ Point maActPos;
+
+ WMFRasterOp mnRop;
+ bool mbNopMode;
+ bool mbFillStyleSelected;
+ bool mbClipNeedsUpdate;
+ bool mbComplexClip;
+
+ std::vector< std::shared_ptr<SaveStruct> > vSaveStack;
+
+ sal_uInt32 mnGfxMode;
+ sal_uInt32 mnMapMode;
+
+ XForm maXForm;
+ sal_Int32 mnDevOrgX, mnDevOrgY;
+ sal_Int32 mnDevWidth, mnDevHeight;
+ sal_Int32 mnWinOrgX, mnWinOrgY; // aktuel window origin
+ sal_Int32 mnWinExtX, mnWinExtY; // aktuel window extend
+ bool mbIsMapWinSet;
+ bool mbIsMapDevSet;
+
+ sal_Int32 mnPixX, mnPixY; // Reference Device in pixel
+ sal_Int32 mnMillX, mnMillY; // Reference Device in Mill
+ tools::Rectangle mrclFrame; // rectangle in logical units 1/100th mm
+ tools::Rectangle mrclBounds;
+
+ GDIMetaFile* mpGDIMetaFile;
+
+ void UpdateLineStyle();
+ void UpdateFillStyle();
+
+ Point ImplMap(const Point& rPt);
+ Point ImplScale(const Point& rPt);
+ Size ImplMap(const Size& rSize, bool bDoWorldTransform = true);
+ tools::Rectangle ImplMap(const tools::Rectangle& rRectangle);
+ void ImplMap(vcl::Font& rFont);
+ tools::Polygon& ImplMap(tools::Polygon& rPolygon);
+ tools::PolyPolygon& ImplMap(tools::PolyPolygon& rPolyPolygon);
+ void ImplScale(tools::Polygon& rPolygon);
+ tools::PolyPolygon& ImplScale(tools::PolyPolygon& rPolyPolygon);
+ void ImplResizeObjectArry(sal_uInt32 nNewEntry);
+ void ImplSetNonPersistentLineColorTransparenz();
+ void ImplDrawClippedPolyPolygon(const tools::PolyPolygon& rPolyPoly);
+ void ImplDrawBitmap(const Point& rPos, const Size& rSize, const BitmapEx& rBitmap);
+
+ public:
+
+ void SetDevByWin(); //Hack to set varying defaults for incompletely defined files.
+ void SetDevOrg(const Point& rPoint);
+ void SetDevOrgOffset(sal_Int32 nXAdd, sal_Int32 nYAdd);
+ void SetDevExt(const Size& rSize, bool regular = true);
+ void ScaleDevExt(double fX, double fY);
+
+ void SetWinOrg(const Point& rPoint, bool bIsEMF = false);
+ void SetWinOrgOffset(sal_Int32 nX, sal_Int32 nY);
+ void SetWinExt(const Size& rSize, bool bIsEMF = false);
+ void ScaleWinExt(double fX, double fY);
+
+ void SetrclBounds(const tools::Rectangle& rRect);
+ void SetrclFrame(const tools::Rectangle& rRect);
+ void SetRefPix(const Size& rSize);
+ void SetRefMill(const Size& rSize);
+
+ void SetMapMode(sal_uInt32 mnMapMode);
+ void SetWorldTransform(const XForm& rXForm);
+ void ModifyWorldTransform(const XForm& rXForm, sal_uInt32 nMode);
+
+ void Push();
+ void Pop();
+
+ WMFRasterOp SetRasterOp(WMFRasterOp nRasterOp);
+ void StrokeAndFillPath(bool bStroke, bool bFill);
+
+ void SetGfxMode(sal_Int32 nGfxMode) { mnGfxMode = nGfxMode; };
+ sal_Int32 GetGfxMode() const { return mnGfxMode; };
+ void SetBkMode(BkMode nMode);
+ void SetBkColor(const Color& rColor);
+ void SetTextColor(const Color& rColor);
+ void SetTextAlign(sal_uInt32 nAlign);
+
+ void CreateObject(std::unique_ptr<GDIObj> pObject);
+ void CreateObjectIndexed(sal_Int32 nIndex, std::unique_ptr<GDIObj> pObject);
+
+ void CreateObject()
+ {
+ CreateObject(o3tl::make_unique<GDIObj>());
+ }
+
+ void DeleteObject(sal_Int32 nIndex);
+ void SelectObject(sal_Int32 nIndex);
+ rtl_TextEncoding GetCharSet() { return maFont.GetCharSet(); };
+ const vcl::Font& GetFont() const { return maFont; }
+ void SetTextLayoutMode(ComplexTextLayoutFlags nLayoutMode);
+
+ void ClearPath() { aPathObj.Init(); };
+ void ClosePath() { aPathObj.ClosePath(); };
+ const tools::PolyPolygon& GetPathObj() { return aPathObj; };
+
+ void MoveTo(const Point& rPoint, bool bRecordPath = false);
+ void LineTo(const Point& rPoint, bool bRecordPath = false);
+ void DrawPixel(const Point& rSource, const Color& rColor);
+ void DrawRect(const tools::Rectangle& rRect, bool bEdge = true);
+ void DrawRoundRect(const tools::Rectangle& rRect, const Size& rSize);
+ void DrawEllipse(const tools::Rectangle& rRect);
+ void DrawArc(
+ const tools::Rectangle& rRect,
+ const Point& rStartAngle,
+ const Point& rEndAngle,
+ bool bDrawTo = false
+ );
+ void DrawPie(
+ const tools::Rectangle& rRect,
+ const Point& rStartAngle,
+ const Point& rEndAngle
+ );
+ void DrawChord(
+ const tools::Rectangle& rRect,
+ const Point& rStartAngle,
+ const Point& rEndAngle
+ );
+ void DrawPolygon(tools::Polygon& rPolygon, bool bRecordPath);
+ void DrawPolyPolygon(tools::PolyPolygon& rPolyPolygon, bool bRecordPath = false);
+ void DrawPolyLine(tools::Polygon& rPolygon,
+ bool bDrawTo = false,
+ bool bRecordPath = false
+ );
+ void DrawPolyBezier(tools::Polygon& rPolygin,
+ bool bDrawTo,
+ bool bRecordPath
+ );
+ void DrawText(Point& rPosition,
+ OUString& rString,
+ long* pDXArry = nullptr,
+ long* pDYArry = nullptr,
+ bool bRecordPath = false,
+ sal_Int32 nGraphicsMode = GM_COMPATIBLE);
+
+ void ResolveBitmapActions(std::vector<std::unique_ptr<BSaveStruct>>& rSaveList);
+
+ void IntersectClipRect(const tools::Rectangle& rRect);
+ void ExcludeClipRect(const tools::Rectangle& rRect);
+ void MoveClipRegion(const Size& rSize);
+ void SetClipPath(
+ const tools::PolyPolygon& rPolyPoly,
+ sal_Int32 nClippingMode,
+ bool bIsMapped
+ );
+ void SetDefaultClipPath();
+ void UpdateClipRegion();
+ void AddFromGDIMetaFile(GDIMetaFile& rGDIMetaFile);
+
+ void PassEMFPlus(void* pBuffer, sal_uInt32 nLength);
+ void PassEMFPlusHeaderInfo();
+
+ explicit MtfToolsWriter(GDIMetaFile& rGDIMetaFile);
+ ~MtfToolsWriter();
+ };
+
+ class MtfTools
+ {
+ protected:
+ std::unique_ptr<MtfToolsWriter> pOut;
+ SvStream* pWMF; // the WMF/EMF file to be read
+
+ sal_uInt32 nStartPos, nEndPos;
+ std::vector<std::unique_ptr<BSaveStruct>> aBmpSaveList;
+
+ FilterConfigItem* pFilterConfigItem;
+
+ css::uno::Reference< css::task::XStatusIndicator > xStatusIndicator;
+
+ // assures aSampledBrush is the actual brush of the GDIMetaFile
+
+ Color ReadColor();
+ void Callback(sal_uInt16 nPercent);
+
+ MtfTools(
+ GDIMetaFile& rGDIMetaFile,
+ SvStream& rStreamWMF,
+ FilterConfigItem* pConfigItem
+ );
+ ~MtfTools();
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/emfio/inc/wmfreader.hxx b/emfio/inc/wmfreader.hxx
new file mode 100644
index 000000000000..734becbb5886
--- /dev/null
+++ b/emfio/inc/wmfreader.hxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_EMFIO_INC_WMFREADER_HXX
+#define INCLUDED_EMFIO_INC_WMFREADER_HXX
+
+#include <mtftools.hxx>
+#include <tools/stream.hxx>
+
+namespace emfio
+{
+ struct WMF_EXTERNALHEADER
+ {
+ sal_uInt16 xExt;
+ sal_uInt16 yExt;
+
+ /** One of the following values:
+ <ul>
+ <li>MM_TEXT</li>
+ <li>MM_LOMETRIC</li>
+ <li>MM_HIMETRIC</li>
+ <li>MM_LOENGLISH</li>
+ <li>MM_HIENGLISH</li>
+ <li>MM_TWIPS</li>
+ <li>MM_ISOTROPIC</li>
+ <li>MM_ANISOTROPIC</li>
+ </ul>
+ If this value is 0, then no external mapmode has been defined,
+ the internal one should then be used.
+ */
+ sal_uInt16 mapMode;
+
+ WMF_EXTERNALHEADER() :
+ xExt(0),
+ yExt(0),
+ mapMode(0)
+ {
+ }
+ };
+
+ class WmfReader : public MtfTools
+ {
+ private:
+
+ sal_uInt16 nUnitsPerInch;
+ sal_uInt32 nRecSize;
+
+ // embedded EMF data
+ std::unique_ptr<SvMemoryStream> pEMFStream;
+
+ // total number of comment records containing EMF data
+ sal_uInt32 nEMFRecCount;
+
+ // number of EMF records read
+ sal_uInt32 nEMFRec;
+
+ // total size of embedded EMF data
+ sal_uInt32 nEMFSize;
+
+ sal_uInt32 nSkipActions;
+ sal_uInt32 nCurrentAction;
+
+ WMF_EXTERNALHEADER* pExternalHeader;
+
+ // reads header of the WMF-Datei
+ bool ReadHeader();
+
+ // reads parameters of the record with the functionnumber nFunction.
+ void ReadRecordParams(sal_uInt16 nFunction);
+
+ Point ReadPoint(); // reads and converts a point (first X then Y)
+ Point ReadYX(); // reads and converts a point (first Y then X)
+ tools::Rectangle ReadRectangle(); // reads and converts a rectangle
+ Size ReadYXExt();
+ void GetPlaceableBound(tools::Rectangle& rSize, SvStream* pStrm);
+
+ public:
+
+ WmfReader(SvStream& rStreamWMF, GDIMetaFile& rGDIMetaFile,
+ FilterConfigItem* pConfigItem,
+ WMF_EXTERNALHEADER* pExtHeader = nullptr);
+
+ // read WMF file from stream and fill the GDIMetaFile
+ void ReadWMF();
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/emfio/source/emfuno/xemfparser.cxx b/emfio/source/emfuno/xemfparser.cxx
index 3b6e9ad731fa..e532d903e047 100644
--- a/emfio/source/emfuno/xemfparser.cxx
+++ b/emfio/source/emfuno/xemfparser.cxx
@@ -37,6 +37,9 @@
#include <unotools/ucbstreamhelper.hxx>
#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <wmfreader.hxx>
+#include <emfreader.hxx>
+
//#include <com/sun/star/xml/sax/XParser.hpp>
//#include <com/sun/star/xml/sax/Parser.hpp>
//#include <com/sun/star/xml/sax/InputSource.hpp>
@@ -116,7 +119,7 @@ namespace emfio
if (xEmfStream.is())
{
- static bool bTestCode(true);
+ static bool bTestCode(false);
if (bTestCode)
{
@@ -168,59 +171,55 @@ namespace emfio
// new parser here
bool bBla = true;
+ // rouch check - import and conv to primitive
+ GDIMetaFile aMtf;
+ std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream(xEmfStream));
+ sal_uInt32 nMetaType(0);
+ sal_uInt32 nOrgPos = pStream->Tell();
+
+ SvStreamEndian nOrigNumberFormat = pStream->GetEndian();
+ pStream->SetEndian(SvStreamEndian::LITTLE);
+
+ pStream->Seek(0x28);
+ pStream->ReadUInt32(nMetaType);
+ pStream->Seek(nOrgPos);
+
+ if (nMetaType == 0x464d4520)
+ {
+ emfio::EmfReader(*pStream, aMtf, nullptr).ReadEnhWMF();
+ }
+ else
+ {
+ emfio::WmfReader(*pStream, aMtf, nullptr).ReadWMF();
+ }
+
+ pStream->SetEndian(nOrigNumberFormat);
+ Size aSize(aMtf.GetPrefSize());
+
+ if (aMtf.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
+ {
+ aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, MapUnit::Map100thMM);
+ }
+ else
+ {
+ aSize = OutputDevice::LogicToLogic(aSize, aMtf.GetPrefMapMode(), MapMode(MapUnit::Map100thMM));
+ }
+
+ const basegfx::B2DHomMatrix aMetafileTransform(
+ basegfx::tools::createScaleB2DHomMatrix(
+ aSize.Width(),
+ aSize.Height()));
+
+ // 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));
+
- // // local document handler
- // SvgDocHdl* pSvgDocHdl = new SvgDocHdl(aAbsolutePath);
- // uno::Reference< xml::sax::XDocumentHandler > xSvgDocHdl(pSvgDocHdl);
- //
- // try
- // {
- // // prepare ParserInputSrouce
- // xml::sax::InputSource myInputSource;
- // myInputSource.aInputStream = xEmfStream;
- //
- // // get parser
- // uno::Reference< xml::sax::XParser > xParser(
- // xml::sax::Parser::create(context_));
- // // fdo#60471 need to enable internal entities because
- // // certain ... popular proprietary products write SVG files
- // // that use entities to define XML namespaces.
- // uno::Reference<lang::XInitialization> const xInit(xParser,
- // uno::UNO_QUERY_THROW);
- // uno::Sequence<uno::Any> args(1);
- // args[0] <<= OUString("DoSmeplease");
- // xInit->initialize(args);
- //
- // // connect parser and filter
- // xParser->setDocumentHandler(xSvgDocHdl);
- //
- // // finally, parse the stream to a hierarchy of
- // // SVGGraphicPrimitive2D which will be embedded to the
- // // primitive sequence. Their decompositions will in the
- // // end create local low-level primitives, thus SVG will
- // // be processable from all our processors
- // xParser->parseStream(myInputSource);
- // }
- // catch(const uno::Exception& e)
- // {
- // SAL_WARN( "svg", "Parse error! : " << e.Message);
- // }
- //
- // // decompose to primitives
- // const SvgNodeVector& rResults = pSvgDocHdl->getSvgDocument().getSvgNodeVector();
- // const sal_uInt32 nCount(rResults.size());
- //
- // for(sal_uInt32 a(0); a < nCount; a++)
- // {
- // SvgNode* pCandidate = rResults[a];
- //
- // if(Display_none != pCandidate->getDisplay())
- // {
- // pCandidate->decomposeSvgNode(aRetval, false);
- // }
- // }
}
}
else
diff --git a/emfio/source/reader/emfreader.cxx b/emfio/source/reader/emfreader.cxx
new file mode 100644
index 000000000000..9fd005578ab3
--- /dev/null
+++ b/emfio/source/reader/emfreader.cxx
@@ -0,0 +1,1940 @@
+/* -*- 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 <emfreader.hxx>
+
+#include <osl/endian.h>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <vcl/dibtools.hxx>
+#include <o3tl/make_unique.hxx>
+
+#include <memory>
+
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#include <vcl/pngwrite.hxx>
+#endif
+
+using namespace std;
+
+// 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
+
+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
+}
+
+#ifdef OSL_BIGENDIAN
+// little endian <-> big endian switch
+static float GetSwapFloat(SvStream& rStream)
+{
+ float fTmp;
+ sal_Int8* pPtr = (sal_Int8*)&fTmp;
+ rStream.ReadSChar(pPtr[3]);
+ rStream.ReadSChar(pPtr[2]);
+ rStream.ReadSChar(pPtr[1]);
+ rStream.ReadSChar(pPtr[0]);
+ return fTmp;
+}
+#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( tools::PolyPolygon& rPolyPoly, SvStream& rStream, sal_uInt32 nLen )
+{
+ if (nLen == 0)
+ return false;
+
+ sal_uInt32 nHdSize, nType, nCount, nRgnSize, i;
+ rStream.ReadUInt32(nHdSize);
+ rStream.ReadUInt32(nType);
+ rStream.ReadUInt32(nCount);
+ rStream.ReadUInt32(nRgnSize);
+
+ if ( nCount > 0
+ && nType == RDH_RECTANGLES
+ && nLen >= ((nCount << 4) + (nHdSize - 16)))
+ {
+ sal_Int32 nx1, ny1, nx2, ny2;
+
+ for (i = 0; i < nCount; i++)
+ {
+ rStream.ReadInt32(nx1);
+ rStream.ReadInt32(ny1);
+ rStream.ReadInt32(nx2);
+ rStream.ReadInt32(ny2);
+
+ tools::Rectangle aRectangle(Point(nx1, ny1), Point(nx2, ny2));
+
+ tools::Polygon aPolygon(aRectangle);
+ tools::PolyPolygon aPolyPolyOr1(aPolygon);
+ tools::PolyPolygon aPolyPolyOr2(rPolyPoly);
+ rPolyPoly.GetUnion(aPolyPolyOr1, aPolyPolyOr2);
+ rPolyPoly = aPolyPolyOr2;
+ }
+ return true;
+ }
+ return false;
+}
+
+} // anonymous namespace
+
+namespace emfio
+{
+ EmfReader::EmfReader(SvStream& rStream,GDIMetaFile& rGDIMetaFile,FilterConfigItem* pConfigItem)
+ : MtfTools(rGDIMetaFile, rStream , pConfigItem)
+ , bRecordPath(false)
+ , nRecordCount(0)
+ , bEMFPlus(false)
+ {}
+
+ EmfReader::~EmfReader()
+ {}
+
+ void EmfReader::ReadEMFPlusComment(sal_uInt32 length, bool& bHaveDC)
+ {
+ if (!bEMFPlus) {
+ pOut->PassEMFPlusHeaderInfo();
+
+ #if OSL_DEBUG_LEVEL > 1
+ // debug code - write the stream to debug file /tmp/emf-stream.emf
+ sal_uInt64 const pos = pWMF->Tell();
+ pWMF->Seek(0);
+ SvFileStream file( OUString( "/tmp/emf-stream.emf" ), StreamMode::WRITE | StreamMode::TRUNC );
+
+ pWMF->WriteStream(file);
+ file.Flush();
+ file.Close();
+
+ pWMF->Seek( pos );
+ #endif
+
+ }
+ bEMFPlus = true;
+
+ sal_uInt64 const pos = pWMF->Tell();
+ void *buffer = malloc( length );
+ pOut->PassEMFPlus( buffer, pWMF->ReadBytes(buffer, length) );
+ free( buffer );
+ pWMF->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);
+
+ pWMF->ReadUInt16( type ).ReadUInt16( flags ).ReadUInt32( size ).ReadUInt32( dataSize );
+ nRemainder -= nRequiredHeaderSize;
+
+ SAL_INFO ("vcl.emf", "\t\tEMF+ record type: " << std::hex << type << std::dec);
+
+ // Get Device Context
+ // TODO We should use EmfPlusRecordType::GetDC instead
+ if( type == 0x4004 )
+ {
+ bHaveDC = true;
+ SAL_INFO ("vcl.emf", "\t\tEMF+ lock DC (device context)");
+ }
+
+ // 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);
+ pWMF->SeekRel(nRemainingRecordData);
+ nRemainder -= nRemainingRecordData;
+ }
+ pWMF->SeekRel(nRemainder);
+ }
+
+ /**
+ * Reads polygons from the stream.
+ * The \<class T> parameter is for the type of the points (sal_uInt32 or sal_uInt16).
+ * The \<class Drawer> parameter is a c++11 lambda for the method that will draw the polygon.
+ * skipFirst: if the first point read is the 0th point or the 1st point in the array.
+ * */
+ template <class T, class Drawer>
+ void EmfReader::ReadAndDrawPolygon(Drawer drawer, const bool skipFirst)
+ {
+ sal_uInt32 nPoints(0), nStartIndex(0);
+ pWMF->SeekRel( 16 );
+ pWMF->ReadUInt32( nPoints );
+ if (skipFirst)
+ {
+ nPoints ++;
+ nStartIndex ++;
+ }
+
+ tools::Polygon aPolygon = ReadPolygon<T>(nStartIndex, nPoints);
+ drawer(pOut, aPolygon, skipFirst, bRecordPath);
+ }
+
+ /**
+ * 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
+ * pWMF: the stream containing the polygons
+ * */
+ template <class T>
+ tools::Polygon EmfReader::ReadPolygon(sal_uInt32 nStartIndex, sal_uInt32 nPoints)
+ {
+ bool bRecordOk = nPoints <= SAL_MAX_UINT16;
+ SAL_WARN_IF(!bRecordOk, "vcl.emf", "polygon record has more polygons than we can handle");
+ if (!bRecordOk)
+ return tools::Polygon();
+
+ tools::Polygon aPolygon(nPoints);
+ for (sal_uInt32 i = nStartIndex ; i < nPoints && pWMF->good(); i++ )
+ {
+ T nX, nY;
+ *pWMF >> nX >> nY;
+ if (!pWMF->good())
+ {
+ SAL_WARN("vcl.emf", "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 nPoints;
+ sal_uInt32 i, nNumberOfPolylines( 0 ), nCount( 0 );
+ pWMF->SeekRel( 0x10 ); // TODO Skipping Bounds. A 128-bit WMF RectL object (specifies the bounding rectangle in device units.)
+ pWMF->ReadUInt32( nNumberOfPolylines );
+ pWMF->ReadUInt32( nCount ); // total number of points in all polylines
+ if (pWMF->Tell() >= nEndPos)
+ return;
+
+ // taking the amount of points of each polygon, retrieving the total number of points
+ if ( pWMF->good() &&
+ ( nNumberOfPolylines < SAL_MAX_UINT32 / sizeof( sal_uInt16 ) ) &&
+ ( nNumberOfPolylines * sizeof( sal_uInt16 ) ) <= ( nEndPos - pWMF->Tell() )
+ )
+ {
+ std::unique_ptr< sal_uInt32[] > pnPolylinePointCount( new sal_uInt32[ nNumberOfPolylines ] );
+ for ( i = 0; i < nNumberOfPolylines && pWMF->good(); i++ )
+ {
+ pWMF->ReadUInt32( nPoints );
+ pnPolylinePointCount[ i ] = nPoints;
+ }
+ // Get polyline points:
+ for ( i = 0; ( i < nNumberOfPolylines ) && pWMF->good(); i++ )
+ {
+ tools::Polygon aPolygon = ReadPolygon< T >( 0, pnPolylinePointCount[ i ] );
+ pOut->DrawPolyLine( aPolygon, false, bRecordPath );
+ }
+ }
+ }
+
+ // these are referenced from inside the templates
+
+ SvStream& operator>>(SvStream& rStream, sal_Int16 &n)
+ {
+ return rStream.ReadInt16(n);
+ }
+
+ SvStream& operator>>(SvStream& rStream, sal_Int32 &n)
+ {
+ return rStream.ReadInt32(n);
+ }
+
+ /**
+ * 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 nPoly(0), nGesPoints(0), nReadPoints(0);
+ pWMF->SeekRel( 0x10 );
+ // Number of polygons
+ pWMF->ReadUInt32( nPoly ).ReadUInt32( nGesPoints );
+ if (pWMF->Tell() >= nEndPos)
+ return;
+ if (!pWMF->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 - pWMF->Tell())
+ return;
+
+ // Get number of points in each polygon
+ std::vector<sal_uInt16> aPoints(nPoly);
+ for (sal_uInt32 i = 0; i < nPoly && pWMF->good(); ++i)
+ {
+ sal_uInt32 nPoints(0);
+ pWMF->ReadUInt32( nPoints );
+ aPoints[i] = (sal_uInt16)nPoints;
+ }
+ if ( pWMF->good() && ( nGesPoints * (sizeof(T)+sizeof(T)) ) <= ( nEndPos - pWMF->Tell() ) )
+ {
+ // Get polygon points
+ tools::PolyPolygon aPolyPoly(nPoly, nPoly);
+ for (sal_uInt32 i = 0; i < nPoly && pWMF->good(); ++i)
+ {
+ const sal_uInt16 nPointCount(aPoints[i]);
+ std::vector<Point> aPtAry(nPointCount);
+ for (sal_uInt16 j = 0; j < nPointCount && pWMF->good(); ++j)
+ {
+ T nX(0), nY(0);
+ *pWMF >> nX >> nY;
+ aPtAry[j] = Point( nX, nY );
+ ++nReadPoints;
+ }
+
+ aPolyPoly.Insert(tools::Polygon(aPtAry.size(), aPtAry.data()));
+ }
+
+ pOut->DrawPolyPolygon(aPolyPoly, bRecordPath);
+ }
+
+ 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;
+
+ static bool bEnableEMFPlus = ( getenv( "EMF_PLUS_DISABLE" ) == nullptr );
+
+ while( bStatus && nRecordCount-- && pWMF->good())
+ {
+ sal_uInt32 nRecType(0), nRecSize(0);
+ pWMF->ReadUInt32(nRecType).ReadUInt32(nRecSize);
+
+ if ( !pWMF->good() || ( nRecSize < 8 ) || ( nRecSize & 3 ) ) // Parameters are always divisible by 4
+ {
+ bStatus = false;
+ break;
+ }
+
+ auto nCurPos = pWMF->Tell();
+
+ if (nEndPos < nCurPos - 8)
+ {
+ bStatus = false;
+ break;
+ }
+
+ const sal_uInt32 nMaxPossibleRecSize = nEndPos - (nCurPos - 8);
+ if (nRecSize > nMaxPossibleRecSize)
+ {
+ bStatus = false;
+ break;
+ }
+
+ nNextPos = nCurPos + (nRecSize - 8);
+
+ if( !aBmpSaveList.empty()
+ && ( nRecType != EMR_STRETCHBLT )
+ && ( nRecType != EMR_STRETCHDIBITS )
+ ) {
+ pOut->ResolveBitmapActions( aBmpSaveList );
+ }
+
+ bool bFlag = false;
+
+ SAL_INFO ("vcl.emf", "0x" << std::hex << (nNextPos - nRecSize) << "-0x" << nNextPos << " " << record_type_name(nRecType) << " size: " << nRecSize << std::dec);
+
+ if( bEnableEMFPlus && nRecType == EMR_COMMENT ) {
+ sal_uInt32 length;
+
+ pWMF->ReadUInt32( length );
+
+ SAL_INFO("vcl.emf", "\tGDI comment, length: " << length);
+
+ if( pWMF->good() && length >= 4 && length <= pWMF->remainingSize() ) {
+ sal_uInt32 nCommentId;
+
+ pWMF->ReadUInt32( nCommentId );
+
+ SAL_INFO ("vcl.emf", "\t\tbegin " << (char)(nCommentId & 0xff) << (char)((nCommentId & 0xff00) >> 8) << (char)((nCommentId & 0xff0000) >> 16) << (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 )
+ {
+ // TODO: ReadGDIComment()
+ }
+ else if( nCommentId == EMR_COMMENT_EMFSPOOL && nRecSize >= 12 )
+ {
+ // TODO Implement reading EMFSPOOL comment
+
+ }
+ else
+ {
+ SAL_INFO ("vcl.emf", "\t\tunknown id: 0x" << std::hex << nCommentId << std::dec);
+ }
+ }
+ }
+ else if( !bEMFPlus || bHaveDC || nRecType == EMR_EOF )
+ {
+ switch( nRecType )
+ {
+ case EMR_POLYBEZIERTO :
+ ReadAndDrawPolygon<sal_Int32>( [] ( std::unique_ptr<MtfToolsWriter> &pWinMtfOutput, tools::Polygon& rPolygon, bool aTo, bool aRecordPath )
+ { pWinMtfOutput->DrawPolyBezier( rPolygon, aTo, aRecordPath ); }, true );
+ break;
+ case EMR_POLYBEZIER :
+ ReadAndDrawPolygon<sal_Int32>( [] ( std::unique_ptr<MtfToolsWriter> &pWinMtfOutput, tools::Polygon& rPolygon, bool aTo, bool aRecordPath )
+ { pWinMtfOutput->DrawPolyBezier( rPolygon, aTo, aRecordPath ); }, false );
+ break;
+
+ case EMR_POLYGON :
+ ReadAndDrawPolygon<sal_Int32>( [] ( std::unique_ptr<MtfToolsWriter> &pWinMtfOutput, tools::Polygon& rPolygon, bool /*aTo*/, bool aRecordPath )
+ { pWinMtfOutput->DrawPolygon( rPolygon, aRecordPath ); }, false );
+ break;
+
+ case EMR_POLYLINETO :
+ ReadAndDrawPolygon<sal_Int32>( [] ( std::unique_ptr<MtfToolsWriter> &pWinMtfOutput, tools::Polygon& rPolygon, bool aTo, bool aRecordPath )
+ { pWinMtfOutput->DrawPolyLine( rPolygon, aTo, aRecordPath ); }, true );
+ break;
+
+ case EMR_POLYLINE :
+ ReadAndDrawPolygon<sal_Int32>( [] ( std::unique_ptr<MtfToolsWriter> &pWinMtfOutput, tools::Polygon& rPolygon, bool aTo, bool aRecordPath )
+ { pWinMtfOutput->DrawPolyLine( rPolygon, aTo, aRecordPath ); }, false );
+ break;
+
+ case EMR_POLYPOLYLINE :
+ ReadAndDrawPolyLine<sal_Int32>();
+ break;
+
+ case EMR_POLYPOLYGON :
+ ReadAndDrawPolyPolygon<sal_Int32>();
+ break;
+
+ case EMR_SETWINDOWEXTEX :
+ {
+ pWMF->ReadUInt32( nW ).ReadUInt32( nH );
+ pOut->SetWinExt( Size( nW, nH ), true);
+ }
+ break;
+
+ case EMR_SETWINDOWORGEX :
+ {
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 );
+ pOut->SetWinOrg( Point( nX32, nY32 ), true);
+ }
+ break;
+
+ case EMR_SCALEWINDOWEXTEX :
+ {
+ pWMF->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
+ pOut->ScaleWinExt( (double)nNom1 / nDen1, (double)nNom2 / nDen2 );
+ }
+ break;
+
+ case EMR_SETVIEWPORTORGEX :
+ {
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 );
+ pOut->SetDevOrg( Point( nX32, nY32 ) );
+ }
+ break;
+
+ case EMR_SCALEVIEWPORTEXTEX :
+ {
+ pWMF->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
+ pOut->ScaleDevExt( (double)nNom1 / nDen1, (double)nNom2 / nDen2 );
+ }
+ break;
+
+ case EMR_SETVIEWPORTEXTEX :
+ {
+ pWMF->ReadUInt32( nW ).ReadUInt32( nH );
+ pOut->SetDevExt( Size( nW, nH ) );
+ }
+ break;
+
+ case EMR_EOF :
+ nRecordCount = 0;
+ break;
+
+ case EMR_SETPIXELV :
+ {
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 );
+ pOut->DrawPixel( Point( nX32, nY32 ), ReadColor() );
+ }
+ break;
+
+ case EMR_SETMAPMODE :
+ {
+ sal_uInt32 nMapMode;
+ pWMF->ReadUInt32( nMapMode );
+ pOut->SetMapMode( nMapMode );
+ }
+ break;
+
+ case EMR_SETBKMODE :
+ {
+ pWMF->ReadUInt32( nDat32 );
+ pOut->SetBkMode( static_cast<BkMode>(nDat32) );
+ }
+ break;
+
+ case EMR_SETPOLYFILLMODE :
+ break;
+
+ case EMR_SETROP2 :
+ {
+ pWMF->ReadUInt32( nDat32 );
+ pOut->SetRasterOp( (WMFRasterOp)nDat32 );
+ }
+ break;
+
+ case EMR_SETSTRETCHBLTMODE :
+ {
+ pWMF->ReadUInt32( nStretchBltMode );
+ }
+ break;
+
+ case EMR_SETTEXTALIGN :
+ {
+ pWMF->ReadUInt32( nDat32 );
+ pOut->SetTextAlign( nDat32 );
+ }
+ break;
+
+ case EMR_SETTEXTCOLOR :
+ {
+ pOut->SetTextColor( ReadColor() );
+ }
+ break;
+
+ case EMR_SETBKCOLOR :
+ {
+ pOut->SetBkColor( ReadColor() );
+ }
+ break;
+
+ case EMR_OFFSETCLIPRGN :
+ {
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 );
+ pOut->MoveClipRegion( Size( nX32, nY32 ) );
+ }
+ break;
+
+ case EMR_MOVETOEX :
+ {
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 );
+ pOut->MoveTo( Point( nX32, nY32 ), bRecordPath );
+ }
+ break;
+
+ case EMR_INTERSECTCLIPRECT :
+ {
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
+ pOut->IntersectClipRect( ReadRectangle( nX32, nY32, nx32, ny32 ) );
+ }
+ break;
+
+ case EMR_SAVEDC :
+ {
+ pOut->Push();
+ }
+ break;
+
+ case EMR_RESTOREDC :
+ {
+ pOut->Pop();
+ }
+ break;
+
+ case EMR_SETWORLDTRANSFORM :
+ {
+ XForm aTempXForm;
+ *pWMF >> aTempXForm;
+ pOut->SetWorldTransform( aTempXForm );
+ }
+ break;
+
+ case EMR_MODIFYWORLDTRANSFORM :
+ {
+ sal_uInt32 nMode;
+ XForm aTempXForm;
+ *pWMF >> aTempXForm;
+ pWMF->ReadUInt32( nMode );
+ pOut->ModifyWorldTransform( aTempXForm, nMode );
+ }
+ break;
+
+ case EMR_SELECTOBJECT :
+ {
+ pWMF->ReadUInt32( nIndex );
+ pOut->SelectObject( nIndex );
+ }
+ break;
+
+ case EMR_CREATEPEN :
+ {
+ pWMF->ReadUInt32( nIndex );
+ if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+ {
+
+ LineInfo aLineInfo;
+ sal_uInt32 nStyle;
+ Size aSize;
+ // #fdo39428 Remove SvStream operator>>(long&)
+ sal_Int32 nTmpW(0), nTmpH(0);
+
+ pWMF->ReadUInt32( nStyle ).ReadInt32( nTmpW ).ReadInt32( nTmpH );
+ aSize.Width() = nTmpW;
+ aSize.Height() = nTmpH;
+
+ if ( aSize.Width() )
+ aLineInfo.SetWidth( aSize.Width() );
+
+ bool bTransparent = false;
+ switch( nStyle & PS_STYLE_MASK )
+ {
+ case PS_DASHDOTDOT :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 1 );
+ aLineInfo.SetDotCount( 2 );
+ break;
+ case PS_DASHDOT :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 1 );
+ aLineInfo.SetDotCount( 1 );
+ break;
+ case PS_DOT :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 0 );
+ aLineInfo.SetDotCount( 1 );
+ break;
+ case PS_DASH :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 1 );
+ aLineInfo.SetDotCount( 0 );
+ break;
+ case PS_NULL :
+ bTransparent = true;
+ aLineInfo.SetStyle( LineStyle::NONE );
+ break;
+ case PS_INSIDEFRAME :
+ case PS_SOLID :
+ default :
+ aLineInfo.SetStyle( LineStyle::Solid );
+ }
+ switch( nStyle & PS_ENDCAP_STYLE_MASK )
+ {
+ case PS_ENDCAP_ROUND :
+ if ( aSize.Width() )
+ {
+ aLineInfo.SetLineCap( css::drawing::LineCap_ROUND );
+ break;
+ }
+ SAL_FALLTHROUGH;
+ case PS_ENDCAP_SQUARE :
+ if ( aSize.Width() )
+ {
+ aLineInfo.SetLineCap( css::drawing::LineCap_SQUARE );
+ break;
+ }
+ SAL_FALLTHROUGH;
+ case PS_ENDCAP_FLAT :
+ default :
+ aLineInfo.SetLineCap( css::drawing::LineCap_BUTT );
+ }
+ switch( nStyle & PS_JOIN_STYLE_MASK )
+ {
+ case PS_JOIN_ROUND :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Round );
+ break;
+ case PS_JOIN_MITER :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Miter );
+ break;
+ case PS_JOIN_BEVEL :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Bevel );
+ break;
+ default :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::NONE );
+ }
+ pOut->CreateObjectIndexed(nIndex, o3tl::make_unique<WinMtfLineStyle>( ReadColor(), aLineInfo, bTransparent ));
+ }
+ }
+ break;
+
+ case EMR_EXTCREATEPEN :
+ {
+ sal_Int32 elpHatch;
+ sal_uInt32 offBmi, cbBmi, offBits, cbBits, nStyle, nWidth, nBrushStyle, elpNumEntries;
+ Color aColorRef;
+
+ pWMF->ReadUInt32( nIndex );
+ if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+ {
+ pWMF->ReadUInt32( offBmi ).ReadUInt32( cbBmi ).ReadUInt32( offBits ).ReadUInt32( cbBits ). ReadUInt32( nStyle ).ReadUInt32( nWidth ).ReadUInt32( nBrushStyle );
+ aColorRef = ReadColor();
+ pWMF->ReadInt32( elpHatch ).ReadUInt32( elpNumEntries );
+
+ LineInfo aLineInfo;
+ if ( nWidth )
+ aLineInfo.SetWidth( nWidth );
+
+ bool bTransparent = false;
+
+ switch( nStyle & PS_STYLE_MASK )
+ {
+ case PS_DASHDOTDOT :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 1 );
+ aLineInfo.SetDotCount( 2 );
+ break;
+ case PS_DASHDOT :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 1 );
+ aLineInfo.SetDotCount( 1 );
+ break;
+ case PS_DOT :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 0 );
+ aLineInfo.SetDotCount( 1 );
+ break;
+ case PS_DASH :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 1 );
+ aLineInfo.SetDotCount( 0 );
+ break;
+ case PS_NULL :
+ bTransparent = true;
+ aLineInfo.SetStyle( LineStyle::NONE );
+ break;
+
+ case PS_INSIDEFRAME :
+ case PS_SOLID :
+ default :
+ aLineInfo.SetStyle( LineStyle::Solid );
+ }
+ switch( nStyle & PS_ENDCAP_STYLE_MASK )
+ {
+ case PS_ENDCAP_ROUND :
+ if ( aLineInfo.GetWidth() )
+ {
+ aLineInfo.SetLineCap( css::drawing::LineCap_ROUND );
+ break;
+ }
+ SAL_FALLTHROUGH;
+ case PS_ENDCAP_SQUARE :
+ if ( aLineInfo.GetWidth() )
+ {
+ aLineInfo.SetLineCap( css::drawing::LineCap_SQUARE );
+ break;
+ }
+ SAL_FALLTHROUGH;
+ case PS_ENDCAP_FLAT :
+ default :
+ aLineInfo.SetLineCap( css::drawing::LineCap_BUTT );
+ }
+ switch( nStyle & PS_JOIN_STYLE_MASK )
+ {
+ case PS_JOIN_ROUND :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Round );
+ break;
+ case PS_JOIN_MITER :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Miter );
+ break;
+ case PS_JOIN_BEVEL :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Bevel );
+ break;
+ default :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::NONE );
+ }
+ pOut->CreateObjectIndexed(nIndex, o3tl::make_unique<WinMtfLineStyle>( aColorRef, aLineInfo, bTransparent ));
+ }
+ }
+ break;
+
+ case EMR_CREATEBRUSHINDIRECT :
+ {
+ sal_uInt32 nStyle;
+ pWMF->ReadUInt32( nIndex );
+ if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+ {
+ pWMF->ReadUInt32( nStyle );
+ pOut->CreateObjectIndexed(nIndex, o3tl::make_unique<WinMtfFillStyle>( ReadColor(), ( nStyle == BS_HOLLOW ) ));
+ }
+ }
+ break;
+
+ case EMR_DELETEOBJECT :
+ {
+ pWMF->ReadUInt32( nIndex );
+ if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+ pOut->DeleteObject( nIndex );
+ }
+ break;
+
+ case EMR_ELLIPSE :
+ {
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
+ pOut->DrawEllipse( ReadRectangle( nX32, nY32, nx32, ny32 ) );
+ }
+ break;
+
+ case EMR_RECTANGLE :
+ {
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
+ pOut->DrawRect( ReadRectangle( nX32, nY32, nx32, ny32 ) );
+ }
+ break;
+
+ case EMR_ROUNDRECT :
+ {
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nW ).ReadUInt32( nH );
+ Size aSize( Size( nW, nH ) );
+ pOut->DrawRoundRect( ReadRectangle( nX32, nY32, nx32, ny32 ), aSize );
+ }
+ break;
+
+ case EMR_ARC :
+ {
+ sal_uInt32 nStartX, nStartY, nEndX, nEndY;
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nStartX ).ReadUInt32( nStartY ).ReadUInt32( nEndX ).ReadUInt32( nEndY );
+ pOut->DrawArc( ReadRectangle( nX32, nY32, nx32, ny32 ), Point( nStartX, nStartY ), Point( nEndX, nEndY ) );
+ }
+ break;
+
+ case EMR_CHORD :
+ {
+ sal_uInt32 nStartX, nStartY, nEndX, nEndY;
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nStartX ).ReadUInt32( nStartY ).ReadUInt32( nEndX ).ReadUInt32( nEndY );
+ pOut->DrawChord( ReadRectangle( nX32, nY32, nx32, ny32 ), Point( nStartX, nStartY ), Point( nEndX, nEndY ) );
+ }
+ break;
+
+ case EMR_PIE :
+ {
+ sal_uInt32 nStartX, nStartY, nEndX, nEndY;
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nStartX ).ReadUInt32( nStartY ).ReadUInt32( nEndX ).ReadUInt32( nEndY );
+ const tools::Rectangle aRect( ReadRectangle( nX32, nY32, nx32, ny32 ));
+
+ // #i73608# OutputDevice deviates from WMF
+ // semantics. start==end means full ellipse here.
+ if( nStartX == nEndX && nStartY == nEndY )
+ pOut->DrawEllipse( aRect );
+ else
+ pOut->DrawPie( aRect, Point( nStartX, nStartY ), Point( nEndX, nEndY ) );
+ }
+ break;
+
+ case EMR_LINETO :
+ {
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 );
+ pOut->LineTo( Point( nX32, nY32 ), bRecordPath );
+ }
+ break;
+
+ case EMR_ARCTO :
+ {
+ sal_uInt32 nStartX, nStartY, nEndX, nEndY;
+ pWMF->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nStartX ).ReadUInt32( nStartY ).ReadUInt32( nEndX ).ReadUInt32( nEndY );
+ pOut->DrawArc( ReadRectangle( nX32, nY32, nx32, ny32 ), Point( nStartX, nStartY ), Point( nEndX, nEndY ), true );
+ }
+ break;
+
+ case EMR_BEGINPATH :
+ {
+ pOut->ClearPath();
+ bRecordPath = true;
+ }
+ break;
+
+ case EMR_ABORTPATH :
+ pOut->ClearPath();
+ SAL_FALLTHROUGH;
+ case EMR_ENDPATH :
+ bRecordPath = false;
+ break;
+
+ case EMR_CLOSEFIGURE :
+ pOut->ClosePath();
+ break;
+
+ case EMR_FILLPATH :
+ pOut->StrokeAndFillPath( false, true );
+ break;
+
+ case EMR_STROKEANDFILLPATH :
+ pOut->StrokeAndFillPath( true, true );
+ break;
+
+ case EMR_STROKEPATH :
+ pOut->StrokeAndFillPath( true, false );
+ break;
+
+ case EMR_SELECTCLIPPATH :
+ {
+ sal_Int32 nClippingMode;
+ pWMF->ReadInt32(nClippingMode);
+ pOut->SetClipPath(pOut->GetPathObj(), nClippingMode, true);
+ }
+ break;
+
+ case EMR_EXTSELECTCLIPRGN :
+ {
+ sal_Int32 nClippingMode, cbRgnData;
+ pWMF->ReadInt32(cbRgnData);
+ pWMF->ReadInt32(nClippingMode);
+
+ // This record's region data should be ignored if mode
+ // is RGN_COPY - see EMF spec section 2.3.2.2
+ if (nClippingMode == RGN_COPY)
+ {
+ pOut->SetDefaultClipPath();
+ }
+ else
+ {
+ tools::PolyPolygon aPolyPoly;
+ if (cbRgnData)
+ ImplReadRegion(aPolyPoly, *pWMF, nRecSize);
+ pOut->SetClipPath(aPolyPoly, 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 = pWMF->Tell() - 8;
+ pWMF->SeekRel( 0x10 );
+
+ pWMF->ReadInt32( xDest ).ReadInt32( yDest ).ReadInt32( cxDest ).ReadInt32( cyDest );
+ *pWMF >> aFunc;
+ pWMF->ReadInt32( xSrc ).ReadInt32( ySrc );
+ *pWMF >> xformSrc;
+ pWMF->ReadUInt32( BkColorSrc ).ReadUInt32( iUsageSrc ).ReadUInt32( offBmiSrc ).ReadUInt32( cbBmiSrc )
+ .ReadUInt32( offBitsSrc ).ReadUInt32( cbBitsSrc ).ReadInt32( cxSrc ).ReadInt32( cySrc ) ;
+
+ tools::Rectangle aRect( Point( xDest, yDest ), Size( cxDest+1, cyDest+1 ) );
+
+ if ( (cbBitsSrc > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc) )
+ bStatus = false;
+ else
+ {
+ const sal_uInt32 nSourceSize = cbBmiSrc + cbBitsSrc + 14;
+ bool bSafeRead = nSourceSize <= (nEndPos - nStartPos);
+ 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)
+ pWMF->Seek( nStart + offBmiSrc );
+ pWMF->ReadBytes(pBuf + 14, cbBmiSrc);
+
+ 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)
+ pWMF->Seek( nStart + offBitsSrc );
+ pWMF->ReadBytes(pBuf + 14 + nDeltaToDIB5HeaderSize + cbBmiSrc, cbBitsSrc);
+ 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 ) &&
+ ( xSrc + cxSrc < aBitmapEx.GetSizePixel().Width() ) &&
+ ( ySrc + cySrc < aBitmapEx.GetSizePixel().Height() ) )
+ {
+ const tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
+
+ aBitmapEx.Crop( aCropRect );
+ }
+
+ #ifdef DBG_UTIL
+ static bool bDoSaveForVisualControl(false);
+
+ if(bDoSaveForVisualControl)
+ {
+ SvFileStream aNew("c:\\metafile_content.png", StreamMode::WRITE|StreamMode::TRUNC);
+ vcl::PNGWriter aPNGWriter(aBitmapEx);
+ aPNGWriter.Write(aNew);
+ }
+ #endif
+ maBmpSaveList.emplace_back(new BSaveStruct(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 = pWMF->Tell() - 8;
+
+ pWMF->SeekRel( 0x10 );
+ pWMF->ReadInt32( xDest ).ReadInt32( yDest ).ReadInt32( cxDest ).ReadInt32( cyDest ).ReadUInt32( dwRop ).ReadInt32( xSrc ).ReadInt32( ySrc )
+ >> xformSrc;
+ pWMF->ReadUInt32( nColor ).ReadUInt32( iUsageSrc ).ReadUInt32( offBmiSrc ).ReadUInt32( cbBmiSrc )
+ .ReadUInt32( offBitsSrc ).ReadUInt32( cbBitsSrc );
+
+ if ( nRecType == EMR_STRETCHBLT )
+ pWMF->ReadInt32( cxSrc ).ReadInt32( cySrc );
+ else
+ cxSrc = cySrc = 0;
+
+ Bitmap aBitmap;
+ tools::Rectangle aRect( Point( xDest, yDest ), Size( cxDest, cyDest ) );
+
+ if ( (cbBitsSrc > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc) )
+ bStatus = false;
+ else
+ {
+ sal_uInt32 nSize = cbBmiSrc + cbBitsSrc + 14;
+ if ( nSize <= ( nEndPos - nStartPos ) )
+ {
+ 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 );
+ pWMF->Seek( nStart + offBmiSrc );
+ pWMF->ReadBytes(pBuf + 14, cbBmiSrc);
+ pWMF->Seek( nStart + offBitsSrc );
+ pWMF->ReadBytes(pBuf + 14 + cbBmiSrc, cbBitsSrc);
+ aTmp.Seek( 0 );
+ ReadDIB(aBitmap, aTmp, true);
+
+ // test if it is sensible to crop
+ if ( ( cxSrc > 0 ) && ( cySrc > 0 ) &&
+ ( xSrc >= 0 ) && ( ySrc >= 0 ) &&
+ ( xSrc + cxSrc <= aBitmap.GetSizePixel().Width() ) &&
+ ( ySrc + cySrc <= aBitmap.GetSizePixel().Height() ) )
+ {
+ tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
+ aBitmap.Crop( aCropRect );
+ }
+ aBmpSaveList.emplace_back(new BSaveStruct(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 = pWMF->Tell() - 8;
+
+ pWMF->SeekRel( 0x10 );
+ pWMF->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 );
+
+ Bitmap aBitmap;
+ tools::Rectangle aRect( Point( xDest, yDest ), Size( cxDest, cyDest ) );
+
+ if ( ((SAL_MAX_UINT32 - 14) < cbBitsSrc)
+ || ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc )
+ )
+ {
+ bStatus = false;
+ }
+ else
+ {
+ sal_uInt32 nSize = cbBmiSrc + cbBitsSrc + 14;
+ if ( nSize <= ( nEndPos - nStartPos ) )
+ {
+ 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 );
+ pWMF->Seek( nStart + offBmiSrc );
+ pWMF->ReadBytes(pBuf + 14, cbBmiSrc);
+ pWMF->Seek( nStart + offBitsSrc );
+ pWMF->ReadBytes(pBuf + 14 + cbBmiSrc, cbBitsSrc);
+ aTmp.Seek( 0 );
+ ReadDIB(aBitmap, aTmp, true);
+
+ // test if it is sensible to crop
+ if ( ( cxSrc > 0 ) && ( cySrc > 0 ) &&
+ ( xSrc >= 0 ) && ( ySrc >= 0 ) &&
+ ( xSrc + cxSrc <= aBitmap.GetSizePixel().Width() ) &&
+ ( ySrc + cySrc <= aBitmap.GetSizePixel().Height() ) )
+ {
+ tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
+ aBitmap.Crop( aCropRect );
+ }
+ aBmpSaveList.emplace_back(new BSaveStruct(aBitmap, aRect, dwRop));
+ }
+ }
+ }
+ break;
+
+ case EMR_EXTCREATEFONTINDIRECTW :
+ {
+ pWMF->ReadUInt32( nIndex );
+ if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+ {
+ LOGFONTW aLogFont;
+ pWMF->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);
+ pWMF->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 = pOut->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();
+
+ pOut->CreateObjectIndexed(nIndex, o3tl::make_unique<WinMtfFontStyle>( aLogFont ));
+ }
+ }
+ break;
+
+ case EMR_EXTTEXTOUTA :
+ bFlag = true;
+ SAL_FALLTHROUGH;
+ case EMR_EXTTEXTOUTW :
+ {
+ sal_Int32 nLeft, nTop, nRight, nBottom, ptlReferenceX, ptlReferenceY, nGfxMode, nXScale, nYScale;
+ sal_uInt32 nOffString, nOptions, offDx;
+ sal_Int32 nLen;
+
+ nCurPos = pWMF->Tell() - 8;
+
+ pWMF->ReadInt32( nLeft ).ReadInt32( nTop ).ReadInt32( nRight ).ReadInt32( nBottom ).ReadInt32( nGfxMode ).ReadInt32( nXScale ).ReadInt32( nYScale )
+ .ReadInt32( ptlReferenceX ).ReadInt32( ptlReferenceY ).ReadInt32( nLen ).ReadUInt32( nOffString ).ReadUInt32( nOptions );
+
+ pWMF->SeekRel( 0x10 );
+ pWMF->ReadUInt32( offDx );
+
+ ComplexTextLayoutFlags nTextLayoutMode = ComplexTextLayoutFlags::Default;
+ if ( nOptions & ETO_RTLREADING )
+ nTextLayoutMode = ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft;
+ pOut->SetTextLayoutMode( nTextLayoutMode );
+ SAL_WARN_IF( ( nOptions & ( ETO_PDY | ETO_GLYPH_INDEX ) ) != 0, "vcl.emf", "SJ: ETO_PDY || ETO_GLYPH_INDEX in EMF" );
+
+ Point aPos( ptlReferenceX, ptlReferenceY );
+ bool bLenSane = nLen > 0 && nLen < static_cast<sal_Int32>( SAL_MAX_UINT32 / sizeof(sal_Int32) );
+ bool bOffStringSane = nOffString <= nEndPos - nCurPos;
+ if (bLenSane && bOffStringSane)
+ {
+ pWMF->Seek( nCurPos + nOffString );
+ OUString aText;
+ if ( bFlag )
+ {
+ if ( nLen <= static_cast<sal_Int32>( nEndPos - pWMF->Tell() ) )
+ {
+ std::unique_ptr<sal_Char[]> pBuf(new sal_Char[ nLen ]);
+ pWMF->ReadBytes(pBuf.get(), nLen);
+ aText = OUString(pBuf.get(), nLen, pOut->GetCharSet());
+ }
+ }
+ else
+ {
+ if ( ( nLen * sizeof(sal_Unicode) ) <= ( nEndPos - pWMF->Tell() ) )
+ {
+ std::unique_ptr<sal_Unicode[]> pBuf(new sal_Unicode[ nLen ]);
+ pWMF->ReadBytes(pBuf.get(), nLen << 1);
+ #ifdef OSL_BIGENDIAN
+ sal_Char nTmp, *pTmp = (sal_Char*)( pBuf.get() + nLen );
+ while ( pTmp-- != (sal_Char*)pBuf.get() )
+ {
+ nTmp = *pTmp--;
+ pTmp[ 1 ] = *pTmp;
+ *pTmp = nTmp;
+ }
+ #endif
+ aText = OUString(pBuf.get(), nLen);
+ }
+ }
+
+ std::unique_ptr<long[]> pDXAry, pDYAry;
+ sal_Int32 nDxSize = nLen * ((nOptions & ETO_PDY) ? 8 : 4);
+ if ( offDx && (( nCurPos + offDx + nDxSize ) <= nNextPos ) && nNextPos <= nEndPos )
+ {
+ pWMF->Seek( nCurPos + offDx );
+ pDXAry.reset( new long[aText.getLength()] );
+ if (nOptions & ETO_PDY)
+ {
+ pDYAry.reset( new long[aText.getLength()] );
+ }
+
+ for (sal_Int32 i = 0; i < aText.getLength(); ++i)
+ {
+ sal_Int32 nDxCount = 1;
+ if (aText.getLength() != nLen)
+ {
+ sal_Unicode cUniChar = aText[i];
+ OString aTmp(&cUniChar, 1, pOut->GetCharSet());
+ if (aTmp.getLength() > 1)
+ {
+ nDxCount = aTmp.getLength();
+ }
+ }
+
+ sal_Int32 nDx = 0, nDy = 0;
+ while (nDxCount--)
+ {
+ sal_Int32 nDxTmp = 0;
+ pWMF->ReadInt32(nDxTmp);
+ nDx += nDxTmp;
+ if (nOptions & ETO_PDY)
+ {
+ sal_Int32 nDyTmp = 0;
+ pWMF->ReadInt32(nDyTmp);
+ nDy += nDyTmp;
+ }
+ }
+
+ pDXAry[i] = nDx;
+ if (nOptions & ETO_PDY)
+ {
+ pDYAry[i] = nDy;
+ }
+ }
+ }
+ pOut->DrawText(aPos, aText, pDXAry.get(), pDYAry.get(), bRecordPath, nGfxMode);
+ }
+ }
+ break;
+
+ case EMR_POLYBEZIERTO16 :
+ ReadAndDrawPolygon<sal_Int16>( [] ( std::unique_ptr<MtfToolsWriter> &pWinMtfOutput, tools::Polygon& rPolygon, bool aTo, bool aRecordPath )
+ { pWinMtfOutput->DrawPolyBezier( rPolygon, aTo, aRecordPath ); }, true );
+ break;
+
+ case EMR_POLYBEZIER16 :
+ ReadAndDrawPolygon<sal_Int16>( [] ( std::unique_ptr<MtfToolsWriter> &pWinMtfOutput, tools::Polygon& rPolygon, bool aTo, bool aRecordPath )
+ { pWinMtfOutput->DrawPolyBezier( rPolygon, aTo, aRecordPath ); }, false );
+ break;
+
+ case EMR_POLYGON16 :
+ ReadAndDrawPolygon<sal_Int16>( [] ( std::unique_ptr<MtfToolsWriter> &pWinMtfOutput, tools::Polygon& rPolygon, bool /*aTo*/, bool aRecordPath )
+ { pWinMtfOutput->DrawPolygon( rPolygon, aRecordPath ); }, false );
+ break;
+
+ case EMR_POLYLINETO16 :
+ ReadAndDrawPolygon<sal_Int16>( [] ( std::unique_ptr<MtfToolsWriter> &pWinMtfOutput, tools::Polygon& rPolygon, bool aTo, bool aRecordPath )
+ { pWinMtfOutput->DrawPolyLine( rPolygon, aTo, aRecordPath ); }, true );
+ break;
+
+ case EMR_POLYLINE16 :
+ ReadAndDrawPolygon<sal_Int16>( [] ( std::unique_ptr<MtfToolsWriter> &pWinMtfOutput, tools::Polygon& rPolygon, bool aTo, bool aRecordPath )
+ { pWinMtfOutput->DrawPolyLine( rPolygon, aTo, aRecordPath ); }, false );
+ break;
+
+ case EMR_POLYPOLYLINE16 :
+ ReadAndDrawPolyLine<sal_Int16>();
+ break;
+
+ case EMR_POLYPOLYGON16 :
+ ReadAndDrawPolyPolygon<sal_Int16>();
+ break;
+
+ case EMR_FILLRGN :
+ {
+ sal_uInt32 nLen;
+ tools::PolyPolygon aPolyPoly;
+ pWMF->SeekRel( 0x10 );
+ pWMF->ReadUInt32( nLen ).ReadUInt32( nIndex );
+
+ if ( ImplReadRegion( aPolyPoly, *pWMF, nRecSize ) )
+ {
+ pOut->Push();
+ pOut->SelectObject( nIndex );
+ pOut->DrawPolyPolygon( aPolyPoly );
+ pOut->Pop();
+ }
+ }
+ break;
+
+ case EMR_CREATEDIBPATTERNBRUSHPT :
+ {
+ sal_uInt32 nStart = pWMF->Tell() - 8;
+ Bitmap aBitmap;
+
+ pWMF->ReadUInt32( nIndex );
+
+ if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+ {
+ sal_uInt32 usage, offBmi, cbBmi, offBits, cbBits;
+
+ pWMF->ReadUInt32( usage );
+ pWMF->ReadUInt32( offBmi );
+ pWMF->ReadUInt32( cbBmi );
+ pWMF->ReadUInt32( offBits );
+ pWMF->ReadUInt32( cbBits );
+
+ if ( (cbBits > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBits < cbBmi) )
+ bStatus = false;
+ else if ( offBmi )
+ {
+ sal_uInt32 nSize = cbBmi + cbBits + 14;
+ if ( nSize <= ( nEndPos - nStartPos ) )
+ {
+ 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 );
+ pWMF->Seek( nStart + offBmi );
+ pWMF->ReadBytes(pBuf + 14, cbBmi);
+ pWMF->Seek( nStart + offBits );
+ pWMF->ReadBytes(pBuf + 14 + cbBmi, cbBits);
+ aTmp.Seek( 0 );
+ ReadDIB(aBitmap, aTmp, true);
+ }
+ }
+ }
+
+ pOut->CreateObjectIndexed(nIndex, o3tl::make_unique<WinMtfFillStyle>( aBitmap ));
+ }
+ break;
+
+ case EMR_MASKBLT : SAL_INFO("vcl.emf", "not implemented 'MaskBlt'"); break;
+ case EMR_PLGBLT : SAL_INFO("vcl.emf", "not implemented 'PlgBlt'"); break;
+ case EMR_SETDIBITSTODEVICE : SAL_INFO("vcl.emf", "not implemented 'SetDIBitsToDevice'"); break;
+ case EMR_FRAMERGN : SAL_INFO("vcl.emf", "not implemented 'FrameRgn'"); break;
+ case EMR_INVERTRGN : SAL_INFO("vcl.emf", "not implemented 'InvertRgn'"); break;
+ case EMR_PAINTRGN : SAL_INFO("vcl.emf", "not implemented 'PaintRgn'"); break;
+ case EMR_FLATTENPATH : SAL_INFO("vcl.emf", "not implemented 'FlattenPath'"); break;
+ case EMR_WIDENPATH : SAL_INFO("vcl.emf", "not implemented 'WidenPath'"); break;
+ case EMR_POLYDRAW : SAL_INFO("vcl.emf", "not implemented 'Polydraw'"); break;
+ case EMR_SETARCDIRECTION : SAL_INFO("vcl.emf", "not implemented 'SetArcDirection'"); break;
+ case EMR_SETPALETTEENTRIES : SAL_INFO("vcl.emf", "not implemented 'SetPaletteEntries'"); break;
+ case EMR_RESIZEPALETTE : SAL_INFO("vcl.emf", "not implemented 'ResizePalette'"); break;
+ case EMR_EXTFLOODFILL : SAL_INFO("vcl.emf", "not implemented 'ExtFloodFill'"); break;
+ case EMR_ANGLEARC : SAL_INFO("vcl.emf", "not implemented 'AngleArc'"); break;
+ case EMR_SETCOLORADJUSTMENT : SAL_INFO("vcl.emf", "not implemented 'SetColorAdjustment'"); break;
+ case EMR_POLYDRAW16 : SAL_INFO("vcl.emf", "not implemented 'PolyDraw16'"); break;
+ case EMR_POLYTEXTOUTA : SAL_INFO("vcl.emf", "not implemented 'PolyTextOutA'"); break;
+ case EMR_POLYTEXTOUTW : SAL_INFO("vcl.emf", "not implemented 'PolyTextOutW'"); break;
+ case EMR_CREATECOLORSPACE : SAL_INFO("vcl.emf", "not implemented 'CreateColorSpace'"); break;
+ case EMR_SETCOLORSPACE : SAL_INFO("vcl.emf", "not implemented 'SetColorSpace'"); break;
+ case EMR_DELETECOLORSPACE : SAL_INFO("vcl.emf", "not implemented 'DeleteColorSpace'"); break;
+ case EMR_GLSRECORD : SAL_INFO("vcl.emf", "not implemented 'GlsRecord'"); break;
+ case EMR_GLSBOUNDEDRECORD : SAL_INFO("vcl.emf", "not implemented 'GlsBoundRecord'"); break;
+ case EMR_PIXELFORMAT : SAL_INFO("vcl.emf", "not implemented 'PixelFormat'"); break;
+ case EMR_DRAWESCAPE : SAL_INFO("vcl.emf", "not implemented 'DrawEscape'"); break;
+ case EMR_EXTESCAPE : SAL_INFO("vcl.emf", "not implemented 'ExtEscape'"); break;
+ case EMR_STARTDOC : SAL_INFO("vcl.emf", "not implemented 'StartDoc'"); break;
+ case EMR_SMALLTEXTOUT : SAL_INFO("vcl.emf", "not implemented 'SmallTextOut'"); break;
+ case EMR_FORCEUFIMAPPING : SAL_INFO("vcl.emf", "not implemented 'ForceUFIMapping'"); break;
+ case EMR_NAMEDESCAPE : SAL_INFO("vcl.emf", "not implemented 'NamedEscape'"); break;
+ case EMR_COLORCORRECTPALETTE : SAL_INFO("vcl.emf", "not implemented 'ColorCorrectPalette'"); break;
+ case EMR_SETICMPROFILEA : SAL_INFO("vcl.emf", "not implemented 'SetICMProfileA'"); break;
+ case EMR_SETICMPROFILEW : SAL_INFO("vcl.emf", "not implemented 'SetICMProfileW'"); break;
+ case EMR_TRANSPARENTBLT : SAL_INFO("vcl.emf", "not implemented 'TransparenBlt'"); break;
+ case EMR_TRANSPARENTDIB : SAL_INFO("vcl.emf", "not implemented 'TransparenDib'"); break;
+ case EMR_GRADIENTFILL : SAL_INFO("vcl.emf", "not implemented 'GradientFill'"); break;
+ case EMR_SETLINKEDUFIS : SAL_INFO("vcl.emf", "not implemented 'SetLinkedUFIS'"); break;
+
+ case EMR_SETMAPPERFLAGS : SAL_INFO("vcl.emf", "not implemented 'SetMapperFlags'"); break;
+ case EMR_SETICMMODE : SAL_INFO("vcl.emf", "not implemented 'SetICMMode'"); break;
+ case EMR_CREATEMONOBRUSH : SAL_INFO("vcl.emf", "not implemented 'CreateMonoBrush'"); break;
+ case EMR_SETBRUSHORGEX : SAL_INFO("vcl.emf", "not implemented 'SetBrushOrgEx'"); break;
+ case EMR_SETMETARGN : SAL_INFO("vcl.emf", "not implemented 'SetMetArgn'"); break;
+ case EMR_SETMITERLIMIT : SAL_INFO("vcl.emf", "not implemented 'SetMiterLimit'"); break;
+ case EMR_EXCLUDECLIPRECT : SAL_INFO("vcl.emf", "not implemented 'ExcludeClipRect'"); break;
+ case EMR_REALIZEPALETTE : SAL_INFO("vcl.emf", "not implemented 'RealizePalette'"); break;
+ case EMR_SELECTPALETTE : SAL_INFO("vcl.emf", "not implemented 'SelectPalette'"); break;
+ case EMR_CREATEPALETTE : SAL_INFO("vcl.emf", "not implemented 'CreatePalette'"); break;
+ case EMR_ALPHADIBBLEND : SAL_INFO("vcl.emf", "not implemented 'AlphaDibBlend'"); break;
+ case EMR_SETTEXTJUSTIFICATION : SAL_INFO("vcl.emf", "not implemented 'SetTextJustification'"); break;
+
+ case EMR_COMMENT :
+ case EMR_HEADER : // has already been read at ReadHeader()
+ break;
+
+ default : SAL_INFO("vcl.emf", "Unknown Meta Action"); break;
+ }
+ }
+ pWMF->Seek( nNextPos );
+ }
+ if( !aBmpSaveList.empty() )
+ pOut->ResolveBitmapActions( aBmpSaveList );
+
+ if ( bStatus )
+ pWMF->Seek(nEndPos);
+
+ return bStatus;
+ };
+
+ bool EmfReader::ReadHeader()
+ {
+ sal_uInt32 nType, nSignature, nVersion;
+ sal_uInt32 nHeaderSize, nPalEntries;
+
+ // Spare me the METAFILEHEADER here
+ // Reading the METAHEADER - EMR_HEADER ([MS-EMF] section 2.3.4.2 EMR_HEADER Record Types)
+ pWMF->ReadUInt32( nType ).ReadUInt32( nHeaderSize );
+ if (nType != 0x00000001)
+ {
+ // per [MS-EMF] 2.3.4.2 EMF Header Record Types, type MUST be 0x00000001
+ SAL_WARN("vcl.emf", "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)
+ tools::Rectangle rclBounds = ReadRectangle(); // rectangle in logical units
+
+ // picture frame size (RectL object)
+ tools::Rectangle rclFrame = ReadRectangle(); // rectangle in device units 1/100th mm
+
+ pWMF->ReadUInt32( nSignature );
+
+ // 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("vcl.emf", "EMF\t\tSignature is not 0x464d4520 (\"FME\") - possibly corrupted file?");
+ return false;
+ }
+
+ pWMF->ReadUInt32(nVersion); // according to [WS-EMF] 2.2.9, this SHOULD be 0x0001000, however
+ // Microsoft note that not even Windows checks this...
+ if (nVersion != 0x00010000)
+ {
+ SAL_WARN("vcl.emf", "EMF\t\tThis really should be 0x00010000, though not absolutely essential...");
+ }
+
+ pWMF->ReadUInt32(nEndPos); // size of metafile
+ nEndPos += nStartPos;
+
+ sal_uInt32 nStrmPos = pWMF->Tell(); // checking if nEndPos is valid
+ pWMF->Seek(STREAM_SEEK_TO_END);
+ sal_uInt32 nActualFileSize = pWMF->Tell();
+
+ if ( nActualFileSize < nEndPos )
+ {
+ SAL_WARN("vcl.emf", "EMF\t\tEMF Header object records number of bytes as " << nEndPos
+ << ", however the file size is actually " << nActualFileSize
+ << " bytes. Possible file corruption?");
+ nEndPos = nActualFileSize;
+ }
+ pWMF->Seek(nStrmPos);
+
+ pWMF->ReadInt32(nRecordCount);
+
+ if (nRecordCount <= 0)
+ {
+ SAL_WARN("vcl.emf", "EMF\t\tEMF Header object shows record counter as <= 0! This shouldn't "
+ "be possible... indicator of possible file corruption?");
+ return false;
+ }
+
+ // the number of "handles", or graphics objects used in the metafile
+
+ sal_uInt16 nHandlesCount;
+ pWMF->ReadUInt16(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;
+ pWMF->ReadUInt16(nReserved);
+
+ if ( nReserved != 0x0000 )
+ {
+ SAL_WARN("vcl.emf", "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.
+
+ pWMF->SeekRel(0x8);
+
+ sal_Int32 nPixX, nPixY, nMillX, nMillY;
+ pWMF->ReadUInt32(nPalEntries);
+ pWMF->ReadInt32(nPixX);
+ pWMF->ReadInt32(nPixY);
+ pWMF->ReadInt32(nMillX);
+ pWMF->ReadInt32(nMillY);
+
+ pOut->SetrclFrame(rclFrame);
+ pOut->SetrclBounds(rclBounds);
+ pOut->SetRefPix(Size( nPixX, nPixY ) );
+ pOut->SetRefMill(Size( nMillX, nMillY ) );
+
+ pWMF->Seek(nStartPos + nHeaderSize);
+ return true;
+ }
+
+ tools::Rectangle EmfReader::ReadRectangle()
+ {
+ sal_Int32 nLeft, nTop, nRight, nBottom;
+ pWMF->ReadInt32(nLeft);
+ pWMF->ReadInt32(nTop);
+ pWMF->ReadInt32(nRight);
+ pWMF->ReadInt32(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 ( Point( x1, y1 ) );
+ Point aBR( Point( --x2, --y2 ) );
+ 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 000000000000..e48398522669
--- /dev/null
+++ b/emfio/source/reader/mtftools.cxx
@@ -0,0 +1,2283 @@
+/* -*- 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 <memory>
+//#include "winmtf.hxx"
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+//#include <vcl/metaact.hxx>
+#include <vcl/graphictools.hxx>
+#include <vcl/canvastools.hxx>
+//#include <vcl/metric.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/fract.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/tencinfo.h>
+#include <vcl/virdev.hxx>
+#include <o3tl/make_unique.hxx>
+#include <officecfg/Setup.hxx>
+#include <officecfg/Office/Linguistic.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/wincodepage.hxx>
+#include <tools/helpers.hxx>
+#include <vcl/bitmapaccess.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
+ {
+#ifdef OSL_BIGENDIAN
+ rXForm.eM11 = GetSwapFloat(rInStream);
+ rXForm.eM12 = GetSwapFloat(rInStream);
+ rXForm.eM21 = GetSwapFloat(rInStream);
+ rXForm.eM22 = GetSwapFloat(rInStream);
+ rXForm.eDx = GetSwapFloat(rInStream);
+ rXForm.eDy = GetSwapFloat(rInStream);
+#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);
+#endif
+ }
+ return rInStream;
+ }
+
+ void WinMtfClipPath::intersectClipRect( const tools::Rectangle& rRect )
+ {
+ maClip.intersectRange(
+ vcl::unotools::b2DRectangleFromRectangle(rRect));
+ }
+
+ void WinMtfClipPath::excludeClipRect( const tools::Rectangle& rRect )
+ {
+ maClip.subtractRange(
+ vcl::unotools::b2DRectangleFromRectangle(rRect));
+ }
+
+ void WinMtfClipPath::setClipPath( const tools::PolyPolygon& rPolyPolygon, sal_Int32 nClippingMode )
+ {
+ const basegfx::B2DPolyPolygon& rB2DPoly=rPolyPolygon.getB2DPolyPolygon();
+ switch ( nClippingMode )
+ {
+ case RGN_OR :
+ maClip.unionPolyPolygon(rB2DPoly);
+ break;
+ case RGN_XOR :
+ maClip.xorPolyPolygon(rB2DPoly);
+ break;
+ case RGN_DIFF :
+ maClip.subtractPolyPolygon(rB2DPoly);
+ break;
+ case RGN_AND :
+ maClip.intersectPolyPolygon(rB2DPoly);
+ break;
+ case RGN_COPY :
+ maClip = basegfx::tools::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::tools::B2DClipState();
+ }
+
+ basegfx::B2DPolyPolygon 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;
+ }
+
+ namespace {
+
+ OUString getLODefaultLanguage()
+ {
+ if (utl::ConfigManager::IsAvoidConfig())
+ return OUString("en-US");
+ OUString result(officecfg::Office::Linguistic::General::DefaultLocale::get());
+ if (result.isEmpty())
+ result = officecfg::Setup::L10N::ooSetupSystemLocale::get();
+ return result;
+ }
+
+ }
+
+ WinMtfFontStyle::WinMtfFontStyle( LOGFONTW& 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(getLODefaultLanguage().toUtf8().getStr(),
+ 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 & 0xf0 )
+ {
+ case FF_ROMAN:
+ eFamily = FAMILY_ROMAN;
+ break;
+
+ case FF_SWISS:
+ eFamily = FAMILY_SWISS;
+ break;
+
+ case FF_MODERN:
+ eFamily = FAMILY_MODERN;
+ break;
+
+ case FF_SCRIPT:
+ eFamily = FAMILY_SCRIPT;
+ break;
+
+ case 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( (short)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() );
+ long nHeight = aMetric.GetAscent() + aMetric.GetDescent();
+ if (nHeight)
+ {
+ double fHeight = ((double)aFontSize.Height() * rFont.lfHeight ) / nHeight;
+ aFontSize.Height() = (sal_Int32)( fHeight + 0.5 );
+ }
+ }
+
+ // Convert height to positive
+ aFontSize.Height() = std::abs(aFontSize.Height());
+
+ aFont.SetFontSize(aFontSize);
+ };
+
+ MtfTools::MtfTools( GDIMetaFile& rGDIMetaFile, SvStream& rStreamWMF, FilterConfigItem* pConfigItem )
+ : pOut( o3tl::make_unique<MtfToolsWriter>(rGDIMetaFile) )
+ , pWMF( &rStreamWMF )
+ , nEndPos( 0 )
+ , pFilterConfigItem( pConfigItem )
+ {
+ SvLockBytes *pLB = pWMF->GetLockBytes();
+ if ( pLB )
+ pLB->SetSynchronMode();
+
+ nStartPos = pWMF->Tell();
+
+ pOut->SetDevOrg( Point() );
+ if ( pFilterConfigItem )
+ {
+ xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
+ if ( xStatusIndicator.is() )
+ {
+ OUString aMsg;
+ xStatusIndicator->start( aMsg, 100 );
+ }
+ }
+ }
+
+ MtfTools::~MtfTools()
+ {
+ if ( xStatusIndicator.is() )
+ xStatusIndicator->end();
+ }
+
+ void MtfTools::Callback( sal_uInt16 nPercent )
+ {
+ if ( xStatusIndicator.is() )
+ xStatusIndicator->setValue( nPercent );
+ }
+
+ Color MtfTools::ReadColor()
+ {
+ sal_uInt32 nColor;
+ pWMF->ReadUInt32( nColor );
+ return Color( (sal_uInt8)nColor, (sal_uInt8)( nColor >> 8 ), (sal_uInt8)( nColor >> 16 ) );
+ };
+
+ Point MtfToolsWriter::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 MtfToolsWriter::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 ( mnGfxMode == GM_COMPATIBLE )
+ {
+ switch( mnMapMode )
+ {
+ case MM_LOENGLISH :
+ {
+ fX2 -= mnWinOrgX;
+ fY2 = mnWinOrgY-fY2;
+ fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH*10;
+ fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH*10;
+ fX2 += mnDevOrgX;
+ fY2 += mnDevOrgY;
+ }
+ break;
+ case MM_HIENGLISH :
+ {
+ fX2 -= mnWinOrgX;
+ fY2 = mnWinOrgY-fY2;
+ fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH;
+ fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH;
+ fX2 += mnDevOrgX;
+ fY2 += mnDevOrgY;
+ }
+ break;
+ case MM_TWIPS:
+ {
+ fX2 -= mnWinOrgX;
+ fY2 = mnWinOrgY-fY2;
+ fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH/MILLIINCH_PER_TWIPS;
+ fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH/MILLIINCH_PER_TWIPS;
+ fX2 += mnDevOrgX;
+ fY2 += mnDevOrgY;
+ }
+ break;
+ case MM_LOMETRIC :
+ {
+ fX2 -= mnWinOrgX;
+ fY2 = mnWinOrgY-fY2;
+ fX2 *= 10;
+ fY2 *= 10;
+ fX2 += mnDevOrgX;
+ fY2 += mnDevOrgY;
+ }
+ break;
+ case MM_HIMETRIC : // in hundredth of a millimeter
+ {
+ fX2 -= mnWinOrgX;
+ fY2 = mnWinOrgY-fY2;
+ fX2 += mnDevOrgX;
+ fY2 += mnDevOrgY;
+ }
+ break;
+ default :
+ {
+ fX2 -= mnWinOrgX;
+ fY2 -= mnWinOrgY;
+ fX2 /= mnWinExtX;
+ fY2 /= mnWinExtY;
+ fX2 *= mnDevWidth;
+ fY2 *= mnDevHeight;
+ fX2 += mnDevOrgX;
+ fY2 += mnDevOrgY; // fX2, fY2 now in device units
+ fX2 *= (double)mnMillX * 100.0 / (double)mnPixX;
+ fY2 *= (double)mnMillY * 100.0 / (double)mnPixY;
+ }
+ break;
+ }
+ fX2 -= mrclFrame.Left();
+ fY2 -= mrclFrame.Top();
+ }
+ return Point( FRound( fX2 ), FRound( fY2 ) );
+ }
+ else
+ return Point();
+ };
+
+ Size MtfToolsWriter::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 ( mnGfxMode == GM_COMPATIBLE )
+ {
+ switch( mnMapMode )
+ {
+ case MM_LOENGLISH :
+ {
+ fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH*10;
+ fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH*10;
+ }
+ break;
+ case MM_HIENGLISH :
+ {
+ fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH;
+ fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH;
+ }
+ break;
+ case MM_LOMETRIC :
+ {
+ fWidth *= 10;
+ fHeight*=-10;
+ }
+ break;
+ case MM_HIMETRIC : // in hundredth of millimeters
+ {
+ fHeight *= -1;
+ }
+ break;
+ case MM_TWIPS:
+ {
+ fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH/MILLIINCH_PER_TWIPS;
+ fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH/MILLIINCH_PER_TWIPS;
+ }
+ break;
+ default :
+ {
+ fWidth /= mnWinExtX;
+ fHeight /= mnWinExtY;
+ fWidth *= mnDevWidth;
+ fHeight *= mnDevHeight;
+ fWidth *= (double)mnMillX * 100 / (double)mnPixX;
+ fHeight *= (double)mnMillY * 100 / (double)mnPixY;
+ }
+ break;
+ }
+ }
+ return Size( FRound( fWidth ), FRound( fHeight ) );
+ }
+ else
+ return Size();
+ }
+
+ tools::Rectangle MtfToolsWriter::ImplMap( const tools::Rectangle& rRect )
+ {
+ return tools::Rectangle( ImplMap( rRect.TopLeft() ), ImplMap( rRect.GetSize() ) );
+ }
+
+ void MtfToolsWriter::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);
+
+ if( aFontSize.Height() < 0 )
+ aFontSize.Height() *= -1;
+
+ rFont.SetFontSize( aFontSize );
+
+ if( ( mnWinExtX * mnWinExtY ) < 0 )
+ rFont.SetOrientation( 3600 - rFont.GetOrientation() );
+ }
+
+ tools::Polygon& MtfToolsWriter::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 MtfToolsWriter::ImplScale( tools::Polygon& rPolygon )
+ {
+ sal_uInt16 nPoints = rPolygon.GetSize();
+ for ( sal_uInt16 i = 0; i < nPoints; i++ )
+ {
+ rPolygon[ i ] = ImplScale( rPolygon[ i ] );
+ }
+ }
+
+ tools::PolyPolygon& MtfToolsWriter::ImplScale( tools::PolyPolygon& rPolyPolygon )
+ {
+ sal_uInt16 nPolys = rPolyPolygon.Count();
+ for (sal_uInt16 i = 0; i < nPolys; ++i)
+ {
+ ImplScale(rPolyPolygon[i]);
+ }
+ return rPolyPolygon;
+ }
+
+ tools::PolyPolygon& MtfToolsWriter::ImplMap( tools::PolyPolygon& rPolyPolygon )
+ {
+ sal_uInt16 nPolys = rPolyPolygon.Count();
+ for ( sal_uInt16 i = 0; i < nPolys; ImplMap( rPolyPolygon[ i++ ] ) ) ;
+ return rPolyPolygon;
+ }
+
+ void MtfToolsWriter::SelectObject( sal_Int32 nIndex )
+ {
+ if ( nIndex & ENHMETA_STOCK_OBJECT )
+ {
+ sal_uInt16 nStockId = (sal_uInt8)nIndex;
+ switch( nStockId )
+ {
+ case WHITE_BRUSH :
+ {
+ maFillStyle = WinMtfFillStyle( Color( COL_WHITE ) );
+ mbFillStyleSelected = true;
+ }
+ break;
+ case LTGRAY_BRUSH :
+ {
+ maFillStyle = WinMtfFillStyle( Color( COL_LIGHTGRAY ) );
+ mbFillStyleSelected = true;
+ }
+ break;
+ case GRAY_BRUSH :
+ case DKGRAY_BRUSH :
+ {
+ maFillStyle = WinMtfFillStyle( Color( COL_GRAY ) );
+ mbFillStyleSelected = true;
+ }
+ break;
+ case BLACK_BRUSH :
+ {
+ maFillStyle = WinMtfFillStyle( Color( COL_BLACK ) );
+ mbFillStyleSelected = true;
+ }
+ break;
+ case NULL_BRUSH :
+ {
+ maFillStyle = WinMtfFillStyle( Color( COL_TRANSPARENT ), true );
+ mbFillStyleSelected = true;
+ }
+ break;
+ case WHITE_PEN :
+ {
+ maLineStyle = WinMtfLineStyle( Color( COL_WHITE ) );
+ }
+ break;
+ case BLACK_PEN :
+ {
+ maLineStyle = WinMtfLineStyle( Color( COL_BLACK ) );
+ }
+ break;
+ case NULL_PEN :
+ {
+ maLineStyle = WinMtfLineStyle( Color( COL_TRANSPARENT ), true );
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ nIndex &= 0xffff; // safety check: don't allow index to be > 65535
+
+ GDIObj *pGDIObj = nullptr;
+
+ if ( (sal_uInt32)nIndex < vGDIObj.size() )
+ pGDIObj = vGDIObj[ nIndex ].get();
+
+ if ( pGDIObj )
+ {
+ const auto pen = dynamic_cast<WinMtfLineStyle*>(pGDIObj);
+ if (pen)
+ maLineStyle = *pen;
+
+ const auto brush = dynamic_cast<WinMtfFillStyle*>(pGDIObj);
+ if (brush)
+ {
+ maFillStyle = *brush;
+ mbFillStyleSelected = true;
+ }
+
+ const auto font = dynamic_cast<WinMtfFontStyle*>(pGDIObj);
+ if (font)
+ maFont = font->aFont;
+ }
+ }
+ }
+
+ void MtfToolsWriter::SetTextLayoutMode( ComplexTextLayoutFlags nTextLayoutMode )
+ {
+ mnTextLayoutMode = nTextLayoutMode;
+ }
+
+ void MtfToolsWriter::SetBkMode( BkMode nMode )
+ {
+ mnBkMode = nMode;
+ }
+
+ void MtfToolsWriter::SetBkColor( const Color& rColor )
+ {
+ maBkColor = rColor;
+ }
+
+ void MtfToolsWriter::SetTextColor( const Color& rColor )
+ {
+ maTextColor = rColor;
+ }
+
+ void MtfToolsWriter::SetTextAlign( sal_uInt32 nAlign )
+ {
+ mnTextAlign = nAlign;
+ }
+
+ void MtfToolsWriter::ImplResizeObjectArry( sal_uInt32 nNewEntrys )
+ {
+ vGDIObj.resize(nNewEntrys);
+ }
+
+ void MtfToolsWriter::ImplDrawClippedPolyPolygon( const tools::PolyPolygon& rPolyPoly )
+ {
+ if ( rPolyPoly.Count() )
+ {
+ 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 MtfToolsWriter::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 < vGDIObj.size(); nIndex++ )
+ {
+ if ( !vGDIObj[ nIndex ] )
+ break;
+ }
+ if ( nIndex == vGDIObj.size() )
+ ImplResizeObjectArry( vGDIObj.size() + 16 );
+
+ vGDIObj[ nIndex ] = std::move(pObject);
+ }
+
+ void MtfToolsWriter::CreateObjectIndexed( sal_Int32 nIndex, std::unique_ptr<GDIObj> pObject )
+ {
+ if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+ {
+ 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.Width() += 1;
+ long nDotLen = ImplMap( aSize ).Width();
+ pLineStyle->aLineInfo.SetDistance( nDotLen );
+ pLineStyle->aLineInfo.SetDotLen( nDotLen );
+ pLineStyle->aLineInfo.SetDashLen( nDotLen * 3 );
+ }
+ }
+ }
+ if ( (sal_uInt32)nIndex >= vGDIObj.size() )
+ ImplResizeObjectArry( nIndex + 16 );
+
+ vGDIObj[ nIndex ] = std::move(pObject);
+ }
+ }
+
+ void MtfToolsWriter::DeleteObject( sal_Int32 nIndex )
+ {
+ if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
+ {
+ if ( (sal_uInt32)nIndex < vGDIObj.size() )
+ {
+ vGDIObj[ nIndex ].reset();
+ }
+ }
+ }
+
+ void MtfToolsWriter::IntersectClipRect( const tools::Rectangle& rRect )
+ {
+ mbClipNeedsUpdate=true;
+ if ((rRect.Left()-rRect.Right()==0) && (rRect.Top()-rRect.Bottom()==0))
+ {
+ return; // empty rectangles cause trouble
+ }
+ aClipPath.intersectClipRect( ImplMap( rRect ) );
+ }
+
+ void MtfToolsWriter::ExcludeClipRect( const tools::Rectangle& rRect )
+ {
+ mbClipNeedsUpdate=true;
+ aClipPath.excludeClipRect( ImplMap( rRect ) );
+ }
+
+ void MtfToolsWriter::MoveClipRegion( const Size& rSize )
+ {
+ mbClipNeedsUpdate=true;
+ aClipPath.moveClipRegion( ImplMap( rSize ) );
+ }
+
+ void MtfToolsWriter::SetClipPath( const tools::PolyPolygon& rPolyPolygon, sal_Int32 nClippingMode, bool bIsMapped )
+ {
+ mbClipNeedsUpdate = true;
+ tools::PolyPolygon aPolyPolygon(rPolyPolygon);
+
+ if (!bIsMapped)
+ {
+ if (!mbIsMapDevSet && (mnMapMode == MM_ISOTROPIC || mnMapMode == MM_ANISOTROPIC))
+ aPolyPolygon = ImplScale(aPolyPolygon);
+ else
+ aPolyPolygon = ImplMap(aPolyPolygon);
+ }
+ aClipPath.setClipPath(aPolyPolygon, nClippingMode);
+ }
+
+ void MtfToolsWriter::SetDefaultClipPath()
+ {
+ mbClipNeedsUpdate = true;
+ aClipPath.setDefaultClipPath();
+ }
+
+ MtfToolsWriter::MtfToolsWriter( GDIMetaFile& rGDIMetaFile ) :
+ mnLatestTextAlign ( 0 ),
+ mnTextAlign ( TA_LEFT | TA_TOP | TA_NOUPDATECP ),
+ maLatestBkColor ( 0x12345678 ),
+ maBkColor ( COL_WHITE ),
+ mnLatestTextLayoutMode( ComplexTextLayoutFlags::Default ),
+ mnTextLayoutMode ( ComplexTextLayoutFlags::Default ),
+ mnLatestBkMode ( BkMode::NONE ),
+ mnBkMode ( BkMode::OPAQUE ),
+ meLatestRasterOp ( RasterOp::Invert ),
+ meRasterOp ( RasterOp::OverPaint ),
+ maActPos ( Point() ),
+ mbNopMode ( false ),
+ mbFillStyleSelected ( false ),
+ mbClipNeedsUpdate ( true ),
+ mbComplexClip ( false ),
+ mnGfxMode ( GM_COMPATIBLE ),
+ mnMapMode ( 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 )
+ {
+ mbIsMapWinSet = false;
+ mbIsMapDevSet = false;
+ mpGDIMetaFile->AddAction( new MetaPushAction( 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 ) );
+ }
+
+ MtfToolsWriter::~MtfToolsWriter()
+ {
+ mpGDIMetaFile->AddAction( new MetaPopAction() );
+ mpGDIMetaFile->SetPrefMapMode( MapUnit::Map100thMM );
+ if ( mrclFrame.IsEmpty() )
+ mpGDIMetaFile->SetPrefSize( Size( mnDevWidth, mnDevHeight ) );
+ else
+ mpGDIMetaFile->SetPrefSize( mrclFrame.GetSize() );
+ }
+
+ void MtfToolsWriter::UpdateClipRegion()
+ {
+ if ( mbClipNeedsUpdate )
+ {
+ mbClipNeedsUpdate = false;
+ mbComplexClip = false;
+
+ mpGDIMetaFile->AddAction( new MetaPopAction() ); // taking the original clipregion
+ mpGDIMetaFile->AddAction( new MetaPushAction( PushFlags::CLIPREGION ) );
+
+ // skip for 'no clipping at all' case
+ if( !aClipPath.isEmpty() )
+ {
+ const basegfx::B2DPolyPolygon& rClipPoly( aClipPath.getClipPath() );
+
+ mbComplexClip = rClipPoly.count() > 1
+ || !basegfx::tools::isRectangle(rClipPoly);
+
+ static bool bEnableComplexClipViaRegion = getenv("SAL_WMF_COMPLEXCLIP_VIA_REGION") != nullptr;
+
+ if (bEnableComplexClipViaRegion)
+ {
+ //this makes cases like tdf#45820 work in reasonable time, and I feel in theory should
+ //be just fine. In practice I see the output is different so needs work before its the
+ //default, but for file fuzzing it should be good enough
+ if (mbComplexClip)
+ {
+ mpGDIMetaFile->AddAction(
+ new MetaISectRegionClipRegionAction(
+ vcl::Region(rClipPoly)));
+ mbComplexClip = false;
+ }
+ else
+ {
+ mpGDIMetaFile->AddAction(
+ new MetaISectRectClipRegionAction(
+ vcl::unotools::rectangleFromB2DRectangle(
+ rClipPoly.getB2DRange())));
+ }
+ }
+ else
+ {
+ //normal case
+ mpGDIMetaFile->AddAction(
+ new MetaISectRectClipRegionAction(
+ vcl::unotools::rectangleFromB2DRectangle(
+ rClipPoly.getB2DRange())));
+ }
+ }
+ }
+ }
+
+ void MtfToolsWriter::ImplSetNonPersistentLineColorTransparenz()
+ {
+ Color aColor( COL_TRANSPARENT);
+ WinMtfLineStyle aTransparentLine( aColor, true );
+ if ( ! ( maLatestLineStyle == aTransparentLine ) )
+ {
+ maLatestLineStyle = aTransparentLine;
+ mpGDIMetaFile->AddAction( new MetaLineColorAction( aTransparentLine.aLineColor, !aTransparentLine.bTransparent ) );
+ }
+ }
+
+ void MtfToolsWriter::UpdateLineStyle()
+ {
+ if (!( maLatestLineStyle == maLineStyle ) )
+ {
+ maLatestLineStyle = maLineStyle;
+ mpGDIMetaFile->AddAction( new MetaLineColorAction( maLineStyle.aLineColor, !maLineStyle.bTransparent ) );
+ }
+ }
+
+ void MtfToolsWriter::UpdateFillStyle()
+ {
+ if ( !mbFillStyleSelected ) // SJ: #i57205# taking care of bkcolor if no brush is selected
+ maFillStyle = WinMtfFillStyle( maBkColor, mnBkMode == BkMode::Transparent );
+ if (!( maLatestFillStyle == maFillStyle ) )
+ {
+ maLatestFillStyle = maFillStyle;
+ if (maFillStyle.aType == WinMtfFillStyleType::Solid)
+ mpGDIMetaFile->AddAction( new MetaFillColorAction( maFillStyle.aFillColor, !maFillStyle.bTransparent ) );
+ }
+ }
+
+ WMFRasterOp MtfToolsWriter::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 = m_NopFillStyle;
+ maLineStyle = m_NopLineStyle;
+ 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 )
+ {
+ m_NopFillStyle = maFillStyle;
+ m_NopLineStyle = maLineStyle;
+ maFillStyle = WinMtfFillStyle( Color( COL_TRANSPARENT ), true );
+ maLineStyle = WinMtfLineStyle( Color( COL_TRANSPARENT ), true );
+ mbNopMode = true;
+ }
+ }
+ break;
+
+ default:
+ meRasterOp = RasterOp::OverPaint;
+ break;
+ }
+ }
+ if ( nRetROP != nRasterOp )
+ mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) );
+ return nRetROP;
+ };
+
+ void MtfToolsWriter::StrokeAndFillPath( bool bStroke, bool bFill )
+ {
+ if ( aPathObj.Count() )
+ {
+ UpdateClipRegion();
+ UpdateLineStyle();
+ UpdateFillStyle();
+ if ( bFill )
+ {
+ if ( !bStroke )
+ {
+ mpGDIMetaFile->AddAction( new MetaPushAction( PushFlags::LINECOLOR ) );
+ mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
+ }
+ if ( aPathObj.Count() == 1 )
+ mpGDIMetaFile->AddAction( new MetaPolygonAction( aPathObj.GetObject( 0 ) ) );
+ else
+ mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aPathObj ) );
+
+ if ( !bStroke )
+ mpGDIMetaFile->AddAction( new MetaPopAction() );
+ }
+ else
+ {
+ sal_uInt16 i, nCount = aPathObj.Count();
+ for ( i = 0; i < nCount; i++ )
+ mpGDIMetaFile->AddAction( new MetaPolyLineAction( aPathObj[ i ], maLineStyle.aLineInfo ) );
+ }
+ ClearPath();
+ }
+ }
+
+ void MtfToolsWriter::DrawPixel( const Point& rSource, const Color& rColor )
+ {
+ mpGDIMetaFile->AddAction( new MetaPixelAction( ImplMap( rSource), rColor ) );
+ }
+
+ void MtfToolsWriter::MoveTo( const Point& rPoint, bool bRecordPath )
+ {
+ Point aDest( ImplMap( rPoint ) );
+ if ( bRecordPath )
+ {
+ // fdo#57353 create new subpath for subsequent moves
+ if ( aPathObj.Count() )
+ if ( aPathObj[ aPathObj.Count() - 1 ].GetSize() )
+ aPathObj.Insert( tools::Polygon() );
+ aPathObj.AddPoint( aDest );
+ }
+ maActPos = aDest;
+ }
+
+ void MtfToolsWriter::LineTo( const Point& rPoint, bool bRecordPath )
+ {
+ UpdateClipRegion();
+ Point aDest( ImplMap( rPoint ) );
+ if ( bRecordPath )
+ aPathObj.AddPoint( aDest );
+ else
+ {
+ UpdateLineStyle();
+ mpGDIMetaFile->AddAction( new MetaLineAction( maActPos, aDest, maLineStyle.aLineInfo ) );
+ }
+ maActPos = aDest;
+ }
+
+ void MtfToolsWriter::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(aClipPath.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 MtfToolsWriter::DrawRoundRect( const tools::Rectangle& rRect, const Size& rSize )
+ {
+ UpdateClipRegion();
+ UpdateLineStyle();
+ UpdateFillStyle();
+ mpGDIMetaFile->AddAction( new MetaRoundRectAction( ImplMap( rRect ), labs( ImplMap( rSize ).Width() ), labs( ImplMap( rSize ).Height() ) ) );
+ }
+
+ void MtfToolsWriter::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 MtfToolsWriter::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 MtfToolsWriter::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 MtfToolsWriter::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 MtfToolsWriter::DrawPolygon( tools::Polygon& rPolygon, bool bRecordPath )
+ {
+ UpdateClipRegion();
+ ImplMap( rPolygon );
+ if ( bRecordPath )
+ aPathObj.AddPolygon( rPolygon );
+ else
+ {
+ UpdateFillStyle();
+
+ if ( mbComplexClip )
+ {
+ tools::PolyPolygon aPolyPoly( rPolygon );
+ tools::PolyPolygon aDest;
+ tools::PolyPolygon(aClipPath.getClipPath()).GetIntersection( aPolyPoly, aDest );
+ 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 = SvtGraphicFill( tools::PolyPolygon( rPolygon ),
+ Color(),
+ 0.0,
+ SvtGraphicFill::fillNonZero,
+ SvtGraphicFill::fillTexture,
+ SvtGraphicFill::Transform(),
+ true,
+ SvtGraphicFill::hatchSingle,
+ Color(),
+ SvtGraphicFill::GradientType::Linear,
+ Color(),
+ Color(),
+ 0,
+ Graphic (maLatestFillStyle.aBmp) );
+
+ SvMemoryStream aMemStm;
+
+ WriteSvtGraphicFill( aMemStm, aFill );
+
+ mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0,
+ static_cast<const sal_uInt8*>(aMemStm.GetData()),
+ aMemStm.Seek( STREAM_SEEK_TO_END ) ) );
+ mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_END" ) );
+ }
+
+ }
+ }
+ }
+ }
+
+ void MtfToolsWriter::DrawPolyPolygon( tools::PolyPolygon& rPolyPolygon, bool bRecordPath )
+ {
+ UpdateClipRegion();
+
+ ImplMap( rPolyPolygon );
+
+ if ( bRecordPath )
+ aPathObj.AddPolyPolygon( rPolyPolygon );
+ else
+ {
+ UpdateFillStyle();
+
+ if ( mbComplexClip )
+ {
+ tools::PolyPolygon aDest;
+ tools::PolyPolygon(aClipPath.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 MtfToolsWriter::DrawPolyLine( tools::Polygon& rPolygon, bool bTo, bool bRecordPath )
+ {
+ UpdateClipRegion();
+
+ sal_uInt16 nPoints = rPolygon.GetSize();
+ if (nPoints >= 1)
+ {
+ ImplMap( rPolygon );
+ if ( bTo )
+ {
+ rPolygon[ 0 ] = maActPos;
+ maActPos = rPolygon[ rPolygon.GetSize() - 1 ];
+ }
+ if ( bRecordPath )
+ aPathObj.AddPolyLine( rPolygon );
+ else
+ {
+ UpdateLineStyle();
+ mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) );
+ }
+ }
+ }
+
+ void MtfToolsWriter::DrawPolyBezier( tools::Polygon& rPolygon, bool bTo, bool bRecordPath )
+ {
+ sal_uInt16 nPoints = rPolygon.GetSize();
+ if ( ( nPoints >= 4 ) && ( ( ( nPoints - 4 ) % 3 ) == 0 ) )
+ {
+ 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 )
+ aPathObj.AddPolyLine( rPolygon );
+ else
+ {
+ UpdateLineStyle();
+ mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) );
+ }
+ }
+ }
+
+ void MtfToolsWriter::DrawText( Point& rPosition, OUString& rText, long* pDXArry, long* pDYArry, bool bRecordPath, sal_Int32 nGfxMode )
+ {
+ UpdateClipRegion();
+ rPosition = ImplMap( rPosition );
+ sal_Int32 nOldGfxMode = GetGfxMode();
+ SetGfxMode( GM_COMPATIBLE );
+
+ if (pDXArry)
+ {
+ sal_Int32 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()) * (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()) * (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 == BkMode::Transparent )
+ aTmp.SetTransparent( true );
+ else
+ aTmp.SetTransparent( false );
+
+ aTmp.SetAlignment( eTextAlign );
+
+ if ( nGfxMode == GM_ADVANCED )
+ {
+ // check whether there is a font rotation applied via transformation
+ Point aP1( ImplMap( Point() ) );
+ Point aP2( ImplMap( Point( 0, 100 ) ) );
+ aP2.X() -= aP1.X();
+ aP2.Y() -= aP1.Y();
+ double fX = aP2.X();
+ double fY = aP2.Y();
+ if ( fX )
+ {
+ double fOrientation = acos( fX / sqrt( fX * fX + fY * fY ) ) * 57.29577951308;
+ if ( fY > 0 )
+ fOrientation = 360 - fOrientation;
+ fOrientation += 90;
+ fOrientation *= 10;
+ fOrientation += aTmp.GetOrientation();
+ aTmp.SetOrientation( 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.X() = pDXArry[ nLen - 1 ];
+ if ( pDYArry )
+ {
+ aActPosDelta.Y() = pDYArry[ nLen - 1 ];
+ }
+ }
+ else
+ {
+ nTextWidth = pVDev->GetTextWidth( rText );
+ aActPosDelta.X() = 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.X(), aDisplacement.Y(), maFont.GetOrientation());
+ rPosition -= aDisplacement;
+ }
+
+ if( mnTextAlign & TA_UPDATECP )
+ {
+ Point().RotateAround(aActPosDelta.X(), aActPosDelta.Y(), maFont.GetOrientation());
+ maActPos = rPosition + aActPosDelta;
+ }
+ }
+ if ( bChangeFont || ( maLatestFont != aTmp ) )
+ {
+ maLatestFont = aTmp;
+ mpGDIMetaFile->AddAction( new MetaFontAction( aTmp ) );
+ 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.X(), aCharDisplacement.Y(), maFont.GetOrientation());
+ mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition + aCharDisplacement, OUString( rText[i] ), nullptr, 0, 1 ) );
+ }
+ }
+ else
+ {
+ /* because text without dx array is badly scaled, we
+ will create such an array if necessary */
+ long* pDX = pDXArry;
+ if (!pDXArry)
+ {
+ // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
+ SolarMutexGuard aGuard;
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ pDX = new long[ rText.getLength() ];
+ pVDev->SetMapMode( MapUnit::Map100thMM );
+ pVDev->SetFont( maLatestFont );
+ pVDev->GetTextArray( rText, pDX, 0, rText.getLength());
+ }
+ mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, rText, pDX, 0, rText.getLength() ) );
+ if ( !pDXArry ) // this means we have created our own array
+ delete[] pDX; // which must be deleted
+ }
+ }
+ SetGfxMode( nOldGfxMode );
+ }
+
+ void MtfToolsWriter::ImplDrawBitmap( const Point& rPos, const Size& rSize, const BitmapEx& rBitmap )
+ {
+ BitmapEx aBmpEx( rBitmap );
+ if ( mbComplexClip )
+ {
+ VclPtrInstance< VirtualDevice > pVDev;
+ MapMode aMapMode( MapUnit::Map100thMM );
+ aMapMode.SetOrigin( Point( -rPos.X(), -rPos.Y() ) );
+ const Size aOutputSizePixel( pVDev->LogicToPixel( rSize, aMapMode ) );
+ const Size aSizePixel( rBitmap.GetSizePixel() );
+ if ( aOutputSizePixel.Width() && aOutputSizePixel.Height() )
+ {
+ aMapMode.SetScaleX( Fraction( aSizePixel.Width(), aOutputSizePixel.Width() ) );
+ aMapMode.SetScaleY( Fraction( aSizePixel.Height(), aOutputSizePixel.Height() ) );
+ }
+ pVDev->SetMapMode( aMapMode );
+ pVDev->SetOutputSizePixel( aSizePixel );
+ pVDev->SetFillColor( Color( COL_BLACK ) );
+ const tools::PolyPolygon aClip( aClipPath.getClipPath() );
+ pVDev->DrawPolyPolygon( aClip );
+ const Point aEmptyPoint;
+
+ // #i50672# Extract whole VDev content (to match size of rBitmap)
+ pVDev->EnableMapMode( false );
+ const Bitmap aVDevMask(pVDev->GetBitmap(aEmptyPoint, aSizePixel));
+
+ if(aBmpEx.IsTransparent())
+ {
+ // bitmap already uses a Mask or Alpha, we need to blend that with
+ // the new masking in pVDev
+ if(aBmpEx.IsAlpha())
+ {
+ // need to blend in AlphaMask quality (8Bit)
+ AlphaMask fromVDev(aVDevMask);
+ AlphaMask fromBmpEx(aBmpEx.GetAlpha());
+ AlphaMask::ScopedReadAccess pR(fromVDev);
+ AlphaMask::ScopedWriteAccess pW(fromBmpEx);
+
+ if(pR && pW)
+ {
+ const long nWidth(std::min(pR->Width(), pW->Width()));
+ const long nHeight(std::min(pR->Height(), pW->Height()));
+
+ for(long nY(0); nY < nHeight; nY++) for(long nX(0); nX < nWidth; nX++)
+ {
+ const sal_uInt8 nIndR(pR->GetPixelIndex(nY, nX));
+ const sal_uInt8 nIndW(pW->GetPixelIndex(nY, nX));
+
+ // these values represent transparency (0 == no, 255 == fully transparent),
+ // so to blend these we have to multiply the inverse (opacity)
+ // and re-invert the result to transparence
+ const sal_uInt8 nCombined(0x00ff - (((0x00ff - nIndR) * (0x00ff - nIndW)) >> 8));
+
+ pW->SetPixelIndex(nY, nX, nCombined);
+ }
+ }
+
+ pR.reset();
+ pW.reset();
+ aBmpEx = BitmapEx(aBmpEx.GetBitmap(), fromBmpEx);
+ }
+ else
+ {
+ // need to blend in Mask quality (1Bit)
+ Bitmap aMask(aVDevMask.CreateMask(Color(COL_WHITE)));
+
+ if ( rBitmap.GetTransparentColor() == Color( COL_WHITE ) )
+ {
+ aMask.CombineSimple( rBitmap.GetMask(), BmpCombine::Or );
+ }
+ else
+ {
+ aMask.CombineSimple( rBitmap.GetMask(), BmpCombine::And );
+ }
+
+ aBmpEx = BitmapEx( rBitmap.GetBitmap(), aMask );
+ }
+ }
+ else
+ {
+ // no mask yet, create and add new mask. For better quality, use Alpha,
+ // this allows the drawn mask being processed with AntiAliasing (AAed)
+ aBmpEx = BitmapEx(rBitmap.GetBitmap(), aVDevMask);
+ }
+ }
+
+ if ( aBmpEx.IsTransparent() )
+ mpGDIMetaFile->AddAction( new MetaBmpExScaleAction( rPos, rSize, aBmpEx ) );
+ else
+ mpGDIMetaFile->AddAction( new MetaBmpScaleAction( rPos, rSize, aBmpEx.GetBitmap() ) );
+ }
+
+ void MtfToolsWriter::ResolveBitmapActions( std::vector<std::unique_ptr<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].get();
+ tools::Rectangle aRect( pSave->aOutRect );
+
+ for ( i = nObjectStartIndex; i < nObjects; )
+ {
+ nObjectsOfSameSize++;
+ if ( ++i < nObjects )
+ {
+ pSave = rSaveList[i].get();
+ 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].get();
+
+ sal_uInt32 nWinRop = pSave->nWinRop;
+ sal_uInt8 nRasterOperation = (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].get();
+ 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 ) )
+ {
+ Bitmap aMask( pSave->aBmpEx.GetBitmap() );
+ BitmapEx aBmpEx( pSave2->aBmpEx.GetBitmap(), aMask );
+ ImplDrawBitmap( aPos, aSize, aBmpEx );
+ bDrawn = true;
+ i++;
+ }
+ // tdf#90539
+ else if ( ( nWinRop == SRCAND ) && ( pSave2->nWinRop == SRCINVERT ) )
+ {
+ Bitmap aMask( pSave->aBmpEx.GetBitmap() );
+ BitmapEx aBmpEx( pSave2->aBmpEx.GetBitmap(), aMask );
+ 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, 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, 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, aBitmap );
+ if ( nOperation == 0xd )
+ {
+ SetRasterOp( WMFRasterOp::Not );
+ DrawRect( aRect, false );
+ }
+ }
+ break;
+ case 0x6 :
+ case 0x9 :
+ {
+ SetRasterOp( WMFRasterOp::XorPen );
+ ImplDrawBitmap( aPos, aSize, 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();
+ ImplDrawBitmap( aPos, aSize, 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 MtfToolsWriter::SetDevOrg( const Point& rPoint )
+ {
+ mnDevOrgX = rPoint.X();
+ mnDevOrgY = rPoint.Y();
+ }
+
+ void MtfToolsWriter::SetDevOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd )
+ {
+ mnDevOrgX += nXAdd;
+ mnDevOrgY += nYAdd;
+ }
+
+ void MtfToolsWriter::SetDevExt( const Size& rSize ,bool regular)
+ {
+ if ( rSize.Width() && rSize.Height() )
+ {
+ switch( mnMapMode )
+ {
+ case MM_ISOTROPIC :
+ case MM_ANISOTROPIC :
+ {
+ mnDevWidth = rSize.Width();
+ mnDevHeight = rSize.Height();
+ }
+ }
+ if (regular)
+ {
+ mbIsMapDevSet=true;
+ }
+ }
+ }
+
+ void MtfToolsWriter::ScaleDevExt( double fX, double fY )
+ {
+ mnDevWidth = FRound( mnDevWidth * fX );
+ mnDevHeight = FRound( mnDevHeight * fY );
+ }
+
+ void MtfToolsWriter::SetWinOrg( const Point& rPoint , bool bIsEMF)
+ {
+ mnWinOrgX = rPoint.X();
+ mnWinOrgY = rPoint.Y();
+ if (bIsEMF)
+ {
+ SetDevByWin();
+ }
+ mbIsMapWinSet=true;
+ }
+
+ void MtfToolsWriter::SetWinOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd )
+ {
+ mnWinOrgX += nXAdd;
+ mnWinOrgY += nYAdd;
+ }
+
+ void MtfToolsWriter::SetDevByWin() //mnWinExt...-stuff has to be assigned before.
+ {
+ if (!mbIsMapDevSet)
+ {
+ if ( mnMapMode == MM_ISOTROPIC ) //TODO: WHAT ABOUT ANISOTROPIC???
+ {
+ Size aSize( (mnWinExtX + mnWinOrgX) >> MS_FIXPOINT_BITCOUNT_28_4,
+ -((mnWinExtY - mnWinOrgY) >> MS_FIXPOINT_BITCOUNT_28_4));
+
+ SetDevExt(aSize, false);
+ }
+ }
+ }
+
+ void MtfToolsWriter::SetWinExt(const Size& rSize, bool bIsEMF)
+ {
+ if (rSize.Width() && rSize.Height())
+ {
+ switch( mnMapMode )
+ {
+ case MM_ISOTROPIC :
+ case MM_ANISOTROPIC :
+ {
+ mnWinExtX = rSize.Width();
+ mnWinExtY = rSize.Height();
+ if (bIsEMF)
+ {
+ SetDevByWin();
+ }
+ mbIsMapWinSet = true;
+ }
+ }
+ }
+ }
+
+ void MtfToolsWriter::ScaleWinExt( double fX, double fY )
+ {
+ mnWinExtX = FRound( mnWinExtX * fX );
+ mnWinExtY = FRound( mnWinExtY * fY );
+ }
+
+ void MtfToolsWriter::SetrclBounds( const tools::Rectangle& rRect )
+ {
+ mrclBounds = rRect;
+ }
+
+ void MtfToolsWriter::SetrclFrame( const tools::Rectangle& rRect )
+ {
+ mrclFrame = rRect;
+ }
+
+ void MtfToolsWriter::SetRefPix( const Size& rSize )
+ {
+ mnPixX = rSize.Width();
+ mnPixY = rSize.Height();
+ }
+
+ void MtfToolsWriter::SetRefMill( const Size& rSize )
+ {
+ mnMillX = rSize.Width();
+ mnMillY = rSize.Height();
+ }
+
+ void MtfToolsWriter::SetMapMode( sal_uInt32 nMapMode )
+ {
+ mnMapMode = nMapMode;
+ if ( nMapMode == MM_TEXT && !mbIsMapWinSet )
+ {
+ mnWinExtX = mnDevWidth;
+ mnWinExtY = mnDevHeight;
+ }
+ else if ( mnMapMode == MM_HIMETRIC )
+ {
+ mnWinExtX = mnMillX * 100;
+ mnWinExtY = mnMillY * 100;
+ }
+ }
+
+ void MtfToolsWriter::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 MtfToolsWriter::ModifyWorldTransform( const XForm& rXForm, sal_uInt32 nMode )
+ {
+ switch( nMode )
+ {
+ case MWT_IDENTITY :
+ {
+ maXForm.eM11 = maXForm.eM22 = 1.0f;
+ maXForm.eM12 = maXForm.eM21 = maXForm.eDx = maXForm.eDy = 0.0f;
+ break;
+ }
+
+ case MWT_RIGHTMULTIPLY :
+ case MWT_LEFTMULTIPLY :
+ {
+ const XForm* pLeft;
+ const XForm* pRight;
+
+ if ( nMode == 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 MWT_SET:
+ {
+ SetWorldTransform(rXForm);
+ break;
+ }
+ }
+ }
+
+ void MtfToolsWriter::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)
+ std::shared_ptr<SaveStruct> pSave( new SaveStruct );
+
+ pSave->aLineStyle = maLineStyle;
+ pSave->aFillStyle = maFillStyle;
+
+ pSave->aFont = maFont;
+ pSave->aTextColor = maTextColor;
+ pSave->nTextAlign = mnTextAlign;
+ pSave->nTextLayoutMode = mnTextLayoutMode;
+ pSave->nMapMode = mnMapMode;
+ pSave->nGfxMode = mnGfxMode;
+ pSave->nBkMode = mnBkMode;
+ pSave->aBkColor = maBkColor;
+ 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->aPathObj = aPathObj;
+ pSave->aClipPath = aClipPath;
+
+ vSaveStack.push_back( pSave );
+ }
+
+ void MtfToolsWriter::Pop()
+ {
+ // Get the latest data from the stack
+ if( !vSaveStack.empty() )
+ {
+ // Backup the current data on the stack
+ std::shared_ptr<SaveStruct> pSave( vSaveStack.back() );
+
+ maLineStyle = pSave->aLineStyle;
+ maFillStyle = pSave->aFillStyle;
+
+ maFont = pSave->aFont;
+ maTextColor = pSave->aTextColor;
+ mnTextAlign = pSave->nTextAlign;
+ mnTextLayoutMode = pSave->nTextLayoutMode;
+ mnBkMode = pSave->nBkMode;
+ mnGfxMode = pSave->nGfxMode;
+ mnMapMode = pSave->nMapMode;
+ maBkColor = pSave->aBkColor;
+ 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;
+
+ aPathObj = pSave->aPathObj;
+ if ( ! ( aClipPath == pSave->aClipPath ) )
+ {
+ aClipPath = pSave->aClipPath;
+ mbClipNeedsUpdate = true;
+ }
+ if ( meLatestRasterOp != meRasterOp )
+ mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) );
+ vSaveStack.pop_back();
+ }
+ }
+
+ void MtfToolsWriter::AddFromGDIMetaFile( GDIMetaFile& rGDIMetaFile )
+ {
+ rGDIMetaFile.Play( *mpGDIMetaFile );
+ }
+
+ void MtfToolsWriter::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 MtfToolsWriter::PassEMFPlus( void* 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 000000000000..80e134909266
--- /dev/null
+++ b/emfio/source/reader/wmfreader.cxx
@@ -0,0 +1,1846 @@
+/* -*- 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 <memory>
+#include <boost/optional.hpp>
+//#include <vcl/gdimtf.hxx>
+//#include <vcl/wmf.hxx>
+#include <rtl/crc.h>
+#include <rtl/tencinfo.h>
+#include <osl/endian.h>
+#include <vcl/svapp.hxx>
+#include <vcl/dibtools.hxx>
+#include <tools/fract.hxx>
+#include <o3tl/make_unique.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <osl/thread.h>
+
+// MS Windows defines
+
+#define W_META_SETBKCOLOR 0x0201
+#define W_META_SETBKMODE 0x0102
+#define W_META_SETMAPMODE 0x0103
+#define W_META_SETROP2 0x0104
+#define W_META_SETRELABS 0x0105
+#define W_META_SETPOLYFILLMODE 0x0106
+#define W_META_SETSTRETCHBLTMODE 0x0107
+#define W_META_SETTEXTCHAREXTRA 0x0108
+#define W_META_SETTEXTCOLOR 0x0209
+#define W_META_SETTEXTJUSTIFICATION 0x020A
+#define W_META_SETWINDOWORG 0x020B
+#define W_META_SETWINDOWEXT 0x020C
+#define W_META_SETVIEWPORTORG 0x020D
+#define W_META_SETVIEWPORTEXT 0x020E
+#define W_META_OFFSETWINDOWORG 0x020F
+#define W_META_SCALEWINDOWEXT 0x0410
+#define W_META_OFFSETVIEWPORTORG 0x0211
+#define W_META_SCALEVIEWPORTEXT 0x0412
+#define W_META_LINETO 0x0213
+#define W_META_MOVETO 0x0214
+#define W_META_EXCLUDECLIPRECT 0x0415
+#define W_META_INTERSECTCLIPRECT 0x0416
+#define W_META_ARC 0x0817
+#define W_META_ELLIPSE 0x0418
+#define W_META_FLOODFILL 0x0419
+#define W_META_PIE 0x081A
+#define W_META_RECTANGLE 0x041B
+#define W_META_ROUNDRECT 0x061C
+#define W_META_PATBLT 0x061D
+#define W_META_SAVEDC 0x001E
+#define W_META_SETPIXEL 0x041F
+#define W_META_OFFSETCLIPRGN 0x0220
+#define W_META_TEXTOUT 0x0521
+#define W_META_BITBLT 0x0922
+#define W_META_STRETCHBLT 0x0B23
+#define W_META_POLYGON 0x0324
+#define W_META_POLYLINE 0x0325
+#define W_META_ESCAPE 0x0626
+#define W_META_RESTOREDC 0x0127
+#define W_META_FILLREGION 0x0228
+#define W_META_FRAMEREGION 0x0429
+#define W_META_INVERTREGION 0x012A
+#define W_META_PAINTREGION 0x012B
+#define W_META_SELECTCLIPREGION 0x012C
+#define W_META_SELECTOBJECT 0x012D
+#define W_META_SETTEXTALIGN 0x012E
+#define W_META_DRAWTEXT 0x062F
+#define W_META_CHORD 0x0830
+#define W_META_SETMAPPERFLAGS 0x0231
+#define W_META_EXTTEXTOUT 0x0a32
+#define W_META_SETDIBTODEV 0x0d33
+#define W_META_SELECTPALETTE 0x0234
+#define W_META_REALIZEPALETTE 0x0035
+#define W_META_ANIMATEPALETTE 0x0436
+#define W_META_SETPALENTRIES 0x0037
+#define W_META_POLYPOLYGON 0x0538
+#define W_META_RESIZEPALETTE 0x0139
+#define W_META_DIBBITBLT 0x0940
+#define W_META_DIBSTRETCHBLT 0x0b41
+#define W_META_DIBCREATEPATTERNBRUSH 0x0142
+#define W_META_STRETCHDIB 0x0f43
+#define W_META_EXTFLOODFILL 0x0548
+#define W_META_RESETDC 0x014C
+#define W_META_STARTDOC 0x014D
+#define W_META_STARTPAGE 0x004F
+#define W_META_ENDPAGE 0x0050
+#define W_META_ABORTDOC 0x0052
+#define W_META_ENDDOC 0x005E
+#define W_META_DELETEOBJECT 0x01f0
+#define W_META_CREATEPALETTE 0x00f7
+#define W_META_CREATEBRUSH 0x00F8
+#define W_META_CREATEPATTERNBRUSH 0x01F9
+#define W_META_CREATEPENINDIRECT 0x02FA
+#define W_META_CREATEFONTINDIRECT 0x02FB
+#define W_META_CREATEBRUSHINDIRECT 0x02FC
+#define W_META_CREATEBITMAPINDIRECT 0x02FD
+#define W_META_CREATEBITMAP 0x06FE
+#define W_META_CREATEREGION 0x06FF
+
+namespace
+{
+ static void GetWinExtMax(const Point& rSource, tools::Rectangle& rPlaceableBound, const sal_Int16 nMapMode)
+ {
+ Point aSource(rSource);
+ if (nMapMode == MM_HIMETRIC)
+ aSource.Y() = -rSource.Y();
+ if (aSource.X() < rPlaceableBound.Left())
+ rPlaceableBound.Left() = aSource.X();
+ if (aSource.X() > rPlaceableBound.Right())
+ rPlaceableBound.Right() = aSource.X();
+ if (aSource.Y() < rPlaceableBound.Top())
+ rPlaceableBound.Top() = aSource.Y();
+ if (aSource.Y() > rPlaceableBound.Bottom())
+ rPlaceableBound.Bottom() = aSource.Y();
+ }
+
+ static void GetWinExtMax(const tools::Rectangle& rSource, tools::Rectangle& rPlaceableBound, const sal_Int16 nMapMode)
+ {
+ GetWinExtMax(rSource.TopLeft(), rPlaceableBound, nMapMode);
+ GetWinExtMax(rSource.BottomRight(), rPlaceableBound, nMapMode);
+ }
+
+}
+
+namespace emfio
+{
+ inline Point WmfReader::ReadPoint()
+ {
+ short nX = 0, nY = 0;
+ pWMF->ReadInt16( nX ).ReadInt16( nY );
+ return Point( nX, nY );
+ }
+
+ inline Point WmfReader::ReadYX()
+ {
+ short nX = 0, nY = 0;
+ pWMF->ReadInt16( nY ).ReadInt16( nX );
+ return Point( nX, nY );
+ }
+
+ tools::Rectangle WmfReader::ReadRectangle()
+ {
+ Point aBR, aTL;
+ aBR = ReadYX();
+ aTL = ReadYX();
+ aBR.X()--;
+ aBR.Y()--;
+ return tools::Rectangle( aTL, aBR );
+ }
+
+ Size WmfReader::ReadYXExt()
+ {
+ short nW=0, nH=0;
+ pWMF->ReadInt16( nH ).ReadInt16( nW );
+ return Size( nW, nH );
+ }
+
+ void WmfReader::ReadRecordParams( sal_uInt16 nFunc )
+ {
+ switch( nFunc )
+ {
+ case W_META_SETBKCOLOR:
+ {
+ pOut->SetBkColor( ReadColor() );
+ }
+ break;
+
+ case W_META_SETBKMODE:
+ {
+ sal_uInt16 nDat = 0;
+ pWMF->ReadUInt16( nDat );
+ pOut->SetBkMode( static_cast<BkMode>(nDat) );
+ }
+ break;
+
+ // !!!
+ case W_META_SETMAPMODE:
+ {
+ sal_Int16 nMapMode = 0;
+ pWMF->ReadInt16( nMapMode );
+ pOut->SetMapMode( nMapMode );
+ }
+ break;
+
+ case W_META_SETROP2:
+ {
+ sal_uInt16 nROP2 = 0;
+ pWMF->ReadUInt16( nROP2 );
+ pOut->SetRasterOp( (WMFRasterOp)nROP2 );
+ }
+ break;
+
+ case W_META_SETTEXTCOLOR:
+ {
+ pOut->SetTextColor( ReadColor() );
+ }
+ break;
+
+ case W_META_SETWINDOWORG:
+ {
+ pOut->SetWinOrg( ReadYX() );
+ }
+ break;
+
+ case W_META_SETWINDOWEXT:
+ {
+ short nWidth = 0, nHeight = 0;
+ pWMF->ReadInt16( nHeight ).ReadInt16( nWidth );
+ pOut->SetWinExt( Size( nWidth, nHeight ) );
+ }
+ break;
+
+ case W_META_OFFSETWINDOWORG:
+ {
+ short nXAdd = 0, nYAdd = 0;
+ pWMF->ReadInt16( nYAdd ).ReadInt16( nXAdd );
+ pOut->SetWinOrgOffset( nXAdd, nYAdd );
+ }
+ break;
+
+ case W_META_SCALEWINDOWEXT:
+ {
+ short nXNum = 0, nXDenom = 0, nYNum = 0, nYDenom = 0;
+ pWMF->ReadInt16( nYDenom ).ReadInt16( nYNum ).ReadInt16( nXDenom ).ReadInt16( nXNum );
+ if (!nYDenom || !nXDenom)
+ {
+ pWMF->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ break;
+ }
+ pOut->ScaleWinExt( (double)nXNum / nXDenom, (double)nYNum / nYDenom );
+ }
+ break;
+
+ case W_META_SETVIEWPORTORG:
+ case W_META_SETVIEWPORTEXT:
+ break;
+
+ case W_META_OFFSETVIEWPORTORG:
+ {
+ short nXAdd = 0, nYAdd = 0;
+ pWMF->ReadInt16( nYAdd ).ReadInt16( nXAdd );
+ pOut->SetDevOrgOffset( nXAdd, nYAdd );
+ }
+ break;
+
+ case W_META_SCALEVIEWPORTEXT:
+ {
+ short nXNum = 0, nXDenom = 0, nYNum = 0, nYDenom = 0;
+ pWMF->ReadInt16( nYDenom ).ReadInt16( nYNum ).ReadInt16( nXDenom ).ReadInt16( nXNum );
+ if (!nYDenom || !nXDenom)
+ {
+ pWMF->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ break;
+ }
+ pOut->ScaleDevExt( (double)nXNum / nXDenom, (double)nYNum / nYDenom );
+ }
+ break;
+
+ case W_META_LINETO:
+ {
+ pOut->LineTo( ReadYX() );
+ }
+ break;
+
+ case W_META_MOVETO:
+ {
+ pOut->MoveTo( ReadYX() );
+ }
+ break;
+
+ case W_META_INTERSECTCLIPRECT:
+ {
+ pOut->IntersectClipRect( ReadRectangle() );
+ }
+ break;
+
+ case W_META_RECTANGLE:
+ {
+ pOut->DrawRect( ReadRectangle() );
+ }
+ break;
+
+ case W_META_ROUNDRECT:
+ {
+ Size aSize( ReadYXExt() );
+ pOut->DrawRoundRect( ReadRectangle(), Size( aSize.Width() / 2, aSize.Height() / 2 ) );
+ }
+ break;
+
+ case W_META_ELLIPSE:
+ {
+ pOut->DrawEllipse( ReadRectangle() );
+ }
+ break;
+
+ case W_META_ARC:
+ {
+ Point aEnd( ReadYX() );
+ Point aStart( ReadYX() );
+ tools::Rectangle aRect( ReadRectangle() );
+ aRect.Justify();
+ pOut->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 )
+ pOut->DrawEllipse( aRect );
+ else
+ pOut->DrawPie( aRect, aStart, aEnd );
+ }
+ break;
+
+ case W_META_CHORD:
+ {
+ Point aEnd( ReadYX() );
+ Point aStart( ReadYX() );
+ tools::Rectangle aRect( ReadRectangle() );
+ aRect.Justify();
+ pOut->DrawChord( aRect, aStart, aEnd );
+ }
+ break;
+
+ case W_META_POLYGON:
+ {
+ bool bRecordOk = true;
+
+ sal_uInt16 nPoints(0);
+ pWMF->ReadUInt16(nPoints);
+
+ if (nPoints > pWMF->remainingSize() / (2 * sizeof(sal_uInt16)))
+ {
+ bRecordOk = false;
+ }
+ else
+ {
+ tools::Polygon aPoly(nPoints);
+ for (sal_uInt16 i(0); i < nPoints && pWMF->good(); ++i)
+ aPoly[ i ] = ReadPoint();
+ pOut->DrawPolygon(aPoly, false/*bRecordPath*/);
+ }
+
+ SAL_WARN_IF(!bRecordOk, "vcl.wmf", "polygon record has more points than we can handle");
+
+ bRecordOk &= pWMF->good();
+
+ if (!bRecordOk)
+ {
+ pWMF->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ break;
+ }
+ }
+ break;
+
+ case W_META_POLYPOLYGON:
+ {
+ sal_uInt16 nPolyCount(0);
+ // Number of polygons:
+ pWMF->ReadUInt16( nPolyCount );
+ if (nPolyCount && pWMF->good())
+ {
+ bool bRecordOk = true;
+ if (nPolyCount > pWMF->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, nPolyCount);
+ sal_uInt16 nPoints = 0;
+ for (sal_uInt16 a = 0; a < nPolyCount && pWMF->good(); ++a)
+ {
+ pWMF->ReadUInt16( pnPoints[a] );
+
+ if (pnPoints[a] > SAL_MAX_UINT16 - nPoints)
+ {
+ bRecordOk = false;
+ break;
+ }
+
+ nPoints += pnPoints[a];
+ }
+
+ SAL_WARN_IF(!bRecordOk, "vcl.wmf", "polypolygon record has more polygons than we can handle");
+
+ bRecordOk &= pWMF->good();
+
+ if (!bRecordOk)
+ {
+ pWMF->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ break;
+ }
+
+ // Polygon points are:
+ for (sal_uInt16 a = 0; a < nPolyCount && pWMF->good(); ++a)
+ {
+ const sal_uInt16 nPointCount(pnPoints[a]);
+
+ if (nPointCount > pWMF->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 && pWMF->good(); ++b)
+ {
+ pPtAry[b] = ReadPoint();
+ }
+
+ aPolyPoly.Insert( tools::Polygon(nPointCount, pPtAry) );
+ }
+
+ bRecordOk &= pWMF->good();
+
+ if (!bRecordOk)
+ {
+ pWMF->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ break;
+ }
+
+ pOut->DrawPolyPolygon( aPolyPoly );
+ }
+ }
+ break;
+
+ case W_META_POLYLINE:
+ {
+ bool bRecordOk = true;
+
+ sal_uInt16 nPoints(0);
+ pWMF->ReadUInt16(nPoints);
+
+ if (nPoints > pWMF->remainingSize() / (2 * sizeof(sal_uInt16)))
+ {
+ bRecordOk = false;
+ }
+ else
+ {
+ tools::Polygon aPoly(nPoints);
+ for (sal_uInt16 i(0); i < nPoints && pWMF->good(); ++i)
+ aPoly[ i ] = ReadPoint();
+ pOut->DrawPolyLine( aPoly );
+ }
+
+ SAL_WARN_IF(!bRecordOk, "vcl.wmf", "polyline record has more points than we can handle");
+
+ bRecordOk &= pWMF->good();
+
+ if (!bRecordOk)
+ {
+ pWMF->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ break;
+ }
+ }
+ break;
+
+ case W_META_SAVEDC:
+ {
+ pOut->Push();
+ }
+ break;
+
+ case W_META_RESTOREDC:
+ {
+ pOut->Pop();
+ }
+ break;
+
+ case W_META_SETPIXEL:
+ {
+ const Color aColor = ReadColor();
+ pOut->DrawPixel( ReadYX(), aColor );
+ }
+ break;
+
+ case W_META_OFFSETCLIPRGN:
+ {
+ pOut->MoveClipRegion( ReadYXExt() );
+ }
+ break;
+
+ case W_META_TEXTOUT:
+ {
+ sal_uInt16 nLength = 0;
+ pWMF->ReadUInt16( nLength );
+ if ( nLength )
+ {
+ std::unique_ptr<char[]> pChar(new char[ ( nLength + 1 ) &~ 1 ]);
+ pWMF->ReadBytes(pChar.get(), (nLength + 1) &~ 1);
+ OUString aText( pChar.get(), nLength, pOut->GetCharSet() );
+ pChar.reset();
+ Point aPosition( ReadYX() );
+ pOut->DrawText( aPosition, aText );
+ }
+ }
+ break;
+
+ case W_META_EXTTEXTOUT:
+ {
+ pWMF->SeekRel(-6);
+ sal_Int32 nRecordPos = pWMF->Tell(), nRecordSize = 0;
+ pWMF->ReadInt32( nRecordSize );
+ pWMF->SeekRel(2);
+ Point aPosition = ReadYX();
+ sal_uInt16 nLen = 0, nOptions = 0;
+ pWMF->ReadUInt16( nLen ).ReadUInt16( nOptions );
+
+ ComplexTextLayoutFlags nTextLayoutMode = ComplexTextLayoutFlags::Default;
+ if ( nOptions & ETO_RTLREADING )
+ nTextLayoutMode = ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft;
+ pOut->SetTextLayoutMode( nTextLayoutMode );
+ SAL_WARN_IF( ( nOptions & ( ETO_PDY | ETO_GLYPH_INDEX ) ) != 0, "vcl.wmf", "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;
+ tools::Rectangle aRect;
+ if( nOptions & ETO_CLIPPED )
+ {
+ const Point aPt1( ReadPoint() );
+ const Point aPt2( ReadPoint() );
+ aRect = tools::Rectangle( aPt1, aPt2 );
+ }
+
+ auto nRemainingSize = pWMF->remainingSize();
+ if (nRemainingSize < static_cast<sal_uInt32>(nOriginalBlockLen))
+ {
+ SAL_WARN("vcl.wmf", "exttextout record claimed more data than the stream can provide");
+ nOriginalTextLen = nOriginalBlockLen = nRemainingSize;
+ }
+
+ std::unique_ptr<char[]> pChar(new char[nOriginalBlockLen]);
+ pWMF->ReadBytes(pChar.get(), nOriginalBlockLen);
+ OUString aText(pChar.get(), nOriginalTextLen, pOut->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 )
+ {
+ std::unique_ptr<long[]> pDXAry, pDYAry;
+ sal_uInt32 nMaxStreamPos = nRecordPos + ( nRecordSize << 1 );
+ sal_Int32 nDxArySize = nMaxStreamPos - pWMF->Tell();
+ sal_Int32 nDxAryEntries = nDxArySize >> 1;
+ bool bUseDXAry = false;
+
+ if ( ( ( nDxAryEntries % nOriginalTextLen ) == 0 ) && ( nNewTextLen <= nOriginalTextLen ) )
+ {
+ sal_Int32 i; // needed just outside the for
+ pDXAry.reset(new long[ nNewTextLen ]);
+ if ( nOptions & ETO_PDY )
+ {
+ pDYAry.reset(new long[ nNewTextLen ]);
+ }
+ for (i = 0; i < nNewTextLen; i++ )
+ {
+ if ( pWMF->Tell() >= nMaxStreamPos )
+ break;
+ sal_Int32 nDxCount = 1;
+ if ( nNewTextLen != nOriginalTextLen )
+ {
+ sal_Unicode cUniChar = aText[i];
+ OString aTmp(&cUniChar, 1, pOut->GetCharSet());
+ if ( aTmp.getLength() > 1 )
+ {
+ nDxCount = aTmp.getLength();
+ }
+ }
+
+ sal_Int16 nDx = 0, nDy = 0;
+ while ( nDxCount-- )
+ {
+ if ( ( pWMF->Tell() + 2 ) > nMaxStreamPos )
+ break;
+ sal_Int16 nDxTmp = 0;
+ pWMF->ReadInt16(nDxTmp);
+ nDx += nDxTmp;
+ if ( nOptions & ETO_PDY )
+ {
+ if ( ( pWMF->Tell() + 2 ) > nMaxStreamPos )
+ break;
+ sal_Int16 nDyTmp = 0;
+ pWMF->ReadInt16(nDyTmp);
+ nDy += nDyTmp;
+ }
+ }
+
+ pDXAry[ i ] = nDx;
+ if ( nOptions & ETO_PDY )
+ {
+ pDYAry[i] = nDy;
+ }
+ }
+ if ( i == nNewTextLen )
+ bUseDXAry = true;
+ }
+ if ( pDXAry && bUseDXAry )
+ pOut->DrawText( aPosition, aText, pDXAry.get(), pDYAry.get() );
+ else
+ pOut->DrawText( aPosition, aText );
+ }
+ }
+ }
+ break;
+
+ case W_META_SELECTOBJECT:
+ {
+ sal_Int16 nObjIndex = 0;
+ pWMF->ReadInt16( nObjIndex );
+ pOut->SelectObject( nObjIndex );
+ }
+ break;
+
+ case W_META_SETTEXTALIGN:
+ {
+ sal_uInt16 nAlign = 0;
+ pWMF->ReadUInt16( nAlign );
+ pOut->SetTextAlign( nAlign );
+ }
+ break;
+
+ case W_META_BITBLT:
+ {
+ // 0-3 : nWinROP #93454#
+ // 4-5 : y offset of source bitmap
+ // 6-7 : x offset of source bitmap
+ // 8-9 : used height of source bitmap
+ // 10-11 : used width of source bitmap
+ // 12-13 : destination position y (in pixel)
+ // 14-15 : destination position x (in pixel)
+ // 16-17 : don't know
+ // 18-19 : Width Bitmap in Pixel
+ // 20-21 : Height Bitmap in Pixel
+ // 22-23 : bytes per scanline
+ // 24 : planes
+ // 25 : bitcount
+
+ sal_Int32 nWinROP = 0;
+ sal_uInt16 nSx = 0, nSy = 0, nSxe = 0, nSye = 0, nDontKnow = 0, nWidth = 0, nHeight = 0, nBytesPerScan = 0;
+ sal_uInt8 nPlanes, nBitCount;
+
+ pWMF->ReadInt32( nWinROP )
+ .ReadUInt16( nSy ).ReadUInt16( nSx ).ReadUInt16( nSye ).ReadUInt16( nSxe );
+ Point aPoint( ReadYX() );
+ pWMF->ReadUInt16( nDontKnow ).ReadUInt16( nWidth ).ReadUInt16( nHeight ).ReadUInt16( nBytesPerScan ).ReadUChar( nPlanes ).ReadUChar( nBitCount );
+
+ bool bOk = nWidth && nHeight && nPlanes == 1 && nBitCount == 1;
+ if (bOk)
+ {
+ bOk = nBytesPerScan <= pWMF->remainingSize() / nHeight;
+ }
+ if (bOk)
+ {
+ Bitmap aBmp( Size( nWidth, nHeight ), nBitCount );
+ Bitmap::ScopedWriteAccess pAcc(aBmp);
+ if ( pAcc )
+ {
+ for (sal_uInt16 y = 0; y < nHeight && pWMF->good(); ++y)
+ {
+ sal_uInt16 x = 0;
+ for (sal_uInt16 scan = 0; scan < nBytesPerScan; scan++ )
+ {
+ sal_Int8 nEightPixels = 0;
+ pWMF->ReadSChar( nEightPixels );
+ for (sal_Int8 i = 7; i >= 0; i-- )
+ {
+ if ( x < nWidth )
+ {
+ pAcc->SetPixelIndex( y, x, (nEightPixels>>i)&1 );
+ }
+ x++;
+ }
+ }
+ }
+ pAcc.reset();
+ if ( nSye && nSxe &&
+ ( nSx + nSxe <= aBmp.GetSizePixel().Width() ) &&
+ ( nSy + nSye <= aBmp.GetSizePixel().Height() ) )
+ {
+ tools::Rectangle aCropRect( Point( nSx, nSy ), Size( nSxe, nSye ) );
+ aBmp.Crop( aCropRect );
+ }
+ tools::Rectangle aDestRect( aPoint, Size( nSxe, nSye ) );
+ aBmpSaveList.emplace_back(new BSaveStruct(aBmp, aDestRect, nWinROP));
+ }
+ }
+ }
+ break;
+
+ case W_META_STRETCHBLT:
+ case W_META_DIBBITBLT:
+ case W_META_DIBSTRETCHBLT:
+ case W_META_STRETCHDIB:
+ {
+ sal_Int32 nWinROP = 0;
+ sal_uInt16 nSx = 0, nSy = 0, nSxe = 0, nSye = 0, nUsage = 0;
+ Bitmap aBmp;
+
+ pWMF->ReadInt32( nWinROP );
+
+ if( nFunc == W_META_STRETCHDIB )
+ pWMF->ReadUInt16( nUsage );
+
+ // nSye and nSxe 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_STRETCHDIB || nFunc == W_META_STRETCHBLT || nFunc == W_META_DIBSTRETCHBLT )
+ pWMF->ReadUInt16( nSye ).ReadUInt16( nSxe );
+
+ // nSy and nx is the offset of the first pixel
+ pWMF->ReadUInt16( nSy ).ReadUInt16( nSx );
+
+ if( nFunc == W_META_STRETCHDIB || nFunc == W_META_DIBBITBLT || nFunc == W_META_DIBSTRETCHBLT )
+ {
+ if ( nWinROP == PATCOPY )
+ pWMF->ReadUInt16( nUsage ); // i don't know anything of this parameter, so its called nUsage
+ // pOut->DrawRect( Rectangle( ReadYX(), aDestSize ), false );
+
+ Size aDestSize( ReadYXExt() );
+ if ( aDestSize.Width() && aDestSize.Height() ) // #92623# do not try to read buggy bitmaps
+ {
+ tools::Rectangle aDestRect( ReadYX(), aDestSize );
+ if ( nWinROP != PATCOPY )
+ ReadDIB(aBmp, *pWMF, false);
+
+ // test if it is sensible to crop
+ if ( nSye && nSxe &&
+ ( nSx + nSxe <= aBmp.GetSizePixel().Width() ) &&
+ ( nSy + nSye <= aBmp.GetSizePixel().Height() ) )
+ {
+ tools::Rectangle aCropRect( Point( nSx, nSy ), Size( nSxe, nSye ) );
+ aBmp.Crop( aCropRect );
+ }
+ aBmpSaveList.emplace_back(new BSaveStruct(aBmp, aDestRect, nWinROP));
+ }
+ }
+ }
+ break;
+
+ case W_META_DIBCREATEPATTERNBRUSH:
+ {
+ Bitmap aBmp;
+ sal_uInt32 nRed = 0, nGreen = 0, nBlue = 0, nCount = 1;
+ sal_uInt16 nFunction = 0;
+
+ pWMF->ReadUInt16( nFunction ).ReadUInt16( nFunction );
+
+ ReadDIB(aBmp, *pWMF, false);
+ Bitmap::ScopedReadAccess pBmp(aBmp);
+ if ( pBmp )
+ {
+ for ( long y = 0; y < pBmp->Height(); y++ )
+ {
+ for ( 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++;
+ pBmp.reset();
+ }
+ Color aColor( (sal_uInt8)( nRed / nCount ), (sal_uInt8)( nGreen / nCount ), (sal_uInt8)( nBlue / nCount ) );
+ pOut->CreateObject(o3tl::make_unique<WinMtfFillStyle>( aColor, false ));
+ }
+ break;
+
+ case W_META_DELETEOBJECT:
+ {
+ sal_Int16 nIndex = 0;
+ pWMF->ReadInt16( nIndex );
+ pOut->DeleteObject( nIndex );
+ }
+ break;
+
+ case W_META_CREATEPALETTE:
+ {
+ pOut->CreateObject();
+ }
+ break;
+
+ case W_META_CREATEBRUSH:
+ {
+ pOut->CreateObject(o3tl::make_unique<WinMtfFillStyle>( Color( COL_WHITE ), false ));
+ }
+ break;
+
+ case W_META_CREATEPATTERNBRUSH:
+ {
+ pOut->CreateObject(o3tl::make_unique<WinMtfFillStyle>( Color( COL_WHITE ), false ));
+ }
+ break;
+
+ case W_META_CREATEPENINDIRECT:
+ {
+ LineInfo aLineInfo;
+ sal_uInt16 nStyle = 0;
+ sal_uInt16 nWidth = 0;
+ sal_uInt16 nHeight = 0;
+
+ pWMF->ReadUInt16(nStyle);
+ pWMF->ReadUInt16(nWidth);
+ pWMF->ReadUInt16(nHeight);
+
+ if (nWidth > 0)
+ aLineInfo.SetWidth(nWidth);
+
+ bool bTransparent = false;
+
+ switch( nStyle & 0xFF )
+ {
+ case PS_DASHDOTDOT :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 1 );
+ aLineInfo.SetDotCount( 2 );
+ break;
+ case PS_DASHDOT :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 1 );
+ aLineInfo.SetDotCount( 1 );
+ break;
+ case PS_DOT :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 0 );
+ aLineInfo.SetDotCount( 1 );
+ break;
+ case PS_DASH :
+ aLineInfo.SetStyle( LineStyle::Dash );
+ aLineInfo.SetDashCount( 1 );
+ aLineInfo.SetDotCount( 0 );
+ break;
+ case PS_NULL :
+ bTransparent = true;
+ aLineInfo.SetStyle( LineStyle::NONE );
+ break;
+ default :
+ case PS_INSIDEFRAME :
+ case PS_SOLID :
+ aLineInfo.SetStyle( LineStyle::Solid );
+ }
+ switch( nStyle & 0xF00 )
+ {
+ case PS_ENDCAP_ROUND :
+ aLineInfo.SetLineCap( css::drawing::LineCap_ROUND );
+ break;
+ case PS_ENDCAP_SQUARE :
+ aLineInfo.SetLineCap( css::drawing::LineCap_SQUARE );
+ break;
+ case PS_ENDCAP_FLAT :
+ default :
+ aLineInfo.SetLineCap( css::drawing::LineCap_BUTT );
+ }
+ switch( nStyle & 0xF000 )
+ {
+ case PS_JOIN_ROUND :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Round );
+ break;
+ case PS_JOIN_MITER :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Miter );
+ break;
+ case PS_JOIN_BEVEL :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Bevel );
+ break;
+ default :
+ aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::NONE );
+ }
+ pOut->CreateObject(o3tl::make_unique<WinMtfLineStyle>( ReadColor(), aLineInfo, bTransparent ));
+ }
+ break;
+
+ case W_META_CREATEBRUSHINDIRECT:
+ {
+ sal_uInt16 nStyle = 0;
+ pWMF->ReadUInt16( nStyle );
+ pOut->CreateObject(o3tl::make_unique<WinMtfFillStyle>( ReadColor(), ( nStyle == BS_HOLLOW ) ));
+ }
+ 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();
+ pWMF->ReadInt16( lfEscapement );
+ pWMF->ReadInt16( lfOrientation );
+ pWMF->ReadInt16( lfWeight );
+ pWMF->ReadUChar( aLogFont.lfItalic );
+ pWMF->ReadUChar( aLogFont.lfUnderline );
+ pWMF->ReadUChar( aLogFont.lfStrikeOut );
+ pWMF->ReadUChar( aLogFont.lfCharSet );
+ pWMF->ReadUChar( aLogFont.lfOutPrecision );
+ pWMF->ReadUChar( aLogFont.lfClipPrecision );
+ pWMF->ReadUChar( aLogFont.lfQuality );
+ pWMF->ReadUChar( aLogFont.lfPitchAndFamily );
+ size_t nRet = pWMF->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 );
+
+ pOut->CreateObject(o3tl::make_unique<WinMtfFontStyle>( aLogFont ));
+ }
+ break;
+
+ case W_META_CREATEBITMAPINDIRECT:
+ {
+ pOut->CreateObject();
+ }
+ break;
+
+ case W_META_CREATEBITMAP:
+ {
+ pOut->CreateObject();
+ }
+ break;
+
+ case W_META_CREATEREGION:
+ {
+ pOut->CreateObject();
+ }
+ break;
+
+ case W_META_EXCLUDECLIPRECT :
+ {
+ pOut->ExcludeClipRect( ReadRectangle() );
+ }
+ break;
+
+ case W_META_PATBLT:
+ {
+ sal_uInt32 nROP = 0;
+ WMFRasterOp nOldROP = WMFRasterOp::NONE;
+ pWMF->ReadUInt32( nROP );
+ Size aSize = ReadYXExt();
+ nOldROP = pOut->SetRasterOp( (WMFRasterOp)nROP );
+ pOut->DrawRect( tools::Rectangle( ReadYX(), aSize ), false );
+ pOut->SetRasterOp( nOldROP );
+ }
+ break;
+
+ case W_META_SELECTCLIPREGION:
+ {
+ sal_Int16 nObjIndex = 0;
+ pWMF->ReadInt16( nObjIndex );
+ if ( !nObjIndex )
+ {
+ tools::PolyPolygon aEmptyPolyPoly;
+ pOut->SetClipPath( aEmptyPolyPoly, RGN_COPY, true );
+ }
+ }
+ break;
+
+ case W_META_ESCAPE :
+ {
+ // nRecSize has been checked previously to be greater than 3
+ sal_uInt64 nMetaRecSize = static_cast< sal_uInt64 >( nRecSize - 2 ) * 2;
+ sal_uInt64 nMetaRecEndPos = pWMF->Tell() + nMetaRecSize;
+
+ // taking care that nRecSize does not exceed the maximal stream position
+ if ( nMetaRecEndPos > nEndPos )
+ {
+ pWMF->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ break;
+ }
+ if ( nRecSize >= 4 ) // minimal escape length
+ {
+ sal_uInt16 nMode = 0, nLen = 0;
+ pWMF->ReadUInt16( nMode )
+ .ReadUInt16( nLen );
+ if ( ( nMode == W_MFCOMMENT ) && ( nLen >= 4 ) )
+ {
+ sal_uInt32 nNewMagic = 0; // we have to read int32 for
+ pWMF->ReadUInt32( nNewMagic ); // META_ESCAPE_ENHANCED_METAFILE CommentIdentifier
+
+ if( nNewMagic == 0x2c2a4f4f && nLen >= 14 )
+ {
+ sal_uInt16 nMagic2 = 0;
+ pWMF->ReadUInt16( nMagic2 );
+ if( nMagic2 == 0x0a ) // 2nd half of magic
+ { // continue with private escape
+ sal_uInt32 nCheck = 0, nEsc = 0;
+ pWMF->ReadUInt32( nCheck )
+ .ReadUInt32( nEsc );
+
+ sal_uInt32 nEscLen = nLen - 14;
+ if ( nEscLen <= ( nRecSize * 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 ) + pWMF->Tell() ) > nMetaRecEndPos )
+ {
+ pWMF->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ break;
+ }
+ if ( nEscLen > 0 )
+ {
+ pData.reset(new sal_Int8[ nEscLen ]);
+ pWMF->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( pOut->GetFont().GetFamilyName() ) )
+ {
+ Point aPt;
+ OUString aString;
+ sal_uInt32 nStringLen, nDXCount;
+ std::unique_ptr<long[]> pDXAry;
+ 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.X() = nTmpX;
+ aPt.Y() = nTmpY;
+
+ if ( ( static_cast< sal_uInt64 >( nStringLen ) * sizeof( sal_Unicode ) ) < ( nEscLen - aMemoryStream.Tell() ) )
+ {
+
+ 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 )
+ pDXAry.reset(new long[ nDXCount ]);
+ for (sal_uInt32 i = 0; i < nDXCount; i++ )
+ {
+ sal_Int32 val;
+ aMemoryStream.ReadInt32( val);
+ pDXAry[ i ] = val;
+ }
+ aMemoryStream.ReadUInt32( nSkipActions );
+ pOut->DrawText( aPt, aString, pDXAry.get() );
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ else if ( (nNewMagic == static_cast< sal_uInt32 >(0x43464D57)) && (nLen >= 34) && ( (sal_Int32)(nLen + 10) <= (sal_Int32)(nRecSize * 2) ))
+ {
+ sal_uInt32 nComType = 0, nVersion = 0, nFlags = 0, nComRecCount = 0,
+ nCurRecSize = 0, nRemainingSize = 0, nEMFTotalSize = 0;
+ sal_uInt16 nCheck = 0;
+
+ pWMF->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( !nEMFRec )
+ { // first EMF comment
+ nEMFRecCount = nComRecCount;
+ nEMFSize = nEMFTotalSize;
+ if (nEMFSize > pWMF->remainingSize())
+ {
+ SAL_WARN("vcl.wmf", "emf size claims to be larger than remaining data");
+ pEMFStream.reset();
+ }
+ else
+ pEMFStream = o3tl::make_unique<SvMemoryStream>(nEMFSize, 0);
+ }
+ else if( ( nEMFRecCount != nComRecCount ) || ( nEMFSize != nEMFTotalSize ) ) // add additional checks here
+ {
+ // total records should be the same as in previous comments
+ nEMFRecCount = 0xFFFFFFFF;
+ pEMFStream.reset();
+ }
+ nEMFRec++;
+
+ if (pEMFStream && nCurRecSize + 34 > nLen)
+ {
+ nEMFRecCount = 0xFFFFFFFF;
+ pEMFStream.reset();
+ }
+
+ if (pEMFStream && nCurRecSize > pWMF->remainingSize())
+ {
+ SAL_WARN("vcl.wmf", "emf record size claims to be larger than remaining data");
+ nEMFRecCount = 0xFFFFFFFF;
+ pEMFStream.reset();
+ }
+
+ if (pEMFStream)
+ {
+ std::vector<sal_Int8> aBuf(nCurRecSize);
+ sal_uInt32 nCount = pWMF->ReadBytes(aBuf.data(), nCurRecSize);
+ if( nCount == nCurRecSize )
+ pEMFStream->WriteBytes(aBuf.data(), nCount);
+ }
+ }
+ }
+ }
+ }
+ }
+ 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_SELECTPALETTE:
+ 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:
+ break;
+ }
+ }
+
+ static const long aMaxWidth = 1024;
+
+ bool WmfReader::ReadHeader()
+ {
+ sal_uInt64 const nStrmPos = pWMF->Tell();
+
+ sal_uInt32 nPlaceableMetaKey(0);
+ // if available read the METAFILEHEADER
+ pWMF->ReadUInt32( nPlaceableMetaKey );
+ if (!pWMF->good())
+ return false;
+
+ tools::Rectangle aPlaceableBound;
+
+ bool bPlaceable = nPlaceableMetaKey == 0x9ac6cdd7L;
+
+ SAL_INFO("vcl.wmf", "Placeable: \"" << (bPlaceable ? "yes" : "no") << "\"");
+
+ if (bPlaceable)
+ {
+ //TODO do some real error handling here
+ sal_Int16 nVal;
+
+ // Skip reserved bytes
+ pWMF->SeekRel(2);
+
+ // BoundRect
+ pWMF->ReadInt16( nVal );
+ aPlaceableBound.Left() = nVal;
+ pWMF->ReadInt16( nVal );
+ aPlaceableBound.Top() = nVal;
+ pWMF->ReadInt16( nVal );
+ aPlaceableBound.Right() = nVal;
+ pWMF->ReadInt16( nVal );
+ aPlaceableBound.Bottom() = nVal;
+
+ // inch
+ pWMF->ReadUInt16( nUnitsPerInch );
+
+ // reserved
+ pWMF->SeekRel( 4 );
+
+ // Skip and don't check the checksum
+ pWMF->SeekRel( 2 );
+ }
+ else
+ {
+ nUnitsPerInch = 96;
+
+
+ if ( pExternalHeader != nullptr
+ && pExternalHeader->xExt > 0
+ && pExternalHeader->yExt > 0
+ && (pExternalHeader->mapMode == MM_ISOTROPIC || pExternalHeader->mapMode == MM_ANISOTROPIC))
+ {
+ // #n417818#: If we have an external header then overwrite the bounds!
+ tools::Rectangle aExtRect(0, 0,
+ (double) pExternalHeader->xExt * 567 * nUnitsPerInch / 1440000,
+ (double) pExternalHeader->yExt * 567 * nUnitsPerInch / 1440000);
+ aPlaceableBound = aExtRect;
+
+ SAL_INFO("vcl.wmf", "External header size "
+ " t: " << aPlaceableBound.Left() << " l: " << aPlaceableBound.Top()
+ << " b: " << aPlaceableBound.Right() << " r: " << aPlaceableBound.Bottom());
+
+ pOut->SetMapMode( pExternalHeader->mapMode );
+ }
+ else
+ {
+ pWMF->Seek( nStrmPos + 18 ); // set the streampos to the start of the metaactions
+ GetPlaceableBound( aPlaceableBound, pWMF );
+
+ // The image size is not known so normalize the calculated bounds so that the
+ // resulting image is not too big
+ const double fMaxWidth = static_cast<double>(aMaxWidth);
+ if (aPlaceableBound.GetWidth() > aMaxWidth)
+ {
+ double fRatio = aPlaceableBound.GetWidth() / fMaxWidth;
+
+ aPlaceableBound = tools::Rectangle(
+ aPlaceableBound.Left() / fRatio,
+ aPlaceableBound.Top() / fRatio,
+ aPlaceableBound.Right() / fRatio,
+ aPlaceableBound.Bottom() / fRatio);
+
+ SAL_INFO("vcl.wmf", "Placeable bounds "
+ " t: " << aPlaceableBound.Left() << " l: " << aPlaceableBound.Top()
+ << " b: " << aPlaceableBound.Right() << " r: " << aPlaceableBound.Bottom());
+ }
+ }
+
+ pWMF->Seek( nStrmPos );
+ }
+
+ pOut->SetWinOrg( aPlaceableBound.TopLeft() );
+ Size aWMFSize( labs( aPlaceableBound.GetWidth() ), labs( aPlaceableBound.GetHeight() ) );
+ pOut->SetWinExt( aWMFSize );
+
+ SAL_INFO("vcl.wmf", "WMF size w: " << aWMFSize.Width() << " h: " << aWMFSize.Height());
+
+ Size aDevExt( 10000, 10000 );
+ if( ( labs( aWMFSize.Width() ) > 1 ) && ( labs( aWMFSize.Height() ) > 1 ) )
+ {
+ const Fraction aFrac( 1, nUnitsPerInch );
+ MapMode aWMFMap( MapUnit::MapInch, Point(), aFrac, aFrac );
+ Size aSize100( OutputDevice::LogicToLogic( aWMFSize, aWMFMap, MapUnit::Map100thMM ) );
+ aDevExt = Size( labs( aSize100.Width() ), labs( aSize100.Height() ) );
+ }
+ pOut->SetDevExt( aDevExt );
+
+ SAL_INFO("vcl.wmf", "Dev size w: " << aDevExt.Width() << " h: " << aDevExt.Height());
+
+ // read the METAHEADER
+ sal_uInt32 nMetaKey(0);
+ pWMF->ReadUInt32( nMetaKey ); // type and headersize
+ if (!pWMF->good())
+ return false;
+ if (nMetaKey != 0x00090001)
+ {
+ sal_uInt16 aNextWord(0);
+ pWMF->ReadUInt16( aNextWord );
+ if (nMetaKey != 0x10000 || aNextWord != 0x09)
+ {
+ pWMF->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ return false;
+ }
+ }
+
+ pWMF->SeekRel( 2 ); // Version (of Windows)
+ pWMF->SeekRel( 4 ); // Size (of file in words)
+ pWMF->SeekRel( 2 ); // NoObjects (maximum number of simultaneous objects)
+ pWMF->SeekRel( 4 ); // MaxRecord (size of largest record in words)
+ pWMF->SeekRel( 2 ); // NoParameters (Unused
+
+ return pWMF->good();
+ }
+
+ void WmfReader::ReadWMF()
+ {
+ sal_uInt16 nFunction;
+ sal_uLong nPos, nPercent, nLastPercent;
+
+ nSkipActions = 0;
+ nCurrentAction = 0;
+
+ pEMFStream.reset();
+ nEMFRecCount = 0;
+ nEMFRec = 0;
+ nEMFSize = 0;
+
+ pOut->SetMapMode( MM_ANISOTROPIC );
+ pOut->SetWinOrg( Point() );
+ pOut->SetWinExt( Size( 1, 1 ) );
+ pOut->SetDevExt( Size( 10000, 10000 ) );
+
+ nEndPos=pWMF->Seek( STREAM_SEEK_TO_END );
+ pWMF->Seek( nStartPos );
+ Callback( (sal_uInt16) ( nLastPercent = 0 ) );
+
+ if ( ReadHeader( ) )
+ {
+ nPos = pWMF->Tell();
+
+ if( nEndPos - nStartPos )
+ {
+ bool bEMFAvailable = false;
+ while( true )
+ {
+ nCurrentAction++;
+ nPercent = ( nPos - nStartPos ) * 100 / ( nEndPos - nStartPos );
+
+ if( nLastPercent + 4 <= nPercent )
+ {
+ Callback( (sal_uInt16) nPercent );
+ nLastPercent = nPercent;
+ }
+ pWMF->ReadUInt32( nRecSize ).ReadUInt16( nFunction );
+
+ if( pWMF->GetError()
+ || ( nRecSize < 3 )
+ || ( nRecSize == 3
+ && nFunction == 0
+ )
+ || pWMF->IsEof()
+ )
+ {
+ if( pWMF->IsEof() )
+ pWMF->SetError( SVSTREAM_FILEFORMAT_ERROR );
+
+ break;
+ }
+ if ( !bEMFAvailable )
+ {
+ if( !aBmpSaveList.empty()
+ && ( nFunction != W_META_STRETCHDIB )
+ && ( nFunction != W_META_DIBBITBLT )
+ && ( nFunction != W_META_DIBSTRETCHBLT )
+ )
+ {
+ pOut->ResolveBitmapActions( aBmpSaveList );
+ }
+
+ if ( !nSkipActions )
+ ReadRecordParams( nFunction );
+ else
+ nSkipActions--;
+
+ if( pEMFStream && nEMFRecCount == nEMFRec )
+ {
+ GDIMetaFile aMeta;
+ pEMFStream->Seek( 0 );
+ std::unique_ptr<EmfReader> pEMFReader(o3tl::make_unique<EmfReader>( *pEMFStream, aMeta ));
+ bEMFAvailable = pEMFReader->ReadEnhWMF();
+ pEMFReader.reset(); // destroy first!!!
+
+ if( bEMFAvailable )
+ {
+ pOut->AddFromGDIMetaFile( aMeta );
+ pOut->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
+ pEMFStream.reset();
+ }
+ }
+ }
+ const sal_uInt32 nAvailableBytes = nEndPos - nPos;
+ const sal_uInt32 nMaxPossibleRecordSize = nAvailableBytes/2;
+ if (nRecSize <= nMaxPossibleRecordSize)
+ {
+ nPos += nRecSize * 2;
+ pWMF->Seek(nPos);
+ }
+ else
+ pWMF->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ }
+ }
+ else
+ pWMF->SetError( SVSTREAM_GENERALERROR );
+
+ if( !pWMF->GetError() && !aBmpSaveList.empty() )
+ pOut->ResolveBitmapActions( aBmpSaveList );
+ }
+ if ( pWMF->GetError() )
+ pWMF->Seek( nStartPos );
+ }
+
+ void WmfReader::GetPlaceableBound( tools::Rectangle& rPlaceableBound, SvStream* pStm )
+ {
+ bool bRet = true;
+
+ tools::Rectangle aBound;
+ aBound.Left() = RECT_MAX;
+ aBound.Top() = RECT_MAX;
+ aBound.Right() = RECT_MIN;
+ aBound.Bottom() = RECT_MIN;
+ bool bBoundsDetermined = false;
+
+ sal_uInt32 nPos = pStm->Tell();
+ sal_uInt32 nEnd = pStm->Seek( STREAM_SEEK_TO_END );
+
+ pStm->Seek( nPos );
+
+ Point aWinOrg(0,0);
+ boost::optional<Size> aWinExt;
+
+ Point aViewportOrg(0,0);
+ boost::optional<Size> aViewportExt;
+
+ if( nEnd - nPos )
+ {
+ sal_Int16 nMapMode = MM_ANISOTROPIC;
+ sal_uInt16 nFunction;
+ sal_uInt32 nRSize;
+
+ while( bRet )
+ {
+ pStm->ReadUInt32( nRSize ).ReadUInt16( nFunction );
+
+ if( pStm->GetError() )
+ {
+ bRet = false;
+ break;
+ }
+ else if ( nRSize==3 && nFunction==0 )
+ {
+ break;
+ }
+ else if ( nRSize < 3 || pStm->IsEof() )
+ {
+ pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ bRet = false;
+ break;
+ }
+ switch( nFunction )
+ {
+ 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 :
+ pStm->ReadInt16( nMapMode );
+ break;
+
+ case W_META_MOVETO:
+ case W_META_LINETO:
+ GetWinExtMax( ReadYX(), aBound, nMapMode );
+ bBoundsDetermined = true;
+ break;
+
+ case W_META_RECTANGLE:
+ case W_META_INTERSECTCLIPRECT:
+ case W_META_EXCLUDECLIPRECT :
+ case W_META_ELLIPSE:
+ GetWinExtMax( ReadRectangle(), aBound, nMapMode );
+ bBoundsDetermined = true;
+ break;
+
+ case W_META_ROUNDRECT:
+ ReadYXExt(); // size
+ GetWinExtMax( ReadRectangle(), aBound, nMapMode );
+ bBoundsDetermined = true;
+ break;
+
+ case W_META_ARC:
+ case W_META_PIE:
+ case W_META_CHORD:
+ ReadYX(); // end
+ ReadYX(); // start
+ GetWinExtMax( ReadRectangle(), aBound, nMapMode );
+ 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, nMapMode );
+ bBoundsDetermined = true;
+ }
+ }
+
+ SAL_WARN_IF(!bRecordOk, "vcl.wmf", "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, "vcl.wmf", "polypolygon record has more polygons than we can handle");
+
+ bRecordOk = 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, nMapMode );
+ bBoundsDetermined = true;
+ }
+ }
+
+ SAL_WARN_IF(!bRecordOk, "vcl.wmf", "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, nMapMode );
+ bBoundsDetermined = true;
+ }
+ }
+
+ SAL_WARN_IF(!bRecordOk, "vcl.wmf", "polyline record claimed more points than the stream can provide");
+
+ if (!bRecordOk)
+ {
+ pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ bRet = false;
+ break;
+ }
+ }
+ break;
+
+ case W_META_SETPIXEL:
+ {
+ ReadColor();
+ GetWinExtMax( ReadYX(), aBound, nMapMode );
+ bBoundsDetermined = true;
+ }
+ break;
+
+ case W_META_TEXTOUT:
+ {
+ sal_uInt16 nLength;
+ pStm->ReadUInt16( nLength );
+ // todo: we also have to take care of the text width
+ if ( nLength )
+ {
+ pStm->SeekRel( ( nLength + 1 ) &~ 1 );
+ GetWinExtMax( ReadYX(), aBound, nMapMode );
+ bBoundsDetermined = true;
+ }
+ }
+ break;
+
+ case W_META_EXTTEXTOUT:
+ {
+ sal_uInt16 nLen, nOptions;
+ Point aPosition;
+
+ aPosition = ReadYX();
+ pStm->ReadUInt16( nLen ).ReadUInt16( nOptions );
+ // todo: we also have to take care of the text width
+ if( nLen )
+ {
+ GetWinExtMax( aPosition, aBound, nMapMode );
+ bBoundsDetermined = true;
+ }
+ }
+ break;
+ case W_META_BITBLT:
+ case W_META_STRETCHBLT:
+ case W_META_DIBBITBLT:
+ case W_META_DIBSTRETCHBLT:
+ case W_META_STRETCHDIB:
+ {
+ sal_Int32 nWinROP;
+ sal_uInt16 nSx, nSy, nUsage;
+ pStm->ReadInt32( nWinROP );
+
+ if( nFunction == W_META_STRETCHDIB )
+ pStm->ReadUInt16( nUsage );
+
+ // nSye and nSxe is the number of pixels that has to been used
+ if( nFunction == W_META_STRETCHDIB || nFunction == W_META_STRETCHBLT || nFunction == W_META_DIBSTRETCHBLT )
+ {
+ sal_uInt16 nSxe, nSye;
+ pStm->ReadUInt16( nSye ).ReadUInt16( nSxe );
+ }
+
+ // nSy and nx is the offset of the first pixel
+ pStm->ReadUInt16( nSy ).ReadUInt16( nSx );
+
+ if( nFunction == W_META_STRETCHDIB || nFunction == W_META_DIBBITBLT || nFunction == W_META_DIBSTRETCHBLT )
+ {
+ if ( nWinROP == PATCOPY )
+ pStm->ReadUInt16( nUsage ); // i don't know anything of this parameter, so its called nUsage
+ // pOut->DrawRect( Rectangle( ReadYX(), aDestSize ), false );
+
+ Size aDestSize( ReadYXExt() );
+ if ( aDestSize.Width() && aDestSize.Height() ) // #92623# do not try to read buggy bitmaps
+ {
+ tools::Rectangle aDestRect( ReadYX(), aDestSize );
+ GetWinExtMax( aDestRect, aBound, nMapMode );
+ bBoundsDetermined = true;
+ }
+ }
+ }
+ break;
+
+ case W_META_PATBLT:
+ {
+ sal_uInt32 nROP;
+ pStm->ReadUInt32( nROP );
+ Size aSize = ReadYXExt();
+ GetWinExtMax( tools::Rectangle( ReadYX(), aSize ), aBound, nMapMode );
+ bBoundsDetermined = true;
+ }
+ break;
+ }
+
+ const sal_uInt32 nAvailableBytes = nEnd - nPos;
+ const sal_uInt32 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)
+ {
+ if (aWinExt)
+ {
+ rPlaceableBound = tools::Rectangle(aWinOrg, *aWinExt);
+ SAL_INFO("vcl.wmf", "Window dimension "
+ " t: " << rPlaceableBound.Left() << " l: " << rPlaceableBound.Top()
+ << " b: " << rPlaceableBound.Right() << " r: " << rPlaceableBound.Bottom());
+ }
+ else if (aViewportExt)
+ {
+ rPlaceableBound = tools::Rectangle(aViewportOrg, *aViewportExt);
+ SAL_INFO("vcl.wmf", "Viewport dimension "
+ " t: " << rPlaceableBound.Left() << " l: " << rPlaceableBound.Top()
+ << " b: " << rPlaceableBound.Right() << " r: " << rPlaceableBound.Bottom());
+ }
+ else if (bBoundsDetermined)
+ {
+ rPlaceableBound = aBound;
+ SAL_INFO("vcl.wmf", "Determined dimension "
+ " t: " << rPlaceableBound.Left() << " l: " << rPlaceableBound.Top()
+ << " b: " << rPlaceableBound.Right() << " r: " << rPlaceableBound.Bottom());
+ }
+ else
+ {
+ rPlaceableBound.Left() = 0;
+ rPlaceableBound.Top() = 0;
+ rPlaceableBound.Right() = aMaxWidth;
+ rPlaceableBound.Bottom() = aMaxWidth;
+ SAL_INFO("vcl.wmf", "Default dimension "
+ " t: " << rPlaceableBound.Left() << " l: " << rPlaceableBound.Top()
+ << " b: " << rPlaceableBound.Right() << " r: " << rPlaceableBound.Bottom());
+ }
+ }
+ }
+
+ WmfReader::WmfReader(SvStream& rStreamWMF, GDIMetaFile& rGDIMetaFile,
+ FilterConfigItem* pConfigItem, WMF_EXTERNALHEADER* pExtHeader)
+ : MtfTools(rGDIMetaFile, rStreamWMF, pConfigItem)
+ , nUnitsPerInch(96)
+ , nRecSize(0)
+ , nEMFRecCount(0)
+ , nEMFRec(0)
+ , nEMFSize(0)
+ , nSkipActions(0)
+ , nCurrentAction(0)
+ , pExternalHeader(pExtHeader)
+ {}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/dibtools.hxx b/include/vcl/dibtools.hxx
index 6f6752efc08b..fcb3f3671616 100644
--- a/include/vcl/dibtools.hxx
+++ b/include/vcl/dibtools.hxx
@@ -64,7 +64,8 @@ bool VCL_DLLPUBLIC WriteDIBBitmapEx(
const BitmapEx& rSource,
SvStream& rOStm);
-sal_uInt32 getDIBV5HeaderSize();
+// needed for emfio migration
+sal_uInt32 VCL_DLLPUBLIC getDIBV5HeaderSize();
#endif // INCLUDED_VCL_DIBTOOLS_HXX