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