summaryrefslogtreecommitdiff
path: root/oox
diff options
context:
space:
mode:
authorTünde Tóth <toth.tunde@nisz.hu>2022-03-22 09:47:57 +0100
committerLászló Németh <nemeth@numbertext.org>2022-03-30 18:24:45 +0200
commitaea8043bc5f5187498fa450505d6de9d6986e2a6 (patch)
tree2047231de903ae94656848f14d31a3523d429fe8 /oox
parent94ace0e51744f82b58156392e53b4c4ad819e4bf (diff)
tdf#74670 tdf#91286 PPTX XLSX export: save image once
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.cxx207
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 );