2026年10月21日 星期三

請AGY神器,協助生成離線版PDF檔合併工具,Combine PDF Files Using Python

情境:筆者換電腦沒有PDF合併工具,而AGY CLI具備程式生成能力,且筆者曾購買過好用合併工具需具備預覽功能相當實用(順道測試它的生成翻轉功能神力),下達下面藍色提示詞(二次性,首先生成骨幹;第2次補強具備預覽功能)如下:

首次PROMPT  :生成一個離線版PDF檔合併工具,可以翻轉等基本功能,嚴禁有上傳客戶重要資料行為,請運用PYHTON開放源碼工具 


補提(追加提示詞) : 看不到預覽視窗,不知道翻轉實際狀況,請更新PY程式

PDF檔案合併(單頁)翻轉  (大約340行PYTHON程式),只要在相同路徑執行PYTHON MergePDF.py 即可立馬擁有GUI圖形界面(含預覽) 離線版PDF合併工具。 


💜首次執行,可能會因為缺乏LIB套件,可以自行下達指令安裝

pip install pypdf pymupdf pillow

💜將欠缺套件模組,貼給AGY CLI神器,請它協助代為安裝

ModuleNotFoundError: No module named 'pypdf' 請協助安裝

  ## 🗂️ 一、

程式碼套件 費曼角色比喻 在程式裡的負責工作
tkinter (tk, ttk) 🏗️ 店面裝潢師傅 負責蓋出視窗、按鈕、左邊的表格清單 (Treeview) 與右邊的黑底畫布 (Canvas)。
fitz (PyMuPDF) 📸 高速翻拍神攝手 Python 內建視窗看不懂 PDF 格式。fitz 負責以極快速度把 PDF 的每一頁「拍照(渲染成像素)」給右邊畫面預覽。
PIL (Image, ImageTk) 🖼️ 相片沖洗師 把 fitz 拍出來的原始像素 (frombytes),沖洗成 tkinter 畫布唯一看得懂的相框格式 (PhotoImage)。
pypdf (PdfReader, PdfWriter) 📕 無損裝訂師傅 當您最後按下「執行合併」時,它直接針對原始 PDF 進行向量級別的頁面旋轉與裝訂,畫質與文字完全不會因為預覽縮放而變模糊!

  ## 🗂️ 二、程式的大腦記憶庫:self.file_list(__init__)

 PDFToolApp.__init__ 中,整支程式最重要的核心是 self.file_list 這個清單。

每當您透過 add_files 加入一個 PDF 檔,程式就會幫這個檔案建立一張 「數位身分證(字典 Dict)」:

    {

        "path": "C:/.../報告A.pdf",          # 1. 檔案住在硬碟哪裡(最後給 pypdf 裝訂用)

        "name": "報告A.pdf",                 # 2. 顯示在左邊表格的簡短檔名

        "doc": doc,                                 # 3. fitz 打開的書本物件(隨時翻頁拍照用)

        "pages": 5,                                 # 4. 這本書總共有幾頁

        "page_rotations": {0: 0, 1: 90, ...} # 5. 【關鍵設計】每一頁「預計要轉幾度」的便利貼!

    }


💡 費曼亮點解析(非破壞性編輯):

注意 "page_rotations"!當您在預覽畫面按「順轉 90°」時,程式 並沒有真的去改動硬碟裡的原始 PDF,它只是在這張便利貼上記錄:第 1 頁要轉 90 度。這樣不管您怎麼亂轉,原檔永遠安全!

  ## ⚙️ 三、五大核心功能模組拆解(跟著您的滑鼠點擊走一遍)

  ### 1. 介面佈局:左右分欄的工作桌(_setup_ui)

  • 使用 ttk.PanedWindow(可左右拉動調整寬度的分隔窗):

  • 左邊 (weight=3):放 self.tree(Treeview 表格),顯示「順序、檔案名稱、總頁數」。

  • 右邊 (weight=4):放 self.preview_canvas(深灰色 #2B2B2B 畫布),用來顯示 PDF 預覽圖。

  • 隱藏版神細節(L123):

  self.preview_canvas.bind("<Configure>", lambda e: self.render_preview())

  這行的意思是:「只要使用者用滑鼠拉大或縮小視窗(觸發 <Configure> 事件),就立刻重新計算比例重畫一次 PDF!」  所以預覽圖永遠會自動塞滿視窗且不變形。

  ### 2. 角度計算機:為什麼要 % 360?(rotate_current_page & rotate_document)

     file_item["page_rotations"][p] = (file_item["page_rotations"][p] + angle) % 360

  • 費曼比喻(時鐘算術):

  圓圈轉一圈是 360 度。如果您連按 4 次「順轉 90°」,角度會變成 360°,除以 360 的餘數(% 360)剛好變回 0°;如果您按「逆轉 -  90°」,在 Python 中 (0 - 90) % 360 會自動算出等同於順時針轉 270°!一行程式碼就搞定正轉、逆轉與歸零。

  ### 3. 即時預覽引擎:把 PDF 變成等比例圖片(render_preview)

  這是整支程式技術含量最高的地方,分成 4 步:

  1. 翻開指定頁並轉向(L247-L248):從 fitz 取出第 page_idx 頁,套用便利貼上記錄的旋轉角度 page.set_rotation(rot)。

  2. 計算「不超出畫布」的縮放倍率(L251-L254):

      • 畫布寬度扣掉邊距 20px 除以頁面寬度 = scale_w

      • 畫布高度扣掉邊距 20px 除以頁面高度 = scale_h

      • 取最小值 scale = min(scale_w, scale_h, 2.0):確保 「長邊剛好頂到畫布邊緣、短邊留空」(長寬比絕不變形),且最多放大 2  倍避免記憶體爆掉。

  3. 記憶體內無痕轉圖(L258-L261):

 page.get_pixmap(...) 拍照 → Image.frombytes(...) 轉成 PIL 影像 → ImageTk.PhotoImage(img) 轉成 Tkinter 圖檔。全程在記憶體 (RAM)  完成,硬碟不留任何暫存圖片檔!

  4. 為什麼要用 self.tk_preview_img 存起來?(L261)

      • Python 新手最常踩的坑:Tkinter 的畫布非常健忘,如果您只寫區域變數 tk_img = ImageTk.PhotoImage(img),函式一結束 Python  的「垃圾回收機制 (Garbage Collector)」就會把圖片當垃圾丟掉,畫布上會一片空白!綁在 self.tk_preview_img 上,就像用圖釘把照片釘在牆上,才不會消失。

  ### 4. 上移與下移:Python 招牌「一手交錢一手交貨」(move_item)

    self.file_list[idx], self.file_list[target_idx] = (

        self.file_list[target_idx],

        self.file_list[idx],

    )

 當您按「▲ 上移」(direction = -1) 或「▼ 下移」(direction = 1) 時,直接用 Python 獨有的 Tuple 交換語法,把清單裡兩個檔案的位置對調,然後重新整理左側表格。

  ### 5. 最終裝訂出廠(merge_pdfs)

  當您按下右下角「執行合併並匯出 PDF」時:

  1. 請出裝訂師傅 writer = PdfWriter()(準備一本空白新書)。

  2. 照著 self.file_list 排好的順序,用 PdfReader(item["path"]) 一本一本讀進來。

  3. 一頁一頁檢查便利貼 item["page_rotations"]:如果那一頁記錄的角度不是 0,就呼叫 page.rotate(angle) 把它轉正,然後 writer.add_page(page) 釘進新書裡。

  4. 最後 writer.write(f_out) 寫入您指定的儲存路徑,大功告成!

  ──────

  ## 🔄 四、整體運作架構圖

    [使用者操作 (User Events)]
         │
         ├──► (1. 新增 PDF) ──► fitz.open() 讀取頁數 + 建立 page_rotations 旋轉紀錄表
         │
         ├──► (2. 點選/翻頁/旋轉)
         │         │
         │         ▼
         │   [render_preview() 記憶體即時預覽管線 (RAM Pixel Pipeline)]
         │   fitz 頁面套用角度 ──► Matrix 等比例縮放 ──► get_pixmap 像素
         │         ──► PIL Image.frombytes ──► ImageTk.PhotoImage ──► Canvas 置中顯示
         │
         └──► (3. 執行合併並匯出 PDF)
                   │
                   ▼
             [merge_pdfs() 無損向量裝訂管線 (Lossless Vector Pipeline)]
             依序讀取 file_list ──► PdfReader 逐頁提取 ──► 套用 page_rotations 角度
                   ──► PdfWriter.add_page() ──► 寫入本地硬碟新 PDF
      

📌 管線對比與職責分工

管線名稱 核心處理介質 核心套件 關鍵指標
1. 狀態追蹤管線 檔案元數據、旋轉字典 fitz + Python dict 頁數即時解析、索引對照
2. 記憶體預覽管線 螢幕 RGB 像素矩陣 fitz + PIL + Canvas 低延遲 (<50ms)、動態自適應
3. 向量裝訂管線 二進位 PDF 語法樹 pypdf (Reader / Writer) 100% 原始向量保真、無損無失真

  

相關資訊:

PYTHON 需安裝,電腦才能知道PY檔是什麼,透過PYTHON直譯器協助解譯PY檔

PDF相關工具

Merge Multiple PDF in one