diff options
Diffstat (limited to '')
-rw-r--r-- | emfio/source/reader/wmfreader.cxx | 2107 |
1 files changed, 2107 insertions, 0 deletions
diff --git a/emfio/source/reader/wmfreader.cxx b/emfio/source/reader/wmfreader.cxx new file mode 100644 index 0000000000..4c74229fc8 --- /dev/null +++ b/emfio/source/reader/wmfreader.cxx @@ -0,0 +1,2107 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <wmfreader.hxx> +#include <emfreader.hxx> + +#include <cstdlib> +#include <memory> +#include <optional> +#include <o3tl/safeint.hxx> +#include <o3tl/sprintf.hxx> +#include <o3tl/unit_conversion.hxx> +#include <rtl/crc.h> +#include <rtl/tencinfo.h> +#include <sal/log.hxx> +#include <osl/endian.h> +#include <vcl/gdimtf.hxx> +#include <vcl/svapp.hxx> +#include <vcl/dibtools.hxx> +#include <vcl/outdev.hxx> +#include <vcl/wmfexternal.hxx> +#include <tools/fract.hxx> +#include <vcl/BitmapReadAccess.hxx> +#include <vcl/BitmapTools.hxx> +#include <osl/thread.h> + +namespace +{ + // MS Windows defines + enum WMFRecords + { + W_META_EOF = 0x0000, + W_META_SETBKCOLOR = 0x0201, + W_META_SETBKMODE = 0x0102, + W_META_SETMAPMODE = 0x0103, + W_META_SETROP2 = 0x0104, + W_META_SETRELABS = 0x0105, + W_META_SETPOLYFILLMODE = 0x0106, + W_META_SETSTRETCHBLTMODE = 0x0107, + W_META_SETTEXTCHAREXTRA = 0x0108, + W_META_SETTEXTCOLOR = 0x0209, + W_META_SETTEXTJUSTIFICATION = 0x020A, + W_META_SETWINDOWORG = 0x020B, + W_META_SETWINDOWEXT = 0x020C, + W_META_SETVIEWPORTORG = 0x020D, + W_META_SETVIEWPORTEXT = 0x020E, + W_META_OFFSETWINDOWORG = 0x020F, + W_META_SCALEWINDOWEXT = 0x0410, + W_META_OFFSETVIEWPORTORG = 0x0211, + W_META_SCALEVIEWPORTEXT = 0x0412, + W_META_LINETO = 0x0213, + W_META_MOVETO = 0x0214, + W_META_EXCLUDECLIPRECT = 0x0415, + W_META_INTERSECTCLIPRECT = 0x0416, + W_META_ARC = 0x0817, + W_META_ELLIPSE = 0x0418, + W_META_FLOODFILL = 0x0419, + W_META_PIE = 0x081A, + W_META_RECTANGLE = 0x041B, + W_META_ROUNDRECT = 0x061C, + W_META_PATBLT = 0x061D, + W_META_SAVEDC = 0x001E, + W_META_SETPIXEL = 0x041F, + W_META_OFFSETCLIPRGN = 0x0220, + W_META_TEXTOUT = 0x0521, + W_META_BITBLT = 0x0922, + W_META_STRETCHBLT = 0x0B23, + W_META_POLYGON = 0x0324, + W_META_POLYLINE = 0x0325, + W_META_ESCAPE = 0x0626, + W_META_RESTOREDC = 0x0127, + W_META_FILLREGION = 0x0228, + W_META_FRAMEREGION = 0x0429, + W_META_INVERTREGION = 0x012A, + W_META_PAINTREGION = 0x012B, + W_META_SELECTCLIPREGION = 0x012C, + W_META_SELECTOBJECT = 0x012D, + W_META_SETTEXTALIGN = 0x012E, + W_META_DRAWTEXT = 0x062F, + W_META_CHORD = 0x0830, + W_META_SETMAPPERFLAGS = 0x0231, + W_META_EXTTEXTOUT = 0x0a32, + W_META_SETDIBTODEV = 0x0d33, + W_META_SELECTPALETTE = 0x0234, + W_META_REALIZEPALETTE = 0x0035, + W_META_ANIMATEPALETTE = 0x0436, + W_META_SETPALENTRIES = 0x0037, + W_META_POLYPOLYGON = 0x0538, + W_META_RESIZEPALETTE = 0x0139, + W_META_DIBBITBLT = 0x0940, + W_META_DIBSTRETCHBLT = 0x0b41, + W_META_DIBCREATEPATTERNBRUSH = 0x0142, + W_META_STRETCHDIB = 0x0f43, + W_META_EXTFLOODFILL = 0x0548, + W_META_RESETDC = 0x014C, + W_META_STARTDOC = 0x014D, + W_META_STARTPAGE = 0x004F, + W_META_ENDPAGE = 0x0050, + W_META_ABORTDOC = 0x0052, + W_META_ENDDOC = 0x005E, + W_META_DELETEOBJECT = 0x01f0, + W_META_CREATEPALETTE = 0x00f7, + W_META_CREATEBRUSH = 0x00F8, + W_META_CREATEPATTERNBRUSH = 0x01F9, + W_META_CREATEPENINDIRECT = 0x02FA, + W_META_CREATEFONTINDIRECT = 0x02FB, + W_META_CREATEBRUSHINDIRECT = 0x02FC, + W_META_CREATEBITMAPINDIRECT = 0x02FD, + W_META_CREATEBITMAP = 0x06FE, + W_META_CREATEREGION = 0x06FF + }; + + void GetWinExtMax(const Point& rSource, tools::Rectangle& rPlaceableBound, emfio::MappingMode eMapMode) + { + Point aSource(rSource); + if (eMapMode == emfio::MappingMode::MM_HIMETRIC) + aSource.setY( -rSource.Y() ); + if (aSource.X() < rPlaceableBound.Left()) + rPlaceableBound.SetLeft( aSource.X() ); + if (aSource.X() > rPlaceableBound.Right()) + rPlaceableBound.SetRight( aSource.X() ); + if (aSource.Y() < rPlaceableBound.Top()) + rPlaceableBound.SetTop( aSource.Y() ); + if (aSource.Y() > rPlaceableBound.Bottom()) + rPlaceableBound.SetBottom( aSource.Y() ); + } + + void GetWinExtMax(const tools::Rectangle& rSource, tools::Rectangle& rPlaceableBound, emfio::MappingMode nMapMode) + { + GetWinExtMax(rSource.TopLeft(), rPlaceableBound, nMapMode); + GetWinExtMax(rSource.BottomRight(), rPlaceableBound, nMapMode); + } + + const char * + record_type_name(sal_uInt16 nRecType) + { + #ifndef SAL_LOG_INFO + (void) nRecType; + return ""; + #else + switch( nRecType ) + { + case W_META_EOF: return "W_META_EOF"; + case W_META_SETBKCOLOR: return "META_SETBKCOLOR"; + case W_META_SETBKMODE: return "META_SETBKMODE"; + case W_META_SETMAPMODE: return "META_SETMAPMODE"; + case W_META_SETROP2: return "META_SETROP2"; + case W_META_SETRELABS: return "META_SETRELABS"; + case W_META_SETPOLYFILLMODE: return "META_SETPOLYFILLMODE"; + case W_META_SETSTRETCHBLTMODE: return "META_SETSTRETCHBLTMODE"; + case W_META_SETTEXTCHAREXTRA: return "META_SETTEXTCHAREXTRA"; + case W_META_SETTEXTCOLOR: return "META_SETTEXTCOLOR"; + case W_META_SETTEXTJUSTIFICATION: return "META_SETTEXTJUSTIFICATION"; + case W_META_SETWINDOWORG: return "META_SETWINDOWORG"; + case W_META_SETWINDOWEXT: return "META_SETWINDOWEXT"; + case W_META_SETVIEWPORTORG: return "META_SETVIEWPORTORG"; + case W_META_SETVIEWPORTEXT: return "META_SETVIEWPORTEXT"; + case W_META_OFFSETWINDOWORG: return "META_OFFSETWINDOWORG"; + case W_META_SCALEWINDOWEXT: return "META_SCALEWINDOWEXT"; + case W_META_OFFSETVIEWPORTORG: return "META_OFFSETVIEWPORTORG"; + case W_META_SCALEVIEWPORTEXT: return "META_SCALEVIEWPORTEXT"; + case W_META_LINETO: return "META_LINETO"; + case W_META_MOVETO: return "META_MOVETO"; + case W_META_EXCLUDECLIPRECT: return "META_EXCLUDECLIPRECT"; + case W_META_INTERSECTCLIPRECT: return "META_INTERSECTCLIPRECT"; + case W_META_ARC: return "META_ARC"; + case W_META_ELLIPSE: return "META_ELLIPSE"; + case W_META_FLOODFILL: return "META_FLOODFILL"; + case W_META_PIE: return "META_PIE"; + case W_META_RECTANGLE: return "META_RECTANGLE"; + case W_META_ROUNDRECT: return "META_ROUNDRECT"; + case W_META_PATBLT: return "META_PATBLT"; + case W_META_SAVEDC: return "META_SAVEDC"; + case W_META_SETPIXEL: return "META_SETPIXEL"; + case W_META_OFFSETCLIPRGN: return "META_OFFSETCLIPRGN"; + case W_META_TEXTOUT: return "META_TEXTOUT"; + case W_META_BITBLT: return "META_BITBLT"; + case W_META_STRETCHBLT: return "META_STRETCHBLT"; + case W_META_POLYGON: return "META_POLYGON"; + case W_META_POLYLINE: return "META_POLYLINE"; + case W_META_ESCAPE: return "META_ESCAPE"; + case W_META_RESTOREDC: return "META_RESTOREDC"; + case W_META_FILLREGION: return "META_FILLREGION"; + case W_META_FRAMEREGION: return "META_FRAMEREGION"; + case W_META_INVERTREGION: return "META_INVERTREGION"; + case W_META_PAINTREGION: return "META_PAINTREGION"; + case W_META_SELECTCLIPREGION: return "META_SELECTCLIPREGION"; + case W_META_SELECTOBJECT: return "META_SELECTOBJECT"; + case W_META_SETTEXTALIGN: return "META_SETTEXTALIGN"; + case W_META_DRAWTEXT: return "META_DRAWTEXT"; + case W_META_CHORD: return "META_CHORD"; + case W_META_SETMAPPERFLAGS: return "META_SETMAPPERFLAGS"; + case W_META_EXTTEXTOUT: return "META_EXTTEXTOUT"; + case W_META_SETDIBTODEV: return "META_SETDIBTODEV"; + case W_META_SELECTPALETTE: return "META_SELECTPALETTE"; + case W_META_REALIZEPALETTE: return "META_REALIZEPALETTE"; + case W_META_ANIMATEPALETTE: return "META_ANIMATEPALETTE"; + case W_META_SETPALENTRIES: return "META_SETPALENTRIES"; + case W_META_POLYPOLYGON: return "META_POLYPOLYGON"; + case W_META_RESIZEPALETTE: return "META_RESIZEPALETTE"; + case W_META_DIBBITBLT: return "META_DIBBITBLT"; + case W_META_DIBSTRETCHBLT: return "META_DIBSTRETCHBLT"; + case W_META_DIBCREATEPATTERNBRUSH: return "META_DIBCREATEPATTERNBRUSH"; + case W_META_STRETCHDIB: return "META_STRETCHDIB"; + case W_META_EXTFLOODFILL: return "META_EXTFLOODFILL"; + case W_META_RESETDC: return "META_RESETDC"; + case W_META_STARTDOC: return "META_STARTDOC"; + case W_META_STARTPAGE: return "META_STARTPAGE"; + case W_META_ENDPAGE: return "META_ENDPAGE"; + case W_META_ABORTDOC: return "META_ABORTDOC"; + case W_META_ENDDOC: return "META_ENDDOC"; + case W_META_DELETEOBJECT: return "META_DELETEOBJECT"; + case W_META_CREATEPALETTE: return "META_CREATEPALETTE"; + case W_META_CREATEBRUSH: return "META_CREATEBRUSH"; + case W_META_CREATEPATTERNBRUSH: return "META_CREATEPATTERNBRUSH"; + case W_META_CREATEPENINDIRECT: return "META_CREATEPENINDIRECT"; + case W_META_CREATEFONTINDIRECT: return "META_CREATEFONTINDIRECT"; + case W_META_CREATEBRUSHINDIRECT: return "META_CREATEBRUSHINDIRECT"; + case W_META_CREATEBITMAPINDIRECT: return "META_CREATEBITMAPINDIRECT"; + case W_META_CREATEBITMAP: return "META_CREATEBITMAP"; + case W_META_CREATEREGION: return "META_CREATEREGION"; + default: + // Yes, return a pointer to a static buffer. This is a very + // local debugging output function, so no big deal. + static char buffer[11]; + o3tl::sprintf(buffer, "0x%08" SAL_PRIxUINT32, sal_uInt32(nRecType)); + return buffer; + } + #endif + } + +} + +namespace emfio +{ + inline Point WmfReader::ReadPoint() + { + short nX = 0, nY = 0; + mpInputStream->ReadInt16( nX ).ReadInt16( nY ); + return Point( nX, nY ); + } + + inline Point WmfReader::ReadYX() + { + short nX = 0, nY = 0; + mpInputStream->ReadInt16( nY ).ReadInt16( nX ); + return Point( nX, nY ); + } + + tools::Rectangle WmfReader::ReadRectangle() + { + Point aBR, aTL; + aBR = ReadYX(); + aTL = ReadYX(); + aBR.AdjustX( -1 ); + aBR.AdjustY( -1 ); + if (aTL.X() > aBR.X() || aTL.Y() > aBR.Y()) + { + SAL_WARN("emfio", "broken rectangle"); + return tools::Rectangle::Normalize(aTL, aBR); + } + return tools::Rectangle( aTL, aBR ); + } + + Size WmfReader::ReadYXExt() + { + short nW=0, nH=0; + mpInputStream->ReadInt16( nH ).ReadInt16( nW ); + return Size( nW, nH ); + } + + void WmfReader::ReadRecordParams( sal_uInt32 nRecordSize, sal_uInt16 nFunc ) + { + bool bRecordOk = true; + SAL_INFO("emfio", "\t" << record_type_name(nFunc)); + switch(nFunc) + { + case W_META_SETBKCOLOR: + { + if (nRecordSize != 5) + bRecordOk = false; + SetBkColor(ReadColor()); + } + break; + + case W_META_SETBKMODE: + { + // It could have Reserved values. Both 4 and 5 sizes are allowed + if ((nRecordSize != 4) && (nRecordSize != 5)) + bRecordOk = false; + sal_uInt16 nDat = 0; + mpInputStream->ReadUInt16( nDat ); + SetBkMode( static_cast<BackgroundMode>(nDat) ); + } + break; + + case W_META_SETMAPMODE: + { + if (nRecordSize != 4) + bRecordOk = false; + sal_uInt16 nMapMode = 0; + mpInputStream->ReadUInt16(nMapMode); + SetMapMode(static_cast<MappingMode>(nMapMode)); + } + break; + + case W_META_SETROP2: + { + // It could have Reserved values. Both 4 and 5 sizes are allowed + if ((nRecordSize != 4) && (nRecordSize != 5)) + bRecordOk = false; + sal_uInt16 nROP2 = 0; + mpInputStream->ReadUInt16(nROP2); + SetRasterOp(static_cast<WMFRasterOp>(nROP2)); + mpInputStream->SeekRel(2); // reserved data + } + break; + + case W_META_SETTEXTCOLOR: + { + if (nRecordSize != 5) + bRecordOk = false; + SetTextColor( ReadColor() ); + } + break; + + case W_META_SETWINDOWORG: + { + if (nRecordSize != 5) + bRecordOk = false; + SetWinOrg( ReadYX() ); + } + break; + + case W_META_SETWINDOWEXT: + { + if (nRecordSize != 5) + bRecordOk = false; + short nWidth = 0, nHeight = 0; + mpInputStream->ReadInt16( nHeight ).ReadInt16( nWidth ); + SetWinExt( Size( nWidth, nHeight ) ); + } + break; + + case W_META_OFFSETWINDOWORG: + { + if (nRecordSize != 5) + bRecordOk = false; + short nXAdd = 0, nYAdd = 0; + mpInputStream->ReadInt16( nYAdd ).ReadInt16( nXAdd ); + SetWinOrgOffset( nXAdd, nYAdd ); + } + break; + + case W_META_SCALEWINDOWEXT: + { + if (nRecordSize != 7) + bRecordOk = false; + short nXNum = 0, nXDenom = 0, nYNum = 0, nYDenom = 0; + mpInputStream->ReadInt16( nYDenom ).ReadInt16( nYNum ).ReadInt16( nXDenom ).ReadInt16( nXNum ); + if (!nYDenom || !nXDenom) + { + mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR ); + break; + } + ScaleWinExt( static_cast<double>(nXNum) / nXDenom, static_cast<double>(nYNum) / nYDenom ); + } + break; + + case W_META_SETVIEWPORTORG: + case W_META_SETVIEWPORTEXT: + { + if (nRecordSize != 5) + bRecordOk = false; + } + break; + + case W_META_OFFSETVIEWPORTORG: + { + if (nRecordSize != 5) + bRecordOk = false; + short nXAdd = 0, nYAdd = 0; + mpInputStream->ReadInt16( nYAdd ).ReadInt16( nXAdd ); + SetDevOrgOffset( nXAdd, nYAdd ); + } + break; + + case W_META_SCALEVIEWPORTEXT: + { + if (nRecordSize != 7) + bRecordOk = false; + short nXNum = 0, nXDenom = 0, nYNum = 0, nYDenom = 0; + mpInputStream->ReadInt16( nYDenom ).ReadInt16( nYNum ).ReadInt16( nXDenom ).ReadInt16( nXNum ); + if (!nYDenom || !nXDenom) + { + mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR ); + break; + } + ScaleDevExt( static_cast<double>(nXNum) / nXDenom, static_cast<double>(nYNum) / nYDenom ); + } + break; + + case W_META_LINETO: + { + if (nRecordSize != 5) + bRecordOk = false; + LineTo( ReadYX() ); + } + break; + + case W_META_MOVETO: + { + if (nRecordSize != 5) + bRecordOk = false; + MoveTo( ReadYX() ); + } + break; + + case W_META_INTERSECTCLIPRECT: + { + if (nRecordSize != 7) + bRecordOk = false; + IntersectClipRect(ReadRectangle()); + } + break; + + case W_META_RECTANGLE: + { + if (nRecordSize != 7) + bRecordOk = false; + DrawRect(ReadRectangle()); + } + break; + + case W_META_ROUNDRECT: + { + if (nRecordSize != 9) + bRecordOk = false; + Size aSize( ReadYXExt() ); + DrawRoundRect( ReadRectangle(), Size( aSize.Width() / 2, aSize.Height() / 2 ) ); + } + break; + + case W_META_ELLIPSE: + { + if (nRecordSize != 7) + bRecordOk = false; + DrawEllipse(ReadRectangle()); + } + break; + + case W_META_ARC: + { + if (nRecordSize != 11) + bRecordOk = false; + Point aEnd( ReadYX() ); + Point aStart( ReadYX() ); + tools::Rectangle aRect( ReadRectangle() ); + aRect.Normalize(); + DrawArc( aRect, aStart, aEnd ); + } + break; + + case W_META_PIE: + { + if (nRecordSize != 11) + bRecordOk = false; + Point aEnd( ReadYX() ); + Point aStart( ReadYX() ); + tools::Rectangle aRect( ReadRectangle() ); + aRect.Normalize(); + + // #i73608# OutputDevice deviates from WMF + // semantics. start==end means full ellipse here. + if( aStart == aEnd ) + DrawEllipse( aRect ); + else + DrawPie( aRect, aStart, aEnd ); + } + break; + + case W_META_CHORD: + { + if (nRecordSize != 11) + bRecordOk = false; + Point aEnd( ReadYX() ); + Point aStart( ReadYX() ); + tools::Rectangle aRect( ReadRectangle() ); + aRect.Normalize(); + DrawChord( aRect, aStart, aEnd ); + } + break; + + case W_META_POLYGON: + { + sal_uInt16 nPoints(0); + mpInputStream->ReadUInt16(nPoints); + + if (nPoints > mpInputStream->remainingSize() / (2 * sizeof(sal_uInt16))) + { + bRecordOk = false; + } + else + { + tools::Polygon aPoly(nPoints); + for (sal_uInt16 i(0); i < nPoints && mpInputStream->good(); ++i) + aPoly[ i ] = ReadPoint(); + DrawPolygon(std::move(aPoly), false/*bRecordPath*/); + } + + SAL_WARN_IF(!bRecordOk, "emfio", "polygon record has more points than we can handle"); + + bRecordOk &= mpInputStream->good(); + } + break; + + case W_META_POLYPOLYGON: + { + sal_uInt16 nPolyCount(0); + // Number of polygons: + mpInputStream->ReadUInt16( nPolyCount ); + if (nPolyCount && mpInputStream->good()) + { + if (nPolyCount > mpInputStream->remainingSize() / sizeof(sal_uInt16)) + break; + + // Number of points of each polygon. Determine total number of points + std::unique_ptr<sal_uInt16[]> xPolygonPointCounts(new sal_uInt16[nPolyCount]); + sal_uInt16* pnPoints = xPolygonPointCounts.get(); + tools::PolyPolygon aPolyPoly(nPolyCount); + sal_uInt16 nPoints = 0; + for (sal_uInt16 a = 0; a < nPolyCount && mpInputStream->good(); ++a) + { + mpInputStream->ReadUInt16( pnPoints[a] ); + + if (pnPoints[a] > SAL_MAX_UINT16 - nPoints) + { + bRecordOk = false; + break; + } + + nPoints += pnPoints[a]; + } + + SAL_WARN_IF(!bRecordOk, "emfio", "polypolygon record has more polygons than we can handle"); + + bRecordOk &= mpInputStream->good(); + + if (!bRecordOk) + break; + + // Polygon points are: + for (sal_uInt16 a = 0; a < nPolyCount && mpInputStream->good(); ++a) + { + const sal_uInt16 nPointCount(pnPoints[a]); + + if (nPointCount > mpInputStream->remainingSize() / (2 * sizeof(sal_uInt16))) + { + bRecordOk = false; + break; + } + + std::unique_ptr<Point[]> xPolygonPoints(new Point[nPointCount]); + Point* pPtAry = xPolygonPoints.get(); + + for(sal_uInt16 b(0); b < nPointCount && mpInputStream->good(); ++b) + { + pPtAry[b] = ReadPoint(); + } + + aPolyPoly.Insert( tools::Polygon(nPointCount, pPtAry) ); + } + + bRecordOk &= mpInputStream->good(); + + if (!bRecordOk) + break; + + DrawPolyPolygon( aPolyPoly ); + } + } + break; + + case W_META_POLYLINE: + { + sal_uInt16 nPoints(0); + mpInputStream->ReadUInt16(nPoints); + + if (nPoints > mpInputStream->remainingSize() / (2 * sizeof(sal_uInt16))) + { + bRecordOk = false; + } + else + { + tools::Polygon aPoly(nPoints); + for (sal_uInt16 i(0); i < nPoints && mpInputStream->good(); ++i) + aPoly[ i ] = ReadPoint(); + DrawPolyLine( std::move(aPoly) ); + } + + SAL_WARN_IF(!bRecordOk, "emfio", "polyline record has more points than we can handle"); + + bRecordOk &= mpInputStream->good(); + } + break; + + case W_META_SAVEDC: + { + if (nRecordSize != 3) + bRecordOk = false; + Push(); + } + break; + + case W_META_RESTOREDC: + { + sal_Int16 nSavedDC(0); + if (nRecordSize != 4) + bRecordOk = false; + mpInputStream->ReadInt16(nSavedDC); + SAL_INFO("emfio", "\t\t SavedDC: " << nSavedDC); + SAL_WARN_IF(nSavedDC < 0, "emfio", "TODO implement relative to the current state"); + Pop(nSavedDC); + } + break; + + case W_META_SETPIXEL: + { + if (nRecordSize != 7) + bRecordOk = false; + const Color aColor = ReadColor(); + DrawPixel( ReadYX(), aColor ); + } + break; + + case W_META_OFFSETCLIPRGN: + { + if (nRecordSize != 5) + bRecordOk = false; + MoveClipRegion( ReadYXExt() ); + } + break; + + case W_META_TEXTOUT: + { + //record is Recordsize, RecordFunction, StringLength, <String>, YStart, XStart + const sal_uInt32 nNonStringLen = sizeof(sal_uInt32) + 4 * sizeof(sal_uInt16); + const sal_uInt32 nRecSize = nRecordSize * 2; + + if (nRecSize < nNonStringLen) + { + bRecordOk = false; + break; + } + + sal_uInt16 nLength = 0; + mpInputStream->ReadUInt16(nLength); + sal_uInt16 nStoredLength = (nLength + 1) &~ 1; + + if (nRecSize - nNonStringLen < nStoredLength) + { + SAL_WARN("emfio", "W_META_TEXTOUT too short, truncating string"); + nLength = nStoredLength = nRecSize - nNonStringLen; + } + + if (nLength) + { + std::vector<char> aChars(nStoredLength); + nLength = std::min<sal_uInt16>(nLength, mpInputStream->ReadBytes(aChars.data(), aChars.size())); + OUString aText(aChars.data(), nLength, GetCharSet()); + Point aPosition( ReadYX() ); + DrawText( aPosition, aText ); + } + } + break; + + case W_META_EXTTEXTOUT: + { + //record is Recordsize, RecordFunction, Y, X, StringLength, options, maybe rectangle, <String> + sal_uInt32 nNonStringLen = sizeof(sal_uInt32) + 5 * sizeof(sal_uInt16); + const sal_uInt32 nRecSize = nRecordSize * 2; + + if (nRecSize < nNonStringLen) + { + bRecordOk = false; + break; + } + + auto nRecordPos = mpInputStream->Tell() - 6; + Point aPosition = ReadYX(); + sal_uInt16 nLen = 0, nOptions = 0; + mpInputStream->ReadUInt16( nLen ).ReadUInt16( nOptions ); + SAL_INFO( "emfio", "\t\t\t Pos: " << aPosition.getX() << ":" << aPosition.getY() << " String Length: " << nLen << " Options: " << nOptions ); + tools::Rectangle aRect; + if ( ( nOptions & ETO_CLIPPED ) || ( nOptions & ETO_OPAQUE ) ) + { + nNonStringLen += 2 * sizeof(sal_uInt16); + + if (nRecSize < nNonStringLen) + { + bRecordOk = false; + break; + } + const Point aTopLeft = ReadPoint(); + const Point aBottomRight = ReadPoint(); + aRect = tools::Rectangle( aTopLeft, aBottomRight ); + if ( nOptions & ETO_OPAQUE ) + DrawRectWithBGColor( aRect ); + SAL_INFO( "emfio", "\t\t\t Rectangle : " << aTopLeft.getX() << ":" << aTopLeft.getY() << ", " << aBottomRight.getX() << ":" << aBottomRight.getY() ); + } + + vcl::text::ComplexTextLayoutFlags nTextLayoutMode = vcl::text::ComplexTextLayoutFlags::Default; + if ( nOptions & ETO_RTLREADING ) + nTextLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft; + SetTextLayoutMode( nTextLayoutMode ); + SAL_WARN_IF( ( nOptions & ( ETO_PDY | ETO_GLYPH_INDEX ) ) != 0, "emfio", "SJ: ETO_PDY || ETO_GLYPH_INDEX in WMF" ); + + // output only makes sense if the text contains characters + if (nLen) + { + sal_Int32 nOriginalTextLen = nLen; + sal_Int32 nOriginalBlockLen = ( nOriginalTextLen + 1 ) &~ 1; + + auto nMaxStreamPos = nRecordPos + nRecSize; + auto nRemainingSize = std::min(mpInputStream->remainingSize(), nMaxStreamPos - mpInputStream->Tell()); + if (nRemainingSize < o3tl::make_unsigned(nOriginalBlockLen)) + { + SAL_WARN("emfio", "exttextout record claimed more data than the stream can provide"); + nOriginalTextLen = nOriginalBlockLen = nRemainingSize; + } + + std::vector<char> pChar(nOriginalBlockLen); + mpInputStream->ReadBytes(pChar.data(), nOriginalBlockLen); + OUString aText(pChar.data(), nOriginalTextLen, GetCharSet()); // after this conversion the text may contain + sal_Int32 nNewTextLen = aText.getLength(); // less character (japanese version), so the + // dxAry will not fit + if ( nNewTextLen ) + { + if ( nOptions & ETO_CLIPPED ) + { + Push(); // Save the current clip. It will be restored after text drawing + IntersectClipRect( aRect ); + } + SAL_INFO( "emfio", "\t\t\t Text : " << aText ); + KernArray aDXAry; + std::unique_ptr<tools::Long[]> pDYAry; + auto nDxArySize = nMaxStreamPos - mpInputStream->Tell(); + auto nDxAryEntries = nDxArySize >> 1; + bool bUseDXAry = false; + + if ( ( ( nDxAryEntries % nOriginalTextLen ) == 0 ) && ( nNewTextLen <= nOriginalTextLen ) ) + { + sal_Int32 i; // needed just outside the for + aDXAry.resize( nNewTextLen ); + if ( nOptions & ETO_PDY ) + { + pDYAry.reset(new tools::Long[ nNewTextLen ]); + } + for (i = 0; i < nNewTextLen; i++ ) + { + if ( mpInputStream->Tell() >= nMaxStreamPos ) + break; + sal_Int32 nDxCount = 1; + if ( nNewTextLen != nOriginalTextLen ) + { + sal_Unicode cUniChar = aText[i]; + OString aTmp(&cUniChar, 1, GetCharSet()); + if ( aTmp.getLength() > 1 ) + { + nDxCount = aTmp.getLength(); + } + } + + sal_Int16 nDx = 0, nDy = 0; + while ( nDxCount-- ) + { + if ( ( mpInputStream->Tell() + 2 ) > nMaxStreamPos ) + break; + sal_Int16 nDxTmp = 0; + mpInputStream->ReadInt16(nDxTmp); + nDx += nDxTmp; + if ( nOptions & ETO_PDY ) + { + if ( ( mpInputStream->Tell() + 2 ) > nMaxStreamPos ) + break; + sal_Int16 nDyTmp = 0; + mpInputStream->ReadInt16(nDyTmp); + nDy += nDyTmp; + } + } + + aDXAry.set(i, nDx); + if ( nOptions & ETO_PDY ) + { + pDYAry[i] = nDy; + } + } + if ( i == nNewTextLen ) + bUseDXAry = true; + } + if ( bUseDXAry ) + DrawText( aPosition, aText, &aDXAry, pDYAry.get() ); + else + DrawText( aPosition, aText ); + if ( nOptions & ETO_CLIPPED ) + Pop(); + } + } + } + break; + + case W_META_SELECTOBJECT: + case W_META_SELECTPALETTE: + { + if (nRecordSize != 4) + bRecordOk = false; + sal_uInt16 nObjIndex = 0; + mpInputStream->ReadUInt16( nObjIndex ); + SelectObject( nObjIndex ); + } + break; + + case W_META_SETTEXTALIGN: + { + // It could have Reserved values. Both 4 and 5 sizes are allowed + if ((nRecordSize != 4) && (nRecordSize != 5)) + bRecordOk = false; + sal_uInt16 nAlign = 0; + mpInputStream->ReadUInt16( nAlign ); + SetTextAlign( nAlign ); + } + break; + + case W_META_BITBLT: + case W_META_STRETCHBLT: + { + sal_uInt32 nRasterOperation = 0; + sal_Int16 nSrcHeight = 0, nSrcWidth = 0, nYSrc, nXSrc, nSye, nSxe, nBitmapType, nWidth, nHeight, nBytesPerScan; + sal_uInt8 nPlanes, nBitCount; + const bool bNoSourceBitmap = ( nRecordSize == ( static_cast< sal_uInt32 >( nFunc ) >> 8 ) + 3 ); + + mpInputStream->ReadUInt32( nRasterOperation ); + SAL_INFO("emfio", "\t\t Raster operation: 0x" << std::hex << nRasterOperation << std::dec << ", No source bitmap: " << bNoSourceBitmap); + + if (nFunc == W_META_STRETCHBLT) + { + if (nRecordSize < 18) + bRecordOk = false; + mpInputStream->ReadInt16(nSrcHeight).ReadInt16(nSrcWidth); + } + else + if (nRecordSize < 16) + bRecordOk = false; + mpInputStream->ReadInt16( nYSrc ).ReadInt16( nXSrc ); + if ( bNoSourceBitmap ) + mpInputStream->SeekRel( 2 ); // Skip Reserved 2 bytes (it must be ignored) + mpInputStream->ReadInt16( nSye ).ReadInt16( nSxe ); + Point aPoint( ReadYX() ); // The upper-left corner of the destination rectangle. + mpInputStream->ReadInt16( nBitmapType ).ReadInt16( nWidth ).ReadInt16( nHeight ).ReadInt16( nBytesPerScan ).ReadUChar( nPlanes ).ReadUChar( nBitCount ); + + SAL_INFO("emfio", "\t\t Bitmap type:" << nBitmapType << " Width:" << nWidth << " Height:" << nHeight << " WidthBytes:" << nBytesPerScan << " Planes: " << static_cast< sal_uInt16 >( nPlanes ) << " BitCount: " << static_cast< sal_uInt16 >( nBitCount ) ); + if (!mpInputStream->good() || bNoSourceBitmap || nBitCount == 4 || nBitCount == 8 || nPlanes != 1) + { + SAL_WARN("emfio", "\t\t TODO The unsupported Bitmap record. Please fill a bug."); + break; + } + bool bOk = nWidth > 0 && nHeight > 0 && nBytesPerScan > 0 + && (nBitCount == 1 || nBitCount == 8 || nBitCount == 24 || nBitCount == 32); + if (bOk) + { + // must be enough data to fulfil the request + bOk = o3tl::make_unsigned( nBytesPerScan ) <= mpInputStream->remainingSize() / nHeight; + } + if (bOk) + { + // scanline must be large enough to provide all pixels + bOk = nBytesPerScan >= nWidth * nBitCount / 8; + } + if (bOk) + { + std::unique_ptr< sal_uInt8[] > pData; + pData.reset( new sal_uInt8[ nHeight * nBytesPerScan ] ); + mpInputStream->ReadBytes( pData.get(), nHeight * nBytesPerScan ); + BitmapEx aBitmap = vcl::bitmap::CreateFromData( pData.get(), nWidth, nHeight, nBytesPerScan, nBitCount, true ); + if ( nSye && nSxe && + ( nXSrc + nSxe <= nWidth ) && + ( nYSrc + nSye <= nHeight ) ) + { + tools::Rectangle aCropRect( Point( nXSrc, nYSrc ), Size( nSxe, nSye ) ); + aBitmap.Crop( aCropRect ); + } + tools::Rectangle aDestRect( aPoint, Size( nSxe, nSye ) ); + maBmpSaveList.emplace_back(aBitmap, aDestRect, nRasterOperation); + } + } + break; + + case W_META_DIBBITBLT: + case W_META_DIBSTRETCHBLT: + case W_META_STRETCHDIB: + { + sal_uInt32 nRasterOperation = 0; + sal_uInt16 nColorUsage = 0; + sal_Int16 nSrcHeight = 0, nSrcWidth = 0, nYSrc = 0, nXSrc = 0; + Bitmap aBmp; + const bool bNoSourceBitmap = ( nFunc != W_META_STRETCHDIB ) && ( nRecordSize == ( ( static_cast< sal_uInt32 >( nFunc ) >> 8 ) + 3 ) ); + + if (nRecordSize < 12) + bRecordOk = false; + mpInputStream->ReadUInt32( nRasterOperation ); + SAL_INFO("emfio", "\t\t Raster operation: 0x" << std::hex << nRasterOperation << std::dec << ", No source bitmap: " << bNoSourceBitmap); + if (nFunc == W_META_STRETCHDIB) + { + if (nRecordSize < 15) + bRecordOk = false; + mpInputStream->ReadUInt16(nColorUsage); + } + + // nSrcHeight and nSrcWidth is the number of pixels that has to been used + // If they are set to zero, it is as indicator not to scale the bitmap later + if( nFunc == W_META_DIBSTRETCHBLT || + nFunc == W_META_STRETCHDIB ) + mpInputStream->ReadInt16( nSrcHeight ).ReadInt16( nSrcWidth ); + + // nYSrc and nXSrc is the offset of the first pixel + mpInputStream->ReadInt16( nYSrc ).ReadInt16( nXSrc ); + + if ( bNoSourceBitmap ) + mpInputStream->SeekRel( 2 ); // Skip Reserved 2 bytes (it must be ignored) + + Size aDestSize( ReadYXExt() ); + if ( aDestSize.Width() && aDestSize.Height() ) // #92623# do not try to read buggy bitmaps + { + tools::Rectangle aDestRect( ReadYX(), aDestSize ); + if ( !bNoSourceBitmap ) + { + // tdf#142625 Read the DIBHeader and check if bitmap is supported + // If bitmap is not supported don't run ReadDIB, as it will interrupt image processing + const auto nOldPos(mpInputStream->Tell()); + sal_uInt32 nHeaderSize(0); + mpInputStream->ReadUInt32( nHeaderSize ); + if ( nHeaderSize == 0xC ) // BitmapCoreHeader + mpInputStream->SeekRel( 6 ); // skip Width (16), Height (16), Planes (16) + else + mpInputStream->SeekRel( 10 ); // skip Width (32), Height (32), Planes (16) + sal_uInt16 nBitCount(0); + mpInputStream->ReadUInt16( nBitCount ); + if ( nBitCount == 0 ) // TODO Undefined BitCount (JPEG/PNG), which are not supported + break; + mpInputStream->Seek(nOldPos); + + if ( !ReadDIB( aBmp, *mpInputStream, false ) ) + SAL_WARN( "emfio", "\tTODO Read DIB failed. Interrupting processing whole image. Please report bug report." ); + } + // test if it is sensible to crop + if ( nSrcHeight && nSrcWidth && + ( nXSrc + nSrcWidth <= aBmp.GetSizePixel().Width() ) && + ( nYSrc + nSrcHeight <= aBmp.GetSizePixel().Height() ) ) + { + tools::Rectangle aCropRect( Point( nXSrc, nYSrc ), Size( nSrcWidth, nSrcHeight ) ); + aBmp.Crop( aCropRect ); + } + + maBmpSaveList.emplace_back(aBmp, aDestRect, nRasterOperation); + } + } + break; + + case W_META_DIBCREATEPATTERNBRUSH: + { + Bitmap aBmp; + sal_uInt32 nRed(0), nGreen(0), nBlue(0), nCount(1); + sal_uInt16 nStyle(0), nColorUsage(0); + + if (nRecordSize < 5) + bRecordOk = false; + mpInputStream->ReadUInt16( nStyle ).ReadUInt16( nColorUsage ); + BrushStyle eStyle = static_cast<BrushStyle>(nStyle); + SAL_INFO( "emfio", "\t\t Style:" << nStyle << ", ColorUsage: " << nColorUsage ); + if ( eStyle == BrushStyle::BS_PATTERN ) // TODO tdf#142625 Add support for pattern + { + SAL_WARN( "emfio", "\tTODO: Pattern brush style is not supported." ); + CreateObject(); + break; + } + if ( !ReadDIB( aBmp, *mpInputStream, false ) ) + SAL_WARN( "emfio", "\tTODO Read DIB failed. Interrupting processing whole image. Please report bug report." ); + if ( !aBmp.IsEmpty() ) + { + BitmapScopedReadAccess pBmp(aBmp); + for ( tools::Long y = 0; y < pBmp->Height(); y++ ) + { + for ( tools::Long x = 0; x < pBmp->Width(); x++ ) + { + const BitmapColor aColor( pBmp->GetColor( y, x ) ); + + nRed += aColor.GetRed(); + nGreen += aColor.GetGreen(); + nBlue += aColor.GetBlue(); + } + } + nCount = pBmp->Height() * pBmp->Width(); + if ( !nCount ) + nCount++; + } + Color aColor( static_cast<sal_uInt8>( nRed / nCount ), static_cast<sal_uInt8>( nGreen / nCount ), static_cast<sal_uInt8>( nBlue / nCount ) ); + CreateObject(std::make_unique<WinMtfFillStyle>( aColor, false )); + } + break; + + case W_META_DELETEOBJECT: + { + if (nRecordSize != 4) + bRecordOk = false; + sal_uInt16 nIndex = 0; + mpInputStream->ReadUInt16( nIndex ); + DeleteObject( nIndex ); + } + break; + + case W_META_CREATEPALETTE: + { + sal_uInt16 nStart = 0; + sal_uInt16 nNumberOfEntries = 0; + mpInputStream->ReadUInt16(nStart); + mpInputStream->ReadUInt16(nNumberOfEntries); + + if (nRecordSize != 2u * nNumberOfEntries + 5u) + bRecordOk = false; + SAL_INFO("emfio", "\t\t Start 0x" << std::hex << nStart << std::dec << ", Number of entries: " << nNumberOfEntries); + sal_uInt32 nPalleteEntry; + std::vector< Color > aPaletteColors; + for (sal_uInt16 i = 0; i < nNumberOfEntries; ++i) + { + //PALETTEENTRY: Values, Blue, Green, Red + mpInputStream->ReadUInt32( nPalleteEntry ); + SAL_INFO("emfio", "\t\t " << i << ". Palette entry: " << std::setw(10) << std::showbase <<std::hex << nPalleteEntry << std::dec ); + aPaletteColors.push_back(Color(static_cast<sal_uInt8>(nPalleteEntry), static_cast<sal_uInt8>(nPalleteEntry >> 8), static_cast<sal_uInt8>(nPalleteEntry >> 16))); + } + CreateObject(std::make_unique<WinMtfPalette>( aPaletteColors )); + } + break; + + case W_META_CREATEBRUSH: + { + SAL_WARN( "emfio", "TODO: Not implemented. Please fill the bug report" ); + CreateObject(std::make_unique<WinMtfFillStyle>( COL_WHITE, false )); + } + break; + + case W_META_CREATEPATTERNBRUSH: + { + SAL_WARN( "emfio", "TODO: Not implemented. Please fill the bug report" ); + CreateObject(std::make_unique<WinMtfFillStyle>( COL_WHITE, false )); + } + break; + + case W_META_CREATEPENINDIRECT: + { + // FIXME For some WMF correct size is 8 and for some 9 + if ((nRecordSize != 8) && (nRecordSize != 9)) + bRecordOk = false; + LineInfo aLineInfo; + sal_uInt16 nStyle = 0; + sal_uInt16 nWidth = 0; + sal_uInt16 nHeight = 0; + + mpInputStream->ReadUInt16(nStyle); + mpInputStream->ReadUInt16(nWidth); + mpInputStream->ReadUInt16(nHeight); + CreateObject(std::make_unique<WinMtfLineStyle>(ReadColor(), nStyle, nWidth)); + } + break; + + case W_META_CREATEBRUSHINDIRECT: + { + if (nRecordSize != 7) + bRecordOk = false; + sal_uInt16 nBrushStyle = 0; + mpInputStream->ReadUInt16( nBrushStyle ); + BrushStyle eBrushStyle = static_cast<BrushStyle>(nBrushStyle); + CreateObject(std::make_unique<WinMtfFillStyle>( ReadColor(), ( eBrushStyle == BrushStyle::BS_NULL ) )); + SAL_WARN_IF( (eBrushStyle != BrushStyle::BS_SOLID) && (eBrushStyle != BrushStyle::BS_NULL), "emfio", "TODO: Brush style not implemented. Please fill the bug report" ); + } + break; + + case W_META_CREATEFONTINDIRECT: + { + Size aFontSize; + char lfFaceName[LF_FACESIZE+1]; + sal_Int16 lfEscapement = 0; + sal_Int16 lfOrientation = 0; + sal_Int16 lfWeight = 0; + + LOGFONTW aLogFont; + aFontSize = ReadYXExt(); + mpInputStream->ReadInt16( lfEscapement ); + mpInputStream->ReadInt16( lfOrientation ); + mpInputStream->ReadInt16( lfWeight ); + mpInputStream->ReadUChar( aLogFont.lfItalic ); + mpInputStream->ReadUChar( aLogFont.lfUnderline ); + mpInputStream->ReadUChar( aLogFont.lfStrikeOut ); + mpInputStream->ReadUChar( aLogFont.lfCharSet ); + mpInputStream->ReadUChar( aLogFont.lfOutPrecision ); + mpInputStream->ReadUChar( aLogFont.lfClipPrecision ); + mpInputStream->ReadUChar( aLogFont.lfQuality ); + mpInputStream->ReadUChar( aLogFont.lfPitchAndFamily ); + size_t nRet = mpInputStream->ReadBytes( lfFaceName, LF_FACESIZE ); + lfFaceName[nRet] = 0; + aLogFont.lfWidth = aFontSize.Width(); + aLogFont.lfHeight = aFontSize.Height(); + aLogFont.lfEscapement = lfEscapement; + aLogFont.lfOrientation = lfOrientation; + aLogFont.lfWeight = lfWeight; + + rtl_TextEncoding eCharSet; + if ( ( aLogFont.lfCharSet == OEM_CHARSET ) || ( aLogFont.lfCharSet == DEFAULT_CHARSET ) ) + eCharSet = osl_getThreadTextEncoding(); + else + eCharSet = rtl_getTextEncodingFromWindowsCharset( aLogFont.lfCharSet ); + if ( eCharSet == RTL_TEXTENCODING_DONTKNOW ) + eCharSet = osl_getThreadTextEncoding(); + if ( eCharSet == RTL_TEXTENCODING_SYMBOL ) + eCharSet = RTL_TEXTENCODING_MS_1252; + size_t nLength = strlen(lfFaceName); + aLogFont.alfFaceName = OUString( lfFaceName, nLength, eCharSet ); + SAL_INFO("emfio", "\tFacename: " << lfFaceName); + + if ((nRecordSize < 12) || (nRecordSize > 28)) + bRecordOk = false; + else + CreateObject(std::make_unique<WinMtfFontStyle>(aLogFont)); + } + break; + + case W_META_CREATEBITMAPINDIRECT: + { + SAL_WARN( "emfio", "TODO: W_META_CREATEBITMAPINDIRECT is not implemented. Please fill the bug report" ); + CreateObject(); + } + break; + + case W_META_CREATEBITMAP: + { + SAL_WARN( "emfio", "TODO: W_META_CREATEBITMAP is not implemented. Please fill the bug report" ); + CreateObject(); + } + break; + + case W_META_CREATEREGION: + { + SAL_WARN( "emfio", "TODO: W_META_CREATEREGION is not implemented. Please fill the bug report" ); + CreateObject(); + } + break; + + case W_META_EXCLUDECLIPRECT: + { + if (nRecordSize != 7) + bRecordOk = false; + SAL_WARN("emfio", "TODO: Not working correctly. Please fill the bug report"); + ExcludeClipRect(ReadRectangle()); + } + break; + + case W_META_PATBLT: + { + if (nRecordSize != 9) + bRecordOk = false; + sal_uInt32 nROP = 0; + mpInputStream->ReadUInt32( nROP ); + Size aSize = ReadYXExt(); + WMFRasterOp nOldROP = SetRasterOp( static_cast<WMFRasterOp>(nROP) ); + DrawRect( tools::Rectangle( ReadYX(), aSize ), false ); + SetRasterOp( nOldROP ); + } + break; + + case W_META_SELECTCLIPREGION: + { + if (nRecordSize != 4) + bRecordOk = false; + sal_uInt16 nObjIndex = 0; + mpInputStream->ReadUInt16( nObjIndex ); + SAL_WARN( "emfio", "TODO: W_META_SELECTCLIPREGION is not implemented. Please fill the bug report" ); + if ( !nObjIndex ) + { + tools::PolyPolygon aEmptyPolyPoly; + SetClipPath( aEmptyPolyPoly, RegionMode::RGN_COPY, true ); + } + } + break; + + case W_META_ESCAPE: + { + sal_uInt64 nMetaRecSize = static_cast<sal_uInt64>(nRecordSize - 2) * 2; + sal_uInt64 nMetaRecEndPos = mpInputStream->Tell() + nMetaRecSize; + + // taking care that nRecordSize does not exceed the maximal stream position + if (nMetaRecEndPos > mnEndPos) + { + mpInputStream->SetError(SVSTREAM_FILEFORMAT_ERROR); + break; + } + sal_uInt16 nMode = 0, nLen = 0; + mpInputStream->ReadUInt16(nMode).ReadUInt16(nLen); + if (nRecordSize != ((nLen + 1u) >> 1u) + 5u) + { + bRecordOk = false; + break; + } + if ((nMode == W_MFCOMMENT) && (nLen >= 4)) + { + sal_uInt32 nNewMagic = 0; // we have to read int32 for + // META_ESCAPE_ENHANCED_METAFILE CommentIdentifier + mpInputStream->ReadUInt32(nNewMagic); + + if (nNewMagic == 0x2c2a4f4f && nLen >= 14) + { + sal_uInt16 nMagic2 = 0; + mpInputStream->ReadUInt16(nMagic2); + if (nMagic2 == 0x0a) // 2nd half of magic + { // continue with private escape + sal_uInt32 nCheck = 0, nEsc = 0; + mpInputStream->ReadUInt32(nCheck).ReadUInt32(nEsc); + + sal_uInt32 nEscLen = nLen - 14; + if (nEscLen <= (nRecordSize * 2)) + { +#ifdef OSL_BIGENDIAN + sal_uInt32 nTmp = OSL_SWAPDWORD(nEsc); + sal_uInt32 nCheckSum = rtl_crc32(0, &nTmp, 4); +#else + sal_uInt32 nCheckSum = rtl_crc32(0, &nEsc, 4); +#endif + std::unique_ptr<sal_Int8[]> pData; + + if ((static_cast<sal_uInt64>(nEscLen) + mpInputStream->Tell()) + > nMetaRecEndPos) + { + mpInputStream->SetError(SVSTREAM_FILEFORMAT_ERROR); + break; + } + if (nEscLen > 0) + { + pData.reset(new sal_Int8[nEscLen]); + mpInputStream->ReadBytes(pData.get(), nEscLen); + nCheckSum = rtl_crc32(nCheckSum, pData.get(), nEscLen); + } + if (nCheck == nCheckSum) + { + switch (nEsc) + { + case PRIVATE_ESCAPE_UNICODE: + { + // we will use text instead of polygons only if we have the correct font + if (Application::GetDefaultDevice()->IsFontAvailable( + GetFont().GetFamilyName())) + { + Point aPt; + sal_uInt32 nStringLen, nDXCount; + KernArray aDXAry; + SvMemoryStream aMemoryStream(nEscLen); + aMemoryStream.WriteBytes(pData.get(), nEscLen); + aMemoryStream.Seek(STREAM_SEEK_TO_BEGIN); + sal_Int32 nTmpX(0), nTmpY(0); + aMemoryStream.ReadInt32(nTmpX) + .ReadInt32(nTmpY) + .ReadUInt32(nStringLen); + aPt.setX(nTmpX); + aPt.setY(nTmpY); + + if ((static_cast<sal_uInt64>(nStringLen) + * sizeof(sal_Unicode)) + < (nEscLen - aMemoryStream.Tell())) + { + OUString aString = read_uInt16s_ToOUString( + aMemoryStream, nStringLen); + aMemoryStream.ReadUInt32(nDXCount); + if ((static_cast<sal_uInt64>(nDXCount) + * sizeof(sal_Int32)) + >= (nEscLen - aMemoryStream.Tell())) + nDXCount = 0; + if (nDXCount) + aDXAry.resize(nDXCount); + for (sal_uInt32 i = 0; i < nDXCount; i++) + { + sal_Int32 val; + aMemoryStream.ReadInt32(val); + aDXAry.set(i, val); + } + aMemoryStream.ReadUInt32(mnSkipActions); + DrawText(aPt, aString, + aDXAry.empty() ? nullptr : &aDXAry); + } + } + } + break; + } + } + } + } + } + else if ((nNewMagic == static_cast<sal_uInt32>(0x43464D57)) && (nLen >= 34) + && (static_cast<sal_Int32>(nLen + 10) + <= static_cast<sal_Int32>(nRecordSize * 2))) + { + sal_uInt32 nComType = 0, nVersion = 0, nFlags = 0, nComRecCount = 0, + nCurRecSize = 0, nRemainingSize = 0, nEMFTotalSize = 0; + sal_uInt16 nCheck = 0; + + mpInputStream->ReadUInt32(nComType) + .ReadUInt32(nVersion) + .ReadUInt16(nCheck) + .ReadUInt32(nFlags) + .ReadUInt32(nComRecCount) + .ReadUInt32(nCurRecSize) + .ReadUInt32(nRemainingSize) + .ReadUInt32( + nEMFTotalSize); // the nRemainingSize is not mentioned in MSDN documentation + // but it seems to be required to read in data produced by OLE + + if (nComType == 0x01 && nVersion == 0x10000 && nComRecCount) + { + if (!mnEMFRec) + { // first EMF comment + mnEMFRecCount = nComRecCount; + mnEMFSize = nEMFTotalSize; + if (mnEMFSize > mpInputStream->remainingSize()) + { + SAL_WARN("emfio", + "emf size claims to be larger than remaining data"); + mpEMFStream.reset(); + } + else + mpEMFStream = std::vector<sal_uInt8>(); + } + else if ((mnEMFRecCount != nComRecCount) + || (mnEMFSize != nEMFTotalSize)) // add additional checks here + { + // total records should be the same as in previous comments + mnEMFRecCount = 0xFFFFFFFF; + mpEMFStream.reset(); + } + mnEMFRec++; + + if (mpEMFStream && nCurRecSize + 34 > nLen) + { + mnEMFRecCount = 0xFFFFFFFF; + mpEMFStream.reset(); + } + + if (mpEMFStream && nCurRecSize > mpInputStream->remainingSize()) + { + SAL_WARN("emfio", + "emf record size claims to be larger than remaining data"); + mnEMFRecCount = 0xFFFFFFFF; + mpEMFStream.reset(); + } + + if (mpEMFStream) + { + std::vector<sal_Int8> aBuf(nCurRecSize); + sal_uInt32 nCount = mpInputStream->ReadBytes(aBuf.data(), nCurRecSize); + if (nCount == nCurRecSize) + { + mpEMFStream->insert(mpEMFStream->end(), aBuf.begin(), aBuf.end()); + } + } + } + } + } + } + break; + + case W_META_SETRELABS: + case W_META_SETPOLYFILLMODE: + case W_META_SETSTRETCHBLTMODE: + case W_META_SETTEXTCHAREXTRA: + case W_META_SETTEXTJUSTIFICATION: + case W_META_FLOODFILL: + case W_META_FILLREGION: + case W_META_FRAMEREGION: + case W_META_INVERTREGION: + case W_META_PAINTREGION: + case W_META_DRAWTEXT: + case W_META_SETMAPPERFLAGS: + case W_META_SETDIBTODEV: + case W_META_REALIZEPALETTE: + case W_META_ANIMATEPALETTE: + case W_META_SETPALENTRIES: + case W_META_RESIZEPALETTE: + case W_META_EXTFLOODFILL: + case W_META_RESETDC: + case W_META_STARTDOC: + case W_META_STARTPAGE: + case W_META_ENDPAGE: + case W_META_ABORTDOC: + case W_META_ENDDOC: + { + SAL_WARN("emfio", "TODO: WMF record not implemented: " << record_type_name(nFunc)); + } + break; + + default: + { + SAL_WARN("emfio", "Unknown Meta Action: 0x" << std::hex << nFunc << std::dec); + } + } + + if (!bRecordOk) + { + SAL_WARN("emfio", "WMF validation failed, record: " << + record_type_name(nFunc) << ", size: " << nRecordSize); + mpInputStream->SetError(SVSTREAM_FILEFORMAT_ERROR); + } + + // tdf#127471 + maScaledFontHelper.applyAlternativeFontScale(); + } + + const tools::Long aMaxWidth = 1024; + + bool WmfReader::ReadHeader() + { + sal_uInt64 const nStrmPos = mpInputStream->Tell(); + + sal_uInt32 nPlaceableMetaKey(0); + // if available read the METAFILEHEADER + mpInputStream->ReadUInt32( nPlaceableMetaKey ); + if (!mpInputStream->good()) + return false; + + tools::Rectangle aPlaceableBound; + + mbPlaceable = nPlaceableMetaKey == 0x9ac6cdd7L; + + SAL_INFO("emfio", "Placeable: \"" << (mbPlaceable ? "yes" : "no") << "\""); + + if (mbPlaceable) + { + //TODO do some real error handling here + sal_Int16 nVal(0); + + // Skip reserved bytes + mpInputStream->SeekRel(2); + + // BoundRect + // These are simply ignored for now + mpInputStream->ReadInt16( nVal ); + aPlaceableBound.SetLeft( nVal ); + mpInputStream->ReadInt16( nVal ); + aPlaceableBound.SetTop( nVal ); + mpInputStream->ReadInt16( nVal ); + aPlaceableBound.SetRight( nVal ); + mpInputStream->ReadInt16( nVal ); + aPlaceableBound.SetBottom( nVal ); + + // inch + mpInputStream->ReadUInt16( mnUnitsPerInch ); + + // reserved + mpInputStream->SeekRel( 4 ); + + // Skip and don't check the checksum + mpInputStream->SeekRel( 2 ); + + // Skip wmf header + mpInputStream->Seek( nStrmPos + 40 ); // set the streampos to the start of the metaactions + GetPlaceableBound( aPlaceableBound, mpInputStream ); + // Go back to the place after placeable header + mpInputStream->Seek( nStrmPos + 22); + } + else + { + // Default is 1440, but it is set to 96 to show the wmf larger + mnUnitsPerInch = 96; + + if (mpExternalHeader != nullptr + && mpExternalHeader->xExt > 0 + && mpExternalHeader->yExt > 0 + && (mpExternalHeader->mapMode == MappingMode::MM_ISOTROPIC || mpExternalHeader->mapMode == MappingMode::MM_ANISOTROPIC)) + { + // #n417818#: If we have an external header then overwrite the bounds! + tools::Rectangle aExtRect(0, 0, + o3tl::convert(mpExternalHeader->xExt, o3tl::Length::mm100, o3tl::Length::px), + o3tl::convert(mpExternalHeader->yExt, o3tl::Length::mm100, o3tl::Length::px)); + aPlaceableBound = aExtRect; + + SAL_INFO("emfio", "External header size " + " left: " << aPlaceableBound.Left() << " top: " << aPlaceableBound.Top() + << " right: " << aPlaceableBound.Right() << " bottom: " << aPlaceableBound.Bottom()); + + SetMapMode(static_cast<MappingMode>(mpExternalHeader->mapMode));; + } + else + { + mpInputStream->Seek(nStrmPos + 18); // set the streampos to the start of the metaactions + GetPlaceableBound(aPlaceableBound, mpInputStream); + + // The image size is not known so normalize the calculated bounds so that the + // resulting image is not too big + if (aPlaceableBound.GetWidth() > aMaxWidth) + { + const double fMaxWidth = static_cast<double>(aMaxWidth); + double fRatio = aPlaceableBound.GetWidth() / fMaxWidth; + + // changing mnUnitsPerInch as a tool to scale wmf + mnUnitsPerInch *= fRatio; + + } + SAL_INFO("emfio", "Placeable bounds " + " left: " << aPlaceableBound.Left() << " top: " << aPlaceableBound.Top() << + " right: " << aPlaceableBound.Right() << " bottom: " << aPlaceableBound.Bottom()); + } + + mpInputStream->Seek( nStrmPos ); + } + + SetWinOrg( aPlaceableBound.TopLeft() ); + Size aWMFSize( + std::abs( aPlaceableBound.GetWidth() ), std::abs( aPlaceableBound.GetHeight() ) ); + SetWinExt( aWMFSize ); + + SAL_INFO("emfio", "WMF size w: " << aWMFSize.Width() << " h: " << aWMFSize.Height()); + + Size aDevExt( 10000, 10000 ); + if( ( std::abs( aWMFSize.Width() ) > 1 ) && ( std::abs( aWMFSize.Height() ) > 1 ) ) + { + const Fraction aFrac( 1, mnUnitsPerInch); + MapMode aWMFMap( MapUnit::MapInch, Point(), aFrac, aFrac ); + Size aSize100(OutputDevice::LogicToLogic(aWMFSize, aWMFMap, MapMode(MapUnit::Map100thMM))); + aDevExt = Size( std::abs( aSize100.Width() ), std::abs( aSize100.Height() ) ); + } + SetDevExt( aDevExt ); + + SAL_INFO("emfio", "Dev size w: " << aDevExt.Width() << " h: " << aDevExt.Height()); + + // read the METAHEADER + sal_uInt32 nMetaKey(0); + mpInputStream->ReadUInt32( nMetaKey ); // type and headersize + if (!mpInputStream->good()) + return false; + if (nMetaKey != 0x00090001) + { + sal_uInt16 aNextWord(0); + mpInputStream->ReadUInt16( aNextWord ); + if (nMetaKey != 0x10000 || aNextWord != 0x09) + { + mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR ); + return false; + } + } + + mpInputStream->SeekRel( 2 ); // Version (of Windows) + mpInputStream->SeekRel( 4 ); // Size (of file in words) + mpInputStream->SeekRel( 2 ); // NoObjects (maximum number of simultaneous objects) + mpInputStream->SeekRel( 4 ); // MaxRecord (size of largest record in words) + mpInputStream->SeekRel( 2 ); // NoParameters (Unused + + return mpInputStream->good(); + } + + void WmfReader::ReadWMF() + { + sal_uInt16 nFunction; + + mnSkipActions = 0; + + mpEMFStream.reset(); + mnEMFRecCount = 0; + mnEMFRec = 0; + mnEMFSize = 0; + + SetMapMode( MappingMode::MM_ANISOTROPIC ); + SetWinOrg( Point() ); + SetWinExt( Size( 1, 1 ) ); + SetDevExt( Size( 10000, 10000 ) ); + + mnEndPos=mpInputStream->TellEnd(); + mpInputStream->Seek( mnStartPos ); + + if ( ReadHeader( ) ) + { + auto nPos = mpInputStream->Tell(); + + if( mnEndPos - mnStartPos ) + { + bool bEMFAvailable = false; + while( !mpInputStream->eof() ) + { + mpInputStream->ReadUInt32(mnRecSize).ReadUInt16( nFunction ); + + if (!mpInputStream->good() || (mnRecSize < 3) || (nFunction == W_META_EOF)) + { + if (mpInputStream->eof()) + mpInputStream->SetError(SVSTREAM_FILEFORMAT_ERROR); + + break; + } + + const sal_uInt32 nAvailableBytes = mnEndPos - nPos; + const sal_uInt32 nMaxPossibleRecordSize = nAvailableBytes/2; + if (mnRecSize > nMaxPossibleRecordSize) + { + mpInputStream->SetError(SVSTREAM_FILEFORMAT_ERROR); + break; + } + + if ( !bEMFAvailable ) + { + if( !maBmpSaveList.empty() + && ( nFunction != W_META_STRETCHDIB ) + && ( nFunction != W_META_DIBBITBLT ) + && ( nFunction != W_META_DIBSTRETCHBLT ) + ) + { + ResolveBitmapActions( maBmpSaveList ); + } + + if ( !mnSkipActions) + ReadRecordParams( mnRecSize, nFunction ); + else + mnSkipActions--; + + if(mpEMFStream && mnEMFRecCount == mnEMFRec) + { + GDIMetaFile aMeta; + SvMemoryStream aStream(mpEMFStream->data(), mpEMFStream->size(), StreamMode::STD_READ); + std::unique_ptr<EmfReader> pEMFReader(std::make_unique<EmfReader>(aStream, aMeta)); + pEMFReader->SetEnableEMFPlus(mbEnableEMFPlus); + bEMFAvailable = pEMFReader->ReadEnhWMF(); + pEMFReader.reset(); // destroy first!!! + + if( bEMFAvailable ) + { + AddFromGDIMetaFile( aMeta ); + SetrclFrame( tools::Rectangle( Point(0, 0), aMeta.GetPrefSize())); + + // the stream needs to be set to the wmf end position, + // otherwise the GfxLink that is created will be incorrect + // (leading to graphic loss after swapout/swapin). + // so we will proceed normally, but are ignoring further wmf + // records + } + else + { + // something went wrong + // continue with WMF, don't try this again + mpEMFStream.reset(); + } + } + } + + nPos += mnRecSize * 2; + mpInputStream->Seek(nPos); + } + } + else + mpInputStream->SetError( SVSTREAM_GENERALERROR ); + + if( !mpInputStream->GetError() && !maBmpSaveList.empty() ) + ResolveBitmapActions( maBmpSaveList ); + } + if ( mpInputStream->GetError() ) + mpInputStream->Seek( mnStartPos ); + } + + void WmfReader::GetPlaceableBound( tools::Rectangle& rPlaceableBound, SvStream* pStm ) + { + bool bRet = true; + + tools::Rectangle aBound; + aBound.SetLeft( RECT_MAX ); + aBound.SetTop( RECT_MAX ); + aBound.SetRight( RECT_MIN ); + aBound.SetBottom( RECT_MIN ); + bool bBoundsDetermined = false; + + auto nPos = pStm->Tell(); + auto nEnd = nPos + pStm->remainingSize(); + + Point aWinOrg(0,0); + std::optional<Size> aWinExt; + + Point aViewportOrg(0,0); + std::optional<Size> aViewportExt; + + MappingMode eMapMode = MappingMode::MM_ANISOTROPIC; + + if (nEnd - nPos) + { + sal_uInt16 nFunction; + sal_uInt32 nRSize; + + while( bRet ) + { + pStm->ReadUInt32( nRSize ).ReadUInt16( nFunction ); + + if( pStm->GetError() ) + { + bRet = false; + break; + } + else if (pStm->eof() || nRSize < 3) + { + pStm->SetError( SVSTREAM_FILEFORMAT_ERROR ); + bRet = false; + break; + } + else if (nFunction == W_META_EOF) + { + 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: + { + sal_uInt16 nMapMode(0); + pStm->ReadUInt16(nMapMode); + eMapMode = static_cast<MappingMode>(nMapMode); + } + break; + + case W_META_MOVETO: + case W_META_LINETO: + { + GetWinExtMax( ReadYX(), aBound, eMapMode ); + bBoundsDetermined = true; + } + break; + + case W_META_RECTANGLE: + case W_META_INTERSECTCLIPRECT: + case W_META_EXCLUDECLIPRECT: + case W_META_ELLIPSE: + { + GetWinExtMax( ReadRectangle(), aBound, eMapMode ); + bBoundsDetermined = true; + } + break; + + case W_META_ROUNDRECT: + { + ReadYXExt(); // size + GetWinExtMax( ReadRectangle(), aBound, eMapMode ); + bBoundsDetermined = true; + } + break; + + case W_META_ARC: + case W_META_PIE: + case W_META_CHORD: + { + ReadYX(); // end + ReadYX(); // start + GetWinExtMax( ReadRectangle(), aBound, eMapMode ); + bBoundsDetermined = true; + } + break; + + case W_META_POLYGON: + { + bool bRecordOk = true; + + sal_uInt16 nPoints(0); + pStm->ReadUInt16( nPoints ); + + if (nPoints > pStm->remainingSize() / (2 * sizeof(sal_uInt16))) + { + bRecordOk = false; + } + else + { + for(sal_uInt16 i = 0; i < nPoints; i++ ) + { + GetWinExtMax( ReadPoint(), aBound, eMapMode ); + bBoundsDetermined = true; + } + } + + bRecordOk &= pStm->good(); + + SAL_WARN_IF(!bRecordOk, "emfio", "polyline record claimed more points than the stream can provide"); + + if (!bRecordOk) + { + pStm->SetError( SVSTREAM_FILEFORMAT_ERROR ); + bRet = false; + break; + } + } + break; + + case W_META_POLYPOLYGON: + { + bool bRecordOk = true; + sal_uInt16 nPoly(0), nPoints(0); + pStm->ReadUInt16(nPoly); + if (nPoly > pStm->remainingSize() / sizeof(sal_uInt16)) + { + bRecordOk = false; + } + else + { + for(sal_uInt16 i = 0; i < nPoly; i++ ) + { + sal_uInt16 nP = 0; + pStm->ReadUInt16( nP ); + if (nP > SAL_MAX_UINT16 - nPoints) + { + bRecordOk = false; + break; + } + nPoints += nP; + } + } + + SAL_WARN_IF(!bRecordOk, "emfio", "polypolygon record has more polygons than we can handle"); + + bRecordOk &= pStm->good(); + + if (!bRecordOk) + { + pStm->SetError( SVSTREAM_FILEFORMAT_ERROR ); + bRet = false; + break; + } + + if (nPoints > pStm->remainingSize() / (2 * sizeof(sal_uInt16))) + { + bRecordOk = false; + } + else + { + for (sal_uInt16 i = 0; i < nPoints; i++ ) + { + GetWinExtMax( ReadPoint(), aBound, eMapMode ); + bBoundsDetermined = true; + } + } + + SAL_WARN_IF(!bRecordOk, "emfio", "polypolygon record claimed more points than the stream can provide"); + + bRecordOk &= pStm->good(); + + if (!bRecordOk) + { + pStm->SetError( SVSTREAM_FILEFORMAT_ERROR ); + bRet = false; + break; + } + } + break; + + case W_META_POLYLINE: + { + bool bRecordOk = true; + + sal_uInt16 nPoints(0); + pStm->ReadUInt16(nPoints); + if (nPoints > pStm->remainingSize() / (2 * sizeof(sal_uInt16))) + { + bRecordOk = false; + } + else + { + for (sal_uInt16 i = 0; i < nPoints; ++i) + { + GetWinExtMax( ReadPoint(), aBound, eMapMode ); + bBoundsDetermined = true; + } + } + + SAL_WARN_IF(!bRecordOk, "emfio", "polyline record claimed more points than the stream can provide"); + + bRecordOk &= pStm->good(); + + if (!bRecordOk) + { + pStm->SetError( SVSTREAM_FILEFORMAT_ERROR ); + bRet = false; + break; + } + } + break; + + case W_META_SETPIXEL: + { + ReadColor(); + GetWinExtMax( ReadYX(), aBound, eMapMode ); + bBoundsDetermined = true; + } + break; + + case W_META_TEXTOUT: + { + sal_uInt16 nLength(0); + pStm->ReadUInt16( nLength ); + // todo: we also have to take care of the text width + if ( nLength ) + { + pStm->SeekRel( ( nLength + 1 ) &~ 1 ); + GetWinExtMax( ReadYX(), aBound, eMapMode ); + bBoundsDetermined = true; + } + } + break; + + case W_META_EXTTEXTOUT: + { + sal_uInt16 nLen(0), nOptions; + Point aPosition = ReadYX(); + pStm->ReadUInt16( nLen ).ReadUInt16( nOptions ); + // todo: we also have to take care of the text width + if( nLen ) + { + GetWinExtMax( aPosition, aBound, eMapMode ); + bBoundsDetermined = true; + } + } + break; + case W_META_BITBLT: + case W_META_DIBBITBLT: + case W_META_DIBSTRETCHBLT: + case W_META_STRETCHBLT: + case W_META_STRETCHDIB: + { + sal_uInt32 nRasterOperation; + sal_Int16 nYSrc, nXSrc; + sal_uInt16 nColorUsage; + pStm->ReadUInt32( nRasterOperation ); + + if( nFunction == W_META_STRETCHDIB ) + pStm->ReadUInt16( nColorUsage ); + + if( nFunction == W_META_DIBSTRETCHBLT || + nFunction == W_META_STRETCHBLT || + nFunction == W_META_STRETCHDIB ) + { + sal_Int16 nSrcHeight, nSrcWidth; + pStm->ReadInt16( nSrcHeight ).ReadInt16( nSrcWidth ); + } + + // nYSrc and nXSrc is the offset of the first pixel + pStm->ReadInt16( nYSrc ).ReadInt16( nXSrc ); + + const bool bNoSourceBitmap = ( nFunction != W_META_STRETCHDIB ) && ( nRSize == ( ( static_cast< sal_uInt32 >( nFunction ) >> 8 ) + 3 ) ); + if ( bNoSourceBitmap ) + mpInputStream->SeekRel( 2 ); // Skip Reserved 2 bytes (it must be ignored) + + Size aDestSize( ReadYXExt() ); + if ( aDestSize.Width() && aDestSize.Height() ) // #92623# do not try to read buggy bitmaps + { + tools::Rectangle aDestRect( ReadYX(), aDestSize ); + GetWinExtMax( aDestRect, aBound, eMapMode ); + bBoundsDetermined = true; + } + } + break; + + case W_META_PATBLT: + { + sal_uInt32 nROP(0); + pStm->ReadUInt32( nROP ); + Size aSize = ReadYXExt(); + GetWinExtMax( tools::Rectangle( ReadYX(), aSize ), aBound, eMapMode ); + bBoundsDetermined = true; + } + break; + } + + const auto nAvailableBytes = nEnd - nPos; + const auto nMaxPossibleRecordSize = nAvailableBytes/2; + if (nRSize <= nMaxPossibleRecordSize) + { + nPos += nRSize * 2; + pStm->Seek(nPos); + } + else + { + pStm->SetError( SVSTREAM_FILEFORMAT_ERROR ); + bRet = false; + } + } + } + else + { + pStm->SetError( SVSTREAM_GENERALERROR ); + bRet = false; + } + if (!bRet) + { + SAL_WARN("emfio", "Unable to calculate Placeable Bounds"); + return; + } + + if (aWinExt) + { + rPlaceableBound = tools::Rectangle(aWinOrg, *aWinExt); + if (mbPlaceable && eMapMode == MM_ANISOTROPIC) + { + // It seems that (in MM_ANISOTROPIC WMFs) the "inch" field (PPI) in META_PLACEABLE is + // ignored and instead competitor office suites decide what it should be arbitrarily + // Could have to do with MM_ANISOTROPICs definition: + // Logical units are mapped to arbitrary units with arbitrarily scaled axes. + // The issue is that when PPI is bigger than the window size, the image appears + // tiny (smaller than an inch squared). + // A solution is to scale PPI down in such images to an arbitrary amount that makes + // the image visible: + auto nWidth = rPlaceableBound.GetWidth(); + auto nHeight = rPlaceableBound.GetHeight(); + if (mnUnitsPerInch > nWidth && mnUnitsPerInch > nHeight) + mnUnitsPerInch = std::max(nWidth, nHeight); + } + SAL_INFO("emfio", "Window dimension " + " left: " << rPlaceableBound.Left() << " top: " << rPlaceableBound.Top() + << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom()); + } + else if (aViewportExt) + { + rPlaceableBound = tools::Rectangle(aViewportOrg, *aViewportExt); + SAL_INFO("emfio", "Viewport dimension " + " left: " << rPlaceableBound.Left() << " top: " << rPlaceableBound.Top() + << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom()); + } + else if (bBoundsDetermined) + { + rPlaceableBound = aBound; + SAL_INFO("emfio", "Determined dimension " + " left: " << rPlaceableBound.Left() << " top: " << rPlaceableBound.Top() + << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom()); + } + else + { + rPlaceableBound.SetLeft( 0 ); + rPlaceableBound.SetTop( 0 ); + rPlaceableBound.SetRight( aMaxWidth ); + rPlaceableBound.SetBottom( aMaxWidth ); + SAL_INFO("emfio", "Default dimension " + " left: " << rPlaceableBound.Left() << " top: " << rPlaceableBound.Top() + << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom()); + } + } + + WmfReader::WmfReader(SvStream& rStreamWMF, GDIMetaFile& rGDIMetaFile, const WmfExternal* pExternalHeader) + : MtfTools(rGDIMetaFile, rStreamWMF) + , mnUnitsPerInch(96) + , mnRecSize(0) + , mbPlaceable(false) + , mnEMFRecCount(0) + , mnEMFRec(0) + , mnEMFSize(0) + , mnSkipActions(0) + , mpExternalHeader(pExternalHeader) + { + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |