多数场景优先选 dompdf,因其易上手且支持 html 模板;需高级功能(如数字签名、acroform)则选 tcpdf;解析 pdf 文字应区分扫描件与文本型,结构化提取推荐上游提供 json。

PHP 生成 PDF 用 tcpdf 还是 dompdf?
多数场景下,dompdf 更快上手,尤其当你已有 HTML 模板(比如发票、报告页),它能直接把 HTML + CSS 渲染成 PDF;tcpdf 功能更底层,支持页眉页脚、数字签名、多栏排版,但 API 冗长,写个带中文的表格容易卡在字体注册上。
常见错误现象:dompdf 导出 PDF 中文乱码或空白 —— 默认不加载中文字体,必须显式注册;tcpdf 报错 Could not include font definition file —— 是因为没把 dejavusans.php 或自定义字体文件放对位置,且没调用 addTTFfont()。
- 简单报表、邮件附件、后台导出:优先试
dompdf,配合set_option('isRemoteEnabled', true)才能加载远程图片 - 需精确控制每页内容、插入条形码、生成可填写表单(AcroForm):选
tcpdf,别碰fpdf(不原生支持 UTF-8) - 两者都不适合高频生成(如每秒 10+ 份):PDF 渲染吃 CPU,应改用预渲染 + 缓存,或把生成逻辑移到队列里
PHP 解析 PDF 文字内容为什么总漏字或错行?
PDF 不是文本容器,而是图形指令集合。文字可能被拆成单个字符绘制、旋转、叠加描边,甚至用图片替代。所以 smalot/pdfparser 或 setasign/fpdi 只能尽力还原,无法保证 100% 准确。
典型问题:pdfparser 读取扫描件 PDF 返回空字符串 —— 因为那是图像,不是可提取文字;用 pdftotext 命令行工具也一样失败,必须先 OCR。
- 纯文字型 PDF(由 Word / LibreOffice 导出):用
smalot/pdfparser,调用$parser->parseContent()后,再用strip_tags()清理残留标签 - 含表格的 PDF:别指望自动识别行列结构,
pdfparser返回的是线性文本流,需结合正则匹配固定字段(如/订单号:(\w+)/)或按换行切分后人工对齐 - 要稳定提取结构化数据:不如让上游系统同时提供 JSON 接口,PDF 仅作展示副本
tcpdf::writeHTML() 插入变量时怎么避免 XSS 和格式崩溃?
writeHTML() 会解析 HTML 标签,如果直接拼接用户输入,既可能执行 JS,也会因未闭合标签导致后续样式失效。它不等价于 echo,而是一个简易渲染引擎。
- 永远对动态内容调用
htmlspecialchars($str, ENT_QUOTES, 'UTF-8'),再传入writeHTML()—— 单引号、双引号、、<code>>都得转义 - 禁用内联样式以外的 CSS:
writeHTML()对@media、flex、grid支持极差,表格建议用<table border="1"> 而非 class 控制边框 <li>图片路径必须是绝对 URL 或服务器本地完整路径(如 <code>/var/www/html/uploads/logo.png),相对路径大概率 404 - 保留书签需额外加载
setasign/fpdf-tpl并手动重建:读取源 PDF 的Outline字典,用addTOC()逐条添加,无自动化方案 - 合并后页数异常(比如第 3 页变空白):检查是否混用
fpdi和tcpdf实例 —— 必须统一用FPDI子类(如class PdfMerger extends \setasign\Fpdi\Fpdi) - 想加水印或页眉?在
useTemplate()后立即用SetFont()+Text()绘制,别依赖页脚钩子,FPDI 的Header()不生效
用 setasign/fpdi 合并多个 PDF 时页数/书签丢失怎么办?
fpdi 本质是把源 PDF 当作“模板”导入,只保留页面内容(Page Content Stream),元信息(如书签、表单域、页面标签)默认不继承。合并后只剩白纸黑字,没有导航。
真正难的不是代码写几行,而是 PDF 规范本身复杂:一个文件里可能混合 Type1 / CID / TrueType 字体,不同生成工具嵌入方式不同,连“第一页”都可能被 PDF 查看器按逻辑页而非物理页解释。处理前先用 pdfinfo your.pdf 看基础元数据,比硬试更省时间。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











