drawinglayer/source/tools/emfphelperdata.cxx | 75 ++++++---- drawinglayer/source/tools/emfpimage.cxx | 34 ++++ drawinglayer/source/tools/emfpimage.hxx | 12 + drawinglayer/source/tools/primitive2dxmldump.cxx | 12 - emfio/qa/cppunit/emf/EmfImportTest.cxx | 53 +++++++ emfio/qa/cppunit/emf/data/TestEmfPlusDrawImagePointsWithMetafile.emf |binary svgio/qa/cppunit/SvgImportTest.cxx | 8 - 7 files changed, 156 insertions(+), 38 deletions(-)
New commits: commit 4dccec254c0f3159356f9fb74c2f8863288331c6 Author: Bartosz Kosiorek <[email protected]> AuthorDate: Mon Feb 16 20:54:27 2026 +0100 Commit: Bartosz Kosiorek <[email protected]> CommitDate: Fri Mar 6 22:13:37 2026 +0100 tdf166084 tdf138087 tdf59814 EMF+ Fix scale and trans DrawImagePoints Metafile This commit fixes the incorrect positioning and scaling of Metafiles rendered via EMF+ DrawImagePoints. Key changes: - Implemented a normalization matrix for Metafiles to properly map source coordinates (SrcRect) to the destination. - Updated EMFPImage to parse and store bounds from nested EMF headers, ensuring correct dimensions (x, y, width, height) are available. Change-Id: I07b4d99c334611a10e7faff42b76699f04fa1520 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201084 Tested-by: Jenkins Reviewed-by: Bartosz Kosiorek <[email protected]> diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx index ec5590e5eb26..37fbefa15f97 100644 --- a/drawinglayer/source/tools/emfphelperdata.cxx +++ b/drawinglayer/source/tools/emfphelperdata.cxx @@ -30,19 +30,20 @@ #include <wmfemfhelper.hxx> #include <drawinglayer/attribute/fillgraphicattribute.hxx> #include <drawinglayer/attribute/fontattribute.hxx> -#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx> -#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/metafileprimitive2d.hxx> #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> #include <drawinglayer/primitive2d/textlayoutdevice.hxx> #include <drawinglayer/primitive2d/textprimitive2d.hxx> -#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> -#include <drawinglayer/primitive2d/metafileprimitive2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> #include <basegfx/color/bcolor.hxx> #include <basegfx/color/bcolormodifier.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> @@ -1543,11 +1544,8 @@ namespace emfplushelper ReadPoint(rMS, x2, y2, flags); // upper-right ReadPoint(rMS, x3, y3, flags); // lower-left - SAL_INFO("drawinglayer.emf", "EMF+ destination points: " - << x1 << "," << y1 << " " << x2 << "," - << y2 << " " << x3 << "," << y3); dx = x1; - dy = y2; + dy = y1; dw = x2 - x1; dh = y3 - y1; fShearX = x3 - x1; @@ -1557,7 +1555,7 @@ namespace emfplushelper ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000)); SAL_INFO("drawinglayer.emf", - "EMF+ Rectangle: " << dx << "," << dy << " " << dw << "x" << dh); + "EMF+ Destination rectangle: " << dx << "," << dy << " " << dw << "x" << dh); Size aSize; if (image->type == ImageDataTypeBitmap) { @@ -1589,22 +1587,15 @@ namespace emfplushelper else if (sy + sh > aSize.Height()) dh = ((aSize.Height() - sy) / sh) * dh; } - else - SAL_INFO( - "drawinglayer.emf", - "EMF+ TODO: Add support for SrcRect to ImageDataTypeMetafile"); - const ::basegfx::B2DPoint aDstPoint(dx, dy); - const ::basegfx::B2DSize aDstSize(dw, dh); - - const basegfx::B2DHomMatrix aTransformMatrix - = maMapTransform - * basegfx::B2DHomMatrix( - /* Row 0, Column 0 */ aDstSize.getWidth(), + + const basegfx::B2DHomMatrix aDestMatrix = + maMapTransform * basegfx::B2DHomMatrix( + /* Row 0, Column 0 */ dw, /* Row 0, Column 1 */ fShearX, - /* Row 0, Column 2 */ aDstPoint.getX(), + /* Row 0, Column 2 */ dx, /* Row 1, Column 0 */ fShearY, - /* Row 1, Column 1 */ aDstSize.getHeight(), - /* Row 1, Column 2 */ aDstPoint.getY()); + /* Row 1, Column 1 */ dh, + /* Row 1, Column 2 */ dy); if (image->type == ImageDataTypeBitmap) { @@ -1615,18 +1606,46 @@ namespace emfplushelper { mrTargetHolders.Current().append( new drawinglayer::primitive2d::BitmapPrimitive2D( - aBmp, aTransformMatrix)); + aBmp, aDestMatrix)); } else SAL_WARN("drawinglayer.emf", "EMF+ warning: empty bitmap"); } else if (image->type == ImageDataTypeMetafile) { - GDIMetaFile aGDI(image->graphic.GetGDIMetaFile()); - aGDI.Clip(aSource); + SAL_INFO("drawinglayer.emf", + " Metafile Dimensions: " << image->x << "," << image->y + << " [" << image->width << "x" + << image->height); + + const GDIMetaFile aGDI(image->graphic.GetGDIMetaFile()); + basegfx::B2DHomMatrix aFinalMatrix = aDestMatrix; + if (image->width > 0 && image->height > 0 && sw > 0. && sh > 0.) + { + const double fRelSrcX = sx - static_cast<double>(image->x); + const double fRelSrcY = sy - static_cast<double>(image->y); + const basegfx::B2DHomMatrix aNormalizeMatrix( + /* Row 0, Column 0 */ static_cast<double>(image->width) / sw, + /* Row 0, Column 1 */ 0.0, + /* Row 0, Column 2 */ -fRelSrcX / sw, + /* Row 1, Column 0 */ 0.0, + /* Row 1, Column 1 */ static_cast<double>(image->height) / sh, + /* Row 1, Column 2 */ -fRelSrcY / sh); + + aFinalMatrix = aDestMatrix * aNormalizeMatrix; + } + + basegfx::B2DPolygon aClipPoly = basegfx::utils::createUnitPolygon(); + aClipPoly.transform(aDestMatrix); + mrTargetHolders.Current().append( - new drawinglayer::primitive2d::MetafilePrimitive2D(aTransformMatrix, - aGDI)); + new drawinglayer::primitive2d::MaskPrimitive2D( + basegfx::B2DPolyPolygon(aClipPoly), + drawinglayer::primitive2d::Primitive2DContainer { + new drawinglayer::primitive2d::MetafilePrimitive2D(aFinalMatrix, aGDI) + } + ) + ); } } else diff --git a/drawinglayer/source/tools/emfpimage.cxx b/drawinglayer/source/tools/emfpimage.cxx index 67a0cef99ed2..bb18de52bb27 100644 --- a/drawinglayer/source/tools/emfpimage.cxx +++ b/drawinglayer/source/tools/emfpimage.cxx @@ -60,6 +60,40 @@ namespace emfplushelper SvMemoryStream mfStream(const_cast<char *>(static_cast<char const *>(s.GetData()) + s.Tell()), dataSize, StreamMode::READ); filter.ImportGraphic(graphic, u"", mfStream); + mfStream.Seek(0); + // 1 = Wmf, 2 = WmfPlaceable, 3 = Emf, 4 = EmfPlusOnly, 5 = EmfPlusDual + if (mfType == 3 || mfType == 4 || mfType == 5) + { + sal_uInt32 dwRecordType, dwRecordSize, dSignature, nVersion; + sal_Int32 rclBoundsLeft, rclBoundsTop, rclBoundsRight, rclBoundsBottom; + + mfStream.ReadUInt32(dwRecordType); + mfStream.ReadUInt32(dwRecordSize); + mfStream.ReadInt32(rclBoundsLeft); + mfStream.ReadInt32(rclBoundsTop); + mfStream.ReadInt32(rclBoundsRight); + mfStream.ReadInt32(rclBoundsBottom); + + mfStream.SeekRel(16); + + mfStream.ReadUInt32(dSignature); + mfStream.ReadUInt32(nVersion); + + if (dwRecordType == 1 && dSignature == 0x464D4520) + { + x = rclBoundsLeft; + y = rclBoundsTop; + width = rclBoundsRight - rclBoundsLeft; + height = rclBoundsBottom - rclBoundsTop; + SAL_INFO("drawinglayer.emf", "EMF+ Nested EMF Header detected."); + SAL_INFO("drawinglayer.emf", + "EMF+ Bounds: [" << rclBoundsLeft << "," << rclBoundsTop << ", " + << rclBoundsRight << ", " << rclBoundsBottom << "]"); + SAL_INFO("drawinglayer.emf", + "EMF+ Version: 0x" << std::hex << nVersion << std::dec); + } + } + // debug code - write the stream to debug file /tmp/emf-stream.emf #if OSL_DEBUG_LEVEL > 1 mfStream.Seek(0); diff --git a/drawinglayer/source/tools/emfpimage.hxx b/drawinglayer/source/tools/emfpimage.hxx index 75e42e7d21e4..05e2e098ee63 100644 --- a/drawinglayer/source/tools/emfpimage.hxx +++ b/drawinglayer/source/tools/emfpimage.hxx @@ -35,12 +35,24 @@ namespace emfplushelper struct EMFPImage : public EMFPObject { sal_uInt32 type; + sal_Int32 x; + sal_Int32 y; sal_Int32 width; sal_Int32 height; sal_Int32 stride; sal_uInt32 pixelFormat; Graphic graphic; + EMFPImage(): + type(ImageDataTypeUnknown), + x(0), + y(0), + width(0), + height(0), + stride(0), + pixelFormat(0) + {} + void Read(SvMemoryStream &s, sal_uInt32 dataSize, bool bUseWholeStream); }; } diff --git a/drawinglayer/source/tools/primitive2dxmldump.cxx b/drawinglayer/source/tools/primitive2dxmldump.cxx index a1fffa4cbee9..4cd2d1604b16 100644 --- a/drawinglayer/source/tools/primitive2dxmldump.cxx +++ b/drawinglayer/source/tools/primitive2dxmldump.cxx @@ -125,12 +125,12 @@ void writePolyPolygon(::tools::XmlWriter& rWriter, const basegfx::B2DPolyPolygon { rWriter.startElement("polypolygon"); const basegfx::B2DRange aB2DRange(rB2DPolyPolygon.getB2DRange()); - rWriter.attributeDouble("height", aB2DRange.getHeight()); - rWriter.attributeDouble("width", aB2DRange.getWidth()); - rWriter.attributeDouble("minx", aB2DRange.getMinX()); - rWriter.attributeDouble("miny", aB2DRange.getMinY()); - rWriter.attributeDouble("maxx", aB2DRange.getMaxX()); - rWriter.attributeDouble("maxy", aB2DRange.getMaxY()); + rWriter.attribute("height", aB2DRange.getHeight()); + rWriter.attribute("width", aB2DRange.getWidth()); + rWriter.attribute("minx", aB2DRange.getMinX()); + rWriter.attribute("miny", aB2DRange.getMinY()); + rWriter.attribute("maxx", aB2DRange.getMaxX()); + rWriter.attribute("maxy", aB2DRange.getMaxY()); rWriter.attribute("path", basegfx::utils::exportToSvgD(rB2DPolyPolygon, true, true, false)); for (basegfx::B2DPolygon const& rPolygon : rB2DPolyPolygon) diff --git a/emfio/qa/cppunit/emf/EmfImportTest.cxx b/emfio/qa/cppunit/emf/EmfImportTest.cxx index 7643e44ce9fa..c4a9b3ee2730 100644 --- a/emfio/qa/cppunit/emf/EmfImportTest.cxx +++ b/emfio/qa/cppunit/emf/EmfImportTest.cxx @@ -147,6 +147,59 @@ CPPUNIT_TEST_FIXTURE(Test, testDrawImagePointsTypeBitmap) "b63539,b53335,ba3236,a2393e,1c0000"); } +CPPUNIT_TEST_FIXTURE(Test, testDrawImagePointsWithMetafile) +{ + // tdf166084 tdf138087 tdf59814 EMF+ file with ObjectTypeImage, DrawImagePoints + // The test is checking the position and scale of embedded metafile drawed by DrawImagePoints + + Primitive2DSequence aSequence + = parseEmf(u"/emfio/qa/cppunit/emf/data/TestEmfPlusDrawImagePointsWithMetafile.emf"); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength())); + drawinglayer::Primitive2dXmlDump dumper; + xmlDocUniquePtr pDocument = dumper.dumpAndParse(Primitive2DContainer(aSequence)); + CPPUNIT_ASSERT(pDocument); + + assertXPath(pDocument, aXPathPrefix + "mask/polypolygon", 1); + assertXPath(pDocument, aXPathPrefix + "mask/polypolygon", "minx", u"0"); + assertXPath(pDocument, aXPathPrefix + "mask/polypolygon", "miny", u"0"); + assertXPath(pDocument, aXPathPrefix + "mask/polypolygon", "maxx", u"6921"); + assertXPath(pDocument, aXPathPrefix + "mask/polypolygon", "maxy", u"20306"); + assertXPath(pDocument, aXPathPrefix + "mask/mask/metafile/transform", "xy11", u"1"); + assertXPath(pDocument, aXPathPrefix + "mask/mask/metafile/transform", "xy12", u"0"); + assertXPath(pDocument, aXPathPrefix + "mask/mask/metafile/transform", "xy13", u"-3279"); + assertXPath(pDocument, aXPathPrefix + "mask/mask/metafile/transform", "xy21", u"0"); + assertXPath(pDocument, aXPathPrefix + "mask/mask/metafile/transform", "xy22", u"1"); + assertXPath(pDocument, aXPathPrefix + "mask/mask/metafile/transform", "xy23", u"-176"); + + assertXPath(pDocument, aXPathPrefix + "mask/mask/metafile/transform/mask/polypolygon", "height", + u"15747"); + assertXPath(pDocument, aXPathPrefix + "mask/mask/metafile/transform/mask/polypolygon", "width", + u"10088"); + + assertXPath( + pDocument, + aXPathPrefix + "mask/mask/metafile/transform/mask/mask/metafile/transform/mask/group", 17); + + assertXPath(pDocument, + aXPathPrefix + "mask/mask/metafile/transform/mask/mask/metafile/transform", "xy11", + u"1"); + assertXPath(pDocument, + aXPathPrefix + "mask/mask/metafile/transform/mask/mask/metafile/transform", "xy12", + u"0"); + assertXPath(pDocument, + aXPathPrefix + "mask/mask/metafile/transform/mask/mask/metafile/transform", "xy13", + u"3"); + assertXPath(pDocument, + aXPathPrefix + "mask/mask/metafile/transform/mask/mask/metafile/transform", "xy21", + u"0"); + assertXPath(pDocument, + aXPathPrefix + "mask/mask/metafile/transform/mask/mask/metafile/transform", "xy22", + u"1"); + assertXPath(pDocument, + aXPathPrefix + "mask/mask/metafile/transform/mask/mask/metafile/transform", "xy23", + u"-140"); +} + CPPUNIT_TEST_FIXTURE(Test, testFillRectsWithTextureBrush) { // tdf#129342 tdf#170985 EMF+ file with ObjectTypeBrush, ObjectTypepen, FillRects diff --git a/emfio/qa/cppunit/emf/data/TestEmfPlusDrawImagePointsWithMetafile.emf b/emfio/qa/cppunit/emf/data/TestEmfPlusDrawImagePointsWithMetafile.emf new file mode 100644 index 000000000000..ee0069284fca Binary files /dev/null and b/emfio/qa/cppunit/emf/data/TestEmfPlusDrawImagePointsWithMetafile.emf differ diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx index 8c3e39afe733..f8a6892c7c61 100644 --- a/svgio/qa/cppunit/SvgImportTest.cxx +++ b/svgio/qa/cppunit/SvgImportTest.cxx @@ -1424,8 +1424,8 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf158445) xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/tdf158445.svg"); assertXPath(pDocument, "/primitive2D/transform/mask/transform/transform/transform/polypolygoncolor", "color", u"#000000"); - assertXPath(pDocument, "/primitive2D/transform/mask/transform/transform/transform/polypolygoncolor/polypolygon", "height", u"8.052"); - assertXPath(pDocument, "/primitive2D/transform/mask/transform/transform/transform/polypolygoncolor/polypolygon", "width", u"5.328"); + assertXPath(pDocument, "/primitive2D/transform/mask/transform/transform/transform/polypolygoncolor/polypolygon", "height", u"8"); + assertXPath(pDocument, "/primitive2D/transform/mask/transform/transform/transform/polypolygoncolor/polypolygon", "width", u"5"); } CPPUNIT_TEST_FIXTURE(Test, testTdf159594) @@ -1433,8 +1433,8 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf159594) xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/tdf159594.svg"); assertXPath(pDocument, "/primitive2D/transform/transform/polypolygoncolor", "color", u"#000000"); - assertXPath(pDocument, "/primitive2D/transform/transform/polypolygoncolor/polypolygon", "height", u"11.671875"); - assertXPath(pDocument, "/primitive2D/transform/transform/polypolygoncolor/polypolygon", "width", u"7.5"); + assertXPath(pDocument, "/primitive2D/transform/transform/polypolygoncolor/polypolygon", "height", u"12"); + assertXPath(pDocument, "/primitive2D/transform/transform/polypolygoncolor/polypolygon", "width", u"8"); } CPPUNIT_TEST_FIXTURE(Test, testTdf97663)
