Apache Batik 快速入门与集成示例

Apache Batik 1.19 快速入门:Maven 与无构建工具两种依赖引入、Transcoder API 最小可运行程序把 SVG 渲染为 PNG/JPEG、完整编译运行回显、jps 堆参数核验与脚本渲染边界,全部实测可复现。

Apache BatikBatik2026/10/13

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 内嵌字体)才能保证跨机产物一致。

七、注意事项

  1. 提示键值类型是 Float:KEY_WIDTH/KEY_HEIGHT/KEY_QUALITY 传 Integer/Double 编译不过,字面量记得写 800f、0.8f。
  2. TranscoderOutput 不接受 File:传 File 直接编译错(实测 argument mismatch),用 new FileOutputStream(...) 包裹;用完必须 close。
  3. Transcoder 实例非线程安全:每个并发任务各建一个 Transcoder 实例,或串行复用;批量转换更多进程比大堆单进程更稳(堆建议 256–512MB,jps -v 核验见 5.2 节)。
  4. 输入源优先用 file: URI 或 InputStream:字符串路径形式的 URI 没有基准目录,SVG 内的相对引用资源会 404;Document 输入适合已经用 DOM 解析过的场景。
  5. 1.19 没有 KEY_XML_PARSER_CLASS_NAME:别照旧示例抄,编译必错;用 -Dorg.xml.sax.driver 系统属性替代。
  6. 静态渲染不执行脚本:安全上省心(无需考虑脚本沙箱),但依赖 onload 的 SVG 模板经 Transcoder 输出的是初始画面。

八、参考链接