Skip to main content

SVG and Java UIs part 6: transcoding SVG to pure Java2D code

Posted by kirillcool on October 20, 2006 at 11:01 PM PDT

This entry is the sixth part in the ongoing series about using SVG-based icons in Swing-based applications.

  • The first part outlined the need for using scalable icons in next-generation Java UIs and proposed using SVG images as a possible format, showing the ribbon component which employs heavy icon rescaling.
  • The second entry introduced an SVG file previewer based on the breadcrumb bar component, a ribbon button from the ribbon component and SVG Salamander renderer
  • The third part showed how the Apache Batik SVG renderer can initialize and scale SVG-based application in an asynchronous and non-blocking fashion.
  • The fourth entry showed what happens beneath the hood and described some of the problems found with Batik integration.
  • The fifth entry described the feedback from Batik developer and improvements made to the ribbon and SVG file previewer.

Over the past few months i have seen quite a few questions about converting SVG files to pure Java2D painting code. Since no such converter (to the best of my knowledge) exists (at least in the open-source world), this has been implemented in the latest drop of Flamingo project (release candidate of version 1.1 code-named Briana is scheduled for October 30). How do you run it? Very simple - click on the WebStart link below, grant all permissions (it needs read access to read the SVG files and write access to create the Java2D-based classes), use the breadcrumb bar to navigate to a folder that contains SVG files, wait for them to appear (they'll be loaded asynchronously) and just start clicking on the icons.

Clicking on an icon will create a Java class under the same folder with the same name (spaces and hyphens are replaced by the underscores). The class will have a single static paint method that gets a Graphics2D object and paints the icon.
Note that before you call this method, you can set any AffineTransform on the Graphics2D that you pass to the method in order to scale, shear or rotate the painting.

Here are few known limitations of this tool:

  • The generated code requires Mustang to run, since it uses the LinearGradientPaint and RadialGradientPaint mentioned in Chris's blog.
  • Since Mustang's versions of these classes are a little different from Batik's implementation, some of the SVGs fail to transcode (when the stop fractions are not strictly increasing).
  • The TextNode is not supported. The support for this would involve writing and debugging tons of text-related code. I don't know about general usage, but Tango iconset uses this only on 2 out of 203 icons. If you want to chip in and provide the support - you're welcome.

Here is how you use the generated code:

public class Test extends JFrame {

  public static class TestPanel extends JPanel {

    @Override

    public void paintComponent(Graphics g) {

      Graphics2D g2 = (Graphics2Dg.create();

      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,

          RenderingHints.VALUE_ANTIALIAS_ON);

      g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,

          RenderingHints.VALUE_INTERPOLATION_BILINEAR);

      g2.translate(1010);

      address_book_new.paint(g2);

      g2.translate(500);

      g2.transform(AffineTransform.getScaleInstance(2.02.0));

      internet_web_browser.paint(g2);

      g2.dispose();

    }

  }



  public Test() {

    super("SVG samples");

    this.setLayout(new BorderLayout());

    this.add(new TestPanel(), BorderLayout.CENTER);



    this.setSize(180140);

    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    this.setLocationRelativeTo(null);

  }



  public static void main(String[] args) {

    SwingUtilities.invokeLater(new Runnable() {

      public void run() {

        new Test().setVisible(true);

      }

    });

  }

}

The address_book_new and internet_web_browser are the transcoded classes. Note that before the second icon is painted, i apply the scaling transformation (to illustrate how it is done). The result is:

Note how the second icon is scaled (relative to the first one).

The size of the generated code is comparable to the size of the original SVG file. The first icon in the above example takes 20KB in SVG format and 22KB in Java2D code. The second icon is 50KB in SVG format and 55KB in Java2D code. The generated code itself contains a few comments to help in mapping the Java2D sections to the corresponding SVG sections, but in general you don't need to look at it at all (as you wouldn't look at the SVG contents).

The implementation is quite straightforward. It uses the Apache Batik library (that's why the WebStart is so big) to load the SVG file and create a renderer tree (called GVT renderer tree). The nodes in this tree can be mapped directly to Java2D code. The only tricky part is in chaining and restoring transformations on nested nodes. In addition, some Batik classes do not provide getters for the relevant properties - i had to use reflection to obtain the field values.

As already mentioned, this tool has been successfully tested on the Tango iconset. Apart from two known issues (TextNode support and non-strictly increasing fractions), all the icons have been converted and displayed properly. If you're trying it on other SVG files and see UnsupportedOperationException, feel free to send me the relevant SVG file. Happy converting.

Related Topics >>