Apache FOP 2.11 配置参数详解
剖析 fop.xconf 顶层结构与各渲染器配置,重点实测 TrueType/TTC 字体注册、CFF 轮廓不兼容坑、triplet 匹配替换、字体缓存与 TIFF 压缩,附内存压测数据
Apache FOP 2.11 配置参数详解
一、配置体系概览
FOP 的行为由一个 XML 配置文件控制,习惯命名为 fop.xconf。三层事实需要先弄清:
| 层 | 说明 |
|---|---|
| 内置默认值 | 编译在 fop.jar 里,不提供任何配置文件时全部行为按默认值走 |
| 发行包示例 | /opt/fop/fop/conf/fop.xconf,官方注明「contains the same settings as the default values and will have no effect if used unchanged」,即内容等于默认值,仅作模板参考 |
| 用户配置 | 通过 -c cfg.xml 显式传入的文件,与默认值合并后生效 |
本文所有实验基于发行包 + 自定义配置实测,环境为 Ubuntu 22.04.5 / OpenJDK 11.0.32.1 / Apache FOP 2.11,安装目录 /opt/fop(软链 /opt/fop-2.11)。
验证配置是否被加载,最直接的办法是看启动日志——顶层元素的设置会打印 INFO 行:
Oct 14, 2026 2:02:05 PM org.apache.fop.apps.FopConfParser configure
INFO: Default page-height set to: 297mm
Oct 14, 2026 2:02:05 PM org.apache.fop.apps.FopConfParser configure
INFO: Default page-width set to: 210mm
也可以用 -x 把解析后的配置转储出来辅助排查:
cd /opt/fop/fop && ./fop -c /work/conf/fop-cjk.xconf -x
实测输出(节选):
INFO: Input mode:
INFO: not set
INFO: Output mode:
INFO: not set
二、顶层结构
fop.xconf 的骨架(发行包 conf/fop.xconf 原文,注释略):
<fop version="1.0">
<base>.</base>
<source-resolution>72</source-resolution>
<target-resolution>72</target-resolution>
<default-page-settings height="11.00in" width="8.50in"/>
<renderers>
<renderer mime="application/pdf">...</renderer>
...
</renderers>
</fop>
| 元素/属性 | 说明 | 实测备注 |
|---|---|---|
| fop@version | 配置格式版本 | 固定写 1.0;写错值会解析告警 |
| base | 相对 URL 解析基准 | 默认 .;font embed-url、fo 里引用图片的相对路径都基于它 |
| source-resolution | 输入位图/SVG 的像素密度 | 默认 72 dpi,影响 fo:external-graphic 无 intrinsic size 时换算 |
| target-resolution | 输出位图目标密度 | 默认 72 dpi;可被 CLI -dpi 覆盖 |
| default-page-settings | page-width/height 为 auto 时的兜底 | 发行包默认 8.5in×11in(US Letter),中文场景常改为 210mm×297mm(A4) |
| renderers | 按输出格式分节 | 每个 renderer 用 mime 类型区分 |
实测:把 default-page-settings 改为 A4 后启动,日志出现 Default page-height set to: 297mm / Default page-width set to: 210mm(见第一章),确认加载成功。
三、字体配置(重点)
字体是 FOP 使用中最容易踩坑的部分,本章全部结论来自容器内实测。
3.1 Base-14 内置字体与缺字行为
FOP 不配置任何字体时,PDF 使用 Adobe Base-14 字体(Times-Roman、Helvetica、Courier 及其变体),只覆盖拉丁字符。渲染中文时逐字告警且内容丢失:
WARNING: Glyph "?" (0x67d3) not available in font "Times-Roman".
WARNING: Glyph "文" (0x6587) not available in font "Times-Roman".
(0x67d3 = 染,0x6587 = 文。)规则:必须在 PDF renderer 的 fonts 节显式注册 CJK 字体。
3.2 TrueType 字体内联注册
基本写法:
<renderer mime="application/pdf">
<fonts>
<font kerning="yes" embed-url="file:///usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf">
<font-triplet name="DroidSansFallback" style="normal" weight="normal"/>
</font>
</fonts>
</renderer>
| 属性/元素 | 说明 |
|---|---|
| embed-url | 字体文件 URL,支持 file:// 绝对路径;省略该属性则字体度量用于排版但不嵌入 PDF(读者机器没装该字体就显示不出来) |
| kerning | 是否启用 kerning 对,中文场景无感 |
| font-triplet@name | FO 里 font-family 要写的名字(自定义,与文件内部名无关) |
| font-triplet@style | normal / italic / oblique / backslant |
| font-triplet@weight | normal(400) / bold(700) 或 100~900 |
同一物理字体文件可以挂多个 triplet。实测验证嵌入结果(pdffonts 属 poppler-utils):
name type encoding emb sub uni
EAAAAB+DroidSansFallback CID TrueType Identity-H yes yes yes
emb=yes 表示已嵌入,Identity-H 是 CID 编码,uni=yes 表示带 ToUnicode 映射(文本可复制检索)。
3.3 TTC 字体集合与 sub-font 属性
Ubuntu 的 fonts-wqy-zenhei 包提供 wqy-zenhei.ttc(TrueType Collection,内含 3 个字体)。TTC 必须用 sub-font 属性指定集合成员,值是字体在集合内的内部名:
<font kerning="yes" embed-url="file:///usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"
sub-font="WenQuanYi Zen Hei">
<font-triplet name="WQYZenHei" style="normal" weight="normal"/>
</font>
实测渲染成功后 pdffonts 显示:
EAAAAA+WenQuanYiZenHei CID TrueType Identity-H yes yes yes
sub-font 写错的报错非常直白,例如对 fonts-noto-cjk 的 TTC 写 sub-font="Noto Serif CJK SC"(实际内部名是 Noto Serif CJK SC Bold):
SEVERE: Failed to read font file file:///usr/share/fonts/opentype/noto/NotoSerifCJK-Bold.ttc
Name does not exist in the TrueType collection: Noto Serif CJK SC
排查子字体名可用 FOP 的自报告:加载 TTC 时它会打印集合成员清单到 INFO 日志:
INFO: This is a TrueType collection file with 5 fonts
INFO: Containing the following fonts:
INFO: Noto Serif CJK JP Bold
INFO: Noto Serif CJK KR Bold
INFO: Noto Serif CJK SC Bold
...
注意内部名带样式后缀:Regular TTC 的成员是 Noto Serif CJK SC,Bold TTC 的成员是 Noto Serif CJK SC Bold。
3.4 CFF 轮廓字体是硬伤(fonts-noto-cjk 不可用)
Ubuntu 22.04 的 fonts-noto-cjk 包(NotoSerifCJK-.ttc / NotoSansCJK-.ttc)是 OpenType/CFF 轮廓(sfnt 容器里是 CFF 表而非 glyf 表)。FOP 2.11 的 TrueType 加载器读不了它,注册后渲染直接失败:
SEVERE: Failed to read font file file:///usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc
'loca' table not found, happens when the font file doesn't contain TrueType outlines (trying to read an OpenType CFF font maybe?)
实测结论(用 python 解析 sfnt 表验证过两个 ttc 都是 CFF):
| 字体来源 | 轮廓格式 | FOP 2.11 PDF 渲染 |
|---|---|---|
| fonts-noto-cjk(*.ttc) | CFF | 失败('loca' not found) |
| fonts-wqy-zenhei(wqy-zenhei.ttc) | TrueType/glyf | 成功,中文正常嵌入 |
| fonts-droid-fallback(DroidSansFallbackFull.ttf) | TrueType/glyf | 成功,但该字体只有 CJK 字形 |
可用字体的判断标准:fc-list 能看到只是入场券,轮廓必须是 TrueType(glyf 表)。文泉驿正黑是 Ubuntu 下最省心的选择,一个字体同时覆盖中文与拉丁字符。
3.5 CJK 专属字体缺拉丁字形
Droid Sans Fallback 是纯 CJK 字体,拉丁字符也走它时会缺字:
WARNING: Glyph "D" (0x44, D) not available in font "DroidSansFallback".
WARNING: Glyph "r" (0x72, r) not available in font "DroidSansFallback".
方案:正文用覆盖面广的字体(文泉驿正黑),或为拉丁与 CJK 分别注册并在 FO 里用 font-family 列表声明回退顺序。
3.6 triplet 匹配与替换行为
FO 里的 font-family/font-weight/font-style 组合必须命中已注册的 triplet,否则替换为默认字体并告警。实测 FO 中写 font-weight="bold" 但只注册了 weight="normal" 的 triplet:
WARNING: Font "WQYZenHei,normal,700" not found. Substituting with "WQYZenHei,normal,400".
PDF 照常生成,只是粗体被弱化为同字重的伪加粗(FOP 用描边模拟或直接不加粗,取决于渲染器)。工程实践:同一 ttc/ttf 给 normal 和 bold 各挂一个 triplet(可指向同一物理文件或对应的 Bold 文件):
<font kerning="yes" embed-url="file:///usr/share/fonts/truetype/wqy/wqy-zenhei.ttc" sub-font="WenQuanYi Zen Hei">
<font-triplet name="WQYZenHei" style="normal" weight="normal"/>
<font-triplet name="WQYZenHei" style="normal" weight="bold"/>
</font>
3.7 连字符替换告警
CJK 字体通常没有 U+002D 连字符字形,启用断词时会出现:
WARNING: Substituted specified hyphenation character (0x2d) with 0x20 because the font doesn't have the specified hyphenation character: DroidSansFallback,normal,400
无功能影响,可忽略或换字体消除。
3.8 auto-detect 自动探测
fonts 节写 <auto-detect/> 时,FOP 扫描平台字体目录并按 fontconfig 名注册全部字体:
<fonts>
<auto-detect/>
</fonts>
优点是零维护;缺点是 triplet 名由字体内部名决定(如 font-family="WenQuanYi Zen Hei"),且系统装了 CFF 字体(如 Noto)后仍会触发 3.4 的加载失败。生产环境建议显式注册而非 auto-detect。
3.9 字体缓存
FOP 解析字体文件的开销不小,度量结果缓存在 $HOME/.fop/fop-fonts.cache:
ls -l /root/.fop/
实测输出:
-rw-r--r-- 1 root root 2105 Oct 14 14:04 fop-fonts.cache
| 选项 | 作用 |
|---|---|
| -cache 路径 | 指定缓存文件/目录位置(无家目录的 CI 环境必备) |
| -flush | 清空当前字体缓存 |
换字体文件后若排版结果诡异,先 -flush 再试。
3.10 metrics-url 与老式注册
<font metrics-url="arial.xml" embed-url="arial.ttf"> 是 FOP 1.x 时代用 TTFReader 预生成度量 XML 的写法,2.x 仍兼容(发行包 fop.xconf 注释里有示例),新部署直接用 embed-url 即可,无需预生成。
四、PDF 渲染器配置
4.1 filterList:PDF 流压缩
<renderer mime="application/pdf">
<filterList>
<value>flate</value>
<!-- <value>ascii-85</value> -->
<!-- <value>ascii-hex</value> -->
</filterList>
</renderer>
| 滤镜 | 说明 | 实测建议 |
|---|---|---|
| flate | zlib 压缩 | 默认开启,保持即可 |
| ascii-85 | 二进制转 ASCII(约 4:5 膨胀) | 仅兼容极老设备时开 |
| ascii-hex | 十六进制编码(体积翻倍) | 不建议 |
4.2 xml-handler:内嵌 SVG 的行为
<xml-handler namespace="http://www.w3.org/2000/svg">
<stroke-text>false</stroke-text>
</xml-handler>
stroke-text=false(默认)时 SVG 文本以文本对象写入 PDF(可选中复制),true 则描边成路径。
五、位图渲染器配置
5.1 TIFF 压缩
TIFF 渲染器默认不压缩,A4 文本页实测一张 2,010,936 字节;配置 CCITT T.6 后同一页仅 12,214 字节(约 165 倍差距):
<renderer mime="image/tiff">
<compression>CCITT T.6</compression>
</renderer>
| compression 取值 | 说明 |
|---|---|
| CCITT T.6 | 传真级二值压缩,黑白文本文档首选 |
| CCITT T.4 | 老式传真压缩 |
| PackBits / Deflate / LZW | 通用无损 |
5.2 PNG / TIFF 透明背景
<renderer mime="image/png">
<transparent-page-background>true</transparent-page-background>
</renderer>
默认 false(白底),true 时不绘制页面背景,便于叠加合成。
5.3 target-resolution 与 -dpi
renderer 节的 <target-resolution>300</target-resolution> 与命令行 -dpi 300 等效(CLI 优先)。实测同一 .fo 渲染 PNG:默认 72 dpi 时 14,312 字节,-dpi 300 时 98,728 字节。位图批产建议在配置里固化目标 dpi,避免每次传参。
六、PS / AFP 渲染器要点
| renderer | 关键配置 | 说明 |
|---|---|---|
| application/postscript | auto-rotate-landscape | true 时横向页强制旋转输出 |
| application/x-afp | images mode/bits-per-pixel、renderer-resolution、resource-group-file、afp-font 字体表 | AFP 面向大型机打印,发行包 fop.xconf 内置完整 Times/Helvetica/Courier raster 字体映射表,可直接参考 |
七、运行时选项与内存
配置文件之外,CLI 还有 -conserve(内存保守策略,序列化 area tree 换取内存)。实测 300 页 A4 纯文本文档(Ubuntu 22.04 / OpenJDK 11 / 默认堆):
| 场景 | 命令 | 耗时 | 峰值 RSS |
|---|---|---|---|
| 默认 | fop big.fo -pdf out.pdf | 1.95s | 235,796 KB |
| 保守内存 | fop -conserve big.fo ... | 1.90s | 234,624 KB |
| 限 64MB 堆 | FOP_OPTS="-Xmx64m" fop ... | 1.85s | 193,068 KB |
简单文本文档下三者都能跑完;含大图、复杂表格的文档才需要 -conserve 与加大堆。JVM 堆通过环境变量 FOP_OPTS 传递(见安装篇 6.2)。
八、编码与国际化
- 配置文件与 .fo 文件本体统一 UTF-8(XML 声明 encoding="UTF-8"),文件内容不受系统 locale 影响。
- 命令行参数受 locale 影响:POSIX locale 下
-param传中文会变问号(实测确认),解决方法是export LANG=C.UTF-8。 -nocs关闭复杂文字布局(complex script),对阿拉伯语等 RTL 场景有影响,中文不需要动它。- 断词(hyphenation)需要对应语言的 hyphenation pattern(发行包 fop-hyph.jar 内置常用语言)。
九、常见问题
9.1 报 Name does not exist in the TrueType collection
sub-font 值与集合内部名不完全一致(注意样式后缀 Bold/Regular)。用加载时的 INFO 日志清单核对(3.3)。
9.2 报 'loca' table not found
字体是 CFF 轮廓(如 fonts-noto-cjk),FOP 2.11 的 PDF 渲染器无法使用,换 TrueType 轮廓字体(3.4)。
9.3 粗体没效果且有 WARNING: Font ...,normal,700 not found
triplet 没覆盖 bold 字重,按 3.6 补注册。
9.4 换了字体文件渲染结果没变
字体缓存未失效,fop -flush 后重试(3.9)。
9.5 auto-detect 后中文还是缺字
auto-detect 按内部名注册,FO 里的 font-family 必须写 fontconfig 名(如 WenQuanYi Zen Hei);且若系统只有 CFF 字体同样会失败。生产环境建议显式注册(3.8)。
9.6 PDF 里中文可复制但显示为方块
字体嵌入了(emb=yes)但查看器用了替代字体渲染,或 uni 映射缺失。用 pdffonts 检查 emb/sub/uni 三列应全为 yes(3.2)。
9.7 页面尺寸不对(US Letter 而非 A4)
default-page-settings 默认 8.5in×11in;要么改配置,要么在 FO 的 simple-page-master 里显式写 page-width/page-height(后者优先级更高)。
9.8 相对路径图片加载失败
检查 base 元素:相对 URL 基于它解析,cron/systemd 环境下工作目录与调试时不同,建议 base 写绝对路径。