From 825e10aa0e512a98abcf86eec3dec329cf42f178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Kr=C3=A1sa?= Date: Fri, 25 Aug 2023 22:00:20 +0200 Subject: [PATCH] #393, #400, #399, #398, #396 Migrated to EchoSVG --- CHANGELOG.md | 8 ++- build.gradle.kts | 20 ++---- gradle.properties | 2 +- .../preview/image/svg/MyImageEditorImpl.java | 6 +- .../preview/image/svg/batik/MyJDOMUtil.java | 67 ------------------- .../image/svg/batik/MySvgDocumentFactory.kt | 41 ++++++------ .../image/svg/batik/MySvgTranscoder.kt | 41 +++++++----- .../idea/rendering/CompatibilityCheck.java | 51 +++++++------- .../image/svg/batik/MySvgTranscoderTest.java | 13 +--- 9 files changed, 92 insertions(+), 157 deletions(-) delete mode 100644 src/main/java/org/plantuml/idea/preview/image/svg/batik/MyJDOMUtil.java diff --git a/CHANGELOG.md b/CHANGELOG.md index f9d55888..fd410e88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,5 +2,11 @@ ## [Unreleased] -- Rendering exception fix +## [7.0.0-IJ2023.2] - 2023-08-25 +- SVG rendering migrated to EchoSVG from Batik +- PlantUML library upgrade to v1.2023.10 + +[Unreleased]: https://github.com/esteinberg/plantuml4idea/compare/v7.0.0-IJ2023.2...HEAD + +[7.0.0-IJ2023.2]: https://github.com/esteinberg/plantuml4idea/commits/v7.0.0-IJ2023.2 diff --git a/build.gradle.kts b/build.gradle.kts index 03e318ea..4455becb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,29 +31,19 @@ java.sourceSets["main"].java { // Dependencies are managed with Gradle version catalog - read more: https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog dependencies { // https://mvnrepository.com/artifact/net.sourceforge.plantuml/plantuml - implementation("net.sourceforge.plantuml:plantuml:1.2023.9") + implementation("net.sourceforge.plantuml:plantuml:1.2023.10") testImplementation(platform("org.junit:junit-bom:5.9.1")) testImplementation("org.junit.jupiter:junit-jupiter") - implementation("org.jetbrains.intellij.deps.batik:batik-transcoder:1.16.0-32") { - exclude(group = "it.unimi.dsi", module = "fastutil") -// exclude(group = "xml-apis", module = "xml-apis") + implementation("io.sf.carte:echosvg-all:0.3.3") { +// exclude(group = "it.unimi.dsi", module = "fastutil") + exclude(group = "xml-apis", module = "xml-apis") // exclude(group = "xml-apis", module = "xml-apis-ext") exclude(group = "commons-io", module = "commons-io") exclude(group = "commons-logging", module = "commons-logging") } -// implementation("io.sf.carte:echosvg-all:0.3") - -// 2.7.3 breaks org.plantuml.idea.rendering.ImageItem.parseLinks - implementation("xalan:xalan:2.7.0") - - -// https://mvnrepository.com/artifact/xerces/xercesImpl - implementation("xerces:xercesImpl:2.12.2") -// https://mvnrepository.com/artifact/net.sf.saxon/Saxon-HE - implementation("net.sf.saxon:Saxon-HE:12.2") } @@ -157,7 +147,7 @@ tasks { } publishPlugin { -// dependsOn("patchChangelog") + dependsOn("patchChangelog") token = environment("PUBLISH_TOKEN") // The pluginVersion is based on the SemVer (https://semver.org) and supports pre-release labels, like 2.1.7-alpha.3 // Specify pre-release label to publish the plugin in a custom Release Channel automatically. Read more: diff --git a/gradle.properties b/gradle.properties index aefd06c6..1a6310b7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ pluginName=plantuml4idea pluginRepositoryUrl=https://github.com/esteinberg/plantuml4idea # SemVer format -> https://semver.org -pluginVersion=6.3.1-IJ2023.2 +pluginVersion=7.0.0-IJ2023.2 # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html pluginSinceBuild=232.6734.9 diff --git a/src/main/java/org/plantuml/idea/preview/image/svg/MyImageEditorImpl.java b/src/main/java/org/plantuml/idea/preview/image/svg/MyImageEditorImpl.java index 1a2e1c30..14491da6 100644 --- a/src/main/java/org/plantuml/idea/preview/image/svg/MyImageEditorImpl.java +++ b/src/main/java/org/plantuml/idea/preview/image/svg/MyImageEditorImpl.java @@ -39,8 +39,6 @@ import javax.xml.transform.stream.StreamResult; import java.awt.*; import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.InputStreamReader; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -227,9 +225,7 @@ public synchronized void createImage(Component component, double zoom) { throw new RuntimeException("Empty file"); } - ByteArrayInputStream in = new ByteArrayInputStream(buf); - InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8); - Document svgDocument = MySvgDocumentFactoryKt.createSvgDocument(null, reader); + Document svgDocument = MySvgDocumentFactoryKt.createSvgDocument(null, buf); logDocument(svgDocument); //it shows what is in png document - unZOOMED values, not limited by px limit diff --git a/src/main/java/org/plantuml/idea/preview/image/svg/batik/MyJDOMUtil.java b/src/main/java/org/plantuml/idea/preview/image/svg/batik/MyJDOMUtil.java deleted file mode 100644 index 3402d94e..00000000 --- a/src/main/java/org/plantuml/idea/preview/image/svg/batik/MyJDOMUtil.java +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package org.plantuml.idea.preview.image.svg.batik; - -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.text.Strings; -import org.jetbrains.annotations.ApiStatus; - -import javax.xml.stream.XMLInputFactory; - -public final class MyJDOMUtil { - - private static final Logger LOG = Logger.getInstance(MyJDOMUtil.class); - - private static final String XML_INPUT_FACTORY_KEY = "javax.xml.stream.XMLInputFactory"; - private static final String XML_INPUT_FACTORY_IMPL = "com.sun.xml.internal.stream.XMLInputFactoryImpl"; - - private static volatile XMLInputFactory XML_INPUT_FACTORY; - - // do not use AtomicNotNullLazyValue to reduce class loading - @ApiStatus.Internal - public static XMLInputFactory getXmlInputFactory() { - XMLInputFactory factory = XML_INPUT_FACTORY; - if (factory != null) { - return factory; - } - - //noinspection SynchronizeOnThis - synchronized (MyJDOMUtil.class) { - factory = XML_INPUT_FACTORY; - if (factory != null) { - return factory; - } - - // requests default JRE factory implementation instead of an incompatible one from the classpath - String property = System.setProperty(XML_INPUT_FACTORY_KEY, XML_INPUT_FACTORY_IMPL); - try { - //its fine - factory = XMLInputFactory.newFactory(); - - } finally { - if (property != null) { - System.setProperty(XML_INPUT_FACTORY_KEY, property); - } else { - System.clearProperty(XML_INPUT_FACTORY_KEY); - } - } - - // avoid loading of SystemInfo class - if (Strings.indexOfIgnoreCase(System.getProperty("java.vm.vendor", ""), "IBM", 0) < 0) { - try { - factory.setProperty("http://java.sun.com/xml/stream/properties/report-cdata-event", true); - } catch (Exception e) { - LOG.error("cannot set \"report-cdata-event\" property for XMLInputFactory", e); - } - } - - factory.setProperty(XMLInputFactory.IS_COALESCING, true); - factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); - factory.setProperty(XMLInputFactory.SUPPORT_DTD, false); - XML_INPUT_FACTORY = factory; - return factory; - } - } - - private MyJDOMUtil() { - } -} diff --git a/src/main/java/org/plantuml/idea/preview/image/svg/batik/MySvgDocumentFactory.kt b/src/main/java/org/plantuml/idea/preview/image/svg/batik/MySvgDocumentFactory.kt index 9c9fe038..41c80f3a 100644 --- a/src/main/java/org/plantuml/idea/preview/image/svg/batik/MySvgDocumentFactory.kt +++ b/src/main/java/org/plantuml/idea/preview/image/svg/batik/MySvgDocumentFactory.kt @@ -1,33 +1,37 @@ // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.plantuml.idea.preview.image.svg.batik -import org.apache.batik.anim.dom.SVG12DOMImplementation -import org.apache.batik.anim.dom.SVGDOMImplementation -import org.apache.batik.anim.dom.SVGOMDocument -import org.apache.batik.dom.GenericCDATASection -import org.apache.batik.dom.GenericText -import org.apache.batik.transcoder.TranscoderException -import org.apache.batik.util.ParsedURL +import com.intellij.util.xml.dom.createXmlStreamReader +import io.sf.carte.echosvg.anim.dom.SVG12DOMImplementation +import io.sf.carte.echosvg.anim.dom.SVGDOMImplementation +import io.sf.carte.echosvg.anim.dom.SVGOMDocument +import io.sf.carte.echosvg.dom.GenericCDATASection +import io.sf.carte.echosvg.dom.GenericText +import io.sf.carte.echosvg.transcoder.TranscoderException +import io.sf.carte.echosvg.util.ParsedURL +import org.codehaus.stax2.XMLStreamReader2 import org.jetbrains.annotations.ApiStatus import org.w3c.dom.Document import org.w3c.dom.Element import org.w3c.dom.Node -import java.io.Reader +import java.io.InputStream import javax.xml.stream.XMLStreamConstants import javax.xml.stream.XMLStreamException import javax.xml.stream.XMLStreamReader @ApiStatus.Internal -fun createSvgDocument(uri: String?, reader: Reader): Document { - val result = reader.use { - val xmlStreamReader = MyJDOMUtil.getXmlInputFactory().createXMLStreamReader(reader) - try { - buildDocument(xmlStreamReader) - } catch (e: XMLStreamException) { - throw TranscoderException(e) - } finally { - xmlStreamReader.close() - } +fun createSvgDocument(uri: String?, reader: InputStream) = createSvgDocument(uri, createXmlStreamReader(reader)) + +@ApiStatus.Internal +fun createSvgDocument(uri: String?, data: ByteArray) = createSvgDocument(uri, createXmlStreamReader(data)) + +private fun createSvgDocument(uri: String?, xmlStreamReader: XMLStreamReader2): Document { + val result = try { + buildDocument(xmlStreamReader) + } catch (e: XMLStreamException) { + throw TranscoderException(e) + } finally { + xmlStreamReader.close() } if (uri != null) { @@ -36,7 +40,6 @@ fun createSvgDocument(uri: String?, reader: Reader): Document { } return result } - private fun buildDocument(reader: XMLStreamReader): SVGOMDocument { var state = reader.eventType if (XMLStreamConstants.START_DOCUMENT != state) { diff --git a/src/main/java/org/plantuml/idea/preview/image/svg/batik/MySvgTranscoder.kt b/src/main/java/org/plantuml/idea/preview/image/svg/batik/MySvgTranscoder.kt index 6299eeb9..013b5846 100644 --- a/src/main/java/org/plantuml/idea/preview/image/svg/batik/MySvgTranscoder.kt +++ b/src/main/java/org/plantuml/idea/preview/image/svg/batik/MySvgTranscoder.kt @@ -5,17 +5,22 @@ package org.plantuml.idea.preview.image.svg.batik import com.intellij.openapi.diagnostic.Logger import com.intellij.util.ImageLoader -import org.apache.batik.anim.dom.SVGOMDocument -import org.apache.batik.bridge.* -import org.apache.batik.bridge.svg12.SVG12BridgeContext -import org.apache.batik.ext.awt.RenderingHintsKeyExt -import org.apache.batik.gvt.CanvasGraphicsNode -import org.apache.batik.gvt.CompositeGraphicsNode -import org.apache.batik.gvt.GraphicsNode -import org.apache.batik.transcoder.TranscoderException -import org.apache.batik.util.ParsedURL -import org.apache.batik.util.SVGConstants -import org.apache.batik.util.SVGFeatureStrings +import io.sf.carte.echosvg.anim.dom.SVGOMDocument +import io.sf.carte.echosvg.bridge.* +import io.sf.carte.echosvg.bridge.svg12.SVG12BridgeContext +import io.sf.carte.echosvg.ext.awt.RenderingHintsKeyExt +import io.sf.carte.echosvg.ext.awt.image.codec.imageio.ImageIOJPEGRegistryEntry +import io.sf.carte.echosvg.ext.awt.image.codec.imageio.ImageIOPNGRegistryEntry +import io.sf.carte.echosvg.ext.awt.image.codec.imageio.ImageIOTIFFRegistryEntry +import io.sf.carte.echosvg.ext.awt.image.codec.png.PNGRegistryEntry +import io.sf.carte.echosvg.ext.awt.image.spi.ImageTagRegistry +import io.sf.carte.echosvg.gvt.CanvasGraphicsNode +import io.sf.carte.echosvg.gvt.CompositeGraphicsNode +import io.sf.carte.echosvg.gvt.GraphicsNode +import io.sf.carte.echosvg.transcoder.TranscoderException +import io.sf.carte.echosvg.util.ParsedURL +import io.sf.carte.echosvg.util.SVGConstants +import io.sf.carte.echosvg.util.SVGFeatureStrings import org.jetbrains.annotations.ApiStatus import org.plantuml.idea.settings.PlantUmlSettings import org.w3c.dom.Document @@ -25,11 +30,11 @@ import org.w3c.dom.svg.SVGDocument import java.awt.* import java.awt.geom.AffineTransform import java.awt.image.BufferedImage -import java.io.StringReader import java.lang.ref.WeakReference import kotlin.math.max import kotlin.math.min + private fun logger() = Logger.getInstance(MySvgTranscoder::class.java) private val identityTransform = AffineTransform() @@ -39,6 +44,12 @@ private val supportedFeatures = HashSet() class MySvgTranscoder private constructor(private var width: Float, private var height: Float) : UserAgent { companion object { init { + val registry = ImageTagRegistry.getRegistry() + registry.register(PNGRegistryEntry()) + registry.register(ImageIOTIFFRegistryEntry()) + registry.register(ImageIOJPEGRegistryEntry()) + registry.register(ImageIOPNGRegistryEntry()) + SVGFeatureStrings.addSupportedFeatureStrings(supportedFeatures) } @@ -151,7 +162,7 @@ class MySvgTranscoder private constructor(private var width: Float, private var " \n" + " \n" + "\n" - return createSvgDocument(null, StringReader(fallbackIcon)) as SVGDocument + return createSvgDocument(null, fallbackIcon.toByteArray()) as SVGDocument } override fun getTransform() = currentTransform!! @@ -188,7 +199,7 @@ class MySvgTranscoder private constructor(private var width: Float, private var override fun getPixelUnitToMillimeter(): Float = 0.26458333333333333333333333333333f // 96dpi - override fun getPixelToMM() = pixelUnitToMillimeter +// override fun getPixelToMM() = pixelUnitToMillimeter override fun getDefaultFontFamily() = "Arial, Helvetica, sans-serif" @@ -205,7 +216,7 @@ class MySvgTranscoder private constructor(private var width: Float, private var override fun getUserStyleSheetURI() = null - override fun getXMLParserClassName() = null +// override fun getXMLParserClassName() = null override fun isXMLParserValidating() = false diff --git a/src/main/java/org/plantuml/idea/rendering/CompatibilityCheck.java b/src/main/java/org/plantuml/idea/rendering/CompatibilityCheck.java index 7d739a6e..6b528db6 100644 --- a/src/main/java/org/plantuml/idea/rendering/CompatibilityCheck.java +++ b/src/main/java/org/plantuml/idea/rendering/CompatibilityCheck.java @@ -11,28 +11,31 @@ import static org.plantuml.idea.util.UIUtils.notification; public class CompatibilityCheck { - public static boolean checkTransformer(PlantUmlSettings plantUmlSettings) { - if (!plantUmlSettings.isDisplaySvg()) { - return false; - } - if (plantUmlSettings.isRemoteRendering()) { - return false; - } - - TransformerFactory factory = TransformerFactory.newInstance(); - ClassLoader classLoader = factory.getClass().getClassLoader(); - - if (classLoader instanceof PluginAwareClassLoader) { - String clName = ((PluginAwareClassLoader) classLoader).getPluginDescriptor().getName(); - - - plantUmlSettings.setDisplaySvg(false); - - SwingUtilities.invokeLater(() -> { - Notifications.Bus.notify(notification().createNotification("Conflict detected with '" + clName + "'. Switching to PNG rendering.", NotificationType.WARNING)); - }); - return false; - } - return true; - } + public static boolean checkTransformer(PlantUmlSettings plantUmlSettings) { + if (!plantUmlSettings.isDisplaySvg()) { + return false; + } + if (plantUmlSettings.isRemoteRendering()) { + return false; + } +// if (true) { +// return true; +// } + + TransformerFactory factory = TransformerFactory.newInstance(); + ClassLoader classLoader = factory.getClass().getClassLoader(); + + if (classLoader instanceof PluginAwareClassLoader) { + String clName = ((PluginAwareClassLoader) classLoader).getPluginDescriptor().getName(); + + + plantUmlSettings.setDisplaySvg(false); + + SwingUtilities.invokeLater(() -> { + Notifications.Bus.notify(notification().createNotification("Conflict detected with '" + clName + "'. Switching to PNG rendering.", NotificationType.WARNING)); + }); + return false; + } + return true; + } } diff --git a/src/test/java/org/plantuml/idea/preview/image/svg/batik/MySvgTranscoderTest.java b/src/test/java/org/plantuml/idea/preview/image/svg/batik/MySvgTranscoderTest.java index 7d00d3cd..e872bd81 100644 --- a/src/test/java/org/plantuml/idea/preview/image/svg/batik/MySvgTranscoderTest.java +++ b/src/test/java/org/plantuml/idea/preview/image/svg/batik/MySvgTranscoderTest.java @@ -1,32 +1,25 @@ package org.plantuml.idea.preview.image.svg.batik; -import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.ImageLoader; -import org.apache.batik.transcoder.TranscoderException; +import io.sf.carte.echosvg.transcoder.TranscoderException; import org.apache.commons.io.FileUtils; import org.junit.Test; import org.w3c.dom.Document; import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; public class MySvgTranscoderTest { @Test - public void sanitize() throws IOException, TranscoderException { + public void test() throws IOException, TranscoderException { renderSvg("src/test/resources/testData/test2.svg"); renderSvg("src/test/resources/testData/test3.svg"); } private static void renderSvg(String pathname) throws IOException, TranscoderException { - String text = FileUtils.readFileToString(new File(pathname), CharsetToolkit.UTF8); - ByteArrayInputStream in = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); - InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8); - Document svgDocument = MySvgDocumentFactoryKt.createSvgDocument(null, reader); + Document svgDocument = MySvgDocumentFactoryKt.createSvgDocument(null, FileUtils.readFileToByteArray(new File(pathname))); //it shows what is in png document - unZOOMED values, not limited by px limit ImageLoader.Dimension2DDouble outSize = new ImageLoader.Dimension2DDouble(0.0D, 0.0D); BufferedImage image = MySvgTranscoder.createImage((float) 1.1, svgDocument, outSize);