مقاله فنی

‏OCR با Tesseract DLL در HotPDF: فراخوانی C API از Delphi

‏HotPDF یعنی Tesseract را داخل فرایند Delphi تو از طریق HPDFCreateTesseractDLLOCREngine اجرا می‌کند، یک factory که در v2.772.0 اضافه شد و یک DLL سازگار با Tesseract 5 را به‌شکل پویا load می‌کند، C API اش را می‌راند (TessBaseAPIInit2 و TessBaseAPIRecognize و result iterator) و یک IHPDFOCREngine برمی‌گرداند. ‏THotPDF.ApplyLoadedOCRTextLayer از آن موتور برای اضافه کردن یک لایهٔ متنی Unicode نامرئی و قابل‌جست‌وجو به صفحات PDF اسکن‌شده استفاده می‌کند

همان بازشناس از قبل از طریق آداپتور بیرونی tesseract.exe که یک BMP می‌نویسد و TSV را parse می‌کند در دسترس بود. آن مسیر کار می‌کند اما هر صفحه هزینهٔ راه‌اندازی یک فرایند و یک فایل بیت‌مپ موقت و یک فرمت متنی بدون baseline و بدون کنترل صفحه‌بندی را می‌دهد. صدا زدن DLL هر سه را حذف می‌کند. دیوار فرایند را هم حذف می‌کند، یعنی یک binding در Pascal مستقیم روی ساختارهای C و بولین‌های C و رشته‌های تخصیص‌یافته در C می‌نشیند. بیشتر آنچه دربارهٔ این آداپتور ارزش دانستن دارد این است که آن binding کجا می‌تواند بی‌سروصدا اشتباه برود

با HotPDF چطور Tesseract را درون‌فرایندی از Delphi اجرا می‌کنی؟

اجرای Tesseract درون‌فرایندی با HotPDF یک فراخوانی factory در یونیت HPDFTesseractRecognition و همان فراخوانی ApplyLoadedOCRTextLayer می‌خواهد که هر موتور OCR در HotPDF استفاده می‌کند. ‏factory مشتاقانه اعتبارسنجی می‌کند. فایل DLL و پوشهٔ tessdata باید موجود باشند، شناسهٔ زبان فقط می‌تواند حروف ASCII و ارقام و _ و + داشته باشد، هر مدل در ترکیبی مثل chi_sim+eng باید یک فایل .traineddata منطبق داشته باشد، و هر 21 export الزامی باید قبل از برگرداندن موتور حل شوند. اشتباهات پیکربندی ‏EArgumentException بالا می‌دهند؛ یک DLL که load نشود ‏EOSError با کد خطای Windows و یک سرنخ برای چک کردن معماری و وابستگی‌ها بالا می‌دهد

uses
  SysUtils, HPDFDoc, HPDFTesseractRecognition;

procedure MakeSearchable(const SourceFile, TargetFile: string);
var
  Doc: THotPDF;
  Engine: IHPDFOCREngine;
  Options: THPDFOCRTextLayerOptions;
  Info: THPDFOCRTextLayerInfo;
begin
  // یک اپ Win64 به یک DLL یعنی 64 بیتی نیاز دارد؛ DLLهای وابستگی کنارش می‌روند
  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
    // یک فهرست صفحهٔ خالی یعنی همهٔ صفحات؛ صفحاتی که از قبل متن دارند skip می‌شوند
    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 مقدار PageSegMode را روی tpsAuto و ‏EngineMode را روی temDefault و ‏TimeoutMilliseconds را روی 60,000 و MaxPixels را روی 16,777,216 می‌گذارد. بودجهٔ پیکسل بیشتر از ظاهرش مهم است. یک صفحهٔ US Letter در 300 DPI پیش‌فرض به 2,550 × 3,300 پیکسل یعنی حدود 8.4 میلیون رندر می‌شود که جا می‌شود. همان صفحه در 600 DPI یعنی 5,100 × 6,600، حدود 33.7 میلیون، و آداپتور قبل از اینکه Tesseract یک پیکسل ببیند ردش می‌کند. ‏MaxPixels را بالا ببر (سقفش 67,108,864 است) یا DPI را سر جای نگه دار؛ هر بعت هم به 32,767 پیکسل کپ می‌شود

‏DLL با LoadLibraryEx با فلگ‌های جست‌وجو برای پوشهٔ خود DLL به‌علاوهٔ دایرکتوری‌های امن پیش‌فرض load می‌شود، پس کتابخانه‌های تصویری که Tesseract به آن‌ها وابسته است می‌توانند کنارش زندگی کنند بدون دست زدن به PATH یا پوشهٔ فعلی. ‏HotPDF هیچ runtime یا مدل OCR ای را باندل یا دانلود نمی‌کند؛ هر دو را تو فراهم می‌کنی

در مقایسه با آداپتور tesseract.exe چه چیزی عوض می‌شود؟

آداپتور DLL جداسازی فرایند را با خروجی غنی‌تر و سربار کمتر به‌ازای هر صفحه معامله می‌کند. هر دو آداپتور به همان خط لولهٔ لایهٔ متنی وصل می‌شوند، پس نگاشت مختصات و فیلتر اطمینان و ثبت همه-یا-هیچ یکسان‌اند؛ چیزی که فرق می‌کند این است که پیکسل‌ها چطور وارد و کلمات چطور خارج می‌شوند

جنبهآداپتور tesseract.exeآداپتور DLL یعنی Tesseract
FactoryHPDFCreateTesseractOCREngineHPDFCreateTesseractDLLOCREngine
پیکسل‌های ورودیفایل BMP در یک پوشهٔ موقت خصوصیبافر خاکستری 8 بیتی در حافظه
کلمات خروجی‏TSV سطح-کلمه، سقف 64 مگابایتresult iterator، ‏UTF-8 به‌ازای هر کلمه
baselineهادر دسترس نیستاز TessPageIteratorBaseline عبور داده می‌شوند
صفحه‌بندی و حالت موتورفقط صفحه‌بندی خودکار‏THPDFTesseractPageSegMode، ‏THPDFTesseractEngineMode
تایم‌اوتسخت: فرایند فرزند خاتمه می‌شودمشارکتی: باید Tesseract متوجه شود
جداسازی crash و حافظهفرایند جداهیچ، فضای آدرس تو را شریک می‌شود

یک هزینه ناپدید نمی‌شود. هر فراخوانی Recognize یک instance API خودش را می‌سازد و ‏TessBaseAPIInit2 را صدا می‌زند، پس مدل‌های زبانی به‌ازای هر صفحه مقداردهی می‌شوند نه یک بار به‌ازای هر موتور. کش فایل سیستم‌عامل reload را نرم می‌کند اما روی مجموعه‌های مدل چندزبانهٔ بزرگ همچنان هزینهٔ ثابت غالب به‌ازای هر صفحه است و ضرب‌الاجل بازشناسی را مصرف می‌کند. موتور DLL یعنی RapidOCR درون‌فرایندی طراحی معکوس می‌گیرد و مدل‌های ONNX اش را برای عمر موتور مقیم نگه می‌دارد؛ مشکلات مرزی (‏C ABI و بافرهای قرضی و کار بومی غیرقابل‌وقفه) از یک خانواده‌اند

چرا Delphi نمی‌تواند ساختار مانیتور یعنی Tesseract را کپی کند؟

‏Delphi نمی‌تواند مانیتور پیشرفت یعنی Tesseract را به‌طور امن آینه کند چون ETEXT_DESC فیلدهای داخلی وابسته به نسخه دارد، پس یک رکورد کپی‌دستی callback لغو و ضرب‌الاجل را روی بعضی بیلدها در آفست‌های اشتباه می‌گذارد. وقتی آن می‌شود هیچ چیز بلند شکست نمی‌خورد. ‏Tesseract به‌سادگی اشاره‌گر callback تو را از فیلدی می‌خواند که حالا چیز دیگری دارد، یا اصلاً ضرب‌الاجل را نمی‌بیند

برای همین HotPDF مانیتور را به‌شکل یک اشاره‌گر مات تلقی می‌کند و فقط از طریق توابع صادرشده به آن دست می‌زند: ‏TessMonitorCreate و TessMonitorSetCancelThis و TessMonitorSetCancelFunc و TessMonitorSetDeadlineMSecs و TessMonitorDelete. اگر C API را خودت برای هدف دیگری bind می‌کنی همین الگو صدق می‌کند. طرح زیر کد binding خودت است، نه API یعنی HotPDF، و declareهایی را که HotPDF داخلاً استفاده می‌کند آینه می‌کند

مدیریت مانیتور DLL یعنی Tesseract در HotPDF: کپی کردن رکورد وابسته به نسخه یعنی ETEXT_DESC callback لغو و ضرب‌الاجل را در آفست‌های اشتباه می‌گذارد و بی‌صدا شکست می‌خورد، در حالی که HotPDF مانیتور را مات تلقی می‌کند، ‏TessMonitorCreate و TessMonitorSetCancelThis و TessMonitorSetCancelFunc و TessMonitorSetDeadlineMSecs را می‌راند و callback یعنی cdecl را بدون exception نگه می‌دارد
یک اشاره‌گر مات به‌علاوهٔ پنج export کل قرارداد است؛ callback یک Boolean یک‌بایتی می‌ماند که فقط یک فلگ و یک ساعت می‌خواند
type
  // ‏C: ‏typedef bool (*TessCancelFunc)(void *cancel_this, int words);
  TTessCancelFunc = function(CancelThis: Pointer; Words: Integer): Boolean; cdecl;
  TTessMonitorCreate = function: Pointer; cdecl;   // ‏ETEXT_DESC*، هرگز dereference نمی‌شود
  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
  // روی پشتهٔ Tesseract اجرا می‌شود: فلگ‌ها و ساعت را بخوان، هرگز raise نکن
  Result := (CancelThis = nil) or POCRJob(CancelThis)^.CancelRequested or
    (GetTickCount64 >= POCRJob(CancelThis)^.DeadlineTick);
end;

// استفاده، با اشاره‌گرهای تابعی که GetProcAddress حل کرده:
//   Monitor := MonitorCreate();
//   try
//     MonitorSetCancelThis(Monitor, @Job);
//     MonitorSetCancelFunc(Monitor, ShouldCancel);
//     MonitorSetDeadlineMSecs(Monitor, RemainingMs);
//     RC := BaseAPIRecognize(API, Monitor);
//   finally
//     MonitorDelete(Monitor);
//   end;

دو جزئیات در آن طرح عمدی‌اند. ‏callback یک Boolean برمی‌گرداند که در هر دو Delphi و Free Pascal یک بایتی است، منطبق با bool یعنی C در TessCancelFunc. ‏BOOL چهاربایتی Windows یا LongBool یعنی Delphi قابل‌جایگزین به نظر می‌رسد و نیست: وقتی یک سمت یک بایت تکی می‌نویسد و سمت دیگر چهار تا می‌خواند، بایت‌های بالای ثبات بازگشت هر چیزی بوده که آنجا مانده بود و یک false می‌تواند به‌شکل true برسد. همان هدر کار را پیچیده‌تر می‌کند، چون توابعی مثل TessPageIteratorBoundingBox یک int برمی‌گردانند که HotPDF به‌شکل Integer declare می‌کند. نوع C هر مقدار بازگشتی را بخوان به‌جای اینکه برای کل API یک قرارداد را فرض کنی

جزئیات دوم این است که callback هرگز raise نمی‌کند. یک exception یعنی Delphi که از فریم‌های C++ یعنی Tesseract باز شود رفتار تعریف‌نشده است، پس callback یعنی HotPDF فقط توکن لغو و یک مقدار یکنوای GetTickCount64 را می‌خواند. آداپتور بعد از برگشتن TessBaseAPIRecognize نتیجه را به یک تشخیص لغو یا تایم‌اوت تبدیل می‌کند و آن چک را فارغ از کد بازگشت بومی انجام می‌دهد

سمت Delphi مالک کدام اشاره‌گرهای بومی است؟

آداپتور DLL یعنی Tesseract در HotPDF به‌ازای هر درخواست مالک سه شیء بومی است، instance API و مانیتور و result iterator، و هر چیز دیگری را قرض می‌گیرد. هر فراخوانی Recognize مجموعه خودش را می‌سازد و در یک بلوک finally آزاد می‌کند: ‏TessResultIteratorDelete، بعد TessMonitorDelete، بعد TessBaseAPIDelete. آزاد کردن interface موتور کتابخانه را بارگیری می‌کند

مالکیت اشیای DLL یعنی Tesseract در HotPDF به‌ازای هر فراخوانی Recognize: result iterator و مانیتور و instance API مالکیت شده و در همان ترتیب داخل finally آزاد می‌شوند، page iterator یعنی TessResultIteratorGetPageIterator یک نمای قرضی است که هرگز نباید آزاد شود، و رشته‌های GetUTF8Text کپی و از طریق TessDeleteText برگردانده می‌شوند
سه شیء مالکیت‌شده، بقیه قرضی: با ترتیب ثابت آزاد کن، page iterator را هرگز دوباره آزاد نکن و هرگز allocatorها را قاطی نکن
  • TessResultIteratorGetPageIterator یک نمای قرضی داخل result iterator برمی‌گرداند نه یک شیء جدید. ‏HotPDF از آن برای TessPageIteratorBoundingBox و TessPageIteratorBaseline استفاده می‌کند و هرگز آزادش نمی‌کند؛ حذف جداگانه‌اش همان حافظه را دو بار آزاد می‌کرد
  • TessResultIteratorGetUTF8Text رشته‌ای برمی‌گرداند که runtime خود DLL تخصیصش داده. ‏HotPDF کپی‌اش می‌کند و از طریق TessDeleteText در یک بلوک finally پس می‌دهد؛ ‏FreeMem یعنی Pascal آن را روی heap اشتباه آزاد می‌کرد
  • متن کلمه با اعتبارسنجی سخت‌گیرانهٔ UTF-8 decode و قبل از تبدیل از نظر طول چک می‌شود. کلماتی با نویسه‌های کنترلی یا UTF-8 بدشکل یا جعبه‌های بیرون تصویر یا مستطیل‌های وارونه یا اطمینان بیرون 0–100 درخواست را شکست می‌دهند به‌جای اینکه بی‌صدا وصله شوند
  • متن کل به‌ازای هر درخواست سقف 1,048,576 واحد کد یعنی UTF-16 دارد و شمارش کلمه باید در بودجهٔ درخواستی که ApplyLoadedOCRTextLayer پایین می‌دهد جا شود

اطمینان به‌شکل 0–100 می‌رسد و به 0–1 مقیاس می‌شود، پس THPDFOCRTextLayerOptions.MinimumConfidence برای هر موتور همان معنا را دارد. وقتی Tesseract یک baseline گزارش می‌کند هر دو نقطهٔ پایانی عبور داده می‌شوند؛ وگرنه خط لولهٔ لایهٔ متنی به تخمین هندسی‌اش برمی‌گردد، دقیقاً همان‌طور که برای ورودی TSV می‌کند

چرا یک enum را قبل از رسیدن به DLL اعتبارسنجی می‌کنی؟

‏HotPDF ترتیب خام PageSegMode و EngineMode را قبل از چک بازه داخل یک Integer کپی می‌کند، چون کامپایلر ممکن است فرض کند یک متغیر enum همیشه مقدار declare‌شده دارد و ‏Ord(X) > Ord(High(T)) را به یک false ثابت فولد کند. آن ترتیب‌ها تزئین نیستند: ‏THPDFTesseractPageSegMode از شماره‌گذاری صفحه‌بندی یعنی Tesseract از 0 تا 13 پیروی می‌کند، ‏THPDFTesseractEngineMode از شماره‌گذاری حالت موتور از 0 تا 3، و هر دو به‌شکل اعداد صحیح خام به DLL می‌روند. یک رکورد آپشن که با FillChar ساخته شده یا از یک استریم پر شده یا با یک عدد صحیح cast شده از C++Builder رسیده می‌تواند بایتی مثل 200 حمل کند. اعتبارسنجی ترتیب کپی‌شده آن را در زمان factory به یک EArgumentException تبدیل می‌کند به‌جای یک حالت تعریف‌نشده داخل کد بومی. ‏factory ‏tpsOSDOnly و tpsAutoOnly را هم رد می‌کند که هیچ کلمه‌ای تولید نمی‌کنند و برای tpsAutoOSD و tpsSparseTextOSD یک osd.traineddata می‌خواهد

تایم‌اوت بازشناسی در واقع چه چیزی را تضمین می‌کند؟

تایم‌اوت DLL یعنی Tesseract مشارکتی است: ‏HotPDF می‌تواند کار خودش را متوقف و از Tesseract بخواهد بایستد اما نمی‌تواند کد بومی را مجبور به برگشتن کند. ساعت وقتی Recognize شروع می‌شود راه می‌افتد، پس تبدیل بیت‌مپ و مقداردهی مدل همان بودجهٔ بازشناسی را مصرف می‌کنند. ‏HotPDF زمان سپری‌شده و توکن لغو را در حین تبدیل خاکستری و بین کلمات هنگام پیمایش نتایج چک می‌کند و میلی‌ثانیه‌های باقی‌مانده را قبل از صدا زدن TessBaseAPIRecognize به TessMonitorSetDeadlineMSecs می‌دهد

شکاف داخل فراخوانی بومی است. مانیتور یعنی Tesseract حین بازشناسی کلمه مشورت می‌شود، نه حین TessBaseAPIInit2 یا تحلیل چیدمان صفحه، پس یک load کند مدل یا یک چیدمان پاتولوژیک می‌تواند قبل از گزارش تایم‌اوت از ضرب‌الاجل عبور کند. بودجه‌های پیکسل و خروجی هم مصرف حافظهٔ خود کتابخانهٔ بومی را کپ نمی‌کنند. اگر worker ای لازم داری که بتوان کشتش، از آداپتور فرایندی استفاده کن؛ آن معاملهٔ صادقانه است، نه یک قابلیت جاافتاده

کالبدشکافی تایم‌اوت مشارکتی DLL یعنی Tesseract در HotPDF: ساعت وقتی Recognize شروع می‌شود راه می‌افتد و تبدیل خاکستری و TessBaseAPIInit2 و تحلیل چیدمان را پوشش می‌دهد، اما مانیتور فقط حین بازشناسی کلمه مشورت می‌شود، پس loadهای مدل و چیدمان می‌توانند قبل از اینکه HotPDF یعنی otlsEngineError یا otlsCancelled را گزارش کند عبور کنند
ضرب‌الاجل اینجا یک درخواست است نه یک تضمین: مقداردهی و تحلیل چیدمان می‌توانند طولانی اجرا شوند و worker ای که واقعاً بتوان کشتش به آداپتور فرایندی نیاز دارد

صفحه‌بندی جایی است که آداپتور DLL روی ورودی دشوار ارزش خودش را نشان می‌دهد. فرم‌ها و برچسب‌ها و جدول‌های اسکن‌شده با فیلدهای پراکنده اغلب با tpsSparseText بهتر از صفحه‌بندی خودکار بازشناسی می‌شوند، که سعی می‌کند ستون‌ها و پاراگراف‌هایی را مونتاژ کند که وجود ندارند

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;  // فیلدهای پراکنده، بدون مونتاژ ستون
  TessOptions.EngineMode := temLSTMOnly;     // مدل‌های LSTM در tessdata لازم است
  TessOptions.TimeoutMilliseconds := 20000;  // شامل مقداردهی مدل
  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;

یک تایم‌اوت به‌شکل otlsEngineError با تشخیص Tesseract DLL OCR timed out خودش را نشان می‌دهد و توکن لغوشده به‌شکل otlsCancelled. در هر دو حالت ‏ApplyLoadedOCRTextLayer قبل از شروع تراکنش ثبت، همهٔ صفحات انتخاب‌شده را بازشناسی کرده، پس شکست در صفحهٔ 40 از 50 سند بارگذاری‌شده را دقیقاً همان‌طور که بود باقی می‌گذارد. توجه کن ‏tpsSingleLine و tpsSingleBlock و tpsSparseText فقط صفحه‌بندی را عوض می‌کنند؛ هیچ‌کدام یک اسکن کج را صاف نمی‌کنند

‏Free Pascal و Lazarus: پیکسل‌های کهنه و چینیِ گم‌شده

هر دو factory یعنی Tesseract از v2.772.1 در بیلدهای Win32 و Win64 یعنی Free Pascal و Lazarus در Windows کار می‌کنند، بعد از دو فیکس مخصوص FPC. اول بستهٔ Lazarus را برای معماری هدف دوباره بیلد کن؛ port عمومی در ‏HotPDF روی Free Pascal و Lazarus Win64 پوشش داده شده

فیکس اول مربوط به پیکسل‌هاست. یک TBitmap یعنی LCL که از طریق scanlineها نوشته می‌شود می‌تواند تصویر خامش را بدون تازه‌سازی handle بیت‌مپ Windows به‌روز کند، پس GetDIBits روی آن handle پیکسل‌های قدیمی را برمی‌گرداند. علامت گیج‌کننده بود: متنی که مستقیم روی یک بیت‌مپ کشیده شده بود بازشناسی می‌شد، در حالی که صفحه‌ای که توسط renderer PDF یعنی HotPDF رندر شده بود یک فهرست کلمهٔ خالی تولید می‌کرد. روی FPC آداپتور حالا یک snapshot حساس-به-فرمت از طریق CreateIntfImage می‌خواند که فرمت پیکسل و ترتیب ردیف تصویر خام را محترم می‌شمارد. بیلد Delphi مسیر GetDIBits را روی یک کپی خصوصی 24 بیتی نگه می‌دارد. هیچ‌کدام از دو بیلد بیت‌مپ فراخواننده را تغییر نمی‌دهند

فیکس دوم مال آداپتور یعنی tesseract.exe است. ‏TStringList یعنی FPC رشته‌های ANSI ذخیره می‌کند، پس منتسب کردن متن TSV یعنی UTF-8 decode‌شده به Lines.Text بی‌سروصدا هر نویسهٔ چینی یا صفحهٔ مکملی را که code page ANSI سیستم نمی‌توانست نشان بدهد می‌انداخت. مسیر FPC حالا ‏TSV را به‌شکل بایت‌های UTF-8 نگه می‌دارد، ‏BOM را در سطح بایت می‌تراشد و هر کلمه را جداگانه به UnicodeString decode می‌کند. آداپتور DLL هرگز این مشکل را نداشت چون هر کلمه را مستقیم از iterator decode می‌کند

مرجع سریع

  • ‏factory: ‏HPDFCreateTesseractDLLOCREngine(LibraryPath, TessDataDirectory, Language[, Options]) در HPDFTesseractRecognition، اضافه‌شده در v2.772.0، پشتیبانی FPC در v2.772.1
  • پیش‌فرض‌ها: ‏tpsAuto و temDefault و 60,000 میلی‌ثانیه و 16,777,216 پیکسل؛ بازهٔ تایم‌اوت 1 تا 3,600,000 میلی‌ثانیه، سقف پیکسل 67,108,864
  • ‏bitness یعنی DLL را با برنامه بخوان و DLLهای وابستگی را کنار DLL یعنی Tesseract بگذار
  • مانیتور را مات تلقی کن؛ هرگز ETEXT_DESC را داخل یک رکورد Pascal کپی نکن
  • callback لغو را ‏cdecl با نتیجهٔ Boolean یک‌بایتی declare کن و هرگز نگذار یک exception از آن فرار کند
  • متن iterator را با TessDeleteText آزاد کن؛ page iterator ای که از result iterator گرفتی را هرگز آزاد نکن
  • انتظار ضرب‌الاجل مشارکتی را داشته باش: مقداردهی مدل و تحلیل چیدمان می‌توانند از آن عبور کنند
  • وقتی خاتمه سخت یا جداسازی crash لازم داری از آداپتور یعنی tesseract.exe استفاده کن

آداپتور DLL یعنی Tesseract و آداپتورهای فرایندی و موتور OCR داخلی همه با کامپوننت HotPDF Delphi PDF برای Delphi و C++Builder و Free Pascal عرضه می‌شوند؛ برای ویرایش‌ها و دانلودها ‏صفحهٔ محصول HotPDF را ببین