|
|
你需要用 Python 编写一个程序,实现将指定文件夹中的 PDF 文件按序号合并成一个新 PDF,并且这个新 PDF 要包含可点击的目录(左侧),点击目录项能跳转到对应的 PDF 内容(右侧),最终合并后的 PDF 文件名也由你指定。
实现思路
依赖库准备:使用 PyPDF2 处理 PDF 合并和目录(书签)添加,os 处理文件路径,tkinter 提供简单的图形化输入界面(方便你输入文件夹路径、输出文件名)。
核心步骤:
获取指定文件夹下的所有 PDF 文件,并按文件名序号排序;
创建新的 PDF 写入对象,逐个合并 PDF 文件;
为每个合并的 PDF 文件添加书签(目录项),并设置书签的跳转位置;
保存合并后的 PDF 文件,文件名由你指定。- import os
- import tkinter as tk
- from tkinter import filedialog, simpledialog, messagebox
- from PyPDF2 import PdfMerger, PdfReader, PdfWriter
- def get_sorted_pdf_files(folder_path):
- """
- 获取指定文件夹下的所有PDF文件,并按文件名中的数字序号排序
- :param folder_path: 目标文件夹路径
- :return: 排序后的PDF文件路径列表
- """
- pdf_files = []
- for file in os.listdir(folder_path):
- if file.lower().endswith('.pdf'):
- file_path = os.path.join(folder_path, file)
- pdf_files.append(file_path)
-
- # 按文件名中的数字序号排序(提取文件名中的数字,无数字则放最后)
- def extract_number(filename):
- import re
- numbers = re.findall(r'\d+', os.path.basename(filename))
- return int(numbers[0]) if numbers else 999999
-
- pdf_files.sort(key=extract_number)
- return pdf_files
- def merge_pdfs_with_bookmarks(folder_path, output_filename):
- """
- 合并PDF并添加书签(目录)
- :param folder_path: 源PDF文件夹路径
- :param output_filename: 输出PDF文件名(含路径)
- """
- # 获取排序后的PDF文件
- pdf_files = get_sorted_pdf_files(folder_path)
- if not pdf_files:
- messagebox.showerror("错误", "指定文件夹中未找到PDF文件!")
- return
-
- merger = PdfWriter()
- current_page = 0 # 记录当前合并到的页码
-
- for pdf_file in pdf_files:
- try:
- reader = PdfReader(pdf_file)
- num_pages = len(reader.pages)
-
- # 将当前PDF的所有页面添加到合并器
- for page in reader.pages:
- merger.add_page(page)
-
- # 添加书签(目录项):书签名为PDF文件名,跳转至当前PDF的第一页
- bookmark_name = os.path.splitext(os.path.basename(pdf_file))[0]
- merger.add_outline_item(
- title=bookmark_name,
- page_number=current_page, # 跳转的页码(从0开始)
- parent=None
- )
-
- # 更新当前页码
- current_page += num_pages
- print(f"成功处理:{pdf_file}")
- except Exception as e:
- messagebox.warning("警告", f"处理文件 {pdf_file} 失败:{str(e)}")
- continue
-
- # 保存合并后的PDF
- try:
- with open(output_filename, 'wb') as output_file:
- merger.write(output_file)
- messagebox.showinfo("成功", f"PDF合并完成!\n文件保存至:{output_filename}")
- except Exception as e:
- messagebox.showerror("错误", f"保存PDF失败:{str(e)}")
- finally:
- merger.close()
- def main():
- """主函数:图形化界面交互"""
- root = tk.Tk()
- root.title("PDF合并工具(带目录)")
- root.geometry("400x200")
-
- # 选择文件夹按钮
- def select_folder():
- folder_path = filedialog.askdirectory(title="选择包含PDF的文件夹")
- if folder_path:
- folder_var.set(folder_path)
-
- # 执行合并按钮
- def start_merge():
- folder_path = folder_var.get()
- if not folder_path:
- messagebox.showerror("错误", "请先选择PDF文件夹!")
- return
-
- # 输入输出文件名
- output_name = simpledialog.askstring("输入", "请输入合并后的PDF文件名(无需加.pdf):")
- if not output_name:
- messagebox.showwarning("警告", "文件名不能为空!")
- return
-
- # 拼接输出文件路径(保存到源文件夹)
- output_filename = os.path.join(folder_path, f"{output_name}.pdf")
- merge_pdfs_with_bookmarks(folder_path, output_filename)
-
- # 界面控件
- folder_var = tk.StringVar()
- tk.Label(root, text="PDF文件夹路径:").pack(pady=10)
- tk.Entry(root, textvariable=folder_var, width=50).pack(pady=5)
- tk.Button(root, text="选择文件夹", command=select_folder).pack(pady=5)
- tk.Button(root, text="开始合并PDF", command=start_merge, bg="#4CAF50", fg="white").pack(pady=10)
-
- root.mainloop()
- if __name__ == "__main__":
- # 安装依赖提示(首次运行需执行)
- try:
- import PyPDF2
- except ImportError:
- print("正在安装依赖库 PyPDF2...")
- os.system("pip install PyPDF2")
- import PyPDF2
-
- main()
复制代码
代码使用说明
环境准备:
确保已安装 Python(3.7 及以上版本);
首次运行代码时,程序会自动安装依赖库 PyPDF2(若未安装),也可手动执行:
bash
运行
pip install PyPDF2
操作步骤:
运行代码,弹出图形化界面;
点击「选择文件夹」,选中存放待合并 PDF 的文件夹;
点击「开始合并 PDF」,输入合并后的文件名(无需加.pdf后缀);
等待程序处理完成,合并后的 PDF 会保存在你选择的文件夹中。
关键逻辑说明:
get_sorted_pdf_files:提取文件夹中的 PDF 文件,并按文件名中的数字序号排序(例如:1.pdf、2.pdf、10.pdf 会按 1、2、10 排序);
merge_pdfs_with_bookmarks:核心合并逻辑,每合并一个 PDF 就添加一个书签(目录项),书签名称为 PDF 文件名,跳转至该 PDF 的第一页;
图形化界面(tkinter):简化你输入路径和文件名的操作,无需手动修改代码。
总结
核心功能:按序号合并 PDF + 生成可点击的书签(左侧目录),点击目录项可跳转至对应 PDF 内容;
使用方式:运行代码→选文件夹→输文件名→完成合并,操作简单无需修改代码;
依赖:仅需安装PyPDF2库,兼容 Python3.7+,合并后的 PDF 自动保存在源文件夹中。
注意:若 PDF 文件加密或损坏,程序会给出警告并跳过该文件;排序逻辑优先提取文件名中的数字,无数字的文件会排在最后。
|
|