写一个可以编制pdf文件的python程序

python 复制代码
import io
import tkinter as tk
from tkinter import filedialog, messagebox
import fitz  # PyMuPDF
from PIL import Image, ImageTk


class PDFViewer:
    def __init__(self, root):
        self.root = root
        self.root.title("PDF Viewer & Editor")
        self.doc = None
        self.current_page = 0
        self.total_pages = 0
        self.create_widgets()

    def create_widgets(self):
        # ---------- 顶部控制栏 ----------
        top_frame = tk.Frame(self.root)
        top_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)

        self.page_label = tk.Label(top_frame, text="Page: 0/0")
        self.page_label.pack(side=tk.LEFT)

        tk.Label(top_frame, text="Go to:").pack(side=tk.LEFT, padx=5)
        self.go_to_entry = tk.Entry(top_frame, width=5)
        self.go_to_entry.pack(side=tk.LEFT)
        self.go_to_btn = tk.Button(top_frame, text="Go", command=self.go_to_page)
        self.go_to_btn.pack(side=tk.LEFT)

        self.open_btn = tk.Button(top_frame, text="Open PDF", command=self.open_pdf)
        self.open_btn.pack(side=tk.RIGHT)

        # ---------- 图像显示区域 ----------
        self.canvas = tk.Canvas(self.root, width=600, height=800, bg='gray')
        self.canvas.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)

        # ---------- 底部操作按钮 ----------
        bottom_frame = tk.Frame(self.root)
        bottom_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=5, pady=5)

        self.prev_btn = tk.Button(bottom_frame, text="Previous", command=self.prev_page)
        self.prev_btn.pack(side=tk.LEFT)
        self.next_btn = tk.Button(bottom_frame, text="Next", command=self.next_page)
        self.next_btn.pack(side=tk.LEFT)
        self.delete_btn = tk.Button(bottom_frame, text="Delete", command=self.delete_page)
        self.delete_btn.pack(side=tk.LEFT, padx=5)
        self.rotate_btn = tk.Button(bottom_frame, text="Rotate Left", command=self.rotate_page)
        self.rotate_btn.pack(side=tk.LEFT)

        self.insert_btn = tk.Button(bottom_frame, text="Insert PDF", command=self.insert_pdf_after_current)
        self.insert_btn.pack(side=tk.LEFT, padx=5)

        self.save_btn = tk.Button(bottom_frame, text="Save As...", command=self.save_pdf)
        self.save_btn.pack(side=tk.RIGHT)

        self.set_buttons_state(tk.DISABLED)

    def set_buttons_state(self, state):
        for btn in (self.prev_btn, self.next_btn, self.delete_btn,
                    self.rotate_btn, self.insert_btn, self.save_btn, self.go_to_btn):
            btn.config(state=state)

    def open_pdf(self):
        file_path = filedialog.askopenfilename(filetypes=[("PDF files", "*.pdf")])
        if not file_path:
            return
        try:
            if self.doc:
                self.doc.close()
            self.doc = fitz.open(file_path)
            self.total_pages = len(self.doc)
            self.current_page = 0
            self.set_buttons_state(tk.NORMAL)
            self.show_page()
        except Exception as e:
            messagebox.showerror("Error", f"Failed to open PDF: {e}")

    def show_page(self):
        if not self.doc or self.total_pages == 0:
            self.canvas.delete("all")
            self.page_label.config(text="Page: 0/0")
            return

        if self.current_page < 0:
            self.current_page = 0
        if self.current_page >= self.total_pages:
            self.current_page = self.total_pages - 1

        page = self.doc.load_page(self.current_page)
        pix = page.get_pixmap(matrix=fitz.Matrix(1, 1))
        img_data = pix.tobytes("png")
        img = Image.open(io.BytesIO(img_data))

        canvas_width = self.canvas.winfo_width() if self.canvas.winfo_width() > 1 else 600
        canvas_height = self.canvas.winfo_height() if self.canvas.winfo_height() > 1 else 800

        img_width, img_height = img.size
        scale = min(canvas_width / img_width, canvas_height / img_height, 1.0)
        new_size = (int(img_width * scale), int(img_height * scale))
        img = img.resize(new_size, Image.Resampling.LANCZOS)

        self.tk_image = ImageTk.PhotoImage(img)
        self.canvas.delete("all")
        self.canvas.create_image(canvas_width // 2, canvas_height // 2,
                                 anchor=tk.CENTER, image=self.tk_image)
        self.page_label.config(text=f"Page: {self.current_page + 1}/{self.total_pages}")

    def prev_page(self):
        if self.current_page > 0:
            self.current_page -= 1
            self.show_page()

    def next_page(self):
        if self.current_page < self.total_pages - 1:
            self.current_page += 1
            self.show_page()

    def go_to_page(self):
        try:
            page_num = int(self.go_to_entry.get()) - 1
            if 0 <= page_num < self.total_pages:
                self.current_page = page_num
                self.show_page()
            else:
                messagebox.showwarning("Invalid", f"Page number must be between 1 and {self.total_pages}")
        except ValueError:
            messagebox.showwarning("Invalid", "Please enter a valid number")

    def delete_page(self):
        if not self.doc or self.total_pages == 0:
            return
        if messagebox.askyesno("Delete", f"Delete page {self.current_page + 1}?"):
            self.doc.delete_page(self.current_page)
            self.total_pages = len(self.doc)
            if self.total_pages == 0:
                self.doc.close()
                self.doc = None
                self.set_buttons_state(tk.DISABLED)
                self.show_page()
                return
            if self.current_page >= self.total_pages:
                self.current_page = self.total_pages - 1
            self.show_page()

    def rotate_page(self):
        if not self.doc or self.total_pages == 0:
            return
        page = self.doc.load_page(self.current_page)
        new_rot = (page.rotation - 90) % 360
        page.set_rotation(new_rot)
        self.show_page()

    # ========== 插入 PDF(使用图像方式,兼容所有 PyMuPDF 版本) ==========
    def insert_pdf_after_current(self):
        """在当前页之后插入另一个 PDF,所有页面自动缩放至当前页尺寸"""
        if not self.doc or self.total_pages == 0:
            messagebox.showwarning("No document", "Please open a PDF first.")
            return

        file_path = filedialog.askopenfilename(filetypes=[("PDF files", "*.pdf")])
        if not file_path:
            return

        try:
            src_doc = fitz.open(file_path)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to open insert PDF: {e}")
            return

        if len(src_doc) == 0:
            messagebox.showwarning("Empty", "The selected PDF has no pages.")
            src_doc.close()
            return

        # 获取当前页尺寸
        current_page = self.doc.load_page(self.current_page)
        target_rect = current_page.rect
        target_width = target_rect.width
        target_height = target_rect.height

        insert_index = self.current_page + 1
        new_pages = []  # 记录新页索引

        # 对源文档的每一页,渲染为图像并插入到新页
        for i in range(len(src_doc)):
            src_page = src_doc[i]
            # 提高分辨率(2倍),保证打印清晰
            pix = src_page.get_pixmap(matrix=fitz.Matrix(2, 2))
            img_data = pix.tobytes("png")
            img = Image.open(io.BytesIO(img_data))
            img_width, img_height = img.size

            # 计算缩放比例(保持宽高比,居中)
            scale = min(target_width / img_width, target_height / img_height)
            new_w = int(img_width * scale)
            new_h = int(img_height * scale)
            x_offset = (target_width - new_w) / 2
            y_offset = (target_height - new_h) / 2

            # 调整图像大小
            img_resized = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
            img_bytes = io.BytesIO()
            img_resized.save(img_bytes, format="PNG")
            img_bytes.seek(0)

            # 创建新页(尺寸为目标尺寸)
            new_page = self.doc.new_page(width=target_width, height=target_height)
            # 插入图像
            new_page.insert_image(
                fitz.Rect(x_offset, y_offset, x_offset + new_w, y_offset + new_h),
                stream=img_bytes.getvalue()
            )
            new_pages.append(len(self.doc) - 1)  # 记录末尾索引

        # 将新页从末尾移动到目标位置(从后往前移动)
        old_total = len(self.doc) - len(src_doc)
        for i in range(len(src_doc) - 1, -1, -1):
            src_index = old_total + i
            dest_index = insert_index + i
            self.doc.move_page(src_index, dest_index)

        self.total_pages = len(self.doc)
        self.show_page()
        messagebox.showinfo(
            "Success",
            f"Inserted {len(src_doc)} page(s) after page {self.current_page + 1}.\n"
            "All pages have been resized and centered."
        )
        src_doc.close()

    # ========== 保存 PDF ==========
    def save_pdf(self):
        if not self.doc:
            return
        file_path = filedialog.asksaveasfilename(
            defaultextension=".pdf",
            filetypes=[("PDF files", "*.pdf")]
        )
        if file_path:
            try:
                self.doc.save(file_path)
                messagebox.showinfo("Success", f"PDF saved as {file_path}")
            except Exception as e:
                messagebox.showerror("Error", f"Failed to save: {e}")

    def on_closing(self):
        if self.doc:
            self.doc.close()
        self.root.destroy()


if __name__ == "__main__":
    root = tk.Tk()
    app = PDFViewer(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()

可以在某个页面插入pdf文件,和删除页面,然后可以保存。方便上班族扫描pdf文件,需要去替换一些页面。

相关推荐
卷无止境1 小时前
从Python的cryptography库出发,从零开始理解密码学,到构建零信任网络
后端·python
YFJ_mily1 小时前
**Python 实战:写一个论文 PDF 投稿自检工具|附 IPAT 2026 智能光子学会议征稿信息
人工智能·python·pdf·量子计算·论文投稿·智能光子学
三川6981 小时前
Tkinter库的学习记录05-文本框Entry
python
cui_ruicheng1 小时前
Python从入门到实战(八):封装、多态与抽象类
开发语言·python
apihz1 小时前
台风实时与历史详情查询免费 API 接口完整教程
android·开发语言·tcp/ip·dubbo·台风·天气预报
qq_422152571 小时前
PDF内容复用的几种实用方法:转PPT、转图片、转长图
pdf·php·powerpoint
知无不研1 小时前
c语言和c++中的静态关键字
开发语言·c++·静态关键字
nanawinona2 小时前
2026年AI量化工具怎么选,先定位学习开发还是执行
人工智能·python
bksczm2 小时前
Linux之信号量(POSIX标准)
java·开发语言