PHP实现PDF自动签名

技术要点:在PDF中找到一个固定锚点,在需要放置图片的地方找到测试出锚点对应的XY位

// 使用了poppler方法,其他PDF库在获取坐标方面有各种问题,他的安装是在Linux底层,比在PHP项目中用Composer安装的库看上去更稳定,功能更强
// pdftohtml方法需要先将PDF缓存成一个XML文件来进行坐标及其他具体数据的获取,所以要新建一个$TempPath做为缓存位置可以方便后期统一清理
// $filePath = 'file/SL/06/SL1.01.06.V01_测试'; // PDF文件实际位置
$filePath = 'file/' . input('post.fcate') . '/' . input('post.fcode') . '/' . input('post.fname'); // 自动获取文件+路径
$fname = str_replace('.pdf', '', input('post.fname')); // 文件名去掉PDF后缀
$TempPath = 'cache/' . $fname; // PDF文件缓存位置
$fNo = explode('_', $fname);
$fNo = $fNo[0]; // 只取文件名前面的编号

// 设置PDF文件路径
$pdfFilePath = $filePath;
// 执行pdftohtml命令,转换为HTML并输出坐标
// -c:保持原始布局
// -hidden:隐藏元素
// -xml:生成 XML 格式的输出,适合后续解析
// -noimages:不包含图片
// -stdout:将输出发送到标准输出而不是文件
// -xml方法会生成PDF中的图片,其他不生成图片的方法会与-xml方法冲突导致无法生成xml文件,所以使用缓存方法统一对xml文件进行处理
$command = "pdftohtml -xml -c -hidden $pdfFilePath $TempPath";
// 在缓存位置自动生成XML文件
exec($command);

// 解析生成的XML文件获取坐标信息
$xml = simplexml_load_file($TempPath . '.xml');
// 输出坐标信息
//var_dump($xml->page->text);
// 自动计算用值
$left = '4.2';
$top = '4.06';
$signTime = '11';

//                             print_r($xml->page->text);
// 获取所有签名附近的唯一标识的坐标,通过计算得出签名和签名日期真正的坐标
foreach ($xml->page->text as $v) {
	// 文件编号
    if (strstr($v, 'Procedure No.')) {
        $No_L = (float)($v['left'] / $left) + 19.6;
        $No_T = (float)($v['top'] / $top) - 0.46;
        // echo 'Procedure No. $No_L = ' . $No_L . ' | $No_T = ' . $No_T . '
';
} elseif (strstr($v, 'File No.')) { $No_L = (float)($v['left'] / $left) + 11; $No_T = (float)($v['top'] / $top) - 0.46; // echo 'File No. $No_L = ' . $No_L . ' | $No_T = ' . $No_T . '
';
} // 编辑者签名 if ($v == 'EDITED BY') { $Edit_L = (float)($v['left'] / $left); $Edit_T = (float)($v['top'] / $top); // echo '$Edit_L = ' . $Edit_L . ' | $Edit_T = ' . $Edit_T . '
';
} // 审批签名 if ($v == 'REVIEWED BY') { $Revi_L = (float)($v['left'] / $left); $Revi_T = (float)($v['top'] / $top); // echo '$Revi_L = ' . $Revi_L . ' | $Revi_T = ' . $Revi_T . '
';
} // 批准签名 if ($v == 'APPROVED BY') { $Appr_L = (float)($v['left'] / $left); $Appr_T = (float)($v['top'] / $top); // echo '$Appr_L = ' . $Appr_L . ' | $Appr_T = ' . $Appr_T . '
';
} } // 创建 TCPDF 和 FPDI 的实例 // 在刚才获取的坐标中插入对应的签名及日期 $pdf = new TcpdfFpdi(); // 加载现有的 PDF 文件 $file = $pdfFilePath; $pageCount = $pdf->setSourceFile($file); // 获取 PDF 文件的页数 // 遍历每一页 for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) { // 导入页面 $templateId = $pdf->importPage($pageNo); // 添加新页面 $pdf->addPage(); // 使用导入的页面 $pdf->useTemplate($templateId); // 只修改第一页 if ($pageNo == 1) { // 设置插入图片的位置和大小 $pic1 = 'signature/【pic1】.png'; $pic2 = 'signature/【pic2】.png'; $pic3 = 'signature/【pic3】.png'; $pdf->Image($pic1, $Edit_L, $Edit_T, 16, 9); // Image(签名地址, X坐标, Y坐标, 宽, 高) 位置插入图片,宽高为 50 $pdf->SetFont('', '', 9); // SetFont(字体, 粗/斜 , 字号) $pdf->Text($Edit_L, $Edit_T + $signTime, date('Y-m-s', input('post.addtime_int'))); // Text(X坐标, Y坐标, 内容) $pdf->Image($pic2, $Revi_L, $Revi_T, 16, 9); $pdf->SetFont('', '', 9); $pdf->Text($Revi_L, $Revi_T + $signTime, date('Y-m-s', input('post.revtime_int'))); $pdf->Image($pic3, $Appr_L, $Appr_T, 16, 9); $pdf->SetFont('', '', 9); $pdf->Text($Appr_L, $Appr_T + $signTime, date('Y-m-s')); } $pdf->SetFont('', '', 9); $pdf->Text($No_L, $No_T, $fNo); // $pdf->SetMargins(10, 10, 10); // 设置页面边距 $pdf->SetDrawColor(255, 255, 255); // 设置边框颜色为白色 // $pdf->SetAutoPageBreak(true, 10); // 自动分页的底部边距 } // 输出修改后的 PDF 文件 Output实际使用的是fopen()方法,所以要用绝对路径,不要用相对路径会报错 $pdf->Output($_SERVER['DOCUMENT_ROOT'] . '/' . $pdfFilePath, 'F'); // 'I' 为直接输出到浏览器,'F' 为保存文件

你可能感兴趣的:(DMS文件管理系统开发,PHP)