Context
Search can look inside documents since #923, but only where the words are already text. A scan carries pictures of words, so an invoice photographed by a phone or a contract returned by a flatbed indexes its filename and nothing else. #906 put OCR out of scope on purpose: "different dependency, different cost profile, its own issue if the need shows up". This is that issue.
Most of the work turns out to be already done, and the measurements below were taken against the branch that closed #906 so they do not need repeating.
The parser is already compiled in. iscc-tika ships Tika's TesseractOCRParser inside its native library. Grepping the shared object finds the class along with its runtime strings (hasTesseract, getTesseractPath, Tesseract command:, TesseractOCRParser timeout). It shells out to the tesseract binary at run time rather than embedding it.
The Python surface is a configuration change. workspace/common/documents/extraction.py already builds a PdfParserConfig and pins it to PdfOcrStrategy.NO_OCR. TesseractOcrConfig exposes set_language, set_timeout_seconds, set_density, set_depth, set_enable_image_preprocessing and set_apply_rotation.
Enabling it is free where the binary is absent. Without tesseract on PATH, Tika falls back to ImageParser, returns empty text and raises nothing. Measured over 25 runs each, AUTO and NO_OCR are indistinguishable: ~11 ms on a scan and on a text PDF alike. So the strategy can be switched on without waiting for the image to carry the binary.
AUTO only fires where it is needed. It runs OCR on pages with no text layer and leaves everything else alone, so ordinary documents cost nothing extra.
Scope
- A setting, off or auto by default, that selects the OCR strategy and carries the language and a per-document timeout. Read through
workspace/settings/ like every other setting, not hard-coded.
tesseract-ocr and its language data in the Docker image. Installed sizes on bookworm: tesseract-ocr 2.1 MB amd64 / 2.6 MB arm64, libtesseract5 3.5 / 3.2 MB, tesseract-ocr-eng 4.0 MB. The core is around 15 MB; the transitive image and text chain was not measured and needs a real build to confirm. Each further language adds roughly 1 to 5 MB. Whether this goes in the default image or an -ocr variant is part of the decision.
- A separate Celery queue, or at least a separate rate, so a hundred-page scan cannot starve ordinary indexing. OCR costs seconds per page, and this project targets hardware as small as a Raspberry Pi, so the existing "extraction is fast and bounded" assumption stops holding.
- A bound per document:
set_timeout_seconds plus a page ceiling, so one pathological upload cannot occupy a worker indefinitely.
Notes
- Enabling OCR on an existing instance only takes effect for documents indexed afterwards.
reindex_files_search already exists for the backfill and should be named in whatever documentation this ships with.
- Language is a per-instance choice. A French deployment needs
tesseract-ocr-fra, and the setting has to allow more than one.
- This widens the attack surface deliberately: untrusted PDFs get rendered to images and a external binary is invoked on the result. Worth a look at what the rendering path pulls in before shipping.
- Out of scope here, as before: describing images with a vision model. That is a different dependency again and belongs to
ai rather than to extraction.
Acceptance criteria
Context
Search can look inside documents since #923, but only where the words are already text. A scan carries pictures of words, so an invoice photographed by a phone or a contract returned by a flatbed indexes its filename and nothing else. #906 put OCR out of scope on purpose: "different dependency, different cost profile, its own issue if the need shows up". This is that issue.
Most of the work turns out to be already done, and the measurements below were taken against the branch that closed #906 so they do not need repeating.
The parser is already compiled in.
iscc-tikaships Tika'sTesseractOCRParserinside its native library. Grepping the shared object finds the class along with its runtime strings (hasTesseract,getTesseractPath,Tesseract command:,TesseractOCRParser timeout). It shells out to thetesseractbinary at run time rather than embedding it.The Python surface is a configuration change.
workspace/common/documents/extraction.pyalready builds aPdfParserConfigand pins it toPdfOcrStrategy.NO_OCR.TesseractOcrConfigexposesset_language,set_timeout_seconds,set_density,set_depth,set_enable_image_preprocessingandset_apply_rotation.Enabling it is free where the binary is absent. Without
tesseracton PATH, Tika falls back toImageParser, returns empty text and raises nothing. Measured over 25 runs each,AUTOandNO_OCRare indistinguishable: ~11 ms on a scan and on a text PDF alike. So the strategy can be switched on without waiting for the image to carry the binary.AUTOonly fires where it is needed. It runs OCR on pages with no text layer and leaves everything else alone, so ordinary documents cost nothing extra.Scope
workspace/settings/like every other setting, not hard-coded.tesseract-ocrand its language data in the Docker image. Installed sizes on bookworm:tesseract-ocr2.1 MB amd64 / 2.6 MB arm64,libtesseract53.5 / 3.2 MB,tesseract-ocr-eng4.0 MB. The core is around 15 MB; the transitive image and text chain was not measured and needs a real build to confirm. Each further language adds roughly 1 to 5 MB. Whether this goes in the default image or an-ocrvariant is part of the decision.set_timeout_secondsplus a page ceiling, so one pathological upload cannot occupy a worker indefinitely.Notes
reindex_files_searchalready exists for the backfill and should be named in whatever documentation this ships with.tesseract-ocr-fra, and the setting has to allow more than one.airather than to extraction.Acceptance criteria