[Libreoffice-commits] core.git: filter/qa filter/source test/source

Miklos Vajna (via logerrit) logerrit at kemper.freedesktop.org
Wed Apr 8 16:25:53 UTC 2020


 filter/qa/unit/data/semi-transparent-line.odg |binary
 filter/qa/unit/svg.cxx                        |   34 +++++++++++++++---
 filter/source/svg/svgwriter.cxx               |   47 ++++++++++++++++----------
 test/source/xmltesttools.cxx                  |    5 ++
 4 files changed, 61 insertions(+), 25 deletions(-)

New commits:
commit ea52d24b5a19bb54f91cd679a977332ec330880d
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Wed Apr 8 17:47:24 2020 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Wed Apr 8 18:25:18 2020 +0200

    SVG export: fix lost semi-transparent line shapes
    
    The line shape itself didn't really have a height, rather it had a
    stroke. For some reason, the SVG mask then decides that nothing has to
    be painted there, so unless the line is entirely opaque, the line shape
    gets lost on export.
    
    Fix the problem by handling transparency similar to the PDF export,
    which detects if the whole purpose of the transparency gradient is to
    pass around a transparency percentage. We don't need a mask in that
    case, we can just use opacity as described at e.g.
    <https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/opacity>.
    
    Change-Id: I0355b9b09b6dd48bbacc5b7cc54fb71866304ef1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/91932
    Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
    Tested-by: Jenkins

diff --git a/filter/qa/unit/data/semi-transparent-line.odg b/filter/qa/unit/data/semi-transparent-line.odg
new file mode 100644
index 000000000000..2d28a694cea5
Binary files /dev/null and b/filter/qa/unit/data/semi-transparent-line.odg differ
diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx
index d6bb68283b74..8e6524c688ba 100644
--- a/filter/qa/unit/svg.cxx
+++ b/filter/qa/unit/svg.cxx
@@ -20,9 +20,7 @@
 
 using namespace ::com::sun::star;
 
-#if !defined MACOSX
 char const DATA_DIRECTORY[] = "/filter/qa/unit/data/";
-#endif
 
 /// SVG filter tests.
 class SvgFilterTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
@@ -34,10 +32,8 @@ public:
     void setUp() override;
     void tearDown() override;
     void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override;
-#if !defined MACOSX
     uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
     void load(const OUString& rURL);
-#endif
 };
 
 void SvgFilterTest::setUp()
@@ -55,13 +51,11 @@ void SvgFilterTest::tearDown()
     test::BootstrapFixture::tearDown();
 }
 
-#if !defined MACOSX
 void SvgFilterTest::load(const OUString& rFileName)
 {
     OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + rFileName;
     mxComponent = loadFromDesktop(aURL);
 }
-#endif
 
 void SvgFilterTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx)
 {
@@ -100,6 +94,34 @@ CPPUNIT_TEST_FIXTURE(SvgFilterTest, testPreserveJpg)
 #endif
 }
 
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentLine)
+{
+    // Load a document with a semi-transparent line shape.
+    load("semi-transparent-line.odg");
+
+    // Export it to SVG.
+    uno::Reference<frame::XStorable> xStorable(getComponent(), uno::UNO_QUERY_THROW);
+    SvMemoryStream aStream;
+    uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream);
+    utl::MediaDescriptor aMediaDescriptor;
+    aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export");
+    aMediaDescriptor["OutputStream"] <<= xOut;
+    xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+    aStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+    // Get the style of the group around the actual <path> element.
+    xmlDocPtr pXmlDoc = parseXmlStream(&aStream);
+    OUString aStyle = getXPath(
+        pXmlDoc, "//svg:g[@class='com.sun.star.drawing.LineShape']/svg:g/svg:g", "style");
+    OUString aPrefix("opacity: ");
+    // Without the accompanying fix in place, this test would have failed, as the style was
+    // "mask:url(#mask1)", not "opacity: <value>".
+    CPPUNIT_ASSERT(aStyle.startsWith(aPrefix));
+    int nPercent = std::round(aStyle.copy(aPrefix.getLength()).toDouble() * 100);
+    // Make sure that the line is still 30% opaque, rather than completely invisible.
+    CPPUNIT_ASSERT_EQUAL(30, nPercent);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index 8d491d122d0b..c276fd903048 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -2382,32 +2382,43 @@ void SVGActionWriter::ImplWriteMask( GDIMetaFile& rMtf,
     if( nMoveX || nMoveY )
         rMtf.Move( nMoveX, nMoveY );
 
-    OUString aMaskId = "mask" + OUString::number( mnCurMaskId++ );
-
+    OUString aStyle;
+    if (rGradient.GetStartColor() == rGradient.GetEndColor())
     {
-        SvXMLElementExport aElemDefs( mrExport, XML_NAMESPACE_NONE, aXMLElemDefs, true, true );
+        // Special case: constant alpha value.
+        const Color& rColor = rGradient.GetStartColor();
+        const double fOpacity = 1.0 - static_cast<double>(rColor.GetLuminance()) / 255;
+        aStyle = "opacity: " + OUString::number(fOpacity);
+    }
+    else
+    {
+        OUString aMaskId = "mask" + OUString::number(mnCurMaskId++);
 
-        mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrId, aMaskId );
         {
-            SvXMLElementExport aElemMask( mrExport, XML_NAMESPACE_NONE, "mask", true, true );
+            SvXMLElementExport aElemDefs(mrExport, XML_NAMESPACE_NONE, aXMLElemDefs, true, true);
 
-            const tools::PolyPolygon aPolyPolygon( tools::PolyPolygon( tools::Rectangle( rDestPt, rDestSize ) ) );
-            Gradient aGradient( rGradient );
+            mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrId, aMaskId);
+            {
+                SvXMLElementExport aElemMask(mrExport, XML_NAMESPACE_NONE, "mask", true, true);
 
-            // swap gradient stops to adopt SVG mask
-            Color aTmpColor( aGradient.GetStartColor() );
-            sal_uInt16 nTmpIntensity( aGradient.GetStartIntensity() );
-            aGradient.SetStartColor( aGradient.GetEndColor() );
-            aGradient.SetStartIntensity( aGradient.GetEndIntensity() ) ;
-            aGradient.SetEndColor( aTmpColor );
-            aGradient.SetEndIntensity( nTmpIntensity );
+                const tools::PolyPolygon aPolyPolygon(tools::PolyPolygon(tools::Rectangle(rDestPt, rDestSize)));
+                Gradient aGradient(rGradient);
 
-            ImplWriteGradientEx( aPolyPolygon, aGradient, nWriteFlags );
+                // swap gradient stops to adopt SVG mask
+                Color aTmpColor(aGradient.GetStartColor());
+                sal_uInt16 nTmpIntensity(aGradient.GetStartIntensity());
+                aGradient.SetStartColor(aGradient.GetEndColor());
+                aGradient.SetStartIntensity(aGradient.GetEndIntensity());
+                aGradient.SetEndColor(aTmpColor);
+                aGradient.SetEndIntensity(nTmpIntensity);
+
+                ImplWriteGradientEx(aPolyPolygon, aGradient, nWriteFlags);
+            }
         }
-    }
 
-    OUString aMaskStyle = "mask:url(#" + aMaskId + ")";
-    mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStyle, aMaskStyle );
+        aStyle = "mask:url(#" + aMaskId + ")";
+    }
+    mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStyle, aStyle);
 
     {
         SvXMLElementExport aElemG( mrExport, XML_NAMESPACE_NONE, aXMLElemG, true, true );
diff --git a/test/source/xmltesttools.cxx b/test/source/xmltesttools.cxx
index b4a74aa4a8b4..f89c127171bd 100644
--- a/test/source/xmltesttools.cxx
+++ b/test/source/xmltesttools.cxx
@@ -12,6 +12,7 @@
 #include <memory>
 
 #include <vcl/mtfxmldump.hxx>
+#include <sal/log.hxx>
 
 namespace {
 
@@ -53,7 +54,9 @@ xmlDocPtr XmlTestTools::parseXmlStream(SvStream* pStream)
     std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nSize + 1]);
     pStream->ReadBytes(pBuffer.get(), nSize);
     pBuffer[nSize] = 0;
-    return xmlParseDoc(reinterpret_cast<xmlChar*>(pBuffer.get()));
+    auto pCharBuffer = reinterpret_cast<xmlChar*>(pBuffer.get());
+    SAL_INFO("test", "XmlTestTools::parseXmlStream: pBuffer is '" << pCharBuffer << "'");
+    return xmlParseDoc(pCharBuffer);
 }
 
 xmlDocPtr XmlTestTools::dumpAndParse(MetafileXmlDump& rDumper, const GDIMetaFile& rGDIMetaFile)


More information about the Libreoffice-commits mailing list