From ac8fd70e4e23d12857f766a4918747bbc5a7cb9b Mon Sep 17 00:00:00 2001
From: Xisco Fauli <xiscofauli@libreoffice.org>
Date: Tue, 1 Aug 2023 18:19:20 +0200
Subject: tdf#156283: take remaing dx value into consideration too

Change-Id: I27c6f12edacd68c7f956b67dcf9ef0cc5045e3d1
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155169
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
---
 svgio/inc/svgcharacternode.hxx              |  2 ++
 svgio/qa/cppunit/SvgImportTest.cxx          | 36 +++++++++++++++++++++++++++-
 svgio/qa/cppunit/data/tdf156283.svg         |  4 ++++
 svgio/source/svgreader/svgcharacternode.cxx | 37 ++++++++++++++++++++---------
 4 files changed, 67 insertions(+), 12 deletions(-)
 create mode 100644 svgio/qa/cppunit/data/tdf156283.svg

(limited to 'svgio')

diff --git a/svgio/inc/svgcharacternode.hxx b/svgio/inc/svgcharacternode.hxx
index 5fad2008ba1c..8861055f8e65 100644
--- a/svgio/inc/svgcharacternode.hxx
+++ b/svgio/inc/svgcharacternode.hxx
@@ -82,6 +82,7 @@ namespace svgio::svgreader
             SvgTextPosition*            mpParent;
             ::std::vector< double >     maX;
             ::std::vector< double >     maY;
+            ::std::vector< double >     maDx;
             ::std::vector< double >     maRotate;
             double                      mfTextLength;
 
@@ -103,6 +104,7 @@ namespace svgio::svgreader
             // data read access
             const SvgTextPosition* getParent() const { return mpParent; }
             const ::std::vector< double >& getX() const { return maX; }
+            const ::std::vector< double >& getDx() const { return maDx; }
             double getTextLength() const { return mfTextLength; }
             bool getLengthAdjust() const { return mbLengthAdjust; }
             bool getAbsoluteX() const { return mbAbsoluteX; }
diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx
index 5845b7ea6f52..9bc0dfc24e19 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -1431,6 +1431,39 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156269)
     assertXPath(pDocument, "//textsimpleportion[@text='two']", "fontcolor", "#000000");
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf156283)
+{
+    Primitive2DSequence aSequence = parseSvg(u"/svgio/qa/cppunit/data/tdf156283.svg");
+    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
+
+    drawinglayer::Primitive2dXmlDump dumper;
+    xmlDocUniquePtr pDocument = dumper.dumpAndParse(Primitive2DContainer(aSequence));
+
+    CPPUNIT_ASSERT (pDocument);
+
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "width", "16");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "height", "16");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "x", "30");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "y", "20");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "text", "ABC");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "dx0", "41");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "dx1", "52");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "dx2", "63");
+
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "width", "16");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "height", "16");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "x", "30");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "y", "30");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "text", "ABC");
+
+    // Without the fix in place, this test would have failed with
+    // - Expected: 41
+    // - Actual  : 12
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "dx0", "41");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "dx1", "52");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "dx2", "63");
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testTdf156271)
 {
     Primitive2DSequence aSequence = parseSvg(u"/svgio/qa/cppunit/data/tdf156271.svg");
@@ -1474,7 +1507,8 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156271)
     assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]", "x", "40");
     assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]", "y", "40");
     assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]", "text", "AB");
-    assertXPathNoAttribute(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]", "dx0");
+    assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]", "dx0", "12");
+    assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]", "dx1", "23");
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testTdf149880)
diff --git a/svgio/qa/cppunit/data/tdf156283.svg b/svgio/qa/cppunit/data/tdf156283.svg
new file mode 100644
index 000000000000..ee8c46ef5c89
--- /dev/null
+++ b/svgio/qa/cppunit/data/tdf156283.svg
@@ -0,0 +1,4 @@
+<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
+	<text x="30 71" y="20">ABC</text>
+	<text x="0" dx="30 29" y="30">ABC</text>
+</svg>
diff --git a/svgio/source/svgreader/svgcharacternode.cxx b/svgio/source/svgreader/svgcharacternode.cxx
index e7f08c908ad4..5fab853ed0a0 100644
--- a/svgio/source/svgreader/svgcharacternode.cxx
+++ b/svgio/source/svgreader/svgcharacternode.cxx
@@ -263,7 +263,7 @@ namespace svgio::svgreader
 
                 // prepare TextArray
                 ::std::vector< double > aTextArray(rSvgTextPosition.getX());
-                if(!aTextArray.empty() && aTextArray.size() < nLength)
+                if(aTextArray.size() < nLength)
                 {
                     const sal_uInt32 nArray(aTextArray.size());
 
@@ -273,17 +273,23 @@ namespace svgio::svgreader
                     {
                         fStartX = rSvgTextPosition.getParent()->getPosition().getX();
                     }
-                    else
+                    else if (!aTextArray.empty())
                     {
                         fStartX = aTextArray[nArray - 1];
                     }
 
                     ::std::vector< double > aExtendArray(aTextLayouterDevice.getTextArray(getText(), nArray, nLength - nArray));
-                    aTextArray.reserve(nLength);
+                    ::std::vector< double > aDxArray(rSvgTextPosition.getDx());
+                    double fComulativeDx(0.0);
 
-                    for(const auto &a : aExtendArray)
+                    aTextArray.reserve(nLength);
+                    for(size_t a = 0; a < aExtendArray.size(); ++a)
                     {
-                        aTextArray.push_back(a + fStartX);
+                        if (a < aDxArray.size())
+                        {
+                            fComulativeDx += aDxArray[a];
+                        }
+                        aTextArray.push_back(aExtendArray[a] + fStartX + fComulativeDx);
                     }
                 }
 
@@ -642,16 +648,25 @@ namespace svgio::svgreader
             // fill deltas to maX
             maX.reserve(nSizeX);
 
-            for(sal_uInt32 a(1); a < nSizeX; a++)
+            for(sal_uInt32 a(1); a < std::max(nSizeX, nSizeDx); ++a)
             {
-                double nPos = rSvgTextPositions.getX()[a].solve(rInfoProvider, NumberType::xcoordinate) - maPosition.getX();
+                if (a < nSizeX)
+                {
+                    double nPos = rSvgTextPositions.getX()[a].solve(rInfoProvider, NumberType::xcoordinate) - maPosition.getX();
 
-                if(a < nSizeDx)
+                    if(a < nSizeDx)
+                    {
+                        nPos += rSvgTextPositions.getDx()[a].solve(rInfoProvider, NumberType::xcoordinate);
+                    }
+
+                    maX.push_back(nPos);
+                }
+                else
                 {
-                    nPos += rSvgTextPositions.getDx()[a].solve(rInfoProvider, NumberType::xcoordinate);
+                    // Apply them later since it also needs the character width to calculate
+                    // the final character position
+                    maDx.push_back(rSvgTextPositions.getDx()[a].solve(rInfoProvider, NumberType::xcoordinate));
                 }
-
-                maX.push_back(nPos);
             }
 
             // get text positions Y
-- 
cgit