360 Secure SkillHub
360 安全技能中心

Invoice Pdf Merge

面向发票打印整理的 PDF 拼版工具:按文件名分组配对电子发票与电子行程单,使用 PyMuPDF 将二者上下拼版到同一张 A4,保持文字可选中,自动删除空白尾页,落单发票仅占上半张;支持批量扫描文件夹或合并两个指定 PDF。

leahlu0124-creator
leahlu0124-creator
热度 11.85
最近更新 2026-07-14 18:06:05
分类:效率依赖:代码执行 / 读写文件应用场景:文件转换 / 工作流 / 财务

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add invoice-pdf-merge
Metadata
name
invoice-pdf-merge
description
发票PDF智能合并工具。将多张发票PDF拼版到同一张A4纸上(2合1),保持文字可选中,支持发票+行程单上下布局,自动删除空白尾页,落单发票只占上半张。触发词:发票合并、PDF拼版、打印发票、发票整理、invoice merge、PDF 2合1、节省打印纸张。

发票 PDF 智能合并

将多张发票 PDF 拼版到同一张 A4 纸上,节省打印纸张,同时保持 PDF 文字可选中特性。

核心特性

  • 保持文字可选中 — 使用 PyMuPDF 的 show_pdf_page(),不转图片
  • 发票上 + 行程单下 — 固定上下布局,发票自适应缩放至半页,行程单按原始尺寸铺排
  • 自动删除空白尾页 — 检测并删除仅有页码的无意义尾页
  • 落单发票处理 — 只占上半张 A4,下半留空,大小与合并时一致
  • 批量处理 — 自动扫描文件夹,按文件名智能配对发票和行程单

安装依赖

/usr/bin/python3 -m pip install PyMuPDF --user

注意:必须使用系统 Python 安装,WorkBuddy 管理的 Python 环境可能不同。

快速使用

核心设计原则:一张发票对应一个行程,按文件名中的共同前缀智能配对。

方式一:批量处理文件夹

将脚本放在待处理文件夹同级目录,然后运行:

/usr/bin/python3 merge_invoices.py <输入文件夹> <输出文件夹>

脚本会自动:

  1. 提取分组标识 — 从文件名中【】括号内的文字提取(如 【打车服务-2024年6月-市区到机场】)
  2. 同一标识下的文件归为一组(每组 = 1个独立行程)
  3. 组内精确匹配「电子发票/电子普通发票」+「电子行程单/行程报销单」(用完整后缀判断,避免关键词误匹配)
  4. 每组输出一个 PDF:发票在上 + 行程单在下
  5. 无法配对的单独处理(只占上半张 A4)

方式二:合并两个指定 PDF

/usr/bin/python3 merge_invoices.py invoice1.pdf invoice2.pdf output.pdf

脚本说明

核心脚本:scripts/merge_invoices.py

主要函数

函数 用途
merge_invoice_itinerary() 发票+行程单上下布局合并,自动删空白尾页
merge_two_pdfs() 两个PDF简单2合1拼版(上下或左右)
place_single_invoice() 落单发票放到上半张A4
get_content_bottom() 检测页面实际内容底部坐标
is_trivial_page() 判断页面是否仅有页码等无意义内容
auto_merge_folder() 批量扫描文件夹,按【】前缀智能分组配对
_extract_group_key() 从文件名提取分组标识(【】括号或去后缀)

关键设计决策

  1. 固定上下布局 — 发票在上半张、行程单在下半张,符合阅读习惯
  2. 行程单不压缩 — 按原始尺寸铺排,内容完整可读
  3. 检测内容区域 — 用 page.get_text("dict")["blocks"] 获取 bbox,而非依赖页面尺寸
  4. 删除废页 — 输出后检查最后一页,若仅含页码(支持 "页码:1/1"、"页码: 1 / 1" 等带空格格式)则删除

常见问题

Q:行程单内容太多,合并后变成多页? A:这是预期行为。行程单按原始尺寸铺排,超出部分自动续页。如果不需要多页,可以修改脚本改用左右布局。

Q:合并后的PDF文字不可选? A:检查是否安装了正确版本的 PyMuPDF。必须使用 show_pdf_page() 方法,不能将 PDF 转为图片再合并。

Q:落单发票太大/太小? A:落单发票会使用与合并时相同的缩放逻辑,放到上半张A4的中心位置,大小一致。

输出文件命名

  • 每组配对输出一个独立 PDF:{分组标识前30字}_合并.pdf(如 打车服务-2024年6月_合并.pdf)
  • 落单发票:保持原文件名,只占上半张 A4

配对逻辑详解

旧版(已废弃)用关键词 "发票" in f / "行程" in f 匹配,但发票文件名常含「1个行程」等文字导致误匹配。

新版采用两步法:

  1. 先分组:提取文件名中【】括号内的文字作为分组 key
  2. 再精确匹配:组内用完整后缀(.电子发票.pdf / .电子行程单.pdf)判断类型