Tables and forms
Four types, for three different shapes of tabular data.
| Type | Comes from | For |
|---|---|---|
Table | Doc().Table(), .TableWith() | A table the analyser detected |
Row | Table().Row(), .RowWith() | One row of one |
Rows | Doc().Rows(start, end) | A section that reads like a table but was not detected as one |
Form | Doc().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.
Page() → int64
Section titled “Page() → int64”The page the table sits on.
RowCount() → int64
Section titled “RowCount() → int64”The number of data rows, excluding headers.
ColumnCount() → int64
Section titled “ColumnCount() → int64”The number of columns.
Headers() → list of string
Section titled “Headers() → list of string”The column header texts.
HasHeader(header) → bool
Section titled “HasHeader(header) → bool”Whether a column with this header exists.
| Parameter | Type | Meaning |
|---|---|---|
header | string | The 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");}At(row, header) → Value
Section titled “At(row, header) → Value”The value at a zero-based data row, under the named header.
| Parameter | Type | Meaning |
|---|---|---|
row | int64 | Zero-based data row |
header | string | The column header |
The form to prefer. A header survives a column being inserted; an index does not.
Cell(row, col) → Value
Section titled “Cell(row, col) → Value”The value at a zero-based row and column index.
| Parameter | Type | Meaning |
|---|---|---|
row | int64 | Zero-based data row |
col | int64 | Zero-based column |
For a table whose headers are unreliable or absent. Where headers exist, At is the safer call.
Row(index) → Row
Section titled “Row(index) → Row”A zero-based data row.
| Parameter | Type | Meaning |
|---|---|---|
index | int64 | Zero-based row |
RowWith(text) → Row
Section titled “RowWith(text) → Row”The first data row whose cells contain the text.
| Parameter | Type | Meaning |
|---|---|---|
text | string | Text 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());}Column(header) → list of Value
Section titled “Column(header) → list of Value”Every value under a header, top to bottom.
| Parameter | Type | Meaning |
|---|---|---|
header | string | The column header |
Records() → list of map
Section titled “Records() → list of map”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());}Found() → bool · Reason() → string
Section titled “Found() → bool · Reason() → string”Index() → int64
Section titled “Index() → int64”The zero-based data row index.
Get(header) → Value
Section titled “Get(header) → Value”The value under the named header.
| Parameter | Type | Meaning |
|---|---|---|
header | string | The column header |
Cell(col) → Value
Section titled “Cell(col) → Value”The value at a column index.
| Parameter | Type | Meaning |
|---|---|---|
col | int64 | Zero-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.
Naming the columns
Section titled “Naming the columns”Four locators, because a header can be identified four different ways.
Col(header, name, kind?) → Rows
Section titled “Col(header, name, kind?) → Rows”Maps a page column, found by its header text, to an output key.
| Parameter | Type | Meaning |
|---|---|---|
header | string | The header text on the page |
name | string | The output key |
kind | string, optional | A 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.
| Parameter | Type | Meaning |
|---|---|---|
header | string | The repeated header text |
occurrence | int64 | Which occurrence, from 1 |
name | string | The output key |
kind | string, optional | A 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.
| Parameter | Type | Meaning |
|---|---|---|
anchor | string | The group heading above it |
header | string | The column header |
name | string | The output key |
kind | string, optional | A 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.
| Parameter | Type | Meaning |
|---|---|---|
anchor | string | The group heading to its left |
header | string | The column header |
name | string | The output key |
kind | string, optional | A coercion |
Describing the row
Section titled “Describing the row”Describe(name) → Rows
Section titled “Describe(name) → Rows”Names the output key for each row’s free-text description — the leading column carrying the fee or item name.
| Parameter | Type | Meaning |
|---|---|---|
name | string | The output key |
Number(name) → Rows
Section titled “Number(name) → Rows”Names the output key for the row number, when the section is numbered.
| Parameter | Type | Meaning |
|---|---|---|
name | string | The output key |
Cleaning as you go
Section titled “Cleaning as you go”Strip(chars) → Rows
Section titled “Strip(chars) → Rows”Removes the given characters from every extracted value.
| Parameter | Type | Meaning |
|---|---|---|
chars | string | The characters to remove |
For the OCR artefacts a scan leaves in a fee table — curly quotes read as apostrophes, stray bullets, box-drawing fragments.
SplitOn(delimiter, tailName) → Rows
Section titled “SplitOn(delimiter, tailName) → Rows”Splits the description on a delimiter word, putting the tail in its own column.
| Parameter | Type | Meaning |
|---|---|---|
delimiter | string | The word to split on |
tailName | string | The output key for the tail |
SplitOn("to", "FeePayableToName") turns "TITLE - Lender's Title Insurance to Acme Title Co"
into a description and a payee.
Running it
Section titled “Running it”Records() → list of map
Section titled “Records() → list of map”Runs the extraction and returns the rows, ready for out.Set.
Count() → int64
Section titled “Count() → int64”How many rows the section yields.
Found() → bool · Reason() → string
Section titled “Found() → bool · Reason() → string”Whether it yielded anything, and what failed when it did not.
A worked section
Section titled “A worked section”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.
Found() → bool · Reason() → string
Section titled “Found() → bool · Reason() → string”Whether the form produced anything.
Name() → string
Section titled “Name() → string”The schema name.
Get(field) → Value
Section titled “Get(field) → Value”One extracted field.
| Parameter | Type | Meaning |
|---|---|---|
field | string | The field name |
Has(field) → bool
Section titled “Has(field) → bool”Whether the form produced this field.
| Parameter | Type | Meaning |
|---|---|---|
field | string | The field name |
FieldCount() → int64
Section titled “FieldCount() → int64”How many fields were extracted.
Fields() → map
Section titled “Fields() → map”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()));}Choosing between them
Section titled “Choosing between them”| The data is | Use |
|---|---|
| A ruled table the analyser found | Table |
| One row of that table, identified by content | Table().RowWith() → Row |
| Column-aligned text with no table structure | Rows |
| A layout you extract on many documents | A 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.
See also
Section titled “See also”- Document —
Table(),TableWith(),Rows(),Form() - Values — what a cell or field returns
mapandarray— finding a row inRecords()