Data Format#
Humument.load fetches static JSON produced by the pipeline's
03_export_web.py.
You can point the loader at any origin
that serves these files with these shapes — the npm packages are just one such
origin. Shapes are camelCase and match the exported types
exactly, so decoding is a plain JSON.parse.
The files#
Given a dataBase and an imageBase:
| URL | Contents |
|---|---|
${dataBase}/catalog.json |
{ "pages": number[], "chapters": [{ "pageNum", "label", "roman" }] } |
${dataBase}/pages/pNNNN.json |
One page: { "meta", "words"[], "gutters"[], "docks"[], "graph"[] } |
${dataBase}/pages/pNNNN.json.gz |
Gzipped twin of the above (tried first). |
${dataBase}/search-index.json |
{ "<token>": [[pageNum, count], …] } (lowercased tokens). |
${imageBase}/pNNNN.jpg |
One page image (1400 × 2100 grayscale JPEG). |
NNNN is the zero-padded printed page, e.g. p0033.
Gzip twin#
Page fetches try pages/pNNNN.json.gz first, decoding it in the browser with
DecompressionStream,
and fall back to plain pNNNN.json. The npm-hosted data ships only the .gz
files (the plain set exceeds jsDelivr's package-size limit); a self-hosted export
can supply either.
const res = await fetch(`${dataBase}/pages/p0033.json.gz`);
const page = await new Response(
res.body.pipeThrough(new DecompressionStream('gzip'))
).json();
Per-page JSON shape#
{
"meta": { "width": 1400, "height": 2100, "body": {…} | null, "valid": {…} | null },
"words": [ /* Word objects: id, text, x0,y0,x1,y1, lineIdx, conf,
prefix, suffix, pos, lemma, freq, rarity, isContent, isConnective */ ],
"gutters":[ /* Gutter objects: gutterId, kind, lineIdxA/B, x0,y0,x1,y1,
polyline, minWidth, riverScore */ ],
"docks": [ /* Dock objects, one per word (decoded into a Map<wordId, Dock>) */ ],
"graph": [ /* compact node tuples: [id, x, y, edges],
edges = [ [neighborId, cost, gutterId], … ] */ ]
}
Two representations differ between the wire format and what H gives you:
docksships as an array and is decoded intoH.docks, aMap<wordId, Dock>.graphships as compact[id, x, y, edges]tuples (the informationalkindfield is dropped to save bytes) and is decoded intoH.graph, aPageGraphwith aMap<id, GraphNode>.
Everything else parses 1:1 into the types documented under Pages & Words. Pixel coordinates are on the 1400 × 2100 normalized image (Concepts → Coordinate system).
The OCR database#
The JSON above is exported from data/humument.db, a SQLite database keyed
by page_num (1–367) — the canonical OCR artifact (see
The Pipeline). Its six tables map onto the JSON like so:
| Table | Holds | Exported into |
|---|---|---|
pages |
page_num, image path, width_px, height_px. |
meta.width / meta.height |
words |
one row per OCR'd word (bbox, line, conf, punctuation, NLP columns). | words[] |
page_corrections |
per-page deskew angle/offset + body/valid bboxes. Its page_num set is the content range. |
meta.body / meta.valid; catalog.pages |
page_gutters |
whitespace gutters/slits (polyline_json, min_width, river_score). |
gutters[] |
word_docks |
per-word breathing room, slack direction, ports_json. |
docks[] |
page_graph |
navigation nodes (x, y, edges_json). |
graph[] |
The catalog.json chapters are computed at export time from pages whose top
line reads "CHAPTER <Roman>", and search-index.json is built by counting
lowercased word tokens per page. Consumers never touch SQLite — they only see the
static JSON.
Building your own#
To host the data yourself, run the pipeline's stage 03 (or produce equivalent
JSON) and serve output/db/ as dataBase and your page images as imageBase.
See The Pipeline and
Overriding the data source.