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 <xiscofauli@libreoffice.org>
(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 0db3132..acd310a 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 6c1a176..3a4a89f 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 c7bf11b..fde5c7e 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 0000000..74dc154
--- /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 91ec98a..cb39d6d 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 95c83c2..94209c32 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 961c4ec..492c786 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),