مقاله فنی

محدودیت‌های پیاده‌سازی PDF/A و بررسی رمزگذاری فونت

جزء PDFium محدودیت‌های پیاده‌سازی ISO 19005-1 Annex C را اعتبارسنجی می‌کند — tokenهای نام 127 بایتی، 8191 عنصر آرایه، 4095 entry دیکشنری و 28 سطح تودرتویی container — و یک فونت TrueType نمادی را که یک entry /Encoding حمل می‌کند گزارش می‌کند. هر دو بررسی روی مسیر byte-scan اجرا می‌شوند، پس یک کاربرد Delphi یا Lazarus حکم را بدون بارگذاری اصلاً DLL PDFium می‌گیرد

این‌ها شکست‌هایی هستند که بیشترین مردم را گیج می‌کنند، چون سند درست به‌نظر می‌رسد. رندر می‌شود، چاپ می‌شود، هر فونت جاسازی‌شده است، output intent موجود است. سپس یک اعتبارسنجی آن را روی یک دیکشنری که 4096 entry دارد رد می‌کند، و هیچ‌چیز در سند قابل‌مشاهده توضیح نمی‌دهد چرا

محدودیت‌های Annex C واقعاً از چه محافظت می‌کنند؟

از قابلیت همکاری با پیاده‌سازی‌هایی که قبل از تولیدکننده شما هستند. Annex C محدودیت‌های پیاده‌سازی PDF Reference را در هر بخش PDF/A ادامه می‌دهد، و اعداد دل‌خواه نیستند — آن‌ها توصیف می‌کنند چه چیزی را یک خواننده سازگار تاریخی الزام داشت با آن مقابله کند. فایلی که از آن‌ها فراتر می‌رود شاید در یک viewer مدرن کامل باز شود و در خواننده آرشیوی که یک سیستم record پانزده سال پیش روی آن استاندارد کرده شکست بخورد، که دقیقاً سناریویی است PDF/A برای جلوگیری از آن وجود دارد

چهار محدودیت شامل‌شدنی هستند. یک token نام دقیقاً 127 بایت اعتبارسنجی می‌شود؛ 128 نه. یک آرایه با دقیقاً 8191 عنصر اعتبارسنجی می‌شود؛ 8192 نه. جزء PDFium به همین دلیل هر دو طرف هر مرزی را در test suite خود پین می‌کند، چون یک off-by-one در یک بررسی محدودیت بدترین نوع اعتبارسنجی را تولید می‌کند: یکی که فایل‌های سازگار را رد می‌کند و با این حال باور می‌شود

uses FPdfPdfa;

var
  Src: TFileStream;
  Res: TPdfAValidationResult;
begin
  Src := TFileStream.Create('archive.pdf', fmOpenRead or fmShareDenyWrite);
  try
    Res := ValidatePdfACompliance(Src);
    if pvaiArrayOverLimit in Res.Issues then
      Memo1.Lines.Add('An array carries more than 8191 elements');
    if pvaiDictOverLimit in Res.Issues then
      Memo1.Lines.Add('A dictionary carries more than 4095 entries');
    if pvaiNestingOverLimit in Res.Issues then
      Memo1.Lines.Add('Containers nest deeper than 28 levels');
    if pvaiNameOverLimit in Res.Issues then
      Memo1.Lines.Add('A name token is longer than 127 bytes');
  finally
    Src.Free;
  end;
end;

کدام تولیدکننده‌گان واقعاً این محدودیت‌ها را می‌زنند؟

آن‌هایی که ساختار را به‌صورت برنامه‌ای می‌سازند، که اکثر خروجی line-of-business است. یک فرم با چند هزار فیلد یک آرایه /Annots یا یک آرایه AcroForm /Fields تولید می‌کند که از 8191 رشد می‌کند. صفحه‌ای که دیکشنری منابعش یک entry به ازای هر تصویر یا نمونه فونت تولیدشده انباشته می‌کند از 4095 عبور می‌کند. درختان ساختار عمیق تولیدشده — یک سند تگ‌دار ساخته‌شده با بازگشت روی یک مدل داده تودرتو — بدون اینکه کسی متوجه شود از 28 سطح عبور می‌کنند، چون هیچ‌کس به عمق تودرتو نگاه نمی‌کند

نام‌های بلند از یک عادت متفاوت می‌آیند: رمزگذاری داده در tokenهای نام. یک نام colorant ساخته‌شده از یک شناسه مشتری، یک گروه محتویات اختیاری نام‌گذاری‌شده بر اساس یک مسیر فایل کامل، یک فیلد فرم که نام کاملاً واجد شرایطش شش سطح سلسله‌مراتب را الحاق می‌کند. نام‌ها ارزان برای تولید و آسان برای بلندکردن هستند، و 127 بایت سریع‌تر از آنچه فکر می‌کنید ناپدید می‌شود وقتی یک برچسب رمزگذاری‌شده UTF-8 درگیر است

تعمیر ساختاری در هر مورد است. آرایه را بشکنید، دیکشنری را بشکنید، تودرتویی را تخت کنید، نام را کوتاه کنید — توصیه preflight برای هر مسئله محدودیت ملموس را نام‌گذاری می‌کند به‌جای آنکه بگوید فایل نامعتبر است. تزریق marker اینجا کمک نمی‌تواند بکند: این‌ها ادعاهای فراداده نیستند، شکل گراف شیء هستند

چرا یک فونت TrueType نمادی نباید /Encoding حمل کند

چون ISO 19005-1 §6.3.7 فقط cmap داخلی فونت را برای فونت‌های TrueType نمادی می‌پذیرد، و یک entry /Encoding با آن تناقض خواهد داشت. یک فونت نمادی کدها را به گلیف‌ها به شرط خودش نگاشت می‌کند — که همان معنای نمادی است. یک جدول رمزگذاری اضافه کنید و اکنون دو پاسخ به سؤال «کدام گلیف را بایت 0x41 انتخاب می‌کند» هست، بدون هیچ قانونی در فایل که بگوید کدام برنده است. خواننده‌های متفاوت آن را متفاوت حل می‌کنند، و سندی که در یک viewer به‌عنوان متن رندر می‌شود در دیگری به‌عنوان dingbat رندر می‌شود

جزء PDFium flag نمادی را از /FontDescriptor می‌خواند، چه descriptor درون‌خطی در دیکشنری فونت نوشته شود یا غیرمستقیم ارجاع شود. یک فونت TrueType غیرنمادی /WinAnsiEncoding یا /MacRomanEncoding الزامی خود را بدون علامت‌گذاری نگه می‌دارد، چون برای فونت‌های غیرنمادی رمزگذاری دقیقاً همان چیزی است استاندارد می‌خواهد. بررسی روی تناقض شلیک می‌کند، نه روی حضور یک رمزگذاری

if pvaiSymbolicTrueTypeEncoding in Res.Issues then
  Memo1.Lines.Add(
    'A symbolic TrueType font carries /Encoding; PDF/A admits only its ' +
    'built-in cmap (ISO 19005-1 6.3.7)');

منبع عملی این نقص زیرمجموعه‌سازی فونت توسط تولیدکننده‌ای است که هر فونت TrueType را به همان روش رفتار می‌کند. Symbol، Wingdings، فونت‌های بارکد و فونت‌های آیکن حاملان معمولی هستند — دقیقاً فونت‌هایی که یک سند تجاری برای checkboxها، logoها و بارکدها استفاده می‌کند، و دقیقاً آن‌هایی که هیچ‌کس وقتی سند روی «فونت‌ها» در اعتبارسنجی شکست می‌خورد بازبینی نمی‌کند

مسائل چگونه در یک گزارش preflight می‌رسند

چهار محدودیت container زیر ساختار طبقه‌بندی می‌شوند؛ مسئله رمزگذاری TrueType نمادی زیر محتوا طبقه‌بندی می‌شود. آن تفکیک مهم است وقتی یک گزارش به دو نفر متفاوت می‌رود: یافته‌های ساختار معمولاً به هر کسی تعلق دارد که تولیدکننده را نوشته، و یافته‌های محتوا معمولاً به هر کسی که دارایی‌ها را فراهم کرده

هر مسئله توصیه‌ای حمل می‌کند که درمان را به شرایط ملموس نام‌گذاری می‌کند — tokenهای نام را به 127 بایت یا کمتر کوتاه کنید، آرایه‌ها را بشکنید تا هیچ‌کدام بیش از 8191 عنصر حمل نکند، /Encoding را از فونت‌های TrueType نمادی حذف کنید. گزارشی که می‌گوید «سازگار با PDF/A نیست» یک تحقیق شروع می‌کند. گزارشی که می‌گوید کدام محدودیت فراتر رفته و با چه چیزی یکی را پایان می‌دهد

اعتبارسنجی بدون DLL، و چرا اینجا اهمیت دارد

همه بررسی‌های بالا در برابر بایت‌های فایل اجرا می‌شوند، پس در سرویسی که هیچ باینری PDFium deploy ندارد، در یک مرحله build، یا روی ماشینی که بارگذاری یک DLL بومی یک مسئله خط‌مشی است کار می‌کنند. این یک خط طراحی عمدی در جزء PDFium است: بررسی‌هایی که از ساختار قابل پاسخ هستند از ساختار پاسخ داده می‌شوند، و DLL برای آن‌هایی که واقعاً به یک موتور رندر نیاز دارند رزرو می‌شود

برای workflow پیرامون — اجرای اعتبارسنجی روی یک پوشه، تولید گزارش، و تصمیم درباره کار با یافته‌ها — مرورهای اعتبارسنجی preflight برای PDF/A در Delphi و CLI گزارش preflight دسته‌ای را ببینید. برای انتخاب profile آرشیوی که بالای همه این بررسی‌ها می‌نشیند، یادداشت‌های انطباق آرشیوی PDF/A پوشش می‌دهند کدام بخش و سطح را هدف بگیرید قبل از شروع به تعمیر یافته‌ها

جزء PDFium موتور PDFium را برای Delphi، C++Builder و Lazarus با یک API VCL سطح‌بالا و مجموعه‌ای از اعتبارسنج‌های انطباق که با یا بدون DLL اجرا می‌شوند پیچانده — صفحه محصول جزء PDFium را برای استانداردها و پلتفرم‌های پشتیبانی‌شده ببینید