Documentation
¶
Overview ¶
Package xfa reads Adobe's XML Forms Architecture — the form description carried inside a PDF whose pages are a placeholder.
Why this exists ¶
A PDF form comes in two kinds, and only one of them is a PDF form. Measured over 2 240 real government forms: 560 carry an XFA package, and 546 of those are STATIC — a second, proprietary description of a form that is already drawn on the pages, which go-pdfkit/forms reads and fills without help.
The other fourteen are DYNAMIC. Their pages hold a panel reading "Please wait... your PDF viewer may not be able to display this type of document", and the form exists only as XML, laid out when the document is opened. Adobe removed the format from PDF 2.0; no browser and no other reader lays one out. A person handed such a file has a document that looks blank and is not.
What it does, and what it will not ¶
This reads the template — the description of the form — into a typed tree, and the datasets, which hold what has been filled in. It does not run the forms' scripts, and that is a measured decision rather than a shortcut: of 5 744 scripts across those fourteen forms, 5 131 are handlers for enter, exit, change and click. They fire when somebody types. Only 188 run at load, so the form a reader first sees does not depend on them.
FormCalc, XFA's other scripting language, is not read either: every script in the corpus declares itself as JavaScript.
What is not built yet, and how it is known to be needed ¶
A field is not joined to its value here, and that is measured rather than deferred out of laziness. A form binds its fields to its data explicitly, through <bind> elements — one form in the corpus carries 222 of them — and the template's path to a field need not be the data's path to its value.
Taking the fourteen forms and asking how many template paths the data happens to answer:
cerfa_12064 212 fields, 212 values, 0 paths in common t657-fill-25e 459 fields, 459 values, 0 paths in common cerfa_12818 72 fields, 51 values, 47 paths in common CA-27_sample 136 fields, 99 values, 69 paths in common
The same count of each and not one path in common is what settles it: two of these forms name every field twice over, once for the layout and once for the data, and only <bind> says which goes with which. Guessing by name would answer confidently and wrongly, which is worse than not answering — so Values hands back what the data says, FieldNames what the template says, and nothing here pretends to join them.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FieldNames ¶ added in v0.2.0
FieldNames are the paths the TEMPLATE gives its fields, in the order it places them.
The path is the template's, and it is NOT necessarily the path the data uses. XFA binds a field to its data explicitly, through <bind> elements — one real form here carries 222 of them — and a form may name its root subform one thing while its data calls it another. Joining these paths to the ones Values returns is right for a form that binds implicitly and wrong for one that does not, which is why this package does not do it: see the note in the package documentation about what is not built yet.
Fields are looked for inside page areas as well as inside subforms. That is not a nicety: of the 212 fields in one real form, every one sits under a pageArea, and a walk that followed only subforms found none of them.
func Values ¶ added in v0.2.0
Values are what a form has been filled with, by the whole name of each field: "form1.Page1.Line101".
The names are joined with dots, as XFA's own scripting language names them, and the root of the data tree is included because a package may hold more than one form.
A field a person has not filled in has an empty value rather than no entry. The difference matters: "this field exists and is empty" and "there is no such field" are different answers, and a caller checking whether a form is complete needs the first.
Types ¶
type Measure ¶
type Measure float64
A Measure is a length, in points.
XFA writes lengths as a number and a unit — "62mm", "9pt", "0.25in" — and mixes them freely within one form: the corpus's simplest template gives its page in points, its content area's origin in inches and its fields' widths in millimetres, all in the same subform.
func ParseMeasure ¶
ParseMeasure reads a length. A bare number is points, which is what the specification says and what the corpus writes for a page's height.
type Node ¶
type Node struct {
// Kind is the element's name, without its namespace: "subform", "field",
// "draw", "pageArea".
Kind string
// Attr are its attributes, by name without namespace.
Attr map[string]string
// Text is the character data directly inside it, with surrounding space
// trimmed. A <text> element's caption lives here.
Text string
// Kids are the elements inside it, in order. Order is not decoration: a
// subform laid out top-to-bottom places its children in it.
Kids []*Node
}
A Node is one element of a template, with the children it contains.
The tree is kept whole rather than reduced to the elements this package understands today. XFA has some hundreds of element types and a form uses a few dozen; throwing the rest away at parse time would mean re-parsing to add each one, and would lose the ordering that layout depends on.
func ParseDatasets ¶ added in v0.2.0
ParseDatasets reads the datasets part of an XFA package — what has been filled in — and returns the data under it.
The tree mirrors the template's names. A form whose root subform is called form1 and whose field is called Number1 keeps its value at <data><form1><Number1>1.00</Number1></form1></data>, which is why the values can be found without laying the form out.
What comes back is the <data> element, not the <datasets> wrapper: the wrapper carries the package's plumbing, and every path into the form starts below it.
func ParseTemplate ¶
ParseTemplate reads the template part of an XFA package.
It is lenient in one direction and strict in the other. Namespaces are dropped, because a template writes the same element under xfa-template and under no namespace at all depending on who produced it, and the difference carries nothing. Processing instructions and comments are skipped: a template written by Adobe's designer carries hundreds of them recording what the designer did, which is not part of the form.
A document whose XML does not close is refused. Half a template is not a form, and laying one out would put half a form on paper without saying so.
func (*Node) Child ¶
Child is the first child of that kind, or nil. Most of the template is singular — a field has one ui, one border, one caption — so looking one up by name is how it is read.