svgio/inc/svgcharacternode.hxx                |    4 +-
 svgio/inc/svgtextnode.hxx                     |    7 ----
 svgio/inc/svgtspannode.hxx                    |    7 ++++
 svgio/qa/cppunit/SvgImportTest.cxx            |   38 ++++++++++++++++++++++++++
 svgio/qa/cppunit/data/tdf156616.svg           |   29 +++++++++++++++++++
 svgio/source/svgreader/svgcharacternode.cxx   |    8 ++++-
 svgio/source/svgreader/svgdocumenthandler.cxx |   22 +++++++++++----
 7 files changed, 99 insertions(+), 16 deletions(-)

New commits:
commit dcb3cb0bd4e4bf2bed05ae3b9d370e17a331a9b1
Author:     Xisco Fauli <xiscofa...@libreoffice.org>
AuthorDate: Sat Aug 12 02:28:02 2023 +0200
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Sat Aug 12 10:00:29 2023 +0200

    tdf#156616: check if character's parent has x or y
    
    if so, only concatenate the characters that are in the same line
    so the alignment will be calculated based on the
    line's width
    
    Change-Id: I704370c0a470f8b4cff97c51ad9863158118ee8a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155636
    Tested-by: Jenkins
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/svgio/inc/svgcharacternode.hxx b/svgio/inc/svgcharacternode.hxx
index 391c4029e46c..059aa9ece1fd 100644
--- a/svgio/inc/svgcharacternode.hxx
+++ b/svgio/inc/svgcharacternode.hxx
@@ -40,7 +40,7 @@ namespace svgio::svgreader
             // keep a copy of string data before space handling
             OUString           maTextBeforeSpaceHandling;
 
-            SvgTextNode*        mpTextParent;
+            SvgTspanNode*        mpParentLine;
 
             /// local helpers
             rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> 
createSimpleTextPrimitive(
@@ -68,7 +68,7 @@ namespace svgio::svgreader
             /// Text content
             const OUString& getText() const { return maText; }
 
-            void setTextParent(SvgTextNode* pTextParent) { mpTextParent = 
pTextParent; }
+            void setParentLine(SvgTspanNode* pParentLine) { mpParentLine = 
pParentLine; }
         };
 
 } // end of namespace svgio::svgreader
diff --git a/svgio/inc/svgtextnode.hxx b/svgio/inc/svgtextnode.hxx
index 2d5f98ec18fc..787687977e11 100644
--- a/svgio/inc/svgtextnode.hxx
+++ b/svgio/inc/svgtextnode.hxx
@@ -33,10 +33,6 @@ namespace svgio::svgreader
             std::optional<basegfx::B2DHomMatrix>
                                     mpaTransform;
 
-            // The text line composed by the different SvgCharacterNode 
children
-            // it will be used to calculate their alignment
-            OUString maTextLine;
-
             /// local helpers
             void DecomposeChild(
                 const SvgNode& rCandidate,
@@ -59,9 +55,6 @@ namespace svgio::svgreader
             /// transform content, set if found in current context
             const std::optional<basegfx::B2DHomMatrix>& getTransform() const { 
return mpaTransform; }
             void setTransform(const std::optional<basegfx::B2DHomMatrix>& 
pMatrix) { mpaTransform = pMatrix; }
-
-            void concatenateTextLine(std::u16string_view rText) {maTextLine += 
rText;}
-            const OUString& getTextLine() const { return maTextLine; }
         };
 
 } // end of namespace svgio::svgreader
diff --git a/svgio/inc/svgtspannode.hxx b/svgio/inc/svgtspannode.hxx
index d5d86c5a7c1a..92ed8319c628 100644
--- a/svgio/inc/svgtspannode.hxx
+++ b/svgio/inc/svgtspannode.hxx
@@ -39,6 +39,10 @@ namespace svgio::svgreader
 
             bool                    mbLengthAdjust : 1; // true = spacing, 
false = spacingAndGlyphs
 
+            // The text line composed by the different SvgCharacterNode 
children
+            // it will be used to calculate their alignment
+            OUString maTextLine;
+
         public:
             SvgTspanNode(
                 SVGToken aType,
@@ -78,6 +82,9 @@ namespace svgio::svgreader
             /// LengthAdjust content
             bool getLengthAdjust() const { return mbLengthAdjust; }
             void setLengthAdjust(bool bNew) { mbLengthAdjust = bNew; }
+
+            void concatenateTextLine(std::u16string_view rText) {maTextLine += 
rText;}
+            const OUString& getTextLine() const { return maTextLine; }
         };
 
 } // end of namespace svgio::svgreader
diff --git a/svgio/qa/cppunit/SvgImportTest.cxx 
b/svgio/qa/cppunit/SvgImportTest.cxx
index cf66e5bb623f..d97892f8d98f 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -744,7 +744,45 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf85770)
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", 
"text", " End");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", 
"height", "11");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", 
"familyname", "Times New Roman");
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf156616)
+{
+    Primitive2DSequence aSequence = 
parseSvg(u"/svgio/qa/cppunit/data/tdf156616.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]", 
"text", "First");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "x", 
"114");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "y", 
"103");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", 
"text", " line");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "x", 
"142");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "y", 
"103");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", 
"text", "Second line");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "x", 
"114");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "y", 
"122");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]", 
"text", "First");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]", "x", 
"86");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]", "y", 
"153");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]", 
"text", " line");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]", "x", 
"114");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]", "y", 
"153");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]", 
"text", "Second line");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]", "x", 
"77");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]", "y", 
"172");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[7]", 
"text", "First");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[7]", "x", 
"59");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[7]", "y", 
"203");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[8]", 
"text", " line");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[8]", "x", 
"87");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[8]", "y", 
"203");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[9]", 
"text", "Second line");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[9]", "x", 
"40");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[9]", "y", 
"222");
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testTdf79163)
diff --git a/svgio/qa/cppunit/data/tdf156616.svg 
b/svgio/qa/cppunit/data/tdf156616.svg
new file mode 100644
index 000000000000..6b3bb3c6c6a5
--- /dev/null
+++ b/svgio/qa/cppunit/data/tdf156616.svg
@@ -0,0 +1,29 @@
+<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg";>
+      <text
+         style="text-anchor:start"
+         x="114"
+         y="103"><tspan
+           x="114"
+           y="103"><tspan>First </tspan>line </tspan><tspan
+           x="114"
+           y="122">Second line</tspan>
+      </text>
+      <text
+         style="text-anchor:middle"
+         x="114"
+         y="153"><tspan
+           x="114"
+           y="153"><tspan>First </tspan>line </tspan><tspan
+           x="114"
+           y="172">Second line</tspan>
+      </text>
+      <text
+         style="text-anchor:end"
+         x="114"
+         y="203"><tspan
+           x="114"
+           y="203"><tspan>First </tspan>line </tspan><tspan
+           x="114"
+           y="222">Second line</tspan>
+      </text>
+</svg>
diff --git a/svgio/source/svgreader/svgcharacternode.cxx 
b/svgio/source/svgreader/svgcharacternode.cxx
index 2b88944aa8d0..9ba70ffb3ef5 100644
--- a/svgio/source/svgreader/svgcharacternode.cxx
+++ b/svgio/source/svgreader/svgcharacternode.cxx
@@ -79,7 +79,7 @@ namespace svgio::svgreader
             OUString aText)
         :   SvgNode(SVGToken::Character, rDocument, pParent),
             maText(std::move(aText)),
-            mpTextParent(nullptr)
+            mpParentLine(nullptr)
         {
         }
 
@@ -251,7 +251,7 @@ namespace svgio::svgreader
                 }
 
                 // Use the whole text line to calculate the align position
-                double 
fWholeTextLineWidth(aTextLayouterDevice.getTextWidth(mpTextParent->getTextLine(),
 0, mpTextParent->getTextLine().getLength()));
+                double 
fWholeTextLineWidth(aTextLayouterDevice.getTextWidth(mpParentLine->getTextLine(),
 0, mpParentLine->getTextLine().getLength()));
                 // apply TextAlign
                 switch(aTextAlign)
                 {
@@ -482,6 +482,10 @@ namespace svgio::svgreader
                     
if(pPreviousCharacterNode->maTextBeforeSpaceHandling[nLastLength - 1] != ' ' && 
maTextBeforeSpaceHandling[0] != ' ')
                         bAddGap = false;
 
+                    // Do not add a gap if this node and last node are in 
different lines
+                    if(pPreviousCharacterNode->mpParentLine != mpParentLine)
+                        bAddGap = false;
+
                     // With this option a baseline shift between two char 
parts ('words')
                     // will not add a space 'gap' to the end of the (non-last) 
word. This
                     // seems to be the standard behaviour, see last bugdoc 
attached #122524#
diff --git a/svgio/source/svgreader/svgdocumenthandler.cxx 
b/svgio/source/svgreader/svgdocumenthandler.cxx
index 16f100e0b01e..ea70f3c5cbd6 100644
--- a/svgio/source/svgreader/svgdocumenthandler.cxx
+++ b/svgio/source/svgreader/svgdocumenthandler.cxx
@@ -62,7 +62,7 @@ namespace svgio::svgreader
 
 namespace
 {
-    svgio::svgreader::SvgCharacterNode* 
whiteSpaceHandling(svgio::svgreader::SvgNode const * pNode, 
svgio::svgreader::SvgTextNode* pText, svgio::svgreader::SvgCharacterNode* pLast)
+    svgio::svgreader::SvgCharacterNode* 
whiteSpaceHandling(svgio::svgreader::SvgNode const * pNode, 
svgio::svgreader::SvgTspanNode* pParentLine, 
svgio::svgreader::SvgCharacterNode* pLast)
     {
         if(pNode)
         {
@@ -82,19 +82,31 @@ namespace
                             // clean whitespace in text span
                             svgio::svgreader::SvgCharacterNode* pCharNode = 
static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate);
 
+                            pCharNode->setParentLine(pParentLine);
+
                             pCharNode->whiteSpaceHandling();
                             pLast = pCharNode->addGap(pLast);
 
-                            pCharNode->setTextParent(pText);
-                            pText->concatenateTextLine(pCharNode->getText());
+                            
pParentLine->concatenateTextLine(pCharNode->getText());
                             break;
                         }
                         case SVGToken::Tspan:
+                        {
+                            svgio::svgreader::SvgTspanNode* pTspanNode = 
static_cast< svgio::svgreader::SvgTspanNode* >(pCandidate);
+
+                            // If x or y exist it means it's a new line of text
+                            if(!pTspanNode->getX().empty() || 
!pTspanNode->getY().empty())
+                                pParentLine = pTspanNode;
+
+                            // recursively clean whitespaces in subhierarchy
+                            pLast = whiteSpaceHandling(pCandidate, 
pParentLine, pLast);
+                            break;
+                        }
                         case SVGToken::TextPath:
                         case SVGToken::Tref:
                         {
                             // recursively clean whitespaces in subhierarchy
-                            pLast = whiteSpaceHandling(pCandidate, pText, 
pLast);
+                            pLast = whiteSpaceHandling(pCandidate, 
pParentLine, pLast);
                             break;
                         }
                         default:
@@ -480,7 +492,7 @@ namespace
             if(pTextNode)
             {
                 // cleanup read strings
-                whiteSpaceHandling(pTextNode, static_cast< 
SvgTextNode*>(pTextNode), nullptr);
+                whiteSpaceHandling(pTextNode, static_cast< 
SvgTspanNode*>(pTextNode), nullptr);
             }
         }
 

Reply via email to