PDF拆分与合并工具

使用“万兴PDF”有点麻烦,且无拆分功能

python写

作者:chatGPT && 我(inmark.dev)

GUI版:PDF工具.exe

源码

无GUI版

点击展开/折叠
from pypdf import PdfReader, PdfWriter
import os
import sys
import re

def split_pdf(input_path, output_dir):
    """将指定 PDF 拆分为单页文件"""
    if not os.path.exists(input_path):
        print(f"❌ 找不到文件:{input_path}")
        return

    try:
        reader = PdfReader(input_path)
    except Exception as e:
        print(f"❌ 无法读取 PDF 文件:{e}")
        return

    total_pages = len(reader.pages)
    print(f"📄 原始 PDF 页数:{total_pages}")

    os.makedirs(output_dir, exist_ok=True)

    for i in range(total_pages):
        writer = PdfWriter()
        writer.add_page(reader.pages[i])
        output_path = os.path.join(output_dir, f"{i + 1}.pdf")
        with open(output_path, "wb") as f:
            writer.write(f)
        print(f"[{i + 1}/{total_pages}] ✅ 已保存:{output_path}")

    print(f"\n🎉 拆分完成!输出目录:{os.path.abspath(output_dir)}")


def merge_pdfs(input_dir):
    """将目录下的 PDF 文件按数字顺序合并,并以目录名命名输出文件"""
    if not os.path.isdir(input_dir):
        print(f"❌ 目录不存在:{input_dir}")
        return

    pdf_files = [f for f in os.listdir(input_dir) if f.lower().endswith('.pdf')]
    if not pdf_files:
        print("⚠️ 该目录下没有找到 PDF 文件。")
        return

    # 提取数字排序,例如 1.pdf, 2.pdf, 10.pdf
    def extract_num(filename):
        match = re.search(r"(\d+)", filename)
        return int(match.group(1)) if match else float("inf")

    pdf_files.sort(key=extract_num)

    writer = PdfWriter()

    for pdf in pdf_files:
        path = os.path.join(input_dir, pdf)
        try:
            reader = PdfReader(path)
            for page in reader.pages:
                writer.add_page(page)
            print(f"✅ 已合并:{pdf}")
        except Exception as e:
            print(f"⚠️ 跳过文件 {pdf},原因:{e}")

    # 输出文件路径与目录同名
    output_path = os.path.abspath(f"{input_dir}.pdf")

    with open(output_path, "wb") as f:
        writer.write(f)

    print(f"\n🎉 合并完成!输出文件:{output_path}")


def choose_pdf_in_directory():
    """列出当前目录下的 PDF 文件,并让用户选择"""
    pdf_files = [f for f in os.listdir('.') if f.lower().endswith('.pdf')]
    if not pdf_files:
        print("⚠️ 当前目录下没有找到任何 PDF 文件。")
        sys.exit(0)

    print("📂 当前目录下的 PDF 文件:")
    for idx, pdf in enumerate(pdf_files, start=1):
        print(f"{idx}. {pdf}")

    while True:
        choice = input("\n请输入要拆分的 PDF 编号:").strip()
        if not choice.isdigit():
            print("❌ 请输入数字编号。")
            continue

        choice = int(choice)
        if 1 <= choice <= len(pdf_files):
            selected_pdf = pdf_files[choice - 1]
            print(f"✅ 已选择:{selected_pdf}")
            return selected_pdf
        else:
            print("❌ 编号超出范围,请重新输入。")


def main():
    print("=== PDF 工具 ===")
    print("1️⃣ 拆分 PDF")
    print("2️⃣ 合并 PDF")
    mode = input("\n请选择操作模式 (1 或 2):").strip()

    if mode == "1":
        selected_file = choose_pdf_in_directory()
        base_name = os.path.splitext(selected_file)[0]
        output_dir = f"{base_name}_pages"
        split_pdf(selected_file, output_dir)

    elif mode == "2":
        input_dir = input("\n输入合并PDF文件夹名(PDF按命名1.pdf,2.pdf,3.pdf...合并):").strip()
        if not input_dir:
            print("❌ 目录名称不能为空。")
            return
        merge_pdfs(input_dir)

    else:
        print("❌ 无效输入,请输入 1 或 2。")


if __name__ == "__main__":
    main()
    input("\n按回车键退出程序...")

GUI版

点击展开/折叠
import os
import re
import webbrowser
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from pypdf import PdfReader, PdfWriter


def split_pdf(input_path, output_dir, progress_bar, progress_label, window):
    """将指定 PDF 拆分为单页文件,并更新进度条"""
    try:
        reader = PdfReader(input_path)
        total_pages = len(reader.pages)
        os.makedirs(output_dir, exist_ok=True)

        for i in range(total_pages):
            writer = PdfWriter()
            writer.add_page(reader.pages[i])
            output_path = os.path.join(output_dir, f"{i + 1}.pdf")
            with open(output_path, "wb") as f:
                writer.write(f)

            # 更新进度条
            percent = int(((i + 1) / total_pages) * 100)
            progress_bar["value"] = percent
            progress_label.config(text=f"进度:{percent}% ({i + 1}/{total_pages})")
            window.update_idletasks()

        messagebox.showinfo("完成", f"已成功拆分为 {total_pages} 页。\n输出目录:\n{os.path.abspath(output_dir)}")
    except Exception as e:
        messagebox.showerror("错误", f"拆分失败:{e}")
    finally:
        progress_bar["value"] = 0
        progress_label.config(text="进度:0%")


def merge_pdfs(input_dir, progress_bar, progress_label, window):
    """将目录下的 PDF 文件按数字顺序合并,并更新进度条"""
    try:
        pdf_files = [f for f in os.listdir(input_dir) if f.lower().endswith(".pdf")]
        if not pdf_files:
            messagebox.showwarning("提示", "该目录下没有 PDF 文件。")
            return

        def extract_num(filename):
            match = re.search(r"(\d+)", filename)
            return int(match.group(1)) if match else float("inf")

        pdf_files.sort(key=extract_num)
        writer = PdfWriter()

        total_files = len(pdf_files)
        for idx, pdf in enumerate(pdf_files, start=1):
            path = os.path.join(input_dir, pdf)
            reader = PdfReader(path)
            for page in reader.pages:
                writer.add_page(page)

            # 更新进度条
            percent = int((idx / total_files) * 100)
            progress_bar["value"] = percent
            progress_label.config(text=f"进度:{percent}% ({idx}/{total_files})")
            window.update_idletasks()

        output_path = os.path.abspath(f"{input_dir}.pdf")
        with open(output_path, "wb") as f:
            writer.write(f)

        messagebox.showinfo("完成", f"已成功合并 {total_files} 个文件。\n输出文件:\n{output_path}")
    except Exception as e:
        messagebox.showerror("错误", f"合并失败:{e}")
    finally:
        progress_bar["value"] = 0
        progress_label.config(text="进度:0%")


def choose_split_file(progress_bar, progress_label, window):
    file_path = filedialog.askopenfilename(title="选择要拆分的 PDF 文件", filetypes=[("PDF 文件", "*.pdf")])
    if not file_path:
        return
    base_name = os.path.splitext(os.path.basename(file_path))[0]
    output_dir = f"{base_name}_pages"
    split_pdf(file_path, output_dir, progress_bar, progress_label, window)


def choose_merge_dir(progress_bar, progress_label, window):
    dir_path = filedialog.askdirectory(title="选择要合并的 PDF 目录")
    if not dir_path:
        return
    merge_pdfs(dir_path, progress_bar, progress_label, window)


def open_website(event=None):
    """打开发布网址"""
    webbrowser.open("https://inmark.dev")


def main():
    window = tk.Tk()
    window.title("PDF 工具 - 拆分与合并")
    window.geometry("520x440")
    window.resizable(False, False)

    # 标题
    tk.Label(window, text="PDF 工具", font=("Microsoft YaHei", 20, "bold")).pack(pady=10)

    # 说明文字
    desc_text = (
        "📄 拆分 PDF:选择单个 PDF → 自动拆页到 {文件名}_pages 文件夹。\n\n"
        "🧩 合并 PDF:选择一个文件夹 → 自动合并里面的 1.pdf, 2.pdf, 3.pdf... → 生成 {文件夹名}.pdf"
    )
    tk.Label(window, text=desc_text, font=("Microsoft YaHei", 10), justify="left",
             wraplength=480, fg="#333").pack(pady=10)

    # 按钮框
    btn_frame = tk.Frame(window)
    btn_frame.pack(pady=10)

    btn_split = tk.Button(btn_frame, text="📄 拆分 PDF", font=("Microsoft YaHei", 12),
                          width=20, height=2, bg="#4CAF50", fg="white",
                          command=lambda: choose_split_file(progress_bar, progress_label, window))
    btn_split.grid(row=0, column=0, padx=15, pady=5)

    btn_merge = tk.Button(btn_frame, text="🧩 合并 PDF", font=("Microsoft YaHei", 12),
                          width=20, height=2, bg="#2196F3", fg="white",
                          command=lambda: choose_merge_dir(progress_bar, progress_label, window))
    btn_merge.grid(row=0, column=1, padx=15, pady=5)

    # 进度条区域
    progress_frame = tk.Frame(window)
    progress_frame.pack(pady=20)

    progress_label = tk.Label(progress_frame, text="进度:0%", font=("Microsoft YaHei", 10))
    progress_label.pack(pady=5)

    progress_bar = ttk.Progressbar(progress_frame, length=400, mode='determinate')
    progress_bar.pack()

    # 发布网址(可点击)
    link_label = tk.Label(window, text="🌐 发布网址:https://inmark.dev",
                          font=("Microsoft YaHei", 9, "underline"), fg="#1E88E5", cursor="hand2")
    link_label.pack(pady=5)
    link_label.bind("<Button-1>", open_website)

    # 作者信息
    tk.Label(window, text="作者:ChatGPT && inmark.dev", font=("Microsoft YaHei", 9),
             fg="gray").pack(side="bottom", pady=8)

    window.mainloop()


if __name__ == "__main__":
    main()

消息盒子

# 暂无消息 #

只显示最新10条未读和已读信息