/* -*- 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 <filter/EpsReader.hxx>
#include <vcl/svapp.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/graph.hxx>
#include <vcl/metaact.hxx>
#include <vcl/virdev.hxx>
#include <vcl/cvtgrf.hxx>
#include <vcl/BitmapTools.hxx>
#include <comphelper/configuration.hxx>
#include <unotools/tempfile.hxx>
#include <osl/process.h>
#include <osl/file.hxx>
#include <osl/thread.h>
#include <rtl/byteseq.hxx>
#include <sal/log.hxx>
#include <o3tl/char16_t2wchar_t.hxx>
#include <o3tl/safeint.hxx>
#include <memory>
#include <string_view>
class FilterConfigItem;
/*************************************************************************
|*
|* ImpSearchEntry()
|*
|* Description Checks if there is a string(pDest) of length nSize
|* inside the memory area pSource which is nComp bytes long.
|* Check is NON-CASE-SENSITIVE. The return value is the
|* address where the string is found or NULL
|*
*************************************************************************/
static const sal_uInt8* ImplSearchEntry( const sal_uInt8* pSource, sal_uInt8 const * pDest, size_t nComp, size_t nSize )
{
while ( nComp-- >= nSize )
{
size_t i;
for ( i = 0; i < nSize; i++ )
{
if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
break;
}
if ( i == nSize )
return pSource;
pSource++;
}
return nullptr;
}
// SecurityCount is the buffersize of the buffer in which we will parse for a number
static tools::Long ImplGetNumber(const sal_uInt8* &rBuf, sal_uInt32& nSecurityCount)
{
bool bValid = true;
bool bNegative = false;
tools::Long nRetValue = 0;
while (nSecurityCount && (*rBuf == ' ' || *rBuf == 0x9))
{
++rBuf;
--nSecurityCount;
}
while ( nSecurityCount && ( *rBuf != ' ' ) && ( *rBuf != 0x9 ) && ( *rBuf != 0xd ) && ( *rBuf != 0xa ) )
{
switch ( *rBuf )
{
case '.' :
// we'll only use the integer format
bValid = false;
break;
case '-' :
bNegative = true;
break;
default :
if ( ( *rBuf < '0' ) || ( *rBuf > '9' ) )
nSecurityCount = 1; // error parsing the bounding box values
else if ( bValid )
{
const bool bFail = o3tl::checked_multiply<tools::Long>(nRetValue, 10, nRetValue) ||
o3tl::checked_add<tools::Long>(nRetValue, *rBuf - '0', nRetValue);
if (bFail)
return 0;
}
break;
}
nSecurityCount--;
++rBuf;
}
if ( bNegative )
nRetValue = -nRetValue;
return nRetValue;
}
static int ImplGetLen(const sal_uInt8* pBuf, int nMax)
{
int nLen = 0;
while( nLen != nMax )
{
sal_uInt8 nDat = *pBuf++;
if ( nDat == 0x0a || nDat == 0x25 )
break;
nLen++;
}
return nLen;
}
static void MakeAsMeta(Graphic &rGraphic)
{
ScopedVclPtrInstance< VirtualDevice > pVDev;
GDIMetaFile aMtf;
Size aSize = rGraphic.GetPrefSize();
if( !aSize.Width() || !aSize.Height() )
aSize = Application::GetDefaultDevice()->PixelToLogic(
rGraphic.GetSizePixel(), MapMode(MapUnit::Map100thMM));
else
aSize = OutputDevice::LogicToLogic( aSize,
rGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM));
pVDev->EnableOutput( false );
aMtf.Record( pVDev );
pVDev->DrawBitmapEx( Point(), aSize, rGraphic.GetBitmapEx() );
aMtf.Stop();
aMtf.WindStart();
aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
aMtf.SetPrefSize( aSize );
rGraphic = aMtf;
}
static oslProcessError runProcessWithPathSearch(const OUString &rProgName,
rtl_uString* pArgs[], sal_uInt32 nArgs, oslProcess *pProcess,
oslFileHandle *pIn, oslFileHandle *pOut, oslFileHandle *pErr)
{
// run things that directly or indirectly might call gs in a tmpdir of their own
utl::TempFileNamed aTMPDirectory(nullptr, true);
aTMPDirectory.EnableKillingFile(true);
OUString sTmpDirEnv = u"TMPDIR="_ustr + aTMPDirectory.GetFileName();
rtl_uString* ustrEnvironment[1];
ustrEnvironment[0] = sTmpDirEnv.pData;
oslProcessError result = osl_Process_E_None;
oslSecurity pSecurity = osl_getCurrentSecurity();
#ifdef _WIN32
/*
* ooo#72096
* On Window the underlying SearchPath searches in order of...
* The directory from which the application loaded.
* The current directory.
* The Windows system directory.
* The Windows directory.
* The directories that are listed in the PATH environment variable.
*
* Because one of our programs is called "convert" and there is a convert
* in the windows system directory, we want to explicitly search the PATH
* to avoid picking up on that one if ImageMagick's convert precedes it in
* PATH.
*
*/
OUString url;
OUString path(o3tl::toU(_wgetenv(L"PATH")));
oslFileError err = osl_searchFileURL(rProgName.pData, path.pData, &url.pData);
if (err != osl_File_E_None)
result = osl_Process_E_NotFound;
else
result = osl_executeProcess_WithRedirectedIO(url.pData,
pArgs, nArgs, osl_Process_HIDDEN,
pSecurity, nullptr, ustrEnvironment, 1, pProcess, pIn, pOut, pErr);
#else
result = osl_executeProcess_WithRedirectedIO(rProgName.pData,
pArgs, nArgs, osl_Process_SEARCHPATH | osl_Process_HIDDEN,
pSecurity, nullptr, ustrEnvironment, 1, pProcess, pIn, pOut, pErr);
#endif
osl_freeSecurityHandle( pSecurity );
return result;
}
#if defined(_WIN32)
# define EXESUFFIX ".exe"
#else
# define EXESUFFIX ""
#endif
static bool RenderAsEMF(const sal_uInt8* pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
{
utl::TempFileNamed aTempOutput;
utl::TempFileNamed aTempInput;
aTempOutput.EnableKillingFile();
aTempInput.EnableKillingFile();
OUString output;
osl::FileBase::getSystemPathFromFileURL(aTempOutput.GetURL(), output);
OUString input;
osl::FileBase::getSystemPathFromFileURL(aTempInput.GetURL(), input);
SvStream* pInputStream = aTempInput.GetStream(StreamMode::WRITE);
sal_uInt64 nCount = pInputStream->WriteBytes(pBuf, nBytesRead);
aTempInput.CloseStream();
//fdo#64161 pstoedit under non-windows uses libEMF to output the EMF, but
//libEMF cannot calculate the bounding box of text, so the overall bounding
//box is not increased to include that of any text in the eps
//
//-drawbb will force pstoedit to draw a pair of pixels with the bg color to
//the topleft and bottom right of the bounding box as pstoedit sees it,
//which libEMF will then extend its bounding box to fit
//
//-usebbfrominput forces pstoedit to take the original ps bounding box
//as the bounding box as it sees it, instead of calculating its own
//which also doesn't work for this example
//
//Under Linux, positioning of letters within pstoedit is very approximate.
//Using the -nfw option delegates the positioning to the reader, and we
//will do a proper job. The option is ignored on Windows.
OUString arg1(u"-usebbfrominput"_ustr); //-usebbfrominput use the original ps bounding box
OUString arg2(u"-f"_ustr);
OUString arg3(u"emf:-OO -drawbb -nfw"_ustr); //-drawbb mark out the bounding box extent with bg pixels
//-nfw delegate letter placement to us
rtl_uString *args[] =
{
arg1.pData, arg2.pData, arg3.pData, input.pData, output.pData
};
oslProcess aProcess;
oslFileHandle pIn = nullptr;
oslFileHandle pOut = nullptr;
oslFileHandle pErr = nullptr;
oslProcessError eErr = runProcessWithPathSearch(
u"pstoedit" EXESUFFIX ""_ustr,
args, SAL_N_ELEMENTS(args),
&aProcess, &pIn, &pOut, &pErr);
if (eErr!=osl_Process_E_None)
return false;
bool bRet = false;
if (pIn) osl_closeFile(pIn);
osl_joinProcess(aProcess);
osl_freeProcessHandle(aProcess);
bool bEMFSupported=true;
if (pOut)
{
rtl::ByteSequence seq;
if (osl_File_E_None == osl_readLine(pOut, reinterpret_cast<sal_Sequence **>(&seq)))
{
OString line( reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength() );
if (line.startsWith("Unsupported output format"))
bEMFSupported=false;
}
osl_closeFile(pOut);
}
if (pErr) osl_closeFile(pErr);
if (nCount == nBytesRead && bEMFSupported)
{
SvFileStream aFile(output, StreamMode::READ);
if (GraphicConverter::Import(aFile, rGraphic, ConvertDataFormat::EMF) == ERRCODE_NONE)
bRet = true;
}
return bRet;
}
namespace {
struct WriteData
{
oslFileHandle m_pFile;
const sal_uInt8 *m_pBuf;
sal_uInt32 m_nBytesToWrite;
};
}
extern "C" {
static void WriteFileInThread(void *wData)
{
sal_uInt64 nCount;
WriteData *wdata = static_cast<WriteData *>(wData);
osl_writeFile(wdata->m_pFile, wdata->m_pBuf, wdata->m_nBytesToWrite, &nCount);
// The number of bytes written does not matter.
// The helper process may close its input stream before reading it all.
// (e.g. at "showpage" in EPS)
// File must be closed here.
// Otherwise, the helper process may wait for the next input,
// then its stdout is not closed and osl_readFile() blocks.
if (wdata->m_pFile) osl_closeFile(wdata->m_pFile);
}
}
static bool RenderAsBMPThroughHelper(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
Graphic& rGraphic,
std::initializer_list<std::u16string_view> aProgNames,
rtl_uString* pArgs[], size_t nArgs)
{
oslProcess aProcess = nullptr;
oslFileHandle pIn = nullptr;
oslFileHandle pOut = nullptr;
oslFileHandle pErr = nullptr;
oslProcessError eErr = osl_Process_E_Unknown;
for (const auto& rProgName : aProgNames)
{
eErr = runProcessWithPathSearch(OUString(rProgName), pArgs, nArgs, &aProcess, &pIn, &pOut, &pErr);
if (eErr == osl_Process_E_None)
break;
}
if (eErr!=osl_Process_E_None)
return false;
WriteData Data;
Data.m_pFile = pIn;
Data.m_pBuf = pBuf;
Data.m_nBytesToWrite = nBytesRead;
oslThread hThread = osl_createThread(WriteFileInThread, &Data);
bool bRet = false;
sal_uInt64 nCount;
{
SvMemoryStream aMemStm;
sal_uInt8 aBuf[32000];
oslFileError eFileErr = osl_readFile(pOut, aBuf, 32000, &nCount);
while (eFileErr == osl_File_E_None && nCount)
{
aMemStm.WriteBytes(aBuf, sal::static_int_cast<std::size_t>(nCount));
eFileErr = osl_readFile(pOut, aBuf, 32000, &nCount);
}
aMemStm.Seek(0);
if (
aMemStm.GetEndOfData() &&
GraphicConverter::Import(aMemStm, rGraphic, ConvertDataFormat::BMP) == ERRCODE_NONE
)
{
MakeAsMeta(rGraphic);
bRet = true;
}
}
if (pOut) osl_closeFile(pOut);
if (pErr) osl_closeFile(pErr);
osl_joinProcess(aProcess);
osl_freeProcessHandle(aProcess);
osl_joinWithThread(hThread);
osl_destroyThread(hThread);
return bRet;
}
static bool RenderAsBMPThroughConvert(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
Graphic &rGraphic)
{
// density in pixel/inch
OUString arg1(u"-density"_ustr);
// since the preview is also used for PDF-Export & printing on non-PS-printers,
// use some better quality - 300x300 should allow some resizing as well
OUString arg2(u"300x300"_ustr);
// read eps from STDIN
OUString arg3(u"eps:-"_ustr);
// write bmp to STDOUT
OUString arg4(u"bmp:-"_ustr);
rtl_uString *args[] =
{
arg1.pData, arg2.pData, arg3.pData, arg4.pData
};
return RenderAsBMPThroughHelper(pBuf, nBytesRead, rGraphic,
{ u"convert" EXESUFFIX },
args,
SAL_N_ELEMENTS(args));
}
static bool RenderAsBMPThroughGS(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
Graphic &rGraphic)
{
OUString arg1(u"-q"_ustr);
OUString arg2(u"-dBATCH"_ustr);
OUString arg3(u"-dNOPAUSE"_ustr);
OUString arg4(u"-dPARANOIDSAFER"_ustr);
OUString arg5(u"-dEPSCrop"_ustr);
OUString arg6(u"-dTextAlphaBits=4"_ustr);
OUString arg7(u"-dGraphicsAlphaBits=4"_ustr);
OUString arg8(u"-r300x300"_ustr);
OUString arg9(u"-sDEVICE=bmp16m"_ustr);
OUString arg10(u"-sOutputFile=-"_ustr);
OUString arg11(u"-"_ustr);
rtl_uString *args[] =
{
arg1.pData, arg2.pData, arg3.pData, arg4.pData, arg5.pData,
arg6.pData, arg7.pData, arg8.pData, arg9.pData, arg10.pData,
arg11.pData
};
return RenderAsBMPThroughHelper(pBuf, nBytesRead, rGraphic,
#ifdef _WIN32
// Try both 32-bit and 64-bit ghostscript executable name
{
u"gswin32c" EXESUFFIX,
u"gswin64c" EXESUFFIX,
},
#else
{ u"gs" EXESUFFIX },
#endif
args,
SAL_N_ELEMENTS(args));
}
static bool RenderAsBMP(const sal_uInt8* pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
{
if (RenderAsBMPThroughGS(pBuf, nBytesRead, rGraphic))
return true;
else
return RenderAsBMPThroughConvert(pBuf, nBytesRead, rGraphic);
}
// this method adds a replacement action containing the original wmf or tiff replacement,
// so the original eps can be written when storing to ODF.
static void CreateMtfReplacementAction( GDIMetaFile& rMtf, SvStream& rStrm, sal_uInt32 nOrigPos, sal_uInt32 nPSSize,
sal_uInt32 nPosWMF, sal_uInt32 nSizeWMF, sal_uInt32 nPosTIFF, sal_uInt32 nSizeTIFF )
{
OString aComment("EPSReplacementGraphic"_ostr);
if ( nSizeWMF || nSizeTIFF )
{
std::vector<sal_uInt8> aWMFBuf;
if (nSizeWMF && checkSeek(rStrm, nOrigPos + nPosWMF) && rStrm.remainingSize() >= nSizeWMF)
{
aWMFBuf.resize(nSizeWMF);
aWMFBuf.resize(rStrm.ReadBytes(aWMFBuf.data(), nSizeWMF));
}
nSizeWMF = aWMFBuf.size();
std::vector<sal_uInt8> aTIFFBuf;
if (nSizeTIFF && checkSeek(rStrm, nOrigPos + nPosTIFF) && rStrm.remainingSize() >= nSizeTIFF)
{
aTIFFBuf.resize(nSizeTIFF);
aTIFFBuf.resize(rStrm.ReadBytes(aTIFFBuf.data(), nSizeTIFF));
}
nSizeTIFF = aTIFFBuf.size();
SvMemoryStream aReplacement( nSizeWMF + nSizeTIFF + 28 );
sal_uInt32 const nMagic = 0xc6d3d0c5;
sal_uInt32 nPPos = 28 + nSizeWMF + nSizeTIFF;
sal_uInt32 nWPos = nSizeWMF ? 28 : 0;
sal_uInt32 nTPos = nSizeTIFF ? 28 + nSizeWMF : 0;
aReplacement.WriteUInt32( nMagic ).WriteUInt32( nPPos ).WriteUInt32( nPSSize )
.WriteUInt32( nWPos ).WriteUInt32( nSizeWMF )
.WriteUInt32( nTPos ).WriteUInt32( nSizeTIFF );
aReplacement.WriteBytes(aWMFBuf.data(), nSizeWMF);
aReplacement.WriteBytes(aTIFFBuf.data(), nSizeTIFF);
rMtf.AddAction( static_cast<MetaAction*>( new MetaCommentAction( aComment, 0, static_cast<const sal_uInt8*>(aReplacement.GetData()), aReplacement.Tell() ) ) );
}
else
rMtf.AddAction( static_cast<MetaAction*>( new MetaCommentAction( aComment, 0, nullptr, 0 ) ) );
}
//there is no preview -> make a red box
static void MakePreview(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
tools::Long nWidth, tools::Long nHeight, Graphic &rGraphic)
{
GDIMetaFile aMtf;
ScopedVclPtrInstance< VirtualDevice > pVDev;
vcl::Font aFont;
pVDev->EnableOutput( false );
aMtf.Record( pVDev );
pVDev->SetLineColor( COL_RED );
pVDev->SetFillColor();
aFont.SetColor( COL_LIGHTRED );
pVDev->Push( vcl::PushFlags::FONT );
pVDev->SetFont( aFont );
tools::Rectangle aRect( Point( 1, 1 ), Size( nWidth - 2, nHeight - 2 ) );
pVDev->DrawRect( aRect );
OUString aString;
int nLen;
const sal_uInt8* pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%Title:"), nBytesRead - 32, 8 );
sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
if (nRemainingBytes >= 8)
{
pDest += 8;
nRemainingBytes -= 8;
if (nRemainingBytes && *pDest == ' ')
{
++pDest;
--nRemainingBytes;
}
nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
if (o3tl::make_unsigned(nLen) < nRemainingBytes)
{
std::string_view chunk(reinterpret_cast<const char*>(pDest), nLen);
if (chunk != "none")
{
aString += " Title:" + OStringToOUString(chunk, RTL_TEXTENCODING_ASCII_US) + "\n";
}
}
}
pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%Creator:"), nBytesRead - 32, 10 );
nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
if (nRemainingBytes >= 10)
{
pDest += 10;
nRemainingBytes -= 10;
if (nRemainingBytes && *pDest == ' ')
{
++pDest;
--nRemainingBytes;
}
nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
if (o3tl::make_unsigned(nLen) < nRemainingBytes)
{
std::string_view chunk(reinterpret_cast<const char*>(pDest), nLen);
aString += " Creator:" + OStringToOUString(chunk, RTL_TEXTENCODING_ASCII_US) + "\n";
}
}
pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%CreationDate:"), nBytesRead - 32, 15 );
nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
if (nRemainingBytes >= 15)
{
pDest += 15;
nRemainingBytes -= 15;
if (nRemainingBytes && *pDest == ' ')
{
++pDest;
--nRemainingBytes;
}
nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
if (o3tl::make_unsigned(nLen) < nRemainingBytes)
{
std::string_view chunk(reinterpret_cast<const char*>(pDest), nLen);
if (chunk != "none")
{
aString += " CreationDate:" + OStringToOUString(chunk, RTL_TEXTENCODING_ASCII_US) + "\n";
}
}
}
pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%LanguageLevel:"), nBytesRead - 4, 16 );
nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
if (nRemainingBytes >= 16)
{
pDest += 16;
nRemainingBytes -= 16;
sal_uInt32 nCount = std::min<sal_uInt32>(nRemainingBytes, 4U);
sal_uInt32 nNumber = ImplGetNumber(pDest, nCount);
if (nCount && nNumber < 10)
{
aString += " LanguageLevel:" + OUString::number( nNumber );
}
}
pVDev->DrawText( aRect, aString, DrawTextFlags::Clip | DrawTextFlags::MultiLine );
pVDev->Pop();
aMtf.Stop();
aMtf.WindStart();
aMtf.SetPrefMapMode(MapMode(MapUnit::MapPoint));
aMtf.SetPrefSize( Size( nWidth, nHeight ) );
rGraphic = aMtf;
}
//================== GraphicImport - the exported function ================
bool ImportEpsGraphic( SvStream & rStream, Graphic & rGraphic)
{
if ( rStream.GetError() )
return false;
Graphic aGraphic;
bool bRetValue = false;
bool bHasPreview = false;
sal_uInt32 nSignature = 0, nPSStreamPos, nPSSize = 0;
sal_uInt32 nSizeWMF = 0;
sal_uInt32 nPosWMF = 0;
sal_uInt32 nSizeTIFF = 0;
sal_uInt32 nPosTIFF = 0;
auto nOrigPos = nPSStreamPos = rStream.Tell();
SvStreamEndian nOldFormat = rStream.GetEndian();
rStream.SetEndian( SvStreamEndian::LITTLE );
rStream.ReadUInt32( nSignature );
if ( nSignature == 0xc6d3d0c5 )
{
rStream.ReadUInt32( nPSStreamPos ).ReadUInt32( nPSSize ).ReadUInt32( nPosWMF ).ReadUInt32( nSizeWMF );
// first we try to get the metafile grafix
if ( nSizeWMF )
{
if (nPosWMF && checkSeek(rStream, nOrigPos + nPosWMF))
{
if (GraphicConverter::Import(rStream, aGraphic, ConvertDataFormat::WMF) == ERRCODE_NONE)
bHasPreview = bRetValue = true;
}
}
else
{
rStream.ReadUInt32( nPosTIFF ).ReadUInt32( nSizeTIFF );
// else we have to get the tiff grafix
if (nPosTIFF && nSizeTIFF && checkSeek(rStream, nOrigPos + nPosTIFF))
{
if ( GraphicConverter::Import( rStream, aGraphic, ConvertDataFormat::TIF ) == ERRCODE_NONE )
{
MakeAsMeta(aGraphic);
rStream.Seek( nOrigPos + nPosTIFF );
bHasPreview = bRetValue = true;
}
}
}
}
else
{
nPSStreamPos = nOrigPos; // no preview available _>so we must get the size manually
nPSSize = rStream.Seek( STREAM_SEEK_TO_END ) - nOrigPos;
}
std::vector<sal_uInt8> aHeader(22, 0);
rStream.Seek( nPSStreamPos );
rStream.ReadBytes(aHeader.data(), 22); // check PostScript header
sal_uInt8* pHeader = aHeader.data();
bool bOk = ImplSearchEntry(pHeader, reinterpret_cast<sal_uInt8 const *>("%!PS-Adobe"), 10, 10) &&
ImplSearchEntry(pHeader + 15, reinterpret_cast<sal_uInt8 const *>("EPS"), 3, 3);
if (bOk)
{
rStream.Seek(nPSStreamPos);
bOk = rStream.remainingSize() >= nPSSize;
SAL_WARN_IF(!bOk, "filter.eps", "eps claims to be: " << nPSSize << " in size, but only " << rStream.remainingSize() << " remains");
}
if (bOk)
{
sal_uInt64 nBufStartPos = rStream.Tell();
BinaryDataContainer aBuf(rStream, nPSSize);
if (!aBuf.isEmpty())
{
sal_uInt32 nBytesRead = aBuf.getSize();
sal_uInt32 nSecurityCount = 32;
// if there is no tiff/wmf preview, we will parse for a preview in
// the eps prolog
if (!bHasPreview && nBytesRead >= nSecurityCount)
{
const sal_uInt8* pDest = ImplSearchEntry( aBuf.getData(), reinterpret_cast<sal_uInt8 const *>("%%BeginPreview:"), nBytesRead - nSecurityCount, 15 );
sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - aBuf.getData())) : 0;
if (nRemainingBytes >= 15)
{
pDest += 15;
nSecurityCount = nRemainingBytes - 15;
tools::Long nWidth = ImplGetNumber(pDest, nSecurityCount);
tools::Long nHeight = ImplGetNumber(pDest, nSecurityCount);
tools::Long nBitDepth = ImplGetNumber(pDest, nSecurityCount);
tools::Long nScanLines = ImplGetNumber(pDest, nSecurityCount);
pDest = ImplSearchEntry(pDest, reinterpret_cast<sal_uInt8 const *>("%"), nSecurityCount, 1); // go to the first Scanline
bOk = pDest && nWidth > 0 && nHeight > 0 && ( ( nBitDepth == 1 ) || ( nBitDepth == 8 ) ) && nScanLines;
if (bOk)
{
tools::Long nResult;
bOk = !o3tl::checked_multiply(nWidth, nHeight, nResult) && nResult <= SAL_MAX_INT32/2/3;
}
if (bOk)
{
rStream.Seek( nBufStartPos + ( pDest - aBuf.getData() ) );
vcl::bitmap::RawBitmap aBitmap( Size( nWidth, nHeight ), 24 );
{
bool bIsValid = true;
sal_uInt8 nDat = 0;
char nByte;
for (tools::Long y = 0; bIsValid && y < nHeight; ++y)
{
int nBitsLeft = 0;
for (tools::Long x = 0; x < nWidth; ++x)
{
if ( --nBitsLeft < 0 )
{
while ( bIsValid && ( nBitsLeft != 7 ) )
{
rStream.ReadChar(nByte);
bIsValid = rStream.good();
if (!bIsValid)
break;
switch (nByte)
{
case 0x0a :
if ( --nScanLines < 0 )
bIsValid = false;
break;
case 0x09 :
case 0x0d :
case 0x20 :
case 0x25 :
break;
default:
{
if ( nByte >= '0' )
{
if ( nByte > '9' )
{
nByte &=~0x20; // case none sensitive for hexadecimal values
nByte -= ( 'A' - 10 );
if ( nByte > 15 )
bIsValid = false;
}
else
nByte -= '0';
nBitsLeft += 4;
nDat <<= 4;
nDat |= ( nByte ^ 0xf ); // in epsi a zero bit represents white color
}
else
bIsValid = false;
}
break;
}
}
}
if (!bIsValid)
break;
if ( nBitDepth == 1 )
aBitmap.SetPixel( y, x, Color(ColorTransparency, static_cast<sal_uInt8>(nDat >> nBitsLeft) & 1) );
else
{
aBitmap.SetPixel( y, x, nDat ? COL_WHITE : COL_BLACK ); // nBitDepth == 8
nBitsLeft = 0;
}
}
}
if (bIsValid)
{
ScopedVclPtrInstance<VirtualDevice> pVDev;
GDIMetaFile aMtf;
Size aSize( nWidth, nHeight );
pVDev->EnableOutput( false );
aMtf.Record( pVDev );
aSize = OutputDevice::LogicToLogic(aSize, MapMode(), MapMode(MapUnit::Map100thMM));
pVDev->DrawBitmapEx( Point(), aSize, vcl::bitmap::CreateFromData(std::move(aBitmap)) );
aMtf.Stop();
aMtf.WindStart();
aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
aMtf.SetPrefSize( aSize );
aGraphic = aMtf;
bHasPreview = bRetValue = true;
}
}
}
}
}
const sal_uInt8* pDest = ImplSearchEntry( aBuf.getData(), reinterpret_cast<sal_uInt8 const *>("%%BoundingBox:"), nBytesRead, 14 );
sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - aBuf.getData())) : 0;
if (nRemainingBytes >= 14)
{
pDest += 14;
nSecurityCount = std::min<sal_uInt32>(nRemainingBytes - 14, 100);
tools::Long nNumb[4];
nNumb[0] = nNumb[1] = nNumb[2] = nNumb[3] = 0;
for ( int i = 0; ( i < 4 ) && nSecurityCount; i++ )
{
nNumb[ i ] = ImplGetNumber(pDest, nSecurityCount);
}
bool bFail = nSecurityCount == 0;
tools::Long nWidth(0), nHeight(0);
if (!bFail)
bFail = o3tl::checked_sub(nNumb[2], nNumb[0], nWidth) || o3tl::checked_add(nWidth, tools::Long(1), nWidth);
if (!bFail)
bFail = o3tl::checked_sub(nNumb[3], nNumb[1], nHeight) || o3tl::checked_add(nHeight, tools::Long(1), nHeight);
if (!bFail && nWidth > 0 && nHeight > 0)
{
GDIMetaFile aMtf;
// if there is no preview -> try with gs to make one
if (!bHasPreview && !comphelper::IsFuzzing())
{
bHasPreview = RenderAsEMF(aBuf.getData(), nBytesRead, aGraphic);
if (!bHasPreview)
bHasPreview = RenderAsBMP(aBuf.getData(), nBytesRead, aGraphic);
}
// if there is no preview -> make a red box
if( !bHasPreview )
{
MakePreview(aBuf.getData(), nBytesRead, nWidth, nHeight,
aGraphic);
}
GfxLink aGfxLink(std::move(aBuf), GfxLinkType::EpsBuffer);
aMtf.AddAction( static_cast<MetaAction*>( new MetaEPSAction( Point(), Size( nWidth, nHeight ),
std::move(aGfxLink), aGraphic.GetGDIMetaFile() ) ) );
CreateMtfReplacementAction( aMtf, rStream, nOrigPos, nPSSize, nPosWMF, nSizeWMF, nPosTIFF, nSizeTIFF );
aMtf.WindStart();
aMtf.SetPrefMapMode(MapMode(MapUnit::MapPoint));
aMtf.SetPrefSize( Size( nWidth, nHeight ) );
rGraphic = aMtf;
bRetValue = true;
}
}
}
}
rStream.SetEndian(nOldFormat);
rStream.Seek( nOrigPos );
return bRetValue;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V1048 The 'nPSStreamPos' variable was assigned the same value.