diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 11:48:25 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 11:48:25 +0000 |
commit | 9c0049cfae49c8e4ddef9125a69db2ad134c10c6 (patch) | |
tree | 812a86c0eee63dfc5ace12f2622ed3ce9cd3d680 /svgio | |
parent | Releasing progress-linux version 4:24.2.3-2~progress7.99u1. (diff) | |
download | libreoffice-9c0049cfae49c8e4ddef9125a69db2ad134c10c6.tar.xz libreoffice-9c0049cfae49c8e4ddef9125a69db2ad134c10c6.zip |
Merging upstream version 4:24.2.4.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'svgio')
-rw-r--r-- | svgio/inc/SvgNumber.hxx | 8 | ||||
-rw-r--r-- | svgio/inc/svgcharacternode.hxx | 8 | ||||
-rw-r--r-- | svgio/inc/svgnode.hxx | 7 | ||||
-rw-r--r-- | svgio/inc/svgstyleattributes.hxx | 15 | ||||
-rw-r--r-- | svgio/inc/svgtspannode.hxx | 2 | ||||
-rw-r--r-- | svgio/qa/cppunit/SvgImportTest.cxx | 204 | ||||
-rw-r--r-- | svgio/qa/cppunit/SvgNumberTest.cxx | 4 | ||||
-rw-r--r-- | svgio/qa/cppunit/data/contextFill.svg | 8 | ||||
-rw-r--r-- | svgio/qa/cppunit/data/contextStroke.svg | 14 | ||||
-rw-r--r-- | svgio/qa/cppunit/data/dy_in_ems.svg | 7 | ||||
-rw-r--r-- | svgio/qa/cppunit/data/dy_in_exs.svg | 7 | ||||
-rw-r--r-- | svgio/qa/cppunit/data/tdf160773.svg | 5 | ||||
-rw-r--r-- | svgio/source/svgreader/SvgNumber.cxx | 5 | ||||
-rw-r--r-- | svgio/source/svgreader/svgcharacternode.cxx | 115 | ||||
-rw-r--r-- | svgio/source/svgreader/svgdocumenthandler.cxx | 83 | ||||
-rw-r--r-- | svgio/source/svgreader/svgmarkernode.cxx | 2 | ||||
-rw-r--r-- | svgio/source/svgreader/svgnode.cxx | 36 | ||||
-rw-r--r-- | svgio/source/svgreader/svgstyleattributes.cxx | 80 | ||||
-rw-r--r-- | svgio/source/svgreader/svgtspannode.cxx | 5 |
19 files changed, 422 insertions, 193 deletions
diff --git a/svgio/inc/SvgNumber.hxx b/svgio/inc/SvgNumber.hxx index 4d03335cf4..c62576c2b0 100644 --- a/svgio/inc/SvgNumber.hxx +++ b/svgio/inc/SvgNumber.hxx @@ -37,10 +37,10 @@ class InfoProvider public: virtual ~InfoProvider() {} virtual basegfx::B2DRange getCurrentViewPort() const = 0; - /// return font size of node inherited from parents - virtual double getCurrentFontSizeInherited() const = 0; - /// return xheight of node inherited from parents - virtual double getCurrentXHeightInherited() const = 0; + /// return font size of node, either set here or inherited from parents + virtual double getCurrentFontSize() const = 0; + /// return xheight of node, either set here or inherited from parents + virtual double getCurrentXHeight() const = 0; }; enum class SvgUnit diff --git a/svgio/inc/svgcharacternode.hxx b/svgio/inc/svgcharacternode.hxx index d81066af47..20c60d787c 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/inc/svgnode.hxx b/svgio/inc/svgnode.hxx index 16c1f50bc3..5231635d62 100644 --- a/svgio/inc/svgnode.hxx +++ b/svgio/inc/svgnode.hxx @@ -163,11 +163,8 @@ namespace svgio::svgreader /// InfoProvider support for %, em and ex values virtual basegfx::B2DRange getCurrentViewPort() const override; - virtual double getCurrentFontSizeInherited() const override; - virtual double getCurrentXHeightInherited() const override; - - double getCurrentFontSize() const; - double getCurrentXHeight() const; + virtual double getCurrentFontSize() const override; + virtual double getCurrentXHeight() const override; /// Id access std::optional<OUString> const & getId() const { return mpId; } diff --git a/svgio/inc/svgstyleattributes.hxx b/svgio/inc/svgstyleattributes.hxx index c5c095462f..30cbab9656 100644 --- a/svgio/inc/svgstyleattributes.hxx +++ b/svgio/inc/svgstyleattributes.hxx @@ -247,10 +247,19 @@ namespace svgio::svgreader // #121221# Defines if evtl. an empty array *is* set bool mbStrokeDasharraySet : 1; + // tdf#155651 Defines if 'context-fill' is used in fill + bool mbContextFill : 1; + + // tdf#155651 Defines if 'context-stroke' is used in stroke + bool mbContextStroke : 1; + // tdf#94765 Check id references in gradient/pattern getters OUString maNodeFillURL; OUString maNodeStrokeURL; + const basegfx::BColor* maContextFill; + const basegfx::BColor* maContextStroke; + /// internal helpers void add_fillGradient( const basegfx::B2DPolyPolygon& rPath, @@ -327,6 +336,12 @@ namespace svgio::svgreader /// stroke content const basegfx::BColor* getStroke() const; + /// context fill content + const basegfx::BColor* getContextFill() const; + + /// context stroke content + const basegfx::BColor* getContextStroke() const; + /// stop color content const basegfx::BColor& getStopColor() const; diff --git a/svgio/inc/svgtspannode.hxx b/svgio/inc/svgtspannode.hxx index 84033685d8..0740c3cece 100644 --- a/svgio/inc/svgtspannode.hxx +++ b/svgio/inc/svgtspannode.hxx @@ -53,8 +53,6 @@ namespace svgio::svgreader virtual const SvgStyleAttributes* getSvgStyleAttributes() const override; virtual void parseAttribute(SVGToken aSVGToken, const OUString& aContent) override; - double getCurrentFontSize() const; - /// X content const SvgNumberVector& getX() const { return maX; } void setX(SvgNumberVector&& aX) { maX = std::move(aX); } diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx index 6969dc406a..bc99c672b4 100644 --- a/svgio/qa/cppunit/SvgImportTest.cxx +++ b/svgio/qa/cppunit/SvgImportTest.cxx @@ -39,6 +39,8 @@ protected: Primitive2DSequence parseSvg(std::u16string_view aSource); xmlDocUniquePtr dumpAndParseSvg(std::u16string_view aSource); + void assertXPathDouble(const xmlDocUniquePtr& pXmlDoc, const OString& rXPath, + const OString& rAttribute, double nExpectedValue, double delta); }; Primitive2DSequence Test::parseSvg(std::u16string_view aSource) @@ -91,6 +93,13 @@ void Test::checkRectPrimitive(Primitive2DSequence const & rPrimitive) } +void Test::assertXPathDouble(const xmlDocUniquePtr& pXmlDoc, const OString& rXPath, + const OString& rAttribute, double nExpectedValue, double delta) +{ + auto sVal = getXPath(pXmlDoc, rXPath, rAttribute); + CPPUNIT_ASSERT_DOUBLES_EQUAL(nExpectedValue, sVal.toDouble(), delta); +} + namespace { bool arePrimitive2DSequencesEqual(const Primitive2DSequence& rA, const Primitive2DSequence& rB) @@ -660,6 +669,25 @@ CPPUNIT_TEST_FIXTURE(Test, testMarkerOrient) assertXPath(pDocument, "/primitive2D/transform/transform[2]"_ostr, "xy33"_ostr, "1"); } +CPPUNIT_TEST_FIXTURE(Test, testContextFill) +{ + xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/contextFill.svg"); + + assertXPath(pDocument, "/primitive2D/transform/polypolygonstroke/line"_ostr, "color"_ostr, "#ff0000"); + assertXPath(pDocument, "/primitive2D/transform/transform/polypolygoncolor"_ostr, "color"_ostr, "#ff0000"); +} + +CPPUNIT_TEST_FIXTURE(Test, testContextStroke) +{ + xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/contextStroke.svg"); + + assertXPath(pDocument, "/primitive2D/transform/polypolygonstroke/line"_ostr, "color"_ostr, "#ff0000"); + assertXPath(pDocument, "/primitive2D/transform/transform[1]/polypolygonstroke/line"_ostr, "color"_ostr, "#ff0000"); + assertXPath(pDocument, "/primitive2D/transform/transform[2]/polypolygonstroke/line"_ostr, "color"_ostr, "#ff0000"); + assertXPath(pDocument, "/primitive2D/transform/transform[3]/polypolygonstroke/line"_ostr, "color"_ostr, "#ff0000"); + assertXPath(pDocument, "/primitive2D/transform/transform[4]/polypolygonstroke/line"_ostr, "color"_ostr, "#ff0000"); +} + CPPUNIT_TEST_FIXTURE(Test, testMarkerInPresentation) { Primitive2DSequence aSequence = parseSvg(u"/svgio/qa/cppunit/data/markerInPresentation.svg"); @@ -812,7 +840,7 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156834) assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, "text"_ostr, "Hanging"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, "x"_ostr, "30"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, "y"_ostr, "94"); + assertXPathDouble(pDocument, "/primitive2D/transform/textsimpleportion[3]"_ostr, "y"_ostr, 93.5, 0.5); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]"_ostr, "text"_ostr, "Central"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]"_ostr, "x"_ostr, "30"); @@ -846,7 +874,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"); @@ -891,17 +919,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, "127"); + 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, "187"); + 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"); @@ -917,29 +945,29 @@ 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, "142"); + 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, "114"); + 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, "x"_ostr, "87"); + 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"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[9]"_ostr, "x"_ostr, "40"); @@ -1383,12 +1411,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) @@ -1720,11 +1748,11 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf95400) assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "x"_ostr, "30"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "y"_ostr, "20"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "text"_ostr, "ABC"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx0"_ostr, "36"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx1"_ostr, "69"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx2"_ostr, "102"); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx0"_ostr, 36, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx1"_ostr, 69, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx2"_ostr, 102, 0.5); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "width"_ostr, "48"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "width"_ostr, "49"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "height"_ostr, "16"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "x"_ostr, "30"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "y"_ostr, "30"); @@ -1782,11 +1810,11 @@ CPPUNIT_TEST_FIXTURE(Test, testTextAnchor) assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[10]"_ostr, "y"_ostr, "40"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[10]"_ostr, "text"_ostr, "A"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[11]"_ostr, "x"_ostr, "72"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[11]"_ostr, "x"_ostr, "71"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[11]"_ostr, "y"_ostr, "40"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[11]"_ostr, "text"_ostr, "B"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[12]"_ostr, "x"_ostr, "83"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[12]"_ostr, "x"_ostr, "82"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[12]"_ostr, "y"_ostr, "40"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[12]"_ostr, "text"_ostr, "C"); @@ -1797,11 +1825,11 @@ CPPUNIT_TEST_FIXTURE(Test, testTextAnchor) assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[13]"_ostr, "y"_ostr, "50"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[13]"_ostr, "text"_ostr, "A"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[14]"_ostr, "x"_ostr, "55"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[14]"_ostr, "x"_ostr, "54"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[14]"_ostr, "y"_ostr, "50"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[14]"_ostr, "text"_ostr, "B"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[15]"_ostr, "x"_ostr, "66"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[15]"_ostr, "x"_ostr, "65"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[15]"_ostr, "y"_ostr, "50"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[15]"_ostr, "text"_ostr, "C"); @@ -1809,11 +1837,11 @@ CPPUNIT_TEST_FIXTURE(Test, testTextAnchor) assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[16]"_ostr, "y"_ostr, "60"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[16]"_ostr, "text"_ostr, "A"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[17]"_ostr, "x"_ostr, "38"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[17]"_ostr, "x"_ostr, "37"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[17]"_ostr, "y"_ostr, "60"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[17]"_ostr, "text"_ostr, "B"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[18]"_ostr, "x"_ostr, "49"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[18]"_ostr, "x"_ostr, "48"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[18]"_ostr, "y"_ostr, "60"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[18]"_ostr, "text"_ostr, "C"); } @@ -1844,9 +1872,9 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156577) // Without the fix in place, this test would have failed with // - Expected: 22 // - Actual : 52 - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx0"_ostr, "22"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx1"_ostr, "53"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx2"_ostr, "94"); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx0"_ostr, 22, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx1"_ostr, 52, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx2"_ostr, 93, 0.5); } CPPUNIT_TEST_FIXTURE(Test, testTdf156283) @@ -1864,9 +1892,9 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156283) assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "x"_ostr, "30"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "y"_ostr, "20"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "text"_ostr, "ABC"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx0"_ostr, "41"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx1"_ostr, "52"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx2"_ostr, "63"); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx0"_ostr, 41, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx1"_ostr, 52, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx2"_ostr, 62, 0.5); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "width"_ostr, "16"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "height"_ostr, "16"); @@ -1877,9 +1905,9 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156283) // Without the fix in place, this test would have failed with // - Expected: 41 // - Actual : 12 - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx0"_ostr, "41"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx1"_ostr, "52"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx2"_ostr, "63"); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx0"_ostr, 41, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx1"_ostr, 51, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx2"_ostr, 62, 0.5); } CPPUNIT_TEST_FIXTURE(Test, testTdf156569) @@ -1897,22 +1925,22 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156569) assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "x"_ostr, "0"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "y"_ostr, "20"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "text"_ostr, "ABC"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx0"_ostr, "40"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx1"_ostr, "80"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx2"_ostr, "91"); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx0"_ostr, 40, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx1"_ostr, 80, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[1]"_ostr, "dx2"_ostr, 91, 0.5); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "width"_ostr, "16"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "height"_ostr, "16"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "x"_ostr, "0"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "y"_ostr, "30"); assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "text"_ostr, "ABC"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx0"_ostr, "40"); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx0"_ostr, 40, 0.5); // Without the fix in place, this test would have failed with // - Expected: 80 // - Actual : 51 assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx1"_ostr, "80"); - assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx2"_ostr, "91"); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "dx2"_ostr, 91, 0.5); } CPPUNIT_TEST_FIXTURE(Test, testTdf156837) @@ -1929,15 +1957,31 @@ 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, testTdf160773) +{ + xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/tdf160773.svg"); + + // tdf#160773 Check there is a rectangle + assertXPath(pDocument, "/primitive2D/transform/mask/transform/polypolygoncolor"_ostr, "color"_ostr, "#ff0000"); + + // tdf#159661 Check there is text in the right position + assertXPath(pDocument, "/primitive2D/transform/mask/transform/textsimpleportion"_ostr, 1); + assertXPath(pDocument, "/primitive2D/transform/mask/transform/textsimpleportion"_ostr, "x"_ostr, "0"); + assertXPath(pDocument, "/primitive2D/transform/mask/transform/textsimpleportion"_ostr, "y"_ostr, "0"); + assertXPath(pDocument, "/primitive2D/transform/mask/transform/textsimpleportion"_ostr, "height"_ostr, "0"); + assertXPath(pDocument, "/primitive2D/transform/mask/transform/textsimpleportion"_ostr, "width"_ostr, "0"); + assertXPath(pDocument, "/primitive2D/transform/mask/transform/textsimpleportion"_ostr, "text"_ostr, "Red"); } CPPUNIT_TEST_FIXTURE(Test, testTdf156271) @@ -1955,8 +1999,8 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156271) assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[1]"_ostr, "x"_ostr, "40"); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[1]"_ostr, "y"_ostr, "10"); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[1]"_ostr, "text"_ostr, "AB"); - assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[1]"_ostr, "dx0"_ostr, "-30"); - assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[1]"_ostr, "dx1"_ostr, "-19"); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/mask/textsimpleportion[1]"_ostr, "dx0"_ostr, -30, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/mask/textsimpleportion[1]"_ostr, "dx1"_ostr, -19, 0.5); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[2]"_ostr, "width"_ostr, "16"); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[2]"_ostr, "height"_ostr, "16"); @@ -1967,24 +2011,24 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156271) // Without the fix in place, this test would have failed with // - Expected: -30 // - Actual : 0 - assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[2]"_ostr, "dx0"_ostr, "-30"); - assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[2]"_ostr, "dx1"_ostr, "-19"); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/mask/textsimpleportion[2]"_ostr, "dx0"_ostr, -30, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/mask/textsimpleportion[2]"_ostr, "dx1"_ostr, -19, 0.5); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[3]"_ostr, "width"_ostr, "16"); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[3]"_ostr, "height"_ostr, "16"); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[3]"_ostr, "x"_ostr, "40"); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[3]"_ostr, "y"_ostr, "30"); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[3]"_ostr, "text"_ostr, "AB"); - assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[3]"_ostr, "dx0"_ostr, "-30"); - assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[3]"_ostr, "dx1"_ostr, "-19"); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/mask/textsimpleportion[3]"_ostr, "dx0"_ostr, -30, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/mask/textsimpleportion[3]"_ostr, "dx1"_ostr, -19, 0.5); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]"_ostr, "width"_ostr, "16"); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]"_ostr, "height"_ostr, "16"); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]"_ostr, "x"_ostr, "40"); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]"_ostr, "y"_ostr, "40"); assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]"_ostr, "text"_ostr, "AB"); - assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]"_ostr, "dx0"_ostr, "12"); - assertXPath(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]"_ostr, "dx1"_ostr, "23"); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]"_ostr, "dx0"_ostr, 12, 0.5); + assertXPathDoubleValue(pDocument, "/primitive2D/transform/mask/textsimpleportion[4]"_ostr, "dx1"_ostr, 22, 0.5); } CPPUNIT_TEST_FIXTURE(Test, testTdf159968) @@ -2067,6 +2111,52 @@ CPPUNIT_TEST_FIXTURE(Test, testTspanFillOpacity) CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(70), nTransparence); } +CPPUNIT_TEST_FIXTURE(Test, testDyInEms) +{ + // tdf#160593 given an SVG file with <tspan dy="1.5em" style="font-size:0.5em">: + xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/dy_in_ems.svg"); + + assertXPath(pDocument, "//textsimpleportion"_ostr, 2); + assertXPath(pDocument, "//textsimpleportion[1]"_ostr, "y"_ostr, u"20"_ustr); + // Then make sure that the vertical offset is based on font-size of tspan, not of its parent. + // Given the parent's font-size is 16 px, the expected vertical offset is 1.5 * (16 * 0.5) = 12, + // which means that the resulting y is expected to be 20 + 12 = 32. + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 32 + // - Actual : 44 + // i.e. the offset was calculated as 1.5 multiplied by the parent's font-size of 16 px, + // not by the current tspan's half font-size. + assertXPath(pDocument, "//textsimpleportion[2]"_ostr, "y"_ostr, u"32"_ustr); +} + +CPPUNIT_TEST_FIXTURE(Test, testExs) +{ + // tdf#160594, tdf#160717 given an SVG file with <tspan dy="3ex" style="font-size:1ex">: + xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/dy_in_exs.svg"); + + assertXPath(pDocument, "//textsimpleportion"_ostr, 2); + assertXPath(pDocument, "//textsimpleportion[1]"_ostr, "height"_ostr, u"16"_ustr); + assertXPath(pDocument, "//textsimpleportion[1]"_ostr, "y"_ostr, u"20"_ustr); + + sal_Int32 nSize = getXPath(pDocument, "//textsimpleportion[2]"_ostr, "height"_ostr).toInt32(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected less than: 16 + // - Actual : 16 + // i.e. the parent font-size was used, instead of its x-size. + CPPUNIT_ASSERT_LESS(sal_Int32(16), nSize); + + sal_Int32 nYPos = getXPath(pDocument, "//textsimpleportion[2]"_ostr, "y"_ostr).toInt32(); + // Then make sure that the vertical offset is based on x-size of tspan, not of its parent. + // Given the tspan's font-size is nSize, its x-size is less than nSize, and the expected + // vertical offset is less than 3 * nSize, which means that the resulting y is expected + // to be strictly less than 20 + 3 * nSize. + // Without the accompanying fix in place, this test would have failed with: + // - Expected less than: 44 + // - Actual : 44 + // i.e. the parent x-size (or current font-size) was used, instead of current x-size. + CPPUNIT_ASSERT_LESS(sal_Int32(20 + 3 * nSize), nYPos); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svgio/qa/cppunit/SvgNumberTest.cxx b/svgio/qa/cppunit/SvgNumberTest.cxx index f420a44b42..49e1394bce 100644 --- a/svgio/qa/cppunit/SvgNumberTest.cxx +++ b/svgio/qa/cppunit/SvgNumberTest.cxx @@ -38,9 +38,9 @@ public: return basegfx::B2DRange(0.0, 0.0, 0.0, 0.0); } - double getCurrentFontSizeInherited() const override { return 12.0; } + double getCurrentFontSize() const override { return 12.0; } - double getCurrentXHeightInherited() const override { return 5.0; } + double getCurrentXHeight() const override { return 5.0; } }; void TestNumber::testSetting() diff --git a/svgio/qa/cppunit/data/contextFill.svg b/svgio/qa/cppunit/data/contextFill.svg new file mode 100644 index 0000000000..399d3c16b0 --- /dev/null +++ b/svgio/qa/cppunit/data/contextFill.svg @@ -0,0 +1,8 @@ +<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"> +<defs> + <marker id="triangle" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto"> + <path d="M 0 0 L 10 5 L 0 10 z" fill="context-fill" /> + </marker> +</defs> +<line x1="10" y1="10" x2="90" y2="90" fill="red" stroke="red" marker-end="url(#triangle)" /> +</svg> diff --git a/svgio/qa/cppunit/data/contextStroke.svg b/svgio/qa/cppunit/data/contextStroke.svg new file mode 100644 index 0000000000..5a9b27d69c --- /dev/null +++ b/svgio/qa/cppunit/data/contextStroke.svg @@ -0,0 +1,14 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> + <style> + path { + fill: none; + stroke-width: 4px; + marker: url(#diamond); + } + </style> + <path d="M 10,50 v -20 h 40 v -20" stroke="red"/> + + <marker id="diamond" markerWidth="12" markerHeight="12" refX="6" refY="6" markerUnits="userSpaceOnUse"> + <circle cx="6" cy="6" r="3" fill="white" stroke="context-stroke" stroke-width="2"/> + </marker> +</svg> diff --git a/svgio/qa/cppunit/data/dy_in_ems.svg b/svgio/qa/cppunit/data/dy_in_ems.svg new file mode 100644 index 0000000000..316239edf7 --- /dev/null +++ b/svgio/qa/cppunit/data/dy_in_ems.svg @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> + +<svg xmlns="http://www.w3.org/2000/svg" width="1in" height="1in" viewBox="0 0 100% 100%"> + + <text x="5" y="20" style="font-size:16px;font-family:Liberation Sans">foo + <tspan x="5" dy="1.5em" style="font-size:0.5em">bar</tspan></text> +</svg>
\ No newline at end of file diff --git a/svgio/qa/cppunit/data/dy_in_exs.svg b/svgio/qa/cppunit/data/dy_in_exs.svg new file mode 100644 index 0000000000..816a64a458 --- /dev/null +++ b/svgio/qa/cppunit/data/dy_in_exs.svg @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> + +<svg xmlns="http://www.w3.org/2000/svg" width="1in" height="1in" viewBox="0 0 100% 100%"> + + <text x="5" y="20" style="font-size:16px;font-family:Liberation Sans">foo + <tspan x="5" dy="3ex" style="font-size:1ex">bar</tspan></text> +</svg>
\ No newline at end of file diff --git a/svgio/qa/cppunit/data/tdf160773.svg b/svgio/qa/cppunit/data/tdf160773.svg new file mode 100644 index 0000000000..812c082486 --- /dev/null +++ b/svgio/qa/cppunit/data/tdf160773.svg @@ -0,0 +1,5 @@ +<?xml version="1.0" ?> +<svg width="600px" height="600px" viewBox="0 0 1 1" xmlns="http://www.w3.org/2000/svg"> + <text x="0" y="0.5" font-size="0.2px" dominant-baseline="middle" >Red</text> + <rect x="0.05" y="0.05" fill="#f00" width="0.36" height="0.242" /> +</svg> diff --git a/svgio/source/svgreader/SvgNumber.cxx b/svgio/source/svgreader/SvgNumber.cxx index c1558f3e64..72a44dbdd0 100644 --- a/svgio/source/svgreader/SvgNumber.cxx +++ b/svgio/source/svgreader/SvgNumber.cxx @@ -35,10 +35,11 @@ double SvgNumber::solveNonPercentage(const InfoProvider& rInfoProvider) const switch (meUnit) { + // See https://drafts.csswg.org/css-values-4/#font-relative-length case SvgUnit::em: - return mfNumber * rInfoProvider.getCurrentFontSizeInherited(); + return mfNumber * rInfoProvider.getCurrentFontSize(); case SvgUnit::ex: - return mfNumber * rInfoProvider.getCurrentXHeightInherited() * 0.5; + return mfNumber * rInfoProvider.getCurrentXHeight(); case SvgUnit::px: return mfNumber; case SvgUnit::pt: diff --git a/svgio/source/svgreader/svgcharacternode.cxx b/svgio/source/svgreader/svgcharacternode.cxx index 02bc3911a3..0aea1976c1 100644 --- a/svgio/source/svgreader/svgcharacternode.cxx +++ b/svgio/source/svgreader/svgcharacternode.cxx @@ -138,11 +138,11 @@ namespace svgio::svgreader { // prepare retval, index and length rtl::Reference<BasePrimitive2D> pRetval; - sal_uInt32 nLength(getText().getLength()); + const sal_uInt32 nLength(getText().getLength()); if(nLength) { - sal_uInt32 nIndex(0); + const sal_uInt32 nIndex(0); // prepare FontAttribute const drawinglayer::attribute::FontAttribute aFontAttribute(getFontAttribute(rSvgStyleAttributes)); @@ -154,9 +154,12 @@ namespace svgio::svgreader // prepare locale css::lang::Locale aLocale; - // prepare TextLayouterDevice + // prepare TextLayouterDevice; use a larger font size for more linear size + // calculations. Similar to nTextSizeFactor in sd/source/ui/view/sdview.cxx + // (ViewRedirector::createRedirectedPrimitive2DSequence). + const double sizeFactor = fFontHeight < 50000 ? 50000 / fFontHeight : 1.0; TextLayouterDevice aTextLayouterDevice; - aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale); + aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth * sizeFactor, fFontHeight * sizeFactor, aLocale); // prepare TextArray ::std::vector< double > aTextArray(rSvgTextPosition.getX()); @@ -190,13 +193,13 @@ namespace svgio::svgreader { fComulativeDx += aDxArray[a]; } - aTextArray.push_back(aExtendArray[a] + fStartX + fComulativeDx); + aTextArray.push_back(aExtendArray[a] / sizeFactor + fStartX + fComulativeDx); } } // get current TextPosition and TextWidth in units basegfx::B2DPoint aPosition(rSvgTextPosition.getPosition()); - double fTextWidth(aTextLayouterDevice.getTextWidth(getText(), nIndex, nLength)); + double fTextWidth(aTextLayouterDevice.getTextWidth(getText(), nIndex, nLength) / sizeFactor); // check for user-given TextLength if(0.0 != rSvgTextPosition.getTextLength() @@ -209,7 +212,10 @@ namespace svgio::svgreader // spacing, need to create and expand TextArray if(aTextArray.empty()) { - aTextArray = aTextLayouterDevice.getTextArray(getText(), nIndex, nLength); + auto aExtendArray(aTextLayouterDevice.getTextArray(getText(), nIndex, nLength)); + aTextArray.reserve(aExtendArray.size()); + for (auto n : aExtendArray) + aTextArray.push_back(n / sizeFactor); } for(auto &a : aTextArray) @@ -289,12 +295,12 @@ namespace svgio::svgreader case DominantBaseline::Middle: case DominantBaseline::Central: { - aPosition.setY(aPosition.getY() - aRange.getCenterY()); + aPosition.setY(aPosition.getY() - aRange.getCenterY() / sizeFactor); break; } case DominantBaseline::Hanging: { - aPosition.setY(aPosition.getY() - aRange.getMinY()); + aPosition.setY(aPosition.getY() - aRange.getMinY() / sizeFactor); break; } default: // DominantBaseline::Auto @@ -312,12 +318,12 @@ namespace svgio::svgreader { case BaselineShift::Sub: { - aPosition.setY(aPosition.getY() + aTextLayouterDevice.getUnderlineOffset()); + aPosition.setY(aPosition.getY() + aTextLayouterDevice.getUnderlineOffset() / sizeFactor); break; } case BaselineShift::Super: { - aPosition.setY(aPosition.getY() + aTextLayouterDevice.getOverlineOffset()); + aPosition.setY(aPosition.getY() + aTextLayouterDevice.getOverlineOffset() / sizeFactor); break; } case BaselineShift::Percentage: @@ -480,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"\n", bIsDefault ? u"" : u" ").replaceAll(u"\t", u" "); + maText = maText.replaceAll(u"\n", bIsDefault ? u"" : u" ").replaceAll(u"\t", 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 8d2cc8849c..5e6e0d4048 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); } } diff --git a/svgio/source/svgreader/svgmarkernode.cxx b/svgio/source/svgreader/svgmarkernode.cxx index 083471b49c..2279920634 100644 --- a/svgio/source/svgreader/svgmarkernode.cxx +++ b/svgio/source/svgreader/svgmarkernode.cxx @@ -174,7 +174,7 @@ namespace svgio::svgreader const drawinglayer::primitive2d::Primitive2DContainer& SvgMarkerNode::getMarkerPrimitives() const { - if(aPrimitives.empty() && Display::None != getDisplay()) + if(Display::None != getDisplay()) { decomposeSvgNode(const_cast< SvgMarkerNode* >(this)->aPrimitives, true); } diff --git a/svgio/source/svgreader/svgnode.cxx b/svgio/source/svgreader/svgnode.cxx index 7a45c681f1..8fdc29eab7 100644 --- a/svgio/source/svgreader/svgnode.cxx +++ b/svgio/source/svgreader/svgnode.cxx @@ -691,45 +691,23 @@ namespace { } } - double SvgNode::getCurrentFontSizeInherited() const - { - if(getParent()) - { - return getParent()->getCurrentFontSize(); - } - else - { - return 0.0; - } - } - double SvgNode::getCurrentFontSize() const { if(getSvgStyleAttributes()) return getSvgStyleAttributes()->getFontSizeNumber().solve(*this, NumberType::xcoordinate); - return getCurrentFontSizeInherited(); - } - - double SvgNode::getCurrentXHeightInherited() const - { if(getParent()) - { - return getParent()->getCurrentXHeight(); - } - else - { - return 0.0; - } + return getParent()->getCurrentFontSize(); + + return 0.0; } double SvgNode::getCurrentXHeight() const { - if(getSvgStyleAttributes()) - // for XHeight, use FontSize currently - return getSvgStyleAttributes()->getFontSizeNumber().solve(*this, NumberType::ycoordinate); - - return getCurrentXHeightInherited(); + // https://drafts.csswg.org/css-values-4/#ex + // for XHeight, use 0.5em fallback currently + // FIXME: use "x-height of the first available font" + return getCurrentFontSize() * 0.5; } void SvgNode::setId(OUString const & rId) diff --git a/svgio/source/svgreader/svgstyleattributes.cxx b/svgio/source/svgreader/svgstyleattributes.cxx index 58bdb9add8..42e19c51fc 100644 --- a/svgio/source/svgreader/svgstyleattributes.cxx +++ b/svgio/source/svgreader/svgstyleattributes.cxx @@ -817,6 +817,16 @@ namespace svgio::svgreader rMarkerTransform.identity(); rClipRange.reset(); + // Set the current fill to the marker before calling getMarkerPrimitives, + // which calls decomposeSvgNode to decompose the children of the marker. + // If any of the children uses 'fill="context-fill"', then it will use it + const_cast<SvgStyleAttributes*>(rMarker.getSvgStyleAttributes())->maContextFill = getFill(); + + // Set the current stroke to the marker before calling getMarkerPrimitives, + // which calls decomposeSvgNode to decompose the children of the marker. + // If any of the children uses 'stroke="context-stroke"', then it will use it + const_cast<SvgStyleAttributes*>(rMarker.getSvgStyleAttributes())->maContextStroke = getStroke(); + // get marker primitive representation rMarkerPrimitives = rMarker.getMarkerPrimitives(); @@ -1287,8 +1297,12 @@ namespace svgio::svgreader maBaselineShift(BaselineShift::Baseline), maBaselineShiftNumber(0), maDominantBaseline(DominantBaseline::Auto), - maResolvingParent(32, 0), - mbStrokeDasharraySet(false) + maResolvingParent(34, 0), + mbStrokeDasharraySet(false), + mbContextFill(false), + mbContextStroke(false), + maContextFill(nullptr), + maContextStroke(nullptr) { } @@ -1308,7 +1322,11 @@ namespace svgio::svgreader OUString aURL; SvgNumber aOpacity; - if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity)) + if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-fill")) + { + mbContextFill = true; + } + else if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity)) { setFill(aSvgPaint); if(aOpacity.isSet()) @@ -1353,7 +1371,11 @@ namespace svgio::svgreader OUString aURL; SvgNumber aOpacity; - if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity)) + if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-stroke")) + { + mbContextStroke = true; + } + else if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity)) { maStroke = aSvgPaint; if(aOpacity.isSet()) @@ -1996,6 +2018,40 @@ namespace svgio::svgreader } } + const basegfx::BColor* SvgStyleAttributes::getContextFill() const + { + if (SVGToken::Marker == mrOwner.getType()) + return maContextFill; + + const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); + if (pSvgStyleAttributes && maResolvingParent[33] < nStyleDepthLimit) + { + ++maResolvingParent[33]; + auto ret = pSvgStyleAttributes->getContextFill(); + --maResolvingParent[33]; + return ret; + } + + return nullptr; + } + + const basegfx::BColor* SvgStyleAttributes::getContextStroke() const + { + if (SVGToken::Marker == mrOwner.getType()) + return maContextStroke; + + const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); + if (pSvgStyleAttributes && maResolvingParent[32] < nStyleDepthLimit) + { + ++maResolvingParent[32]; + auto ret = pSvgStyleAttributes->getContextStroke(); + --maResolvingParent[32]; + return ret; + } + + return nullptr; + } + bool SvgStyleAttributes::isClipPathContent() const { if (SVGToken::ClipPathNode == mrOwner.getType()) @@ -2064,6 +2120,10 @@ namespace svgio::svgreader } } } + else if (mbContextFill) + { + return getContextFill(); + } else if (maNodeFillURL.isEmpty()) { const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); @@ -2109,6 +2169,10 @@ namespace svgio::svgreader return &maStroke.getBColor(); } } + else if (mbContextStroke) + { + return getContextStroke(); + } else if (maNodeStrokeURL.isEmpty()) { const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); @@ -2642,11 +2706,11 @@ namespace svgio::svgreader if(pSvgStyleAttributes) { const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber(); + double n = aParentNumber.getNumber() * maFontSizeNumber.getNumber(); + if (SvgUnit::ex == maFontSizeNumber.getUnit()) + n *= 0.5; // FIXME: use "x-height of the first available font" - return SvgNumber( - aParentNumber.getNumber() * maFontSizeNumber.getNumber(), - aParentNumber.getUnit(), - true); + return SvgNumber(n, aParentNumber.getUnit()); } } diff --git a/svgio/source/svgreader/svgtspannode.cxx b/svgio/source/svgreader/svgtspannode.cxx index 27d714e66a..4e97c2bfc4 100644 --- a/svgio/source/svgreader/svgtspannode.cxx +++ b/svgio/source/svgreader/svgtspannode.cxx @@ -141,11 +141,6 @@ namespace svgio::svgreader } } - double SvgTspanNode::getCurrentFontSize() const - { - return getCurrentFontSizeInherited(); - } - } // end of namespace svgio::svgreader /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |