Add ExistingFormFiller: read, fill, flatten and stamp existing PDF forms - #1
Open
christiaan wants to merge 7 commits into
Open
Add ExistingFormFiller: read, fill, flatten and stamp existing PDF forms#1christiaan wants to merge 7 commits into
christiaan wants to merge 7 commits into
Conversation
The library can already author new fillable forms from scratch, but has no way to open a PDF someone else produced (an uploaded template, a government form) and read its already-defined fields by name — that is the entry point for filling in an existing template's values. Field names are resolved through the /Parent chain into their fully qualified dotted form, since real-world AcroForms commonly nest fields (radio groups, grouped sections) rather than keep them flat.
setValue()/setValues() now mutate a copy of the field, never the source, so the same call site can safely reuse a filler across multiple documents without any risk of the input template changing underneath it. Checkbox and radio correctness needs more than writing /V: each widget's /AS must match one of the on-state keys actually present in that widget's own /AP/N sub-dictionary, since authoring tools are free to name those states however they like — guessing a fixed convention like "Yes" would silently produce an unrendered field in real viewers. A field can also have more than one widget annotation (the same checkbox or radio repeated across pages), so every widget is checked against the value and given a consistent /AS rather than only the first one. Radio requires an exact match against a widget's export name, since the generic yes/true/1 aliases meant for a single on/off checkbox would otherwise spuriously match whichever radio option happened to be checked last. A field reachable only through /Parent chain inheritance (grouped fields, as most real-world AcroForms use) previously had no way to be addressed at all. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Downstream, a filled form sometimes needs to stop being interactive (archival copies, signed submissions where the recipient's viewer shouldn't let anyone edit the answers afterward). Interactive fill alone (/V + /NeedAppearances) isn't enough for that — the field removal and baked appearance need to happen for real. Flattening reads each field's *current* value (including one set earlier in the same call chain, not just the source), draws it with the field's own /DA font pulled from the AcroForm's /DR so no font gets redeclared, then removes the widget annotation and the field entry itself. Supports flattening a named subset so unrelated fields stay interactive, and drops /AcroForm entirely once nothing is left in it, including once a grouped parent's last child is flattened. A page's /Resources and /Annots, and the AcroForm's /DR, are read through a shared helper that dereferences an indirect object first, since ISO 32000-1 allows any of these to be stored as their own indirect object rather than inlined — which Acrobat-produced forms commonly do — and treating that as absent would otherwise discard the page's existing fonts or leave a flattened widget's annotation behind. The baked `Tf` operator is only emitted once a font actually resolves against /DR, and baked text escapes bytes outside printable ASCII, so a non-ASCII field value survives as a valid PDF literal string. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Templates commonly need a signature or photo placed at a fixed spot alongside the answered fields, and that placement doesn't always map to a form field at all — so this is a plain page/x/y/w/h primitive, not something bolted onto the field API, and it composes with setValue()/flatten() on the same document. Reuses the existing pure-PHP PNG/JPEG decoder rather than adding an Imagick dependency, matching how the rest of the reader/merge stack already avoids one. Reads a page's existing /Resources through the same indirect-reference-aware helper flatten() uses, since Acrobat- produced forms commonly store it as its own indirect object, and treating that as absent would silently discard the page's existing fonts and XObjects instead of adding the stamp alongside them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Structural tests already cover field/annotation correctness, but nothing catches a rendering regression in the new flatten() drawing path or the image stamp. The existing visual-check.sh harness already renders reference PDFs and diffs them against committed goldens for pdfa-1b/pdfx-4 — this adds a form-fill document exercising the whole fill + flatten + stamp pipeline through the same check. The golden PNG itself is not committed here: this repo's own harness warns against generating goldens outside CI, since poppler builds anti-alias differently across platforms — the first CI run against this branch needs to establish it via visual-check.sh --update.
Fill-existing-form is a distinct entry point from the from-scratch FormField authoring already documented, and downstream users need to find it the same way they'd find the existing Forms section. Kept in lockstep with the other three translated USAGE.md files, same as the rest of the guide.
Some AcroForm templates carry a hidden field whose /TU is repurposed to hold structured metadata (e.g. a JSON placement hint for stamping an image onto the filled template) rather than a literal tooltip — a convention older pdftk-based fillers exposed via their own field dump. ExistingFormFiller had no way to read it back, blocking a migration off such tooling. Tooltips are stored as PDF text strings, which non-ASCII values encode as UTF-16BE with a byte-order mark rather than raw bytes (ISO 32000-1 §7.9.2.2), so the value is decoded accordingly to stay readable for anything beyond plain ASCII. A field's /T name segment is decoded through that same text-string helper, since forms use the same UTF-16BE convention there too. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
christiaan
force-pushed
the
feature/existing-acroform-fill
branch
from
September 2, 2026 17:51
6b08e09 to
ede8399
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The library can already author new fillable forms from scratch, but has no
way to open a PDF someone else produced (an uploaded template, a government
form) and read its already-defined fields by name, fill them in, bake the
result into static page content, and stamp an image alongside it.
field names through the /Parent chain into their fully qualified dotted form
names, matching every widget's own /AP/N on-state rather than guessing a
fixed convention
remove the AcroForm entries, for archival/signed copies that shouldn't stay
editable, correctly handling Resources/Annots/DR stored as indirect objects
(as Acrobat-produced forms commonly do)
placing a signature or photo that doesn't map to a form field
PDF text string (UTF-16BE with BOM, per ISO 32000-1 §7.9.2.2), same as a
field's /T name segment
Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com