Having trouble adding text to PDF's using Tkinter and PyMuPDF

87 views Asked by At

I'm having trouble getting the back end to work correctly with the front end of my Tkinter gui. I'll explain what I'm trying to do with the gui, but for a more specific example you can look at the picture in this post.

I want the user to be able to select from one of three options for how they can add text to a list of PDF's. "Autopopulate text From v1" and "Autopopulate text From v2" are both just going to use text from variables that were created outside of this program and will take the place of the "text_fields" strings for the forms.

The problem I'm having here is that neither of the two autopopulate options are actually adding text to either form A or form B. With the manual option, text is actually being added to the forms, but the problem there is that each form has different text fields that need to be added to different spots on the PDF's and what I'm typing in for the manual text entry is just the same text being added to the same areas on both forms. I'm sure this would be easy to fix under my add_text_to_pdf function under the else clause.

I'm more concerned about getting the autopopulate parts to work how they're supposed to. If anyone would be willing to try to help me I would greatly appreciate it, as I've been going at this for hours with no luck. If there's any questions that anyone has for anything that I haven't been specific about please let me know. Thank you!

Note: I'm sure it would be more appropriate for the button on my gui to say "Add Text to PDF's" since I'm trying to add text to multiple PDF's at once.


#pip install PyMuPDF
#pip install tkinter
import fitz  
import tkinter as tk
from tkinter import filedialog

# Define form-specific information
forms_info = {
    'Form A': {
        'text_fields': ['Text for Form A', 'Other Text for Form A', 'Date for Form A'],
        'text_positions': [fitz.Point(202, 644), fitz.Point(104, 685), fitz.Point(85, 726)],
    },
    'Form B': {
        'text_fields': ['Text for Form B', 'Other Text for Form B', 'Date for Form B'],
        'text_positions': [fitz.Point(192, 645), fitz.Point(105, 688), fitz.Point(92, 723)],
    },
    # Add more forms and their details as needed
}

def add_text_to_pdf(autopopulate, selected_forms):
    for form_name in selected_forms:
        # Open the selected PDF document
        file_path = filedialog.askopenfilename(filetypes=[("PDF files", "*.pdf")])
        pdf_document = fitz.open(file_path)

        # For a single page PDF, you can set the page number to 1
        page_number = 1
        page = pdf_document.load_page(page_number - 1)

        if autopopulate:
            # Check if form_name exists in the forms_info dictionary
            if form_name in forms_info:
                form_data = forms_info[form_name]
                text_fields = form_data['text_fields']
                text_positions = form_data['text_positions']
                
                for i, text in enumerate(text_fields):
                    page.insert_text(text_positions[i], text)
        else:
            # Get text from the manual entry fields (you can customize this part)
            text_fields = [text_entry.get(), text_entry2.get(), text_entry3.get()]
            text_positions = [fitz.Point(93, 300), fitz.Point(193, 400), fitz.Point(200, 500)]
            
            for i, text in enumerate(text_fields):
                page.insert_text(text_positions[i], text)

        # Save the modified PDF for the current form
        output_file_path = filedialog.asksaveasfilename(defaultextension=".pdf", filetypes=[("PDF files", "*.pdf")])
        if output_file_path:
            pdf_document.save(output_file_path)

        # Close the PDF document
        pdf_document.close()

def select_mode():
    mode = var.get()
    autopopulate_frame.pack_forget()
    add_text_button_autopopulate_mr.pack_forget()
    autopopulate_frame.pack_forget()
    add_text_button_autopopulate_pdf.pack_forget()
    text_entry.pack_forget()
    text_label1.pack_forget()
    text_entry2.pack_forget()
    text_label2.pack_forget()
    text_entry3.pack_forget()
    text_label3.pack_forget()
    manual_entry_frame.pack_forget()
    add_text_button_manual.pack_forget()

    if mode == "autopopulate_v1":
        autopopulate_frame.pack()
        add_text_button_autopopulate_mr.pack()
    elif mode == "autopopulate_v2":
        autopopulate_frame.pack()
        add_text_button_autopopulate_pdf.pack()
    elif mode == "manual":
        text_entry.pack()
        text_label1.pack()
        text_entry2.pack()
        text_label2.pack()
        text_entry3.pack()
        text_label3.pack()
        manual_entry_frame.pack()
        add_text_button_manual.pack()

def apply_text_to_selected_forms():
    autopopulate = var.get() != "manual"
    selected_forms = [form_var.get() for form_var in form_vars]
    add_text_to_pdf(autopopulate, selected_forms)

root = tk.Tk()
root.title("FMB")

autopopulate_frame = tk.Frame(root)

text_label1 = tk.Label(root, text="Name:")
text_entry = tk.Entry(root)

text_label2 = tk.Label(root, text="Class:")
text_entry2 = tk.Entry(root)

text_label3 = tk.Label(root, text="Date:")
text_entry3 = tk.Entry(root)

manual_entry_frame = tk.Frame(root)

# Create a button to trigger the PDF editing process for autopopulation
add_text_button_autopopulate_mr = tk.Button(autopopulate_frame, text="Add text to PDF", command=lambda: apply_text_to_selected_forms())

add_text_button_autopopulate_pdf = tk.Button(autopopulate_frame, text="Add text to PDF", command=lambda: apply_text_to_selected_forms())

# Create a button to trigger the PDF editing process for manual entry
add_text_button_manual = tk.Button(manual_entry_frame, text="Add Text to PDF", command=lambda: apply_text_to_selected_forms())

# Create radio buttons for selecting the mode
var = tk.StringVar()
var.set("autopopulate")
autopopulate_mr_radio = tk.Radiobutton(root, text="Autopopulate text From v1", variable=var, value="autopopulate_v1", command=select_mode)
autopopulate_pdf_radio = tk.Radiobutton(root, text="Autopopulate text From v2", variable=var, value="autopopulate_v2", command=select_mode)
manual_entry_radio = tk.Radiobutton(root, text="Manual Entry", variable=var, value="manual", command=select_mode)

autopopulate_mr_radio.pack()
autopopulate_pdf_radio.pack()
manual_entry_radio.pack()

# Create checkboxes for selecting forms
form_vars = []
form_vars.append(tk.StringVar())
form_vars.append(tk.StringVar())
# Add more form vars as needed

form_checkboxes = []
form_checkboxes.append(tk.Checkbutton(root, text="Form A", variable=form_vars[0], onvalue="Form A"))
form_checkboxes.append(tk.Checkbutton(root, text="Form B", variable=form_vars[1], onvalue="Form B"))
# Add more checkboxes for other forms as needed

for checkbox in form_checkboxes:
    checkbox.pack()

# Call select_mode() after setting the initial mode
select_mode()

# Start the Tkinter main loop
root.mainloop()

  enter image description here

1

There are 1 answers

0
acw1668 On BEST ANSWER

Note that the default value of offvalue option of Checkbutton is "0", so the initial value of the StringVar does not match either the onvalue or offvalue. It is better to specify offvalue="" in Checkbutton(...), so those checkbuttons are not checked initially.

Also selected_forms should contain only checked form, but currently it does not.

Below is the required change to fix the issue:

...

def apply_text_to_selected_forms():
    ...
    selected_forms = [form_var.get() for form_var in form_vars if form_var.get() != ""]
    ...

...

# specify the offvalue
form_checkboxes.append(tk.Checkbutton(root, text="Form A", variable=form_vars[0], onvalue="Form A", offvalue=""))
form_checkboxes.append(tk.Checkbutton(root, text="Form B", variable=form_vars[1], onvalue="Form B", offvalue=""))

...