loplugin:sequenceloop in unoxml..vcl
[LibreOffice.git] / vcl / source / filter / graphicfilter.cxx
blob901c9407e36f4a22644a18575ea0fcb96c25c108
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <config_folders.h>
22 #include <sal/log.hxx>
23 #include <osl/mutex.hxx>
24 #include <comphelper/processfactory.hxx>
25 #include <comphelper/threadpool.hxx>
26 #include <ucbhelper/content.hxx>
27 #include <cppuhelper/implbase.hxx>
28 #include <tools/fract.hxx>
29 #include <unotools/configmgr.hxx>
30 #include <tools/stream.hxx>
31 #include <tools/urlobj.hxx>
32 #include <tools/zcodec.hxx>
33 #include <vcl/dibtools.hxx>
34 #include <vcl/fltcall.hxx>
35 #include <vcl/salctype.hxx>
36 #include <vcl/pngread.hxx>
37 #include <vcl/pngwrite.hxx>
38 #include <vcl/vectorgraphicdata.hxx>
39 #include <vcl/virdev.hxx>
40 #include <impgraph.hxx>
41 #include <vcl/svapp.hxx>
42 #include <osl/file.hxx>
43 #include <vcl/graphicfilter.hxx>
44 #include <vcl/FilterConfigItem.hxx>
45 #include <vcl/wmf.hxx>
46 #include "igif/gifread.hxx"
47 #include <vcl/pdfread.hxx>
48 #include "jpeg/jpeg.hxx"
49 #include "ixbm/xbmread.hxx"
50 #include "ixpm/xpmread.hxx"
51 #include <osl/module.hxx>
52 #include <com/sun/star/uno/Reference.h>
53 #include <com/sun/star/awt/Size.hpp>
54 #include <com/sun/star/uno/XInterface.hpp>
55 #include <com/sun/star/io/XActiveDataSource.hpp>
56 #include <com/sun/star/io/XOutputStream.hpp>
57 #include <com/sun/star/svg/XSVGWriter.hpp>
58 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
59 #include <com/sun/star/xml/sax/Writer.hpp>
60 #include <com/sun/star/ucb/CommandAbortedException.hpp>
61 #include <com/sun/star/ucb/ContentCreationException.hpp>
62 #include <unotools/ucbstreamhelper.hxx>
63 #include <rtl/bootstrap.hxx>
64 #include <rtl/instance.hxx>
65 #include <tools/svlibrary.h>
66 #include <comphelper/string.hxx>
67 #include <vector>
68 #include <memory>
70 #include "FilterConfigCache.hxx"
71 #include "graphicfilter_internal.hxx"
73 #include <graphic/GraphicFormatDetector.hxx>
75 #define PMGCHUNG_msOG 0x6d734f47 // Microsoft Office Animated GIF
77 typedef ::std::vector< GraphicFilter* > FilterList_impl;
78 static FilterList_impl* pFilterHdlList = nullptr;
80 static ::osl::Mutex& getListMutex()
82 static ::osl::Mutex s_aListProtection;
83 return s_aListProtection;
86 class ImpFilterOutputStream : public ::cppu::WeakImplHelper< css::io::XOutputStream >
88 SvStream& mrStm;
90 virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& rData ) override
91 { mrStm.WriteBytes(rData.getConstArray(), rData.getLength()); }
92 virtual void SAL_CALL flush() override
93 { mrStm.Flush(); }
94 virtual void SAL_CALL closeOutput() override {}
96 public:
98 explicit ImpFilterOutputStream( SvStream& rStm ) : mrStm( rStm ) {}
101 static bool DirEntryExists( const INetURLObject& rObj )
103 bool bExists = false;
107 ::ucbhelper::Content aCnt( rObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
108 css::uno::Reference< css::ucb::XCommandEnvironment >(),
109 comphelper::getProcessComponentContext() );
111 bExists = aCnt.isDocument();
113 catch(const css::ucb::CommandAbortedException&)
115 SAL_WARN( "vcl.filter", "CommandAbortedException" );
117 catch(const css::ucb::ContentCreationException&)
119 SAL_WARN( "vcl.filter", "ContentCreationException" );
121 catch( ... )
123 SAL_WARN( "vcl.filter", "Any other exception" );
125 return bExists;
128 static void KillDirEntry( const OUString& rMainUrl )
132 ::ucbhelper::Content aCnt( rMainUrl,
133 css::uno::Reference< css::ucb::XCommandEnvironment >(),
134 comphelper::getProcessComponentContext() );
136 aCnt.executeCommand( "delete",
137 css::uno::makeAny( true ) );
139 catch(const css::ucb::CommandAbortedException&)
141 SAL_WARN( "vcl.filter", "CommandAbortedException" );
143 catch( ... )
145 SAL_WARN( "vcl.filter", "Any other exception" );
149 // Helper functions
151 sal_uInt8* ImplSearchEntry( sal_uInt8* pSource, sal_uInt8 const * pDest, sal_uLong nComp, sal_uLong nSize )
153 while ( nComp-- >= nSize )
155 sal_uLong i;
156 for ( i = 0; i < nSize; i++ )
158 if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
159 break;
161 if ( i == nSize )
162 return pSource;
163 pSource++;
165 return nullptr;
168 static OUString ImpGetExtension( const OUString &rPath )
170 OUString aExt;
171 INetURLObject aURL( rPath );
172 aExt = aURL.GetFileExtension().toAsciiUpperCase();
173 return aExt;
176 bool isPCT(SvStream& rStream, sal_uLong nStreamPos, sal_uLong nStreamLen)
178 sal_uInt8 sBuf[3];
179 // store number format
180 SvStreamEndian oldNumberFormat = rStream.GetEndian();
181 sal_uInt32 nOffset; // in MS documents the pict format is used without the first 512 bytes
182 for ( nOffset = 0; ( nOffset <= 512 ) && ( ( nStreamPos + nOffset + 14 ) <= nStreamLen ); nOffset += 512 )
184 short y1,x1,y2,x2;
185 bool bdBoxOk = true;
187 rStream.Seek( nStreamPos + nOffset);
188 // size of the pict in version 1 pict ( 2bytes) : ignored
189 rStream.SeekRel(2);
190 // bounding box (bytes 2 -> 9)
191 rStream.SetEndian(SvStreamEndian::BIG);
192 rStream.ReadInt16( y1 ).ReadInt16( x1 ).ReadInt16( y2 ).ReadInt16( x2 );
193 rStream.SetEndian(oldNumberFormat); // reset format
195 if (x1 > x2 || y1 > y2 || // bad bdbox
196 (x1 == x2 && y1 == y2) || // 1 pixel picture
197 x2-x1 > 2048 || y2-y1 > 2048 ) // picture abnormally big
198 bdBoxOk = false;
200 // read version op
201 rStream.ReadBytes(sBuf, 3);
202 // see http://developer.apple.com/legacy/mac/library/documentation/mac/pdf/Imaging_With_QuickDraw/Appendix_A.pdf
203 // normal version 2 - page A23 and A24
204 if ( sBuf[ 0 ] == 0x00 && sBuf[ 1 ] == 0x11 && sBuf[ 2 ] == 0x02)
205 return true;
206 // normal version 1 - page A25
207 else if (sBuf[ 0 ] == 0x11 && sBuf[ 1 ] == 0x01 && bdBoxOk)
208 return true;
210 return false;
213 /*************************************************************************
215 * ImpPeekGraphicFormat()
217 * Description:
218 * This function is two-fold:
219 * 1.) Start reading file, determine the file format:
220 * Input parameters:
221 * rPath - file path
222 * rFormatExtension - content matter
223 * bTest - set false
224 * Output parameters:
225 * Return value - true if success
226 * rFormatExtension - on success: normal file extension in capitals
227 * 2.) Start reading file, verify file format
228 * Input parameters:
229 * rPath - file path
230 * rFormatExtension - normal file extension in capitals
231 * bTest - set true
232 * Output parameters:
233 * Return value - false, if cannot verify the file type
234 * passed to the function
235 * true, when the format is PROBABLY verified or
236 * WHEN THE FORMAT IS NOT KNOWN!
238 *************************************************************************/
240 bool ImpPeekGraphicFormat( SvStream& rStream, OUString& rFormatExtension, bool bTest )
242 vcl::GraphicFormatDetector aDetector(rStream, rFormatExtension);
243 if (!aDetector.detect())
244 return false;
246 // The following variable is used when bTest == true. It remains false
247 // if the format (rFormatExtension) has not yet been set.
248 bool bSomethingTested = false;
250 // Now the different formats are checked. The order *does* matter. e.g. a MET file
251 // could also go through the BMP test, however, a BMP file can hardly go through the MET test.
252 // So MET should be tested prior to BMP. However, theoretically a BMP file could conceivably
253 // go through the MET test. These problems are of course not only in MET and BMP.
254 // Therefore, in the case of a format check (bTest == true) we only test *exactly* this
255 // format. Everything else could have fatal consequences, for example if the user says it is
256 // a BMP file (and it is a BMP) file, and the file would go through the MET test ...
258 if (!bTest || rFormatExtension.startsWith("MET"))
260 bSomethingTested = true;
261 if (aDetector.checkMET())
263 rFormatExtension = aDetector.msDetectedFormat;
264 return true;
268 if (!bTest || rFormatExtension.startsWith("BMP"))
270 bSomethingTested = true;
271 if (aDetector.checkBMP())
273 rFormatExtension = aDetector.msDetectedFormat;
274 return true;
278 if (!bTest ||
279 rFormatExtension.startsWith("WMF") ||
280 rFormatExtension.startsWith("EMF"))
282 bSomethingTested = true;
283 if (aDetector.checkWMForEMF())
285 rFormatExtension = aDetector.msDetectedFormat;
286 return true;
290 if (!bTest || rFormatExtension.startsWith("PCX"))
292 bSomethingTested = true;
293 if (aDetector.checkPCX())
295 rFormatExtension = aDetector.msDetectedFormat;
296 return true;
300 if (!bTest || rFormatExtension.startsWith("TIF"))
302 bSomethingTested = true;
303 if (aDetector.checkTIF())
305 rFormatExtension = aDetector.msDetectedFormat;
306 return true;
310 if (!bTest || rFormatExtension.startsWith("GIF"))
312 bSomethingTested = true;
313 if (aDetector.checkGIF())
315 rFormatExtension = aDetector.msDetectedFormat;
316 return true;
320 if (!bTest || rFormatExtension.startsWith("PNG"))
322 bSomethingTested = true;
323 if (aDetector.checkPNG())
325 rFormatExtension = aDetector.msDetectedFormat;
326 return true;
330 if (!bTest || rFormatExtension.startsWith("JPG"))
332 bSomethingTested = true;
333 if (aDetector.checkJPG())
335 rFormatExtension = aDetector.msDetectedFormat;
336 return true;
340 if (!bTest || rFormatExtension.startsWith("SVM"))
342 bSomethingTested = true;
343 if (aDetector.checkSVM())
345 rFormatExtension = aDetector.msDetectedFormat;
346 return true;
350 if (!bTest || rFormatExtension.startsWith("PCD"))
352 bSomethingTested = true;
353 if (aDetector.checkPCD())
355 rFormatExtension = aDetector.msDetectedFormat;
356 return true;
360 if (!bTest || rFormatExtension.startsWith("PSD"))
362 bSomethingTested = true;
363 if (aDetector.checkPSD())
365 rFormatExtension = aDetector.msDetectedFormat;
366 return true;
370 if (!bTest || rFormatExtension.startsWith("EPS"))
372 bSomethingTested = true;
373 if (aDetector.checkEPS())
375 rFormatExtension = aDetector.msDetectedFormat;
376 return true;
380 if (!bTest || rFormatExtension.startsWith("DXF"))
382 if (aDetector.checkDXF())
384 rFormatExtension = aDetector.msDetectedFormat;
385 return true;
389 if (!bTest || rFormatExtension.startsWith("PCT"))
391 bSomethingTested = true;
392 if (aDetector.checkPCT())
394 rFormatExtension = aDetector.msDetectedFormat;
395 return true;
399 if (!bTest ||
400 rFormatExtension.startsWith("PBM") ||
401 rFormatExtension.startsWith("PGM") ||
402 rFormatExtension.startsWith("PPM"))
404 bSomethingTested = true;
405 if (aDetector.checkPBMorPGMorPPM())
407 rFormatExtension = aDetector.msDetectedFormat;
408 return true;
412 if (!bTest || rFormatExtension.startsWith("RAS"))
414 bSomethingTested = true;
415 if (aDetector.checkRAS())
417 rFormatExtension = aDetector.msDetectedFormat;
418 return true;
422 if (!bTest)
424 bSomethingTested = true;
425 if (aDetector.checkXPM())
427 rFormatExtension = aDetector.msDetectedFormat;
428 return true;
432 else if (rFormatExtension.startsWith("XPM"))
434 return true;
437 if (!bTest)
439 if (aDetector.checkXBM())
441 rFormatExtension = aDetector.msDetectedFormat;
442 return true;
445 else if (rFormatExtension.startsWith("XBM"))
447 return true;
450 if (!bTest)
452 if (aDetector.checkSVG())
454 rFormatExtension = aDetector.msDetectedFormat;
455 return true;
458 else if (rFormatExtension.startsWith("SVG"))
460 return true;
463 if (!bTest || rFormatExtension.startsWith("TGA"))
465 bSomethingTested = true;
466 if (aDetector.checkTGA())
468 rFormatExtension = aDetector.msDetectedFormat;
469 return true;
473 if (!bTest || rFormatExtension.startsWith("MOV"))
475 if (aDetector.checkMOV())
477 rFormatExtension = aDetector.msDetectedFormat;
478 return true;
482 if (!bTest || rFormatExtension.startsWith("PDF"))
484 if (aDetector.checkPDF())
486 rFormatExtension = aDetector.msDetectedFormat;
487 return true;
491 return bTest && !bSomethingTested;
494 ErrCode GraphicFilter::ImpTestOrFindFormat( const OUString& rPath, SvStream& rStream, sal_uInt16& rFormat )
496 // determine or check the filter/format by reading into it
497 if( rFormat == GRFILTER_FORMAT_DONTKNOW )
499 OUString aFormatExt;
500 if( ImpPeekGraphicFormat( rStream, aFormatExt, false ) )
502 rFormat = pConfig->GetImportFormatNumberForExtension( aFormatExt );
503 if( rFormat != GRFILTER_FORMAT_DONTKNOW )
504 return ERRCODE_NONE;
506 // determine filter by file extension
507 if( !rPath.isEmpty() )
509 OUString aExt( ImpGetExtension( rPath ) );
510 rFormat = pConfig->GetImportFormatNumberForExtension( aExt );
511 if( rFormat != GRFILTER_FORMAT_DONTKNOW )
512 return ERRCODE_NONE;
514 return ERRCODE_GRFILTER_FORMATERROR;
516 else
518 OUString aTmpStr( pConfig->GetImportFormatExtension( rFormat ) );
519 aTmpStr = aTmpStr.toAsciiUpperCase();
520 if( !ImpPeekGraphicFormat( rStream, aTmpStr, true ) )
521 return ERRCODE_GRFILTER_FORMATERROR;
522 if ( pConfig->GetImportFormatExtension( rFormat ).equalsIgnoreAsciiCase( "pcd" ) )
524 sal_Int32 nBase = 2; // default Base0
525 if ( pConfig->GetImportFilterType( rFormat ).equalsIgnoreAsciiCase( "pcd_Photo_CD_Base4" ) )
526 nBase = 1;
527 else if ( pConfig->GetImportFilterType( rFormat ).equalsIgnoreAsciiCase( "pcd_Photo_CD_Base16" ) )
528 nBase = 0;
529 FilterConfigItem aFilterConfigItem( "Office.Common/Filter/Graphic/Import/PCD" );
530 aFilterConfigItem.WriteInt32( "Resolution", nBase );
534 return ERRCODE_NONE;
537 static Graphic ImpGetScaledGraphic( const Graphic& rGraphic, FilterConfigItem& rConfigItem )
539 Graphic aGraphic;
541 sal_Int32 nLogicalWidth = rConfigItem.ReadInt32( "LogicalWidth", 0 );
542 sal_Int32 nLogicalHeight = rConfigItem.ReadInt32( "LogicalHeight", 0 );
544 if ( rGraphic.GetType() != GraphicType::NONE )
546 sal_Int32 nMode = rConfigItem.ReadInt32( "ExportMode", -1 );
548 if ( nMode == -1 ) // the property is not there, this is possible, if the graphic filter
549 { // is called via UnoGraphicExporter and not from a graphic export Dialog
550 nMode = 0; // then we are defaulting this mode to 0
551 if ( nLogicalWidth || nLogicalHeight )
552 nMode = 2;
555 Size aOriginalSize;
556 Size aPrefSize( rGraphic.GetPrefSize() );
557 MapMode aPrefMapMode( rGraphic.GetPrefMapMode() );
558 if (aPrefMapMode.GetMapUnit() == MapUnit::MapPixel)
559 aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM));
560 else
561 aOriginalSize = OutputDevice::LogicToLogic(aPrefSize, aPrefMapMode, MapMode(MapUnit::Map100thMM));
562 if ( !nLogicalWidth )
563 nLogicalWidth = aOriginalSize.Width();
564 if ( !nLogicalHeight )
565 nLogicalHeight = aOriginalSize.Height();
566 if( rGraphic.GetType() == GraphicType::Bitmap )
569 // Resolution is set
570 if( nMode == 1 )
572 BitmapEx aBitmap( rGraphic.GetBitmapEx() );
573 MapMode aMap( MapUnit::Map100thInch );
575 sal_Int32 nDPI = rConfigItem.ReadInt32( "Resolution", 75 );
576 Fraction aFrac( 1, std::min( std::max( nDPI, sal_Int32( 75 ) ), sal_Int32( 600 ) ) );
578 aMap.SetScaleX( aFrac );
579 aMap.SetScaleY( aFrac );
581 Size aOldSize = aBitmap.GetSizePixel();
582 aGraphic = rGraphic;
583 aGraphic.SetPrefMapMode( aMap );
584 aGraphic.SetPrefSize( Size( aOldSize.Width() * 100,
585 aOldSize.Height() * 100 ) );
587 // Size is set
588 else if( nMode == 2 )
590 aGraphic = rGraphic;
591 aGraphic.SetPrefMapMode( MapMode( MapUnit::Map100thMM ) );
592 aGraphic.SetPrefSize( Size( nLogicalWidth, nLogicalHeight ) );
594 else
595 aGraphic = rGraphic;
597 sal_Int32 nColors = rConfigItem.ReadInt32( "Color", 0 );
598 if ( nColors ) // graphic conversion necessary ?
600 BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
601 aBmpEx.Convert( static_cast<BmpConversion>(nColors) ); // the entries in the xml section have the same meaning as
602 aGraphic = aBmpEx; // they have in the BmpConversion enum, so it should be
603 } // allowed to cast them
605 else
607 if( ( nMode == 1 ) || ( nMode == 2 ) )
609 GDIMetaFile aMtf( rGraphic.GetGDIMetaFile() );
610 Size aNewSize( OutputDevice::LogicToLogic(Size(nLogicalWidth, nLogicalHeight), MapMode(MapUnit::Map100thMM), aMtf.GetPrefMapMode()) );
612 if( aNewSize.Width() && aNewSize.Height() )
614 const Size aPreferredSize( aMtf.GetPrefSize() );
615 aMtf.Scale( Fraction( aNewSize.Width(), aPreferredSize.Width() ),
616 Fraction( aNewSize.Height(), aPreferredSize.Height() ) );
618 aGraphic = Graphic( aMtf );
620 else
621 aGraphic = rGraphic;
625 else
626 aGraphic = rGraphic;
628 return aGraphic;
631 static OUString ImpCreateFullFilterPath( const OUString& rPath, const OUString& rFilterName )
633 OUString aPathURL;
635 ::osl::FileBase::getFileURLFromSystemPath( rPath, aPathURL );
636 aPathURL += "/";
638 OUString aSystemPath;
639 ::osl::FileBase::getSystemPathFromFileURL( aPathURL, aSystemPath );
640 aSystemPath += rFilterName;
642 return aSystemPath;
645 class ImpFilterLibCache;
647 struct ImpFilterLibCacheEntry
649 ImpFilterLibCacheEntry* mpNext;
650 #ifndef DISABLE_DYNLOADING
651 osl::Module maLibrary;
652 #endif
653 OUString const maFiltername;
654 OUString const maFormatName;
655 PFilterCall mpfnImport;
657 ImpFilterLibCacheEntry(const OUString& rPathname, const OUString& rFiltername, const OUString& rFormatName);
658 bool operator==( const OUString& rFiltername ) const { return maFiltername == rFiltername; }
660 PFilterCall GetImportFunction();
663 ImpFilterLibCacheEntry::ImpFilterLibCacheEntry( const OUString& rPathname, const OUString& rFiltername, const OUString& rFormatName ) :
664 mpNext ( nullptr ),
665 #ifndef DISABLE_DYNLOADING
666 maLibrary ( rPathname ),
667 #endif
668 maFiltername ( rFiltername ),
669 maFormatName ( rFormatName ),
670 mpfnImport ( nullptr )
672 #ifdef DISABLE_DYNLOADING
673 (void) rPathname;
674 #endif
677 #ifdef DISABLE_DYNLOADING
679 extern "C" bool icdGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
680 extern "C" bool idxGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
681 extern "C" bool imeGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
682 extern "C" bool ipbGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
683 extern "C" bool ipdGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
684 extern "C" bool ipsGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
685 extern "C" bool iptGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
686 extern "C" bool ipxGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
687 extern "C" bool iraGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
688 extern "C" bool itgGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
689 extern "C" bool itiGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
691 #endif
693 PFilterCall ImpFilterLibCacheEntry::GetImportFunction()
695 if( !mpfnImport )
697 #ifndef DISABLE_DYNLOADING
698 if (maFormatName == "icd")
699 mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("icdGraphicImport"));
700 else if (maFormatName == "idx")
701 mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("idxGraphicImport"));
702 else if (maFormatName == "ime")
703 mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("imeGraphicImport"));
704 else if (maFormatName == "ipb")
705 mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("ipbGraphicImport"));
706 else if (maFormatName == "ipd")
707 mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("ipdGraphicImport"));
708 else if (maFormatName == "ips")
709 mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("ipsGraphicImport"));
710 else if (maFormatName == "ipt")
711 mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("iptGraphicImport"));
712 else if (maFormatName == "ipx")
713 mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("ipxGraphicImport"));
714 else if (maFormatName == "ira")
715 mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("iraGraphicImport"));
716 else if (maFormatName == "itg")
717 mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("itgGraphicImport"));
718 else if (maFormatName == "iti")
719 mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("itiGraphicImport"));
720 #else
721 if (maFormatName == "icd")
722 mpfnImport = icdGraphicImport;
723 else if (maFormatName == "idx")
724 mpfnImport = idxGraphicImport;
725 else if (maFormatName == "ime")
726 mpfnImport = imeGraphicImport;
727 else if (maFormatName == "ipb")
728 mpfnImport = ipbGraphicImport;
729 else if (maFormatName == "ipd")
730 mpfnImport = ipdGraphicImport;
731 else if (maFormatName == "ips")
732 mpfnImport = ipsGraphicImport;
733 else if (maFormatName == "ipt")
734 mpfnImport = iptGraphicImport;
735 else if (maFormatName == "ipx")
736 mpfnImport = ipxGraphicImport;
737 else if (maFormatName == "ira")
738 mpfnImport = iraGraphicImport;
739 else if (maFormatName == "itg")
740 mpfnImport = itgGraphicImport;
741 else if (maFormatName == "iti")
742 mpfnImport = itiGraphicImport;
743 #endif
746 return mpfnImport;
749 class ImpFilterLibCache
751 ImpFilterLibCacheEntry* mpFirst;
752 ImpFilterLibCacheEntry* mpLast;
754 public:
755 ImpFilterLibCache();
756 ~ImpFilterLibCache();
758 ImpFilterLibCacheEntry* GetFilter( const OUString& rFilterPath, const OUString& rFiltername, const OUString& rFormatName );
761 ImpFilterLibCache::ImpFilterLibCache() :
762 mpFirst ( nullptr ),
763 mpLast ( nullptr )
767 ImpFilterLibCache::~ImpFilterLibCache()
769 ImpFilterLibCacheEntry* pEntry = mpFirst;
770 while( pEntry )
772 ImpFilterLibCacheEntry* pNext = pEntry->mpNext;
773 delete pEntry;
774 pEntry = pNext;
778 ImpFilterLibCacheEntry* ImpFilterLibCache::GetFilter(const OUString& rFilterPath, const OUString& rFilterName, const OUString& rFormatName)
780 ImpFilterLibCacheEntry* pEntry = mpFirst;
782 while( pEntry )
784 if( *pEntry == rFilterName && pEntry->maFormatName == rFormatName )
785 break;
786 else
787 pEntry = pEntry->mpNext;
789 if( !pEntry )
791 OUString aPhysicalName( ImpCreateFullFilterPath( rFilterPath, rFilterName ) );
792 pEntry = new ImpFilterLibCacheEntry(aPhysicalName, rFilterName, rFormatName );
793 #ifndef DISABLE_DYNLOADING
794 if ( pEntry->maLibrary.is() )
795 #endif
797 if( !mpFirst )
798 mpFirst = mpLast = pEntry;
799 else
800 mpLast = mpLast->mpNext = pEntry;
802 #ifndef DISABLE_DYNLOADING
803 else
805 delete pEntry;
806 pEntry = nullptr;
808 #endif
810 return pEntry;
813 namespace { struct Cache : public rtl::Static<ImpFilterLibCache, Cache> {}; }
815 GraphicFilter::GraphicFilter( bool bConfig )
816 : bUseConfig(bConfig)
818 ImplInit();
821 GraphicFilter::~GraphicFilter()
824 ::osl::MutexGuard aGuard( getListMutex() );
825 auto it = std::find(pFilterHdlList->begin(), pFilterHdlList->end(), this);
826 if( it != pFilterHdlList->end() )
827 pFilterHdlList->erase( it );
829 if( pFilterHdlList->empty() )
831 delete pFilterHdlList;
832 pFilterHdlList = nullptr;
833 delete pConfig;
837 pErrorEx.reset();
840 void GraphicFilter::ImplInit()
843 ::osl::MutexGuard aGuard( getListMutex() );
845 if ( !pFilterHdlList )
847 pFilterHdlList = new FilterList_impl;
848 pConfig = new FilterConfigCache( bUseConfig );
850 else
851 pConfig = pFilterHdlList->front()->pConfig;
853 pFilterHdlList->push_back( this );
856 if( bUseConfig )
858 OUString url("$BRAND_BASE_DIR/" LIBO_LIB_FOLDER);
859 rtl::Bootstrap::expandMacros(url); //TODO: detect failure
860 osl::FileBase::getSystemPathFromFileURL(url, aFilterPath);
863 pErrorEx.reset( new FilterErrorEx );
866 ErrCode GraphicFilter::ImplSetError( ErrCode nError, const SvStream* pStm )
868 pErrorEx->nStreamError = pStm ? pStm->GetError() : ERRCODE_NONE;
869 return nError;
872 sal_uInt16 GraphicFilter::GetImportFormatCount()
874 return pConfig->GetImportFormatCount();
877 sal_uInt16 GraphicFilter::GetImportFormatNumber( const OUString& rFormatName )
879 return pConfig->GetImportFormatNumber( rFormatName );
882 sal_uInt16 GraphicFilter::GetImportFormatNumberForShortName( const OUString& rShortName )
884 return pConfig->GetImportFormatNumberForShortName( rShortName );
887 sal_uInt16 GraphicFilter::GetImportFormatNumberForTypeName( const OUString& rType )
889 return pConfig->GetImportFormatNumberForTypeName( rType );
892 OUString GraphicFilter::GetImportFormatName( sal_uInt16 nFormat )
894 return pConfig->GetImportFormatName( nFormat );
897 OUString GraphicFilter::GetImportFormatTypeName( sal_uInt16 nFormat )
899 return pConfig->GetImportFilterTypeName( nFormat );
902 #ifdef _WIN32
903 OUString GraphicFilter::GetImportFormatMediaType( sal_uInt16 nFormat )
905 return pConfig->GetImportFormatMediaType( nFormat );
907 #endif
909 OUString GraphicFilter::GetImportFormatShortName( sal_uInt16 nFormat )
911 return pConfig->GetImportFormatShortName( nFormat );
914 OUString GraphicFilter::GetImportWildcard( sal_uInt16 nFormat, sal_Int32 nEntry )
916 return pConfig->GetImportWildcard( nFormat, nEntry );
919 sal_uInt16 GraphicFilter::GetExportFormatCount()
921 return pConfig->GetExportFormatCount();
924 sal_uInt16 GraphicFilter::GetExportFormatNumber( const OUString& rFormatName )
926 return pConfig->GetExportFormatNumber( rFormatName );
929 sal_uInt16 GraphicFilter::GetExportFormatNumberForMediaType( const OUString& rMediaType )
931 return pConfig->GetExportFormatNumberForMediaType( rMediaType );
934 sal_uInt16 GraphicFilter::GetExportFormatNumberForShortName( const OUString& rShortName )
936 return pConfig->GetExportFormatNumberForShortName( rShortName );
939 OUString GraphicFilter::GetExportInternalFilterName( sal_uInt16 nFormat )
941 return pConfig->GetExportInternalFilterName( nFormat );
944 sal_uInt16 GraphicFilter::GetExportFormatNumberForTypeName( const OUString& rType )
946 return pConfig->GetExportFormatNumberForTypeName( rType );
949 OUString GraphicFilter::GetExportFormatName( sal_uInt16 nFormat )
951 return pConfig->GetExportFormatName( nFormat );
954 OUString GraphicFilter::GetExportFormatMediaType( sal_uInt16 nFormat )
956 return pConfig->GetExportFormatMediaType( nFormat );
959 OUString GraphicFilter::GetExportFormatShortName( sal_uInt16 nFormat )
961 return pConfig->GetExportFormatShortName( nFormat );
964 OUString GraphicFilter::GetExportWildcard( sal_uInt16 nFormat )
966 return pConfig->GetExportWildcard( nFormat, 0 );
969 bool GraphicFilter::IsExportPixelFormat( sal_uInt16 nFormat )
971 return pConfig->IsExportPixelFormat( nFormat );
974 ErrCode GraphicFilter::CanImportGraphic( const INetURLObject& rPath,
975 sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat )
977 ErrCode nRetValue = ERRCODE_GRFILTER_FORMATERROR;
978 SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::CanImportGraphic() : ProtType == INetProtocol::NotValid" );
980 OUString aMainUrl( rPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
981 std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::READ | StreamMode::SHARE_DENYNONE ));
982 if (xStream)
984 nRetValue = CanImportGraphic( aMainUrl, *xStream, nFormat, pDeterminedFormat );
986 return nRetValue;
989 ErrCode GraphicFilter::CanImportGraphic( const OUString& rMainUrl, SvStream& rIStream,
990 sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat )
992 sal_uLong nStreamPos = rIStream.Tell();
993 ErrCode nRes = ImpTestOrFindFormat( rMainUrl, rIStream, nFormat );
995 rIStream.Seek(nStreamPos);
997 if( nRes==ERRCODE_NONE && pDeterminedFormat!=nullptr )
998 *pDeterminedFormat = nFormat;
1000 return ImplSetError( nRes, &rIStream );
1003 //SJ: TODO, we need to create a GraphicImporter component
1004 ErrCode GraphicFilter::ImportGraphic( Graphic& rGraphic, const INetURLObject& rPath,
1005 sal_uInt16 nFormat, sal_uInt16 * pDeterminedFormat, GraphicFilterImportFlags nImportFlags )
1007 ErrCode nRetValue = ERRCODE_GRFILTER_FORMATERROR;
1008 SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::ImportGraphic() : ProtType == INetProtocol::NotValid" );
1010 OUString aMainUrl( rPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1011 std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::READ | StreamMode::SHARE_DENYNONE ));
1012 if (xStream)
1014 nRetValue = ImportGraphic( rGraphic, aMainUrl, *xStream, nFormat, pDeterminedFormat, nImportFlags );
1016 return nRetValue;
1019 ErrCode GraphicFilter::ImportGraphic(
1020 Graphic& rGraphic,
1021 const OUString& rPath,
1022 SvStream& rIStream,
1023 sal_uInt16 nFormat,
1024 sal_uInt16* pDeterminedFormat,
1025 GraphicFilterImportFlags nImportFlags,
1026 WmfExternal const *pExtHeader)
1028 return ImportGraphic( rGraphic, rPath, rIStream, nFormat, pDeterminedFormat, nImportFlags, nullptr, pExtHeader );
1031 /// Contains a stream and other associated data to import pixels into a
1032 /// Graphic.
1033 struct GraphicImportContext
1035 /// Pixel data is read from this stream.
1036 std::unique_ptr<SvStream> m_pStream;
1037 /// The Graphic the import filter gets.
1038 std::shared_ptr<Graphic> m_pGraphic;
1039 /// Write pixel data using this access.
1040 std::unique_ptr<BitmapScopedWriteAccess> m_pAccess;
1041 /// Signals if import finished correctly.
1042 ErrCode m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
1043 /// Original graphic format.
1044 GfxLinkType m_eLinkType = GfxLinkType::NONE;
1045 /// Position of the stream before reading the data.
1046 sal_uInt64 m_nStreamBegin = 0;
1047 /// Flags for the import filter.
1048 GraphicFilterImportFlags m_nImportFlags = GraphicFilterImportFlags::NONE;
1051 /// Graphic import worker that gets executed on a thread.
1052 class GraphicImportTask : public comphelper::ThreadTask
1054 GraphicImportContext& m_rContext;
1055 public:
1056 GraphicImportTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, GraphicImportContext& rContext);
1057 void doWork() override;
1058 /// Shared code between threaded and non-threaded version.
1059 static void doImport(GraphicImportContext& rContext);
1062 GraphicImportTask::GraphicImportTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, GraphicImportContext& rContext)
1063 : comphelper::ThreadTask(pTag),
1064 m_rContext(rContext)
1068 void GraphicImportTask::doWork()
1070 GraphicImportTask::doImport(m_rContext);
1073 void GraphicImportTask::doImport(GraphicImportContext& rContext)
1075 if (!ImportJPEG(*rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, rContext.m_pAccess.get()))
1076 rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
1077 else
1078 rContext.m_eLinkType = GfxLinkType::NativeJpg;
1081 void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGraphics, std::vector< std::unique_ptr<SvStream> > vStreams)
1083 static bool bThreads = !getenv("VCL_NO_THREAD_IMPORT");
1084 std::vector<GraphicImportContext> aContexts;
1085 aContexts.reserve(vStreams.size());
1086 comphelper::ThreadPool& rSharedPool = comphelper::ThreadPool::getSharedOptimalPool();
1087 std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
1089 for (auto& pStream : vStreams)
1091 aContexts.emplace_back();
1092 GraphicImportContext& rContext = aContexts.back();
1094 if (pStream)
1096 rContext.m_pStream = std::move(pStream);
1097 rContext.m_pGraphic = std::make_shared<Graphic>();
1098 rContext.m_nStatus = ERRCODE_NONE;
1100 // Detect the format.
1101 ResetLastError();
1102 rContext.m_nStreamBegin = rContext.m_pStream->Tell();
1103 sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
1104 rContext.m_nStatus = ImpTestOrFindFormat(OUString(), *rContext.m_pStream, nFormat);
1105 rContext.m_pStream->Seek(rContext.m_nStreamBegin);
1107 // Import the graphic.
1108 if (rContext.m_nStatus == ERRCODE_NONE && !rContext.m_pStream->GetError())
1110 OUString aFilterName = pConfig->GetImportFilterName(nFormat);
1112 if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
1114 rContext.m_nImportFlags = GraphicFilterImportFlags::SetLogsizeForJpeg;
1116 if (!ImportJPEG( *rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr))
1117 rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
1118 else
1120 Bitmap& rBitmap = const_cast<Bitmap&>(rContext.m_pGraphic->GetBitmapExRef().GetBitmapRef());
1121 rContext.m_pAccess = std::make_unique<BitmapScopedWriteAccess>(rBitmap);
1122 rContext.m_pStream->Seek(rContext.m_nStreamBegin);
1123 if (bThreads)
1124 rSharedPool.pushTask(std::make_unique<GraphicImportTask>(pTag, rContext));
1125 else
1126 GraphicImportTask::doImport(rContext);
1129 else
1130 rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
1135 rSharedPool.waitUntilDone(pTag);
1137 // Process data after import.
1138 for (auto& rContext : aContexts)
1140 rContext.m_pAccess.reset();
1142 if (rContext.m_nStatus == ERRCODE_NONE && (rContext.m_eLinkType != GfxLinkType::NONE) && !rContext.m_pGraphic->GetContext())
1144 std::unique_ptr<sal_uInt8[]> pGraphicContent;
1146 const sal_uInt64 nStreamEnd = rContext.m_pStream->Tell();
1147 sal_Int32 nGraphicContentSize = nStreamEnd - rContext.m_nStreamBegin;
1149 if (nGraphicContentSize > 0)
1153 pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
1155 catch (const std::bad_alloc&)
1157 rContext.m_nStatus = ERRCODE_GRFILTER_TOOBIG;
1160 if (rContext.m_nStatus == ERRCODE_NONE)
1162 rContext.m_pStream->Seek(rContext.m_nStreamBegin);
1163 rContext.m_pStream->ReadBytes(pGraphicContent.get(), nGraphicContentSize);
1167 if (rContext.m_nStatus == ERRCODE_NONE)
1168 rContext.m_pGraphic->SetGfxLink(std::make_shared<GfxLink>(std::move(pGraphicContent), nGraphicContentSize, rContext.m_eLinkType));
1171 if (rContext.m_nStatus != ERRCODE_NONE)
1172 rContext.m_pGraphic = nullptr;
1174 rGraphics.push_back(rContext.m_pGraphic);
1178 Graphic GraphicFilter::ImportUnloadedGraphic(SvStream& rIStream, sal_uInt64 sizeLimit,
1179 Size* pSizeHint)
1181 Graphic aGraphic;
1182 sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
1183 GfxLinkType eLinkType = GfxLinkType::NONE;
1185 ResetLastError();
1187 const sal_uLong nStreamBegin = rIStream.Tell();
1189 rIStream.Seek(nStreamBegin);
1191 ErrCode nStatus = ImpTestOrFindFormat("", rIStream, nFormat);
1193 rIStream.Seek(nStreamBegin);
1194 sal_uInt32 nStreamLength(rIStream.remainingSize());
1195 if (sizeLimit && sizeLimit < nStreamLength)
1196 nStreamLength = sizeLimit;
1198 OUString aFilterName = pConfig->GetImportFilterName(nFormat);
1199 OUString aExternalFilterName = pConfig->GetExternalFilterName(nFormat, false);
1201 std::unique_ptr<sal_uInt8[]> pGraphicContent;
1202 sal_Int32 nGraphicContentSize = 0;
1204 // read graphic
1205 if (pConfig->IsImportInternalFilter(nFormat))
1207 if (aFilterName.equalsIgnoreAsciiCase(IMP_GIF))
1209 eLinkType = GfxLinkType::NativeGif;
1211 else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
1213 vcl::PNGReader aPNGReader(rIStream);
1215 // check if this PNG contains a GIF chunk!
1216 const std::vector<vcl::PNGReader::ChunkData>& rChunkData = aPNGReader.GetChunks();
1217 for (auto const& chunk : rChunkData)
1219 // Microsoft Office is storing Animated GIFs in following chunk
1220 if (chunk.nType == PMGCHUNG_msOG)
1222 sal_uInt32 nChunkSize = chunk.aData.size();
1224 if (nChunkSize > 11)
1226 const std::vector<sal_uInt8>& rData = chunk.aData;
1227 nGraphicContentSize = nChunkSize - 11;
1228 SvMemoryStream aIStrm(const_cast<sal_uInt8*>(&rData[11]), nGraphicContentSize, StreamMode::READ);
1229 pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
1230 sal_uInt64 aCurrentPosition = aIStrm.Tell();
1231 aIStrm.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
1232 aIStrm.Seek(aCurrentPosition);
1233 eLinkType = GfxLinkType::NativeGif;
1234 break;
1238 if (eLinkType == GfxLinkType::NONE)
1240 eLinkType = GfxLinkType::NativePng;
1243 else if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
1245 eLinkType = GfxLinkType::NativeJpg;
1247 else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVG))
1249 bool bOkay(false);
1251 if (nStreamLength > 0)
1253 std::vector<sal_uInt8> aTwoBytes(2);
1254 rIStream.ReadBytes(aTwoBytes.data(), 2);
1255 rIStream.Seek(nStreamBegin);
1257 if (aTwoBytes[0] == 0x1F && aTwoBytes[1] == 0x8B)
1259 SvMemoryStream aMemStream;
1260 ZCodec aCodec;
1261 long nMemoryLength;
1263 aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, false, true);
1264 nMemoryLength = aCodec.Decompress(rIStream, aMemStream);
1265 aCodec.EndCompression();
1267 if (!rIStream.GetError() && nMemoryLength >= 0)
1269 nGraphicContentSize = nMemoryLength;
1270 pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
1272 aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
1273 aMemStream.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
1275 bOkay = true;
1278 else
1280 nGraphicContentSize = nStreamLength;
1281 pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
1282 rIStream.ReadBytes(pGraphicContent.get(), nStreamLength);
1284 bOkay = true;
1288 if (bOkay)
1290 eLinkType = GfxLinkType::NativeSvg;
1292 else
1294 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1297 else if (aFilterName.equalsIgnoreAsciiCase(IMP_BMP))
1299 eLinkType = GfxLinkType::NativeBmp;
1301 else if (aFilterName.equalsIgnoreAsciiCase(IMP_MOV))
1303 eLinkType = GfxLinkType::NativeMov;
1305 else if (aFilterName.equalsIgnoreAsciiCase(IMP_WMF) ||
1306 aFilterName.equalsIgnoreAsciiCase(IMP_EMF))
1308 nGraphicContentSize = nStreamLength;
1309 pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
1311 rIStream.Seek(nStreamBegin);
1312 rIStream.ReadBytes(pGraphicContent.get(), nStreamLength);
1314 if (!rIStream.GetError())
1316 eLinkType = GfxLinkType::NativeWmf;
1318 else
1320 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1323 else if (aFilterName == IMP_PDF)
1325 eLinkType = GfxLinkType::NativePdf;
1327 else
1329 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1332 else
1334 ImpFilterLibCacheEntry* pFilter = nullptr;
1336 if (!aFilterPath.isEmpty())
1338 // find first filter in filter paths
1339 ImpFilterLibCache &rCache = Cache::get();
1340 sal_Int32 nIdx{0};
1341 do {
1342 pFilter = rCache.GetFilter(aFilterPath.getToken(0, ';', nIdx), aFilterName, aExternalFilterName);
1343 } while (nIdx>=0 && pFilter==nullptr);
1346 if( !pFilter )
1347 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1348 else
1350 PFilterCall pFunc = pFilter->GetImportFunction();
1352 if (!pFunc)
1353 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1354 else
1356 OUString aShortName;
1357 if (nFormat != GRFILTER_FORMAT_DONTKNOW)
1358 aShortName = GetImportFormatShortName(nFormat).toAsciiUpperCase();
1360 if (aShortName.startsWith(TIF_SHORTNAME))
1361 eLinkType = GfxLinkType::NativeTif;
1362 else if( aShortName.startsWith(MET_SHORTNAME))
1363 eLinkType = GfxLinkType::NativeMet;
1364 else if( aShortName.startsWith(PCT_SHORTNAME))
1365 eLinkType = GfxLinkType::NativePct;
1370 if (nStatus == ERRCODE_NONE && eLinkType != GfxLinkType::NONE)
1372 if (!pGraphicContent)
1374 nGraphicContentSize = nStreamLength;
1376 if (nGraphicContentSize > 0)
1380 pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
1382 catch (const std::bad_alloc&)
1384 nStatus = ERRCODE_GRFILTER_TOOBIG;
1387 if (nStatus == ERRCODE_NONE)
1389 rIStream.Seek(nStreamBegin);
1390 rIStream.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
1395 if( nStatus == ERRCODE_NONE )
1397 bool bAnimated = false;
1398 if (eLinkType == GfxLinkType::NativeGif)
1400 SvMemoryStream aMemoryStream(pGraphicContent.get(), nGraphicContentSize, StreamMode::READ);
1401 bAnimated = IsGIFAnimated(aMemoryStream);
1403 aGraphic.SetGfxLink(std::make_shared<GfxLink>(std::move(pGraphicContent), nGraphicContentSize, eLinkType));
1404 aGraphic.ImplGetImpGraphic()->ImplSetPrepared(bAnimated, pSizeHint);
1408 // Set error code or try to set native buffer
1409 if (nStatus != ERRCODE_NONE)
1410 ImplSetError(nStatus, &rIStream);
1411 if (nStatus != ERRCODE_NONE || eLinkType == GfxLinkType::NONE)
1412 rIStream.Seek(nStreamBegin);
1414 return aGraphic;
1417 void GraphicFilter::preload()
1419 sal_Int32 nTokenCount = comphelper::string::getTokenCount(aFilterPath, ';');
1420 ImpFilterLibCache& rCache = Cache::get();
1421 static const std::initializer_list<OUStringLiteral> aFilterNames = {
1422 "icd", "idx", "ime", "ipb", "ipd", "ips", "ipt", "ipx", "ira", "itg", "iti",
1425 // Load library for each filter.
1426 for (const auto& rFilterName : aFilterNames)
1428 ImpFilterLibCacheEntry* pFilter = nullptr;
1429 // Look at the library in each element inside the filter path.
1430 for (sal_Int32 i = 0; i < nTokenCount; ++i)
1432 pFilter = rCache.GetFilter(aFilterPath.getToken(i, ';'), SVLIBRARY("gie"), rFilterName);
1433 if (pFilter)
1435 break;
1441 ErrCode GraphicFilter::ImportGraphic( Graphic& rGraphic, const OUString& rPath, SvStream& rIStream,
1442 sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat, GraphicFilterImportFlags nImportFlags,
1443 const css::uno::Sequence< css::beans::PropertyValue >* pFilterData,
1444 WmfExternal const *pExtHeader )
1446 OUString aFilterName;
1447 OUString aExternalFilterName;
1448 sal_uLong nStreamBegin;
1449 ErrCode nStatus;
1450 GfxLinkType eLinkType = GfxLinkType::NONE;
1451 const bool bLinkSet = rGraphic.IsGfxLink();
1453 Size aPreviewSizeHint( 0, 0 );
1454 bool bAllowPartialStreamRead = false;
1455 bool bCreateNativeLink = true;
1457 std::unique_ptr<sal_uInt8[]> pGraphicContent;
1458 sal_Int32 nGraphicContentSize = 0;
1460 ResetLastError();
1462 if ( pFilterData )
1464 for ( const auto& rPropVal : *pFilterData )
1466 if ( rPropVal.Name == "PreviewSizeHint" )
1468 css::awt::Size aSize;
1469 if ( rPropVal.Value >>= aSize )
1471 aPreviewSizeHint = Size( aSize.Width, aSize.Height );
1472 if ( aSize.Width || aSize.Height )
1473 nImportFlags |= GraphicFilterImportFlags::ForPreview;
1474 else
1475 nImportFlags &=~GraphicFilterImportFlags::ForPreview;
1478 else if ( rPropVal.Name == "AllowPartialStreamRead" )
1480 rPropVal.Value >>= bAllowPartialStreamRead;
1482 else if ( rPropVal.Name == "CreateNativeLink" )
1484 rPropVal.Value >>= bCreateNativeLink;
1489 std::shared_ptr<GraphicReader> pContext = rGraphic.GetContext();
1490 bool bDummyContext = rGraphic.IsDummyContext();
1491 if( !pContext || bDummyContext )
1493 if( bDummyContext )
1495 rGraphic.SetDummyContext( false );
1496 nStreamBegin = 0;
1498 else
1499 nStreamBegin = rIStream.Tell();
1501 nStatus = ImpTestOrFindFormat( rPath, rIStream, nFormat );
1502 // if pending, return ERRCODE_NONE in order to request more bytes
1503 if( rIStream.GetError() == ERRCODE_IO_PENDING )
1505 rGraphic.SetDummyContext(true);
1506 rIStream.ResetError();
1507 rIStream.Seek( nStreamBegin );
1508 return ImplSetError( ERRCODE_NONE );
1511 rIStream.Seek( nStreamBegin );
1513 if( ( nStatus != ERRCODE_NONE ) || rIStream.GetError() )
1514 return ImplSetError( ( nStatus != ERRCODE_NONE ) ? nStatus : ERRCODE_GRFILTER_OPENERROR, &rIStream );
1516 if( pDeterminedFormat )
1517 *pDeterminedFormat = nFormat;
1519 aFilterName = pConfig->GetImportFilterName( nFormat );
1520 aExternalFilterName = pConfig->GetExternalFilterName(nFormat, false);
1522 else
1524 aFilterName = pContext->GetUpperFilterName();
1526 nStreamBegin = 0;
1527 nStatus = ERRCODE_NONE;
1530 // read graphic
1531 if ( pConfig->IsImportInternalFilter( nFormat ) )
1533 if( aFilterName.equalsIgnoreAsciiCase( IMP_GIF ) )
1535 if( !ImportGIF( rIStream, rGraphic ) )
1536 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1537 else
1538 eLinkType = GfxLinkType::NativeGif;
1540 else if( aFilterName.equalsIgnoreAsciiCase( IMP_PNG ) )
1542 vcl::PNGReader aPNGReader( rIStream );
1544 // ignore animation for previews and set preview size
1545 if( aPreviewSizeHint.Width() || aPreviewSizeHint.Height() )
1547 // position the stream at the end of the image if requested
1548 if( !bAllowPartialStreamRead )
1549 aPNGReader.GetChunks();
1551 else
1553 // check if this PNG contains a GIF chunk!
1554 const std::vector<vcl::PNGReader::ChunkData>& rChunkData = aPNGReader.GetChunks();
1555 for (auto const& chunk : rChunkData)
1557 // Microsoft Office is storing Animated GIFs in following chunk
1558 if (chunk.nType == PMGCHUNG_msOG)
1560 sal_uInt32 nChunkSize = chunk.aData.size();
1562 if (nChunkSize > 11)
1564 const std::vector<sal_uInt8>& rData = chunk.aData;
1565 nGraphicContentSize = nChunkSize - 11;
1566 SvMemoryStream aIStrm(const_cast<sal_uInt8*>(&rData[11]), nGraphicContentSize, StreamMode::READ);
1567 pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
1568 sal_uInt64 aCurrentPosition = aIStrm.Tell();
1569 aIStrm.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
1570 aIStrm.Seek(aCurrentPosition);
1571 ImportGIF(aIStrm, rGraphic);
1572 eLinkType = GfxLinkType::NativeGif;
1573 break;
1579 if ( eLinkType == GfxLinkType::NONE )
1581 BitmapEx aBmpEx( aPNGReader.Read( aPreviewSizeHint ) );
1582 if ( aBmpEx.IsEmpty() )
1583 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1584 else
1586 rGraphic = aBmpEx;
1587 eLinkType = GfxLinkType::NativePng;
1591 else if( aFilterName.equalsIgnoreAsciiCase( IMP_JPEG ) )
1593 // set LOGSIZE flag always, if not explicitly disabled
1594 // (see #90508 and #106763)
1595 if( !( nImportFlags & GraphicFilterImportFlags::DontSetLogsizeForJpeg ) )
1596 nImportFlags |= GraphicFilterImportFlags::SetLogsizeForJpeg;
1598 sal_uInt64 nPosition = rIStream.Tell();
1599 if( !ImportJPEG( rIStream, rGraphic, nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr ) )
1600 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1601 else
1603 Bitmap& rBitmap = const_cast<Bitmap&>(rGraphic.GetBitmapExRef().GetBitmapRef());
1604 BitmapScopedWriteAccess pWriteAccess(rBitmap);
1605 rIStream.Seek(nPosition);
1606 if( !ImportJPEG( rIStream, rGraphic, nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, &pWriteAccess ) )
1607 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1608 else
1609 eLinkType = GfxLinkType::NativeJpg;
1612 else if( aFilterName.equalsIgnoreAsciiCase( IMP_SVG ) )
1614 const sal_uInt32 nStreamPosition(rIStream.Tell());
1615 const sal_uInt32 nStreamLength(rIStream.remainingSize());
1617 bool bOkay(false);
1619 if(nStreamLength > 0)
1621 std::vector<sal_uInt8> aTwoBytes(2);
1622 rIStream.ReadBytes(aTwoBytes.data(), 2);
1623 rIStream.Seek(nStreamPosition);
1625 if(aTwoBytes[0] == 0x1F && aTwoBytes[1] == 0x8B)
1627 SvMemoryStream aMemStream;
1628 ZCodec aCodec;
1629 long nMemoryLength;
1631 aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, false, true);
1632 nMemoryLength = aCodec.Decompress(rIStream, aMemStream);
1633 aCodec.EndCompression();
1635 if (!rIStream.GetError() && nMemoryLength >= 0)
1637 VectorGraphicDataArray aNewData(nMemoryLength);
1638 aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
1639 aMemStream.ReadBytes(aNewData.begin(), nMemoryLength);
1641 // Make a uncompressed copy for GfxLink
1642 nGraphicContentSize = nMemoryLength;
1643 pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
1644 std::copy(aNewData.begin(), aNewData.end(), pGraphicContent.get());
1646 if(!aMemStream.GetError() )
1648 VectorGraphicDataPtr aVectorGraphicDataPtr(new VectorGraphicData(aNewData, rPath, VectorGraphicDataType::Svg));
1649 rGraphic = Graphic(aVectorGraphicDataPtr);
1650 bOkay = true;
1654 else
1656 VectorGraphicDataArray aNewData(nStreamLength);
1657 rIStream.ReadBytes(aNewData.begin(), nStreamLength);
1659 if(!rIStream.GetError())
1661 VectorGraphicDataPtr aVectorGraphicDataPtr(new VectorGraphicData(aNewData, rPath, VectorGraphicDataType::Svg));
1662 rGraphic = Graphic(aVectorGraphicDataPtr);
1663 bOkay = true;
1668 if (bOkay)
1670 eLinkType = GfxLinkType::NativeSvg;
1672 else
1674 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1677 else if( aFilterName.equalsIgnoreAsciiCase( IMP_XBM ) )
1679 if( !ImportXBM( rIStream, rGraphic ) )
1680 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1682 else if( aFilterName.equalsIgnoreAsciiCase( IMP_XPM ) )
1684 if( !ImportXPM( rIStream, rGraphic ) )
1685 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1687 else if( aFilterName.equalsIgnoreAsciiCase( IMP_BMP ) ||
1688 aFilterName.equalsIgnoreAsciiCase( IMP_SVMETAFILE ) )
1690 // SV internal filters for import bitmaps and MetaFiles
1691 ReadGraphic( rIStream, rGraphic );
1692 if( rIStream.GetError() )
1694 nStatus = ERRCODE_GRFILTER_FORMATERROR;
1696 else if (aFilterName.equalsIgnoreAsciiCase(IMP_BMP))
1698 // #i15508# added BMP type (checked, works)
1699 eLinkType = GfxLinkType::NativeBmp;
1702 else if( aFilterName.equalsIgnoreAsciiCase( IMP_MOV ) )
1704 ReadGraphic( rIStream, rGraphic );
1705 if( rIStream.GetError() )
1706 nStatus = ERRCODE_GRFILTER_FORMATERROR;
1707 else
1709 rGraphic.SetDefaultType();
1710 rIStream.Seek( STREAM_SEEK_TO_END );
1711 eLinkType = GfxLinkType::NativeMov;
1714 else if( aFilterName.equalsIgnoreAsciiCase( IMP_WMF ) ||
1715 aFilterName.equalsIgnoreAsciiCase( IMP_EMF ) )
1717 // use new UNO API service, do not directly import but create a
1718 // Graphic that contains the original data and decomposes to
1719 // primitives on demand
1721 const sal_uInt32 nStreamLength(rIStream.remainingSize());
1722 VectorGraphicDataArray aNewData(nStreamLength);
1723 bool bOkay(false);
1725 rIStream.ReadBytes(aNewData.begin(), nStreamLength);
1727 if (!rIStream.GetError())
1729 const bool bIsWmf(aFilterName.equalsIgnoreAsciiCase(IMP_WMF));
1730 const VectorGraphicDataType aDataType(bIsWmf ? VectorGraphicDataType::Wmf : VectorGraphicDataType::Emf);
1731 VectorGraphicDataPtr aVectorGraphicDataPtr(
1732 new VectorGraphicData(
1733 aNewData,
1734 rPath,
1735 aDataType));
1737 if (pExtHeader)
1739 aVectorGraphicDataPtr->setWmfExternalHeader(*pExtHeader);
1742 rGraphic = Graphic(aVectorGraphicDataPtr);
1743 bOkay = true;
1746 if (bOkay)
1748 eLinkType = GfxLinkType::NativeWmf;
1750 else
1752 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1755 else if (aFilterName == IMP_PDF)
1757 if (!vcl::ImportPDF(rIStream, rGraphic))
1758 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1759 else
1760 eLinkType = GfxLinkType::NativePdf;
1762 else
1763 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1765 else
1767 ImpFilterLibCacheEntry* pFilter = nullptr;
1769 if (!aFilterPath.isEmpty())
1771 // find first filter in filter paths
1772 ImpFilterLibCache &rCache = Cache::get();
1773 sal_Int32 nIdx{0};
1774 do {
1775 pFilter = rCache.GetFilter(aFilterPath.getToken(0, ';', nIdx), aFilterName, aExternalFilterName);
1776 } while (nIdx>=0 && pFilter==nullptr);
1779 if( !pFilter )
1780 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1781 else
1783 PFilterCall pFunc = pFilter->GetImportFunction();
1785 if( !pFunc )
1786 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1787 else
1789 std::unique_ptr<FilterConfigItem> pFilterConfigItem;
1790 OUString aShortName;
1791 if( nFormat != GRFILTER_FORMAT_DONTKNOW )
1793 aShortName = GetImportFormatShortName( nFormat ).toAsciiUpperCase();
1794 if (aShortName == "PCD" && !utl::ConfigManager::IsFuzzing())
1796 OUString aFilterConfigPath( "Office.Common/Filter/Graphic/Import/PCD" );
1797 pFilterConfigItem = std::make_unique<FilterConfigItem>( aFilterConfigPath );
1800 if( !(*pFunc)( rIStream, rGraphic, pFilterConfigItem.get() ) )
1801 nStatus = ERRCODE_GRFILTER_FORMATERROR;
1802 else
1804 // try to set link type if format matches
1805 if( nFormat != GRFILTER_FORMAT_DONTKNOW )
1807 if( aShortName.startsWith( TIF_SHORTNAME ) )
1808 eLinkType = GfxLinkType::NativeTif;
1809 else if( aShortName.startsWith( MET_SHORTNAME ) )
1810 eLinkType = GfxLinkType::NativeMet;
1811 else if( aShortName.startsWith( PCT_SHORTNAME ) )
1812 eLinkType = GfxLinkType::NativePct;
1819 if( nStatus == ERRCODE_NONE && bCreateNativeLink && ( eLinkType != GfxLinkType::NONE ) && !rGraphic.GetContext() && !bLinkSet )
1821 if (!pGraphicContent)
1823 const sal_uLong nStreamEnd = rIStream.Tell();
1824 nGraphicContentSize = nStreamEnd - nStreamBegin;
1826 if (nGraphicContentSize > 0)
1830 pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
1832 catch (const std::bad_alloc&)
1834 nStatus = ERRCODE_GRFILTER_TOOBIG;
1837 if( nStatus == ERRCODE_NONE )
1839 rIStream.Seek(nStreamBegin);
1840 rIStream.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
1844 if( nStatus == ERRCODE_NONE )
1846 rGraphic.SetGfxLink(std::make_shared<GfxLink>(std::move(pGraphicContent), nGraphicContentSize, eLinkType));
1850 // Set error code or try to set native buffer
1851 if( nStatus != ERRCODE_NONE )
1853 ImplSetError( nStatus, &rIStream );
1854 rIStream.Seek( nStreamBegin );
1855 rGraphic.Clear();
1858 return nStatus;
1861 ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const INetURLObject& rPath,
1862 sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
1864 SAL_INFO( "vcl.filter", "GraphicFilter::ExportGraphic() (thb)" );
1865 ErrCode nRetValue = ERRCODE_GRFILTER_FORMATERROR;
1866 SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::ExportGraphic() : ProtType == INetProtocol::NotValid" );
1867 bool bAlreadyExists = DirEntryExists( rPath );
1869 OUString aMainUrl( rPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1870 std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::WRITE | StreamMode::TRUNC ));
1871 if (xStream)
1873 nRetValue = ExportGraphic( rGraphic, aMainUrl, *xStream, nFormat, pFilterData );
1874 xStream.reset();
1876 if( ( ERRCODE_NONE != nRetValue ) && !bAlreadyExists )
1877 KillDirEntry( aMainUrl );
1879 return nRetValue;
1882 #ifdef DISABLE_DYNLOADING
1884 extern "C" bool egiGraphicExport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
1885 extern "C" bool epsGraphicExport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
1886 extern "C" bool etiGraphicExport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
1888 #endif
1890 ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const OUString& rPath,
1891 SvStream& rOStm, sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
1893 SAL_INFO( "vcl.filter", "GraphicFilter::ExportGraphic() (thb)" );
1894 sal_uInt16 nFormatCount = GetExportFormatCount();
1896 ResetLastError();
1898 if( nFormat == GRFILTER_FORMAT_DONTKNOW )
1900 INetURLObject aURL( rPath );
1901 OUString aExt( aURL.GetFileExtension().toAsciiUpperCase() );
1903 for( sal_uInt16 i = 0; i < nFormatCount; i++ )
1905 if ( pConfig->GetExportFormatExtension( i ).equalsIgnoreAsciiCase( aExt ) )
1907 nFormat=i;
1908 break;
1912 if( nFormat >= nFormatCount )
1913 return ImplSetError( ERRCODE_GRFILTER_FORMATERROR );
1915 FilterConfigItem aConfigItem( pFilterData );
1916 OUString aFilterName( pConfig->GetExportFilterName( nFormat ) );
1917 OUString aExternalFilterName(pConfig->GetExternalFilterName(nFormat, true));
1918 ErrCode nStatus = ERRCODE_NONE;
1919 GraphicType eType;
1920 Graphic aGraphic = ImpGetScaledGraphic( rGraphic, aConfigItem );
1921 eType = aGraphic.GetType();
1923 if( pConfig->IsExportPixelFormat( nFormat ) )
1925 if( eType != GraphicType::Bitmap )
1927 Size aSizePixel;
1928 sal_uLong nBitsPerPixel,nNeededMem,nMaxMem;
1929 ScopedVclPtrInstance< VirtualDevice > aVirDev;
1931 nMaxMem = 1024;
1932 nMaxMem *= 1024; // In Bytes
1934 // Calculate how big the image would normally be:
1935 aSizePixel=aVirDev->LogicToPixel(aGraphic.GetPrefSize(),aGraphic.GetPrefMapMode());
1937 // Calculate how much memory the image will take up
1938 nBitsPerPixel=aVirDev->GetBitCount();
1939 nNeededMem=(static_cast<sal_uLong>(aSizePixel.Width())*static_cast<sal_uLong>(aSizePixel.Height())*nBitsPerPixel+7)/8;
1941 // is the image larger than available memory?
1942 if (nMaxMem<nNeededMem)
1944 double fFak=sqrt(static_cast<double>(nMaxMem)/static_cast<double>(nNeededMem));
1945 aSizePixel.setWidth(static_cast<sal_uLong>(static_cast<double>(aSizePixel.Width())*fFak) );
1946 aSizePixel.setHeight(static_cast<sal_uLong>(static_cast<double>(aSizePixel.Height())*fFak) );
1949 aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
1950 aVirDev->SetOutputSizePixel(aSizePixel);
1951 Graphic aGraphic2=aGraphic;
1952 aGraphic2.Draw(aVirDev.get(),Point(0,0),aSizePixel); // this changes the MapMode
1953 aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
1954 aGraphic=Graphic(aVirDev->GetBitmapEx(Point(0,0),aSizePixel));
1957 if( rOStm.GetError() )
1958 nStatus = ERRCODE_GRFILTER_IOERROR;
1959 if( ERRCODE_NONE == nStatus )
1961 if ( pConfig->IsExportInternalFilter( nFormat ) )
1963 if( aFilterName.equalsIgnoreAsciiCase( EXP_BMP ) )
1965 BitmapEx aBmp( aGraphic.GetBitmapEx() );
1966 BmpConversion nColorRes = static_cast<BmpConversion>(aConfigItem.ReadInt32( "Colors", 0 ));
1967 if ( nColorRes != BmpConversion::NNONE && ( nColorRes <= BmpConversion::N24Bit) )
1969 if( !aBmp.Convert( nColorRes ) )
1970 aBmp = aGraphic.GetBitmapEx();
1972 bool bRleCoding = aConfigItem.ReadBool( "RLE_Coding", true );
1973 // save RLE encoded?
1974 WriteDIB(aBmp, rOStm, bRleCoding);
1976 if( rOStm.GetError() )
1977 nStatus = ERRCODE_GRFILTER_IOERROR;
1979 else if( aFilterName.equalsIgnoreAsciiCase( EXP_SVMETAFILE ) )
1981 sal_Int32 nVersion = aConfigItem.ReadInt32( "Version", 0 ) ;
1982 if ( nVersion )
1983 rOStm.SetVersion( nVersion );
1985 // #i119735# just use GetGDIMetaFile, it will create a bufferd version of contained bitmap now automatically
1986 GDIMetaFile aMTF(aGraphic.GetGDIMetaFile());
1988 aMTF.Write( rOStm );
1990 if( rOStm.GetError() )
1991 nStatus = ERRCODE_GRFILTER_IOERROR;
1993 else if ( aFilterName.equalsIgnoreAsciiCase( EXP_WMF ) )
1995 bool bDone(false);
1997 // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
1998 const VectorGraphicDataPtr& aVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
2000 if (aVectorGraphicDataPtr.get()
2001 && aVectorGraphicDataPtr->getVectorGraphicDataArrayLength()
2002 && VectorGraphicDataType::Wmf == aVectorGraphicDataPtr->getVectorGraphicDataType())
2004 rOStm.WriteBytes(aVectorGraphicDataPtr->getVectorGraphicDataArray().getConstArray(), aVectorGraphicDataPtr->getVectorGraphicDataArrayLength());
2006 if (rOStm.GetError())
2008 nStatus = ERRCODE_GRFILTER_IOERROR;
2010 else
2012 bDone = true;
2016 if (!bDone)
2018 // #i119735# just use GetGDIMetaFile, it will create a bufferd version of contained bitmap now automatically
2019 if (!ConvertGDIMetaFileToWMF(aGraphic.GetGDIMetaFile(), rOStm, &aConfigItem))
2020 nStatus = ERRCODE_GRFILTER_FORMATERROR;
2022 if (rOStm.GetError())
2023 nStatus = ERRCODE_GRFILTER_IOERROR;
2026 else if ( aFilterName.equalsIgnoreAsciiCase( EXP_EMF ) )
2028 bool bDone(false);
2030 // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
2031 const VectorGraphicDataPtr& aVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
2033 if (aVectorGraphicDataPtr.get()
2034 && aVectorGraphicDataPtr->getVectorGraphicDataArrayLength()
2035 && VectorGraphicDataType::Emf == aVectorGraphicDataPtr->getVectorGraphicDataType())
2037 rOStm.WriteBytes(aVectorGraphicDataPtr->getVectorGraphicDataArray().getConstArray(), aVectorGraphicDataPtr->getVectorGraphicDataArrayLength());
2039 if (rOStm.GetError())
2041 nStatus = ERRCODE_GRFILTER_IOERROR;
2043 else
2045 bDone = true;
2049 if (!bDone)
2051 // #i119735# just use GetGDIMetaFile, it will create a bufferd version of contained bitmap now automatically
2052 if (!ConvertGDIMetaFileToEMF(aGraphic.GetGDIMetaFile(), rOStm))
2053 nStatus = ERRCODE_GRFILTER_FORMATERROR;
2055 if (rOStm.GetError())
2056 nStatus = ERRCODE_GRFILTER_IOERROR;
2059 else if( aFilterName.equalsIgnoreAsciiCase( EXP_JPEG ) )
2061 bool bExportedGrayJPEG = false;
2062 if( !ExportJPEG( rOStm, aGraphic, pFilterData, &bExportedGrayJPEG ) )
2063 nStatus = ERRCODE_GRFILTER_FORMATERROR;
2065 if( rOStm.GetError() )
2066 nStatus = ERRCODE_GRFILTER_IOERROR;
2068 else if ( aFilterName.equalsIgnoreAsciiCase( EXP_PNG ) )
2070 vcl::PNGWriter aPNGWriter( aGraphic.GetBitmapEx(), pFilterData );
2071 if ( pFilterData )
2073 for ( const auto& rPropVal : *pFilterData )
2075 if ( rPropVal.Name == "AdditionalChunks" )
2077 css::uno::Sequence< css::beans::PropertyValue > aAdditionalChunkSequence;
2078 if ( rPropVal.Value >>= aAdditionalChunkSequence )
2080 for ( const auto& rAdditionalChunk : std::as_const(aAdditionalChunkSequence) )
2082 if ( rAdditionalChunk.Name.getLength() == 4 )
2084 sal_uInt32 nChunkType = 0;
2085 for ( sal_Int32 k = 0; k < 4; k++ )
2087 nChunkType <<= 8;
2088 nChunkType |= static_cast<sal_uInt8>(rAdditionalChunk.Name[ k ]);
2090 css::uno::Sequence< sal_Int8 > aByteSeq;
2091 if ( rAdditionalChunk.Value >>= aByteSeq )
2093 std::vector< vcl::PNGWriter::ChunkData >& rChunkData = aPNGWriter.GetChunks();
2094 if ( !rChunkData.empty() )
2096 sal_uInt32 nChunkLen = aByteSeq.getLength();
2098 vcl::PNGWriter::ChunkData aChunkData;
2099 aChunkData.nType = nChunkType;
2100 if ( nChunkLen )
2102 aChunkData.aData.resize( nChunkLen );
2103 memcpy( aChunkData.aData.data(), aByteSeq.getConstArray(), nChunkLen );
2105 std::vector< vcl::PNGWriter::ChunkData >::iterator aIter = rChunkData.end() - 1;
2106 rChunkData.insert( aIter, aChunkData );
2115 aPNGWriter.Write( rOStm );
2117 if( rOStm.GetError() )
2118 nStatus = ERRCODE_GRFILTER_IOERROR;
2120 else if( aFilterName.equalsIgnoreAsciiCase( EXP_SVG ) )
2122 bool bDone(false);
2124 // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
2125 const VectorGraphicDataPtr& aVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
2127 if (aVectorGraphicDataPtr.get()
2128 && aVectorGraphicDataPtr->getVectorGraphicDataArrayLength()
2129 && VectorGraphicDataType::Svg == aVectorGraphicDataPtr->getVectorGraphicDataType())
2131 rOStm.WriteBytes(aVectorGraphicDataPtr->getVectorGraphicDataArray().getConstArray(), aVectorGraphicDataPtr->getVectorGraphicDataArrayLength());
2133 if( rOStm.GetError() )
2135 nStatus = ERRCODE_GRFILTER_IOERROR;
2137 else
2139 bDone = true;
2143 if( !bDone )
2145 // do the normal GDIMetaFile export instead
2148 css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
2150 css::uno::Reference< css::xml::sax::XDocumentHandler > xSaxWriter(
2151 css::xml::sax::Writer::create( xContext ), css::uno::UNO_QUERY_THROW);
2152 css::uno::Sequence< css::uno::Any > aArguments( 1 );
2153 aArguments[ 0 ] <<= aConfigItem.GetFilterData();
2154 css::uno::Reference< css::svg::XSVGWriter > xSVGWriter(
2155 xContext->getServiceManager()->createInstanceWithArgumentsAndContext( "com.sun.star.svg.SVGWriter", aArguments, xContext),
2156 css::uno::UNO_QUERY );
2157 if( xSaxWriter.is() && xSVGWriter.is() )
2159 css::uno::Reference< css::io::XActiveDataSource > xActiveDataSource(
2160 xSaxWriter, css::uno::UNO_QUERY );
2162 if( xActiveDataSource.is() )
2164 const css::uno::Reference< css::uno::XInterface > xStmIf(
2165 static_cast< ::cppu::OWeakObject* >( new ImpFilterOutputStream( rOStm ) ) );
2167 SvMemoryStream aMemStm( 65535, 65535 );
2169 // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
2170 const_cast<GDIMetaFile&>( aGraphic.GetGDIMetaFile() ).Write( aMemStm );
2172 xActiveDataSource->setOutputStream( css::uno::Reference< css::io::XOutputStream >(
2173 xStmIf, css::uno::UNO_QUERY ) );
2174 css::uno::Sequence< sal_Int8 > aMtfSeq( static_cast<sal_Int8 const *>(aMemStm.GetData()), aMemStm.Tell() );
2175 xSVGWriter->write( xSaxWriter, aMtfSeq );
2179 catch(const css::uno::Exception&)
2181 nStatus = ERRCODE_GRFILTER_IOERROR;
2185 else
2186 nStatus = ERRCODE_GRFILTER_FILTERERROR;
2188 else
2190 sal_Int32 nIdx {aFilterPath.isEmpty() ? -1 : 0};
2191 while (nIdx>=0)
2193 #ifndef DISABLE_DYNLOADING
2194 OUString aPhysicalName( ImpCreateFullFilterPath( aFilterPath.getToken(0, ';', nIdx), aFilterName ) );
2195 osl::Module aLibrary( aPhysicalName );
2197 PFilterCall pFunc = nullptr;
2198 if (aExternalFilterName == "egi")
2199 pFunc = reinterpret_cast<PFilterCall>(aLibrary.getFunctionSymbol("egiGraphicExport"));
2200 else if (aExternalFilterName == "eps")
2201 pFunc = reinterpret_cast<PFilterCall>(aLibrary.getFunctionSymbol("epsGraphicExport"));
2202 else if (aExternalFilterName == "eti")
2203 pFunc = reinterpret_cast<PFilterCall>(aLibrary.getFunctionSymbol("etiGraphicExport"));
2204 // Execute dialog in DLL
2205 #else
2206 --nIdx; // Just one iteration
2207 PFilterCall pFunc = NULL;
2208 if (aExternalFilterName == "egi")
2209 pFunc = egiGraphicExport;
2210 else if (aExternalFilterName == "eps")
2211 pFunc = epsGraphicExport;
2212 else if (aExternalFilterName == "eti")
2213 pFunc = etiGraphicExport;
2214 #endif
2215 if( pFunc )
2217 if ( !(*pFunc)( rOStm, aGraphic, &aConfigItem ) )
2218 nStatus = ERRCODE_GRFILTER_FORMATERROR;
2219 break;
2221 else
2222 nStatus = ERRCODE_GRFILTER_FILTERERROR;
2226 if( nStatus != ERRCODE_NONE )
2228 ImplSetError( nStatus, &rOStm );
2230 return nStatus;
2234 void GraphicFilter::ResetLastError()
2236 pErrorEx->nStreamError = ERRCODE_NONE;
2239 const Link<ConvertData&,bool> GraphicFilter::GetFilterCallback() const
2241 const Link<ConvertData&,bool> aLink( LINK( const_cast<GraphicFilter*>(this), GraphicFilter, FilterCallback ) );
2242 return aLink;
2245 IMPL_LINK( GraphicFilter, FilterCallback, ConvertData&, rData, bool )
2247 bool bRet = false;
2249 sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
2250 OString aShortName;
2251 css::uno::Sequence< css::beans::PropertyValue > aFilterData;
2252 switch( rData.mnFormat )
2254 case ConvertDataFormat::BMP: aShortName = BMP_SHORTNAME; break;
2255 case ConvertDataFormat::GIF: aShortName = GIF_SHORTNAME; break;
2256 case ConvertDataFormat::JPG: aShortName = JPG_SHORTNAME; break;
2257 case ConvertDataFormat::MET: aShortName = MET_SHORTNAME; break;
2258 case ConvertDataFormat::PCT: aShortName = PCT_SHORTNAME; break;
2259 case ConvertDataFormat::PNG: aShortName = PNG_SHORTNAME; break;
2260 case ConvertDataFormat::SVM: aShortName = SVM_SHORTNAME; break;
2261 case ConvertDataFormat::TIF: aShortName = TIF_SHORTNAME; break;
2262 case ConvertDataFormat::WMF: aShortName = WMF_SHORTNAME; break;
2263 case ConvertDataFormat::EMF: aShortName = EMF_SHORTNAME; break;
2264 case ConvertDataFormat::SVG: aShortName = SVG_SHORTNAME; break;
2266 default:
2267 break;
2269 if( GraphicType::NONE == rData.maGraphic.GetType() || rData.maGraphic.GetContext() ) // Import
2271 // Import
2272 nFormat = GetImportFormatNumberForShortName( OStringToOUString( aShortName, RTL_TEXTENCODING_UTF8) );
2273 bRet = ImportGraphic( rData.maGraphic, OUString(), rData.mrStm, nFormat ) == ERRCODE_NONE;
2275 else if( !aShortName.isEmpty() )
2277 // Export
2278 #ifdef IOS
2279 if (aShortName == PNG_SHORTNAME)
2281 aFilterData.realloc(aFilterData.getLength() + 1);
2282 aFilterData[aFilterData.getLength() - 1].Name = "Compression";
2283 // We "know" that this gets passed to zlib's deflateInit2_(). 1 means best speed.
2284 aFilterData[aFilterData.getLength() - 1].Value <<= 1;
2286 #endif
2287 nFormat = GetExportFormatNumberForShortName( OStringToOUString(aShortName, RTL_TEXTENCODING_UTF8) );
2288 bRet = ExportGraphic( rData.maGraphic, OUString(), rData.mrStm, nFormat, &aFilterData ) == ERRCODE_NONE;
2291 return bRet;
2294 namespace
2296 class StandardGraphicFilter
2298 public:
2299 StandardGraphicFilter()
2301 m_aFilter.GetImportFormatCount();
2303 GraphicFilter m_aFilter;
2306 class theGraphicFilter : public rtl::Static<StandardGraphicFilter, theGraphicFilter> {};
2309 GraphicFilter& GraphicFilter::GetGraphicFilter()
2311 return theGraphicFilter::get().m_aFilter;
2314 ErrCode GraphicFilter::LoadGraphic( const OUString &rPath, const OUString &rFilterName,
2315 Graphic& rGraphic, GraphicFilter* pFilter,
2316 sal_uInt16* pDeterminedFormat )
2318 if ( !pFilter )
2319 pFilter = &GetGraphicFilter();
2321 const sal_uInt16 nFilter = !rFilterName.isEmpty() && pFilter->GetImportFormatCount()
2322 ? pFilter->GetImportFormatNumber( rFilterName )
2323 : GRFILTER_FORMAT_DONTKNOW;
2325 INetURLObject aURL( rPath );
2326 if ( aURL.HasError() )
2328 aURL.SetSmartProtocol( INetProtocol::File );
2329 aURL.SetSmartURL( rPath );
2332 std::unique_ptr<SvStream> pStream;
2333 if ( INetProtocol::File != aURL.GetProtocol() )
2334 pStream = ::utl::UcbStreamHelper::CreateStream( rPath, StreamMode::READ );
2336 ErrCode nRes = ERRCODE_NONE;
2337 if ( !pStream )
2338 nRes = pFilter->ImportGraphic( rGraphic, aURL, nFilter, pDeterminedFormat );
2339 else
2340 nRes = pFilter->ImportGraphic( rGraphic, rPath, *pStream, nFilter, pDeterminedFormat );
2342 #ifdef DBG_UTIL
2343 OUString aReturnString;
2345 if (nRes == ERRCODE_GRFILTER_OPENERROR)
2346 aReturnString="open error";
2347 else if (nRes == ERRCODE_GRFILTER_IOERROR)
2348 aReturnString="IO error";
2349 else if (nRes == ERRCODE_GRFILTER_FORMATERROR)
2350 aReturnString="format error";
2351 else if (nRes == ERRCODE_GRFILTER_VERSIONERROR)
2352 aReturnString="version error";
2353 else if (nRes == ERRCODE_GRFILTER_FILTERERROR)
2354 aReturnString="filter error";
2355 else if (nRes == ERRCODE_GRFILTER_TOOBIG)
2356 aReturnString="graphic is too big";
2358 SAL_INFO_IF( nRes, "vcl.filter", "Problem importing graphic " << rPath << ". Reason: " << aReturnString );
2359 #endif
2361 return nRes;
2364 ErrCode GraphicFilter::compressAsPNG(const Graphic& rGraphic, SvStream& rOutputStream)
2366 css::uno::Sequence< css::beans::PropertyValue > aFilterData(1);
2367 aFilterData[0].Name = "Compression";
2368 aFilterData[0].Value <<= sal_uInt32(9);
2370 sal_uInt16 nFilterFormat = GetExportFormatNumberForShortName("PNG");
2371 return ExportGraphic(rGraphic, OUString(), rOutputStream, nFilterFormat, &aFilterData);
2374 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */