summaryrefslogtreecommitdiffstats
path: root/vcl/source/filter/graphicfilter.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/filter/graphicfilter.cxx')
-rw-r--r--vcl/source/filter/graphicfilter.cxx2056
1 files changed, 2056 insertions, 0 deletions
diff --git a/vcl/source/filter/graphicfilter.cxx b/vcl/source/filter/graphicfilter.cxx
new file mode 100644
index 000000000..d208c7d81
--- /dev/null
+++ b/vcl/source/filter/graphicfilter.cxx
@@ -0,0 +1,2056 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+
+#include <sal/log.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/threadpool.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <tools/fract.hxx>
+#include <unotools/configmgr.hxx>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/zcodec.hxx>
+#include <fltcall.hxx>
+#include <vcl/salctype.hxx>
+#include <vcl/filter/PngImageReader.hxx>
+#include <vcl/filter/SvmWriter.hxx>
+#include <vcl/pngwrite.hxx>
+#include <vcl/vectorgraphicdata.hxx>
+#include <vcl/virdev.hxx>
+#include <impgraph.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/file.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <vcl/wmf.hxx>
+#include "igif/gifread.hxx"
+#include <vcl/pdfread.hxx>
+#include "jpeg/jpeg.hxx"
+#include "png/png.hxx"
+#include "ixbm/xbmread.hxx"
+#include <filter/XpmReader.hxx>
+#include <filter/TiffReader.hxx>
+#include <filter/TiffWriter.hxx>
+#include <filter/TgaReader.hxx>
+#include <filter/PictReader.hxx>
+#include <filter/MetReader.hxx>
+#include <filter/RasReader.hxx>
+#include <filter/PcxReader.hxx>
+#include <filter/EpsReader.hxx>
+#include <filter/EpsWriter.hxx>
+#include <filter/PsdReader.hxx>
+#include <filter/PcdReader.hxx>
+#include <filter/PbmReader.hxx>
+#include <filter/DxfReader.hxx>
+#include <filter/GifWriter.hxx>
+#include <filter/BmpReader.hxx>
+#include <filter/BmpWriter.hxx>
+#include <filter/WebpReader.hxx>
+#include <filter/WebpWriter.hxx>
+#include <osl/module.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/io/XActiveDataSource.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/svg/XSVGWriter.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <unotools/ucbstreamhelper.hxx>
+#include <rtl/bootstrap.hxx>
+#include <tools/svlibrary.h>
+#include <comphelper/string.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <vector>
+#include <memory>
+#include <mutex>
+#include <string_view>
+#include <o3tl/string_view.hxx>
+#include <vcl/TypeSerializer.hxx>
+
+#include "FilterConfigCache.hxx"
+#include "graphicfilter_internal.hxx"
+
+#include <graphic/GraphicFormatDetector.hxx>
+#include <graphic/GraphicReader.hxx>
+
+// Support for GfxLinkType::NativeWebp is so far disabled,
+// as enabling it would write .webp images e.g. to .odt documents,
+// making those images unreadable for older readers. So for now
+// disable the support so that .webp images will be written out as .png,
+// and somewhen later enable the support unconditionally.
+static bool supportNativeWebp()
+{
+ const char* const testname = getenv("LO_TESTNAME");
+ if(testname == nullptr)
+ return false;
+ // Enable support only for those unittests that test it.
+ if( std::string_view("_anonymous_namespace___GraphicTest__testUnloadedGraphicLoading_") == testname
+ || std::string_view("VclFiltersTest__testExportImport_") == testname
+ || o3tl::starts_with(std::string_view(testname), "WebpFilterTest__"))
+ return true;
+ return false;
+}
+
+static std::vector< GraphicFilter* > gaFilterHdlList;
+
+static std::mutex& getListMutex()
+{
+ static std::mutex s_aListProtection;
+ return s_aListProtection;
+}
+
+namespace {
+
+class ImpFilterOutputStream : public ::cppu::WeakImplHelper< css::io::XOutputStream >
+{
+ SvStream& mrStm;
+
+ virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& rData ) override
+ { mrStm.WriteBytes(rData.getConstArray(), rData.getLength()); }
+ virtual void SAL_CALL flush() override
+ { mrStm.FlushBuffer(); }
+ virtual void SAL_CALL closeOutput() override {}
+
+public:
+
+ explicit ImpFilterOutputStream( SvStream& rStm ) : mrStm( rStm ) {}
+};
+
+}
+
+// Helper functions
+
+sal_uInt8* ImplSearchEntry( sal_uInt8* pSource, sal_uInt8 const * pDest, sal_uLong nComp, sal_uLong nSize )
+{
+ while ( nComp-- >= nSize )
+ {
+ sal_uLong i;
+ for ( i = 0; i < nSize; i++ )
+ {
+ if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
+ break;
+ }
+ if ( i == nSize )
+ return pSource;
+ pSource++;
+ }
+ return nullptr;
+}
+
+static OUString ImpGetExtension( std::u16string_view rPath )
+{
+ OUString aExt;
+ INetURLObject aURL( rPath );
+ aExt = aURL.GetFileExtension().toAsciiUpperCase();
+ return aExt;
+}
+
+bool isPCT(SvStream& rStream, sal_uLong nStreamPos, sal_uLong nStreamLen)
+{
+ sal_uInt8 sBuf[3];
+ // store number format
+ SvStreamEndian oldNumberFormat = rStream.GetEndian();
+ sal_uInt32 nOffset; // in MS documents the pict format is used without the first 512 bytes
+ for ( nOffset = 0; ( nOffset <= 512 ) && ( ( nStreamPos + nOffset + 14 ) <= nStreamLen ); nOffset += 512 )
+ {
+ short y1,x1,y2,x2;
+ bool bdBoxOk = true;
+
+ rStream.Seek( nStreamPos + nOffset);
+ // size of the pict in version 1 pict ( 2bytes) : ignored
+ rStream.SeekRel(2);
+ // bounding box (bytes 2 -> 9)
+ rStream.SetEndian(SvStreamEndian::BIG);
+ rStream.ReadInt16( y1 ).ReadInt16( x1 ).ReadInt16( y2 ).ReadInt16( x2 );
+ rStream.SetEndian(oldNumberFormat); // reset format
+
+ if (x1 > x2 || y1 > y2 || // bad bdbox
+ (x1 == x2 && y1 == y2) || // 1 pixel picture
+ x2-x1 > 2048 || y2-y1 > 2048 ) // picture abnormally big
+ bdBoxOk = false;
+
+ // read version op
+ rStream.ReadBytes(sBuf, 3);
+ // see http://developer.apple.com/legacy/mac/library/documentation/mac/pdf/Imaging_With_QuickDraw/Appendix_A.pdf
+ // normal version 2 - page A23 and A24
+ if ( sBuf[ 0 ] == 0x00 && sBuf[ 1 ] == 0x11 && sBuf[ 2 ] == 0x02)
+ return true;
+ // normal version 1 - page A25
+ else if (sBuf[ 0 ] == 0x11 && sBuf[ 1 ] == 0x01 && bdBoxOk)
+ return true;
+ }
+ return false;
+}
+
+ErrCode GraphicFilter::ImpTestOrFindFormat( std::u16string_view rPath, SvStream& rStream, sal_uInt16& rFormat )
+{
+ // determine or check the filter/format by reading into it
+ if( rFormat == GRFILTER_FORMAT_DONTKNOW )
+ {
+ OUString aFormatExt;
+ if (vcl::peekGraphicFormat(rStream, aFormatExt, false))
+ {
+ rFormat = pConfig->GetImportFormatNumberForExtension( aFormatExt );
+ if( rFormat != GRFILTER_FORMAT_DONTKNOW )
+ return ERRCODE_NONE;
+ }
+ // determine filter by file extension
+ if( !rPath.empty() )
+ {
+ OUString aExt( ImpGetExtension( rPath ) );
+ rFormat = pConfig->GetImportFormatNumberForExtension( aExt );
+ if( rFormat != GRFILTER_FORMAT_DONTKNOW )
+ return ERRCODE_NONE;
+ }
+ return ERRCODE_GRFILTER_FORMATERROR;
+ }
+ else
+ {
+ OUString aTmpStr( pConfig->GetImportFormatExtension( rFormat ) );
+ aTmpStr = aTmpStr.toAsciiUpperCase();
+ if (!vcl::peekGraphicFormat(rStream, aTmpStr, true))
+ return ERRCODE_GRFILTER_FORMATERROR;
+ if ( pConfig->GetImportFormatExtension( rFormat ).equalsIgnoreAsciiCase( "pcd" ) )
+ {
+ sal_Int32 nBase = 2; // default Base0
+ if ( pConfig->GetImportFilterType( rFormat ).equalsIgnoreAsciiCase( "pcd_Photo_CD_Base4" ) )
+ nBase = 1;
+ else if ( pConfig->GetImportFilterType( rFormat ).equalsIgnoreAsciiCase( "pcd_Photo_CD_Base16" ) )
+ nBase = 0;
+ FilterConfigItem aFilterConfigItem( u"Office.Common/Filter/Graphic/Import/PCD" );
+ aFilterConfigItem.WriteInt32( "Resolution", nBase );
+ }
+ }
+
+ return ERRCODE_NONE;
+}
+
+static Graphic ImpGetScaledGraphic( const Graphic& rGraphic, FilterConfigItem& rConfigItem )
+{
+ Graphic aGraphic;
+
+ sal_Int32 nLogicalWidth = rConfigItem.ReadInt32( "LogicalWidth", 0 );
+ sal_Int32 nLogicalHeight = rConfigItem.ReadInt32( "LogicalHeight", 0 );
+
+ if ( rGraphic.GetType() != GraphicType::NONE )
+ {
+ sal_Int32 nMode = rConfigItem.ReadInt32( "ExportMode", -1 );
+
+ if ( nMode == -1 ) // the property is not there, this is possible, if the graphic filter
+ { // is called via UnoGraphicExporter and not from a graphic export Dialog
+ nMode = 0; // then we are defaulting this mode to 0
+ if ( nLogicalWidth || nLogicalHeight )
+ nMode = 2;
+ }
+
+ Size aOriginalSize;
+ Size aPrefSize( rGraphic.GetPrefSize() );
+ MapMode aPrefMapMode( rGraphic.GetPrefMapMode() );
+ if (aPrefMapMode.GetMapUnit() == MapUnit::MapPixel)
+ aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM));
+ else
+ aOriginalSize = OutputDevice::LogicToLogic(aPrefSize, aPrefMapMode, MapMode(MapUnit::Map100thMM));
+ if ( !nLogicalWidth )
+ nLogicalWidth = aOriginalSize.Width();
+ if ( !nLogicalHeight )
+ nLogicalHeight = aOriginalSize.Height();
+ if( rGraphic.GetType() == GraphicType::Bitmap )
+ {
+
+ // Resolution is set
+ if( nMode == 1 )
+ {
+ BitmapEx aBitmap( rGraphic.GetBitmapEx() );
+ MapMode aMap( MapUnit::Map100thInch );
+
+ sal_Int32 nDPI = rConfigItem.ReadInt32( "Resolution", 75 );
+ Fraction aFrac( 1, std::clamp( nDPI, sal_Int32(75), sal_Int32(600) ) );
+
+ aMap.SetScaleX( aFrac );
+ aMap.SetScaleY( aFrac );
+
+ Size aOldSize = aBitmap.GetSizePixel();
+ aGraphic = rGraphic;
+ aGraphic.SetPrefMapMode( aMap );
+ aGraphic.SetPrefSize( Size( aOldSize.Width() * 100,
+ aOldSize.Height() * 100 ) );
+ }
+ // Size is set
+ else if( nMode == 2 )
+ {
+ aGraphic = rGraphic;
+ aGraphic.SetPrefMapMode( MapMode( MapUnit::Map100thMM ) );
+ aGraphic.SetPrefSize( Size( nLogicalWidth, nLogicalHeight ) );
+ }
+ else
+ aGraphic = rGraphic;
+
+ sal_Int32 nColors = rConfigItem.ReadInt32( "Color", 0 );
+ if ( nColors ) // graphic conversion necessary ?
+ {
+ BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
+ aBmpEx.Convert( static_cast<BmpConversion>(nColors) ); // the entries in the xml section have the same meaning as
+ aGraphic = aBmpEx; // they have in the BmpConversion enum, so it should be
+ } // allowed to cast them
+ }
+ else
+ {
+ if( ( nMode == 1 ) || ( nMode == 2 ) )
+ {
+ GDIMetaFile aMtf( rGraphic.GetGDIMetaFile() );
+ Size aNewSize( OutputDevice::LogicToLogic(Size(nLogicalWidth, nLogicalHeight), MapMode(MapUnit::Map100thMM), aMtf.GetPrefMapMode()) );
+
+ if( aNewSize.Width() && aNewSize.Height() )
+ {
+ const Size aPreferredSize( aMtf.GetPrefSize() );
+ aMtf.Scale( Fraction( aNewSize.Width(), aPreferredSize.Width() ),
+ Fraction( aNewSize.Height(), aPreferredSize.Height() ) );
+ }
+ aGraphic = Graphic( aMtf );
+ }
+ else
+ aGraphic = rGraphic;
+ }
+
+ }
+ else
+ aGraphic = rGraphic;
+
+ return aGraphic;
+}
+
+GraphicFilter::GraphicFilter( bool bConfig )
+ : bUseConfig(bConfig)
+{
+ ImplInit();
+}
+
+GraphicFilter::~GraphicFilter()
+{
+ {
+ std::scoped_lock aGuard( getListMutex() );
+ auto it = std::find(gaFilterHdlList.begin(), gaFilterHdlList.end(), this);
+ if( it != gaFilterHdlList.end() )
+ gaFilterHdlList.erase( it );
+
+ if( gaFilterHdlList.empty() )
+ delete pConfig;
+ }
+
+ mxErrorEx.reset();
+}
+
+void GraphicFilter::ImplInit()
+{
+ {
+ std::scoped_lock aGuard( getListMutex() );
+
+ if ( gaFilterHdlList.empty() )
+ pConfig = new FilterConfigCache( bUseConfig );
+ else
+ pConfig = gaFilterHdlList.front()->pConfig;
+
+ gaFilterHdlList.push_back( this );
+ }
+
+ if( bUseConfig )
+ {
+ OUString url("$BRAND_BASE_DIR/" LIBO_LIB_FOLDER);
+ rtl::Bootstrap::expandMacros(url); //TODO: detect failure
+ osl::FileBase::getSystemPathFromFileURL(url, aFilterPath);
+ }
+
+ mxErrorEx = ERRCODE_NONE;
+}
+
+ErrCode GraphicFilter::ImplSetError( ErrCode nError, const SvStream* pStm )
+{
+ mxErrorEx = pStm ? pStm->GetError() : ERRCODE_NONE;
+ return nError;
+}
+
+sal_uInt16 GraphicFilter::GetImportFormatCount() const
+{
+ return pConfig->GetImportFormatCount();
+}
+
+sal_uInt16 GraphicFilter::GetImportFormatNumber( std::u16string_view rFormatName )
+{
+ return pConfig->GetImportFormatNumber( rFormatName );
+}
+
+sal_uInt16 GraphicFilter::GetImportFormatNumberForShortName( std::u16string_view rShortName )
+{
+ return pConfig->GetImportFormatNumberForShortName( rShortName );
+}
+
+sal_uInt16 GraphicFilter::GetImportFormatNumberForTypeName( std::u16string_view rType )
+{
+ return pConfig->GetImportFormatNumberForTypeName( rType );
+}
+
+OUString GraphicFilter::GetImportFormatName( sal_uInt16 nFormat )
+{
+ return pConfig->GetImportFormatName( nFormat );
+}
+
+OUString GraphicFilter::GetImportFormatTypeName( sal_uInt16 nFormat )
+{
+ return pConfig->GetImportFilterTypeName( nFormat );
+}
+
+#ifdef _WIN32
+OUString GraphicFilter::GetImportFormatMediaType( sal_uInt16 nFormat )
+{
+ return pConfig->GetImportFormatMediaType( nFormat );
+}
+#endif
+
+OUString GraphicFilter::GetImportFormatShortName( sal_uInt16 nFormat )
+{
+ return pConfig->GetImportFormatShortName( nFormat );
+}
+
+OUString GraphicFilter::GetImportWildcard( sal_uInt16 nFormat, sal_Int32 nEntry )
+{
+ return pConfig->GetImportWildcard( nFormat, nEntry );
+}
+
+sal_uInt16 GraphicFilter::GetExportFormatCount() const
+{
+ return pConfig->GetExportFormatCount();
+}
+
+sal_uInt16 GraphicFilter::GetExportFormatNumber( std::u16string_view rFormatName )
+{
+ return pConfig->GetExportFormatNumber( rFormatName );
+}
+
+sal_uInt16 GraphicFilter::GetExportFormatNumberForMediaType( std::u16string_view rMediaType )
+{
+ return pConfig->GetExportFormatNumberForMediaType( rMediaType );
+}
+
+sal_uInt16 GraphicFilter::GetExportFormatNumberForShortName( std::u16string_view rShortName )
+{
+ return pConfig->GetExportFormatNumberForShortName( rShortName );
+}
+
+OUString GraphicFilter::GetExportInternalFilterName( sal_uInt16 nFormat )
+{
+ return pConfig->GetExportInternalFilterName( nFormat );
+}
+
+sal_uInt16 GraphicFilter::GetExportFormatNumberForTypeName( std::u16string_view rType )
+{
+ return pConfig->GetExportFormatNumberForTypeName( rType );
+}
+
+OUString GraphicFilter::GetExportFormatName( sal_uInt16 nFormat )
+{
+ return pConfig->GetExportFormatName( nFormat );
+}
+
+OUString GraphicFilter::GetExportFormatMediaType( sal_uInt16 nFormat )
+{
+ return pConfig->GetExportFormatMediaType( nFormat );
+}
+
+OUString GraphicFilter::GetExportFormatShortName( sal_uInt16 nFormat )
+{
+ return pConfig->GetExportFormatShortName( nFormat );
+}
+
+OUString GraphicFilter::GetExportWildcard( sal_uInt16 nFormat )
+{
+ return pConfig->GetExportWildcard( nFormat, 0 );
+}
+
+bool GraphicFilter::IsExportPixelFormat( sal_uInt16 nFormat )
+{
+ return pConfig->IsExportPixelFormat( nFormat );
+}
+
+ErrCode GraphicFilter::CanImportGraphic( const INetURLObject& rPath,
+ sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat )
+{
+ ErrCode nRetValue = ERRCODE_GRFILTER_FORMATERROR;
+ SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::CanImportGraphic() : ProtType == INetProtocol::NotValid" );
+
+ OUString aMainUrl( rPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::READ | StreamMode::SHARE_DENYNONE ));
+ if (xStream)
+ {
+ nRetValue = CanImportGraphic( aMainUrl, *xStream, nFormat, pDeterminedFormat );
+ }
+ return nRetValue;
+}
+
+ErrCode GraphicFilter::CanImportGraphic( std::u16string_view rMainUrl, SvStream& rIStream,
+ sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat )
+{
+ sal_uInt64 nStreamPos = rIStream.Tell();
+ ErrCode nRes = ImpTestOrFindFormat( rMainUrl, rIStream, nFormat );
+
+ rIStream.Seek(nStreamPos);
+
+ if( nRes==ERRCODE_NONE && pDeterminedFormat!=nullptr )
+ *pDeterminedFormat = nFormat;
+
+ return ImplSetError( nRes, &rIStream );
+}
+
+//SJ: TODO, we need to create a GraphicImporter component
+ErrCode GraphicFilter::ImportGraphic( Graphic& rGraphic, const INetURLObject& rPath,
+ sal_uInt16 nFormat, sal_uInt16 * pDeterminedFormat, GraphicFilterImportFlags nImportFlags )
+{
+ ErrCode nRetValue = ERRCODE_GRFILTER_FORMATERROR;
+ SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::ImportGraphic() : ProtType == INetProtocol::NotValid" );
+
+ OUString aMainUrl( rPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::READ | StreamMode::SHARE_DENYNONE ));
+ if (xStream)
+ {
+ nRetValue = ImportGraphic( rGraphic, aMainUrl, *xStream, nFormat, pDeterminedFormat, nImportFlags );
+ }
+ return nRetValue;
+}
+
+namespace {
+
+/// Contains a stream and other associated data to import pixels into a
+/// Graphic.
+struct GraphicImportContext
+{
+ /// Pixel data is read from this stream.
+ std::unique_ptr<SvStream> m_pStream;
+ /// The Graphic the import filter gets.
+ std::shared_ptr<Graphic> m_pGraphic;
+ /// Write pixel data using this access.
+ std::unique_ptr<BitmapScopedWriteAccess> m_pAccess;
+ std::unique_ptr<AlphaScopedWriteAccess> m_pAlphaAccess;
+ // Need to have an AlphaMask instance to keep its lifetime.
+ AlphaMask mAlphaMask;
+ /// Signals if import finished correctly.
+ ErrCode m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ /// Original graphic format.
+ GfxLinkType m_eLinkType = GfxLinkType::NONE;
+ /// Position of the stream before reading the data.
+ sal_uInt64 m_nStreamBegin = 0;
+ /// Flags for the import filter.
+ GraphicFilterImportFlags m_nImportFlags = GraphicFilterImportFlags::NONE;
+};
+
+/// Graphic import worker that gets executed on a thread.
+class GraphicImportTask : public comphelper::ThreadTask
+{
+ GraphicImportContext& m_rContext;
+public:
+ GraphicImportTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, GraphicImportContext& rContext);
+ void doWork() override;
+ /// Shared code between threaded and non-threaded version.
+ static void doImport(GraphicImportContext& rContext);
+};
+
+}
+
+GraphicImportTask::GraphicImportTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, GraphicImportContext& rContext)
+ : comphelper::ThreadTask(pTag),
+ m_rContext(rContext)
+{
+}
+
+void GraphicImportTask::doWork()
+{
+ GraphicImportTask::doImport(m_rContext);
+}
+
+void GraphicImportTask::doImport(GraphicImportContext& rContext)
+{
+ if(rContext.m_eLinkType == GfxLinkType::NativeJpg)
+ {
+ if (!ImportJPEG(*rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, rContext.m_pAccess.get()))
+ rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ else if(rContext.m_eLinkType == GfxLinkType::NativePng)
+ {
+ if (!vcl::ImportPNG(*rContext.m_pStream, *rContext.m_pGraphic,
+ rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap,
+ rContext.m_pAccess.get(), rContext.m_pAlphaAccess.get()))
+ {
+ rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ }
+}
+
+void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGraphics, std::vector< std::unique_ptr<SvStream> > vStreams)
+{
+ static bool bThreads = !getenv("VCL_NO_THREAD_IMPORT");
+ std::vector<GraphicImportContext> aContexts;
+ aContexts.reserve(vStreams.size());
+ comphelper::ThreadPool& rSharedPool = comphelper::ThreadPool::getSharedOptimalPool();
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+
+ for (auto& pStream : vStreams)
+ {
+ aContexts.emplace_back();
+ GraphicImportContext& rContext = aContexts.back();
+
+ if (pStream)
+ {
+ rContext.m_pStream = std::move(pStream);
+ rContext.m_pGraphic = std::make_shared<Graphic>();
+ rContext.m_nStatus = ERRCODE_NONE;
+
+ // Detect the format.
+ ResetLastError();
+ rContext.m_nStreamBegin = rContext.m_pStream->Tell();
+ sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
+ rContext.m_nStatus = ImpTestOrFindFormat(u"", *rContext.m_pStream, nFormat);
+ rContext.m_pStream->Seek(rContext.m_nStreamBegin);
+
+ // Import the graphic.
+ if (rContext.m_nStatus == ERRCODE_NONE && !rContext.m_pStream->GetError())
+ {
+ OUString aFilterName = pConfig->GetImportFilterName(nFormat);
+
+ if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
+ {
+ rContext.m_eLinkType = GfxLinkType::NativeJpg;
+ rContext.m_nImportFlags = GraphicFilterImportFlags::SetLogsizeForJpeg;
+
+ if (ImportJPEG( *rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr))
+ {
+ Bitmap& rBitmap = const_cast<Bitmap&>(rContext.m_pGraphic->GetBitmapExRef().GetBitmap());
+ rContext.m_pAccess = std::make_unique<BitmapScopedWriteAccess>(rBitmap);
+ rContext.m_pStream->Seek(rContext.m_nStreamBegin);
+ if (bThreads)
+ rSharedPool.pushTask(std::make_unique<GraphicImportTask>(pTag, rContext));
+ else
+ GraphicImportTask::doImport(rContext);
+ }
+ else
+ rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
+ {
+ rContext.m_eLinkType = GfxLinkType::NativePng;
+
+ if (vcl::ImportPNG( *rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr, nullptr))
+ {
+ const BitmapEx& rBitmapEx = rContext.m_pGraphic->GetBitmapExRef();
+ Bitmap& rBitmap = const_cast<Bitmap&>(rBitmapEx.GetBitmap());
+ rContext.m_pAccess = std::make_unique<BitmapScopedWriteAccess>(rBitmap);
+ if(rBitmapEx.IsAlpha())
+ {
+ // The separate alpha bitmap causes a number of complications. Not only
+ // we need to have an extra bitmap access for it, but we also need
+ // to keep an AlphaMask instance in the context. This is because
+ // BitmapEx internally keeps Bitmap and not AlphaMask (because the Bitmap
+ // may be also a mask, not alpha). So BitmapEx::GetAlpha() returns
+ // a temporary, and direct access to the Bitmap wouldn't work
+ // with AlphaScopedBitmapAccess. *sigh*
+ rContext.mAlphaMask = rBitmapEx.GetAlpha();
+ rContext.m_pAlphaAccess = std::make_unique<AlphaScopedWriteAccess>(rContext.mAlphaMask);
+ }
+ rContext.m_pStream->Seek(rContext.m_nStreamBegin);
+ if (bThreads)
+ rSharedPool.pushTask(std::make_unique<GraphicImportTask>(pTag, rContext));
+ else
+ GraphicImportTask::doImport(rContext);
+ }
+ else
+ rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ else
+ rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ }
+ }
+
+ rSharedPool.waitUntilDone(pTag);
+
+ // Process data after import.
+ for (auto& rContext : aContexts)
+ {
+ if(rContext.m_pAlphaAccess) // Need to move the AlphaMask back to the BitmapEx.
+ *rContext.m_pGraphic = BitmapEx( rContext.m_pGraphic->GetBitmapExRef().GetBitmap(), rContext.mAlphaMask );
+ rContext.m_pAccess.reset();
+ rContext.m_pAlphaAccess.reset();
+
+ if (rContext.m_nStatus == ERRCODE_NONE && (rContext.m_eLinkType != GfxLinkType::NONE) && !rContext.m_pGraphic->GetReaderContext())
+ {
+ std::unique_ptr<sal_uInt8[]> pGraphicContent;
+
+ const sal_uInt64 nStreamEnd = rContext.m_pStream->Tell();
+ sal_Int32 nGraphicContentSize = nStreamEnd - rContext.m_nStreamBegin;
+
+ if (nGraphicContentSize > 0)
+ {
+ try
+ {
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+ }
+ catch (const std::bad_alloc&)
+ {
+ rContext.m_nStatus = ERRCODE_GRFILTER_TOOBIG;
+ }
+
+ if (rContext.m_nStatus == ERRCODE_NONE)
+ {
+ rContext.m_pStream->Seek(rContext.m_nStreamBegin);
+ rContext.m_pStream->ReadBytes(pGraphicContent.get(), nGraphicContentSize);
+ }
+ }
+
+ if (rContext.m_nStatus == ERRCODE_NONE)
+ rContext.m_pGraphic->SetGfxLink(std::make_shared<GfxLink>(std::move(pGraphicContent), nGraphicContentSize, rContext.m_eLinkType));
+ }
+
+ if (rContext.m_nStatus != ERRCODE_NONE)
+ rContext.m_pGraphic = nullptr;
+
+ rGraphics.push_back(rContext.m_pGraphic);
+ }
+}
+
+void GraphicFilter::MakeGraphicsAvailableThreaded(std::vector<Graphic*>& graphics)
+{
+ // Graphic::makeAvailable() is not thread-safe. Only the jpeg and png loaders are, so here
+ // we process only jpeg and png images that also have their stream data, load new Graphic's
+ // from them and then update the passed objects using them.
+ std::vector< Graphic* > toLoad;
+ for(auto graphic : graphics)
+ {
+ // Need to use GetSharedGfxLink, to access the pointer without copying.
+ if(!graphic->isAvailable() && graphic->IsGfxLink()
+ && graphic->GetSharedGfxLink()->GetDataSize() != 0
+ && (graphic->GetSharedGfxLink()->GetType() == GfxLinkType::NativeJpg
+ || graphic->GetSharedGfxLink()->GetType() == GfxLinkType::NativePng))
+ {
+ // Graphic objects share internal ImpGraphic, do not process any of those twice.
+ const auto predicate = [graphic](Graphic* item) { return item->ImplGetImpGraphic() == graphic->ImplGetImpGraphic(); };
+ if( std::find_if(toLoad.begin(), toLoad.end(), predicate ) == toLoad.end())
+ toLoad.push_back( graphic );
+ }
+ }
+ if( toLoad.empty())
+ return;
+ std::vector< std::unique_ptr<SvStream>> streams;
+ for( auto graphic : toLoad )
+ {
+ streams.push_back( std::make_unique<SvMemoryStream>( const_cast<sal_uInt8*>(graphic->GetSharedGfxLink()->GetData()),
+ graphic->GetSharedGfxLink()->GetDataSize(), StreamMode::READ | StreamMode::WRITE));
+ }
+ std::vector< std::shared_ptr<Graphic>> loadedGraphics;
+ ImportGraphics(loadedGraphics, std::move(streams));
+ assert(loadedGraphics.size() == toLoad.size());
+ for( size_t i = 0; i < toLoad.size(); ++i )
+ {
+ if(loadedGraphics[ i ] != nullptr)
+ toLoad[ i ]->ImplGetImpGraphic()->updateFromLoadedGraphic(loadedGraphics[ i ]->ImplGetImpGraphic());
+ }
+}
+
+Graphic GraphicFilter::ImportUnloadedGraphic(SvStream& rIStream, sal_uInt64 sizeLimit,
+ const Size* pSizeHint)
+{
+ Graphic aGraphic;
+ sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
+ GfxLinkType eLinkType = GfxLinkType::NONE;
+
+ ResetLastError();
+
+ const sal_uInt64 nStreamBegin = rIStream.Tell();
+
+ rIStream.Seek(nStreamBegin);
+
+ ErrCode nStatus = ImpTestOrFindFormat(u"", rIStream, nFormat);
+
+ rIStream.Seek(nStreamBegin);
+ sal_uInt32 nStreamLength(rIStream.remainingSize());
+ if (sizeLimit && sizeLimit < nStreamLength)
+ nStreamLength = sizeLimit;
+
+ OUString aFilterName = pConfig->GetImportFilterName(nFormat);
+
+ std::unique_ptr<sal_uInt8[]> pGraphicContent;
+ sal_Int32 nGraphicContentSize = 0;
+
+ // read graphic
+ {
+ if (aFilterName.equalsIgnoreAsciiCase(IMP_GIF))
+ {
+ eLinkType = GfxLinkType::NativeGif;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
+ {
+ // check if this PNG contains a GIF chunk!
+ pGraphicContent = vcl::PngImageReader::getMicrosoftGifChunk(rIStream, &nGraphicContentSize);
+ if( pGraphicContent )
+ eLinkType = GfxLinkType::NativeGif;
+ else
+ eLinkType = GfxLinkType::NativePng;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
+ {
+ eLinkType = GfxLinkType::NativeJpg;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVG))
+ {
+ bool bOkay(false);
+
+ if (nStreamLength > 0)
+ {
+ std::vector<sal_uInt8> aTwoBytes(2);
+ rIStream.ReadBytes(aTwoBytes.data(), 2);
+ rIStream.Seek(nStreamBegin);
+
+ if (aTwoBytes[0] == 0x1F && aTwoBytes[1] == 0x8B)
+ {
+ SvMemoryStream aMemStream;
+ ZCodec aCodec;
+ tools::Long nMemoryLength;
+
+ aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
+ nMemoryLength = aCodec.Decompress(rIStream, aMemStream);
+ aCodec.EndCompression();
+
+ if (!rIStream.GetError() && nMemoryLength >= 0)
+ {
+ nGraphicContentSize = nMemoryLength;
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+
+ aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
+ aMemStream.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
+
+ bOkay = true;
+ }
+ }
+ else
+ {
+ nGraphicContentSize = nStreamLength;
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+ rIStream.ReadBytes(pGraphicContent.get(), nStreamLength);
+
+ bOkay = true;
+ }
+ }
+
+ if (bOkay)
+ {
+ eLinkType = GfxLinkType::NativeSvg;
+ }
+ else
+ {
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_BMP))
+ {
+ eLinkType = GfxLinkType::NativeBmp;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_MOV))
+ {
+ eLinkType = GfxLinkType::NativeMov;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_WMF) ||
+ aFilterName.equalsIgnoreAsciiCase(IMP_EMF))
+ {
+ nGraphicContentSize = nStreamLength;
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+ rIStream.Seek(nStreamBegin);
+ if (ZCodec::IsZCompressed(rIStream))
+ {
+ ZCodec aCodec;
+ SvMemoryStream aMemStream;
+ tools::Long nMemoryLength;
+ aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
+ nMemoryLength = aCodec.Decompress(rIStream, aMemStream);
+ aCodec.EndCompression();
+
+ if (!rIStream.GetError() && nMemoryLength >= 0)
+ {
+ nGraphicContentSize = nMemoryLength;
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+
+ aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
+ aMemStream.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
+ }
+ }
+ else
+ {
+ rIStream.ReadBytes(pGraphicContent.get(), nStreamLength);
+ }
+ if (!rIStream.GetError())
+ {
+ eLinkType = GfxLinkType::NativeWmf;
+ }
+ else
+ {
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ }
+ else if (aFilterName == IMP_PDF)
+ {
+ eLinkType = GfxLinkType::NativePdf;
+ }
+ else if (aFilterName == IMP_TIFF)
+ {
+ eLinkType = GfxLinkType::NativeTif;
+ }
+ else if (aFilterName == IMP_PICT)
+ {
+ eLinkType = GfxLinkType::NativePct;
+ }
+ else if (aFilterName == IMP_MET)
+ {
+ eLinkType = GfxLinkType::NativeMet;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_WEBP))
+ {
+ if(supportNativeWebp())
+ eLinkType = GfxLinkType::NativeWebp;
+ else
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ else
+ {
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ }
+
+ if (nStatus == ERRCODE_NONE && eLinkType != GfxLinkType::NONE)
+ {
+ if (!pGraphicContent)
+ {
+ nGraphicContentSize = nStreamLength;
+
+ if (nGraphicContentSize > 0)
+ {
+ try
+ {
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+ }
+ catch (const std::bad_alloc&)
+ {
+ nStatus = ERRCODE_GRFILTER_TOOBIG;
+ }
+
+ if (nStatus == ERRCODE_NONE)
+ {
+ rIStream.Seek(nStreamBegin);
+ nGraphicContentSize = rIStream.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
+ }
+ }
+ }
+
+ if( nStatus == ERRCODE_NONE )
+ {
+ bool bAnimated = false;
+ Size aLogicSize;
+ if (eLinkType == GfxLinkType::NativeGif)
+ {
+ SvMemoryStream aMemoryStream(pGraphicContent.get(), nGraphicContentSize, StreamMode::READ);
+ bAnimated = IsGIFAnimated(aMemoryStream, aLogicSize);
+ if (!pSizeHint && aLogicSize.getWidth() && aLogicSize.getHeight())
+ {
+ pSizeHint = &aLogicSize;
+ }
+ }
+ aGraphic.SetGfxLink(std::make_shared<GfxLink>(std::move(pGraphicContent), nGraphicContentSize, eLinkType));
+ aGraphic.ImplGetImpGraphic()->setPrepared(bAnimated, pSizeHint);
+ }
+ }
+
+ // Set error code or try to set native buffer
+ if (nStatus != ERRCODE_NONE)
+ ImplSetError(nStatus, &rIStream);
+ if (nStatus != ERRCODE_NONE || eLinkType == GfxLinkType::NONE)
+ rIStream.Seek(nStreamBegin);
+
+ return aGraphic;
+}
+
+ErrCode GraphicFilter::readGIF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (ImportGIF(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativeGif;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPNG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, std::unique_ptr<sal_uInt8[]> & rpGraphicContent,
+ sal_Int32& rGraphicContentSize)
+{
+ ErrCode aReturnCode = ERRCODE_NONE;
+
+ // check if this PNG contains a GIF chunk!
+ rpGraphicContent = vcl::PngImageReader::getMicrosoftGifChunk(rStream, &rGraphicContentSize);
+ if( rpGraphicContent )
+ {
+ SvMemoryStream aIStrm(rpGraphicContent.get(), rGraphicContentSize, StreamMode::READ);
+ ImportGIF(aIStrm, rGraphic);
+ rLinkType = GfxLinkType::NativeGif;
+ return aReturnCode;
+ }
+
+ // PNG has no GIF chunk
+ vcl::PngImageReader aPNGReader(rStream);
+ BitmapEx aBitmapEx(aPNGReader.read());
+ if (!aBitmapEx.IsEmpty())
+ {
+ rGraphic = aBitmapEx;
+ rLinkType = GfxLinkType::NativePng;
+ }
+ else
+ aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+
+ return aReturnCode;
+}
+
+ErrCode GraphicFilter::readJPEG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, GraphicFilterImportFlags nImportFlags)
+{
+ ErrCode aReturnCode = ERRCODE_NONE;
+
+ // set LOGSIZE flag always, if not explicitly disabled
+ // (see #90508 and #106763)
+ if (!(nImportFlags & GraphicFilterImportFlags::DontSetLogsizeForJpeg))
+ {
+ nImportFlags |= GraphicFilterImportFlags::SetLogsizeForJpeg;
+ }
+
+ sal_uInt64 nPosition = rStream.Tell();
+ if (!ImportJPEG(rStream, rGraphic, nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr))
+ aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+ else
+ {
+ Bitmap& rBitmap = const_cast<Bitmap&>(rGraphic.GetBitmapExRef().GetBitmap());
+ BitmapScopedWriteAccess pWriteAccess(rBitmap);
+ rStream.Seek(nPosition);
+ if (!ImportJPEG(rStream, rGraphic, nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, &pWriteAccess))
+ aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+ else
+ rLinkType = GfxLinkType::NativeJpg;
+ }
+
+ return aReturnCode;
+}
+
+ErrCode GraphicFilter::readSVG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, std::unique_ptr<sal_uInt8[]> & rpGraphicContent,
+ sal_Int32& rGraphicContentSize)
+{
+ ErrCode aReturnCode = ERRCODE_NONE;
+
+ const sal_uInt64 nStreamPosition(rStream.Tell());
+ const sal_uInt64 nStreamLength(rStream.remainingSize());
+
+ bool bOkay(false);
+
+ if (nStreamLength > 0)
+ {
+ std::vector<sal_uInt8> aTwoBytes(2);
+ rStream.ReadBytes(aTwoBytes.data(), 2);
+ rStream.Seek(nStreamPosition);
+
+ if (aTwoBytes[0] == 0x1F && aTwoBytes[1] == 0x8B)
+ {
+ SvMemoryStream aMemStream;
+ ZCodec aCodec;
+ tools::Long nMemoryLength;
+
+ aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
+ nMemoryLength = aCodec.Decompress(rStream, aMemStream);
+ aCodec.EndCompression();
+
+ if (!rStream.GetError() && nMemoryLength >= 0)
+ {
+ VectorGraphicDataArray aNewData(nMemoryLength);
+ aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
+ aMemStream.ReadBytes(aNewData.getArray(), nMemoryLength);
+
+ // Make a uncompressed copy for GfxLink
+ rGraphicContentSize = nMemoryLength;
+ rpGraphicContent.reset(new sal_uInt8[rGraphicContentSize]);
+ std::copy(std::cbegin(aNewData), std::cend(aNewData), rpGraphicContent.get());
+
+ if (!aMemStream.GetError())
+ {
+ BinaryDataContainer aDataContainer(reinterpret_cast<const sal_uInt8*>(aNewData.getConstArray()), aNewData.getLength());
+ auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aDataContainer, VectorGraphicDataType::Svg);
+ rGraphic = Graphic(aVectorGraphicDataPtr);
+ bOkay = true;
+ }
+ }
+ }
+ else
+ {
+ VectorGraphicDataArray aNewData(nStreamLength);
+ rStream.ReadBytes(aNewData.getArray(), nStreamLength);
+
+ if (!rStream.GetError())
+ {
+ BinaryDataContainer aDataContainer(reinterpret_cast<const sal_uInt8*>(aNewData.getConstArray()), aNewData.getLength());
+ auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aDataContainer, VectorGraphicDataType::Svg);
+ rGraphic = Graphic(aVectorGraphicDataPtr);
+ bOkay = true;
+ }
+ }
+ }
+
+ if (bOkay)
+ {
+ rLinkType = GfxLinkType::NativeSvg;
+ }
+ else
+ {
+ aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+ }
+
+ return aReturnCode;
+}
+
+ErrCode GraphicFilter::readXBM(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportXBM(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readXPM(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportXPM(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readWMF_EMF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, VectorGraphicDataType eType)
+{
+ // use new UNO API service, do not directly import but create a
+ // Graphic that contains the original data and decomposes to
+ // primitives on demand
+ sal_uInt32 nStreamLength(rStream.remainingSize());
+ SvStream* aNewStream = &rStream;
+ ErrCode aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+ SvMemoryStream aMemStream;
+ if (ZCodec::IsZCompressed(rStream))
+ {
+ ZCodec aCodec;
+ aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
+ auto nDecompressLength = aCodec.Decompress(rStream, aMemStream);
+ aCodec.EndCompression();
+ aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
+ if (nDecompressLength >= 0)
+ {
+ nStreamLength = nDecompressLength;
+ aNewStream = &aMemStream;
+ }
+ }
+ VectorGraphicDataArray aNewData(nStreamLength);
+ aNewStream->ReadBytes(aNewData.getArray(), nStreamLength);
+ if (!aNewStream->GetError())
+ {
+ const VectorGraphicDataType aDataType(eType);
+ BinaryDataContainer aDataContainer(reinterpret_cast<const sal_uInt8*>(aNewData.getConstArray()), aNewData.getLength());
+
+ auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aDataContainer, aDataType);
+
+ rGraphic = Graphic(aVectorGraphicDataPtr);
+ rLinkType = GfxLinkType::NativeWmf;
+ aReturnCode = ERRCODE_NONE;
+ }
+
+ return aReturnCode;
+}
+
+ErrCode GraphicFilter::readWMF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ return readWMF_EMF(rStream, rGraphic, rLinkType,VectorGraphicDataType::Wmf);
+}
+
+ErrCode GraphicFilter::readEMF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ return readWMF_EMF(rStream, rGraphic, rLinkType, VectorGraphicDataType::Emf);
+}
+
+ErrCode GraphicFilter::readPDF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (vcl::ImportPDF(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativePdf;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readTIFF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (ImportTiffGraphicImport(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativeTif;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readWithTypeSerializer(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, std::u16string_view aFilterName)
+{
+ ErrCode aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+
+ // SV internal filters for import bitmaps and MetaFiles
+ TypeSerializer aSerializer(rStream);
+ aSerializer.readGraphic(rGraphic);
+
+ if (!rStream.GetError())
+ {
+ if (o3tl::equalsIgnoreAsciiCase(aFilterName, u"" IMP_MOV))
+ {
+ rGraphic.SetDefaultType();
+ rStream.Seek(STREAM_SEEK_TO_END);
+ rLinkType = GfxLinkType::NativeMov;
+ }
+ aReturnCode = ERRCODE_NONE;
+ }
+ return aReturnCode;
+}
+
+ErrCode GraphicFilter::readBMP(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (BmpReader(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativeBmp;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readTGA(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportTgaGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPICT(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (ImportPictGraphic(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativePct;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readMET(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (ImportMetGraphic(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativeMet;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readRAS(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportRasGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPCX(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportPcxGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readEPS(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportEpsGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPSD(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportPsdGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPCD(SvStream & rStream, Graphic & rGraphic)
+{
+ std::unique_ptr<FilterConfigItem> pFilterConfigItem;
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ OUString aFilterConfigPath( "Office.Common/Filter/Graphic/Import/PCD" );
+ pFilterConfigItem = std::make_unique<FilterConfigItem>(aFilterConfigPath);
+ }
+
+ if (ImportPcdGraphic(rStream, rGraphic, pFilterConfigItem.get()))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPBM(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportPbmGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readDXF(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportDxfGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readWEBP(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (ImportWebpGraphic(rStream, rGraphic))
+ {
+ if(supportNativeWebp())
+ rLinkType = GfxLinkType::NativeWebp;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::ImportGraphic(Graphic& rGraphic, std::u16string_view rPath, SvStream& rIStream,
+ sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat, GraphicFilterImportFlags nImportFlags)
+{
+ OUString aFilterName;
+ sal_uInt64 nStreamBegin;
+ ErrCode nStatus;
+ GfxLinkType eLinkType = GfxLinkType::NONE;
+ const bool bLinkSet = rGraphic.IsGfxLink();
+
+ std::unique_ptr<sal_uInt8[]> pGraphicContent;
+ sal_Int32 nGraphicContentSize = 0;
+
+ ResetLastError();
+
+ std::shared_ptr<GraphicReader> pContext = rGraphic.GetReaderContext();
+ bool bDummyContext = rGraphic.IsDummyContext();
+ if( !pContext || bDummyContext )
+ {
+ if( bDummyContext )
+ {
+ rGraphic.SetDummyContext( false );
+ nStreamBegin = 0;
+ }
+ else
+ nStreamBegin = rIStream.Tell();
+
+ nStatus = ImpTestOrFindFormat( rPath, rIStream, nFormat );
+ // if pending, return ERRCODE_NONE in order to request more bytes
+ if( rIStream.GetError() == ERRCODE_IO_PENDING )
+ {
+ rGraphic.SetDummyContext(true);
+ rIStream.ResetError();
+ rIStream.Seek( nStreamBegin );
+ return ImplSetError( ERRCODE_NONE );
+ }
+
+ rIStream.Seek( nStreamBegin );
+
+ if( ( nStatus != ERRCODE_NONE ) || rIStream.GetError() )
+ return ImplSetError( ( nStatus != ERRCODE_NONE ) ? nStatus : ERRCODE_GRFILTER_OPENERROR, &rIStream );
+
+ if( pDeterminedFormat )
+ *pDeterminedFormat = nFormat;
+
+ aFilterName = pConfig->GetImportFilterName( nFormat );
+ }
+ else
+ {
+ aFilterName = pContext->GetUpperFilterName();
+
+ nStreamBegin = 0;
+ nStatus = ERRCODE_NONE;
+ }
+
+ // read graphic
+ {
+ if (aFilterName.equalsIgnoreAsciiCase(IMP_GIF))
+ {
+ nStatus = readGIF(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
+ {
+ nStatus = readPNG(rIStream, rGraphic, eLinkType, pGraphicContent, nGraphicContentSize);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
+ {
+ nStatus = readJPEG(rIStream, rGraphic, eLinkType, nImportFlags);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVG))
+ {
+ nStatus = readSVG(rIStream, rGraphic, eLinkType, pGraphicContent, nGraphicContentSize);
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase( IMP_XBM ) )
+ {
+ nStatus = readXBM(rIStream, rGraphic);
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase( IMP_XPM ) )
+ {
+ nStatus = readXPM(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_BMP))
+ {
+ nStatus = readBMP(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVMETAFILE))
+ {
+ nStatus = readWithTypeSerializer(rIStream, rGraphic, eLinkType, aFilterName);
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase(IMP_MOV))
+ {
+ nStatus = readWithTypeSerializer(rIStream, rGraphic, eLinkType, aFilterName);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_WMF))
+ {
+ nStatus = readWMF(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_EMF))
+ {
+ nStatus = readEMF(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PDF))
+ {
+ nStatus = readPDF(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_TIFF) )
+ {
+ nStatus = readTIFF(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_TGA) )
+ {
+ nStatus = readTGA(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PICT))
+ {
+ nStatus = readPICT(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_MET))
+ {
+ nStatus = readMET(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_RAS))
+ {
+ nStatus = readRAS(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PCX))
+ {
+ nStatus = readPCX(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_EPS))
+ {
+ nStatus = readEPS(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PSD))
+ {
+ nStatus = readPSD(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PCD))
+ {
+ nStatus = readPCD(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PBM))
+ {
+ nStatus = readPBM(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_DXF))
+ {
+ nStatus = readDXF(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_WEBP))
+ {
+ nStatus = readWEBP(rIStream, rGraphic, eLinkType);
+ }
+ else
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+
+ if( nStatus == ERRCODE_NONE && ( eLinkType != GfxLinkType::NONE ) && !rGraphic.GetReaderContext() && !bLinkSet )
+ {
+ if (!pGraphicContent)
+ {
+ const sal_uInt64 nStreamEnd = rIStream.Tell();
+ nGraphicContentSize = nStreamEnd - nStreamBegin;
+
+ if (nGraphicContentSize > 0)
+ {
+ try
+ {
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+ }
+ catch (const std::bad_alloc&)
+ {
+ nStatus = ERRCODE_GRFILTER_TOOBIG;
+ }
+
+ if( nStatus == ERRCODE_NONE )
+ {
+ rIStream.Seek(nStreamBegin);
+ rIStream.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
+ }
+ }
+ }
+ if( nStatus == ERRCODE_NONE )
+ {
+ rGraphic.SetGfxLink(std::make_shared<GfxLink>(std::move(pGraphicContent), nGraphicContentSize, eLinkType));
+ }
+ }
+
+ // Set error code or try to set native buffer
+ if( nStatus != ERRCODE_NONE )
+ {
+ ImplSetError( nStatus, &rIStream );
+ rIStream.Seek( nStreamBegin );
+ rGraphic.Clear();
+ }
+
+ return nStatus;
+}
+
+ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const INetURLObject& rPath,
+ sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
+{
+ SAL_INFO( "vcl.filter", "GraphicFilter::ExportGraphic() (thb)" );
+ ErrCode nRetValue = ERRCODE_GRFILTER_FORMATERROR;
+ SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::ExportGraphic() : ProtType == INetProtocol::NotValid" );
+
+ OUString aMainUrl(rPath.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ bool bAlreadyExists = utl::UCBContentHelper::IsDocument(aMainUrl);
+
+ std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::WRITE | StreamMode::TRUNC ));
+ if (xStream)
+ {
+ nRetValue = ExportGraphic( rGraphic, aMainUrl, *xStream, nFormat, pFilterData );
+ xStream.reset();
+
+ if( ( ERRCODE_NONE != nRetValue ) && !bAlreadyExists )
+ utl::UCBContentHelper::Kill(aMainUrl);
+ }
+ return nRetValue;
+}
+
+ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, std::u16string_view rPath,
+ SvStream& rOStm, sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
+{
+ SAL_INFO( "vcl.filter", "GraphicFilter::ExportGraphic() (thb)" );
+ sal_uInt16 nFormatCount = GetExportFormatCount();
+
+ ResetLastError();
+
+ if( nFormat == GRFILTER_FORMAT_DONTKNOW )
+ {
+ INetURLObject aURL( rPath );
+ OUString aExt( aURL.GetFileExtension().toAsciiUpperCase() );
+
+ for( sal_uInt16 i = 0; i < nFormatCount; i++ )
+ {
+ if ( pConfig->GetExportFormatExtension( i ).equalsIgnoreAsciiCase( aExt ) )
+ {
+ nFormat=i;
+ break;
+ }
+ }
+ }
+ if( nFormat >= nFormatCount )
+ return ImplSetError( ERRCODE_GRFILTER_FORMATERROR );
+
+ FilterConfigItem aConfigItem( pFilterData );
+ OUString aFilterName( pConfig->GetExportFilterName( nFormat ) );
+ ErrCode nStatus = ERRCODE_NONE;
+ GraphicType eType;
+ Graphic aGraphic = ImpGetScaledGraphic( rGraphic, aConfigItem );
+ eType = aGraphic.GetType();
+
+ if( pConfig->IsExportPixelFormat( nFormat ) )
+ {
+ if( eType != GraphicType::Bitmap )
+ {
+ Size aSizePixel;
+ sal_uLong nBitsPerPixel,nNeededMem,nMaxMem;
+ ScopedVclPtrInstance< VirtualDevice > aVirDev;
+
+ nMaxMem = 1024;
+ nMaxMem *= 1024; // In Bytes
+
+ // Calculate how big the image would normally be:
+ aSizePixel=aVirDev->LogicToPixel(aGraphic.GetPrefSize(),aGraphic.GetPrefMapMode());
+
+ // Calculate how much memory the image will take up
+ nBitsPerPixel=aVirDev->GetBitCount();
+ nNeededMem=(static_cast<sal_uLong>(aSizePixel.Width())*static_cast<sal_uLong>(aSizePixel.Height())*nBitsPerPixel+7)/8;
+
+ // is the image larger than available memory?
+ if (nMaxMem<nNeededMem)
+ {
+ double fFak=sqrt(static_cast<double>(nMaxMem)/static_cast<double>(nNeededMem));
+ aSizePixel.setWidth(static_cast<sal_uLong>(static_cast<double>(aSizePixel.Width())*fFak) );
+ aSizePixel.setHeight(static_cast<sal_uLong>(static_cast<double>(aSizePixel.Height())*fFak) );
+ }
+
+ aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ aVirDev->SetOutputSizePixel(aSizePixel);
+ Graphic aGraphic2=aGraphic;
+ aGraphic2.Draw(*aVirDev, Point(0, 0), aSizePixel); // this changes the MapMode
+ aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ aGraphic=Graphic(aVirDev->GetBitmapEx(Point(0,0),aSizePixel));
+ }
+ }
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ if( ERRCODE_NONE == nStatus )
+ {
+ if( aFilterName.equalsIgnoreAsciiCase( EXP_BMP ) )
+ {
+ if (!BmpWriter(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+ if (rOStm.GetError())
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(EXP_TIFF))
+ {
+ if (!ExportTiffGraphicImport(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(EXP_GIF))
+ {
+ if (!ExportGifGraphic(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase( EXP_SVMETAFILE ) )
+ {
+ sal_Int32 nVersion = aConfigItem.ReadInt32( "Version", 0 ) ;
+ if ( nVersion )
+ rOStm.SetVersion( nVersion );
+
+ // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ GDIMetaFile aMTF(aGraphic.GetGDIMetaFile());
+
+ SvmWriter aWriter( rOStm );
+ aWriter.Write( aMTF );
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if ( aFilterName.equalsIgnoreAsciiCase( EXP_WMF ) )
+ {
+ bool bDone(false);
+
+ // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
+ auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
+
+ bool bIsEMF = rGraphic.GetGfxLink().IsEMF();
+
+ // VectorGraphicDataType::Wmf means WMF or EMF, allow direct write in the WMF case
+ // only.
+ if (rVectorGraphicDataPtr
+ && rVectorGraphicDataPtr->getType() == VectorGraphicDataType::Wmf
+ && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty()
+ && !bIsEMF)
+ {
+ auto & aDataContainer = rVectorGraphicDataPtr->getBinaryDataContainer();
+ rOStm.WriteBytes(aDataContainer.getData(), aDataContainer.getSize());
+
+ if (rOStm.GetError())
+ {
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else
+ {
+ bDone = true;
+ }
+ }
+
+ if (!bDone)
+ {
+ // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ if (!ConvertGraphicToWMF(aGraphic, rOStm, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if (rOStm.GetError())
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ }
+ else if ( aFilterName.equalsIgnoreAsciiCase( EXP_EMF ) )
+ {
+ bool bDone(false);
+
+ // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
+ auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
+
+ if (rVectorGraphicDataPtr
+ && rVectorGraphicDataPtr->getType() == VectorGraphicDataType::Emf
+ && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty())
+ {
+ auto & aDataContainer = rVectorGraphicDataPtr->getBinaryDataContainer();
+ rOStm.WriteBytes(aDataContainer.getData(), aDataContainer.getSize());
+
+ if (rOStm.GetError())
+ {
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else
+ {
+ bDone = true;
+ }
+ }
+
+ if (!bDone)
+ {
+ // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ if (!ConvertGDIMetaFileToEMF(aGraphic.GetGDIMetaFile(), rOStm))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if (rOStm.GetError())
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase( EXP_JPEG ) )
+ {
+ bool bExportedGrayJPEG = false;
+ if( !ExportJPEG( rOStm, aGraphic, pFilterData, &bExportedGrayJPEG ) )
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(EXP_EPS))
+ {
+ if (!ExportEpsGraphic(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if (rOStm.GetError())
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if ( aFilterName.equalsIgnoreAsciiCase( EXP_PNG ) )
+ {
+ vcl::PNGWriter aPNGWriter( aGraphic.GetBitmapEx(), pFilterData );
+ if ( pFilterData )
+ {
+ for ( const auto& rPropVal : *pFilterData )
+ {
+ if ( rPropVal.Name == "AdditionalChunks" )
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aAdditionalChunkSequence;
+ if ( rPropVal.Value >>= aAdditionalChunkSequence )
+ {
+ for ( const auto& rAdditionalChunk : std::as_const(aAdditionalChunkSequence) )
+ {
+ if ( rAdditionalChunk.Name.getLength() == 4 )
+ {
+ sal_uInt32 nChunkType = 0;
+ for ( sal_Int32 k = 0; k < 4; k++ )
+ {
+ nChunkType <<= 8;
+ nChunkType |= static_cast<sal_uInt8>(rAdditionalChunk.Name[ k ]);
+ }
+ css::uno::Sequence< sal_Int8 > aByteSeq;
+ if ( rAdditionalChunk.Value >>= aByteSeq )
+ {
+ std::vector< vcl::PNGWriter::ChunkData >& rChunkData = aPNGWriter.GetChunks();
+ if ( !rChunkData.empty() )
+ {
+ sal_uInt32 nChunkLen = aByteSeq.getLength();
+
+ vcl::PNGWriter::ChunkData aChunkData;
+ aChunkData.nType = nChunkType;
+ if ( nChunkLen )
+ {
+ aChunkData.aData.resize( nChunkLen );
+ memcpy( aChunkData.aData.data(), aByteSeq.getConstArray(), nChunkLen );
+ }
+ std::vector< vcl::PNGWriter::ChunkData >::iterator aIter = rChunkData.end() - 1;
+ rChunkData.insert( aIter, aChunkData );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ aPNGWriter.Write( rOStm );
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase( EXP_SVG ) )
+ {
+ bool bDone(false);
+
+ // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
+ auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
+
+ if (rVectorGraphicDataPtr
+ && rVectorGraphicDataPtr->getType() == VectorGraphicDataType::Svg
+ && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty())
+ {
+ auto & aDataContainer = rVectorGraphicDataPtr->getBinaryDataContainer();
+ rOStm.WriteBytes(aDataContainer.getData(), aDataContainer.getSize());
+
+ if( rOStm.GetError() )
+ {
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else
+ {
+ bDone = true;
+ }
+ }
+
+ if( !bDone )
+ {
+ // do the normal GDIMetaFile export instead
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+
+ css::uno::Reference< css::xml::sax::XDocumentHandler > xSaxWriter(
+ css::xml::sax::Writer::create( xContext ), css::uno::UNO_QUERY_THROW);
+ css::uno::Sequence< css::uno::Any > aArguments{ css::uno::Any(
+ aConfigItem.GetFilterData()) };
+ css::uno::Reference< css::svg::XSVGWriter > xSVGWriter(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext( "com.sun.star.svg.SVGWriter", aArguments, xContext),
+ css::uno::UNO_QUERY );
+ if( xSaxWriter.is() && xSVGWriter.is() )
+ {
+ css::uno::Reference< css::io::XActiveDataSource > xActiveDataSource(
+ xSaxWriter, css::uno::UNO_QUERY );
+
+ if( xActiveDataSource.is() )
+ {
+ const css::uno::Reference< css::uno::XInterface > xStmIf(
+ static_cast< ::cppu::OWeakObject* >( new ImpFilterOutputStream( rOStm ) ) );
+
+ SvMemoryStream aMemStm( 65535, 65535 );
+
+ // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ SvmWriter aWriter( aMemStm );
+ aWriter.Write( aGraphic.GetGDIMetaFile() );
+
+ xActiveDataSource->setOutputStream( css::uno::Reference< css::io::XOutputStream >(
+ xStmIf, css::uno::UNO_QUERY ) );
+ css::uno::Sequence< sal_Int8 > aMtfSeq( static_cast<sal_Int8 const *>(aMemStm.GetData()), aMemStm.Tell() );
+ xSVGWriter->write( xSaxWriter, aMtfSeq );
+ }
+ }
+ }
+ catch(const css::uno::Exception&)
+ {
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ }
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(EXP_WEBP))
+ {
+ if (!ExportWebpGraphic(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ if( nStatus != ERRCODE_NONE )
+ {
+ ImplSetError( nStatus, &rOStm );
+ }
+ return nStatus;
+}
+
+
+void GraphicFilter::ResetLastError()
+{
+ mxErrorEx = ERRCODE_NONE;
+}
+
+Link<ConvertData&,bool> GraphicFilter::GetFilterCallback() const
+{
+ Link<ConvertData&,bool> aLink( LINK( const_cast<GraphicFilter*>(this), GraphicFilter, FilterCallback ) );
+ return aLink;
+}
+
+IMPL_LINK( GraphicFilter, FilterCallback, ConvertData&, rData, bool )
+{
+ bool bRet = false;
+
+ sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
+ OUString aShortName;
+ css::uno::Sequence< css::beans::PropertyValue > aFilterData;
+ switch( rData.mnFormat )
+ {
+ case ConvertDataFormat::BMP: aShortName = BMP_SHORTNAME; break;
+ case ConvertDataFormat::GIF: aShortName = GIF_SHORTNAME; break;
+ case ConvertDataFormat::JPG: aShortName = JPG_SHORTNAME; break;
+ case ConvertDataFormat::MET: aShortName = MET_SHORTNAME; break;
+ case ConvertDataFormat::PCT: aShortName = PCT_SHORTNAME; break;
+ case ConvertDataFormat::PNG: aShortName = PNG_SHORTNAME; break;
+ case ConvertDataFormat::SVM: aShortName = SVM_SHORTNAME; break;
+ case ConvertDataFormat::TIF: aShortName = TIF_SHORTNAME; break;
+ case ConvertDataFormat::WMF: aShortName = WMF_SHORTNAME; break;
+ case ConvertDataFormat::EMF: aShortName = EMF_SHORTNAME; break;
+ case ConvertDataFormat::SVG: aShortName = SVG_SHORTNAME; break;
+ case ConvertDataFormat::WEBP: aShortName = WEBP_SHORTNAME; break;
+
+ default:
+ break;
+ }
+ if( GraphicType::NONE == rData.maGraphic.GetType() || rData.maGraphic.GetReaderContext() ) // Import
+ {
+ // Import
+ nFormat = GetImportFormatNumberForShortName( aShortName );
+ bRet = ImportGraphic( rData.maGraphic, u"", rData.mrStm, nFormat ) == ERRCODE_NONE;
+ }
+ else if( !aShortName.isEmpty() )
+ {
+ // Export
+#if defined(IOS) || defined(ANDROID)
+ if (aShortName == PNG_SHORTNAME)
+ {
+ aFilterData.realloc(aFilterData.getLength() + 1);
+ auto pFilterData = aFilterData.getArray();
+ pFilterData[aFilterData.getLength() - 1].Name = "Compression";
+ // We "know" that this gets passed to zlib's deflateInit2_(). 1 means best speed.
+ pFilterData[aFilterData.getLength() - 1].Value <<= static_cast<sal_Int32>(1);
+ }
+#endif
+ nFormat = GetExportFormatNumberForShortName( aShortName );
+ bRet = ExportGraphic( rData.maGraphic, u"", rData.mrStm, nFormat, &aFilterData ) == ERRCODE_NONE;
+ }
+
+ return bRet;
+}
+
+namespace
+{
+ class StandardGraphicFilter
+ {
+ public:
+ StandardGraphicFilter()
+ {
+ m_aFilter.GetImportFormatCount();
+ }
+ GraphicFilter m_aFilter;
+ };
+}
+
+GraphicFilter& GraphicFilter::GetGraphicFilter()
+{
+ static StandardGraphicFilter gStandardFilter;
+ return gStandardFilter.m_aFilter;
+}
+
+ErrCode GraphicFilter::LoadGraphic( const OUString &rPath, const OUString &rFilterName,
+ Graphic& rGraphic, GraphicFilter* pFilter,
+ sal_uInt16* pDeterminedFormat )
+{
+ if ( !pFilter )
+ pFilter = &GetGraphicFilter();
+
+ const sal_uInt16 nFilter = !rFilterName.isEmpty() && pFilter->GetImportFormatCount()
+ ? pFilter->GetImportFormatNumber( rFilterName )
+ : GRFILTER_FORMAT_DONTKNOW;
+
+ INetURLObject aURL( rPath );
+ if ( aURL.HasError() )
+ {
+ aURL.SetSmartProtocol( INetProtocol::File );
+ aURL.SetSmartURL( rPath );
+ }
+
+ std::unique_ptr<SvStream> pStream;
+ if ( INetProtocol::File != aURL.GetProtocol() )
+ pStream = ::utl::UcbStreamHelper::CreateStream( rPath, StreamMode::READ );
+
+ ErrCode nRes = ERRCODE_NONE;
+ if ( !pStream )
+ nRes = pFilter->ImportGraphic( rGraphic, aURL, nFilter, pDeterminedFormat );
+ else
+ nRes = pFilter->ImportGraphic( rGraphic, rPath, *pStream, nFilter, pDeterminedFormat );
+
+#ifdef DBG_UTIL
+ OUString aReturnString;
+
+ if (nRes == ERRCODE_GRFILTER_OPENERROR)
+ aReturnString="open error";
+ else if (nRes == ERRCODE_GRFILTER_IOERROR)
+ aReturnString="IO error";
+ else if (nRes == ERRCODE_GRFILTER_FORMATERROR)
+ aReturnString="format error";
+ else if (nRes == ERRCODE_GRFILTER_VERSIONERROR)
+ aReturnString="version error";
+ else if (nRes == ERRCODE_GRFILTER_FILTERERROR)
+ aReturnString="filter error";
+ else if (nRes == ERRCODE_GRFILTER_TOOBIG)
+ aReturnString="graphic is too big";
+
+ SAL_INFO_IF( nRes, "vcl.filter", "Problem importing graphic " << rPath << ". Reason: " << aReturnString );
+#endif
+
+ return nRes;
+}
+
+ErrCode GraphicFilter::compressAsPNG(const Graphic& rGraphic, SvStream& rOutputStream)
+{
+ css::uno::Sequence< css::beans::PropertyValue > aFilterData{ comphelper::makePropertyValue(
+ "Compression", sal_uInt32(9)) };
+
+ sal_uInt16 nFilterFormat = GetExportFormatNumberForShortName(u"PNG");
+ return ExportGraphic(rGraphic, u"", rOutputStream, nFilterFormat, &aFilterData);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */