OCR and visible-text automation
shaft-ocr is available in SHAFT_ENGINE source but is not included in the
current published SHAFT release. Do not add the dependency below to a released
BOM project yet. Build the current source reactor only when you are evaluating
this preview, and wait for a containing release before using it in a released
project.
Use io.github.shafthq:shaft-ocr in a source-build preview when you need to
recognize text from pixels. The module runs Tesseract locally through bundled
native JavaCPP libraries; it does not require a system Tesseract installation
or a cloud OCR service.
Add the module
After a containing release is published, import its SHAFT BOM, then add
shaft-engine and shaft-ocr without module versions:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.shafthq</groupId>
<artifactId>shaft-bom</artifactId>
<version>${shaft.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.shafthq</groupId>
<artifactId>shaft-engine</artifactId>
</dependency>
<dependency>
<groupId>io.github.shafthq</groupId>
<artifactId>shaft-ocr</artifactId>
</dependency>
</dependencies>
Without shaft-ocr, OCR calls fail with a message that names the missing optional dependency.
Target visible text
Create exact or partial OCR targets through the usual locator namespace. Use the target with WebDriver, Appium, or Playwright element actions:
var exactText = SHAFT.GUI.Locator.hasOcrText("Checkout");
var partialText = SHAFT.GUI.Locator.containsOcrText("Check");
driver.element().click(exactText);
driver.element().hover(partialText);
driver.element().doubleClick(exactText);
The default target requires one match. Select a zero-based occurrence when the same text appears more than once:
driver.element().click(
SHAFT.GUI.Locator.containsOcrText("Save").occurrence(1)
);
SHAFT maps recognized screenshot coordinates to the active backend. WebDriver uses the browser viewport, Appium uses touch input, and Playwright uses its page mouse. Add shaft-sikulix to use the same OCR targets for desktop screen actions:
new SHAFT.GUI.SikuliX().element()
.click(SHAFT.GUI.Locator.hasOcrText("Calculator"));
Assert recognized text
Assert an element screenshot with the same native string assertion syntax used elsewhere in SHAFT:
driver.element().assertThat(By.id("receipt"))
.ocrText()
.contains("Payment complete");
Assert an encoded image or image file directly:
SHAFT.Validations.assertThat()
.image(Path.of("test-data/receipt.png"))
.ocrText()
.contains("Total");
SHAFT.Validations.verifyThat()
.image(imageBytes)
.ocrText()
.contains("Order number");
OCR recognition attaches the source image and recognition details to the report. OCR targeting also attaches the selected match, including its text, confidence, and bounds.
Process PDF documents
Use PdfFileManager to read native text and recognize text from scanned or mixed pages in the same PDF. SHAFT processes every page, keeps native positioned text when it is available, and calls shaft-ocr with a whole-page render only for pages that need pixel recognition.
var document = new PdfFileManager("test-data/invoice.pdf").process();
String text = document.fullText();
var firstPage = document.pages().getFirst();
System.out.println(firstPage.source()); // NATIVE, OCR, or HYBRID
System.out.println(firstPage.confidence());
System.out.println(firstPage.tables());
Each page result contains page, block, paragraph, line, and word geometry. It also reports any applied orientation or deskew correction, inferred tables, confidence, and warnings. Result lists are immutable. Table inference uses aligned word geometry; validate irregular, borderless, or merged-cell results before using them as structured data.
Request exports explicitly. SHAFT writes each export through a temporary sibling file and then moves it into place:
Files.createDirectories(Path.of("build"));
var result = new PdfFileManager("test-data/scanned-invoice.pdf").process(
PdfExportRequest.to(PdfExportFormat.SEARCHABLE_PDF, Path.of("build/invoice-searchable.pdf")),
PdfExportRequest.to(PdfExportFormat.HOCR, Path.of("build/invoice.hocr")),
PdfExportRequest.to(PdfExportFormat.TSV, Path.of("build/invoice.tsv")),
PdfExportRequest.to(PdfExportFormat.JSON, Path.of("build/invoice.json"))
);
result.exports().forEach(export ->
System.out.println(export.output() + " " + export.sha256())
);
Existing output files are rejected by default. Call replacingExisting() on an export request when replacement is intentional. Searchable export of a signed PDF is also rejected because changing the document invalidates its signatures; call allowingSignatureInvalidation() only when that consequence is acceptable.
Process independent PDFs as an ordered batch:
var requests = List.of(
PdfDocumentRequest.of(Path.of("test-data/one.pdf")),
PdfDocumentRequest.of(Path.of("test-data/two.pdf"))
);
PdfBatchResult batch = PdfFileManager.processAll(
requests,
new PdfBatchOptions(4, 256L * 1024 * 1024, false)
);
batch.items().forEach(item ->
System.out.println(item.source() + " successful=" + item.successful())
);
The batch keeps request order and records item failures without discarding successful results. Set failFast to true to stop before later requests can publish exports; fail-fast execution is serial for that reason.
Set per-call recognition and safety limits through PdfDocumentOptions:
var options = PdfDocumentOptions.defaults()
.withRenderDpi(240)
.withResourceLimits(100L * 1024 * 1024, 250, 20_000_000)
.withPageTimeout(Duration.ofSeconds(60))
.withAllureEvidence(false);
var document = new PdfFileManager("test-data/archive.pdf").process(options);
PDF processing attaches a JSON document summary and page-level recognition details to Allure by default. Those details can contain recognized document text, geometry, tables, warnings, and the source path. Disable them with withAllureEvidence(false) when the document is sensitive. shaft.ocr.document.maximumAllureArtifactBytes controls whether explicit export files are attached or represented by a size and checksum manifest; it does not cap page-level JSON details.
SHAFT accepts PDF input only; it does not add Tabula or an ML table runtime. It rejects encrypted PDFs, inputs and page counts above their limits, and individually oversized rendered pages. Concurrent raster work is throttled by the batch byte budget. Treat OCR page timeouts as caller-side bounds: a native OCR library call may finish in its background thread after the timed operation has returned.
Tune recognition
Start from OcrOptions.defaults() when asserting an image or element. Tune a target directly when interacting with visible text:
var options = OcrOptions.defaults()
.withLanguages("English", "Arabic")
.withMinimumConfidence(0.80)
.withPreprocessingMode(OcrPreprocessingMode.GRAYSCALE)
.withPageSegmentationMode(OcrPageSegmentationMode.SPARSE_TEXT)
.within(new OcrRectangle(0, 0, 900, 500));
SHAFT.Validations.assertThat()
.image(Path.of("test-data/bilingual-receipt.png"))
.ocrText(options)
.contains("الإجمالي");
Available preprocessing modes are AUTO, NONE, GRAYSCALE, BINARY, and
INVERT. AUTO derives an Otsu threshold from the image, composites alpha onto
white, and preserves pixel coordinates. Page segmentation modes cover automatic
text, a single block, line, or word, and sparse text.
Configure language models
English and Arabic are the default languages. Pass Tesseract three-letter model
codes or supported human-readable names for other languages. SHAFT downloads
missing models on first use from a pinned tessdata_fast revision, verifies
their integrity, and stores them in the user cache.
Configure provisioning through the typed property namespace:
SHAFT.Properties.ocr.set()
.cacheDirectory("build/shaft-ocr-models")
.downloadEnabled(false)
.documentRenderDpi(300)
.documentMaximumPages(500)
.documentMaximumInFlightRasterBytes(256L * 1024 * 1024);
Use the matching shaft.ocr.* keys in custom.properties or as system properties when code configuration is not appropriate. Document options passed to process(...) override the defaults for that call.
When downloads are disabled, every requested language model must already exist in the configured cache and pass integrity verification. SHAFT fails before recognition if a model is missing or altered.
Preview: managed OCR setup
The selection-aware managed setup workflow below is not yet available on
SHAFT_ENGINE main or in a published SHAFT release. Do not run these commands
or compile against these overloads until a containing release is available.
The planned provider pins every supported Tesseract language to one
tessdata_fast revision and SHA-256 checksum. It also applies the setup
artifact safety ceiling. Create and review an immutable plan before install:
shaft-cli setup plan --profile OCR --mode MANAGED \
--language eng --language ara \
--output /absolute/path/ocr-plan.json
# Review the JSON and copy its digest, then repeat every policy option.
shaft-cli setup install --plan /absolute/path/ocr-plan.json \
--approve sha256:<reviewed-digest>
shaft-cli setup verify --profile OCR \
--language eng --language ara
Omit --language for the baseline eng and ara bundle. Repeat it with
three-letter Tesseract codes such as fra and deu to provision another exact
set. Java callers will be able to pass
new SetupSelection(List.of("fra", "deu"))
to the selection-aware SHAFT.Infrastructure.plan,
SHAFT.Infrastructure.status, SHAFT.Infrastructure.verify, and
SHAFT.Infrastructure.install overloads (or the equivalent low-level service
overloads). CLI install recovers the selection from the reviewed actions; repeating
--language is optional and must match when supplied. A custom cache must be
absolute. The provider will prefer it only when the complete requested set
verifies there, otherwise it will use the platform-native shared setup cache
when that complete set verifies.
Choose OCR for pixel-only text
Prefer semantic locators when the application exposes a stable DOM, accessibility tree, or native element hierarchy. Use OCR for canvases, remote desktops, streamed applications, rendered documents, screenshots, inaccessible native surfaces, and other cases where text exists only as pixels.
OCR accuracy depends on image resolution, contrast, font rendering, language models, and segmentation. Restrict the region, choose a suitable preprocessing mode, and raise the confidence threshold when the screen contains unrelated text.