Help us shape the future of Go! Take the 2026 Go Developer Survey

forms

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: BSD-3-Clause Imports: 12 Imported by: 0

README

forms

Read, fill and draw a PDF's AcroForm — the boxes somebody is meant to type into, the squares they tick, the lists they choose from.

Pure Go, CGO_ENABLED=0, standard library plus the rest of go-pdfkit. Builds for js/wasm and for every architecture the fleet targets.

d, _ := reader.Open(b)
f, ok := forms.Read(d)
if !ok {
    // The document has no form. An AcroForm dictionary with an empty field
    // list is not a form: 561 of the 118 833 files in the figure corpus are
    // exactly that, left behind by a producer.
}
for _, field := range f.Fields() {
    fmt.Println(field.Name, field.Kind, field.Value)
}
f.Fill("f1_02", "Wolfgang Amadeus Mozart")
f.Fill("c1_1", "yes")

What is awkward about forms

A field's value is not what gets drawn. What gets drawn is a little content stream written down beside it, called an appearance, and whoever changes the value has to write a new one. A reader that fills a field and does not do that has filled in nothing anybody can see.

So this draws them. Field.Appearance gives back the stream a value needs, built from the default appearance string the document supplies — a fragment of content stream naming a font, a size and a colour — in which a size of zero means "as large as fits", which is what every form in the corpus asks for.

It handles one line and several, ranged left, centred and ranged right, comb fields divided into one cell a character, list boxes with the chosen rows marked, and the largest size that fits both the width and the height of the box.

A checkbox needs none of this: its widget already carries a drawing for each of its states, so choosing among them is a matter of saying which. That is why every one of the corpus's 2 015 checkboxes has an appearance dictionary and its 11 803 text fields mostly have none — an empty box has nothing to show.

What it was measured against

The fleet's figure corpus is the wrong corpus for forms: of its 118 863 files, 561 carry an AcroForm and every one of those has an empty field list, and there are 40 widget annotations in the whole of it. Nobody puts a fillable form in a figure.

So a second one was fetched: 69 public forms, 68 with a form in them, 13 854 fields — 11 803 text (232 of them several lines, 121 comb), 2 015 checkboxes, 34 push buttons, 2 signature fields; 9 968 quadded, 1 787 with a stated length, 1 435 read-only. Every field that would take one was filled and 10 391 appearances drawn, every one naming a font the document actually carries.

That is what found a file whose fields name HelveticaLTStd-Bold 106 times and which does not carry it. A stream naming a font nothing can find draws nothing at all, so the form's own fallback is used instead.

What it does not do

XFA — Adobe's XML form language. 66 of those 68 forms carry an XFA copy beside the standard one, and every one of them is fillable through the standard one. A file that has both is not made more readable by reading the harder one. Form.HasXFA says when one is there.

Only one weight of each metric-compatible stand-in is carried, so a bold standard face is measured as its regular where the document gives no widths of its own. A bold string is a little wider than that, so an auto-sized bold value comes out a little larger than Acrobat would make it — a point on a line of text, stated here rather than hidden.

Licence

BSD-3-Clause.

Documentation

Overview

Package forms is the fillable half of a PDF: the boxes someone is meant to type into, the squares they tick, and the lists they choose from.

A form is not drawn where it is defined. The document holds one list of fields, each field holds its value, and each field points at one or more widgets — the rectangles on the pages where it shows. What a filled-in field looks like is a little content stream of its own, called an appearance, and the awkward part of forms is that a field's appearance is not derived from its value when the page is drawn: it is written down beside the value, and whoever changes the value has to write a new one. A reader that fills a field and does not do that has filled in nothing anyone can see.

So this package does three things. It reads the field tree — which is a tree, because a field may be a parent of others and inherits from wherever up the tree a thing was last said. It sets values. And it draws the appearance a value needs, from the default appearance string the document supplies, which is a fragment of content stream naming a font, a size and a colour, and in which a size of zero means "as large as fits".

What it does not do is XFA, Adobe's XML form language: 66 of the 68 forms in the corpus this was measured against carry an XFA copy alongside the standard one, and every one of them is fillable through the standard one. XFA is a second, proprietary description of the same form, and a file that has both is not made more readable by reading the harder one.

Index

Constants

This section is empty.

Variables

View Source
var ErrKind = errors.New("forms: the field does not hold that sort of value")

ErrKind is what a field says when it is asked for a value of the wrong sort — a checkbox given a sentence, or a text field ticked.

View Source
var ErrReadOnly = errors.New("forms: the field is read-only")

ErrReadOnly is what a field says when the document asks that it not be filled in.

Functions

This section is empty.

Types

type Appearance

type Appearance struct {
	// Content is the stream itself.
	Content []byte
	// BBox is the box it is drawn in, which is the widget's own size with its
	// corner at the origin: an appearance is drawn in a space of its own and
	// placed by the widget's rectangle.
	BBox [4]float64
	// FontName is the name Content uses for its font, and Font the object in
	// the form's resource dictionary that name stands for. Whoever writes the
	// stream out has to give it a resource dictionary saying so.
	FontName string
	Font     reader.Object
}

An Appearance is the little content stream that shows a field's value, and everything needed to put it in the document.

type Field

type Field struct {
	// Name is the whole name, with a dot between each part, which is how a
	// field is addressed from outside: the document's own names are only
	// unique among their brothers.
	Name string
	Kind Kind

	// Value is what the field holds. For a checkbox or a radio button it is
	// the name of the chosen state, and Off means not chosen.
	Value string
	// Values is what a list box holds when more than one row is chosen.
	Values []string
	// Options are the rows of a choice field: what is stored and what is
	// shown, which a file may say separately.
	Options []Option

	// MaxLen is how many characters a text field takes, or zero for as many
	// as one likes.
	MaxLen int
	// Quadding is 0 for ranged left, 1 for centred and 2 for ranged right.
	Quadding int

	ReadOnly bool
	Required bool
	NoExport bool
	// Multiline, Password and Comb are the ways a text field can be unusual.
	// A comb field is one divided into MaxLen equal cells, one character to
	// each, which is how a form asks for a code or a number one digit at a
	// time.
	Multiline bool
	Password  bool
	Comb      bool
	// Sorted and MultiSelect belong to a choice field, and Editable says a
	// combo box will take something that is not one of its rows.
	Sorted      bool
	MultiSelect bool
	Editable    bool

	// Widgets are the places on the pages where the field shows. Nearly every
	// field has one; a field asking the same thing on several pages has more.
	Widgets []Widget
	// contains filtered or unexported fields
}

A Field is one thing a person fills in.

func (*Field) Appearance

func (f *Field) Appearance(w Widget) (Appearance, bool)

Appearance draws the stream that shows what a field holds, for one of its widgets.

A checkbox or a radio button does not need one: its widget already carries a drawing for every state it has, and choosing among them is a matter of saying which — which is why every one of the 2 015 checkboxes in the corpus has an appearance dictionary and 11 803 text fields mostly have none, since an empty box has nothing to show.

func (*Field) Changed

func (f *Field) Changed() bool

Changed says a value was put here rather than read from the file, which is what tells whoever writes the document out what has to be written.

func (*Field) Checked

func (f *Field) Checked() bool

Checked says whether a box is ticked or a button pressed.

func (*Field) Choose

func (f *Field) Choose(values ...string) error

Choose picks rows of a choice field. A list box that says it takes several takes several; anything else takes one.

func (*Field) Press

func (f *Field) Press(state string) error

Press chooses one particular button of a radio group by the state its widget names, which is what to use when a group has more than two buttons.

func (*Field) SetChecked

func (f *Field) SetChecked(on bool) error

SetChecked ticks or unticks a checkbox, and presses a radio button.

A ticked box does not hold "true": it holds the name of the state its widget calls chosen, which every widget names for itself. That is how a row of radio buttons sharing one value says which of them was pressed.

func (*Field) SetText

func (f *Field) SetText(s string) error

SetText puts a string in a text field, or in a combo box that takes one.

func (*Field) States

func (f *Field) States() []string

States are the names the field's buttons answer to, which is what Press takes. Off is not among them; it is what every button is when none is pressed.

func (*Field) Text

func (f *Field) Text() string

Text is what the field shows: its value, or for a choice field the label its value stands for, since a file may store one thing and show another.

type Form

type Form struct {
	// contains filtered or unexported fields
}

A Form is a document's fillable part.

func Read

func Read(d *reader.Document) (*Form, bool)

Read reads a document's form, or reports that it has none. A document with an AcroForm dictionary and no fields in it has none: producers leave the dictionary behind, and 561 of the 118 833 files in the figure corpus have exactly that and nothing else.

func (*Form) Changed

func (f *Form) Changed() []*Field

Changed lists the fields somebody has filled in, which is what a writer needs and nothing more.

func (*Form) Field

func (f *Form) Field(name string) (*Field, bool)

Field finds one by its whole name.

func (*Form) Fields

func (f *Form) Fields() []*Field

Fields are every field that can hold a value, in the document's own order.

func (*Form) Fill

func (f *Form) Fill(name, value string) error

Fill sets a field by name, choosing the right sort of setter for what it is, which is what a command line or a map of values wants.

func (*Form) HasXFA

func (f *Form) HasXFA() bool

HasXFA says the document also carries Adobe's XML form description. Nothing here reads it: a file that has both is fillable through the standard half, which is the half this reads.

type Kind

type Kind uint8

A Kind says what sort of a field it is, which decides both what a value means and what its appearance looks like.

const (
	Text Kind = iota
	Checkbox
	Radio
	PushButton
	ComboBox
	ListBox
	Signature
)

The kinds a field may be. A push button is a field that holds no value at all — it is a place to click — and a signature field is one this package will read and will not pretend to fill.

func (Kind) String

func (k Kind) String() string

String names the kind, for a message someone has to read.

type Option

type Option struct {
	// Value is what is stored when the row is chosen and Label what is shown.
	// A file that gives only one gives the value, and the label is the same.
	Value string
	Label string
}

An Option is one row of a choice field.

type Widget

type Widget struct {
	// Page counts from one. It is zero when the document does not say which
	// page the widget is on, which happens in files whose page tree and
	// annotation lists disagree.
	Page int
	// Rect is where on that page, in the page's own coordinates, lower left
	// corner first.
	Rect [4]float64
	// On is the state name that means "chosen", for a checkbox or a radio
	// button. Every such widget has one of its own, which is how a set of
	// radio buttons sharing a value tells which one was pressed.
	On string
	// Hidden says the document asks for this one not to be drawn.
	Hidden bool
	// contains filtered or unexported fields
}

A Widget is one place on a page where a field shows.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL