svgio/inc/svgnode.hxx                         |    2 
 svgio/inc/svgstyleattributes.hxx              |   13 ++++
 svgio/inc/svgtoken.hxx                        |    3 
 svgio/qa/cppunit/SvgImportTest.cxx            |   47 +++++++++++++++
 svgio/qa/cppunit/data/tdf129356.svg           |   34 +++++++++++
 svgio/qa/cppunit/data/tdf156834.svg           |    7 ++
 svgio/source/svgreader/svgcharacternode.cxx   |   24 +++++++
 svgio/source/svgreader/svgnode.cxx            |   68 +++++++---------------
 svgio/source/svgreader/svgstyleattributes.cxx |   43 +++++++++++++
 svgio/source/svgreader/svgtoken.cxx           |    6 +
 sw/qa/core/layout/paintfrm.cxx                |   10 +--
 sw/source/core/layout/paintfrm.cxx            |   80 +++++++++++++++++++++++++-
 12 files changed, 281 insertions(+), 56 deletions(-)

New commits:
commit e7c02ea29d92bed2d713b9a3180bfb1674add908
Author:     Miklos Vajna <[email protected]>
AuthorDate: Mon Aug 21 08:33:14 2023 +0200
Commit:     Xisco Fauli <[email protected]>
CommitDate: Tue Aug 22 21:00:20 2023 +0200

    tdf#156351 sw floattable: fix missing bottom border in master table
    
    The problem was that the bugdoc has a split floating table, and the
    bottom of the table on the first page was missing its border. The
    borders on the second page were correct.
    
    This happens because the cell itself has no bottom border and the layout
    did not extra effort to mirror the top table margin at the buttom, like
    Word does.
    
    Fix the problem similar to commit
    53798fef2cc0b5b0b9706081a4af5ceca964a41b (Related: tdf#156351 sw
    floattable: fix missing top border in follow table, 2023-08-18), so we
    add a bottom border for master tables at a layout level, similar how a
    top border was already added for follow tables.
    
    Given that this kind of worked already in the past (just top borders,
    just the 1 row case), do this unconditionally; but if needed this could
    be limited to the "Word table cell" case.
    
    Change-Id: Iafdcd90226fdc425be597d36ad97fb69dca5a89a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155884
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins
    (cherry picked from commit 08aea5526c75ff4c5385e960bd940f10ffa19cd5)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155914
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/sw/qa/core/layout/paintfrm.cxx b/sw/qa/core/layout/paintfrm.cxx
index 15b9df6a4fb4..2416c6b95f8c 100644
--- a/sw/qa/core/layout/paintfrm.cxx
+++ b/sw/qa/core/layout/paintfrm.cxx
@@ -36,7 +36,8 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitTableBorder)
     // When rendering that document:
     std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
 
-    // Then make sure that the follow table has a top border:
+    // Then make sure that the master table has a bottom border and the follow 
table has a top
+    // border:
     MetafileXmlDump aDumper;
     xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
     xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, 
"//polyline[@style='solid']/point");
@@ -61,10 +62,11 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitTableBorder)
     }
     xmlXPathFreeObject(pXmlObj);
     // Without the accompanying fix in place, this test would have failed with:
-    // - Expected: 3
+    // - Expected: 4
     // - Actual  : 2
-    // i.e. the top border in the follow table was missing.
-    CPPUNIT_ASSERT_EQUAL(3, nHorizontalBorders);
+    // i.e. the bottom border in the master table and the top border in the 
follow table were
+    // missing.
+    CPPUNIT_ASSERT_EQUAL(4, nHorizontalBorders);
 }
 }
 
diff --git a/sw/source/core/layout/paintfrm.cxx 
b/sw/source/core/layout/paintfrm.cxx
index fdc6c6140a57..361e750570e9 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -2399,6 +2399,9 @@ class SwTabFramePainter
     void InsertFollowTopBorder(const SwFrame& rFrame, const SvxBoxItem& 
rBoxItem,
                                bool bWordTableCell, SwTwips nTop, SwTwips 
nLeft, SwTwips nRight,
                                bool bTopIsOuter);
+    void InsertMasterBottomBorder(const SwFrame& rFrame, const SvxBoxItem& 
rBoxItem,
+                                  bool bWordTableCell, SwTwips nBottom, 
SwTwips nLeft, SwTwips nRight,
+                                  bool bBottomIsOuter);
 
     void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea);
     void FindStylesForLine( Point&,
@@ -2893,7 +2896,7 @@ void SwTabFramePainter::InsertFollowTopBorder(const 
SwFrame& rFrame, const SvxBo
 
     // This is then a first row in a follow table, without repeated headlines.
     auto pLastRow = dynamic_cast<const SwRowFrame*>(mrTabFrame.GetLastLower());
-    if (!pLastRow && pLastRow == pThisRow)
+    if (!pLastRow || pLastRow == pThisRow)
     {
         return;
     }
@@ -2929,6 +2932,79 @@ void SwTabFramePainter::InsertFollowTopBorder(const 
SwFrame& rFrame, const SvxBo
     Insert(aFollowTop, true);
 }
 
+void SwTabFramePainter::InsertMasterBottomBorder(const SwFrame& rFrame, const 
SvxBoxItem& rBoxItem,
+                                              bool bWordTableCell, SwTwips 
nBottom, SwTwips nLeft,
+                                              SwTwips nRight, bool 
bBottomIsOuter)
+{
+    // Figure out which cell to copy.
+    int nCol = 0;
+    const SwFrame* pCell = &rFrame;
+    while (pCell)
+    {
+        if (!pCell->GetPrev())
+        {
+            break;
+        }
+
+        ++nCol;
+        pCell = pCell->GetPrev();
+    }
+
+    auto pThisRow = dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
+    if (!pThisRow || pThisRow->GetUpper() != &mrTabFrame)
+    {
+        return;
+    }
+
+    if (mrTabFrame.IsFollow() || !mrTabFrame.GetFollow())
+    {
+        return;
+    }
+
+    // This is a master table that is split.
+    if (pThisRow->GetNext() || rBoxItem.GetTop() || rBoxItem.GetBottom())
+    {
+        return;
+    }
+
+    // This is then a last row in a master table.
+    auto pFirstRow = dynamic_cast<const SwRowFrame*>(mrTabFrame.GetLower());
+    if (!pFirstRow || pFirstRow == pThisRow)
+    {
+        return;
+    }
+
+    const SwFrame* pFirstCell = pFirstRow->GetLower();
+    for (int i = 0; i < nCol; ++i)
+    {
+        if (!pFirstCell)
+        {
+            break;
+        }
+
+        pFirstCell = pFirstCell->GetNext();
+    }
+    if (!pFirstCell)
+    {
+        return;
+    }
+
+    SwBorderAttrAccess aAccess(SwFrame::GetCache(), pFirstCell);
+    const SwBorderAttrs& rAttrs = *aAccess.Get();
+    const SvxBoxItem& rFirstBoxItem = rAttrs.GetBox();
+    if (!rFirstBoxItem.GetTop())
+    {
+        return;
+    }
+
+    // The matching (same column) row in the first row has a top border for us.
+    svx::frame::Style aMasterT(rFirstBoxItem.GetTop(), 1.0);
+    aMasterT.SetWordTableCell(bWordTableCell);
+    SwLineEntry aMasterBottom(nBottom, nLeft, nRight, bBottomIsOuter, 
aMasterT);
+    aMasterT.SetRefMode(svx::frame::RefMode::Begin);
+    Insert(aMasterBottom, true);
+}
+
 void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& 
rBoxItem, const SwRect& rPaintArea)
 {
     // build 4 line entries for the 4 borders:
@@ -3019,6 +3095,8 @@ void SwTabFramePainter::Insert(const SwFrame& rFrame, 
const SvxBoxItem& rBoxItem
     Insert( aTop, true );
     Insert( aBottom, true );
 
+    InsertMasterBottomBorder(rFrame, rBoxItem, bWordTableCell, nBottom, nLeft, 
nRight,
+                             bBottomIsOuter);
     InsertFollowTopBorder(rFrame, rBoxItem, bWordTableCell, nTop, nLeft, 
nRight, bTopIsOuter);
 }
 
commit 603e1a7563a8ab8900fc2d7b99fbc0522e815056
Author:     Xisco Fauli <[email protected]>
AuthorDate: Tue Aug 22 12:46:02 2023 +0200
Commit:     Xisco Fauli <[email protected]>
CommitDate: Tue Aug 22 21:00:13 2023 +0200

    tdf#129356: handle css combinator when the element name is combined...
    
    ... with the ID or the class
    
    While at it, simplify the code a bit
    
    Change-Id: I9e36f334b884d31229568835a346d4427a47c760
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155945
    Tested-by: Jenkins
    Reviewed-by: Xisco Fauli <[email protected]>
    (cherry picked from commit 0bf0871f2dabb6c11f4976a40dcc12f6f5cb90c8)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155919

diff --git a/svgio/inc/svgnode.hxx b/svgio/inc/svgnode.hxx
index 0d8008d41c90..96c6ade01efa 100644
--- a/svgio/inc/svgnode.hxx
+++ b/svgio/inc/svgnode.hxx
@@ -125,7 +125,7 @@ namespace svgio::svgreader
                 const OUString& aConcatenated);
             void fillCssStyleVectorUsingHierarchyAndSelectors(
                 const SvgNode& rCurrent,
-                const OUString& aConcatenated);
+                std::u16string_view aConcatenated);
             void fillCssStyleVectorUsingParent(
                 const SvgNode& rCurrent);
 
diff --git a/svgio/qa/cppunit/SvgImportTest.cxx 
b/svgio/qa/cppunit/SvgImportTest.cxx
index fde5c7ee22dc..06a5cfb63328 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -441,6 +441,29 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156168)
     assertXPath(pDocument, "/primitive2D/transform/polypolygonstroke[4]/line", 
"color", "#00ff00");
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf129356)
+{
+    Primitive2DSequence aSequence = 
parseSvg(u"/svgio/qa/cppunit/data/tdf129356.svg");
+    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
+
+    drawinglayer::Primitive2dXmlDump dumper;
+    xmlDocUniquePtr pDocument = dumper.dumpAndParse(aSequence);
+
+    CPPUNIT_ASSERT (pDocument);
+
+    // Without the fix in place, this test would have failed with
+    // - Expected: #008000
+    // - Actual  : #0000ff
+    assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[1]", 
"color", "#008000");
+    assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[2]", 
"color", "#008000");
+    assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[3]", 
"color", "#008000");
+    assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[4]", 
"color", "#008000");
+    assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[5]", 
"color", "#008000");
+    assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[6]", 
"color", "#008000");
+    assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[7]", 
"color", "#008000");
+    assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[8]", 
"color", "#008000");
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testTdf156034)
 {
     Primitive2DSequence aSequence = 
parseSvg(u"/svgio/qa/cppunit/data/tdf156034.svg");
diff --git a/svgio/qa/cppunit/data/tdf129356.svg 
b/svgio/qa/cppunit/data/tdf129356.svg
new file mode 100644
index 000000000000..46bd6935daa4
--- /dev/null
+++ b/svgio/qa/cppunit/data/tdf129356.svg
@@ -0,0 +1,34 @@
+<svg xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; viewBox="-0 0 800 800">
+
+    <style>
+        g.g1 rect {fill:green;}
+        g#g3 rect {fill:green;}
+        g.g4 #r1 {fill:green;}
+        g#g3 .r5 {fill:green;}
+    </style>
+
+    <g class="g4 g1">
+        <g class="g2">
+            <rect x="0" y="0" height="50" width="50" fill="blue"></rect>
+        </g>
+        <rect x="60" y="0" height="50" width="50" fill="blue"></rect>
+    </g>
+    <g id="g3">
+        <g id="g4">
+            <rect x="120" y="0" height="50" width="50" fill="blue"></rect>
+        </g>
+        <rect x="180" y="0" height="50" width="50" fill="blue"></rect>
+    </g>
+    <g class="g4 g1">
+        <g>
+            <rect id="r1" x="240" y="0" height="50" width="50" 
fill="blue"></rect>
+        </g>
+        <rect id="r1" x="300" y="0" height="50" width="50" fill="blue"></rect>
+    </g>
+    <g id="g3">
+        <g id="g4">
+            <rect class="r5" x="360" y="0" height="50" width="50" 
fill="blue"></rect>
+        </g>
+        <rect class="r5" x="420" y="0" height="50" width="50" 
fill="blue"></rect>
+    </g>
+</svg>
diff --git a/svgio/source/svgreader/svgnode.cxx 
b/svgio/source/svgreader/svgnode.cxx
index 4f82a16ebf1e..fd80337eb5b6 100644
--- a/svgio/source/svgreader/svgnode.cxx
+++ b/svgio/source/svgreader/svgnode.cxx
@@ -94,7 +94,7 @@ namespace {
 
         void SvgNode::fillCssStyleVectorUsingHierarchyAndSelectors(
             const SvgNode& rCurrent,
-            const OUString& aConcatenated)
+            std::u16string_view aConcatenated)
         {
             const SvgDocument& rDocument = getDocument();
 
@@ -102,6 +102,7 @@ namespace {
                 return;
 
             const SvgNode* pParent = rCurrent.getParent();
+            OUString sCurrentType(SVGTokenToStr(rCurrent.getType()));
 
             // check for ID (highest priority)
             if(rCurrent.getId())
@@ -110,21 +111,17 @@ namespace {
 
                 if(rId.getLength())
                 {
-                    const OUString aNewConcatenated(
-                        "#" + rId + aConcatenated);
+                    const OUString aNewConcatenated("#" + rId + aConcatenated);
+                    addCssStyle(rDocument, aNewConcatenated);
+
+                    if(!sCurrentType.isEmpty())
+                        addCssStyle(rDocument, sCurrentType + 
aNewConcatenated);
+
                     if(pParent)
                     {
                         // check for combined selectors at parent first so 
that higher specificity will be in front
                         fillCssStyleVectorUsingHierarchyAndSelectors(*pParent, 
aNewConcatenated);
                     }
-                    addCssStyle(rDocument, aNewConcatenated);
-
-                    // look further up in the hierarchy
-                    if(!aConcatenated.isEmpty() && pParent && pParent->getId())
-                    {
-                        const OUString& rParentId = pParent->getId().value();
-                        addCssStyle(rDocument, "#" + rParentId + 
aConcatenated);
-                    }
                 }
             }
 
@@ -132,55 +129,34 @@ namespace {
             for(const auto &aClass : aClasses)
             {
                 const OUString aNewConcatenated("." + aClass + aConcatenated);
+                addCssStyle(rDocument, aNewConcatenated);
+
+                if(!sCurrentType.isEmpty())
+                    addCssStyle(rDocument, sCurrentType + aNewConcatenated);
+
                 if(pParent)
                 {
                     // check for combined selectors at parent first so that 
higher specificity will be in front
                     fillCssStyleVectorUsingHierarchyAndSelectors(*pParent, 
aNewConcatenated);
                 }
-                addCssStyle(rDocument, aNewConcatenated);
+            }
 
-                // look further up in the hierarchy
-                if(!aConcatenated.isEmpty() && pParent)
-                {
-                    std::vector <OUString> aParentClasses = 
parseClass(*pParent);
-                    for(const auto &aParentClass : aParentClasses)
-                    {
-                        addCssStyle(rDocument, "." + aParentClass + 
aConcatenated);
-                    }
-                }
+            if(!sCurrentType.isEmpty())
+            {
+                const OUString aNewConcatenated(sCurrentType + aConcatenated);
+                addCssStyle(rDocument, aNewConcatenated);
             }
 
-            OUString sCurrentType(SVGTokenToStr(getType()));
+            OUString sType(SVGTokenToStr(getType()));
 
             // check for class-dependent references to CssStyles
-            if(sCurrentType.isEmpty())
+            if(sType.isEmpty())
                 return;
 
-            OUString aNewConcatenated(aConcatenated);
-
-            if(!rCurrent.getId() && !rCurrent.getClass() && 0 == 
aConcatenated.indexOf(sCurrentType))
-            {
-                // no new CssStyle Selector and already starts with 
sCurrentType, do not concatenate;
-                // we pass an 'empty' node (in the sense of CssStyle Selector)
-            }
-            else
-            {
-                aNewConcatenated = sCurrentType + aConcatenated;
-            }
-
             if(pParent)
             {
                 // check for combined selectors at parent first so that higher 
specificity will be in front
-                fillCssStyleVectorUsingHierarchyAndSelectors(*pParent, 
aNewConcatenated);
-            }
-
-            addCssStyle(rDocument, aNewConcatenated);
-
-            // check if there is a css style with element inside element
-            if(pParent)
-            {
-                OUString sParentType(SVGTokenToStr(pParent->getType()));
-                addCssStyle(rDocument, sParentType + sCurrentType);
+                fillCssStyleVectorUsingHierarchyAndSelectors(*pParent, sType);
             }
         }
 
@@ -309,7 +285,7 @@ namespace {
             fillCssStyleVectorUsingParent(*this);
 
             // check the hierarchy for concatenated patterns of Selectors
-            fillCssStyleVectorUsingHierarchyAndSelectors(*this, OUString());
+            fillCssStyleVectorUsingHierarchyAndSelectors(*this, 
std::u16string_view());
 
 
             // tdf#99115, Add css selector '*' style only if the element is on 
top of the hierarchy
commit 7ecfaad43a41f2e20f178d5693d37ce1128e2dea
Author:     Xisco Fauli <[email protected]>
AuthorDate: Mon Aug 21 12:35:49 2023 +0200
Commit:     Xisco Fauli <[email protected]>
CommitDate: Tue Aug 22 21:00:06 2023 +0200

    tdf#156834: Add basic support for dominant-baseline attribute
    
    Change-Id: I005d6ca6bc340d73cae639ccd09321a0a00bc4b7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155892
    Tested-by: Jenkins
    Reviewed-by: Xisco Fauli <[email protected]>
    (cherry picked from commit 3b357ecb7bca4ab3844d1900eced55f46e6f8e1c)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155913

diff --git a/svgio/inc/svgstyleattributes.hxx b/svgio/inc/svgstyleattributes.hxx
index 0db313243a8c..acd310adbf6d 100644
--- a/svgio/inc/svgstyleattributes.hxx
+++ b/svgio/inc/svgstyleattributes.hxx
@@ -162,6 +162,13 @@ namespace svgio::svgreader
             Length
         };
 
+        enum class DominantBaseline
+        {
+            Auto,
+            Middle,
+            Hanging
+        };
+
         enum class Visibility
         {
             notset,
@@ -229,6 +236,8 @@ namespace svgio::svgreader
             BaselineShift               maBaselineShift;
             SvgNumber                   maBaselineShiftNumber;
 
+            DominantBaseline            maDominantBaseline;
+
             mutable std::vector<sal_uInt16> maResolvingParent;
 
             // defines if this attributes are part of a ClipPath. If yes,
@@ -444,6 +453,10 @@ namespace svgio::svgreader
             void setBaselineShift(const BaselineShift aBaselineShift) { 
maBaselineShift = aBaselineShift; }
             BaselineShift getBaselineShift() const;
             SvgNumber getBaselineShiftNumber() const;
+
+            // DominantBaseline
+            void setDominantBaseline(const DominantBaseline aDominantBaseline) 
{ maDominantBaseline = aDominantBaseline; }
+            DominantBaseline getDominantBaseline() const;
         };
 
 } // end of namespace svgio::svgreader
diff --git a/svgio/inc/svgtoken.hxx b/svgio/inc/svgtoken.hxx
index 6c1a17692f22..3a4a89f285e2 100644
--- a/svgio/inc/svgtoken.hxx
+++ b/svgio/inc/svgtoken.hxx
@@ -188,7 +188,8 @@ namespace svgio::svgreader
 
             // text tokens
             Text,
-            BaselineShift
+            BaselineShift,
+            DominantBaseline
         };
 
         SVGToken StrToSVGToken(const OUString& rStr, bool bCaseIndependent);
diff --git a/svgio/qa/cppunit/SvgImportTest.cxx 
b/svgio/qa/cppunit/SvgImportTest.cxx
index c7bf11ba32c5..fde5c7ee22dc 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -727,6 +727,30 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156777)
     assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "y", 
"23");
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf156834)
+{
+    Primitive2DSequence aSequence = 
parseSvg(u"/svgio/qa/cppunit/data/tdf156834.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", 3);
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", 
"text", "Auto");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "x", 
"30");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "y", 
"20");
+
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", 
"text", "Middle");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "x", 
"30");
+    //assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", 
"y", "57");
+
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", 
"text", "Hanging");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "x", 
"30");
+    assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "y", 
"94");
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testTdf104339)
 {
     Primitive2DSequence aSequenceTdf104339 = 
parseSvg(u"/svgio/qa/cppunit/data/tdf104339.svg");
diff --git a/svgio/qa/cppunit/data/tdf156834.svg 
b/svgio/qa/cppunit/data/tdf156834.svg
new file mode 100644
index 000000000000..74dc1548186e
--- /dev/null
+++ b/svgio/qa/cppunit/data/tdf156834.svg
@@ -0,0 +1,7 @@
+<svg viewBox="0 0 200 120" xmlns="http://www.w3.org/2000/svg";>
+  <path d="M20,20 L180,20 M20,50 L180,50 M20,80 L180,80" stroke="grey" />
+
+  <text dominant-baseline="auto" x="30" y="20" font-size="20">Auto</text>
+  <text dominant-baseline="middle" x="30" y="50" font-size="20">Middle</text>
+  <text dominant-baseline="Hanging" x="30" y="80" font-size="20">Hanging</text>
+</svg>
diff --git a/svgio/source/svgreader/svgcharacternode.cxx 
b/svgio/source/svgreader/svgcharacternode.cxx
index 91ec98ae9b68..cb39d6dc542f 100644
--- a/svgio/source/svgreader/svgcharacternode.cxx
+++ b/svgio/source/svgreader/svgcharacternode.cxx
@@ -279,6 +279,30 @@ namespace svgio::svgreader
                     }
                 }
 
+                // get DominantBaseline
+                const DominantBaseline 
aDominantBaseline(rSvgStyleAttributes.getDominantBaseline());
+
+                basegfx::B2DRange 
aRange(aTextLayouterDevice.getTextBoundRect(getText(), nIndex, nLength));
+                // apply DominantBaseline
+                switch(aDominantBaseline)
+                {
+                    case DominantBaseline::Middle:
+                    {
+                        aPosition.setY(aPosition.getY() - aRange.getCenterY());
+                        break;
+                    }
+                    case DominantBaseline::Hanging:
+                    {
+                        aPosition.setY(aPosition.getY() - aRange.getMinY());
+                        break;
+                    }
+                    default: // DominantBaseline::Auto
+                    {
+                        // nothing to do
+                        break;
+                    }
+                }
+
                 // get BaselineShift
                 const BaselineShift 
aBaselineShift(rSvgStyleAttributes.getBaselineShift());
 
diff --git a/svgio/source/svgreader/svgstyleattributes.cxx 
b/svgio/source/svgreader/svgstyleattributes.cxx
index 95c83c2f2c33..94209c32ac25 100644
--- a/svgio/source/svgreader/svgstyleattributes.cxx
+++ b/svgio/source/svgreader/svgstyleattributes.cxx
@@ -1292,7 +1292,8 @@ namespace svgio::svgreader
             maClipRule(FillRule::notset),
             maBaselineShift(BaselineShift::Baseline),
             maBaselineShiftNumber(0),
-            maResolvingParent(30, 0),
+            maDominantBaseline(DominantBaseline::Auto),
+            maResolvingParent(31, 0),
             mbIsClipPathContent(SVGToken::ClipPathNode == mrOwner.getType()),
             mbStrokeDasharraySet(false)
         {
@@ -1964,6 +1965,26 @@ namespace svgio::svgreader
                     }
                     break;
                 }
+                case SVGToken::DominantBaseline:
+                {
+                    if(!aContent.isEmpty())
+                    {
+                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), 
u"middle"))
+                        {
+                            setDominantBaseline(DominantBaseline::Middle);
+                        }
+                        else 
if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"hanging"))
+                        {
+                            setDominantBaseline(DominantBaseline::Hanging);
+                        }
+                        else
+                        {
+                            // no DominantBaseline
+                            setDominantBaseline(DominantBaseline::Auto);
+                        }
+                    }
+                    break;
+                }
                 default:
                 {
                     break;
@@ -3113,6 +3134,26 @@ namespace svgio::svgreader
 
             return BaselineShift::Baseline;
         }
+
+        DominantBaseline SvgStyleAttributes::getDominantBaseline() const
+        {
+            if(maDominantBaseline != DominantBaseline::Auto)
+            {
+                return maDominantBaseline;
+            }
+
+            const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
+
+            if (pSvgStyleAttributes && maResolvingParent[30] < 
nStyleDepthLimit)
+            {
+                ++maResolvingParent[30];
+                auto ret = pSvgStyleAttributes->getDominantBaseline();
+                --maResolvingParent[30];
+                return ret;
+            }
+
+            return DominantBaseline::Auto;
+        }
 } // end of namespace svgio
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svgio/source/svgreader/svgtoken.cxx 
b/svgio/source/svgreader/svgtoken.cxx
index 961c4ec3b5a4..492c78623f14 100644
--- a/svgio/source/svgreader/svgtoken.cxx
+++ b/svgio/source/svgreader/svgtoken.cxx
@@ -177,7 +177,8 @@ constexpr auto aSVGTokenMapperList = 
frozen::make_unordered_map<std::u16string_v
     { u"stroke-width", SVGToken::StrokeWidth },
 
     { u"text", SVGToken::Text },
-    { u"baseline-shift", SVGToken::BaselineShift }
+    { u"baseline-shift", SVGToken::BaselineShift },
+    { u"dominant-baseline", SVGToken::DominantBaseline }
 });
 
 // The same elements as the map above but lowercase. CSS is case insensitive
@@ -334,7 +335,8 @@ constexpr auto  aSVGLowerCaseTokenMapperList = 
frozen::make_unordered_map<std::u
     { u"stroke-width", SVGToken::StrokeWidth },
 
     { u"text", SVGToken::Text },
-    { u"baseline-shift", SVGToken::BaselineShift }
+    { u"baseline-shift", SVGToken::BaselineShift },
+    { u"dominant-baseline", SVGToken::DominantBaseline }
 });
 
 static_assert(sizeof(aSVGTokenMapperList) == 
sizeof(aSVGLowerCaseTokenMapperList),

Reply via email to