PDF离线比对工具 · 自动构建 Skill
这是什么(先给用户看的一段说明)
本技能会帮你在自己电脑上生成一个纯本地、可断网运行的小程序,用来比对你「发出」和「收回」的任意两份 PDF 是否一致。
适用场景
- 往来需要签字、盖章的合同、协议、公文、表格等正式文档;
- 你发出定稿 PDF,对方盖章/签字后回传,想确认对方没有偷偷改动内容;
- 任何"同一份文件前后两版要逐字一致"的核对需求。
为什么放心用
- 全程断网运行,不联网、不上传任何数据,不侵犯隐私;
- 不依赖 OCR、不读文本层,直接比渲染出来的页面图像,改一个字、挪一个标点都会留下痕迹。
工具默认功能
- 双框上传:左侧「发出文件(PDF原稿)」、右侧「收回文件PDF(对方签盖)」;
- 页数不一致 → 直接停止并报告,不继续比对;
- 页数一致 → 逐页像素比对,圈出差异区域,生成带红框的差异图 + 文本报告;
- 灵敏度滑块:电子 PDF 用中档即可;扫描件调高以压制噪点误报;
- 输出:在 exe 同目录生成「比对报告_时间戳」文件夹(差异图 PNG + 报告 txt)。
使用须知
- 工具只告诉你"哪里像素不一样",不自动判定是否恶意篡改,最终需你人工确认红框是否落在文字/条款区;
- 签章/签字区域本就会显示差异,属正常,可忽略红框里的"章/签名";
- 扫描件可能带来少量误报,以肉眼核对为准;
- 本机打包自己用,无需代码签名;未签名 exe 在 Win10/11 可能弹"未知发布者",点"仍要运行"即可。
关于功能改动
如果你需要增删或调整功能(如改默认 DPI、加图片比对、支持拖拽排序、换输出格式等),请提前告诉我,我会在开干前说明具体改动;没问题的话,我就按上面的默认功能直接开干。
激活后立即执行的工作流
不要只讲解——激活就开干。 按顺序执行:
1. 输出说明(见上)并建任务清单
用 TaskCreate 建任务:环境准备 / 写代码 / 打包 / 自测 / 交付。
2. 准备 Python 环境
使用本环境托管 Python 3.13 的 venv(路径以 python --version 实际探测为准)。
- 装核心依赖:pyinstaller PyMuPDF Pillow numpy scipy
- PySide6 安装陷阱:venv 内直接 pip install PySide6 可能被沙箱"安全删除"拦截。改为装到独立 --target 目录(见 references/build-notes.md),打包时再 --paths 指过去。若遇拦截,用放开沙箱的方式重装(仅作用于隔离 venv,不碰个人文件)。
3. 落地源码
把本 skill scripts/ 目录下的三个文件复制到当前工作目录:
contract_diff.py(比对引擎,无 GUI 依赖)、gui.py(PySide6 界面)、test_engine.py(自测)。
> 直接 cp 本 skill 的 scripts 目录即可,不要重新生成代码。
4. 跑引擎自测
python test_engine.py —— 验证:篡改数字被精准圈出、页数不符被拦截、相同文件零差异。三项全过才继续。
5. PyInstaller 打包单文件 exe
参照 references/build-notes.md 的精确命令,关键参数:
--onefile --windowed --name ScanDiff(或用户指定的名字)
--paths <pyside6-target> --paths <工作目录>
--hidden-import PySide6 --hidden-import PySide6.QtWidgets --hidden-import PySide6.QtCore --hidden-import PySide6.QtGui
入口 gui.py。输出 dist/ScanDiff.exe。
6. 存活检测
启动 dist/ScanDiff.exe,等待数秒,确认进程存活(Qt 平台插件与所有 import 加载成功,无关键报错)。
> 若重建后 exe 被锁,复制到新目录再测。
7. 交付
present_files交付ScanDiff.exe+ 使用说明。- 用
certutil -hashfile ScanDiff.exe SHA256(或 Python hashlib)算 SHA256 附给用户,便于分发时校验。 - 提示用户:这是在自己机器上打包的,直接双击运行即可;如需给别人用,把 exe + SHA256 一起发,并说明"纯本地离线、不联网"。
关键陷阱速查(详见 references/build-notes.md)
fitz.Font(filename=...)在 PyMuPDF 1.28+ 改为fontfile=...。- 中文 PDF 渲染用
fitz.TextWriter(...).append(pos, text, font=font).write_text(page);但真实用户 PDF 自带字体,引擎不依赖系统中文字体,自测脚本用英数内容规避字体依赖。 - Qt 日志滚动用
QTextCursor.End(不是self.log.textCursor().End),否则崩溃且无提示。 - Windowed exe 启动崩溃无控制台,务必加
sys.excepthook弹QMessageBox.critical避免离线静默失败。
资源
scripts/contract_diff.py— 比对引擎(render/compare/report)。scripts/gui.py— PySide6 界面(双上传区、灵敏度滑块、预览、报告)。scripts/test_engine.py— 引擎自测(英数样本,跨平台可跑)。references/build-notes.md— 完整 shell 命令、PyInstaller 调用、沙箱绕过、字体与 Qt 坑点。