Apache Batik 快速入门与集成示例
Apache Batik 1.19 快速入门:Maven 与无构建工具两种依赖引入、Transcoder API 最小可运行程序把 SVG 渲染为 PNG/JPEG、完整编译运行回显、jps 堆参数核验与脚本渲染边界,全部实测可复现。
Apache Batik 快速入门与集成示例
目标:用最少步骤把 Apache Batik 1.19 跑起来——Maven 与无构建工具两种依赖引入、一个把 SVG 同时渲染为 PNG 与 JPEG 的最小可运行程序、完整编译运行回显、
jps -v堆参数核验,并交代 Transcoder API 的脚本边界与两个高频编译错误,已在 Ubuntu 22.04(OpenJDK 17.0.20.1、Maven 3.6.3)实测通过。
一、环境信息
本篇所有命令与回显均在以下环境中实测采集:
- 操作系统:Ubuntu 22.04.5 LTS,x86_64
- JDK:OpenJDK 17.0.20.1(
openjdk-17-jdk-headless) - 构建工具:Apache Maven 3.6.3(方式二无需)
- 组件版本:Apache Batik 1.19(Maven 坐标 org.apache.xmlgraphics:batik-transcoder/batik-codec,或官方 batik-bin-1.19.tar.gz 发行包)
- 实验目录:
/opt/lab/batik-quick(Maven 工程)、/opt/lab/batik-manual(手动直跑) - 执行用户:root
二、前置条件
确认 JDK 与 Maven 就位:
java -version 2>&1 | head -1 && mvn -version 2>&1 | head -1
回显(实测):
openjdk version "17.0.20.1" 2026-08-18
Apache Maven 3.6.3
准备贯穿全篇的测试 SVG(400x200,含矩形、圆形与文本):
mkdir -p /opt/lab/batik-quick /opt/lab/batik-manual && cat > /opt/lab/batik-quick/hello.svg << "SVGEOF"
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="200" viewBox="0 0 400 200">
<rect x="0" y="0" width="400" height="200" fill="#1e88e5"/>
<circle cx="80" cy="100" r="50" fill="#ffca28"/>
<text x="150" y="110" font-family="DejaVu Sans" font-size="32" fill="#ffffff">Hello Batik</text>
</svg>
SVGEOF
cp /opt/lab/batik-quick/hello.svg /opt/lab/batik-manual/hello.svg
回显(实测,写入成功无输出):
⚠️ 注意:文本元素依赖 DejaVu 字体,Ubuntu 自带(
/usr/share/fonts/truetype/dejavu/);极简系统无字体时apt-get install -y fonts-dejavu-core,否则文字渲染为空白或缺字。
三、依赖引入
3.1 方式一:Maven 依赖(推荐)
Batik 的 API 入口是 batik-transcoder,PNG/JPEG 编码能力在 batik-codec,两个坐标即可起步。工程结构:
/opt/lab/batik-quick/
├── pom.xml
└── src/main/java/com/example/SvgToPng.java
最小 pom(完整文件):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>batik-quick</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-transcoder</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>1.19</version>
</dependency>
</dependencies>
</project>
首次编译(依赖已在本地仓库时秒级完成):
cd /opt/lab/batik-quick && mvn -B compile
输出节选(实测收尾段):
[INFO] Compiling 1 source file to /opt/lab/batik-quick/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.117 s
batik-transcoder 会传递引入整套渲染模块,用依赖树看清全貌:
mvn -B dependency:tree 2>/dev/null | grep -E "batik|xmlgraphics|xml-apis|commons" | head -18
输出节选(实测首部):
[INFO] +- org.apache.xmlgraphics:batik-transcoder:jar:1.19:compile
[INFO] | +- org.apache.xmlgraphics:batik-anim:jar:1.19:compile
[INFO] | | +- org.apache.xmlgraphics:batik-css:jar:1.19:compile
[INFO] | | +- org.apache.xmlgraphics:batik-ext:jar:1.19:compile
[INFO] | | +- org.apache.xmlgraphics:batik-parser:jar:1.19:compile
[INFO] | | \- org.apache.xmlgraphics:batik-svg-dom:jar:1.19:compile
[INFO] | +- org.apache.xmlgraphics:batik-awt-util:jar:1.19:compile
[INFO] | | \- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.11:compile
[INFO] | | +- commons-io:commons-io:jar:2.17.0:compile
[INFO] | | \- commons-logging:commons-logging:jar:1.3.0:compile
[INFO] | +- org.apache.xmlgraphics:batik-bridge:jar:1.19:compile
[INFO] | | \- org.apache.xmlgraphics:batik-script:jar:1.19:compile
[INFO] | +- org.apache.xmlgraphics:batik-dom:jar:1.19:compile
[INFO] | | \- xml-apis:xml-apis:jar:1.4.01:compile
[INFO] | +- org.apache.xmlgraphics:batik-gvt:jar:1.19:compile
[INFO] \- org.apache.xmlgraphics:batik-codec:jar:1.19:compile
两个坐标最终传递引入 22 个 jar(实测 dependency:build-classpath 导出后按冒号计数),其中 batik-script 是脚本引擎桥(不引第三方脚本引擎时不会真正加载)。
3.2 方式二:无构建工具,发行包 lib/ 直跑
服务器上不想装 Maven 时,用官方发行包的 lib/ 全量 jar 做类路径(发行包获取与校验见《Ubuntu 22.04 安装 Apache Batik 指南》方案 B,解压到 /opt/batik)。把同一个渲染逻辑写成不带 package 的类:
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
public class SvgToPngManual {
public static void main(String[] args) throws Exception {
TranscoderInput input = new TranscoderInput(new File("/opt/lab/batik-manual/hello.svg").toURI().toString());
PNGTranscoder png = new PNGTranscoder();
png.addTranscodingHint(PNGTranscoder.KEY_WIDTH, 800f);
png.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, 400f);
OutputStream out = new FileOutputStream("/opt/lab/batik-manual/hello-manual.png");
png.transcode(input, new TranscoderOutput(out));
out.close();
System.out.println("PNG done -> hello-manual.png");
}
}
编译与运行(保存为 /opt/lab/batik-manual/SvgToPngManual.java):
cd /opt/lab/batik-manual && javac -cp "/opt/batik/lib/*" SvgToPngManual.java && echo COMPILED
java -Xms256m -Xmx256m -cp ".:/opt/batik/lib/*" SvgToPngManual
回显(实测):
COMPILED
PNG done -> hello-manual.png
ls -l /opt/lab/batik-manual/hello-manual.png | awk '{print $5}' && od -An -tu1 -j16 -N8 /opt/lab/batik-manual/hello-manual.png
回显(实测):
11881
0 0 3 32 0 0 1 144
11881 字节、IHDR 显示 800x400,与 Maven 方式产物完全一致(同代码同参数)。
💡 技术拓展:两种引入方式怎么选——
方式 依赖来源 适用场景 注意点 Maven 坐标 中央仓库,按需传递 22 个 jar 常规工程、CI 流水线 版本统一在 pom 管理,升级只改两处 发行包 lib/ batik-bin-1.19.tar.gz 解压 无构建工具的脚本机、临时任务 lib/ 全量 35 个 jar 都在类路径,注意别与应用自带库冲突 同一进程内混用两种来源(类路径同时出现 Maven 版与发行包版 jar)会造成类重复加载,务必二选一。
四、核心 API 速览
服务端光栅化只需掌握一小块 API 面:
| 类/接口 | 作用 |
|---|---|
| PNGTranscoder / JPEGTranscoder | 图像转码器本体,transcode(input, output) 一步完成渲染编码;TIFF 对应 TIFFTranscoder |
| TranscoderInput | 渲染源:URI 字符串、InputStream、Reader、Document 或 SVGOMDocument 均可 |
| TranscoderOutput | 渲染目标:OutputStream、Writer 或 URI;不接受 java.io.File |
| addTranscodingHint(KEY_*, value) | 调整输出行为:KEY_WIDTH/KEY_HEIGHT(浮点像素,Float)、KEY_QUALITY(JPEG 质量 0~1,Float)、KEY_BACKGROUND_COLOR 等 |
三个实测得来的硬约束:提示键值必须是 Float 类型(传 int 或 double 会编译错);TranscoderOutput 直接传 File 报编译错 argument mismatch; java.io.File cannot be converted to java.lang.String(实测踩坑,须用 FileOutputStream 包裹);1.19 已删除 XMLAbstractTranscoder.KEY_XML_PARSER_CLASS_NAME 常量(实测编译 cannot find symbol,固定解析器改用 -Dorg.xml.sax.driver 系统属性)。
五、最小可运行程序:SVG 转 PNG 与 JPEG
5.1 完整代码
/opt/lab/batik-quick/src/main/java/com/example/SvgToPng.java(完整文件):
package com.example;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.JPEGTranscoder;
import org.apache.batik.transcoder.image.PNGTranscoder;
public class SvgToPng {
public static void main(String[] args) throws Exception {
TranscoderInput input = new TranscoderInput(new File("/opt/lab/batik-quick/hello.svg").toURI().toString());
PNGTranscoder png = new PNGTranscoder();
png.addTranscodingHint(PNGTranscoder.KEY_WIDTH, 800f);
png.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, 400f);
OutputStream pngOut = new FileOutputStream("/opt/lab/batik-quick/hello-api.png");
png.transcode(input, new TranscoderOutput(pngOut));
pngOut.close();
System.out.println("PNG done -> hello-api.png");
JPEGTranscoder jpg = new JPEGTranscoder();
jpg.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, 0.8f);
OutputStream jpgOut = new FileOutputStream("/opt/lab/batik-quick/hello-api.jpg");
jpg.transcode(input, new TranscoderOutput(jpgOut));
jpgOut.close();
System.out.println("JPEG done -> hello-api.jpg");
}
}
5.2 编译、运行与堆参数核验
编译已由 3.1 节的 mvn -B compile 完成。导出依赖类路径后带堆参数运行:
cd /opt/lab/batik-quick && mvn -B -q dependency:build-classpath -Dmdep.outputFile=cp.txt
java -Xms256m -Xmx256m -cp target/classes:$(cat cp.txt) com.example.SvgToPng
回显(实测):
PNG done -> hello-api.png
JPEG done -> hello-api.jpg
运行期间用 jps 抓取进程,核验堆参数真实生效(程序渲染很快,抓取窗口要密集轮询):
java -Xms256m -Xmx256m -cp target/classes:$(cat cp.txt) com.example.SvgToPng > /tmp/quick-run.log 2>&1 &
for i in 1 2 3 4 5 6 7 8; do jps -lv 2>/dev/null | grep -i svgto && break; sleep 0.3; done
回显(实测):
9423 com.example.SvgToPng -Xms256m -Xmx256m
5.3 产物验证
ls -l /opt/lab/batik-quick/hello-api.png /opt/lab/batik-quick/hello-api.jpg | awk '{print $5, $9}'
od -An -tu1 -j16 -N8 /opt/lab/batik-quick/hello-api.png
回显(实测):
11881 /opt/lab/batik-quick/hello-api.png
5486 /opt/lab/batik-quick/hello-api.jpg
0 0 3 32 0 0 1 144
PNG 11881 字节,IHDR 前三字节 0x0320=800、后四字节 0x0190=400——KEY_WIDTH/KEY_HEIGHT 精确生效;JPEG 5486 字节为 KEY_QUALITY 0.8f 的编码结果(与 rasterizer -q 0.8 同尺寸产物一致)。
5.4 代码逐段解读
new File("/opt/lab/batik-quick/hello.svg").toURI().toString():把文件路径转成file:URI 再交给 TranscoderInput。这是相对引用资源(外链图片、CSS)能正确解析的关键——直接读字符串路径会丢失基准 URI。png.addTranscodingHint(PNGTranscoder.KEY_WIDTH, 800f):不给宽度提示时按 SVG 文档声明的 400x200 输出;给了 800x400 后按提示尺寸缩放渲染。png.transcode(input, new TranscoderOutput(pngOut)):同步阻塞调用,单线程内一次渲染一个文件;多核并行转换请起多个 JVM 进程或加锁串行化(Transcoder 实例非线程安全)。pngOut.close():transcode 返回只代表编码写入完成,流必须显式关闭,否则 PNG/JPEG 文件可能缺尾部字节。
六、集成边界与实测结论
三个决定集成方案边界的实测结论:
其一,Transcoder API 是静态渲染,不派发脚本事件。 用带 onload 脚本(把背景色改为红色)的 SVG 分别在有/无 Rhino 引擎的类路径下经 Transcoder 渲染,产物 sha256 前 24 位完全一致(均为 2bd7b2e091762e32015e7808,与不执行脚本时的基线相同)——onload 回调根本不会触发。需要执行 SVG 内嵌脚本的场景只能走 rasterizer -onload(1.19 实测受脚本沙箱与兼容性双重限制,见《Apache Batik 配置参数详解》第七章)或 JSVGCanvas 动态环境。
其二,固定解析器用系统属性而非提示键。 1.19 删除了 KEY_XML_PARSER_CLASS_NAME,需要固定 Xerces 实现时在 JVM 启动参数加:
java -Dorg.xml.sax.driver=com.sun.org.apache.xerces.internal.parsers.SAXParser -Xms256m -Xmx256m -cp target/classes:$(cat cp.txt) com.example.SvgToPng
输出节选(实测):
PNG done -> hello-api.png
JPEG done -> hello-api.jpg
其三,字体环境决定文本渲染结果。 同一份 hello.svg 在无 DejaVu 字体的机器上文本会缺字或走兜底字体;实测 Ubuntu 22.04(有 DejaVu)与另一台仅装基本字体的 CentOS 8.4 对同一 SVG 的 rasterizer 产物分别为 5870B 与 5452B——体积差异就是字体度量与字形差落的直接体现。生产机统一 fonts-dejavu-core(或用 ttf2svg 内嵌字体)才能保证跨机产物一致。
七、注意事项
- 提示键值类型是 Float:
KEY_WIDTH/KEY_HEIGHT/KEY_QUALITY传 Integer/Double 编译不过,字面量记得写800f、0.8f。 - TranscoderOutput 不接受 File:传 File 直接编译错(实测
argument mismatch),用new FileOutputStream(...)包裹;用完必须 close。 - Transcoder 实例非线程安全:每个并发任务各建一个 Transcoder 实例,或串行复用;批量转换更多进程比大堆单进程更稳(堆建议 256–512MB,
jps -v核验见 5.2 节)。 - 输入源优先用 file: URI 或 InputStream:字符串路径形式的 URI 没有基准目录,SVG 内的相对引用资源会 404;Document 输入适合已经用 DOM 解析过的场景。
- 1.19 没有 KEY_XML_PARSER_CLASS_NAME:别照旧示例抄,编译必错;用
-Dorg.xml.sax.driver系统属性替代。 - 静态渲染不执行脚本:安全上省心(无需考虑脚本沙箱),但依赖 onload 的 SVG 模板经 Transcoder 输出的是初始画面。
八、参考链接
- 官方 Transcoder API 文档:https://xmlgraphics.apache.org/batik/using/transcoder.html
- Maven 中央仓库 batik-transcoder:https://central.sonatype.com/artifact/org.apache.xmlgraphics/batik-transcoder
- Maven 中央仓库 batik-codec:https://central.sonatype.com/artifact/org.apache.xmlgraphics/batik-codec
- 官方发行包下载目录:https://dlcdn.apache.org/xmlgraphics/batik/binaries/