Forms
An interactive PDF form, an AcroForm, is a document-level collection of fields
that a viewer lets the user edit: text boxes, checkboxes, radio groups, dropdown
and list boxes, push buttons and signature fields. UniPDF reads that collection
through reader.AcroForm, a *model.PdfAcroForm, and creates one with
model.NewPdfAcroForm().
The field tree
PdfAcroForm.Fields holds only the top-level fields. Fields nest through
PdfField.Kids, so a form is a tree, and form.AllFields() is the flattened
list you almost always want to iterate. It returns nil on a nil form, which
matters because a document with no form has reader.AcroForm == nil.
Two things are separate in a field, and confusing them is the usual source of “my value is in the file but nothing shows up”:
| Part | What it is |
|---|---|
| The field | The data. Name (T), value (V), default value (DV), flags (Ff). |
| The widget annotation | The appearance. A PdfAnnotationWidget in field.Annotations, positioned by its Rect and attached to a page. |
A field can own several widgets, which is how one value appears in more than one
place, and how a radio group’s buttons are siblings under a single parent field.
Viewers do not draw V; they draw the widget’s appearance stream. That is why
setting a value is not always enough on its own, and why appearance generation
gets its own guides below.
Names come in two forms. PartialName() is the field’s own T string;
FullName() is the dotted path from the root, parent.child. A field inside a
parent has both, and they are not the same string.
The specific field type lives in the context object rather than in PdfField:
switch t := field.GetContext().(type) {
case *model.PdfFieldText:
// text box; t.MaxLen, t.Q for alignment
case *model.PdfFieldButton:
// t.IsCheckbox(), t.IsRadio(), t.IsPush()
case *model.PdfFieldChoice:
// t.Opt holds the available options
case *model.PdfFieldSignature:
// signature placeholder
}Filling versus flattening
These are different operations with different outcomes, and a lot of workflows do both in sequence.
| Fill | Flatten | |
|---|---|---|
| Call | form.Fill / form.FillWithAppearance | reader.FlattenFields / FlattenFieldsWithOpts |
| Field values | Set in the AcroForm | Drawn into the page content stream |
| Still editable | Yes | No |
| Fields afterwards | Present | Removed from the page’s Annots and from the AcroForm |
| Text extractable as content | No, it lives in the field value | Yes |
Filling keeps the document interactive. The value is stored in the field, so a viewer can change it again, form data can be exported, and a later pass can read the value back out. What you lose is control over presentation: what the reader actually sees is whatever appearance stream is attached to the widget, and different viewers disagree about how much they are willing to regenerate on their own.
Flattening bakes the current appearance into the page and deletes the fields. Everything renders identically everywhere, the text becomes extractable page content, and nobody can edit it. What you lose is everything about the form: no more values to read back, no export, and no way to undo it in the same document. Flatten a copy, not your only copy.
The trap in between is a field that has a value but no appearance stream. Fill
does not generate appearances; flattening such a field draws nothing at all and
you get a blank space where the value should be. Pass an
annotator.FieldAppearance to FillWithAppearance or to FlattenFields to have
UniPDF generate the appearance itself.
Where to look
| Guide | Covers |
|---|---|
| List form fields | Walking the field tree, reading names, types and values. |
| Fill form fields | Setting values from JSON, keeping the form editable. |
| Create form | Building fields and widget annotations from scratch. |
| Flatten form | Baking appearances into the page and dropping the fields. |
| Fill and flatten with appearance | Generating appearance streams so flattened values are visible. |
| Custom text color | Per-field colors and fonts through AppearanceStyle. |
For signature fields specifically, see the signature guides. Signature fields are AcroForm fields, but signing has its own flow and flattening a signed document invalidates the signature.