Java生成PDF时该如何正确支持中文生僻字详解
作者:yeye19891224
——从字体原理到工程落地的完整实践指南
一、问题背景:为什么“生僻字”在 PDF 中总是出问题?
在 Java 项目中使用 iText / OpenPDF / Flying Saucer 生成 PDF,是非常常见的需求,典型场景包括:
- 合同 / 协议生成
- 电子档案
- 报表导出
- 存证类文件
但一旦涉及中文生僻字(如姓名、地名、少数民族用字、古籍用字),就很容易出现以下问题:
- PDF 中显示为
□ - 直接空白
- 只有数字、符号、序号,中文正文消失
- 本机正常,换一台机器就乱码
这些问题并不是 iText 的 bug,而是对 PDF 字体机制 + 中文字符集 理解不足导致的。
二、核心结论先行(非常重要)
Java 生成 PDF 要支持中文生僻字,本质上只取决于三件事:
- 字体是否真正包含该字符(Glyph 是否存在)
- PDF 是否使用了该字体(CSS / font-family)
- 字体是否随 PDF 一起嵌入(EMBEDDED)
只要其中任意一个不满足,生僻字一定失败。
三、为什么宋体(SimSun)无法支持生僻字?
1. SimSun 的历史定位
SimSun.ttc(宋体)是一个非常早期的中文字库,主要覆盖:
- GB2312 / GBK
- 常用汉字
但它不包含:
- CJK 扩展区 A / B / C / D / E / F
- 大量现代姓名用字(如:𠮷、𡿺)
- 古籍、少数民族汉字
2. 结论
宋体不是“坏字体”,而是“时代局限字体”
它从设计之初就没打算覆盖 Unicode 全中文字符集。
因此:
- Word 里能显示 ≠ PDF 一定能显示
- 系统里有 ≠ 所有人机器都有
四、真正支持生僻字的字体:Noto / 思源系列
1. 推荐字体
在 Java PDF 场景中,强烈推荐使用静态版本的 Noto 字体:
NotoSansCJKsc-Regular.otf
特点:
- 覆盖几乎全部 Unicode 中文字符
- 支持 CJK 扩展区
- 免费可商用(SIL OFL)
- iText / Flying Saucer 完全支持
五、常见字体后缀解析(避免踩坑)
后缀 | 含义 | 是否可用于 Java PDF |
| TrueType Font | ✅ |
| OpenType Font | ✅(推荐) |
| 字体集合 | ⚠️(易出问题) |
| 可变字体 | ❌(不支持) |
| Web 字体 | ❌ |
特别警告:Variable Font(VF)
NotoSansCJKsc-VF.ttf
iText / Flying Saucer 完全不支持,使用后常见现象是:
- 只有数字、符号
- 中文正文全部消失
- 不报错,非常迷惑
六、Java 端正确的字体加载方式
1. 必须使用IDENTITY_H
BaseFont.IDENTITY_H
作用:
告诉 PDF 使用 Unicode 编码,而不是单字节编码。
2. 强烈建议使用EMBEDDED
BaseFont.EMBEDDED
为什么?
NOT_EMBEDDED只是“引用字体名”- 是否能显示,完全依赖打开 PDF 的环境
- 换机器 / Linux / Docker 必定翻车
你现在能显示,只是环境“碰巧”有该字体
3. 标准代码示例
ITextFontResolver fontResolver = renderer.getFontResolver();
fontResolver.addFont(
"NotoSansCJKsc-Regular.otf",
BaseFont.IDENTITY_H,
BaseFont.EMBEDDED
);七、最容易被忽略的一点:HTML / CSS 才是真正的“使用字体”
1. 一个常见误区
“我在 Java 里 addFont 了,为什么中文还不显示?”
原因是:
addFont 只是“注册”,不是“使用”
2. 必须在模板中显式指定font-family
<style>
body {
font-family: "Noto Sans CJK SC";
font-size: 12pt;
}
</style>关键点:
- 使用 字体内部 family 名称
- 不是文件名
- 建议写在
body,让所有元素继承
如果不写这一步,Flying Saucer 会退回到内置 Latin 字体,中文直接丢失。
八、为什么换成 Noto 后,版式可能发生变化?
这是一个正常且不可避免的现象。
原因:
- 不同字体的字宽不同
- 行高(ascent / descent)不同
- 笔画粗细不同
因此可能导致:
- 换行点变化
- 表格溢出
- 分页变化
九、工程级应对方案(必须知道)
1. 固定行高
body {
line-height: 1.4;
}2. 表格列宽避免“卡死”
优先:
width: 25%;
避免:
width: 80px;
3. 字号微调
经验值:
原宋体字号 | Noto 建议 |
12pt | 11pt |
10.5pt | 10pt |
十、生僻字验证(上线前必做)
在模板中加入测试文本:
龘 麤 鱻 𠮷 𡿺
全部可见,才算真正支持生僻字。
十一、最终推荐的“标准方案”
生产级、稳定、可迁移的最佳实践
- 字体:
NotoSansCJKsc-Regular.otf - 编码:
IDENTITY_H - 嵌入:
EMBEDDED - CSS:
body全局font-family - 行高:显式设置
- 表格:避免极限宽度
十二、一句话总结
Java 生成 PDF 的生僻字问题,从来不是“渲染问题”,
而是“字体工程问题”。
理解了 字体覆盖范围 + PDF 字体嵌入机制 + CSS 使用方式,
这个问题可以一次性、永久性解决。
到此这篇关于Java生成PDF时该如何正确支持中文生僻字的文章就介绍到这了,更多相关Java生成PDF支持中文生僻字内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
