Hi I am trying to create a program that allows a pdf to be annotated using .png and then exported but when I export the pdf the tick.png are all in the wrong locations to run the program you have to have a tick.png file in the same location as the .py file import tkinter as tk from tkinter import filedialog, messagebox from PIL import Image, ImageTk import fitz # PyMuPDF import os import json class PDFAnnotator: def __init__(self): self.root = tk.Tk() self.root.title("PDF Annotator") self.pdf = None self.page_index = 0 self.annotations = [] # [page, tool, canvas_id, x_rel, y_rel] self.current_tool = None self.tick_count = 0 self.undo_stack = [] self.canvas_width = 600 self.canvas_height = 800 self.canvas = tk.Canvas(self.root, width=self.canvas_width, height=self.canvas_height, bg='grey') self.canvas.pack(side=tk.LEFT) self.canvas.bind("<Button-1>", self.on_click) self.canvas.bind("<B1-Motion>", self.on_drag) self.canvas.bind("<ButtonRelease-1>", self.on_drop) ctrl = tk.Frame(self.root) ctrl.pack(side=tk.RIGHT, fill=tk.Y) tk.Button(ctrl, text="Load PDF", command=self.load_pdf).pack(pady=5) tk.Button(ctrl, text="Prev Page", command=lambda: self.change_page(-1)).pack(pady=5) tk.Button(ctrl, text="Next Page", command=lambda: self.change_page(1)).pack(pady=5) self.icons = {} self.tk_icons = {} for name in ["tick", "cross", "noresponse", "TV", "BOD", "NDOB"]: path = f"{name}.png" if os.path.exists(path): img = Image.open(path).resize((30, 30)).convert("RGBA") img.save(path) self.tk_icons[name] = ImageTk.PhotoImage(img) self.icons[name] = path tk.Button(ctrl, text=name, command=lambda n=name: self.select_tool(n)).pack(pady=2) tk.Button(ctrl, text="Eraser", command=lambda: self.select_tool("eraser")).pack(pady=10) tk.Button(ctrl, text="Undo", command=self.undo).pack(pady=10) tk.Button(ctrl, text="Save Progress", command=self.save_progress).pack(pady=10) tk.Button(ctrl, text="Load Progress", command=self.load_progress).pack(pady=10) tk.Button(ctrl, text="Export PDF", command=self.export_pdf).pack(pady=15) self.tick_label = tk.Label(ctrl, text="Ticks: 0") self.tick_label.pack(pady=5) self.drag_data = {'item': None, 'dx': 0, 'dy': 0} self.page_render_info = {} # page_index: (img_width, img_height, x_offset, y_offset) self.root.mainloop() def load_pdf(self): path = filedialog.askopenfilename(filetypes=[("PDF", "*.pdf")]) if not path: return self.pdf = fitz.open(path) self.page_index = 0 self.annotations.clear() self.tick_count = 0 self.tick_label.config(text="Ticks: 0") self.page_render_info.clear() self.render_page() def render_page(self): self.canvas.delete("all") if not self.pdf: return page = self.pdf[self.page_index] pix = page.get_pixmap(matrix=fitz.Matrix(2, 2)) img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) img_ratio = img.width / img.height canvas_ratio = self.canvas_width / self.canvas_height if img_ratio > canvas_ratio: new_width = self.canvas_width new_height = int(self.canvas_width / img_ratio) x_offset = 0 y_offset = (self.canvas_height - new_height) / 2 else: new_height = self.canvas_height new_width = int(self.canvas_height * img_ratio) x_offset = (self.canvas_width - new_width) / 2 y_offset = 0 img = img.resize((new_width, new_height), Image.LANCZOS) self.tk_background = ImageTk.PhotoImage(img) self.canvas.create_image(x_offset, y_offset, anchor=tk.NW, image=self.tk_background) self.page_render_info[self.page_index] = (new_width, new_height, x_offset, y_offset) for ann in self.annotations: if ann[0] == self.page_index: x, y = ann[3] * self.canvas_width, ann[4] * self.canvas_height canvas_id = self.canvas.create_image(x, y, image=self.tk_icons[ann[1]]) ann[2] = canvas_id def change_page(self, delta): if not self.pdf: return new = self.page_index + delta if 0 <= new < len(self.pdf): self.page_index = new self.render_page() def select_tool(self, name): self.current_tool = name def on_click(self, ev): if self.current_tool == "eraser": item = self.canvas.find_closest(ev.x, ev.y)[0] for ann in self.annotations: if ann[0] == self.page_index and ann[2] == item: if ann[1] == "tick": self.tick_count -= 1 self.tick_label.config(text=f"Ticks: {self.tick_count}") self.undo_stack.append(('delete', ann.copy())) self.canvas.delete(item) self.annotations.remove(ann) break elif self.current_tool: self.place_annotation(ev.x, ev.y, self.current_tool) else: item = self.canvas.find_closest(ev.x, ev.y) if item: self.drag_data['item'] = item[0] self.drag_data['dx'] = ev.x self.drag_data['dy'] = ev.y def place_annotation(self, x, y, tool): tk_img = self.tk_icons[tool] canvas_id = self.canvas.create_image(x, y, image=tk_img) ann = [self.page_index, tool, canvas_id, x / self.canvas_width, y / self.canvas_height] self.annotations.append(ann) self.undo_stack.append(('add', ann.copy())) if tool == "tick": self.tick_count += 1 self.tick_label.config(text=f"Ticks: {self.tick_count}") def on_drag(self, ev): item = self.drag_data['item'] if item: dx = ev.x - self.drag_data['dx'] dy = ev.y - self.drag_data['dy'] self.canvas.move(item, dx, dy) self.drag_data['dx'] = ev.x self.drag_data['dy'] = ev.y def on_drop(self, ev): item = self.drag_data['item'] if item: coords = self.canvas.coords(item) for ann in self.annotations: if ann[0] == self.page_index and ann[2] == item: ann[3] = coords[0] / self.canvas_width ann[4] = coords[1] / self.canvas_height self.undo_stack.append(('move', ann.copy())) break self.drag_data['item'] = None def undo(self): if not self.undo_stack: return action, ann = self.undo_stack.pop() if action == 'add': if ann[1] == "tick": self.tick_count -= 1 self.tick_label.config(text=f"Ticks: {self.tick_count}") self.canvas.delete(ann[2]) self.annotations.remove(ann) elif action == 'delete': tk_img = self.tk_icons[ann[1]] canvas_id = self.canvas.create_image(ann[3]*self.canvas_width, ann[4]*self.canvas_height, image=tk_img) ann[2] = canvas_id self.annotations.append(ann) if ann[1] == "tick": self.tick_count += 1 self.tick_label.config(text=f"Ticks: {self.tick_count}") elif action == 'move': for a in self.annotations: if a[2] == ann[2]: a[3], a[4] = ann[3], ann[4] self.canvas.coords(ann[2], ann[3]*self.canvas_width, ann[4]*self.canvas_height) break def save_progress(self): file = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON files", "*.json")]) if not file: return data = { "annotations": [ [page, tool, x_rel, y_rel] for page, tool, _, x_rel, y_rel in self.annotations ], "tick_count": self.tick_count } with open(file, "w") as f: json.dump(data, f) messagebox.showinfo("Save", "Progress saved!") def load_progress(self): file = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")]) if not file: return with open(file, "r") as f: data = json.load(f) self.annotations.clear() self.tick_count = data.get("tick_count", 0) self.tick_label.config(text=f"Ticks: {self.tick_count}") for page, tool, x_rel, y_rel in data.get("annotations", []): if tool not in self.tk_icons: continue if page == self.page_index: canvas_id = self.canvas.create_image(x_rel*self.canvas_width, y_rel*self.canvas_height, image=self.tk_icons[tool]) else: canvas_id = None self.annotations.append([page, tool, canvas_id, x_rel, y_rel]) self.render_page() messagebox.showinfo("Load", "Progress loaded!") def export_pdf(self): if not self.pdf: messagebox.showwarning("Export", "No PDF loaded") return save = filedialog.asksaveasfilename(defaultextension=".pdf", filetypes=[("PDF", "*.pdf")]) if not save: return for page_idx, tool, _, x_rel, y_rel in self.annotations: page = self.pdf[page_idx] rect = page.rect img_width, img_height, x_offset, y_offset = self.page_render_info.get(page_idx, (self.canvas_width, self.canvas_height, 0, 0)) x_canvas = x_rel * self.canvas_width y_canvas = y_rel * self.canvas_height y_canvas_flipped = self.canvas_height - y_canvas x_img = x_canvas - x_offset y_img = y_canvas_flipped - y_offset x_img_rel = x_img / img_width y_img_rel = y_img / img_height x_img_rel = max(0, min(1, x_img_rel)) y_img_rel = max(0, min(1, y_img_rel)) x_pdf = x_img_rel * rect.width y_pdf = y_img_rel * rect.height pix = fitz.Pixmap(self.icons[tool]) icon_size = 20 page.insert_image(fitz.Rect(x_pdf, y_pdf - icon_size, x_pdf + icon_size, y_pdf), pixmap=pix) last = self.pdf[-1] last.insert_text((50, 50), f"Total Score (Ticks): {self.tick_count}", fontsize=20, color=(0, 0, 1)) self.pdf.save(save) messagebox.showinfo("Export", f"Saved to:\n{save}") if __name__ == "__main__": PDFAnnotator() Continue reading...