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 b096ce2fcb7dded9fd764a9265ee3f50b4ce8fef
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Tue Nov 29 09:14:08 2022 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Tue Nov 29 13:58:02 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.
    
    (cherry picked from commit d34036e60c4e7d13feccd089f7749d35d956b358)
    
    Conflicts:
            svgio/inc/svgcharacternode.hxx
            svgio/qa/cppunit/SvgImportTest.cxx
            svgio/source/svgreader/svgcharacternode.cxx
    
    Change-Id: If5c48613b70eac662b54b8c9da835cd0a966ba89

diff --git a/svgio/inc/svgcharacternode.hxx b/svgio/inc/svgcharacternode.hxx
index 647d327494ee..f3247fad8303 100644
--- a/svgio/inc/svgcharacternode.hxx
+++ b/svgio/inc/svgcharacternode.hxx
@@ -135,7 +135,7 @@ namespace svgio
             OUString           maText;
 
             /// local helpers
-            drawinglayer::primitive2d::TextSimplePortionPrimitive2D* 
createSimpleTextPrimitive(
+            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 8120241e227a..0eb5b19585ff 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -68,6 +68,7 @@ class Test : public test::BootstrapFixture, public 
XmlTestTools
     void testTdf101237();
     void testTdf94765();
     void testBehaviourWhenWidthAndHeightIsOrIsNotSet();
+    void testTspanFillOpacity();
 
     Primitive2DSequence parseSvg(const OUString& aSource);
 
@@ -103,6 +104,7 @@ public:
     CPPUNIT_TEST(testTdf101237);
     CPPUNIT_TEST(testTdf94765);
     CPPUNIT_TEST(testBehaviourWhenWidthAndHeightIsOrIsNotSet);
+    CPPUNIT_TEST(testTspanFillOpacity);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -803,6 +805,28 @@ void Test::testBehaviourWhenWidthAndHeightIsOrIsNotSet()
     }
 }
 
+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::tools::Primitive2dXmlDump aDumper;
+    xmlDocUniquePtr pDocument = 
aDumper.dumpAndParse(comphelper::sequenceToContainer<Primitive2DContainer>(aSequence));
+    double fTransparence = getXPath(pDocument, 
"//textsimpleportion[@text='hello ']/parent::unifiedtransparence", 
"transparence").toDouble();
+    // 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(0.7, fTransparence);
+}
+
 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 bc4e739eccb8..d88d54d7d866 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>
 
 namespace svgio::svgreader
 {
@@ -219,12 +220,12 @@ namespace svgio::svgreader
             }
         }
 
-        drawinglayer::primitive2d::TextSimplePortionPrimitive2D* 
SvgCharacterNode::createSimpleTextPrimitive(
+        drawinglayer::primitive2d::BasePrimitive2D* 
SvgCharacterNode::createSimpleTextPrimitive(
             SvgTextPosition& rSvgTextPosition,
             const SvgStyleAttributes& rSvgStyleAttributes) const
         {
             // prepare retval, index and length
-            drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pRetval = 
nullptr;
+            drawinglayer::primitive2d::BasePrimitive2D* pRetval = nullptr;
             sal_uInt32 nIndex(0);
             sal_uInt32 nLength(getText().getLength());
 
@@ -423,6 +424,13 @@ namespace svgio::svgreader
                     ? *rSvgStyleAttributes.getFill()
                     : basegfx::BColor(0.0, 0.0, 0.0));
 
+                // get fill opacity
+                double fFillOpacity = 1.0;
+                if (rSvgStyleAttributes.getFillOpacity().isSet())
+                {
+                    fFillOpacity = 
rSvgStyleAttributes.getFillOpacity().getNumber();
+                }
+
                 // prepare TextTransformation
                 basegfx::B2DHomMatrix aTextTransform;
 
@@ -481,6 +489,13 @@ namespace svgio::svgreader
                         aFill);
                 }
 
+                if (fFillOpacity != 1.0)
+                {
+                    pRetval = new 
drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+                        drawinglayer::primitive2d::Primitive2DContainer{ 
pRetval },
+                        1.0 - fFillOpacity);
+                }
+
                 // advance current TextPosition
                 rSvgTextPosition.setPosition(rSvgTextPosition.getPosition() + 
basegfx::B2DVector(fTextWidth, 0.0));
             }

Reply via email to