File size: 4,650 Bytes
6bf5b4d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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)