Document
bbox.Doc() returns the Doc facade, the whole surface a rule has for reading a document.
Documents reach rules through kis rules only today (--bbox, --with-bbox); rules.svc does not
load them. See bbox.
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");}HasAny(phrases...) → bool
Section titled “HasAny(phrases...) → bool”Whether any of the phrases appears.
| Parameter | Type | Meaning |
|---|---|---|
phrases | string, variadic | The alternatives |
Each phrase is a separate argument, so none can 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 |
Occurrences(phrase) → list
Section titled “Occurrences(phrase) → list”Every place the phrase resolves in scope, under the current matching settings, each with its page,
box and matched text. Diagnostic: it shows the same matches a read binds, in the same order. A rule
can only count the list, with .Len().
| Parameter | Type | Meaning |
|---|---|---|
phrase | string | The phrase to locate |
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 |
Column(fromPct, toPct) → Doc · LeftHalf(pct) → Doc · RightHalf(pct) → Doc
Section titled “Column(fromPct, toPct) → Doc · LeftHalf(pct) → Doc · RightHalf(pct) → Doc”The vertical strip between two page percentages, or the left or right pct% of the page: the
x-axis twins of Band, Top and Bottom.
| Parameter | Type | Meaning |
|---|---|---|
fromPct, toPct | int64 | Left and right bounds, as page percentages |
pct | int64 | Page percentage |
Two-column forms repeat a label set in each column, an appraiser block and a supervisory appraiser block both carrying “Company Name”. Nothing in the text tells them apart, so the rule says which side it means:
rule SupervisorCompany "the right-hand block's company" { when bbox.Doc().RightHalf(50).Has("Company Name") then out.Set("supervisorCompany", bbox.Doc().RightHalf(50).Right("Company Name").Str());}The names are LeftHalf and RightHalf because Left and Right are the directional reads.
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.
In(name) → Doc
Section titled “In(name) → Doc”Narrows to a region the host defined by name. A name that is not defined resolves to an empty scope
whose reason names it, never to the whole page. Neither kis rules nor rules.svc defines named
regions today, so use Region or the bands.
| Parameter | Type | Meaning |
|---|---|---|
name | string | The region name |
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 |
A heading-then-label lookup is a scope, so it is written as one: Section(heading) and then the
read.
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());}Row(phrase) → Doc
Section titled “Row(phrase) → Doc”The line the phrase sits on, on that phrase’s own page.
| Parameter | Type | Meaning |
|---|---|---|
phrase | string | The label whose line you want |
Section and Between start below their heading, because a heading labels what follows it. A
form question is answered on its own line, Escrow (T&I) [ ] Yes [X] No, and Row is the
scope that keeps one question’s options apart from the next one’s:
bbox.Doc().Row("Escrow").CheckedOption("Yes", "No").
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.
MaxGap(px) → Doc
Section titled “MaxGap(px) → Doc”Sets the widest blank, in pixels, a value read may cross.
| Parameter | Type | Meaning |
|---|---|---|
px | int64 | Gap in pixels |
A read ends where the page’s spacing says a column does. A form that sets its value far from the
label, Gross Living Area 2,924, stops the read short. kis bbox probe prints the page’s
threshold and the gap to each neighbour; set this above the gap it reports. Where the whole row is
spread out, LineWith needs no measurement at all.
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());}Because each label is a separate argument, each is separately checkable, so a failing chain
reports which link broke in Reason() 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");}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 schemas are registered on the kis rules command line, --schema <file> or --schemas <dir>;
the file’s base name, without .schema, is the name a rule uses.
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