Skip to content
Talk to our solutions team

Tables and forms

Four types, for three different shapes of tabular data.

TypeComes fromFor
TableDoc().Table(), .TableWith()A table the analyser detected
RowTable().Row(), .RowWith()One row of one
RowsDoc().Rows(start, end)A section that reads like a table but was not detected as one
FormDoc().Form(name)A named schema, defined outside the rules

Rows is the one worth understanding: scanned forms are full of column-aligned sections that no table detector will find, and it is the tool for those.

Found() → bool · Missing() → bool · Reason() → string

Section titled “Found() → bool · Missing() → bool · Reason() → string”

Whether a table was located, and what failed when it was not.

The page the table sits on.

The number of data rows, excluding headers.

The number of columns.

The column header texts.

Whether a column with this header exists.

ParameterTypeMeaning
headerstringThe header text
rule ScheduleHasPrincipal "the payment schedule breaks out principal" {
when
bbox.Doc().Table().Found() &&
!bbox.Doc().Table().HasHeader("Principal")
then
findings.Warn("ScheduleHasPrincipal", "completeness",
"Payment schedule has no Principal column");
}

The value at a zero-based data row, under the named header.

ParameterTypeMeaning
rowint64Zero-based data row
headerstringThe column header

The form to prefer. A header survives a column being inserted; an index does not.

The value at a zero-based row and column index.

ParameterTypeMeaning
rowint64Zero-based data row
colint64Zero-based column

For a table whose headers are unreliable or absent. Where headers exist, At is the safer call.

A zero-based data row.

ParameterTypeMeaning
indexint64Zero-based row

The first data row whose cells contain the text.

ParameterTypeMeaning
textstringText to find within the row

The row-lookup counterpart to TableWith — say what the row contains rather than where it sits.

rule TotalRowPresent "the schedule carries a total row" {
when
bbox.Doc().Table().RowWith("Total").Found()
then
out.Set("scheduleTotal",
bbox.Doc().Table().RowWith("Total").Get("Amount").Currency());
}

Every value under a header, top to bottom.

ParameterTypeMeaning
headerstringThe column header

Every data row as a map keyed by header — ready to hand to out.Set.

rule CaptureSchedule "store the payment schedule" {
when
bbox.Doc().Table().Found()
then
out.Set("paymentSchedule", bbox.Doc().Table().Records());
}

The zero-based data row index.

The value under the named header.

ParameterTypeMeaning
headerstringThe column header

The value at a column index.

ParameterTypeMeaning
colint64Zero-based column

A builder. Each method returns another Rows, so a section is described by chaining, and nothing is extracted until Records() or Count() runs.

This exists because the sections that matter on a scanned form — fee tables, contact blocks, itemised charges — are column-aligned text that no table detector will identify as a table.

Four locators, because a header can be identified four different ways.

Maps a page column, found by its header text, to an output key.

ParameterTypeMeaning
headerstringThe header text on the page
namestringThe output key
kindstring, optionalA coercion: currency, number, percent, date, yesno

ColNth(header, occurrence, name, kind?) → Rows

Section titled “ColNth(header, occurrence, name, kind?) → Rows”

Maps the Nth left-to-right occurrence of a repeated header.

ParameterTypeMeaning
headerstringThe repeated header text
occurrenceint64Which occurrence, from 1
namestringThe output key
kindstring, optionalA coercion

The answer to a form with two “Real Estate Broker” columns side by side.

ColUnder(anchor, header, name, kind?) → Rows

Section titled “ColUnder(anchor, header, name, kind?) → Rows”

Maps a column whose header sits beneath a group heading.

ParameterTypeMeaning
anchorstringThe group heading above it
headerstringThe column header
namestringThe output key
kindstring, optionalA coercion

This is the two-level header that closing forms use constantly — Borrower-Paid spanning At Closing and Before Closing.

ColRightOf(anchor, header, name, kind?) → Rows

Section titled “ColRightOf(anchor, header, name, kind?) → Rows”

Maps a column whose header sits to the right of a group heading.

ParameterTypeMeaning
anchorstringThe group heading to its left
headerstringThe column header
namestringThe output key
kindstring, optionalA coercion

Names the output key for each row’s free-text description — the leading column carrying the fee or item name.

ParameterTypeMeaning
namestringThe output key

Names the output key for the row number, when the section is numbered.

ParameterTypeMeaning
namestringThe output key

Removes the given characters from every extracted value.

ParameterTypeMeaning
charsstringThe characters to remove

For the OCR artefacts a scan leaves in a fee table — curly quotes read as apostrophes, stray bullets, box-drawing fragments.

Splits the description on a delimiter word, putting the tail in its own column.

ParameterTypeMeaning
delimiterstringThe word to split on
tailNamestringThe output key for the tail

SplitOn("to", "FeePayableToName") turns "TITLE - Lender's Title Insurance to Acme Title Co" into a description and a payee.

Runs the extraction and returns the rows, ready for out.Set.

How many rows the section yields.

Whether it yielded anything, and what failed when it did not.

rule ExtractSectionBFees "itemised services the borrower could not shop for" {
when
bbox.Doc().Has("Services Borrower Did Not Shop For")
then
out.Set("sectionBFees",
bbox.Doc().
Rows("Services Borrower Did Not Shop For", "Services Borrower Did Shop For").
Number("lineNumber").
Describe("feeName").
SplitOn("to", "payableTo").
ColUnder("Borrower-Paid", "At Closing", "borrowerAtClosing", "currency").
ColUnder("Borrower-Paid", "Before Closing", "borrowerBeforeClosing", "currency").
ColUnder("Seller-Paid", "At Closing", "sellerAtClosing", "currency").
Strip("|·").
Records());
}

Every element earns its place: the two headings bound the section, Number and Describe name the leading columns, SplitOn separates the payee from the fee name, three ColUnder calls reach columns under two group headings, Strip removes scan artefacts, and Records runs it.

Written against a detected table, this section would not be found at all.

A form schema is registered outside the rules, so the same extraction can be reused across rulesets without being restated.

Whether the form produced anything.

The schema name.

One extracted field.

ParameterTypeMeaning
fieldstringThe field name

Whether the form produced this field.

ParameterTypeMeaning
fieldstringThe field name

How many fields were extracted.

Every extracted field — ready to hand to out.Set.

rule ExtractBorrowerForm "run the registered borrower schema" {
when
bbox.Doc().HasForm("borrower_details") &&
bbox.Doc().Form("borrower_details").Found()
then
out.Set("borrower", bbox.Doc().Form("borrower_details").Fields());
audit.Log("ExtractBorrowerForm",
strings.Sprintf("extracted %d fields",
bbox.Doc().Form("borrower_details").FieldCount()));
}
The data isUse
A ruled table the analyser foundTable
One row of that table, identified by contentTable().RowWith()Row
Column-aligned text with no table structureRows
A layout you extract on many documentsA registered schema, via Form

If Doc().Table().Missing() on something that looks like a table to you, that is the signal to reach for Rows rather than to fight the detector.

  • DocumentTable(), TableWith(), Rows(), Form()
  • Values — what a cell or field returns
  • map and array — finding a row in Records()