PdfElixide.Form (pdf_elixide v0.15.1)

Copy Markdown View Source

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

source()

@type source() :: PdfElixide.Document.t() | PdfElixide.Editor.t()

Functions

field(source, name)

@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{…}}}

field!(source, name)

@spec field!(source(), String.t()) :: PdfElixide.Form.Field.t()

Same as field/2 but raises an error if it fails.

fields(arg1)

@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.

fields!(source)

@spec fields!(source()) :: [PdfElixide.Form.Field.t()]

Extracts form fields from the given PDF document or editor, raising an error if it fails.

flatten(editor)

@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.

flatten(editor, page_index)

@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.

flatten!(editor)

@spec flatten!(PdfElixide.Editor.t()) :: PdfElixide.Editor.t()

Marks every page's form fields for flattening, raising an error if it fails.

flatten!(editor, page_index)

Marks the form fields of the page at the given zero-based index for flattening, raising an error if it fails.

put_value(editor, name, value)

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.

put_value!(editor, name, value)

Writes the value of an existing form field on the given editor, raising an error if it fails.

put_values(editor, values)

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.

put_values!(editor, values)

Writes several form field values on the given editor, raising an error if it fails.

update_value(editor, name, fun)

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.

update_value!(editor, name, fun)

Reads a form field's value, applies fun to it and writes the result back, raising an error if it fails.

value(source, name)

@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"}

value!(source, name)

@spec value!(source(), String.t()) :: PdfElixide.Form.Field.value()

Same as value/2 but raises an error if it fails.