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>
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>
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 {
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 
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::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,
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);
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() &&
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;
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( 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  {
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  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: */
tuple line
bool ImportEpsGraphic(SvStream &rStream, Graphic &rGraphic)
Definition: ieps.cxx:582
std::enable_if< std::is_signed< T >::value, bool >::type checked_sub(T a, T b, T &result)
static bool RenderAsBMPThroughGS(const sal_uInt8 *pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
Definition: ieps.cxx:383
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
Definition: map.cxx:1562
Size GetSizePixel(const OutputDevice *pRefDevice=nullptr) const
Definition: graph.cxx:412
constexpr::Color COL_RED(0x80, 0x00, 0x00)
long Long
void CloseStream()
void SetPrefSize(const Size &rSize)
Definition: gdimtf.hxx:173
aBuf
#define STREAM_SEEK_TO_END
tuple args
sal_uInt64 Seek(sal_uInt64 nPos)
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
exports com.sun.star.xml. input
float x
static OutputDevice * GetDefaultDevice()
Get the default "device" (in this case the default window).
Definition: svapp.cxx:1069
constexpr tools::Long Width() const
std::size_t GetEndOfData() const
ErrCode GetError() const
void Record(OutputDevice *pOutDev)
Definition: gdimtf.cxx:311
const sal_uInt16 nMagic
static bool IsFuzzing()
static void WriteFileInThread(void *wData)
Definition: ieps.cxx:288
int nCount
oslFileHandle & pOut
const GDIMetaFile & GetGDIMetaFile() const
Definition: graph.cxx:340
SvStream & WriteUInt32(sal_uInt32 nUInt32)
sal_uInt64 remainingSize()
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_LIGHTRED
float y
#define SAL_N_ELEMENTS(arr)
SvStream & ReadUInt32(sal_uInt32 &rUInt32)
err
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
bool checkSeek(SvStream &rSt, sal_uInt64 nOffset)
#define SAL_MAX_INT32
int i
OUString const & GetURL() const
static bool RenderAsBMP(const sal_uInt8 *pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
Definition: ieps.cxx:417
static void MakePreview(sal_uInt8 *pBuf, sal_uInt32 nBytesRead, tools::Long nWidth, tools::Long nHeight, Graphic &rGraphic)
Definition: ieps.cxx:468
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::size_t WriteBytes(const void *pData, std::size_t nSize)
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
float u
static bool RenderAsBMPThroughConvert(const sal_uInt8 *pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
Definition: ieps.cxx:361
static tools::Long ImplGetNumber(sal_uInt8 *&rBuf, sal_uInt32 &nSecurityCount)
Definition: ieps.cxx:73
ColorTransparency
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
Definition: graph.cxx:330
void WindStart()
Definition: gdimtf.cxx:549
static ErrCode Import(SvStream &rIStm, Graphic &rGraphic, ConvertDataFormat nFormat=ConvertDataFormat::Unknown)
Definition: cvtgrf.cxx:34
Intended to be used to feed into CreateFromData to create a BitmapEx.
Definition: RawBitmap.hxx:21
static oslProcessError runProcessWithPathSearch(const OUString &rProgName, rtl_uString *pArgs[], sal_uInt32 nArgs, oslProcess *pProcess, oslFileHandle *pIn, oslFileHandle *pOut, oslFileHandle *pErr)
Definition: ieps.cxx:151
void SetColor(const Color &)
Definition: font/font.cxx:90
std::size_t ReadBytes(void *pData, std::size_t nSize)
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1107
Size GetPrefSize() const
Definition: graph.cxx:364
SvStreamEndian GetEndian() const
void Stop()
Definition: gdimtf.cxx:536
static void MakeAsMeta(Graphic &rGraphic)
Definition: ieps.cxx:128
SvStream & ReadChar(char &rChar)
#define SAL_WARN_IF(condition, area, stream)
#define ERRCODE_NONE
Definition: errcode.hxx:196
MapMode GetPrefMapMode() const
Definition: graph.cxx:375
constexpr tools::Long Height() const
unsigned char sal_uInt8
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:562
void SetEndian(SvStreamEndian SvStreamEndian)
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
#define EXESUFFIX
Definition: ieps.cxx:195
sal_uInt64 Tell() const
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
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.
std::unique_ptr< ::osl::File > m_pFile
bool good() const
static sal_uInt8 * ImplSearchEntry(sal_uInt8 *pSource, sal_uInt8 const *pDest, size_t nComp, size_t nSize)
Definition: ieps.cxx:54
Any result
SvStreamEndian
static bool RenderAsEMF(const sal_uInt8 *pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
Definition: ieps.cxx:198
SvStream * GetStream(StreamMode eMode)
void SetPixel(tools::Long nY, tools::Long nX, Color nColor)
Definition: RawBitmap.hxx:47
void EnableKillingFile(bool bEnable=true)
const void * GetData()
void SetPrefMapMode(const MapMode &rMapMode)
Definition: gdimtf.hxx:176
static int ImplGetLen(sal_uInt8 *pBuf, int nMax)
Definition: ieps.cxx:115