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 <xiscofauli@libreoffice.org>
Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155657
diff --git a/svgio/inc/svgcharacternode.hxx b/svgio/inc/svgcharacternode.hxx
index 391c402..059aa9e 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 2d5f98e..7876879 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 d5d86c5..92ed831 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 c52e205..42cb98c 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -740,7 +740,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 0000000..6b3bb3c
--- /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 2b88944..9ba70ff 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 4521399..d552db8 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:
@@ -552,7 +564,7 @@ namespace
            if(pTextNode)
            {
                // cleanup read strings
                whiteSpaceHandling(pTextNode, static_cast< SvgTextNode*>(pTextNode), nullptr);
                whiteSpaceHandling(pTextNode, static_cast< SvgTspanNode*>(pTextNode), nullptr);
            }
        }