PDF forms
View SourceUse NativeElixirPdfUtilities.Forms to inspect, fill and flatten AcroForm
fields. HTML rendering creates these fields by default. An ordinary PDF with
printed lines or scanned boxes has no field names to fill; use Stamp.text/3
for explicit text placement in those documents.
Generate and complete a form
alias NativeElixirPdfUtilities.{Forms, HtmlToPdf}
html = """
<div>Your name: <input name="full_name"></div>
<div><input type="checkbox" name="consent"> I agree</div>
"""
{:ok, pdf} = HtmlToPdf.render(html)
{:ok, fields} = Forms.fields(pdf)
{:ok, completed} = Forms.fill(pdf, %{
"full_name" => "Cees Kettenis",
"consent" => true
})
{:ok, flattened} = Forms.flatten(completed)
File.write!("completed.pdf", flattened)fields/1 returns a list of maps with :name, :type, :value, :choices,
:read_only, :export_values and :widgets. Each widget has a one-based
:page and a :rect of [left, bottom, right, top] in PDF page coordinates.
Choice entries contain :value and :label. Checkbox values are booleans;
radio export values are strings.
HTML field names and compatibility
Text inputs, textareas, checkboxes, radio buttons and single-selection selects
become interactive fields. Buttons remain static artwork. Use
HtmlToPdf.render(html, forms: :static) for the pre-0.18 behavior.
A nonempty HTML name becomes the field key. Otherwise the renderer generates
TYPE_PAGENR_ELEMENT, such as TEXT_1_1, CHECKBOX_1_2, RADIO_2_1 or
CHOICE_2_2. Textareas use TEXT. Pages and element numbers start at 1.
The element number counts controls on that page in HTML document order after
pagination. CSS visual ordering does not change this order. The HTML id
attribute does not supply a field name.
All explicit names are reserved before assigning generated names. A generated
name that collides gains _1, _2, and so on until unique. Explicit duplicate
names fail, except for radio controls sharing a group name. Radio group values
must be distinct, nonempty, and different from the reserved Off state. At
most one radio control in a group may be checked. Select options require unique
values. HTML names cannot contain a period or NUL; periods separate nested
field names in existing PDFs.
Generated names can change when pagination changes. Supply name for keys
used by application code. Controls cannot span pages or repeat with the same
control identity in page furniture. HTML disabled makes a field read-only.
The HTML parser still supports its documented subset; HTML listboxes and
multiple-selection selects are not implemented.
Initial appearances use the rendered HTML styling. Text extraction reads page
content, so flatten fields first when you need their values included in
Text.extract/2 output.
Fill an existing PDF
{:ok, original} = File.read("application.pdf")
{:ok, fields} = Forms.fields(original)
IO.inspect(fields)
{:ok, completed} = Forms.fill(original, %{"applicant.full_name" => "Cees Kettenis"})Use the fully qualified names returned by inspection, including parent names separated by periods. Repeated widgets of a field are updated together. Unselected fields and unrelated annotations, metadata and page content remain in place.
| Field type | Fill value |
|---|---|
| Single-line text | UTF-8 string without line breaks, or nil to clear |
| Multiline text | UTF-8 string with explicit line breaks, or nil to clear |
| Checkbox | true or false |
| Radio group | One declared export-value string |
| Single choice | One declared value, or nil to clear |
| Multiple choice | A list of distinct declared values, or nil to clear |
Multiple choice is supported for existing PDFs with the appropriate field flag.
Inspection includes declared choices and export values. Unknown names,
read-only fields, invalid selections and field MaxLen violations return
diagnostics.
Filling generates text and choice appearances using bundled DejaVu Sans and black text. Text shrinks to fit, up to 12 points, with a two-point inset. Explicit line breaks are retained; automatic word wrapping is not performed. PDF left, center and right alignment are respected. Missing glyphs and widgets too small for the inset return diagnostics. Existing external font/style instructions are replaced by this supported appearance style.
Checkbox and radio fills preserve their existing Off/on artwork. Fields generated by this library retain their background and border artwork when filled. Existing external text-field decorations inside an old appearance are not reconstructed. All filled fields have self-contained appearances and do not depend on a viewer regenerating them.
Flatten selected fields
{:ok, flat_name} = Forms.flatten(completed, fields: ["applicant.full_name"])
{:ok, filled_and_flat} = Forms.fill(original,
%{"applicant.full_name" => "Cees Kettenis"}, flatten: true)flatten: true flattens only the fields supplied to that fill call.
Forms.flatten(pdf) flattens all supported fields. fields: [] selects none.
Flattening paints current appearances into page content, removes selected
widgets and prunes their field tree. Remaining fields stay interactive.
Appearance transforms, page rotation and existing resource names are preserved.
Flattening preserves screen visibility. Widgets marked Hidden or NoView are
removed without painting their appearances, including fields whose widgets
are all hidden. The Print flag does not change this screen policy;
print-only widgets are omitted and screen-only widgets become ordinary page
content. Invisible does not hide the supported Widget annotation type.
Missing or degenerate appearances on visible widgets return errors.
Updates are incremental. Earlier field values and attachments can remain in historical revisions. Flattening removes active interactivity and is not secure redaction.
Unsupported structures
XFA, pushbuttons, signature operations, password/comb/rich-text fields, editable combo boxes, field/widget actions and widget-specific rotation are unsupported. Changing signed PDFs or PDFs with signature permissions is rejected. Encrypted input follows the reader's existing rejection policy. Malformed field trees, ambiguous names and widgets not uniquely associated with a page fail with diagnostics. Automatic detection or conversion of drawn or scanned forms is outside this API.
Selected fields and widgets with either primary /A or additional /AA
actions are rejected, including indirect actions and action chains. Inspection
and editing unrelated fields do not certify a PDF as safe to open. Filling,
flattening, merging and other edits are not document sanitization; applications
must apply their own policy to uploaded PDFs before distributing them.
See resource limits for field, appearance-object and byte budgets, and attachments for embedding supporting files.