情境:筆者換電腦沒有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檔