使用Setasign/FPDI合并PDF时签名等绘图内容丢失的解决方法求助
FPDI合并PDF时签名等内容丢失的解决方案
问题背景
使用Setasign/FPDI及其PDF-Parser合并多个PDF文件时,部分包含签名等绘图内容的文件,合并后签名内容消失,需确保所有PDF内容完整转移到输出文件。
原始合并代码
private function mergeFiles() { $fpdi = new Fpdi(); foreach ($files as $file) { $this->mergePDFFile($fpdi, $file->getFilePath()); } $fpdi->Output($outputFilePath, 'F'); } private function mergePDFFile(Fpdi $pdf, $filePath) { $pageCount = $pdf->setSourceFile($filePath); for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) { $templateId = $pdf->importPage($pageNo); $size = $pdf->getTemplateSize($templateId); $pdf->AddPage($size['orientation']); $pdf->useTemplate($templateId, null, null, null, null, true); } }
效果对比
合并前的原始文件

合并后的异常结果文件

问题原因
FPDI默认的importPage方法仅导入页面的内容流,而PDF签名通常以注释(Annotation)或AcroForm表单字段的形式存储,这类内容不会被自动导入,因此合并后签名等内容丢失。
解决方案
方案1:基于FPDI手动导入注释与表单字段
通过FPDI的PDF解析器提取原PDF的注释和表单字段,手动添加到新生成的页面中。修改后的代码如下:
private function mergeFiles() { $fpdi = new Fpdi(); foreach ($files as $file) { $this->mergePDFFile($fpdi, $file->getFilePath()); } $fpdi->Output($outputFilePath, 'F'); } private function mergePDFFile(Fpdi $pdf, $filePath) { $pageCount = $pdf->setSourceFile($filePath); // 获取原PDF的解析器实例 $parser = $pdf->getPdfParser(); $catalog = $parser->getCatalog(); $acroForm = $catalog->getAcroForm(); for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) { $templateId = $pdf->importPage($pageNo); $size = $pdf->getTemplateSize($templateId); $pdf->AddPage($size['orientation']); $pdf->useTemplate($templateId, null, null, null, null, true); // 导入当前页面的注释内容 $page = $parser->getPage($pageNo); $annotations = $page->getAnnotations(); foreach ($annotations as $annot) { $pdf->addAnnotation($annot->getDictionary()); } // 导入关联当前页面的AcroForm字段 if ($acroForm) { $fields = $acroForm->getFields(); foreach ($fields as $field) { $fieldPage = $field->getPage(); if ($fieldPage && $fieldPage->getNumber() === $pageNo) { $pdf->addField($field->getDictionary()); } } } } }
方案2:使用SetaPDF-Merger(推荐)
Setasign官方提供的SetaPDF-Merger是专门针对PDF合并的组件,能自动处理签名、注释、表单等所有内容,无需手动编码。示例代码:
use SetaPDF_Merger; private function mergeFilesWithMerger() { $merger = new SetaPDF_Merger(); foreach ($files as $file) { $merger->addFile($file->getFilePath()); } // 执行合并并保存到指定路径 $merger->merge()->save($outputFilePath); }
说明
- 方案1需确保FPDI版本支持注释和表单字段的导入,部分复杂签名可能仍存在兼容问题。
- 方案2的SetaPDF-Merger为商业组件,需购买授权,但能完美保留PDF的所有元素,包括签名、表单、注释等。
内容的提问来源于stack exchange,提问作者felmez
相关产品推荐
相关产品推荐

