v0.2.0 — native PDF OCR, robot run_complete carries markdown, native Windows
franken_ocr v0.2.0 — the document-input release
focr now reads PDFs natively, the agent-facing robot stream finally carries the recognized text, and native Windows (x86_64) is proven end to end — all in pure, memory-safe Rust, still with no Python, no CUDA, and no FFI at inference.
Added
- Native PDF page OCR —
focr ocr file.pdf(bd-0a7). Scanned / image-XObject PDF pages are rasterized in-process, in pure memory-safe Rust with no FFI, then fed through the identical preprocess → vision → decoder → postprocess pipeline a PNG takes — no out-of-bandpdftoppm. The fast path covers JPEG (DCTDecode), CCITT Group 4 fax, andFlateDecode/LZWDecode/ASCII85Decoderaw rasters in RGB / gray / CMYK / 1-bpc bilevel, with/Rotateapplied. Pages render lazily one at a time.JPXDecode(JPEG 2000),JBIG2Decode, and born-digital vector/text pages (no pure-Rust decoder) surface a precise, actionable error instead of a wrong guess. The document loop is per-page resilient: an undecodable page is skipped so one bad page never discards the rest, while whole-run conditions (model-not-found, cooperative cancel, model format mismatch) abort immediately. - Native Windows (x86_64) (
bd-3u97).focr.exeruns full OCR end to end on Windows 10;robot selftestpasses 24/24 (incl. the K=6848 i32-overflow case),focr pullcompletes the full 3.9 GB multi-part download over the native async HTTP/TLS stack (theWSAENOTCONNsend-path bug,bd-15ow, is fixed), andinstall.ps1installs + offers to pull. ARM64 Windows is not yet published.
Changed
- Robot
run_completecarries the recognized markdown (bd-3o5p).focr ocr --robot/focr robot runpreviously emitted a payload-lessrun_complete, so a machine consumer received no OCR text at all; the terminal success event now carries themarkdown. No schema-version bump (run_completewas already an advertised event kind). - Input is now document images and PDFs, not image-only.
Fixed and hardened (PDF codec review)
A two-pass fresh-eyes security/correctness review of the new PDF path before release:
- 1-Gpx declared-dimension guard (computed in
u64) fires before any per-pixel allocation — closes a gigapixel-claim overflow/DoS. - CCITT no longer pre-reserves from the attacker-controlled
/Height. - Multi-filter codec chains ending in
DCTDecode/CCITTFaxDecodeare rejected with an accurate message instead of feeding still-encoded bytes to the codec. - Cooperative cancel (Ctrl+C) and model format-mismatch propagate instead of being swallowed per page.
Performance
- The ~9.9 MB BPE tokenizer is cached on the model (
OnceLock) — a multi-page PDF parsestokenizer.jsononce instead of twice per page.
Binaries are raw single-file executables (one per architecture, ISA dispatched at runtime), each with a <asset>.sha256 sidecar. Linux is gnu (glibc), not musl. Fetch the weights once with focr pull (~3.9 GB int8, SHA-256 verified) and run fully offline thereafter.
Full history: CHANGELOG.md. Model weights remain MIT, Copyright (c) 2026 Baidu.