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