有次中文站上线后,网页订单、邮件都正常,只有后台下载的发票 PDF 里,商品名和地址变成一排方框。最初有人在主题 CSS 里加了 font-family: Microsoft YaHei,当然没有任何变化——Magento 的原生销售 PDF 不是把 HTML 交给浏览器打印,而是代码一行一行把文字画到 PDF 页面上。
先分清“缺字形”还是“编码坏了”
用 PDF 阅读器复制那串方框,粘贴到纯文本里:
- 粘贴后能看到正确中文,多半是字体不包含中文字形;
- 粘贴后也是乱码,要继续检查进入 PDF 前的字符串编码和自定义模块处理;
- 只有某些生僻字是方框,说明字体覆盖范围不完整,不是整个流程都坏了。
再做一个最小样本:客户名、地址、商品名分别放常用中文、数字和英文。这样能看出是所有绘制位置都用了错误字体,还是只有商品表格的 renderer 没换。
修复位置在 PDF 绘制代码,不在前台主题
我会做一个小模块扩展发票 PDF 的字体选择,让正文、粗体和必要的标题都使用服务器上可读取、允许嵌入并包含 CJK 字形的字体文件。不要直接改 vendor,升级后一定会丢;也不建议用一个 preference 粗暴复制整个核心 PDF 类,后续版本改分页或金额布局时很难跟。
更稳妥的做法是把“创建字体”集中成一个服务,再在实际调用 drawText 前切换字体。伪代码大致是:
$font = $fontFactory->fromPath(
$moduleDir . '/view/base/web/fonts/YourCjkFont.ttf');
$page->setFont($font, 9);
$page->drawText($text, $x, $y, 'UTF-8');具体 API 会随项目使用的 PDF 库和 Magento 版本不同,不能照抄一行就上线。重点是:传入的字体对象必须真的嵌入 PDF,并且每个绘制中文的 renderer 都走到它。
字体选好了,布局还可能坏
CJK 字体通常比内置西文字体大得多,字宽也不同。换字体后我会专门看三件事:
- 长中文商品名是否越过数量和金额列;
- 两行地址换页时有没有压住页脚;
- 粗体标题是否因为没有对应字重,又退回缺字的字体。
如果项目只是为了十几个固定中文标签,盲目嵌入完整大字库会让每张发票膨胀很多。可以选择允许子集嵌入的字体和实现,但要确认所用 PDF 库是否真的做子集化。不要随便从本机复制商业字体到服务器,字体授权也属于上线检查项。
缓存清理不是核心,但部署路径要一致
字体文件随模块发布后,要确认生产节点都包含同一文件、PHP 进程有读取权限,编译后的类确实是新版本。只在一台节点放字体会造成“有时正常、有时方框”的诡异现象。
最后用发票、发货单、贷项通知单各导出一份,因为它们可能共享部分抽象类,却有不同 item renderer。再用至少两种 PDF 阅读器打开并实际复制文字。能显示只是第一关,能搜索、复制,打印时也不丢字,才算这次字体修复完成。

