JairoSVG.java
package io.brunoborges.jairosvg;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.UnaryOperator;
import io.brunoborges.jairosvg.css.Colors;
import io.brunoborges.jairosvg.dom.Node;
import io.brunoborges.jairosvg.surface.*;
/**
* JairoSVG - A Java port of CairoSVG. SVG 1.1 to PNG, PDF, and PS converter.
*
* <p>
* Usage:
* </p>
*
* <pre>{@code
* // Convert SVG to PNG bytes
* byte[] png = JairoSVG.svg2png(svgBytes);
*
* // Convert SVG file to PDF file
* JairoSVG.svg2pdf(Path.of("input.svg"), Path.of("output.pdf"));
*
* // Convert with options
* byte[] png = JairoSVG.builder().fromBytes(svgBytes).dpi(150).scale(2).backgroundColor("#ffffff").toPng();
* }</pre>
*/
public final class JairoSVG {
public static final String VERSION = "1.0.1";
private JairoSVG() {
}
// ---- Simple API ----
/** Convert SVG bytes to PNG bytes. */
public static byte[] svg2png(byte[] svgBytes) throws Exception {
return builder().fromBytes(svgBytes).toPng();
}
/** Convert SVG bytes to PDF bytes. */
public static byte[] svg2pdf(byte[] svgBytes) throws Exception {
return builder().fromBytes(svgBytes).toPdf();
}
/** Convert SVG file to PNG file. */
public static void svg2png(Path input, Path output) throws Exception {
byte[] result = builder().fromFile(input).toPng();
Files.write(output, result);
}
/** Convert SVG file to PDF file. */
public static void svg2pdf(Path input, Path output) throws Exception {
byte[] result = builder().fromFile(input).toPdf();
Files.write(output, result);
}
/** Convert SVG URL to PNG bytes. */
public static byte[] svg2png(String url) throws Exception {
return builder().fromUrl(url).toPng();
}
/** Convert SVG URL to PDF bytes. */
public static byte[] svg2pdf(String url) throws Exception {
return builder().fromUrl(url).toPdf();
}
// ---- Builder API ----
/** Create a new conversion builder. */
public static ConversionBuilder builder() {
return new ConversionBuilder();
}
/**
* Fluent builder for SVG conversion.
*/
public static final class ConversionBuilder {
private byte[] bytestring;
private InputStream fileObj;
private String url;
private double dpi = 96;
private Double parentWidth;
private Double parentHeight;
private double scale = 1;
private boolean unsafe = false;
private String backgroundColor;
private boolean negateColors = false;
private Double outputWidth;
private Double outputHeight;
private int pngCompressionLevel = -1;
private float jpegQuality = -1f;
private String tiffCompressionType;
ConversionBuilder() {
}
public ConversionBuilder fromBytes(byte[] svgBytes) {
this.bytestring = svgBytes;
return this;
}
public ConversionBuilder fromString(String svgString) {
this.bytestring = svgString.getBytes(java.nio.charset.StandardCharsets.UTF_8);
return this;
}
public ConversionBuilder fromFile(Path path) throws IOException {
this.bytestring = Files.readAllBytes(path);
this.url = path.toUri().toString();
return this;
}
public ConversionBuilder fromStream(InputStream stream) throws IOException {
this.bytestring = stream.readAllBytes();
return this;
}
public ConversionBuilder fromUrl(String url) {
this.url = url;
return this;
}
public ConversionBuilder dpi(double dpi) {
this.dpi = dpi;
return this;
}
public ConversionBuilder parentWidth(double width) {
this.parentWidth = width;
return this;
}
public ConversionBuilder parentHeight(double height) {
this.parentHeight = height;
return this;
}
public ConversionBuilder scale(double scale) {
this.scale = scale;
return this;
}
public ConversionBuilder unsafe(boolean unsafe) {
this.unsafe = unsafe;
return this;
}
public ConversionBuilder backgroundColor(String color) {
this.backgroundColor = color;
return this;
}
public ConversionBuilder negateColors(boolean negate) {
this.negateColors = negate;
return this;
}
public ConversionBuilder outputWidth(double width) {
this.outputWidth = width;
return this;
}
public ConversionBuilder outputHeight(double height) {
this.outputHeight = height;
return this;
}
/**
* Set PNG compression level (0-9). 0 = no compression (fastest), 9 = max
* compression (smallest). Default uses the JDK default (~6).
*/
public ConversionBuilder pngCompressionLevel(int level) {
this.pngCompressionLevel = level;
return this;
}
/**
* Set JPEG quality (0.0-1.0). 0.0 = lowest quality (smallest file), 1.0 =
* highest quality (largest file). Default uses the JDK default (~0.75).
*/
public ConversionBuilder jpegQuality(float quality) {
this.jpegQuality = quality;
return this;
}
/**
* Set TIFF compression type. Common values: "Deflate", "LZW", "JPEG", "ZLib",
* "PackBits", "Uncompressed". Default uses the writer's default.
*/
public ConversionBuilder tiffCompressionType(String type) {
this.tiffCompressionType = type;
return this;
}
/** Convert to PNG bytes. */
public byte[] toPng() throws Exception {
var surface = new PngSurface();
if (pngCompressionLevel >= 0)
surface.setCompressionLevel(pngCompressionLevel);
return convert(surface);
}
/** Convert to PNG and write to output stream. */
public void toPng(OutputStream out) throws Exception {
var surface = new PngSurface();
if (pngCompressionLevel >= 0)
surface.setCompressionLevel(pngCompressionLevel);
convert(surface, out);
}
/** Convert to JPEG bytes. */
public byte[] toJpeg() throws Exception {
var surface = new JpegSurface();
if (jpegQuality >= 0f)
surface.setQuality(jpegQuality);
return convert(surface);
}
/** Convert to JPEG and write to output stream. */
public void toJpeg(OutputStream out) throws Exception {
var surface = new JpegSurface();
if (jpegQuality >= 0f)
surface.setQuality(jpegQuality);
convert(surface, out);
}
/** Convert to TIFF bytes. */
public byte[] toTiff() throws Exception {
var surface = new TiffSurface();
if (tiffCompressionType != null)
surface.setCompressionType(tiffCompressionType);
return convert(surface);
}
/** Convert to TIFF and write to output stream. */
public void toTiff(OutputStream out) throws Exception {
var surface = new TiffSurface();
if (tiffCompressionType != null)
surface.setCompressionType(tiffCompressionType);
convert(surface, out);
}
/** Convert to PDF bytes. */
public byte[] toPdf() throws Exception {
checkPdfBoxAvailable();
return convert(new PdfSurface());
}
/** Convert to PDF and write to output stream. */
public void toPdf(OutputStream out) throws Exception {
checkPdfBoxAvailable();
convert(new PdfSurface(), out);
}
/** Convert to PS bytes. */
public byte[] toPs() throws Exception {
return convert(new PsSurface());
}
/** Convert to EPS bytes. */
public byte[] toEps() throws Exception {
return convert(new PsSurface(true));
}
/** Convert and return the rendered BufferedImage (useful for in-memory use). */
public BufferedImage toImage() throws Exception {
Node tree = parseInput();
var surface = new PngSurface();
initSurface(surface, tree, new ByteArrayOutputStream());
return surface.getImage();
}
private byte[] convert(Surface surface) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
convert(surface, baos);
return baos.toByteArray();
}
private void convert(Surface surface, OutputStream out) throws Exception {
Node tree = parseInput();
initSurface(surface, tree, out);
surface.finish();
}
private void initSurface(Surface surface, Node tree, OutputStream out) {
UnaryOperator<Colors.RGBA> colorMapper = negateColors ? Colors::negateColor : null;
// invertImages handling would require a BufferedImage mapper
surface.init(tree, out, dpi, null, parentWidth, parentHeight, scale, outputWidth, outputHeight,
backgroundColor, colorMapper);
}
private Node parseInput() throws Exception {
byte[] data = this.bytestring;
if (data == null && this.fileObj != null) {
data = this.fileObj.readAllBytes();
}
if (data == null && this.url != null) {
return Node.parseTree(null, this.url, unsafe);
}
if (data == null) {
throw new IllegalArgumentException(
"No input. Use fromBytes(), fromFile(), fromUrl(), or fromStream().");
}
return Node.parseTree(data, this.url, unsafe);
}
private static void checkPdfBoxAvailable() {
try {
Class.forName("org.apache.pdfbox.pdmodel.PDDocument");
} catch (ClassNotFoundException e) {
throw new UnsupportedOperationException(
"PDF output requires Apache PDFBox. Add org.apache.pdfbox:pdfbox to your classpath.", e);
}
}
}
}