AcroForm field access for documents and editors.
fields/1, field/2, value/2 and export/3 read from either source — a
read-only PdfElixide.Document or a mutable PdfElixide.Editor
(source/0) — so inspecting a form needs no editor. Writing needs one:
put_value/3, put_values/2, update_value/3 and flatten/1,2 take an
Editor only, and each returns it, so filling and saving compose as a
pipeline.
"form.pdf"
|> PdfElixide.Editor.open!()
|> PdfElixide.Form.put_value!("full_name", "Jane Doe")
|> PdfElixide.Editor.save!("filled.pdf")
|> PdfElixide.Editor.close()flatten/1,2 is how a filled form stops being fillable: it draws the field
values into the page and takes the interactive fields away. It is deferred:
it takes effect when the editor is written.
export/3 hands the same fields back as FDF or XFDF bytes, so a filled form's
data can leave without the PDF around it.
Fields come back as per-type structs, addressed by their fully qualified
names; see PdfElixide.Form.Field. Recognized types carry :kind and decoded
:flags; PdfElixide.Form.Field.Unknown has flags but no :kind.
Only existing fillable fields can be written. Missing names and signature
fields return {:error, %PdfElixide.Error{reason: :not_found}}.
PdfElixide.Signature reads signatures separately. Check a button field's
:on_states before writing it back; see
Button limitations.
Operations that read the field hierarchy reject cycles and excessive depth
or size. Deferred flatten/1,2 marks do not validate that hierarchy; see
Field hierarchy errors.
Which lock a call takes follows its source: a shared read on a
PdfElixide.Document, and the editor's exclusive lock on a
PdfElixide.Editor — which fields/1 and export/3 take too, so concurrent
form work on one editor serializes even when it only reads. See the
Concurrency guide.
The Forms guide covers the field structs, both filling shapes, saving, and the signature and check-box caveats in full.
Summary
Functions
Exports the form's field data as FDF or XFDF bytes.
Same as export/3 but returns the bytes directly, raising on error.
Returns the single form field carrying the given name.
Same as field/2 but raises an error if it fails.
Extracts form fields from the given PDF document or editor.
Extracts form fields from the given PDF document or editor, raising an error if it fails.
Marks every page's form fields for flattening.
Marks the form fields of the page at the given zero-based index for flattening.
Marks every page's form fields for flattening, raising an error if it fails.
Marks the form fields of the page at the given zero-based index for flattening, raising an error if it fails.
Writes the value of an existing form field on the given editor, and returns the editor it was given.
Writes the value of an existing form field on the given editor, raising an error if it fails.
Writes several form field values on the given editor, and returns the editor.
Writes several form field values on the given editor, raising an error if it fails.
Reads a form field's value, applies fun to it, writes the result back, and
returns the editor.
Reads a form field's value, applies fun to it and writes the result back,
raising an error if it fails.
Returns the value of the single form field carrying the given name.
Same as value/2 but raises an error if it fails.
Types
@type export_format() :: :fdf | :xfdf
The encoding export/3 writes.
:fdf is the binary Forms Data Format of ISO 32000-1 §12.7.7; :xfdf is its
XML counterpart. They are not interchangeable for every value — see the
"Exporting field data" section of the Forms guide. Any other
value raises ArgumentError.
@type export_opts() :: [{:file_spec, String.t()}]
Options for export/3.
:file_spec— the name of the PDF this data came from, written into the output so a reader can pair the two. It is a label, not a path, and is absent by default.
Unknown keys and an invalid :file_spec raise ArgumentError. See the
"Exporting field data" section of the Forms guide for the
accepted characters.
@type source() :: PdfElixide.Document.t() | PdfElixide.Editor.t()
Functions
@spec export(source(), export_format(), export_opts()) :: {:ok, binary()} | {:error, PdfElixide.Error.t()}
Exports the form's field data as FDF or XFDF bytes.
The bytes are returned rather than written, so no path is involved:
File.write!("data.xfdf", PdfElixide.Form.export!(editor, :xfdf))What is exported is exactly what fields/1 reports for the same source, under
the same fully qualified names — signature fields are omitted from both. From
a PdfElixide.Editor that includes values written with put_value/3 but not
yet saved, so filling and exporting need no write in between.
Prefer :xfdf when values may contain non-ASCII text. The "Exporting field
data" section of the Forms guide covers both formats and
their field-value limitations.
@spec export!(source(), export_format(), export_opts()) :: binary()
Same as export/3 but returns the bytes directly, raising on error.
@spec field(source(), String.t()) :: {:ok, PdfElixide.Form.Field.t()} | {:error, PdfElixide.Error.t()}
Returns the single form field carrying the given name.
The name is the fully qualified one PdfElixide.Form.Field describes. A name
that is not in the form is {:error, %PdfElixide.Error{reason: :not_found}};
a form with two fields of the same name answers with the first.
PdfElixide.Form.field(doc, "full_name")
#=> {:ok, %PdfElixide.Form.Field.Text{name: "full_name", kind: :single_line,
# value: "John Doe", flags: %PdfElixide.Form.Field.Text.Flags{…}}}
@spec field!(source(), String.t()) :: PdfElixide.Form.Field.t()
Same as field/2 but raises an error if it fails.
@spec fields(source()) :: {:ok, [PdfElixide.Form.Field.t()]} | {:error, PdfElixide.Error.t()}
Extracts form fields from the given PDF document or editor.
A document with no AcroForm answers {:ok, []}, as does one whose form
declares no fields.
Signature fields are not reported; this API covers fillable form fields only.
PdfElixide.Signature.list/1 reads the signed ones and
PdfElixide.Signature.unsigned_fields/1 names the rest. See the
Forms guide.
@spec fields!(source()) :: [PdfElixide.Form.Field.t()]
Extracts form fields from the given PDF document or editor, raising an error if it fails.
@spec flatten(PdfElixide.Editor.t()) :: {:ok, PdfElixide.Editor.t()} | {:error, PdfElixide.Error.t()}
Marks every page's form fields for flattening.
Flattening draws each field's appearance into the page content and removes the
interactive fields, so the written PDF is no longer fillable. Nothing happens
until the next full write: PdfElixide.Editor.save/3 without :incremental,
or PdfElixide.Editor.to_binary/2. An incremental save is refused once a page
is marked, and because the mark cannot be removed it stays refused for the life
of the editor.
This also removes the document's AcroForm, and any signature field goes with
it — the dictionary stays in the file, but nothing references it any more, so
the document comes back unsigned. flatten/2 keeps the ones whose widgets
survive.
A document carrying no form is not an error. The mark cannot be removed — reopen the source for an unflattened document.
Refused on an encrypted source; filling is unaffected. See What an encrypted source cannot do.
Returns the editor. Check PdfElixide.Editor.flatten_warnings/1 after the
write: a field value can be flattened wrongly and still produce a valid PDF.
See the "Flattening" section of the Forms guide.
@spec flatten(PdfElixide.Editor.t(), non_neg_integer()) :: {:ok, PdfElixide.Editor.t()} | {:error, PdfElixide.Error.t()}
Marks the form fields of the page at the given zero-based index for flattening.
Deferred until the next full write, exactly as flatten/1 is, and refused on
an encrypted source for the same reason. Unlike
flatten/1 the AcroForm is kept, rebuilt to hold only the fields that still
have a widget on a page left unflattened; a field whose widgets do not name a
page is kept as it was. A signature field is kept or dropped by that same rule,
so one whose widget is on a flattened page is lost as it would be by
flatten/1.
Returns the editor, or {:error, %PdfElixide.Error{reason: :out_of_range}} if
the page does not exist. See the "Flattening" section of the
Forms guide.
@spec flatten!(PdfElixide.Editor.t()) :: PdfElixide.Editor.t()
Marks every page's form fields for flattening, raising an error if it fails.
@spec flatten!(PdfElixide.Editor.t(), non_neg_integer()) :: PdfElixide.Editor.t()
Marks the form fields of the page at the given zero-based index for flattening, raising an error if it fails.
@spec put_value(PdfElixide.Editor.t(), String.t(), PdfElixide.Form.Field.value()) :: {:ok, PdfElixide.Editor.t()} | {:error, PdfElixide.Error.t()}
Writes the value of an existing form field on the given editor, and returns the editor it was given.
The value is a plain term, the same shape fields/1 returns — a string,
true/false, a list of strings, or nil. Anything else raises
ArgumentError; see PdfElixide.Form.Field.value/0 for the full set.
{:ok, editor} = PdfElixide.Form.put_value(editor, "full_name", "Jane Doe")
{:ok, editor} = PdfElixide.Form.put_value(editor, "subscribe", true)Button fields are limited to /Yes and /Off; check for "Yes" in the
field's :on_states before writing true. An unknown name, or a signature
field, answers :not_found. The Forms guide has both.
@spec put_value!(PdfElixide.Editor.t(), String.t(), PdfElixide.Form.Field.value()) :: PdfElixide.Editor.t()
Writes the value of an existing form field on the given editor, raising an error if it fails.
@spec put_values( PdfElixide.Editor.t(), Enumerable.t({String.t(), PdfElixide.Form.Field.value()}) ) :: {:ok, PdfElixide.Editor.t()} | {:error, PdfElixide.Error.t()}
Writes several form field values on the given editor, and returns the editor.
Takes a map with string keys, or a list of {name, value} pairs.
Everything is validated before anything is written. A duplicated name, a name
that is not a string, or a value outside PdfElixide.Form.Field.value/0
raises ArgumentError naming the field. A name the form does not carry — a
signature field included — is {:error, %PdfElixide.Error{reason: :not_found}},
and nothing is written for the other names either.
The editor is held exclusively while the values are applied, so concurrent calls see the form before or after the batch, never partway through it. An unexpected runtime failure during application may leave earlier writes applied.
A list is applied in its own order; a map is applied in Enum order, which is
unspecified, so pass a list where the order matters.
See the Concurrency guide for what it locks.
@spec put_values!( PdfElixide.Editor.t(), Enumerable.t({String.t(), PdfElixide.Form.Field.value()}) ) :: PdfElixide.Editor.t()
Writes several form field values on the given editor, raising an error if it fails.
@spec update_value( PdfElixide.Editor.t(), String.t(), (PdfElixide.Form.Field.value() -> PdfElixide.Form.Field.value()) ) :: {:ok, PdfElixide.Editor.t()} | {:error, PdfElixide.Error.t()}
Reads a form field's value, applies fun to it, writes the result back, and
returns the editor.
A field carrying no value hands fun a nil. A name the form does not carry
is {:error, %PdfElixide.Error{reason: :not_found}} and fun is not called.
Whatever fun returns is written by put_value/3 and must be a
PdfElixide.Form.Field.value/0.
This is a read and then a write, not an atomic read-modify-write — another process holding the same editor can write in between; see the Concurrency guide.
@spec update_value!( PdfElixide.Editor.t(), String.t(), (PdfElixide.Form.Field.value() -> PdfElixide.Form.Field.value()) ) :: PdfElixide.Editor.t()
Reads a form field's value, applies fun to it and writes the result back,
raising an error if it fails.
@spec value(source(), String.t()) :: {:ok, PdfElixide.Form.Field.value()} | {:error, PdfElixide.Error.t()}
Returns the value of the single form field carrying the given name.
{:ok, nil} means the field exists but carries no value — distinct from
{:error, %PdfElixide.Error{reason: :not_found}}, which means no field
carries that name. Reach for field/2 when the field's type is needed too.
PdfElixide.Form.value(doc, "full_name")
#=> {:ok, "John Doe"}
@spec value!(source(), String.t()) :: PdfElixide.Form.Field.value()
Same as value/2 but raises an error if it fails.