cid#1606940 Check of thread-shared field evades lock acquisition
[LibreOffice.git] / vcl / source / filter / ieps / ieps.cxx
blobb5dfcfa40b64da487bbdaf630df2bfdd38439b9d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <filter/EpsReader.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/gdimtf.hxx>
24 #include <vcl/graph.hxx>
25 #include <vcl/metaact.hxx>
26 #include <vcl/virdev.hxx>
27 #include <vcl/cvtgrf.hxx>
28 #include <vcl/BitmapTools.hxx>
29 #include <comphelper/configuration.hxx>
30 #include <unotools/tempfile.hxx>
31 #include <osl/process.h>
32 #include <osl/file.hxx>
33 #include <osl/thread.h>
34 #include <rtl/byteseq.hxx>
35 #include <sal/log.hxx>
36 #include <o3tl/char16_t2wchar_t.hxx>
37 #include <o3tl/safeint.hxx>
38 #include <memory>
39 #include <string_view>
41 class FilterConfigItem;
43 /*************************************************************************
45 |* ImpSearchEntry()
47 |* Description Checks if there is a string(pDest) of length nSize
48 |* inside the memory area pSource which is nComp bytes long.
49 |* Check is NON-CASE-SENSITIVE. The return value is the
50 |* address where the string is found or NULL
52 *************************************************************************/
54 static const sal_uInt8* ImplSearchEntry( const sal_uInt8* pSource, sal_uInt8 const * pDest, size_t nComp, size_t nSize )
56 while ( nComp-- >= nSize )
58 size_t i;
59 for ( i = 0; i < nSize; i++ )
61 if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
62 break;
64 if ( i == nSize )
65 return pSource;
66 pSource++;
68 return nullptr;
72 // SecurityCount is the buffersize of the buffer in which we will parse for a number
73 static tools::Long ImplGetNumber(const sal_uInt8* &rBuf, sal_uInt32& nSecurityCount)
75 bool bValid = true;
76 bool bNegative = false;
77 tools::Long nRetValue = 0;
78 while (nSecurityCount && (*rBuf == ' ' || *rBuf == 0x9))
80 ++rBuf;
81 --nSecurityCount;
83 while ( nSecurityCount && ( *rBuf != ' ' ) && ( *rBuf != 0x9 ) && ( *rBuf != 0xd ) && ( *rBuf != 0xa ) )
85 switch ( *rBuf )
87 case '.' :
88 // we'll only use the integer format
89 bValid = false;
90 break;
91 case '-' :
92 bNegative = true;
93 break;
94 default :
95 if ( ( *rBuf < '0' ) || ( *rBuf > '9' ) )
96 nSecurityCount = 1; // error parsing the bounding box values
97 else if ( bValid )
99 const bool bFail = o3tl::checked_multiply<tools::Long>(nRetValue, 10, nRetValue) ||
100 o3tl::checked_add<tools::Long>(nRetValue, *rBuf - '0', nRetValue);
101 if (bFail)
102 return 0;
104 break;
106 nSecurityCount--;
107 ++rBuf;
109 if ( bNegative )
110 nRetValue = -nRetValue;
111 return nRetValue;
115 static int ImplGetLen(const sal_uInt8* pBuf, int nMax)
117 int nLen = 0;
118 while( nLen != nMax )
120 sal_uInt8 nDat = *pBuf++;
121 if ( nDat == 0x0a || nDat == 0x25 )
122 break;
123 nLen++;
125 return nLen;
128 static void MakeAsMeta(Graphic &rGraphic)
130 ScopedVclPtrInstance< VirtualDevice > pVDev;
131 GDIMetaFile aMtf;
132 Size aSize = rGraphic.GetPrefSize();
134 if( !aSize.Width() || !aSize.Height() )
135 aSize = Application::GetDefaultDevice()->PixelToLogic(
136 rGraphic.GetSizePixel(), MapMode(MapUnit::Map100thMM));
137 else
138 aSize = OutputDevice::LogicToLogic( aSize,
139 rGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM));
141 pVDev->EnableOutput( false );
142 aMtf.Record( pVDev );
143 pVDev->DrawBitmapEx( Point(), aSize, rGraphic.GetBitmapEx() );
144 aMtf.Stop();
145 aMtf.WindStart();
146 aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
147 aMtf.SetPrefSize( aSize );
148 rGraphic = aMtf;
151 static oslProcessError runProcessWithPathSearch(const OUString &rProgName,
152 rtl_uString* pArgs[], sal_uInt32 nArgs, oslProcess *pProcess,
153 oslFileHandle *pIn, oslFileHandle *pOut, oslFileHandle *pErr)
155 // run things that directly or indirectly might call gs in a tmpdir of their own
156 utl::TempFileNamed aTMPDirectory(nullptr, true);
157 aTMPDirectory.EnableKillingFile(true);
158 OUString sTmpDirEnv = u"TMPDIR="_ustr + aTMPDirectory.GetFileName();
160 rtl_uString* ustrEnvironment[1];
161 ustrEnvironment[0] = sTmpDirEnv.pData;
163 oslProcessError result = osl_Process_E_None;
164 oslSecurity pSecurity = osl_getCurrentSecurity();
165 #ifdef _WIN32
167 * ooo#72096
168 * On Window the underlying SearchPath searches in order of...
169 * The directory from which the application loaded.
170 * The current directory.
171 * The Windows system directory.
172 * The Windows directory.
173 * The directories that are listed in the PATH environment variable.
175 * Because one of our programs is called "convert" and there is a convert
176 * in the windows system directory, we want to explicitly search the PATH
177 * to avoid picking up on that one if ImageMagick's convert precedes it in
178 * PATH.
181 OUString url;
182 OUString path(o3tl::toU(_wgetenv(L"PATH")));
184 oslFileError err = osl_searchFileURL(rProgName.pData, path.pData, &url.pData);
185 if (err != osl_File_E_None)
186 result = osl_Process_E_NotFound;
187 else
188 result = osl_executeProcess_WithRedirectedIO(url.pData,
189 pArgs, nArgs, osl_Process_HIDDEN,
190 pSecurity, nullptr, ustrEnvironment, 1, pProcess, pIn, pOut, pErr);
191 #else
192 result = osl_executeProcess_WithRedirectedIO(rProgName.pData,
193 pArgs, nArgs, osl_Process_SEARCHPATH | osl_Process_HIDDEN,
194 pSecurity, nullptr, ustrEnvironment, 1, pProcess, pIn, pOut, pErr);
195 #endif
196 osl_freeSecurityHandle( pSecurity );
197 return result;
200 #if defined(_WIN32)
201 # define EXESUFFIX ".exe"
202 #else
203 # define EXESUFFIX ""
204 #endif
206 static bool RenderAsEMF(const sal_uInt8* pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
208 utl::TempFileNamed aTempOutput;
209 utl::TempFileNamed aTempInput;
210 aTempOutput.EnableKillingFile();
211 aTempInput.EnableKillingFile();
212 OUString output;
213 osl::FileBase::getSystemPathFromFileURL(aTempOutput.GetURL(), output);
214 OUString input;
215 osl::FileBase::getSystemPathFromFileURL(aTempInput.GetURL(), input);
217 SvStream* pInputStream = aTempInput.GetStream(StreamMode::WRITE);
218 sal_uInt64 nCount = pInputStream->WriteBytes(pBuf, nBytesRead);
219 aTempInput.CloseStream();
221 //fdo#64161 pstoedit under non-windows uses libEMF to output the EMF, but
222 //libEMF cannot calculate the bounding box of text, so the overall bounding
223 //box is not increased to include that of any text in the eps
225 //-drawbb will force pstoedit to draw a pair of pixels with the bg color to
226 //the topleft and bottom right of the bounding box as pstoedit sees it,
227 //which libEMF will then extend its bounding box to fit
229 //-usebbfrominput forces pstoedit to take the original ps bounding box
230 //as the bounding box as it sees it, instead of calculating its own
231 //which also doesn't work for this example
233 //Under Linux, positioning of letters within pstoedit is very approximate.
234 //Using the -nfw option delegates the positioning to the reader, and we
235 //will do a proper job. The option is ignored on Windows.
236 OUString arg1(u"-usebbfrominput"_ustr); //-usebbfrominput use the original ps bounding box
237 OUString arg2(u"-f"_ustr);
238 OUString arg3(u"emf:-OO -drawbb -nfw"_ustr); //-drawbb mark out the bounding box extent with bg pixels
239 //-nfw delegate letter placement to us
240 rtl_uString *args[] =
242 arg1.pData, arg2.pData, arg3.pData, input.pData, output.pData
244 oslProcess aProcess;
245 oslFileHandle pIn = nullptr;
246 oslFileHandle pOut = nullptr;
247 oslFileHandle pErr = nullptr;
248 oslProcessError eErr = runProcessWithPathSearch(
249 u"pstoedit" EXESUFFIX ""_ustr,
250 args, SAL_N_ELEMENTS(args),
251 &aProcess, &pIn, &pOut, &pErr);
253 if (eErr!=osl_Process_E_None)
254 return false;
256 bool bRet = false;
257 if (pIn) osl_closeFile(pIn);
258 osl_joinProcess(aProcess);
259 osl_freeProcessHandle(aProcess);
260 bool bEMFSupported=true;
261 if (pOut)
263 rtl::ByteSequence seq;
264 if (osl_File_E_None == osl_readLine(pOut, reinterpret_cast<sal_Sequence **>(&seq)))
266 OString line( reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength() );
267 if (line.startsWith("Unsupported output format"))
268 bEMFSupported=false;
270 osl_closeFile(pOut);
272 if (pErr) osl_closeFile(pErr);
273 if (nCount == nBytesRead && bEMFSupported)
275 SvFileStream aFile(output, StreamMode::READ);
276 if (GraphicConverter::Import(aFile, rGraphic, ConvertDataFormat::EMF) == ERRCODE_NONE)
277 bRet = true;
280 return bRet;
283 namespace {
285 struct WriteData
287 oslFileHandle m_pFile;
288 const sal_uInt8 *m_pBuf;
289 sal_uInt32 m_nBytesToWrite;
294 extern "C" {
296 static void WriteFileInThread(void *wData)
298 sal_uInt64 nCount;
299 WriteData *wdata = static_cast<WriteData *>(wData);
300 osl_writeFile(wdata->m_pFile, wdata->m_pBuf, wdata->m_nBytesToWrite, &nCount);
301 // The number of bytes written does not matter.
302 // The helper process may close its input stream before reading it all.
303 // (e.g. at "showpage" in EPS)
305 // File must be closed here.
306 // Otherwise, the helper process may wait for the next input,
307 // then its stdout is not closed and osl_readFile() blocks.
308 if (wdata->m_pFile) osl_closeFile(wdata->m_pFile);
313 static bool RenderAsBMPThroughHelper(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
314 Graphic& rGraphic,
315 std::initializer_list<std::u16string_view> aProgNames,
316 rtl_uString* pArgs[], size_t nArgs)
318 oslProcess aProcess = nullptr;
319 oslFileHandle pIn = nullptr;
320 oslFileHandle pOut = nullptr;
321 oslFileHandle pErr = nullptr;
322 oslProcessError eErr = osl_Process_E_Unknown;
323 for (const auto& rProgName : aProgNames)
325 eErr = runProcessWithPathSearch(OUString(rProgName), pArgs, nArgs, &aProcess, &pIn, &pOut, &pErr);
326 if (eErr == osl_Process_E_None)
327 break;
329 if (eErr!=osl_Process_E_None)
330 return false;
332 WriteData Data;
333 Data.m_pFile = pIn;
334 Data.m_pBuf = pBuf;
335 Data.m_nBytesToWrite = nBytesRead;
336 oslThread hThread = osl_createThread(WriteFileInThread, &Data);
338 bool bRet = false;
339 sal_uInt64 nCount;
341 SvMemoryStream aMemStm;
342 sal_uInt8 aBuf[32000];
343 oslFileError eFileErr = osl_readFile(pOut, aBuf, 32000, &nCount);
344 while (eFileErr == osl_File_E_None && nCount)
346 aMemStm.WriteBytes(aBuf, sal::static_int_cast<std::size_t>(nCount));
347 eFileErr = osl_readFile(pOut, aBuf, 32000, &nCount);
350 aMemStm.Seek(0);
351 if (
352 aMemStm.GetEndOfData() &&
353 GraphicConverter::Import(aMemStm, rGraphic, ConvertDataFormat::BMP) == ERRCODE_NONE
356 MakeAsMeta(rGraphic);
357 bRet = true;
360 if (pOut) osl_closeFile(pOut);
361 if (pErr) osl_closeFile(pErr);
362 osl_joinProcess(aProcess);
363 osl_freeProcessHandle(aProcess);
364 osl_joinWithThread(hThread);
365 osl_destroyThread(hThread);
366 return bRet;
369 static bool RenderAsBMPThroughConvert(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
370 Graphic &rGraphic)
372 // density in pixel/inch
373 OUString arg1(u"-density"_ustr);
374 // since the preview is also used for PDF-Export & printing on non-PS-printers,
375 // use some better quality - 300x300 should allow some resizing as well
376 OUString arg2(u"300x300"_ustr);
377 // read eps from STDIN
378 OUString arg3(u"eps:-"_ustr);
379 // write bmp to STDOUT
380 OUString arg4(u"bmp:-"_ustr);
381 rtl_uString *args[] =
383 arg1.pData, arg2.pData, arg3.pData, arg4.pData
385 return RenderAsBMPThroughHelper(pBuf, nBytesRead, rGraphic,
386 { u"convert" EXESUFFIX },
387 args,
388 SAL_N_ELEMENTS(args));
391 static bool RenderAsBMPThroughGS(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
392 Graphic &rGraphic)
394 OUString arg1(u"-q"_ustr);
395 OUString arg2(u"-dBATCH"_ustr);
396 OUString arg3(u"-dNOPAUSE"_ustr);
397 OUString arg4(u"-dPARANOIDSAFER"_ustr);
398 OUString arg5(u"-dEPSCrop"_ustr);
399 OUString arg6(u"-dTextAlphaBits=4"_ustr);
400 OUString arg7(u"-dGraphicsAlphaBits=4"_ustr);
401 OUString arg8(u"-r300x300"_ustr);
402 OUString arg9(u"-sDEVICE=bmp16m"_ustr);
403 OUString arg10(u"-sOutputFile=-"_ustr);
404 OUString arg11(u"-"_ustr);
405 rtl_uString *args[] =
407 arg1.pData, arg2.pData, arg3.pData, arg4.pData, arg5.pData,
408 arg6.pData, arg7.pData, arg8.pData, arg9.pData, arg10.pData,
409 arg11.pData
411 return RenderAsBMPThroughHelper(pBuf, nBytesRead, rGraphic,
412 #ifdef _WIN32
413 // Try both 32-bit and 64-bit ghostscript executable name
415 u"gswin32c" EXESUFFIX,
416 u"gswin64c" EXESUFFIX,
418 #else
419 { u"gs" EXESUFFIX },
420 #endif
421 args,
422 SAL_N_ELEMENTS(args));
425 static bool RenderAsBMP(const sal_uInt8* pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
427 if (RenderAsBMPThroughGS(pBuf, nBytesRead, rGraphic))
428 return true;
429 else
430 return RenderAsBMPThroughConvert(pBuf, nBytesRead, rGraphic);
433 // this method adds a replacement action containing the original wmf or tiff replacement,
434 // so the original eps can be written when storing to ODF.
435 static void CreateMtfReplacementAction( GDIMetaFile& rMtf, SvStream& rStrm, sal_uInt32 nOrigPos, sal_uInt32 nPSSize,
436 sal_uInt32 nPosWMF, sal_uInt32 nSizeWMF, sal_uInt32 nPosTIFF, sal_uInt32 nSizeTIFF )
438 OString aComment("EPSReplacementGraphic"_ostr);
439 if ( nSizeWMF || nSizeTIFF )
441 std::vector<sal_uInt8> aWMFBuf;
442 if (nSizeWMF && checkSeek(rStrm, nOrigPos + nPosWMF) && rStrm.remainingSize() >= nSizeWMF)
444 aWMFBuf.resize(nSizeWMF);
445 aWMFBuf.resize(rStrm.ReadBytes(aWMFBuf.data(), nSizeWMF));
447 nSizeWMF = aWMFBuf.size();
449 std::vector<sal_uInt8> aTIFFBuf;
450 if (nSizeTIFF && checkSeek(rStrm, nOrigPos + nPosTIFF) && rStrm.remainingSize() >= nSizeTIFF)
452 aTIFFBuf.resize(nSizeTIFF);
453 aTIFFBuf.resize(rStrm.ReadBytes(aTIFFBuf.data(), nSizeTIFF));
455 nSizeTIFF = aTIFFBuf.size();
457 SvMemoryStream aReplacement( nSizeWMF + nSizeTIFF + 28 );
458 sal_uInt32 const nMagic = 0xc6d3d0c5;
459 sal_uInt32 nPPos = 28 + nSizeWMF + nSizeTIFF;
460 sal_uInt32 nWPos = nSizeWMF ? 28 : 0;
461 sal_uInt32 nTPos = nSizeTIFF ? 28 + nSizeWMF : 0;
463 aReplacement.WriteUInt32( nMagic ).WriteUInt32( nPPos ).WriteUInt32( nPSSize )
464 .WriteUInt32( nWPos ).WriteUInt32( nSizeWMF )
465 .WriteUInt32( nTPos ).WriteUInt32( nSizeTIFF );
467 aReplacement.WriteBytes(aWMFBuf.data(), nSizeWMF);
468 aReplacement.WriteBytes(aTIFFBuf.data(), nSizeTIFF);
469 rMtf.AddAction( static_cast<MetaAction*>( new MetaCommentAction( aComment, 0, static_cast<const sal_uInt8*>(aReplacement.GetData()), aReplacement.Tell() ) ) );
471 else
472 rMtf.AddAction( static_cast<MetaAction*>( new MetaCommentAction( aComment, 0, nullptr, 0 ) ) );
475 //there is no preview -> make a red box
476 static void MakePreview(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
477 tools::Long nWidth, tools::Long nHeight, Graphic &rGraphic)
479 GDIMetaFile aMtf;
480 ScopedVclPtrInstance< VirtualDevice > pVDev;
481 vcl::Font aFont;
483 pVDev->EnableOutput( false );
484 aMtf.Record( pVDev );
485 pVDev->SetLineColor( COL_RED );
486 pVDev->SetFillColor();
488 aFont.SetColor( COL_LIGHTRED );
490 pVDev->Push( vcl::PushFlags::FONT );
491 pVDev->SetFont( aFont );
493 tools::Rectangle aRect( Point( 1, 1 ), Size( nWidth - 2, nHeight - 2 ) );
494 pVDev->DrawRect( aRect );
496 OUString aString;
497 int nLen;
498 const sal_uInt8* pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%Title:"), nBytesRead - 32, 8 );
499 sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
500 if (nRemainingBytes >= 8)
502 pDest += 8;
503 nRemainingBytes -= 8;
504 if (nRemainingBytes && *pDest == ' ')
506 ++pDest;
507 --nRemainingBytes;
509 nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
510 if (o3tl::make_unsigned(nLen) < nRemainingBytes)
512 std::string_view chunk(reinterpret_cast<const char*>(pDest), nLen);
513 if (chunk != "none")
515 aString += " Title:" + OStringToOUString(chunk, RTL_TEXTENCODING_ASCII_US) + "\n";
519 pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%Creator:"), nBytesRead - 32, 10 );
520 nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
521 if (nRemainingBytes >= 10)
523 pDest += 10;
524 nRemainingBytes -= 10;
525 if (nRemainingBytes && *pDest == ' ')
527 ++pDest;
528 --nRemainingBytes;
530 nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
531 if (o3tl::make_unsigned(nLen) < nRemainingBytes)
533 std::string_view chunk(reinterpret_cast<const char*>(pDest), nLen);
534 aString += " Creator:" + OStringToOUString(chunk, RTL_TEXTENCODING_ASCII_US) + "\n";
537 pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%CreationDate:"), nBytesRead - 32, 15 );
538 nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
539 if (nRemainingBytes >= 15)
541 pDest += 15;
542 nRemainingBytes -= 15;
543 if (nRemainingBytes && *pDest == ' ')
545 ++pDest;
546 --nRemainingBytes;
548 nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
549 if (o3tl::make_unsigned(nLen) < nRemainingBytes)
551 std::string_view chunk(reinterpret_cast<const char*>(pDest), nLen);
552 if (chunk != "none")
554 aString += " CreationDate:" + OStringToOUString(chunk, RTL_TEXTENCODING_ASCII_US) + "\n";
558 pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%LanguageLevel:"), nBytesRead - 4, 16 );
559 nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
560 if (nRemainingBytes >= 16)
562 pDest += 16;
563 nRemainingBytes -= 16;
564 sal_uInt32 nCount = std::min<sal_uInt32>(nRemainingBytes, 4U);
565 sal_uInt32 nNumber = ImplGetNumber(pDest, nCount);
566 if (nCount && nNumber < 10)
568 aString += " LanguageLevel:" + OUString::number( nNumber );
571 pVDev->DrawText( aRect, aString, DrawTextFlags::Clip | DrawTextFlags::MultiLine );
572 pVDev->Pop();
573 aMtf.Stop();
574 aMtf.WindStart();
575 aMtf.SetPrefMapMode(MapMode(MapUnit::MapPoint));
576 aMtf.SetPrefSize( Size( nWidth, nHeight ) );
577 rGraphic = aMtf;
580 //================== GraphicImport - the exported function ================
583 bool ImportEpsGraphic( SvStream & rStream, Graphic & rGraphic)
585 if ( rStream.GetError() )
586 return false;
588 Graphic aGraphic;
589 bool bRetValue = false;
590 bool bHasPreview = false;
591 sal_uInt32 nSignature = 0, nPSStreamPos, nPSSize = 0;
592 sal_uInt32 nSizeWMF = 0;
593 sal_uInt32 nPosWMF = 0;
594 sal_uInt32 nSizeTIFF = 0;
595 sal_uInt32 nPosTIFF = 0;
597 auto nOrigPos = nPSStreamPos = rStream.Tell();
598 SvStreamEndian nOldFormat = rStream.GetEndian();
600 rStream.SetEndian( SvStreamEndian::LITTLE );
601 rStream.ReadUInt32( nSignature );
602 if ( nSignature == 0xc6d3d0c5 )
604 rStream.ReadUInt32( nPSStreamPos ).ReadUInt32( nPSSize ).ReadUInt32( nPosWMF ).ReadUInt32( nSizeWMF );
606 // first we try to get the metafile grafix
608 if ( nSizeWMF )
610 if (nPosWMF && checkSeek(rStream, nOrigPos + nPosWMF))
612 if (GraphicConverter::Import(rStream, aGraphic, ConvertDataFormat::WMF) == ERRCODE_NONE)
613 bHasPreview = bRetValue = true;
616 else
618 rStream.ReadUInt32( nPosTIFF ).ReadUInt32( nSizeTIFF );
620 // else we have to get the tiff grafix
622 if (nPosTIFF && nSizeTIFF && checkSeek(rStream, nOrigPos + nPosTIFF))
624 if ( GraphicConverter::Import( rStream, aGraphic, ConvertDataFormat::TIF ) == ERRCODE_NONE )
626 MakeAsMeta(aGraphic);
627 rStream.Seek( nOrigPos + nPosTIFF );
628 bHasPreview = bRetValue = true;
633 else
635 nPSStreamPos = nOrigPos; // no preview available _>so we must get the size manually
636 nPSSize = rStream.Seek( STREAM_SEEK_TO_END ) - nOrigPos;
639 std::vector<sal_uInt8> aHeader(22, 0);
640 rStream.Seek( nPSStreamPos );
641 rStream.ReadBytes(aHeader.data(), 22); // check PostScript header
642 sal_uInt8* pHeader = aHeader.data();
643 bool bOk = ImplSearchEntry(pHeader, reinterpret_cast<sal_uInt8 const *>("%!PS-Adobe"), 10, 10) &&
644 ImplSearchEntry(pHeader + 15, reinterpret_cast<sal_uInt8 const *>("EPS"), 3, 3);
645 if (bOk)
647 rStream.Seek(nPSStreamPos);
648 bOk = rStream.remainingSize() >= nPSSize;
649 SAL_WARN_IF(!bOk, "filter.eps", "eps claims to be: " << nPSSize << " in size, but only " << rStream.remainingSize() << " remains");
651 if (bOk)
653 sal_uInt64 nBufStartPos = rStream.Tell();
654 BinaryDataContainer aBuf(rStream, nPSSize);
655 if (!aBuf.isEmpty())
657 sal_uInt32 nBytesRead = aBuf.getSize();
658 sal_uInt32 nSecurityCount = 32;
659 // if there is no tiff/wmf preview, we will parse for a preview in
660 // the eps prolog
661 if (!bHasPreview && nBytesRead >= nSecurityCount)
663 const sal_uInt8* pDest = ImplSearchEntry( aBuf.getData(), reinterpret_cast<sal_uInt8 const *>("%%BeginPreview:"), nBytesRead - nSecurityCount, 15 );
664 sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - aBuf.getData())) : 0;
665 if (nRemainingBytes >= 15)
667 pDest += 15;
668 nSecurityCount = nRemainingBytes - 15;
669 tools::Long nWidth = ImplGetNumber(pDest, nSecurityCount);
670 tools::Long nHeight = ImplGetNumber(pDest, nSecurityCount);
671 tools::Long nBitDepth = ImplGetNumber(pDest, nSecurityCount);
672 tools::Long nScanLines = ImplGetNumber(pDest, nSecurityCount);
673 pDest = ImplSearchEntry(pDest, reinterpret_cast<sal_uInt8 const *>("%"), nSecurityCount, 1); // go to the first Scanline
674 bOk = pDest && nWidth > 0 && nHeight > 0 && ( ( nBitDepth == 1 ) || ( nBitDepth == 8 ) ) && nScanLines;
675 if (bOk)
677 tools::Long nResult;
678 bOk = !o3tl::checked_multiply(nWidth, nHeight, nResult) && nResult <= SAL_MAX_INT32/2/3;
680 if (bOk)
682 rStream.Seek( nBufStartPos + ( pDest - aBuf.getData() ) );
684 vcl::bitmap::RawBitmap aBitmap( Size( nWidth, nHeight ), 24 );
686 bool bIsValid = true;
687 sal_uInt8 nDat = 0;
688 char nByte;
689 for (tools::Long y = 0; bIsValid && y < nHeight; ++y)
691 int nBitsLeft = 0;
692 for (tools::Long x = 0; x < nWidth; ++x)
694 if ( --nBitsLeft < 0 )
696 while ( bIsValid && ( nBitsLeft != 7 ) )
698 rStream.ReadChar(nByte);
699 bIsValid = rStream.good();
700 if (!bIsValid)
701 break;
702 switch (nByte)
704 case 0x0a :
705 if ( --nScanLines < 0 )
706 bIsValid = false;
707 break;
708 case 0x09 :
709 case 0x0d :
710 case 0x20 :
711 case 0x25 :
712 break;
713 default:
715 if ( nByte >= '0' )
717 if ( nByte > '9' )
719 nByte &=~0x20; // case none sensitive for hexadecimal values
720 nByte -= ( 'A' - 10 );
721 if ( nByte > 15 )
722 bIsValid = false;
724 else
725 nByte -= '0';
726 nBitsLeft += 4;
727 nDat <<= 4;
728 nDat |= ( nByte ^ 0xf ); // in epsi a zero bit represents white color
730 else
731 bIsValid = false;
733 break;
737 if (!bIsValid)
738 break;
739 if ( nBitDepth == 1 )
740 aBitmap.SetPixel( y, x, Color(ColorTransparency, static_cast<sal_uInt8>(nDat >> nBitsLeft) & 1) );
741 else
743 aBitmap.SetPixel( y, x, nDat ? COL_WHITE : COL_BLACK ); // nBitDepth == 8
744 nBitsLeft = 0;
748 if (bIsValid)
750 ScopedVclPtrInstance<VirtualDevice> pVDev;
751 GDIMetaFile aMtf;
752 Size aSize( nWidth, nHeight );
753 pVDev->EnableOutput( false );
754 aMtf.Record( pVDev );
755 aSize = OutputDevice::LogicToLogic(aSize, MapMode(), MapMode(MapUnit::Map100thMM));
756 pVDev->DrawBitmapEx( Point(), aSize, vcl::bitmap::CreateFromData(std::move(aBitmap)) );
757 aMtf.Stop();
758 aMtf.WindStart();
759 aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
760 aMtf.SetPrefSize( aSize );
761 aGraphic = aMtf;
762 bHasPreview = bRetValue = true;
769 const sal_uInt8* pDest = ImplSearchEntry( aBuf.getData(), reinterpret_cast<sal_uInt8 const *>("%%BoundingBox:"), nBytesRead, 14 );
770 sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - aBuf.getData())) : 0;
771 if (nRemainingBytes >= 14)
773 pDest += 14;
774 nSecurityCount = std::min<sal_uInt32>(nRemainingBytes - 14, 100);
775 tools::Long nNumb[4];
776 nNumb[0] = nNumb[1] = nNumb[2] = nNumb[3] = 0;
777 for ( int i = 0; ( i < 4 ) && nSecurityCount; i++ )
779 nNumb[ i ] = ImplGetNumber(pDest, nSecurityCount);
781 bool bFail = nSecurityCount == 0;
782 tools::Long nWidth(0), nHeight(0);
783 if (!bFail)
784 bFail = o3tl::checked_sub(nNumb[2], nNumb[0], nWidth) || o3tl::checked_add(nWidth, tools::Long(1), nWidth);
785 if (!bFail)
786 bFail = o3tl::checked_sub(nNumb[3], nNumb[1], nHeight) || o3tl::checked_add(nHeight, tools::Long(1), nHeight);
787 if (!bFail && nWidth > 0 && nHeight > 0)
789 GDIMetaFile aMtf;
791 // if there is no preview -> try with gs to make one
792 if (!bHasPreview && !comphelper::IsFuzzing())
794 bHasPreview = RenderAsEMF(aBuf.getData(), nBytesRead, aGraphic);
795 if (!bHasPreview)
796 bHasPreview = RenderAsBMP(aBuf.getData(), nBytesRead, aGraphic);
799 // if there is no preview -> make a red box
800 if( !bHasPreview )
802 MakePreview(aBuf.getData(), nBytesRead, nWidth, nHeight,
803 aGraphic);
806 GfxLink aGfxLink(std::move(aBuf), GfxLinkType::EpsBuffer);
807 aMtf.AddAction( static_cast<MetaAction*>( new MetaEPSAction( Point(), Size( nWidth, nHeight ),
808 std::move(aGfxLink), aGraphic.GetGDIMetaFile() ) ) );
809 CreateMtfReplacementAction( aMtf, rStream, nOrigPos, nPSSize, nPosWMF, nSizeWMF, nPosTIFF, nSizeTIFF );
810 aMtf.WindStart();
811 aMtf.SetPrefMapMode(MapMode(MapUnit::MapPoint));
812 aMtf.SetPrefSize( Size( nWidth, nHeight ) );
813 rGraphic = aMtf;
814 bRetValue = true;
820 rStream.SetEndian(nOldFormat);
821 rStream.Seek( nOrigPos );
822 return bRetValue;
825 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */