summaryrefslogtreecommitdiffstats
path: root/svgio
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 11:48:25 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 11:48:25 +0000
commit9c0049cfae49c8e4ddef9125a69db2ad134c10c6 (patch)
tree812a86c0eee63dfc5ace12f2622ed3ce9cd3d680 /svgio
parentReleasing progress-linux version 4:24.2.3-2~progress7.99u1. (diff)
downloadlibreoffice-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.hxx8
-rw-r--r--svgio/inc/svgcharacternode.hxx8
-rw-r--r--svgio/inc/svgnode.hxx7
-rw-r--r--svgio/inc/svgstyleattributes.hxx15
-rw-r--r--svgio/inc/svgtspannode.hxx2
-rw-r--r--svgio/qa/cppunit/SvgImportTest.cxx204
-rw-r--r--svgio/qa/cppunit/SvgNumberTest.cxx4
-rw-r--r--svgio/qa/cppunit/data/contextFill.svg8
-rw-r--r--svgio/qa/cppunit/data/contextStroke.svg14
-rw-r--r--svgio/qa/cppunit/data/dy_in_ems.svg7
-rw-r--r--svgio/qa/cppunit/data/dy_in_exs.svg7
-rw-r--r--svgio/qa/cppunit/data/tdf160773.svg5
-rw-r--r--svgio/source/svgreader/SvgNumber.cxx5
-rw-r--r--svgio/source/svgreader/svgcharacternode.cxx115
-rw-r--r--svgio/source/svgreader/svgdocumenthandler.cxx83
-rw-r--r--svgio/source/svgreader/svgmarkernode.cxx2
-rw-r--r--svgio/source/svgreader/svgnode.cxx36
-rw-r--r--svgio/source/svgreader/svgstyleattributes.cxx80
-rw-r--r--svgio/source/svgreader/svgtspannode.cxx5
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: */