skills/handbook.adra.dev/pdf-acroform-fill

pdf-acroform-fill

SKILL.md

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

  1. 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.
  2. Inventory fields

    • Use reader.get_fields() for writer-safe keys (e.g. dotted child names).

    • Walk page /Annots widgets for /TU (tooltip) → human label when /T is 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
      
  3. Map data → field names

    • Build a single dict of {field_name: value}.
    • Checkboxes / radios: inspect /AP /N keys 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.
  4. 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")
    
  5. Deliver

    • Save filled.pdf to 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).

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).

Installs
3
First Seen
Apr 17, 2026