This is an automated email from the ASF dual-hosted git repository.

ebakke pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new 12719ec2353 Switch SVG loading from the Batik library to JSVG.
12719ec2353 is described below

commit 12719ec2353f518cb4c0eb8e8b858989fd9cd598
Author: Eirik Bakke <[email protected]>
AuthorDate: Thu Nov 7 14:02:16 2024 -0500

    Switch SVG loading from the Batik library to JSVG.
---
 nbbuild/cluster.properties                         |   1 +
 nbbuild/licenses/MIT-jsvg                          |  21 +++
 nbbuild/licenses/names.properties                  |   1 +
 platform/libs.jsvg/build.xml                       |  29 ++++
 platform/libs.jsvg/external/binaries-list          |  18 +++
 platform/libs.jsvg/external/jsvg-1.6.1-license.txt |  29 ++++
 platform/libs.jsvg/manifest.mf                     |   5 +
 platform/libs.jsvg/nbproject/project.properties    |  26 ++++
 platform/libs.jsvg/nbproject/project.xml           |  64 +++++++++
 .../src/org/netbeans/libs/jsvg/Bundle.properties   |  22 +++
 platform/openide.util.ui.svg/nbproject/project.xml |   2 +-
 .../org/openide/util/svg/DenyingElementLoader.java |  59 ++++++++
 .../src/org/openide/util/svg/SVGIcon.java          | 154 +++++++++++----------
 .../org/openide/util/svg/SVGLoaderImplTest.java    |  16 ++-
 .../src/org/openide/util/svg/externalImageHref.svg |  26 ++++
 .../openide/util/svg/externalImageHrefXLink.svg    |  26 ++++
 16 files changed, 422 insertions(+), 77 deletions(-)

diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties
index c850dcd0db1..b9a79066f94 100644
--- a/nbbuild/cluster.properties
+++ b/nbbuild/cluster.properties
@@ -189,6 +189,7 @@ nb.cluster.platform=\
         libs.jna,\
         libs.jna.platform,\
         libs.jsr223,\
+        libs.jsvg,\
         libs.junit4,\
         libs.junit5,\
         libs.osgi,\
diff --git a/nbbuild/licenses/MIT-jsvg b/nbbuild/licenses/MIT-jsvg
new file mode 100644
index 00000000000..f3a3d71f2de
--- /dev/null
+++ b/nbbuild/licenses/MIT-jsvg
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021-2024 Jannis Weis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/nbbuild/licenses/names.properties 
b/nbbuild/licenses/names.properties
index 36ea7e99937..0cd108da0ea 100644
--- a/nbbuild/licenses/names.properties
+++ b/nbbuild/licenses/names.properties
@@ -69,6 +69,7 @@ MIT-phpstan=MIT license PHPStan variant
 MIT-validator=MIT license validator variant
 MIT-vscode=MIT license for VSCode
 MIT-vscode-ext=MIT license VS Code variant
+MIT-jsvg=MIT license JSVG variant
 MPL-1.0=MPL 1.0 license
 CDDL-SPL=CDDL 1.0 + SPL 1.0
 W3C2=W3C Software and Document Notice and License
diff --git a/platform/libs.jsvg/build.xml b/platform/libs.jsvg/build.xml
new file mode 100644
index 00000000000..70b06619279
--- /dev/null
+++ b/platform/libs.jsvg/build.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+
+<!-- You may freely edit this file. See harness/README in the NetBeans 
platform -->
+<!-- for some information on what you could do (e.g. targets to override). -->
+<!-- If you delete this file and reopen the project it will be recreated. -->
+<project name="platform/libs.jsvg" default="build" basedir=".">
+    <description>Builds the JSVG library wrapper module.</description>
+    <import file="../../nbbuild/templates/projectized.xml"/>
+</project>
diff --git a/platform/libs.jsvg/external/binaries-list 
b/platform/libs.jsvg/external/binaries-list
new file mode 100644
index 00000000000..9076715ac7c
--- /dev/null
+++ b/platform/libs.jsvg/external/binaries-list
@@ -0,0 +1,18 @@
+# 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
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+A84D34480044DB51A1448E9C0E32BD284B797288 com.github.weisj:jsvg:1.6.1
diff --git a/platform/libs.jsvg/external/jsvg-1.6.1-license.txt 
b/platform/libs.jsvg/external/jsvg-1.6.1-license.txt
new file mode 100644
index 00000000000..3b8f0f66221
--- /dev/null
+++ b/platform/libs.jsvg/external/jsvg-1.6.1-license.txt
@@ -0,0 +1,29 @@
+Name: JSVG
+Version: 1.6.1
+Description: JSVG - A Java SVG implementation
+License: MIT-jsvg
+Origin: JSVG
+URL: https://github.com/weisJ/jsvg
+Files: jsvg-1.6.1.jar
+
+MIT License
+
+Copyright (c) 2021-2024 Jannis Weis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/platform/libs.jsvg/manifest.mf b/platform/libs.jsvg/manifest.mf
new file mode 100644
index 00000000000..c0657cdc1e4
--- /dev/null
+++ b/platform/libs.jsvg/manifest.mf
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+OpenIDE-Module: org.netbeans.libs.jsvg
+OpenIDE-Module-Implementation-Version: 1
+OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/jsvg/Bundle.properties
+AutoUpdate-Show-In-Client: false
diff --git a/platform/libs.jsvg/nbproject/project.properties 
b/platform/libs.jsvg/nbproject/project.properties
new file mode 100644
index 00000000000..6fb34845af4
--- /dev/null
+++ b/platform/libs.jsvg/nbproject/project.properties
@@ -0,0 +1,26 @@
+# 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
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+file.reference.jsvg-1.6.1.jar=external/jsvg-1.6.1.jar
+release.external/jsvg-1.6.1.jar=modules/ext/jsvg-1.6.1.jar
+
+is.autoload=true
+javac.source=1.8
+
+nbm.homepage=https://github.com/weisJ/jsvg
+sigtest.gen.fail.on.error=false
+spec.version.base=1.22.0
diff --git a/platform/libs.jsvg/nbproject/project.xml 
b/platform/libs.jsvg/nbproject/project.xml
new file mode 100644
index 00000000000..0053019fd85
--- /dev/null
+++ b/platform/libs.jsvg/nbproject/project.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<project xmlns="http://www.netbeans.org/ns/project/1";>
+    <type>org.netbeans.modules.apisupport.project</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/nb-module-project/2";>
+            <code-name-base>org.netbeans.libs.jsvg</code-name-base>
+            <module-dependencies/>
+            <public-packages>
+                <package>com.github.weisj.jsvg</package>
+                <package>com.github.weisj.jsvg.attributes</package>
+                <package>com.github.weisj.jsvg.attributes.filter</package>
+                <package>com.github.weisj.jsvg.attributes.font</package>
+                <package>com.github.weisj.jsvg.attributes.paint</package>
+                <package>com.github.weisj.jsvg.attributes.stroke</package>
+                <package>com.github.weisj.jsvg.attributes.text</package>
+                <package>com.github.weisj.jsvg.geometry</package>
+                <package>com.github.weisj.jsvg.geometry.mesh</package>
+                <package>com.github.weisj.jsvg.geometry.noise</package>
+                <package>com.github.weisj.jsvg.geometry.path</package>
+                <package>com.github.weisj.jsvg.geometry.size</package>
+                <package>com.github.weisj.jsvg.geometry.util</package>
+                <package>com.github.weisj.jsvg.nodes</package>
+                <package>com.github.weisj.jsvg.nodes.animation</package>
+                <package>com.github.weisj.jsvg.nodes.container</package>
+                <package>com.github.weisj.jsvg.nodes.filter</package>
+                <package>com.github.weisj.jsvg.nodes.mesh</package>
+                <package>com.github.weisj.jsvg.nodes.prototype</package>
+                <package>com.github.weisj.jsvg.nodes.prototype.spec</package>
+                <package>com.github.weisj.jsvg.nodes.text</package>
+                <package>com.github.weisj.jsvg.parser</package>
+                <package>com.github.weisj.jsvg.parser.css</package>
+                <package>com.github.weisj.jsvg.parser.resources</package>
+                <package>com.github.weisj.jsvg.renderer</package>
+                <package>com.github.weisj.jsvg.renderer.awt</package>
+                <package>com.github.weisj.jsvg.renderer.jdk</package>
+                <package>com.github.weisj.jsvg.util</package>
+            </public-packages>
+            <class-path-extension>
+                
<runtime-relative-path>ext/jsvg-1.6.1.jar</runtime-relative-path>
+                <binary-origin>external/jsvg-1.6.1.jar</binary-origin>
+            </class-path-extension>
+        </data>
+    </configuration>
+</project>
diff --git a/platform/libs.jsvg/src/org/netbeans/libs/jsvg/Bundle.properties 
b/platform/libs.jsvg/src/org/netbeans/libs/jsvg/Bundle.properties
new file mode 100644
index 00000000000..cc006f2d790
--- /dev/null
+++ b/platform/libs.jsvg/src/org/netbeans/libs/jsvg/Bundle.properties
@@ -0,0 +1,22 @@
+# 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
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+OpenIDE-Module-Name=JSVG Library
+OpenIDE-Module-Short-Description=JSVG Library
+OpenIDE-Module-Long-Description=Contains the JSVG library, for reading and 
painting SVG files.
+OpenIDE-Module-Display-Category=Libraries
diff --git a/platform/openide.util.ui.svg/nbproject/project.xml 
b/platform/openide.util.ui.svg/nbproject/project.xml
index 851ebd109a7..251685f9ec8 100644
--- a/platform/openide.util.ui.svg/nbproject/project.xml
+++ b/platform/openide.util.ui.svg/nbproject/project.xml
@@ -26,7 +26,7 @@
             <code-name-base>org.openide.util.ui.svg</code-name-base>
             <module-dependencies>
                 <dependency>
-                    
<code-name-base>org.netbeans.libs.batik.read</code-name-base>
+                    <code-name-base>org.netbeans.libs.jsvg</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
diff --git 
a/platform/openide.util.ui.svg/src/org/openide/util/svg/DenyingElementLoader.java
 
b/platform/openide.util.ui.svg/src/org/openide/util/svg/DenyingElementLoader.java
new file mode 100644
index 00000000000..6fbd29ce705
--- /dev/null
+++ 
b/platform/openide.util.ui.svg/src/org/openide/util/svg/DenyingElementLoader.java
@@ -0,0 +1,59 @@
+/*
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.openide.util.svg;
+
+import com.github.weisj.jsvg.attributes.AttributeParser;
+import com.github.weisj.jsvg.parser.ElementLoader;
+import com.github.weisj.jsvg.parser.ParsedDocument;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+class DenyingElementLoader implements ElementLoader {
+    private final Set<String> attemptedExternalURLsLoaded = new 
LinkedHashSet<>();
+
+    public Set<String> getAttemptedExternalURLsLoaded() {
+        return Collections.unmodifiableSet(attemptedExternalURLsLoaded);
+    }
+
+    @Override
+    public <T> T loadElement(Class<T> type, String value,
+            ParsedDocument document, AttributeParser attributeParser)
+    {
+        /* Same logic as in com.github.weisj.jsvg.parser.DefaultElementLoader 
for the
+        AllowExternalResources.DENY case, but gathering up the attempted 
externally loaded URLs so
+        we can make the whole loading operation fail and make 
testLoadImageWithExternalUseXlinkHref
+        pass. */
+        String url = attributeParser.parseUrl(value);
+        if (url == null) {
+            return null;
+        }
+        if (url.contains("#")) {
+            String[] parts = url.split("#", 2);
+            String name = parts[0];
+            if (!name.isEmpty()) {
+                attemptedExternalURLsLoaded.add(value);
+                return null;
+            }
+            return document.getElementById(type, parts[1]);
+        } else {
+            return document.getElementById(type, url);
+        }
+    }
+}
diff --git a/platform/openide.util.ui.svg/src/org/openide/util/svg/SVGIcon.java 
b/platform/openide.util.ui.svg/src/org/openide/util/svg/SVGIcon.java
index 1d5b227c5c7..48a04ec64b4 100644
--- a/platform/openide.util.ui.svg/src/org/openide/util/svg/SVGIcon.java
+++ b/platform/openide.util.ui.svg/src/org/openide/util/svg/SVGIcon.java
@@ -23,7 +23,6 @@ import java.awt.Dimension;
 import java.awt.Graphics2D;
 import java.awt.Image;
 import java.awt.RenderingHints;
-import java.awt.geom.Dimension2D;
 import java.awt.image.BufferedImage;
 import java.awt.image.ColorModel;
 import java.io.IOException;
@@ -35,21 +34,17 @@ import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.swing.Icon;
-import org.apache.batik.anim.dom.SAXSVGDocumentFactory;
-import org.apache.batik.bridge.BridgeContext;
-import org.apache.batik.bridge.DocumentLoader;
-import org.apache.batik.bridge.ExternalResourceSecurity;
-import org.apache.batik.bridge.GVTBuilder;
-import org.apache.batik.bridge.NoLoadExternalResourceSecurity;
-import org.apache.batik.bridge.UserAgent;
-import org.apache.batik.bridge.UserAgentAdapter;
-import org.apache.batik.ext.awt.image.GraphicsUtil;
-import org.apache.batik.gvt.GraphicsNode;
-import org.apache.batik.util.ParsedURL;
-import org.apache.batik.util.XMLResourceDescriptor;
+import com.github.weisj.jsvg.SVGDocument;
+import com.github.weisj.jsvg.geometry.size.FloatSize;
+import com.github.weisj.jsvg.parser.LoaderContext;
+import com.github.weisj.jsvg.parser.ResourceLoader;
+import com.github.weisj.jsvg.parser.SVGLoader;
+import com.github.weisj.jsvg.renderer.awt.NullPlatformSupport;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
 import org.openide.util.CachedHiDPIIcon;
 import org.openide.util.Parameters;
-import org.w3c.dom.Document;
 
 /**
  * An icon loaded from an SVG file resource. Renders in high resolution on 
HiDPI displays.
@@ -62,62 +57,64 @@ final class SVGIcon extends CachedHiDPIIcon {
     enough to avoid an OutOfMemoryError but large enough to cater for most SVG 
loading scenarios.
     Photoshop had 10000 pixels as a maximum limit for many years. */
     private static final int MAX_DIMENSION_PIXELS = 8192;
-    // XML document factories are expensive to initialize, so do it once per 
thread only.
-    private static final ThreadLocal<SAXSVGDocumentFactory> DOCUMENT_FACTORY =
-            new ThreadLocal<SAXSVGDocumentFactory>()
+    /* XML document factories are expensive to initialize, so do it once per 
thread only. This
+    optimization was originally done for the Batik SVG library, but I suspect 
it might be beneficial
+    for JSVG as well. The SVGLoader constructor does initialize some XML 
parser stuff. */
+    private static final ThreadLocal<SVGLoader> SVG_LOADER =
+            new ThreadLocal<SVGLoader>()
     {
         @Override
-        protected SAXSVGDocumentFactory initialValue() {
-            return new 
SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName());
+        protected SVGLoader initialValue() {
+            return new SVGLoader();
         }
     };
 
     private final URL url;
     /**
-     * Cache of the parsed SVG document. Just painting the GraphicsNode is 
much faster than also
+     * Cache of the parsed SVG document. Just painting the SVGDocument is 
probably faster than also
      * re-parsing the underlying SVG file, yet we want to avoid keeping 
potentially complex object
-     * trees in memory for the lifetime of the Icon instance. Thus we allow 
the GraphicsNode to be
+     * trees in memory for the lifetime of the Icon instance. Thus we allow 
the SVGDocument to be
      * garbage collected after the first paint. The rasterized bitmap will be 
cached separately by
      * the superclass.
      */
-    private WeakReference<GraphicsNode> graphicsNodeWeakRef;
+    private WeakReference<SVGDocument> svgDocumentWeakRef;
     /**
-     * A strong reference version of {@link #graphicsNodeWeakRef}, which can 
be set to ensure that
-     * the latter is not yet garbage collected. Used to ensure that the 
initially loaded
-     * GraphicsNode is cached at least until the first time the icon is 
painted. May be null.
+     * A strong reference version of {@link #svgDocumentWeakRef}, which can be 
set to ensure that
+     * the latter is not yet garbage collected. Used to ensure that the 
initially loaded SVGDocument
+     * is cached at least until the first time the icon is painted. May be 
null.
      */
-    private GraphicsNode graphicsNodeStrongRef;
+    private SVGDocument svgDocumentStrongRef;
 
-    private SVGIcon(URL url, GraphicsNode initialGraphicsNode, int width, int 
height) {
+    private SVGIcon(URL url, SVGDocument initialSVGDocument, int width, int 
height) {
         super(width, height);
         Parameters.notNull("url", url);
-        Parameters.notNull("initialGraphicsNode", initialGraphicsNode);
+        Parameters.notNull("initialSVGDocument", initialSVGDocument);
         this.url = url;
-        this.graphicsNodeStrongRef = initialGraphicsNode;
-        this.graphicsNodeWeakRef = new 
WeakReference<GraphicsNode>(initialGraphicsNode);
+        this.svgDocumentStrongRef = initialSVGDocument;
+        this.svgDocumentWeakRef = new 
WeakReference<SVGDocument>(initialSVGDocument);
     }
 
     public static Icon load(URL url) throws IOException {
         Parameters.notNull("url", url);
         Dimension size = new Dimension();
-        GraphicsNode initialGraphicsNode = loadGraphicsNode(url, size);
-        return new SVGIcon(url, initialGraphicsNode, size.width, size.height);
+        SVGDocument initialSVGDocument = loadSVGDocument(url, size);
+        return new SVGIcon(url, initialSVGDocument, size.width, size.height);
     }
 
     /**
-     * Get the {@code GraphicsNode}, re-loading it from the original resource 
if a cached instance
+     * Get the {@code SVGDocument}, re-loading it from the original resource 
if a cached instance
      * is no longer available. Once this method has been called at least once, 
garbage collection
      * may cause the cache to be cleared.
      */
-    private synchronized GraphicsNode getGraphicsNode() throws IOException {
-        GraphicsNode ret = graphicsNodeWeakRef.get();
+    private synchronized SVGDocument getSVGDocument() throws IOException {
+        SVGDocument ret = svgDocumentWeakRef.get();
         if (ret != null) {
-            // Allow the GraphicsNode to be garbage collected after the 
initial paint.
-            graphicsNodeStrongRef = null;
+            // Allow the SVGDocument to be garbage collected after the initial 
paint.
+            svgDocumentStrongRef = null;
             return ret;
         }
-        ret = loadGraphicsNode(url, null);
-        graphicsNodeWeakRef = new WeakReference<GraphicsNode>(ret);
+        ret = loadSVGDocument(url, null);
+        svgDocumentWeakRef = new WeakReference<SVGDocument>(ret);
         return ret;
     }
 
@@ -126,40 +123,50 @@ final class SVGIcon extends CachedHiDPIIcon {
      *
      * @param toSize if not null, will be set to the image's size
      */
-    private static GraphicsNode loadGraphicsNode(URL url, Dimension toSize)
-            throws IOException
-    {
+    private static SVGDocument loadSVGDocument(URL url, Dimension toSize) 
throws IOException {
         Parameters.notNull("url", url);
-        final GraphicsNode graphicsNode;
-        final Dimension2D documentSize;
-        final Document doc;
+
+        final SVGDocument svgDocument;
+        FloatSize documentSize;
         InputStream is = url.openStream();
         try {
-            // See 
http://batik.2283329.n4.nabble.com/rendering-directly-to-java-awt-Graphics2D-td3716202.html
-            SAXSVGDocumentFactory factory = DOCUMENT_FACTORY.get();
-            /* Don't provide an URI here; we shouldn't commit to supporting 
relative links from
-            loaded SVG documents. */
-            doc = factory.createDocument(null, is);
-            // Disallow external resource dereferences
-            UserAgent userAgent = new UserAgentAdapter() {
-              @Override
-              public ExternalResourceSecurity getExternalResourceSecurity(
-                  ParsedURL resourceURL, ParsedURL docURL) {
-                return new NoLoadExternalResourceSecurity();
-              }
+            // Explicitly deny loading of external URLs.
+
+            /* Handle e.g. <image href="https://example.com/image.png";> 
elements. Tested in
+            testLoadImageWithExternalImageHref. */
+            List<IOException> externalResourceExceptions = new ArrayList<>();
+            ResourceLoader resourceLoader = (URI nnuri) -> {
+              IOException e = new IOException("External resource loading from 
SVG file not permitted ("+
+                  nnuri + " from " + url + ")");
+              externalResourceExceptions.add(e);
+              throw e;
             };
-            DocumentLoader loader = new DocumentLoader(userAgent);
-            BridgeContext bctx = new BridgeContext(userAgent, loader);
-            try {
-                bctx.setDynamicState(BridgeContext.STATIC);
-                graphicsNode = new GVTBuilder().build(bctx, doc);
-                documentSize = bctx.getDocumentSize();
-            } finally {
-                bctx.dispose();
+            /* Handle e.g. <use xlink:href="http://foobar/not/exists#text";> 
elements. Tested in
+            testLoadImageWithExternalUseXlinkHref. */
+            DenyingElementLoader elementLoader = new DenyingElementLoader();
+
+            svgDocument = SVG_LOADER.get().load(is, null, 
LoaderContext.builder()
+                .resourceLoader(resourceLoader)
+                .elementLoader(elementLoader)
+                .build());
+            if (!elementLoader.getAttemptedExternalURLsLoaded().isEmpty()) {
+              throw new IOException("SVG loading failed; external document 
loading prohibited (" +
+                  elementLoader.getAttemptedExternalURLsLoaded() + ")");
+            }
+            if (!externalResourceExceptions.isEmpty()) {
+              IOException e = new IOException("SVG loading failed due to 
disallowed external resources");
+              for (IOException e2 : externalResourceExceptions) {
+                  e.addSuppressed(e2);
+              }
+              throw e;
+            }
+            if (svgDocument == null) {
+                throw new IOException(
+                        "SVG loading failed for " + url + " (SVGLoader.load 
returned null)");
             }
+            documentSize = svgDocument.size();
         } catch (RuntimeException e) {
-            /* Rethrow the many different exceptions that can occur when 
parsing invalid SVG files;
-            DOMException, BridgeException etc. */
+            /* Rethrow any uncaught exceptions that could be thrown when 
parsing invalid SVG files. */
             throw new IOException("Error parsing SVG file", e);
         } finally {
             is.close();
@@ -180,7 +187,7 @@ final class SVGIcon extends CachedHiDPIIcon {
             toSize.width = widthLimited;
             toSize.height = heightLimited;
         }
-        return graphicsNode;
+        return svgDocument;
     }
 
     private static RenderingHints createHints() {
@@ -188,7 +195,8 @@ final class SVGIcon extends CachedHiDPIIcon {
         hints.put(RenderingHints.KEY_ANTIALIASING, 
RenderingHints.VALUE_ANTIALIAS_ON);
         hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, 
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
         /* Ensure that outlined strokes (strokes converted to solid shapes) 
appear the same as
-        regular strokes, as they do during editing in Adobe Illustrator. */
+        regular strokes, as they do during editing in Adobe Illustrator. This 
hint is also
+        specifically recommended by JSVG's README ( 
https://github.com/weisJ/jsvg ). */
         hints.put(RenderingHints.KEY_STROKE_CONTROL, 
RenderingHints.VALUE_STROKE_PURE);
         return new RenderingHints(hints);
     }
@@ -198,15 +206,13 @@ final class SVGIcon extends CachedHiDPIIcon {
             Component c, ColorModel colorModel, int deviceWidth, int 
deviceHeight, double scale)
     {
         BufferedImage img = createBufferedImage(colorModel, deviceWidth, 
deviceHeight);
-        /* Use Batik's createGraphics method to improve performance and avoid 
the
-        "Graphics2D from BufferedImage lacks BUFFERED_IMAGE hint" warning. */
-        final Graphics2D g = GraphicsUtil.createGraphics(img);
+        final Graphics2D g = img.createGraphics();
         try {
             g.scale(scale, scale);
             try {
-                GraphicsNode graphicsNode = getGraphicsNode();
+                SVGDocument svgDocument = getSVGDocument();
                 g.addRenderingHints(createHints());
-                graphicsNode.paint(g);
+                svgDocument.renderWithPlatform(NullPlatformSupport.INSTANCE, 
g, null);
             } catch (IOException e) {
                 LOG.log(Level.WARNING,
                         "Unexpected exception while re-loading an SVG file 
that previously loaded successfully", e);
diff --git 
a/platform/openide.util.ui.svg/test/unit/src/org/openide/util/svg/SVGLoaderImplTest.java
 
b/platform/openide.util.ui.svg/test/unit/src/org/openide/util/svg/SVGLoaderImplTest.java
index c182c73593c..5aa2d7cbd22 100644
--- 
a/platform/openide.util.ui.svg/test/unit/src/org/openide/util/svg/SVGLoaderImplTest.java
+++ 
b/platform/openide.util.ui.svg/test/unit/src/org/openide/util/svg/SVGLoaderImplTest.java
@@ -44,7 +44,19 @@ public class SVGLoaderImplTest extends NbTestCase {
         assertNotNull("Image must not load", im);
     }
     
-    public void testLoadImageWithExternalHref() throws Exception {
+    public void testLoadImageWithExternalUseHrefXlink() throws Exception {
+      testLoadImageWithExternalHref("org/openide/util/svg/externalHref.svg");
+    }
+
+    public void testLoadImageWithExternalImageHrefXLink() throws Exception {
+      
testLoadImageWithExternalHref("org/openide/util/svg/externalImageHrefXLink.svg");
+    }
+
+    public void testLoadImageWithExternalImageHref() throws Exception {
+      
testLoadImageWithExternalHref("org/openide/util/svg/externalImageHref.svg");
+    }
+
+    public void testLoadImageWithExternalHref(String image) throws Exception {
         class H extends Handler {
             List<LogRecord> recorded = new ArrayList<>();
 
@@ -64,7 +76,7 @@ public class SVGLoaderImplTest extends NbTestCase {
         H h = new H();
         Logger.getLogger(ImageUtilities.class.getName()).addHandler(h);
         try {
-            Image im = 
ImageUtilities.loadImage("org/openide/util/svg/externalHref.svg", false); // 
NOI18N
+            Image im = ImageUtilities.loadImage(image, false); // NOI18N
             assertNull("Image must not load", im);
         } finally {
             Logger.getLogger(ImageUtilities.class.getName()).removeHandler(h);
diff --git 
a/platform/openide.util.ui.svg/test/unit/src/org/openide/util/svg/externalImageHref.svg
 
b/platform/openide.util.ui.svg/test/unit/src/org/openide/util/svg/externalImageHref.svg
new file mode 100644
index 00000000000..f36ed112730
--- /dev/null
+++ 
b/platform/openide.util.ui.svg/test/unit/src/org/openide/util/svg/externalImageHref.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+   "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd";>
+
+<!--
+
+   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
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; width="100" height="100" viewBox="0 
0 100 100">
+    <image href="https://example.com/image.png"; x="10" y="10" height="160" 
width="380"/>
+</svg>
diff --git 
a/platform/openide.util.ui.svg/test/unit/src/org/openide/util/svg/externalImageHrefXLink.svg
 
b/platform/openide.util.ui.svg/test/unit/src/org/openide/util/svg/externalImageHrefXLink.svg
new file mode 100644
index 00000000000..06f17561b2f
--- /dev/null
+++ 
b/platform/openide.util.ui.svg/test/unit/src/org/openide/util/svg/externalImageHrefXLink.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+   "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd";>
+
+<!--
+
+   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
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; width="100" height="100" viewBox="0 
0 100 100">
+    <image xlink:href="https://example.com/image.png"; x="10" y="10" 
height="160" width="380"/>
+</svg>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Reply via email to