svgio/inc/svgcharacternode.hxx                |    8 --
 svgio/qa/cppunit/SvgImportTest.cxx            |   46 ++++++-------
 svgio/source/svgreader/svgcharacternode.cxx   |   87 +++++++++++++++++---------
 svgio/source/svgreader/svgdocumenthandler.cxx |   83 +++++++++++++++---------
 4 files changed, 134 insertions(+), 90 deletions(-)

New commits:
commit 44b6b90080e7585e46250fb4076b210619e10bb3
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Thu Apr 18 13:23:43 2024 +0500
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Fri May 3 17:05:18 2024 +0200

    tdf#157103: fix SVG whitespace handling
    
    Previous code tried to hack some tricks to restore whitespaces after
    trimming them according to the xml:space attribute value. But it was
    wrong in multiple ways.
    
    1. The collapsed space must stay in the block where its start was.
       When a block ended with a space, then trimming the space from it,
       and adding to a next block, can change the size of the space.
    2. The shift of a line (e.g., specifying x and y values) doesn't end
       the logical line. A space before such a shift must be kept by the
       same rules, as if there weren't a shift.
    3. A block with xml:space="preserve" is treated as if it consists of
       all non-whitespace characters, even if its leading or trailing
       characters are spaces. I.e., a trailing space in a block before,
       or a leading space in a block after, should be collapsed into a
       single space, not removed - even when the space-preserving block
       itself is made invisible.
    
    Change-Id: Ic778d1e9d6b9d0c342ea74ad78d44bb47bc8d708
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166239
    Tested-by: Mike Kaganski <mike.kagan...@collabora.com>
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/167050
    Tested-by: Jenkins

diff --git a/svgio/inc/svgcharacternode.hxx b/svgio/inc/svgcharacternode.hxx
index d81066af47a6..20c60d787cb5 100644
--- a/svgio/inc/svgcharacternode.hxx
+++ b/svgio/inc/svgcharacternode.hxx
@@ -38,11 +38,10 @@ namespace svgio::svgreader
             /// the string data
             OUString           maText;
 
-            // keep a copy of string data before space handling
-            OUString           maTextBeforeSpaceHandling;
-
             SvgTspanNode*        mpParentLine;
 
+            bool mbHadTrailingSpace = false;
+
             /// local helpers
             rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> 
createSimpleTextPrimitive(
                 SvgTextPosition& rSvgTextPosition,
@@ -65,8 +64,7 @@ namespace svgio::svgreader
             virtual const SvgStyleAttributes* getSvgStyleAttributes() const 
override;
 
             void 
decomposeText(drawinglayer::primitive2d::Primitive2DContainer& rTarget, 
SvgTextPosition& rSvgTextPosition) const;
-            void whiteSpaceHandling();
-            SvgCharacterNode* addGap(SvgCharacterNode* pPreviousCharacterNode);
+            SvgCharacterNode* whiteSpaceHandling(SvgCharacterNode* 
pPreviousCharacterNode);
             void concatenate(std::u16string_view rText);
 
             /// Text content
diff --git a/svgio/qa/cppunit/SvgImportTest.cxx 
b/svgio/qa/cppunit/SvgImportTest.cxx
index ed3fa02e1c22..2c9f9ea72485 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -855,7 +855,7 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf85770)
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"height"_ostr, "11");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"familyname"_ostr, "Times New Roman");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"fontcolor"_ostr, "#000000");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"text"_ostr, "Start");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"text"_ostr, "Start ");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"height"_ostr, "11");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"familyname"_ostr, "Times New Roman");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"fontcolor"_ostr, "#000000");
@@ -900,17 +900,17 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf93583)
     CPPUNIT_ASSERT (pDocument);
 
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"text"_ostr, "This is the");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"x"_ostr, "62");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"x"_ostr, "58");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"y"_ostr, "303");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"width"_ostr, "16");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"height"_ostr, "16");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"text"_ostr, " first");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"x"_ostr, "128");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"text"_ostr, " first ");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"x"_ostr, "124");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"y"_ostr, "303");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"width"_ostr, "32");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"height"_ostr, "32");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"text"_ostr, " line");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"x"_ostr, "188");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"text"_ostr, "line");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"x"_ostr, "192");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"y"_ostr, "303");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"width"_ostr, "16");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"height"_ostr, "16");
@@ -926,28 +926,28 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156616)
 
     CPPUNIT_ASSERT (pDocument);
 
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"text"_ostr, "First");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"text"_ostr, "First ");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"x"_ostr, "114");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"y"_ostr, "103");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"text"_ostr, " line");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"x"_ostr, "143");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"text"_ostr, "line ");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"x"_ostr, "147");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"y"_ostr, "103");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"text"_ostr, "Second line");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"x"_ostr, "114");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"y"_ostr, "122");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]"_ostr, 
"text"_ostr, "First");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]"_ostr, 
"x"_ostr, "86");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]"_ostr, 
"text"_ostr, "First ");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]"_ostr, 
"x"_ostr, "84");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]"_ostr, 
"y"_ostr, "153");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]"_ostr, 
"text"_ostr, " line");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]"_ostr, 
"x"_ostr, "115");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]"_ostr, 
"text"_ostr, "line ");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]"_ostr, 
"x"_ostr, "117");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]"_ostr, 
"y"_ostr, "153");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]"_ostr, 
"text"_ostr, "Second line");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]"_ostr, 
"x"_ostr, "77");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]"_ostr, 
"y"_ostr, "172");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[7]"_ostr, 
"text"_ostr, "First");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[7]"_ostr, 
"x"_ostr, "59");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[7]"_ostr, 
"text"_ostr, "First ");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[7]"_ostr, 
"x"_ostr, "55");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[7]"_ostr, 
"y"_ostr, "203");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[8]"_ostr, 
"text"_ostr, " line");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[8]"_ostr, 
"text"_ostr, "line ");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[8]"_ostr, 
"x"_ostr, "88");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[8]"_ostr, 
"y"_ostr, "203");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[9]"_ostr, 
"text"_ostr, "Second line");
@@ -1392,12 +1392,12 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156251)
     // Without the fix in place, this test would have failed with
     // - Expected: 'You are '
     // - Actual  : 'You are'
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"text"_ostr, "You are");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"text"_ostr, " not");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"text"_ostr, "You are ");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"text"_ostr, "not");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, 
"text"_ostr, " a banana!");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]"_ostr, 
"text"_ostr, "You are");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]"_ostr, 
"text"_ostr, " not");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]"_ostr, 
"text"_ostr, " a banana!");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]"_ostr, 
"text"_ostr, " not ");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]"_ostr, 
"text"_ostr, "a banana!");
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testMaskText)
@@ -1938,15 +1938,15 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156837)
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"x"_ostr, "114");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"y"_ostr, "103");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"height"_ostr, "16");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"text"_ostr, "x");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"x"_ostr, "122");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, 
"text"_ostr, "x ");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"x"_ostr, "126");
 
     // Without the fix in place, this test would have failed with
     // - Expected: 94
     // - Actual  : 103
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"y"_ostr, "94");
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"height"_ostr, "10");
-    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"text"_ostr, " 3");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, 
"text"_ostr, "3");
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testTdf156271)
diff --git a/svgio/source/svgreader/svgcharacternode.cxx 
b/svgio/source/svgreader/svgcharacternode.cxx
index 7fdac9e92bc5..0aea1976c10f 100644
--- a/svgio/source/svgreader/svgcharacternode.cxx
+++ b/svgio/source/svgreader/svgcharacternode.cxx
@@ -486,53 +486,80 @@ namespace svgio::svgreader
             }
         }
 
-        void SvgCharacterNode::whiteSpaceHandling()
+        SvgCharacterNode*
+        SvgCharacterNode::whiteSpaceHandling(SvgCharacterNode* 
pPreviousCharacterNode)
         {
             bool bIsDefault(XmlSpace::Default == getXmlSpace());
             // if xml:space="default" then remove all newline characters, 
otherwise convert them to space
             // convert tab to space too
-            maText = maTextBeforeSpaceHandling = maText.replaceAll(u"
", bIsDefault ? u"" : u" ").replaceAll(u"       ", u" ");
+            maText = maText.replaceAll(u"
", bIsDefault ? u"" : u" ").replaceAll(u"       ", u" ");
 
-            if(bIsDefault)
+            if (!bIsDefault)
             {
-                // strip of all leading and trailing spaces
-                // and consolidate contiguous space
-                maText = consolidateContiguousSpace(maText.trim());
-            }
-        }
+                if (maText.isEmpty())
+                {
+                    // Ignore this empty node for the purpose of whitespace 
handling
+                    return pPreviousCharacterNode;
+                }
 
-        SvgCharacterNode* SvgCharacterNode::addGap(SvgCharacterNode* 
pPreviousCharacterNode)
-        {
-            // maText may have lost all text. If that's the case, ignore as 
invalid character node
-            // Also ignore if maTextBeforeSpaceHandling just have spaces
-            if(!maText.isEmpty() && 
!o3tl::trim(maTextBeforeSpaceHandling).empty())
-            {
-                if(pPreviousCharacterNode)
+                if (pPreviousCharacterNode && 
pPreviousCharacterNode->mbHadTrailingSpace)
                 {
-                    bool bAddGap(true);
+                    // pPreviousCharacterNode->mbHadTrailingSpace implies its 
xml:space="default".
+                    // Even if this xml:space="preserve" node is 
whitespace-only, the trailing space
+                    // of the previous node is significant - restore it
+                    pPreviousCharacterNode->maText += " ";
+                }
+
+                return this;
+            }
 
-                    // Do not add a gap if last node doesn't end with a space 
and
-                    // current note doesn't start with a space
-                    const sal_uInt32 
nLastLength(pPreviousCharacterNode->maTextBeforeSpaceHandling.getLength());
-                    
if(pPreviousCharacterNode->maTextBeforeSpaceHandling[nLastLength - 1] != ' ' && 
maTextBeforeSpaceHandling[0] != ' ')
-                        bAddGap = false;
+            bool bHadLeadingSpace = maText.startsWith(" ");
+            mbHadTrailingSpace = maText.endsWith(" "); // Only set for 
xml:space="default"
 
-                    // Do not add a gap if this node and last node are in 
different lines
-                    if(pPreviousCharacterNode->mpParentLine != mpParentLine)
-                        bAddGap = false;
+            // strip of all leading and trailing spaces
+            // and consolidate contiguous space
+            maText = consolidateContiguousSpace(maText.trim());
 
-                    // add in-between whitespace (single space) to the 
beginning of the current character node
-                    if(bAddGap)
+            if (pPreviousCharacterNode)
+            {
+                if (pPreviousCharacterNode->mbHadTrailingSpace)
+                {
+                    // pPreviousCharacterNode->mbHadTrailingSpace implies its 
xml:space="default".
+                    // The previous node already has a pending trailing space.
+                    if (maText.isEmpty())
                     {
-                        maText = " " + maText;
+                        // Leading spaces in this empty node are insignificant.
+                        // Ignore this empty node for the purpose of 
whitespace handling
+                        return pPreviousCharacterNode;
                     }
+                    // The previous node's trailing space is significant - 
restore it. Note that
+                    // it is incorrect to insert a space in this node instead: 
the spaces in
+                    // different nodes may have different size
+                    pPreviousCharacterNode->maText += " ";
+                    return this;
                 }
 
-                // this becomes the previous character node
-                return this;
+                if (bHadLeadingSpace)
+                {
+                    // This possibly whitespace-only xml:space="default" node 
goes after another
+                    // node either having xml:space="default", but without a 
trailing space; or
+                    // having xml:space="preserve" (in that case, it's 
irrelevant if that node had
+                    // any trailing spaces).
+                    if (!maText.isEmpty())
+                    {
+                        // The leading whitespace in this node is significant 
- restore it
+                        maText = " " + maText;
+                    }
+                    // The trailing whitespace in this node may or may not be
+                    // significant (it will be significant, if there will be 
more nodes). Keep it as
+                    // it is (even empty), but return this, to participate in 
whitespace handling
+                    return this;
+                }
             }
 
-            return pPreviousCharacterNode;
+            // No previous node, or no leading/trailing space on the previous 
node's boundary: if
+            // this is whitespace-only, its whitespace is never significant
+            return maText.isEmpty() ? pPreviousCharacterNode : this;
         }
 
         void SvgCharacterNode::concatenate(std::u16string_view rText)
diff --git a/svgio/source/svgreader/svgdocumenthandler.cxx 
b/svgio/source/svgreader/svgdocumenthandler.cxx
index 8d2cc8849c55..5e6e0d404843 100644
--- a/svgio/source/svgreader/svgdocumenthandler.cxx
+++ b/svgio/source/svgreader/svgdocumenthandler.cxx
@@ -66,7 +66,46 @@ namespace svgio::svgreader
 
 namespace
 {
-    svgio::svgreader::SvgCharacterNode* 
whiteSpaceHandling(svgio::svgreader::SvgNode const * pNode, 
svgio::svgreader::SvgTspanNode* pParentLine, 
svgio::svgreader::SvgCharacterNode* pLast)
+using CharacterNodeHandlerFunc
+    = svgio::svgreader::SvgCharacterNode*(svgio::svgreader::SvgCharacterNode* 
pCharNode,
+                                          svgio::svgreader::SvgTspanNode* 
pParentLine,
+                                          svgio::svgreader::SvgCharacterNode* 
pLast);
+    // clean whitespace in text span
+    svgio::svgreader::SvgCharacterNode* 
whiteSpaceHandling(svgio::svgreader::SvgCharacterNode* pCharNode,
+                                                           
svgio::svgreader::SvgTspanNode* pParentLine,
+                                                           
svgio::svgreader::SvgCharacterNode* pLast)
+    {
+        pCharNode->setParentLine(pParentLine);
+        return pCharNode->whiteSpaceHandling(pLast);
+    }
+
+    // set correct widths of text lines
+    svgio::svgreader::SvgCharacterNode* 
calcTextLineWidths(svgio::svgreader::SvgCharacterNode* pCharNode,
+                                                           
svgio::svgreader::SvgTspanNode* pParentLine,
+                                                           
svgio::svgreader::SvgCharacterNode* /*pLast*/)
+    {
+        if (const SvgStyleAttributes* pSvgStyleAttributes = 
pCharNode->getSvgStyleAttributes())
+        {
+            const drawinglayer::attribute::FontAttribute aFontAttribute(
+                
svgio::svgreader::SvgCharacterNode::getFontAttribute(*pSvgStyleAttributes));
+
+            double 
fFontWidth(pSvgStyleAttributes->getFontSizeNumber().solve(*pCharNode));
+            double fFontHeight(fFontWidth);
+
+            css::lang::Locale aLocale;
+            drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+            aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, 
fFontHeight, aLocale);
+            double fTextWidth = 
aTextLayouterDevice.getTextWidth(pCharNode->getText(), 0.0,
+                                                                 
pCharNode->getText().getLength());
+            pParentLine->concatenateTextLineWidth(fTextWidth);
+        }
+        return nullptr; // no pLast handling
+    }
+
+    svgio::svgreader::SvgCharacterNode* 
walkRecursive(svgio::svgreader::SvgNode const* pNode,
+                                                      
svgio::svgreader::SvgTspanNode* pParentLine,
+                                                      
svgio::svgreader::SvgCharacterNode* pLast,
+                                                      
CharacterNodeHandlerFunc* pHandlerFunc)
     {
         if(pNode)
         {
@@ -83,34 +122,9 @@ namespace
                     {
                         case SVGToken::Character:
                         {
-                            // clean whitespace in text span
                             svgio::svgreader::SvgCharacterNode* pCharNode = 
static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate);
 
-                            pCharNode->setParentLine(pParentLine);
-
-                            pCharNode->whiteSpaceHandling();
-                            pLast = pCharNode->addGap(pLast);
-
-                            double fTextWidth(0.0);
-
-                            const SvgStyleAttributes* pSvgStyleAttributes = 
pCharNode->getSvgStyleAttributes();
-
-                            if(pSvgStyleAttributes)
-                            {
-                                const drawinglayer::attribute::FontAttribute 
aFontAttribute(
-                                        
svgio::svgreader::SvgCharacterNode::getFontAttribute(*pSvgStyleAttributes));
-
-                                double 
fFontWidth(pSvgStyleAttributes->getFontSizeNumber().solve(*pCharNode));
-                                double fFontHeight(fFontWidth);
-
-                                css::lang::Locale aLocale;
-
-                                drawinglayer::primitive2d::TextLayouterDevice 
aTextLayouterDevice;
-                                
aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, 
aLocale);
-                                fTextWidth = 
aTextLayouterDevice.getTextWidth(pCharNode->getText(), 0.0, 
pCharNode->getText().getLength());
-                            }
-
-                            pParentLine->concatenateTextLineWidth(fTextWidth);
+                            pLast = pHandlerFunc(pCharNode, pParentLine, 
pLast);
                             break;
                         }
                         case SVGToken::Tspan:
@@ -121,15 +135,15 @@ namespace
                             if(!pTspanNode->getX().empty() || 
!pTspanNode->getY().empty())
                                 pParentLine = pTspanNode;
 
-                            // recursively clean whitespaces in subhierarchy
-                            pLast = whiteSpaceHandling(pCandidate, 
pParentLine, pLast);
+                            // recursively handle subhierarchy
+                            pLast = walkRecursive(pCandidate, pParentLine, 
pLast, pHandlerFunc);
                             break;
                         }
                         case SVGToken::TextPath:
                         case SVGToken::Tref:
                         {
-                            // recursively clean whitespaces in subhierarchy
-                            pLast = whiteSpaceHandling(pCandidate, 
pParentLine, pLast);
+                            // recursively handle subhierarchy
+                            pLast = walkRecursive(pCandidate, pParentLine, 
pLast, pHandlerFunc);
                             break;
                         }
                         default:
@@ -516,7 +530,12 @@ namespace
             if(pTextNode)
             {
                 // cleanup read strings
-                whiteSpaceHandling(pTextNode, static_cast< 
SvgTspanNode*>(pTextNode), nullptr);
+                // First pass: handle whitespace. This works in a way that 
handling a following
+                // node may append a space to a previous node; so correct line 
width calculation
+                // may only happen after this pass finishes
+                walkRecursive(pTextNode, 
static_cast<SvgTspanNode*>(pTextNode), nullptr, whiteSpaceHandling);
+                // Second pass: calculate line widths
+                walkRecursive(pTextNode, 
static_cast<SvgTspanNode*>(pTextNode), nullptr, calcTextLineWidths);
             }
         }
 

Reply via email to