Git Product home page Git Product logo

echosvg's Introduction

EchoSVG

EchoSVG is a fork of Apache Batik, a Java based toolkit for applications which handle images in the Scalable Vector Graphics (SVG) format for various purposes, such as viewing, generation or manipulation.

Thanks to its usage of the CSS4J style parser, supports level 4 CSS selectors and color values (including colors with alpha channel) as well as the Level 5 color-mix() function.

This document mostly contains information of interest to developers, like how to build from source or run tests. If you want general information (like how to use the binaries) you are invited to read the Wiki, especially the part about why this fork was created.

If you are using Apache Batik you may want to read how Batik's testing infrastructure was improved, and if you decide to switch to EchoSVG, the MIGRATING_FROM_BATIK document may be of help.


Building from source

Requirements

To build EchoSVG you need the following software installed:

  • The Git version control system is required to obtain the sources. Any recent version should suffice.

  • Java 11 or later. You can install it from your favourite package manager or by downloading from Adoptium.


Building with Gradle

Execute the build script with gradlew build to build. For example:

git clone https://github.com/css4j/echosvg.git
cd echosvg
./gradlew build

Tests

Most of the (JUnit-based) test suite is executed during build (except some tests that are currently incompatible with Gradle), but beware that some tests are platform-dependent and could fail. If you encounter test failures, please open an issue with the details so the necessary tweaks can be made. If the failing test involves an image comparison, you may want to read IMAGE_COMPARISONS.

To build without running the tests:

./gradlew build -x test

The full test suite can be executed from the Eclipse IDE, and most of it runs satisfactorily from IntelliJ IDEA; please open an issue if you find issues executing the tests from other IDEs.

Note: when running tests from the Eclipse IDE, it is recommended to run them as a "JUnit Test" in the "Run As" menu option. If you run them as "Gradle Test" you may encounter well-known security-related issues (issue #19).

Interactive tests

Several interactive tests can be executed via:

./gradlew iTest

Note that several of those tests are failing (a few being unfinished).


Benchmarks

This project uses JMH for benchmarking. To run all the benchmarks (currently only one):

./gradlew runJmh

To run specific benchmark(s) matched by a regular expression:

./gradlew runJmh <regexp>

A jmh-ready fat Jar with all the dependencies is available at echosvg-test/build/libs/echosvg-<version>-jmh.jar, and is the recommended way to run benchmarks:

java -jar echosvg-test/build/libs/echosvg-<version>-jmh.jar <regexp>

Deploying to a Maven repository

Use:

  • ./gradlew publishToMavenLocal to install in your local Maven repository.

  • ./gradlew publish to deploy to a (generally remote) Maven repository.

If you plan to deploy to a repository, please configure the mavenReleaseRepoUrl and/or mavenSnapshotRepoUrl properties (for example in GRADLE_USER_HOME/gradle.properties or in the command line). Otherwise, Gradle shall create a build/repository subdirectory and deploy there.

Properties mavenRepoUsername and mavenRepoPassword can also be set (generally from the command line).

If you would rather look directly at the Gradle publish configurations, please read the publishing.repositories.maven block of echosvg.java-conventions.gradle.


Creating a uber Jar or fat Jar

Sometimes, in non-modular projects it is useful to have a single Jar file bundled with all the dependencies, often called a uber Jar or fat Jar. Execute the uberjar task to create it:

./gradlew uberjar

The file is to be found at echosvg-all/build/libs/echosvg-all-<version>-alldeps.jar.


Produce a modular Javadoc

A Javadoc of all the modules is produced by the modularJavadoc task:

./gradlew modularJavadoc

The Javadocs are created at echosvg-all/build/docs/modular.


Produce a non-modular Javadoc

If you do not like modular javadocs, a merged non-modular Javadoc can be built with the mergedJavadoc task:

./gradlew mergedJavadoc

The Javadocs are created at echosvg-all/build/docs/javadoc.

This task may be removed in the future; if the non-modular javadocs are useful to you, please open an issue so it is preserved.


Other common tasks

OWASP Dependency Check

Check for known vulnerable dependencies that are used in the build.

./gradlew dependencyCheckAnalyze

Dependency Licensing

Generate a report about the licenses of the dependencies:

./gradlew generateLicenseReport

Check that the licenses of all the dependencies are allowed, fail otherwise:

./gradlew checkLicense

(the above task is executed on each build)


Open the project in your IDE

Modern IDEs are able to import Gradle projects and let it manage the dependencies. In IntelliJ IDEA you can just open the root directory and the Gradle project is opened, while in the Eclipse IDE you need to import it explicitly:

File > Import... > Gradle > Existing Gradle Project

Eclipse shall ask you if you want to use a wrapper or its own instance of Gradle, select the "wrapper" choice.

In Eclipse, it is advisable to run a build with ./gradlew build before importing the project. Apparently Eclipse requires some files produced by a build but is unable to do that by itself.

Note: it is normal to experience build issues with the echosvg-test-scripts subproject in Eclipse, you may prefer to keep that project closed.


Usage from a Gradle project

If your Gradle project depends on echosvg, you can use this project's own Maven repository in a repositories section of your build file:

repositories {
    maven {
        url "https://css4j.github.io/maven/"
        mavenContent {
            releasesOnly()
        }
        content {
            includeGroup 'com.github.css4j'
            includeGroup 'io.sf.carte'
            includeGroup 'io.sf.jclf'
        }
    }
}

please use that repository only for the artifact groups that it supplies (basically those listed in the above includeGroup statements).

Then, in your build.gradle file you can list the dependencies, for example:

dependencies {
    implementation "io.sf.carte:echosvg-transcoder:${echosvgVersion}"
}

or, if you want all of the main modules:

dependencies {
    implementation "io.sf.carte:echosvg-all:${echosvgVersion}"
}

where echosvgVersion would be defined in a gradle.properties file (current version is 1.0.1).


Licensing

For licensing issues, please read the LICENSE and NOTICE files. The tests use files which may have their own additional licenses, under the samples directory. Please look for the appropriate license files there.

echosvg's People

Contributors

carlosame avatar cjolif avatar dependabot[bot] avatar jmaerki avatar simonsteiner1984 avatar smowton avatar stefanom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

Forkers

mongox develar

echosvg's Issues

CleanerThread is never cleaned up and causes memory leak

As explained in BATIK-939, CleanerThread does not self-terminate and messes with garbage collection.

@jjYBdx4IL has provided a pull request to Apache Batik to fix BATIK-939 and I have been experimenting with a patch for EchoSVG based on it. Perhaps @jjYBdx4IL may want to provide his own PR but meanwhile my adapted patch looks like this:

In echosvg-util/src/main/java/io/sf/carte/echosvg/util/CleanerThread.java:

@@ -21,10 +21,12 @@ package io.sf.carte.echosvg.util;
 import java.lang.ref.PhantomReference;
 import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.SoftReference;
 import java.lang.ref.WeakReference;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * One line Class Desc
  *
  * Complete Class Desc
@@ -35,15 +37,16 @@ import java.lang.ref.WeakReference;
  */
 public class CleanerThread extends Thread {
 
 	static volatile ReferenceQueue<Object> queue = null;
 	static CleanerThread thread = null;
+	private static CountDownLatch shutdownLatch = new CountDownLatch(1);
 
 	public static ReferenceQueue<Object> getReferenceQueue() {
 
-		if (queue == null) {
-			synchronized (CleanerThread.class) {
+		synchronized (CleanerThread.class) {
+			if (queue == null) {
 				queue = new ReferenceQueue<>();
 				thread = new CleanerThread();
 			}
 		}
 		return queue;
@@ -95,27 +98,42 @@ public class CleanerThread extends Thread {
 		start();
 	}
 
 	@Override
 	public void run() {
-		while (true) {
-			try {
+		try {
+			/*
+			 * Give a big enough timeout to account for busy VMs.
+			 */
+			while(!shutdownLatch.await(4000, TimeUnit.MILLISECONDS)) {
 				Reference<?> ref;
-				try {
-					ref = queue.remove();
-					// System.err.println("Cleaned: " + ref);
-				} catch (InterruptedException ie) {
-					continue;
-				}
-
-				if (ref instanceof ReferenceCleared) {
-					ReferenceCleared rc = (ReferenceCleared) ref;
-					rc.cleared();
-				}
-			} catch (ThreadDeath td) {
-				throw td;
-			} catch (Throwable t) {
-				t.printStackTrace();
+				do {
+					ref = queue.poll();
+					if (ref instanceof ReferenceCleared) {
+						ReferenceCleared rc = (ReferenceCleared) ref;
+						rc.cleared();
+					}
+				} while (ref != null);
+			}
+		} catch (ThreadDeath td) {
+			throw td;
+		} catch (InterruptedException ie) {
+			// Expected in this context, we do not want to print the
+			// stack trace
+		} catch (Throwable t) {
+			// Unexpected: print the stack trace
+			t.printStackTrace();
+		} finally {
+			// Release the latch in case it wasn't.
+			shutdownLatch.countDown();
+			// Synchronize to avoid a race with getReferenceQueue()
+			synchronized (CleanerThread.class) {
+				queue = null;
 			}
 		}
 	}
+
+	public static void shutdown() {
+		shutdownLatch.countDown();
+	}
+
 }

and in echosvg-util/src/main/java/io/sf/carte/echosvg/util/HaltingThread.java:

@@ -30,11 +30,11 @@ package io.sf.carte.echosvg.util;
  */
 public class HaltingThread extends Thread {
 	/**
 	 * Boolean indicating if this thread has ever been 'halted'.
 	 */
-	protected boolean beenHalted = false;
+	protected volatile boolean beenHalted = false;
 
 	public HaltingThread() {
 	}
 
 	public HaltingThread(Runnable r) {

DocumentFactory constructors with String argument are not practical and use a deprecated method

EchoSVG has some *DocumentFactory constructors with String argument, to specify the XML parser class as a string. To secure those parser (actually XMLReader) instances against XXE and SSRF attacks, the http://apache.org/xml/features/nonvalidating/load-external-dtd feature (as well as others) is disabled.

However, that feature only applies to Xerces-J and the Xerces-based parser that is bundled with the OpenJDK, so if another parser is being used it will not work. Even when using only the supported parsers this also limits their configurability, and one consequence is that XML entities are always going to be lost because no possibility is given to configure the XMLReader.

Moreover, the instantiation of the parser uses a deprecated method.

Solution: modify the constructors to use a XMLReader as argument instead of a String, and configure the parsers in a way that they can handle XML entities and are still secure.

ConvolveMatrix test fails with an ImagingOpException

The ConvolveMatrix test (rendering of samples/tests/spec/filters/feConvolveMatrix.svg in SamplesRenderingTest.testFilterFeConvolveMatrix()) fails with an exception:

java.awt.image.ImagingOpException: Unable to convolve src image

This bug is the same as BATIK-1280.

The problem happens at line 404 of io.sf.carte.echosvg.ext.awt.image.renderable.ConvolveMatrixRable8Bit:

    // Easy case just apply the op...
    destBI = op.filter(srcBI, null);

which calls java.awt.image.ConvolveOp.filter(BufferedImage, BufferedImage) and there (line 197):

if (ImagingLib.filter(this, src, dst) == null) {
    throw new ImagingOpException ("Unable to convolve src image");
}

Once in ImagingLib.filter we get into ImagingLib.convolveBI(BufferedImage, BufferedImage, Kernel, int) which is where it fails (line 259): it tests for the convolveBI result being greater than zero which is not the case, and then returns the null value which triggers the exception.

It is difficult to debug what is going on in ImagingLib.convolveBI because it is native code.

Test 'textDecoration2' needs to use a different font

The test SamplesRenderingTest.testTextDecoration2() (which renders samples/tests/spec/text/textDecoration2.svg) fails. The expected result is on the left:

textDecoration2_cmp

The reason of the failure is that the two images are rendering a different font: the original (on the left) uses the non-free Lucida Sans Typewriter while the current test (on the right) uses NotCourierSans. The font faces are obviously different and that was expected, but a few decorations are lost and the problem is clearly related to the new font (when the old font is used, the test passes).

Either a new free font has to be used, or go back to the old test which uses the non-free font Lucida Sans Typewriter.

Modify the licensing notice at the top of source files

Most or all of the source files coming from Apache Batik contain the following comment at the header:

/*

   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.

 */

however, it is no longer the case that the ASF manages those files, so the idea is to replace the above text with the following:

/*

   See the NOTICE file distributed with this work for additional
   information regarding copyright ownership.

   Licensed 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.

 */

This issue is opened as a reference for the change.

Prepare Milestone Release 0.1

The project is in a state where a preliminary release could be produced, if the test binary artifacts are skipped (tests are not ready yet). Realistically, nobody is going to use this project unless some binaries are available.

This issue is opened for possible feedback abut this forthcoming release, which shall be published at the css4j Maven repository.

Security Manager is deprecated for removal

Since Java 17, the Security Manager is deprecated for removal (see JEP 411).

This project uses the Security Manager API and needs to adapt, by using the Security Manager only if available. That should be done in two steps:

  • Milestone Release 0.1.2: refactor classes that use the deprecated APIs, deprecate for removal the public API that uses deprecated SM classes in their signatures.

  • Milestone Release 0.2: remove the part of the public API that uses deprecated-for-removal SM classes in their signatures.

If you'd be affected by the removal of that part of the public API, please comment in this issue.

Xalan jar file is broken

The latest Xalan jar file (released July 24, 2014) is xalan-2.7.2.jar, which claims to provide a service (via META-INF/services) with a non-existent class file. This breaks modularisation attempts with:

[ERROR] Error occurred during initialization of boot layer
[ERROR] java.lang.module.FindException: Module xalan not found, required by io.sf.carte.echosvg.dom

The issue was reported by @i-make-robots as XALANJ-2632 but was well known already:

When Dan's issue was posted to the xalan-dev mailing list, he got a response from Joseph Kesselman @keshlam on that list. Here is an excerpt:

As far as I know, Xalan is not currently being actively maintained. I've been
considering putting some time into it.

Which are good news —at least for us, not sure about him ;-) given the current shape of that code— however Xalan-J is an ASF project, and even if a single developer is active he may not even be able to trigger a new release.

Meanwhile, I decided to look for ways to get rid of Xalan. This project uses Xalan to implement XPath support in the old Batik/EchoSVG DOM implementation, something that is used by the (deprecated) SVG 1.2 support. At some point the obsolete SVG 1.2 support is going to be removed (and hence Xalan or whatever XPath provider this project adopts), but only when the support for more modern CSS and SVG justifies that.

Another possibility was to refactor the code so Xalan is only used when available in the classpath/modulepath, but that's still problematic. And switching to a better XPath provider like Saxon HE would require too much coding for an already deprecated feature.

So today I finally forked Xalan and I'm experimenting with that fork (called EchoXSL). You can already browse the repository at css4j/echoxsl: it has a new Gradle build and the tests at least do compile (not without removing a few files that depended on classes that were removed long ago from Xalan).

For now, my intention is to use the new Xalan fork in EchoSVG 0.2 (planned for June). Comments are welcome.

Support modern CSS

Although EchoSVG supports level 4 selectors, the rest of CSS is only supported at CSS 2. Current SVG images tend to use modern CSS, for example:

  • Custom properties (see #35).
  • Level 4 values and units (including calc() and other functions).
  • Media queries.

This issue is opened to discuss the transition of this project to a more modern CSS. The intention is to migrate the current CSS Object Model to something based on CSS4J's Object Model.

incorrect relative move from getNormalizedPathSegList

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="210mm" height="297mm" viewBox="0 0 210 297" version="1.1" id="svg5" inkscape:version="1.1.1 (c3084ef, 2021-09-22)" sodipodi:docname="anniversaire.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
  <sodipodi:namedview id="namedview7" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:document-units="mm" showgrid="false" inkscape:zoom="0.77771465" inkscape:cx="7.7149119" inkscape:cy="576.04676" inkscape:window-width="2560" inkscape:window-height="1330" inkscape:window-x="1680" inkscape:window-y="25" inkscape:window-maximized="1" inkscape:current-layer="layer1"/>
  <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1">
    <g id="g888" transform="matrix(0.11652319,0,0,0.12098981,5.0275387,231.79878)">
      <path d="
M 118.5,102.5
C 118.5,80.168 136.668,62 159,62 
h 144
c 22.332,0 40.5,18.168 40.5,40.5
v 48
c 0,8.239 -16.171,16.553 -30.019,21.169
C 291.56,178.976 262.268,183 231,183 199.732,183 170.439,178.976 148.519,171.669 134.671,167.053 118.5,158.739 118.5,150.5
Z
m 0,80
v -8.518
c 5.971,4.143 14.148,8.208 25.275,11.918 23.41,7.803 54.387,12.1 87.225,12.1 32.838,0 63.815,-4.297 87.225,-12.101 11.128,-3.709 19.304,-7.775 25.275,-11.918
v 8.518
c 0,22.332 -18.168,40.5 -40.5,40.5 
H 159
C 136.668,223 118.5,204.832 118.5,182.5
Z
"/>
    </g>
  </g>
</svg>

Raw data I log receiving from EchoSVG 0.2 is

M 118.5 102.5
C 118.5 80.168 136.668 62.0 159.0 62.0
L 303.0 62.0
C 325.332 62.0 343.5 80.168 343.5 102.5
L 343.5 150.5
C 343.5 158.739 327.329 167.053 313.481 171.669
C 291.56 178.976 262.268 183.0 231.0 183.0
C 199.732 183.0 170.439 178.976 148.519 171.669
C 134.671 167.053 118.5 158.739 118.5 150.5
z
closing to (118.5, 102.5, 0.0)
M 118.5 230.5 // <-- UH OH should be 118.5, 182.5
L 118.5 221.982
C 124.471 226.125 132.648 230.19 143.775 233.9
C 167.185 241.70299 198.16199 246.0 231.0 246.0
C 263.838 246.0 294.815 241.703 318.225 233.899
C 329.353 230.19 337.529 226.12401 343.5 221.981
L 343.5 230.49901
C 343.5 252.83101 325.332 270.99902 303.0 270.99902
L 159.0 270.99902
C 136.668 223.0 118.5 204.832 118.5 182.5
z
closing to (118.5, 230.5, 0.0)

Filter extension attributes test fails

The filter extension attributes test (rendering of samples/tests/spec12/filters/filterRegion.svg) fails, expected result is at the left:

filterRegion_cmp

It is not drawing the line elements without the filter. And both Chrome and Firefox render the file differently to Batik's reference.

Reduce the public API

This project has a number of public and protected fields and methods that aren't really expected to be used by downstream software. This was very common in old Java code, but nowadays the tendency is to reduce the public supported API to a minimum.

  • Some public and protected methods are going to be deprecated for removal (where 'removal' means becoming package-visible), while others that are highly unlikely to be used by anyone else will have its visibility directly reduced.

  • public and protected fields are going to be removed. If you use any, you should switch to the relevant getter or setter method.

This reduction of the public API is a pre-requisite for other issues like #39.

Separate build steps into BUILD.md

It is better to have them displayed at the Github's project homepage. Most projects have it that way.

Leaving argumentum ad populum aside, at the very least we'll want to have a clearly enumerated set of instructions. For example:


Building

This section defines how to build the library.

Requirements

Download then install the following software packages:

  • Java version X
  • Maven version X
  • Git version X
  • JCLF
  • CSS4J

Install dependencies (Linux)

Install the software dependencies for the first time as follows:

  1. wget https://raw.githubusercontent.com/css4j/css4j-dist/master/install-jclf.sh
  2. sh install-jclf.sh
  3. wget https://raw.githubusercontent.com/css4j/css4j-dist/master/install-css4j.sh
  4. sh install-css4j.sh

The dependencies of JCLF and CSS4J are installed.

Build library

Build the library as follows:

  1. git clone https://github.com/css4j/echosvg
  2. cd echosvg
  3. mvn package

The library is built as path/to/file.jar.


From here, we'll need additional instructions showing how to build variations.

At this point, it's trivial to see how we could have a build.sh script that installs the Maven dependencies on behalf of the end user. That, eventually, could lead to a Maven (arguably easier with Gradle) integration in the pom.xml file.

Externalize application title

Create a class similar to JCLF's ApplicationInfo that can import application information from an external source, such as a properties file.

SVG12TextElementBridge performs cast without instanceof

Re: https://issues.apache.org/jira/browse/BATIK-1302

@carlosame notes:

The proper fix would be a major refactor: the code is asking for it, especially the CSSOM part (already did a small one there, but far from enough). Meanwhile a smaller fix would be of help.

The smaller, simple fix in the interim is to add an instanceof check to In SVG12TextElementBridge, around line 67, like:

if( ctx instanceof SVG12BridgeContext ) {
   ctx12 = (SVG12BridgeContext) ctx;
}

Generator: support decoration attributes in text elements

In svggen, decoration attributes UNDERLINE and STRIKETHROUGH are supported when drawing an AttributedCharacterIterator (which results in generated tspan elements), but not for plain drawString(String, float, float) which gives text elements.

`org.w3c.dom.xpath` classes not found when compiling with source/target 8

The org.w3c.dom.xpath package is supposed to be available in modular JDKs, however it is not found when compiling for source/target 8.

For example:

$ ./gradlew build -x test

> Task :echosvg-svg-dom:compileLegacyJava FAILED
C:\***\echosvg\echosvg-svg-dom\src\main\java\io\sf\carte\echosvg\dom\svg\SVGOMUseShadowRoot.java:85: error: cannot access XPathEvaluator
                return ownerDocument.getChildElementById(this, id);
                                    ^
  class file for org.w3c.dom.xpath.XPathEvaluator not found
1 error

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':echosvg-svg-dom:compileLegacyJava'.
> Compilation failed; see the compiler error output for details.

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 8s
56 actionable tasks: 17 executed, 39 up-to-date

This project uses Xalan which carries a transitive dependency on xml-apis that is incompatible with modular JDKs. If that dependency is completely excluded (see the build failure above), this project won't compile when using source/target 8. If it is not excluded (which is currently the case), then downstream users with modular JDKs are forced to exclude it when declaring our dependency:

dependencies {
    api ("io.sf.carte:echosvg-transcoder:0.1.2") {
        exclude group: 'xml-apis', module: 'xml-apis'
    }
}

or

  <dependency>
      <groupId>io.sf.carte</groupId>
      <artifactId>echosvg-transcoder</artifactId>
      <version>0.1.2</version>
      <exclusions>
        <exclusion>
          <groupId>xml-apis</groupId>
          <artifactId>xml-apis</artifactId>
        </exclusion>
      </exclusions>
  </dependency>

Although this looks like a JDK bug, a workaround would be to block the xml-apis transitive dependency from Xalan, while adding a number of:

  implementation "xml-apis:xml-apis:$xmlApisVersion"

to this project's build files. This way, one can have xml-apis during the Java 8 compilation but it is not carried as a transitive dependency.

Migrate to an API not centered on SVGDocument

The APIs of this project were designed around the SVGDocument, but the current SVG specification uses the concept of a SVG document fragment instead.

The current code assumes that every and each SVG image is a separate document with the svg element being the root element. However, modern documents often contain a number of SVG sub-trees (each being a different image) and the API of a SVG toolkit like this project should allow that.

Supporting more modern SVG should not be controversial, however this implies breaking backwards compatibility at the API level and some people may believe that this makes no sense for a project with old roots like this one. This issue is opened to discuss the change: If you have an opinion on whether the current API should be kept or not, please post it here.

Fonts registered after DefaultFontFamilyResolver singleton was instantiated are not found

If a font is registered with the GraphicsEnvironment after the DefaultFontFamilyResolver singleton was instantiated, it is not found and EchoSVG is unable to use it.

Task: add a method to register new fonts with DefaultFontFamilyResolver.

Another possible solution would be to make DefaultFontFamilyResolver more efficient and able to resolve fonts on-the-fly, but this would require a wider test suite than the project has now (to detect regressions) and perhaps JMH benchmarks as well...

Remove obsolete Python 2.x scripting support

The echosvg-script module supports Python 2.x scripting (which is disabled by default) via the jython package, but that package is obsolete and gets in the way of full modularisation. Quoting from #8 (comment):

The obsolete Python 2.x scripting seems an obvious target for removal: nobody is going to pay jython developers to implement Python 3.x (all the resources in that area are being dropped into GraalVM) and that 3.x upgrade is too big for the volunteer developers working in their spare time (see jython/jython/issues/24).

So removing the support for the superseded Python 2.x scripting seems appropriate.

Security hardening

The security and privacy aspects of this project could be improved in several fronts:

  1. Enable optional resource limits intended for the processing of untrusted documents.

  2. Identify potentially insecure parts of a document, for example resource-intensive selectors or privacy-challenging attribute selectors. This could be achieved while processing the SVG or through external tools (like an improved version of AntiSamy).

  3. Migrate to a different security model. The security model currently used by this project is problematic and I'd like to switch to a model closer to CSS4J's model. Done by commit e92d319.

  4. Create a SECURITY.md file with security and privacy considerations. Done by e92d319.

Positive baseline-shift values are not being applied

The baseline-shift attribute is not being applied to renderings when it is positive, see the following image where the expected result is at the left:
textLayout2_cmp
As a result, some tests like SamplesRenderingTest.testTextLayout2() or testTextOnPath() are failing.

Gradle is unable to execute some tests

Gradle was executing the tests just fine until I added tests that use a SecurityManager. Now, when I attempt to run the test suite with Gradle I get the following:

$ gradle test
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :echosvg-test:test

Exception: java.lang.IllegalStateException thrown from the UncaughtExceptionHandler in thread "main"

> Task :echosvg-test:test FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':echosvg-test:test'.
> Process 'Gradle Test Executor 1' finished with non-zero exit value 1
  This problem might be caused by incorrect test process configuration.
  Please refer to the test execution section in the User Manual at https://docs.gradle.org/7.0.1/userguide/java_testing.html#sec:test_execution

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1m 13s
85 actionable tasks: 4 executed, 81 up-to-date

I tried to give full permissions to the .gradle directory in the security policies, but that does not help. And setting java.security.debug=access,failure yields nothing. Perhaps the problem is not related to security permissions at all.

Meanwhile, I successfully execute the test suite with the Eclipse IDE.

FontGlyphChoice test reference seems incorrect

The test SamplesRenderingTest.testFontGlyphChoice() (rendering of samples/tests/spec/fonts/fontGlyphChoice.svg) fails because the vertical text is shifted to the left, compared to the reference. The expected result is at the left:

fontGlyphChoice_cmp

However, it seems that the current rendered position is correct: both Chrome and Firefox reproduce the current rendering, so I'm replacing the old reference with the new one. I open this issue to keep a log that the change was made.

Render MermaidJS diagrams

Background

MermaidJS is a popular (38k ⭐) server-side rendering suite for translating text-based diagram descriptions into scalable vector graphics. No open source rendering library that I tried can render its diagrams (see issue). Kroki is a REST-based service for rendering text-based diagrams, including mermaid, as SVG.

Replicate

  1. Copy attached mermaid.svg.txt as samples/mermaid.svg (see Kroki render).
  2. Add the following method to SamplesRenderingTest.java:
    @Test
    public void testMermaid() throws TranscoderException, IOException {
    	test("samples/mermaid.svg");
    }
  3. Run the unit test method.

Expected

Diagram is rendered.

Actual

Exception while validating the XML: the <div> element is unexpected.

Work around

Comment out XML validation in SAXDocumentFactory.java:

  //parser.setFeature("http://xml.org/sax/features/validation", isValidating);
  //parser.setProperty("http://xml.org/sax/properties/lexical-handler", this);

Continue

  1. Re-run the test.

Expected

The diagram renders.

Actual

<style> is expected to be <style type="text/css">.

Work around

Add the missing type attribute. Consider relaxing the DTD.

Continue

  1. Re-run the test.

Expected

Diagram is rendered.

Actual

Cannot parse the CSS.

Work around

None. At this point the CSS parser cannot handle the semi-colon. Specifically, in CSSParser.java, the handleSemicolon method fires an endOfPropertyDeclaration. Is parsing the double-quotes for "trebuchet ms" causing the parser to get into an invalid state?

Addendum

Using the W3C validator service, the document validates correctly as an SVG 1.1 document.

JDK 18 deprecates finalization for removal

Forthcoming JDK 18 deprecates finalization for removal, see JEP 421 for details.

EchoSVG uses finalization in two places:

and the code should be changed so the finalize() can be removed in favor of a more modern construct.

SVGBrokenLinkProvider produces empty 'broken link' images

SVGBrokenLinkProvider produces empty 'broken link' images of zero size. One consequence is that when a test fails due to a 'broken link', it is difficult for a novice to figure out what is going on.

It is difficult to grasp what the initial intent of SVGBrokenLinkProvider was, but I'll try to come up with something that at least has a size.

Script service provider metadata is at wrong module

The echosvg-script module contains a service provider descriptor for io.sf.carte.echosvg.script.InterpreterFactory under the META-INF/services directory. It lists io.sf.carte.echosvg.bridge.RhinoInterpreterFactory as a provider, which belongs to echosvg-bridge (a different module).

relative move at start of path interpreted incorrectly?

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="420mm"
   height="297mm"
   viewBox="0 0 420 297"
   version="1.1"
   id="svg8"
   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
   sodipodi:docname="For Dan 3.svg">
  <defs
     id="defs2" />
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="2.5011693"
     inkscape:cx="368.99467"
     inkscape:cy="332.15532"
     inkscape:document-units="mm"
     inkscape:current-layer="layer1"
     inkscape:document-rotation="0"
     showgrid="false"
     inkscape:window-width="1816"
     inkscape:window-height="1033"
     inkscape:window-x="86"
     inkscape:window-y="0"
     inkscape:window-maximized="0" />
  <metadata
     id="metadata5">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Calque 1"
     inkscape:groupmode="layer"
     id="layer1">
    <path
       style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       d="m 44.111867,99.648392 c 14.280822,-4.231354 16.925417,2.115678 16.925417,2.115678 0,0 11.953576,17.6659 15.656012,-3.173516 3.702434,-20.839419 8.78006,-11.318872 8.885843,-10.261034 0.105783,1.057839 40.092081,-0.740487 20.310501,-1.692542 -19.78158,-0.952056 -13.434549,-1.692542 -13.434549,-1.692542 0,0 2.433027,-0.634704 4.019786,-1.375191 1.586759,-0.740486 -2.538812,-8.251139 2.961947,-1.163621 5.500766,7.087518 16.079146,0.84627 15.338656,0.317352 C 114.035,82.194055 76.481727,110.43835 92.560874,100.49466 108.64002,90.55098 101.5525,97.744284 101.5525,97.744284"
       id="path31" />
    <path
       sodipodi:type="spiral"
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583"
       id="path37"
       sodipodi:cx="52.786144"
       sodipodi:cy="84.41552"
       sodipodi:expansion="1"
       sodipodi:revolution="3"
       sodipodi:radius="2.3200209"
       sodipodi:argument="-18.03191"
       sodipodi:t0="0"
       d="m 52.786144,84.41552 c 0.07956,0.08486 -0.07514,0.149109 -0.141045,0.132229 -0.178597,-0.04574 -0.200082,-0.277087 -0.123414,-0.41432 0.137141,-0.245477 0.473299,-0.263909 0.687595,-0.114598 0.314488,0.219119 0.329729,0.672209 0.105783,0.96087 -0.298484,0.38474 -0.872081,0.396403 -1.234145,0.09697 -0.45562,-0.376809 -0.46352,-1.072414 -0.08815,-1.50742 0.454622,-0.52685 1.273006,-0.530895 1.780695,-0.07934 0.598294,0.532147 0.598431,1.473759 0.07052,2.05397 -0.609494,0.669876 -1.67462,0.666075 -2.327245,0.06171 -0.741554,-0.686724 -0.733794,-1.875557 -0.05289,-2.60052 0.763873,-0.813298 2.07655,-0.801568 2.873795,-0.04407 0.885093,0.840962 0.869382,2.277584 0.03526,3.14707" />
    <path
       sodipodi:type="spiral"
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583"
       id="path39"
       sodipodi:cx="67.490105"
       sodipodi:cy="81.347786"
       sodipodi:expansion="1"
       sodipodi:revolution="3"
       sodipodi:radius="9.7441807"
       sodipodi:argument="-17.645155"
       sodipodi:t0="0"
       d="m 67.490105,81.347786 c 0.175027,0.45613 -0.528502,0.460971 -0.758118,0.290906 -0.622245,-0.460865 -0.339324,-1.394787 0.176305,-1.807141 0.922339,-0.737604 2.25913,-0.276762 2.856165,0.643516 0.876173,1.350544 0.217675,3.137123 -1.110727,3.905188 -1.770551,1.023711 -4.020215,0.16039 -4.954212,-1.577937 -1.175341,-2.187508 -0.1041,-4.905799 2.045149,-6.003236 2.603028,-1.329144 5.792797,-0.0484 7.052259,2.51236 1.484233,3.017765 -0.0069,6.680681 -2.979571,8.101283 -3.432031,1.640141 -7.569155,-0.06201 -9.150307,-3.446782 -1.796605,-3.845992 0.11692,-8.458044 3.913993,-10.199331 4.259743,-1.953461 9.347234,0.171703 11.248355,4.381204 2.110604,4.673345 -0.226393,10.23665 -4.848415,12.297378" />
  </g>
</svg>

should look like:
image
but I receive from EchoSVG:

m 44.111866 99.64839
c 14.280822 -4.231354 16.925417 2.115678 16.925417 2.115678
c 0.0 0.0 11.953576 17.6659 15.656012 -3.173516
c 3.702434 -20.839418 8.78006 -11.318872 8.885843 -10.261034
c 0.105783 1.057839 40.09208 -0.740487 20.310501 -1.692542
c -19.78158 -0.952056 -13.434549 -1.692542 -13.434549 -1.692542
c 0.0 0.0 2.433027 -0.634704 4.019786 -1.375191
c 1.586759 -0.740486 -2.538812 -8.251139 2.961947 -1.163621
c 5.500766 7.087518 16.079145 0.84627 15.338656 0.317352
C 114.035 82.19405 76.48173 110.43835 92.560875 100.49466
C 108.64002 90.55098 101.5525 97.744286 101.5525 97.744286
...
m 52.786144 84.41552
c 0.07956 0.08486 -0.07514 0.149109 -0.141045 0.132229
c -0.178597 -0.04574 -0.200082 -0.277087 -0.123414 -0.41432
c 0.137141 -0.245477 0.473299 -0.263909 0.687595 -0.114598
c 0.314488 0.219119 0.329729 0.672209 0.105783 0.96087
c -0.298484 0.38474 -0.872081 0.396403 -1.234145 0.09697
c -0.45562 -0.376809 -0.46352 -1.072414 -0.08815 -1.50742
c 0.454622 -0.52685 1.273006 -0.530895 1.780695 -0.07934
c 0.598294 0.532147 0.598431 1.473759 0.07052 2.05397
c -0.609494 0.669876 -1.67462 0.666075 -2.327245 0.06171
c -0.741554 -0.686724 -0.733794 -1.875557 -0.05289 -2.60052
c 0.763873 -0.813298 2.07655 -0.801568 2.873795 -0.04407
c 0.885093 0.840962 0.869382 2.277584 0.03526 3.14707
...
m 67.490105 81.347786
c 0.175027 0.45613 -0.528502 0.460971 -0.758118 0.290906
c -0.622245 -0.460865 -0.339324 -1.394787 0.176305 -1.807141
c 0.922339 -0.737604 2.25913 -0.276762 2.856165 0.643516
c 0.876173 1.350544 0.217675 3.137123 -1.110727 3.905188
c -1.770551 1.023711 -4.020215 0.16039 -4.954212 -1.577937
c -1.175341 -2.187508 -0.1041 -4.905799 2.045149 -6.003236
c 2.603028 -1.329144 5.792797 -0.0484 7.052259 2.51236
c 1.484233 3.017765 -0.0069 6.680681 -2.979571 8.101283
c -3.432031 1.640141 -7.569155 -0.06201 -9.150307 -3.446782
c -1.796605 -3.845992 0.11692 -8.458044 3.913993 -10.199331
c 4.259743 -1.953461 9.347234 0.171703 11.248355 4.381204
c 2.110604 4.673345 -0.226393 10.23665 -4.848415 12.297378

While this is verbatim the file contents, note https://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands says

If a relative moveto (m) appears as the first element of the path, then it is treated as a pair of absolute coordinates. In this case, subsequent pairs of coordinates are treated as relative even though the initial moveto is interpreted as an absolute moveto.

So I'm wondering if it's on me or on EchoSVG to catch this edge case.

Generator: conversions to SVG fonts are incomplete

Conversions to SVG fonts in svggen are incomplete and do not include all font metrics. As a result, text decorations are rendered inaccurately, like in the following comparison image where the expected render is at the left:

ContextFontDecoration_cmp

Support at least strikethrough-* and underline-* attributes.

Modularize the xml-apis-ext dependency

This project depends on the org.w3c.dom.svg package (and also a couple of org.w3c.dom.smil classes), as provided by xml-apis-ext-1.3.04.jar which also contains the SAC classes.

For modularization purposes, it is more convenient to depend on two separate packages, svgom-api and smil-api with their own module names. The sources of the xml-apis-ext-1.3.04.jar package are archived at:

http://archive.apache.org/dist/xml/commons/

although there are more recent versions of the package, 1.3.05 and 1.4.01 available from:

http://xerces.apache.org/mirrors.cgi#source

In principle this project is not using those sources, instead I downloaded the original Java bindings from W3C, although it seems that the classes in xml-apis-ext-1.3.04.jar are a mixture of SVG versions. I found the following source downloads:

The following URL about SMIL 3.0 may also be relevant as a reference for the latest version:

https://www.w3.org/TR/SMIL3/smil-timing.html#DOM-SupportedMethods

I had to modify several API method signatures so they matched the implementations in EchoSVG. Although it would have been easy to upgrade EchoSVG instead, that would have created problems in cases where both xml-apis-ext-1.3.04.jar and svgom-api / smil-api are found in the classpath or modulepath (which is conceivable if somebody is using EchoSVG and Apache FOP in the same project).

EchoSVG depends on Apache FOP which in recent versions depends on Batik

The echosvg-rasterizer and related modules introduce a dependency on Apache FOP, which in recent versions depends on Batik. The original Batik code worked around the issue by including a 2.3-SNAPSHOT binary of FOP in the lib directory, but that's really ugly.

This issue tracks the removal of the rasterizer module and related files, as well as dropping the support for PDF images in the svgrasterizer app.

Many tests use Microsoft fonts and fail on non-Windows systems

Many Generator and Transcoder tests use Microsoft fonts like Arial and fail on non-Windows systems. The solution is to replace those fonts with free fonts that can be shipped with EchoSVG.

Most WMF tests use MS fonts and perhaps the sample WMF files could be modified to alter the fonts, but I haven't find a suitable editor. Perhaps it is unavoidable to exclude those tests from being run during build, at least on non-Windows systems.

svg1.2: flow elements use non-existent attribute value text-align="middle"

In SVG, attributes based on CSS properties are supposed to borrow their values from that property's valid values. However, the (deprecated) SVG 1.2 flow elements have a text-align attribute based on the CSS text-alignproperty, for which they use the non-CSS attribute value text-align="middle" while at the same time they do not accept the CSS-valid center.

The SVG 1.2 flow elements are no longer part of SVG and only Batik and Inkscape support them, however for consistency they should support text-align="center" in addition to text-align="middle" (preserved for Batik compatibility).

Prevent rasterization of a vector that has opacity

HI there, thanks for this awesome work.
This is a question.

Currently, we tried using echosvg to load SVG through the bridge and then paint it unto a Graphics2D context. However, if an item has opacity less than 1, the system automatically rasterizes it into a very low quality image. It happens for Text, Paths, Rect any svg element with an opacity gets painted as a raster image.

I assume it's treating the opacity as a filter effect, whereas it shouldn't be.

Is there a way to prevent this?

Transcoder disallows valid script media types

The transcoder keeps a list of allowed script media types based on the KEY_ALLOWED_SCRIPT_TYPES hint, which by default is:

"text/ecmascript, application/ecmascript, text/javascript, application/javascript, application/java-archive"

Note the whitespaces after the commas. Given to how it is parsed, the mime types are stored with a whitespace at the beginning, resulting in only the first item of the list actually matching the contains mime type check:

As a result, valid script media types are not allowed.

Fix the testing infrastructure

This project has only 26 JUnit tests (of which two fail), in the util package. Most tests in the original Batik project were really integration tests, and they depended on files containing ASF-related logos (which are unusable in this project for copyright and licensing reasons).

I have removed those conflicting resources but a cleanup of the old tests should be done at some point; this issue is opened to raise attention about this fact. If you have already worked with those tests or are somehow familiarized with them, advice would be welcome.

Remove Tcl scripting support

The script module supported Tcl scripting in the past, but it is excluded from the build and unsupported. Remove it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.