svgio/inc/svgcharacternode.hxx | 2 +- svgio/qa/cppunit/SvgImportTest.cxx | 24 ++++++++++++++++++++++++ svgio/qa/cppunit/data/tspan-fill-opacity.svg | 15 +++++++++++++++ svgio/source/svgreader/svgcharacternode.cxx | 19 +++++++++++++++++-- 4 files changed, 57 insertions(+), 3 deletions(-)
New commits: commit cf32309ff82213cc2bc31cd03999b2ba763a3ee3 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Nov 29 09:14:08 2022 +0100 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Tue Nov 29 13:38:40 2022 +0100 SVG import: add support for semi-transparent text <tspan fill-opacity="..."> from SVG was ignored so far, only taking RGB colors from the 'fill' attribute, but ignoring transparency. The problem is that an SVG file is imported by mapping it to drawinglayer primitives, but TextSimplePortionPrimitive2D takes a basegfx::BColor as the font color, which doesn't handle transparency. Fix the problem by rendering SVG similar to how commit 81b0d5393ca4cf2ff0954e53b05928cde047c2e0 (svx: add rendering for semi-transparent shape text, 2019-11-20) did it for shape text: wrap the text primitive in a UnifiedTransparencePrimitive2D when opacity is not 1. Note that the drawinglayer primitive works with transparency and SVG works with opacity, which is the opposite of each other, but both are 0..1 ranges. Change-Id: If5c48613b70eac662b54b8c9da835cd0a966ba89 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143429 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143460 diff --git a/svgio/inc/svgcharacternode.hxx b/svgio/inc/svgcharacternode.hxx index 4c3c653459cb..0e618c3f1d29 100644 --- a/svgio/inc/svgcharacternode.hxx +++ b/svgio/inc/svgcharacternode.hxx @@ -123,7 +123,7 @@ namespace svgio::svgreader OUString maText; /// local helpers - rtl::Reference<drawinglayer::primitive2d::TextSimplePortionPrimitive2D> createSimpleTextPrimitive( + rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> createSimpleTextPrimitive( SvgTextPosition& rSvgTextPosition, const SvgStyleAttributes& rSvgStyleAttributes) const; void decomposeTextWithStyle( diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx index 61c467b7872a..ed178ffb6ace 100644 --- a/svgio/qa/cppunit/SvgImportTest.cxx +++ b/svgio/qa/cppunit/SvgImportTest.cxx @@ -81,6 +81,7 @@ class Test : public test::BootstrapFixture, public XmlTestTools void testTdf97663(); void testTdf149880(); void testCssClassRedefinition(); + void testTspanFillOpacity(); Primitive2DSequence parseSvg(std::u16string_view aSource); @@ -128,6 +129,7 @@ public: CPPUNIT_TEST(testTdf97663); CPPUNIT_TEST(testTdf149880); CPPUNIT_TEST(testCssClassRedefinition); + CPPUNIT_TEST(testTspanFillOpacity); CPPUNIT_TEST_SUITE_END(); }; @@ -1070,6 +1072,28 @@ void Test::testCssClassRedefinition() pDocument, "/primitive2D/transform/textsimpleportion[1]", "familyname", "Open Symbol"); } +void Test::testTspanFillOpacity() +{ + // Given an SVG file with <tspan fill-opacity="0.30">: + std::u16string_view aPath = u"/svgio/qa/cppunit/data/tspan-fill-opacity.svg"; + + // When rendering that SVG: + Primitive2DSequence aSequence = parseSvg(aPath); + + // Then make sure that the text portion is wrapped in a transparency primitive with the correct + // transparency value: + drawinglayer::Primitive2dXmlDump aDumper; + xmlDocUniquePtr pDocument = aDumper.dumpAndParse(Primitive2DContainer(aSequence)); + sal_Int32 nTransparence = getXPath(pDocument, "//textsimpleportion[@text='hello']/parent::unifiedtransparence", "transparence").toInt32(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // - XPath '//textsimpleportion[@text='hello']/parent::unifiedtransparence' number of nodes is incorrect + // i.e. the relevant <textsimpleportion> had no <unifiedtransparence> parent, the text was not + // semi-transparent. + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(70), nTransparence); +} + CPPUNIT_TEST_SUITE_REGISTRATION(Test); } diff --git a/svgio/qa/cppunit/data/tspan-fill-opacity.svg b/svgio/qa/cppunit/data/tspan-fill-opacity.svg new file mode 100644 index 000000000000..ef6d5352a8d2 --- /dev/null +++ b/svgio/qa/cppunit/data/tspan-fill-opacity.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg version="1.2" width="210mm" height="297mm" viewBox="0 0 21000 29700" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xml:space="preserve"> + <g class="ClosedBezierShape"> + <rect stroke="none" fill="none" x="9737" y="6537" width="7527" height="3527"/> + <g style="opacity: 0.30"> + <path fill="none" stroke="rgb(255,0,0)" stroke-width="25" stroke-linejoin="round" d="M 9875,6550 C 9806,6550 9750,6606 9750,6675 L 9750,9925 C 9750,9994 9806,10050 9875,10050 L 17125,10050 C 17194,10050 17250,9994 17250,9925 L 17250,6675 C 17250,6606 17194,6550 17125,6550 L 17000,6550 9875,6550 Z"/> + </g> + </g> + <g class="TextShape"> + <rect stroke="none" fill="none" x="9825" y="6550" width="4076" height="955"/> + <text> + <tspan x="9825" y="7939" font-family="Arial Narrow, sans-serif" font-size="800px" fill-opacity="0.30" fill="rgb(255,0,0)" stroke="none">hello</tspan> + </text> + </g> +</svg> diff --git a/svgio/source/svgreader/svgcharacternode.cxx b/svgio/source/svgreader/svgcharacternode.cxx index 854a9c21a426..a70884241e3a 100644 --- a/svgio/source/svgreader/svgcharacternode.cxx +++ b/svgio/source/svgreader/svgcharacternode.cxx @@ -24,6 +24,7 @@ #include <drawinglayer/primitive2d/textlayoutdevice.hxx> #include <drawinglayer/primitive2d/textbreakuphelper.hxx> #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> #include <osl/diagnose.h> using namespace drawinglayer::primitive2d; @@ -206,12 +207,12 @@ namespace svgio::svgreader } } - rtl::Reference<TextSimplePortionPrimitive2D> SvgCharacterNode::createSimpleTextPrimitive( + rtl::Reference<BasePrimitive2D> SvgCharacterNode::createSimpleTextPrimitive( SvgTextPosition& rSvgTextPosition, const SvgStyleAttributes& rSvgStyleAttributes) const { // prepare retval, index and length - rtl::Reference<TextSimplePortionPrimitive2D> pRetval; + rtl::Reference<BasePrimitive2D> pRetval; sal_uInt32 nLength(getText().getLength()); if(nLength) @@ -410,6 +411,13 @@ namespace svgio::svgreader if(rSvgStyleAttributes.getFill()) aFill = *rSvgStyleAttributes.getFill(); + // get fill opacity + double fFillOpacity = 1.0; + if (rSvgStyleAttributes.getFillOpacity().isSet()) + { + fFillOpacity = rSvgStyleAttributes.getFillOpacity().getNumber(); + } + // prepare TextTransformation basegfx::B2DHomMatrix aTextTransform; @@ -483,6 +491,13 @@ namespace svgio::svgreader aFill); } + if (fFillOpacity != 1.0) + { + pRetval = new UnifiedTransparencePrimitive2D( + drawinglayer::primitive2d::Primitive2DContainer{ pRetval }, + 1.0 - fFillOpacity); + } + // advance current TextPosition rSvgTextPosition.setPosition(rSvgTextPosition.getPosition() + basegfx::B2DVector(fTextWidth, 0.0)); }