/* -*- 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 <sal/config.h>
#include <sal/log.hxx>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/io/NotConnectedException.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/util/XCancellable.hpp>
#include <com/sun/star/embed/XHierarchicalStorageAccess.hpp>
#include <comphelper/fileformat.h>
#include <comphelper/graphicmimetype.hxx>
#include <comphelper/compbase.hxx>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <rtl/ref.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <unotools/streamwrap.hxx>
#include <unotools/tempfile.hxx>
#include <unotools/saveopt.hxx>
#include <vcl/filter/SvmWriter.hxx>
#include <vcl/gfxlink.hxx>
#include <vcl/metaact.hxx>
#include <tools/zcodec.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <vcl/GraphicObject.hxx>
#include <vcl/graphicfilter.hxx>
#include <svx/xmlgrhlp.hxx>
#include <svx/xmleohlp.hxx>
#include <algorithm>
#include <memory>
#include <string_view>
#include <utility>
using namespace com::sun::star;
using namespace com::sun::star::uno;
using namespace com::sun::star::io;
namespace com::sun::star::uno { class XComponentContext; }
constexpr OUStringLiteral XML_GRAPHICSTORAGE_NAME = u"Pictures" ;
constexpr OUStringLiteral XML_GRAPHICOBJECT_URL_BASE = u"vnd.sun.star.GraphicObject:" ;
namespace {
const MetaCommentAction* ImplCheckForEPS( GDIMetaFile const & rMtf )
{
const MetaCommentAction* pComment = nullptr;
if ( rMtf.GetActionSize() >= 2
&& rMtf.GetAction(0 )->GetType() == MetaActionType::EPS
&& rMtf.GetAction(1 )->GetType() == MetaActionType::COMMENT
&& ( static_cast <const MetaCommentAction*>(rMtf.GetAction( 1 ))->GetComment() == "EPSReplacementGraphic" ) )
pComment = static_cast <const MetaCommentAction*>(rMtf.GetAction( 1 ));
return pComment;
}
class GraphicInputStream : public cppu::WeakImplHelper<XInputStream>
{
private :
virtual sal_Int32 SAL_CALL readBytes(Sequence<sal_Int8> & aData, sal_Int32 nBytesToRead) override;
virtual sal_Int32 SAL_CALL readSomeBytes(Sequence<sal_Int8> & aData, sal_Int32 nMaxBytesToRead) override;
virtual void SAL_CALL skipBytes(sal_Int32 nBytesToSkip) override;
virtual sal_Int32 SAL_CALL available() override;
virtual void SAL_CALL closeInput() override;
private :
utl::TempFileFast maTempFile;
rtl::Reference<::utl::OInputStreamWrapper> mxStreamWrapper;
public :
explicit GraphicInputStream(GraphicObject const & raGraphicObject, const OUString & rMimeType);
GraphicInputStream(const GraphicInputStream&) = delete ;
GraphicInputStream& operator =(const GraphicInputStream&) = delete ;
bool exists() const
{
return mxStreamWrapper.is();
}
};
GraphicInputStream::GraphicInputStream(GraphicObject const & aGraphicObject, const OUString & rMimeType)
{
if (aGraphicObject.GetType() == GraphicType::NONE)
return ;
SvStream* pStream = maTempFile.GetStream(StreamMode::READWRITE);
if (!pStream)
return ;
const Graphic& aGraphic(aGraphicObject.GetGraphic());
const GfxLink aGfxLink(aGraphic.GetGfxLink());
bool bRet = false ;
if (aGfxLink.GetDataSize() && aGfxLink.GetData())
{
if (rMimeType.isEmpty())
{
pStream->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
bRet = (pStream->GetError() == ERRCODE_NONE);
}
else
{
GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter();
bRet = (rFilter.ExportGraphic(aGraphic, u"" , *pStream, rFilter.GetExportFormatNumberForMediaType(rMimeType)) == ERRCODE_NONE);
}
}
else
{
if (aGraphic.GetType() == GraphicType::Bitmap)
{
GraphicFilter & rFilter = GraphicFilter::GetGraphicFilter();
OUString aFormat = rMimeType;
if (aGraphic.IsAnimated())
aFormat = "image/gif" ;
else if (aFormat.isEmpty())
aFormat = "image/png" ;
bRet = (rFilter.ExportGraphic(aGraphic, u"" , *pStream, rFilter.GetExportFormatNumberForMediaType(aFormat)) == ERRCODE_NONE);
}
else if (rMimeType.isEmpty() && aGraphic.GetType() == GraphicType::GdiMetafile)
{
pStream->SetVersion(SOFFICE_FILEFORMAT_8);
pStream->SetCompressMode(SvStreamCompressFlags::ZBITMAP);
SvmWriter aWriter(*pStream);
aWriter.Write(aGraphic.GetGDIMetaFile());
bRet = (pStream->GetError() == ERRCODE_NONE);
}
else if (!rMimeType.isEmpty())
{
GraphicFilter & rFilter = GraphicFilter::GetGraphicFilter();
bRet = ( rFilter.ExportGraphic( aGraphic, u"" , *pStream, rFilter.GetExportFormatNumberForMediaType( rMimeType ) ) == ERRCODE_NONE );
}
}
if (bRet)
{
pStream->Seek( 0 );
mxStreamWrapper = new ::utl::OInputStreamWrapper(*pStream);
}
}
sal_Int32 SAL_CALL GraphicInputStream::readBytes(Sequence<sal_Int8> & rData, sal_Int32 nBytesToRead)
{
if (!mxStreamWrapper.is())
throw NotConnectedException();
return mxStreamWrapper->readBytes(rData, nBytesToRead);
}
sal_Int32 SAL_CALL GraphicInputStream::readSomeBytes(Sequence<sal_Int8>& rData, sal_Int32 nMaxBytesToRead )
{
if (!mxStreamWrapper.is())
throw NotConnectedException() ;
return mxStreamWrapper->readSomeBytes(rData, nMaxBytesToRead);
}
void SAL_CALL GraphicInputStream::skipBytes(sal_Int32 nBytesToSkip)
{
if (!mxStreamWrapper.is())
throw NotConnectedException();
mxStreamWrapper->skipBytes(nBytesToSkip);
}
sal_Int32 SAL_CALL GraphicInputStream::available()
{
if (!mxStreamWrapper.is())
throw NotConnectedException();
return mxStreamWrapper->available();
}
void SAL_CALL GraphicInputStream::closeInput()
{
if (!mxStreamWrapper.is())
throw NotConnectedException();
mxStreamWrapper->closeInput();
}
class SvXMLGraphicOutputStream:
public cppu::WeakImplHelper<XOutputStream>
{
private :
// XOutputStream
virtual void SAL_CALL writeBytes( const Sequence< sal_Int8 >& rData ) override;
virtual void SAL_CALL flush() override;
virtual void SAL_CALL closeOutput() override;
private :
std::optional<::utl::TempFileFast> moTmp;
SvStream* mpOStm;
rtl::Reference< ::utl::OOutputStreamWrapper > mxStmWrapper;
std::optional<GraphicObject> moGrfObj;
bool mbClosed;
public :
SvXMLGraphicOutputStream();
virtual ~SvXMLGraphicOutputStream() override;
SvXMLGraphicOutputStream(const SvXMLGraphicOutputStream&) = delete ;
SvXMLGraphicOutputStream& operator =(const SvXMLGraphicOutputStream&) = delete ;
bool Exists() const { return mxStmWrapper.is(); }
const GraphicObject& GetGraphicObject();
Graphic GetGraphic();
};
SvXMLGraphicOutputStream::SvXMLGraphicOutputStream()
: moTmp(std::in_place)
, moGrfObj(std::in_place)
, mbClosed(false )
{
mpOStm = moTmp->GetStream( StreamMode::READWRITE );
if ( mpOStm )
mxStmWrapper = new ::utl::OOutputStreamWrapper( *mpOStm );
}
SvXMLGraphicOutputStream::~SvXMLGraphicOutputStream()
{
moTmp.reset();
}
void SAL_CALL SvXMLGraphicOutputStream::writeBytes( const Sequence< sal_Int8 >& rData )
{
if ( !mxStmWrapper.is() )
throw NotConnectedException() ;
mxStmWrapper->writeBytes( rData );
}
void SAL_CALL SvXMLGraphicOutputStream::flush()
{
if ( !mxStmWrapper.is() )
throw NotConnectedException() ;
mxStmWrapper->flush();
}
void SAL_CALL SvXMLGraphicOutputStream::closeOutput()
{
if ( !mxStmWrapper.is() )
throw NotConnectedException() ;
mxStmWrapper->closeOutput();
mxStmWrapper.clear();
mbClosed = true ;
}
Graphic SvXMLGraphicOutputStream::GetGraphic()
{
Graphic aGraphic;
if (mbClosed && moGrfObj->GetType() == GraphicType::NONE && mpOStm)
{
mpOStm->Seek( 0 );
sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
sal_uInt16 nDeterminedFormat = GRFILTER_FORMAT_DONTKNOW;
GraphicFilter::GetGraphicFilter().ImportGraphic( aGraphic, u"" , *mpOStm ,nFormat,&nDeterminedFormat);
if (nDeterminedFormat == GRFILTER_FORMAT_DONTKNOW)
{
//Read the first two byte to check whether it is a gzipped stream, is so it may be in wmz or emz format
//unzip them and try again
sal_uInt8 sFirstBytes[ 2 ];
sal_uInt64 nStreamLen = mpOStm->TellEnd();
mpOStm->Seek( 0 );
if ( nStreamLen == 0 )
{
SvLockBytes* pLockBytes = mpOStm->GetLockBytes();
if ( pLockBytes )
pLockBytes->SetSynchronMode();
nStreamLen = mpOStm->TellEnd();
mpOStm->Seek( 0 );
}
if ( nStreamLen >= 2 )
{
//read two byte
mpOStm->ReadBytes(sFirstBytes, 2 );
if ( sFirstBytes[0 ] == 0 x1f && sFirstBytes[1 ] == 0 x8b )
{
SvMemoryStream aDest;
ZCodec aZCodec( 0 x8000, 0 x8000 );
aZCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
mpOStm->Seek( 0 );
aZCodec.Decompress( *mpOStm, aDest );
if (aZCodec.EndCompression())
{
sal_uInt64 nStreamLen_ = aDest.TellEnd();
if (nStreamLen_ > 0 )
{
aDest.Seek(0 );
GraphicFilter::GetGraphicFilter().ImportGraphic( aGraphic, u"" , aDest ,nFormat,&nDeterminedFormat );
}
}
}
}
}
}
if (aGraphic.GetType() != GraphicType::NONE)
{
mpOStm = nullptr;
moTmp.reset();
}
return aGraphic;
}
const GraphicObject& SvXMLGraphicOutputStream::GetGraphicObject()
{
Graphic aGraphic(GetGraphic());
if (aGraphic.GetType() != GraphicType::NONE)
{
moGrfObj.emplace(std::move(aGraphic));
}
return *moGrfObj;
}
}
SvXMLGraphicHelper::SvXMLGraphicHelper(SvXMLGraphicHelperMode eCreateMode)
{
Init( nullptr, eCreateMode );
}
SvXMLGraphicHelper::SvXMLGraphicHelper()
: meCreateMode(SvXMLGraphicHelperMode::Read)
{
}
SvXMLGraphicHelper::~SvXMLGraphicHelper()
{
}
bool SvXMLGraphicHelper::ImplGetStreamNames( const OUString& rURLStr,
OUString& rPictureStorageName,
OUString& rPictureStreamName )
{
if (rURLStr.isEmpty())
return false ;
const OUString aURLStr {rURLStr.copy(rURLStr.lastIndexOf(':' )+1 )};
if ( !aURLStr.isEmpty() && aURLStr.indexOf('/' )<0 ) // just one token?
{
rPictureStorageName = OUString();
rPictureStreamName = aURLStr;
}
else
SvXMLEmbeddedObjectHelper::splitObjectURL(aURLStr, rPictureStorageName, rPictureStreamName);
SAL_WARN_IF(rPictureStreamName.isEmpty(), "svx" , "SvXMLGraphicHelper::ImplInsertGraphicURL: invalid scheme: " << rURLStr);
return !rPictureStreamName.isEmpty();
}
uno::Reference < embed::XStorage > SvXMLGraphicHelper::ImplGetGraphicStorage( const OUString& rStorageName )
{
uno::Reference < embed::XStorage > xRetStorage;
if ( mxRootStorage.is() )
{
try
{
maCurStorageName = rStorageName;
xRetStorage = mxRootStorage->openStorageElement(
maCurStorageName,
( SvXMLGraphicHelperMode::Write == meCreateMode )
? embed::ElementModes::READWRITE
: embed::ElementModes::READ );
}
catch ( uno::Exception& )
{
}
//#i43196# try again to open the storage element - this time readonly
if (!xRetStorage.is())
{
try
{
maCurStorageName = rStorageName;
xRetStorage = mxRootStorage->openStorageElement( maCurStorageName, embed::ElementModes::READ );
}
catch ( uno::Exception& )
{
}
}
}
return xRetStorage;
}
SvxGraphicHelperStream_Impl SvXMLGraphicHelper::ImplGetGraphicStream( const OUString& rPictureStorageName,
const OUString& rPictureStreamName )
{
SvxGraphicHelperStream_Impl aRet;
if (!rPictureStorageName.isEmpty())
aRet.xStorage = ImplGetGraphicStorage(rPictureStorageName);
else
aRet.xStorage = mxRootStorage;
sal_Int32 nMode = embed::ElementModes::READ;
if (SvXMLGraphicHelperMode::Write == meCreateMode)
{
nMode = embed::ElementModes::READWRITE;
}
if (aRet.xStorage.is())
{
aRet.xStream = aRet.xStorage->openStreamElement( rPictureStreamName, nMode );
}
else if (rPictureStorageName.indexOf('/' ) != -1 )
{
uno::Reference<embed::XHierarchicalStorageAccess> xHierRootStorage(mxRootStorage,
uno::UNO_QUERY);
if (xHierRootStorage.is())
{
try
{
aRet.xStream = xHierRootStorage->openStreamElementByHierarchicalName(
rPictureStorageName + "/" + rPictureStreamName, nMode);
aRet.xStorage = mxRootStorage;
}
catch (const uno::Exception&)
{
TOOLS_WARN_EXCEPTION("svx" ,
"SvXMLGraphicHelper::ImplGetGraphicStream: failed to open "
<< rPictureStreamName);
}
}
}
if (aRet.xStream.is() && (SvXMLGraphicHelperMode::Write == meCreateMode))
{
uno::Reference<beans::XPropertySet> xProps(aRet.xStream, uno::UNO_QUERY);
xProps->setPropertyValue(u"UseCommonStoragePasswordEncryption" _ustr, uno::Any(true ));
}
return aRet;
}
OUString SvXMLGraphicHelper::ImplGetGraphicMimeType( std::u16string_view rFileName )
{
if ( ( rFileName.size() >= 4 ) && ( rFileName[ rFileName.size() - 4 ] == '.' ) )
{
const OString aExt(OUStringToOString(rFileName.substr(rFileName.size() - 3 ),
RTL_TEXTENCODING_ASCII_US));
return comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension( aExt );
}
return OUString();
}
Graphic SvXMLGraphicHelper::ImplReadGraphic(const OUString& rPictureStorageName,
const OUString& rPictureStreamName, sal_Int32 nPage)
{
Graphic aReturnGraphic;
SvxGraphicHelperStream_Impl aStream( ImplGetGraphicStream( rPictureStorageName, rPictureStreamName ) );
if (aStream.xStream.is())
{
GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(aStream.xStream));
Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(*pStream, 0 , nullptr, nPage);
if (!aGraphic.IsNone())
aReturnGraphic = std::move(aGraphic);
else
rGraphicFilter.ImportGraphic(aReturnGraphic, u"" , *pStream, GRFILTER_FORMAT_DONTKNOW,
nullptr, GraphicFilterImportFlags::NONE, nPage);
}
return aReturnGraphic;
}
void SvXMLGraphicHelper::Init( const uno::Reference < embed::XStorage >& rXMLStorage,
SvXMLGraphicHelperMode eCreateMode,
const OUString& rGraphicMimeType )
{
mxRootStorage = rXMLStorage;
meCreateMode = eCreateMode;
maOutputMimeType = rGraphicMimeType;
}
rtl::Reference<SvXMLGraphicHelper> SvXMLGraphicHelper::Create( const uno::Reference < embed::XStorage >& rXMLStorage,
SvXMLGraphicHelperMode eCreateMode )
{
rtl::Reference<SvXMLGraphicHelper> pThis = new SvXMLGraphicHelper;
pThis->Init( rXMLStorage, eCreateMode, OUString() );
return pThis;
}
rtl::Reference<SvXMLGraphicHelper> SvXMLGraphicHelper::Create( SvXMLGraphicHelperMode eCreateMode,
const OUString& rGraphicMimeType )
{
rtl::Reference<SvXMLGraphicHelper> pThis = new SvXMLGraphicHelper;
pThis->Init( nullptr, eCreateMode, rGraphicMimeType );
return pThis;
}
namespace
{
void splitUserDataFromURL(OUString const & rWholeURL, OUString & rJustURL, OUString & rUserData)
{
sal_Int32 nUser = rWholeURL.indexOf('?' );
if (nUser >= 0 )
{
rJustURL = rWholeURL.copy(0 , nUser);
nUser++;
rUserData = rWholeURL.copy(nUser);
}
else
{
rJustURL = rWholeURL;
}
}
} // end anonymous namespace
// XGraphicObjectResolver
OUString SAL_CALL SvXMLGraphicHelper::resolveGraphicObjectURL( const OUString& /*rURL*/ )
{
throw uno::RuntimeException(u"XGraphicObjectResolver has been removed in LibreOffice 6.1" _ustr);
}
// XGraphicStorageHandler
uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicHelper::loadGraphic(OUString const & rURL)
{
return loadGraphicAtPage(rURL, -1 );
}
// XGraphicStorageHandler
uno::Reference<graphic::XGraphic>
SAL_CALL SvXMLGraphicHelper::loadGraphicAtPage(OUString const & rURL, sal_Int32 nPage)
{
std::unique_lock aGuard(m_aMutex);
uno::Reference<graphic::XGraphic> xGraphic;
OUString aURLOnly;
OUString aUserData;
splitUserDataFromURL(rURL, aURLOnly, aUserData);
size_t nIndex = (nPage >= 0 ? nPage : 0 );
auto aIterator = maGraphicObjects.find(aURLOnly);
if (aIterator != maGraphicObjects.end() && aIterator->second.size() > nIndex
&& aIterator->second[nIndex].is())
{
return aIterator->second[nIndex];
}
OUString aPictureStorageName, aPictureStreamName;
if (ImplGetStreamNames(aURLOnly, aPictureStorageName, aPictureStreamName))
{
const GraphicObject aGraphicObject(
ImplReadGraphic(aPictureStorageName, aPictureStreamName, nPage));
if (aGraphicObject.GetType() != GraphicType::NONE)
{
xGraphic = aGraphicObject.GetGraphic().GetXGraphic();
if (aIterator != maGraphicObjects.end())
{
if (aIterator->second.size() <= nIndex)
aIterator->second.resize(nIndex + 1 );
aIterator->second[nIndex] = xGraphic;
}
else
{
maGraphicObjects.emplace(
aURLOnly, std::vector<uno::Reference<graphic::XGraphic>>(nIndex + 1 ));
maGraphicObjects[aURLOnly][nIndex] = xGraphic;
}
}
}
return xGraphic;
}
uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicHelper::loadGraphicFromOutputStream(uno::Reference<io::XOutputStream> const & rxOutputStream)
{
std::unique_lock aGuard(m_aMutex);
uno::Reference<graphic::XGraphic> xGraphic;
if ((SvXMLGraphicHelperMode::Read == meCreateMode) && rxOutputStream.is())
{
SvXMLGraphicOutputStream* pGraphicOutputStream = static_cast <SvXMLGraphicOutputStream*>(rxOutputStream.get());
if (pGraphicOutputStream)
{
xGraphic = pGraphicOutputStream->GetGraphic().GetXGraphic();
}
}
return xGraphic;
}
OUString SAL_CALL SvXMLGraphicHelper::saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
OUString & rOutSavedMimeType, OUString const & rRequestName)
{
return implSaveGraphic(rxGraphic, rOutSavedMimeType, rRequestName);
}
OUString SAL_CALL SvXMLGraphicHelper::saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic)
{
OUString aOutMimeType;
return implSaveGraphic(rxGraphic, aOutMimeType, std::u16string_view());
}
OUString SvXMLGraphicHelper::implSaveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
OUString & rOutSavedMimeType, std::u16string_view rRequestName)
{
Graphic aGraphic(rxGraphic);
auto aIterator = maExportGraphics.find(aGraphic);
if (aIterator != maExportGraphics.end())
{
auto const & aURLAndMimePair = aIterator->second;
rOutSavedMimeType = aURLAndMimePair.second;
return aURLAndMimePair.first;
}
GraphicObject aGraphicObject(aGraphic);
if (aGraphicObject.GetType() != GraphicType::NONE)
{
const GfxLink aGfxLink(aGraphic.GetGfxLink());
OUString aExtension;
bool bUseGfxLink = true ;
if (aGfxLink.GetDataSize())
{
switch (aGfxLink.GetType())
{
case GfxLinkType::EpsBuffer: aExtension = ".eps" ; break ;
case GfxLinkType::NativeGif: aExtension = ".gif" ; break ;
// #i15508# added BMP type for better exports (checked, works)
case GfxLinkType::NativeBmp: aExtension = ".bmp" ; break ;
case GfxLinkType::NativeJpg: aExtension = ".jpg" ; break ;
case GfxLinkType::NativePng: aExtension = ".png" ; break ;
case GfxLinkType::NativeTif: aExtension = ".tif" ; break ;
case GfxLinkType::NativeWmf:
if (aGfxLink.IsEMF())
aExtension = ".emf" ;
else
aExtension = ".wmf" ;
break ;
case GfxLinkType::NativeMet: aExtension = ".met" ; break ;
case GfxLinkType::NativePct: aExtension = ".pct" ; break ;
case GfxLinkType::NativeSvg:
{
// backward-compat kludge: since no released OOo
// version to date can handle svg properly, wrap it up
// into an svm. slight catch22 here, since strict ODF
// conformance _recommends_ svg - then again, most old
// ODF consumers are believed to be OOo
auto nSaneVersion = GetODFSaneDefaultVersion();
if ( nSaneVersion < SvtSaveOptions::ODFSVER_012
|| nSaneVersion == SvtSaveOptions::ODFSVER_012_EXT_COMPAT)
{
bUseGfxLink = false ;
aExtension = ".svm" ;
}
else
{
aExtension = ".svg" ;
}
break ;
}
case GfxLinkType::NativePdf: aExtension = ".pdf" ; break ;
case GfxLinkType::NativeWebp: aExtension = ".webp" ; break ;
default :
aExtension = ".grf" ;
break ;
}
}
else
{
if (aGraphicObject.GetType() == GraphicType::Bitmap)
{
if (aGraphicObject.IsAnimated())
aExtension = ".gif" ;
else
aExtension = ".png" ;
}
else if (aGraphicObject.GetType() == GraphicType::GdiMetafile)
{
// SJ: first check if this metafile is just an eps file, then we will store the eps instead of svm
GDIMetaFile& rMetafile(const_cast <GDIMetaFile&>(aGraphic.GetGDIMetaFile()));
if (ImplCheckForEPS(rMetafile))
aExtension = ".eps" ;
else
aExtension = ".svm" ;
}
}
OUString rPictureStreamName;
if (!rRequestName.empty())
{
rPictureStreamName = rRequestName + aExtension;
}
else
{
OUString sId = OStringToOUString(aGraphicObject.GetUniqueID(), RTL_TEXTENCODING_ASCII_US);
rPictureStreamName = sId + aExtension;
}
SvxGraphicHelperStream_Impl aStream(ImplGetGraphicStream(XML_GRAPHICSTORAGE_NAME, rPictureStreamName));
if (aStream.xStream.is())
{
const OUString aMimeType(ImplGetGraphicMimeType(rPictureStreamName));
uno::Reference<beans::XPropertySet> xProps(aStream.xStream, uno::UNO_QUERY);
// set stream properties (MediaType/Compression)
if (!aMimeType.isEmpty())
{
xProps->setPropertyValue(u"MediaType" _ustr, uno::Any(aMimeType));
}
// picture formats that actually _do_ benefit from zip
// storage compression
// .svm pics gets compressed via ZBITMAP old-style stream
// option below
static const char * aCompressiblePics[] =
{
"image/svg+xml" ,
"image/x-emf" ,
"image/x-wmf" ,
"image/tiff" ,
"image/x-eps" ,
"image/bmp" ,
"image/x-pict"
};
bool bSuccess = false ;
bool bCompressed = aMimeType.isEmpty();
if ( !bCompressed )
{
for (const char * p : aCompressiblePics)
{
if ( aMimeType.equalsIgnoreAsciiCaseAscii(p) )
{
bCompressed = true ;
break ;
}
}
}
xProps->setPropertyValue(u"Compressed" _ustr, Any(bCompressed));
std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(aStream.xStream));
if (bUseGfxLink && aGfxLink.GetDataSize() && aGfxLink.GetData())
{
pStream->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
rOutSavedMimeType = aMimeType;
bSuccess = (pStream->GetError() == ERRCODE_NONE);
}
else
{
if (aGraphic.GetType() == GraphicType::Bitmap)
{
GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
OUString aFormat;
if (aGraphic.IsAnimated())
{
aFormat = "gif" ;
}
else
{
aFormat = "png" ;
}
rOutSavedMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension(aFormat.toUtf8());
bSuccess = (rFilter.ExportGraphic(aGraphic, u"" , *pStream, rFilter.GetExportFormatNumberForShortName(aFormat)) == ERRCODE_NONE);
}
else if (aGraphic.GetType() == GraphicType::GdiMetafile)
{
pStream->SetVersion(SOFFICE_FILEFORMAT_8);
pStream->SetCompressMode(SvStreamCompressFlags::ZBITMAP);
rOutSavedMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension("svm" );
// SJ: first check if this metafile is just an eps file, then we will store the eps instead of svm
GDIMetaFile& rMtf(const_cast <GDIMetaFile&>(aGraphic.GetGDIMetaFile()));
const MetaCommentAction* pComment = ImplCheckForEPS(rMtf);
if (pComment)
{
sal_uInt32 nSize = pComment->GetDataSize();
const sal_uInt8* pData = pComment->GetData();
if (nSize && pData)
pStream->WriteBytes(pData, nSize);
const MetaEPSAction* pAct = static_cast <const MetaEPSAction*>(rMtf.FirstAction());
const GfxLink& rLink = pAct->GetLink();
pStream->WriteBytes(rLink.GetData(), rLink.GetDataSize());
}
else
{
SvmWriter aWriter(*pStream);
aWriter.Write(rMtf);
}
bSuccess = (pStream->GetError() == ERRCODE_NONE);
}
}
if (!bSuccess)
return OUString();
uno::Reference<embed::XTransactedObject> xStorage(aStream.xStorage, uno::UNO_QUERY);
pStream.reset();
aStream.xStream->getOutputStream()->closeOutput();
if (xStorage.is())
xStorage->commit();
OUString aStoragePath = "Pictures/" + rPictureStreamName;
// put into cache
maExportGraphics[aGraphic] = std::make_pair(aStoragePath, rOutSavedMimeType);
return aStoragePath;
}
}
return OUString();
}
uno::Reference<io::XInputStream> SAL_CALL SvXMLGraphicHelper::createInputStream(uno::Reference<graphic::XGraphic> const & rxGraphic)
{
Reference<XInputStream> xInputStream;
GraphicObject aGraphicObject((Graphic(rxGraphic)));
if (SvXMLGraphicHelperMode::Write == meCreateMode)
{
OUString sMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension(OUStringToOString(maOutputMimeType, RTL_TEXTENCODING_ASCII_US));
rtl::Reference<GraphicInputStream> pInputStream(new GraphicInputStream(aGraphicObject, sMimeType));
// We release the pointer from unique_ptr and assign it to the input stream return type.
// In case the stream doesn't exists, unique_ptr will delete the pointer when we go out of scope.
if (pInputStream->exists())
xInputStream = pInputStream.get();
}
return xInputStream;
}
// XBinaryStreamResolver
Reference< XInputStream > SAL_CALL SvXMLGraphicHelper::getInputStream( const OUString& /*rURL*/ )
{
Reference<XInputStream> xRet;
return xRet;
}
Reference< XOutputStream > SAL_CALL SvXMLGraphicHelper::createOutputStream()
{
Reference< XOutputStream > xRet;
if ( SvXMLGraphicHelperMode::Read == meCreateMode )
{
rtl::Reference<SvXMLGraphicOutputStream> pOutputStream(new SvXMLGraphicOutputStream);
if ( pOutputStream->Exists() )
{
xRet = pOutputStream.get();
maGrfStms.push_back( xRet );
}
}
return xRet;
}
OUString SAL_CALL SvXMLGraphicHelper::resolveOutputStream( const Reference< XOutputStream >& rxBinaryStream )
{
OUString aRet;
if ( ( SvXMLGraphicHelperMode::Read == meCreateMode ) && rxBinaryStream.is() )
{
if ( ::std::find( maGrfStms.begin(), maGrfStms.end(), rxBinaryStream ) != maGrfStms.end() )
{
SvXMLGraphicOutputStream* pOStm = static_cast < SvXMLGraphicOutputStream* >( rxBinaryStream.get() );
if ( pOStm )
{
const GraphicObject& rGrfObj = pOStm->GetGraphicObject();
const OUString aId(OStringToOUString(
rGrfObj.GetUniqueID(), RTL_TEXTENCODING_ASCII_US));
if ( !aId.isEmpty() )
{
aRet = XML_GRAPHICOBJECT_URL_BASE + aId;
}
}
}
}
return aRet;
}
// for instantiation via service manager
namespace {
namespace impl
{
typedef comphelper::WeakComponentImplHelper<lang::XInitialization,
document::XGraphicObjectResolver,
document::XGraphicStorageHandler,
document::XBinaryStreamResolver,
lang::XServiceInfo>
SvXMLGraphicImportExportHelper_Base;
} // namespace impl
class SvXMLGraphicImportExportHelper :
public impl::SvXMLGraphicImportExportHelper_Base
{
public :
explicit SvXMLGraphicImportExportHelper( SvXMLGraphicHelperMode eMode );
protected :
// is called from WeakComponentImplHelper when XComponent::dispose() was
// called from outside
virtual void disposing(std::unique_lock<std::mutex>&) override;
// ____ XInitialization ____
// one argument is allowed, which is the XStorage
virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
// ____ XGraphicObjectResolver ____
virtual OUString SAL_CALL resolveGraphicObjectURL( const OUString& aURL ) override;
// ____ XGraphicStorageHandler ____
virtual css::uno::Reference<css::graphic::XGraphic> SAL_CALL
loadGraphic(const OUString& aURL) override;
// ____ XGraphicStorageHandler ____
virtual css::uno::Reference<css::graphic::XGraphic>
SAL_CALL loadGraphicAtPage(const OUString& aURL, sal_Int32 nPage) override;
virtual css::uno::Reference<css::graphic::XGraphic> SAL_CALL
loadGraphicFromOutputStream(css::uno::Reference<css::io::XOutputStream> const & rxOutputStream) override;
virtual OUString SAL_CALL
saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic) override;
virtual OUString SAL_CALL
saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic, OUString & rOutSavedMimeType, OUString const & rRequestName) override;
virtual css::uno::Reference<css::io::XInputStream> SAL_CALL
createInputStream(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic) override;
// ____ XBinaryStreamResolver ____
virtual Reference< io::XInputStream > SAL_CALL getInputStream( const OUString& aURL ) override;
virtual Reference< io::XOutputStream > SAL_CALL createOutputStream() override;
virtual OUString SAL_CALL resolveOutputStream( const Reference< io::XOutputStream >& aBinaryStream ) override;
// ____ XServiceInfo ____
virtual OUString SAL_CALL getImplementationName() override;
virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
private :
SvXMLGraphicHelperMode m_eGraphicHelperMode;
rtl::Reference<SvXMLGraphicHelper> m_xXMLGraphicHelper;
};
SvXMLGraphicImportExportHelper::SvXMLGraphicImportExportHelper( SvXMLGraphicHelperMode eMode ) :
m_eGraphicHelperMode( eMode )
{}
void SvXMLGraphicImportExportHelper::disposing(std::unique_lock<std::mutex>&)
{
if (m_xXMLGraphicHelper)
{
m_xXMLGraphicHelper->dispose();
m_xXMLGraphicHelper.clear();
}
}
// ____ XInitialization ____
void SAL_CALL SvXMLGraphicImportExportHelper::initialize(
const Sequence< Any >& aArguments )
{
Reference< embed::XStorage > xStorage;
if ( aArguments.hasElements() )
aArguments[0 ] >>= xStorage;
m_xXMLGraphicHelper = SvXMLGraphicHelper::Create( xStorage, m_eGraphicHelperMode );
}
// ____ XGraphicObjectResolver ____
OUString SAL_CALL SvXMLGraphicImportExportHelper::resolveGraphicObjectURL( const OUString& aURL )
{
return m_xXMLGraphicHelper->resolveGraphicObjectURL( aURL );
}
// ____ XGraphicStorageHandler ____
uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicImportExportHelper::loadGraphic(OUString const & rURL)
{
return m_xXMLGraphicHelper->loadGraphic(rURL);
}
// ____ XGraphicStorageHandler ____
uno::Reference<graphic::XGraphic> SAL_CALL
SvXMLGraphicImportExportHelper::loadGraphicAtPage(OUString const & rURL, sal_Int32 nPage)
{
return m_xXMLGraphicHelper->loadGraphicAtPage(rURL, nPage);
}
uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicImportExportHelper::loadGraphicFromOutputStream(uno::Reference<io::XOutputStream> const & rxOutputStream)
{
return m_xXMLGraphicHelper->loadGraphicFromOutputStream(rxOutputStream);
}
OUString SAL_CALL SvXMLGraphicImportExportHelper::saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic)
{
return m_xXMLGraphicHelper->saveGraphic(rxGraphic);
}
OUString SAL_CALL SvXMLGraphicImportExportHelper::saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
OUString & rOutSavedMimeType, OUString const & rRequestName)
{
return m_xXMLGraphicHelper->saveGraphicByName(rxGraphic, rOutSavedMimeType, rRequestName);
}
uno::Reference<io::XInputStream> SAL_CALL SvXMLGraphicImportExportHelper::createInputStream(uno::Reference<graphic::XGraphic> const & rxGraphic)
{
return m_xXMLGraphicHelper->createInputStream(rxGraphic);
}
// ____ XBinaryStreamResolver ____
Reference< io::XInputStream > SAL_CALL SvXMLGraphicImportExportHelper::getInputStream( const OUString& aURL )
{
return m_xXMLGraphicHelper->getInputStream( aURL );
}
Reference< io::XOutputStream > SAL_CALL SvXMLGraphicImportExportHelper::createOutputStream()
{
return m_xXMLGraphicHelper->createOutputStream();
}
OUString SAL_CALL SvXMLGraphicImportExportHelper::resolveOutputStream( const Reference< io::XOutputStream >& aBinaryStream )
{
return m_xXMLGraphicHelper->resolveOutputStream( aBinaryStream );
}
// ____ XServiceInfo ____
OUString SAL_CALL SvXMLGraphicImportExportHelper::getImplementationName()
{
if ( m_eGraphicHelperMode == SvXMLGraphicHelperMode::Read )
return u"com.sun.star.comp.Svx.GraphicImportHelper" _ustr;
return u"com.sun.star.comp.Svx.GraphicExportHelper" _ustr;
}
sal_Bool SAL_CALL SvXMLGraphicImportExportHelper::supportsService( const OUString& ServiceName )
{
return cppu::supportsService(this , ServiceName);
}
Sequence< OUString > SAL_CALL SvXMLGraphicImportExportHelper::getSupportedServiceNames()
{
return { u"com.sun.star.document.GraphicObjectResolver" _ustr,
u"com.sun.star.document.GraphicStorageHandler" _ustr,
u"com.sun.star.document.BinaryStreamResolver" _ustr };
}
}
/** Create this with createInstanceWithArguments. service name
" com . sun . star . comp . Svx . GraphicImportHelper " , one argument which is the
XStorage . Without arguments no helper class is created . With an empty
argument the helper class is created and initialized like in the CTOR to
SvXMLGraphicHelper that only gets the create mode .
You should call dispose after you no longer need this component .
uses eCreateMode = = SvXMLGraphicHelperMode : : Read , bDirect = = sal_True in
SvXMLGraphicHelper
*/
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_Svx_GraphicImportHelper_get_implementation(
css::uno::XComponentContext *,
css::uno::Sequence<css::uno::Any> const &)
{
return cppu::acquire(new SvXMLGraphicImportExportHelper(SvXMLGraphicHelperMode::Read));
}
/** Create this with createInstanceWithArguments. service name
" com . sun . star . comp . Svx . GraphicExportHelper " , one argument which is the
XStorage . Without arguments no helper class is created . With an empty
argument the helper class is created and initialized like in the CTOR to
SvXMLGraphicHelper that only gets the create mode
To write the Pictures stream , you have to call dispose at this component .
Make sure you call dispose before you commit the parent storage .
uses eCreateMode = = SvXMLGraphicHelperMode : : Write , bDirect = = sal_True in
SvXMLGraphicHelper
*/
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_Svx_GraphicExportHelper_get_implementation(
css::uno::XComponentContext *,
css::uno::Sequence<css::uno::Any> const &)
{
return cppu::acquire(new SvXMLGraphicImportExportHelper(SvXMLGraphicHelperMode::Write));
}
namespace svx {
void DropUnusedNamedItems(css::uno::Reference<css::uno::XInterface> const & xModel)
{
uno::Reference<lang::XMultiServiceFactory> const xModelFactory(xModel, uno::UNO_QUERY);
assert(xModelFactory.is());
try
{
uno::Reference<util::XCancellable> const xGradient(
xModelFactory->createInstance(u"com.sun.star.drawing.GradientTable" _ustr),
uno::UNO_QUERY );
if (xGradient.is())
{
xGradient->cancel();
}
uno::Reference<util::XCancellable> const xHatch(
xModelFactory->createInstance(u"com.sun.star.drawing.HatchTable" _ustr),
uno::UNO_QUERY );
if (xHatch.is())
{
xHatch->cancel();
}
uno::Reference<util::XCancellable> const xBitmap(
xModelFactory->createInstance(u"com.sun.star.drawing.BitmapTable" _ustr),
uno::UNO_QUERY );
if (xBitmap.is())
{
xBitmap->cancel();
}
uno::Reference<util::XCancellable> const xTransGradient(
xModelFactory->createInstance(u"com.sun.star.drawing.TransparencyGradientTable" _ustr),
uno::UNO_QUERY );
if (xTransGradient.is())
{
xTransGradient->cancel();
}
uno::Reference<util::XCancellable> const xMarker(
xModelFactory->createInstance(u"com.sun.star.drawing.MarkerTable" _ustr),
uno::UNO_QUERY );
if (xMarker.is())
{
xMarker->cancel();
}
uno::Reference<util::XCancellable> const xDashes(
xModelFactory->createInstance(u"com.sun.star.drawing.DashTable" _ustr),
uno::UNO_QUERY );
if (xDashes.is())
{
xDashes->cancel();
}
}
catch (const Exception&)
{
TOOLS_WARN_EXCEPTION("svx" , "dropUnusedNamedItems(): exception during clearing of unused named items" );
}
}
} // namespace svx
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=97 H=100 G=98
¤ Dauer der Verarbeitung: 0.26 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland