As I mentioned in the
first post on SVGRoundTrip, my initial interest in SVG images came from wanting scalable images that I could use within the UI of my applications.
When I originally started work on
3DAssembler I used PNG versions of the icons from the Tango and Gnome Desktop icon sets as I didn't know how to deal with the original SVG versions. During development I noticed that the
Flamingo component suite (which I was using for the ribbon) also contained support for converting SVG files into Java 2D based classes that would draw the icons at the correct size we required. For version 2 of 3DAssembler I changed from all PNG images to all SVG and the different sized icons look much better. Whilst most of the SVG files I wanted to used converted without any problems some didn't and so I set about figuring out why.
The first problem that I noticed was that the clipping wasn't correct. This usually showed up where I had an image that went outside the bounds of the SVG page size. When exporting to a PNG from
Inkscape everything outside the page boundaries was removed and I expected the same behavior when the images were converted to Java code. It was definitely a bug as the image being drawn in my app was actually a lot wider than it should have been and because it wasn't being clipped was actually spilling out over other components. I initially fixed the issue by simply setting a clip on the
Graphics2D
object before passing it to the generated Java code. Whilst this fixed the overflow issue it didn't fix clipping issues within the bounds of the image. Each element in an SVG file can specify a clip and this was being ignored when the code within Flamingo was converting the SVG file to Java code. After a little trial and error I managed to add support for clipping any SVG element that required it.
The second problem related to scaling. Whilst SVG images can be scaled at will without a loss in quality they are originally drawn at a specific size. Knowing how to scale all the SVG elements requires knowing this original size. Unfortunately, the code in the Flamingo library didn't extract the page size from the original SVG file rather it used the bounds of the image (i.e. the rectangle that fully encloses all the elements) for scaling. Usually (at least in the files I was using) the bounding rectangle only differed from the page size by a pixel or so and therefore the images drawn using the wrong information were almost identical to those using the correct page size. The problem really only appeared when I fixed the clipping issue. With correct clipping I kept seeing the right/bottom column/row of the image being clipped. It turns out that the correct page size information is available (although it's well buried) in the information provided by
Batik when it parses the SVG file so it was fairly easy to fix this problem as well.
The final problem that I noticed was that the code wasn't transcoding alpha values properly either. Each SVG element can have an alpha value associated with it to set the transparency. The problem arose when elements were nested. So for example if you have a set of nested shapes and you set alpha to 0.5 for the root element all the nested elements should have this value to start with before any other values are applied. Again once I knew what the problem was fixing it was easy.
These three fixes allowed me to convert all the SVG files I wanted to use for 3DAssembler without any problems. I patched my copy of Flamingo and then posted the patch to the
Flamingo discussion forum. I would probably have left it at that, but a few days later a message was posted by the developer of Flamingo to say that he was no longer supporting or developing the library, and so SVGRoundTrip was born to hold these fixes but also as a place for new features and other SVG related ideas.
The original library (as well as having the bugs outlined above) doesn't support all SVG files as it doesn't support embedded raster images or text -- there might be other things as well but these are the things I know about. So I set about adding support for these as well. Supporting embedded raster images is easy, I just store the images on disk and then add code to the generated classes to read the images back when they are needed. Supporting all other unsupported elements (including text) is also easy -- I just convert the elements to images and then treat them like raster nodes. This isn't ideal as now they don't scale well and so by default raster images and other elements not supported by the original library are still not supported.
Anyway, enough waffle, I've wrapped up all the SVG to Java 2D code into a simple to use command line application (it can also be accessed via an API), the usage of which is given below
SVGBatchConverter: Convert SVG files into Java2D based classes
Usage: java -jar SVGRoundTrip.jar [OPTIONS] SVGFile1 ... SVGFileN
-f by defualt classes are not generated if they exist and are newer
than the SVG files, use this option to force them to be regenerated
-i by default raster images embedded in SVG files are not supported
enabling this option generates PNG files for raster nodes which
are loaded and drawn as required by the generated code
-n name classname format -- by default the generated class will have the
same name as the SVG file use this option to specify
'prefix+suffix', if the format doesn't contain a + then it just
specifies a prefix
-o dir output directory (required), this should be the root of the source
tree the correct sub-directory will be used based upon specified
package name
-p name the name of the package the generated classes should be a member of
-t name the template name or a file containing a template -- valid template
names are currently 'plain' and 'flamingo', see the docs for details
if unspecified the 'plain' template will be used
-u name specify how to treat unsupported SVG elements -- possible values are
'fail' the default behaviour which causes conversion to fail
'skip' unsupported elements will be skipped, conversion will succeed
'image' unsupported elements will be converted to images to maintain
the look of the original SVG file -- image support must (-i) must
also be enabled for this to work
If directories are specified instead of single SVG files then all SVG files in
the directories will be converted
So now you know how I convert SVG files into Java 2D based classes for use in my applications. If you want to try this yourself then
check out the code and have a go.