1. 项目背景与问题定位
在Web开发中动态生成PDF文档时,pdfmake作为一款流行的JavaScript库,因其简洁的API和强大的功能被广泛使用。但许多开发者首次尝试输出中文内容时,都会遇到令人头疼的乱码问题——文档中的中文全部显示为方框或空白。这本质上是因为pdfmake默认仅内置了西文字体(Roboto),缺乏对CJK字符集的支持。
关键点:pdfmake通过虚拟文件系统(VFS)加载字体,默认配置不包含中文字体文件,导致渲染引擎无法找到对应的字形数据。
2. 解决方案技术解析
2.1 字体打包核心原理
pdfmake采用base64编码将字体文件内联到vfs_fonts.js中。该文件本质上是一个JavaScript对象,键名对应字体文件名,键值为字体文件的base64编码字符串。例如:
javascript复制this.pdfMake = this.pdfMake || {};
this.pdfMake.vfs = {
"hyzjh_zh.ttf": "AAEAAAASAQA...", // 实际为长base64字符串
"hylxt_bold.ttf": "AAEAAAASAQA..."
};
2.2 中文字体选型建议
选择字体时需考虑:
- 文件体积:完整中文字体通常10MB+,推荐使用精简版(如文泉驿微米黑)
- 版权合规:商用需确认授权(思源黑体可免费商用)
- 样式匹配:需包含normal/bold/italics三种基础样式
实测数据对比:
| 字体名称 | 文件体积 | 字符覆盖率 |
|---|---|---|
| 思源黑体完整版 | 12.8MB | 99.9% |
| 文泉驿微米黑 | 3.2MB | 95% |
| 方正仿宋 | 8.4MB | 98% |
3. 完整实现步骤
3.1 环境准备与工具链
- 安装Node.js(建议v14+)
- 克隆字体生成工具仓库:
bash复制git clone https://github.com/tekintian/vfs_fonts.js-build-tools.git
cd vfs_fonts.js-build-tools
3.2 字体文件处理
- 将下载的中文字体(如
hyzjh_zh.ttf)放入fonts目录 - 删除不需要的默认字体(如Roboto系列)
- 执行构建命令:
bash复制npm install
npm run build
3.3 项目集成配置
在pdfmake初始化代码中注册字体:
javascript复制pdfMake.fonts = {
Hyzh: {
normal: 'hyzjh_zh.ttf',
bold: 'hylxt_bold.ttf',
italics: 'hyzjh_zh.ttf',
bolditalics: 'hylxt_bold.ttf'
}
};
// 使用示例
const docDefinition = {
content: '中文内容测试',
defaultStyle: {
font: 'Hyzh'
}
};
4. 深度优化方案
4.1 字体子集化进阶
使用fonttools提取仅需的字符集:
bash复制pyftsubset SourceHanSansCN-Regular.ttf \
--text-file=used_chars.txt \
--output-file=font_subset.ttf
4.2 动态字体加载
对于大型应用,可按需加载字体包:
javascript复制async function loadFonts() {
const response = await fetch('/fonts/vfs_zh.json');
pdfMake.vfs = await response.json();
}
5. 常见问题排查
5.1 乱码现象诊断表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 部分中文显示为方框 | 字体缺少对应字形 | 更换更全的字库 |
| 全部中文无显示 | 字体未正确注册 | 检查pdfMake.fonts配置 |
| 控制台报404错误 | vfs文件路径错误 | 确认vfs_fonts.js加载路径 |
5.2 性能优化技巧
- 使用WOFF2格式字体(比TTF小30%)
- 启用HTTP/2的服务器推送字体资源
- 对于固定内容PDF,考虑服务端预生成
6. 工程化实践建议
- 建立字体管理目录结构:
code复制assets/
fonts/
zh/
regular.ttf
bold.ttf
en/
Roboto-Regular.ttf
scripts/
build-fonts.js
- 自动化构建脚本示例:
javascript复制const fs = require('fs');
const { execSync } = require('child_process');
function buildFonts() {
// 清空输出目录
execSync('rm -rf dist/fonts');
// 子集化处理
execSync('pyftsubset src/fonts/zh.ttf --output-file=temp.ttf');
// 生成vfs
execSync('node scripts/build-vfs.js temp.ttf > dist/vfs_fonts.js');
// 清理临时文件
fs.unlinkSync('temp.ttf');
}
通过以上方案,我们不仅解决了中文乱码问题,还构建了一套可持续维护的PDF生成字体管理体系。实际项目中,建议将字体打包流程纳入CI/CD流水线,确保每次部署都能自动生成最新的字体资源包。
