مقاله فنی

خودکارسازی بررسی Preflight در PDF با دلفی و HotPDF

فایل روی ماشین شما تمیز باز می‌شود. Acrobat نشانش می‌دهد، پیش‌نمایش چاپ درست به نظر می‌رسد، همه صفحه‌ها سر جایشان‌اند. بعد به چاپخانه می‌رود، یا به سامانه بایگانی‌ای که دسته ماهانه شما را می‌بلعد، و مردود برمی‌گردد: تصویرهای RGB در کاری CMYK، نبودن کلید /Trapped، یک output intent که با ماشین چاپ نمی‌خواند. هیچ ایراد قابل دیدنی در سند نبود. سند در برابر یک پروفایل ایراد داشت، و پروفایل جایی بررسی شد که شما آن‌جا نبودید. preflight نام صنعت پیش‌از‌چاپ برای همان بررسی است، و پرسش واقعی این است که وقتی PDFها از دل کد دلفی خودتان بیرون می‌آیند نه از دسکتاپ یک طراح، جای این بررسی کجاست

HotPDF تابعی به نام preflight در اختیارتان نمی‌گذارد. کامپوننت در دموی گرافیکی‌اش یک پنجره گزارش preflight دارد، اما هیچ API پشت آن نیست که یک سرویس یا اسکریپت ساخت بتواند فراخوانی کند، و وانمودکردن به خلافش شما را به جست‌وجوی متدی می‌فرستد که وجود ندارد. این تا وقتی یک کاستی به نظر می‌رسد که متوجه نشده باشید برای فایل‌هایی که خودتان تولید می‌کنید، فراخوانی یک اعتبارسنج روی خروجی خودتان از اساس شکل درستی ندارد. شما همین حالا هر ویژگی‌ای را که یک اعتبارسنج بازرسی می‌کند در اختیار دارید. تقسیم کار مفید این است که تولیدکننده را ناتوان از بیرون‌دادن فایل بد کنید، و بعد آن را با ابزاری که خودتان ننوشته‌اید اثبات کنید

دیاگرام یک خط لوله preflight در دلفی که در آن تنظیمات انطباق HotPDF از تولید PDF بد جلوگیری می‌کند و veraPDF به‌همراه Acrobat Preflight نتیجه را به‌صورت بیرونی اثبات می‌کنند
پیشگیری قواعد PDF/A و PDF/X را در خودِ تولید HotPDF می‌پزد، در حالی که اعتبارسنج‌های بیرونی حکمی را صادر می‌کنند که تولیدکننده نمی‌تواند برای خودش نمره بدهد

چرا خروجی خودتان را جور دیگری بررسی می‌کنید

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

دلیل دومی هم برای بیرونی نگه‌داشتن راستی‌آزمایی هست: اعتبار. کتابخانه‌ای که خروجی خودش را تأیید می‌کند، ورقه امتحان خودش را تصحیح می‌کند. وقتی سامانه بایگانی مشتری یا RIP یک چاپخانه فایل شما را رد می‌کند، جمله «کامپوننت ما می‌گوید فایل سالم است» وزنی ندارد. حکم veraPDF یا Acrobat وزن دارد، چون طرف مقابل همان ابزارها را اجرا می‌کند

انطباق را یک تنظیم کنید، نه یک سیاهه وارسی

لایه پیشگیری فقط پیکربندی است. پیش از BeginDoc مقدار PDFACompliance یا PDFXCompliance را تنظیم کنید و HotPDF قواعد متناظر را در تمام گذر تولید نگه می‌دارد: قلم‌ها را جاسازی می‌کند، مصرف DeviceRGB و DeviceCMYK را در برابر output intent اعلامی شما می‌پاید، و ویژگی‌هایی را که پروفایل ممنوع کرده رد می‌کند. تناقض‌ها در EndDoc سر بر می‌آورند، جایی که دروازه‌های انطباق به‌جای فرستادن بی‌صدای چیزی که پایین‌دست شکست خواهد خورد، استثنا می‌دهند. پس از ذخیره فایل، همان ویژگی‌ها آنچه را واقعاً اعمال شده بازمی‌خوانند، و این همان یک واقعیتی است که لاگ خط لوله شما بیش از همه لازم دارد:

// پس از EndDoc: پروفایل‌های اعمال‌شده را همراه فراداده اجرا ثبت کنید
if Pdf.PDFACompliance <> '' then
  Log('Generated as PDF/A level ' + Pdf.PDFACompliance);
if Pdf.PDFXCompliance <> '' then
  Log('Generated as PDF/X profile ' + Pdf.PDFXCompliance);

آن پرچم‌ها را در همان خط لاگی بگذارید که هش داده ورودی و نسخه HotPDF در آن است. روزی که یک اعتبارسنج و تولیدکننده شما درباره یک فایل اختلاف پیدا کنند، آن خط به شما می‌گوید کدام قالب آن را ساخته و کدام بیلد کتابخانه بارگذاری شده بود، و بحثی که وگرنه یک بعدازظهر را می‌خورد به یک grep تبدیل می‌شود. output intentها، پروفایل‌های ICC و برچسب‌گذاری که پشت این پرچم‌ها نشسته‌اند در راهنمای خروجی PDF/A، PDF/X و PDF/UA با HotPDF شرح داده شده‌اند

یک دروازه نخست ارزان برای فایل‌هایی که خودتان نساخته‌اید

هر خط لوله‌ای صرفاً تولیدی نیست. مشتری‌ها PDF بارگذاری می‌کنند، اسکنرها آن را در پوشه‌ای می‌ریزند، شریک‌ها به ایمیل پیوستش می‌کنند. راندن تک‌تک این‌ها از یک اعتبارسنج ساختاری کامل، وقت صف را روی فایل‌هایی هدر می‌دهد که حتی باز نمی‌شوند. رابط Direct File API در HotPDF به‌اندازه‌ای از ساختار فایل را می‌خواند که به پرسش «آیا این اصلاً یک PDF قابل استفاده است» پاسخ دهد، بی‌آنکه کل درخت اشیا را بارگذاری کند، و همین آن را جای خوبی برای شکست سریع می‌کند:

function TriagePdf(Pdf: THotPDF; const FileName: string): Boolean;
var
  Handle, Pages: Integer;
begin
  Result := False;
  Handle := Pdf.DAOpenFileReadOnly(FileName, '');
  if Handle <= 0 then
    Exit;  // از نظر ساختاری ناخوانا: قرنطینه کنید، اعتبارسنجی نکنید
  try
    Pages := Pdf.DAGetPageCount(Handle);
    Result := Pages > 0;
  finally
    Pdf.DACloseFile(Handle);
  end;
end;

دو واقعیت درباره این API تعیین می‌کند چطور آن را بپیچید. میان‌بُر حافظه تخت فقط برای ورودی رمزنگاری‌نشده برقرار است؛ به DAOpenFileReadOnly یک گذرواژه بدهید و بی‌صدا به تجزیه کامل بازمی‌گردد، پس فایلی که می‌دانید رمزنگاری‌شده است باید پیش از تریاژ از راه DecryptFile به یک نسخه کاری ساده تبدیل شود. و DAGetPageCount روی دسته‌ای که تمیز باز نشده هیچ معنایی ندارد، پس بررسی دسته سخت‌گیر می‌ماند و نتیجه غیرمثبت یعنی رد، نه تلاش دوباره. الگوهای بیشتری از این دست در مقاله Direct File API برای جریان‌های کاری PDF بزرگ آمده است

veraPDF، اجرا به‌عنوان بخشی از بیلد

برای هر چیزی که آن را PDF/A یا PDF/UA می‌خوانید، veraPDF همان اعتبارسنجی است که باید سیم‌کشی شود. بدون رابط گرافیکی اجرا می‌شود، یک دسته می‌گیرد، XML یا JSON بیرون می‌دهد، و هر شکست را با بند ISO مربوط به خودش نام می‌برد، پس شکست یک قاعده در برابر بند 6.2.2 از ISO 19005-1 مستقیم به یک تنظیم تولیدکننده اشاره می‌کند به‌جای اینکه شما را به حدس‌زدن بسپارد. راندن آن از دلفی کنترل ساده فرایند است:

function RunVeraPdf(const PdfFile, ReportFile: string): Cardinal;
var
  Cmd: string;
  SI: TStartupInfo;
  PI: TProcessInformation;
begin
  Cmd := Format('cmd /c verapdf.bat --format xml "%s" > "%s"',
    [PdfFile, ReportFile]);
  FillChar(SI, SizeOf(SI), 0);
  SI.cb := SizeOf(SI);
  if not CreateProcess(nil, PChar(Cmd), nil, nil, False,
      CREATE_NO_WINDOW, nil, nil, SI, PI) then
    RaiseLastOSError;
  try
    WaitForSingleObject(PI.hProcess, 120000);  // انتظار را برای هر فایل کران‌دار کنید
    GetExitCodeProcess(PI.hProcess, Result);
  finally
    CloseHandle(PI.hThread);
    CloseHandle(PI.hProcess);
  end;
end;

آن مهلت زمانی نان خودش را در می‌آورد. یک فایل بدساخت می‌تواند هر تجزیه‌گری را به گوشه‌ای براند که هرگز از آن بیرون نیاید، و انتظار بی‌پایان درون یک کارگر صف، بقیه صف را هم با خودش پایین می‌کشد. انتظار را کران‌دار کنید، به مهلت‌شدن کد شکست خودش را بدهید، و فایل را برای یک انسان کنار بگذارید. هنگام خواندن نتیجه، XML را برای شناسه‌های قاعده تجزیه کنید نه برای متن خوانای انسان. شناسه‌های قاعده از ارتقای اعتبارسنج جان سالم به در می‌برند؛ عبارت‌بندی پیام‌ها نه، و یک کد پایدار چیزی است که مهندس پشتیبانی می‌تواند در تیکت‌های قدیمی جست‌وجویش کند

نحوه اجرای دسته به همان اندازه مهم است که پاس‌شدن تک‌تک فایل‌ها. یک فرایند برای هر فایل، نه یکی برای هر دسته، تا یک ورودی سمی فقط به اندازه مهلت همان فایل برایتان خرج بردارد. شمار فرایندهای اعتبارسنج را به تعداد هسته‌ها محدود کنید، چون ساختن گزارش XML کران‌دار به CPU است و بیش‌ازحد اشتراک فقط سیستم را می‌کوبد. و در ورودی یک سقف اندازه بگذارید، چون یک کتاب اسکن‌شده دو گیگابایتی هرقدر هم تجزیه‌گر صبور باشد صف را از آن خود می‌کند. هیچ‌کدام از این‌ها به معنای دقیق کلمه preflight نیست. این‌ها تفاوت میان دروازه‌ای است که از حجم پایان ماه جان به در می‌برد و دروازه‌ای که همان شب اولی که ساعت ۲ بامداد خط لوله را می‌خواباند خاموش می‌شود

دیاگرام یک دروازه دسته‌ای در دلفی که برای هر PDF یک فرایند veraPDF با مهلت کران‌دار اجرا می‌کند، به‌جای پیام‌ها شناسه‌های قاعده را از XML استخراج می‌کند و هر گزارش را کنار فایلش بایگانی می‌کند
یک نگهبان ورودی بار صف را محدود می‌کند، در حالی که یک کارگر تازه veraPDF برای هر فایل نمی‌گذارد ورودی مسموم بیلد را بخواباند

PDF/X جایی است که این رویکرد کم می‌آورد. veraPDF آن را اعتبارسنجی نمی‌کند، پس بررسی کارآمد همچنان Preflight در Acrobat با همان پروفایل ISO 15930 است که چاپخانه‌تان نام برده. Acrobat انسان می‌خواهد، و این یعنی نمونه‌گیری به‌جای پوشش کامل: نخستین فایل از هر قالب تازه، به‌علاوه یک قرعه تصادفی کوچک از هر دسته، در حالی که دروازه خودکار هر چیزی را که بدون انسان می‌شود سر و سامان می‌دهد. یک بررسی نمونه‌ای که واقعاً اجرا می‌شود بهتر از یک خودکارسازی کامل است که برای همیشه نیمه‌کاره می‌ماند

گزارشی که یک سال بعد هم بازش می‌خواهید

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

استثناها هم باید نوشته شوند. وقتی مشتری اصرار دارد فایلی را بفرستد که دروازه نمی‌پسندد، پاسخ این نیست که قاعده را برای همه شل کنید. ثبت کنید چه کسی این فایل را تأیید کرده، بر چه مبنایی، و تا چه تاریخی، و بعد آن معافیت را به گزارشش پیوست کنید. معافیتی که نام و تاریخ انقضا دارد تصمیمی است که کسی مالکش است. بررسی‌ای که «موقتاً» کامنت شده، حادثه‌ای است که منتظر تاریخ خودش نشسته

یک عادت دیگر هم خرجش را در می‌آورد: وقتی فایلی رد می‌شود، پیش از آنکه کسی دستش بزند آن را در پوشه‌ای با نام مشخص برای رگرسیون کپی کنید. تقریباً هر مشکل preflight که ارزش اشکال‌زدایی دارد به یک ورودی مشخص برمی‌گردد، و تیم‌هایی که آن ورودی‌ها را نگه می‌دارند تکرار مشکل را در یک ساعت درست می‌کنند به‌جای اینکه منتظر بمانند دوباره در محیط تولید سر بلند کند. ویژگی‌های انطباق و Direct File API که این‌جا نشان داده شدند بخشی از HotPDF Delphi Component برای دلفی و C++Builder هستند، که مستنداتش هر فراخوانی را به‌تفصیل پوشش می‌دهد