/* -*- 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 <o3tl/sprintf.hxx> #include <tools/stream.hxx> #include <memory> #include <comphelper/configuration.hxx> #include <vcl/graph.hxx> #include <vcl/pdfread.hxx> #include <rtl/bootstrap.hxx>
constchar *
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. staticchar buffer[11];
o3tl::sprintf(buffer, "0x%08" SAL_PRIxUINT32, nRecType); return buffer;
} #endif
}
sal_Int32 nLeft, nTop, nRight, nBottom; //bounds of the region
rStream.ReadInt32(nLeft);
rStream.ReadInt32(nTop);
rStream.ReadInt32(nRight);
rStream.ReadInt32(nBottom);
// aGraphic will be the only output of the EMF parser, so its size hint can be the same as // ours.
aGraphic.getVectorGraphicData()->setSizeHint(maSizeHint);
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);
}
/** * 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(constbool skipFirst, sal_uInt32 nNextPos)
{
sal_uInt32 nPoints(0), nStartIndex(0);
mpInputStream->SeekRel( 16 );
mpInputStream->ReadUInt32( nPoints ); if (skipFirst)
{
nPoints ++;
nStartIndex ++;
}
/** * 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;
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 nCount = 0;
mpInputStream->ReadUInt32( nCount ); // total number of points in all polylines
SAL_INFO("emfio", "\t\t\tPoints: " << nCount);
constauto 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
constauto 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 );
OUString aEMFPlusDisable;
rtl::Bootstrap::get(u"EMF_PLUS_DISABLE"_ustr, aEMFPlusDisable); bool bEnableEMFPlus = aEMFPlusDisable.isEmpty(); if (!mbEnableEMFPlus)
{ // EMF+ is enabled if neither the bootstrap variable, not the member variable disables // it.
bEnableEMFPlus = mbEnableEMFPlus;
}
case EMR_CREATEPEN:
{
mpInputStream->ReadUInt32(nIndex); if ((nIndex & ENHMETA_STOCK_OBJECT) == 0)
{
sal_uInt32 nPenStyle(0);
sal_Int32 nPenWidth(0), nIgnored;
mpInputStream->ReadUInt32(nPenStyle).ReadInt32(nPenWidth).ReadInt32(nIgnored);
SAL_INFO("emfio", "\t\tIndex: " << nIndex << " Style: 0x" << std::hex
<< nPenStyle << std::dec
<< " PenWidth: " << nPenWidth); // PS_COSMETIC width is always fixed at one logical unit // and is not affected by any geometric transformations like scaling if (nPenStyle == PS_COSMETIC)
nPenWidth = 1;
CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>(ReadColor(), nPenStyle, nPenWidth));
}
} break;
case EMR_EXTCREATEPEN:
{
mpInputStream->ReadUInt32(nIndex); if ((nIndex & ENHMETA_STOCK_OBJECT) == 0)
{
sal_uInt32 offBmi, cbBmi, offBits, cbBits, nPenStyle, nWidth, nBrushStyle, elpNumEntries;
sal_Int32 elpHatch;
mpInputStream->ReadUInt32(offBmi).ReadUInt32(cbBmi).ReadUInt32(offBits).ReadUInt32(cbBits);
mpInputStream->ReadUInt32(nPenStyle).ReadUInt32(nWidth).ReadUInt32(nBrushStyle);
Color aColorRef = ReadColor();
mpInputStream->ReadInt32(elpHatch).ReadUInt32(elpNumEntries);
if (!mpInputStream->good())
bStatus = false; else
{
SAL_INFO("emfio", "\t\tStyle: 0x" << std::hex << nPenStyle << std::dec); // PS_COSMETIC width is always fixed at one logical unit // and is not affected by any geometric transformations like scaling if (nPenStyle == PS_COSMETIC)
nWidth = 1;
SAL_INFO("emfio", "\t\tWidth: " << nWidth);
CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>(aColorRef, nPenStyle, nWidth));
}
}
} break;
if (!mpInputStream->good())
bStatus = false; else
{ // Convert from degrees to radians and start angle to draw from x-axis
fStartAngle = basegfx::deg2rad(fStartAngle);
fSweepAngle = basegfx::deg2rad(fSweepAngle);
if (!aPoly.GetSize())
{
SAL_WARN("emfio", "EMF file error: 0 points");
} else
{ // Before drawing the arc, AngleArc draws the line segment from the current position to the beginning of the arc
LineTo(aPoly[0], mbRecordPath);
DrawPolyLine(std::move(aPoly), true, mbRecordPath);
}
}
} break;
// 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, GetWinOrg()); 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);
const sal_uInt32 nSourceSize = cbBmiSrc + cbBitsSrc + 14; bool bSafeRead = nSourceSize <= (mnEndPos - mnStartPos);
sal_uInt32 nDeltaToDIB5HeaderSize(0); constbool 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
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.