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
ch_PP-OCRv4_det_mobile.onnxch_PP-OCRv4_rec_mobile.onnxch_ppocr_mobile_v2.0_cls_mobile.onnx
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
THotPDF.ApplyLoadedOCRTextLayerIHPDFOCREngineTHPDFOCRRequestTHPDFOCRWordTHPDFOCRTextLayerOptionsTHPDFOCRTextLayerInfoTHPDFOCRTextLayerStatus
Progressive Rendering and Cancellation ยท ExtractLoadedPageText Method