Apache Batik
| Language | Java |
|---|---|
| License | Apache License 2.0 |
| Repository | github.com/apache/xmlgraphics-batik |
| Documentation | xmlgraphics.apache.org/batik, Javadoc |
| Depends on | xmlgraphics-commons, xml-apis-ext (pulled in by Maven) |
| Read / write | yes / yes (DOM, rendering, transcoding to PNG/JPEG/TIFF, SVG generation from Java2D) |
Batik is a set of modules for working with SVG in Java: a parser and SVG DOM (org.w3c.dom.svg), the GVT renderer, the JSVGCanvas Swing component, the Squiggle viewer, the rasterizer command line tool, transcoders to raster formats, and SVGGraphics2D, which records Java2D drawing as SVG. It implements most of SVG 1.1 including filters, fonts, SMIL animation and ECMAScript scripting.
The cost is size and speed: the full toolkit is several megabytes and rendering is slower and more memory-hungry than JSVG. For PDF output, use the PDFTranscoder from Apache FOP (fop-transcoder-allinone).
Installation#
Maven, for converting SVG to images:
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-transcoder</artifactId>
<version>1.20</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>1.20</version>
</dependency>
Add batik-svggen and batik-dom to generate SVG, or depend on batik-all for everything. Gradle: implementation("org.apache.xmlgraphics:batik-transcoder:1.20").
Basic usage: convert SVG to PNG#
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class SvgToPng {
public static void main(String[] args) throws IOException, TranscoderException {
PNGTranscoder transcoder = new PNGTranscoder();
transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, 512f); // optional: height keeps aspect ratio
TranscoderInput input = new TranscoderInput(new File("in.svg").toURI().toString());
try (OutputStream out = new FileOutputStream("out.png")) {
transcoder.transcode(input, new TranscoderOutput(out));
}
}
}
Pass the input as a URI string (not a bare path) so relative references inside the SVG resolve.
How to convert SVG to JPEG#
JPEG has no transparency, so set a background color:
import org.apache.batik.transcoder.image.JPEGTranscoder;
import java.awt.Color;
JPEGTranscoder jpg = new JPEGTranscoder();
jpg.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, 0.9f);
jpg.addTranscodingHint(JPEGTranscoder.KEY_BACKGROUND_COLOR, Color.WHITE);
try (OutputStream out = new FileOutputStream("out.jpg")) {
jpg.transcode(new TranscoderInput(new File("in.svg").toURI().toString()), new TranscoderOutput(out));
}
How to read and modify the SVG DOM#
import org.apache.batik.anim.dom.SAXSVGDocumentFactory;
import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.apache.batik.util.XMLResourceDescriptor;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.svg.SVGDocument;
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(parser);
SVGDocument doc = factory.createSVGDocument(new File("in.svg").toURI().toString());
Element root = doc.getDocumentElement();
System.out.println("width=" + root.getAttribute("width") + " viewBox=" + root.getAttribute("viewBox"));
NodeList circles = doc.getElementsByTagNameNS(SVGDOMImplementation.SVG_NAMESPACE_URI, "circle");
for (int i = 0; i < circles.getLength(); i++) {
((Element) circles.item(i)).setAttribute("fill", "green");
}
// render the modified document: new TranscoderInput(doc)
How to get the bounding box of the drawing#
Build the graphics tree (GVT) from the DOM:
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.gvt.GraphicsNode;
import java.awt.geom.Rectangle2D;
UserAgentAdapter ua = new UserAgentAdapter();
BridgeContext ctx = new BridgeContext(ua, new DocumentLoader(ua));
ctx.setDynamicState(BridgeContext.STATIC);
GraphicsNode node = new GVTBuilder().build(ctx, doc);
Rectangle2D bounds = node.getGeometryBounds();
System.out.println(bounds);
How to generate SVG from Java2D code#
SVGGraphics2D records Graphics2D calls as SVG elements:
import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.apache.batik.svggen.SVGGraphics2D;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import java.awt.Color;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
Document document = impl.createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, "svg", null);
SVGGraphics2D g = new SVGGraphics2D(document);
g.setPaint(Color.ORANGE);
g.fillOval(10, 10, 80, 80);
g.setPaint(Color.BLACK);
g.drawString("Hello", 20, 110);
try (Writer out = new OutputStreamWriter(new FileOutputStream("out.svg"), StandardCharsets.UTF_8)) {
g.stream(out, true); // true: use CSS style attributes
}
Tips#
- Run headless on servers with
-Djava.awt.headless=true. - Batik can execute scripts and fetch external resources. For untrusted SVG, use a custom
UserAgentthat denies external resources and keep scripting disabled (transcoders don’t run scripts by default). - The command line rasterizer:
java -jar batik-rasterizer-1.20.jar -w 512 -m image/png in.svg.