diff options
Diffstat (limited to 'emfio')
-rw-r--r-- | emfio/Library_emfio.mk | 8 | ||||
-rw-r--r-- | emfio/inc/emfreader.hxx | 55 | ||||
-rw-r--r-- | emfio/inc/mtftools.hxx | 667 | ||||
-rw-r--r-- | emfio/inc/wmfreader.hxx | 106 | ||||
-rw-r--r-- | emfio/source/emfuno/xemfparser.cxx | 101 | ||||
-rw-r--r-- | emfio/source/reader/emfreader.cxx | 1940 | ||||
-rw-r--r-- | emfio/source/reader/mtftools.cxx | 2283 | ||||
-rw-r--r-- | emfio/source/reader/wmfreader.cxx | 1846 |
8 files changed, 6955 insertions, 51 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: */ |