告别WPS会员!用Python自制电子发票批量打印排版工具

告别WPS会员!用Python自制电子发票批量打印排版工具

日常办公中,报销贴发票是件琐碎但避不开的事。

相信很多人都遇到过:WPS批量打印电子发票时,合并排版功能居然需要会员才能用!

作为程序员,这哪能忍。

今天就和大家分享我基于Python开发的电子发票PDF批量排版工具,彻底摆脱会员限制。

工具核心功能

  • 支持批量导入电子发票PDF文件
  • 两种排版模式:每页2张(纵向A4)/每页4张(横向A4)
  • 灵活的尺寸设置:可手动指定发票尺寸,也可自动适配
  • 可选添加裁剪虚线,方便后续裁剪
  • 可视化操作界面,无需命令行操作
  • 配置自动保存,无需重复设置

工具下载

我知道很多人都是奔着工具来的,这里直接放出工具获取方式:

关注公众号"易派森",在对话框输入"发票排版"获取下载地址。

其中exe文件直接双击打开即可使用。

使用方法

第一步

首先点击"添加PDF文件"选择电子发票文件,按住Ctrl键然后点击可以多选多个文件

第二步

选择每页上发的数量,如果选择一页放两张,那就是竖着排;如果一页放四张,那就是横着拍两行,每行两个。

另外,如果有公司对发票大小有格式要求,还可以自己制定长宽,如果空着,那就是原始大小,软件会自动排版。

如果需要打印出来然后裁剪,软件有标注裁切线的功能。

第三步

点击"开始保存并处理",会跳出对话框让你选择保存位置,文件名默认就是"排版发票"+当前的unix时间。

如果一切顺利,就会弹出这样的对话框:

然后就能打开对应的文件

技术讲解

如果对技术感兴趣,可以看看下面的一些技术细节

技术选型与环境准备

核心依赖库

  • tkinter:Python内置的GUI库,用于制作可视化操作界面
  • PyMuPDF (fitz):强大的PDF处理库,实现PDF的合并、排版和导出
  • json:用于保存用户配置,避免重复设置

环境安装

首先确保你的Python环境正常,然后安装核心依赖:

bash 复制代码
# 先卸载可能存在的旧版fitz(如有)
pip uninstall fitz -y

# 安装正确的PyMuPDF库
pip install pymupdf

注意:不要直接安装fitz,而是安装pymupdf,这是很多新手容易踩的坑

核心代码解析

1. 程序初始化与配置管理

python 复制代码
class InvoiceMergerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("电子发票PDF批量排版工具 (自动适应版)")
        self.root.geometry("600x680")
        self.root.resizable(False, False)

        self.file_list = []
        self.config = self.load_config()  # 加载配置
        self.create_widgets()  # 创建界面控件
        self.apply_config()    # 应用保存的配置

程序启动时会加载本地配置文件invoice_config.json,保存用户上次的排版设置,提升使用体验。

2. 文件管理功能

实现了PDF文件的添加、清空、移除选中功能,核心代码:

python 复制代码
def add_files(self):
    files = filedialog.askopenfilenames(filetypes=[("PDF Files", "*.pdf")])
    for f in files:
        if f not in self.file_list:
            self.file_list.append(f)
            self.listbox.insert(tk.END, os.path.basename(f))
    self.status_label.config(text=f"当前已选: {len(self.file_list)} 个文件")

3. 核心排版逻辑

这是整个工具的核心,实现了PDF页面的合并排版:

python 复制代码
def run_processing(self, output_path, t_w_cm, t_h_cm):
    doc_out = fitz.open()
    mode = self.layout_var.get()
    draw_cut = self.cut_line_var.get()

    # 设置页面尺寸和排版行列数
    if mode == 2:
        pg_w, pg_h = A4_WIDTH_PT, A4_HEIGHT_PT
        rows, cols = 2, 1
    else:
        pg_w, pg_h = A4_HEIGHT_PT, A4_WIDTH_PT
        rows, cols = 2, 2

    count = len(self.file_list)
    per_page = rows * cols
    current_page = None

    for idx, fpath in enumerate(self.file_list):
        # 进度更新
        self.progress_var.set((idx / count) * 100)
        self.log_label.config(text=f"正在处理: {os.path.basename(fpath)}")
        self.root.update()

        pos = idx % per_page
        
        # 新建页面(每满per_page个文件新建一页)
        if pos == 0:
            current_page = doc_out.new_page(width=pg_w, height=pg_h)
            # 绘制裁剪虚线
            if draw_cut:
                shape = current_page.new_shape()
                shape.draw_line((0, pg_h / 2), (pg_w, pg_h / 2))
                if mode == 4:
                    shape.draw_line((pg_w / 2, 0), (pg_w / 2, pg_h))
                shape.finish(color=(0.7, 0.7, 0.7), dashes="[3 5] 0", width=0.5)
                shape.commit()

        # 计算当前发票在页面中的位置
        r = pos // cols
        c = pos % cols
        cell_w = pg_w / cols
        cell_h = pg_h / rows
        cell_x0 = c * cell_w
        cell_y0 = r * cell_h

        # 尺寸适配逻辑
        if t_w_cm is not None and t_h_cm is not None:
            # 手动指定尺寸:居中放置
            inv_w = self.get_points(t_w_cm)
            inv_h = self.get_points(t_h_cm)
            cx = cell_x0 + (cell_w / 2)
            cy = cell_y0 + (cell_h / 2)
            rect = fitz.Rect(cx - inv_w / 2, cy - inv_h / 2, cx + inv_w / 2, cy + inv_h / 2)
        else:
            # 自动适配:留20点边距
            margin = 20
            rect = fitz.Rect(
                cell_x0 + margin,
                cell_y0 + margin,
                cell_x0 + cell_w - margin,
                cell_y0 + cell_h - margin
            )

        # 嵌入PDF页面
        try:
            src = fitz.open(fpath)
            current_page.show_pdf_page(rect, src, 0, keep_proportion=True)
            src.close()
        except Exception as e:
            print(f"文件出错 {fpath}: {e}")
            current_page.insert_text((cell_x0 + 10, cell_y0 + 10), 
                                    f"读取失败: {os.path.basename(fpath)}",
                                    fontsize=8, color=(1, 0, 0))

    doc_out.save(output_path)
    doc_out.close()

提示词

当然,本工具也是利用AI开发的, 提示词如下:

复制代码
使用Python开发一个电子发票PDF批量排版软件,该软件应具备以下核心功能和技术要求:

1. 文件选择功能:
   - 实现批量选择电子发票PDF文件的界面组件
   - 支持通过文件浏览器多选PDF文件
   - 提供已选文件列表预览及移除功能

2. 排版设置功能:
   - 提供清晰的选项界面,允许用户选择每页A4纸上排列的发票数量(2张或4张)
   - 支持预览排版效果
   - 提供纸张方向选择(横向/纵向)

3. PDF处理功能:
   - 确保发票PDF正确缩放和定位在A4页面上
   - 支持不同尺寸的发票PDF自适应排版
   - 处理PDF文件时保持原始清晰度和内容完整性

4. 输出功能:
   - 将排版后的发票合并为单个PDF文件
   - 提供输出路径选择和文件名自定义功能
   - 显示处理进度和完成状态

技术要求:
- 使用适合的PDF处理库(如PyPDF2、ReportLab或pdfplumber)
- 设计简洁直观的用户界面(可使用Tkinter、PyQt或PySide)
- 确保软件稳定性,处理过程中出现异常时提供明确提示
- 支持Windows主流操作系统

最终交付物应包括完整的可运行代码、必要的依赖说明以及简单的使用文档。

总结

  1. 该工具基于Python的tkinter和PyMuPDF库开发,完美解决了WPS合并打印需要会员的问题;
  2. 核心逻辑是将多个PDF发票文件按照指定排版规则合并到同一个PDF页面中,支持手动指定尺寸和自动适配两种模式;
  3. 工具具备可视化操作界面、配置保存、进度显示等实用功能,开箱即用,大幅提升报销发票处理效率。

这个小工具虽然简单,但切实解决了日常办公中的痛点。作为程序员,我们最大的优势就是能用代码解决实际问题,拒绝被各种会员限制束缚。希望这篇文章能帮助到有同样需求的朋友!

相关推荐
流水线上的指令侠19 小时前
C# 实战:从 0 到 1 搭建基于 NUnit + FlaUI 的 WPF UI 自动化测试项目
功能测试·ui·c#·自动化·wpf·visual studio
m0_7066532319 小时前
Python数据库操作:SQLAlchemy ORM指南
jvm·数据库·python
试剂小课堂 Pro19 小时前
Ald-PEG-Ald:丙醛与聚乙二醇两端连接的对称分子
java·c语言·c++·python·ffmpeg
玄同76519 小时前
SQLAlchemy 初始化全流程详解:从引擎到会话的每一步
数据库·人工智能·python·sql·mysql·语言模型·知识图谱
小北方城市网19 小时前
MyBatis-Plus 生产级深度优化:从性能到安全的全维度方案
开发语言·redis·分布式·python·缓存·性能优化·mybatis
diediedei19 小时前
使用XGBoost赢得Kaggle比赛
jvm·数据库·python
小芳矶19 小时前
使用 Langgraph 构建本地 RAG 知识库:从文档加载到检索
python·langchain
naruto_lnq19 小时前
Python入门:从零到一的第一个程序
jvm·数据库·python
玄同76519 小时前
大模型生成 Token 的原理:从文本到模型理解的 “翻译官”
人工智能·python·语言模型·自然语言处理·nlp·知识图谱·token
m0_5613596719 小时前
如何从Python初学者进阶为专家?
jvm·数据库·python