31

I am new to JavaFX 2.2 and as of now I couldn't find a way to display SVG Images in my JavaFX 2.2 application. I took a look at Batik, but it didn't do the trick for me as it converts to BufferedImages and not to javafx.ImageView.

Is there any way to display an SVG image in an JavaFX application? Or can you at least export an SVG image from JavaFX? Does the function Node.snapshot() help there in any way?

SpaceCore186
  • 586
  • 1
  • 8
  • 20
user1582432
  • 645
  • 2
  • 7
  • 9

3 Answers3

52

Is there any way to display an SVG image in an JavaFX application?

Here are some options:

  1. Create a WebView and load the svg image into the WebView's WebEngine.
  2. The e(fx)clipse project includes an svg to fxml converter (link now dead :().
  3. This NetBeans plugin also converts svg to fxml (link now dead :().
  4. You can use an awt based library like Batik or svgsalamander and convert the resultant BufferedImage to a JavaFX Image.
  5. JavaFX 2.2 natively includes support for some minimal SVGPath strings (not the full SVG spec).
  6. You could write a convertor which renders the svg using JavaFX Canvas GraphicsContext commands.
  7. FranzXaver provides an SVGLoader, whose usage is demonstrated in the answer to: Load SVG file in a Button on JavaFX

Or can you at least export an SVG image from javafx?

This feature sounds like a JavaFX SceneGraph to svg converter. While it is theoretically possible, I am not aware that anybody has created such a tool yet.

Does the function Node.snapshot() help there in any way?

Node.snapshot() will not help with exporting an svg image from a JavaFX scene graph as svg is a vector based format and a node snapshot is a bit mapped format.

I took a look at Batik, but it didn't do the trick for me as it converts to BufferedImages and not to javafx.ImageView.

You can easily convert between awt BufferedImages and javafx images using SwingFXUtils.

In terms of performance - which way would you recommend?

For complex svg's, rather than rendering from fxml, you will probably get better performance rendering to a bitmapped image or canvas graphics context. This is because your scene graph will end up a lot simpler, with far less nodes - which means that the javafx runtime will have a lot less work to do. For simple svgs (< 1000 nodes generated), it probably won't matter too much.

Is converting an SVG to an FXML about creating instances of javafx.scene.shape.SVGPath?

Partially. The full SVG specification covers much more ground than the basic shapes. In addition gradients, effects, animations, etc can be performed by an SVG. So if the source SVG image includes those additional items, then they also need to be converted to FXML (as best they can be, there will be some aspects of SVG such as scripting which would be hard to translate to FXML).

My guess is that the size and complexity of the SVG specification is one of the reasons why no direct support for the full SVG image system is currently included in the JavaFX core API and, instead, only limited similar functionality such as SVGPath is offered. Implementing SVG natively in the JavaFX core API would have been high cost for relatively little reward when compared to other desirable items.

If that is the case, wouldn't #2 (and #3) and #5 be just the same?

No. The NetBeans and Eclipse tools referenced in #2 and #3 were more functional than just the SVGPath facilities mentioned in #5, as those tools converted additional aspects of the the SVG specification to FXML equivalents (e.g. gradients, effects and transforms).

Community
  • 1
  • 1
jewelsea
  • 130,119
  • 12
  • 333
  • 365
  • 1
    Thanks for youre detailed answer! In terms of performance - which way would you recommend? Converting them all to fxml? – user1582432 Sep 17 '12 at 21:07
  • Thanks! I'd like to vote your answer up, but I don't have the reputattion to do that. Sorry! – user1582432 Sep 19 '12 at 10:13
  • @jewelsea Is converting an SVG to an FXML about creating instances of `javafx.scene.shape.SVGPath`? If that is the case, wouldn't #2 (and #3) and #5 be just the same? Or are you mentioning a different `SVGPath` in #5? (Edit: Apparently it's the same as I see the link. So I 'd rather ask if the ones in #2 and #3 are not about `javafx.scene.shape.SVGPath`.) I'm looking at this example: https://raw.githubusercontent.com/usander/e-fx-clipse/master/at.bestsolution.efxclipse.formats.svg/samples/application-exit.fxml – konsolebox Oct 12 '16 at 06:23
28

I used the transcoder class above to create a little project on github that adds SVG support to JavaFX (JavaFX 8 though): javafxsvg

After calling

SvgImageLoaderFactory.install();

you can use SVG images anywhere in your Java code or CSS just like any other supported image type.

Jan Gassen
  • 2,938
  • 1
  • 21
  • 39
12

JavaFX + Batik + Custom Transcoder

Since I've nowhere found a complete example of using Batik in conjunction with JavaFX, I am providing a complete solution below.

Note that I removed exception handling (e.g. for the TranscoderException) for brevity.
You can access the full code here: https://gist.github.com/ComFreek/b0684ac324c815232556

  1. In order to convert a SVG file with Batik to a BufferedImage, you have to implement a custom transcoder:

    /**
     * Many thanks to bb-generation for sharing this code!
     * @author bb-generation
     * @link http://bbgen.net/blog/2011/06/java-svg-to-bufferedimage/
     * @link In case the link above is still down: https://web.archive.org/web/20131215231214/http://bbgen.net/blog/2011/06/java-svg-to-bufferedimage/
     */
    
    public class BufferedImageTranscoder extends ImageTranscoder {
    
        private BufferedImage img = null;
    
        @Override
        public BufferedImage createImage(int width, int height) {
            BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            return bi;
        }
    
        @Override
        public void writeImage(BufferedImage img, TranscoderOutput to) throws TranscoderException {
            this.img = img;
        }
    
        public BufferedImage getBufferedImage() {
            return img;
        }
    }
    
  2. Use the transcoder to generate a BufferedImage object:

    BufferedImageTranscoder trans = new BufferedImageTranscoder();
    
    // file may be an InputStream.
    // Consult Batik's documentation for more possibilities!
    TranscoderInput transIn = new TranscoderInput(file);
    
    trans.transcode(transIn, null);
    
  3. Convert the BufferedImage object to an Image object (from JavaFX):

    // Use WritableImage if you want to further modify the image (by using a PixelWriter)
    Image img = SwingFXUtils.toFXImage(trans.getBufferedImage(), null);
    
  4. Finally assign the previously received object to your ImageView:

    imgView.setImage(img);
    
ComFreek
  • 27,416
  • 16
  • 97
  • 150
  • 1
    Batik has been broken on Java9+ for a few years now and the maintainers don't seem too interested in fixing it so if you need to run on a JRE released in the last 5 years it is going to be a waste of time pursuing this approach. See https://issues.apache.org/jira/browse/BATIK-1260 for details. Combined with the kitchen sink non-optional dependencies which shade common api jars - eg jython - I wouldn't recommend using Batik at all in production code. – Dave Mar 12 '21 at 07:42