PDF forms

View Source

Use 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 typeFill value
Single-line textUTF-8 string without line breaks, or nil to clear
Multiline textUTF-8 string with explicit line breaks, or nil to clear
Checkboxtrue or false
Radio groupOne declared export-value string
Single choiceOne declared value, or nil to clear
Multiple choiceA 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.