How to Make Interactive PDF Forms in Python: A Comprehensive Tutorial

How to Make Interactive PDF Forms in Python – Creating interactive PDF forms can be incredibly useful for various applications, from collecting data to automating tasks. Python, with its powerful libraries, makes it easy to create and manipulate PDF forms.

In this tutorial, we’ll walk through the steps to create interactive PDF forms using Python, with detailed explanations and coding examples.

Libraries Required

To create and manipulate PDF forms in Python, we’ll use the reportlab and pdfrw libraries. You can install these libraries using pip:

pip install reportlab pdfrw

Step 1: Create a PDF Template with ReportLab

First, we’ll create a PDF template that contains the layout of the form. We’ll use the reportlab library for this purpose. Here’s how you can create a simple PDF form template:

from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors

def create_pdf_template(output_path):
    c = canvas.Canvas(output_path, pagesize=letter)
    width, height = letter

    c.drawString(100, height - 100, "Name:")
    c.drawString(100, height - 150, "Email:")
    c.drawString(100, height - 200, "Phone:")

    # Adding text fields (AcroForm fields)
    form = c.acroForm
    form.textfield(name='name', x=200, y=height - 110, width=300, height=20, textColor=colors.black, borderColor=colors.black)
    form.textfield(name='email', x=200, y=height - 160, width=300, height=20, textColor=colors.black, borderColor=colors.black)
    form.textfield(name='phone', x=200, y=height - 210, width=300, height=20, textColor=colors.black, borderColor=colors.black)

    c.save()

create_pdf_template("form_template.pdf")

Understanding the ReportLab toolkit in Python

The Python ReportLab toolkit allows the programmers to create forms that are interactive and fillable. The PDF standard usually consists of a rich set of interactive elements. The ReportLab toolkit does not support all of these elements; however, it does cover most of them. In the following section, we will look at the following widgets:

  1. Checkbox
  2. Radio
  3. Choice
  4. Listbox
  5. Textfield

All of the stated widgets are developed by calling different methods on the canvas.acroform property. Note that we can only have one form per document. Let us understand these widgets of the ReportLab toolkit.

Example:

from reportlab.pdfgen import canvas  
from reportlab.pdfbase import pdfform  
from reportlab.lib.colors import magenta, pink, blue, green  
def createSimpleCheckboxes():  
    my_Canvas = canvas.Canvas('checkboxes.pdf')      
    my_Canvas.setFont("Courier", 24)  
    my_Canvas.drawCentredString(300, 700, 'Programming Languages')  
    my_Canvas.setFont("Courier", 16)  
    form = my_Canvas.acroForm      
    my_Canvas.drawString(10, 650, 'Python:')  
    form.checkbox(  
        name = 'cb1',  
        tooltip = 'Field cb1',  
        x = 110,  
        y = 645,  
        buttonStyle = 'check',  
        borderColor = magenta,  
        fillColor = pink,   
        textColor = blue,  
        forceBorder = True  
        )      
    my_Canvas.drawString(10, 600, 'Java:')  
    form.checkbox(  
        name = 'cb2',  
        tooltip = 'Field cb2',  
        x = 110,  
        y = 595,  
        buttonStyle = 'cross',  
        borderWidth = 2,  
        forceBorder = True  
        )      
    my_Canvas.drawString(10, 550, 'C++:')  
    form.checkbox(  
        name = 'cb3',  
        tooltip = 'Field cb3',  
        x = 110,  
        y = 545,  
        buttonStyle = 'star',  
        borderWidth = 1,  
        forceBorder = True  
        )      
    my_Canvas.drawString(10, 500, 'C:')  
    form.checkbox(  
        name = 'cb4',  
        tooltip = 'Field cb4',  
        x = 110,  
        y = 495,  
        buttonStyle = 'circle',  
        borderWidth = 3,  
        forceBorder = True  
        )     
    my_Canvas.drawString(10, 450, 'JavaScript:')  
    form.checkbox(  
        name = 'cb5',  
        tooltip = 'Field cb5',  
        x = 110,  
        y = 445,  
        buttonStyle = 'diamond',  
        borderWidth = None,  
        checked = True,  
        forceBorder = True  
        )      
    my_Canvas.save()      
if __name__ == '__main__':  
    createSimpleCheckboxes()  

Output:

Populate the PDF Form with Data

Next, we’ll populate the PDF form with data using the pdfrw library. This library allows us to read the PDF template and fill in the form fields with the desired data.

import pdfrw

def fill_pdf_form(template_path, output_path, data):
    template_pdf = pdfrw.PdfReader(template_path)
    annotations = template_pdf.pages[0]['/Annots']

    for annotation in annotations:
        if annotation['/Subtype'] == '/Widget' and annotation['/T']:
            key = annotation['/T'][1:-1]
            if key in data:
                annotation.update(pdfrw.PdfDict(V='{}'.format(data[key])))
    
    pdfrw.PdfWriter().write(output_path, template_pdf)

data = {
    'name': 'John Doe',
    'email': 'john.doe@example.com',
    'phone': '123-456-7890'
}

fill_pdf_form("form_template.pdf", "filled_form.pdf", data)

Output:

In this script, we read the PDF template created in the previous step, and then populate the form fields with the data provided in the data dictionary. The PdfReader class is used to read the PDF, and the PdfWriter class is used to write the populated PDF to a new file.

Reading Data from a PDF Form

You might also want to read data from an existing filled PDF form. The following script demonstrates how to extract data from a filled PDF form using pdfrw:

def read_pdf_form(input_path):
    pdf = pdfrw.PdfReader(input_path)
    data = {}
    annotations = pdf.pages[0]['/Annots']

    for annotation in annotations:
        if annotation['/Subtype'] == '/Widget' and annotation['/T']:
            key = annotation['/T'][1:-1]
            value = annotation.get('/V')
            if value:
                data[key] = value[1:-1] if isinstance(value, pdfrw.objects.pdfstring.PdfString) else value
    
    return data

form_data = read_pdf_form("filled_form.pdf")
print(form_data)

In this script, we read the filled PDF form and extract the data from the form fields. The data is stored in a dictionary and printed to the console.

Detailed Explanation

Creating PDF Template

  1. Canvas Initialization: The canvas.Canvas function initializes a new PDF canvas. The pagesize parameter sets the size of the PDF, which is letter in this case.
  2. Drawing Text: The drawString method draws static text on the PDF. This is used to label the form fields.
  3. Adding Text Fields: The acroForm.textfield method creates interactive text fields. The name parameter assigns a unique name to each field, which is used later for data population. The position and size of the text fields are specified using the x, y, width, and height parameters.

Populating PDF Form

  1. Reading PDF Template: The pdfrw.PdfReader function reads the PDF template. The template_pdf.pages[0]['/Annots'] retrieves the list of annotations (form fields) from the first page of the PDF.
  2. Updating Form Fields: The update method updates the value of each form field. The PdfDict function is used to create a new dictionary with the updated value.

Reading PDF Form Data

Extracting Data: The pdfrw.PdfReader function reads the filled PDF form. The annotations are iterated to extract the values of the form fields. The extracted data is stored in a dictionary and printed to the console.

Understanding the Choice widget

The choice widget is fundamentally a combo box that shows a drop-down when the user clicks on it. This enables the user to pick one or more options from the drop-down list, relying on the fieldFlags we have set. If we insert edit to the fieldFlags, then the user can edit the element in the choice widget.

Let us understand the following example demonstrating the use of choice widgets in a PDF document:

Example:

from reportlab.pdfgen import canvas  
from reportlab.pdfbase import pdfform  
from reportlab.lib.colors import magenta, pink, blue, green, red  
def createSimpleChoices():  
    my_canvas = canvas.Canvas('choicesFile.pdf')      
    my_canvas.setFont("Courier", 24)  
    my_canvas.drawCentredString(300, 700, 'Sample Choices')  
    my_canvas.setFont("Courier", 16)  
    form = my_canvas.acroForm     
    my_canvas.drawString(10, 650, 'Choose a Letter:')  
    my_options = [('A', 'Av'), 'B', ('C', 'Cv'), ('D', 'Dv'), 'E',('F', ), ('G', 'Gv')]  
    form.choice(  
        name = 'choice1',  
        tooltip = 'Field choice1',  
        value = 'A',  
        x = 165,  
        y = 645,  
        width = 72,  
        height = 20,  
        borderColor = magenta,  
        fillColor = pink,   
        textColor = blue,  
        forceBorder = True,  
        options = my_options)    
    my_canvas.drawString(10, 600, 'Choose a Programming language:')  
    my_options = [('Python', 'python'), ('Java', 'java'), ('C++', 'C++')]  
    form.choice(  
        name = 'choice2',  
        tooltip = 'Field choice2',  
        value = 'Python',  
        options = my_options,   
        x = 305,  
        y = 595,  
        width = 72,  
        height = 20,  
        borderStyle = 'solid',  
        borderWidth = 1,  
        forceBorder = True  
        )     
    my_canvas.save()      
if __name__ == '__main__':  
    createSimpleChoices()  

Output:

In the above snippet of code, we have imported the required functions from the different modules of the ReportLab toolkit. We have then defined a function and created a new PDF file. We have then created two choice widgets with slightly distinct styles applied and saved the file. Remember to include the value parameter to the function; else, the program will return a bizarre error that does not say anything associated with the parameter being missing. At last, we have called the function.

Textfield widget

The textfield is a text entry widget. We can see these textfield widgets in forms to fill up the entries like name, address, and more. Most of the parameters of the textfield are the same as the ones we have observed in the earlier widgets.

Let us consider the following example to understand the same:

Example:

from reportlab.pdfgen import canvas  
from reportlab.pdfbase import pdfform  
from reportlab.lib.colors import magenta, pink, blue, green  
def createSimpleForm():  
    my_canvas = canvas.Canvas('PDFform.pdf')      
    my_canvas.setFont("Courier", 24)  
    my_canvas.drawCentredString(300, 700, 'Application Form')  
    my_canvas.setFont("Courier", 16)  
    form = my_canvas.acroForm      
    my_canvas.drawString(10, 650, 'First Name:')  
    form.textfield(  
        name = 'fname',  
        tooltip = 'First Name',  
        x = 110,  
        y = 635,  
        borderStyle = 'inset',  
        borderColor = magenta,  
        fillColor = pink,   
        width = 300,  
        textColor = blue,  
        forceBorder = True  
        )      
    my_canvas.drawString(10, 600, 'Last Name:')  
    form.textfield(  
        name = 'lname',  
        tooltip = 'Last Name',  
        x = 110,  
        y = 585,  
        borderStyle = 'inset',  
        borderColor = green,  
        fillColor = magenta,   
        width = 300,  
        textColor = blue,  
        forceBorder = True  
        )     
    my_canvas.drawString(10, 550, 'Address:')  
    form.textfield(  
        name = 'address',  
        tooltip = 'Address',  
        x = 110,  
        y = 535,  
        borderStyle = 'inset',  
        width = 400,  
        forceBorder = True  
        )      
    my_canvas.drawString(10, 500, 'City:')  
    form.textfield(  
        name = 'city',  
        tooltip = 'City',  
        x = 110,  
        y = 485,  
        borderStyle = 'inset',  
        forceBorder = True  
        )      
    my_canvas.drawString(250, 500, 'State:')  
    form.textfield(  
        name = 'state',  
        tooltip = 'State',  
        x = 350,  
        y = 485,  
        borderStyle = 'inset',  
        forceBorder = True  
        )      
    my_canvas.drawString(10, 450, 'Zip Code:')  
    form.textfield(  
        name = 'zip_code',  
        tooltip = 'Zip Code',  
        x = 110,  
        y = 435,  
        borderStyle = 'inset',  
        forceBorder = True  
        )      
    my_canvas.save()     
if __name__ == '__main__':  
    createSimpleForm()  

Output:

In the provided code snippet, we start by importing essential functions from various modules of the ReportLab toolkit. A function is defined to create a new PDF file, where textfield widgets with different styles are added. The text fields exhibit varied settings, including customized border and background colors. Some fields are kept standard, while the width argument is used to adjust the width of the text fields. Finally, the file is saved to reflect these changes.

    Author

    Sona Avatar

    Written by

    Leave a Reply

    Trending

    CodeMagnet

    Your Magnetic Resource, For Coding Brilliance

    Programming Languages

    Web Development

    Data Science and Visualization

    Career Section

    <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4205364944170772"
         crossorigin="anonymous"></script>