Skip to content

Commit f51b3ff

Browse files
authored
Create Split-PDF-Tool.py
1 parent 656b59a commit f51b3ff

1 file changed

Lines changed: 188 additions & 0 deletions

File tree

62-PDF-merger/Split-PDF-Tool.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import os
2+
import threading
3+
import tkinter as tk
4+
from tkinter import filedialog, messagebox
5+
from tkinterdnd2 import DND_FILES, TkinterDnD
6+
import ttkbootstrap as tb
7+
from ttkbootstrap.constants import *
8+
from PyPDF2 import PdfReader, PdfWriter
9+
from pdf2image import convert_from_path
10+
from PIL import Image, ImageTk
11+
12+
# ---------------- GLOBALS ---------------- #
13+
pdf_path = None
14+
pages = []
15+
preview_cache = {}
16+
drag_index = None
17+
18+
# ---------------- CORE FUNCTIONS ---------------- #
19+
def load_pdf(path):
20+
global pdf_path, pages
21+
pdf_path = path
22+
pages.clear()
23+
listbox.delete(0, tk.END)
24+
25+
reader = PdfReader(path)
26+
for i in range(len(reader.pages)):
27+
pages.append(i)
28+
listbox.insert(tk.END, f"Page {i+1}")
29+
30+
def on_drop(event):
31+
files = app.tk.splitlist(event.data)
32+
for f in files:
33+
f = f.strip("{}")
34+
if f.lower().endswith(".pdf"):
35+
load_pdf(f)
36+
break
37+
38+
def add_pdf():
39+
f = filedialog.askopenfilename(filetypes=[("PDF Files", "*.pdf")])
40+
if f:
41+
load_pdf(f)
42+
43+
def refresh_list():
44+
listbox.delete(0, tk.END)
45+
for p in pages:
46+
listbox.insert(tk.END, f"Page {p+1}")
47+
48+
# ---------------- PAGE PREVIEW ---------------- #
49+
def show_preview(event=None):
50+
sel = listbox.curselection()
51+
if not sel or not pdf_path:
52+
return
53+
54+
page_num = pages[sel[0]]
55+
key = (pdf_path, page_num)
56+
57+
if key in preview_cache:
58+
preview_label.config(image=preview_cache[key])
59+
preview_label.image = preview_cache[key]
60+
return
61+
62+
img = convert_from_path(
63+
pdf_path, first_page=page_num+1, last_page=page_num+1
64+
)[0]
65+
img.thumbnail((300, 400))
66+
tk_img = ImageTk.PhotoImage(img)
67+
preview_cache[key] = tk_img
68+
preview_label.config(image=tk_img)
69+
preview_label.image = tk_img
70+
71+
# ---------------- DRAG REORDER ---------------- #
72+
def drag_start(e):
73+
global drag_index
74+
drag_index = listbox.nearest(e.y)
75+
76+
def drag_release(e):
77+
global drag_index
78+
drop = listbox.nearest(e.y)
79+
if drag_index is not None and drop != drag_index:
80+
pages.insert(drop, pages.pop(drag_index))
81+
refresh_list()
82+
listbox.selection_set(drop)
83+
drag_index = None
84+
85+
# ---------------- ROTATE ---------------- #
86+
def rotate_selected(angle):
87+
sel = listbox.curselection()
88+
if not sel:
89+
return
90+
reader = PdfReader(pdf_path)
91+
writer = PdfWriter()
92+
93+
for i in pages:
94+
page = reader.pages[i]
95+
if i in [pages[s] for s in sel]:
96+
page.rotate(angle)
97+
writer.add_page(page)
98+
99+
save_output(writer)
100+
101+
# ---------------- SPLIT ---------------- #
102+
def split_pdf():
103+
if not pdf_path:
104+
return
105+
106+
start = simple_input("Start page (1-based)")
107+
end = simple_input("End page")
108+
109+
if not start or not end:
110+
return
111+
112+
start, end = int(start)-1, int(end)-1
113+
114+
reader = PdfReader(pdf_path)
115+
writer = PdfWriter()
116+
117+
for i in range(start, end+1):
118+
writer.add_page(reader.pages[i])
119+
120+
save_output(writer)
121+
122+
# ---------------- SAVE ---------------- #
123+
def save_output(writer):
124+
out = filedialog.asksaveasfilename(
125+
defaultextension=".pdf", filetypes=[("PDF Files", "*.pdf")]
126+
)
127+
if not out:
128+
return
129+
with open(out, "wb") as f:
130+
writer.write(f)
131+
messagebox.showinfo("Success", "Operation completed!")
132+
133+
# ---------------- INPUT DIALOG ---------------- #
134+
def simple_input(title):
135+
popup = tk.Toplevel(app)
136+
popup.title(title)
137+
popup.geometry("250x100")
138+
val = tk.StringVar()
139+
140+
tb.Label(popup, text=title).pack(pady=5)
141+
entry = tb.Entry(popup, textvariable=val)
142+
entry.pack(pady=5)
143+
entry.focus()
144+
145+
tb.Button(popup, text="OK", command=popup.destroy).pack()
146+
popup.wait_window()
147+
return val.get()
148+
149+
# ---------------- GUI ---------------- #
150+
app = TkinterDnD.Tk()
151+
app.title("Advanced PDF Tool")
152+
app.geometry("1000x600")
153+
tb.Style("darkly")
154+
155+
app.drop_target_register(DND_FILES)
156+
app.dnd_bind("<<Drop>>", on_drop)
157+
158+
main = tb.Frame(app, padding=10)
159+
main.pack(fill=BOTH, expand=True)
160+
161+
# Left panel
162+
left = tb.Frame(main)
163+
left.pack(side=LEFT, fill=BOTH, expand=True)
164+
165+
listbox = tk.Listbox(left)
166+
listbox.pack(fill=BOTH, expand=True)
167+
listbox.bind("<<ListboxSelect>>", show_preview)
168+
listbox.bind("<Button-1>", drag_start)
169+
listbox.bind("<ButtonRelease-1>", drag_release)
170+
171+
tb.Button(left, text="Load PDF", command=add_pdf, bootstyle=PRIMARY).pack(fill=X, pady=5)
172+
173+
# Right panel
174+
right = tb.Frame(main)
175+
right.pack(side=RIGHT, fill=Y, padx=10)
176+
177+
preview_label = tk.Label(right)
178+
preview_label.pack(pady=10)
179+
180+
tb.Button(right, text="Rotate 90°", command=lambda: rotate_selected(90), bootstyle=WARNING).pack(fill=X, pady=2)
181+
tb.Button(right, text="Rotate 180°", command=lambda: rotate_selected(180), bootstyle=WARNING).pack(fill=X, pady=2)
182+
tb.Button(right, text="Rotate 270°", command=lambda: rotate_selected(270), bootstyle=WARNING).pack(fill=X, pady=2)
183+
184+
tb.Separator(right).pack(fill=X, pady=5)
185+
186+
tb.Button(right, text="Split PDF", command=split_pdf, bootstyle=DANGER).pack(fill=X)
187+
188+
app.mainloop()

0 commit comments

Comments
 (0)