Apache FOP 是基于 Java 的 PDF 生成引擎,需先将 XML 用 XSLT 转为合规 XSL-FO(含 fo:root、正确命名空间等),再交由 FOP 渲染;不支持直接 XML+XSLT 一步生成,且对 XSL-FO 1.0/1.1 子集支持较好,不支持 2.0 新特性。
Apache FOP 是一个开源的、基于 Java 的 PDF 生成引擎,它把符合 XSL-FO 规范的 XML 文档(即 .fo 文件)渲染成 PDF、PostScript、AFP 等格式。它不
直接处理任意 XML —— 你必须先用 XSLT 把原始 XML 转成 XSL-FO,再交给 FOP 渲染。
FOP 本身不执行 XSLT;它只消费已转换好的 XSL-FO。常见错误是以为 fop -xml data.xml -xsl style.xsl -pdf out.pdf 能一步到位,但实际中:如果 style.xsl 输出不是严格合规的 XSL-FO(比如漏了 fo:root、用了非标准命名空间、或没声明 xmlns:fo="http://www.w3.org/1999/XSL/Format"),FOP 会报类似 org.apache.fop.fo.ValidationException: Missing root element 'fo:root' 的错误。
开头fo: 前缀,且在正确命名空间下fo:table-column 的 column-width 自适应算法)分两步最可控:先 XSLT 转 FO,再用 FOP 渲染。推荐用 saxon 或 xsltproc 做第一步,避免 FOP 内置 XSLT 引擎(默认使用 Apache Xalan)的兼容性问题。
saxon -s:data.xml -xsl:transform.xsl -o:output.fo
fop -fo output.fo -pdf result.pdf
saxon -s:data.xml -xsl:transform.xsl | fop -fo - -pdf result.pdf
userconfig.xml;中文乱码通常是因为没配好 font metrics 或未启用 embed-fonts
直接调用 FOP API 比命令行更灵活,但也更容易踩内存和线程坑。核心是 FopFactory 和 Fop 实例的复用策略。
FopFactory 是重量级对象,应全局单例或池化,不要每次渲染都新建Fop 实例是轻量级、线程不安全的,每次转换需新建OutputStream 和 Fop 关联的流,否则 PDF 可能截断或损坏FopFactory fopFactory = FopFactory.newInstance(new File("fop.xconf"));
FOUserAgent userAgent = fopFactory.newFOUserAgent();
OutputStream out = new FileOutputStream("out.pdf");
try (Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent, out)) {
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(new StreamSource("transform.xsl"));
Source src = new StreamSource("data.xml");
Result res = new SAXResult(fop.getDefaultHandler());
transformer.transform(src, res);
}真正难的不是语法,而是 XSL-FO 的盒模型细节:页边距继承规则、fo:block-container 的绝对定位限制、表格跨页断裂行为、以及中文字体嵌入时的 metrics-url 路径解析失败——这些不会报编译错,但会让 PDF 在某一页突然空白或文字堆叠。调试时优先用 fop -fo input.fo -pdf - 输出到 stdout 查看是否生成有效 PDF 流,再逐段注释 FO 片段定位问题。