Found while scanning the built v2.8.8 bundle for any remaining CDN URL (PR #3). Everything else — the three WASM libraries and, as of v2.8.8, the text editor's fallback fonts — is bundled locally and rewritten at startup. OCR is the one remaining external fetch. It has been this way since the first release (2.8.4:1), so it is not a v2.8.8 regression; README.md / instructions.md now state the exception.
What actually happens (verified against the v2.8.8 -simple image)
It is a two-layer fetch:
- The main bundle (
assets/tesseract-runtime-*.js) carries only tesseract.js's built-in default workerPath: https://cdn.jsdelivr.net/npm/tesseract.js@v${n}/dist/worker.min.js. BentoPDF's override layer (VITE_TESSERACT_{WORKER,CORE,LANG}_URL) is build-time only and unset in the prebuilt image, and no local worker.min.js ships — so the worker is fetched from jsdelivr.
- The core WASM and language-data defaults are not in the main bundle at all (zero
tesseract.js-core / tessdata literals there). They live inside the worker script, which fetches tesseract.js-core from jsdelivr and <lang>.traineddata from tessdata.projectnaptha.com on its own.
The OCR language picker offers 30+ languages.
Fix — same pattern as the WASM libraries and fonts, with two rewrite points
- Vendor
worker.min.js and the tesseract.js-core WASM variants, pinned to the exact tesseract.js version the bundle uses, under /usr/share/nginx/html/tesseract/.
- Rewrite
workerPath in the main bundle via the rewrite-wasm-urls oneshot (content-matched, like the fonts).
- Patch the
corePath / langPath defaults inside our vendored worker at Docker build time (a sed on a file we ship — no oneshot needed for that layer).
- Add the tesseract.js / core versions to the bump checklist in
AGENTS.md / CONTRIBUTING.md.
The open decision: which languages to bundle
Each language needs its own traineddata (eng ≈ a few MB; CJK languages tens of MB each). Bundling all 30+ is roughly 150–400 MB — versus 23 MB for the fonts — and a much larger .s9pk. Options: bundle a curated subset (e.g. eng plus the major European and CJK languages) and either let the rest fall back to the CDN (partial, documented) or hide them from the picker.
Verification
The oneshot proves no CDN literals remain; only a real OCR run with the browser network tab proves the worker loads core + language data locally.
Target: 2.8.8:1.
Found while scanning the built v2.8.8 bundle for any remaining CDN URL (PR #3). Everything else — the three WASM libraries and, as of v2.8.8, the text editor's fallback fonts — is bundled locally and rewritten at startup. OCR is the one remaining external fetch. It has been this way since the first release (
2.8.4:1), so it is not a v2.8.8 regression;README.md/instructions.mdnow state the exception.What actually happens (verified against the v2.8.8
-simpleimage)It is a two-layer fetch:
assets/tesseract-runtime-*.js) carries only tesseract.js's built-in defaultworkerPath: https://cdn.jsdelivr.net/npm/tesseract.js@v${n}/dist/worker.min.js. BentoPDF's override layer (VITE_TESSERACT_{WORKER,CORE,LANG}_URL) is build-time only and unset in the prebuilt image, and no localworker.min.jsships — so the worker is fetched from jsdelivr.tesseract.js-core/tessdataliterals there). They live inside the worker script, which fetchestesseract.js-corefrom jsdelivr and<lang>.traineddatafromtessdata.projectnaptha.comon its own.The OCR language picker offers 30+ languages.
Fix — same pattern as the WASM libraries and fonts, with two rewrite points
worker.min.jsand thetesseract.js-coreWASM variants, pinned to the exact tesseract.js version the bundle uses, under/usr/share/nginx/html/tesseract/.workerPathin the main bundle via therewrite-wasm-urlsoneshot (content-matched, like the fonts).corePath/langPathdefaults inside our vendored worker at Docker build time (asedon a file we ship — no oneshot needed for that layer).AGENTS.md/CONTRIBUTING.md.The open decision: which languages to bundle
Each language needs its own traineddata (
eng≈ a few MB; CJK languages tens of MB each). Bundling all 30+ is roughly 150–400 MB — versus 23 MB for the fonts — and a much larger.s9pk. Options: bundle a curated subset (e.g.engplus the major European and CJK languages) and either let the rest fall back to the CDN (partial, documented) or hide them from the picker.Verification
The oneshot proves no CDN literals remain; only a real OCR run with the browser network tab proves the worker loads core + language data locally.
Target:
2.8.8:1.