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)

Reply via email to