/* -*- 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 <sal/log.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <vcl/dibtools.hxx>
#include <o3tl/make_unique.hxx>
#include <o3tl/safeint.hxx>
#include <tools/stream.hxx>
#include <memory>
#ifdef DBG_UTIL
#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
}
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 (!rStream.good() || nCount == 0 || nType != RDH_RECTANGLES)
return false;
sal_uInt32 nSize;
if (o3tl::checked_multiply<sal_uInt32>(nCount, 16, nSize))
return false;
if (o3tl::checked_add<sal_uInt32>(nSize, nHdSize - 16, nSize))
return false;
if (nLen < nSize)
return false;
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;
}
} // anonymous namespace
namespace emfio
{
EmfReader::EmfReader(SvStream& rStream,GDIMetaFile& rGDIMetaFile)
: MtfTools(rGDIMetaFile, rStream)
, mnRecordCount(0)
, mbRecordPath(false)
, mbEMFPlus(false)
,mbEMFPlusDualMode(false)
{
}
EmfReader::~EmfReader()
{
}
void EmfReader::ReadEMFPlusComment(sal_uInt32 length, bool& bHaveDC)
{
if (!mbEMFPlus)
{
PassEMFPlusHeaderInfo();
#if OSL_DEBUG_LEVEL > 1
// debug code - write the stream to debug file /tmp/emf-stream.emf
sal_uInt64 const pos = mpInputStream->Tell();
mpInputStream->Seek(0);
SvFileStream file( OUString( "/tmp/emf-stream.emf" ), StreamMode::WRITE | StreamMode::TRUNC );
mpInputStream->WriteStream(file);
file.Flush();
file.Close();
mpInputStream->Seek( pos );
#endif
}
mbEMFPlus = true;
sal_uInt64 const pos = mpInputStream->Tell();
void *buffer = malloc( length );
PassEMFPlus( buffer, mpInputStream->ReadBytes(buffer, length) );
free( buffer );
mpInputStream->Seek( pos );
bHaveDC = false;
// skip in SeekRel if impossibly unavailable
sal_uInt32 nRemainder = length;
const size_t nRequiredHeaderSize = 12;
while (nRemainder >= nRequiredHeaderSize)
{
sal_uInt16 type(0), flags(0);
sal_uInt32 size(0), dataSize(0);
mpInputStream->ReadUInt16( type ).ReadUInt16( flags ).ReadUInt32( size ).ReadUInt32( dataSize );
nRemainder -= nRequiredHeaderSize;
SAL_INFO ("emfio", "\t\tEMF+ record type: " << std::hex << type << std::dec);
// Get Device Context
// TODO We should use EmfPlusRecordType::GetDC instead
if( type == 0x4004 )
{
bHaveDC = true;
SAL_INFO ("emfio", "\t\tEMF+ lock DC (device context)");
}
// look for the "dual mode" in header
// it indicates that either EMF or EMF+ records should be processed
// 0x4001 = EMF+ header
// flags & 1 = dual mode active
if ( type == 0x4001 && flags & 1 )
{
mbEMFPlusDualMode = true;
}
// Get the length of the remaining data of this record based
// on the alleged size
sal_uInt32 nRemainingRecordData = size >= nRequiredHeaderSize ?
size-nRequiredHeaderSize : 0;
// clip to available size
nRemainingRecordData = std::min(nRemainingRecordData, nRemainder);
mpInputStream->SeekRel(nRemainingRecordData);
nRemainder -= nRemainingRecordData;
}
mpInputStream->SeekRel(nRemainder);
}
// these are referenced from inside the templates
SvStream& operator >> (SvStream& rStream, sal_Int16 &n)
{
return rStream.ReadInt16(n);
}
SvStream& operator >> (SvStream& rStream, sal_Int32 &n)
{
return rStream.ReadInt32(n);
}
/**
* Reads polygons from the stream.
* The \<class T> parameter is for the type of the points (sal_uInt32 or sal_uInt16).
* skipFirst: if the first point read is the 0th point or the 1st point in the array.
* */
template <class T>
tools::Polygon EmfReader::ReadPolygonWithSkip(const bool skipFirst, sal_uInt32 nNextPos)
{
sal_uInt32 nPoints(0), nStartIndex(0);
mpInputStream->SeekRel( 16 );
mpInputStream->ReadUInt32( nPoints );
if (skipFirst)
{
nPoints ++;
nStartIndex ++;
}
return ReadPolygon<T>(nStartIndex, nPoints, nNextPos);
}
/**
* Reads polygons from the stream.
* The \<class T> parameter is for the type of the points
* nStartIndex: which is the starting index in the polygon of the first point read
* nPoints: number of points
* mpInputStream: the stream containing the polygons
* */
template <class T>
tools::Polygon EmfReader::ReadPolygon(sal_uInt32 nStartIndex, sal_uInt32 nPoints, sal_uInt32 nNextPos)
{
bool bRecordOk = nPoints <= SAL_MAX_UINT16;
SAL_WARN_IF(!bRecordOk, "emfio", "polygon record has more polygons than we can handle");
if (!bRecordOk || !nPoints)
return tools::Polygon();
auto nRemainingSize = std::min(nNextPos - mpInputStream->Tell(), mpInputStream->remainingSize());
auto nMaxPossiblePoints = nRemainingSize / (sizeof(T) * 2);
auto nPointCount = nPoints - nStartIndex;
if (nPointCount > nMaxPossiblePoints)
{
SAL_WARN("emfio", "polygon claims more points than record can provide, truncating");
nPoints = nMaxPossiblePoints + nStartIndex;
}
tools::Polygon aPolygon(nPoints);
for (sal_uInt32 i = nStartIndex ; i < nPoints && mpInputStream->good(); i++ )
{
T nX, nY;
*mpInputStream >> nX >> nY;
if (!mpInputStream->good())
{
SAL_WARN("emfio", "short read on polygon, truncating");
aPolygon.SetSize(i);
break;
}
aPolygon[ i ] = Point( nX, nY );
}
return aPolygon;
}
/**
* Reads a polyline from the WMF file and draws it
* The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
* */
template <class T>
void EmfReader::ReadAndDrawPolyLine(sal_uInt32 nNextPos)
{
sal_uInt32 nPoints;
sal_uInt32 i, nNumberOfPolylines( 0 ), nCount( 0 );
mpInputStream->SeekRel( 0x10 ); // TODO Skipping Bounds. A 128-bit WMF RectL object (specifies the bounding rectangle in device units.)
mpInputStream->ReadUInt32( nNumberOfPolylines );
mpInputStream->ReadUInt32( nCount ); // total number of points in all polylines
const auto nEndPos = std::min(nNextPos, mnEndPos);
if (mpInputStream->Tell() >= nEndPos)
return;
// taking the amount of points of each polygon, retrieving the total number of points
if ( mpInputStream->good() &&
( nNumberOfPolylines < SAL_MAX_UINT32 / sizeof( sal_uInt16 ) ) &&
( nNumberOfPolylines * sizeof( sal_uInt16 ) ) <= ( nEndPos - mpInputStream->Tell() )
)
{
std::unique_ptr< sal_uInt32[] > pnPolylinePointCount( new sal_uInt32[ nNumberOfPolylines ] );
for ( i = 0; i < nNumberOfPolylines && mpInputStream->good(); i++ )
{
mpInputStream->ReadUInt32( nPoints );
pnPolylinePointCount[ i ] = nPoints;
}
// Get polyline points:
for ( i = 0; ( i < nNumberOfPolylines ) && mpInputStream->good(); i++ )
{
tools::Polygon aPolygon = ReadPolygon<T>(0, pnPolylinePointCount[i], nNextPos);
DrawPolyLine(aPolygon, false, mbRecordPath);
}
}
}
/**
* Reads a poly polygon from the WMF file and draws it.
* The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
* */
template <class T>
void EmfReader::ReadAndDrawPolyPolygon(sal_uInt32 nNextPos)
{
sal_uInt32 nPoly(0), nGesPoints(0), nReadPoints(0);
mpInputStream->SeekRel( 0x10 );
// Number of polygons
mpInputStream->ReadUInt32( nPoly ).ReadUInt32( nGesPoints );
const auto nEndPos = std::min(nNextPos, mnEndPos);
if (mpInputStream->Tell() >= nEndPos)
return;
if (!mpInputStream->good())
return;
//check against numeric overflowing
if (nGesPoints >= SAL_MAX_UINT32 / sizeof(Point))
return;
if (nPoly >= SAL_MAX_UINT32 / sizeof(sal_uInt16))
return;
if (nPoly * sizeof(sal_uInt16) > nEndPos - mpInputStream->Tell())
return;
// Get number of points in each polygon
std::vector<sal_uInt16> aPoints(nPoly);
for (sal_uInt32 i = 0; i < nPoly && mpInputStream->good(); ++i)
{
sal_uInt32 nPoints(0);
mpInputStream->ReadUInt32( nPoints );
aPoints[i] = static_cast<sal_uInt16>(nPoints);
}
if ( mpInputStream->good() && ( nGesPoints * (sizeof(T)+sizeof(T)) ) <= ( nEndPos - mpInputStream->Tell() ) )
{
// Get polygon points
tools::PolyPolygon aPolyPoly(nPoly);
for (sal_uInt32 i = 0; i < nPoly && mpInputStream->good(); ++i)
{
const sal_uInt16 nPointCount(aPoints[i]);
std::vector<Point> aPtAry(nPointCount);
for (sal_uInt16 j = 0; j < nPointCount && mpInputStream->good(); ++j)
{
T nX(0), nY(0);
*mpInputStream >> nX >> nY;
aPtAry[j] = Point( nX, nY );
++nReadPoints;
}
aPolyPoly.Insert(tools::Polygon(aPtAry.size(), aPtAry.data()));
}
DrawPolyPolygon(aPolyPoly, mbRecordPath);
}
OSL_ENSURE(nReadPoints == nGesPoints, "The number Points processed from EMR_POLYPOLYGON is unequal imported number (!)");
}
bool EmfReader::ReadEnhWMF()
{
sal_uInt32 nStretchBltMode = 0;
sal_uInt32 nNextPos(0),
nW(0), nH(0), nColor(0), nIndex(0),
nDat32(0), nNom1(0), nDen1(0), nNom2(0), nDen2(0);
sal_Int32 nX32(0), nY32(0), nx32(0), ny32(0);
bool bStatus = ReadHeader();
bool bHaveDC = false;
static bool bEnableEMFPlus = ( getenv( "EMF_PLUS_DISABLE" ) == nullptr );
while( bStatus && mnRecordCount-- && mpInputStream->good())
{
sal_uInt32 nRecType(0), nRecSize(0);
mpInputStream->ReadUInt32(nRecType).ReadUInt32(nRecSize);
if ( !mpInputStream->good() || ( nRecSize < 8 ) || ( nRecSize & 3 ) ) // Parameters are always divisible by 4
{
bStatus = false;
break;
}
auto nCurPos = mpInputStream->Tell();
if (mnEndPos < nCurPos - 8)
{
bStatus = false;
break;
}
const sal_uInt32 nMaxPossibleRecSize = mnEndPos - (nCurPos - 8);
if (nRecSize > nMaxPossibleRecSize)
{
bStatus = false;
break;
}
nNextPos = nCurPos + (nRecSize - 8);
if( !maBmpSaveList.empty()
&& ( nRecType != EMR_STRETCHBLT )
&& ( nRecType != EMR_STRETCHDIBITS )
) {
ResolveBitmapActions( maBmpSaveList );
}
bool bFlag = false;
SAL_INFO ("emfio", "0x" << std::hex << (nNextPos - nRecSize) << "-0x" << nNextPos << " " << record_type_name(nRecType) << " size: " << nRecSize << std::dec);
if( bEnableEMFPlus && nRecType == EMR_COMMENT ) {
sal_uInt32 length;
mpInputStream->ReadUInt32( length );
SAL_INFO("emfio", "\tGDI comment, length: " << length);
if( mpInputStream->good() && length >= 4 && length <= mpInputStream->remainingSize() ) {
sal_uInt32 nCommentId;
mpInputStream->ReadUInt32( nCommentId );
SAL_INFO ("emfio", "\t\tbegin " << static_cast<char>(nCommentId & 0xff) << static_cast<char>((nCommentId & 0xff00) >> 8) << static_cast<char>((nCommentId & 0xff0000) >> 16) << static_cast<char>((nCommentId & 0xff000000) >> 24) << " id: 0x" << std::hex << nCommentId << std::dec);
if( nCommentId == EMR_COMMENT_EMFPLUS && nRecSize >= 12 )
{
// [MS-EMF] 2.3.3: DataSize includes both CommentIdentifier and CommentRecordParm fields.
// We have already read 4-byte CommentIdentifier, so reduce length appropriately
ReadEMFPlusComment( length-4, bHaveDC );
}
else if( nCommentId == EMR_COMMENT_PUBLIC && nRecSize >= 12 )
{
// TODO: ReadGDIComment()
}
else if( nCommentId == EMR_COMMENT_EMFSPOOL && nRecSize >= 12 )
{
// TODO Implement reading EMFSPOOL comment
}
else
{
SAL_INFO ("emfio", "\t\tunknown id: 0x" << std::hex << nCommentId << std::dec);
}
}
}
else if ( !bHaveDC && mbEMFPlusDualMode && nRecType != EMR_HEADER && nRecType != EMR_EOF )
{
// skip content (EMF record) in dual mode
// we process only EMR_COMMENT (see above) to access EMF+ data
// with 2 exceptions, according to EMF+ specification:
// EMR_HEADER and EMR_EOF
// if a device context is given (bHaveDC) process the following EMF record, too.
}
else if( !mbEMFPlus || bHaveDC || nRecType == EMR_EOF )
{
switch( nRecType )
{
case EMR_POLYBEZIERTO :
DrawPolyBezier(ReadPolygonWithSkip<sal_Int32>(true, nNextPos), true, mbRecordPath);
break;
case EMR_POLYBEZIER :
DrawPolyBezier(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), false, mbRecordPath);
break;
case EMR_POLYGON :
DrawPolygon(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), mbRecordPath);
break;
case EMR_POLYLINETO :
DrawPolyLine(ReadPolygonWithSkip<sal_Int32>(true, nNextPos), true, mbRecordPath);
break;
case EMR_POLYLINE :
DrawPolyLine(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), false, mbRecordPath);
break;
case EMR_POLYPOLYLINE :
ReadAndDrawPolyLine<sal_Int32>(nNextPos);
break;
case EMR_POLYPOLYGON :
ReadAndDrawPolyPolygon<sal_Int32>(nNextPos);
break;
case EMR_SETWINDOWEXTEX :
{
sal_Int32 w = 0, h = 0;
mpInputStream->ReadInt32( w ).ReadInt32( h );
SetWinExt( Size( w, h ), true);
}
break;
case EMR_SETWINDOWORGEX :
{
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
SetWinOrg( Point( nX32, nY32 ), true);
}
break;
case EMR_SCALEWINDOWEXTEX :
{
mpInputStream->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
if (nDen1 != 0 && nDen2 != 0)
ScaleWinExt( static_cast<double>(nNom1) / nDen1, static_cast<double>(nNom2) / nDen2 );
else
SAL_WARN("vcl.emf", "ignoring bogus divide by zero");
}
break;
case EMR_SETVIEWPORTORGEX :
{
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
SetDevOrg( Point( nX32, nY32 ) );
}
break;
case EMR_SCALEVIEWPORTEXTEX :
{
mpInputStream->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
if (nDen1 != 0 && nDen2 != 0)
ScaleDevExt( static_cast<double>(nNom1) / nDen1, static_cast<double>(nNom2) / nDen2 );
else
SAL_WARN("vcl.emf", "ignoring bogus divide by zero");
}
break;
case EMR_SETVIEWPORTEXTEX :
{
mpInputStream->ReadUInt32( nW ).ReadUInt32( nH );
SetDevExt( Size( nW, nH ) );
}
break;
case EMR_EOF :
mnRecordCount = 0;
break;
case EMR_SETPIXELV :
{
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
DrawPixel( Point( nX32, nY32 ), ReadColor() );
}
break;
case EMR_SETMAPMODE :
{
sal_uInt32 nMapMode;
mpInputStream->ReadUInt32( nMapMode );
SetMapMode( nMapMode );
}
break;
case EMR_SETBKMODE :
{
mpInputStream->ReadUInt32( nDat32 );
SetBkMode( static_cast<BkMode>(nDat32) );
}
break;
case EMR_SETPOLYFILLMODE :
break;
case EMR_SETROP2 :
{
mpInputStream->ReadUInt32( nDat32 );
SetRasterOp( static_cast<WMFRasterOp>(nDat32) );
}
break;
case EMR_SETSTRETCHBLTMODE :
{
mpInputStream->ReadUInt32( nStretchBltMode );
}
break;
case EMR_SETTEXTALIGN :
{
mpInputStream->ReadUInt32( nDat32 );
SetTextAlign( nDat32 );
}
break;
case EMR_SETTEXTCOLOR :
{
SetTextColor( ReadColor() );
}
break;
case EMR_SETBKCOLOR :
{
SetBkColor( ReadColor() );
}
break;
case EMR_OFFSETCLIPRGN :
{
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
MoveClipRegion( Size( nX32, nY32 ) );
}
break;
case EMR_MOVETOEX :
{
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
MoveTo( Point( nX32, nY32 ), mbRecordPath);
}
break;
case EMR_INTERSECTCLIPRECT :
{
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
IntersectClipRect( ReadRectangle( nX32, nY32, nx32, ny32 ) );
}
break;
case EMR_SAVEDC :
{
Push();
}
break;
case EMR_RESTOREDC :
{
Pop();
}
break;
case EMR_SETWORLDTRANSFORM :
{
XForm aTempXForm;
*mpInputStream >> aTempXForm;
SetWorldTransform( aTempXForm );
}
break;
case EMR_MODIFYWORLDTRANSFORM :
{
sal_uInt32 nMode;
XForm aTempXForm;
*mpInputStream >> aTempXForm;
mpInputStream->ReadUInt32( nMode );
ModifyWorldTransform( aTempXForm, nMode );
}
break;
case EMR_SELECTOBJECT :
{
mpInputStream->ReadUInt32( nIndex );
SelectObject( nIndex );
}
break;
case EMR_CREATEPEN :
{
mpInputStream->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);
mpInputStream->ReadUInt32( nStyle ).ReadInt32( nTmpW ).ReadInt32( nTmpH );
aSize.setWidth( nTmpW );
aSize.setHeight( 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 );
}
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;
mpInputStream->ReadUInt32( nIndex );
if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
{
mpInputStream->ReadUInt32( offBmi ).ReadUInt32( cbBmi ).ReadUInt32( offBits ).ReadUInt32( cbBits ). ReadUInt32( nStyle ).ReadUInt32( nWidth ).ReadUInt32( nBrushStyle );
aColorRef = ReadColor();
mpInputStream->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 );
}
CreateObjectIndexed(nIndex, o3tl::make_unique<WinMtfLineStyle>( aColorRef, aLineInfo, bTransparent ));
}
}
break;
case EMR_CREATEBRUSHINDIRECT :
{
sal_uInt32 nStyle;
mpInputStream->ReadUInt32( nIndex );
if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
{
mpInputStream->ReadUInt32( nStyle );
CreateObjectIndexed(nIndex, o3tl::make_unique<WinMtfFillStyle>( ReadColor(), ( nStyle == BS_HOLLOW ) ));
}
}
break;
case EMR_DELETEOBJECT :
{
mpInputStream->ReadUInt32( nIndex );
if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
DeleteObject( nIndex );
}
break;
case EMR_ELLIPSE :
{
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
DrawEllipse( ReadRectangle( nX32, nY32, nx32, ny32 ) );
}
break;
case EMR_RECTANGLE :
{
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
DrawRect( ReadRectangle( nX32, nY32, nx32, ny32 ) );
}
break;
case EMR_ROUNDRECT :
{
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nW ).ReadUInt32( nH );
Size aSize( Size( nW, nH ) );
DrawRoundRect( ReadRectangle( nX32, nY32, nx32, ny32 ), aSize );
}
break;
case EMR_ARC :
{
sal_uInt32 nStartX, nStartY, nEndX, nEndY;
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nStartX ).ReadUInt32( nStartY ).ReadUInt32( nEndX ).ReadUInt32( nEndY );
DrawArc( ReadRectangle( nX32, nY32, nx32, ny32 ), Point( nStartX, nStartY ), Point( nEndX, nEndY ) );
}
break;
case EMR_CHORD :
{
sal_uInt32 nStartX, nStartY, nEndX, nEndY;
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nStartX ).ReadUInt32( nStartY ).ReadUInt32( nEndX ).ReadUInt32( nEndY );
DrawChord( ReadRectangle( nX32, nY32, nx32, ny32 ), Point( nStartX, nStartY ), Point( nEndX, nEndY ) );
}
break;
case EMR_PIE :
{
sal_uInt32 nStartX, nStartY, nEndX, nEndY;
mpInputStream->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 )
DrawEllipse( aRect );
else
DrawPie( aRect, Point( nStartX, nStartY ), Point( nEndX, nEndY ) );
}
break;
case EMR_LINETO :
{
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
LineTo( Point( nX32, nY32 ), mbRecordPath);
}
break;
case EMR_ARCTO :
{
sal_uInt32 nStartX, nStartY, nEndX, nEndY;
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nStartX ).ReadUInt32( nStartY ).ReadUInt32( nEndX ).ReadUInt32( nEndY );
DrawArc( ReadRectangle( nX32, nY32, nx32, ny32 ), Point( nStartX, nStartY ), Point( nEndX, nEndY ), true );
}
break;
case EMR_BEGINPATH :
{
ClearPath();
mbRecordPath = true;
}
break;
case EMR_ABORTPATH :
ClearPath();
SAL_FALLTHROUGH;
case EMR_ENDPATH :
mbRecordPath = false;
break;
case EMR_CLOSEFIGURE :
ClosePath();
break;
case EMR_FILLPATH :
StrokeAndFillPath( false, true );
break;
case EMR_STROKEANDFILLPATH :
StrokeAndFillPath( true, true );
break;
case EMR_STROKEPATH :
StrokeAndFillPath( true, false );
break;
case EMR_SELECTCLIPPATH :
{
sal_Int32 nClippingMode(0);
mpInputStream->ReadInt32(nClippingMode);
SetClipPath(GetPathObj(), nClippingMode, true);
}
break;
case EMR_EXTSELECTCLIPRGN :
{
sal_Int32 nClippingMode(0), cbRgnData(0);
mpInputStream->ReadInt32(cbRgnData);
mpInputStream->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)
{
SetDefaultClipPath();
}
else
{
tools::PolyPolygon aPolyPoly;
if (cbRgnData)
ImplReadRegion(aPolyPoly, *mpInputStream, nRecSize);
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 = mpInputStream->Tell() - 8;
mpInputStream->SeekRel( 0x10 );
mpInputStream->ReadInt32( xDest ).ReadInt32( yDest ).ReadInt32( cxDest ).ReadInt32( cyDest );
*mpInputStream >> aFunc;
mpInputStream->ReadInt32( xSrc ).ReadInt32( ySrc );
*mpInputStream >> xformSrc;
mpInputStream->ReadUInt32( BkColorSrc ).ReadUInt32( iUsageSrc ).ReadUInt32( offBmiSrc ).ReadUInt32( cbBmiSrc )
.ReadUInt32( offBitsSrc ).ReadUInt32( cbBitsSrc ).ReadInt32( cxSrc ).ReadInt32( cySrc ) ;
if ( (cbBitsSrc > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc) ||
cxDest == SAL_MAX_INT32 || cyDest == SAL_MAX_INT32 )
{
bStatus = false;
}
else
{
tools::Rectangle aRect(Point(xDest, yDest), Size(cxDest + 1, cyDest + 1));
const sal_uInt32 nSourceSize = cbBmiSrc + cbBitsSrc + 14;
bool bSafeRead = nSourceSize <= (mnEndPos - mnStartPos);
sal_uInt32 nDeltaToDIB5HeaderSize(0);
const bool bReadAlpha(0x01 == aFunc.aAlphaFormat);
if (bSafeRead && bReadAlpha)
{
// we need to read alpha channel data if AlphaFormat of BLENDFUNCTION is
// AC_SRC_ALPHA (==0x01). To read it, create a temp DIB-File which is ready
// for DIB-5 format
const sal_uInt32 nHeaderSize = getDIBV5HeaderSize();
if (cbBmiSrc > nHeaderSize)
bSafeRead = false;
else
nDeltaToDIB5HeaderSize = nHeaderSize - cbBmiSrc;
}
if (bSafeRead)
{
const sal_uInt32 nTargetSize(cbBmiSrc + nDeltaToDIB5HeaderSize + cbBitsSrc + 14);
char* pBuf = new char[ nTargetSize ];
SvMemoryStream aTmp( pBuf, nTargetSize, StreamMode::READ | StreamMode::WRITE );
aTmp.ObjectOwnsMemory( true );
// write BM-Header (14 bytes)
aTmp.WriteUChar( 'B' )
.WriteUChar( 'M' )
.WriteUInt32( cbBitsSrc )
.WriteUInt16( 0 )
.WriteUInt16( 0 )
.WriteUInt32( cbBmiSrc + nDeltaToDIB5HeaderSize + 14 );
// copy DIBInfoHeader from source (cbBmiSrc bytes)
mpInputStream->Seek( nStart + offBmiSrc );
mpInputStream->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)
mpInputStream->Seek( nStart + offBitsSrc );
mpInputStream->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 = mpInputStream->Tell() - 8;
mpInputStream->SeekRel( 0x10 );
mpInputStream->ReadInt32( xDest ).ReadInt32( yDest ).ReadInt32( cxDest ).ReadInt32( cyDest ).ReadUInt32( dwRop ).ReadInt32( xSrc ).ReadInt32( ySrc )
>> xformSrc;
mpInputStream->ReadUInt32( nColor ).ReadUInt32( iUsageSrc ).ReadUInt32( offBmiSrc ).ReadUInt32( cbBmiSrc )
.ReadUInt32( offBitsSrc ).ReadUInt32( cbBitsSrc );
if ( nRecType == EMR_STRETCHBLT )
mpInputStream->ReadInt32( cxSrc ).ReadInt32( cySrc );
else
cxSrc = cySrc = 0;
Bitmap aBitmap;
tools::Rectangle aRect( Point( xDest, yDest ), Size( cxDest, cyDest ) );
if (!mpInputStream->good() || (cbBitsSrc > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc))
bStatus = false;
else
{
sal_uInt32 nSize = cbBmiSrc + cbBitsSrc + 14;
if ( nSize <= ( mnEndPos - mnStartPos ) )
{
char* pBuf = new char[ nSize ];
SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
aTmp.ObjectOwnsMemory( true );
aTmp.WriteUChar( 'B' )
.WriteUChar( 'M' )
.WriteUInt32( cbBitsSrc )
.WriteUInt16( 0 )
.WriteUInt16( 0 )
.WriteUInt32( cbBmiSrc + 14 );
mpInputStream->Seek( nStart + offBmiSrc );
mpInputStream->ReadBytes(pBuf + 14, cbBmiSrc);
mpInputStream->Seek( nStart + offBitsSrc );
mpInputStream->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) &&
(aBitmap.GetSizePixel().Width() >= cxSrc) &&
(xSrc <= aBitmap.GetSizePixel().Width() - cxSrc) &&
(aBitmap.GetSizePixel().Height() >= cySrc) &&
(ySrc <= aBitmap.GetSizePixel().Height() - cySrc) )
{
tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
aBitmap.Crop( aCropRect );
}
maBmpSaveList.emplace_back(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 = mpInputStream->Tell() - 8;
mpInputStream->SeekRel( 0x10 );
mpInputStream->ReadInt32( xDest )
.ReadInt32( yDest )
.ReadInt32( xSrc )
.ReadInt32( ySrc )
.ReadInt32( cxSrc )
.ReadInt32( cySrc )
.ReadUInt32( offBmiSrc )
.ReadUInt32( cbBmiSrc )
.ReadUInt32( offBitsSrc )
.ReadUInt32( cbBitsSrc )
.ReadUInt32( iUsageSrc )
.ReadUInt32( dwRop )
.ReadInt32( cxDest )
.ReadInt32( cyDest );
if (!mpInputStream->good() ||
((SAL_MAX_UINT32 - 14) < cbBitsSrc) ||
((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc))
{
bStatus = false;
}
else
{
Bitmap aBitmap;
tools::Rectangle aRect(xDest, yDest);
aRect.SaturatingSetSize(Size(cxDest, cyDest));
sal_uInt32 nSize = cbBmiSrc + cbBitsSrc + 14;
if ( nSize <= ( mnEndPos - mnStartPos ) )
{
char* pBuf = new char[ nSize ];
SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
aTmp.ObjectOwnsMemory( true );
aTmp.WriteUChar( 'B' )
.WriteUChar( 'M' )
.WriteUInt32( cbBitsSrc )
.WriteUInt16( 0 )
.WriteUInt16( 0 )
.WriteUInt32( cbBmiSrc + 14 );
mpInputStream->Seek( nStart + offBmiSrc );
mpInputStream->ReadBytes(pBuf + 14, cbBmiSrc);
mpInputStream->Seek( nStart + offBitsSrc );
mpInputStream->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) &&
(aBitmap.GetSizePixel().Width() >= cxSrc) &&
(xSrc <= aBitmap.GetSizePixel().Width() - cxSrc) &&
(aBitmap.GetSizePixel().Height() >= cySrc) &&
(ySrc <= aBitmap.GetSizePixel().Height() - cySrc) )
{
tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
aBitmap.Crop( aCropRect );
}
maBmpSaveList.emplace_back(new BSaveStruct(aBitmap, aRect, dwRop));
}
}
}
break;
case EMR_EXTCREATEFONTINDIRECTW :
{
mpInputStream->ReadUInt32( nIndex );
if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
{
LOGFONTW aLogFont;
mpInputStream->ReadInt32( aLogFont.lfHeight )
.ReadInt32( aLogFont.lfWidth )
.ReadInt32( aLogFont.lfEscapement )
.ReadInt32( aLogFont.lfOrientation )
.ReadInt32( aLogFont.lfWeight )
.ReadUChar( aLogFont.lfItalic )
.ReadUChar( aLogFont.lfUnderline )
.ReadUChar( aLogFont.lfStrikeOut )
.ReadUChar( aLogFont.lfCharSet )
.ReadUChar( aLogFont.lfOutPrecision )
.ReadUChar( aLogFont.lfClipPrecision )
.ReadUChar( aLogFont.lfQuality )
.ReadUChar( aLogFont.lfPitchAndFamily );
sal_Unicode lfFaceName[LF_FACESIZE+1];
lfFaceName[LF_FACESIZE] = 0;
for (int i = 0; i < LF_FACESIZE; ++i)
{
sal_uInt16 nChar(0);
mpInputStream->ReadUInt16(nChar);
lfFaceName[i] = nChar;
}
aLogFont.alfFaceName = OUString( lfFaceName );
// #i123216# Not used in the test case of #121382# (always identity in XForm), also
// no hints in ms docu if FontSize should be scaled with WT. Using with the example
// from #i123216# creates errors, so removing.
// // #i121382# Need to apply WorldTransform to FontHeight/Width; this should be completely
// // changed to basegfx::B2DHomMatrix instead of 'struct XForm', but not now due to time
// // constraints and dangers
// const XForm& rXF = GetWorldTransform();
// const basegfx::B2DHomMatrix aWT(rXF.eM11, rXF.eM21, rXF.eDx, rXF.eM12, rXF.eM22, rXF.eDy);
// const basegfx::B2DVector aTransVec(aWT * basegfx::B2DVector(aLogFont.lfWidth, aLogFont.lfHeight));
// aLogFont.lfWidth = aTransVec.getX();
// aLogFont.lfHeight = aTransVec.getY();
if (mpInputStream->good() && aLogFont.lfHeight != SAL_MIN_INT32 && aLogFont.lfWidth != SAL_MIN_INT32)
{
CreateObjectIndexed(nIndex, 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 = mpInputStream->Tell() - 8;
mpInputStream->ReadInt32( nLeft ).ReadInt32( nTop ).ReadInt32( nRight ).ReadInt32( nBottom ).ReadInt32( nGfxMode ).ReadInt32( nXScale ).ReadInt32( nYScale )
.ReadInt32( ptlReferenceX ).ReadInt32( ptlReferenceY ).ReadInt32( nLen ).ReadUInt32( nOffString ).ReadUInt32( nOptions );
mpInputStream->SeekRel( 0x10 );
mpInputStream->ReadUInt32( offDx );
ComplexTextLayoutFlags nTextLayoutMode = ComplexTextLayoutFlags::Default;
if ( nOptions & ETO_RTLREADING )
nTextLayoutMode = ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft;
SetTextLayoutMode( nTextLayoutMode );
SAL_WARN_IF( ( nOptions & ( ETO_PDY | ETO_GLYPH_INDEX ) ) != 0, "emfio", "SJ: ETO_PDY || ETO_GLYPH_INDEX in EMF" );
Point aPos( ptlReferenceX, ptlReferenceY );
bool bLenSane = nLen > 0 && nLen < static_cast<sal_Int32>( SAL_MAX_UINT32 / sizeof(sal_Int32) );
bool bOffStringSane = nOffString <= mnEndPos - nCurPos;
if (bLenSane && bOffStringSane)
{
mpInputStream->Seek( nCurPos + nOffString );
OUString aText;
if ( bFlag )
{
if ( nLen <= static_cast<sal_Int32>( mnEndPos - mpInputStream->Tell() ) )
{
std::unique_ptr<sal_Char[]> pBuf(new sal_Char[ nLen ]);
mpInputStream->ReadBytes(pBuf.get(), nLen);
aText = OUString(pBuf.get(), nLen, GetCharSet());
}
}
else
{
if ( ( nLen * sizeof(sal_Unicode) ) <= ( mnEndPos - mpInputStream->Tell() ) )
{
aText = read_uInt16s_ToOUString(*mpInputStream, nLen);
}
}
std::unique_ptr<long[]> pDXAry, pDYAry;
sal_Int32 nDxSize;
bool bOverflow = o3tl::checked_multiply<sal_Int32>(nLen, (nOptions & ETO_PDY) ? 8 : 4, nDxSize);
if (!bOverflow && offDx && ((nCurPos + offDx + nDxSize) <= nNextPos ) && nNextPos <= mnEndPos)
{
mpInputStream->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, GetCharSet());
if (aTmp.getLength() > 1)
{
nDxCount = aTmp.getLength();
}
}
sal_Int32 nDx = 0, nDy = 0;
while (nDxCount--)
{
sal_Int32 nDxTmp = 0;
mpInputStream->ReadInt32(nDxTmp);
nDx += nDxTmp;
if (nOptions & ETO_PDY)
{
sal_Int32 nDyTmp = 0;
mpInputStream->ReadInt32(nDyTmp);
nDy += nDyTmp;
}
}
pDXAry[i] = nDx;
if (nOptions & ETO_PDY)
{
pDYAry[i] = nDy;
}
}
}
DrawText(aPos, aText, pDXAry.get(), pDYAry.get(), mbRecordPath, nGfxMode);
}
}
break;
case EMR_POLYBEZIERTO16 :
DrawPolyBezier(ReadPolygonWithSkip<sal_Int16>(true, nNextPos), true, mbRecordPath);
break;
case EMR_POLYBEZIER16 :
DrawPolyBezier(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), false, mbRecordPath);
break;
case EMR_POLYGON16 :
DrawPolygon(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), mbRecordPath);
break;
case EMR_POLYLINETO16 :
DrawPolyLine(ReadPolygonWithSkip<sal_Int16>(true, nNextPos), true, mbRecordPath);
break;
case EMR_POLYLINE16 :
DrawPolyLine(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), false, mbRecordPath);
break;
case EMR_POLYPOLYLINE16 :
ReadAndDrawPolyLine<sal_Int16>(nNextPos);
break;
case EMR_POLYPOLYGON16 :
ReadAndDrawPolyPolygon<sal_Int16>(nNextPos);
break;
case EMR_FILLRGN :
{
sal_uInt32 nLen;
tools::PolyPolygon aPolyPoly;
mpInputStream->SeekRel( 0x10 );
mpInputStream->ReadUInt32( nLen ).ReadUInt32( nIndex );
if ( ImplReadRegion( aPolyPoly, *mpInputStream, nRecSize ) )
{
Push();
SelectObject( nIndex );
DrawPolyPolygon( aPolyPoly );
Pop();
}
}
break;
case EMR_CREATEDIBPATTERNBRUSHPT :
{
sal_uInt32 nStart = mpInputStream->Tell() - 8;
Bitmap aBitmap;
mpInputStream->ReadUInt32( nIndex );
if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
{
sal_uInt32 usage, offBmi, cbBmi, offBits, cbBits;
mpInputStream->ReadUInt32( usage );
mpInputStream->ReadUInt32( offBmi );
mpInputStream->ReadUInt32( cbBmi );
mpInputStream->ReadUInt32( offBits );
mpInputStream->ReadUInt32( cbBits );
if ( (cbBits > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBits < cbBmi) )
bStatus = false;
else if ( offBmi )
{
sal_uInt32 nSize = cbBmi + cbBits + 14;
if ( nSize <= ( mnEndPos - mnStartPos ) )
{
char* pBuf = new char[ nSize ];
SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
aTmp.ObjectOwnsMemory( true );
aTmp.WriteUChar( 'B' )
.WriteUChar( 'M' )
.WriteUInt32( cbBits )
.WriteUInt16( 0 )
.WriteUInt16( 0 )
.WriteUInt32( cbBmi + 14 );
mpInputStream->Seek( nStart + offBmi );
mpInputStream->ReadBytes(pBuf + 14, cbBmi);
mpInputStream->Seek( nStart + offBits );
mpInputStream->ReadBytes(pBuf + 14 + cbBmi, cbBits);
aTmp.Seek( 0 );
ReadDIB(aBitmap, aTmp, true);
}
}
}
CreateObjectIndexed(nIndex, o3tl::make_unique<WinMtfFillStyle>( aBitmap ));
}
break;
case EMR_MASKBLT : SAL_INFO("emfio", "not implemented 'MaskBlt'"); break;
case EMR_PLGBLT : SAL_INFO("emfio", "not implemented 'PlgBlt'"); break;
case EMR_SETDIBITSTODEVICE : SAL_INFO("emfio", "not implemented 'SetDIBitsToDevice'"); break;
case EMR_FRAMERGN : SAL_INFO("emfio", "not implemented 'FrameRgn'"); break;
case EMR_INVERTRGN : SAL_INFO("emfio", "not implemented 'InvertRgn'"); break;
case EMR_PAINTRGN : SAL_INFO("emfio", "not implemented 'PaintRgn'"); break;
case EMR_FLATTENPATH : SAL_INFO("emfio", "not implemented 'FlattenPath'"); break;
case EMR_WIDENPATH : SAL_INFO("emfio", "not implemented 'WidenPath'"); break;
case EMR_POLYDRAW : SAL_INFO("emfio", "not implemented 'Polydraw'"); break;
case EMR_SETARCDIRECTION : SAL_INFO("emfio", "not implemented 'SetArcDirection'"); break;
case EMR_SETPALETTEENTRIES : SAL_INFO("emfio", "not implemented 'SetPaletteEntries'"); break;
case EMR_RESIZEPALETTE : SAL_INFO("emfio", "not implemented 'ResizePalette'"); break;
case EMR_EXTFLOODFILL : SAL_INFO("emfio", "not implemented 'ExtFloodFill'"); break;
case EMR_ANGLEARC : SAL_INFO("emfio", "not implemented 'AngleArc'"); break;
case EMR_SETCOLORADJUSTMENT : SAL_INFO("emfio", "not implemented 'SetColorAdjustment'"); break;
case EMR_POLYDRAW16 : SAL_INFO("emfio", "not implemented 'PolyDraw16'"); break;
case EMR_POLYTEXTOUTA : SAL_INFO("emfio", "not implemented 'PolyTextOutA'"); break;
case EMR_POLYTEXTOUTW : SAL_INFO("emfio", "not implemented 'PolyTextOutW'"); break;
case EMR_CREATECOLORSPACE : SAL_INFO("emfio", "not implemented 'CreateColorSpace'"); break;
case EMR_SETCOLORSPACE : SAL_INFO("emfio", "not implemented 'SetColorSpace'"); break;
case EMR_DELETECOLORSPACE : SAL_INFO("emfio", "not implemented 'DeleteColorSpace'"); break;
case EMR_GLSRECORD : SAL_INFO("emfio", "not implemented 'GlsRecord'"); break;
case EMR_GLSBOUNDEDRECORD : SAL_INFO("emfio", "not implemented 'GlsBoundRecord'"); break;
case EMR_PIXELFORMAT : SAL_INFO("emfio", "not implemented 'PixelFormat'"); break;
case EMR_DRAWESCAPE : SAL_INFO("emfio", "not implemented 'DrawEscape'"); break;
case EMR_EXTESCAPE : SAL_INFO("emfio", "not implemented 'ExtEscape'"); break;
case EMR_STARTDOC : SAL_INFO("emfio", "not implemented 'StartDoc'"); break;
case EMR_SMALLTEXTOUT : SAL_INFO("emfio", "not implemented 'SmallTextOut'"); break;
case EMR_FORCEUFIMAPPING : SAL_INFO("emfio", "not implemented 'ForceUFIMapping'"); break;
case EMR_NAMEDESCAPE : SAL_INFO("emfio", "not implemented 'NamedEscape'"); break;
case EMR_COLORCORRECTPALETTE : SAL_INFO("emfio", "not implemented 'ColorCorrectPalette'"); break;
case EMR_SETICMPROFILEA : SAL_INFO("emfio", "not implemented 'SetICMProfileA'"); break;
case EMR_SETICMPROFILEW : SAL_INFO("emfio", "not implemented 'SetICMProfileW'"); break;
case EMR_TRANSPARENTBLT : SAL_INFO("emfio", "not implemented 'TransparenBlt'"); break;
case EMR_TRANSPARENTDIB : SAL_INFO("emfio", "not implemented 'TransparenDib'"); break;
case EMR_GRADIENTFILL : SAL_INFO("emfio", "not implemented 'GradientFill'"); break;
case EMR_SETLINKEDUFIS : SAL_INFO("emfio", "not implemented 'SetLinkedUFIS'"); break;
case EMR_SETMAPPERFLAGS : SAL_INFO("emfio", "not implemented 'SetMapperFlags'"); break;
case EMR_SETICMMODE : SAL_INFO("emfio", "not implemented 'SetICMMode'"); break;
case EMR_CREATEMONOBRUSH : SAL_INFO("emfio", "not implemented 'CreateMonoBrush'"); break;
case EMR_SETBRUSHORGEX : SAL_INFO("emfio", "not implemented 'SetBrushOrgEx'"); break;
case EMR_SETMETARGN : SAL_INFO("emfio", "not implemented 'SetMetArgn'"); break;
case EMR_SETMITERLIMIT : SAL_INFO("emfio", "not implemented 'SetMiterLimit'"); break;
case EMR_EXCLUDECLIPRECT : SAL_INFO("emfio", "not implemented 'ExcludeClipRect'"); break;
case EMR_REALIZEPALETTE : SAL_INFO("emfio", "not implemented 'RealizePalette'"); break;
case EMR_SELECTPALETTE : SAL_INFO("emfio", "not implemented 'SelectPalette'"); break;
case EMR_CREATEPALETTE : SAL_INFO("emfio", "not implemented 'CreatePalette'"); break;
case EMR_ALPHADIBBLEND : SAL_INFO("emfio", "not implemented 'AlphaDibBlend'"); break;
case EMR_SETTEXTJUSTIFICATION : SAL_INFO("emfio", "not implemented 'SetTextJustification'"); break;
case EMR_COMMENT :
case EMR_HEADER : // has already been read at ReadHeader()
break;
default : SAL_INFO("emfio", "Unknown Meta Action"); break;
}
}
mpInputStream->Seek( nNextPos );
}
if( !maBmpSaveList.empty() )
ResolveBitmapActions( maBmpSaveList );
if ( bStatus )
mpInputStream->Seek(mnEndPos);
return bStatus;
};
bool EmfReader::ReadHeader()
{
// Spare me the METAFILEHEADER here
// Reading the METAHEADER - EMR_HEADER ([MS-EMF] section 2.3.4.2 EMR_HEADER Record Types)
sal_uInt32 nType(0), nHeaderSize(0);
mpInputStream->ReadUInt32(nType).ReadUInt32(nHeaderSize);
if (nType != 0x00000001)
{
// per [MS-EMF] 2.3.4.2 EMF Header Record Types, type MUST be 0x00000001
SAL_WARN("emfio", "EMF header type is not set to 0x00000001 - possibly corrupted file?");
return false;
}
// Start reading the EMR_HEADER Header object
// bound size (RectL object, see [MS-WMF] section 2.2.2.19)
tools::Rectangle rclBounds = ReadRectangle(); // rectangle in logical units
// picture frame size (RectL object)
tools::Rectangle rclFrame = ReadRectangle(); // rectangle in device units 1/100th mm
sal_uInt32 nSignature(0);
mpInputStream->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("emfio", "EMF\t\tSignature is not 0x464d4520 (\"FME\") - possibly corrupted file?");
return false;
}
sal_uInt32 nVersion(0);
mpInputStream->ReadUInt32(nVersion); // according to [WS-EMF] 2.2.9, this SHOULD be 0x0001000, however
// Microsoft note that not even Windows checks this...
if (nVersion != 0x00010000)
{
SAL_WARN("emfio", "EMF\t\tThis really should be 0x00010000, though not absolutely essential...");
}
mpInputStream->ReadUInt32(mnEndPos); // size of metafile
mnEndPos += mnStartPos;
sal_uInt32 nStrmPos = mpInputStream->Tell(); // checking if mnEndPos is valid
sal_uInt32 nActualFileSize = nStrmPos + mpInputStream->remainingSize();
if ( nActualFileSize < mnEndPos )
{
SAL_WARN("emfio", "EMF\t\tEMF Header object records number of bytes as " << mnEndPos
<< ", however the file size is actually " << nActualFileSize
<< " bytes. Possible file corruption?");
mnEndPos = nActualFileSize;
}
mpInputStream->ReadInt32(mnRecordCount);
if (mnRecordCount <= 0)
{
SAL_WARN("emfio", "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;
mpInputStream->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(0);
mpInputStream->ReadUInt16(nReserved);
if ( nReserved != 0x0000 )
{
SAL_WARN("emfio", "EMF\t\tEMF Header object's reserved field is NOT 0x0000... possible "
"corruption?");
}
// The next 4 bytes specifies the number of characters in the metafile description.
// The 4 bytes after that specific the offset from this record that contains the
// metafile description... zero means no description string.
// For now, we ignore it.
mpInputStream->SeekRel(0x8);
sal_uInt32 nPalEntries(0);
mpInputStream->ReadUInt32(nPalEntries);
sal_Int32 nPixX(0), nPixY(0), nMillX(0), nMillY(0);
mpInputStream->ReadInt32(nPixX);
mpInputStream->ReadInt32(nPixY);
mpInputStream->ReadInt32(nMillX);
mpInputStream->ReadInt32(nMillY);
SetrclFrame(rclFrame);
SetrclBounds(rclBounds);
SetRefPix(Size( nPixX, nPixY ) );
SetRefMill(Size( nMillX, nMillY ) );
return checkSeek(*mpInputStream, mnStartPos + nHeaderSize);
}
tools::Rectangle EmfReader::ReadRectangle()
{
sal_Int32 nLeft, nTop, nRight, nBottom;
mpInputStream->ReadInt32(nLeft);
mpInputStream->ReadInt32(nTop);
mpInputStream->ReadInt32(nRight);
mpInputStream->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(x1, y1);
Point aBR(o3tl::saturating_add<sal_Int32>(x2, -1), o3tl::saturating_add<sal_Int32>(y2, -1));
return tools::Rectangle(aTL, aBR);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V1023 A pointer without owner is added to the 'maBmpSaveList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.
↑ V1023 A pointer without owner is added to the 'maBmpSaveList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.
↑ V1023 A pointer without owner is added to the 'maBmpSaveList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.