filter/source/svg/svgwriter.cxx | 101 ++++++++++++++++++++++++++-------------- filter/source/svg/svgwriter.hxx | 3 + 2 files changed, 70 insertions(+), 34 deletions(-)
New commits: commit b7a51924b51b3a24891803141c108c21ba540ac5 Author: Noel Grandin <[email protected]> AuthorDate: Fri Nov 21 19:26:40 2025 +0200 Commit: Noel Grandin <[email protected]> CommitDate: Sat Nov 22 18:18:16 2025 +0100 tdf#169532 Improve draw/impress export to svg with lots of bitmaps use the svg <use> element to avoid exporting bitmaps more than once. With this document we were exporting the same image hundreds of times. Change-Id: I9c2501ce67c263720719bac3d801854b48740905 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194344 Reviewed-by: Noel Grandin <[email protected]> Tested-by: Jenkins diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index 97578079766c..65e1a38e842f 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -2919,41 +2919,72 @@ void SVGActionWriter::ImplWriteBmp( const Bitmap& rBmp, { if( rBmp.IsEmpty() ) return; - if( mpEmbeddedBitmapsMap && !mpEmbeddedBitmapsMap->empty()) + BitmapChecksum nChecksum = rBmp.GetChecksum(); + if( mpEmbeddedBitmapsMap && mpEmbeddedBitmapsMap->contains( nChecksum )) { - BitmapChecksum nChecksum = rBmp.GetChecksum(); - if( mpEmbeddedBitmapsMap->contains( nChecksum ) ) - { - // <use transform="translate(?) scale(?)" xlink:ref="?" > - OUString sTransform; - - Point aPoint; - ImplMap( rPt, aPoint ); - if( aPoint.X() != 0 || aPoint.Y() != 0 ) - sTransform = "translate(" + OUString::number( aPoint.X() ) + ", " + OUString::number( aPoint.Y() ) + ")"; - - Size aSize; - ImplMap( rSz, aSize ); - - MapMode aSourceMode( MapUnit::MapPixel ); - Size aPrefSize = OutputDevice::LogicToLogic( rSrcSz, aSourceMode, maTargetMapMode ); - Fraction aFractionX( aSize.Width(), aPrefSize.Width() ); - Fraction aFractionY( aSize.Height(), aPrefSize.Height() ); - double scaleX = rtl_math_round( double(aFractionX), 3, rtl_math_RoundingMode::rtl_math_RoundingMode_Corrected ); - double scaleY = rtl_math_round( double(aFractionY), 3, rtl_math_RoundingMode::rtl_math_RoundingMode_Corrected ); - if( !rtl_math_approxEqual( scaleX, 1.0 ) || !rtl_math_approxEqual( scaleY, 1.0 ) ) - sTransform += " scale(" + OUString::number( double(aFractionX) ) + ", " + OUString::number( double(aFractionY) ) + ")"; - - if( !sTransform.isEmpty() ) - mrExport.AddAttribute(aXMLAttrTransform, sTransform); - - // referenced bitmap template - OUString sRefId = "#bitmap(" + OUString::number( nChecksum ) + ")"; - mrExport.AddAttribute(aXMLAttrXLinkHRef, sRefId); - - SvXMLElementExport aRefElem(mrExport, u"use"_ustr, true, true); - return; - } + // <use transform="translate(?) scale(?)" xlink:ref="?" > + OUString sTransform; + + Point aPoint; + ImplMap( rPt, aPoint ); + if( aPoint.X() != 0 || aPoint.Y() != 0 ) + sTransform = "translate(" + OUString::number( aPoint.X() ) + ", " + OUString::number( aPoint.Y() ) + ")"; + + Size aSize; + ImplMap( rSz, aSize ); + + MapMode aSourceMode( MapUnit::MapPixel ); + Size aPrefSize = OutputDevice::LogicToLogic( rSrcSz, aSourceMode, maTargetMapMode ); + Fraction aFractionX( aSize.Width(), aPrefSize.Width() ); + Fraction aFractionY( aSize.Height(), aPrefSize.Height() ); + double scaleX = rtl_math_round( double(aFractionX), 3, rtl_math_RoundingMode::rtl_math_RoundingMode_Corrected ); + double scaleY = rtl_math_round( double(aFractionY), 3, rtl_math_RoundingMode::rtl_math_RoundingMode_Corrected ); + if( !rtl_math_approxEqual( scaleX, 1.0 ) || !rtl_math_approxEqual( scaleY, 1.0 ) ) + sTransform += " scale(" + OUString::number( double(aFractionX) ) + ", " + OUString::number( double(aFractionY) ) + ")"; + + if( !sTransform.isEmpty() ) + mrExport.AddAttribute(aXMLAttrTransform, sTransform); + + // referenced bitmap template + OUString sRefId = "#bitmap(" + OUString::number( nChecksum ) + ")"; + mrExport.AddAttribute(aXMLAttrXLinkHRef, sRefId); + + SvXMLElementExport aRefElem(mrExport, u"use"_ustr, true, true); + return; + } + + // check if we can re-use another element to avoid exporting this bitmap more than once + if( maBitmapElements.contains( nChecksum )) + { + const tools::Rectangle & rSourceRect = maBitmapElements[nChecksum]; + + // <use transform="translate(?) scale(?)" xlink:ref="?" > + OUString sTransform; + + Point aPoint; + ImplMap( rPt, aPoint ); + if( aPoint.X() != 0 || aPoint.Y() != 0 ) + sTransform = "translate(" + OUString::number( rSourceRect.TopLeft().X() - aPoint.X() ) + + ", " + OUString::number( rSourceRect.TopLeft().Y() - aPoint.Y() ) + ")"; + + Size aSize; + ImplMap( rSz, aSize ); + + Fraction aFractionX( aSize.Width(), rSourceRect.GetWidth() ); + Fraction aFractionY( aSize.Height(), rSourceRect.GetHeight() ); + double scaleX = rtl_math_round( double(aFractionX), 3, rtl_math_RoundingMode::rtl_math_RoundingMode_Corrected ); + double scaleY = rtl_math_round( double(aFractionY), 3, rtl_math_RoundingMode::rtl_math_RoundingMode_Corrected ); + if( !rtl_math_approxEqual( scaleX, 1.0 ) || !rtl_math_approxEqual( scaleY, 1.0 ) ) + sTransform += " scale(" + OUString::number( double(aFractionX) ) + ", " + OUString::number( double(aFractionY) ) + ")"; + + if( !sTransform.isEmpty() ) + mrExport.AddAttribute(aXMLAttrTransform, sTransform); + + // referenced bitmap template + mrExport.AddAttribute(aXMLAttrXLinkHRef, "#bitmap(" + OUString::number( nChecksum ) + ")"); + + SvXMLElementExport aRefElem(mrExport, u"use"_ustr, true, true); + return; } Bitmap aBmp( rBmp ); @@ -3043,6 +3074,8 @@ void SVGActionWriter::ImplWriteBmp( const Bitmap& rBmp, mrExport.AddAttribute(aXMLAttrWidth, OUString::number(aSz.Width())); mrExport.AddAttribute(aXMLAttrHeight, OUString::number(aSz.Height())); + maBitmapElements[nChecksum] = tools::Rectangle(aPt, aSz); + // If we have a media object (a video), export the video. // Also, use the image generated above as the video poster (thumbnail). SdrMediaObj* pMediaObj diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx index 1ddd34ee7603..8b9b531caa38 100644 --- a/filter/source/svg/svgwriter.hxx +++ b/filter/source/svg/svgwriter.hxx @@ -314,6 +314,9 @@ private: bool mbIsPlaceholderShape; const MetaBitmapActionMap* mpEmbeddedBitmapsMap; bool mbIsPreview; + /// Map of the bitmap we have exported via elements with embedded "data:" URIs. + /// So that we can re-use them. + std::unordered_map<BitmapChecksum, tools::Rectangle> maBitmapElements; tools::Long ImplMap( sal_Int32 nVal ) const;
