Searchable OCR Text Layers

HotPDF can render selected loaded pages for an application-supplied OCR engine and atomically add searchable invisible Unicode text aligned with each recognized word

Engine integration

Implement IHPDFOCREngine with any synchronous OCR provider available to the application

The engine receives a borrowed TBitmap, requested DPI, normalized page rotation, media-box coordinates, remaining word and UTF-16 budgets, and the effective cancellation token through THPDFOCRRequest

Word boxes and optional baselines use top-left bitmap pixel coordinates, and the engine must not retain or free the borrowed bitmap after Recognize returns

Optional local engines

Version 2.754.0 adds explicit Tesseract and RapidOCR factories returning IHPDFOCREngine on Windows; the overload without an engine still selects the existing built-in engine

Install and provision the chosen engine locally before creating its adapter, then pass that adapter to the engine overload of ApplyLoadedOCRTextLayer; executables, Python packages, and OCR models are optional external dependencies and are not bundled with HotPDF

Tesseract

function HPDFCreateTesseractOCREngine(const ExecutablePath,
  TessDataDirectory, Language: string;
  TimeoutMilliseconds: Cardinal = 60000): IHPDFOCREngine;

The declaration is in HPDFTesseractRecognition; provide a Tesseract executable capable of TSV output and a data directory containing the requested language model, such as chi_sim.traineddata for chi_sim

This example assumes the executable and data have already been installed at the shown paths and PDF is a loaded THotPDF instance

uses SysUtils, HPDFDoc, HPDFTesseractRecognition;

procedure AddTesseractText(PDF: THotPDF);
var
  Engine: IHPDFOCREngine;
  Options: THPDFOCRTextLayerOptions;
  Info: THPDFOCRTextLayerInfo;
begin
  Engine := HPDFCreateTesseractOCREngine(
    'C:\OCR\Tesseract\tesseract.exe',
    'C:\OCR\Tesseract\tessdata', 'chi_sim', 60000);
  Options := THPDFOCRTextLayerOptions.Default;
  if not PDF.ApplyLoadedOCRTextLayer([0], Engine, Options, Info) then
    raise Exception.Create('OCR text layer was not added');
end;

RapidOCR

function HPDFCreateRapidOCREngine(const PythonExecutable, BridgeScript,
  ModelDirectory: string; TimeoutMilliseconds: Cardinal = 60000): IHPDFOCREngine;

The declaration is in HPDFRapidOCRRecognition; provision Python with rapidocr and onnxruntime, the supplied tools/OCR/rapidocr_tsv.py bridge, and these three local models

The bridge also requires %WINDIR%\Fonts\arial.ttf for RapidOCR's result container, disables automatic downloads, and uses one ONNX thread per configured execution pool; missing models, packages, or the local font cause recognition to fail

The current bridge uses the Simplified Chinese model configuration and preserves recognized punctuation without fullwidth or halfwidth substitution

uses SysUtils, HPDFDoc, HPDFRapidOCRRecognition;

procedure AddRapidOCRText(PDF: THotPDF);
var
  Engine: IHPDFOCREngine;
  Options: THPDFOCRTextLayerOptions;
  Info: THPDFOCRTextLayerInfo;
begin
  Engine := HPDFCreateRapidOCREngine(
    'C:\OCR\Python\python.exe',
    'C:\HotPDF\tools\OCR\rapidocr_tsv.py',
    'C:\OCR\RapidOCR\models', 60000);
  Options := THPDFOCRTextLayerOptions.Default;
  if not PDF.ApplyLoadedOCRTextLayer([0], Engine, Options, Info) then
    raise Exception.Create('OCR text layer was not added');
end;

Both factories validate existing executable and data paths and accept a timeout from 1 through 3,600,000 milliseconds, defaulting to 60,000; invalid configuration raises EArgumentException

The shared adapter launches a hidden local process with quoted paths and restricted inherited handles, polls cancellation, timeout, and output sizes every 25 milliseconds, and terminates the process on failure before cleaning up temporary files

TSV output is limited to 64 MiB and diagnostic-file output to 1 MiB, with returned diagnostics truncated to 4,096 characters; recognized words must also fit the request's word and UTF-16 budgets and valid bitmap bounds

These are output and request limits, not a hard cap on the external engine's memory use; cancellation and engine failures return through the text-layer status without publishing partial pages

Recognition evidence and limits

The local RapidOCR 3.8.4 baseline passed all 15 repetitions across five fixed Chinese scan regions: two clear regions at 300 DPI and three low-resolution pressure regions at 150 DPI, evaluated against the current reference transcripts with character error rate at most 5% and deletion rate at most 2%

All repetitions passed reading-order checks, 5,190 word or character coordinate checks in total, and visible-pixel equality at 72 DPI; the two clear regions had zero character errors against those references

Two AI visual reviews agree on the Chinese text and numbers, but some punctuation code points in the raster remain ambiguous and human adjudication is pending; punctuation differences still count as errors and these results are corpus evidence, not a general accuracy guarantee

Geometry and search text

HotPDF inverts the renderer page transform so word baselines remain aligned on pages rotated by 0, 90, 180, or 270 degrees

Each accepted word is written with text rendering mode 3 Tr, a fitted horizontal text scale, and a rotation-aware text matrix, leaving the page raster unchanged while preserving selectable content order

A shared Type 0 Identity-H font assigns bounded document-local CIDs to Unicode scalars and writes a complete ToUnicode map, including UTF-16 surrogate targets for supplementary characters

Bounds, cancellation, and atomicity

THPDFOCRTextLayerOptions.Default enables 300 DPI recognition, skips pages that already expose text, and limits page count, pixels, words, UTF-16 units, and generated content bytes

HotPDF validates every engine result and builds all page content before starting one copy-on-write graph transaction, so engine failures, invalid geometry, exhausted budgets, cancellation, or commit errors leave the loaded object graph unchanged

An empty page-index array selects every loaded page, while duplicate page indices are recognized once in first-seen order

Optional-content grouping

Set UseOptionalContentGroup to bind all generated text to one named layer through each page Resources/Properties dictionary

This option requires PDF 1.5 and follows StrictVersionLock; the default keeps the invisible text outside an optional-content group for widest compatibility

Conformance note

The generated searchable layer intentionally uses an unembedded synthetic font because rendering mode 3 never paints glyphs

This API does not by itself produce PDF/A-conforming OCR output, so a PDF/A workflow should use an embedded-font text-layer path and run the requested conformance validation before publication

Primary APIs

Progressive Rendering and Cancellation ยท ExtractLoadedPageText Method