Main.java
package io.brunoborges.jairosvg.cli;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import io.brunoborges.jairosvg.JairoSVG;
/**
* Command-line interface to JairoSVG.
*/
public final class Main {
private static final Map<String, String> FORMAT_EXTENSIONS = Map.of("png", "PNG", "jpeg", "JPEG", "jpg", "JPEG",
"tiff", "TIFF", "tif", "TIFF", "pdf", "PDF", "ps", "PS", "eps", "EPS");
public static void main(String[] args) throws Exception {
ensureJavaHome();
if (args.length == 0 || "--help".equals(args[0]) || "-h".equals(args[0])) {
printUsage();
return;
}
if ("--version".equals(args[0]) || "-v".equals(args[0])) {
System.out.println(JairoSVG.VERSION);
return;
}
// Parse arguments
List<String> inputs = new ArrayList<>();
String output = "-";
String format = null;
double dpi = 96;
Double width = null;
Double height = null;
double scale = 1;
String background = null;
boolean negateColors = false;
boolean unsafe = false;
Double outputWidth = null;
Double outputHeight = null;
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
case "-o", "--output" -> output = args[++i];
case "-f", "--format" -> format = args[++i];
case "-d", "--dpi" -> dpi = Double.parseDouble(args[++i]);
case "-W", "--width" -> width = Double.parseDouble(args[++i]);
case "-H", "--height" -> height = Double.parseDouble(args[++i]);
case "-s", "--scale" -> scale = Double.parseDouble(args[++i]);
case "-b", "--background" -> background = args[++i];
case "-n", "--negate-colors" -> negateColors = true;
case "-u", "--unsafe" -> unsafe = true;
case "--output-width" -> outputWidth = Double.parseDouble(args[++i]);
case "--output-height" -> outputHeight = Double.parseDouble(args[++i]);
default -> {
if (!args[i].startsWith("-") || "-".equals(args[i])) {
inputs.add(args[i]);
}
}
}
}
if (inputs.isEmpty()) {
System.err.println("Error: No input file specified.");
printUsage();
throw new IllegalArgumentException("No input file specified.");
}
boolean multiFile = inputs.size() > 1 || (!"-".equals(output) && Files.isDirectory(Path.of(output)));
// For multiple files, -o must be a directory (or omitted for auto-naming)
if (multiFile && !"-".equals(output) && !Files.isDirectory(Path.of(output))) {
Files.createDirectories(Path.of(output));
}
// Determine output format
if (format != null) {
format = format.toUpperCase();
} else if (!multiFile && !"-".equals(output)) {
String ext = output.substring(output.lastIndexOf('.') + 1).toLowerCase();
format = FORMAT_EXTENSIONS.getOrDefault(ext, "PNG");
} else {
format = "PNG";
}
String formatExt = switch (format) {
case "JPEG" -> "jpeg";
case "TIFF" -> "tiff";
case "PDF" -> "pdf";
case "PS", "EPS" -> "ps";
default -> "png";
};
for (String input : inputs) {
var builder = JairoSVG.builder().dpi(dpi).scale(scale).unsafe(unsafe).negateColors(negateColors);
if (background != null)
builder.backgroundColor(background);
if (width != null)
builder.parentWidth(width);
if (height != null)
builder.parentHeight(height);
if (outputWidth != null)
builder.outputWidth(outputWidth);
if (outputHeight != null)
builder.outputHeight(outputHeight);
// Read input
if ("-".equals(input)) {
builder.fromStream(System.in);
} else {
builder.fromFile(Path.of(input));
}
// Determine per-file output path
String fileOutput;
if (multiFile) {
String baseName = Path.of(input).getFileName().toString().replaceFirst("\\.[^.]+$", "");
if ("-".equals(output)) {
fileOutput = baseName + "." + formatExt;
} else {
fileOutput = Path.of(output, baseName + "." + formatExt).toString();
}
} else {
fileOutput = output;
}
// Write output
if ("-".equals(fileOutput)) {
writeOutput(builder, format, System.out);
} else {
try (var out = new FileOutputStream(fileOutput)) {
writeOutput(builder, format, out);
}
}
}
}
private static void printUsage() {
System.out.println("""
JairoSVG %s - Convert SVG files to other formats
Usage: jairosvg [options] input.svg [input2.svg ...]
Options:
-o, --output FILE|DIR Output filename or directory (default: stdout)
-f, --format FORMAT Output format: png, jpeg, tiff, pdf, ps, eps
-d, --dpi DPI DPI ratio (default: 96)
-W, --width PIXELS Parent container width
-H, --height PIXELS Parent container height
-s, --scale FACTOR Output scaling factor (default: 1)
-b, --background COLOR Output background color
-n, --negate-colors Negate vector colors
-u, --unsafe Allow external file access and XML entities
--output-width PIXELS Desired output width
--output-height PIXELS Desired output height
-v, --version Show version
-h, --help Show this help
When multiple input files are given, -o specifies an output directory.
Output filenames are derived from input names with the appropriate extension.
""".formatted(JairoSVG.VERSION));
}
private static void writeOutput(JairoSVG.ConversionBuilder builder, String format, OutputStream out)
throws Exception {
switch (format) {
case "PNG" -> builder.toPng(out);
case "JPEG" -> builder.toJpeg(out);
case "TIFF" -> builder.toTiff(out);
case "PDF" -> builder.toPdf(out);
case "PS", "EPS" -> {
byte[] data = builder.toPs();
out.write(data);
}
default -> builder.toPng(out);
}
}
/**
* Ensure java.home is set for font configuration (required in GraalVM native
* images).
*/
private static void ensureJavaHome() {
if (System.getProperty("java.home") == null) {
String javaHome = System.getenv("JAVA_HOME");
if (javaHome == null) {
javaHome = "/usr";
}
System.setProperty("java.home", javaHome);
}
}
}