KapoorspaceCopenhagen / presentation_viewer.py
vkapoor's picture
presentation
6bf5b4d
import fitz # PyMuPDF
import os
import tkinter as tk
import sys
import subprocess
from tkinter import filedialog
from PIL import Image, ImageTk
class PDFWindowViewer:
def __init__(self, pdf_path):
self.doc = fitz.open(pdf_path)
self.total_pages = len(self.doc)
self.current_page = 0
self.root = tk.Tk()
self.root.title("Movable PDF Viewer")
self.root.geometry("1024x768") # Start size, can resize manually
self.root.configure(bg='black')
self.canvas = tk.Canvas(self.root, bg='black', highlightthickness=0)
self.canvas.pack(fill=tk.BOTH, expand=True)
self.tk_img = None
self.img_size = None
self.canvas.bind("<Configure>", self.render_page)
self.canvas.bind("<Button-1>", self.on_click)
self.root.bind("<Right>", self.next_slide)
self.root.bind("<Left>", self.prev_slide)
self.root.bind("<Escape>", lambda e: self.root.destroy())
self.root.mainloop()
def render_page(self, event=None):
page = self.doc[self.current_page]
canvas_width = self.canvas.winfo_width()
canvas_height = self.canvas.winfo_height()
pix = page.get_pixmap(dpi=150)
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
scale = min(canvas_width / pix.width, canvas_height / pix.height)
new_size = (int(pix.width * scale), int(pix.height * scale))
img = img.resize(new_size, Image.Resampling.LANCZOS)
self.img_size = new_size
self.tk_img = ImageTk.PhotoImage(img)
self.canvas.delete("all")
x = (canvas_width - new_size[0]) // 2
y = (canvas_height - new_size[1]) // 2
self.canvas.create_image(x, y, anchor=tk.NW, image=self.tk_img)
self.root.title(f"Slide {self.current_page + 1} / {self.total_pages}")
def next_slide(self, event=None):
if self.current_page < self.total_pages - 1:
self.current_page += 1
self.render_page()
def prev_slide(self, event=None):
if self.current_page > 0:
self.current_page -= 1
self.render_page()
def on_click(self, event=None):
canvas_w = self.canvas.winfo_width()
canvas_h = self.canvas.winfo_height()
img_w, img_h = self.img_size
x_offset = (canvas_w - img_w) // 2
y_offset = (canvas_h - img_h) // 2
x_click = event.x - x_offset
y_click = event.y - y_offset
if not (0 <= x_click <= img_w and 0 <= y_click <= img_h):
return # clicked outside the image
# Convert canvas coordinates to PDF coordinates
page = self.doc[self.current_page]
pdf_w, pdf_h = page.rect.width, page.rect.height
scale = img_w / pdf_w
x_pdf = x_click / scale
y_pdf = pdf_h - (y_click / scale)
click_point = fitz.Point(x_pdf, y_pdf)
links = page.get_links()
for link in links:
rect = link.get("from")
if rect and rect.contains(click_point):
uri = link.get("uri") or link.get("file")
if uri:
print(f"▶️ Opening: {uri}")
try:
if sys.platform.startswith("win"):
os.startfile(uri)
elif sys.platform.startswith("darwin"): # macOS
try:
subprocess.Popen(["open", uri])
except:
if uri.startswith("run:"):
filepath = uri.replace("run:", "")
else:
filepath = uri
subprocess.Popen(["open", filepath])
else: # Linux and others
if uri.startswith("run:"):
filepath = uri.replace("run:", "")
else:
filepath = uri
subprocess.Popen(["xdg-open", filepath])
except Exception as e:
print(f"❌ Failed to open: {uri} | {e}")
return # Stop after first matching link
print("⚠️ No clickable link matched the click location.")
# Select PDF
pdf_file = filedialog.askopenfilename(title="Select PDF", filetypes=[("PDF files", "*.pdf")])
if pdf_file:
PDFWindowViewer(pdf_file)