JavaFX is packaged with several default ways to include vector graphics images. SVGs can be loaded into a JavaFX application by setting a Node’s shape using CSS, or by creating an SVGPath.

What we’ll have by the end of the tutorial

We’re going to load SVG images and paths into JavaFX in three different ways:

In each of these cases, the SVG we load in is going to be a node, just like any other UI element, which means you you’ll probably end up adding it to a layout just like anything else. If you haven’t checked out my article on layouts, I would definitely recommend it.

In it, I tried to create a resource that explains every layout and how it sizes and positions it’s children, which is especially important for handling images when you load them into a program. And, honestly, the time I’ve saved just referring back to it myself while I’m designing programs made it time well-spent.

Loading an SVG into JavaFX

There are pros and cons to each way of loading the data into your program, but it more or less boils down to this:

Are you willing to maintain an additional dependency for the convenience of not having to soft through your SVG files?

Convert to Buffered image
Load with CSS
Load with SVGPath
  • No need to manually scrape data from SVG file
  • Included with JavaFX
  • Maintains MVC model well
  • Included with JavaFX

  • Extra dependency you need to manage

  • Manual digging in every SVG for the path you need
  • Manual digging in every SVG for the path you need

We’ll start with the conversion method, because this doesn’t require any knowledge of the SVG format. We’ll then take a look through the CSS and SVGPath approaches. And don’t worry we’ll take you through how to get the data out of the SVG file too.

Converting an SVG file to a BufferedImage

Before we do this it’s important to remember that because of a bug in one of the Batik dependencies, this won’t work on a module-path project, and you’ll therefore need to set your JavaFX project to run from the classpath as in this tutorial here.

If you need support for all the functionality of an SVG file, the final and most comprehensive way to incorporate an image into your program is to use an image transcoder. One of the main benefits of Java is its comprehensive set of well-maintained libraries and the one we’ll use today is the Batik Transcoder from Apache, which you can include as a dependency if you’re managing your project with Maven.

<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-transcoder</artifactId>
    <version>1.13</version>
</dependency>

If your project doesn’t have Maven support, you can download the Jar from Maven Central here.

Either way, we won’t use Maven for importing JavaFX, because of the Batik bug, so it’s entirely up to you. Next, we need to extend the Batik’s ImageTranscoder class – it’s abstract, so we just need to instantiate it and store the image we create.

public class BufferedImageTranscoder extends ImageTranscoder {

    private BufferedImage img = null;

    @Override
    public BufferedImage createImage(int width, int height) {
        return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    }

    @Override
    public void writeImage(BufferedImage img, TranscoderOutput to) throws TranscoderException {
        this.img = img;
    }

    public BufferedImage getBufferedImage() {
        return img;
    }
}

Next, we need to add a dependency for JavaFX’s Swing utilities, because we’ll have to convert the SVG into a BufferedImage using AWT.

import javafx.embed.swing.SwingFXUtils;

That’s important, because the Batik transcoder only translates our SVG file into a java.awt.image.BufferedImage and not a JavaFX BufferedImage. To use it, we need to use the SwingFXUtils static utility class to get the right sort of image for JavaFX.

The following code is wrapped in two try-catch blocks. The first, a try-with-resources, opens the SVG file as an InputStream. The second converts the image into a BufferedImage and saves it to an ImageView.

ImageView githubImage = new ImageView();

BufferedImageTranscoder transcoder = new BufferedImageTranscoder();
try (InputStream file = getClass().getResourceAsStream("/img/github.svg")) {
    TranscoderInput transIn = new TranscoderInput(file);
    try {
        transcoder.transcode(transIn, null);
        Image img = SwingFXUtils.toFXImage(trans.getBufferedImage(), null);
        githubImage.setImage(img);
    } catch (TranscoderException ex) {
        ex.printStackTrace();
    }
}
catch (IOException io) {
    io.printStackTrace();
}

Finally, we set the image size, and add it to our scene. This time, the transcoder takes the color from the SVG metadata, so we aren’t free to set it programatically.

githubImage.setFitHeight(25);
githubImage.setPreserveRatio(true);
contentContainer.getChildren().add(githubImage);

Result!

A simple CSS interface in JavaFX. The Github icon has been added as an ImageView using a transcoder

Loading an SVG with CSS

To start, we’ll take a quick look at the SVG format, because to use some of the options below, you’ll need to take the important information out of the SVG file, so it’s important to know where to get it.

Click the drop-down to learn more about the SVG file. Or, if you’re comfortable getting path data from an SVG, we can dive right in.

The simplest way to add an SVG path into a program is to define it in the CSS layer, by using the -fx-shape attribute. Any node can have an -fx-shape attribute. If you’re applying it to a button, the button will take that shape. If you’re applying it to a Region, it redefines the shape of that whole region!

As a starting point, we’ll use the simple UI we designed in Choosing a UI for Style. The simple UI consists at the moment of just a logo and an exit button.

To add a button with CSS, we’ll add the button directly into the FXML document, MainView.fxml:

 <Button styleClass="git-button"/>

…and we’ll add the style class we just referenced into application.css:

.git-button {
    -fill: -blue;
    -fx-shape: "m12 .5c-6.63 0-12 5.28-12 11.792 0 5.211 3.438 9.63 8.205 11.188.6.111.82-.254.82-.567 0-.28-.01-1.022-.015-2.005-3.338.711-4.042-1.582-4.042-1.582-.546-1.361-1.335-1.725-1.335-1.725-1.087-.731.084-.716.084-.716 1.205.082 1.838 1.215 1.838 1.215 1.07 1.803 2.809 1.282 3.495.981.108-.763.417-1.282.76-1.577-2.665-.295-5.466-1.309-5.466-5.827 0-1.287.465-2.339 1.235-3.164-.135-.298-.54-1.497.105-3.121 0 0 1.005-.316 3.3 1.209.96-.262 1.98-.392 3-.398 1.02.006 2.04.136 3 .398 2.28-1.525 3.285-1.209 3.285-1.209.645 1.624.24 2.823.12 3.121.765.825 1.23 1.877 1.23 3.164 0 4.53-2.805 5.527-5.475 5.817.42.354.81 1.077.81 2.182 0 1.578-.015 2.846-.015 3.229 0 .309.21.678.825.56 4.801-1.548 8.236-5.97 8.236-11.173 0-6.512-5.373-11.792-12-11.792z";
}

It could not be easier!

A simple CSS interface in JavaFX. Both the exit button and Github button are SVG paths set using CSS

If you’re interested, you can make the button launch the default internet browser with just a few lines of code. Once you’ve added onAction="handleGitButtonClicked" to the FXML button defintion, it’s as simple as adding this to your Controller:

@FXML
private void handleGitButtonClicked(ActionEvent event) {
    new Application() {
        @Override
        public void start(Stage stage) {
        }
    }.getHostServices().showDocument("https://github.com/edencoding/javafx-ui/");
    event.consume();
}

Using an SVGPath as an image

If you need to load an SVG in programmatically, JavaFX provides functionality to handle the <path> part of an SVG using its niftily-named SVGPath class. That additional functionality of regular shapes and other extensible functionality is not supported, but we can still load the SVG path into our program with a little code.

    SVGPath githubIcon = new SVGPath();
    githubIcon.setContent("m12 .5c-6.63 0-12 5.28-12 11.792 0 5.211 3.438 9.63 8.205 11.188.6.111.82-.254.82-.567 0-.28-.01-1.022-.015-2.005-3.338.711-4.042-1.582-4.042-1.582-.546-1.361-1.335-1.725-1.335-1.725-1.087-.731.084-.716.084-.716 1.205.082 1.838 1.215 1.838 1.215 1.07 1.803 2.809 1.282 3.495.981.108-.763.417-1.282.76-1.577-2.665-.295-5.466-1.309-5.466-5.827 0-1.287.465-2.339 1.235-3.164-.135-.298-.54-1.497.105-3.121 0 0 1.005-.316 3.3 1.209.96-.262 1.98-.392 3-.398 1.02.006 2.04.136 3 .398 2.28-1.525 3.285-1.209 3.285-1.209.645 1.624.24 2.823.12 3.121.765.825 1.23 1.877 1.23 3.164 0 4.53-2.805 5.527-5.475 5.817.42.354.81 1.077.81 2.182 0 1.578-.015 2.846-.015 3.229 0 .309.21.678.825.56 4.801-1.548 8.236-5.97 8.236-11.173 0-6.512-5.373-11.792-12-11.792z");
    githubIcon.setFill(Color.web("#81c483"));
    contentContainer.getChildren().add(githubIcon);
A simple CSS interface in JavaFX. The Github icon has been added as an SVGPath to the Scene

In this case we’ve added it manually into the scene as an image, and manually set the color in the third line of code. You can also scale the images with setScaleX() and setScaleY() methods of the SVGPath class for convenience.

Conclusions

JavaFX bundles two ways to load SVG images into your application. You can set the shape of any node – usually a button – by setting the -fx-shape attribute via CSS.

You can also load an SVG’s path using JavaFX’s SVGPath class, programmatically setting extra values such as color and size in your program rather than in CSS.

Finally, if you need to load an SVG in with additional support for functionality other than the <path> tag, you can use a library transcoder to convert it into a BufferedImage and load that into your application. A word of caution, though, as once transcoded, you’ll lose the ability to scale without loss of quality.