d35ccaf7c3
Change-Id: Ibc3ed62d5d09d2da9d7866deb21f49340ba71eea Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137117 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
2230 lines
98 KiB
C++
2230 lines
98 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
#include <basegfx/polygon/b2dpolygontools.hxx>
|
|
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
|
|
#include <emfreader.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <vcl/dibtools.hxx>
|
|
#include <o3tl/safeint.hxx>
|
|
#include <tools/stream.hxx>
|
|
#include <memory>
|
|
#include <unotools/configmgr.hxx>
|
|
#include <vcl/graph.hxx>
|
|
#include <vcl/pdfread.hxx>
|
|
#include <rtl/bootstrap.hxx>
|
|
|
|
#ifdef DBG_UTIL
|
|
#include <vcl/filter/PngImageWriter.hxx>
|
|
#endif
|
|
|
|
// GDI-Array
|
|
|
|
#define EMR_HEADER 1
|
|
#define EMR_POLYBEZIER 2
|
|
#define EMR_POLYGON 3
|
|
#define EMR_POLYLINE 4
|
|
#define EMR_POLYBEZIERTO 5
|
|
#define EMR_POLYLINETO 6
|
|
#define EMR_POLYPOLYLINE 7
|
|
#define EMR_POLYPOLYGON 8
|
|
#define EMR_SETWINDOWEXTEX 9
|
|
#define EMR_SETWINDOWORGEX 10
|
|
#define EMR_SETVIEWPORTEXTEX 11
|
|
#define EMR_SETVIEWPORTORGEX 12
|
|
#define EMR_SETBRUSHORGEX 13
|
|
#define EMR_EOF 14
|
|
#define EMR_SETPIXELV 15
|
|
#define EMR_SETMAPPERFLAGS 16
|
|
#define EMR_SETMAPMODE 17
|
|
#define EMR_SETBKMODE 18
|
|
#define EMR_SETPOLYFILLMODE 19
|
|
#define EMR_SETROP2 20
|
|
#define EMR_SETSTRETCHBLTMODE 21
|
|
#define EMR_SETTEXTALIGN 22
|
|
#define EMR_SETCOLORADJUSTMENT 23
|
|
#define EMR_SETTEXTCOLOR 24
|
|
#define EMR_SETBKCOLOR 25
|
|
#define EMR_OFFSETCLIPRGN 26
|
|
#define EMR_MOVETOEX 27
|
|
#define EMR_SETMETARGN 28
|
|
#define EMR_EXCLUDECLIPRECT 29
|
|
#define EMR_INTERSECTCLIPRECT 30
|
|
#define EMR_SCALEVIEWPORTEXTEX 31
|
|
#define EMR_SCALEWINDOWEXTEX 32
|
|
#define EMR_SAVEDC 33
|
|
#define EMR_RESTOREDC 34
|
|
#define EMR_SETWORLDTRANSFORM 35
|
|
#define EMR_MODIFYWORLDTRANSFORM 36
|
|
#define EMR_SELECTOBJECT 37
|
|
#define EMR_CREATEPEN 38
|
|
#define EMR_CREATEBRUSHINDIRECT 39
|
|
#define EMR_DELETEOBJECT 40
|
|
#define EMR_ANGLEARC 41
|
|
#define EMR_ELLIPSE 42
|
|
#define EMR_RECTANGLE 43
|
|
#define EMR_ROUNDRECT 44
|
|
#define EMR_ARC 45
|
|
#define EMR_CHORD 46
|
|
#define EMR_PIE 47
|
|
#define EMR_SELECTPALETTE 48
|
|
#define EMR_CREATEPALETTE 49
|
|
#define EMR_SETPALETTEENTRIES 50
|
|
#define EMR_RESIZEPALETTE 51
|
|
#define EMR_REALIZEPALETTE 52
|
|
#define EMR_EXTFLOODFILL 53
|
|
#define EMR_LINETO 54
|
|
#define EMR_ARCTO 55
|
|
#define EMR_POLYDRAW 56
|
|
#define EMR_SETARCDIRECTION 57
|
|
#define EMR_SETMITERLIMIT 58
|
|
#define EMR_BEGINPATH 59
|
|
#define EMR_ENDPATH 60
|
|
#define EMR_CLOSEFIGURE 61
|
|
#define EMR_FILLPATH 62
|
|
#define EMR_STROKEANDFILLPATH 63
|
|
#define EMR_STROKEPATH 64
|
|
#define EMR_FLATTENPATH 65
|
|
#define EMR_WIDENPATH 66
|
|
#define EMR_SELECTCLIPPATH 67
|
|
#define EMR_ABORTPATH 68
|
|
|
|
#define EMR_COMMENT 70 // Contains arbitrary private data.
|
|
// Comment Identifiers:
|
|
#define EMR_COMMENT_EMFPLUS 0x2B464D45 // Contains embedded EMF+ records.
|
|
#define EMR_COMMENT_EMFSPOOL 0x00000000 // Contains embedded EMFSPOOL records.
|
|
#define EMR_COMMENT_PUBLIC 0x43494447 // Specify extensions to EMF processing.
|
|
|
|
#define EMR_FILLRGN 71
|
|
#define EMR_FRAMERGN 72
|
|
#define EMR_INVERTRGN 73
|
|
#define EMR_PAINTRGN 74
|
|
#define EMR_EXTSELECTCLIPRGN 75
|
|
#define EMR_BITBLT 76
|
|
#define EMR_STRETCHBLT 77
|
|
#define EMR_MASKBLT 78
|
|
#define EMR_PLGBLT 79
|
|
#define EMR_SETDIBITSTODEVICE 80
|
|
#define EMR_STRETCHDIBITS 81
|
|
#define EMR_EXTCREATEFONTINDIRECTW 82
|
|
#define EMR_EXTTEXTOUTA 83
|
|
#define EMR_EXTTEXTOUTW 84
|
|
#define EMR_POLYBEZIER16 85
|
|
#define EMR_POLYGON16 86
|
|
#define EMR_POLYLINE16 87
|
|
#define EMR_POLYBEZIERTO16 88
|
|
#define EMR_POLYLINETO16 89
|
|
#define EMR_POLYPOLYLINE16 90
|
|
#define EMR_POLYPOLYGON16 91
|
|
#define EMR_POLYDRAW16 92
|
|
#define EMR_CREATEMONOBRUSH 93
|
|
#define EMR_CREATEDIBPATTERNBRUSHPT 94
|
|
#define EMR_EXTCREATEPEN 95
|
|
#define EMR_POLYTEXTOUTA 96
|
|
#define EMR_POLYTEXTOUTW 97
|
|
|
|
// WINDOWS VERSION >= 0x400
|
|
#define EMR_SETICMMODE 98
|
|
#define EMR_CREATECOLORSPACE 99
|
|
#define EMR_SETCOLORSPACE 100
|
|
#define EMR_DELETECOLORSPACE 101
|
|
#define EMR_GLSRECORD 102
|
|
#define EMR_GLSBOUNDEDRECORD 103
|
|
#define EMR_PIXELFORMAT 104
|
|
|
|
// WINDOWS VERSION >= 0x500
|
|
#define EMR_DRAWESCAPE 105
|
|
#define EMR_EXTESCAPE 106
|
|
#define EMR_STARTDOC 107
|
|
#define EMR_SMALLTEXTOUT 108
|
|
#define EMR_FORCEUFIMAPPING 109
|
|
#define EMR_NAMEDESCAPE 110
|
|
#define EMR_COLORCORRECTPALETTE 111
|
|
#define EMR_SETICMPROFILEA 112
|
|
#define EMR_SETICMPROFILEW 113
|
|
#define EMR_ALPHABLEND 114
|
|
#define EMR_ALPHADIBBLEND 115
|
|
#define EMR_TRANSPARENTBLT 116
|
|
#define EMR_TRANSPARENTDIB 117
|
|
#define EMR_GRADIENTFILL 118
|
|
#define EMR_SETLINKEDUFIS 119
|
|
#define EMR_SETTEXTJUSTIFICATION 120
|
|
|
|
#define PDF_SIGNATURE 0x50444620 // "PDF "
|
|
|
|
/* [MS-EMF] - v20210625 - page 28 */
|
|
constexpr sal_Int32 ARCDIRECTION_CLOCKWISE = 0x00000002;
|
|
|
|
namespace
|
|
{
|
|
|
|
const char *
|
|
record_type_name(sal_uInt32 nRecType)
|
|
{
|
|
#ifndef SAL_LOG_INFO
|
|
(void) nRecType;
|
|
return "";
|
|
#else
|
|
switch( nRecType )
|
|
{
|
|
case EMR_HEADER: return "HEADER";
|
|
case EMR_POLYBEZIER: return "POLYBEZIER";
|
|
case EMR_POLYGON: return "POLYGON";
|
|
case EMR_POLYLINE: return "POLYLINE";
|
|
case EMR_POLYBEZIERTO: return "POLYBEZIERTO";
|
|
case EMR_POLYLINETO: return "POLYLINETO";
|
|
case EMR_POLYPOLYLINE: return "POLYPOLYLINE";
|
|
case EMR_POLYPOLYGON: return "POLYPOLYGON";
|
|
case EMR_SETWINDOWEXTEX: return "SETWINDOWEXTEX";
|
|
case EMR_SETWINDOWORGEX: return "SETWINDOWORGEX";
|
|
case EMR_SETVIEWPORTEXTEX: return "SETVIEWPORTEXTEX";
|
|
case EMR_SETVIEWPORTORGEX: return "SETVIEWPORTORGEX";
|
|
case EMR_SETBRUSHORGEX: return "SETBRUSHORGEX";
|
|
case EMR_EOF: return "EOF";
|
|
case EMR_SETPIXELV: return "SETPIXELV";
|
|
case EMR_SETMAPPERFLAGS: return "SETMAPPERFLAGS";
|
|
case EMR_SETMAPMODE: return "SETMAPMODE";
|
|
case EMR_SETBKMODE: return "SETBKMODE";
|
|
case EMR_SETPOLYFILLMODE: return "SETPOLYFILLMODE";
|
|
case EMR_SETROP2: return "SETROP2";
|
|
case EMR_SETSTRETCHBLTMODE: return "SETSTRETCHBLTMODE";
|
|
case EMR_SETTEXTALIGN: return "SETTEXTALIGN";
|
|
case EMR_SETCOLORADJUSTMENT: return "SETCOLORADJUSTMENT";
|
|
case EMR_SETTEXTCOLOR: return "SETTEXTCOLOR";
|
|
case EMR_SETBKCOLOR: return "SETBKCOLOR";
|
|
case EMR_OFFSETCLIPRGN: return "OFFSETCLIPRGN";
|
|
case EMR_MOVETOEX: return "MOVETOEX";
|
|
case EMR_SETMETARGN: return "SETMETARGN";
|
|
case EMR_EXCLUDECLIPRECT: return "EXCLUDECLIPRECT";
|
|
case EMR_INTERSECTCLIPRECT: return "INTERSECTCLIPRECT";
|
|
case EMR_SCALEVIEWPORTEXTEX: return "SCALEVIEWPORTEXTEX";
|
|
case EMR_SCALEWINDOWEXTEX: return "SCALEWINDOWEXTEX";
|
|
case EMR_SAVEDC: return "SAVEDC";
|
|
case EMR_RESTOREDC: return "RESTOREDC";
|
|
case EMR_SETWORLDTRANSFORM: return "SETWORLDTRANSFORM";
|
|
case EMR_MODIFYWORLDTRANSFORM: return "MODIFYWORLDTRANSFORM";
|
|
case EMR_SELECTOBJECT: return "SELECTOBJECT";
|
|
case EMR_CREATEPEN: return "CREATEPEN";
|
|
case EMR_CREATEBRUSHINDIRECT: return "CREATEBRUSHINDIRECT";
|
|
case EMR_DELETEOBJECT: return "DELETEOBJECT";
|
|
case EMR_ANGLEARC: return "ANGLEARC";
|
|
case EMR_ELLIPSE: return "ELLIPSE";
|
|
case EMR_RECTANGLE: return "RECTANGLE";
|
|
case EMR_ROUNDRECT: return "ROUNDRECT";
|
|
case EMR_ARC: return "ARC";
|
|
case EMR_CHORD: return "CHORD";
|
|
case EMR_PIE: return "PIE";
|
|
case EMR_SELECTPALETTE: return "SELECTPALETTE";
|
|
case EMR_CREATEPALETTE: return "CREATEPALETTE";
|
|
case EMR_SETPALETTEENTRIES: return "SETPALETTEENTRIES";
|
|
case EMR_RESIZEPALETTE: return "RESIZEPALETTE";
|
|
case EMR_REALIZEPALETTE: return "REALIZEPALETTE";
|
|
case EMR_EXTFLOODFILL: return "EXTFLOODFILL";
|
|
case EMR_LINETO: return "LINETO";
|
|
case EMR_ARCTO: return "ARCTO";
|
|
case EMR_POLYDRAW: return "POLYDRAW";
|
|
case EMR_SETARCDIRECTION: return "SETARCDIRECTION";
|
|
case EMR_SETMITERLIMIT: return "SETMITERLIMIT";
|
|
case EMR_BEGINPATH: return "BEGINPATH";
|
|
case EMR_ENDPATH: return "ENDPATH";
|
|
case EMR_CLOSEFIGURE: return "CLOSEFIGURE";
|
|
case EMR_FILLPATH: return "FILLPATH";
|
|
case EMR_STROKEANDFILLPATH: return "STROKEANDFILLPATH";
|
|
case EMR_STROKEPATH: return "STROKEPATH";
|
|
case EMR_FLATTENPATH: return "FLATTENPATH";
|
|
case EMR_WIDENPATH: return "WIDENPATH";
|
|
case EMR_SELECTCLIPPATH: return "SELECTCLIPPATH";
|
|
case EMR_ABORTPATH: return "ABORTPATH";
|
|
case EMR_COMMENT: return "COMMENT";
|
|
case EMR_FILLRGN: return "FILLRGN";
|
|
case EMR_FRAMERGN: return "FRAMERGN";
|
|
case EMR_INVERTRGN: return "INVERTRGN";
|
|
case EMR_PAINTRGN: return "PAINTRGN";
|
|
case EMR_EXTSELECTCLIPRGN: return "EXTSELECTCLIPRGN";
|
|
case EMR_BITBLT: return "BITBLT";
|
|
case EMR_STRETCHBLT: return "STRETCHBLT";
|
|
case EMR_MASKBLT: return "MASKBLT";
|
|
case EMR_PLGBLT: return "PLGBLT";
|
|
case EMR_SETDIBITSTODEVICE: return "SETDIBITSTODEVICE";
|
|
case EMR_STRETCHDIBITS: return "STRETCHDIBITS";
|
|
case EMR_EXTCREATEFONTINDIRECTW: return "EXTCREATEFONTINDIRECTW";
|
|
case EMR_EXTTEXTOUTA: return "EXTTEXTOUTA";
|
|
case EMR_EXTTEXTOUTW: return "EXTTEXTOUTW";
|
|
case EMR_POLYBEZIER16: return "POLYBEZIER16";
|
|
case EMR_POLYGON16: return "POLYGON16";
|
|
case EMR_POLYLINE16: return "POLYLINE16";
|
|
case EMR_POLYBEZIERTO16: return "POLYBEZIERTO16";
|
|
case EMR_POLYLINETO16: return "POLYLINETO16";
|
|
case EMR_POLYPOLYLINE16: return "POLYPOLYLINE16";
|
|
case EMR_POLYPOLYGON16: return "POLYPOLYGON16";
|
|
case EMR_POLYDRAW16: return "POLYDRAW16";
|
|
case EMR_CREATEMONOBRUSH: return "CREATEMONOBRUSH";
|
|
case EMR_CREATEDIBPATTERNBRUSHPT: return "CREATEDIBPATTERNBRUSHPT";
|
|
case EMR_EXTCREATEPEN: return "EXTCREATEPEN";
|
|
case EMR_POLYTEXTOUTA: return "POLYTEXTOUTA";
|
|
case EMR_POLYTEXTOUTW: return "POLYTEXTOUTW";
|
|
case EMR_SETICMMODE: return "SETICMMODE";
|
|
case EMR_CREATECOLORSPACE: return "CREATECOLORSPACE";
|
|
case EMR_SETCOLORSPACE: return "SETCOLORSPACE";
|
|
case EMR_DELETECOLORSPACE: return "DELETECOLORSPACE";
|
|
case EMR_GLSRECORD: return "GLSRECORD";
|
|
case EMR_GLSBOUNDEDRECORD: return "GLSBOUNDEDRECORD";
|
|
case EMR_PIXELFORMAT: return "PIXELFORMAT";
|
|
case EMR_DRAWESCAPE: return "DRAWESCAPE";
|
|
case EMR_EXTESCAPE: return "EXTESCAPE";
|
|
case EMR_STARTDOC: return "STARTDOC";
|
|
case EMR_SMALLTEXTOUT: return "SMALLTEXTOUT";
|
|
case EMR_FORCEUFIMAPPING: return "FORCEUFIMAPPING";
|
|
case EMR_NAMEDESCAPE: return "NAMEDESCAPE";
|
|
case EMR_COLORCORRECTPALETTE: return "COLORCORRECTPALETTE";
|
|
case EMR_SETICMPROFILEA: return "SETICMPROFILEA";
|
|
case EMR_SETICMPROFILEW: return "SETICMPROFILEW";
|
|
case EMR_ALPHABLEND: return "ALPHABLEND";
|
|
case EMR_ALPHADIBBLEND: return "ALPHADIBBLEND";
|
|
case EMR_TRANSPARENTBLT: return "TRANSPARENTBLT";
|
|
case EMR_TRANSPARENTDIB: return "TRANSPARENTDIB";
|
|
case EMR_GRADIENTFILL: return "GRADIENTFILL";
|
|
case EMR_SETLINKEDUFIS: return "SETLINKEDUFIS";
|
|
case EMR_SETTEXTJUSTIFICATION: return "SETTEXTJUSTIFICATION";
|
|
default:
|
|
// Yes, return a pointer to a static buffer. This is a very
|
|
// local debugging output function, so no big deal.
|
|
static char buffer[11];
|
|
sprintf(buffer, "0x%08" SAL_PRIxUINT32, nRecType);
|
|
return buffer;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
struct BLENDFUNCTION
|
|
{
|
|
unsigned char aBlendOperation;
|
|
unsigned char aBlendFlags;
|
|
unsigned char aSrcConstantAlpha;
|
|
unsigned char aAlphaFormat;
|
|
|
|
friend SvStream& operator>>(SvStream& rInStream, BLENDFUNCTION& rBlendFun);
|
|
};
|
|
|
|
SvStream& operator>>(SvStream& rInStream, BLENDFUNCTION& rBlendFun)
|
|
{
|
|
rInStream.ReadUChar(rBlendFun.aBlendOperation);
|
|
rInStream.ReadUChar(rBlendFun.aBlendFlags);
|
|
rInStream.ReadUChar(rBlendFun.aSrcConstantAlpha);
|
|
rInStream.ReadUChar(rBlendFun.aAlphaFormat);
|
|
return rInStream;
|
|
}
|
|
|
|
bool ImplReadRegion( basegfx::B2DPolyPolygon& rPolyPoly, SvStream& rStream, sal_uInt32 nLen )
|
|
{
|
|
if (nLen < 32) // 32 bytes - Size of RegionDataHeader
|
|
return false;
|
|
|
|
sal_uInt32 nHdSize, nType, nCountRects, nRgnSize;
|
|
rStream.ReadUInt32(nHdSize);
|
|
rStream.ReadUInt32(nType);
|
|
rStream.ReadUInt32(nCountRects);
|
|
rStream.ReadUInt32(nRgnSize);
|
|
|
|
sal_Int32 nLeft, nTop, nRight, nBottom;
|
|
//bounds of the region
|
|
rStream.ReadInt32(nLeft);
|
|
rStream.ReadInt32(nTop);
|
|
rStream.ReadInt32(nRight);
|
|
rStream.ReadInt32(nBottom);
|
|
|
|
if (!rStream.good() || nCountRects == 0 || nType != emfio::RDH_RECTANGLES)
|
|
return false;
|
|
|
|
SAL_INFO("emfio", "\t\tBounds Left: " << nLeft << ", top: " << nTop << ", right: " << nRight << ", bottom: " << nBottom);
|
|
|
|
nLen -= 32;
|
|
|
|
sal_uInt32 nSize;
|
|
if (o3tl::checked_multiply<sal_uInt32>(nCountRects, 16, nSize))
|
|
return false;
|
|
if (nLen < nSize)
|
|
return false;
|
|
|
|
for (sal_uInt32 i = 0; i < nCountRects; ++i)
|
|
{
|
|
rStream.ReadInt32(nLeft);
|
|
rStream.ReadInt32(nTop);
|
|
rStream.ReadInt32(nRight);
|
|
rStream.ReadInt32(nBottom);
|
|
rPolyPoly.append( basegfx::utils::createPolygonFromRect( ::basegfx::B2DRectangle( nLeft, nTop, nRight, nBottom ) ) );
|
|
SAL_INFO("emfio", "\t\t" << i << " Left: " << nLeft << ", top: " << nTop << ", right: " << nRight << ", bottom: " << nBottom);
|
|
}
|
|
if (!utl::ConfigManager::IsFuzzing())
|
|
{
|
|
rPolyPoly = basegfx::utils::solveCrossovers(rPolyPoly);
|
|
rPolyPoly = basegfx::utils::stripNeutralPolygons(rPolyPoly);
|
|
rPolyPoly = basegfx::utils::stripDispensablePolygons(rPolyPoly);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
namespace emfio
|
|
{
|
|
EmfReader::EmfReader(SvStream& rStream,GDIMetaFile& rGDIMetaFile)
|
|
: MtfTools(rGDIMetaFile, rStream)
|
|
, mnRecordCount(0)
|
|
, mbRecordPath(false)
|
|
, mbEMFPlus(false)
|
|
, mbEMFPlusDualMode(false)
|
|
{
|
|
}
|
|
|
|
EmfReader::~EmfReader()
|
|
{
|
|
}
|
|
|
|
const sal_uInt32 EMR_COMMENT_BEGINGROUP = 0x00000002;
|
|
const sal_uInt32 EMR_COMMENT_ENDGROUP = 0x00000003;
|
|
const sal_uInt32 EMR_COMMENT_MULTIFORMATS = 0x40000004;
|
|
const sal_uInt32 EMR_COMMENT_WINDOWS_METAFILE = 0x80000001;
|
|
|
|
void EmfReader::ReadGDIComment(sal_uInt32 nCommentId)
|
|
{
|
|
sal_uInt32 nPublicCommentIdentifier(0);
|
|
mpInputStream->ReadUInt32(nPublicCommentIdentifier);
|
|
|
|
SAL_INFO("emfio", "\t\tEMR_COMMENT_PUBLIC, id: 0x" << std::hex << nCommentId << std::dec);
|
|
switch (nPublicCommentIdentifier)
|
|
{
|
|
case EMR_COMMENT_BEGINGROUP:
|
|
{
|
|
SAL_INFO("emfio", "\t\t\tEMR_COMMENT_BEGINGROUP");
|
|
sal_uInt32 left, top, right, bottom;
|
|
mpInputStream->ReadUInt32(left).ReadUInt32(top).ReadUInt32(right).ReadUInt32(bottom);
|
|
|
|
SAL_INFO("emfio", "\t\t\t\tBounding rect");
|
|
SAL_INFO("emfio", "\t\t\t\t\tLeft: " << left);
|
|
SAL_INFO("emfio", "\t\t\t\t\tTop: " << top);
|
|
SAL_INFO("emfio", "\t\t\t\t\tRight: " << right);
|
|
SAL_INFO("emfio", "\t\t\t\t\tBottom: " << bottom);
|
|
|
|
sal_uInt32 nDescChars(0);
|
|
mpInputStream->ReadUInt32(nDescChars);
|
|
|
|
OUString aDesc;
|
|
for (sal_uInt32 i=0; i < nDescChars; i++)
|
|
{
|
|
sal_uInt16 cChar(0);
|
|
mpInputStream->ReadUInt16(cChar);
|
|
if (cChar == '\0')
|
|
break;
|
|
|
|
sal_Unicode cUniChar = static_cast<sal_Unicode>(cChar);
|
|
aDesc = aDesc + OUStringChar(cUniChar);
|
|
}
|
|
|
|
SAL_INFO("emfio", "\t\tDescription: " << aDesc);
|
|
}
|
|
break;
|
|
|
|
case EMR_COMMENT_ENDGROUP:
|
|
SAL_INFO("emfio", "\t\t\tEMR_COMMENT_ENDGROUP");
|
|
break;
|
|
|
|
case EMR_COMMENT_MULTIFORMATS:
|
|
ReadMultiformatsComment();
|
|
break;
|
|
|
|
case EMR_COMMENT_WINDOWS_METAFILE:
|
|
SAL_WARN("emfio", "\t\tEMR_COMMENT_WINDOWS_METAFILE not implemented");
|
|
break;
|
|
|
|
default:
|
|
SAL_WARN("emfio", "\t\tEMR_COMMENT_PUBLIC not implemented, id: 0x" << std::hex << nCommentId << std::dec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void EmfReader::ReadMultiformatsComment()
|
|
{
|
|
tools::Rectangle aOutputRect = EmfReader::ReadRectangle();
|
|
|
|
sal_uInt32 nCountFormats(0);
|
|
mpInputStream->ReadUInt32(nCountFormats);
|
|
if (nCountFormats < 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Read the first EmrFormat.
|
|
sal_uInt32 nSignature(0);
|
|
mpInputStream->ReadUInt32(nSignature);
|
|
if (nSignature != PDF_SIGNATURE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
sal_uInt32 nVersion(0);
|
|
mpInputStream->ReadUInt32(nVersion);
|
|
if (nVersion != 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
sal_uInt32 nSizeData(0);
|
|
mpInputStream->ReadUInt32(nSizeData);
|
|
if (!nSizeData || nSizeData > mpInputStream->remainingSize())
|
|
{
|
|
return;
|
|
}
|
|
|
|
sal_uInt32 nOffData(0);
|
|
mpInputStream->ReadUInt32(nOffData);
|
|
if (!nOffData)
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::vector<char> aPdfData(nSizeData);
|
|
mpInputStream->ReadBytes(aPdfData.data(), aPdfData.size());
|
|
if (!mpInputStream->good())
|
|
{
|
|
return;
|
|
}
|
|
|
|
SvMemoryStream aPdfStream;
|
|
aPdfStream.WriteBytes(aPdfData.data(), aPdfData.size());
|
|
aPdfStream.Seek(0);
|
|
Graphic aGraphic;
|
|
if (!vcl::ImportPDF(aPdfStream, aGraphic))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// aGraphic will be the only output of the EMF parser, so its size hint can be the same as
|
|
// ours.
|
|
aGraphic.getVectorGraphicData()->setSizeHint(maSizeHint);
|
|
|
|
maBmpSaveList.emplace_back(
|
|
aGraphic.GetBitmapEx(), aOutputRect, SRCCOPY, /*bForceAlpha=*/true);
|
|
const std::shared_ptr<VectorGraphicData> pVectorGraphicData
|
|
= aGraphic.getVectorGraphicData();
|
|
if (!pVectorGraphicData)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pVectorGraphicData->getType() != VectorGraphicDataType::Pdf)
|
|
{
|
|
return;
|
|
}
|
|
|
|
mbReadOtherGraphicFormat = true;
|
|
}
|
|
|
|
void EmfReader::ReadEMFPlusComment(sal_uInt32 length, bool& bHaveDC)
|
|
{
|
|
if (!mbEMFPlus)
|
|
{
|
|
PassEMFPlusHeaderInfo();
|
|
|
|
#if OSL_DEBUG_LEVEL > 1
|
|
// debug code - write the stream to debug file /tmp/emf-stream.emf
|
|
sal_uInt64 const pos = mpInputStream->Tell();
|
|
mpInputStream->Seek(0);
|
|
SvFileStream file( OUString( "/tmp/emf-stream.emf" ), StreamMode::WRITE | StreamMode::TRUNC );
|
|
|
|
mpInputStream->WriteStream(file);
|
|
file.Flush();
|
|
file.Close();
|
|
|
|
mpInputStream->Seek( pos );
|
|
#endif
|
|
|
|
}
|
|
|
|
mbEMFPlus = true;
|
|
sal_uInt64 const pos = mpInputStream->Tell();
|
|
auto buffer = std::make_unique<char[]>( length );
|
|
PassEMFPlus( buffer.get(), mpInputStream->ReadBytes(buffer.get(), length) );
|
|
buffer.reset();
|
|
mpInputStream->Seek( pos );
|
|
|
|
bHaveDC = false;
|
|
|
|
// skip in SeekRel if impossibly unavailable
|
|
sal_uInt32 nRemainder = length;
|
|
|
|
const size_t nRequiredHeaderSize = 12;
|
|
while (nRemainder >= nRequiredHeaderSize)
|
|
{
|
|
sal_uInt16 type(0), flags(0);
|
|
sal_uInt32 size(0), dataSize(0);
|
|
|
|
mpInputStream->ReadUInt16( type ).ReadUInt16( flags ).ReadUInt32( size ).ReadUInt32( dataSize );
|
|
nRemainder -= nRequiredHeaderSize;
|
|
|
|
SAL_INFO("emfio", "\t\tEMF+ record type: 0x" << std::hex << type << std::dec);
|
|
|
|
// Get Device Context
|
|
// TODO We should use EmfPlusRecordType::GetDC instead
|
|
if( type == 0x4004 )
|
|
{
|
|
bHaveDC = true;
|
|
SAL_INFO("emfio", "\t\tEMF+ lock DC (device context)");
|
|
}
|
|
|
|
// look for the "dual mode" in header
|
|
// it indicates that either EMF or EMF+ records should be processed
|
|
// 0x4001 = EMF+ header
|
|
// flags & 1 = dual mode active
|
|
if ( type == 0x4001 && flags & 1 )
|
|
{
|
|
mbEMFPlusDualMode = true;
|
|
SAL_INFO ("emfio", "\t\tEMF+ dual mode detected");
|
|
}
|
|
|
|
// Get the length of the remaining data of this record based
|
|
// on the alleged size
|
|
sal_uInt32 nRemainingRecordData = size >= nRequiredHeaderSize ?
|
|
size-nRequiredHeaderSize : 0;
|
|
// clip to available size
|
|
nRemainingRecordData = std::min(nRemainingRecordData, nRemainder);
|
|
mpInputStream->SeekRel(nRemainingRecordData);
|
|
nRemainder -= nRemainingRecordData;
|
|
}
|
|
mpInputStream->SeekRel(nRemainder);
|
|
}
|
|
|
|
// these are referenced from inside the templates
|
|
static SvStream& operator >> (SvStream& rStream, sal_Int16 &n)
|
|
{
|
|
return rStream.ReadInt16(n);
|
|
}
|
|
|
|
static SvStream& operator >> (SvStream& rStream, sal_Int32 &n)
|
|
{
|
|
return rStream.ReadInt32(n);
|
|
}
|
|
|
|
/**
|
|
* Reads polygons from the stream.
|
|
* The \<class T> parameter is for the type of the points (sal_uInt32 or sal_uInt16).
|
|
* skipFirst: if the first point read is the 0th point or the 1st point in the array.
|
|
* */
|
|
template <class T>
|
|
tools::Polygon EmfReader::ReadPolygonWithSkip(const bool skipFirst, sal_uInt32 nNextPos)
|
|
{
|
|
sal_uInt32 nPoints(0), nStartIndex(0);
|
|
mpInputStream->SeekRel( 16 );
|
|
mpInputStream->ReadUInt32( nPoints );
|
|
if (skipFirst)
|
|
{
|
|
nPoints ++;
|
|
nStartIndex ++;
|
|
}
|
|
|
|
return ReadPolygon<T>(nStartIndex, nPoints, nNextPos);
|
|
}
|
|
|
|
/**
|
|
* Reads polygons from the stream.
|
|
* The \<class T> parameter is for the type of the points
|
|
* nStartIndex: which is the starting index in the polygon of the first point read
|
|
* nPoints: number of points
|
|
* mpInputStream: the stream containing the polygons
|
|
* */
|
|
template <class T>
|
|
tools::Polygon EmfReader::ReadPolygon(sal_uInt32 nStartIndex, sal_uInt32 nPoints, sal_uInt32 nNextPos)
|
|
{
|
|
SAL_INFO ("emfio", "\t\tPolygon:");
|
|
|
|
bool bRecordOk = nPoints <= SAL_MAX_UINT16;
|
|
SAL_WARN_IF(!bRecordOk, "emfio", "polygon record has more polygons than we can handle");
|
|
if (!bRecordOk || !nPoints)
|
|
return tools::Polygon();
|
|
|
|
auto nRemainingSize = std::min(nNextPos - mpInputStream->Tell(), mpInputStream->remainingSize());
|
|
auto nMaxPossiblePoints = nRemainingSize / (sizeof(T) * 2);
|
|
auto nPointCount = nPoints - nStartIndex;
|
|
if (nPointCount > nMaxPossiblePoints)
|
|
{
|
|
SAL_WARN("emfio", "polygon claims more points than record can provide, truncating");
|
|
nPoints = nMaxPossiblePoints + nStartIndex;
|
|
}
|
|
|
|
tools::Polygon aPolygon(nPoints);
|
|
for (sal_uInt32 i = nStartIndex ; i < nPoints && mpInputStream->good(); i++ )
|
|
{
|
|
T nX, nY;
|
|
*mpInputStream >> nX >> nY;
|
|
|
|
SAL_INFO("emfio", "\t\t\tPoint " << i << " of " << nPoints - 1 << ": " << nX << ", " << nY);
|
|
|
|
if (!mpInputStream->good())
|
|
{
|
|
SAL_WARN("emfio", "short read on polygon, truncating");
|
|
aPolygon.SetSize(i);
|
|
break;
|
|
}
|
|
aPolygon[ i ] = Point( nX, nY );
|
|
}
|
|
|
|
return aPolygon;
|
|
}
|
|
|
|
/**
|
|
* Reads a polyline from the WMF file and draws it
|
|
* The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
|
|
* */
|
|
template <class T>
|
|
void EmfReader::ReadAndDrawPolyLine(sal_uInt32 nNextPos)
|
|
{
|
|
SAL_INFO("emfio", "\t\tPolyline: ");
|
|
|
|
mpInputStream->SeekRel( 0x10 ); // TODO Skipping Bounds. A 128-bit WMF RectL object (specifies the bounding rectangle in device units.)
|
|
|
|
sal_uInt32 nNumberOfPolylines = 0;
|
|
mpInputStream->ReadUInt32( nNumberOfPolylines );
|
|
SAL_INFO("emfio", "\t\t\tPolylines: " << nNumberOfPolylines);
|
|
|
|
sal_uInt32 nCount = 0;
|
|
mpInputStream->ReadUInt32( nCount ); // total number of points in all polylines
|
|
SAL_INFO("emfio", "\t\t\tPoints: " << nCount);
|
|
|
|
const auto nEndPos = std::min(nNextPos, mnEndPos);
|
|
if (mpInputStream->Tell() >= nEndPos)
|
|
return;
|
|
|
|
// taking the amount of points of each polygon, retrieving the total number of points
|
|
if ( !(mpInputStream->good() &&
|
|
( nNumberOfPolylines < SAL_MAX_UINT32 / sizeof( sal_uInt16 ) ) &&
|
|
( nNumberOfPolylines * sizeof( sal_uInt16 ) ) <= ( nEndPos - mpInputStream->Tell() ))
|
|
)
|
|
return;
|
|
|
|
std::unique_ptr< sal_uInt32[] > pnPolylinePointCount( new sal_uInt32[ nNumberOfPolylines ] );
|
|
for ( sal_uInt32 i = 0; i < nNumberOfPolylines && mpInputStream->good(); i++ )
|
|
{
|
|
sal_uInt32 nPoints;
|
|
mpInputStream->ReadUInt32( nPoints );
|
|
SAL_INFO("emfio", "\t\t\tPoint " << i << " of " << nNumberOfPolylines << ": " << nPoints);
|
|
pnPolylinePointCount[ i ] = nPoints;
|
|
}
|
|
|
|
// Get polyline points:
|
|
for ( sal_uInt32 i = 0; ( i < nNumberOfPolylines ) && mpInputStream->good(); i++ )
|
|
{
|
|
tools::Polygon aPolygon = ReadPolygon<T>(0, pnPolylinePointCount[i], nNextPos);
|
|
DrawPolyLine(std::move(aPolygon), false, mbRecordPath);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reads a poly polygon from the WMF file and draws it.
|
|
* The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
|
|
* */
|
|
template <class T>
|
|
void EmfReader::ReadAndDrawPolyPolygon(sal_uInt32 nNextPos)
|
|
{
|
|
SAL_INFO("emfio", "\t\tPolygon: ");
|
|
mpInputStream->SeekRel( 0x10 ); // RectL bounds
|
|
|
|
sal_uInt32 nPoly(0), nGesPoints(0), nReadPoints(0);
|
|
// Number of polygons
|
|
mpInputStream->ReadUInt32( nPoly ).ReadUInt32( nGesPoints );
|
|
SAL_INFO("emfio", "\t\t\tPolygons: " << nPoly);
|
|
SAL_INFO("emfio", "\t\t\tPoints: " << nGesPoints);
|
|
|
|
const auto nEndPos = std::min(nNextPos, mnEndPos);
|
|
if (mpInputStream->Tell() >= nEndPos)
|
|
return;
|
|
if (!mpInputStream->good())
|
|
return;
|
|
//check against numeric overflowing
|
|
if (nGesPoints >= SAL_MAX_UINT32 / sizeof(Point))
|
|
return;
|
|
if (nPoly >= SAL_MAX_UINT32 / sizeof(sal_uInt16))
|
|
return;
|
|
if (nPoly * sizeof(sal_uInt16) > nEndPos - mpInputStream->Tell())
|
|
return;
|
|
|
|
// Get number of points in each polygon
|
|
std::vector<sal_uInt16> aPoints(nPoly);
|
|
for (sal_uInt32 i = 0; i < nPoly && mpInputStream->good(); ++i)
|
|
{
|
|
sal_uInt32 nPoints(0);
|
|
mpInputStream->ReadUInt32( nPoints );
|
|
|
|
SAL_INFO("emfio", "\t\t\t\tPolygon " << i << " points: " << nPoints);
|
|
|
|
aPoints[i] = static_cast<sal_uInt16>(nPoints);
|
|
}
|
|
|
|
if ( mpInputStream->good() && ( nGesPoints * (sizeof(T)+sizeof(T)) ) <= ( nEndPos - mpInputStream->Tell() ) )
|
|
{
|
|
// Get polygon points
|
|
tools::PolyPolygon aPolyPoly(nPoly);
|
|
for (sal_uInt32 i = 0; i < nPoly && mpInputStream->good(); ++i)
|
|
{
|
|
const sal_uInt16 nPointCount(aPoints[i]);
|
|
std::vector<Point> aPtAry(nPointCount);
|
|
for (sal_uInt16 j = 0; j < nPointCount && mpInputStream->good(); ++j)
|
|
{
|
|
T nX(0), nY(0);
|
|
*mpInputStream >> nX >> nY;
|
|
aPtAry[j] = Point( nX, nY );
|
|
++nReadPoints;
|
|
}
|
|
|
|
aPolyPoly.Insert(tools::Polygon(aPtAry.size(), aPtAry.data()));
|
|
}
|
|
|
|
DrawPolyPolygon(aPolyPoly, mbRecordPath);
|
|
}
|
|
|
|
OSL_ENSURE(nReadPoints == nGesPoints, "The number Points processed from EMR_POLYPOLYGON is unequal imported number (!)");
|
|
}
|
|
|
|
bool EmfReader::ReadEnhWMF()
|
|
{
|
|
sal_uInt32 nStretchBltMode = 0;
|
|
sal_uInt32 nNextPos(0),
|
|
nW(0), nH(0), nColor(0), nIndex(0),
|
|
nDat32(0), nNom1(0), nDen1(0), nNom2(0), nDen2(0);
|
|
sal_Int32 nX32(0), nY32(0), nx32(0), ny32(0);
|
|
|
|
bool bStatus = ReadHeader();
|
|
bool bHaveDC = false;
|
|
|
|
OUString aEMFPlusDisable;
|
|
rtl::Bootstrap::get("EMF_PLUS_DISABLE", aEMFPlusDisable);
|
|
bool bEnableEMFPlus = aEMFPlusDisable.isEmpty();
|
|
if (!mbEnableEMFPlus)
|
|
{
|
|
// EMF+ is enabled if neither the bootstrap variable, not the member variable disables
|
|
// it.
|
|
bEnableEMFPlus = mbEnableEMFPlus;
|
|
}
|
|
|
|
SAL_INFO("emfio", "EMF+ reading is " << (bEnableEMFPlus ? "enabled" : "disabled"));
|
|
|
|
while (bStatus && mnRecordCount-- && mpInputStream->good() && !mbReadOtherGraphicFormat)
|
|
{
|
|
sal_uInt32 nRecType(0), nRecSize(0);
|
|
mpInputStream->ReadUInt32(nRecType).ReadUInt32(nRecSize);
|
|
|
|
if ( !mpInputStream->good() || ( nRecSize < 8 ) || ( nRecSize & 3 ) ) // Parameters are always divisible by 4
|
|
{
|
|
bStatus = false;
|
|
break;
|
|
}
|
|
|
|
auto nCurPos = mpInputStream->Tell();
|
|
|
|
if (mnEndPos < nCurPos - 8)
|
|
{
|
|
bStatus = false;
|
|
break;
|
|
}
|
|
|
|
const sal_uInt32 nMaxPossibleRecSize = mnEndPos - (nCurPos - 8);
|
|
if (nRecSize > nMaxPossibleRecSize)
|
|
{
|
|
bStatus = false;
|
|
break;
|
|
}
|
|
|
|
nNextPos = nCurPos + (nRecSize - 8);
|
|
|
|
if( !maBmpSaveList.empty()
|
|
&& ( nRecType != EMR_STRETCHBLT )
|
|
&& ( nRecType != EMR_STRETCHDIBITS )
|
|
) {
|
|
ResolveBitmapActions( maBmpSaveList );
|
|
}
|
|
|
|
bool bFlag = false;
|
|
|
|
SAL_INFO("emfio", "0x" << std::hex << (nNextPos - nRecSize) << "-0x" << nNextPos << " " << record_type_name(nRecType) << " size: "
|
|
<< std::dec << nRecSize);
|
|
|
|
if( bEnableEMFPlus && nRecType == EMR_COMMENT )
|
|
{
|
|
sal_uInt32 length;
|
|
|
|
mpInputStream->ReadUInt32( length );
|
|
|
|
SAL_INFO("emfio", "\tGDI comment, length: " << length);
|
|
|
|
if( mpInputStream->good() && length >= 4 && length <= mpInputStream->remainingSize() ) {
|
|
sal_uInt32 nCommentId;
|
|
|
|
mpInputStream->ReadUInt32( nCommentId );
|
|
|
|
SAL_INFO("emfio", "\t\tbegin " << static_cast<char>(nCommentId & 0xff) << static_cast<char>((nCommentId & 0xff00) >> 8) << static_cast<char>((nCommentId & 0xff0000) >> 16) << static_cast<char>((nCommentId & 0xff000000) >> 24) << " id: 0x" << std::hex << nCommentId << std::dec);
|
|
|
|
if( nCommentId == EMR_COMMENT_EMFPLUS && nRecSize >= 12 )
|
|
{
|
|
// [MS-EMF] 2.3.3: DataSize includes both CommentIdentifier and CommentRecordParm fields.
|
|
// We have already read 4-byte CommentIdentifier, so reduce length appropriately
|
|
ReadEMFPlusComment( length-4, bHaveDC );
|
|
}
|
|
else if( nCommentId == EMR_COMMENT_PUBLIC && nRecSize >= 12 )
|
|
{
|
|
ReadGDIComment(nCommentId);
|
|
}
|
|
else if( nCommentId == EMR_COMMENT_EMFSPOOL && nRecSize >= 12 )
|
|
{
|
|
SAL_WARN("emfio", "\t\tEMFSPOOL not implemented, id: 0x" << std::hex << nCommentId << std::dec);
|
|
// TODO Implement reading EMFSPOOL comment
|
|
|
|
}
|
|
else
|
|
{
|
|
SAL_WARN("emfio", "\t\tunknown id: 0x" << std::hex << nCommentId << std::dec);
|
|
}
|
|
}
|
|
}
|
|
else if ( !bHaveDC && mbEMFPlusDualMode && nRecType != EMR_HEADER && nRecType != EMR_EOF )
|
|
{
|
|
// skip content (EMF record) in dual mode
|
|
// we process only EMR_COMMENT (see above) to access EMF+ data
|
|
// with 2 exceptions, according to EMF+ specification:
|
|
// EMR_HEADER and EMR_EOF
|
|
// if a device context is given (bHaveDC) process the following EMF record, too.
|
|
}
|
|
else if( !mbEMFPlus || bHaveDC || nRecType == EMR_EOF )
|
|
{
|
|
switch( nRecType )
|
|
{
|
|
case EMR_POLYBEZIERTO :
|
|
DrawPolyBezier(ReadPolygonWithSkip<sal_Int32>(true, nNextPos), true, mbRecordPath);
|
|
break;
|
|
case EMR_POLYBEZIER :
|
|
DrawPolyBezier(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), false, mbRecordPath);
|
|
break;
|
|
|
|
case EMR_POLYGON :
|
|
DrawPolygon(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), mbRecordPath);
|
|
break;
|
|
|
|
case EMR_POLYLINETO :
|
|
DrawPolyLine(ReadPolygonWithSkip<sal_Int32>(true, nNextPos), true, mbRecordPath);
|
|
break;
|
|
|
|
case EMR_POLYLINE :
|
|
DrawPolyLine(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), false, mbRecordPath);
|
|
break;
|
|
|
|
case EMR_POLYPOLYLINE :
|
|
ReadAndDrawPolyLine<sal_Int32>(nNextPos);
|
|
break;
|
|
|
|
case EMR_POLYPOLYGON :
|
|
ReadAndDrawPolyPolygon<sal_Int32>(nNextPos);
|
|
break;
|
|
|
|
case EMR_SETWINDOWEXTEX :
|
|
{
|
|
sal_Int32 w = 0, h = 0;
|
|
mpInputStream->ReadInt32( w ).ReadInt32( h );
|
|
SAL_INFO("emfio", "\t\tWidth: " << w);
|
|
SAL_INFO("emfio", "\t\tHeight: " << h);
|
|
SetWinExt( Size( w, h ), true);
|
|
}
|
|
break;
|
|
|
|
case EMR_SETWINDOWORGEX :
|
|
{
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
|
|
SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
|
|
SetWinOrg( Point( nX32, nY32 ), true);
|
|
}
|
|
break;
|
|
|
|
case EMR_SCALEWINDOWEXTEX :
|
|
{
|
|
mpInputStream->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
|
|
SAL_INFO("emfio", "\t\tHorizontal scale: " << nNom1 << " / " << nDen1);
|
|
SAL_INFO("emfio", "\t\tVertical scale: " << nNom2 << " / " << nDen2);
|
|
|
|
if (nDen1 != 0 && nDen2 != 0)
|
|
ScaleWinExt( static_cast<double>(nNom1) / nDen1, static_cast<double>(nNom2) / nDen2 );
|
|
else
|
|
SAL_WARN("vcl.emf", "ignoring bogus divide by zero");
|
|
}
|
|
break;
|
|
|
|
case EMR_SETVIEWPORTORGEX :
|
|
{
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
|
|
SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
|
|
SetDevOrg( Point( nX32, nY32 ) );
|
|
}
|
|
break;
|
|
|
|
case EMR_SCALEVIEWPORTEXTEX :
|
|
{
|
|
mpInputStream->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
|
|
SAL_INFO("emfio", "\t\tHorizontal scale: " << nNom1 << " / " << nDen1);
|
|
SAL_INFO("emfio", "\t\tVertical scale: " << nNom2 << " / " << nDen2);
|
|
|
|
if (nDen1 != 0 && nDen2 != 0)
|
|
ScaleDevExt( static_cast<double>(nNom1) / nDen1, static_cast<double>(nNom2) / nDen2 );
|
|
else
|
|
SAL_WARN("vcl.emf", "ignoring bogus divide by zero");
|
|
}
|
|
break;
|
|
|
|
case EMR_SETVIEWPORTEXTEX :
|
|
{
|
|
sal_Int32 w = 0, h = 0;
|
|
mpInputStream->ReadInt32( w ).ReadInt32( h );
|
|
SAL_INFO("emfio", "\t\tWidth: " << w);
|
|
SAL_INFO("emfio", "\t\tHeight: " << h);
|
|
|
|
SetDevExt( Size( w, h ) );
|
|
}
|
|
break;
|
|
|
|
case EMR_EOF :
|
|
mnRecordCount = 0;
|
|
break;
|
|
|
|
case EMR_SETPIXELV :
|
|
{
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
|
|
SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
|
|
DrawPixel( Point( nX32, nY32 ), ReadColor() );
|
|
}
|
|
break;
|
|
|
|
case EMR_SETMAPMODE :
|
|
{
|
|
sal_uInt32 nMapMode(0);
|
|
mpInputStream->ReadUInt32( nMapMode );
|
|
SAL_INFO("emfio", "\t\tMapMode: 0x" << std::hex << nMapMode << std::dec);
|
|
SetMapMode( static_cast<MappingMode>(nMapMode) );
|
|
}
|
|
break;
|
|
|
|
case EMR_SETBKMODE :
|
|
{
|
|
mpInputStream->ReadUInt32( nDat32 );
|
|
SAL_INFO("emfio", "\t\tBkMode: 0x" << std::hex << nDat32 << std::dec);
|
|
SetBkMode( static_cast<BackgroundMode>(nDat32) );
|
|
}
|
|
break;
|
|
|
|
case EMR_SETPOLYFILLMODE :
|
|
break;
|
|
|
|
case EMR_SETROP2 :
|
|
{
|
|
mpInputStream->ReadUInt32( nDat32 );
|
|
SAL_INFO("emfio", "\t\tROP2: 0x" << std::hex << nDat32 << std::dec);
|
|
SetRasterOp( static_cast<WMFRasterOp>(nDat32) );
|
|
}
|
|
break;
|
|
|
|
case EMR_SETSTRETCHBLTMODE :
|
|
{
|
|
mpInputStream->ReadUInt32( nStretchBltMode );
|
|
SAL_INFO("emfio", "\t\tStretchBltMode: 0x" << std::hex << nDat32 << std::dec);
|
|
}
|
|
break;
|
|
|
|
case EMR_SETTEXTALIGN :
|
|
{
|
|
mpInputStream->ReadUInt32( nDat32 );
|
|
SAL_INFO("emfio", "\t\tTextAlign: 0x" << std::hex << nDat32 << std::dec);
|
|
SetTextAlign( nDat32 );
|
|
}
|
|
break;
|
|
|
|
case EMR_SETTEXTCOLOR :
|
|
{
|
|
SetTextColor( ReadColor() );
|
|
}
|
|
break;
|
|
|
|
case EMR_SETARCDIRECTION:
|
|
{
|
|
mpInputStream->ReadUInt32(nIndex);
|
|
SetArcDirection(nIndex == ARCDIRECTION_CLOCKWISE);
|
|
}
|
|
break;
|
|
|
|
case EMR_SETBKCOLOR :
|
|
{
|
|
SetBkColor( ReadColor() );
|
|
}
|
|
break;
|
|
|
|
case EMR_OFFSETCLIPRGN :
|
|
{
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
|
|
SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
|
|
MoveClipRegion( Size( nX32, nY32 ) );
|
|
}
|
|
break;
|
|
|
|
case EMR_MOVETOEX :
|
|
{
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
|
|
SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
|
|
MoveTo( Point( nX32, nY32 ), mbRecordPath);
|
|
}
|
|
break;
|
|
|
|
case EMR_INTERSECTCLIPRECT :
|
|
{
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
|
|
IntersectClipRect( ReadRectangle( nX32, nY32, nx32, ny32 ) );
|
|
SAL_INFO("emfio", "\t\tPoint: (" << nX32 << ", " << nY32 << ")");
|
|
SAL_INFO("emfio", "\t\tPoint: (" << nx32 << ", " << ny32 << ")");
|
|
}
|
|
break;
|
|
|
|
case EMR_SAVEDC :
|
|
{
|
|
Push();
|
|
}
|
|
break;
|
|
|
|
case EMR_RESTOREDC :
|
|
{
|
|
sal_Int32 nSavedDC(0);
|
|
mpInputStream->ReadInt32( nSavedDC );
|
|
SAL_INFO( "emfio", "\t\t SavedDC Index: " << nSavedDC );
|
|
if ( nSavedDC < 0 )
|
|
Pop( nSavedDC );
|
|
else
|
|
Pop( -1 ); // For RestoreDC values above -1, treat as get last element
|
|
}
|
|
break;
|
|
|
|
case EMR_SETWORLDTRANSFORM :
|
|
{
|
|
XForm aTempXForm;
|
|
*mpInputStream >> aTempXForm;
|
|
SetWorldTransform( aTempXForm );
|
|
}
|
|
break;
|
|
|
|
case EMR_MODIFYWORLDTRANSFORM :
|
|
{
|
|
sal_uInt32 nMode(0);
|
|
XForm aTempXForm;
|
|
*mpInputStream >> aTempXForm;
|
|
mpInputStream->ReadUInt32( nMode );
|
|
ModifyWorldTransform( aTempXForm, static_cast<ModifyWorldTransformMode>(nMode) );
|
|
}
|
|
break;
|
|
|
|
case EMR_SELECTOBJECT :
|
|
{
|
|
mpInputStream->ReadUInt32( nIndex );
|
|
SelectObject( nIndex );
|
|
}
|
|
break;
|
|
|
|
case EMR_CREATEPEN:
|
|
{
|
|
mpInputStream->ReadUInt32(nIndex);
|
|
if ((nIndex & ENHMETA_STOCK_OBJECT) == 0)
|
|
{
|
|
sal_uInt32 nStyle(0);
|
|
sal_Int32 nPenWidth(0), nIgnored;
|
|
mpInputStream->ReadUInt32(nStyle).ReadInt32(nPenWidth).ReadInt32(nIgnored);
|
|
SAL_INFO("emfio", "\t\tIndex: " << nIndex << " Style: 0x" << std::hex
|
|
<< nStyle << std::dec
|
|
<< " PenWidth: " << nPenWidth);
|
|
CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>(ReadColor(), nStyle, nPenWidth));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_EXTCREATEPEN:
|
|
{
|
|
mpInputStream->ReadUInt32(nIndex);
|
|
if ((nIndex & ENHMETA_STOCK_OBJECT) == 0)
|
|
{
|
|
sal_uInt32 offBmi, cbBmi, offBits, cbBits, nStyle, nWidth, nBrushStyle, elpNumEntries;
|
|
sal_Int32 elpHatch;
|
|
mpInputStream->ReadUInt32(offBmi).ReadUInt32(cbBmi).ReadUInt32(offBits).ReadUInt32(cbBits);
|
|
mpInputStream->ReadUInt32(nStyle).ReadUInt32(nWidth).ReadUInt32(nBrushStyle);
|
|
|
|
SAL_INFO("emfio", "\t\tStyle: 0x" << std::hex << nStyle << std::dec);
|
|
SAL_INFO("emfio", "\t\tWidth: " << nWidth);
|
|
Color aColorRef = ReadColor();
|
|
mpInputStream->ReadInt32(elpHatch).ReadUInt32(elpNumEntries);
|
|
|
|
if (!mpInputStream->good())
|
|
bStatus = false;
|
|
else
|
|
CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>(aColorRef, nStyle, nWidth));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_CREATEBRUSHINDIRECT :
|
|
{
|
|
mpInputStream->ReadUInt32( nIndex );
|
|
if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
|
|
{
|
|
sal_uInt32 nStyle;
|
|
mpInputStream->ReadUInt32( nStyle );
|
|
BrushStyle eStyle = static_cast<BrushStyle>(nStyle);
|
|
CreateObjectIndexed(nIndex, std::make_unique<WinMtfFillStyle>( ReadColor(), ( eStyle == BrushStyle::BS_HOLLOW ) ));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_DELETEOBJECT :
|
|
{
|
|
mpInputStream->ReadUInt32( nIndex );
|
|
if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
|
|
DeleteObject( nIndex );
|
|
}
|
|
break;
|
|
|
|
case EMR_ELLIPSE :
|
|
{
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
|
|
SAL_INFO("emfio", "\t\t Rectangle, left: " << nX32 << ", top: " << nY32 << ", right: " << nx32 << ", bottom: " << ny32);
|
|
|
|
sal_Int32 w(0), h(0);
|
|
if (o3tl::checked_sub(nx32, nX32, w) || o3tl::checked_sub(ny32, nY32, h))
|
|
SAL_WARN("emfio", "broken ellipse");
|
|
else
|
|
{
|
|
tools::Long dw = w / 2;
|
|
tools::Long dh = h / 2;
|
|
Point aCenter( nX32 + dw, nY32 + dh );
|
|
tools::Polygon aPoly( aCenter, dw, dh );
|
|
DrawPolygon( std::move(aPoly), mbRecordPath );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_RECTANGLE :
|
|
{
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
|
|
SAL_INFO("emfio", "\t\t Rectangle, left: " << nX32 << ", top: " << nY32 << ", right: " << nx32 << ", bottom: " << ny32);
|
|
Point aPoints[] { Point(nX32, nY32),
|
|
Point(nx32, nY32),
|
|
Point(nx32, ny32),
|
|
Point(nX32, ny32) };
|
|
tools::Polygon aPoly(4, aPoints);
|
|
aPoly.Optimize( PolyOptimizeFlags::CLOSE );
|
|
DrawPolygon( std::move(aPoly), mbRecordPath );
|
|
}
|
|
break;
|
|
|
|
case EMR_ROUNDRECT :
|
|
{
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nW ).ReadUInt32( nH );
|
|
tools::Polygon aRoundRectPoly( ReadRectangle( nX32, nY32, nx32, ny32 ), nW, nH );
|
|
DrawPolygon( std::move(aRoundRectPoly), mbRecordPath );
|
|
}
|
|
break;
|
|
|
|
case EMR_ARC :
|
|
case EMR_ARCTO :
|
|
case EMR_CHORD :
|
|
{
|
|
sal_Int32 nStartX, nStartY, nEndX, nEndY;
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadInt32( nStartX ).ReadInt32( nStartY ).ReadInt32( nEndX ).ReadInt32( nEndY );
|
|
if (!mpInputStream->good())
|
|
bStatus = false;
|
|
else
|
|
{
|
|
SAL_INFO( "emfio", "\t\t Bounds: " << nX32 << ":" << nY32 << ", " << nx32 << ":" << ny32 << ", Start: " << nStartX << ":" << nStartY << ", End: " << nEndX << ":" << nEndY );
|
|
tools::Polygon aPoly(ReadRectangle(nX32, nY32, nx32, ny32), Point(nStartX, nStartY), Point(nEndX, nEndY), PolyStyle::Arc, IsArcDirectionClockWise());
|
|
|
|
if ( nRecType == EMR_CHORD )
|
|
DrawPolygon( std::move(aPoly), mbRecordPath );
|
|
else
|
|
DrawPolyLine( std::move(aPoly), nRecType == EMR_ARCTO, mbRecordPath );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_PIE :
|
|
{
|
|
sal_Int32 nStartX, nStartY, nEndX, nEndY;
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadInt32( nStartX ).ReadInt32( nStartY ).ReadInt32( nEndX ).ReadInt32( nEndY );
|
|
if (!mpInputStream->good())
|
|
bStatus = false;
|
|
else
|
|
{
|
|
tools::Polygon aPoly(ReadRectangle(nX32, nY32, nx32, ny32), Point(nStartX, nStartY), Point(nEndX, nEndY), PolyStyle::Pie, IsArcDirectionClockWise());
|
|
DrawPolygon( std::move(aPoly), mbRecordPath );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_LINETO :
|
|
{
|
|
mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
|
|
LineTo( Point( nX32, nY32 ), mbRecordPath);
|
|
}
|
|
break;
|
|
|
|
case EMR_BEGINPATH :
|
|
{
|
|
ClearPath();
|
|
mbRecordPath = true;
|
|
}
|
|
break;
|
|
|
|
case EMR_ABORTPATH :
|
|
ClearPath();
|
|
[[fallthrough]];
|
|
case EMR_ENDPATH :
|
|
mbRecordPath = false;
|
|
break;
|
|
|
|
case EMR_CLOSEFIGURE :
|
|
ClosePath();
|
|
break;
|
|
|
|
case EMR_FILLPATH :
|
|
StrokeAndFillPath( false, true );
|
|
break;
|
|
|
|
case EMR_STROKEANDFILLPATH :
|
|
StrokeAndFillPath( true, true );
|
|
break;
|
|
|
|
case EMR_STROKEPATH :
|
|
StrokeAndFillPath( true, false );
|
|
break;
|
|
|
|
case EMR_SELECTCLIPPATH :
|
|
{
|
|
sal_Int32 nClippingMode(0);
|
|
mpInputStream->ReadInt32(nClippingMode);
|
|
SetClipPath(GetPathObj(), static_cast<RegionMode>(nClippingMode), true);
|
|
}
|
|
break;
|
|
|
|
case EMR_EXTSELECTCLIPRGN :
|
|
{
|
|
sal_uInt32 nRemainingRecSize = nRecSize - 8;
|
|
if (nRemainingRecSize < 8)
|
|
bStatus = false;
|
|
else
|
|
{
|
|
sal_Int32 nClippingMode(0), cbRgnData(0);
|
|
mpInputStream->ReadInt32(cbRgnData);
|
|
mpInputStream->ReadInt32(nClippingMode);
|
|
nRemainingRecSize -= 8;
|
|
|
|
// This record's region data should be ignored if mode
|
|
// is RGN_COPY - see EMF spec section 2.3.2.2
|
|
if (static_cast<RegionMode>(nClippingMode) == RegionMode::RGN_COPY)
|
|
{
|
|
SetDefaultClipPath();
|
|
}
|
|
else
|
|
{
|
|
basegfx::B2DPolyPolygon aPolyPoly;
|
|
if (cbRgnData)
|
|
ImplReadRegion(aPolyPoly, *mpInputStream, nRemainingRecSize);
|
|
const tools::PolyPolygon aPolyPolygon(aPolyPoly);
|
|
SetClipPath(aPolyPolygon, static_cast<RegionMode>(nClippingMode), false);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_ALPHABLEND:
|
|
{
|
|
sal_Int32 xDest(0), yDest(0), cxDest(0), cyDest(0);
|
|
|
|
BLENDFUNCTION aFunc;
|
|
sal_Int32 xSrc(0), ySrc(0), cxSrc(0), cySrc(0);
|
|
XForm xformSrc;
|
|
sal_uInt32 BkColorSrc(0), iUsageSrc(0), offBmiSrc(0);
|
|
sal_uInt32 cbBmiSrc(0), offBitsSrc(0), cbBitsSrc(0);
|
|
|
|
sal_uInt32 nStart = mpInputStream->Tell() - 8;
|
|
mpInputStream->SeekRel( 0x10 );
|
|
|
|
mpInputStream->ReadInt32( xDest ).ReadInt32( yDest ).ReadInt32( cxDest ).ReadInt32( cyDest );
|
|
*mpInputStream >> aFunc;
|
|
mpInputStream->ReadInt32( xSrc ).ReadInt32( ySrc );
|
|
*mpInputStream >> xformSrc;
|
|
mpInputStream->ReadUInt32( BkColorSrc ).ReadUInt32( iUsageSrc ).ReadUInt32( offBmiSrc ).ReadUInt32( cbBmiSrc )
|
|
.ReadUInt32( offBitsSrc ).ReadUInt32( cbBitsSrc ).ReadInt32( cxSrc ).ReadInt32( cySrc ) ;
|
|
|
|
if ( !mpInputStream->good() ||
|
|
(cbBitsSrc > (SAL_MAX_UINT32 - 14)) ||
|
|
((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc) ||
|
|
cxDest == SAL_MAX_INT32 || cyDest == SAL_MAX_INT32 )
|
|
{
|
|
bStatus = false;
|
|
}
|
|
else
|
|
{
|
|
tools::Rectangle aRect(Point(xDest, yDest), Size(cxDest + 1, cyDest + 1));
|
|
|
|
const sal_uInt32 nSourceSize = cbBmiSrc + cbBitsSrc + 14;
|
|
bool bSafeRead = nSourceSize <= (mnEndPos - mnStartPos);
|
|
sal_uInt32 nDeltaToDIB5HeaderSize(0);
|
|
const bool bReadAlpha(0x01 == aFunc.aAlphaFormat);
|
|
if (bSafeRead && bReadAlpha)
|
|
{
|
|
// we need to read alpha channel data if AlphaFormat of BLENDFUNCTION is
|
|
// AC_SRC_ALPHA (==0x01). To read it, create a temp DIB-File which is ready
|
|
// for DIB-5 format
|
|
const sal_uInt32 nHeaderSize = getDIBV5HeaderSize();
|
|
if (cbBmiSrc > nHeaderSize)
|
|
bSafeRead = false;
|
|
else
|
|
nDeltaToDIB5HeaderSize = nHeaderSize - cbBmiSrc;
|
|
}
|
|
if (bSafeRead)
|
|
{
|
|
const sal_uInt32 nTargetSize(cbBmiSrc + nDeltaToDIB5HeaderSize + cbBitsSrc + 14);
|
|
char* pBuf = new char[ nTargetSize ];
|
|
SvMemoryStream aTmp( pBuf, nTargetSize, StreamMode::READ | StreamMode::WRITE );
|
|
|
|
aTmp.ObjectOwnsMemory( true );
|
|
|
|
// write BM-Header (14 bytes)
|
|
aTmp.WriteUChar( 'B' )
|
|
.WriteUChar( 'M' )
|
|
.WriteUInt32( cbBitsSrc )
|
|
.WriteUInt16( 0 )
|
|
.WriteUInt16( 0 )
|
|
.WriteUInt32( cbBmiSrc + nDeltaToDIB5HeaderSize + 14 );
|
|
|
|
// copy DIBInfoHeader from source (cbBmiSrc bytes)
|
|
mpInputStream->Seek( nStart + offBmiSrc );
|
|
char* pWritePos = pBuf + 14;
|
|
auto nRead = mpInputStream->ReadBytes(pWritePos, cbBmiSrc);
|
|
if (nRead != cbBmiSrc)
|
|
{
|
|
// zero remainder if short read
|
|
memset(pWritePos + nRead, 0, cbBmiSrc - nRead);
|
|
}
|
|
|
|
if (bReadAlpha)
|
|
{
|
|
// need to add values for all stuff that DIBV5Header is bigger
|
|
// than DIBInfoHeader, all values are correctly initialized to zero,
|
|
// so we can use memset here
|
|
memset(pBuf + cbBmiSrc + 14, 0, nDeltaToDIB5HeaderSize);
|
|
}
|
|
|
|
// copy bitmap data from source (offBitsSrc bytes)
|
|
mpInputStream->Seek( nStart + offBitsSrc );
|
|
pWritePos = pBuf + 14 + nDeltaToDIB5HeaderSize + cbBmiSrc;
|
|
nRead = mpInputStream->ReadBytes(pWritePos, cbBitsSrc);
|
|
if (nRead != cbBitsSrc)
|
|
{
|
|
// zero remainder if short read
|
|
memset(pWritePos + nRead, 0, cbBitsSrc - nRead);
|
|
}
|
|
|
|
aTmp.Seek( 0 );
|
|
|
|
// prepare to read and fill BitmapEx
|
|
BitmapEx aBitmapEx;
|
|
|
|
if(bReadAlpha)
|
|
{
|
|
Bitmap aBitmap;
|
|
AlphaMask aAlpha;
|
|
|
|
if(ReadDIBV5(aBitmap, aAlpha, aTmp))
|
|
{
|
|
aBitmapEx = BitmapEx(aBitmap, aAlpha);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Bitmap aBitmap;
|
|
|
|
if(ReadDIB(aBitmap, aTmp, true))
|
|
{
|
|
if(0xff != aFunc.aSrcConstantAlpha)
|
|
{
|
|
// add const alpha channel
|
|
aBitmapEx = BitmapEx(
|
|
aBitmap,
|
|
AlphaMask(aBitmap.GetSizePixel(), &aFunc.aSrcConstantAlpha));
|
|
}
|
|
else
|
|
{
|
|
// just use Bitmap
|
|
aBitmapEx = BitmapEx(aBitmap);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!aBitmapEx.IsEmpty())
|
|
{
|
|
// test if it is sensible to crop
|
|
if (cxSrc > 0 && cySrc > 0 && xSrc >= 0 && ySrc >= 0)
|
|
{
|
|
sal_Int32 xEndSrc;
|
|
sal_Int32 yEndSrc;
|
|
if (!o3tl::checked_add(xSrc, cxSrc, xEndSrc) && xEndSrc < aBitmapEx.GetSizePixel().Width() &&
|
|
!o3tl::checked_add(ySrc, cySrc, yEndSrc) && yEndSrc < aBitmapEx.GetSizePixel().Height())
|
|
{
|
|
const tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
|
|
aBitmapEx.Crop( aCropRect );
|
|
}
|
|
}
|
|
|
|
#ifdef DBG_UTIL
|
|
static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
|
|
|
|
if(bDoSaveForVisualControl)
|
|
{
|
|
SvFileStream aNew("c:\\metafile_content.png", StreamMode::WRITE|StreamMode::TRUNC);
|
|
vcl::PngImageWriter aPNGWriter(aNew);
|
|
aPNGWriter.write(aBitmapEx);
|
|
}
|
|
#endif
|
|
maBmpSaveList.emplace_back(aBitmapEx, aRect, SRCAND|SRCINVERT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_BITBLT : // PASSTHROUGH INTENDED
|
|
case EMR_STRETCHBLT :
|
|
{
|
|
sal_Int32 xDest, yDest, cxDest, cyDest, xSrc, ySrc, cxSrc, cySrc;
|
|
sal_uInt32 dwRop, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc;
|
|
XForm xformSrc;
|
|
|
|
sal_uInt32 nStart = mpInputStream->Tell() - 8;
|
|
|
|
mpInputStream->SeekRel( 0x10 );
|
|
mpInputStream->ReadInt32( xDest ).ReadInt32( yDest ).ReadInt32( cxDest ).ReadInt32( cyDest ).ReadUInt32( dwRop ).ReadInt32( xSrc ).ReadInt32( ySrc )
|
|
>> xformSrc;
|
|
mpInputStream->ReadUInt32( nColor ).ReadUInt32( iUsageSrc ).ReadUInt32( offBmiSrc ).ReadUInt32( cbBmiSrc )
|
|
.ReadUInt32( offBitsSrc ).ReadUInt32( cbBitsSrc );
|
|
|
|
if ( nRecType == EMR_STRETCHBLT )
|
|
mpInputStream->ReadInt32( cxSrc ).ReadInt32( cySrc );
|
|
else
|
|
cxSrc = cySrc = 0;
|
|
|
|
if (!mpInputStream->good() || (cbBitsSrc > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc))
|
|
bStatus = false;
|
|
else
|
|
{
|
|
Bitmap aBitmap;
|
|
tools::Rectangle aRect(Point(xDest, yDest), Size(cxDest, cyDest));
|
|
|
|
sal_uInt32 nSize = cbBmiSrc + cbBitsSrc + 14;
|
|
if ( nSize <= ( mnEndPos - mnStartPos ) )
|
|
{
|
|
char* pBuf = new char[ nSize ];
|
|
SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
|
|
aTmp.ObjectOwnsMemory( true );
|
|
aTmp.WriteUChar( 'B' )
|
|
.WriteUChar( 'M' )
|
|
.WriteUInt32( cbBitsSrc )
|
|
.WriteUInt16( 0 )
|
|
.WriteUInt16( 0 )
|
|
.WriteUInt32( cbBmiSrc + 14 );
|
|
|
|
mpInputStream->Seek( nStart + offBmiSrc );
|
|
char* pWritePos = pBuf + 14;
|
|
auto nRead = mpInputStream->ReadBytes(pWritePos, cbBmiSrc);
|
|
if (nRead != cbBmiSrc)
|
|
{
|
|
// zero remainder if short read
|
|
memset(pWritePos + nRead, 0, cbBmiSrc - nRead);
|
|
}
|
|
|
|
mpInputStream->Seek( nStart + offBitsSrc );
|
|
pWritePos = pBuf + 14 + cbBmiSrc;
|
|
nRead = mpInputStream->ReadBytes(pWritePos, cbBitsSrc);
|
|
if (nRead != cbBitsSrc)
|
|
{
|
|
// zero remainder if short read
|
|
memset(pWritePos + nRead, 0, cbBitsSrc - nRead);
|
|
}
|
|
|
|
aTmp.Seek( 0 );
|
|
ReadDIB(aBitmap, aTmp, true);
|
|
|
|
// test if it is sensible to crop
|
|
if ( (cxSrc > 0) && (cySrc > 0) &&
|
|
(xSrc >= 0) && (ySrc >= 0) &&
|
|
(aBitmap.GetSizePixel().Width() >= cxSrc) &&
|
|
(xSrc <= aBitmap.GetSizePixel().Width() - cxSrc) &&
|
|
(aBitmap.GetSizePixel().Height() >= cySrc) &&
|
|
(ySrc <= aBitmap.GetSizePixel().Height() - cySrc) )
|
|
{
|
|
tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
|
|
aBitmap.Crop( aCropRect );
|
|
}
|
|
|
|
maBmpSaveList.emplace_back(aBitmap, aRect, dwRop);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_STRETCHDIBITS :
|
|
{
|
|
sal_Int32 xDest, yDest, xSrc, ySrc, cxSrc, cySrc, cxDest, cyDest;
|
|
sal_uInt32 offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, iUsageSrc, dwRop;
|
|
sal_uInt32 nStart = mpInputStream->Tell() - 8;
|
|
|
|
mpInputStream->SeekRel( 0x10 );
|
|
mpInputStream->ReadInt32( xDest )
|
|
.ReadInt32( yDest )
|
|
.ReadInt32( xSrc )
|
|
.ReadInt32( ySrc )
|
|
.ReadInt32( cxSrc )
|
|
.ReadInt32( cySrc )
|
|
.ReadUInt32( offBmiSrc )
|
|
.ReadUInt32( cbBmiSrc )
|
|
.ReadUInt32( offBitsSrc )
|
|
.ReadUInt32( cbBitsSrc )
|
|
.ReadUInt32( iUsageSrc )
|
|
.ReadUInt32( dwRop )
|
|
.ReadInt32( cxDest )
|
|
.ReadInt32( cyDest );
|
|
|
|
if (!mpInputStream->good() ||
|
|
((SAL_MAX_UINT32 - 14) < cbBitsSrc) ||
|
|
((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc))
|
|
{
|
|
bStatus = false;
|
|
}
|
|
else
|
|
{
|
|
Bitmap aBitmap;
|
|
tools::Rectangle aRect(xDest, yDest);
|
|
aRect.SaturatingSetSize(Size(cxDest, cyDest));
|
|
|
|
sal_uInt32 nSize = cbBmiSrc + cbBitsSrc + 14;
|
|
if ( nSize <= ( mnEndPos - mnStartPos ) )
|
|
{
|
|
char* pBuf = new char[ nSize ];
|
|
SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
|
|
aTmp.ObjectOwnsMemory( true );
|
|
aTmp.WriteUChar( 'B' )
|
|
.WriteUChar( 'M' )
|
|
.WriteUInt32( cbBitsSrc )
|
|
.WriteUInt16( 0 )
|
|
.WriteUInt16( 0 )
|
|
.WriteUInt32( cbBmiSrc + 14 );
|
|
|
|
mpInputStream->Seek( nStart + offBmiSrc );
|
|
char* pWritePos = pBuf + 14;
|
|
auto nRead = mpInputStream->ReadBytes(pWritePos, cbBmiSrc);
|
|
if (nRead != cbBmiSrc)
|
|
{
|
|
// zero remainder if short read
|
|
memset(pWritePos + nRead, 0, cbBmiSrc - nRead);
|
|
}
|
|
|
|
mpInputStream->Seek( nStart + offBitsSrc );
|
|
pWritePos = pBuf + 14 + cbBmiSrc;
|
|
nRead = mpInputStream->ReadBytes(pWritePos, cbBitsSrc);
|
|
if (nRead != cbBitsSrc)
|
|
{
|
|
// zero remainder if short read
|
|
memset(pWritePos + nRead, 0, cbBitsSrc - nRead);
|
|
}
|
|
|
|
aTmp.Seek( 0 );
|
|
ReadDIB(aBitmap, aTmp, true);
|
|
|
|
const tools::Long nWidthDiff = aBitmap.GetSizePixel().Width() - cxSrc;
|
|
const tools::Long nHeightDiff = aBitmap.GetSizePixel().Height() - cySrc;
|
|
|
|
// test if it is sensible to crop
|
|
if ( (cxSrc > 0) && (cySrc > 0) &&
|
|
(xSrc >= 0) && (ySrc >= 0) &&
|
|
(xSrc <= nWidthDiff) && (ySrc <= nHeightDiff) )
|
|
{
|
|
tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
|
|
aBitmap.Crop( aCropRect );
|
|
}
|
|
maBmpSaveList.emplace_back(aBitmap, aRect, dwRop);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_EXTCREATEFONTINDIRECTW :
|
|
{
|
|
mpInputStream->ReadUInt32( nIndex );
|
|
if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
|
|
{
|
|
LOGFONTW aLogFont;
|
|
mpInputStream->ReadInt32( aLogFont.lfHeight )
|
|
.ReadInt32( aLogFont.lfWidth )
|
|
.ReadInt32( aLogFont.lfEscapement )
|
|
.ReadInt32( aLogFont.lfOrientation )
|
|
.ReadInt32( aLogFont.lfWeight )
|
|
.ReadUChar( aLogFont.lfItalic )
|
|
.ReadUChar( aLogFont.lfUnderline )
|
|
.ReadUChar( aLogFont.lfStrikeOut )
|
|
.ReadUChar( aLogFont.lfCharSet )
|
|
.ReadUChar( aLogFont.lfOutPrecision )
|
|
.ReadUChar( aLogFont.lfClipPrecision )
|
|
.ReadUChar( aLogFont.lfQuality )
|
|
.ReadUChar( aLogFont.lfPitchAndFamily );
|
|
|
|
sal_Unicode lfFaceName[LF_FACESIZE+1];
|
|
lfFaceName[LF_FACESIZE] = 0;
|
|
for (int i = 0; i < LF_FACESIZE; ++i)
|
|
{
|
|
sal_uInt16 nChar(0);
|
|
mpInputStream->ReadUInt16(nChar);
|
|
lfFaceName[i] = nChar;
|
|
}
|
|
aLogFont.alfFaceName = OUString( lfFaceName );
|
|
|
|
// #i123216# Not used in the test case of #121382# (always identity in XForm), also
|
|
// no hints in ms docu if FontSize should be scaled with WT. Using with the example
|
|
// from #i123216# creates errors, so removing.
|
|
|
|
// // #i121382# Need to apply WorldTransform to FontHeight/Width; this should be completely
|
|
// // changed to basegfx::B2DHomMatrix instead of 'struct XForm', but not now due to time
|
|
// // constraints and dangers
|
|
// const XForm& rXF = GetWorldTransform();
|
|
// const basegfx::B2DHomMatrix aWT(rXF.eM11, rXF.eM21, rXF.eDx, rXF.eM12, rXF.eM22, rXF.eDy);
|
|
// const basegfx::B2DVector aTransVec(aWT * basegfx::B2DVector(aLogFont.lfWidth, aLogFont.lfHeight));
|
|
// aLogFont.lfWidth = aTransVec.getX();
|
|
// aLogFont.lfHeight = aTransVec.getY();
|
|
if (mpInputStream->good() && aLogFont.lfHeight != SAL_MIN_INT32 && aLogFont.lfWidth != SAL_MIN_INT32)
|
|
{
|
|
CreateObjectIndexed(nIndex, std::make_unique<WinMtfFontStyle>( aLogFont ));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_POLYTEXTOUTA :
|
|
case EMR_EXTTEXTOUTA :
|
|
bFlag = true;
|
|
[[fallthrough]];
|
|
case EMR_POLYTEXTOUTW :
|
|
case EMR_EXTTEXTOUTW :
|
|
{
|
|
sal_Int32 nLeft, nTop, nRight, nBottom;
|
|
sal_uInt32 nGfxMode;
|
|
float nXScale, nYScale;
|
|
sal_uInt32 ncStrings( 1 );
|
|
sal_Int32 ptlReferenceX, ptlReferenceY;
|
|
sal_uInt32 nLen, nOffString, nOptions, offDx;
|
|
sal_Int32 nLeftRect, nTopRect, nRightRect, nBottomRect;
|
|
|
|
nCurPos = mpInputStream->Tell() - 8;
|
|
|
|
mpInputStream->ReadInt32( nLeft ).ReadInt32( nTop ).ReadInt32( nRight ).ReadInt32( nBottom )
|
|
.ReadUInt32( nGfxMode ).ReadFloat( nXScale ).ReadFloat( nYScale );
|
|
SAL_INFO("emfio", "\t\tBounds: " << nLeft << ", " << nTop << ", " << nRight << ", " << nBottom);
|
|
SAL_INFO("emfio", "\t\tiGraphicsMode: 0x" << std::hex << nGfxMode << std::dec);
|
|
SAL_INFO("emfio", "\t\t Scale: " << nXScale << " x " << nYScale);
|
|
if ( ( nRecType == EMR_POLYTEXTOUTA ) || ( nRecType == EMR_POLYTEXTOUTW ) )
|
|
{
|
|
mpInputStream->ReadUInt32( ncStrings );
|
|
SAL_INFO("emfio", "\t\t Number of Text objects: " << ncStrings);
|
|
if ( ncStrings == 0 )
|
|
break;
|
|
}
|
|
mpInputStream->ReadInt32( ptlReferenceX ).ReadInt32( ptlReferenceY ).ReadUInt32( nLen ).ReadUInt32( nOffString ).ReadUInt32( nOptions );
|
|
SAL_INFO("emfio", "\t\tReference: (" << ptlReferenceX << ", " << ptlReferenceY << ")");
|
|
|
|
mpInputStream->ReadInt32( nLeftRect ).ReadInt32( nTopRect ).ReadInt32( nRightRect ).ReadInt32( nBottomRect );
|
|
mpInputStream->ReadUInt32( offDx );
|
|
|
|
if (!mpInputStream->good())
|
|
bStatus = false;
|
|
else
|
|
{
|
|
const tools::Rectangle aRect( nLeftRect, nTopRect, nRightRect, nBottomRect );
|
|
const BackgroundMode mnBkModeBackup = mnBkMode;
|
|
if ( nOptions & ETO_NO_RECT ) // Don't draw the background rectangle and text background
|
|
mnBkMode = BackgroundMode::Transparent;
|
|
else if ( nOptions & ETO_OPAQUE )
|
|
DrawRectWithBGColor( aRect );
|
|
|
|
// ETO_RTLREADING indicates that the characters are laid from right to left
|
|
vcl::text::ComplexTextLayoutFlags nTextLayoutMode = vcl::text::ComplexTextLayoutFlags::Default;
|
|
if ( nOptions & ETO_RTLREADING )
|
|
nTextLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
|
|
SetTextLayoutMode( nTextLayoutMode );
|
|
SAL_WARN_IF( ( nOptions & ( ETO_PDY | ETO_GLYPH_INDEX ) ) != 0, "emfio", "SJ: ETO_PDY || ETO_GLYPH_INDEX in EMF" );
|
|
|
|
Point aPos( ptlReferenceX, ptlReferenceY );
|
|
bool bOffStringSane = nOffString <= mnEndPos - nCurPos;
|
|
if ( bOffStringSane )
|
|
{
|
|
mpInputStream->Seek( nCurPos + nOffString );
|
|
OUString aText;
|
|
if ( bFlag )
|
|
{
|
|
if ( nLen <= ( mnEndPos - mpInputStream->Tell() ) )
|
|
{
|
|
std::vector<char> pBuf( nLen );
|
|
mpInputStream->ReadBytes(pBuf.data(), nLen);
|
|
aText = OUString(pBuf.data(), nLen, GetCharSet());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ( nLen * sizeof(sal_Unicode) ) <= ( mnEndPos - mpInputStream->Tell() ) )
|
|
{
|
|
aText = read_uInt16s_ToOUString(*mpInputStream, nLen);
|
|
}
|
|
}
|
|
|
|
SAL_INFO("emfio", "\t\tText: " << aText);
|
|
SAL_INFO("emfio", "\t\tDxBuffer:");
|
|
|
|
std::vector<sal_Int32> aDXAry;
|
|
std::unique_ptr<tools::Long[]> pDYAry;
|
|
|
|
sal_Int32 nDxSize;
|
|
sal_Int32 nBytesEach;
|
|
|
|
// Reading OutputDx
|
|
// ETO_PDY flag indicates that we should read twice values
|
|
// compared to the number of characters in the output string.
|
|
// Values are stored in an array of 32-bit unsigned integers
|
|
// named OutputDx, so there will be either 8 bytes or 4 bytes
|
|
// each depending on ETO_PDY is set or not.
|
|
if (nOptions & ETO_PDY)
|
|
nBytesEach = 8;
|
|
else
|
|
nBytesEach = 4;
|
|
|
|
bool bOverflow = o3tl::checked_multiply<sal_Int32>(nLen, nBytesEach, nDxSize);
|
|
if (!bOverflow && offDx && ((nCurPos + offDx + nDxSize) <= nNextPos ) && nNextPos <= mnEndPos)
|
|
{
|
|
mpInputStream->Seek( nCurPos + offDx );
|
|
aDXAry.resize(aText.getLength());
|
|
if (nOptions & ETO_PDY)
|
|
{
|
|
pDYAry.reset( new tools::Long[aText.getLength()] );
|
|
}
|
|
|
|
for (sal_Int32 i = 0; i < aText.getLength(); ++i)
|
|
{
|
|
sal_Int32 nDxCount = 1;
|
|
if ( static_cast<sal_uInt32>( aText.getLength() ) != nLen )
|
|
{
|
|
sal_Unicode cUniChar = aText[i];
|
|
OString aTmp(&cUniChar, 1, GetCharSet());
|
|
if (aTmp.getLength() > 1)
|
|
{
|
|
nDxCount = aTmp.getLength();
|
|
}
|
|
}
|
|
|
|
aDXAry[i] = 0;
|
|
if (nOptions & ETO_PDY)
|
|
{
|
|
pDYAry[i] = 0;
|
|
}
|
|
|
|
while (nDxCount--)
|
|
{
|
|
sal_Int32 nDxTmp = 0;
|
|
mpInputStream->ReadInt32(nDxTmp);
|
|
aDXAry[i] = o3tl::saturating_add(aDXAry[i], nDxTmp);
|
|
if (nOptions & ETO_PDY)
|
|
{
|
|
sal_Int32 nDyTmp = 0;
|
|
mpInputStream->ReadInt32(nDyTmp);
|
|
pDYAry[i] += nDyTmp;
|
|
}
|
|
}
|
|
|
|
SAL_INFO("emfio", "\t\t\tSpacing " << i << ": " << aDXAry[i]);
|
|
}
|
|
}
|
|
if ( nOptions & ETO_CLIPPED )
|
|
{
|
|
Push(); // Save the current clip. It will be restored after text drawing
|
|
IntersectClipRect( aRect );
|
|
}
|
|
DrawText(aPos, aText, aDXAry.empty() ? nullptr : &aDXAry, pDYAry.get(), mbRecordPath, static_cast<GraphicsMode>(nGfxMode));
|
|
if ( nOptions & ETO_CLIPPED )
|
|
Pop();
|
|
}
|
|
mnBkMode = mnBkModeBackup;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_POLYBEZIERTO16 :
|
|
DrawPolyBezier(ReadPolygonWithSkip<sal_Int16>(true, nNextPos), true, mbRecordPath);
|
|
break;
|
|
|
|
case EMR_POLYBEZIER16 :
|
|
DrawPolyBezier(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), false, mbRecordPath);
|
|
break;
|
|
|
|
case EMR_POLYGON16 :
|
|
DrawPolygon(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), mbRecordPath);
|
|
break;
|
|
|
|
case EMR_POLYLINETO16 :
|
|
DrawPolyLine(ReadPolygonWithSkip<sal_Int16>(true, nNextPos), true, mbRecordPath);
|
|
break;
|
|
|
|
case EMR_POLYLINE16 :
|
|
DrawPolyLine(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), false, mbRecordPath);
|
|
break;
|
|
|
|
case EMR_POLYPOLYLINE16 :
|
|
ReadAndDrawPolyLine<sal_Int16>(nNextPos);
|
|
break;
|
|
|
|
case EMR_POLYPOLYGON16 :
|
|
ReadAndDrawPolyPolygon<sal_Int16>(nNextPos);
|
|
break;
|
|
|
|
case EMR_FILLRGN :
|
|
{
|
|
sal_uInt32 nRemainingRecSize = nRecSize - 8;
|
|
if (nRemainingRecSize < 24)
|
|
bStatus = false;
|
|
else
|
|
{
|
|
sal_uInt32 nRgnDataSize;
|
|
basegfx::B2DPolyPolygon aPolyPoly;
|
|
mpInputStream->SeekRel(16); // RectL bounds
|
|
mpInputStream->ReadUInt32( nRgnDataSize ).ReadUInt32( nIndex );
|
|
nRemainingRecSize -= 24;
|
|
|
|
if (ImplReadRegion(aPolyPoly, *mpInputStream, nRemainingRecSize))
|
|
{
|
|
Push();
|
|
SelectObject( nIndex );
|
|
tools::PolyPolygon aPolyPolygon(aPolyPoly);
|
|
DrawPolyPolygon( aPolyPolygon );
|
|
Pop();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_PAINTRGN :
|
|
{
|
|
sal_uInt32 nRemainingRecSize = nRecSize - 8;
|
|
if (nRemainingRecSize < 20)
|
|
bStatus = false;
|
|
else
|
|
{
|
|
sal_uInt32 nRgnDataSize;
|
|
basegfx::B2DPolyPolygon aPolyPoly;
|
|
mpInputStream->SeekRel(16); // Skipping RectL bounds
|
|
mpInputStream->ReadUInt32( nRgnDataSize );
|
|
nRemainingRecSize -= 20;
|
|
|
|
if (ImplReadRegion(aPolyPoly, *mpInputStream, nRemainingRecSize))
|
|
{
|
|
tools::PolyPolygon aPolyPolygon(aPolyPoly);
|
|
DrawPolyPolygon( aPolyPolygon );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EMR_CREATEDIBPATTERNBRUSHPT :
|
|
{
|
|
sal_uInt32 nStart = mpInputStream->Tell() - 8;
|
|
Bitmap aBitmap;
|
|
|
|
mpInputStream->ReadUInt32( nIndex );
|
|
|
|
if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
|
|
{
|
|
sal_uInt32 usage, offBmi, cbBmi, offBits, cbBits;
|
|
|
|
mpInputStream->ReadUInt32( usage );
|
|
mpInputStream->ReadUInt32( offBmi );
|
|
mpInputStream->ReadUInt32( cbBmi );
|
|
mpInputStream->ReadUInt32( offBits );
|
|
mpInputStream->ReadUInt32( cbBits );
|
|
|
|
if ( !mpInputStream->good() || (cbBits > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBits < cbBmi) )
|
|
bStatus = false;
|
|
else if ( offBmi )
|
|
{
|
|
sal_uInt32 nSize = cbBmi + cbBits + 14;
|
|
if ( nSize <= ( mnEndPos - mnStartPos ) )
|
|
{
|
|
char* pBuf = new char[ nSize ];
|
|
|
|
SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
|
|
aTmp.ObjectOwnsMemory( true );
|
|
aTmp.WriteUChar( 'B' )
|
|
.WriteUChar( 'M' )
|
|
.WriteUInt32( cbBits )
|
|
.WriteUInt16( 0 )
|
|
.WriteUInt16( 0 )
|
|
.WriteUInt32( cbBmi + 14 );
|
|
|
|
mpInputStream->Seek( nStart + offBmi );
|
|
char* pWritePos = pBuf + 14;
|
|
auto nRead = mpInputStream->ReadBytes(pWritePos, cbBmi);
|
|
if (nRead != cbBmi)
|
|
{
|
|
// zero remainder if short read
|
|
memset(pWritePos + nRead, 0, cbBmi - nRead);
|
|
}
|
|
|
|
mpInputStream->Seek( nStart + offBits );
|
|
pWritePos = pBuf + 14 + cbBmi;
|
|
nRead = mpInputStream->ReadBytes(pWritePos, cbBits);
|
|
if (nRead != cbBits)
|
|
{
|
|
// zero remainder if short read
|
|
memset(pWritePos + nRead, 0, cbBits - nRead);
|
|
}
|
|
|
|
aTmp.Seek( 0 );
|
|
ReadDIB(aBitmap, aTmp, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
CreateObjectIndexed(nIndex, std::make_unique<WinMtfFillStyle>( aBitmap ));
|
|
}
|
|
break;
|
|
|
|
case EMR_MASKBLT :
|
|
case EMR_PLGBLT :
|
|
case EMR_SETDIBITSTODEVICE :
|
|
case EMR_FRAMERGN :
|
|
case EMR_INVERTRGN :
|
|
case EMR_FLATTENPATH :
|
|
case EMR_WIDENPATH :
|
|
case EMR_POLYDRAW :
|
|
case EMR_SETPALETTEENTRIES :
|
|
case EMR_RESIZEPALETTE :
|
|
case EMR_EXTFLOODFILL :
|
|
case EMR_ANGLEARC :
|
|
case EMR_SETCOLORADJUSTMENT :
|
|
case EMR_POLYDRAW16 :
|
|
case EMR_CREATECOLORSPACE :
|
|
case EMR_SETCOLORSPACE :
|
|
case EMR_DELETECOLORSPACE :
|
|
case EMR_GLSRECORD :
|
|
case EMR_GLSBOUNDEDRECORD :
|
|
case EMR_PIXELFORMAT :
|
|
case EMR_DRAWESCAPE :
|
|
case EMR_EXTESCAPE :
|
|
case EMR_STARTDOC :
|
|
case EMR_SMALLTEXTOUT :
|
|
case EMR_FORCEUFIMAPPING :
|
|
case EMR_NAMEDESCAPE :
|
|
case EMR_COLORCORRECTPALETTE :
|
|
case EMR_SETICMPROFILEA :
|
|
case EMR_SETICMPROFILEW :
|
|
case EMR_TRANSPARENTBLT :
|
|
case EMR_TRANSPARENTDIB :
|
|
case EMR_GRADIENTFILL :
|
|
case EMR_SETLINKEDUFIS :
|
|
case EMR_SETMAPPERFLAGS :
|
|
case EMR_SETICMMODE :
|
|
case EMR_CREATEMONOBRUSH :
|
|
case EMR_SETBRUSHORGEX :
|
|
case EMR_SETMETARGN :
|
|
case EMR_SETMITERLIMIT :
|
|
case EMR_EXCLUDECLIPRECT :
|
|
case EMR_REALIZEPALETTE :
|
|
case EMR_SELECTPALETTE :
|
|
case EMR_CREATEPALETTE :
|
|
case EMR_ALPHADIBBLEND :
|
|
case EMR_SETTEXTJUSTIFICATION :
|
|
{
|
|
SAL_WARN("emfio", "TODO: EMF record not implemented: " << record_type_name(nRecType));
|
|
}
|
|
break;
|
|
|
|
case EMR_COMMENT :
|
|
case EMR_HEADER : // has already been read at ReadHeader()
|
|
break;
|
|
|
|
default : SAL_INFO("emfio", "Unknown Meta Action"); break;
|
|
}
|
|
}
|
|
mpInputStream->Seek( nNextPos );
|
|
}
|
|
|
|
// tdf#127471
|
|
maScaledFontHelper.applyAlternativeFontScale();
|
|
|
|
if( !maBmpSaveList.empty() )
|
|
ResolveBitmapActions( maBmpSaveList );
|
|
|
|
if ( bStatus )
|
|
mpInputStream->Seek(mnEndPos);
|
|
|
|
return bStatus;
|
|
};
|
|
|
|
bool EmfReader::ReadHeader()
|
|
{
|
|
// Spare me the METAFILEHEADER here
|
|
// Reading the METAHEADER - EMR_HEADER ([MS-EMF] section 2.3.4.2 EMR_HEADER Record Types)
|
|
sal_uInt32 nType(0), nHeaderSize(0);
|
|
mpInputStream->ReadUInt32(nType).ReadUInt32(nHeaderSize);
|
|
SAL_INFO ("emfio", "0x0-0x" << std::hex << nHeaderSize << " " << record_type_name(nType) << " size: " << std::dec << nHeaderSize);
|
|
if (nType != 0x00000001)
|
|
{
|
|
// per [MS-EMF] 2.3.4.2 EMF Header Record Types, type MUST be 0x00000001
|
|
SAL_WARN("emfio", "EMF header type is not set to 0x00000001 - possibly corrupted file?");
|
|
return false;
|
|
}
|
|
|
|
// Start reading the EMR_HEADER Header object
|
|
|
|
// bound size (RectL object, see [MS-WMF] section 2.2.2.19)
|
|
SAL_INFO("emfio", "\tBounding rectangle");
|
|
tools::Rectangle rclBounds = ReadRectangle(); // rectangle in logical units
|
|
|
|
// picture frame size (RectL object)
|
|
SAL_INFO("emfio", "\tPicture frame");
|
|
tools::Rectangle rclFrame = ReadRectangle(); // rectangle in device units 1/100th mm
|
|
|
|
sal_uInt32 nSignature(0);
|
|
mpInputStream->ReadUInt32(nSignature);
|
|
SAL_INFO("emfio", "\tSignature: 0x" << std::hex << nSignature << std::dec);
|
|
|
|
// nSignature MUST be the ASCII characters "FME", see [WS-EMF] 2.2.9 Header Object
|
|
// and 2.1.14 FormatSignature Enumeration
|
|
if (nSignature != 0x464d4520)
|
|
{
|
|
SAL_WARN("emfio", "EMF\t\tSignature is not 0x464d4520 (\"FME\") - possibly corrupted file?");
|
|
return false;
|
|
}
|
|
|
|
sal_uInt32 nVersion(0);
|
|
mpInputStream->ReadUInt32(nVersion); // according to [WS-EMF] 2.2.9, this SHOULD be 0x0001000, however
|
|
// Microsoft note that not even Windows checks this...
|
|
SAL_INFO("emfio", "\tVersion: 0x" << std::hex << nVersion << std::dec);
|
|
if (nVersion != 0x00010000)
|
|
{
|
|
SAL_WARN("emfio", "EMF\t\tThis really should be 0x00010000, though not absolutely essential...");
|
|
}
|
|
|
|
mpInputStream->ReadUInt32(mnEndPos); // size of metafile
|
|
SAL_INFO("emfio", "\tMetafile size: " << mnEndPos);
|
|
mnEndPos += mnStartPos;
|
|
|
|
sal_uInt32 nStrmPos = mpInputStream->Tell(); // checking if mnEndPos is valid
|
|
sal_uInt32 nActualFileSize = nStrmPos + mpInputStream->remainingSize();
|
|
|
|
if ( nActualFileSize < mnEndPos )
|
|
{
|
|
SAL_WARN("emfio", "EMF\t\tEMF Header object records number of bytes as " << mnEndPos
|
|
<< ", however the file size is actually " << nActualFileSize
|
|
<< " bytes. Possible file corruption?");
|
|
mnEndPos = nActualFileSize;
|
|
}
|
|
|
|
mpInputStream->ReadUInt32(mnRecordCount);
|
|
SAL_INFO("emfio", "\tRecords: " << mnRecordCount);
|
|
|
|
// the number of "handles", or graphics objects used in the metafile
|
|
|
|
sal_uInt16 nHandlesCount;
|
|
mpInputStream->ReadUInt16(nHandlesCount);
|
|
SAL_INFO("emfio", "\tGraphics: " << nHandlesCount);
|
|
|
|
// the next 2 bytes are reserved, but according to [MS-EMF] section 2.2.9
|
|
// it MUST be 0x000 and MUST be ignored... the thing is, having such a specific
|
|
// value is actually pretty useful in checking if there is possible corruption
|
|
|
|
sal_uInt16 nReserved(0);
|
|
mpInputStream->ReadUInt16(nReserved);
|
|
SAL_INFO("emfio", "\tReserved: 0x" << std::hex << nReserved << std::dec);
|
|
|
|
if ( nReserved != 0x0000 )
|
|
{
|
|
SAL_WARN("emfio", "EMF\t\tEMF Header object's reserved field is NOT 0x0000... possible "
|
|
"corruption?");
|
|
}
|
|
|
|
// The next 4 bytes specifies the number of characters in the metafile description.
|
|
// The 4 bytes after that specific the offset from this record that contains the
|
|
// metafile description... zero means no description string.
|
|
// For now, we ignore it.
|
|
|
|
mpInputStream->SeekRel(0x8);
|
|
|
|
sal_uInt32 nPalEntries(0);
|
|
mpInputStream->ReadUInt32(nPalEntries);
|
|
SAL_INFO("emfio", "\tPalette entries: " << nPalEntries);
|
|
sal_Int32 nPixX(0), nPixY(0), nMillX(0), nMillY(0);
|
|
mpInputStream->ReadInt32(nPixX);
|
|
mpInputStream->ReadInt32(nPixY);
|
|
SAL_INFO("emfio", "\tRef (pixels): " << nPixX << ", " << nPixY);
|
|
mpInputStream->ReadInt32(nMillX);
|
|
mpInputStream->ReadInt32(nMillY);
|
|
SAL_INFO("emfio", "\tRef (mm): " << nMillX << ", " << nMillY);
|
|
|
|
SetrclFrame(rclFrame);
|
|
SetrclBounds(rclBounds);
|
|
SetRefPix(Size( nPixX, nPixY ) );
|
|
SetRefMill(Size( nMillX, nMillY ) );
|
|
|
|
return checkSeek(*mpInputStream, mnStartPos + nHeaderSize);
|
|
}
|
|
|
|
tools::Rectangle EmfReader::ReadRectangle()
|
|
{
|
|
sal_Int32 nLeft(0), nTop(0), nRight(0), nBottom(0);
|
|
mpInputStream->ReadInt32(nLeft);
|
|
mpInputStream->ReadInt32(nTop);
|
|
mpInputStream->ReadInt32(nRight);
|
|
mpInputStream->ReadInt32(nBottom);
|
|
|
|
SAL_INFO("emfio", "\t\tLeft: " << nLeft << ", top: " << nTop << ", right: " << nRight << ", bottom: " << nBottom);
|
|
if (nLeft > nRight || nTop > nBottom)
|
|
{
|
|
SAL_WARN("emfio", "broken rectangle");
|
|
return tools::Rectangle::Justify(Point(nLeft, nTop), Point(nRight, nBottom));
|
|
}
|
|
|
|
return tools::Rectangle(nLeft, nTop, nRight, nBottom);
|
|
}
|
|
|
|
tools::Rectangle EmfReader::ReadRectangle( sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 )
|
|
{
|
|
Point aTL(x1, y1);
|
|
Point aBR(o3tl::saturating_add<sal_Int32>(x2, -1), o3tl::saturating_add<sal_Int32>(y2, -1));
|
|
return tools::Rectangle(aTL, aBR);
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|