HotPDF membuat halaman PDF hasil scan jadi searchable dengan RapidOCR in-process lewat HPDFCreateRapidOCRDLLOCREngine, sebuah factory yang ditambahkan di v2.774.0 yang memuat HotPDFRapidOCR.dll, menahan model deteksi, klasifikasi sudut, dan recognition ONNX tetap resident di memori, serta mengembalikan sebuah IHPDFOCREngine. Engine itu Anda teruskan ke THotPDF.ApplyLoadedOCRTextLayer, yang merender tiap halaman, menjalankan inferensi CPU tanpa Python atau proses anak, dan meng-commit text layer Unicode yang tak terlihat
Motivasinya adalah biaya per halaman. Adapter proses RapidOCR yang terbit lebih dulu, HPDFCreateRapidOCREngine, menyalakan worker Python untuk setiap panggilan Recognize, dan worker itu meng-import runtime-nya serta memuat model ONNX-nya sebelum membaca satu piksel pun. Di arsip 500 halaman, pajak start-up itu berulang 500 kali, dan deployment berarti mengirim lingkungan Python di samping executable Delphi. DLL native memuat model sekali, saat Anda menciptakan engine-nya, dan deployment menyusut menjadi DLL-nya, file modelnya, dan satu character dictionary. Yang Anda serah sebagai gantinya adalah kemampuan membunuh recognizer yang macet, dan sebagian besar engineering di adapter ini adalah soal hidup dengannya secara jujur
Bagaimana membuat PDF hasil scan jadi searchable dengan DLL RapidOCR?
Membuat searchable PDF dengan DLL RapidOCR native butuh satu panggilan factory dan panggilan ApplyLoadedOCRTextLayer yang sama yang dipakai setiap engine OCR HotPDF. Factory-nya tinggal di unit HPDFRapidOCRRecognition dan memvalidasi dengan serobot: direktori DLL dan model harus ada, setiap file model dan dictionary harus resolve, versi ABI harus 1, dan semua export wajib harus tersedia sebelum model mana pun diinisialisasi. Kesalahan konfigurasi me-raise EArgumentException; model yang gagal dimuat me-raise EInvalidOperation yang membawa teks diagnostik yang ditulis DLL
uses
SysUtils, HPDFTypes, HPDFDoc, HPDFRapidOCRRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// Model dimuat di sini, di luar deadline recognition apa pun.
// Nama model relatif di THPDFRapidOCRDLLOptions.Default di-resolve
// terhadap direktori model.
Engine := HPDFCreateRapidOCRDLLOCREngine(
'C:\OCR\Win64\HotPDFRapidOCR.dll', 'C:\OCR\models');
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
// daftar halaman kosong berarti semua halaman; halaman yang sudah punya teks dilewati
if not Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
raise Exception.Create(string(Info.Diagnostic));
Writeln(string(Info.EngineName), ': ', Info.AcceptedWordCount,
' lines accepted, ', Info.DroppedWordCount, ' dropped');
Doc.SaveLoadedDocument(TargetFile);
finally
Doc.Free;
end;
end;
THPDFRapidOCRDLLOptions.Default menyebut ch_PP-OCRv3_det_infer.onnx, ch_PP-OCRv3_rec_infer.onnx, ch_ppocr_mobile_v2.0_cls_infer.onnx, dan ppocr_keys_v1.txt, dengan satu CPU thread, limit input 16.777.216 piksel, dan deadline recognition 60.000 ms. Sejak v2.775.0, THPDFRapidOCRDLLOptions.ForLanguage menukar masuk model recognition dan dictionary yang cocok untuk Traditional Chinese, Rusia, Jepang, Arab, dan profil lain; kenapa model dan dictionary harus berganti bersama dibahas di model multibahasa dan CTC dictionary RapidOCR di HotPDF. Engine melaporkan dirinya sebagai RapidOCR (native DLL) di Info.EngineName, yang menjaga log tetap jelas di samping adapter proses Tesseract OCR eksternal dan engine OCR template-matching bawaan
Kenapa C ABI hanya berbicara int32_t dan byte UTF-8?
ABI milik HotPDFRapidOCR.dll hanya memakai integer fixed-width, pointer mentah, dan panjang byte eksplisit karena Delphi, C++Builder, dan Free Pascal tak berbagi apa pun dengan MSVC selain calling convention C. Sebuah std::string, std::vector, atau exception C++ punya layout dan model unwinding yang milik satu compiler dan satu runtime library. Biarkan salah satunya melintasi boundary, kegagalannya adalah stack korup atau heap block yang di-free oleh allocator yang salah, bukan error yang bersih
Versi ABI 1 karenanya mengikuti daftar aturan pendek. Setiap export adalah cdecl dan mengembalikan status int32_t, di mana 1 berarti sukses dan 0 berarti gagal. Setiap fungsi yang bisa gagal menerima buffer diagnostik milik caller beserta kapasitasnya dalam byte; DLL menulis pesan UTF-8 yang diakhiri NUL dan dipotong agar muat, dan adapter men-decode-nya dengan terminator keras di byte terakhir buffer 4.096 byte miliknya sendiri. Tiap badan export dibungkus try dengan catch (const std::exception &) dan catch (...) sekaligus, sehingga error ONNX Runtime, assertion OpenCV, atau dictionary yang invalid menjadi status 0 plus teks, tak pernah exception yang lolos ke code Pascal
| Export | Peran | Kapan adapter me-resolve-nya |
|---|---|---|
HPDFRapidOCRAbiVersion | Mengembalikan 1; nilai lain ditolak | Pertama, sebelum apa pun |
HPDFRapidOCRCreate | Memuat model deteksi, klasifikasi opsional, recognition, dan dictionary | Di factory |
HPDFRapidOCRRecognize | Menjalankan satu bitmap dan memancarkan satu callback per baris teks | Di factory |
HPDFRapidOCRDestroy | Membebaskan instance model | Di factory |
HPDFRapidOCRSetReadingDirection | Urutan baris kanan-ke-kiri opsional, ditambah di v2.775.0 | Hanya ketika RightToLeft diset |
Export opsional itu di-resolve secara lazy dengan sengaja: DLL v2.774.0 yang tak memilikinya tetap melayani permintaan kiri-ke-kanan. DLL dimuat dengan LoadLibraryEx dengan flag pencarian yang mencakup folder DLL itu sendiri plus direktori safe default, sehingga dependensi ONNX Runtime atau OpenCV yang diletakkan di samping HotPDFRapidOCR.dll ditemukan tanpa menyentuh PATH. Path model dan dictionary berkelana sebagai UTF-8 dan DLL mengonversinya dengan MultiByteToWideChar dalam mode strict sebelum membuka file lewat API wide-character, sehingga direktori model di bawah nama pengguna Tionghoa atau Kiril bekerja alih-alih di-widen byte demi byte menjadi omong kosong
Satu aturan tinggal di build, bukan di header. DLL itu menautkan secara statis ONNX Runtime dan OpenCV, dan konfigurasi CMake default memakai static release CRT (/MT). Static library yang di-compile terhadap /MD yang tercampur ke DLL /MT menghasilkan link error paling bagus dan dua heap independen paling buruk, jadi library yang diprovisionkan harus cocok dengan mode CRT mana pun yang dipakai DLL
Apa yang terjadi antara sebuah TBitmap dan satu baris teks?
HotPDF menyerahkan snapshot BGR top-down independen dari halaman hasil render ke DLL, dan DLL menyerahkan kembali satu callback per baris teks yang dikenali dengan teks UTF-8 pinjaman yang harus disalin adapter sebelum kembali
Di Delphi, adapter meng-assign bitmap halaman ke TBitmap privat, memaksa pf24bit, dan membaca baris dengan GetDIBits memakai biHeight negatif, yang menghasilkan baris top-down yang di-pad ke align empat byte; stride itu diteruskan eksplisit. Di FPC ia membaca lewat CreateIntfImage, karena penulisan scanline LCL bisa memperbarui image mentah tanpa me-refresh handle GDI. Bitmap milik caller tak pernah dimodifikasi, dan anggaran piksel (MaxPixels, default 16.777.216 dan bisa dikonfigurasi sampai 67.108.864) serta limit 32.767 piksel per dimensi dicek sebelum buffer snapshot dialokasikan
Di dalam DLL, snapshot di-pad dengan 50 piksel putih, region teks dideteksi dengan sisi maksimum 1.024 piksel, box diurutkan menjadi baris horizontal, dan tiap crop secara opsional dirotasi oleh angle classifier sebelum recognition. Tiap baris teks lalu melewati callback yang menerima const char*, jumlah byte, box integer dalam piksel image asli, dan mean character confidence. Pointer teks hanya valid selama callback, jadi adapter menyalinnya seketika, dan ia ketat soal apa yang diterimanya:
- UTF-8 di-decode dengan
MB_ERR_INVALID_CHARS; sekuens yang malformed menggagalkan halaman alih-alih menghasilkan replacement character di searchable layer - Karakter kontrol C0 dan C1 ditolak, dan baris yang isinya whitespace saja dilewati
- Box harus berada di dalam bitmap dan confidence harus nilai finite dari 0 sampai 1
- Teks dihitung terhadap
MaxTextCodeUnitsmilik permintaan dengan langit-langit keras 1.048.576 unit UTF-16 per panggilan, dan karakter supplementary-plane berbiaya dua unit - Exception Pascal apa pun di dalam callback ditangkap di situ, disimpan, dan diubah menjadi return 0, yang membuat DLL berhenti dan melaporkan kegagalan; pesan tersimpan itu lalu menjadi diagnostiknya
Dua konsekuensi penting untuk tuning. Pertama, satuan output adalah baris, bukan kata: tiap baris mengonsumsi satu slot MaxWords, Info.AcceptedWordCount dan Info.DroppedWordCount menghitung baris, dan highlight pencarian membentang di box baris. Kedua, MinimumConfidence (default 0,5) dibandingkan terhadap mean character confidence baris itu, sehingga baris dengan satu karakter tak terbaca di antara dua puluh yang bersih biasanya selamat. DLL tak menyuplai baseline, jadi pipeline text layer mengestimasi satu dari box. Halaman kosong sukses dengan nol baris, dan kegagalan apa pun membersihkan hasil parsial agar commit multi-halaman tetap all-or-nothing
Kepemilikan model dan thread safety
Tiap engine RapidOCR DLL memiliki tepat satu instance model sepanjang hidupnya, dan panggilan Recognize ke engine itu diserialisasi oleh critical section. Memegang interface IHPDFOCREngine itulah yang menjaga model tetap hangat, jadi pola yang benar untuk kerja batch adalah menciptakan engine sekali dan memakai ulangnya lintas dokumen
procedure OcrBatch(const Files: TStrings; const OutputDir: string);
var
Models: THPDFRapidOCRDLLOptions;
Engine: IHPDFOCREngine;
Doc: THotPDF;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
I: Integer;
begin
Models := THPDFRapidOCRDLLOptions.Default;
Models.UseAngleClassifier := False; // scan tegak: tak ada model classifier yang dimuat
Models.Threads := 4; // 1..64, di-clamp ke jumlah logical processor
Models.TimeoutMilliseconds := 120000; // per panggilan Recognize, kooperatif
Engine := HPDFCreateRapidOCRDLLOCREngine(
'C:\OCR\Win64\HotPDFRapidOCR.dll', 'C:\OCR\models', Models);
Options := THPDFOCRTextLayerOptions.Default;
for I := 0 to Files.Count - 1 do
begin
Doc := THotPDF.Create(nil);
try
Doc.AutoLaunch := False;
if (Doc.LoadFromFile(Files[I]) > 0) and
Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
Doc.SaveLoadedDocument(IncludeTrailingPathDelimiter(OutputDir) +
ExtractFileName(Files[I]))
else
Writeln(Files[I], ': ', string(Info.Diagnostic));
finally
Doc.Free;
end;
end;
end; // referensi terakhir dilepas: model dihancurkan, lalu DLL di-unload
Nilai Threads menyetel jumlah thread intra-op dan inter-op sekaligus dari tiap sesi ONNX, dan DLL men-clamp-nya ke jumlah processor aktif. Dua thread yang berbagi satu engine tak berjalan paralel; yang kedua menunggu lock. Wait itu bukan EnterCriticalSection buta: adapter memanggil TryEnterCriticalSection tiap 25 ms dan mengecek cancellation token serta deadline di antara percobaan, sehingga permintaan yang mengantre tetap bisa dibatalkan atau habis waktu. Kalau Anda butuh paralelisme sungguhan, ciptakan satu engine per worker dan terima bahwa tiap engine memegang salinan modelnya sendiri di memori
Urutan teardown ditetapkan oleh destructor engine: HPDFRapidOCRDestroy membebaskan instance model lebih dulu, lalu FreeLibrary meng-unload DLL. Di sisi native, inisialisasi model sama rapihnya; ketika model recognition gagal setelah sesi detector dan classifier sudah dibangun, sesi-sesi itu dilepas sebelum error dilaporkan, dan jumlah class dictionary dicek terhadap output model saat inisialisasi, bukan di halaman pertama
Kenapa panggilan OCR native tak bisa dibunuh di tengah inferensi?
Panggilan RapidOCR native tak bisa dibunuh di tengah inferensi karena ia jalan di thread Anda, di dalam proses Anda, di tengah sesi ONNX Runtime yang tak menerima interupsi. Pembatalan di adapter DLL milik HotPDF karenanya kooperatif: DLL memanggil abort callback sebelum dan sesudah deteksi, sesudah klasifikasi, dan sesudah tiap baris yang dikenali, dan berhenti di checkpoint pertama tempat callback mengembalikan 0. Satu Run ONNX yang sudah mulai akan menyelesaikan dirinya lebih dulu
Alternatifnya lebih buruk daripada menunggu. TerminateThread akan membiarkan heap lock CRT, thread pool ONNX Runtime, dan state OpenCV mana pun dalam kondisi apa pun yang kebetulan mereka sedang berada, meracuni sisa prosesnya. FreeLibrary selagi panggilan masih dieksekusi meng-unload code yang sedang ada di stack. Tak satu pun bisa dibuat aman, jadi adapter tak pernah mencobanya. Deadline di TimeoutMilliseconds karenanya deadline kooperatif, dan deadline yang lewat muncul sebagai engine error dengan diagnostik timed-out, sementara token yang dibatalkan muncul sebagai otlsCancelled:
// Token dibuat oleh caller dan dibagikan dengan UI thread,
// yang memanggil Token.Cancel saat pengguna menekan Stop
Options := THPDFOCRTextLayerOptions.Default;
Options.CancellationToken := Token;
if not Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
case Info.Status of
otlsCancelled:
// dikembalikan di boundary stage atau baris berikutnya; dokumen tak berubah
Writeln('Cancelled');
otlsEngineError:
// termasuk kedaluwarsa deadline kooperatif dan diagnostik native
Writeln('Engine: ', string(Info.Diagnostic));
otlsBudgetExceeded:
Writeln('Budget: ', string(Info.Diagnostic));
else
Writeln(string(Info.Diagnostic));
end;
Inilah trade-off inti antara adapter proses milik HotPDF dan DLL in-process, dan tak ada sisi yang menang di setiap baris:
- Biaya start-up: adapter Tesseract dan Python RapidOCR menyalakan proses dan memuat model untuk setiap halaman; DLL memuat model sekali per engine
- Menghentikan: proses anak bisa di-terminate bulat-bulat, dan worker Python jalan di dalam Job Object kill-on-close sehingga seluruh process tree-nya ikut; DLL hanya bisa berhenti di boundary stage dan baris
- Penampungan fault: crash di
tesseract.exemenggagalkan satu halaman; access violation di dalam DLL membawa proses Anda ikut tumbang - Deployment: adapter proses butuh program terinstal atau lingkungan Python; DLL butuh dirinya, modelnya, dan dictionary-nya, yang cocok dengan bitness aplikasi
- Memori: adapter proses melepas semuanya saat anaknya keluar; engine DLL menahan modelnya resident sampai referensi interface terakhir dilepas
Untuk aplikasi desktop interaktif yang meng-OCR satu halaman per waktu, responsivitas DLL biasanya menang. Untuk server yang menelan scan tak terpercaya sepanjang waktu, boundary proses pantas membayar biaya start-upnya
Membangun dan men-deploy HotPDFRapidOCR.dll
HotPDFRapidOCR.dll dibangun dari source C++ di Native/RapidOCR dengan MSVC, C++17, sebuah Windows SDK, dan CMake 3.20 atau lebih baru, memakai script helper yang menerima direktori sumber jaringan native, ONNX Runtime, dan OpenCV plus platform Win32 atau Win64. Bangun keduanya jika Anda mengirim keduanya, karena aplikasi Delphi 32-bit tak bisa memuat DLL 64-bit, dan static library yang Anda provisionkan harus cocok dengan arsitektur target sekaligus mode CRT
Sisi model punya limit kompatibilitasnya sendiri. Detector-nya adalah DB text detector; recognizer menerima model CTC berlayout NCHW dengan tinggi input tetap 32 atau 48, dan memakai 48 untuk model bertinggi dinamis. ONNX Runtime statis bawaan tak bisa memuat model yang disimpan dengan versi IR lebih baru, jadi export PP-OCRv5 terbaru gagal inisialisasi dengan diagnostik alih-alih termuat sebagian. Dictionary harus UTF-8 tanpa BOM, dalam urutan karakter model yang persis, dan jumlah class-nya harus cocok dengan output model; line ending CRLF diterima. Recognition itu offline: DLL tak pernah mengunduh model yang hilang
Referensi cepat
- Factory:
HPDFCreateRapidOCRDLLOCREngine(LibraryPath, ModelDirectory[, Options])diHPDFRapidOCRRecognition, tersedia sejak v2.774.0 di build Delphi, C++Builder, dan Windows FPC/Lazarus - Jaga
IHPDFOCREngineyang dikembalikan tetap hidup lintas halaman dan dokumen; melepaskannya menghancurkan model dan meng-unload DLL - Satu engine menjalankan satu recognition pada satu waktu; ciptakan beberapa engine untuk worker paralel dan anggarkan memori untuk tiap salinan model
- Output adalah satu entri per baris teks dengan mean character confidence, difilter oleh
THPDFOCRTextLayerOptions.MinimumConfidence - Pembatalan dan
TimeoutMillisecondsitu kooperatif; run ONNX yang sedang berjalan selalu selesai - Cocokkan bitness DLL dengan aplikasi dan mode CRT static library ONNX Runtime serta OpenCV dengan DLL
- Pilih profil bahasa per engine dengan
THPDFRapidOCRDLLOptions.ForLanguage(v2.775.0); satu engine tak mendeteksi bahasa sendirian
Adapter RapidOCR native, adapter OCR berbasis proses, page renderer yang menyuapi mereka, dan penulis text layer Unicode tak terlihat semuanya dikirim bersama di HotPDF, komponen PDF VCL native untuk Delphi dan C++Builder. Kalau aplikasi document capture atau arsip Anda butuh output searchable tanpa runtime Python di mesin target, komponen PDF Delphi HotPDF menyediakan seluruh pipeline dengan hanya DLL dan modelnya yang tersisa untuk di-deploy