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