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