Document
bbox.Doc() returns the Doc facade — 54 methods, and the whole surface a rule has for reading a
document.
Four things compose: an entry, a scope, a verb, and a coercion.
bbox.Doc() .Top(10) .Has("Closing Disclosure")// entry scope verb
out.Set("amount", bbox.Doc().Right("Loan Amount").Currency());// entry verb coercionScopes return another Doc, so they chain. Verbs end the chain, returning a
Value, a boolean, a number, or a
table type.
Matching is fuzzy and case-insensitive by default. You do not configure it — you opt out with
Exact() or MatchCase() when a near-miss would be wrong. OCR case is unreliable, so the default
is right far more often than not.
Presence
Section titled “Presence”Has(phrase) → bool
Section titled “Has(phrase) → bool”Whether the phrase appears within the current scope.
| Parameter | Type | Meaning |
|---|---|---|
phrase | string | The phrase to look for |
The edit budget is derived from the phrase, so you never supply a tolerance.
rule ClosingDisclosure "classify by the header band" salience 100 { when bbox.Doc().Top(10).Has("Closing Disclosure") then out.Set("docType", "closing_disclosure"); Retract("ClosingDisclosure");}HasAny(phrases...) → bool
Section titled “HasAny(phrases...) → bool”Whether any of the phrases appears.
| Parameter | Type | Meaning |
|---|---|---|
phrases | string, variadic | The alternatives |
Separate arguments replace the delimiter string the old API took: they cannot be mis-escaped, and each is matched on its own terms.
HasAll(phrases...) → bool
Section titled “HasAll(phrases...) → bool”Whether every phrase appears.
| Parameter | Type | Meaning |
|---|---|---|
phrases | string, variadic | The phrases that must all appear |
rule FormComplete "all three required sections are present" { when bbox.Doc().HasAll("Loan Terms", "Projected Payments", "Costs at Closing") then out.Set("sectionsComplete", true);}Count(phrase) → int64
Section titled “Count(phrase) → int64”How many times the phrase appears in scope.
| Parameter | Type | Meaning |
|---|---|---|
phrase | string | The phrase to count |
PageCount() → int64
Section titled “PageCount() → int64”How many pages the document has.
Scopes
Section titled “Scopes”Each returns a narrowed Doc. They compose left to right.
Top(pct) → Doc · Bottom(pct) → Doc
Section titled “Top(pct) → Doc · Bottom(pct) → Doc”Narrows to the top or bottom pct% of the page.
| Parameter | Type | Meaning |
|---|---|---|
pct | int64 | Page percentage |
A geometric band, not a detected header. There is no fixed “header”: the production corpus uses
depths of 15, 20, 25, 30, 40 and 50 percent, so the depth is yours to choose. Top(10) reads
better than a rectangle and is what most rules meant.
Band(fromPct, toPct) → Doc
Section titled “Band(fromPct, toPct) → Doc”The horizontal band between two page percentages.
| Parameter | Type | Meaning |
|---|---|---|
fromPct | int64 | Upper bound, as a page percentage |
toPct | int64 | Lower bound, as a page percentage |
Region(x0, y0, x1, y1) → Doc
Section titled “Region(x0, y0, x1, y1) → Doc”An explicit rectangle, in page percentages.
| Parameter | Type | Meaning |
|---|---|---|
x0, y0, x1, y1 | int64 | The corners, as whole page percentages |
The escape hatch for a layout the named bands do not fit. Percentages are whole numbers deliberately — a rule that needs fractional precision on a scan is a rule tuned to one document.
Page(n) → Doc
Section titled “Page(n) → Doc”One page of a multi-page document, 1-indexed.
| Parameter | Type | Meaning |
|---|---|---|
n | int64 | The page number, from 1 |
Section(heading) → Doc
Section titled “Section(heading) → Doc”The band beneath a heading, running to the bottom of the heading’s own page.
| Parameter | Type | Meaning |
|---|---|---|
heading | string | The heading to scope beneath |
Replaces the old "Heading<below>|Label" chain syntax — a chain was always a scope in disguise.
rule BorrowerSection "read the name from the borrower section only" { when bbox.Doc().Section("Borrower Information").Has("Name") then out.Set("borrowerName", bbox.Doc().Section("Borrower Information").Right("Name").Str());}Between(start, end) → Doc
Section titled “Between(start, end) → Doc”The band between two headings, on the start heading’s own page.
| Parameter | Type | Meaning |
|---|---|---|
start | string | The opening heading |
end | string | The closing heading |
The end heading is a real boundary: when it is given but cannot be found below the start, the scope does not silently run to the page bottom.
Exact() → Doc
Section titled “Exact() → Doc”Turns off fuzzy matching for this chain.
MatchCase() → Doc
Section titled “MatchCase() → Doc”Makes matching case-sensitive, and exact.
Case-sensitive-but-still-fuzzy is not a combination that means anything useful, so the facade does not offer it. Reach for either when a false positive costs you something — a document code, an identifier, a legal string.
rule ExactFormCode "the form code must match exactly" { when bbox.Doc().Exact().Has("FORM-1099-B") then out.Set("formCode", "FORM-1099-B");}Within(px) → Doc
Section titled “Within(px) → Doc”Caps a value read at px pixels from the label.
| Parameter | Type | Meaning |
|---|---|---|
px | int64 | Distance in pixels |
Prefer Words or Until. A pixel distance is resolution-dependent, so it does not transfer
between scans of the same form.
Words(n) → Doc
Section titled “Words(n) → Doc”Stops a value read after n words.
| Parameter | Type | Meaning |
|---|---|---|
n | int64 | Word limit |
Until(phrase) → Doc
Section titled “Until(phrase) → Doc”Stops a value read before the given phrase.
| Parameter | Type | Meaning |
|---|---|---|
phrase | string | Where to stop |
Anchoring to content rather than to a distance survives a change of layout, which is what makes this the best of the three limiters.
rule ReadAddressUntilNextLabel "the address runs up to the next field" { when bbox.Doc().Has("Property Address") then out.Set("propertyAddress", bbox.Doc().Until("Loan Amount").Right("Property Address").Str());}Line() → Doc · Lines(n) → Doc
Section titled “Line() → Doc · Lines(n) → Doc”Restricts a value read to the label’s own row, or to at most n rows.
| Parameter | Type | Meaning |
|---|---|---|
n | int64 | Row limit |
Reading a value
Section titled “Reading a value”The four directional verbs read a value positioned relative to a label. Each returns a
Value.
Right(anchors...) → Value · Left(anchors...) → Value · Below(anchors...) → Value · Above(anchors...) → Value
Section titled “Right(anchors...) → Value · Left(anchors...) → Value · Below(anchors...) → Value · Above(anchors...) → Value”| Parameter | Type | Meaning |
|---|---|---|
anchors | string, variadic | One label, or a chain |
Extra labels form a chain: each anchor is located below the one before it, and the value is read in the verb’s direction from the last.
rule DateIssuedUnderClosing "the date issued that sits under the closing heading" { when bbox.Doc().Has("Closing Date") then out.Set("dateIssued", bbox.Doc().Right("Date Issued", "Closing Date").Date());}That chain replaced three separate string mini-languages — a <below> form, a |> anchor path
used at 111 sites, and their combinations. All three meant the same thing, and of 161 directional
hops in the corpus, 146 were downward. Making them arguments rather than syntax means each label is
separately checkable, so a failing chain reports which link broke rather than returning an
empty string.
Cell(rowLabel, colLabel) → Value
Section titled “Cell(rowLabel, colLabel) → Value”The value at the intersection of a row label and a column label.
| Parameter | Type | Meaning |
|---|---|---|
rowLabel | string | The row’s label |
colLabel | string | The column’s label |
The honest expression for a grid. Many rules reach the same cell with a long directional chain; this says what it means.
rule PrincipalAtYearFive "the principal column, year five row" { when bbox.Doc().Table().Found() then out.Set("principalYear5", bbox.Doc().Cell("Year 5", "Principal").Currency());}Checked(label) → bool
Section titled “Checked(label) → bool”Whether a checkbox with the given label is ticked.
| Parameter | Type | Meaning |
|---|---|---|
label | string | The checkbox’s label |
Scope narrows which checkbox: the detector probes the whole document, so Page(2).Checked(...)
is how you reach the one on page two when the same label appears twice.
CheckedOption(options...) → Value
Section titled “CheckedOption(options...) → Value”Whichever of the options is ticked, or a miss.
| Parameter | Type | Meaning |
|---|---|---|
options | string, variadic | The mutually exclusive options |
Scope-aware like Checked, so two copies of the same option group on different pages do not
interfere.
rule OccupancyType "read the ticked occupancy option" { when bbox.Doc().Section("Occupancy").CheckedOption( "Primary Residence", "Second Home", "Investment").Found() then out.Set("occupancy", bbox.Doc().Section("Occupancy").CheckedOption( "Primary Residence", "Second Home", "Investment").Str());}Title() → Value
Section titled “Title() → Value”The largest line in scope — the document’s title, or a section’s heading when the scope is a section. It honours scope.
AllText() → Value
Section titled “AllText() → Value”Every block of text in the current scope, in reading order.
LineWith(phrase) → Value
Section titled “LineWith(phrase) → Value”The full OCR row containing a phrase.
| Parameter | Type | Meaning |
|---|---|---|
phrase | string | The phrase whose row you want |
This exists because a label and its value routinely share a row but land in different reading
blocks — so a directional read misses, and the row does not. Reach for it when Right returns
nothing and you can see the value sitting right there.
LinesWith(phrase) → list of string
Section titled “LinesWith(phrase) → list of string”Every OCR row containing the phrase, in reading order.
| Parameter | Type | Meaning |
|---|---|---|
phrase | string | The phrase to find rows for |
FirstLine() → Value
Section titled “FirstLine() → Value”The first line in scope, in reading order. Unscoped, the document’s own first line; scoped, the first row the scope contains.
LineCount() → int64
Section titled “LineCount() → int64”How many text lines are in scope.
Typography and structure
Section titled “Typography and structure”Typography is how you tell a heading that reads like body text from one that is actually set as a heading.
Headings() → list of string
Section titled “Headings() → list of string”Every phrase the document renders as a heading, in reading order. A heading is a line set larger than the document’s body text.
HeadingsAbove(ratio) → list of string
Section titled “HeadingsAbove(ratio) → list of string”Only the headings set at least ratio times body size.
| Parameter | Type | Meaning |
|---|---|---|
ratio | float64 | Multiple of body size |
HeadingsAbove(2.0) selects headings at twice body size — the major ones.
HeadingCount() → int64
Section titled “HeadingCount() → int64”How many headings are in scope.
IsHeading(phrase) → bool · IsTitle(phrase) → bool
Section titled “IsHeading(phrase) → bool · IsTitle(phrase) → bool”Whether the phrase is rendered as a heading — larger or heavier than body text — rather than merely present.
| Parameter | Type | Meaning |
|---|---|---|
phrase | string | The phrase to test |
The same test under two names, so the call site can read naturally. For classification this is usually stronger evidence than presence: a form that mentions “Closing Disclosure” in a paragraph is not a Closing Disclosure.
rule ClassifyByTitle "the phrase is set as a title, not merely mentioned" salience 100 { when bbox.Doc().Top(20).IsTitle("Closing Disclosure") then out.Set("docType", "closing_disclosure"); out.Set("classificationBasis", "title"); Retract("ClassifyByTitle");}FontSize(phrase) → float64
Section titled “FontSize(phrase) → float64”The point size at which a phrase is rendered, or 0 when absent.
| Parameter | Type | Meaning |
|---|---|---|
phrase | string | The phrase to measure |
Resolves the phrase the same way Has does — fuzzily, across blocks.
BodyFontSize() → float64
Section titled “BodyFontSize() → float64”The document’s body text size. Informational — do not compare FontSize against it directly;
use IsBigText, which handles the comparison properly.
IsBigText(phrase) → bool
Section titled “IsBigText(phrase) → bool”Whether a phrase is rendered notably larger than body text.
| Parameter | Type | Meaning |
|---|---|---|
phrase | string | The phrase to test |
Patterns
Section titled “Patterns”A pattern is a named value shape — "date", "currency", "ssn", "email", and whatever else
the installed pattern pack registers.
HasPattern(pattern) → bool
Section titled “HasPattern(pattern) → bool”Whether the scope contains any value of the named pattern.
| Parameter | Type | Meaning |
|---|---|---|
pattern | string | The pattern name |
CountOf(pattern) → int64
Section titled “CountOf(pattern) → int64”How many values of the pattern the scope contains.
| Parameter | Type | Meaning |
|---|---|---|
pattern | string | The pattern name |
FirstOf(pattern) → Value
Section titled “FirstOf(pattern) → Value”The first in-scope value of the pattern.
| Parameter | Type | Meaning |
|---|---|---|
pattern | string | The pattern name |
rule UnredactedSSN "a tax identifier appears in the clear" salience 90 { when bbox.Doc().HasPattern("ssn") then findings.Critical("UnredactedSSN", "privacy", strings.Sprintf("%d unredacted tax identifiers found", bbox.Doc().CountOf("ssn")));}Patterns are how you ask “is there a thing of this kind anywhere here” without knowing its label — the privacy sweep above being the canonical case.
Tables
Section titled “Tables”Full detail on the returned types is in Tables and forms.
Table() → Table
Section titled “Table() → Table”The first table within the current scope.
Tables() → list of Table
Section titled “Tables() → list of Table”Every table in scope, in document order. The scope’s bounds are page-local: each table is tested against the bounds on its own page.
TableCount() → int64
Section titled “TableCount() → int64”How many tables are in scope.
TableWith(text) → Table
Section titled “TableWith(text) → Table”The table containing the given text.
| Parameter | Type | Meaning |
|---|---|---|
text | string | Text that appears inside the wanted table |
Scope still applies, so Page(2).TableWith("Origination Charges") looks only at page two.
Rows(startHeading, endHeading) → Rows
Section titled “Rows(startHeading, endHeading) → Rows”Starts a column-aligned extraction of the band between two headings — for sections that read like tables but are not detected as one.
| Parameter | Type | Meaning |
|---|---|---|
startHeading | string | Where the band starts |
endHeading | string | Where it ends. Empty means “to the bottom of the page” |
Form(name) → Form
Section titled “Form(name) → Form”Runs the named form schema within the current scope.
| Parameter | Type | Meaning |
|---|---|---|
name | string | The registered schema name |
HasForm(name) → bool
Section titled “HasForm(name) → bool”Whether a form schema with this name is registered.
| Parameter | Type | Meaning |
|---|---|---|
name | string | The schema name |
FormTable(name) → list of map
Section titled “FormTable(name) → list of map”Runs a named table or repeater schema, returning its rows.
| Parameter | Type | Meaning |
|---|---|---|
name | string | The schema name |
Returns nil, not an empty list, when no such schema is registered — so a rule can tell “no schema” from “schema found nothing”.
HasFormTable(name) → bool
Section titled “HasFormTable(name) → bool”Whether a table or repeater schema with this name is registered.
| Parameter | Type | Meaning |
|---|---|---|
name | string | The schema name |
Escape hatches
Section titled “Escape hatches”Document() → Document
Section titled “Document() → Document”Returns the underlying document object.
For the cases the facade does not cover. Reaching for this is a signal that something is missing here — if you need it, that is worth reporting, because the facade is meant to be sufficient.
Debug(on)
Section titled “Debug(on)”Turns verbose spatial logging on or off.
| Parameter | Type | Meaning |
|---|---|---|
on | bool | Whether to log |
It mutates the underlying document, so it affects every Doc sharing it — including reads in
other rules. Turn it off again, or leave it out of anything but a debugging session.
See also
Section titled “See also”- Values — what a read returns, and how to coerce it
- Tables and forms — the table, row and form types
bbox— documents — loading, andbbox.Doc()itself- The Document API — the same surface as a guide, with the reasoning