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
