Crear un formulario en un documento PDF en Python con borb

En esta guía, veremos cómo agregar un formulario interactivo que se puede completar en un documento PDF usando Python y borb, con menús desplegables y campos de texto de entrada.

El Formato de documento portátil (PDF) no es un formato WYSIWYG (Lo que ves es lo que obtienes). Fue desarrollado para ser independiente de la plataforma, independiente del sistema operativo subyacente y los motores de renderizado.

Para lograr esto, PDF se construyó para poder interactuar a través de algo más parecido a un lenguaje de programación, y se basa en una serie de instrucciones y operaciones para lograr un resultado. De hecho, PDF está basado en un lenguaje de secuencias de comandos: Posdata, que fue el primer lenguaje de descripción de página independiente del dispositivo.

En esta guía, utilizaremos borracho, una biblioteca de Python dedicada a leer, manipular y generar documentos PDF. Ofrece un modelo de bajo nivel (que le permite acceder a las coordenadas y el diseño exactos si elige usarlos) y un modelo de alto nivel (donde puede delegar los cálculos precisos de márgenes, posiciones, etc. a un administrador de diseño) .

En esta guía, veremos cómo generar un PDF con un formulario rellenable.

Instalando borb

borb puede descargarse desde la fuente en GitHub, o instalarse a través de pip:

1
$ pip install borb

Generación de un documento PDF con borb

Ahora que borb está instalado, podemos importar los bloques de construcción y construir una página PDF simple:

1
2
3
4
5
from borb.pdf.document import Document
from borb.pdf.page.page import Page
from borb.pdf.pdf import PDF
from borb.pdf.canvas.layout.page_layout.multi_column_layout import SingleColumnLayout
from borb.pdf.canvas.layout.page_layout.page_layout import PageLayout

El siguiente código representa los pasos básicos para crear un documento PDF usando borb:

  • Creación de un documento vacío
  • Crear una página vacía
  • Agregar la página al documento
  • Crear un PageLayout que sea responsable de manejar el flujo de contenido (aquí usaremos SingleColumnLayout)
  • Agregar contenido al PageLayout
  • Persistencia del documento en el disco

Dicho esto, sigamos adelante y creemos un Documento:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Create empty Document
pdf = Document()

# Create empty Page
page = Page()

# Add Page to Document
pdf.append_page(page)

# Create PageLayout
layout: PageLayout = SingleColumnLayout(page)

Con los pasos iniciales fuera del camino, podemos agregar el contenido. En este caso, será un formulario rellenable. Vamos a crear un formulario con algunas preguntas básicas de información del usuario, como nombre, apellido, etc.:

  • Nombre
  • Apellido
  • Género
  • Lugar de residencia
  • Nacionalidad

Para asegurarnos de que todo se presenta correctamente, vamos a agregar este contenido a una Tabla. La columna de la izquierda contendrá el nombre del campo (por ejemplo, "nombre", "apellido"), la columna de la derecha contendrá los campos que se deben completar.

Además, añadiremos otro Párrafo justo encima del formulario para anotarlo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# New import(s)
from borb.pdf.canvas.layout.table.fixed_column_width_table import FixedColumnWidthTable
from borb.pdf.canvas.layout.text.paragraph import Paragraph
from borb.pdf.canvas.layout.forms.text_field import TextField
from borb.pdf.canvas.color.color import HexColor
from decimal import Decimal
from borb.pdf.canvas.layout.layout_element import Alignment
from borb.pdf.canvas.layout.forms.drop_down_list import DropDownList

# Let's start by adding a heading
layout.add(Paragraph("Patient Information:", font="Helvetica-Bold"))

# Use a table to lay out the form
table: FixedColumnWidthTable = FixedColumnWidthTable(number_of_rows=5, number_of_columns=2)

# Name
table.add(Paragraph("Name : ", horizontal_alignment=Alignment.RIGHT, font_color=HexColor("56cbf9")))
table.add(TextField(value="Doe", font_color=HexColor("56cbf9"), font_size=Decimal(20)))

# Surname
table.add(Paragraph("Surname : ", horizontal_alignment=Alignment.RIGHT, font_color=HexColor("56cbf9")))
table.add(TextField(value="John", font_color=HexColor("56cbf9"), font_size=Decimal(20)))

Estos campos inpt son TextFields, que aceptan una cadena que se les pasa. Vamos a modelar el campo género como una lista desplegable, de la cual el lector puede elegir una de cuatro opciones:

  • Femenino
  • Masculino
  • Otro
  • Prefiero no revelar

Veamos cómo se traduce eso a borb:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Gender
table.add(Paragraph("Gender : ", horizontal_alignment=Alignment.RIGHT))
table.add(DropDownList(
    possible_values=[
                    "Female",
                    "Male",
                    "Other",
                    "Prefer not to disclose",
                    ]
))

Podríamos hacer algo similar para el país de residencia y la nacionalidad, pero implicaría tener que buscar una lista de todos los países del mundo y pasarla al constructor de DropDownList.

Esto significa cualquier lista suficientemente larga.

Debido a que este campo en particular (una lista de todos los países) es un requisito tan común, borb viene precargado con la clase CountryDropDownList:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# New import(s)
from borb.pdf.canvas.layout.forms.country_drop_down_list import CountryDropDownList

# Country of Residence
table.add(Paragraph("Country of Residence : ", horizontal_alignment=Alignment.RIGHT))
table.add(CountryDropDownList(value="Belgium"))

# Nationality
table.add(Paragraph("Nationality : ", horizontal_alignment=Alignment.RIGHT))
table.add(CountryDropDownList(value="Belgium"))

Ahora finalmente podemos agregar la Tabla a nuestro PageLayout:

1
2
3
4
5
6
# Set some properties on the table to make the layout prettier
table.set_padding_on_all_cells(Decimal(5), Decimal(5), Decimal(5), Decimal(5))
table.no_borders()

# Adding Table to PageLayout
layout.add(table)

Ahora agreguemos una política de protección de datos (sin sentido):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Data protection policy
layout.add(Paragraph("Data Protection Policy", 
                     font="Helvetica-Bold"))

# Dummy text
layout.add(Paragraph(
    """
    ** Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
    Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
    Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    """,
    font="Helvetica-Oblique"
))

Terminemos agregando un pie de página. Por ahora, solo agregaremos un rectángulo relleno con el color de acento, en la parte inferior de la página. Nada demasiado elegante.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# New import(s)
import typing
from borb.pdf.canvas.geometry.rectangle import Rectangle
from borb.pdf.page.page_size import PageSize
from borb.pdf.canvas.line_art.line_art_factory import LineArtFactory
from borb.pdf.canvas.layout.image.shape import Shape

ps: typing.Tuple[Decimal, Decimal] = PageSize.A4_PORTRAIT.value
r: Rectangle = Rectangle(Decimal(0), Decimal(32), ps[0], Decimal(8))
Shape(points=LineArtFactory.rectangle(r), stroke_color=HexColor("56cbf9"), fill_color=HexColor("56cbf9")).layout(page, r)

Por último, podemos almacenar el Documento que creamos usando la clase PDF:

1
2
3
4
5
6
# New import(s)
from borb.pdf.pdf import PDF

# Store
with open("output.pdf", "wb") as out_file_handle:
    PDF.dumps(out_file_handle, pdf)

¿Cómo se ve esto al final? Cuando ejecutemos el código y produzcamos el archivo PDF, tendrá algunos campos vacíos:

Fillable form in pdf created in python with borb

Al seleccionar estos campos, puede usar su teclado para ingresar los detalles en:

formulario pdf relleno python borb

Conclusión

En esta guía, ha aprendido cómo incluir elementos de formulario en su PDF, lo que permite al lector interactuar con el PDF.