pdf-acroform-fill
Goal
Produce a correctly filled PDF (new file) from a blank AcroForm template and structured user data, without relying on browser automation for the PDF viewer.
Do not use browser automation for in-browser PDFs
Chrome (and similar) render PDFs inside an internal viewer with no HTML form controls. Playwright/Cypress cannot reliably target AcroForm fields there.
Preferred path: download or use a local .pdf, then fill with pypdf via uv run.
Workflow
-
Confirm the PDF is AcroForm
- Run field listing (see below). If
get_fields()is empty and the doc still looks interactive, it may be XFA-only — say so and propose Adobe/manual or a specialized XFA tool; do not pretend pypdf will work.
- Run field listing (see below). If
-
Inventory fields
-
Use
reader.get_fields()for writer-safe keys (e.g. dotted child names). -
Walk page
/Annotswidgets for/TU(tooltip) → human label when/Tis opaque. -
Bundled helper from this skill (run from repo root or pass absolute path):
uv run --with pypdf --with cryptography python skills/pdf-acroform-fill/scripts/list_pdf_form_fields.py /path/to/form.pdf
-
-
Map data → field names
- Build a single
dictof{field_name: value}. - Checkboxes / radios: inspect
/AP/Nkeys for the checked export value (/On,/Oui, etc.). Wrong export = box stays off. - Repeating rows: often encoded as parent field + indexed kids; use keys exactly as
get_fields()returns them. - Dates: ask or follow user preference (e.g.
YYYY-MM-DD). Tooltip text hints are not always authoritative.
- Build a single
-
Write the filled PDF
from pypdf import PdfReader, PdfWriter reader = PdfReader("template.pdf") writer = PdfWriter() writer.append(reader) merged = {**text_fields, **button_fields} for page in writer.pages: writer.update_page_form_field_values(page, merged, auto_regenerate=False) writer.write("filled.pdf") -
Deliver
- Save
filled.pdfto a clear path. - Prefer a small, repeatable script next to the project if the user will regenerate (template path, constants, item list).
- Summarize non-obvious mappings (especially checkboxes and row indices).
- Save
When the user only has a URL
- Fetch or ask them to download the PDF; then fill locally.
- Do not promise Playwright will type into the embedded viewer.
Reference
See references/pypdf-filling.md for limitations and the write pattern.
Evals
Prompts for regression testing live in evals/evals.json. Extend with assertions when outputs are machine-checkable (field values in output PDF).