JSVG

repository·master·Indexed 42 days ago

https://github.com/weisj/jsvg

A lightweight, fast Java implementation of an SVG user agent using AWT graphics. Designed for memory efficiency, JSVG is used in IDEs like JetBrains IDEA and Apache NetBeans. It supports rendering SVG documents to Graphics2D, provides an experimental JavaFX renderer, and includes features for DOM manipulation via DomProcessor and basic SVG animation support.

Tokens
4K
Snippets
9
Records
13
Agent score
4%

What's inside JSVG

  1. Manipulate SVG DOM during loading

    master

    You can modify SVG elements (e.g., changing colors or attributes) before they are fully parsed by using a DomProcessor via the LoaderContext.

    To implement this:

    1. Implement the DomProcessor interface and override the process(DomElement root) method.
    2. Inside process, use element.attribute(name, default) to read attributes and element.setAttribute(name, value) to modify them.
    3. To inject custom dynamic values (like a color that changes at runtime), use document.registerNamedElement(id, paint) and then set the element's attribute to that id.
    4. Pass the processor to the loader using LoaderContext.builder().preProcessor(processor).build().
    // 1. Define a processor
    CustomColorsProcessor processor = new CustomColorsProcessor(List.of("myRect"));
    
    // 2. Load with the processor
    SVGLoader loader = new SVGLoader();
    SVGDocument document = loader.load(svgUrl, LoaderContext.builder().preProcessor(processor).build());
    
    // 3. Update the dynamic value later
    DynamicAWTSvgPaint dynamicColor = processor.customColorForId("myRect");
    dynamicColor.setColor(Color.RED);
    // Then call repaint() on your component
  2. How animations work in JSVG

    master

    Animation support is currently experimental and limited to basic timing and interpolation. Most animatable properties are not yet supported.

    Animations are not automatic; they must be driven by user code. You control animations on a per-frame basis by supplying an AnimationState to SVGDocument#renderWithPlatform.

  3. Render SVG in JavaFX (Experimental)

    master

    The JavaFX renderer is experimental and requires the com.github.weisj:jsvg-javafx dependency (for JavaFX 17+).

    Use the FXSVGCanvas control, which is a standard JavaFX Control. You can choose between two rendering backends:

    • RenderBackend.JavaFX: Faster, hardware-accelerated, but may have issues with advanced features like filters and masks.
    • RenderBackend.AWT: Uses the JSVG AWT pipeline; slower but more accurate.

    FXSVGCanvas supports JavaFX properties for easy binding and provides methods to control animation playback.

    // Required dependency: com.github.weisj:jsvg-javafx:2.0.1
    
    SVGLoader loader = new SVGLoader();
    SVGDocument document = loader.load(getClass().getResource("path/to/image.svg"));
    
    FXSVGCanvas canvas = new FXSVGCanvas();
    canvas.setRenderBackend(FXSVGCanvas.RenderBackend.JavaFX);
    canvas.setDocument(document);
    
    // Control playback
    canvas.pauseAnimation();
    canvas.playAnimation();
    canvas.restartAnimation();
    
    // Configuration
    canvas.setShowTransparentPattern(true);
    canvas.setViewBox(new ViewBox(0, 0, 200, 200));
  4. Install JSVG via Maven/Gradle

    master

    JSVG is available on Maven Central. You can use the stable version or nightly snapshot builds.

    Stable version:

    dependencies {
        implementation("com.github.weisj:jsvg:2.2.0")
    }

    Nightly snapshot builds: To use snapshots, add the Sonatype repository and use the latest.integration version.

    repositories {
        maven {
            url = uri("https://central.sonatype.com/repository/maven-snapshots")
        }
    }
    
    // Optional: prevent caching of changing modules
    configurations.all {
        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
    }
    
    dependencies {
        implementation("com.github.weisj:jsvg:latest.integration")
    }
  5. Render SVG in Swing

    master

    To render an SVG in a Swing application, use SVGLoader to load an SVGDocument and then call document.render() within a component's paintComponent method. It is recommended to set RenderingHints.KEY_ANTIALIASING and RenderingHints.KEY_STROKE_CONTROL for better visual quality.

    Key steps:

    1. Instantiate SVGLoader.
    2. Load the document using loader.load(URL).
    3. Create a custom JPanel (or JComponent) that overrides paintComponent.
    4. Inside paintComponent, call document.render(this, graphics2D, viewBox) where viewBox defines the visible area.
    SVGLoader loader = new SVGLoader();
    URL svgUrl = RenderExample.class.getResource("path/to/image.svg");
    SVGDocument document = loader.load(Objects.requireNonNull(svgUrl, "SVG file not found"));
    
    // Inside a JPanel's paintComponent(Graphics g):
    Graphics2D g2d = (Graphics2D) g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
    document.render(this, g2d, new ViewBox(0, 0, getWidth(), getHeight()));
  6. Animate SVG in Swing

    master

    For Swing applications, use the AnimationPlayer helper class to drive SVG animations.

    1. Create an AnimationPlayer passing a repaint callback: new AnimationPlayer(e -> repaint()).
    2. Set the animation state: player.setAnimation(document.animation()).
    3. In paintComponent, use document.renderWithPlatform instead of document.render. You must provide an AwtComponentPlatformSupport instance and the player.animationState().
    public class AnimationPanel extends JComponent {
        private final @NotNull SVGDocument document;
        private final @NotNull AnimationPlayer player;
    
        public AnimationPanel(@NotNull SVGDocument document) {
            this.document = document;
            this.player = new AnimationPlayer(e -> repaint());
            player.setAnimation(document.animation());
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            // ... set rendering hints ...
            document.renderWithPlatform(
                new AwtComponentPlatformSupport(this),
                Output.createForGraphics((Graphics2D) g),
                new ViewBox(0, 0, getWidth(), getHeight()),
                player.animationState());
        }
    
        public void startAnimation() { player.start(); }
        public void stopAnimation() { player.stop(); }
    }
  7. Use the experimental JavaFX renderer

    master

    JSVG provides an optional module for rendering SVG documents within JavaFX applications. This requires JavaFX 17 or later.

    Dependency setup:

    dependencies {
        implementation("com.github.weisj:jsvg:2.0.1")
        implementation("com.github.weisj:jsvg-javafx:2.0.1")
    }
  8. Use a custom XML parser

    master

    If you need to control the XML parsing process (e.g., using a specific XMLInputFactory), implement the XMLInput interface and pass it to SVGLoader.load().

    public class CustomXMLInput implements XMLInput {
        private final @NotNull XMLInputFactory factory;
        private final @NotNull InputStream inputStream;
    
        @Override
        public @NotNull XMLEventReader createReader() throws XMLStreamException {
            return factory.createXMLEventReader(inputStream);
        }
    }
    
    // Usage
    XMLInputFactory factory = XMLInputFactory.newFactory();
    SVGLoader loader = new SVGLoader();
    try (InputStream inputStream = inputUrl.openStream()) {
        SVGDocument document = loader.load(
                new CustomXMLInput(factory, inputStream),
                inputUrl,
                LoaderContext.createDefault()
        );
    }
  9. Render an SVG document to Graphics2D

    master

    An SVGDocument can be rendered to any Graphics2D object (such as a BufferedImage or a Swing component's graphics context).

    Rendering to a BufferedImage:

    FloatSize size = svgDocument.size();
    BufferedImage image = new BufferedImage((int) size.width,(int) size.height);
    Graphics2D g = image.createGraphics();
    svgDocument.render(null,g);
    g.dispose();

    Rendering to a Swing component:

    class MyComponent extends JComponent {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            svgDocument.render(this, (Graphics2D) g, new ViewBox(0, 0, getWidth(), getHeight()));
        }
    }
  10. Load an SVG document using SVGLoader

    master

    Use the SVGLoader class to load an SVG file from a URL into an SVGDocument.

    Important: SVGLoader is not guaranteed to be thread-safe and should not be shared across multiple threads.

    To provide more configuration, you can pass a LoaderContext built via LoaderContext.builder() to the load method.

    SVGLoader loader = new SVGLoader();
    URL svgUrl = MyClass.class.getResource("mySvgFile.svg");
    SVGDocument svgDocument = loader.load(svgUrl);
  11. Understand JSVG element support status

    master

    JSVG provides varying levels of support for SVG elements. When choosing elements for your SVG files, refer to the following status indicators to understand expected behavior:

    • ✅ Supported: The element is fully implemented (though not all attributes may be supported).
    • ✅* Supported (Limited): The element is supported, but certain features (like querying content for <desc>) may not work.
    • ☑️ Partially Implemented: The element is implemented but may lack basic features.
    • ❌ Not Supported: The element is currently not implemented.
    • ⚠️ Deprecated: The element is deprecated in the SVG specification and has low implementation priority.
    • 🧪 Experimental: Part of the SVG 2.* spec; behavior may not be fully predictable.
    | Element       | Status              |
    |---------------|---------------------|
    | a             | :white_check_mark:  |
    | circle        | :white_check_mark:  |
    | clipPath      | :white_check_mark:  |
    | defs          | :white_check_mark:  |
    | ellipse       | :white_check_mark:  |
    | foreignObject | :x:                 |
    | g             | :white_check_mark:  |
    | image         | :white_check_mark:  |
    | line          | :white_check_mark:  |
    | marker        | :white_check_mark:  |
    | mask          | :white_check_mark:  |
    | path          | :white_check_mark:  |
    | polygon       | :white_check_mark:  |
    | polyline      | :white_check_mark:  |
    | rect          | :white_check_mark:  |
    | svg           | :white_check_mark:  |
    | symbol        | :white_check_mark:  |
    | use           | :white_check_mark:  |
    | view          | :white_check_mark:* |
  12. Configure JSVG logging adapters

    master

    By default, JSVG uses java.util.logging (JUL). You can route logs to other frameworks by adding an adapter module to your classpath. Only one adapter should be present at a time.

    SLF4J Adapter: Routes logs through any SLF4J 2.x compatible backend (e.g., Logback, Log4j 2).

    dependencies {
        implementation("com.github.weisj:jsvg:2.0.1")
        implementation("com.github.weisj:jsvg-slf4j:2.0.1")
        runtimeOnly("ch.qos.logback:logback-classic:1.5.6")
    }

    System.Logger Adapter: Routes logs through the Java 9+ System.Logger API.

    dependencies {
        implementation("com.github.weisj:jsvg:2.0.1")
        implementation("com.github.weisj:jsvg-systemlogger:2.0.1")
    }