I had a scenario where I needed PDF from Quicket that had 20 tickets.
Problem: I needed each ticket as a separate JPG so I could send to those receiving them. Manual way would be to screenshot each ticket.
Solution: This python script split it up into JPG tickets but also names the tickets based on the ticket numbers in PDF.
import fitz # PyMuPDF
import re
import os
# ───────────── CONFIGURATION ─────────────
pdf_path = r'D:/Event Tickets/AllTickets.pdf' # path to your PDF
output_dir = r'D:/Event Tickets/' # folder to save individual PNGs
split_ratio = 0.43 # fraction down the page to split (e.g. 0.43 = 43%)
dpi_scale = 2 # image resolution multiplier
bottom_margin = 10 # pixels to add below content when trimming
# ──────────────────────────────────────────
os.makedirs(output_dir, exist_ok=True)
doc = fitz.open(pdf_path)
for page_index, page in enumerate(doc, start=1):
w, h = page.rect.width, page.rect.height
y_split = h * split_ratio
# Clip rect for top ticket
top_rect = fitz.Rect(0, 0, w, y_split)
# Collect spans & ticket‐number centers
ticket_positions = {}
bottom_span_ys = [] # for trimming
for block in page.get_text("dict")["blocks"]:
for line in block.get("lines", []):
for span in line.get("spans", []):
m = re.search(r"(QTK\d+)", span["text"])
y0, y1 = span["bbox"][1], span["bbox"][3]
center_y = (y0 + y1) / 2
# record ticket numbers
if m:
num = m.group(1)
if num not in ticket_positions:
ticket_positions[num] = center_y
# record spans that lie in bottom half region
if center_y > y_split:
bottom_span_ys.append(y1)
# Expect exactly two tickets per page
if len(ticket_positions) != 2:
print(f"⚠️ Page {page_index}: found {len(ticket_positions)} tickets!")
continue
# Decide how far down to crop bottom ticket
if bottom_span_ys:
max_content_y = max(bottom_span_ys)
bottom_end = min(max_content_y + bottom_margin, h)
else:
bottom_end = h # fallback to full height
# Clip rect for bottom ticket
bottom_rect = fitz.Rect(0, y_split, w, bottom_end)
# Sort tickets by vertical center to assign top/bottom
sorted_tickets = sorted(ticket_positions.items(), key=lambda kv: kv[1])
top_num, bottom_num = sorted_tickets[0][0], sorted_tickets[1][0]
# Render & save top ticket
pix_top = page.get_pixmap(matrix=fitz.Matrix(dpi_scale, dpi_scale), clip=top_rect)
top_path = os.path.join(output_dir, f"{top_num}.png")
pix_top.save(top_path)
# Render & save bottom ticket (trimmed)
pix_bot = page.get_pixmap(matrix=fitz.Matrix(dpi_scale, dpi_scale), clip=bottom_rect)
bot_path = os.path.join(output_dir, f"{bottom_num}.png")
pix_bot.save(bot_path)
doc.close()
print(f"Done! Saved images in: {output_dir}/")