根本原因是mpdf默认不加载中文字体且未配置字体路径和编码;须显式配置fontDir、fontData(含useOTL/useKashida),选用Noto Sans CJK SC等跨平台字体,禁用CSS font-family回退,PDF模板需内联样式、手动分页、调优内存与超时设置。

为什么 mpdf 生成的中文全是方块或问号
根本原因不是 ThinkPHP,而是 mpdf 默认不加载中文字体,且未正确配置字体路径和编码。它默认只认 Latin 字符集,哪怕你用了 UTF-8 编码的 HTML,只要没告诉 mpdf “这个字体支持中文”,就必然乱码。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 必须在
config/mpdf.php(或初始化时)显式配置fontDir和fontData,不能只改default_font -
fontDir要指向真实可读的本地路径(如public/fonts/),不是 URL;fontData中每个字体项必须含useOTL和useKashida配置,否则部分汉字仍会丢形 - 推荐直接用 Noto Sans CJK SC 或思源黑体,避免用 Windows 自带的“微软雅黑”——它在 Linux 服务器上根本不存在
- HTML 中不要用
<style>body{font-family: "Microsoft YaHei"}</style>这种写法,mpdf不解析 CSS font-family 回退链,只认你fontData里注册过的字体名
ThinkPHP 6.x 中如何正确初始化 Mpdf 实例
别在控制器里 new Mpdf() 然后硬塞一堆参数——容易漏掉 tempDir 权限、mode 兼容性、format 单位等关键项,导致导出失败或 PDF 格式错乱。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 通过 ThinkPHP 的依赖注入容器统一管理,新建
app/service/PdfService.php,构造函数里初始化Mpdf实例并预设好中文支持 - 必须设置
tempDir指向可写目录(如runtime/mpdf/),否则并发导出时会报Unable to create output stream -
mode推荐用'c'(兼容模式),比'utf-8'更稳;format别写'A4',改用[595.28, 841.89](点单位),避免某些打印机驱动解析异常 - 如果要加页眉页脚,别用
SetHeader()直接塞 HTML,先调用WriteHTML()渲染内容,再用SetHTMLHeader()注入已编译的 HTML 片段,否则样式丢失
$mpdf->WriteHTML() 渲染动态数据时为何样式失效或布局错乱
WriteHTML() 不是浏览器,不执行 JS、不重排版、不支持 Flex/Grid 布局,连 position: sticky 都直接忽略。你传进去的 Blade 模板若含 Tailwind 类或复杂 CSS,大概率白忙活。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- PDF 模板必须用内联样式,CSS 类全部展开,例如把
class="text-center font-bold"改成style="text-align:center;font-weight:bold;" - 表格用
<table border="1">,别依赖 CSS 边框;宽度统一用width="100%"或固定像素值,百分比在某些mpdf版本中计算错误 - 动态数据插入前,对变量做
htmlspecialchars($data, ENT_NOQUOTES, 'UTF-8'),否则单引号、&可能破坏 HTML 结构 - 避免在 HTML 中写
<img src="data:image/png;base64,...">,大图 base64 会导致内存溢出;改用src="/public/images/report-logo.png"并确保mpdf能读取该路径
导出大报表时内存爆了或超时怎么办
ThinkPHP 默认的响应生命周期 + PHP 的 memory_limit + mpdf 自身缓存机制,三者叠加很容易在渲染 50+ 行表格时触发 Fatal error: Allowed memory size exhausted 或 Maximum execution time exceeded。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在 PDF 生成前调用
ini_set('memory_limit', '512M')和set_time_limit(300),但仅限当前请求,别全局改 - 分页处理:用
$mpdf->AddPage()手动切页,每 30 行数据调一次,别指望mpdf自动分页——它对thead重复逻辑支持极弱 - 关闭调试:确保
APP_DEBUG = false,否则 ThinkPHP 日志写入会拖慢生成速度;同时禁用mpdf的 debug 输出(debug => false) - 最后一步输出前,调用
$mpdf->cleanOutputBuffers(),清除可能残留的 ob 缓存,否则 PDF 头部混入空白字符会提示“Failed to load PDF document”


















