مقاله فنی

بک‌اندهای رمزگذار JBIG2 و لینکر Free Pascal

PDFlibPas می‌تواند تصاویر دوسطحی را از طریق دو بک‌اند متفاوت به JBIG2 رمزگذاری کند. یکی رمزگذار MMR بومی Object Pascal است که همیشه حاضر است. دیگری رمزگذار خارجی دیکشنری-نماد است که روی متن اسکن‌شده خروجی به‌مراتب کوچک‌تری تولید می‌کند، و اختیاری است: یک پروژه باید یونیت بک‌اند را لینک کند تا اصلاً وجود داشته باشد. همین تمایز منبع رایج‌ترین غافلگیری با این ویژگی است، پس ارزش دارد اول گفته شود: DefaultJBIG2EncodeOptions به‌طور پیش‌فرض رمزگذار خارجی را درخواست می‌کند، و وقتی یونیت بک‌اند لینک نشده باشد این درخواست بی‌سروصدا به مسیر MMR پاسکال برمی‌گردد

روی Delphi و C++Builder بک‌اند خارجی مجموعه‌ای از آبجکت‌های استاتیک از پیش ساخته‌شده است. روی Free Pascal باید به یک DLL تبدیل می‌شد، و راه رسیدن به آن نتیجه قصه‌ای درباره لینکر است که برای هر کسی که تلاش کرده آبجکت‌های ++C را در برنامه Free Pascal لینک کند مفید است

ثبت‌نام، قرارداد است

یونیت بک‌اند خود را از بخش initialisation خود با فراخوانی RegisterJBIG2EncoderBackend ثبت می‌کند. فراخوانندگان یا از طریق بیت گزینه‌ها سراغش می‌روند، یعنی PDF_JBIG2_OPTION_EXTERNAL_ENCODER که مقدارش ۴ است، یا از طریق پارامتر UseExternalEncoder در نقاط ورود تصویر توسعه‌یافته. یونیت چتر کتابخانه عمداً یونیت بک‌اند را درون خود نمی‌کشد، چون حمل یک مجموعه آبجکت بزرگ باید تصمیم هر پروژه باشد؛ در درخت C++Builder مثلاً پروژه‌هایی که آن را می‌خواهند صراحتاً include می‌کنند

پیامد برای فراخوانندگان این است که درخواست رمزگذار خارجی یک ترجیح است نه یک تضمین، و بیلدی که یونیت را فراموش کند به‌جای خطا فایل‌های بزرگ‌تر تولید می‌کند. اگر اندازه خروجی به‌اندازه کافی مهم است که رمزگذار بهتر را بخواهید، به‌اندازه کافی مهم است که بررسی کنید واقعاً آن را گرفته‌اید

جریان درخواست رمزگذاری JBIG2 در PDFlibPas که ترجیح رمزگذار خارجی بدون یونیت بک‌اند بی‌سروصدا به مسیر بومی MMR پاسکال برمی‌گردد
درخواست رمزگذار خارجی دیکشنری-نماد یک ترجیح است: با لینک، خروجی کوچک می‌شود؛ بدون لینک، مسیر MMR پاسکال بی‌سروصدا با فایل‌های بزرگ‌تر اجرا می‌شود
uses
  PDFlibrary,
{$IFDEF FPC}
  PDFlibJBIG2EncDLL;    // بک‌اند پویا برای Free Pascal
{$ELSE}
  PDFlibJBIG2EncC;      // مجموعه آبجکت استاتیک برای Delphi / C++Builder
{$ENDIF}

var
  Pdf: TPDFlib;
  ImageId: Integer;
begin
  Pdf := TPDFlib.Create(nil);
  try
    Pdf.NewDocument;
    Pdf.NewPage;
    // Interpolate, SymbolExtract, UseExternalEncoder, SkipBlackDots,
    // BlackDotSize, LossyLevel
    ImageId := Pdf.AddImageJBIG2FromFileEx('scan-page-1.tif',
      0, 1, 1, 0, 0, 0);
    if ImageId = 0 then
      raise Exception.Create('JBIG2 encoding failed');
    Pdf.SaveToFile('archive.pdf');
  finally
    Pdf.Free;
  end;
end;

کامپایل یونیت دو خط بود. سمبل‌ها کار اصلی بود

کامپایل‌شدن خود یونیت بک‌اند زیر Free Pascal دقیقاً دو تغییر برد: تنظیم گویش اسمبلر، و جایگزین‌کردن یک سازنده تنظیمات فرمت مبتنی بر رکورد با متغیر پیش‌فرض سراسری. این بازتاب منصفانه‌ای از میزان قابل انتقال بودن پاسکال سرراست میان دو کامپایلر است

سمت سمبل‌ها کار واقعی بود. مجموعه آبجکت به ۱۷۶ سمبل C ارجاع می‌دهد. از آن‌ها، ۱۲۸ مورد از قبل پیاده‌سازی پاسکالی درون یونیت داشتند و فقط به نام‌های export نیاز داشتند، چون دلفی نام تابع را به‌عنوان نام سمبل به کار می‌برد در حالی که Free Pascal اعلان صریح نام public می‌خواهد. بیست‌وهفت مورد با کدک JPEG 2000 مشترک بودند و باید دقیقاً از یک جا export می‌شدند، چون تعریف دوبارشان هر برنامه‌ای که هر دو را لینک کند می‌شکند. ۲۱ مورد باقی‌مانده ورودی‌های پلتفرم و زمان اجرای C بودند، شانزده تابع فایل Win32 به‌علاوه مشتی فراخوانی کتابخانه استاندارد، و به یک یونیت سازگاری جدید رفتند

هیچ‌کدام از این‌ها از نظر مفهومی سخت نیست، و همه آن لازم است پیش از آنکه لینکر حتی تلاش کند. لینکر همان‌جایی است که کار متوقف شد

سه مسیر لینک، سه بن‌بست

لینکر داخلی Free Pascal نمی‌تواند فایل‌های آبجکت را بخواند، چون آن‌ها توسط کامپایلری تولید شده‌اند که سکشن‌های COMDAT انجمنی ساطع می‌کند و لینکر داخلی گزارش می‌دهد که از آن‌ها پشتیبانی نمی‌کند. این یک رد صریح است، نه یک هشدار

رفتن سراغ یک لینکر خارجی شبیه پاسخ بود. لینکر binutils همراه Free Pascal هنگام اعمال garbage collection سکشن روی این آرشیو کلاً crash می‌کند، و آن پرچم بخشی از مجموعه پارامتر ثابتی است که Free Pascal برای هدف Windows 64بیتی پاس می‌دهد، پس نمی‌توان آن را از خط فرمان حذف کرد؛ کلیدهای مستندشده برای سرکوبش در این مسیر نادیده گرفته می‌شوند. دادن یک binutils بسیار جدیدتر جور دیگری شکست می‌خورد: اصلاً نمی‌تواند اسکریپت لینک Free Pascal را پردازش کند، بدون اسکریپت خروجی خالی تولید می‌کند و با اسکریپت دیواری از خطاهای relocation

یک مرزی که در این مسیر کشف شد حتی اگر هرگز به مشکل لینکر برخورد نکنید ارزش دانستن دارد. لینکر خارجی مسیرهای فایل آبجکت را نسبت به دایرکتوری خروجی فایل اجرایی حل می‌کند نه درخت سورس، پس دستور include-object نسبی فقط وقتی کار می‌کند که دایرکتوری خروجی اتفاقاً با دایرکتوری کاری زمان کامپایل برابر باشد. یک کتابخانه نمی‌تواند چنین چیزی را درباره پروژه مصرف‌کننده فرض کند، که خودش دلیل کافی است برای ترجیح یک کتابخانه لینک‌شده به آبجکت‌های پراکنده

سه مسیر شکست‌خورده لینکر برای آبجکت‌های رمزگذار JBIG2 ++C زیر Free Pascal و DLLای با دو نقطه ورود C تخت که آن‌ها را حل کرد
سکشن‌های COMDAT لینکر داخلی را مأیوس می‌کنند و هر دو لینکر خارجی شکست می‌خورند، پس رمزگذار ++C به‌صورت یک DLL عرضه می‌شود که یونیت بک‌اند به‌طور پویا به آن متصل می‌شود

چرا یک کامپایلر ++C دیگر کمک نمی‌کند

ایده بدیهی بعدی بازساختن سمت ++C با کامپایلری است که Free Pascal بتواند آبجکت‌هایش را بخواند. آن هم کار نمی‌کند، و دلیلش بنیادی است نه مساله کلیدها. یک یگان ترجمه حداقلی ++C حاوی یک template، که با خاموش‌بودن همه قابلیت‌های تولید کد کامپایل شده، همچنان سمبل‌های خارجی ضعیف ساطع می‌کند، چون نمونه‌سازی template و inline ذاتاً آن‌ها را تولید می‌کند. Free Pascal آن رده سمبل را کلاً رد می‌کند. جهت معکوس هم شکست می‌خورد: یک لینکر رایج ++C به‌خاطر همان رفتار سکشن COMDAT نمی‌تواند آبجکت‌های کامپایلر دیگر را مصرف کند

پس کد ++C نمی‌تواند به هیچ مسیر موجودی به‌صورت آبجکت به Free Pascal تحویل شود. می‌تواند به‌صورت DLL تحویل شود، و همان شد: رمزگذار و وابستگی پردازش تصویرش در یک کتابخانه ساخته شدند که دو نقطه ورود تخت C آشکار می‌کند، و یونیت بک‌اند Free Pascal به‌طور پویا به آن‌ها متصل می‌شود و خود را دقیقاً مثل بک‌اند استاتیک ثبت می‌کند. مسیر Delphi و C++Builder اصلاً دست نخورد، که نتیجه درست است؛ یک مشکل قابلیت انتقال روی یک toolchain نباید toolchainی را که از قبل کار می‌کرد آشفته کند

قطبیت همان چیزی است که گازتان می‌گیرد

میان یک bitmap دوسطحی ویندوز و یک رمزگذار JBIG2 ناهمخوانی قراردادی هست که هیچ سامانه نوعی آن را نمی‌گیرد. scanline با یک بیت‌برپیکسل bitmap مستقل از دستگاه، بیت یک را سفید می‌داند. رمزگذار بیت یک را سیاه می‌داند. scanlineها را بدون تغییر تحویل دهید و یک جریان JBIG2 کاملاً معتبر از نگاتیو عکاسی صفحه‌تان می‌گیرید

قراردادهای قطبیت DIB یک‌بیتی و JBIG2 که در آن بیت یک در scanline سفید و در رمزگذار سیاه است، و با وارون‌کردن هر بایت حل می‌شود
همان بایت‌ها، معنای مخالف: بدون وارون‌کردن هر بایت، رمزگذار یک جریان JBIG2 معتبر از نگاتیو تولید می‌کند
// DIB یک‌بیتی: بیت یک یعنی سفید. رمزگذار JBIG2: بیت یک یعنی
// سیاه. در مسیر ورود هر بایت را وارون کنید
for I := 0 to RowBytes - 1 do
  Row[I] := Row[I] xor $FF;

روش راستی‌آزمایی به اندازه خود fix مهم است. مقایسه طول جریان‌های فشرده هیچ نمی‌گوید، چون تصویر نگاتیو به اندازه مشابهی فشرده می‌شود. نگاه‌کردن به صفحه فقط ثابت می‌کند که به‌وضوح وارون نیست. کنترل مطمئن این است که خروجی هر دو مسیر رمزگذاری، پاسکال بومی و خارجی، به PNG رندر و بایت‌به‌بایت مقایسه شود: هر دو رمزگذار روی همان تصویر مبدأ بی‌اتلاف‌اند، پس هر چیزی جز تطابق دقیق باگ در یکی از آن‌هاست. آن مقایسه اکنون یک تست رگرسیون دائمی است، و از جنس ادعاهایی است که هر وقت قرار است دو پیاده‌سازی دقیقاً موافق باشند ارزش ساختن دارد

کدام بک‌اند را استفاده کنیم

برای محتوای دوسطحی عمومی، هافتون‌های dither شده، line art و گرافیک مخلوط، رمزگذار MMR بومی پاسکال کافی است و هیچ هزینه استقراری ندارد. برای متن اسکن‌شده، که همان مورد طراحی JBIG2 است، رمزگذار خارجی دیکشنری-نماد جای کاهش اندازه است، چون شکل‌های تکراری glyph را به یک دیکشنری فاکتور می‌کند به‌جای رمزگذاری دوباره هر وقوع. اگر در حال تولید آرشیو اسناد اسکن‌شده هستید، آن تفاوت به‌اندازه کافی بزرگ است که برنامه‌ریزی ذخیره‌سازی را تغییر دهد

پرسش بالادستی، اینکه تصویر دوسطحی اول سر چگونه تولید می‌شود، برای اندازه خروجی به همان اندازه اهمیت دارد؛ رندر تک‌رنگ مبتنی بر ناحیه در مقاله رندر ناحیه تک‌رنگ پوشش داده شده، و راهبرد اندازه در سطح سند در بهینه‌سازی اندازه فایل PDF و subsetting فونت. برای مجموعه اسکن با صفحات تکراری، حذف تکرار اغلب بهتر از فشرده‌سازی بهتر عمل می‌کند، که موضوع حذف تکرار ادراکی تصویر است. در دسترس بودن toolchain و بک‌اند به‌ازای هر پلتفرم در صفحه محصول losLab PDF Developer Library فهرست شده است