diff options
author | Tünde Tóth <toth.tunde@nisz.hu> | 2022-03-22 09:47:57 +0100 |
---|---|---|
committer | László Németh <nemeth@numbertext.org> | 2022-03-30 18:24:45 +0200 |
commit | aea8043bc5f5187498fa450505d6de9d6986e2a6 (patch) | |
tree | 2047231de903ae94656848f14d31a3523d429fe8 /oox | |
parent | 94ace0e51744f82b58156392e53b4c4ad819e4bf (diff) |
Impress and Calc used to dump the same image file
as many times as it was featured in the document,
resulting redundant, sometimes huge documents.
Note: using only checksum to recognize image duplication
is a regression, because checksum collision results
image loss. This is a very unlikely event, and
the following commits have got the same problem.
The solution is comparing the images with the same
checksum byte for byte.
See also commit b484e9814c66d8d51cea974390963a6944bc9d73
"tdf#83227 oox: reuse RelId in DML/VML export for the same graphic"
and commit 797fef38612fb2fd62d1f6591619b9361e526bca
"tdf#118535 DOCX export: save header image once".
Change-Id: I9f233d521941381746634cf4f9b5991da0dadda9
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131928
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
Diffstat (limited to 'oox')
-rw-r--r-- | oox/source/export/drawingml.cxx | 207 |
1 files changed, 117 insertions, 90 deletions
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index a790a643abc0..a99a0474a458 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -111,7 +111,6 @@ #include <tools/stream.hxx> #include <unotools/fontdefs.hxx> #include <vcl/cvtgrf.hxx> -#include <vcl/graph.hxx> #include <vcl/svapp.hxx> #include <rtl/strbuf.hxx> #include <filter/msfilter/escherex.hxx> @@ -237,6 +236,7 @@ int DrawingML::mnWdpImageCounter = 1; std::map<OUString, OUString> DrawingML::maWdpCache; sal_Int32 DrawingML::mnDrawingMLCount = 0; sal_Int32 DrawingML::mnVmlCount = 0; +std::stack<std::unordered_map<BitmapChecksum, OUString>> DrawingML::maExportGraphics; sal_Int16 DrawingML::GetScriptType(const OUString& rStr) { @@ -275,6 +275,16 @@ void DrawingML::ResetMlCounters() mnVmlCount = 0; } +void DrawingML::PushExportGraphics() +{ + maExportGraphics.emplace(); +} + +void DrawingML::PopExportGraphics() +{ + maExportGraphics.pop(); +} + bool DrawingML::GetProperty( const Reference< XPropertySet >& rXPropertySet, const OUString& aName ) { try @@ -1264,113 +1274,130 @@ const char* DrawingML::GetRelationCompPrefix() const OUString DrawingML::WriteImage( const Graphic& rGraphic , bool bRelPathToMedia, OUString* pFileName ) { GfxLink aLink = rGraphic.GetGfxLink (); + BitmapChecksum aChecksum = rGraphic.GetChecksum(); OUString sMediaType; const char* pExtension = ""; OUString sRelId; + OUString sPath; - SvMemoryStream aStream; - const void* aData = aLink.GetData(); - std::size_t nDataSize = aLink.GetDataSize(); - - switch ( aLink.GetType() ) + // tdf#74670 tdf#91286 Save image only once (this is no problem for DOCX) + if (GetDocumentType() != DOCUMENT_DOCX && !maExportGraphics.empty()) { - case GfxLinkType::NativeGif: - sMediaType = "image/gif"; - pExtension = ".gif"; - break; + auto aIterator = maExportGraphics.top().find(aChecksum); + if (aIterator != maExportGraphics.top().end()) + sPath = aIterator->second; + } - // #i15508# added BMP type for better exports - // export not yet active, so adding for reference (not checked) - case GfxLinkType::NativeBmp: - sMediaType = "image/bmp"; - pExtension = ".bmp"; - break; + if (sPath.isEmpty()) + { + SvMemoryStream aStream; + const void* aData = aLink.GetData(); + std::size_t nDataSize = aLink.GetDataSize(); - case GfxLinkType::NativeJpg: - sMediaType = "image/jpeg"; - pExtension = ".jpeg"; - break; - case GfxLinkType::NativePng: - sMediaType = "image/png"; - pExtension = ".png"; - break; - case GfxLinkType::NativeTif: - sMediaType = "image/tiff"; - pExtension = ".tif"; - break; - case GfxLinkType::NativeWmf: - sMediaType = "image/x-wmf"; - pExtension = ".wmf"; - break; - case GfxLinkType::NativeMet: - sMediaType = "image/x-met"; - pExtension = ".met"; - break; - case GfxLinkType::NativePct: - sMediaType = "image/x-pict"; - pExtension = ".pct"; - break; - case GfxLinkType::NativeMov: - sMediaType = "application/movie"; - pExtension = ".MOV"; - break; - default: + switch (aLink.GetType()) { - GraphicType aType = rGraphic.GetType(); - if ( aType == GraphicType::Bitmap || aType == GraphicType::GdiMetafile) + case GfxLinkType::NativeGif: + sMediaType = "image/gif"; + pExtension = ".gif"; + break; + + // #i15508# added BMP type for better exports + // export not yet active, so adding for reference (not checked) + case GfxLinkType::NativeBmp: + sMediaType = "image/bmp"; + pExtension = ".bmp"; + break; + + case GfxLinkType::NativeJpg: + sMediaType = "image/jpeg"; + pExtension = ".jpeg"; + break; + case GfxLinkType::NativePng: + sMediaType = "image/png"; + pExtension = ".png"; + break; + case GfxLinkType::NativeTif: + sMediaType = "image/tiff"; + pExtension = ".tif"; + break; + case GfxLinkType::NativeWmf: + sMediaType = "image/x-wmf"; + pExtension = ".wmf"; + break; + case GfxLinkType::NativeMet: + sMediaType = "image/x-met"; + pExtension = ".met"; + break; + case GfxLinkType::NativePct: + sMediaType = "image/x-pict"; + pExtension = ".pct"; + break; + case GfxLinkType::NativeMov: + sMediaType = "application/movie"; + pExtension = ".MOV"; + break; + default: { - if ( aType == GraphicType::Bitmap ) + GraphicType aType = rGraphic.GetType(); + if (aType == GraphicType::Bitmap || aType == GraphicType::GdiMetafile) { - (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::PNG ); - sMediaType = "image/png"; - pExtension = ".png"; + if (aType == GraphicType::Bitmap) + { + (void)GraphicConverter::Export(aStream, rGraphic, ConvertDataFormat::PNG); + sMediaType = "image/png"; + pExtension = ".png"; + } + else + { + (void)GraphicConverter::Export(aStream, rGraphic, ConvertDataFormat::EMF); + sMediaType = "image/x-emf"; + pExtension = ".emf"; + } } else { - (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::EMF ); - sMediaType = "image/x-emf"; - pExtension = ".emf"; + SAL_WARN("oox.shape", "unhandled graphic type " << static_cast<int>(aType)); + /*Earlier, even in case of unhandled graphic types we were + proceeding to write the image, which would eventually + write an empty image with a zero size, and return a valid + relationID, which is incorrect. + */ + return sRelId; } - } - else - { - SAL_WARN("oox.shape", "unhandled graphic type " << static_cast<int>(aType) ); - /*Earlier, even in case of unhandled graphic types we were - proceeding to write the image, which would eventually - write an empty image with a zero size, and return a valid - relationID, which is incorrect. - */ - return sRelId; - } - aData = aStream.GetData(); - nDataSize = aStream.GetEndOfData(); - break; + aData = aStream.GetData(); + nDataSize = aStream.GetEndOfData(); + break; + } } - } - Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer() - .appendAscii( GetComponentDir() ) - .append( "/media/image" + - OUString::number(mnImageCounter) ) - .appendAscii( pExtension ) - .makeStringAndClear(), - sMediaType ); - xOutStream->writeBytes( Sequence< sal_Int8 >( static_cast<const sal_Int8*>(aData), nDataSize ) ); - xOutStream->closeOutput(); + Reference<XOutputStream> xOutStream = mpFB->openFragmentStream( + OUStringBuffer() + .appendAscii(GetComponentDir()) + .append("/media/image" + OUString::number(mnImageCounter)) + .appendAscii(pExtension) + .makeStringAndClear(), + sMediaType); + xOutStream->writeBytes(Sequence<sal_Int8>(static_cast<const sal_Int8*>(aData), nDataSize)); + xOutStream->closeOutput(); + + const OString sRelPathToMedia = "media/image"; + OString sRelationCompPrefix; + if (bRelPathToMedia) + sRelationCompPrefix = "../"; + else + sRelationCompPrefix = GetRelationCompPrefix(); + sPath = OUStringBuffer() + .appendAscii(sRelationCompPrefix.getStr()) + .appendAscii(sRelPathToMedia.getStr()) + .append(static_cast<sal_Int32>(mnImageCounter++)) + .appendAscii(pExtension) + .makeStringAndClear(); + + if (GetDocumentType() != DOCUMENT_DOCX && !maExportGraphics.empty()) + maExportGraphics.top()[aChecksum] = sPath; + } - const OString sRelPathToMedia = "media/image"; - OString sRelationCompPrefix; - if ( bRelPathToMedia ) - sRelationCompPrefix = "../"; - else - sRelationCompPrefix = GetRelationCompPrefix(); - OUString sPath = OUStringBuffer() - .appendAscii( sRelationCompPrefix.getStr() ) - .appendAscii( sRelPathToMedia.getStr() ) - .append( static_cast<sal_Int32>(mnImageCounter ++) ) - .appendAscii( pExtension ) - .makeStringAndClear(); sRelId = mpFB->addRelation( mpFS->getOutputStream(), oox::getRelationship(Relationship::IMAGE), sPath ); |