在使用 PDF DO(如 iText、Apache PDFBox 或 Flying Saucer)解析或生成含中文的 PDF 时,常因未嵌入中文字体或未正确设置编码导致乱码:文字显示为方块、空格或乱码字符。根本原因在于 PDF 标准默认仅支持 Latin-1 字符集,而 GBK/UTF-8 中文需依赖 TrueType(TTF)字体嵌入与显式编码映射。常见错误包括:仅指定字体路径却未调用 `setFont()` 或 `setEmbedFont(true)`;使用系统字体(如 SimSun)但未嵌入,导致跨平台渲染失败;或解析时未配置 Unicode 解码器,致使文本提取返回乱码字符串。解决方案需三步闭环:① 选用支持 GB18030/Unicode 的开源字体(如 Noto Sans CJK、思源黑体);② 在文档创建/渲染阶段强制嵌入字体并设置 BaseFont.IDENTITY_H 编码(iText)或使用 PDType0Font(PDFBox);③ 解析文本时启用 Unicode 提取模式(如 PDFBox 的 `PDFTextStripper.setSortByPosition(true)` + 指定 `StandardCharsets.UTF_8`)。忽略任一环节均可能导致中文失效。
1条回答 默认 最新
羽漾月辰 2026-02-27 08:45关注```html一、现象层:中文 PDF 乱码的典型表征
- 生成 PDF 时中文显示为“□□□”或空白方块(字体缺失)
- 浏览器或 Acrobat 中文字错位、重叠或纵向压缩(未启用 CID 字体特性)
- 解析文本返回“锟斤拷”“”或空字符串(Unicode 解码链断裂)
- Flying Saucer 渲染 HTML→PDF 后标题正常但正文全为空格(CSS font-family 未触发嵌入)
- iText 7 中调用
font = PdfFontFactory.createFont("simhei.ttf")却未绑定到Canvas或Paragraph(隐式 fallback 失败)
二、机制层:PDF 字体模型与中文支持的本质约束
PDF 规范(ISO 32000-1)原生仅定义 Adobe Standard Latin 编码(WinAnsi),不包含 GBK/GB18030/UTF-16 映射。中文必须通过以下双轨机制实现:
技术维度 iText 7 PDFBox 2.x Flying Saucer (XR) 字体类型要求 PdfFont必须为Type0(CID)字体PDType0Font(非PDType1Font)CSS @font-face必须声明src: url(...); unicode-range: U+4E00-9FFF;编码标识关键 Identity-H(水平 CID 编码)Identity-H+toUnicode CMap依赖 XML Worker 自动注入 Identity-H映射三、实践层:三步闭环解决方案(含可运行代码片段)
- 字体选型与准备:
推荐使用 Google/Noto 项目NotoSansCJKsc-Regular.otf(覆盖 GB18030,免版权风险),存放于resources/fonts/;禁用 Windows 系统字体路径硬编码(如C:\Windows\Fonts\simsun.ttc)。 - 生成阶段强制嵌入与编码绑定:
// iText 7 示例 PdfFont font = PdfFontFactory.createFont( getClass().getResourceAsStream("/fonts/NotoSansCJKsc-Regular.otf"), PdfEncodings.IDENTITY_H, true); // ← true=嵌入,IDENTITY_H=必需! document.add(new Paragraph("你好,世界!").setFont(font)); - 解析阶段 Unicode 提取增强:
// PDFBox 示例 PDFParser parser = new PDFParser(new RandomAccessFile(file, "r")); parser.parse(); PDDocument doc = parser.getPDDocument(); PDFTextStripper stripper = new PDFTextStripper(); stripper.setSortByPosition(true); // 保持阅读顺序 stripper.setStartPage(1); stripper.setEndPage(doc.getNumberOfPages()); String text = stripper.getText(doc); // 内置 UTF-16→UTF-8 转换
四、诊断层:跨工具链的乱码根因定位流程图
graph TD A[中文乱码现象] --> B{生成还是解析?} B -->|生成失败| C[检查字体是否嵌入?] B -->|解析失败| D[检查 PDF 是否含 ToUnicode CMap?] C --> E[iText: isEmbedded() == true?] C --> F[PDFBox: font.getFontDescriptor().getFontFile2() != null?] D --> G[pdfinfo -meta input.pdf | grep -i unicode] D --> H[PDFBox: PDType0Font.load(doc, fontStream).hasToUnicodeCMap()] E -->|否| I[强制 setEmbedFont(true) / load(..., true)] F -->|否| I G -->|缺失| J[重新生成:必须嵌入+Identity-H] H -->|false| J五、进阶层:生产环境高可靠性加固策略
- 构建时校验:用
pdfcpu validate -v扫描 PDF 中所有字体是否含DescendantFonts和ToUnicode条目 - 容器化隔离:在 Alpine Linux 容器中禁用 host 字体缓存(
rm -rf /usr/share/fonts),杜绝意外 fallback - 灰度发布:对新字体版本生成
sha256sum NotoSansCJKsc-Regular.otf并写入 manifest.json,避免 CDN 缓存脏字体 - 监控埋点:在解析服务中捕获
IllegalArgumentException: No glyph for U+XXXX并上报至 Prometheus(指标名:pdf_chinese_glyph_missing_total)
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报