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

xfa

package module
v0.1.0 Latest Latest
Warning

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

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

README

xfa — go-pdfkit

License

Read the form inside a PDF whose pages are blank. Pure Go, no cgo.

A PDF form comes in two kinds and only one of them is a PDF form. Of 2 240 real government forms, 560 carry an XFA package — and 546 of those are static: a second, proprietary description of a form 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. Adobe removed the format from PDF 2.0; no browser and no other reader lays one out. Somebody handed such a file has a document that looks blank and is not.

What it does not do, and why that is a decision

It does not run the forms' scripts. That is measured, not assumed: 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. The reference implementation (pdf.js) spends 37 KB on FormCalc that this does not need.

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.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

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

func ParseMeasure(s string) (Measure, error)

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.

func (Measure) Points

func (m Measure) Points() float64

Points converts to the unit a PDF is drawn in.

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 ParseTemplate

func ParseTemplate(r io.Reader) (*Node, error)

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

func (n *Node) Child(kind string) *Node

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.

func (*Node) Children

func (n *Node) Children(kind string) []*Node

Children are every child of that kind, in order.

func (*Node) Get

func (n *Node) Get(name string) string

Get returns an attribute, or the empty string.

func (*Node) Walk

func (n *Node) Walk(visit func(*Node))

Walk calls visit on the node and everything under it, depth first, in document order.

Jump to

Keyboard shortcuts

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