在SpringBoot中使用Apache POI(如POI-OOXML)生成Excel后,再通过iText、Flying Saucer或Apache PDFBox等工具转PDF时,常出现中文乱码——根本原因在于:POI本身不直接生成PDF,而中间转换环节(如HTML→PDF或XSL-FO→PDF)未正确配置中文字体。典型场景是用XSSFSheet导出为HTML再用Flying Saucer渲染PDF时,CSS未指定支持中文的字体(如"SimSun", "Noto Sans CJK SC"),且未注册对应TrueType字体文件(如simhei.ttf)到ITextRenderer或FontResolver;或iText7中未显式设置PdfFont并绑定中文字体路径。此外,SpringBoot默认字符集为UTF-8,但部分旧版工具链(如iText5)若未设置BaseFont.IDENTITY_H或未加载CMap,仍会回退至默认拉丁字体。解决关键:统一编码声明 + 显式注册可嵌入的中文字体 + 确保HTML/CSS与PDF渲染器字体链一致。
1条回答 默认 最新
小小浏 2026-05-16 23:50关注```html一、现象层:中文乱码的典型表现与触发场景
- SpringBoot应用调用
XSSFWorkbook或XSSFSheet导出Excel后,经HTML中间格式(如Workbook#write(OutputStream)转HTML字符串)再交由Flying Saucer渲染PDF,中文全部显示为方框(□)或空格; - iText7生成PDF时抛出
IllegalArgumentException: Font 'SimSun' not found或静默回退至Helvetica导致乱码; - Apache PDFBox 3.x中使用
PDPageContentStream#showText()写入中文时输出空白,日志提示Cannot find a glyph for '你' in font Helvetica; - 同一套UTF-8编码的HTML模板,在浏览器中正常显示中文,但Flying Saucer(via
ITextRenderer)生成PDF后全乱码。
二、机制层:字体链断裂的三大技术断点
中文PDF渲染失败本质是字体解析路径中断,下图展示典型转换链路中的关键断点:
graph LR A[POI-XSSF Excel] --> B[HTML String
(含table/tr/td)] B --> C[Flying Saucer
CSS解析引擎] C --> D[iTextRenderer
FontResolver] D --> E[PDF字形嵌入] E --> F[最终PDF文档] subgraph 断点区 B -.未声明charset及font-family.-> C C -.CSS无@font-face或font-family: “Noto Sans CJK SC”.-> D D -.未注册simhei.ttf/NotoSansCJKsc-Regular.otf.-> E end三、配置层:统一编码与字体注册的强制规范
工具链 必需配置项 示例代码片段 Flying Saucer + iText7 ① HTML meta charset=utf-8
② CSS @font-face + font-family
③FontResolver注册TTFrenderer.getFontResolver().addFont(
"fonts/NotoSansCJKsc-Regular.otf",
BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);iText7 直接生成 ① PdfFontFactory.register()
② 显式传入PdfFont到ParagraphPdfFont font = PdfFontFactory.
register("fonts/simhei.ttf");
new Paragraph("你好世界").setFont(font);四、工程层:SpringBoot生产环境字体治理方案
- 字体资源标准化:将
NotoSansCJKsc-Regular.otf(Google开源、免版权、全CJK覆盖)置于src/main/resources/fonts/,禁止使用Windows系统字体(如simsum.ttf)——因Linux容器中不可用; - 自动字体探测与注册:编写
@PostConstruct初始化Bean,扫描classpath:/fonts/**/*.ttf并批量注册至iText7全局FontProvider; - CSS模板强约束:在Thymeleaf HTML模板头部注入:
<style> body { font-family: "Noto Sans CJK SC", sans-serif; } @font-face { font-family: "Noto Sans CJK SC"; src: url('fonts/NotoSansCJKsc-Regular.otf') format('opentype'); } </style> - 字符集防御性声明:所有HTTP响应头添加
Content-Type: text/html;charset=UTF-8,且HTML中显式声明<meta charset="UTF-8">;
五、验证层:可落地的端到端测试清单
- ✅ 使用
curl -I http://localhost:8080/export/pdf确认响应头含Content-Type: application/pdf且无charset歧义; - ✅ 用
pdfinfo output.pdf | grep "Fonts"验证PDF内嵌字体列表含NotoSansCJKsc-Regular; - ✅ 在Adobe Acrobat Pro中打开PDF → 文件 → 属性 → 字体,确认每个中文字符均映射至已嵌入的CJK字体;
- ✅ 编写JUnit5集成测试:加载生成PDF → 用
PDFBox TextStripper提取文本 → 断言包含“测试中文内容”; - ✅ Docker容器化部署后,在Alpine Linux镜像中执行
fc-list :lang=zh确认无系统字体依赖。
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报- SpringBoot应用调用