LibreOffice Module vcl (master) 1
ieps.cxx
Go to the documentation of this file.
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>
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>
37#include <o3tl/safeint.hxx>
38#include <memory>
39#include <string_view>
40
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
54static 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
73static 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
115static 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
128static void MakeAsMeta(Graphic &rGraphic)
129{
131 GDIMetaFile aMtf;
132 Size aSize = rGraphic.GetPrefSize();
133
134 if( !aSize.Width() || !aSize.Height() )
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
151static 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
198static bool RenderAsEMF(const sal_uInt8* pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
199{
200 utl::TempFile aTempOutput;
201 utl::TempFile 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,
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);
269 bRet = true;
270 }
271
272 return bRet;
273}
274
275namespace {
276
277struct WriteData
278{
279 oslFileHandle m_pFile;
280 const sal_uInt8 *m_pBuf;
281 sal_uInt32 m_nBytesToWrite;
282};
283
284}
285
286extern "C" {
287
288static 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
305static 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() &&
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
361static 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,
381}
382
383static 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,
415}
416
417static 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.
427static 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
468static void MakePreview(sal_uInt8* pBuf, sal_uInt32 nBytesRead,
469 tools::Long nWidth, tools::Long nHeight, Graphic &rGraphic)
470{
471 GDIMetaFile aMtf;
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
582bool 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 {
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 {
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: */
static OutputDevice * GetDefaultDevice()
Get the default "device" (in this case the default window).
Definition: svapp.cxx:1166
void WindStart()
Definition: gdimtf.cxx:568
void Stop()
Definition: gdimtf.cxx:555
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:581
void SetPrefMapMode(const MapMode &rMapMode)
Definition: gdimtf.hxx:176
void Record(OutputDevice *pOutDev)
Definition: gdimtf.cxx:311
void SetPrefSize(const Size &rSize)
Definition: gdimtf.hxx:173
static ErrCode Import(SvStream &rIStm, Graphic &rGraphic, ConvertDataFormat nFormat=ConvertDataFormat::Unknown)
Definition: cvtgrf.cxx:30
Size GetPrefSize() const
Definition: graph.cxx:363
const GDIMetaFile & GetGDIMetaFile() const
Definition: graph.cxx:339
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
Definition: graph.cxx:329
MapMode GetPrefMapMode() const
Definition: graph.cxx:374
Size GetSizePixel(const OutputDevice *pRefDevice=nullptr) const
Definition: graph.cxx:411
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1169
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
Definition: map.cxx:1639
constexpr tools::Long Height() const
constexpr tools::Long Width() const
const void * GetData()
std::size_t GetEndOfData() const
sal_uInt64 Tell() const
void SetEndian(SvStreamEndian SvStreamEndian)
bool good() const
std::size_t WriteBytes(const void *pData, std::size_t nSize)
SvStream & WriteUInt32(sal_uInt32 nUInt32)
SvStream & ReadUInt32(sal_uInt32 &rUInt32)
SvStreamEndian GetEndian() const
SvStream & ReadChar(char &rChar)
sal_uInt64 Seek(sal_uInt64 nPos)
std::size_t ReadBytes(void *pData, std::size_t nSize)
ErrCode GetError() const
sal_uInt64 remainingSize()
static bool IsFuzzing()
OUString const & GetURL() const
void CloseStream()
void EnableKillingFile(bool bEnable=true)
SvStream * GetStream(StreamMode eMode)
void SetColor(const Color &)
Definition: font/font.cxx:107
Intended to be used to feed into CreateFromData to create a BitmapEx.
Definition: RawBitmap.hxx:22
void SetPixel(tools::Long nY, tools::Long nX, Color nColor)
Definition: RawBitmap.hxx:47
constexpr ::Color COL_LIGHTRED(0xFF, 0x00, 0x00)
constexpr ::Color COL_WHITE(0xFF, 0xFF, 0xFF)
constexpr ::Color COL_RED(0x80, 0x00, 0x00)
ColorTransparency
constexpr ::Color COL_BLACK(0x00, 0x00, 0x00)
int nCount
float u
float y
float x
#define ERRCODE_NONE
std::unique_ptr< ::osl::File > 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)
Definition: ieps.cxx:305
static bool RenderAsEMF(const sal_uInt8 *pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
Definition: ieps.cxx:198
static sal_uInt8 * ImplSearchEntry(sal_uInt8 *pSource, sal_uInt8 const *pDest, size_t nComp, size_t nSize)
Definition: ieps.cxx:54
static void MakePreview(sal_uInt8 *pBuf, sal_uInt32 nBytesRead, tools::Long nWidth, tools::Long nHeight, Graphic &rGraphic)
Definition: ieps.cxx:468
bool ImportEpsGraphic(SvStream &rStream, Graphic &rGraphic)
Definition: ieps.cxx:582
static oslProcessError runProcessWithPathSearch(const OUString &rProgName, rtl_uString *pArgs[], sal_uInt32 nArgs, oslProcess *pProcess, oslFileHandle *pIn, oslFileHandle *pOut, oslFileHandle *pErr)
Definition: ieps.cxx:151
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)
Definition: ieps.cxx:427
static int ImplGetLen(sal_uInt8 *pBuf, int nMax)
Definition: ieps.cxx:115
static void MakeAsMeta(Graphic &rGraphic)
Definition: ieps.cxx:128
static bool RenderAsBMPThroughGS(const sal_uInt8 *pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
Definition: ieps.cxx:383
#define EXESUFFIX
Definition: ieps.cxx:195
static tools::Long ImplGetNumber(sal_uInt8 *&rBuf, sal_uInt32 &nSecurityCount)
Definition: ieps.cxx:73
static void WriteFileInThread(void *wData)
Definition: ieps.cxx:288
static bool RenderAsBMP(const sal_uInt8 *pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
Definition: ieps.cxx:417
static bool RenderAsBMPThroughConvert(const sal_uInt8 *pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
Definition: ieps.cxx:361
#define SAL_WARN_IF(condition, area, stream)
#define SAL_N_ELEMENTS(arr)
aBuf
err
int i
line
void SvStream & rStrm
std::enable_if< std::is_signed< T >::value, bool >::type checked_add(T a, T b, T &result)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
std::enable_if< std::is_signed< T >::value, bool >::type checked_sub(T a, T b, T &result)
args
long Long
BitmapEx CreateFromData(sal_uInt8 const *pData, sal_Int32 nWidth, sal_Int32 nHeight, sal_Int32 nStride, vcl::PixelFormat ePixelFormat, bool bReversColors, bool bReverseAlpha)
Copy block of image data into the bitmap.
const sal_uInt16 nMagic
#define STREAM_SEEK_TO_END
SvStreamEndian
TOOLS_DLLPUBLIC bool checkSeek(SvStream &rSt, sal_uInt64 nOffset)
unsigned char sal_uInt8
#define SAL_MAX_INT32
Any result
oslFileHandle & pOut