@techspokes/ocr-box-geometry-descriptor
v1.2.0
Published
Transform OCR bounding boxes into LLM-friendly geometry descriptors
Readme
@techspokes/ocr-box-geometry-descriptor
Transform OCR bounding boxes into LLM-friendly geometry descriptors.
A TypeScript npm package that converts normalized bounding boxes (as produced by OCR engines) into rich, structured geometry descriptors. Pure math, zero runtime dependencies, works in Node.js, Cloudflare Workers, Deno, Bun, or any V8/ESM-compatible runtime.
Installation
npm install @techspokes/ocr-box-geometry-descriptorQuick Start
import { describeBox } from '@techspokes/ocr-box-geometry-descriptor';
const descriptor = describeBox(
{ left: 0.58, top: 0.92, right: 0.93, bottom: 0.97 },
{ pageNumber: 1, totalPages: 11 },
);
console.log(descriptor.box_bands.primary_cell); // "bottom:center"
console.log(descriptor.box_size.width_percent); // 35Batch Processing (OCR Chunks)
Process an array of OCR chunks in one call. The grounding.page field is 0-based (as OCR engines emit); it is converted to 1-based internally.
import { describeBoxes } from '@techspokes/ocr-box-geometry-descriptor';
const descriptors = describeBoxes(ocrChunks, totalPages, { preset: 'portrait' });If any chunk has invalid data, describeBoxes throws immediately (fail-fast). Wrap individual calls in try/catch if you need per-element tolerance.
Presets
Three built-in presets define the vertical/horizontal band boundaries for different page orientations:
| Preset | Vertical boundaries | Horizontal boundaries |
|--------|--------------------|-----------------------|
| portrait (default) | [0.05, 0.15, 0.82, 0.93] | [0.12, 0.88] |
| landscape | [0.08, 0.18, 0.78, 0.90] | [0.08, 0.92] |
| square | [0.08, 0.18, 0.82, 0.92] | [0.10, 0.90] |
Vertical boundaries produce 5 bands: top, upper, middle, lower, bottom.
Horizontal boundaries produce 3 bands: left, center, right.
Inspect a preset:
import { getPreset } from '@techspokes/ocr-box-geometry-descriptor';
const bands = getPreset('portrait');
console.log(bands.vertical); // [0.05, 0.15, 0.82, 0.93]
console.log(bands.horizontal); // [0.12, 0.88]Custom Bands
Override the preset with explicit band boundaries:
describeBox(box, page, {
bands: {
vertical: [0.10, 0.20, 0.75, 0.90],
horizontal: [0.15, 0.85],
},
});Resolution order: explicit bands > named preset > default ('portrait').
Output Schema
A GeometryDescriptor has this structure (all values are percentages rounded to 2 decimal places):
{
"descriptor_version": "1.0",
"box_in_document": {
"page_number": 1,
"total_pages": 11,
"page_position_percent": 9.09,
"is_first_page": true,
"is_last_page": false
},
"box_edges": {
"left_from_page_left": 58.36,
"right_from_page_left": 92.84,
"top_from_page_top": 92.15,
"bottom_from_page_top": 96.95
},
"box_center": {
"horizontal_from_page_left": 75.6,
"vertical_from_page_top": 94.55
},
"box_size": {
"width_percent": 34.48,
"height_percent": 4.8,
"area_percent": 1.66
},
"box_distance_to_page_edges": {
"to_left": 58.36,
"to_right": 7.16,
"to_top": 92.15,
"to_bottom": 3.05
},
"box_shape": {
"aspect_ratio": 7.18,
"is_wider_than_tall": true
},
"box_bands": {
"primary_cell": "bottom:center",
"start_cell": "lower:center",
"end_cell": "bottom:right",
"vertical_band": "bottom",
"horizontal_band": "center",
"spans_rows": true,
"spans_columns": true,
"row_span_count": 2,
"column_span_count": 2,
"overlap_cell": "bottom:center",
"overlap_percent": 70.74,
"all_cells": ["lower:center", "lower:right", "bottom:center", "bottom:right"]
}
}Coordinate Origin
By default, input coordinates use a top-left origin (standard for most OCR engines). For bottom-left origin systems (e.g., PDF coordinate space), pass origin: 'bottom-left':
describeBox(box, page, { origin: 'bottom-left' });Warning Callback
Out-of-range values (< 0 or > 1) are clamped automatically. To capture clamping events, pass an onWarn callback:
describeBox(box, page, {
onWarn: (msg) => console.warn(msg),
});The callback is fire-and-forget. If it throws, the error propagates to the caller.
Percentage-Space Caveat
All size values (width_percent, height_percent, area_percent) and aspect_ratio are computed in percentage space (0-100), not physical space. The aspect_ratio is the ratio of width percent to height percent, which equals the physical aspect ratio only for square pages.
Workers Compatibility
This package is pure JavaScript with zero dependencies and no Node.js APIs. It works out of the box in Cloudflare Workers, Deno, Bun, or any V8-compatible runtime.
License
MIT
