svgio/Library_svgio.mk                        |    1 
 svgio/inc/svgfeimagenode.hxx                  |   45 ++++++++
 svgio/inc/svgtoken.hxx                        |    1 
 svgio/qa/cppunit/SvgImportTest.cxx            |   13 ++
 svgio/qa/cppunit/data/filterFeImage.svg       |   16 +++
 svgio/source/svgreader/svgdocumenthandler.cxx |    9 +
 svgio/source/svgreader/svgfeimagenode.cxx     |  134 ++++++++++++++++++++++++++
 svgio/source/svgreader/svgfilternode.cxx      |    6 +
 svgio/source/svgreader/svgtoken.cxx           |    3 
 9 files changed, 227 insertions(+), 1 deletion(-)

New commits:
commit 74c9fd3ae5b63981fb256e019f0cf974329157f0
Author:     Xisco Fauli <xiscofa...@libreoffice.org>
AuthorDate: Thu Jun 29 15:46:42 2023 +0200
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Thu Jun 29 21:35:17 2023 +0200

    tdf#156066: Add support for feImage filter
    
    Change-Id: I76cf8932ae352c271283483c9c734408a35b6074
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/153770
    Tested-by: Jenkins
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/svgio/Library_svgio.mk b/svgio/Library_svgio.mk
index 56d1f8dc1ae3..c25077ed94d3 100644
--- a/svgio/Library_svgio.mk
+++ b/svgio/Library_svgio.mk
@@ -63,6 +63,7 @@ $(eval $(call gb_Library_add_exception_objects,svgio,\
     svgio/source/svgreader/svgfecolormatrixnode \
     svgio/source/svgreader/svgfedropshadownode \
     svgio/source/svgreader/svgfefloodnode \
+    svgio/source/svgreader/svgfeimagenode \
     svgio/source/svgreader/svgfegaussianblurnode \
     svgio/source/svgreader/svgfeoffsetnode \
     svgio/source/svgreader/svgfilternode \
diff --git a/svgio/inc/svgfeimagenode.hxx b/svgio/inc/svgfeimagenode.hxx
new file mode 100644
index 000000000000..ff3c87930a6c
--- /dev/null
+++ b/svgio/inc/svgfeimagenode.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "svgnode.hxx"
+#include "svgstyleattributes.hxx"
+
+namespace svgio::svgreader
+{
+class SvgFeImageNode final : public SvgNode
+{
+private:
+    OUString maUrl; // external link
+    OUString maData; // base64 data
+
+public:
+    SvgFeImageNode(SvgDocument& rDocument, SvgNode* pParent);
+    virtual ~SvgFeImageNode() override;
+
+    virtual void parseAttribute(const OUString& rTokenName, SVGToken aSVGToken,
+                                const OUString& aContent) override;
+
+    void apply(drawinglayer::primitive2d::Primitive2DContainer& rTarget) const;
+};
+
+} // end of namespace svgio::svgreader
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svgio/inc/svgtoken.hxx b/svgio/inc/svgtoken.hxx
index 7d75a0e097a5..61abd28f11e8 100644
--- a/svgio/inc/svgtoken.hxx
+++ b/svgio/inc/svgtoken.hxx
@@ -83,6 +83,7 @@ namespace svgio::svgreader
             FeColorMatrix,
             FeDropShadow,
             FeFlood,
+            FeImage,
             FeGaussianBlur,
             FeOffset,
             Filter,
diff --git a/svgio/qa/cppunit/SvgImportTest.cxx 
b/svgio/qa/cppunit/SvgImportTest.cxx
index 5aa25aed5b1e..6827d4f81d39 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -238,6 +238,19 @@ CPPUNIT_TEST_FIXTURE(Test, testFilterFeDropShadow)
     assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor", "color", 
"#ffc0cb");
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testFilterFeImage)
+{
+    Primitive2DSequence aSequenceTdf132246 = 
parseSvg(u"/svgio/qa/cppunit/data/filterFeImage.svg");
+    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequenceTdf132246.getLength()));
+
+    drawinglayer::Primitive2dXmlDump dumper;
+    xmlDocUniquePtr pDocument = dumper.dumpAndParse(aSequenceTdf132246);
+
+    CPPUNIT_ASSERT (pDocument);
+
+    assertXPath(pDocument, "/primitive2D/transform/transform/bitmap");
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testTdf87309)
 {
     Primitive2DSequence aSequenceTdf87309 = 
parseSvg(u"/svgio/qa/cppunit/data/tdf87309.svg");
diff --git a/svgio/qa/cppunit/data/filterFeImage.svg 
b/svgio/qa/cppunit/data/filterFeImage.svg
new file mode 100644
index 000000000000..5682dbf4692c
--- /dev/null
+++ b/svgio/qa/cppunit/data/filterFeImage.svg
@@ -0,0 +1,16 @@
+<svg
+  viewBox="0 0 200 200"
+  xmlns="http://www.w3.org/2000/svg";
+  xmlns:xlink="http://www.w3.org/1999/xlink";
+  width="200"
+  height="200">
+  <defs>
+    <filter id="image">
+      <feImage
+             
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIEAAACBCAYAAADnoNlQAAAABHNCSVQICAgIfAhkiAAAAUJJREFUeJzt3EENAkEQRcG3aMAjMpYzBgjqSDCyWJhbZ5IqBf/w0sc+6rzaxnN6wLpzesC62/QA5okAESACEgGJgERAIiARkAhIBCQCEgGJgERAIiARkAhIBCQCEgGJgERAIiARkAhIBCQCEgGJgERAIiARkAhIBCQCEgGJgERAIiARkAiojvv12+at7aPP9IRlO211CRABIiARkAhIBCQCEgGJgERAIiARkAhIBCQCEgGJgERAIiARkAhIBCQCEgGJgERAIiARkAhIBCQCEgGJgERAIiARkAhIBCQCEgGJgERAdVzvtnlr22t6wLrnd3rBOpcAESACEgGJgERAIiARkAhIBCQCEgGJgERAIiARkAhIBCQCEgGJgERAIiARkAhIBCQCEgGJgERAIiARkAhIBCQCEgGJgERAIiARkAhIBFR/ALUM7y9VYLsAAAAASUVORK5CYII="/>
+    </filter>
+  </defs>
+
+  <rect x="10%" y="10%" width="80%" height="80%" style="filter:url(#image);" />
+</svg>
+
diff --git a/svgio/source/svgreader/svgdocumenthandler.cxx 
b/svgio/source/svgreader/svgdocumenthandler.cxx
index a070540e68eb..5f251b601bd0 100644
--- a/svgio/source/svgreader/svgdocumenthandler.cxx
+++ b/svgio/source/svgreader/svgdocumenthandler.cxx
@@ -44,6 +44,7 @@
 #include <svgfecolormatrixnode.hxx>
 #include <svgfedropshadownode.hxx>
 #include <svgfefloodnode.hxx>
+#include <svgfeimagenode.hxx>
 #include <svgfegaussianblurnode.hxx>
 #include <svgfeoffsetnode.hxx>
 #include <svgfilternode.hxx>
@@ -356,6 +357,13 @@ namespace
                     mpTarget->parseAttributes(xAttribs);
                     break;
                 }
+                case SVGToken::FeImage:
+                {
+                    /// new node for feImage
+                    mpTarget = new SvgFeImageNode(maDocument, mpTarget);
+                    mpTarget->parseAttributes(xAttribs);
+                    break;
+                }
                 case SVGToken::FeGaussianBlur:
                 {
                     /// new node for feGaussianBlur
@@ -479,6 +487,7 @@ namespace
                 case SVGToken::FeColorMatrix:
                 case SVGToken::FeDropShadow:
                 case SVGToken::FeFlood:
+                case SVGToken::FeImage:
                 case SVGToken::FeGaussianBlur:
                 case SVGToken::FeOffset:
                 case SVGToken::Filter:
diff --git a/svgio/source/svgreader/svgfeimagenode.cxx 
b/svgio/source/svgreader/svgfeimagenode.cxx
new file mode 100644
index 000000000000..56121739fe87
--- /dev/null
+++ b/svgio/source/svgreader/svgfeimagenode.cxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <svgfeimagenode.hxx>
+#include <o3tl/string_view.hxx>
+#include <svgdocument.hxx>
+#include <comphelper/base64.hxx>
+#include <tools/stream.hxx>
+#include <rtl/uri.hxx>
+
+namespace svgio::svgreader
+{
+SvgFeImageNode::SvgFeImageNode(SvgDocument& rDocument, SvgNode* pParent)
+    : SvgNode(SVGToken::FeImage, rDocument, pParent)
+{
+}
+
+SvgFeImageNode::~SvgFeImageNode() {}
+
+void SvgFeImageNode::parseAttribute(const OUString& /*rTokenName*/, SVGToken 
aSVGToken,
+                                    const OUString& aContent)
+{
+    // parse own
+    switch (aSVGToken)
+    {
+        case SVGToken::Style:
+        {
+            readLocalCssStyle(aContent);
+            break;
+        }
+        case SVGToken::Href:
+        case SVGToken::XlinkHref:
+        {
+            const sal_Int32 nLen(aContent.getLength());
+
+            if (nLen)
+            {
+                OUString aXLink;
+                readImageLink(aContent, aXLink, maUrl, maData);
+            }
+            break;
+        }
+
+        default:
+        {
+            break;
+        }
+    }
+}
+
+void SvgFeImageNode::apply(drawinglayer::primitive2d::Primitive2DContainer& 
rTarget) const
+{
+    BitmapEx aBitmapEx;
+
+    if (!maData.isEmpty())
+    {
+        // use embedded base64 encoded data
+        css::uno::Sequence<sal_Int8> aPass;
+        ::comphelper::Base64::decode(aPass, maData);
+
+        if (aPass.hasElements())
+        {
+            SvMemoryStream aStream(aPass.getArray(), aPass.getLength(), 
StreamMode::READ);
+            Graphic aGraphic;
+
+            if (ERRCODE_NONE
+                == GraphicFilter::GetGraphicFilter().ImportGraphic(aGraphic, 
u"", aStream))
+            {
+                aBitmapEx = aGraphic.GetBitmapEx();
+            }
+        }
+    }
+    else if (!maUrl.isEmpty())
+    {
+        const OUString& rPath = getDocument().getAbsolutePath();
+        OUString aAbsUrl;
+        try
+        {
+            aAbsUrl = rtl::Uri::convertRelToAbs(rPath, maUrl);
+        }
+        catch (rtl::MalformedUriException& e)
+        {
+            SAL_WARN("svg", "caught rtl::MalformedUriException \"" << 
e.getMessage() << "\"");
+        }
+
+        if (!aAbsUrl.isEmpty() && rPath != aAbsUrl)
+        {
+            SvFileStream aStream(aAbsUrl, StreamMode::STD_READ);
+            Graphic aGraphic;
+
+            if (ERRCODE_NONE
+                == GraphicFilter::GetGraphicFilter().ImportGraphic(aGraphic, 
aAbsUrl, aStream))
+            {
+                aBitmapEx = aGraphic.GetBitmapEx();
+            }
+        }
+    }
+
+    if (!aBitmapEx.IsEmpty() && 0 != aBitmapEx.GetSizePixel().Width()
+        && 0 != aBitmapEx.GetSizePixel().Height())
+    {
+        basegfx::B2DRange aViewBox
+            = rTarget.getB2DRange(drawinglayer::geometry::ViewInformation2D());
+        const drawinglayer::primitive2d::Primitive2DReference xRef(
+            new drawinglayer::primitive2d::BitmapPrimitive2D(
+                aBitmapEx, basegfx::utils::createScaleTranslateB2DHomMatrix(
+                               aViewBox.getRange(), aViewBox.getMinimum())));
+
+        rTarget = drawinglayer::primitive2d::Primitive2DContainer{ xRef };
+    }
+}
+
+} // end of namespace svgio::svgreader
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svgio/source/svgreader/svgfilternode.cxx 
b/svgio/source/svgreader/svgfilternode.cxx
index 3afdc6b0b524..f7d291b42c79 100644
--- a/svgio/source/svgreader/svgfilternode.cxx
+++ b/svgio/source/svgreader/svgfilternode.cxx
@@ -21,6 +21,7 @@
 #include <svgfecolormatrixnode.hxx>
 #include <svgfedropshadownode.hxx>
 #include <svgfefloodnode.hxx>
+#include <svgfeimagenode.hxx>
 #include <svgfegaussianblurnode.hxx>
 #include <svgfeoffsetnode.hxx>
 
@@ -74,6 +75,11 @@ void 
SvgFilterNode::apply(drawinglayer::primitive2d::Primitive2DContainer& rTarg
                 = dynamic_cast<const SvgFeDropShadowNode&>(*pCandidate);
             rFeDropShadowNode.apply(rTarget);
         }
+        else if (pCandidate->getType() == SVGToken::FeImage)
+        {
+            const SvgFeImageNode& rFeImageNode = dynamic_cast<const 
SvgFeImageNode&>(*pCandidate);
+            rFeImageNode.apply(rTarget);
+        }
     }
 }
 
diff --git a/svgio/source/svgreader/svgtoken.cxx 
b/svgio/source/svgreader/svgtoken.cxx
index 2383a6d79abd..6af6a1f5856e 100644
--- a/svgio/source/svgreader/svgtoken.cxx
+++ b/svgio/source/svgreader/svgtoken.cxx
@@ -28,7 +28,7 @@ namespace svgio::svgreader
 constexpr const std::u16string_view constToken_Title = u"title";
 constexpr const std::u16string_view constToken_Desc = u"desc";
 
-constexpr frozen::unordered_map<std::u16string_view, SVGToken, 144> 
aSVGTokenMapperList
+constexpr frozen::unordered_map<std::u16string_view, SVGToken, 145> 
aSVGTokenMapperList
 {
     { u"width", SVGToken::Width },
     { u"height", SVGToken::Height },
@@ -83,6 +83,7 @@ constexpr frozen::unordered_map<std::u16string_view, 
SVGToken, 144> aSVGTokenMap
     { u"feColorMatrix", SVGToken::FeColorMatrix },
     { u"feDropShadow", SVGToken::FeDropShadow },
     { u"feFlood", SVGToken::FeFlood },
+    { u"feImage", SVGToken::FeImage },
     { u"feGaussianBlur", SVGToken::FeGaussianBlur },
     { u"feOffset", SVGToken::FeOffset },
     { u"filter", SVGToken::Filter },

Reply via email to