xref: /core/vcl/source/filter/ieps/ieps.cxx (revision 4b95451f)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
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 <unotools/configmgr.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>
40 
41 class FilterConfigItem;
42 
43 /*************************************************************************
44 |*
45 |*    ImpSearchEntry()
46 |*
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
51 |*
52 *************************************************************************/
53 
54 static sal_uInt8* ImplSearchEntry( sal_uInt8* pSource, sal_uInt8 const * pDest, size_t nComp, size_t nSize )
55 {
56     while ( nComp-- >= nSize )
57     {
58         size_t i;
59         for ( i = 0; i < nSize; i++ )
60         {
61             if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
62                 break;
63         }
64         if ( i == nSize )
65             return pSource;
66         pSource++;
67     }
68     return nullptr;
69 }
70 
71 
72 // SecurityCount is the buffersize of the buffer in which we will parse for a number
73 static tools::Long ImplGetNumber(sal_uInt8* &rBuf, sal_uInt32& nSecurityCount)
74 {
75     bool    bValid = true;
76     bool    bNegative = false;
77     tools::Long    nRetValue = 0;
78     while (nSecurityCount && (*rBuf == ' ' || *rBuf == 0x9))
79     {
80         ++rBuf;
81         --nSecurityCount;
82     }
83     while ( nSecurityCount && ( *rBuf != ' ' ) && ( *rBuf != 0x9 ) && ( *rBuf != 0xd ) && ( *rBuf != 0xa ) )
84     {
85         switch ( *rBuf )
86         {
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 )
98                 {
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;
103                 }
104                 break;
105         }
106         nSecurityCount--;
107         ++rBuf;
108     }
109     if ( bNegative )
110         nRetValue = -nRetValue;
111     return nRetValue;
112 }
113 
114 
115 static int ImplGetLen( sal_uInt8* pBuf, int nMax )
116 {
117     int nLen = 0;
118     while( nLen != nMax )
119     {
120         sal_uInt8 nDat = *pBuf++;
121         if ( nDat == 0x0a || nDat == 0x25 )
122             break;
123         nLen++;
124     }
125     return nLen;
126 }
127 
128 static void MakeAsMeta(Graphic &rGraphic)
129 {
130     ScopedVclPtrInstance< VirtualDevice > pVDev;
131     GDIMetaFile     aMtf;
132     Size            aSize = rGraphic.GetPrefSize();
133 
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));
140 
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;
149 }
150 
151 static oslProcessError runProcessWithPathSearch(const OUString &rProgName,
152     rtl_uString* pArgs[], sal_uInt32 nArgs, oslProcess *pProcess,
153     oslFileHandle *pIn, oslFileHandle *pOut, oslFileHandle *pErr)
154 {
155     oslProcessError result = osl_Process_E_None;
156     oslSecurity pSecurity = osl_getCurrentSecurity();
157 #ifdef _WIN32
158     /*
159      * ooo#72096
160      * On Window the underlying SearchPath searches in order of...
161      * The directory from which the application loaded.
162      * The current directory.
163      * The Windows system directory.
164      * The Windows directory.
165      * The directories that are listed in the PATH environment variable.
166      *
167      * Because one of our programs is called "convert" and there is a convert
168      * in the windows system directory, we want to explicitly search the PATH
169      * to avoid picking up on that one if ImageMagick's convert precedes it in
170      * PATH.
171      *
172      */
173     OUString url;
174     OUString path(o3tl::toU(_wgetenv(L"PATH")));
175 
176     oslFileError err = osl_searchFileURL(rProgName.pData, path.pData, &url.pData);
177     if (err != osl_File_E_None)
178         result = osl_Process_E_NotFound;
179     else
180         result = osl_executeProcess_WithRedirectedIO(url.pData,
181             pArgs, nArgs, osl_Process_HIDDEN,
182             pSecurity, nullptr, nullptr, 0, pProcess, pIn, pOut, pErr);
183 #else
184     result = osl_executeProcess_WithRedirectedIO(rProgName.pData,
185         pArgs, nArgs, osl_Process_SEARCHPATH | osl_Process_HIDDEN,
186         pSecurity, nullptr, nullptr, 0, pProcess, pIn, pOut, pErr);
187 #endif
188     osl_freeSecurityHandle( pSecurity );
189     return result;
190 }
191 
192 #if defined(_WIN32)
193 #    define EXESUFFIX ".exe"
194 #else
195 #    define EXESUFFIX ""
196 #endif
197 
198 static bool RenderAsEMF(const sal_uInt8* pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
199 {
200     utl::TempFileNamed aTempOutput;
201     utl::TempFileNamed aTempInput;
202     aTempOutput.EnableKillingFile();
203     aTempInput.EnableKillingFile();
204     OUString output;
205     osl::FileBase::getSystemPathFromFileURL(aTempOutput.GetURL(), output);
206     OUString input;
207     osl::FileBase::getSystemPathFromFileURL(aTempInput.GetURL(), input);
208 
209     SvStream* pInputStream = aTempInput.GetStream(StreamMode::WRITE);
210     sal_uInt64 nCount = pInputStream->WriteBytes(pBuf, nBytesRead);
211     aTempInput.CloseStream();
212 
213     //fdo#64161 pstoedit under non-windows uses libEMF to output the EMF, but
214     //libEMF cannot calculate the bounding box of text, so the overall bounding
215     //box is not increased to include that of any text in the eps
216     //
217     //-drawbb will force pstoedit to draw a pair of pixels with the bg color to
218     //the topleft and bottom right of the bounding box as pstoedit sees it,
219     //which libEMF will then extend its bounding box to fit
220     //
221     //-usebbfrominput forces pstoedit to take the original ps bounding box
222     //as the bounding box as it sees it, instead of calculating its own
223     //which also doesn't work for this example
224     //
225     //Under Linux, positioning of letters within pstoedit is very approximate.
226     //Using the -nfw option delegates the positioning to the reader, and we
227     //will do a proper job.  The option is ignored on Windows.
228     OUString arg1("-usebbfrominput");   //-usebbfrominput use the original ps bounding box
229     OUString arg2("-f");
230     OUString arg3("emf:-OO -drawbb -nfw"); //-drawbb mark out the bounding box extent with bg pixels
231                                            //-nfw delegate letter placement to us
232     rtl_uString *args[] =
233     {
234         arg1.pData, arg2.pData, arg3.pData, input.pData, output.pData
235     };
236     oslProcess aProcess;
237     oslFileHandle pIn = nullptr;
238     oslFileHandle pOut = nullptr;
239     oslFileHandle pErr = nullptr;
240     oslProcessError eErr = runProcessWithPathSearch(
241             "pstoedit" EXESUFFIX,
242             args, SAL_N_ELEMENTS(args),
243             &aProcess, &pIn, &pOut, &pErr);
244 
245     if (eErr!=osl_Process_E_None)
246         return false;
247 
248     bool bRet = false;
249     if (pIn) osl_closeFile(pIn);
250     osl_joinProcess(aProcess);
251     osl_freeProcessHandle(aProcess);
252     bool bEMFSupported=true;
253     if (pOut)
254     {
255         rtl::ByteSequence seq;
256         if (osl_File_E_None == osl_readLine(pOut, reinterpret_cast<sal_Sequence **>(&seq)))
257         {
258             OString line( reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength() );
259             if (line.startsWith("Unsupported output format"))
260                 bEMFSupported=false;
261         }
262         osl_closeFile(pOut);
263     }
264     if (pErr) osl_closeFile(pErr);
265     if (nCount == nBytesRead && bEMFSupported)
266     {
267         SvFileStream aFile(output, StreamMode::READ);
268         if (GraphicConverter::Import(aFile, rGraphic, ConvertDataFormat::EMF) == ERRCODE_NONE)
269             bRet = true;
270     }
271 
272     return bRet;
273 }
274 
275 namespace {
276 
277 struct WriteData
278 {
279     oslFileHandle   m_pFile;
280     const sal_uInt8 *m_pBuf;
281     sal_uInt32      m_nBytesToWrite;
282 };
283 
284 }
285 
286 extern "C" {
287 
288 static void WriteFileInThread(void *wData)
289 {
290     sal_uInt64 nCount;
291     WriteData *wdata = static_cast<WriteData *>(wData);
292     osl_writeFile(wdata->m_pFile, wdata->m_pBuf, wdata->m_nBytesToWrite, &nCount);
293     // The number of bytes written does not matter.
294     // The helper process may close its input stream before reading it all.
295     // (e.g. at "showpage" in EPS)
296 
297     // File must be closed here.
298     // Otherwise, the helper process may wait for the next input,
299     // then its stdout is not closed and osl_readFile() blocks.
300     if (wdata->m_pFile) osl_closeFile(wdata->m_pFile);
301 }
302 
303 }
304 
305 static bool RenderAsBMPThroughHelper(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
306                                      Graphic& rGraphic,
307                                      std::initializer_list<std::u16string_view> aProgNames,
308                                      rtl_uString* pArgs[], size_t nArgs)
309 {
310     oslProcess aProcess = nullptr;
311     oslFileHandle pIn = nullptr;
312     oslFileHandle pOut = nullptr;
313     oslFileHandle pErr = nullptr;
314     oslProcessError eErr = osl_Process_E_Unknown;
315     for (const auto& rProgName : aProgNames)
316     {
317         eErr = runProcessWithPathSearch(OUString(rProgName), pArgs, nArgs, &aProcess, &pIn, &pOut, &pErr);
318         if (eErr == osl_Process_E_None)
319             break;
320     }
321     if (eErr!=osl_Process_E_None)
322         return false;
323 
324     WriteData Data;
325     Data.m_pFile = pIn;
326     Data.m_pBuf = pBuf;
327     Data.m_nBytesToWrite = nBytesRead;
328     oslThread hThread = osl_createThread(WriteFileInThread, &Data);
329 
330     bool bRet = false;
331     sal_uInt64 nCount;
332     {
333         SvMemoryStream aMemStm;
334         sal_uInt8 aBuf[32000];
335         oslFileError eFileErr = osl_readFile(pOut, aBuf, 32000, &nCount);
336         while (eFileErr == osl_File_E_None && nCount)
337         {
338             aMemStm.WriteBytes(aBuf, sal::static_int_cast<std::size_t>(nCount));
339             eFileErr = osl_readFile(pOut, aBuf, 32000, &nCount);
340         }
341 
342         aMemStm.Seek(0);
343         if (
344             aMemStm.GetEndOfData() &&
345             GraphicConverter::Import(aMemStm, rGraphic, ConvertDataFormat::BMP) == ERRCODE_NONE
346            )
347         {
348             MakeAsMeta(rGraphic);
349             bRet = true;
350         }
351     }
352     if (pOut) osl_closeFile(pOut);
353     if (pErr) osl_closeFile(pErr);
354     osl_joinProcess(aProcess);
355     osl_freeProcessHandle(aProcess);
356     osl_joinWithThread(hThread);
357     osl_destroyThread(hThread);
358     return bRet;
359 }
360 
361 static bool RenderAsBMPThroughConvert(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
362     Graphic &rGraphic)
363 {
364     // density in pixel/inch
365     OUString arg1("-density");
366     // since the preview is also used for PDF-Export & printing on non-PS-printers,
367     // use some better quality - 300x300 should allow some resizing as well
368     OUString arg2("300x300");
369     // read eps from STDIN
370     OUString arg3("eps:-");
371     // write bmp to STDOUT
372     OUString arg4("bmp:-");
373     rtl_uString *args[] =
374     {
375         arg1.pData, arg2.pData, arg3.pData, arg4.pData
376     };
377     return RenderAsBMPThroughHelper(pBuf, nBytesRead, rGraphic,
378         { u"convert" EXESUFFIX },
379         args,
380         SAL_N_ELEMENTS(args));
381 }
382 
383 static bool RenderAsBMPThroughGS(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
384     Graphic &rGraphic)
385 {
386     OUString arg1("-q");
387     OUString arg2("-dBATCH");
388     OUString arg3("-dNOPAUSE");
389     OUString arg4("-dPARANOIDSAFER");
390     OUString arg5("-dEPSCrop");
391     OUString arg6("-dTextAlphaBits=4");
392     OUString arg7("-dGraphicsAlphaBits=4");
393     OUString arg8("-r300x300");
394     OUString arg9("-sDEVICE=bmp16m");
395     OUString arg10("-sOutputFile=-");
396     OUString arg11("-");
397     rtl_uString *args[] =
398     {
399         arg1.pData, arg2.pData, arg3.pData, arg4.pData, arg5.pData,
400         arg6.pData, arg7.pData, arg8.pData, arg9.pData, arg10.pData,
401         arg11.pData
402     };
403     return RenderAsBMPThroughHelper(pBuf, nBytesRead, rGraphic,
404 #ifdef _WIN32
405         // Try both 32-bit and 64-bit ghostscript executable name
406         {
407             u"gswin32c" EXESUFFIX,
408             u"gswin64c" EXESUFFIX,
409         },
410 #else
411         { u"gs" EXESUFFIX },
412 #endif
413         args,
414         SAL_N_ELEMENTS(args));
415 }
416 
417 static bool RenderAsBMP(const sal_uInt8* pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
418 {
419     if (RenderAsBMPThroughGS(pBuf, nBytesRead, rGraphic))
420         return true;
421     else
422         return RenderAsBMPThroughConvert(pBuf, nBytesRead, rGraphic);
423 }
424 
425 // this method adds a replacement action containing the original wmf or tiff replacement,
426 // so the original eps can be written when storing to ODF.
427 static void CreateMtfReplacementAction( GDIMetaFile& rMtf, SvStream& rStrm, sal_uInt32 nOrigPos, sal_uInt32 nPSSize,
428                                 sal_uInt32 nPosWMF, sal_uInt32 nSizeWMF, sal_uInt32 nPosTIFF, sal_uInt32 nSizeTIFF )
429 {
430     OString aComment("EPSReplacementGraphic");
431     if ( nSizeWMF || nSizeTIFF )
432     {
433         std::vector<sal_uInt8> aWMFBuf;
434         if (nSizeWMF && checkSeek(rStrm, nOrigPos + nPosWMF) && rStrm.remainingSize() >= nSizeWMF)
435         {
436             aWMFBuf.resize(nSizeWMF);
437             aWMFBuf.resize(rStrm.ReadBytes(aWMFBuf.data(), nSizeWMF));
438         }
439         nSizeWMF = aWMFBuf.size();
440 
441         std::vector<sal_uInt8> aTIFFBuf;
442         if (nSizeTIFF && checkSeek(rStrm, nOrigPos + nPosTIFF) && rStrm.remainingSize() >= nSizeTIFF)
443         {
444             aTIFFBuf.resize(nSizeTIFF);
445             aTIFFBuf.resize(rStrm.ReadBytes(aTIFFBuf.data(), nSizeTIFF));
446         }
447         nSizeTIFF = aTIFFBuf.size();
448 
449         SvMemoryStream aReplacement( nSizeWMF + nSizeTIFF + 28 );
450         sal_uInt32 const nMagic = 0xc6d3d0c5;
451         sal_uInt32 nPPos = 28 + nSizeWMF + nSizeTIFF;
452         sal_uInt32 nWPos = nSizeWMF ? 28 : 0;
453         sal_uInt32 nTPos = nSizeTIFF ? 28 + nSizeWMF : 0;
454 
455         aReplacement.WriteUInt32( nMagic ).WriteUInt32( nPPos ).WriteUInt32( nPSSize )
456                     .WriteUInt32( nWPos ).WriteUInt32( nSizeWMF )
457                     .WriteUInt32( nTPos ).WriteUInt32( nSizeTIFF );
458 
459         aReplacement.WriteBytes(aWMFBuf.data(), nSizeWMF);
460         aReplacement.WriteBytes(aTIFFBuf.data(), nSizeTIFF);
461         rMtf.AddAction( static_cast<MetaAction*>( new MetaCommentAction( aComment, 0, static_cast<const sal_uInt8*>(aReplacement.GetData()), aReplacement.Tell() ) ) );
462     }
463     else
464         rMtf.AddAction( static_cast<MetaAction*>( new MetaCommentAction( aComment, 0, nullptr, 0 ) ) );
465 }
466 
467 //there is no preview -> make a red box
468 static void MakePreview(sal_uInt8* pBuf, sal_uInt32 nBytesRead,
469     tools::Long nWidth, tools::Long nHeight, Graphic &rGraphic)
470 {
471     GDIMetaFile aMtf;
472     ScopedVclPtrInstance< VirtualDevice > pVDev;
473     vcl::Font       aFont;
474 
475     pVDev->EnableOutput( false );
476     aMtf.Record( pVDev );
477     pVDev->SetLineColor( COL_RED );
478     pVDev->SetFillColor();
479 
480     aFont.SetColor( COL_LIGHTRED );
481 
482     pVDev->Push( vcl::PushFlags::FONT );
483     pVDev->SetFont( aFont );
484 
485     tools::Rectangle aRect( Point( 1, 1 ), Size( nWidth - 2, nHeight - 2 ) );
486     pVDev->DrawRect( aRect );
487 
488     OUString aString;
489     int nLen;
490     sal_uInt8* pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%Title:"), nBytesRead - 32, 8 );
491     sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
492     if (nRemainingBytes >= 8)
493     {
494         pDest += 8;
495         nRemainingBytes -= 8;
496         if (nRemainingBytes && *pDest == ' ')
497         {
498             ++pDest;
499             --nRemainingBytes;
500         }
501         nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
502         if (o3tl::make_unsigned(nLen) < nRemainingBytes)
503         {
504             sal_uInt8 aOldValue(pDest[ nLen ]); pDest[ nLen ] = 0;
505             if ( strcmp( reinterpret_cast<char*>(pDest), "none" ) != 0 )
506             {
507                 const char* pStr = reinterpret_cast<char*>(pDest);
508                 aString += " Title:" + OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US) + "\n";
509             }
510             pDest[ nLen ] = aOldValue;
511         }
512     }
513     pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%Creator:"), nBytesRead - 32, 10 );
514     nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
515     if (nRemainingBytes >= 10)
516     {
517         pDest += 10;
518         nRemainingBytes -= 10;
519         if (nRemainingBytes && *pDest == ' ')
520         {
521             ++pDest;
522             --nRemainingBytes;
523         }
524         nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
525         if (o3tl::make_unsigned(nLen) < nRemainingBytes)
526         {
527             sal_uInt8 aOldValue(pDest[nLen]); pDest[nLen] = 0;
528             const char* pStr = reinterpret_cast<char*>(pDest);
529             aString += " Creator:" + OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US) + "\n";
530             pDest[nLen] = aOldValue;
531         }
532     }
533     pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%CreationDate:"), nBytesRead - 32, 15 );
534     nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
535     if (nRemainingBytes >= 15)
536     {
537         pDest += 15;
538         nRemainingBytes -= 15;
539         if (nRemainingBytes && *pDest == ' ')
540         {
541             ++pDest;
542             --nRemainingBytes;
543         }
544         nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
545         if (o3tl::make_unsigned(nLen) < nRemainingBytes)
546         {
547             sal_uInt8 aOldValue(pDest[ nLen ]); pDest[ nLen ] = 0;
548             if ( strcmp( reinterpret_cast<char*>(pDest), "none" ) != 0 )
549             {
550                 aString += " CreationDate:" + OUString::createFromAscii( reinterpret_cast<char*>(pDest) ) + "\n";
551                 const char* pStr = reinterpret_cast<char*>(pDest);
552                 aString += " CreationDate:" + OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US) + "\n";
553             }
554             pDest[ nLen ] = aOldValue;
555         }
556     }
557     pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%LanguageLevel:"), nBytesRead - 4, 16 );
558     nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
559     if (nRemainingBytes >= 16)
560     {
561         pDest += 16;
562         nRemainingBytes -= 16;
563         sal_uInt32 nCount = std::min<sal_uInt32>(nRemainingBytes, 4U);
564         sal_uInt32 nNumber = ImplGetNumber(pDest, nCount);
565         if (nCount && nNumber < 10)
566         {
567             aString += " LanguageLevel:" + OUString::number( nNumber );
568         }
569     }
570     pVDev->DrawText( aRect, aString, DrawTextFlags::Clip | DrawTextFlags::MultiLine );
571     pVDev->Pop();
572     aMtf.Stop();
573     aMtf.WindStart();
574     aMtf.SetPrefMapMode(MapMode(MapUnit::MapPoint));
575     aMtf.SetPrefSize( Size( nWidth, nHeight ) );
576     rGraphic = aMtf;
577 }
578 
579 //================== GraphicImport - the exported function ================
580 
581 
582 bool ImportEpsGraphic( SvStream & rStream, Graphic & rGraphic)
583 {
584     if ( rStream.GetError() )
585         return false;
586 
587     Graphic     aGraphic;
588     bool    bRetValue = false;
589     bool    bHasPreview = false;
590     sal_uInt32  nSignature = 0, nPSStreamPos, nPSSize = 0;
591     sal_uInt32  nSizeWMF = 0;
592     sal_uInt32  nPosWMF = 0;
593     sal_uInt32  nSizeTIFF = 0;
594     sal_uInt32  nPosTIFF = 0;
595 
596     auto nOrigPos = nPSStreamPos = rStream.Tell();
597     SvStreamEndian nOldFormat = rStream.GetEndian();
598 
599     rStream.SetEndian( SvStreamEndian::LITTLE );
600     rStream.ReadUInt32( nSignature );
601     if ( nSignature == 0xc6d3d0c5 )
602     {
603         rStream.ReadUInt32( nPSStreamPos ).ReadUInt32( nPSSize ).ReadUInt32( nPosWMF ).ReadUInt32( nSizeWMF );
604 
605         // first we try to get the metafile grafix
606 
607         if ( nSizeWMF )
608         {
609             if (nPosWMF && checkSeek(rStream, nOrigPos + nPosWMF))
610             {
611                 if (GraphicConverter::Import(rStream, aGraphic, ConvertDataFormat::WMF) == ERRCODE_NONE)
612                     bHasPreview = bRetValue = true;
613             }
614         }
615         else
616         {
617             rStream.ReadUInt32( nPosTIFF ).ReadUInt32( nSizeTIFF );
618 
619             // else we have to get the tiff grafix
620 
621             if (nPosTIFF && nSizeTIFF && checkSeek(rStream, nOrigPos + nPosTIFF))
622             {
623                 if ( GraphicConverter::Import( rStream, aGraphic, ConvertDataFormat::TIF ) == ERRCODE_NONE )
624                 {
625                     MakeAsMeta(aGraphic);
626                     rStream.Seek( nOrigPos + nPosTIFF );
627                     bHasPreview = bRetValue = true;
628                 }
629             }
630         }
631     }
632     else
633     {
634         nPSStreamPos = nOrigPos;            // no preview available _>so we must get the size manually
635         nPSSize = rStream.Seek( STREAM_SEEK_TO_END ) - nOrigPos;
636     }
637 
638     std::vector<sal_uInt8> aHeader(22, 0);
639     rStream.Seek( nPSStreamPos );
640     rStream.ReadBytes(aHeader.data(), 22); // check PostScript header
641     sal_uInt8* pHeader = aHeader.data();
642     bool bOk = ImplSearchEntry(pHeader, reinterpret_cast<sal_uInt8 const *>("%!PS-Adobe"), 10, 10) &&
643                ImplSearchEntry(pHeader + 15, reinterpret_cast<sal_uInt8 const *>("EPS"), 3, 3);
644     if (bOk)
645     {
646         rStream.Seek(nPSStreamPos);
647         bOk = rStream.remainingSize() >= nPSSize;
648         SAL_WARN_IF(!bOk, "filter.eps", "eps claims to be: " << nPSSize << " in size, but only " << rStream.remainingSize() << " remains");
649     }
650     if (bOk)
651     {
652         std::unique_ptr<sal_uInt8[]> pBuf( new sal_uInt8[ nPSSize ] );
653 
654         sal_uInt32 nBufStartPos = rStream.Tell();
655         sal_uInt32 nBytesRead = rStream.ReadBytes(pBuf.get(), nPSSize);
656         if ( nBytesRead == nPSSize )
657         {
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)
662             {
663                 sal_uInt8* pDest = ImplSearchEntry( pBuf.get(), reinterpret_cast<sal_uInt8 const *>("%%BeginPreview:"), nBytesRead - nSecurityCount, 15 );
664                 sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf.get())) : 0;
665                 if (nRemainingBytes >= 15)
666                 {
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)
676                     {
677                         tools::Long nResult;
678                         bOk = !o3tl::checked_multiply(nWidth, nHeight, nResult) && nResult <= SAL_MAX_INT32/2/3;
679                     }
680                     if (bOk)
681                     {
682                         rStream.Seek( nBufStartPos + ( pDest - pBuf.get() ) );
683 
684                         vcl::bitmap::RawBitmap aBitmap( Size( nWidth, nHeight ), 24 );
685                         {
686                             bool bIsValid = true;
687                             sal_uInt8 nDat = 0;
688                             char nByte;
689                             for (tools::Long y = 0; bIsValid && y < nHeight; ++y)
690                             {
691                                 int nBitsLeft = 0;
692                                 for (tools::Long x = 0; x < nWidth; ++x)
693                                 {
694                                     if ( --nBitsLeft < 0 )
695                                     {
696                                         while ( bIsValid && ( nBitsLeft != 7 ) )
697                                         {
698                                             rStream.ReadChar(nByte);
699                                             bIsValid = rStream.good();
700                                             if (!bIsValid)
701                                                 break;
702                                             switch (nByte)
703                                             {
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:
714                                                 {
715                                                     if ( nByte >= '0' )
716                                                     {
717                                                         if ( nByte > '9' )
718                                                         {
719                                                             nByte &=~0x20;  // case none sensitive for hexadecimal values
720                                                             nByte -= ( 'A' - 10 );
721                                                             if ( nByte > 15 )
722                                                                 bIsValid = false;
723                                                         }
724                                                         else
725                                                             nByte -= '0';
726                                                         nBitsLeft += 4;
727                                                         nDat <<= 4;
728                                                         nDat |= ( nByte ^ 0xf ); // in epsi a zero bit represents white color
729                                                     }
730                                                     else
731                                                         bIsValid = false;
732                                                 }
733                                                 break;
734                                             }
735                                         }
736                                     }
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
742                                     {
743                                         aBitmap.SetPixel( y, x, nDat ? COL_WHITE : COL_BLACK );  // nBitDepth == 8
744                                         nBitsLeft = 0;
745                                     }
746                                 }
747                             }
748                             if (bIsValid)
749                             {
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;
763                             }
764                         }
765                     }
766                 }
767             }
768 
769             sal_uInt8* pDest = ImplSearchEntry( pBuf.get(), reinterpret_cast<sal_uInt8 const *>("%%BoundingBox:"), nBytesRead, 14 );
770             sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf.get())) : 0;
771             if (nRemainingBytes >= 14)
772             {
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++ )
778                 {
779                     nNumb[ i ] = ImplGetNumber(pDest, nSecurityCount);
780                 }
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)
788                 {
789                     GDIMetaFile aMtf;
790 
791                     // if there is no preview -> try with gs to make one
792                     if (!bHasPreview && !utl::ConfigManager::IsFuzzing())
793                     {
794                         bHasPreview = RenderAsEMF(pBuf.get(), nBytesRead, aGraphic);
795                         if (!bHasPreview)
796                             bHasPreview = RenderAsBMP(pBuf.get(), nBytesRead, aGraphic);
797                     }
798 
799                     // if there is no preview -> make a red box
800                     if( !bHasPreview )
801                     {
802                         MakePreview(pBuf.get(), nBytesRead, nWidth, nHeight,
803                             aGraphic);
804                     }
805 
806                     GfxLink     aGfxLink( std::move(pBuf), nPSSize, 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;
815                 }
816             }
817         }
818     }
819 
820     rStream.SetEndian(nOldFormat);
821     rStream.Seek( nOrigPos );
822     return bRetValue;
823 }
824 
825 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
826