readany-render
v0.1.3
Published
Local, inspectable office document display lists for browsers
Maintainers
Readme
readany-render
readany-render turns common office documents into deterministic, inspectable
display lists and rasterises those lists without uploading a document. Every
text run retains a source cell, paragraph range, or slide shape so applications
can connect visible evidence back to its origin.
use readany_render::{Options, Rect, rasterise_rect, render};
let bytes = std::fs::read("statement.xlsx")?;
let document = render(&bytes, &Options {
filename: Some("statement.xlsx"),
..Options::default()
})?;
let viewport_png = rasterise_rect(
&document.pages[0],
Rect { x: 0.0, y: 0.0, width: 1_200.0, height: 800.0 },
1.0,
)?.encode_png()?;
# Ok::<(), Box<dyn std::error::Error>>(())import init, { open } from "readany-render";
await init();
const document = open(bytes, { filename: "statement.xlsx" });
document.renderRectToCanvas(
0,
{ x: scrollLeft, y: scrollTop, width: canvas.clientWidth, height: canvas.clientHeight },
canvas,
{ scale: devicePixelRatio },
);
const visibleItems = document.itemsInRect(0, {
x: scrollLeft,
y: scrollTop,
width: canvas.clientWidth,
height: canvas.clientHeight,
});
console.log(document.unrendered);open retains the display list in WASM and is the intended browser API for
natural spreadsheet canvases. Only visible pixels or provenance-bearing items
cross the JavaScript boundary. The plain-object render API remains useful for
small documents and inspection. In Rust, use rasterise_rect and
items_in_rect for the same viewport workflow; rasterise deliberately
refuses full canvases above 100 million pixels.
Measured format status
“Implemented” means the parser reaches a display list and names omissions. It does not mean visual fidelity has been demonstrated. Every claim below names the corpus behind it; results from one format are never generalized to another.
Read the flow-format rows carefully. The page-aligned scores in
docs/FIDELITY.md combine text identity with geometry. The August 13 fixes
restored exact pagination for the real DOCX and ODT documents, and the August 16
round laid Word tables out as columns and placed slide shapes through their
groups. ODT is now placed about as well as it is read; DOCX and PPTX improved
materially and the remaining p95 drift is still reported rather than promoted as
publication readiness.
| Format | Status | Evidence |
| --- | --- | --- |
| XLSX | Proven on current corpus | two real workbooks: 100% exhaustive exact cell text; sampled geometry p95 0.27 px and 3.27 px |
| XLSM | Implemented; not real-corpus proven | Generated macro fixture and the XLSX path; macro omission is explicit |
| ODS | Implemented; not real-corpus proven | Generated ODS fixtures and golden/raster tests only |
| CSV | Implemented; parser-tested only | Generated quoted-field/newline fixtures only |
| TSV | Implemented; parser-tested only | Generated delimiter fixtures only |
| DOCX | Reading and table structure good, vertical flow poor | Real NIST chapter: 34 pages against 34, exact text 0.8549, and table columns that agree with LibreOffice to 0.2 px — but geometry 0.2756 and p95 348 px, because content still drifts vertically down the page. |
| ODT | Proven on current corpus | Real UK IPO agreement: 1.0000 exact text, geometry 0.9533, p95 1.73 px, and pagination matching LibreOffice. |
| RTF | Implemented; synthetic evidence only | basic.rtf: 100% exact text, 8.07 px p95; no real RTF corpus document |
| PPTX | Text near-complete, placement improving | Real NASA deck: correct slide count and exact text 0.9473, geometry 0.3719 and p95 178 px on a 720 px slide. Shape position is right where the deck says it plainly; the residue is in grouped and tabular frames. |
| ODP | Implemented; synthetic evidence only | basic.odp: 100% exact text, 4.97 px p95; no real ODP corpus document |
| PNG | Implemented; parser/raster tested | Generated image fixtures |
| JPEG | Implemented; photographed evidence | Real photographed receipt plus EXIF-orientation fixtures |
| GIF | Implemented; parser/raster tested | Generated image fixtures |
| BMP | Implemented; parser/raster tested | Generated image fixtures |
| WebP | Implemented; parser/raster tested | Generated image fixtures |
| PDF | Delegated to host by design | Official CFPB statement pins DelegateToHost { format: Pdf } |
| HEIC | Delegated to host by design | Contract tests pin DelegateToHost { format: Heic } |
PDF is deliberately outside this renderer. A PDF never becomes a partial local preview:
DelegateToHostrequires the caller to use its platform PDF viewer. Browsers, iOS, and Android already ship mature viewers, and this library does not compete with PDFium.
Fonts and size
Native builds include regular, bold, italic, and bold-italic Carlito, Caladea,
and Liberation Sans/Serif/Mono faces plus DejaVu Sans, with their
OFL/Bitstream licence texts. They substitute Calibri, Cambria, Arial/Helvetica,
Times New Roman, and Courier New using compatible metrics.
Browser builds support either --features fonts or the core build plus
addFont(bytes). Measured on 13 August 2026 on an Apple M4 Pro MacBook Pro (14
cores, 24 GB, macOS 26.5.1, Rust 1.97.1), the release WASM is 1,604,208 bytes
gzipped without fonts (4 MiB budget) and 5,632,353 bytes gzipped with
fonts (9 MiB budget).
The root npm export works with ESM bundlers; readany-render/no-bundler exposes
the same web initializer explicitly for direct browser imports. Both modes let
the caller pass a wasmUrl to init, and the npm tarball includes every
bundled font's licence text.
The library never opens a socket, never resolves an external relationship, and never writes document data to disk. The CLI and fidelity harness are separate native tools and are the only filesystem users.
Measured performance
On the same machine and date, ./scripts/check-performance.sh measured the
generated 400 x 350 sheet at 441 ms, the real sheet A workbook parse at 243
ms, its 1,200 x 800 viewport raster at 39 ms, the generated 100-page DOCX
at 1 ms, and a small-page raster below the timer's 1 ms resolution. Budgets
are 500 ms for sheet parsing, 100 ms for the real viewport, 3,000 ms for the
100-page DOCX, and 100 ms for the small page.
Development
python3 fixtures/generate.py
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
./scripts/check-performance.sh
./scripts/check-size.sh
./harness/run.shThe measured release evidence and per-format result are recorded in
docs/IMPLEMENTATION_REPORT.md.
MSRV is Rust 1.85. The Rust crate and npm package are MIT licensed; bundled
font license text ships under fonts/.
