HotPDF draait Tesseract binnen uw Delphi-proces via HPDFCreateTesseractDLLOCREngine, een factory toegevoegd in v2.772.0 die dynamisch een Tesseract 5-compatibele DLL laadt, zijn C API aanstuurt (TessBaseAPIInit2, TessBaseAPIRecognize, de resultaatiterator) en een IHPDFOCREngine teruggeeft. THotPDF.ApplyLoadedOCRTextLayer gebruikt die engine om een onzichtbare, doorzoekbare Unicode-tekstlaag toe te voegen aan gescande PDF-pagina's
Dezelfde recognizer was al bereikbaar via de externe tesseract.exe-adapter die een BMP wegschrijft en TSV parset. Dat pad werkt, maar elke pagina betaalt voor een processtart, een tijdelijk bitmapbestand en een tekstformaat zonder baselines en zonder zeggenschap over paginasegmentatie. Het aanroepen van de DLL schrapt alle drie. Het schrapt ook de procesmuur, en dat betekent dat een Pascal-binding rechtstreeks op C-structuren, C-booleans en door C toegewezen strings zit. Het meeste wat de moeite van weten waard is over deze adapter, is waar die binding stilletjes mis kan gaan
Hoe draait u Tesseract in-process vanuit Delphi met HotPDF?
Tesseract in-process draaien met HotPDF kost één factory-aanroep in de unit HPDFTesseractRecognition en dezelfde ApplyLoadedOCRTextLayer-aanroep die elke HotPDF-OCR-engine gebruikt. De factory valideert gretig. Het DLL-bestand en de tessdata-map moeten bestaan, de taalidentifier mag alleen ASCII-letters, cijfers, _ en + bevatten, elk model in een combinatie zoals chi_sim+eng moet een matchend .traineddata-bestand hebben, en alle 21 vereiste exports moeten resolvbaar zijn voordat de engine wordt teruggegeven. Configuratiefouten geven een EArgumentException; een DLL die niet laadt geeft een EOSError met de Windows-foutcode en de hint om architectuur en afhankelijkheden te controleren
uses
SysUtils, HPDFDoc, HPDFTesseractRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// Een Win64-applicatie heeft een 64-bit DLL nodig; afhankelijkheids-DLL's ernaast
Engine := HPDFCreateTesseractDLLOCREngine('C:\OCR\Win64\libtesseract-5.dll',
'C:\OCR\tessdata', 'chi_sim+eng'); // THPDFTesseractOptions.Default
Doc := THotPDF.Create(nil);
try
Doc.AutoLaunch := False;
if Doc.LoadFromFile(SourceFile) < 1 then
raise Exception.Create('Cannot load ' + SourceFile);
Options := THPDFOCRTextLayerOptions.Default; // 300 DPI, MinimumConfidence 0.5
// Een lege paginalijst betekent elke pagina; pagina's die al tekst hebben worden overgeslagen
if not Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
raise Exception.Create(string(Info.Diagnostic));
Writeln(string(Info.EngineName), ': ', Info.AcceptedWordCount,
' words accepted, ', Info.DroppedWordCount, ' dropped');
Doc.SaveLoadedDocument(TargetFile);
finally
Doc.Free;
end;
end;
THPDFTesseractOptions.Default zet PageSegMode op tpsAuto, EngineMode op temDefault, TimeoutMilliseconds op 60.000 en MaxPixels op 16.777.216. Het pixelbudget telt zwaarder dan het lijkt. Een US Letter-pagina op de default 300 DPI rendert naar 2.550 × 3.300 pixels, zo'n 8,4 miljoen, wat past. Dezelfde pagina op 600 DPI is 5.100 × 6.600, zo'n 33,7 miljoen, en de adapter weigert haar voordat Tesseract één pixel ziet. Verhoog MaxPixels (het plafond is 67.108.864) of laat de DPI staan; elke zijde is bovendien geplafonneerd op 32.767 pixels
De DLL wordt geladen met LoadLibraryEx met de zoekvlaggen voor de eigen map van de DLL plus de default veilige directories, dus de beeldbibliotheken waar Tesseract van afhankelijk is kunnen naast hem huizen zonder PATH of de huidige directory aan te raken. HotPDF bundelt of downloadt geen enkele OCR-runtime of model; beide voorziet u zelf
Wat verandert er vergeleken met de tesseract.exe-adapter?
De DLL-adapter ruilt procesisolatie in voor rijkere uitvoer en lagere overhead per pagina. Beide adapters koppelen in op dezelfde tekstlaag-pijplijn, dus coördinaatmapping, confidencefiltering en de alles-of-niets-commit zijn identiek; wat verschilt is hoe pixels erin gaan en woorden eruit komen
| Aspect | tesseract.exe-adapter | Tesseract DLL-adapter |
|---|---|---|
| Factory | HPDFCreateTesseractOCREngine | HPDFCreateTesseractDLLOCREngine |
| Pixels erin | BMP-bestand in een privé-tijdelijke directory | 8-bit grijswaardenbuffer in het geheugen |
| Woorden eruit | Word-niveau TSV, geplafonneerd op 64 MiB | Resultaatiterator, UTF-8 per woord |
| Baselines | Niet beschikbaar | Doorgegeven vanuit TessPageIteratorBaseline |
| Paginasegmentatie en engine-modus | Alleen automatische segmentatie | THPDFTesseractPageSegMode, THPDFTesseractEngineMode |
| Timeout | Hard: het kindproces wordt beëindigd | Coöperatief: Tesseract moet het merken |
| Crash- en geheugenisolatie | Apart proces | Geen, deelt uw adresruimte |
Eén kost verdwijnt niet. Elke Recognize-aanroep maakt zijn eigen API-instantie aan en roept TessBaseAPIInit2 aan, dus de taalmodellen worden per pagina geïnitialiseerd in plaats van één keer per engine. De bestandscache van het besturingssysteem verzacht het herladen, maar bij grote meertalige modelsets blijft het de dominante vaste kostenpost per pagina, en hij telt mee binnen de recognition-deadline. De in-process RapidOCR-DLL-engine kiest het tegenovergestelde ontwerp en houdt zijn ONNX-modellen resident gedurende de levensduur van de engine; de grensproblemen (C ABI, geleende buffers, onderbreekbaar native werk) zijn dezelfde familie
Waarom kan Delphi de Tesseract-monitorstructuur niet kopiëren?
Delphi kan de Tesseract-voortgangsmonitor niet veilig spiegelen omdat ETEXT_DESC versieafhankelijke interne velden bevat, dus een met de hand gekopieerd record zet de cancel-callback en deadline op verkeerde offsets op sommige builds. Niets faalt luidruchtig zodra dat gebeurt. Tesseract leest simpelweg uw callbackpointer uit een veld dat inmiddels iets anders bevat, of ziet de deadline helemaal nooit
HotPDF behandelt de monitor daarom als een opake pointer en raakt haar alleen aan via geëxporteerde functies: TessMonitorCreate, TessMonitorSetCancelThis, TessMonitorSetCancelFunc, TessMonitorSetDeadlineMSecs en TessMonitorDelete. Bindt u de C API zelf voor een ander doel, dan geldt hetzelfde patroon. De schets hieronder is uw eigen bindingscode, geen HotPDF-API, en spiegelt de declaraties die HotPDF intern gebruikt
type
// C: typedef bool (*TessCancelFunc)(void *cancel_this, int words);
TTessCancelFunc = function(CancelThis: Pointer; Words: Integer): Boolean; cdecl;
TTessMonitorCreate = function: Pointer; cdecl; // ETEXT_DESC*, nooit gederefereerd
TTessMonitorDelete = procedure(Monitor: Pointer); cdecl;
TTessMonitorSetCancelFunc = procedure(Monitor: Pointer; Func: TTessCancelFunc); cdecl;
TTessMonitorSetCancelThis = procedure(Monitor, CancelThis: Pointer); cdecl;
TTessMonitorSetDeadlineMSecs = procedure(Monitor: Pointer; MSecs: Integer); cdecl;
TTessBaseAPIRecognize = function(Handle, Monitor: Pointer): Integer; cdecl;
TOCRJob = record
CancelRequested: Boolean;
DeadlineTick: UInt64;
end;
POCRJob = ^TOCRJob;
function ShouldCancel(CancelThis: Pointer; Words: Integer): Boolean; cdecl;
begin
// Draait op de stack van Tesseract: lees vlaggen en de klok, geef nooit een exception
Result := (CancelThis = nil) or POCRJob(CancelThis)^.CancelRequested or
(GetTickCount64 >= POCRJob(CancelThis)^.DeadlineTick);
end;
// Gebruik, met de functiepointers resolvbaar via GetProcAddress:
// Monitor := MonitorCreate();
// try
// MonitorSetCancelThis(Monitor, @Job);
// MonitorSetCancelFunc(Monitor, ShouldCancel);
// MonitorSetDeadlineMSecs(Monitor, RemainingMs);
// RC := BaseAPIRecognize(API, Monitor);
// finally
// MonitorDelete(Monitor);
// end;
Twee details in die schets zijn bewust zo. De callback geeft een Boolean terug, die in zowel Delphi als Free Pascal één byte is, matchend met de C bool in TessCancelFunc. De vier-byte Windows BOOL of Delphi LongBool oogt uitwisselbaar en is dat niet: schrijft de ene kant één byte en leest de andere er vier, dan zijn de bovenste bytes van het retourregister wat er toevallig stond, en kan een false als true aankomen. Dezelfde header maakt het extra ingewikkeld, omdat functies zoals TessPageIteratorBoundingBox een int teruggeven, die HotPDF als Integer declareert. Lees het C-type van elke retourwaarde in plaats van één conventie voor de hele API te veronderstellen
Het tweede detail is dat de callback nooit een exception gooit. Een Delphi-exception die door de C++-frames van Tesseract heen ontvouwt, is ongedefinieerd gedrag, dus de callback van HotPDF leest alleen de cancellation token en een monotone GetTickCount64-waarde. De adapter zet het resultaat na terugkeer van TessBaseAPIRecognize om in een annulerings- of timeoutdiagnostiek, en voert die controle uit ongeacht de native retourcode
Welke native pointers bezit de Delphi-kant?
De HotPDF Tesseract DLL-adapter bezit drie native objecten per verzoek, de API-instantie, de monitor en de resultaatiterator, en leent alles wat verder gaat. Elke Recognize-aanroep maakt zijn eigen set aan en geeft haar vrij in een finally-blok: TessResultIteratorDelete, dan TessMonitorDelete, dan TessBaseAPIDelete. De engine-interface vrijgeven ontlaadt de bibliotheek
TessResultIteratorGetPageIteratorgeeft een geleende blik in de resultaatiterator terug, geen nieuw object. HotPDF gebruikt hem voorTessPageIteratorBoundingBoxenTessPageIteratorBaselineen geeft hem nooit vrij; hem apart vrijgeven zou hetzelfde geheugen twee keer vrijgevenTessResultIteratorGetUTF8Textgeeft een string terug die door de eigen runtime van de DLL is toegewezen. HotPDF kopieert haar en geeft haar terug viaTessDeleteTextin eenfinally-blok; PascalFreeMemzou haar op de verkeerde heap vrijgeven- Woordtekst wordt gedecodeerd met strikte UTF-8-validatie en lengtecontrole vóór conversie. Woorden met stuurttekens, misvormde UTF-8, boxes buiten de afbeelding, omgekeerde rechthoeken of confidence buiten 0-100 laten het verzoek falen in plaats van stilletjes te worden geplakt
- De totale tekst per verzoek is geplafonneerd op 1.048.576 UTF-16 code units, en het woordaantal moet binnen het verzoekbudget blijven dat
ApplyLoadedOCRTextLayerdoorgeeft
Confidence komt binnen als 0-100 en wordt geschaald naar 0-1, dus THPDFOCRTextLayerOptions.MinimumConfidence betekent hetzelfde voor elke engine. Meldt Tesseract een baseline, dan worden beide eindpunten doorgegeven; anders valt de tekstlaag-pijplijn terug op zijn geometrische schatting, precies zoals bij TSV-invoer
Waarom een enum valideren voordat hij de DLL bereikt?
HotPDF kopieert de rauwe ordinaal van PageSegMode en EngineMode naar een Integer voordat er op bereik wordt gecontroleerd, want een compiler mag aannemen dat een enumvariabele altijd een gedeclareerde waarde bevat en Ord(X) > Ord(High(T)) Invouwen naar een constante false. De ordinalen zijn geen versiering: THPDFTesseractPageSegMode volgt de paginasegmentatienummering van Tesseract van 0 tot 13, THPDFTesseractEngineMode volgt de engine-modusnummering van 0 tot 3, en beide gaan als kale integers naar de DLL. Een optierecord gebouwd met FillChar, gevuld vanuit een stream of vanuit C++Builder met een gecast integer binnenkomend, kan een waarde als 200 bevatten. De gekopieerde ordinaal valideren maakt daar op factory-tijd een EArgumentException van in plaats van een ongedefinieerde modus binnen native code. De factory weigert bovendien tpsOSDOnly en tpsAutoOnly, die geen woorden opleveren, en eist osd.traineddata voor tpsAutoOSD en tpsSparseTextOSD
Wat garandeert de recognition-timeout eigenlijk?
De Tesseract DLL-timeout is coöperatief: HotPDF kan zijn eigen werk stoppen en Tesseract vragen te stoppen, maar hij kan native code niet dwingen terug te keren. De klok begint zodra Recognize begint, dus bitmapconversie en modelinitialisatie verbruiken hetzelfde budget als de herkenning. HotPDF controleert verstreken tijd en de cancellation token tijdens de grijswaardenconversie en tussen woorden door tijdens het itereren van resultaten, en geeft de resterende milliseconden door aan TessMonitorSetDeadlineMSecs vóór de aanroep van TessBaseAPIRecognize
De kloof zit binnen de native aanroep. De monitor van Tesseract wordt geraadpleegd tijdens woordherkenning, niet tijdens TessBaseAPIInit2 of paginalayoutanalyse, dus een trage modellaad of een pathologische layout kan voorbij de deadline lopen voordat de timeout wordt gemeld. Pixel- en uitvoerbudgetten plafonneren ook het eigen geheugengebruik van de native bibliotheek niet. Heeft u een werker nodig die u kunt neerschieten, gebruik dan de procesadapter; dat is de eerlijke afweging, geen ontbrekende functie
Paginasegmentatie is waar de DLL-adapter zijn brood verdient op moeilijke invoer. Formulieren, labels en gescande tabellen met verspreide velden herkennen vaak beter met tpsSparseText dan met automatische segmentatie, die kolommen en alinea's probeert samen te stellen die er niet zijn
procedure OCRFormPages(Doc: THotPDF; const Pages: array of Integer);
var
Engine: IHPDFOCREngine;
TessOptions: THPDFTesseractOptions;
LayerOptions: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
TessOptions := THPDFTesseractOptions.Default;
TessOptions.PageSegMode := tpsSparseText; // verspreide velden, geen kolombouw
TessOptions.EngineMode := temLSTMOnly; // heeft LSTM-modellen in tessdata nodig
TessOptions.TimeoutMilliseconds := 20000; // omvat modelinitialisatie
Engine := HPDFCreateTesseractDLLOCREngine('C:\OCR\Win64\libtesseract-5.dll',
'C:\OCR\tessdata', 'eng+deu', TessOptions);
LayerOptions := THPDFOCRTextLayerOptions.Default;
LayerOptions.MinimumConfidence := 0.6;
if not Doc.ApplyLoadedOCRTextLayer(Pages, Engine, LayerOptions, Info) then
case Info.Status of
otlsCancelled:
Writeln('OCR cancelled, document unchanged');
otlsEngineError:
Writeln('Tesseract failed or timed out: ', string(Info.Diagnostic));
else
Writeln(string(Info.Diagnostic));
end;
end;
Een timeout komt naar boven als otlsEngineError met de diagnostiek Tesseract DLL OCR timed out, terwijl een geannuleerde token naar boven komt als otlsCancelled. In beide gevallen heeft ApplyLoadedOCRTextLayer elke geselecteerde pagina herkend voordat de commit-transactie begint, dus een falering op pagina 40 van 50 laat het geladen document exact zoals het was. Bedenk dat tpsSingleLine, tpsSingleBlock en tpsSparseText alleen de segmentatie veranderen; geen van hen zet een scheve scan recht
Free Pascal en Lazarus: oude pixels en verdwenen Chinees
Beide Tesseract-factories werken in Windows Free Pascal en Lazarus Win32- en Win64-builds sinds v2.772.1, na twee FPC-specifieke fixes. Bouw het Lazarus-pakket eerst voor de doelarchitectuur; de algemene port wordt behandeld in HotPDF op Free Pascal en Lazarus Win64
De eerste fix gaat over pixels. Een LCL TBitmap weggeschreven via scanlines kan zijn rauwe afbeelding bijwerken zonder de Windows-bitmaphandle te verversen, dus GetDIBits op die handle geeft de oude pixels terug. Het symptoom was duister: tekst die rechtstreeks op een bitmap werd getekend werd herkend, terwijl een pagina gerenderd door de PDF-renderer van HotPDF een lege woordenlijst opleverde. Op FPC leest de adapter nu een formaatbewuste snapshot via CreateIntfImage, die het pixelformaat en de rijvolgorde van de rauwe afbeelding eert. De Delphi-build houdt het GetDIBits-pad op een privé-kopie van 24 bits. Geen van beide builds verandert de bitmap van de aanroeper
De tweede fix hoort bij de tesseract.exe-adapter. De TStringList van FPC bewaart ANSI-strings, dus gedecodeerde UTF-8 TSV-tekst aan Lines.Text toewijzen liet stilletjes elk Chinees of supplementair-vlak-karakter vallen dat de systeem-ANSI-codepagina niet kon representeren. Het FPC-pad houdt de TSV nu als UTF-8-bytes, stript de BOM op byteniveau en decodeert elk woord afzonderlijk naar UnicodeString. De DLL-adapter had dit probleem nooit, omdat hij elk woord rechtstreeks uit de iterator decodet
Snelnaslag
- Factory:
HPDFCreateTesseractDLLOCREngine(LibraryPath, TessDataDirectory, Language[, Options])inHPDFTesseractRecognition, toegevoegd in v2.772.0, FPC-ondersteuning in v2.772.1 - Defaults:
tpsAuto,temDefault, 60.000 ms, 16.777.216 pixels; timeoutbereik 1-3.600.000 ms, pixelplafond 67.108.864 - Match de DLL-bitness op de applicatie en leg afhankelijkheids-DLL's naast de Tesseract-DLL
- Behandel de monitor als opaak; kopieer
ETEXT_DESCnooit naar een Pascal-record - Declareer de cancel-callback
cdeclmet eenBoolean-resultaat van één byte, en laat nooit een exception haar ontvluchten - Geef iteratortekst vrij met
TessDeleteText; geef de pagina-iterator uit de resultaatiterator nooit vrij - Verwacht dat de deadline coöperatief is: modelinitialisatie en layoutanalyse kunnen haar overschrijden
- Gebruik de tesseract.exe-adapter wanneer u harde beëindiging of crashisolatie nodig heeft
De Tesseract DLL-adapter, de procesadapters en de ingebouwde OCR-engine worden alle geleverd met de HotPDF Delphi PDF-component voor Delphi, C++Builder en Free Pascal; zie de HotPDF-productpagina voor edities en downloads