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' 为保存文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/79993.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中达瑞和便携式高光谱相机:珠宝鉴定领域的“光谱之眼”

在珠宝行业中&#xff0c;真伪鉴定始终是核心需求。随着合成技术与优化处理手段的日益精进&#xff0c;传统鉴定方法逐渐面临挑战。中达瑞和推出的便携式高光谱相机&#xff0c;凭借其独特的“图谱合一”技术&#xff0c;为珠宝真假鉴定提供了科学、高效且无损的解决方案&#…

2025年渗透测试面试题总结-某战队红队实习面经(附回答)(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 某战队红队实习面经 个人经历与技术能力 2. HVV/攻防演练成绩 3. 上一个工作主要内容 4. 有意思的逻…

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】5.1 描述性统计分析(均值/方差/分位数计算)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 5.1 描述性统计分析&#xff1a;均值、方差与分位数计算实战5.1.1 数据准备与分析目标数据集介绍分析目标 5.1.2 均值计算&#xff1a;从整体到分组分析总体均值计算加权均值…

npm下载插件无法更新package.json和package-lock.json文件的解决办法

经过多番查证&#xff0c;使用npm config ls查看相关配置等方式&#xff0c;最后发现全局的.npmrc文件的配置多写了globaltrue&#xff0c;去掉就好了 如果参数很多&#xff0c;不知道是哪个参数引起的&#xff0c;先只保留registryhttp://xxx/&#xff0c;试试下载&#xff0…

基于Anaconda的Pycharm环境配置

一、前提条件&#xff1a; 1、默认已安装完Anaconda&#xff0c;且创建虚拟环境&#xff0c;参见https://blog.csdn.net/XIAOWEI_JIN/article/details/147657029?spm1001.2014.3001.5501 2、已安装pycharm&#xff0c;下载链接见Pycharm官网&#xff0c;以下以PyCharm 2024.…

Word域操作记录(从1开始的毕业论文格式排版)

傻逼Word。 写在最前面 如果你的文章不包括&#xff1a;自动目录、交叉引用、自动题注。请关闭此页面。继续阅读本文是在浪费您用于跟格式如泥潭里缠斗的时间。 本文内容概述 从指导手册到毕设初稿 基于多级列表的自动目录生成方法 正片开始 关于文字 拿到毕设手册&#…

Linux中的web服务

什么是www www是world wide web的缩写&#xff0c;及万维网&#xff0c;也就是全球信息广播的意思 通常说的上网就是使用www来查询用户所需要的信息。 www可以结合文字、图形、影像以及声音等多媒体&#xff0c;超链接的方式将信息以Internet传递到世界各 处去。 当你连接w…

linux -c程序开发

目的是在linux中创建可执行的c语言程序的步骤 和gcc,make和git的简单运用 建立可执行程序的步骤: -1:预处理: --:头文件展开;--去掉注释;--宏替换;--条件编译 -2:编译 --:将预处理之后的c语言替换为汇编语言带阿米 --:语法分析,语义分析,代码生成 --:检查语法正确性并且优…

Netty 是一个基于 Java NIO 的高性能网络通信框架

Netty 是一个基于 Java NIO 的高性能网络通信框架&#xff0c;广泛应用于构建分布式系统、RPC 框架、即时通信系统等场景。它的核心设计目标是 异步、非阻塞、高可扩展性&#xff0c;其底层原理涉及 事件驱动模型、线程模型、内存管理 等关键技术。以下是 Netty 的核心原理和架…

UI 库 Ant Design 中的 Table 表格和分页器:快速实现数据展示和分页功能

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…

Java实现堆排序算法

1. 堆排序原理图解 堆排序是一种基于二叉堆&#xff08;通常使用最大堆&#xff09;的排序算法。其核心思想是利用堆的性质&#xff08;父节点的值大于或等于子节点的值&#xff09;来高效地进行排序。堆排序分为两个主要阶段&#xff1a;建堆和排序。 堆排序步骤&#xff1a; …

【Hive入门】Hive安全管理与权限控制:审计日志全解析,构建完善的操作追踪体系

目录 引言 1 Hive审计日志概述 1.1 审计日志的核心价值 1.2 Hive审计日志类型 2 HiveServer2操作日志配置 2.1 基础配置方案 2.2 日志格式解析 2.3 日志轮转配置 3 Metastore审计配置 3.1 Metastore审计启用 3.2 审计事件类型 4 高级审计方案 4.1 与Apache Ranger…

力扣-hot100 (缺失的第一个正数)

41. 缺失的第一个正数 困难 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 解释&#xff…

13前端项目----购物车修改

购物车修改 uuid临时游客身份购物车部分功能全选修改商品数量修改商品勾选状态删除产品 uuid临时游客身份 请求数据仓库发起请求 ->问题&#xff1a;获取不到购物车数据&#xff1f; 所以需要一个身份&#xff0c;告诉服务器是谁存的数据&#xff1f;是要获取谁的数据&…

Mac电脑,idea突然文件都展示成了文本格式,导致ts,tsx文件都不能正常加载或提示异常,解决方案详细说明如下

有一天使用clean my mac软件清理电脑 突然发现idea出现了文件都以文本格式展示&#xff0c;如图所示 然后就卸载&#xff0c;计划重新安装&#xff0c;安装了好几个版本&#xff0c;并且setting->file types怎么设置都展示不对&#xff0c;考虑是否idea没卸载干净&#xff…

Nginx搭建test服务器

创建test域名 进入阿里云添加解析 创建域名:test.xxxxx.com 服务器复制项目代码 新建目录,Git拉取项目代码,安装上插件包 修改配置文件,启动测试服务 修改配置文件“服务器接口” 开启服务pm2 start app.js --name "test" 表格含义: 列名含义说明id进程在…

MyBatis-Plus 非 Spring 环境使用时 `GenericTypeResolver` 缺失问题总结

MyBatis-Plus 非 Spring 环境使用时 GenericTypeResolver 缺失问题总结 问题描述 在非 Spring 环境中使用 MyBatis-Plus 3.4.3.1 及以上版本时&#xff0c;启动程序会抛出以下错误&#xff1a; Exception in thread "main" java.lang.NoClassDefFoundError: org/s…

综合案例:使用vuex对购物车的商品数量和价格等公共数据进行状态管理

文章目录 0.实现需求1.新建购物车模块cart2.使用json-server模拟向后端请求数据3.在vuex请求获取并存入数据,并映射到组件中,在组件中渲染【重点】3.1.安装axios3.2.准备actions和mutations,获取和存入数据到vuex中3.3.动态渲染:先用mapState映射list到组件页面 4.点击修改数量…

《数据结构初阶》【顺序表 + 单链表 + 双向链表】

《数据结构初阶》【顺序表 单链表 顺序表】 前言&#xff1a;先聊些其他的东西&#xff01;&#xff01;&#xff01;什么是线性表&#xff1f;什么是顺序表&#xff1f;顺序表的种类有哪些&#xff1f; 什么是链表&#xff1f;链表的种类有哪些&#xff1f; ---------------…

Android Retrofit框架分析(三):自动切换回主线程;bulid的过程;create方法+ServiceMethod源码了解

目录 Okhttp有什么不好&#xff1f;bulid的过程create方法ServiceMethodcall enqueue的过程为什么要学习源码呢&#xff1f; 一、Okhttp有什么不好&#xff1f; Okhttp本身来说&#xff0c;是一个挺好的网络框架&#xff0c;但&#xff0c;对于开发者而言&#xff0c;使用起…