Skip to content
// 0x
Go back
0x0F // 后端实践

mPDF 6.0 动态图片渲染踩坑:当 base64、PNG、绝对路径全都失效

在 WMS(仓储管理系统)中有一个工单模块,需要将客户在网页上手写的签名嵌入到 PDF 工单中。签名数据以 base64 格式存储在数据库中,PDF 生成使用 mPDF 6.0。

听起来很简单对吧?base64 data URI 放 <img src> 里,或者存成临时文件用绝对路径引用——任何一个方案都应该能工作。但事实是,这三种常见方式在 mPDF 6.0 面前全都失效了。

问题背景

工单签名的工作流:

用户在浏览器 Canvas 上手写
    → 签名库输出 data:image/png;base64,...
    → 存入数据库 TEXT 列
    → print.php 查询 DB → 嵌入 PDF → mPDF Output

签名数据是这种格式:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA...

目标是在 mPDF 6.0 生成的 PDF 中正确显示这个签名图片。

排查:四种方案逐一失效

方案一:Base64 data URI 直接嵌入

最直觉的方案——HTML 的 <img> 天然支持 data URI,mPDF 的 WriteHTML 也号称支持标准 HTML:

$html .= '<img src="' . $signatureDataUri . '" style="width:250px; height:75px;" />';
$mpdf->WriteHTML($html);

结果:PDF 生成成功,但签名位置显示 14×16 像素的小红叉。mPDF 的 _getImage() 方法对 data URI 完全无响应,不报错、不记录、直接回退到 noImageFile 占位图。

方案二:$mpdf->Image() 方法

绕过 WriteHTML,使用 mPDF 提供的 Image() 方法:

$mpdf->Image($signatureDataUri, 10, 20, 60);

结果:同样失败,PDF 中没有嵌入任何图片数据。

方案三:保存为临时 PNG 文件 + 绝对路径

怀疑是 data URI 的问题,改为先保存到临时文件,再通过绝对文件路径引用:

$tmpPath = '/tmp/sig_' . uniqid() . '.png';
file_put_contents($tmpPath, base64_decode($base64Data));

// 方式 A:WriteHTML img 标签
$html .= '<img src="' . $tmpPath . '" ... />';

// 方式 B:Image() 方法
$mpdf->Image($tmpPath, 10, 20, 60);

两种方式都试了。文件确实存在、路径正确、PHP 的 file_exists 返回 true。结果——

结果:PDF 依然没有嵌入图片。用 strings 检查生成的 PDF,找不到任何 PNG 文件头标记。

方案四:改为 JPEG 文件

既然 PNG 不行,那 JPEG 呢?用外部工具把同一张图转为 JPEG:

$tmpPath = '/tmp/sig_' . uniqid() . '.jpg';
$mpdf->Image($tmpPath, 10, 20, 60);

结果:✅ PDF 正确显示了签名图片,文件头标记也出现在 PDF 二进制数据中。

根因分析

图片格式WriteHTML <img>$mpdf->Image()说明
PNG data URI❌ 红叉❌ 红叉mPDF 不处理 data URI
PNG 文件路径❌ 红叉❌ 红叉文件存在但拒绝嵌入
JPEG data URI❌ 红叉❌ 红叉同上,mPDF 6.0 不支持 data URI
JPEG 文件路径✅ 成功✅ 成功唯一可行的路径

结论:mPDF 6.0 对 PNG 格式存在兼容性问题,无论是 data URI 还是文件路径都无法正确渲染。JPEG 格式正常工作。

最终方案

由于签名库输出的是 image/png 格式,需要在生成 PDF 时将 PNG 转换为 JPEG:

function saveSigImage($dataUri, $prefix) {
    $commaPos = strpos($dataUri, ',');
    $base64 = substr($dataUri, $commaPos + 1);
    $imgData = base64_decode($base64);

    // 用 GD 库将 PNG 转换为 JPEG
    $src = @imagecreatefromstring($imgData);
    $path = sys_get_temp_dir() . '/' . $prefix . '_' . md5($imgData) . '.jpg';
    imagejpeg($src, $path, 85);
    imagedestroy($src);

    return $path; // 返回绝对路径给 mPDF
}

关键细节:

容器依赖配置(Dockerfile):

RUN docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \
    && docker-php-ext-install gd

为什么这是一个值得记录的坑

问题原因
不报错mPDF 对无法加载的图片静默回退到占位图,没有日志、没有异常
不直观PNG 作为通用图片格式,很少人会想到它是导致 PDF 渲染失败的根因
data URI 不支持现代浏览器和很多 HTML→PDF 引擎都支持 data URI,但 mPDF 6.0 确实不支持
旧版本包袱mPDF 6.0(2016 年发布)的图片处理引擎与新版差异很大,Stack Overflow 上的答案多针对新版

如果系统使用新版 mPDF(v7+)或改用 TCPDF,这个问题可能不存在。但在遗留系统升级受限的场景下——就像这个跑在 PHP 5.6 上的 WMS——这个排查过程可以帮助少走弯路。


Share this post on:

Previous Post
全量 Vendor Rebrand:9 个 PHP 包从 hizpark 到 changhorizon 的实践记录
Next Post
在 PHP 5.6 遗留系统中设计 Shopify 模块:架构取舍