/* -*- 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 <comphelper/configuration.hxx>
#include <tools/stream.hxx>
#include <tools/urlobj.hxx>
#include <tools/zcodec.hxx>
#include <rtl/crc.h>
#include <fltcall.hxx>
#include <vcl/salctype.hxx>
#include <vcl/filter/PngImageReader.hxx>
#include <vcl/filter/SvmWriter.hxx>
#include <vcl/filter/PngImageWriter.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 <o3tl/test_info.hxx>
#include <vcl/TypeSerializer.hxx>
#include "FilterConfigCache.hxx"
#include <graphic/GraphicFormatDetector.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()
{
// Enable support only for unittests
return o3tl::IsRunningUnitTest();
}
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
static OUString ImpGetExtension( std::u16string_view rPath )
{
OUString aExt;
INetURLObject aURL( rPath );
aExt = aURL.GetFileExtension().toAsciiUpperCase();
return aExt;
}
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( u"Resolution"_ustr, nBase );
}
}
return ERRCODE_NONE;
}
static Graphic ImpGetScaledGraphic( const Graphic& rGraphic, FilterConfigItem& rConfigItem )
{
if ( rGraphic.GetType() == GraphicType::NONE )
return rGraphic;
sal_Int32 nLogicalWidth = rConfigItem.ReadInt32( u"LogicalWidth"_ustr, 0 );
sal_Int32 nLogicalHeight = rConfigItem.ReadInt32( u"LogicalHeight"_ustr, 0 );
sal_Int32 nMode = rConfigItem.ReadInt32( u"ExportMode"_ustr, -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;
}
Graphic aGraphic;
Size aPrefSize( rGraphic.GetPrefSize() );
Size aOriginalSize;
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( u"Resolution"_ustr, 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( u"Color"_ustr, 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;
}
return aGraphic;
}
GraphicFilter::GraphicFilter()
{
{
std::scoped_lock aGuard( getListMutex() );
if ( gaFilterHdlList.empty() )
pConfig = new FilterConfigCache;
else
pConfig = gaFilterHdlList.front()->pConfig;
gaFilterHdlList.push_back( this );
}
mxErrorEx = ERRCODE_NONE;
}
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();
}
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 );
}
const OUString & GraphicFilter::GetImportFormatName( sal_uInt16 nFormat )
{
return pConfig->GetImportFormatName( nFormat );
}
const 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 );
}
const OUString & GraphicFilter::GetExportInternalFilterName( sal_uInt16 nFormat )
{
return pConfig->GetExportInternalFilterName( nFormat );
}
sal_uInt16 GraphicFilter::GetExportFormatNumberForTypeName( std::u16string_view rType )
{
return pConfig->GetExportFormatNumberForTypeName( rType );
}
const OUString & GraphicFilter::GetExportFormatName( sal_uInt16 nFormat )
{
return pConfig->GetExportFormatName( nFormat );
}
const 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<BitmapScopedWriteAccess> 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.GetAlphaMask();
rContext.m_pAlphaAccess = std::make_unique<BitmapScopedWriteAccess>(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)
{
rContext.m_pAccess.reset();
rContext.m_pAlphaAccess.reset();
if (!rContext.mAlphaMask.IsEmpty()) // Need to move the AlphaMask back to the BitmapEx.
*rContext.m_pGraphic = BitmapEx( rContext.m_pGraphic->GetBitmapExRef().GetBitmap(), rContext.mAlphaMask );
if (rContext.m_nStatus == ERRCODE_NONE && rContext.m_eLinkType != GfxLinkType::NONE)
{
BinaryDataContainer aGraphicContent;
const sal_uInt64 nStreamEnd = rContext.m_pStream->Tell();
sal_Int32 nGraphicContentSize = nStreamEnd - rContext.m_nStreamBegin;
if (nGraphicContentSize > 0)
{
try
{
rContext.m_pStream->Seek(rContext.m_nStreamBegin);
aGraphicContent = BinaryDataContainer(*rContext.m_pStream, nGraphicContentSize);
}
catch (const std::bad_alloc&)
{
rContext.m_nStatus = ERRCODE_GRFILTER_TOOBIG;
}
}
if (rContext.m_nStatus == ERRCODE_NONE)
rContext.m_pGraphic->SetGfxLink(std::make_shared<GfxLink>(aGraphicContent, 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::none_of(toLoad.begin(), toLoad.end(), predicate ))
toLoad.push_back( graphic );
}
}
if( toLoad.empty())
return;
std::vector< std::unique_ptr<SvStream>> streams;
streams.reserve(toLoad.size());
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;
loadedGraphics.reserve(streams.size());
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());
}
}
namespace
{
BinaryDataContainer insertContentOrDecompressFromZ(SvStream& rStream, sal_uInt32 nStreamLength)
{
BinaryDataContainer aGraphicContent;
if (ZCodec::IsZCompressed(rStream))
{
ZCodec aCodec;
SvMemoryStream aMemStream;
tools::Long nMemoryLength;
aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
nMemoryLength = aCodec.Decompress(rStream, aMemStream);
aCodec.EndCompression();
if (rStream.good() && nMemoryLength >= 0)
{
aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
aGraphicContent = BinaryDataContainer(aMemStream, nMemoryLength);
}
}
else
{
aGraphicContent = BinaryDataContainer(rStream, nStreamLength);
}
return aGraphicContent;
}
ErrCode prepareImageTypeAndData(SvStream& rStream, sal_uInt32 nStreamLength, BinaryDataContainer& rGraphicContent, std::u16string_view rFilterName, GfxLinkType& rLinkType)
{
const sal_uInt64 nStreamBegin = rStream.Tell();
ErrCode nStatus = ERRCODE_GRFILTER_FILTERERROR;
if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_GIF))
{
rLinkType = GfxLinkType::NativeGif;
nStatus = ERRCODE_NONE;
}
else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_PNG))
{
// check if this PNG contains a GIF chunk!
rGraphicContent = vcl::PngImageReader::getMicrosoftGifChunk(rStream);
if (!rGraphicContent.isEmpty())
rLinkType = GfxLinkType::NativeGif;
else
rLinkType = GfxLinkType::NativePng;
nStatus = ERRCODE_NONE;
}
else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_JPEG))
{
rLinkType = GfxLinkType::NativeJpg;
nStatus = ERRCODE_NONE;
}
else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_SVG))
{
rStream.Seek(nStreamBegin);
rGraphicContent = insertContentOrDecompressFromZ(rStream, nStreamLength);
if (rStream.good())
{
rLinkType = GfxLinkType::NativeSvg;
nStatus = ERRCODE_NONE;
}
}
else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_BMP))
{
rLinkType = GfxLinkType::NativeBmp;
nStatus = ERRCODE_NONE;
}
else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_MOV))
{
rLinkType = GfxLinkType::NativeMov;
nStatus = ERRCODE_NONE;
}
else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_WMF) ||
o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_EMF) ||
o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_WMZ) ||
o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_EMZ))
{
rStream.Seek(nStreamBegin);
rGraphicContent = insertContentOrDecompressFromZ(rStream, nStreamLength);
if (rStream.good())
{
rLinkType = GfxLinkType::NativeWmf;
nStatus = ERRCODE_NONE;
}
}
else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_PDF))
{
rLinkType = GfxLinkType::NativePdf;
nStatus = ERRCODE_NONE;
}
else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_TIFF))
{
rLinkType = GfxLinkType::NativeTif;
nStatus = ERRCODE_NONE;
}
else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_PICT))
{
rLinkType = GfxLinkType::NativePct;
nStatus = ERRCODE_NONE;
}
else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_MET))
{
rLinkType = GfxLinkType::NativeMet;
nStatus = ERRCODE_NONE;
}
else if (o3tl::equalsIgnoreAsciiCase(rFilterName, IMP_WEBP))
{
if (supportNativeWebp())
{
rLinkType = GfxLinkType::NativeWebp;
nStatus = ERRCODE_NONE;
}
}
return nStatus;
}
} // end anonymous namespace
Graphic GraphicFilter::ImportUnloadedGraphic(SvStream& rIStream, sal_uInt64 sizeLimit,
const Size* pSizeHint)
{
Graphic aGraphic;
sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
ResetLastError();
const sal_uInt64 nStreamBegin = rIStream.Tell();
// Get the image format
rIStream.Seek(nStreamBegin);
ErrCode nStatus = ImpTestOrFindFormat(u"", rIStream, nFormat);
if (nStatus != ERRCODE_NONE)
return aGraphic;
rIStream.Seek(nStreamBegin);
sal_uInt32 nStreamLength(rIStream.remainingSize());
if (sizeLimit && sizeLimit < nStreamLength)
nStreamLength = sizeLimit;
OUString aFilterName = pConfig->GetImportFilterName(nFormat);
BinaryDataContainer aBinaryDataContainer;
GfxLinkType eLinkType = GfxLinkType::NONE;
rIStream.Seek(nStreamBegin);
nStatus = prepareImageTypeAndData(rIStream, nStreamLength, aBinaryDataContainer, aFilterName, eLinkType);
if (nStatus == ERRCODE_NONE && eLinkType != GfxLinkType::NONE)
{
if (aBinaryDataContainer.isEmpty() && nStreamLength > 0)
{
try
{
rIStream.Seek(nStreamBegin);
aBinaryDataContainer = BinaryDataContainer(rIStream, nStreamLength);
}
catch (const std::bad_alloc&)
{
nStatus = ERRCODE_GRFILTER_TOOBIG;
}
}
if (nStatus == ERRCODE_NONE)
{
bool bAnimated = false;
Size aLogicSize;
if (eLinkType == GfxLinkType::NativeGif && !aBinaryDataContainer.isEmpty())
{
std::shared_ptr<SvStream> pMemoryStream = aBinaryDataContainer.getAsStream();
bAnimated = IsGIFAnimated(*pMemoryStream, aLogicSize);
if (!pSizeHint && aLogicSize.getWidth() && aLogicSize.getHeight())
{
pSizeHint = &aLogicSize;
}
}
aGraphic.SetGfxLink(std::make_shared<GfxLink>(aBinaryDataContainer, 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, BinaryDataContainer& rpGraphicContent)
{
ErrCode aReturnCode = ERRCODE_NONE;
// check if this PNG contains a GIF chunk!
if (auto aMSGifChunk = vcl::PngImageReader::getMicrosoftGifChunk(rStream);
!aMSGifChunk.isEmpty())
{
std::shared_ptr<SvStream> pIStrm(aMSGifChunk.getAsStream());
ImportGIF(*pIStrm, rGraphic);
rLinkType = GfxLinkType::NativeGif;
rpGraphicContent = std::move(aMSGifChunk);
return aReturnCode;
}
// PNG has no GIF chunk
Graphic aGraphic;
vcl::PngImageReader aPNGReader(rStream);
aPNGReader.read(aGraphic);
if (!aGraphic.GetBitmapEx().IsEmpty())
{
rGraphic = std::move(aGraphic);
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, BinaryDataContainer& rpGraphicContent)
{
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)
{
aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
rpGraphicContent = BinaryDataContainer(aMemStream, nMemoryLength);
// Make a uncompressed copy for GfxLink
if (!aMemStream.GetError())
{
auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(rpGraphicContent, VectorGraphicDataType::Svg);
rGraphic = Graphic(aVectorGraphicDataPtr);
bOkay = true;
}
}
}
else
{
BinaryDataContainer aNewData(rStream, nStreamLength);
if (!rStream.GetError())
{
auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aNewData, 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;
}
}
BinaryDataContainer aNewData(*aNewStream, nStreamLength);
if (!aNewStream->GetError())
{
auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aNewData, eType);
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 (!comphelper::IsFuzzing())
{
OUString aFilterConfigPath( u"Office.Common/Filter/Graphic/Import/PCD"_ustr );
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();
BinaryDataContainer aGraphicContent;
ResetLastError();
{
bool bDummyContext = rGraphic.IsDummyContext();
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 );
}
// read graphic
{
if (aFilterName.equalsIgnoreAsciiCase(IMP_GIF))
{
nStatus = readGIF(rIStream, rGraphic, eLinkType);
}
else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
{
nStatus = readPNG(rIStream, rGraphic, eLinkType, aGraphicContent);
}
else if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
{
nStatus = readJPEG(rIStream, rGraphic, eLinkType, nImportFlags);
}
else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVG) || aFilterName.equalsIgnoreAsciiCase(IMP_SVGZ))
{
nStatus = readSVG(rIStream, rGraphic, eLinkType, aGraphicContent);
}
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) || aFilterName.equalsIgnoreAsciiCase(IMP_WMZ))
{
nStatus = readWMF(rIStream, rGraphic, eLinkType);
}
else if (aFilterName.equalsIgnoreAsciiCase(IMP_EMF) || aFilterName.equalsIgnoreAsciiCase(IMP_EMZ))
{
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 && !bLinkSet)
{
if (aGraphicContent.isEmpty())
{
const sal_uInt64 nStreamEnd = rIStream.Tell();
const sal_uInt64 nGraphicContentSize = nStreamEnd - nStreamBegin;
if (nGraphicContentSize > 0)
{
try
{
rIStream.Seek(nStreamBegin);
aGraphicContent = BinaryDataContainer(rIStream, nGraphicContentSize);
}
catch (const std::bad_alloc&)
{
nStatus = ERRCODE_GRFILTER_TOOBIG;
}
}
}
if( nStatus == ERRCODE_NONE )
{
rGraphic.SetGfxLink(std::make_shared<GfxLink>(aGraphicContent, 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();
bool bShouldCompress = false;
SvMemoryStream rCompressableStm;
if( nFormat == GRFILTER_FORMAT_DONTKNOW )
{
OUString aExt = ImpGetExtension( rPath );
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( u"Version"_ustr, 0 ) ;
if ( nVersion )
rOStm.SetVersion( nVersion );
// #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
const GDIMetaFile& aMTF(aGraphic.GetGDIMetaFile());
SvmWriter aWriter( rOStm );
aWriter.Write( aMTF );
if( rOStm.GetError() )
nStatus = ERRCODE_GRFILTER_IOERROR;
}
else if ( aFilterName.equalsIgnoreAsciiCase( EXP_WMF ) || aFilterName.equalsIgnoreAsciiCase( EXP_WMZ ) )
{
bool bDone(false);
SvStream* rTempStm = &rOStm;
if (aFilterName.equalsIgnoreAsciiCase(EXP_WMZ))
{
// Write to a different stream so that we can compress to rOStm later
rCompressableStm.SetBufferSize( rOStm.GetBufferSize() );
rTempStm = &rCompressableStm;
bShouldCompress = true;
}
// 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)
{
rVectorGraphicDataPtr->getBinaryDataContainer().writeToStream(*rTempStm);
if (rTempStm->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, *rTempStm, &aConfigItem))
nStatus = ERRCODE_GRFILTER_FORMATERROR;
if (rTempStm->GetError())
nStatus = ERRCODE_GRFILTER_IOERROR;
}
}
else if ( aFilterName.equalsIgnoreAsciiCase( EXP_EMF ) || aFilterName.equalsIgnoreAsciiCase( EXP_EMZ ) )
{
bool bDone(false);
SvStream* rTempStm = &rOStm;
if (aFilterName.equalsIgnoreAsciiCase(EXP_EMZ))
{
// Write to a different stream so that we can compress to rOStm later
rCompressableStm.SetBufferSize( rOStm.GetBufferSize() );
rTempStm = &rCompressableStm;
bShouldCompress = true;
}
// 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())
{
rVectorGraphicDataPtr->getBinaryDataContainer().writeToStream(*rTempStm);
if (rTempStm->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(), *rTempStm))
nStatus = ERRCODE_GRFILTER_FORMATERROR;
if (rTempStm->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 ) )
{
auto aBitmapEx = aGraphic.GetBitmapEx();
vcl::PngImageWriter aPNGWriter( rOStm );
if ( pFilterData )
aPNGWriter.setParameters( *pFilterData );
aPNGWriter.write( aBitmapEx );
if( rOStm.GetError() )
nStatus = ERRCODE_GRFILTER_IOERROR;
}
else if ( aFilterName.equalsIgnoreAsciiCase( EXP_APNG ) )
{
vcl::PngImageWriter aPNGWriter( rOStm );
if ( pFilterData )
aPNGWriter.setParameters( *pFilterData );
aPNGWriter.write( aGraphic );
if( rOStm.GetError() )
nStatus = ERRCODE_GRFILTER_IOERROR;
}
else if( aFilterName.equalsIgnoreAsciiCase( EXP_SVG ) || aFilterName.equalsIgnoreAsciiCase( EXP_SVGZ ) )
{
bool bDone(false);
SvStream* rTempStm = &rOStm;
if (aFilterName.equalsIgnoreAsciiCase(EXP_SVGZ))
{
// Write to a different stream so that we can compress to rOStm later
rCompressableStm.SetBufferSize(rOStm.GetBufferSize());
rTempStm = &rCompressableStm;
bShouldCompress = true;
}
// 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())
{
rVectorGraphicDataPtr->getBinaryDataContainer().writeToStream(*rTempStm);
if( rTempStm->GetError() )
{
nStatus = ERRCODE_GRFILTER_IOERROR;
}
else
{
bDone = true;
}
}
if( !bDone )
{
// do the normal GDIMetaFile export instead
try
{
const 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( u"com.sun.star.svg.SVGWriter"_ustr, 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(
getXWeak( new ImpFilterOutputStream( *rTempStm ) ) );
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 );
}
else if ( bShouldCompress )
{
sal_uInt32 nUncompressedCRC32
= rtl_crc32( 0, rCompressableStm.GetData(), rCompressableStm.GetSize() );
ZCodec aCodec;
rCompressableStm.Seek( 0 );
aCodec.BeginCompression( ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true );
// the inner modify time/filename doesn't really matter in this context because
// compressed graphic formats are meant to be opened as is - not to be extracted
aCodec.SetCompressionMetadata( "inner"_ostr, 0, nUncompressedCRC32 );
aCodec.Compress( rCompressableStm, rOStm );
tools::Long nCompressedLength = aCodec.EndCompression();
if ( rOStm.GetError() || nCompressedLength <= 0 )
nStatus = ERRCODE_GRFILTER_IOERROR;
}
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()) // 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(
u"Compression"_ustr, sal_uInt32(9)) };
sal_uInt16 nFilterFormat = GetExportFormatNumberForShortName(u"PNG");
return ExportGraphic(rGraphic, u"", rOutputStream, nFilterFormat, &aFilterData);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'ImplSetError' is required to be utilized.
↑ V530 The return value of function 'ImplSetError' is required to be utilized.
↑ V530 The return value of function 'ImplSetError' is required to be utilized.
↑ V506 Pointer to local variable 'aLogicSize' is stored outside the scope of this variable. Such a pointer will become invalid.