xref: /core/vcl/source/filter/graphicfilter.cxx (revision 374a76ad)
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 #include <config_folders.h>
21 
22 #include <sal/log.hxx>
23 #include <comphelper/processfactory.hxx>
24 #include <comphelper/propertyvalue.hxx>
25 #include <comphelper/threadpool.hxx>
26 #include <cppuhelper/implbase.hxx>
27 #include <tools/fract.hxx>
28 #include <comphelper/configuration.hxx>
29 #include <tools/stream.hxx>
30 #include <tools/urlobj.hxx>
31 #include <tools/zcodec.hxx>
32 #include <rtl/crc.h>
33 #include <fltcall.hxx>
34 #include <vcl/salctype.hxx>
35 #include <vcl/filter/PngImageReader.hxx>
36 #include <vcl/filter/SvmWriter.hxx>
37 #include <vcl/filter/PngImageWriter.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 "png/png.hxx"
50 #include "ixbm/xbmread.hxx"
51 #include <filter/XpmReader.hxx>
52 #include <filter/TiffReader.hxx>
53 #include <filter/TiffWriter.hxx>
54 #include <filter/TgaReader.hxx>
55 #include <filter/PictReader.hxx>
56 #include <filter/MetReader.hxx>
57 #include <filter/RasReader.hxx>
58 #include <filter/PcxReader.hxx>
59 #include <filter/EpsReader.hxx>
60 #include <filter/EpsWriter.hxx>
61 #include <filter/PsdReader.hxx>
62 #include <filter/PcdReader.hxx>
63 #include <filter/PbmReader.hxx>
64 #include <filter/DxfReader.hxx>
65 #include <filter/GifWriter.hxx>
66 #include <filter/BmpReader.hxx>
67 #include <filter/BmpWriter.hxx>
68 #include <filter/WebpReader.hxx>
69 #include <filter/WebpWriter.hxx>
70 #include <osl/module.hxx>
71 #include <com/sun/star/uno/Reference.h>
72 #include <com/sun/star/awt/Size.hpp>
73 #include <com/sun/star/uno/XInterface.hpp>
74 #include <com/sun/star/io/XActiveDataSource.hpp>
75 #include <com/sun/star/io/XOutputStream.hpp>
76 #include <com/sun/star/svg/XSVGWriter.hpp>
77 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
78 #include <com/sun/star/xml/sax/Writer.hpp>
79 #include <unotools/ucbstreamhelper.hxx>
80 #include <rtl/bootstrap.hxx>
81 #include <tools/svlibrary.h>
82 #include <comphelper/string.hxx>
83 #include <unotools/ucbhelper.hxx>
84 #include <vector>
85 #include <memory>
86 #include <mutex>
87 #include <string_view>
88 #include <o3tl/string_view.hxx>
89 #include <vcl/TypeSerializer.hxx>
90 
91 #include "FilterConfigCache.hxx"
92 
93 #include <graphic/GraphicFormatDetector.hxx>
94 
95 // Support for GfxLinkType::NativeWebp is so far disabled,
96 // as enabling it would write .webp images e.g. to .odt documents,
97 // making those images unreadable for older readers. So for now
98 // disable the support so that .webp images will be written out as .png,
99 // and somewhen later enable the support unconditionally.
supportNativeWebp()100 static bool supportNativeWebp()
101 {
102     // Enable support only for unittests
103     const char* const testname = getenv("LO_TESTNAME");
104     if(testname)
105         return true;
106     return false;
107 }
108 
109 static std::vector< GraphicFilter* > gaFilterHdlList;
110 
getListMutex()111 static std::mutex& getListMutex()
112 {
113     static std::mutex s_aListProtection;
114     return s_aListProtection;
115 }
116 
117 namespace {
118 
119 class ImpFilterOutputStream : public ::cppu::WeakImplHelper< css::io::XOutputStream >
120 {
121     SvStream&               mrStm;
122 
writeBytes(const css::uno::Sequence<sal_Int8> & rData)123     virtual void SAL_CALL   writeBytes( const css::uno::Sequence< sal_Int8 >& rData ) override
124         { mrStm.WriteBytes(rData.getConstArray(), rData.getLength()); }
flush()125     virtual void SAL_CALL   flush() override
126         { mrStm.FlushBuffer(); }
closeOutput()127     virtual void SAL_CALL   closeOutput() override {}
128 
129 public:
130 
ImpFilterOutputStream(SvStream & rStm)131     explicit ImpFilterOutputStream( SvStream& rStm ) : mrStm( rStm ) {}
132 };
133 
134 }
135 
136 // Helper functions
137 
ImpGetExtension(std::u16string_view rPath)138 static OUString ImpGetExtension( std::u16string_view rPath )
139 {
140     OUString        aExt;
141     INetURLObject   aURL( rPath );
142     aExt = aURL.GetFileExtension().toAsciiUpperCase();
143     return aExt;
144 }
145 
ImpTestOrFindFormat(std::u16string_view rPath,SvStream & rStream,sal_uInt16 & rFormat)146 ErrCode GraphicFilter::ImpTestOrFindFormat( std::u16string_view rPath, SvStream& rStream, sal_uInt16& rFormat )
147 {
148     // determine or check the filter/format by reading into it
149     if( rFormat == GRFILTER_FORMAT_DONTKNOW )
150     {
151         OUString aFormatExt;
152         if (vcl::peekGraphicFormat(rStream, aFormatExt, false))
153         {
154             rFormat = pConfig->GetImportFormatNumberForExtension( aFormatExt );
155             if( rFormat != GRFILTER_FORMAT_DONTKNOW )
156                 return ERRCODE_NONE;
157         }
158         // determine filter by file extension
159         if( !rPath.empty() )
160         {
161             OUString aExt( ImpGetExtension( rPath ) );
162             rFormat = pConfig->GetImportFormatNumberForExtension( aExt );
163             if( rFormat != GRFILTER_FORMAT_DONTKNOW )
164                 return ERRCODE_NONE;
165         }
166         return ERRCODE_GRFILTER_FORMATERROR;
167     }
168     else
169     {
170         OUString aTmpStr( pConfig->GetImportFormatExtension( rFormat ) );
171         aTmpStr = aTmpStr.toAsciiUpperCase();
172         if (!vcl::peekGraphicFormat(rStream, aTmpStr, true))
173             return ERRCODE_GRFILTER_FORMATERROR;
174         if ( pConfig->GetImportFormatExtension( rFormat ).equalsIgnoreAsciiCase( "pcd" ) )
175         {
176             sal_Int32 nBase = 2;    // default Base0
177             if ( pConfig->GetImportFilterType( rFormat ).equalsIgnoreAsciiCase( "pcd_Photo_CD_Base4" ) )
178                 nBase = 1;
179             else if ( pConfig->GetImportFilterType( rFormat ).equalsIgnoreAsciiCase( "pcd_Photo_CD_Base16" ) )
180                 nBase = 0;
181             FilterConfigItem aFilterConfigItem( u"Office.Common/Filter/Graphic/Import/PCD" );
182             aFilterConfigItem.WriteInt32( u"Resolution"_ustr, nBase );
183         }
184     }
185 
186     return ERRCODE_NONE;
187 }
188 
ImpGetScaledGraphic(const Graphic & rGraphic,FilterConfigItem & rConfigItem)189 static Graphic ImpGetScaledGraphic( const Graphic& rGraphic, FilterConfigItem& rConfigItem )
190 {
191     Graphic     aGraphic;
192 
193     sal_Int32 nLogicalWidth = rConfigItem.ReadInt32( u"LogicalWidth"_ustr, 0 );
194     sal_Int32 nLogicalHeight = rConfigItem.ReadInt32( u"LogicalHeight"_ustr, 0 );
195 
196     if ( rGraphic.GetType() != GraphicType::NONE )
197     {
198         sal_Int32 nMode = rConfigItem.ReadInt32( u"ExportMode"_ustr, -1 );
199 
200         if ( nMode == -1 )  // the property is not there, this is possible, if the graphic filter
201         {                   // is called via UnoGraphicExporter and not from a graphic export Dialog
202             nMode = 0;      // then we are defaulting this mode to 0
203             if ( nLogicalWidth || nLogicalHeight )
204                 nMode = 2;
205         }
206 
207         Size aOriginalSize;
208         Size aPrefSize( rGraphic.GetPrefSize() );
209         MapMode aPrefMapMode( rGraphic.GetPrefMapMode() );
210         if (aPrefMapMode.GetMapUnit() == MapUnit::MapPixel)
211             aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM));
212         else
213             aOriginalSize = OutputDevice::LogicToLogic(aPrefSize, aPrefMapMode, MapMode(MapUnit::Map100thMM));
214         if ( !nLogicalWidth )
215             nLogicalWidth = aOriginalSize.Width();
216         if ( !nLogicalHeight )
217             nLogicalHeight = aOriginalSize.Height();
218         if( rGraphic.GetType() == GraphicType::Bitmap )
219         {
220 
221             // Resolution is set
222             if( nMode == 1 )
223             {
224                 BitmapEx    aBitmap( rGraphic.GetBitmapEx() );
225                 MapMode     aMap( MapUnit::Map100thInch );
226 
227                 sal_Int32   nDPI = rConfigItem.ReadInt32( u"Resolution"_ustr, 75 );
228                 Fraction    aFrac( 1, std::clamp( nDPI, sal_Int32(75), sal_Int32(600) ) );
229 
230                 aMap.SetScaleX( aFrac );
231                 aMap.SetScaleY( aFrac );
232 
233                 Size aOldSize = aBitmap.GetSizePixel();
234                 aGraphic = rGraphic;
235                 aGraphic.SetPrefMapMode( aMap );
236                 aGraphic.SetPrefSize( Size( aOldSize.Width() * 100,
237                                             aOldSize.Height() * 100 ) );
238             }
239             // Size is set
240             else if( nMode == 2 )
241             {
242                aGraphic = rGraphic;
243                aGraphic.SetPrefMapMode( MapMode( MapUnit::Map100thMM ) );
244                aGraphic.SetPrefSize( Size( nLogicalWidth, nLogicalHeight ) );
245             }
246             else
247                 aGraphic = rGraphic;
248 
249             sal_Int32 nColors = rConfigItem.ReadInt32( u"Color"_ustr, 0 );
250             if ( nColors )  // graphic conversion necessary ?
251             {
252                 BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
253                 aBmpEx.Convert( static_cast<BmpConversion>(nColors) );   // the entries in the xml section have the same meaning as
254                 aGraphic = aBmpEx;                          // they have in the BmpConversion enum, so it should be
255             }                                               // allowed to cast them
256         }
257         else
258         {
259             if( ( nMode == 1 ) || ( nMode == 2 ) )
260             {
261                 GDIMetaFile aMtf( rGraphic.GetGDIMetaFile() );
262                 Size aNewSize( OutputDevice::LogicToLogic(Size(nLogicalWidth, nLogicalHeight), MapMode(MapUnit::Map100thMM), aMtf.GetPrefMapMode()) );
263 
264                 if( aNewSize.Width() && aNewSize.Height() )
265                 {
266                     const Size aPreferredSize( aMtf.GetPrefSize() );
267                     aMtf.Scale( Fraction( aNewSize.Width(), aPreferredSize.Width() ),
268                                 Fraction( aNewSize.Height(), aPreferredSize.Height() ) );
269                 }
270                 aGraphic = Graphic( aMtf );
271             }
272             else
273                 aGraphic = rGraphic;
274         }
275 
276     }
277     else
278         aGraphic = rGraphic;
279 
280     return aGraphic;
281 }
282 
GraphicFilter(bool bConfig)283 GraphicFilter::GraphicFilter( bool bConfig )
284     : bUseConfig(bConfig)
285 {
286     ImplInit();
287 }
288 
~GraphicFilter()289 GraphicFilter::~GraphicFilter()
290 {
291     {
292         std::scoped_lock aGuard( getListMutex() );
293         auto it = std::find(gaFilterHdlList.begin(), gaFilterHdlList.end(), this);
294         if( it != gaFilterHdlList.end() )
295             gaFilterHdlList.erase( it );
296 
297         if( gaFilterHdlList.empty() )
298             delete pConfig;
299     }
300 
301     mxErrorEx.reset();
302 }
303 
ImplInit()304 void GraphicFilter::ImplInit()
305 {
306     {
307         std::scoped_lock aGuard( getListMutex() );
308 
309         if ( gaFilterHdlList.empty() )
310             pConfig = new FilterConfigCache( bUseConfig );
311         else
312             pConfig = gaFilterHdlList.front()->pConfig;
313 
314         gaFilterHdlList.push_back( this );
315     }
316 
317     if( bUseConfig )
318     {
319         OUString url(u"$BRAND_BASE_DIR/" LIBO_LIB_FOLDER ""_ustr);
320         rtl::Bootstrap::expandMacros(url); //TODO: detect failure
321         osl::FileBase::getSystemPathFromFileURL(url, aFilterPath);
322     }
323 
324     mxErrorEx = ERRCODE_NONE;
325 }
326 
ImplSetError(ErrCode nError,const SvStream * pStm)327 ErrCode GraphicFilter::ImplSetError( ErrCode nError, const SvStream* pStm )
328 {
329     mxErrorEx = pStm ? pStm->GetError() : ERRCODE_NONE;
330     return nError;
331 }
332 
GetImportFormatCount() const333 sal_uInt16 GraphicFilter::GetImportFormatCount() const
334 {
335     return pConfig->GetImportFormatCount();
336 }
337 
GetImportFormatNumber(std::u16string_view rFormatName)338 sal_uInt16 GraphicFilter::GetImportFormatNumber( std::u16string_view rFormatName )
339 {
340     return pConfig->GetImportFormatNumber( rFormatName );
341 }
342 
GetImportFormatNumberForShortName(std::u16string_view rShortName)343 sal_uInt16 GraphicFilter::GetImportFormatNumberForShortName( std::u16string_view rShortName )
344 {
345     return pConfig->GetImportFormatNumberForShortName( rShortName );
346 }
347 
GetImportFormatNumberForTypeName(std::u16string_view rType)348 sal_uInt16 GraphicFilter::GetImportFormatNumberForTypeName( std::u16string_view rType )
349 {
350     return pConfig->GetImportFormatNumberForTypeName( rType );
351 }
352 
GetImportFormatName(sal_uInt16 nFormat)353 OUString GraphicFilter::GetImportFormatName( sal_uInt16 nFormat )
354 {
355     return pConfig->GetImportFormatName( nFormat );
356 }
357 
GetImportFormatTypeName(sal_uInt16 nFormat)358 OUString GraphicFilter::GetImportFormatTypeName( sal_uInt16 nFormat )
359 {
360     return pConfig->GetImportFilterTypeName( nFormat );
361 }
362 
363 #ifdef _WIN32
GetImportFormatMediaType(sal_uInt16 nFormat)364 OUString GraphicFilter::GetImportFormatMediaType( sal_uInt16 nFormat )
365 {
366     return pConfig->GetImportFormatMediaType( nFormat );
367 }
368 #endif
369 
GetImportFormatShortName(sal_uInt16 nFormat)370 OUString GraphicFilter::GetImportFormatShortName( sal_uInt16 nFormat )
371 {
372     return pConfig->GetImportFormatShortName( nFormat );
373 }
374 
GetImportWildcard(sal_uInt16 nFormat,sal_Int32 nEntry)375 OUString GraphicFilter::GetImportWildcard( sal_uInt16 nFormat, sal_Int32 nEntry )
376 {
377     return pConfig->GetImportWildcard( nFormat, nEntry );
378 }
379 
GetExportFormatCount() const380 sal_uInt16 GraphicFilter::GetExportFormatCount() const
381 {
382     return pConfig->GetExportFormatCount();
383 }
384 
GetExportFormatNumber(std::u16string_view rFormatName)385 sal_uInt16 GraphicFilter::GetExportFormatNumber( std::u16string_view rFormatName )
386 {
387     return pConfig->GetExportFormatNumber( rFormatName );
388 }
389 
GetExportFormatNumberForMediaType(std::u16string_view rMediaType)390 sal_uInt16 GraphicFilter::GetExportFormatNumberForMediaType( std::u16string_view rMediaType )
391 {
392     return pConfig->GetExportFormatNumberForMediaType( rMediaType );
393 }
394 
GetExportFormatNumberForShortName(std::u16string_view rShortName)395 sal_uInt16 GraphicFilter::GetExportFormatNumberForShortName( std::u16string_view rShortName )
396 {
397     return pConfig->GetExportFormatNumberForShortName( rShortName );
398 }
399 
GetExportInternalFilterName(sal_uInt16 nFormat)400 OUString GraphicFilter::GetExportInternalFilterName( sal_uInt16 nFormat )
401 {
402     return pConfig->GetExportInternalFilterName( nFormat );
403 }
404 
GetExportFormatNumberForTypeName(std::u16string_view rType)405 sal_uInt16 GraphicFilter::GetExportFormatNumberForTypeName( std::u16string_view rType )
406 {
407     return pConfig->GetExportFormatNumberForTypeName( rType );
408 }
409 
GetExportFormatName(sal_uInt16 nFormat)410 OUString GraphicFilter::GetExportFormatName( sal_uInt16 nFormat )
411 {
412     return pConfig->GetExportFormatName( nFormat );
413 }
414 
GetExportFormatMediaType(sal_uInt16 nFormat)415 OUString GraphicFilter::GetExportFormatMediaType( sal_uInt16 nFormat )
416 {
417     return pConfig->GetExportFormatMediaType( nFormat );
418 }
419 
GetExportFormatShortName(sal_uInt16 nFormat)420 OUString GraphicFilter::GetExportFormatShortName( sal_uInt16 nFormat )
421 {
422     return pConfig->GetExportFormatShortName( nFormat );
423 }
424 
GetExportWildcard(sal_uInt16 nFormat)425 OUString GraphicFilter::GetExportWildcard( sal_uInt16 nFormat )
426 {
427     return pConfig->GetExportWildcard( nFormat, 0 );
428 }
429 
IsExportPixelFormat(sal_uInt16 nFormat)430 bool GraphicFilter::IsExportPixelFormat( sal_uInt16 nFormat )
431 {
432     return pConfig->IsExportPixelFormat( nFormat );
433 }
434 
CanImportGraphic(const INetURLObject & rPath,sal_uInt16 nFormat,sal_uInt16 * pDeterminedFormat)435 ErrCode GraphicFilter::CanImportGraphic( const INetURLObject& rPath,
436                                         sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat )
437 {
438     ErrCode  nRetValue = ERRCODE_GRFILTER_FORMATERROR;
439     SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::CanImportGraphic() : ProtType == INetProtocol::NotValid" );
440 
441     OUString    aMainUrl( rPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
442     std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::READ | StreamMode::SHARE_DENYNONE ));
443     if (xStream)
444     {
445         nRetValue = CanImportGraphic( aMainUrl, *xStream, nFormat, pDeterminedFormat );
446     }
447     return nRetValue;
448 }
449 
CanImportGraphic(std::u16string_view rMainUrl,SvStream & rIStream,sal_uInt16 nFormat,sal_uInt16 * pDeterminedFormat)450 ErrCode GraphicFilter::CanImportGraphic( std::u16string_view rMainUrl, SvStream& rIStream,
451                                         sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat )
452 {
453     sal_uInt64 nStreamPos = rIStream.Tell();
454     ErrCode nRes = ImpTestOrFindFormat( rMainUrl, rIStream, nFormat );
455 
456     rIStream.Seek(nStreamPos);
457 
458     if( nRes==ERRCODE_NONE && pDeterminedFormat!=nullptr )
459         *pDeterminedFormat = nFormat;
460 
461     return ImplSetError( nRes, &rIStream );
462 }
463 
464 //SJ: TODO, we need to create a GraphicImporter component
ImportGraphic(Graphic & rGraphic,const INetURLObject & rPath,sal_uInt16 nFormat,sal_uInt16 * pDeterminedFormat,GraphicFilterImportFlags nImportFlags)465 ErrCode GraphicFilter::ImportGraphic( Graphic& rGraphic, const INetURLObject& rPath,
466                                      sal_uInt16 nFormat, sal_uInt16 * pDeterminedFormat, GraphicFilterImportFlags nImportFlags )
467 {
468     ErrCode nRetValue = ERRCODE_GRFILTER_FORMATERROR;
469     SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::ImportGraphic() : ProtType == INetProtocol::NotValid" );
470 
471     OUString    aMainUrl( rPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
472     std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::READ | StreamMode::SHARE_DENYNONE ));
473     if (xStream)
474     {
475         nRetValue = ImportGraphic( rGraphic, aMainUrl, *xStream, nFormat, pDeterminedFormat, nImportFlags );
476     }
477     return nRetValue;
478 }
479 
480 namespace {
481 
482 /// Contains a stream and other associated data to import pixels into a
483 /// Graphic.
484 struct GraphicImportContext
485 {
486     /// Pixel data is read from this stream.
487     std::unique_ptr<SvStream> m_pStream;
488     /// The Graphic the import filter gets.
489     std::shared_ptr<Graphic> m_pGraphic;
490     /// Write pixel data using this access.
491     std::unique_ptr<BitmapScopedWriteAccess> m_pAccess;
492     std::unique_ptr<BitmapScopedWriteAccess> m_pAlphaAccess;
493     // Need to have an AlphaMask instance to keep its lifetime.
494     AlphaMask mAlphaMask;
495     /// Signals if import finished correctly.
496     ErrCode m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
497     /// Original graphic format.
498     GfxLinkType m_eLinkType = GfxLinkType::NONE;
499     /// Position of the stream before reading the data.
500     sal_uInt64 m_nStreamBegin = 0;
501     /// Flags for the import filter.
502     GraphicFilterImportFlags m_nImportFlags = GraphicFilterImportFlags::NONE;
503 };
504 
505 /// Graphic import worker that gets executed on a thread.
506 class GraphicImportTask : public comphelper::ThreadTask
507 {
508     GraphicImportContext& m_rContext;
509 public:
510     GraphicImportTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, GraphicImportContext& rContext);
511     void doWork() override;
512     /// Shared code between threaded and non-threaded version.
513     static void doImport(GraphicImportContext& rContext);
514 };
515 
516 }
517 
GraphicImportTask(const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,GraphicImportContext & rContext)518 GraphicImportTask::GraphicImportTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, GraphicImportContext& rContext)
519     : comphelper::ThreadTask(pTag),
520       m_rContext(rContext)
521 {
522 }
523 
doWork()524 void GraphicImportTask::doWork()
525 {
526     GraphicImportTask::doImport(m_rContext);
527 }
528 
doImport(GraphicImportContext & rContext)529 void GraphicImportTask::doImport(GraphicImportContext& rContext)
530 {
531     if(rContext.m_eLinkType == GfxLinkType::NativeJpg)
532     {
533         if (!ImportJPEG(*rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, rContext.m_pAccess.get()))
534             rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
535     }
536     else if(rContext.m_eLinkType == GfxLinkType::NativePng)
537     {
538         if (!vcl::ImportPNG(*rContext.m_pStream, *rContext.m_pGraphic,
539             rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap,
540             rContext.m_pAccess.get(), rContext.m_pAlphaAccess.get()))
541         {
542             rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
543         }
544     }
545 }
546 
ImportGraphics(std::vector<std::shared_ptr<Graphic>> & rGraphics,std::vector<std::unique_ptr<SvStream>> vStreams)547 void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGraphics, std::vector< std::unique_ptr<SvStream> > vStreams)
548 {
549     static bool bThreads = !getenv("VCL_NO_THREAD_IMPORT");
550     std::vector<GraphicImportContext> aContexts;
551     aContexts.reserve(vStreams.size());
552     comphelper::ThreadPool& rSharedPool = comphelper::ThreadPool::getSharedOptimalPool();
553     std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
554 
555     for (auto& pStream : vStreams)
556     {
557         aContexts.emplace_back();
558         GraphicImportContext& rContext = aContexts.back();
559 
560         if (pStream)
561         {
562             rContext.m_pStream = std::move(pStream);
563             rContext.m_pGraphic = std::make_shared<Graphic>();
564             rContext.m_nStatus = ERRCODE_NONE;
565 
566             // Detect the format.
567             ResetLastError();
568             rContext.m_nStreamBegin = rContext.m_pStream->Tell();
569             sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
570             rContext.m_nStatus = ImpTestOrFindFormat(u"", *rContext.m_pStream, nFormat);
571             rContext.m_pStream->Seek(rContext.m_nStreamBegin);
572 
573             // Import the graphic.
574             if (rContext.m_nStatus == ERRCODE_NONE && !rContext.m_pStream->GetError())
575             {
576                 OUString aFilterName = pConfig->GetImportFilterName(nFormat);
577 
578                 if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
579                 {
580                     rContext.m_eLinkType = GfxLinkType::NativeJpg;
581                     rContext.m_nImportFlags = GraphicFilterImportFlags::SetLogsizeForJpeg;
582 
583                     if (ImportJPEG( *rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr))
584                     {
585                         Bitmap& rBitmap = const_cast<Bitmap&>(rContext.m_pGraphic->GetBitmapExRef().GetBitmap());
586                         rContext.m_pAccess = std::make_unique<BitmapScopedWriteAccess>(rBitmap);
587                         rContext.m_pStream->Seek(rContext.m_nStreamBegin);
588                         if (bThreads)
589                             rSharedPool.pushTask(std::make_unique<GraphicImportTask>(pTag, rContext));
590                         else
591                             GraphicImportTask::doImport(rContext);
592                     }
593                     else
594                         rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
595                 }
596                 else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
597                 {
598                     rContext.m_eLinkType = GfxLinkType::NativePng;
599 
600                     if (vcl::ImportPNG( *rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr, nullptr))
601                     {
602                         const BitmapEx& rBitmapEx = rContext.m_pGraphic->GetBitmapExRef();
603                         Bitmap& rBitmap = const_cast<Bitmap&>(rBitmapEx.GetBitmap());
604                         rContext.m_pAccess = std::make_unique<BitmapScopedWriteAccess>(rBitmap);
605                         if(rBitmapEx.IsAlpha())
606                         {
607                             // The separate alpha bitmap causes a number of complications. Not only
608                             // we need to have an extra bitmap access for it, but we also need
609                             // to keep an AlphaMask instance in the context. This is because
610                             // BitmapEx internally keeps Bitmap and not AlphaMask (because the Bitmap
611                             // may be also a mask, not alpha). So BitmapEx::GetAlpha() returns
612                             // a temporary, and direct access to the Bitmap wouldn't work
613                             // with AlphaScopedBitmapAccess. *sigh*
614                             rContext.mAlphaMask = rBitmapEx.GetAlphaMask();
615                             rContext.m_pAlphaAccess = std::make_unique<BitmapScopedWriteAccess>(rContext.mAlphaMask);
616                         }
617                         rContext.m_pStream->Seek(rContext.m_nStreamBegin);
618                         if (bThreads)
619                             rSharedPool.pushTask(std::make_unique<GraphicImportTask>(pTag, rContext));
620                         else
621                             GraphicImportTask::doImport(rContext);
622                     }
623                     else
624                         rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
625                 }
626                 else
627                     rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
628             }
629         }
630     }
631 
632     rSharedPool.waitUntilDone(pTag);
633 
634     // Process data after import.
635     for (auto& rContext : aContexts)
636     {
637         rContext.m_pAccess.reset();
638         rContext.m_pAlphaAccess.reset();
639         if (!rContext.mAlphaMask.IsEmpty()) // Need to move the AlphaMask back to the BitmapEx.
640             *rContext.m_pGraphic = BitmapEx( rContext.m_pGraphic->GetBitmapExRef().GetBitmap(), rContext.mAlphaMask );
641 
642         if (rContext.m_nStatus == ERRCODE_NONE && rContext.m_eLinkType != GfxLinkType::NONE)
643         {
644             BinaryDataContainer aGraphicContent;
645 
646             const sal_uInt64 nStreamEnd = rContext.m_pStream->Tell();
647             sal_Int32 nGraphicContentSize = nStreamEnd - rContext.m_nStreamBegin;
648 
649             if (nGraphicContentSize > 0)
650             {
651                 try
652                 {
653                     rContext.m_pStream->Seek(rContext.m_nStreamBegin);
654                     aGraphicContent = BinaryDataContainer(*rContext.m_pStream, nGraphicContentSize);
655                 }
656                 catch (const std::bad_alloc&)
657                 {
658                     rContext.m_nStatus = ERRCODE_GRFILTER_TOOBIG;
659                 }
660             }
661 
662             if (rContext.m_nStatus == ERRCODE_NONE)
663                 rContext.m_pGraphic->SetGfxLink(std::make_shared<GfxLink>(aGraphicContent, rContext.m_eLinkType));
664         }
665 
666         if (rContext.m_nStatus != ERRCODE_NONE)
667             rContext.m_pGraphic = nullptr;
668 
669         rGraphics.push_back(rContext.m_pGraphic);
670     }
671 }
672 
MakeGraphicsAvailableThreaded(std::vector<Graphic * > & graphics)673 void GraphicFilter::MakeGraphicsAvailableThreaded(std::vector<Graphic*>& graphics)
674 {
675     // Graphic::makeAvailable() is not thread-safe. Only the jpeg and png loaders are, so here
676     // we process only jpeg and png images that also have their stream data, load new Graphic's
677     // from them and then update the passed objects using them.
678     std::vector< Graphic* > toLoad;
679     for(auto graphic : graphics)
680     {
681         // Need to use GetSharedGfxLink, to access the pointer without copying.
682         if(!graphic->isAvailable() && graphic->IsGfxLink()
683             && graphic->GetSharedGfxLink()->GetDataSize() != 0
684             && (graphic->GetSharedGfxLink()->GetType() == GfxLinkType::NativeJpg
685                 || graphic->GetSharedGfxLink()->GetType() == GfxLinkType::NativePng))
686         {
687             // Graphic objects share internal ImpGraphic, do not process any of those twice.
688             const auto predicate = [graphic](Graphic* item) { return item->ImplGetImpGraphic() == graphic->ImplGetImpGraphic(); };
689             if( std::none_of(toLoad.begin(), toLoad.end(), predicate ))
690                 toLoad.push_back( graphic );
691         }
692     }
693     if( toLoad.empty())
694         return;
695     std::vector< std::unique_ptr<SvStream>> streams;
696     streams.reserve(toLoad.size());
697     for( auto graphic : toLoad )
698     {
699         streams.push_back( std::make_unique<SvMemoryStream>( const_cast<sal_uInt8*>(graphic->GetSharedGfxLink()->GetData()),
700             graphic->GetSharedGfxLink()->GetDataSize(), StreamMode::READ | StreamMode::WRITE));
701     }
702     std::vector< std::shared_ptr<Graphic>> loadedGraphics;
703     loadedGraphics.reserve(streams.size());
704     ImportGraphics(loadedGraphics, std::move(streams));
705     assert(loadedGraphics.size() == toLoad.size());
706     for( size_t i = 0; i < toLoad.size(); ++i )
707     {
708         if(loadedGraphics[ i ] != nullptr)
709             toLoad[ i ]->ImplGetImpGraphic()->updateFromLoadedGraphic(loadedGraphics[ i ]->ImplGetImpGraphic());
710     }
711 }
712 
ImportUnloadedGraphic(SvStream & rIStream,sal_uInt64 sizeLimit,const Size * pSizeHint)713 Graphic GraphicFilter::ImportUnloadedGraphic(SvStream& rIStream, sal_uInt64 sizeLimit,
714                                              const Size* pSizeHint)
715 {
716     Graphic aGraphic;
717     sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
718     GfxLinkType eLinkType = GfxLinkType::NONE;
719 
720     ResetLastError();
721 
722     const sal_uInt64 nStreamBegin = rIStream.Tell();
723 
724     rIStream.Seek(nStreamBegin);
725 
726     ErrCode nStatus = ImpTestOrFindFormat(u"", rIStream, nFormat);
727 
728     rIStream.Seek(nStreamBegin);
729     sal_uInt32 nStreamLength(rIStream.remainingSize());
730     if (sizeLimit && sizeLimit < nStreamLength)
731         nStreamLength = sizeLimit;
732 
733     OUString aFilterName = pConfig->GetImportFilterName(nFormat);
734 
735     BinaryDataContainer aGraphicContent;
736 
737     // read graphic
738     {
739         if (aFilterName.equalsIgnoreAsciiCase(IMP_GIF))
740         {
741             eLinkType = GfxLinkType::NativeGif;
742         }
743         else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
744         {
745             // check if this PNG contains a GIF chunk!
746             aGraphicContent = vcl::PngImageReader::getMicrosoftGifChunk(rIStream);
747             if (!aGraphicContent.isEmpty())
748                 eLinkType = GfxLinkType::NativeGif;
749             else
750                 eLinkType = GfxLinkType::NativePng;
751         }
752         else if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
753         {
754             eLinkType = GfxLinkType::NativeJpg;
755         }
756         else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVG))
757         {
758             bool bOkay(false);
759 
760             if (nStreamLength > 0)
761             {
762                 std::vector<sal_uInt8> aTwoBytes(2);
763                 rIStream.ReadBytes(aTwoBytes.data(), 2);
764                 rIStream.Seek(nStreamBegin);
765 
766                 if (aTwoBytes[0] == 0x1F && aTwoBytes[1] == 0x8B)
767                 {
768                     SvMemoryStream aMemStream;
769                     ZCodec aCodec;
770                     tools::Long nMemoryLength;
771 
772                     aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
773                     nMemoryLength = aCodec.Decompress(rIStream, aMemStream);
774                     aCodec.EndCompression();
775 
776                     if (!rIStream.GetError() && nMemoryLength >= 0)
777                     {
778                         aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
779                         aGraphicContent = BinaryDataContainer(aMemStream, nMemoryLength);
780 
781                         bOkay = true;
782                     }
783                 }
784                 else
785                 {
786                     aGraphicContent = BinaryDataContainer(rIStream, nStreamLength);
787 
788                     bOkay = true;
789                 }
790             }
791 
792             if (bOkay)
793             {
794                 eLinkType = GfxLinkType::NativeSvg;
795             }
796             else
797             {
798                 nStatus = ERRCODE_GRFILTER_FILTERERROR;
799             }
800         }
801         else if (aFilterName.equalsIgnoreAsciiCase(IMP_BMP))
802         {
803             eLinkType = GfxLinkType::NativeBmp;
804         }
805         else if (aFilterName.equalsIgnoreAsciiCase(IMP_MOV))
806         {
807             eLinkType = GfxLinkType::NativeMov;
808         }
809         else if (aFilterName.equalsIgnoreAsciiCase(IMP_WMF) ||
810                 aFilterName.equalsIgnoreAsciiCase(IMP_EMF) ||
811                 aFilterName.equalsIgnoreAsciiCase(IMP_WMZ) ||
812                 aFilterName.equalsIgnoreAsciiCase(IMP_EMZ))
813         {
814             rIStream.Seek(nStreamBegin);
815             if (ZCodec::IsZCompressed(rIStream))
816             {
817                 ZCodec aCodec;
818                 SvMemoryStream aMemStream;
819                 tools::Long nMemoryLength;
820                 aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
821                 nMemoryLength = aCodec.Decompress(rIStream, aMemStream);
822                 aCodec.EndCompression();
823 
824                 if (!rIStream.GetError() && nMemoryLength >= 0)
825                 {
826                     aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
827                     aGraphicContent = BinaryDataContainer(aMemStream, nMemoryLength);
828                 }
829             }
830             else
831             {
832                 aGraphicContent = BinaryDataContainer(rIStream, nStreamLength);
833             }
834             if (!rIStream.GetError())
835             {
836                 eLinkType = GfxLinkType::NativeWmf;
837             }
838             else
839             {
840                 nStatus = ERRCODE_GRFILTER_FILTERERROR;
841             }
842         }
843         else if (aFilterName == IMP_PDF)
844         {
845             eLinkType = GfxLinkType::NativePdf;
846         }
847         else if (aFilterName == IMP_TIFF)
848         {
849             eLinkType = GfxLinkType::NativeTif;
850         }
851         else if (aFilterName == IMP_PICT)
852         {
853             eLinkType = GfxLinkType::NativePct;
854         }
855         else if (aFilterName == IMP_MET)
856         {
857             eLinkType = GfxLinkType::NativeMet;
858         }
859         else if (aFilterName.equalsIgnoreAsciiCase(IMP_WEBP))
860         {
861             if(supportNativeWebp())
862                 eLinkType = GfxLinkType::NativeWebp;
863             else
864                 nStatus = ERRCODE_GRFILTER_FILTERERROR;
865         }
866         else
867         {
868             nStatus = ERRCODE_GRFILTER_FILTERERROR;
869         }
870     }
871 
872     if (nStatus == ERRCODE_NONE && eLinkType != GfxLinkType::NONE)
873     {
874         if (aGraphicContent.isEmpty())
875         {
876             if (nStreamLength > 0)
877             {
878                 try
879                 {
880                     rIStream.Seek(nStreamBegin);
881                     aGraphicContent = BinaryDataContainer(rIStream, nStreamLength);
882                 }
883                 catch (const std::bad_alloc&)
884                 {
885                     nStatus = ERRCODE_GRFILTER_TOOBIG;
886                 }
887             }
888         }
889 
890         if( nStatus == ERRCODE_NONE )
891         {
892             bool bAnimated = false;
893             Size aLogicSize;
894             if (eLinkType == GfxLinkType::NativeGif && !aGraphicContent.isEmpty())
895             {
896                 std::shared_ptr<SvStream> pMemoryStream = aGraphicContent.getAsStream();
897                 bAnimated = IsGIFAnimated(*pMemoryStream, aLogicSize);
898                 if (!pSizeHint && aLogicSize.getWidth() && aLogicSize.getHeight())
899                 {
900                     pSizeHint = &aLogicSize;
901                 }
902             }
903             aGraphic.SetGfxLink(std::make_shared<GfxLink>(aGraphicContent, eLinkType));
904             aGraphic.ImplGetImpGraphic()->setPrepared(bAnimated, pSizeHint);
905         }
906     }
907 
908     // Set error code or try to set native buffer
909     if (nStatus != ERRCODE_NONE)
910         ImplSetError(nStatus, &rIStream);
911     if (nStatus != ERRCODE_NONE || eLinkType == GfxLinkType::NONE)
912         rIStream.Seek(nStreamBegin);
913 
914     return aGraphic;
915 }
916 
readGIF(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType)917 ErrCode GraphicFilter::readGIF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
918 {
919     if (ImportGIF(rStream, rGraphic))
920     {
921         rLinkType = GfxLinkType::NativeGif;
922         return ERRCODE_NONE;
923     }
924     else
925         return ERRCODE_GRFILTER_FILTERERROR;
926 }
927 
readPNG(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType,BinaryDataContainer & rpGraphicContent)928 ErrCode GraphicFilter::readPNG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, BinaryDataContainer& rpGraphicContent)
929 {
930     ErrCode aReturnCode = ERRCODE_NONE;
931 
932     // check if this PNG contains a GIF chunk!
933     if (auto aMSGifChunk = vcl::PngImageReader::getMicrosoftGifChunk(rStream);
934         !aMSGifChunk.isEmpty())
935     {
936         std::shared_ptr<SvStream> pIStrm(aMSGifChunk.getAsStream());
937         ImportGIF(*pIStrm, rGraphic);
938         rLinkType = GfxLinkType::NativeGif;
939         rpGraphicContent = aMSGifChunk;
940         return aReturnCode;
941     }
942 
943     // PNG has no GIF chunk
944     Graphic aGraphic;
945     vcl::PngImageReader aPNGReader(rStream);
946     aPNGReader.read(aGraphic);
947     if (!aGraphic.GetBitmapEx().IsEmpty())
948     {
949         rGraphic = aGraphic;
950         rLinkType = GfxLinkType::NativePng;
951     }
952     else
953         aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
954 
955     return aReturnCode;
956 }
957 
readJPEG(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType,GraphicFilterImportFlags nImportFlags)958 ErrCode GraphicFilter::readJPEG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, GraphicFilterImportFlags nImportFlags)
959 {
960     ErrCode aReturnCode = ERRCODE_NONE;
961 
962     // set LOGSIZE flag always, if not explicitly disabled
963     // (see #90508 and #106763)
964     if (!(nImportFlags & GraphicFilterImportFlags::DontSetLogsizeForJpeg))
965     {
966         nImportFlags |= GraphicFilterImportFlags::SetLogsizeForJpeg;
967     }
968 
969     sal_uInt64 nPosition = rStream.Tell();
970     if (!ImportJPEG(rStream, rGraphic, nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr))
971         aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
972     else
973     {
974         Bitmap& rBitmap = const_cast<Bitmap&>(rGraphic.GetBitmapExRef().GetBitmap());
975         BitmapScopedWriteAccess pWriteAccess(rBitmap);
976         rStream.Seek(nPosition);
977         if (!ImportJPEG(rStream, rGraphic, nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, &pWriteAccess))
978             aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
979         else
980             rLinkType = GfxLinkType::NativeJpg;
981     }
982 
983     return aReturnCode;
984 }
985 
readSVG(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType,BinaryDataContainer & rpGraphicContent)986 ErrCode GraphicFilter::readSVG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, BinaryDataContainer& rpGraphicContent)
987 {
988     ErrCode aReturnCode = ERRCODE_NONE;
989 
990     const sal_uInt64 nStreamPosition(rStream.Tell());
991     const sal_uInt64 nStreamLength(rStream.remainingSize());
992 
993     bool bOkay(false);
994 
995     if (nStreamLength > 0)
996     {
997         std::vector<sal_uInt8> aTwoBytes(2);
998         rStream.ReadBytes(aTwoBytes.data(), 2);
999         rStream.Seek(nStreamPosition);
1000 
1001         if (aTwoBytes[0] == 0x1F && aTwoBytes[1] == 0x8B)
1002         {
1003             SvMemoryStream aMemStream;
1004             ZCodec aCodec;
1005             tools::Long nMemoryLength;
1006 
1007             aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
1008             nMemoryLength = aCodec.Decompress(rStream, aMemStream);
1009             aCodec.EndCompression();
1010 
1011             if (!rStream.GetError() && nMemoryLength >= 0)
1012             {
1013                 aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
1014                 rpGraphicContent = BinaryDataContainer(aMemStream, nMemoryLength);
1015 
1016                 // Make a uncompressed copy for GfxLink
1017                 if (!aMemStream.GetError())
1018                 {
1019                     auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(rpGraphicContent, VectorGraphicDataType::Svg);
1020                     rGraphic = Graphic(aVectorGraphicDataPtr);
1021                     bOkay = true;
1022                 }
1023             }
1024         }
1025         else
1026         {
1027             BinaryDataContainer aNewData(rStream, nStreamLength);
1028 
1029             if (!rStream.GetError())
1030             {
1031                 auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aNewData, VectorGraphicDataType::Svg);
1032                 rGraphic = Graphic(aVectorGraphicDataPtr);
1033                 bOkay = true;
1034             }
1035         }
1036     }
1037 
1038     if (bOkay)
1039     {
1040         rLinkType = GfxLinkType::NativeSvg;
1041     }
1042     else
1043     {
1044         aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
1045     }
1046 
1047     return aReturnCode;
1048 }
1049 
readXBM(SvStream & rStream,Graphic & rGraphic)1050 ErrCode GraphicFilter::readXBM(SvStream & rStream, Graphic & rGraphic)
1051 {
1052     if (ImportXBM(rStream, rGraphic))
1053         return ERRCODE_NONE;
1054     else
1055         return ERRCODE_GRFILTER_FILTERERROR;
1056 }
1057 
readXPM(SvStream & rStream,Graphic & rGraphic)1058 ErrCode GraphicFilter::readXPM(SvStream & rStream, Graphic & rGraphic)
1059 {
1060     if (ImportXPM(rStream, rGraphic))
1061         return ERRCODE_NONE;
1062     else
1063         return ERRCODE_GRFILTER_FILTERERROR;
1064 }
1065 
readWMF_EMF(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType,VectorGraphicDataType eType)1066 ErrCode GraphicFilter::readWMF_EMF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, VectorGraphicDataType eType)
1067 {
1068     // use new UNO API service, do not directly import but create a
1069     // Graphic that contains the original data and decomposes to
1070     // primitives on demand
1071     sal_uInt32 nStreamLength(rStream.remainingSize());
1072     SvStream* aNewStream = &rStream;
1073     ErrCode aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
1074     SvMemoryStream aMemStream;
1075     if (ZCodec::IsZCompressed(rStream))
1076     {
1077         ZCodec aCodec;
1078         aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
1079         auto nDecompressLength = aCodec.Decompress(rStream, aMemStream);
1080         aCodec.EndCompression();
1081         aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
1082         if (nDecompressLength >= 0)
1083         {
1084             nStreamLength = nDecompressLength;
1085             aNewStream = &aMemStream;
1086         }
1087     }
1088     BinaryDataContainer aNewData(*aNewStream, nStreamLength);
1089     if (!aNewStream->GetError())
1090     {
1091         auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aNewData, eType);
1092 
1093         rGraphic = Graphic(aVectorGraphicDataPtr);
1094         rLinkType = GfxLinkType::NativeWmf;
1095         aReturnCode = ERRCODE_NONE;
1096     }
1097 
1098     return aReturnCode;
1099 }
1100 
readWMF(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType)1101 ErrCode GraphicFilter::readWMF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
1102 {
1103     return readWMF_EMF(rStream, rGraphic, rLinkType,VectorGraphicDataType::Wmf);
1104 }
1105 
readEMF(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType)1106 ErrCode GraphicFilter::readEMF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
1107 {
1108     return readWMF_EMF(rStream, rGraphic, rLinkType, VectorGraphicDataType::Emf);
1109 }
1110 
readPDF(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType)1111 ErrCode GraphicFilter::readPDF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
1112 {
1113     if (vcl::ImportPDF(rStream, rGraphic))
1114     {
1115         rLinkType = GfxLinkType::NativePdf;
1116         return ERRCODE_NONE;
1117     }
1118     else
1119         return ERRCODE_GRFILTER_FILTERERROR;
1120 }
1121 
readTIFF(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType)1122 ErrCode GraphicFilter::readTIFF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
1123 {
1124     if (ImportTiffGraphicImport(rStream, rGraphic))
1125     {
1126         rLinkType = GfxLinkType::NativeTif;
1127         return ERRCODE_NONE;
1128     }
1129     else
1130         return ERRCODE_GRFILTER_FILTERERROR;
1131 }
1132 
readWithTypeSerializer(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType,std::u16string_view aFilterName)1133 ErrCode GraphicFilter::readWithTypeSerializer(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, std::u16string_view aFilterName)
1134 {
1135     ErrCode aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
1136 
1137     // SV internal filters for import bitmaps and MetaFiles
1138     TypeSerializer aSerializer(rStream);
1139     aSerializer.readGraphic(rGraphic);
1140 
1141     if (!rStream.GetError())
1142     {
1143         if (o3tl::equalsIgnoreAsciiCase(aFilterName, u"" IMP_MOV))
1144         {
1145             rGraphic.SetDefaultType();
1146             rStream.Seek(STREAM_SEEK_TO_END);
1147             rLinkType = GfxLinkType::NativeMov;
1148         }
1149         aReturnCode = ERRCODE_NONE;
1150     }
1151     return aReturnCode;
1152 }
1153 
readBMP(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType)1154 ErrCode GraphicFilter::readBMP(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
1155 {
1156     if (BmpReader(rStream, rGraphic))
1157     {
1158         rLinkType = GfxLinkType::NativeBmp;
1159         return ERRCODE_NONE;
1160     }
1161     else
1162         return ERRCODE_GRFILTER_FILTERERROR;
1163 }
1164 
readTGA(SvStream & rStream,Graphic & rGraphic)1165 ErrCode GraphicFilter::readTGA(SvStream & rStream, Graphic & rGraphic)
1166 {
1167     if (ImportTgaGraphic(rStream, rGraphic))
1168         return ERRCODE_NONE;
1169     else
1170         return ERRCODE_GRFILTER_FILTERERROR;
1171 }
1172 
readPICT(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType)1173 ErrCode GraphicFilter::readPICT(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
1174 {
1175     if (ImportPictGraphic(rStream, rGraphic))
1176     {
1177         rLinkType = GfxLinkType::NativePct;
1178         return ERRCODE_NONE;
1179     }
1180     else
1181         return ERRCODE_GRFILTER_FILTERERROR;
1182 }
1183 
readMET(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType)1184 ErrCode GraphicFilter::readMET(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
1185 {
1186     if (ImportMetGraphic(rStream, rGraphic))
1187     {
1188         rLinkType = GfxLinkType::NativeMet;
1189         return ERRCODE_NONE;
1190     }
1191     else
1192         return ERRCODE_GRFILTER_FILTERERROR;
1193 }
1194 
readRAS(SvStream & rStream,Graphic & rGraphic)1195 ErrCode GraphicFilter::readRAS(SvStream & rStream, Graphic & rGraphic)
1196 {
1197     if (ImportRasGraphic(rStream, rGraphic))
1198         return ERRCODE_NONE;
1199     else
1200         return ERRCODE_GRFILTER_FILTERERROR;
1201 }
1202 
readPCX(SvStream & rStream,Graphic & rGraphic)1203 ErrCode GraphicFilter::readPCX(SvStream & rStream, Graphic & rGraphic)
1204 {
1205     if (ImportPcxGraphic(rStream, rGraphic))
1206         return ERRCODE_NONE;
1207     else
1208         return ERRCODE_GRFILTER_FILTERERROR;
1209 }
1210 
readEPS(SvStream & rStream,Graphic & rGraphic)1211 ErrCode GraphicFilter::readEPS(SvStream & rStream, Graphic & rGraphic)
1212 {
1213     if (ImportEpsGraphic(rStream, rGraphic))
1214         return ERRCODE_NONE;
1215     else
1216         return ERRCODE_GRFILTER_FILTERERROR;
1217 }
1218 
readPSD(SvStream & rStream,Graphic & rGraphic)1219 ErrCode GraphicFilter::readPSD(SvStream & rStream, Graphic & rGraphic)
1220 {
1221     if (ImportPsdGraphic(rStream, rGraphic))
1222         return ERRCODE_NONE;
1223     else
1224         return ERRCODE_GRFILTER_FILTERERROR;
1225 }
1226 
readPCD(SvStream & rStream,Graphic & rGraphic)1227 ErrCode GraphicFilter::readPCD(SvStream & rStream, Graphic & rGraphic)
1228 {
1229     std::unique_ptr<FilterConfigItem> pFilterConfigItem;
1230     if (!comphelper::IsFuzzing())
1231     {
1232         OUString aFilterConfigPath( u"Office.Common/Filter/Graphic/Import/PCD"_ustr );
1233         pFilterConfigItem = std::make_unique<FilterConfigItem>(aFilterConfigPath);
1234     }
1235 
1236     if (ImportPcdGraphic(rStream, rGraphic, pFilterConfigItem.get()))
1237         return ERRCODE_NONE;
1238     else
1239         return ERRCODE_GRFILTER_FILTERERROR;
1240 }
1241 
readPBM(SvStream & rStream,Graphic & rGraphic)1242 ErrCode GraphicFilter::readPBM(SvStream & rStream, Graphic & rGraphic)
1243 {
1244     if (ImportPbmGraphic(rStream, rGraphic))
1245         return ERRCODE_NONE;
1246     else
1247         return ERRCODE_GRFILTER_FILTERERROR;
1248 }
1249 
readDXF(SvStream & rStream,Graphic & rGraphic)1250 ErrCode GraphicFilter::readDXF(SvStream & rStream, Graphic & rGraphic)
1251 {
1252     if (ImportDxfGraphic(rStream, rGraphic))
1253         return ERRCODE_NONE;
1254     else
1255         return ERRCODE_GRFILTER_FILTERERROR;
1256 }
1257 
readWEBP(SvStream & rStream,Graphic & rGraphic,GfxLinkType & rLinkType)1258 ErrCode GraphicFilter::readWEBP(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
1259 {
1260     if (ImportWebpGraphic(rStream, rGraphic))
1261     {
1262         if(supportNativeWebp())
1263             rLinkType = GfxLinkType::NativeWebp;
1264         return ERRCODE_NONE;
1265     }
1266     else
1267         return ERRCODE_GRFILTER_FILTERERROR;
1268 }
1269 
ImportGraphic(Graphic & rGraphic,std::u16string_view rPath,SvStream & rIStream,sal_uInt16 nFormat,sal_uInt16 * pDeterminedFormat,GraphicFilterImportFlags nImportFlags)1270 ErrCode GraphicFilter::ImportGraphic(Graphic& rGraphic, std::u16string_view rPath, SvStream& rIStream,
1271                                      sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat, GraphicFilterImportFlags nImportFlags)
1272 {
1273     OUString aFilterName;
1274     sal_uInt64 nStreamBegin;
1275     ErrCode nStatus;
1276     GfxLinkType eLinkType = GfxLinkType::NONE;
1277     const bool bLinkSet = rGraphic.IsGfxLink();
1278 
1279     BinaryDataContainer aGraphicContent;
1280 
1281     ResetLastError();
1282 
1283     {
1284         bool bDummyContext = rGraphic.IsDummyContext();
1285         if( bDummyContext )
1286         {
1287             rGraphic.SetDummyContext( false );
1288             nStreamBegin = 0;
1289         }
1290         else
1291             nStreamBegin = rIStream.Tell();
1292 
1293         nStatus = ImpTestOrFindFormat( rPath, rIStream, nFormat );
1294         // if pending, return ERRCODE_NONE in order to request more bytes
1295         if( rIStream.GetError() == ERRCODE_IO_PENDING )
1296         {
1297             rGraphic.SetDummyContext(true);
1298             rIStream.ResetError();
1299             rIStream.Seek( nStreamBegin );
1300             return ImplSetError( ERRCODE_NONE );
1301         }
1302 
1303         rIStream.Seek( nStreamBegin );
1304 
1305         if( ( nStatus != ERRCODE_NONE ) || rIStream.GetError() )
1306             return ImplSetError( ( nStatus != ERRCODE_NONE ) ? nStatus : ERRCODE_GRFILTER_OPENERROR, &rIStream );
1307 
1308         if( pDeterminedFormat )
1309             *pDeterminedFormat = nFormat;
1310 
1311         aFilterName = pConfig->GetImportFilterName( nFormat );
1312     }
1313 
1314     // read graphic
1315     {
1316         if (aFilterName.equalsIgnoreAsciiCase(IMP_GIF))
1317         {
1318             nStatus = readGIF(rIStream, rGraphic, eLinkType);
1319         }
1320         else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
1321         {
1322             nStatus = readPNG(rIStream, rGraphic, eLinkType, aGraphicContent);
1323         }
1324         else if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
1325         {
1326             nStatus = readJPEG(rIStream, rGraphic, eLinkType, nImportFlags);
1327         }
1328         else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVG) || aFilterName.equalsIgnoreAsciiCase(IMP_SVGZ))
1329         {
1330             nStatus = readSVG(rIStream, rGraphic, eLinkType, aGraphicContent);
1331         }
1332         else if( aFilterName.equalsIgnoreAsciiCase( IMP_XBM ) )
1333         {
1334             nStatus = readXBM(rIStream, rGraphic);
1335         }
1336         else if( aFilterName.equalsIgnoreAsciiCase( IMP_XPM ) )
1337         {
1338             nStatus = readXPM(rIStream, rGraphic);
1339         }
1340         else if (aFilterName.equalsIgnoreAsciiCase(IMP_BMP))
1341         {
1342             nStatus = readBMP(rIStream, rGraphic, eLinkType);
1343         }
1344         else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVMETAFILE))
1345         {
1346             nStatus = readWithTypeSerializer(rIStream, rGraphic, eLinkType, aFilterName);
1347         }
1348         else if( aFilterName.equalsIgnoreAsciiCase(IMP_MOV))
1349         {
1350             nStatus = readWithTypeSerializer(rIStream, rGraphic, eLinkType, aFilterName);
1351         }
1352         else if (aFilterName.equalsIgnoreAsciiCase(IMP_WMF) || aFilterName.equalsIgnoreAsciiCase(IMP_WMZ))
1353         {
1354             nStatus = readWMF(rIStream, rGraphic, eLinkType);
1355         }
1356         else if (aFilterName.equalsIgnoreAsciiCase(IMP_EMF) || aFilterName.equalsIgnoreAsciiCase(IMP_EMZ))
1357         {
1358             nStatus = readEMF(rIStream, rGraphic, eLinkType);
1359         }
1360         else if (aFilterName.equalsIgnoreAsciiCase(IMP_PDF))
1361         {
1362             nStatus = readPDF(rIStream, rGraphic, eLinkType);
1363         }
1364         else if (aFilterName.equalsIgnoreAsciiCase(IMP_TIFF) )
1365         {
1366             nStatus = readTIFF(rIStream, rGraphic, eLinkType);
1367         }
1368         else if (aFilterName.equalsIgnoreAsciiCase(IMP_TGA) )
1369         {
1370             nStatus = readTGA(rIStream, rGraphic);
1371         }
1372         else if (aFilterName.equalsIgnoreAsciiCase(IMP_PICT))
1373         {
1374             nStatus = readPICT(rIStream, rGraphic, eLinkType);
1375         }
1376         else if (aFilterName.equalsIgnoreAsciiCase(IMP_MET))
1377         {
1378             nStatus = readMET(rIStream, rGraphic, eLinkType);
1379         }
1380         else if (aFilterName.equalsIgnoreAsciiCase(IMP_RAS))
1381         {
1382             nStatus = readRAS(rIStream, rGraphic);
1383         }
1384         else if (aFilterName.equalsIgnoreAsciiCase(IMP_PCX))
1385         {
1386             nStatus = readPCX(rIStream, rGraphic);
1387         }
1388         else if (aFilterName.equalsIgnoreAsciiCase(IMP_EPS))
1389         {
1390             nStatus = readEPS(rIStream, rGraphic);
1391         }
1392         else if (aFilterName.equalsIgnoreAsciiCase(IMP_PSD))
1393         {
1394             nStatus = readPSD(rIStream, rGraphic);
1395         }
1396         else if (aFilterName.equalsIgnoreAsciiCase(IMP_PCD))
1397         {
1398             nStatus = readPCD(rIStream, rGraphic);
1399         }
1400         else if (aFilterName.equalsIgnoreAsciiCase(IMP_PBM))
1401         {
1402             nStatus = readPBM(rIStream, rGraphic);
1403         }
1404         else if (aFilterName.equalsIgnoreAsciiCase(IMP_DXF))
1405         {
1406             nStatus = readDXF(rIStream, rGraphic);
1407         }
1408         else if (aFilterName.equalsIgnoreAsciiCase(IMP_WEBP))
1409         {
1410             nStatus = readWEBP(rIStream, rGraphic, eLinkType);
1411         }
1412         else
1413             nStatus = ERRCODE_GRFILTER_FILTERERROR;
1414     }
1415 
1416     if (nStatus == ERRCODE_NONE && eLinkType != GfxLinkType::NONE && !bLinkSet)
1417     {
1418         if (aGraphicContent.isEmpty())
1419         {
1420             const sal_uInt64 nStreamEnd = rIStream.Tell();
1421             const sal_uInt64 nGraphicContentSize = nStreamEnd - nStreamBegin;
1422 
1423             if (nGraphicContentSize > 0)
1424             {
1425                 try
1426                 {
1427                     rIStream.Seek(nStreamBegin);
1428                     aGraphicContent = BinaryDataContainer(rIStream, nGraphicContentSize);
1429                 }
1430                 catch (const std::bad_alloc&)
1431                 {
1432                     nStatus = ERRCODE_GRFILTER_TOOBIG;
1433                 }
1434             }
1435         }
1436         if( nStatus == ERRCODE_NONE )
1437         {
1438             rGraphic.SetGfxLink(std::make_shared<GfxLink>(aGraphicContent, eLinkType));
1439         }
1440     }
1441 
1442     // Set error code or try to set native buffer
1443     if( nStatus != ERRCODE_NONE )
1444     {
1445         ImplSetError( nStatus, &rIStream );
1446         rIStream.Seek( nStreamBegin );
1447         rGraphic.Clear();
1448     }
1449 
1450     return nStatus;
1451 }
1452 
ExportGraphic(const Graphic & rGraphic,const INetURLObject & rPath,sal_uInt16 nFormat,const css::uno::Sequence<css::beans::PropertyValue> * pFilterData)1453 ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const INetURLObject& rPath,
1454     sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
1455 {
1456     SAL_INFO( "vcl.filter", "GraphicFilter::ExportGraphic() (thb)" );
1457     ErrCode  nRetValue = ERRCODE_GRFILTER_FORMATERROR;
1458     SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::ExportGraphic() : ProtType == INetProtocol::NotValid" );
1459 
1460     OUString aMainUrl(rPath.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1461     bool bAlreadyExists = utl::UCBContentHelper::IsDocument(aMainUrl);
1462 
1463     std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::WRITE | StreamMode::TRUNC ));
1464     if (xStream)
1465     {
1466         nRetValue = ExportGraphic( rGraphic, aMainUrl, *xStream, nFormat, pFilterData );
1467         xStream.reset();
1468 
1469         if( ( ERRCODE_NONE != nRetValue ) && !bAlreadyExists )
1470             utl::UCBContentHelper::Kill(aMainUrl);
1471     }
1472     return nRetValue;
1473 }
1474 
ExportGraphic(const Graphic & rGraphic,std::u16string_view rPath,SvStream & rOStm,sal_uInt16 nFormat,const css::uno::Sequence<css::beans::PropertyValue> * pFilterData)1475 ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, std::u16string_view rPath,
1476     SvStream& rOStm, sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
1477 {
1478     SAL_INFO( "vcl.filter", "GraphicFilter::ExportGraphic() (thb)" );
1479     sal_uInt16 nFormatCount = GetExportFormatCount();
1480 
1481     ResetLastError();
1482     bool bShouldCompress = false;
1483     SvMemoryStream rCompressableStm;
1484 
1485     if( nFormat == GRFILTER_FORMAT_DONTKNOW )
1486     {
1487         OUString aExt = ImpGetExtension( rPath );
1488         for( sal_uInt16 i = 0; i < nFormatCount; i++ )
1489         {
1490             if ( pConfig->GetExportFormatExtension( i ).equalsIgnoreAsciiCase( aExt ) )
1491             {
1492                 nFormat=i;
1493                 break;
1494             }
1495         }
1496     }
1497     if( nFormat >= nFormatCount )
1498         return ImplSetError( ERRCODE_GRFILTER_FORMATERROR );
1499 
1500     FilterConfigItem aConfigItem( pFilterData );
1501     OUString aFilterName( pConfig->GetExportFilterName( nFormat ) );
1502     ErrCode     nStatus = ERRCODE_NONE;
1503     GraphicType eType;
1504     Graphic     aGraphic = ImpGetScaledGraphic( rGraphic, aConfigItem );
1505     eType = aGraphic.GetType();
1506 
1507     if( pConfig->IsExportPixelFormat( nFormat ) )
1508     {
1509         if( eType != GraphicType::Bitmap )
1510         {
1511             Size aSizePixel;
1512             sal_uLong nBitsPerPixel,nNeededMem,nMaxMem;
1513             ScopedVclPtrInstance< VirtualDevice > aVirDev;
1514 
1515             nMaxMem = 1024;
1516             nMaxMem *= 1024; // In Bytes
1517 
1518             // Calculate how big the image would normally be:
1519             aSizePixel=aVirDev->LogicToPixel(aGraphic.GetPrefSize(),aGraphic.GetPrefMapMode());
1520 
1521             // Calculate how much memory the image will take up
1522             nBitsPerPixel=aVirDev->GetBitCount();
1523             nNeededMem=(static_cast<sal_uLong>(aSizePixel.Width())*static_cast<sal_uLong>(aSizePixel.Height())*nBitsPerPixel+7)/8;
1524 
1525             // is the image larger than available memory?
1526             if (nMaxMem<nNeededMem)
1527             {
1528                 double fFak=sqrt(static_cast<double>(nMaxMem)/static_cast<double>(nNeededMem));
1529                 aSizePixel.setWidth(static_cast<sal_uLong>(static_cast<double>(aSizePixel.Width())*fFak) );
1530                 aSizePixel.setHeight(static_cast<sal_uLong>(static_cast<double>(aSizePixel.Height())*fFak) );
1531             }
1532 
1533             aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
1534             aVirDev->SetOutputSizePixel(aSizePixel);
1535             Graphic aGraphic2=aGraphic;
1536             aGraphic2.Draw(*aVirDev, Point(0, 0), aSizePixel); // this changes the MapMode
1537             aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
1538             aGraphic=Graphic(aVirDev->GetBitmapEx(Point(0,0),aSizePixel));
1539         }
1540     }
1541     if( rOStm.GetError() )
1542         nStatus = ERRCODE_GRFILTER_IOERROR;
1543     if( ERRCODE_NONE == nStatus )
1544     {
1545             if( aFilterName.equalsIgnoreAsciiCase( EXP_BMP ) )
1546             {
1547                 if (!BmpWriter(rOStm, aGraphic, &aConfigItem))
1548                     nStatus = ERRCODE_GRFILTER_FORMATERROR;
1549                 if (rOStm.GetError())
1550                     nStatus = ERRCODE_GRFILTER_IOERROR;
1551             }
1552             else if (aFilterName.equalsIgnoreAsciiCase(EXP_TIFF))
1553             {
1554                 if (!ExportTiffGraphicImport(rOStm, aGraphic, &aConfigItem))
1555                     nStatus = ERRCODE_GRFILTER_FORMATERROR;
1556 
1557                 if( rOStm.GetError() )
1558                     nStatus = ERRCODE_GRFILTER_IOERROR;
1559             }
1560             else if (aFilterName.equalsIgnoreAsciiCase(EXP_GIF))
1561             {
1562                 if (!ExportGifGraphic(rOStm, aGraphic, &aConfigItem))
1563                     nStatus = ERRCODE_GRFILTER_FORMATERROR;
1564 
1565                 if( rOStm.GetError() )
1566                     nStatus = ERRCODE_GRFILTER_IOERROR;
1567             }
1568             else if( aFilterName.equalsIgnoreAsciiCase( EXP_SVMETAFILE ) )
1569             {
1570                 sal_Int32 nVersion = aConfigItem.ReadInt32( u"Version"_ustr, 0 ) ;
1571                 if ( nVersion )
1572                     rOStm.SetVersion( nVersion );
1573 
1574                 // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
1575                 GDIMetaFile aMTF(aGraphic.GetGDIMetaFile());
1576 
1577                 SvmWriter aWriter( rOStm );
1578                 aWriter.Write( aMTF );
1579 
1580                 if( rOStm.GetError() )
1581                     nStatus = ERRCODE_GRFILTER_IOERROR;
1582             }
1583             else if ( aFilterName.equalsIgnoreAsciiCase( EXP_WMF ) || aFilterName.equalsIgnoreAsciiCase( EXP_WMZ ) )
1584             {
1585                 bool bDone(false);
1586                 SvStream* rTempStm = &rOStm;
1587                 if (aFilterName.equalsIgnoreAsciiCase(EXP_WMZ))
1588                 {
1589                     // Write to a different stream so that we can compress to rOStm later
1590                     rCompressableStm.SetBufferSize( rOStm.GetBufferSize() );
1591                     rTempStm = &rCompressableStm;
1592                     bShouldCompress = true;
1593                 }
1594                 // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
1595                 auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
1596 
1597                 bool bIsEMF = rGraphic.GetGfxLink().IsEMF();
1598 
1599                 // VectorGraphicDataType::Wmf means WMF or EMF, allow direct write in the WMF case
1600                 // only.
1601                 if (rVectorGraphicDataPtr
1602                     && rVectorGraphicDataPtr->getType() == VectorGraphicDataType::Wmf
1603                     && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty()
1604                     && !bIsEMF)
1605                 {
1606                     rVectorGraphicDataPtr->getBinaryDataContainer().writeToStream(*rTempStm);
1607                     if (rTempStm->GetError())
1608                     {
1609                         nStatus = ERRCODE_GRFILTER_IOERROR;
1610                     }
1611                     else
1612                     {
1613                         bDone = true;
1614                     }
1615                 }
1616 
1617                 if (!bDone)
1618                 {
1619                     // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
1620                     if (!ConvertGraphicToWMF(aGraphic, *rTempStm, &aConfigItem))
1621                         nStatus = ERRCODE_GRFILTER_FORMATERROR;
1622 
1623                     if (rTempStm->GetError())
1624                         nStatus = ERRCODE_GRFILTER_IOERROR;
1625                 }
1626             }
1627             else if ( aFilterName.equalsIgnoreAsciiCase( EXP_EMF ) || aFilterName.equalsIgnoreAsciiCase( EXP_EMZ ) )
1628             {
1629                 bool bDone(false);
1630                 SvStream* rTempStm = &rOStm;
1631                 if (aFilterName.equalsIgnoreAsciiCase(EXP_EMZ))
1632                 {
1633                     // Write to a different stream so that we can compress to rOStm later
1634                     rCompressableStm.SetBufferSize( rOStm.GetBufferSize() );
1635                     rTempStm = &rCompressableStm;
1636                     bShouldCompress = true;
1637                 }
1638                 // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
1639                 auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
1640 
1641                 if (rVectorGraphicDataPtr
1642                     && rVectorGraphicDataPtr->getType() == VectorGraphicDataType::Emf
1643                     && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty())
1644                 {
1645                     rVectorGraphicDataPtr->getBinaryDataContainer().writeToStream(*rTempStm);
1646                     if (rTempStm->GetError())
1647                     {
1648                         nStatus = ERRCODE_GRFILTER_IOERROR;
1649                     }
1650                     else
1651                     {
1652                         bDone = true;
1653                     }
1654                 }
1655 
1656                 if (!bDone)
1657                 {
1658                     // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
1659                     if (!ConvertGDIMetaFileToEMF(aGraphic.GetGDIMetaFile(), *rTempStm))
1660                         nStatus = ERRCODE_GRFILTER_FORMATERROR;
1661 
1662                     if (rTempStm->GetError())
1663                         nStatus = ERRCODE_GRFILTER_IOERROR;
1664                 }
1665             }
1666             else if( aFilterName.equalsIgnoreAsciiCase( EXP_JPEG ) )
1667             {
1668                 bool bExportedGrayJPEG = false;
1669                 if( !ExportJPEG( rOStm, aGraphic, pFilterData, &bExportedGrayJPEG ) )
1670                     nStatus = ERRCODE_GRFILTER_FORMATERROR;
1671 
1672                 if( rOStm.GetError() )
1673                     nStatus = ERRCODE_GRFILTER_IOERROR;
1674             }
1675             else if (aFilterName.equalsIgnoreAsciiCase(EXP_EPS))
1676             {
1677                 if (!ExportEpsGraphic(rOStm, aGraphic, &aConfigItem))
1678                     nStatus = ERRCODE_GRFILTER_FORMATERROR;
1679 
1680                 if (rOStm.GetError())
1681                     nStatus = ERRCODE_GRFILTER_IOERROR;
1682             }
1683             else if ( aFilterName.equalsIgnoreAsciiCase( EXP_PNG ) )
1684             {
1685                 auto aBitmapEx = aGraphic.GetBitmapEx();
1686                 vcl::PngImageWriter aPNGWriter( rOStm );
1687                 if ( pFilterData )
1688                     aPNGWriter.setParameters( *pFilterData );
1689                 aPNGWriter.write( aBitmapEx );
1690 
1691                 if( rOStm.GetError() )
1692                     nStatus = ERRCODE_GRFILTER_IOERROR;
1693             }
1694             else if ( aFilterName.equalsIgnoreAsciiCase( EXP_APNG ) )
1695             {
1696                 vcl::PngImageWriter aPNGWriter( rOStm );
1697                 if ( pFilterData )
1698                     aPNGWriter.setParameters( *pFilterData );
1699                 aPNGWriter.write( aGraphic );
1700 
1701                 if( rOStm.GetError() )
1702                     nStatus = ERRCODE_GRFILTER_IOERROR;
1703             }
1704             else if( aFilterName.equalsIgnoreAsciiCase( EXP_SVG ) || aFilterName.equalsIgnoreAsciiCase( EXP_SVGZ ) )
1705             {
1706                 bool bDone(false);
1707                 SvStream* rTempStm = &rOStm;
1708                 if (aFilterName.equalsIgnoreAsciiCase(EXP_SVGZ))
1709                 {
1710                     // Write to a different stream so that we can compress to rOStm later
1711                     rCompressableStm.SetBufferSize(rOStm.GetBufferSize());
1712                     rTempStm = &rCompressableStm;
1713                     bShouldCompress = true;
1714                 }
1715 
1716                 // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
1717                 auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
1718 
1719                 if (rVectorGraphicDataPtr
1720                     && rVectorGraphicDataPtr->getType()  ==  VectorGraphicDataType::Svg
1721                     && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty())
1722                 {
1723                     rVectorGraphicDataPtr->getBinaryDataContainer().writeToStream(*rTempStm);
1724                     if( rTempStm->GetError() )
1725                     {
1726                         nStatus = ERRCODE_GRFILTER_IOERROR;
1727                     }
1728                     else
1729                     {
1730                         bDone = true;
1731                     }
1732                 }
1733 
1734                 if( !bDone )
1735                 {
1736                     // do the normal GDIMetaFile export instead
1737                     try
1738                     {
1739                         css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1740 
1741                         css::uno::Reference< css::xml::sax::XDocumentHandler > xSaxWriter(
1742                             css::xml::sax::Writer::create( xContext ), css::uno::UNO_QUERY_THROW);
1743                         css::uno::Sequence< css::uno::Any > aArguments{ css::uno::Any(
1744                             aConfigItem.GetFilterData()) };
1745                         css::uno::Reference< css::svg::XSVGWriter > xSVGWriter(
1746                             xContext->getServiceManager()->createInstanceWithArgumentsAndContext( u"com.sun.star.svg.SVGWriter"_ustr, aArguments, xContext),
1747                                 css::uno::UNO_QUERY );
1748                         if( xSaxWriter.is() && xSVGWriter.is() )
1749                         {
1750                             css::uno::Reference< css::io::XActiveDataSource > xActiveDataSource(
1751                                 xSaxWriter, css::uno::UNO_QUERY );
1752 
1753                             if( xActiveDataSource.is() )
1754                             {
1755                                 const css::uno::Reference< css::uno::XInterface > xStmIf(
1756                                     getXWeak( new ImpFilterOutputStream( *rTempStm ) ) );
1757 
1758                                 SvMemoryStream aMemStm( 65535, 65535 );
1759 
1760                                 // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
1761                                 SvmWriter aWriter( aMemStm );
1762                                 aWriter.Write( aGraphic.GetGDIMetaFile() );
1763 
1764                                 xActiveDataSource->setOutputStream( css::uno::Reference< css::io::XOutputStream >(
1765                                     xStmIf, css::uno::UNO_QUERY ) );
1766                                 css::uno::Sequence< sal_Int8 > aMtfSeq( static_cast<sal_Int8 const *>(aMemStm.GetData()), aMemStm.Tell() );
1767                                 xSVGWriter->write( xSaxWriter, aMtfSeq );
1768                             }
1769                         }
1770                     }
1771                     catch(const css::uno::Exception&)
1772                     {
1773                         nStatus = ERRCODE_GRFILTER_IOERROR;
1774                     }
1775                 }
1776             }
1777             else if (aFilterName.equalsIgnoreAsciiCase(EXP_WEBP))
1778             {
1779                 if (!ExportWebpGraphic(rOStm, aGraphic, &aConfigItem))
1780                     nStatus = ERRCODE_GRFILTER_FORMATERROR;
1781 
1782                 if( rOStm.GetError() )
1783                     nStatus = ERRCODE_GRFILTER_IOERROR;
1784             }
1785             else
1786                 nStatus = ERRCODE_GRFILTER_FILTERERROR;
1787     }
1788     if( nStatus != ERRCODE_NONE )
1789     {
1790         ImplSetError( nStatus, &rOStm );
1791     }
1792     else if ( bShouldCompress )
1793     {
1794         sal_uInt32 nUncompressedCRC32
1795             = rtl_crc32( 0, rCompressableStm.GetData(), rCompressableStm.GetSize() );
1796         ZCodec aCodec;
1797         rCompressableStm.Seek( 0 );
1798         aCodec.BeginCompression( ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true );
1799         // the inner modify time/filename doesn't really matter in this context because
1800         // compressed graphic formats are meant to be opened as is - not to be extracted
1801         aCodec.SetCompressionMetadata( "inner"_ostr, 0, nUncompressedCRC32 );
1802         aCodec.Compress( rCompressableStm, rOStm );
1803         tools::Long nCompressedLength = aCodec.EndCompression();
1804         if ( rOStm.GetError() || nCompressedLength <= 0 )
1805             nStatus = ERRCODE_GRFILTER_IOERROR;
1806     }
1807     return nStatus;
1808 }
1809 
1810 
ResetLastError()1811 void GraphicFilter::ResetLastError()
1812 {
1813     mxErrorEx = ERRCODE_NONE;
1814 }
1815 
GetFilterCallback() const1816 Link<ConvertData&,bool> GraphicFilter::GetFilterCallback() const
1817 {
1818     Link<ConvertData&,bool> aLink( LINK( const_cast<GraphicFilter*>(this), GraphicFilter, FilterCallback ) );
1819     return aLink;
1820 }
1821 
IMPL_LINK(GraphicFilter,FilterCallback,ConvertData &,rData,bool)1822 IMPL_LINK( GraphicFilter, FilterCallback, ConvertData&, rData, bool )
1823 {
1824     bool bRet = false;
1825 
1826     sal_uInt16      nFormat = GRFILTER_FORMAT_DONTKNOW;
1827     OUString aShortName;
1828     css::uno::Sequence< css::beans::PropertyValue > aFilterData;
1829     switch( rData.mnFormat )
1830     {
1831         case ConvertDataFormat::BMP: aShortName = BMP_SHORTNAME; break;
1832         case ConvertDataFormat::GIF: aShortName = GIF_SHORTNAME; break;
1833         case ConvertDataFormat::JPG: aShortName = JPG_SHORTNAME; break;
1834         case ConvertDataFormat::MET: aShortName = MET_SHORTNAME; break;
1835         case ConvertDataFormat::PCT: aShortName = PCT_SHORTNAME; break;
1836         case ConvertDataFormat::PNG: aShortName = PNG_SHORTNAME; break;
1837         case ConvertDataFormat::SVM: aShortName = SVM_SHORTNAME; break;
1838         case ConvertDataFormat::TIF: aShortName = TIF_SHORTNAME; break;
1839         case ConvertDataFormat::WMF: aShortName = WMF_SHORTNAME; break;
1840         case ConvertDataFormat::EMF: aShortName = EMF_SHORTNAME; break;
1841         case ConvertDataFormat::SVG: aShortName = SVG_SHORTNAME; break;
1842         case ConvertDataFormat::WEBP: aShortName = WEBP_SHORTNAME; break;
1843 
1844         default:
1845         break;
1846     }
1847     if (GraphicType::NONE == rData.maGraphic.GetType()) // Import
1848     {
1849         // Import
1850         nFormat = GetImportFormatNumberForShortName( aShortName );
1851         bRet = ImportGraphic( rData.maGraphic, u"", rData.mrStm, nFormat ) == ERRCODE_NONE;
1852     }
1853     else if( !aShortName.isEmpty() )
1854     {
1855         // Export
1856 #if defined(IOS) || defined(ANDROID)
1857         if (aShortName == PNG_SHORTNAME)
1858         {
1859             aFilterData.realloc(aFilterData.getLength() + 1);
1860             auto pFilterData = aFilterData.getArray();
1861             pFilterData[aFilterData.getLength() - 1].Name = "Compression";
1862             // We "know" that this gets passed to zlib's deflateInit2_(). 1 means best speed.
1863             pFilterData[aFilterData.getLength() - 1].Value <<= static_cast<sal_Int32>(1);
1864         }
1865 #endif
1866         nFormat = GetExportFormatNumberForShortName( aShortName );
1867         bRet = ExportGraphic( rData.maGraphic, u"", rData.mrStm, nFormat, &aFilterData ) == ERRCODE_NONE;
1868     }
1869 
1870     return bRet;
1871 }
1872 
1873 namespace
1874 {
1875     class StandardGraphicFilter
1876     {
1877     public:
StandardGraphicFilter()1878         StandardGraphicFilter()
1879         {
1880             m_aFilter.GetImportFormatCount();
1881         }
1882         GraphicFilter m_aFilter;
1883     };
1884 }
1885 
GetGraphicFilter()1886 GraphicFilter& GraphicFilter::GetGraphicFilter()
1887 {
1888     static StandardGraphicFilter gStandardFilter;
1889     return gStandardFilter.m_aFilter;
1890 }
1891 
LoadGraphic(const OUString & rPath,const OUString & rFilterName,Graphic & rGraphic,GraphicFilter * pFilter,sal_uInt16 * pDeterminedFormat)1892 ErrCode GraphicFilter::LoadGraphic( const OUString &rPath, const OUString &rFilterName,
1893                  Graphic& rGraphic, GraphicFilter* pFilter,
1894                  sal_uInt16* pDeterminedFormat )
1895 {
1896     if ( !pFilter )
1897         pFilter = &GetGraphicFilter();
1898 
1899     const sal_uInt16 nFilter = !rFilterName.isEmpty() && pFilter->GetImportFormatCount()
1900                     ? pFilter->GetImportFormatNumber( rFilterName )
1901                     : GRFILTER_FORMAT_DONTKNOW;
1902 
1903     INetURLObject aURL( rPath );
1904     if ( aURL.HasError() )
1905     {
1906         aURL.SetSmartProtocol( INetProtocol::File );
1907         aURL.SetSmartURL( rPath );
1908     }
1909 
1910     std::unique_ptr<SvStream> pStream;
1911     if ( INetProtocol::File != aURL.GetProtocol() )
1912         pStream = ::utl::UcbStreamHelper::CreateStream( rPath, StreamMode::READ );
1913 
1914     ErrCode nRes = ERRCODE_NONE;
1915     if ( !pStream )
1916         nRes = pFilter->ImportGraphic( rGraphic, aURL, nFilter, pDeterminedFormat );
1917     else
1918         nRes = pFilter->ImportGraphic( rGraphic, rPath, *pStream, nFilter, pDeterminedFormat );
1919 
1920 #ifdef DBG_UTIL
1921     OUString aReturnString;
1922 
1923     if (nRes == ERRCODE_GRFILTER_OPENERROR)
1924             aReturnString="open error";
1925     else if (nRes == ERRCODE_GRFILTER_IOERROR)
1926             aReturnString="IO error";
1927     else if (nRes == ERRCODE_GRFILTER_FORMATERROR)
1928             aReturnString="format error";
1929     else if (nRes == ERRCODE_GRFILTER_VERSIONERROR)
1930             aReturnString="version error";
1931     else if (nRes == ERRCODE_GRFILTER_FILTERERROR)
1932             aReturnString="filter error";
1933     else if (nRes == ERRCODE_GRFILTER_TOOBIG)
1934             aReturnString="graphic is too big";
1935 
1936     SAL_INFO_IF( nRes, "vcl.filter", "Problem importing graphic " << rPath << ". Reason: " << aReturnString );
1937 #endif
1938 
1939     return nRes;
1940 }
1941 
compressAsPNG(const Graphic & rGraphic,SvStream & rOutputStream)1942 ErrCode GraphicFilter::compressAsPNG(const Graphic& rGraphic, SvStream& rOutputStream)
1943 {
1944     css::uno::Sequence< css::beans::PropertyValue > aFilterData{ comphelper::makePropertyValue(
1945         u"Compression"_ustr, sal_uInt32(9)) };
1946 
1947     sal_uInt16 nFilterFormat = GetExportFormatNumberForShortName(u"PNG");
1948     return ExportGraphic(rGraphic, u"", rOutputStream, nFilterFormat, &aFilterData);
1949 }
1950 
1951 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1952