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 . '<br />';
    } 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 . '<br />';
    }

    // 编辑者签名
    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 . '<br />';
    }
    // 审批签名
    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 . '<br />';
    }
    // 批准签名
    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 . '<br />';
    }
}

// 创建 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' 为保存文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值