this code
import tkinter as tk
from tkinter import messagebox, filedialog
from fpdf import FPDF
from PIL import Image, ImageTk
import json
import os
from datetime import datetime

class PDFCanvas(tk.Canvas):
    def __init__(self, master=None):
        super().__init__(master, bg="white")
        self.text_data = []  # Now stores (text_id, x, y, text, font, size, color, style, border_width, border_color)
        self.image_data = []  # Stores (image_id, x, y, image_path, tk_image, width, height, border_width, border_color)
        self.header_data = None  # Store header text and position
        self.footer_data = None  # Store footer text and position
        self.scale_factor = 1.0  # Current zoom scale
        self.dragging = None
        self.selection_box = None
        self.original_width = 0  # Original width of the canvas
        self.original_height = 0  # Original height of the canvas
        self.dpi = 96  # Define DPI as class variable
        self.selected_text_id = None  # Track currently selected text
        self.has_content = False  # Track if canvas has any content
        self.border_ids = {}  # Store border IDs associated with text/image IDs

        self.bind("<ButtonPress-1>", self.on_click)
        self.bind("<B1-Motion>", self.on_drag)
        self.bind("<ButtonRelease-1>", self.on_release)

    def add_text(self, text, font="Arial", size=12, color="black", style="normal", border_width=0, border_color="black"):
        safe_text = text.encode('utf-8', 'ignore').decode('utf-8', 'ignore')
        if not safe_text.strip():
            messagebox.showwarning("Input Error", "Please enter some valid text.")
            return

        x, y = 50, 50 + len(self.text_data) * 20
        
        # Create tkinter font object
        tk_font = (font, size, style)
        text_id = self.create_text(x, y, text=safe_text, fill=color, 
                                 font=tk_font, anchor="nw", tags="text")
        
        # Create border if specified
        if border_width > 0:
            bbox = self.bbox(text_id)
            # Expand border outward by half the border width on each side
            border_id = self.create_rectangle(
                bbox[0] - border_width/2,
                bbox[1] - border_width/2,
                bbox[2] + border_width/2,
                bbox[3] + border_width/2,
                outline=border_color,
                width=border_width
            )
            self.tag_lower(border_id, text_id)
            self.border_ids[text_id] = border_id
        
        # Store all formatting information including border
        self.text_data.append((text_id, x, y, safe_text, font, size, color, style, border_width, border_color))
        self.has_content = True
        self.event_generate("<<ContentAdded>>")

    def add_image(self, image_path, border_width=0, border_color="black"):
        try:
            # Open and resize image if needed
            pil_image = Image.open(image_path)
            # Keep aspect ratio, max size 200px
            pil_image.thumbnail((200, 200), Image.Resampling.LANCZOS)
            
            # Convert to PhotoImage for tkinter
            tk_image = ImageTk.PhotoImage(pil_image)
            
            # Place image on canvas
            x, y = 50, 50
            image_id = self.create_image(x, y, image=tk_image, anchor="nw", tags="image")
            
            # Create border if specified
            if border_width > 0:
                bbox = self.bbox(image_id)
                # Expand border outward by half the border width on each side
                border_id = self.create_rectangle(
                    bbox[0] - border_width/2,
                    bbox[1] - border_width/2,
                    bbox[2] + border_width/2,
                    bbox[3] + border_width/2,
                    outline=border_color,
                    width=border_width
                )
                self.tag_lower(border_id, image_id)
                self.border_ids[image_id] = border_id
            
            # Store image data with border properties
            self.image_data.append((image_id, x, y, image_path, tk_image, 
                                  pil_image.width, pil_image.height,
                                  border_width, border_color))
            self.has_content = True
            self.event_generate("<<ContentAdded>>")
            
        except Exception as e:
            messagebox.showerror("Error", f"Failed to load image: {str(e)}")

    def add_header(self, text="Header Text", y_offset=20):
        if self.header_data:
            self.delete(self.header_data[0])  # Remove old header
        
        # Create header text centered at top
        text_id = self.create_text(
            self.original_width / 2, y_offset,
            text=text, font=("Arial", 10), anchor="n",
            tags="header"
        )
        self.header_data = (text_id, text, y_offset)
        self.has_content = True
        self.event_generate("<<ContentAdded>>")

    def add_footer(self, text="Footer Text", y_offset=20):
        if self.footer_data:
            self.delete(self.footer_data[0])  # Remove old footer
        
        # Create footer text centered at bottom
        text_id = self.create_text(
            self.original_width / 2, self.original_height - y_offset,
            text=text, font=("Arial", 10), anchor="s",
            tags="footer"
        )
        self.footer_data = (text_id, text, y_offset)
        self.has_content = True
        self.event_generate("<<ContentAdded>>")

    def on_click(self, event):
        self.clear_selection_box()
        self.selected_text_id = None

        # Check for image selection first
        for image_id, x, y, path, tk_image, width, height, border_width, border_color in self.image_data:
            bbox = self.bbox(image_id)
            scaled_bbox = (
                bbox[0] * self.scale_factor,
                bbox[1] * self.scale_factor,
                bbox[2] * self.scale_factor,
                bbox[3] * self.scale_factor,
            )
            if scaled_bbox[0] <= event.x <= scaled_bbox[2] and scaled_bbox[1] <= event.y <= scaled_bbox[3]:
                self.dragging = image_id
                self.start_x = event.x
                self.start_y = event.y
                self.selection_box = self.create_rectangle(
                    scaled_bbox[0], scaled_bbox[1], scaled_bbox[2], scaled_bbox[3],
                    outline="blue", dash=(2, 2)
                )
                return

        # If no image was clicked, check for text selection
        for text_id, x, y, text, font, size, color, style, border_width, border_color in self.text_data:
            # Adjust bounding box coordinates for zoom
            bbox = self.bbox(text_id)
            scaled_bbox = (
                bbox[0] * self.scale_factor,
                bbox[1] * self.scale_factor,
                bbox[2] * self.scale_factor,
                bbox[3] * self.scale_factor,
            )
            if scaled_bbox[0] <= event.x <= scaled_bbox[2] and scaled_bbox[1] <= event.y <= scaled_bbox[3]:
                self.dragging = text_id
                self.selected_text_id = text_id
                self.start_x = event.x
                self.start_y = event.y
                self.selection_box = self.create_rectangle(
                    scaled_bbox[0], scaled_bbox[1], scaled_bbox[2], scaled_bbox[3],
                    outline="blue", dash=(2, 2)
                )
                # Notify the app about the selection
                self.event_generate("<<TextSelected>>")
                break

    def on_drag(self, event):
        if self.dragging:
            dx = (event.x - self.start_x) / self.scale_factor
            dy = (event.y - self.start_y) / self.scale_factor
            text_id = self.dragging
            self.move(text_id, dx, dy)
            if self.dragging in self.border_ids:
                self.move(self.border_ids[self.dragging], dx, dy)
            self.start_x = event.x
            self.start_y = event.y
            self.update_selection_box()

    def on_release(self, event):
        if self.dragging:
            # Check if dragging image
            for i, (image_id, x, y, path, tk_image, width, height, border_width, border_color) in enumerate(self.image_data):
                if image_id == self.dragging:
                    new_x = self.bbox(image_id)[0] / self.scale_factor
                    new_y = self.bbox(image_id)[1] / self.scale_factor
                    self.image_data[i] = (image_id, new_x, new_y, path, tk_image, width, height, border_width, border_color)
                    break
            else:
                # If not an image, handle text release
                for index, (text_id, x, y, text, font, size, color, style, border_width, border_color) in enumerate(self.text_data):
                    if text_id == self.dragging:
                        # Update the original coordinates based on the new position
                        new_x = self.bbox(text_id)[0] / self.scale_factor
                        new_y = self.bbox(text_id)[1] / self.scale_factor
                        self.text_data[index] = (text_id, new_x, new_y, text, font, size, color, style, border_width, border_color)
                        break
            self.dragging = None
            self.clear_selection_box()

    def set_canvas_size(self, width, height):
        self.original_width = width
        self.original_height = height
        self.config(width=width, height=height)

        # Clear existing borders and their IDs
        self.delete("border")
        self.border_ids = {}

        # Draw page borders
        self.create_rectangle(0, 0, width-1, height-1, outline="gray", tags="border")
        self.create_line(10, 0, 10, height, fill="lightgray", dash=(4,4), tags="border")
        self.create_line(0, 10, width, 10, fill="lightgray", dash=(4,4), tags="border")

        # Clear and redraw text items with their borders
        self.delete("text")
        for index, (text_id, original_x, original_y, text, font, size, color, style, border_width, border_color) in enumerate(self.text_data):
            new_x = original_x * self.scale_factor
            new_y = original_y * self.scale_factor
            tk_font = (font, size, style)
            new_text_id = self.create_text(new_x, new_y, text=text, fill=color, 
                                         font=tk_font, anchor="nw", tags="text")
            
            # Recreate border if it exists
            if border_width > 0:
                bbox = self.bbox(new_text_id)
                border_id = self.create_rectangle(
                    bbox[0] - border_width/2,
                    bbox[1] - border_width/2,
                    bbox[2] + border_width/2,
                    bbox[3] + border_width/2,
                    outline=border_color,
                    width=border_width
                )
                self.tag_lower(border_id, new_text_id)
                self.border_ids[new_text_id] = border_id
            
            self.text_data[index] = (new_text_id, original_x, original_y, text, 
                                   font, size, color, style, border_width, border_color)

        # Redraw images with their borders
        for image_id, x, y, path, tk_image, width, height, border_width, border_color in self.image_data:
            if border_width > 0:
                bbox = self.bbox(image_id)
                border_id = self.create_rectangle(
                    bbox[0] - border_width/2,
                    bbox[1] - border_width/2,
                    bbox[2] + border_width/2,
                    bbox[3] + border_width/2,
                    outline=border_color,
                    width=border_width
                )
                self.tag_lower(border_id, image_id)
                self.border_ids[image_id] = border_id

    def export_to_pdf(self, filename, pdf_width, pdf_height):
        pdf = FPDF(format=(pdf_width, pdf_height))
        pdf.set_auto_page_break(auto=True, margin=0)
        pdf.add_page()

        # Add header if exists
        if self.header_data:
            pdf.set_font("Arial", "", 10)
            pdf.set_y(10)
            pdf.cell(0, 10, self.header_data[1], align='C')

        # Calculate pixels to mm conversion
        px_to_mm = 25.4 / self.dpi  # 1 inch = 25.4mm

        # Add images with borders
        for image_id, x, y, path, _, width, height, border_width, border_color in self.image_data:
            pdf_x = x * px_to_mm
            pdf_y = y * px_to_mm
            pdf_w = width * px_to_mm
            pdf_h = height * px_to_mm
            
            # Add image
            pdf.image(path, x=pdf_x, y=pdf_y, w=pdf_w, h=pdf_h)
            
            # Add border if specified
            if border_width > 0:
                pdf.set_draw_color(*self._parse_color(border_color))
                pdf.set_line_width(border_width * px_to_mm)
                # Adjust rectangle position and size to account for border width
                border_offset = (border_width/2) * px_to_mm
                pdf.rect(
                    pdf_x - border_offset,
                    pdf_y - border_offset,
                    pdf_w + border_width * px_to_mm,
                    pdf_h + border_width * px_to_mm
                )

        # Handle text elements with borders
        for text_id, x, y, text, font, size, color, style, border_width, border_color in self.text_data:
            # Convert pixel coordinates to millimeters
            pdf_x = x * px_to_mm
            pdf_y = y * px_to_mm
            
            # Set text properties
            style_str = ""
            if "bold" in style.lower():
                style_str += "B"
            if "italic" in style.lower():
                style_str += "I"
            
            # Convert font size from pixels to points
            # FPDF uses points (1/72 inch), while canvas uses pixels (1/96 inch)
            pdf_font_size = size * 72 / self.dpi
            
            try:
                pdf.set_font(font, style_str, pdf_font_size)
            except RuntimeError:
                # Fallback to Helvetica if font not available
                pdf.set_font("Helvetica", style_str, pdf_font_size)
            
            # Set text color
            color_map = {
                "black": (0, 0, 0),
                "red": (255, 0, 0),
                "blue": (0, 0, 255),
                "green": (0, 128, 0)
            }
            rgb = color_map.get(color.lower(), (0, 0, 0))
            pdf.set_text_color(rgb[0], rgb[1], rgb[2])
            
            # Position and write text
            # Add baseline offset correction (approximately 0.2 times the font size)
            baseline_offset = pdf_font_size * 0.2
            pdf.set_xy(pdf_x, pdf_y + baseline_offset)
            pdf.write(pdf_font_size / 2, text)

            # Get text dimensions
            bbox = self.bbox(text_id)
            text_width = (bbox[2] - bbox[0]) * px_to_mm
            text_height = (bbox[3] - bbox[1]) * px_to_mm
            
            # Add border if specified
            if border_width > 0:
                pdf.set_draw_color(*self._parse_color(border_color))
                pdf.set_line_width(border_width * px_to_mm)
                # Adjust rectangle position and size to account for border width
                border_offset = (border_width/2) * px_to_mm
                pdf.rect(
                    pdf_x - border_offset,
                    pdf_y - border_offset,
                    text_width + border_width * px_to_mm,
                    text_height + border_width * px_to_mm
                )

        # Add footer if exists
        if self.footer_data:
            pdf.set_font("Arial", "", 10)
            pdf.set_y(-15)  # 15mm from bottom
            pdf.cell(0, 10, self.footer_data[1], align='C')

        pdf.output(filename)
        messagebox.showinfo("Success", f"PDF saved as {filename}")

    def clear_selection_box(self):
        if self.selection_box:
            self.delete(self.selection_box)
            self.selection_box = None

    def update_selection_box(self):
        if self.selection_box and self.dragging:
            bbox = self.bbox(self.dragging)
            scaled_bbox = (
                bbox[0] * self.scale_factor,
                bbox[1] * self.scale_factor,
                bbox[2] * self.scale_factor,
                bbox[3] * self.scale_factor,
            )
            self.coords(self.selection_box, scaled_bbox[0], scaled_bbox[1], scaled_bbox[2], scaled_bbox[3])

    def get_text_properties(self, text_id):
        # Return formatting properties of selected text
        for id, x, y, text, font, size, color, style, border_width, border_color in self.text_data:
            if id == text_id:
                return {"font": font, "size": size, "color": color, "style": style, "border_width": border_width, "border_color": border_color}
        return None

    def update_text_format(self, font=None, size=None, color=None, style=None, border_width=None, border_color=None):
        if not self.selected_text_id:
            return

        was_dragging = self.dragging  # Store dragging state
        dragging_id = self.dragging   # Store dragging id

        # Find and update the text properties
        for i, (text_id, x, y, text, old_font, old_size, old_color, old_style, old_border_width, old_border_color) in enumerate(self.text_data):
            if text_id == self.selected_text_id:
                # Update with new values, keeping old ones if not specified
                new_font = font if font is not None else old_font
                new_size = size if size is not None else old_size
                new_color = color if color is not None else old_color
                new_style = style if style is not None else old_style
                new_border_width = border_width if border_width is not None else old_border_width
                new_border_color = border_color if border_color is not None else old_border_color

                # Delete old border if it exists
                if text_id in self.border_ids:
                    self.delete(self.border_ids[text_id])
                    del self.border_ids[text_id]

                # Create new text with updated formatting
                self.delete(text_id)
                tk_font = (new_font, new_size, new_style)
                new_text_id = self.create_text(x, y, text=text, fill=new_color,
                                             font=tk_font, anchor="nw", tags="text")
                
                # Create border if specified
                if new_border_width > 0:
                    bbox = self.bbox(new_text_id)
                    border_id = self.create_rectangle(
                        bbox[0] - new_border_width/2,
                        bbox[1] - new_border_width/2,
                        bbox[2] + new_border_width/2,
                        bbox[3] + new_border_width/2,
                        outline=new_border_color,
                        width=new_border_width
                    )
                    self.tag_lower(border_id, new_text_id)
                    self.border_ids[new_text_id] = border_id
                
                # Update stored data
                self.text_data[i] = (new_text_id, x, y, text, new_font, new_size, new_color, new_style, new_border_width, new_border_color)
                self.selected_text_id = new_text_id
                
                # Restore dragging state if needed
                if was_dragging and dragging_id == text_id:
                    self.dragging = new_text_id
                
                # Update selection box
                self.clear_selection_box()
                bbox = self.bbox(new_text_id)
                scaled_bbox = (
                    bbox[0] * self.scale_factor,
                    bbox[1] * self.scale_factor,
                    bbox[2] * self.scale_factor,
                    bbox[3] * self.scale_factor,
                )
                self.selection_box = self.create_rectangle(
                    scaled_bbox[0], scaled_bbox[1], scaled_bbox[2], scaled_bbox[3],
                    outline="blue", dash=(2, 2)
                )
                break

    def update_text_content(self, new_text):
        """Update text content without recreating the text object"""
        if not self.selected_text_id:
            return

        for i, (text_id, x, y, old_text, font, size, color, style, border_width, border_color) in enumerate(self.text_data):
            if text_id == self.selected_text_id:
                # Update the text content directly
                self.itemconfig(text_id, text=new_text)
                # Update stored data while preserving the text_id
                self.text_data[i] = (text_id, x, y, new_text, font, size, color, style, border_width, border_color)
                break

    def get_design_data(self):
        """Export all canvas content as serializable data"""
        return {
            'text_elements': [
                {
                    'text': text,
                    'x': x,
                    'y': y,
                    'font': font,
                    'size': size,
                    'color': color,
                    'style': style,
                    'border_width': border_width,
                    'border_color': border_color
                } for _, x, y, text, font, size, color, style, border_width, border_color in self.text_data
            ],
            'images': [
                {
                    'path': path,
                    'x': x,
                    'y': y,
                    'width': width,
                    'height': height,
                    'border_width': border_width,
                    'border_color': border_color
                } for _, x, y, path, _, width, height, border_width, border_color in self.image_data
            ],
            'header': self.header_data[1] if self.header_data else None,
            'footer': self.footer_data[1] if self.footer_data else None,
            'canvas_size': {
                'width': self.original_width,
                'height': self.original_height
            }
        }

    def load_design_data(self, data):
        """Load design from data"""
        self.delete('all')
        self.text_data = []
        self.image_data = []
        self.header_data = None
        self.footer_data = None
        
        # Set canvas size
        size = data.get('canvas_size', {'width': 595, 'height': 842})  # A4 default
        self.set_canvas_size(size['width'], size['height'])
        
        # Load text elements
        for text_elem in data.get('text_elements', []):
            self.add_text(
                text_elem['text'],
                font=text_elem['font'],
                size=text_elem['size'],
                color=text_elem['color'],
                style=text_elem['style'],
                border_width=text_elem.get('border_width', 0),
                border_color=text_elem.get('border_color', 'black')
            )
            # Update position after adding
            if self.text_data:
                last_id = self.text_data[-1][0]
                self.moveto(last_id, text_elem['x'], text_elem['y'])
        
        # Load images
        for img in data.get('images', []):
            if os.path.exists(img['path']):
                self.add_image(
                    img['path'],
                    border_width=img.get('border_width', 0),
                    border_color=img.get('border_color', 'black')
                )
                if self.image_data:
                    last_id = self.image_data[-1][0]
                    self.moveto(last_id, img['x'], img['y'])
        
        # Load header/footer
        if data.get('header'):
            self.add_header(data['header'])
        if data.get('footer'):
            self.add_footer(data['footer'])

    def _parse_color(self, color):
        """Convert color name to RGB values"""
        color_map = {
            "black": (0, 0, 0),
            "red": (255, 0, 0),
            "blue": (0, 0, 255),
            "green": (0, 128, 0),
        }
        return color_map.get(color.lower(), (0, 0, 0))

    def delete(self, *args):
        # Override delete to also remove border_ids entries
        for arg in args:
            if arg in self.border_ids:
                super().delete(self.border_ids[arg])
                del self.border_ids[arg]
        super().delete(*args)

class ToolIcon(tk.Canvas):
    """Canvas widget that shows a draggable tool icon"""
    def __init__(self, master, tool_type, **kwargs):
        super().__init__(master, width=80, height=80, bg='white', highlightthickness=1)
        self.tool_type = tool_type
        
        # Draw icon representation
        if tool_type == "text":
            self.create_text(40, 40, text="T", font=("Arial", 24, "bold"))
            self.create_text(40, 60, text="Text", font=("Arial", 8))
        elif tool_type == "image":
            self.create_rectangle(20, 20, 60, 50, fill='lightgray')
            self.create_text(40, 60, text="Image", font=("Arial", 8))
        elif tool_type == "table":
            self.create_rectangle(20, 20, 60, 50)
            self.create_line(40, 20, 40, 50)
            self.create_line(20, 35, 60, 35)
            self.create_text(40, 60, text="Table", font=("Arial", 8))
        elif tool_type == "header":
            self.create_rectangle(20, 20, 60, 30, fill='lightblue')
            self.create_line(25, 25, 55, 25, fill='navy')
            self.create_text(40, 60, text="Header", font=("Arial", 8))
        elif tool_type == "footer":
            self.create_rectangle(20, 40, 60, 50, fill='lightblue')
            self.create_line(25, 45, 55, 45, fill='navy')
            self.create_text(40, 60, text="Footer", font=("Arial", 8))
            
        self.bind("<ButtonPress-1>", self.on_press)
        self.bind("<B1-Motion>", self.on_drag)
        self.bind("<ButtonRelease-1>", self.on_release)

    def on_press(self, event):
        self.start_x = event.x
        self.start_y = event.y

    def on_drag(self, event):
        # Create drag image
        pass

    def on_release(self, event):
        # Notify main app of drop
        self.event_generate("<<ToolDropped>>", 
                          data={"tool": self.tool_type, "x": event.x, "y": event.y})

class WYSIWYGApp:
    def __init__(self, root):
        self.root = root
        self.root.title("WYSIWYG PDF Designer")
        self.root.configure(bg='#f0f0f0')  # Light gray background

        root.geometry("1400x900")  # Wider window to accommodate tools

        # Initialize control variables first
        self.fonts = ["Arial", "Helvetica", "Courier"]
        self.colors = ["black", "red", "blue", "green"]
        self.styles = ["normal", "bold", "italic", "bold italic"]
        
        self.font_var = tk.StringVar(value="Arial")
        self.size_var = tk.StringVar(value="12")
        self.color_var = tk.StringVar(value="black")
        self.style_var = tk.StringVar(value="normal")

        self.menubar = tk.Menu(root)
        root.config(menu=self.menubar)

        # Setup complete File Menu first
        self.file_menu = tk.Menu(self.menubar, tearoff=0)
        self.file_menu.add_command(label="New", command=self.new_design, accelerator="Ctrl+N")
        self.file_menu.add_command(label="Open...", command=self.open_design, accelerator="Ctrl+O")
        self.file_menu.add_command(label="Save", command=self.save_design, accelerator="Ctrl+S")
        self.file_menu.add_command(label="Save As...", command=self.save_design_as)
        self.file_menu.add_separator()
        
        # Recent files submenu
        self.recent_menu = tk.Menu(self.file_menu, tearoff=0)
        self.file_menu.add_cascade(label="Recent Files", menu=self.recent_menu)
        self.file_menu.add_separator()
        self.file_menu.add_command(label="Exit", command=root.quit)
        
        self.menubar.add_cascade(label="File", menu=self.file_menu)

        # Paper Size Menu
        self.paper_menu = tk.Menu(self.menubar, tearoff=0)
        paper_sizes = {
            "A4": (210, 297),
            "A5": (148, 210),
            "Letter": (216, 279),
            "Legal": (216, 356),
        }

        for size_name, (width, height) in paper_sizes.items():
            def make_command(w=width, h=height):
                return lambda: self.try_change_paper_size(w, h)
            self.paper_menu.add_command(
                label=size_name,
                command=make_command()
            )
        self.menubar.add_cascade(label="Paper Size", menu=self.paper_menu)

        # Load recent files list (move this after file menu setup)
        self.recent_files = self.load_recent_files()
        self.update_recent_menu()
        
        # Current file tracking (move this after file menu setup)
        self.current_file = None
        self.design_modified = False

        # Tools Panel (left side)
        self.tools_frame = tk.Frame(root, bg='#f0f0f0', width=100)
        self.tools_frame.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=20)
        self.tools_frame.pack_propagate(False)

        # Tools Label
        tk.Label(self.tools_frame, text="Tools", bg='#f0f0f0', 
                font=('Arial', 10, 'bold')).pack(pady=(0, 10))

        # Add tool icons in new order
        for tool_type in ["header", "text", "image", "table", "footer"]:
            tool = ToolIcon(self.tools_frame, tool_type)
            tool.pack(pady=5)
            setattr(self, f"{tool_type}_tool", tool)

        # Canvas Frame (center)
        self.canvas_frame = tk.Frame(root, bg='#f0f0f0')
        self.canvas_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=20)

        # Scroll Canvas setup
        self.scroll_canvas = tk.Canvas(self.canvas_frame, bg='#f0f0f0', bd=0, highlightthickness=0)
        self.scrollbar = tk.Scrollbar(self.canvas_frame, orient="vertical", 
                                    command=self.scroll_canvas.yview)
        self.scroll_canvas.configure(yscrollcommand=self.scrollbar.set)

        # Pack scrollbar and canvas
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.scroll_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        # Canvas Container setup
        self.canvas_container = tk.Frame(self.scroll_canvas, bg='#e0e0e0')
        
        # Create window in scrollable canvas and store the window ID
        self.scroll_window = self.scroll_canvas.create_window(
            (0, 0), 
            window=self.canvas_container, 
            anchor='nw',
            width=800  # Fixed width for container
        )

        # PDF Canvas
        self.canvas = PDFCanvas(master=self.canvas_container)
        self.canvas.pack(pady=(20, 0))  # Top padding only

        # Controls Frame (right side)
        self.controls_frame = tk.Frame(root, bg='#f0f0f0', width=300)  # Fixed width
        self.controls_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=20, pady=20)
        self.controls_frame.pack_propagate(False)  # Prevent shrinking

        # Text Input Section - Modified
        self.input_frame = tk.LabelFrame(self.controls_frame, text="Text Content", 
                                       bg='#f0f0f0', fg='#333333', font=('Arial', 10, 'bold'))
        self.input_frame.pack(fill=tk.X, pady=(0, 10))

        self.text_var = tk.StringVar()  # New variable for text content
        self.entry = tk.Entry(self.input_frame, width=40, font=('Arial', 10),
                            textvariable=self.text_var)
        self.entry.pack(padx=10, pady=10)

        # Add trace for text content updates
        self.text_var.trace('w', self.update_selected_text_content)

        # Formatting Section
        self.format_frame = tk.LabelFrame(self.controls_frame, text="Text Formatting",
                                        bg='#f0f0f0', fg='#333333', font=('Arial', 10, 'bold'))
        self.format_frame.pack(fill=tk.X, pady=10)

        # Font controls with better spacing and layout
        for label, var, options in [
            ("Font", self.font_var, self.fonts),
            ("Size", self.size_var, None),
            ("Color", self.color_var, self.colors),
            ("Style", self.style_var, self.styles)
        ]:
            frame = tk.Frame(self.format_frame, bg='#f0f0f0')
            frame.pack(fill=tk.X, padx=10, pady=2)
            
            tk.Label(frame, text=label, bg='#f0f0f0', width=8, anchor='w').pack(side=tk.LEFT)
            
            if var == self.size_var:
                tk.Spinbox(frame, from_=8, to=72, width=5, textvariable=var).pack(side=tk.LEFT)
            else:
                tk.OptionMenu(frame, var, *options).pack(side=tk.LEFT, fill=tk.X, expand=True)

        # Add border control variables
        self.border_width_var = tk.StringVar(value="0")
        self.border_color_var = tk.StringVar(value="black")

        # Add border controls to format frame
        border_frame = tk.LabelFrame(self.format_frame, text="Border", bg='#f0f0f0')
        border_frame.pack(fill=tk.X, padx=10, pady=5)

        # Border width control
        width_frame = tk.Frame(border_frame, bg='#f0f0f0')
        width_frame.pack(fill=tk.X, padx=5, pady=2)
        tk.Label(width_frame, text="Width", bg='#f0f0f0', width=8, anchor='w').pack(side=tk.LEFT)
        tk.Spinbox(width_frame, from_=0, to=10, width=5, 
                  textvariable=self.border_width_var).pack(side=tk.LEFT)

        # Border color control
        color_frame = tk.Frame(border_frame, bg='#f0f0f0')
        color_frame.pack(fill=tk.X, padx=5, pady=2)
        tk.Label(color_frame, text="Color", bg='#f0f0f0', width=8, anchor='w').pack(side=tk.LEFT)
        tk.OptionMenu(color_frame, self.border_color_var, 
                     "black", "red", "blue", "green").pack(side=tk.LEFT, fill=tk.X, expand=True)

        # Add trace for border updates
        self.border_width_var.trace('w', self.update_selected_text)
        self.border_color_var.trace('w', self.update_selected_text)

        # Actions Section
        self.actions_frame = tk.LabelFrame(self.controls_frame, text="Actions",
                                         bg='#f0f0f0', fg='#333333', font=('Arial', 10, 'bold'))
        self.actions_frame.pack(fill=tk.X, pady=10)

        # Only create export button, remove add_image_button
        self.export_button = tk.Button(self.actions_frame, text="Export to PDF",
                                     command=self.export_pdf,
                                     bg='#2ecc71', fg='white',
                                     relief=tk.FLAT, padx=20)
        self.export_button.pack(pady=5, fill=tk.X, padx=10)

        # Remove old entry frame and its contents
        # self.entry_frame and related widgets are removed

        # Bind text selection event
        self.canvas.bind("<<TextSelected>>", self.on_text_selected)
        
        # Variables are already traced at initialization
        self.font_var.trace('w', self.update_selected_text)
        self.size_var.trace('w', self.update_selected_text)
        self.color_var.trace('w', self.update_selected_text)
        self.style_var.trace('w', self.update_selected_text)

        # Bind tool drop events
        for tool in [self.text_tool, self.image_tool, self.table_tool, self.header_tool, self.footer_tool]:
            tool.bind("<<ToolDropped>>", self.on_tool_dropped)

        # Bind canvas content changes
        self.canvas.bind("<<ContentAdded>>", self.on_content_added)

        # Set initial paper size
        self.set_paper_size(210, 297)  # Initial A4 size

        # Keyboard shortcuts
        root.bind('<Control-n>', lambda e: self.new_design())
        root.bind('<Control-o>', lambda e: self.open_design())
        root.bind('<Control-s>', lambda e: self.save_design())

    def _configure_scroll_region(self, event=None):
        # Get the required height for the container
        required_height = self.canvas.winfo_height() + 40  # Add padding
        
        # Update the canvas container's height using the stored window ID
        self.scroll_canvas.itemconfig(
            self.scroll_window,
            height=max(required_height, self.scroll_canvas.winfo_height())
        )
        
        # Update the scroll region to match the container's full size
        self.scroll_canvas.configure(scrollregion=self.scroll_canvas.bbox("all"))

    def _on_mousewheel(self, event):
        # Respond to mousewheel events for scrolling
        self.scroll_canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

    def set_paper_size(self, width_mm, height_mm):
        # Convert mm to pixels using the same DPI as PDFCanvas
        dpi = self.canvas.dpi
        width_px = int(width_mm / 25.4 * dpi)
        height_px = int(height_mm / 25.4 * dpi)

        # Update stored dimensions
        self.pdf_width = width_mm
        self.pdf_height = height_mm

        # Set exact canvas size
        self.canvas.config(width=width_px, height=height_px)
        self.canvas.set_canvas_size(width_px, height_px)
        
        # Center the canvas in its container
        self.canvas.pack(expand=False, anchor='n', padx=20, pady=20)
        
        # Force update
        self.root.update_idletasks()
        self._configure_scroll_region()

    def try_change_paper_size(self, width_mm, height_mm):
        if self.canvas.has_content:
            messagebox.showwarning(
                "Cannot Change Paper Size",
                "Paper size cannot be changed after adding content to the canvas."
            )
            return
        self.set_paper_size(width_mm, height_mm)

    def add_text(self, default_text=None):
        text = self.entry.get() or default_text or "New Text"
        if text:
            self.canvas.add_text(
                text,
                font=self.font_var.get(),
                size=int(self.size_var.get()),
                color=self.color_var.get(),
                style=self.style_var.get(),
                border_width=int(self.border_width_var.get()),
                border_color=self.border_color_var.get()
            )
            self.entry.delete(0, tk.END)
        else:
            messagebox.showwarning("Input Error", "Please enter some text.")

    def add_image(self):
        file_path = filedialog.askopenfilename(
            filetypes=[
                ("Image files", "*.png *.jpg *.jpeg *.gif *.bmp"),
                ("All files", "*.*")
            ]
        )
        if file_path:
            self.canvas.add_image(
                file_path,
                border_width=int(self.border_width_var.get()),
                border_color=self.border_color_var.get()
            )

    def add_header(self):
        text = self.entry.get() or "Header Text"
        self.canvas.add_header(text)
        self.entry.delete(0, tk.END)

    def add_footer(self):
        text = self.entry.get() or "Footer Text"
        self.canvas.add_footer(text)
        self.entry.delete(0, tk.END)

    def export_pdf(self):
        filename = "output.pdf"
        self.canvas.export_to_pdf(filename, self.pdf_width, self.pdf_height)

    def on_text_selected(self, event):
        # Update formatting controls and text content to match selected text
        props = self.canvas.get_text_properties(self.canvas.selected_text_id)
        if props:
            self.font_var.set(props["font"])
            self.size_var.set(str(props["size"]))
            self.color_var.set(props["color"])
            self.style_var.set(props["style"])
            self.border_width_var.set(str(props["border_width"]))
            self.border_color_var.set(props["border_color"])
            # Get the text content of selected item
            for text_id, _, _, text, *_ in self.canvas.text_data:
                if text_id == self.canvas.selected_text_id:
                    self.text_var.set(text)
                    break

    def update_selected_text(self, *args):
        try:
            size = int(self.size_var.get())
            border_width = int(self.border_width_var.get())
        except ValueError:
            return

        self.canvas.update_text_format(
            font=self.font_var.get(),
            size=size,
            color=self.color_var.get(),
            style=self.style_var.get(),
            border_width=border_width,
            border_color=self.border_color_var.get()
        )

    def update_selected_text_content(self, *args):
        """Update text content when entry field changes"""
        if self.canvas.selected_text_id:
            new_text = self.text_var.get()
            self.canvas.update_text_content(new_text)
            self.design_modified = True

    def on_tool_dropped(self, event):
        tool_type = event.widget.tool_type
        if tool_type == "text":
            self.add_text("New Text")
        elif tool_type == "image":
            self.add_image()
        elif tool_type == "header":
            self.add_header()
        elif tool_type == "footer":
            self.add_footer()
        elif tool_type == "table":
            # TODO: Implement table creation
            pass

    def on_content_added(self, event):
        # Disable paper size menu when content is added
        for i in range(self.paper_menu.index('end') + 1):
            self.paper_menu.entryconfigure(i, state='disabled')
        self.design_modified = True

    def new_design(self):
        if self.design_modified:
            if not messagebox.askyesno("Unsaved Changes", 
                                     "Current design has unsaved changes. Continue anyway?"):
                return
        
        self.canvas.delete('all')
        self.canvas.text_data = []
        self.canvas.image_data = []
        self.canvas.header_data = None
        self.canvas.footer_data = None
        self.canvas.has_content = False
        self.current_file = None
        self.design_modified = False
        
        # Re-enable paper size menu
        for i in range(self.paper_menu.index('end') + 1):
            self.paper_menu.entryconfigure(i, state='normal')

    def save_design(self):
        if not self.current_file:
            return self.save_design_as()
        
        self.save_to_file(self.current_file)
        self.design_modified = False

    def save_design_as(self):
        filename = filedialog.asksaveasfilename(
            defaultextension=".wpd",
            filetypes=[("WYSIWYG PDF Design", "*.wpd"), ("All Files", "*.*")]
        )
        if filename:
            self.save_to_file(filename)
            self.current_file = filename
            self.add_recent_file(filename)
            self.design_modified = False
            return True
        return False

    def save_to_file(self, filename):
        data = {
            'version': '1.0',
            'date_modified': datetime.now().isoformat(),
            'design': self.canvas.get_design_data()
        }
        with open(filename, 'w') as f:
            json.dump(data, f, indent=2)

    def open_design(self):
        if self.design_modified:
            if not messagebox.askyesno("Unsaved Changes", 
                                     "Current design has unsaved changes. Continue anyway?"):
                return
        
        filename = filedialog.askopenfilename(
            filetypes=[("WYSIWYG PDF Design", "*.wpd"), ("All Files", "*.*")]
        )
        if filename:
            self.load_from_file(filename)

    def load_from_file(self, filename):
        try:
            with open(filename, 'r') as f:
                data = json.load(f)
            
            if data.get('version', '1.0') == '1.0':
                self.canvas.load_design_data(data['design'])
                self.current_file = filename
                self.add_recent_file(filename)
                self.design_modified = False
                return True
        except Exception as e:
            messagebox.showerror("Error", f"Failed to load design: {str(e)}")
        return False

    def load_recent_files(self):
        try:
            with open('recent_files.json', 'r') as f:
                return json.load(f)
        except:
            return []

    def save_recent_files(self):
        with open('recent_files.json', 'w') as f:
            json.dump(self.recent_files[-10:], f)  # Keep last 10 files

    def add_recent_file(self, filename):
        if filename in self.recent_files:
            self.recent_files.remove(filename)
        self.recent_files.append(filename)
        self.save_recent_files()
        self.update_recent_menu()

    def update_recent_menu(self):
        self.recent_menu.delete(0, tk.END)
        for filename in reversed(self.recent_files):
            if os.path.exists(filename):
                self.recent_menu.add_command(
                    label=os.path.basename(filename),
                    command=lambda f=filename: self.load_from_file(f)
                )

if __name__ == "__main__":
    root = tk.Tk()
    app = WYSIWYGApp(root)
    root.mainloop()
has a problem that when a text object has been selected and a new one added the priuos text object is removed