动态合同生成本质是先用模板引擎(如str_replace或Twig)将数据注入HTML模板,再用TCPDF或DOMPDF转PDF;TCPDF轻量且中文稳定但需显式注册字体,DOMPDF渲染更贴近浏览器但内存高、不支持Flex。

用 tcpdf 或 dompdf 渲染 HTML 模板为 PDF,不是直接“生成合同”
PHP 本身不内置 PDF 生成功能,所谓“动态合同生成”,本质是:先拼出含数据的 HTML(比如用 file_get_contents + str_replace 或 Twig),再交给 PDF 库转成二进制文件。选错库或跳过模板预处理,后面全卡在乱码、分页、中文不显示上。
常见错误现象:tcpdf 报 Missing or incorrect font definition;dompdf 渲染后中文变方块、表格错位、CSS 不生效。
-
tcpdf更轻量、对中文支持更稳(需显式注册字体,如addTTFfont加载simhei.ttf) -
dompdf更贴近浏览器渲染逻辑,但内存占用高、不支持 Flex 布局,复杂表格易崩溃 - 别用
mpdf—— 它依赖外部二进制且新版 require PHP 8+,老项目踩坑率极高
模板里怎么安全插值?别用 eval,也别裸写 <?php ?>
合同字段常含用户输入(如姓名、金额、日期),直接拼接 HTML 极易 XSS 或格式错乱。必须做两件事:模板分离 + 数据过滤。
使用场景:合同模板是纯 HTML 文件(如 contract.tpl.html),里面用占位符,比如 {{client_name}}、{{amount_cny}},而不是 PHP 代码块。
- 用
str_replace最简够用:str_replace(['{{client_name}}', '{{amount_cny}}'], [$name, number_format($amount, 2)], $html) - 如果字段多、有嵌套逻辑(比如“若签约地为空则显示默认城市”),上
Twig,但禁用Twig_Sandbox_Extension外的所有扩展,防止模板执行任意 PHP - 所有插入前强制过滤:
htmlspecialchars($input, ENT_QUOTES, 'UTF-8'),尤其金额字段要floatval()再格式化,避免字符串 “1000.00元” 被当文本参与计算
PDF 中文字体和页眉页脚怎么不丢?关键在初始化阶段
90% 的中文 PDF 乱码问题,出在 PDF 库加载字体的路径或编码没对齐。不是模板写错了,是 tcpdf 根本没找到字体文件,或用了 UTF-8 字符却配了 ISO-8859-1 编码。
性能影响:每次生成都重新注册字体会拖慢响应,必须复用已注册的字体对象。
-
tcpdf必须调用addTTFfont指向绝对路径的 .ttf 文件(如/var/www/fonts/simhei.ttf),不能用相对路径或 URL - 创建 PDF 实例后立刻设字体:
$pdf->setFont('simhei', '', 12),否则后续writeHTML用默认字体,中文直接空白 - 页眉页脚用
setHeaderData/setFooterData注册,内容里中文同样要确保字体已加载;若需动态内容(如合同编号),重写Header方法,别在writeHTML里硬塞
生成失败时怎么快速定位?看日志比调试 PDF 更有效
PDF 生成失败通常不抛异常,而是返回空字符串或损坏文件。直接输出 PDF 浏览器打不开,不代表代码错——可能只是 header 发送早了、缓冲区被清空、或内存超限。
容易踩的坑:ob_start() 没配对、header() 前有空格、模板里意外 echo 了调试信息。
- 先保存中间 HTML:
file_put_contents('/tmp/contract_debug.html', $final_html),浏览器打开确认渲染正常 - 生成 PDF 后检查返回值:
if (empty($pdf->Output('', 'S'))) { error_log('PDF generation returned empty'); } - 把 PDF 输出改为文件保存:
$pdf->Output('/tmp/contract.pdf', 'F'),再用命令行pdfinfo /tmp/contract.pdf看是否真生成,排除 header 干扰
真正麻烦的是跨页表格断行、签名图位置偏移、电子签章验签失败——这些得靠 PDF 规范级调试,不是换库就能解决的。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











