PHP生成PDF应单一选用tcpdf、dompdf或mpdf之一,禁用共存——因三者共享全局字体缓存与CSS解析器,易致字体冲突、中文乱码、性能骤降;须按场景锁死版本并隔离配置。

不需要集成多个 PDF 生成引擎,也别用 Composer 管理“输出组件”这种模糊概念——这会直接导致依赖冲突、内存泄漏和生成结果不一致。
为什么 tcpdf、dompdf、mpdf 不该共存于同一项目
它们都重写了 HTML→PDF 的渲染管线,共享全局状态(比如字体缓存、CSS 解析器单例),哪怕只 require_once 一个类,就可能污染另一个引擎的输出。常见现象包括:
-
DOMPDF_Exception: Font family "dejavu" not found(dompdf被mpdf的字体注册覆盖) - 中文乱码且无法通过
set_font()修复(不同引擎对TTF子集嵌入逻辑互斥) - 生成速度下降 3 倍以上(三个引擎各自加载完整字体/图像解码栈)
按场景选一个引擎,然后锁死版本
不是“功能多就好”,而是看谁最贴合你的真实输出需求:
- 要导出带复杂 CSS Grid / Flex 布局的报表?用
dompdf2.0+(但必须禁用remote_file_allowed,否则有 SSRF 风险) - 要填表单、加数字签名、控制分页精确到厘米?
tcpdf是唯一选择,但得手动处理 UTF-8 中文——别碰它的addTTFfont(),改用addFont()加载已子集化的.php字体描述文件 - 要兼容 Laravel Blade 模板且支持 @page rules?
mpdf8.1 是目前最稳的,但注意它默认开启autoScriptToLang,会导致简体中文被转成繁体
Composer 只管安装,别让它“管理组件生命周期”
Composer 是依赖安装器,不是运行时调度器。所谓“动态切换引擎”只能靠代码层隔离:
- 每个引擎封装成独立 Service 类,构造函数里不做任何初始化(比如不调
new TCPDF()),只在generate()方法内实例化 - 用
class_alias()或工厂返回具体类,但禁止在config/app.php里写'pdf' => DompdfService::class这种全局绑定 - 如果真要多引擎并存(比如旧报表走
tcpdf,新合同走mpdf),必须用 PHP-FPM 进程级隔离:不同 URL 路由跑在不同 pool,避免 autoload 冲突
真正难的不是装几个包,而是字体路径硬编码、CSS 单位混用(px/em/%)、HTTP 请求中 Content-Type 错写成 text/html ——这些细节一错,PDF 就是空白页,而 Composer 根本不会报错。











