API

Text reconstruction

Turning a bag of boxes back into text that reads like the page did.

import {
    cluster, getSize, getCharacterWidth,
    groupBoxesIntoLines, linesToFormattedText, boxesToFormattedText, boxesToText,
} from '@stabrise/scaledp'

A port of ScaleDP's BaseOcr.box_to_formatted_text, cluster and get_size. This is what keepFormatting: true runs on every recognizer.

boxesToText(boxes, separator = ' ')Reading order, joined
boxesToFormattedText(boxes, lineTolerance = 0)Layout preserved
groupBoxesIntoLines(boxes, lineTolerance = 0)Box[][]; 0 derives from character height
linesToFormattedText(lines, characterHeight)The second half, if you grouped yourself
getCharacterWidth(lines)The modal character width
cluster(items, maxGap, key?) / getSize(items, key?)The primitives

How it works

getSize is a trimmed-quartile mode, not a mean — one enormous heading must not set the character height for the page. cluster groups by gap. Together they turn "these boxes are at these coordinates" into "this is a line, this is an indent, this is a paragraph break".

lineTolerance: 0 derives the tolerance from the measured character height, which is almost always better than a fixed pixel number because it scales with the render DPI. Set it explicitly only when a page's line spacing is unusual.

Truncation matters

All truncation is Math.trunc, matching Python's int() rather than Math.floor. The two differ on negatives, and the parity goldens catch it.

Where you would use it directly

Rarely — the recognizers call it. It is exported for the case where you have boxes from somewhere else (a text layer, a different engine, a merged set) and want the same reconstruction:

import { boxesToFormattedText } from '@stabrise/scaledp'

const merged = [...row.pdf_text.bboxes, ...row.text.bboxes]
const combined = boxesToFormattedText(merged)

On this page