A high-performance SVG 1.1 to PNG, JPEG, TIFF, PDF, and PS/EPS converter powered by Java2D. API inspired by CairoSVG.
- Zero native dependencies — pure Java, runs anywhere the JVM runs
- No heavyweight frameworks — no Batik, no Cairo, no WebKit; just Java2D
- Secure by default — XXE protection enabled,
<script>elements ignored, external resource access disabled unless explicitly opted in - Fast — 3-30× faster than EchoSVG (Batik), competitive with JSVG, 1-2× faster than CairoSVG's native C backend
- Drop-in API — static one-liners, fluent builder, or CLI — your choice
- 🎨 SVG 1.1 rendering using Java2D — no native dependencies (with selective SVG 2 alignment)
- 📄 Multiple output formats: PNG, JPEG, TIFF, PDF (via optional Apache PDFBox), PostScript/EPS
- 🔷 Full shape support: rect, circle, ellipse, line, polygon, polyline, path
- 🌈 Gradients: linear and radial with stop colors, opacity, and
spreadMethod - ✍️ Text rendering with font control, letter-spacing, text-anchor,
<tspan>, and<textPath> - 🔄 Transforms: translate, rotate, scale, skewX, skewY, matrix
- 🎭 Definitions:
<use>,<symbol>,<defs>, clip-path, viewBox, preserveAspectRatio - 🖌️ Patterns:
<pattern>withpatternTransformandpatternUnits - 🎯 Markers:
<marker>withorient="auto"tangent direction support - 😷 Masks:
<mask>with luminance-to-alpha compositing - 🔍 Filters:
feGaussianBlur,feOffset,feFlood,feBlend,feMerge,feDropShadow,feImage,feTile - 🔤 SVG Fonts:
<font>and<glyph>with system font fallback for undefined characters - 🎨 CSS: stylesheets, inline styles, CSS variables (
var()),currentColor,inherit - 🔀 Conditional processing:
<switch>,systemLanguage,requiredExtensions - ⚡ Fast: 3-29× faster than EchoSVG (Batik fork), on par with JSVG, 1-2.6× faster than CairoSVG's native C backend
- 🛡️ Secure: XML external entity (XXE) protection by default
- 🧰 Flexible API: Static methods, fluent builder, CLI
| Test Case | JairoSVG (Java) | EchoSVG (Java) | JSVG (Java) | CairoSVG (Python) |
|---|---|---|---|---|
| Simple shapes | 2.94 ms | 16.28 ms | 3.13 ms | 4.34 ms |
| Gradients | 4.15 ms | 132.07 ms | 3.97 ms | 10.83 ms |
| Filters | 6.61 ms | 33.57 ms | 7.86 ms | 4.47 ms |
| Fe blend modes | 8.96 ms | 25.80 ms | 18.16 ms | 12.55 ms |
| Embedded image | 4.01 ms | 15.79 ms | 10.52 ms | 7.05 ms |
| Localized masks | 12.74 ms | 51.03 ms | 12.93 ms | 15.29 ms |
JairoSVG is about 3–32× faster than EchoSVG in this sample, wins 4 of these 6 cases, and stays competitive with JSVG and CairoSVG across the set.
Run the benchmark yourself: jbang comparison/benchmark/benchmark.java.
See comparison/README.md for full benchmark results, PNG file size comparisons, and feature matrices across JairoSVG, EchoSVG, CairoSVG, and JSVG.
See comparison/COMPARISON.md for side-by-side rendered PNG comparisons, benchmark times, and file sizes across 43 SVG test cases.
Tip: Check Maven Central for the latest released version.
Download the standalone CLI jar from the GitHub Releases page:
# Download the CLI jar (requires Java 25+)
java -jar jairosvg-{version}-cli.jar input.svg -o output.png
# Convert multiple files at once
java -jar jairosvg-{version}-cli.jar *.svg -o output-dir/<dependency>
<groupId>io.brunoborges</groupId>
<artifactId>jairosvg</artifactId>
<version>1.0.15</version>
</dependency>Note: PDF output requires Apache PDFBox on the classpath. It is an optional dependency — if you only need PNG, JPEG, TIFF, or PS/EPS output, you do not need to add PDFBox. To enable PDF support, add it explicitly:
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>3.0.7</version> </dependency>
implementation 'io.brunoborges:jairosvg:1.0.15'jbang --deps io.brunoborges:jairosvg:1.0.15 MyScript.javaimport io.brunoborges.jairosvg.JairoSVG;
// SVG bytes → PNG bytes
byte[] png = JairoSVG.svg2png(svgBytes);
// SVG file → PDF file
JairoSVG.svg2pdf(Path.of("input.svg"), Path.of("output.pdf"));
// One-liner conversions for all formats
byte[] jpeg = JairoSVG.svg2jpeg(svgBytes);
byte[] tiff = JairoSVG.svg2tiff(svgBytes);
byte[] ps = JairoSVG.svg2ps(svgBytes);
byte[] eps = JairoSVG.svg2eps(svgBytes);
// File-to-file for any format
JairoSVG.svg2jpeg(Path.of("input.svg"), Path.of("output.jpg"));
JairoSVG.svg2tiff(Path.of("input.svg"), Path.of("output.tiff"));
JairoSVG.svg2ps(Path.of("input.svg"), Path.of("output.ps"));
JairoSVG.svg2eps(Path.of("input.svg"), Path.of("output.eps"));
// Builder API with options
byte[] scaled = JairoSVG.builder()
.fromBytes(svgBytes)
.dpi(150)
.scale(2)
.backgroundColor("#ffffff")
.toPng();
// Control image compression/quality
byte[] fastPng = JairoSVG.builder()
.fromFile(Path.of("icon.svg"))
.pngCompressionLevel(1) // 0 (fastest) to 9 (smallest)
.toPng();
byte[] highQualityJpeg = JairoSVG.builder()
.fromFile(Path.of("photo.svg"))
.jpegQuality(0.95f) // 0.0 (smallest) to 1.0 (best)
.toJpeg();
// Get BufferedImage directly
BufferedImage image = JairoSVG.builder()
.fromFile(Path.of("icon.svg"))
.toImage();
// SVG string → PNG
byte[] png = JairoSVG.builder()
.fromString("<svg>...</svg>")
.toPng();
// Stream input
byte[] fromStream = JairoSVG.builder()
.fromStream(inputStream)
.toPng();
// PS/EPS output (returns byte[])
byte[] ps = JairoSVG.builder()
.fromFile(Path.of("diagram.svg"))
.toPs();
byte[] eps = JairoSVG.builder()
.fromFile(Path.of("diagram.svg"))
.toEps();
// TIFF output
JairoSVG.builder()
.fromFile(Path.of("drawing.svg"))
.toTiff(new FileOutputStream("output.tiff"));
// Negate colors + output dimensions
byte[] inverted = JairoSVG.builder()
.fromBytes(svgBytes)
.negateColors(true)
.outputWidth(800)
.outputHeight(600)
.toPng();
// Unsafe mode (allows external file access)
byte[] result = JairoSVG.builder()
.fromUrl("https://example.com/chart.svg")
.unsafe(true)
.toPng();
// Customize Java2D rendering hints
import java.awt.RenderingHints;
byte[] quality = JairoSVG.builder()
.fromBytes(svgBytes)
.renderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)
.toPng();Install with JBang
jbang app install io.brunoborges:jairosvg:LATEST
# SVG → PNG
jairosvg input.svg -o output.png
# SVG → PDF with 2x scale
jairosvg input.svg -f pdf -s 2 -o output.pdf
# Convert multiple files to a directory
jairosvg *.svg -o output-dir/Or download the CLI jar from GitHub Releases:
# SVG → PNG
java -jar jairosvg-{version}-cli.jar input.svg -o output.png
# SVG → PDF with 2x scale
java -jar jairosvg-{version}-cli.jar input.svg -f pdf -s 2 -o output.pdf
# Convert multiple files to a directory
java -jar jairosvg-{version}-cli.jar *.svg -o output-dir/Build from source:
mvn package -Pcli
java -jar target/jairosvg-{version}-cli.jar input.svg -o output.png| Option | Description |
|---|---|
-o, --output FILE|DIR |
Output filename or directory (default: stdout) |
-f, --format FORMAT |
Output format: png, jpeg, tiff, pdf, ps, eps |
-d, --dpi DPI |
Resolution (default: 96) |
-s, --scale FACTOR |
Scale factor (default: 1) |
-b, --background COLOR |
Background color |
-W, --width PIXELS |
Parent container width |
-H, --height PIXELS |
Parent container height |
--output-width PIXELS |
Desired output width |
--output-height PIXELS |
Desired output height |
-n, --negate-colors |
Negate vector colors |
-u, --unsafe |
Allow external file access |
When multiple input files are given, -o specifies an output directory. Output filenames are derived from input names with the appropriate extension. Missing parent directories of the output are created. Unknown options are rejected.
Generate PWA icons, favicons, and other raster images from an SVG during the build, with no dedicated plugin: run the CLI in-process with the Exec Maven Plugin (or Gradle JavaExec).
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.6.4</version>
<dependencies>
<dependency>
<groupId>io.brunoborges</groupId>
<artifactId>jairosvg</artifactId>
<version>1.0.15</version>
</dependency>
</dependencies>
<configuration>
<mainClass>io.brunoborges.jairosvg.cli.Main</mainClass>
<includeProjectDependencies>false</includeProjectDependencies>
<includePluginDependencies>true</includePluginDependencies>
</configuration>
<executions>
<execution>
<id>pwa-icon-512</id>
<phase>process-resources</phase>
<goals><goal>java</goal></goals>
<configuration>
<arguments>
<argument>${project.basedir}/src/main/icons/logo.svg</argument>
<argument>-o</argument>
<argument>${project.build.outputDirectory}/META-INF/resources/icons/icon-512.png</argument>
<argument>--output-width</argument><argument>512</argument>
<argument>--output-height</argument><argument>512</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>Add one <execution> per output image. See the Build-time Rendering guide for multiple sizes, generated-resources layout, and a Gradle example. Requires JairoSVG 1.0.15+ and a Java 25 build JVM.
- ✅ Basic shapes:
rect,circle,ellipse,line,polygon,polyline - ✅ Path commands: M, L, C, S, Q, T, A, H, V, Z (absolute and relative)
- ✅
<use>,<symbol>,<defs>,<g> - ✅
<svg>with nesting and viewBox - ✅
<image>— embedded raster (data URI and external) and nested SVG - ✅
<text>,<tspan>,<textPath>,<a> - ✅
<marker>withorient="auto"tangent direction - ✅
<clipPath> - ✅
<mask>with luminance-to-alpha compositing - ✅
<pattern>withpatternTransformandpatternUnits - ✅
<linearGradient>,<radialGradient>withspreadMethodand href chaining - ✅
<font>,<font-face>,<glyph>,<missing-glyph>(SVG fonts) - ✅
<switch>with conditional processing (systemLanguage,requiredExtensions) - ✅
<cursor>,<foreignObject>(parsed but not rendered — see LIMITATIONS.md)
- ✅
<filter>withfilterUnitsand sub-region optimization - ✅
feGaussianBlur,feOffset,feFlood,feBlend(normal, multiply, screen, darken, lighten) - ✅
feMerge,feComposite(over, in, out, atop, xor, arithmetic),feDropShadow - ✅
feImage(data URI + inline references),feTile - ✅
feColorMatrix(matrix, saturate, hueRotate, luminanceToAlpha) - ✅
feComponentTransfer(identity, linear, gamma, table, discrete) - ✅
feMorphology(erode, dilate),feConvolveMatrix - ✅
feTurbulence(turbulence, fractalNoise),feDisplacementMap - ✅
feDiffuseLighting,feSpecularLighting(distant, point, spot lights)
- ✅ CSS stylesheets (embedded
<style>) and inlinestyleattributes - ✅ CSS
@importrules (URL resolution with circular import detection) - ✅ CSS selectors: class, id, element, descendant,
:first-child,:last-child,:nth-child(),:not() - ✅ CSS custom properties / variables (
var(--name, fallback)) - ✅
currentColor,inherit - ✅ Opacity: element, fill, stroke, group
- ✅
display,visibility
- ✅ Transforms: translate, rotate, scale, skewX, skewY, matrix
- ✅ viewBox and preserveAspectRatio (all 9 align values + meet/slice)
- ✅ Stroke properties: dasharray, dashoffset, linecap, linejoin, miterlimit, width
- ✅ Named colors (170+), hex,
rgb(),rgba(),hsl(),hsla() - ✅ Units: px, pt, em, ex, %, cm, mm, in, pc
JairoSVG renders SVG through the standard Java2D (Graphics2D / BufferedImage) API. The architecture is intentionally compact:
| Java Class | Role |
|---|---|
JairoSVG |
Public API + fluent Builder |
Surface |
Java2D rendering engine + state mgmt |
Node |
SVG DOM tree with CSS cascade |
PathDrawer |
SVG path commands → GeneralPath |
ShapeDrawer |
Basic shape elements |
TextDrawer |
Text, tspan, textPath rendering |
Defs |
Definition elements + clip paths |
GradientPainter |
Linear/radial gradient rendering |
PatternPainter |
Pattern → TexturePaint |
MaskPainter |
Mask luminance-to-alpha compositing |
MarkerDrawer |
Marker placement and orientation |
FilterRenderer |
Filter pipeline (blur, blend, merge...) |
GaussianBlur |
Optimized box-blur Gaussian |
BlendCompositor |
feBlend pixel blending modes |
ImageHandler |
Embedded image handling |
SvgDrawer |
Nested <svg> viewport handling |
Colors |
Color parsing (170+ named) |
CssProcessor |
CSS parsing and selector matching |
SvgFont |
SVG font glyph caching |
Helpers |
Units, transforms, aspect ratio |
Main |
CLI entry point |
Key technologies:
java.awt.Graphics2D— 2D rendering contextjava.awt.image.BufferedImage— raster image bufferjava.awt.geom.AffineTransform— coordinate transforms- Apache PDFBox 3.0 — PDF output (optional dependency)
# Clone and build
git clone https://github.com/brunoborges/jairosvg.git
cd jairosvg
./mvnw clean verify
# Run tests
./mvnw test
# Generate documentation site
./mvnw siteSee LIMITATIONS.md for known and intentional limitations (including unsupported embedded content such as <foreignObject>).
See CONTRIBUTING.md for guidelines on building, testing, and submitting pull requests.
JairoSVG is licensed under the MIT License.