AcroForm field access for documents and editors.
fields/1, field/2 and value/2 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. Like every other
edit it takes effect when the editor is written.
Fields come back as one struct per field type, listed in
PdfElixide.Form.Field, each carrying the widget it is as a :kind and its
decoded /Ff bits as :flags, and are addressed by the fully qualified name
each carries. Only an existing field can be set — there is no way to add one — and
a name that is not in the form is
{:error, %PdfElixide.Error{reason: :not_found}}, from field/2 and
value/2 as much as from put_value/3. Signature fields are not fillable and
are not reported at all — PdfElixide.Signature reads those — and writing a
check box or radio group back is not always faithful.
A form whose field hierarchy is cyclic, or nested far deeper than any real form, is reported as an error by every function here rather than read; the Forms guide gives the reason atoms.
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 takes 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
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 source() :: PdfElixide.Document.t() | PdfElixide.Editor.t()
Functions
@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.
Use PdfElixide.Signature.list/1 for those. 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 ignores the mark
entirely.
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.
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. 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, and a signature field cannot be
written at all — it 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, against a single fields/1
read. A name the form does not carry — a signature field included — is
{:error, %PdfElixide.Error{reason: :not_found}}, and a duplicated name, a
name that is not a string, or a value outside
PdfElixide.Form.Field.value/0 raises ArgumentError naming the field.
This is not a transaction. What can still fail after validation is the
handle itself — :closed, :panic, :lock_poisoned — and that stops at the
first error, leaving 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. Empty input returns
{:ok, editor} and makes no native call, so it leaves
PdfElixide.Editor.modified?/1 alone and answers the same way for a closed
editor.
It is a convenience, not a batching optimization — 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.