Rellenar formularios PDF con Python: pypdf, pdfrw y patrones reales
Tienes un formulario PDF (un W-9, un contrato modelo, una factura recurrente) y necesitas rellenar los campos programáticamente desde Python. Esta guía cubre las dos librerías open-source que dominan el espacio (pypdf y pdfrw), cuándo usar cada una, y los patrones reales que llevan código a producción.
Decisión rápida: ¿qué tipo de formulario tienes?
Hay dos estándares de formularios PDF:
- AcroForm (Adobe Acrobat Forms) — el estándar antiguo, sigue siendo el más común. Soportado por todas las herramientas Python.
- XFA (XML Forms Architecture) — versión XML embebida en el PDF, usada por gobiernos y bancos. Soporte limitado en herramientas open-source; a menudo requiere flatten primero.
Para detectar el tipo:
import pypdf
reader = pypdf.PdfReader("formulario.pdf")
fields = reader.get_form_text_fields()
print(f"AcroForm fields: {len(fields)}")
print(f"Form type: {reader.get_fields()}")
Si get_fields() devuelve dict con keys = nombres de campos, es AcroForm y vas bien. Si devuelve None pero el PDF "tiene cajas para rellenar" visualmente, probablemente es XFA — usa Adobe Acrobat o LibreOffice para "Save as standard PDF" antes de procesar.
pypdf — la opción moderna y mantenida activamente
pypdf (anteriormente PyPDF2) es la más usada en 2026, mantenida activamente, con buena documentación y API limpia.
pip install pypdf
Listar campos del formulario
from pypdf import PdfReader
reader = PdfReader("template.pdf")
fields = reader.get_fields()
for name, field in fields.items():
print(f"{name}: type={field.field_type}, value={field.value}, default={field.default_value}")
Esto te muestra todos los campos con su tipo (/Tx text, /Btn button/checkbox, /Ch choice/dropdown).
Rellenar campos de texto
from pypdf import PdfReader, PdfWriter
reader = PdfReader("template.pdf")
writer = PdfWriter(clone_from=reader)
# Rellenar por página (necesario incluso si el campo aparece solo una vez)
writer.update_page_form_field_values(
writer.pages[0],
{
"Nombre": "María García",
"Email": "maria@ejemplo.com",
"Fecha": "2026-05-07",
}
)
with open("rellenado.pdf", "wb") as out:
writer.write(out)
Rellenar checkboxes
Los checkboxes en AcroForm son técnicamente "buttons" con valores /Yes, /Off o nombres custom (como /On o /X). El valor exacto depende de cómo se creó el formulario:
# Inspecciona los valores aceptados
for name, field in reader.get_fields().items():
if field.field_type == "/Btn":
print(f"{name}: states = {field['/_States_']}") # típicamente ['/Off', '/Yes']
# Marcar checkbox
writer.update_page_form_field_values(
writer.pages[0],
{"AceptaTerminos": "/Yes"}
)
# Desmarcar
writer.update_page_form_field_values(
writer.pages[0],
{"AceptaTerminos": "/Off"}
)
Rellenar dropdowns y radio buttons
# Dropdown — usa el value (no el label visible)
writer.update_page_form_field_values(
writer.pages[0],
{"Pais": "ES"} # value, no "España"
)
# Radio button group
writer.update_page_form_field_values(
writer.pages[0],
{"Sexo": "/Female"}
)
"Aplanar" el formulario (lock fields para distribución)
Una vez rellenado, a veces quieres que el PDF NO sea editable más (legal, distribución a clientes). Esto se llama "flatten":
# pypdf no tiene flatten nativo en el momento de escribir esto.
# Workaround con pikepdf:
import pikepdf
with pikepdf.open("rellenado.pdf") as pdf:
pdf.flatten_annotations()
pdf.save("rellenado-flat.pdf")
O con Ghostscript:
gs -dPDFSETTINGS=/prepress -dNOPAUSE -dQUIET -dBATCH \
-sDEVICE=pdfwrite -dPreserveAnnots=false \
-sOutputFile=rellenado-flat.pdf rellenado.pdf
pdfrw — la opción ligera, sin dependencias
pdfrw es más antigua pero sigue siendo útil cuando quieres minimizar dependencias o trabajar con PDFs problemáticos que pypdf no maneja:
pip install pdfrw
from pdfrw import PdfReader, PdfWriter, PdfDict
template_pdf = PdfReader("template.pdf")
# pdfrw requiere encontrar y modificar los anotaciones manualmente
for page in template_pdf.pages:
annotations = page['/Annots']
if annotations:
for annotation in annotations:
if annotation['/T']:
key = annotation['/T'][1:-1] # quitar paréntesis del nombre
if key == "Nombre":
annotation.update(PdfDict(V="María García", AS="María García"))
PdfWriter().write("rellenado-pdfrw.pdf", template_pdf)
API más manual pero a veces es la única que funciona con PDFs raros generados por software empresarial antiguo.
Patrón producción: batch desde CSV
import csv
from pypdf import PdfReader, PdfWriter
template = PdfReader("contract-template.pdf")
with open("clients.csv") as f:
reader_csv = csv.DictReader(f)
for row in reader_csv:
writer = PdfWriter(clone_from=template)
writer.update_page_form_field_values(
writer.pages[0],
{
"ClientName": row["name"],
"ClientEmail": row["email"],
"Date": row["sign_date"],
"Amount": f"€{row['amount']:.2f}",
}
)
with open(f"contracts/{row['id']}.pdf", "wb") as out:
writer.write(out)
Genera 1.000 contratos personalizados en segundos.
Pitfall — los campos no aparecen rellenos en Adobe Reader
Causa típica: el PDF tiene /NeedAppearances flag puesto en False, lo que significa que los valores se guardan pero las "appearances" (cómo se renderizan visualmente) no se regeneran. Solución:
from pypdf import PdfWriter
from pypdf.generic import NameObject, BooleanObject
writer = PdfWriter(clone_from=reader)
# Forzar regeneración de appearances
catalog = writer._root_object
if "/AcroForm" in catalog:
catalog["/AcroForm"][NameObject("/NeedAppearances")] = BooleanObject(True)
Después de esto, Adobe Reader regenera la apariencia visual al abrir el PDF.
Pitfall — caracteres acentuados o tildes aparecen como ?
Los formularios PDF tienen una fuente embebida. Si esa fuente no incluye los glifos para "á", "ñ", etc., aparecen como ? o cuadrados vacíos. La fuente la define el creador del template — no se puede cambiar a posteriori sin reconstruir el PDF.
Workaround: si controlas el template, asegúrate de que usa una fuente con cobertura UTF-8 completa (Arial Unicode MS, Noto Sans, DejaVu Sans). Si no controlas el template y los acentos no aparecen, considera enviar el resultado en otro formato (DOCX rellenado con python-docx, o HTML→PDF con WeasyPrint donde tú controlas la fuente).
Conversiones relacionadas
- PDF a DOCX — extraer formulario a Word para edición masiva
- PDF a HTML — convertir formularios a HTML interactivo
- DOCX a PDF — workflow alternativo: rellenar en Word y exportar
- HTML a PDF — generar PDF rellenado desde plantilla HTML
- PDF a JPG — exportar páginas como imágenes para preview
- PDF a TXT — extraer datos textuales del PDF rellenado