mpdf是ThinkPHP多语言PDF生成最稳选择,因其原生支持UTF-8、自动语言识别(autoScriptToLang/autoLangToFont)、内置中文字体映射,且对中文、日文、阿拉伯文等复杂字形渲染稳定,远超TCPDF和Dompdf。

mpdf 是目前 ThinkPHP 项目里生成多语言 PDF 最稳的选择,尤其对中文、日文、阿拉伯文等双向或复杂字形支持远超 TCPDF 和 Dompdf。直接用 TCPDF 渲染中文,90% 的概率会乱码或字体缺失——不是配置错,是它底层字体子集机制不兼容中文字体全量嵌入。
为什么 mpdf 比 TCPDF 更适合 ThinkPHP 多语言 PDF
ThinkPHP 默认不带 PDF 能力,必须靠第三方库;而 mpdf 在 v7+ 版本起原生启用 autoScriptToLang 和 autoLangToFont,能自动识别中文段落并切换到已注册的中文字体,不用手动切字体或写一堆 <span style="font-family: simsun">。
TCPDF 虽然也能加中文字体(比如用 addTTFfont() 注册 simhei.ttf),但它不自动判断语言块,你得自己把 HTML 中每段中文都包进 <span> 并指定字体,稍一遗漏就回退成方框。
-
mpdf支持 CSS@page、break-before、页眉页脚变量(如{PAGENO})且渲染稳定 -
TCPDF的writeHTML()对含float、display: grid或嵌套<table>的 HTML 容易错位,而mpdf能更好还原结构 -
Dompdf加载远程图片需显式开isRemoteEnabled,且中文字体必须用load_font.php预编译,流程更重
mpdf 在 ThinkPHP6/7 中的最小可用配置
别碰 vendor 目录下的 fonts 文件夹手动复制字体——mpdf v8.1+ 已内置 dejavusans 和 cid0cs(支持中文),只要初始化时打开自动映射即可:
$mpdf = new \Mpdf\Mpdf([
'mode' => 'utf-8',
'format' => 'A4',
'margin_left' => 15,
'margin_right' => 15,
'margin_top' => 20,
'margin_bottom' => 25,
'autoScriptToLang' => true,
'autoLangToFont' => true
]);
关键点:
立即学习“PHP免费学习笔记(深入)”;
- 去掉
'mode' => 'c'这类旧版写法,v8+ 只认'utf-8' - 不要调
$mpdf->SetFont('simhei')——自动模式下手动设字体反而会覆盖语言检测逻辑 - HTML 中避免用
font-family内联样式干扰自动匹配,除非你明确要局部换字体 - 如果导出后仍有个别字显示为方框,检查该字是否在 Unicode 基本多文种平面(BMP)之外(比如某些 emoji 或生僻汉字),
mpdf默认不加载扩展区字体
ThinkPHP 控制器里导出 PDF 的实操写法
别把 HTML 拼在控制器里。用 ThinkPHP 的模板引擎先渲染 HTML 字符串,再喂给 mpdf:
$html = view('report/pdf_template', $data)->render();
$mpdf->WriteHTML($html);
$mpdf->Output('report_' . date('Ymd') . '.pdf', 'D');
注意几个硬坑:
-
view()->render()返回的是 UTF-8 字符串,但若模板里用了<meta charset="gbk">,会导致mpdf解析失败——模板里一律删掉 charset meta 标签 - 图片路径必须是绝对路径(
https://或服务器本地完整路径如/var/www/html/public/uploads/xxx.png),相对路径(./images/logo.png)会 404 - 如果 PDF 里表格列宽异常,别用
width: 20%,改用固定像素值(width: 120px)或table-layout: fixed+width: 100% -
Output()第二个参数:'I'是 inline 浏览器打开,'D'是强制下载,'F'是保存到服务器;生产环境别用'I',部分浏览器会拦截 PDF 弹窗
中文表格、页眉页脚和分页的实际处理
mpdf 的页眉页脚支持 HTML,但只接受纯文本或极简标签(<b>、<div>),不能嵌 CSS 类名或 JS:
$mpdf->SetHeader('<div style="text-align:right;font-size:10pt;">{DATE j-m-Y} | 报表系统</div>');
$mpdf->SetFooter('{PAGENO}/{nb}');
表格跨页断裂是高频问题,解决方式很具体:
- 给
<table>加style="page-break-inside: avoid;",但仅对整张表生效,不保证每行不断裂 - 真正可控的方式是:用
<thead>包住表头,并设置$mpdf->setAutoTopMargin = 'stretch';,这样每页顶部都会重复表头 - 如果某行内容过长导致撑破页面,用
white-space: normal替代nowrap,并限制单元格最大高度(max-height: 60px; overflow: hidden) - 避免在表格内使用
colspan超过 3 列,mpdf对复杂合并单元格的宽度计算容易失准
mpdf 的自动语言映射省掉 70% 字体配置工作,但它的分页逻辑和表格渲染仍依赖你对 HTML 结构的克制——越接近语义化、越少依赖 CSS 布局技巧,PDF 越稳。



















