Technical Article

پیش‌بررسی و حسابرسی ریسک خودکار فایل‌های PDF با PDFium

در جریان‌های کاری چاپ سازمانی، بایگانی و انطباق اسناد، یک فایل PDF نمی‌تواند به سادگی فقط "رندر" شود؛ بلکه باید مورد حسابرسی قرار گیرد. یک فایل PDF ممکن است حاوی گروه‌های شفافیت تخت نشده باشد که پردازنده‌های تصویر شطرنجی (RIP) قدیمی را از کار می‌اندازند، جاوا اسکریپت تعبیه‌شده‌ای داشته باشد که یک خطر امنیتی به همراه دارد، یا دارای تصاویر با وضوح پایینی باشد که هنگام چاپ در 300 DPI بسیار بی‌کیفیت به نظر می‌رسند.

این فرآیند بازرسی یک فایل PDF پیش از ورود به جریان کاری تولید با نام پیش‌بررسی (Preflighting) شناخته می‌شود. در این مقاله، بررسی خواهیم کرد که چگونه با بهره‌گیری از قابلیت‌های تجزیه سطح پایین در PDFium، یک ابزار حسابرسی ریسک و پیش‌بررسی خودکار در دلفی بسازیم.

بررسی‌های کلیدی در مرحله پیش‌بررسی

یک حسابرسی ریسک استاندارد معمولاً موارد زیر را در یک فایل PDF بررسی می‌کند:

  • عناصر تعاملی: اکشن‌های جاوا اسکریپت، اکشن‌های راه‌اندازی (اجرای برنامه‌های خارجی) و AcroForms.
  • معیارهای منابع: وجود تصاویر با وضوح پایین یا فونت‌های جاسازی شده مفقود.
  • وضعیت امنیتی: رمزنگاری سند، نیاز به رمز عبور و مجوزهای دسترسی (مثلاً غیرفعال بودن امکان چاپ).
  • استانداردهای سند: اعتبارسنجی نشانگرهای انطباق PDF/A یا PDF/X در متادیتای سند.

استخراج متادیتا و حاشیه‌نویسی‌های PDF با PDFium

موتور PDFium یک C API قدرتمند را ارائه می‌دهد که در دلفی قابل استفاده است. برای انجام یک حسابرسی پیش‌بررسی، ما فقط صفحه را رندر نمی‌کنیم؛ بلکه در درخت اشیاء PDF حرکت می‌کنیم. بیایید نگاهی بیندازیم به نحوه پیمایش صفحات سند و بازرسی حاشیه‌نویسی‌ها (annotations) برای یافتن اکشن‌های جاوا اسکریپت بالقوه خطرناک.

uses
  System.SysUtils, pdfium_lib;

procedure AuditPdfSecurity(const FileName: string);
var
  Doc: FPDF_DOCUMENT;
  PageCount, i, j: Integer;
  Page: FPDF_PAGE;
  AnnotCount: Integer;
  Annot: FPDF_ANNOTATION;
  AnnotSubType: FPDF_ANNOTATION_SUBTYPE;
  Action: FPDF_ACTION;
  ActionType: ULONG;
begin
  FPDF_InitLibrary();
  try
    // Load the document without a password
    Doc := FPDF_LoadDocument(PAnsiChar(AnsiString(FileName)), nil);
    if Doc = nil then
      raise Exception.Create('Failed to load document or password required.');
      
    try
      PageCount := FPDF_GetPageCount(Doc);
      Writeln(Format('Auditing %d pages...', [PageCount]));
      
      for i := 0 to PageCount - 1 do
      begin
        Page := FPDF_LoadPage(Doc, i);
        if Page <> nil then
        begin
          AnnotCount := FPDFPage_GetAnnotCount(Page);
          for j := 0 to AnnotCount - 1 do
          begin
            Annot := FPDFPage_GetAnnot(Page, j);
            if Annot <> nil then
            begin
              AnnotSubType := FPDFAnnot_GetSubtype(Annot);
              
              // Check for Link Annotations that might have malicious actions
              if AnnotSubType = FPDF_ANNOT_LINK then
              begin
                Action := FPDFAnnot_GetLinkAction(Annot);
                if Action <> nil then
                begin
                  ActionType := FPDFAction_GetType(Action);
                  // 2 represents PDFACTION_URI, 3 represents PDFACTION_SOUND, 4 represents PDFACTION_MOVIE
                  // We specifically look out for PDFACTION_LAUNCH or PDFACTION_JAVA_SCRIPT
                  if (ActionType = PDFACTION_JAVA_SCRIPT) or (ActionType = PDFACTION_LAUNCH) then
                  begin
                    Writeln(Format('WARNING: Malicious action detected on page %d', [i + 1]));
                  end;
                end;
              end;
              FPDFPage_CloseAnnot(Annot);
            end;
          end;
          FPDF_ClosePage(Page);
        end;
      end;
    finally
      FPDF_CloseDocument(Doc);
    end;
  finally
    FPDF_DestroyLibrary();
  end;
end;

بررسی وضوح تصاویر

یکی دیگر از مراحل مهم پیش‌بررسی برای جریان‌های کاری چاپ، اطمینان از این است که هیچ تصویری کمتر از آستانه DPI خاصی نباشد. PDFium به شما امکان می‌دهد اشیاء تصویری را مستقیماً از جریان صفحه استخراج کنید. با تقسیم ابعاد پیکسل تصویر استخراج شده بر ابعاد فیزیکی (بر حسب پوینت) که در صفحه PDF اشغال می‌کند، می‌توانید DPI موثر را محاسبه کنید.

procedure AuditPageImages(Page: FPDF_PAGE);
var
  ObjCount, i: Integer;
  PageObj: FPDF_PAGEOBJECT;
  ImgWidth, ImgHeight: Integer;
begin
  ObjCount := FPDFPage_CountObjects(Page);
  for i := 0 to ObjCount - 1 do
  begin
    PageObj := FPDFPage_GetObject(Page, i);
    if FPDFPageObj_GetType(PageObj) = FPDF_PAGEOBJ_IMAGE then
    begin
      FPDFImageObj_GetBitmap(PageObj); // Returns an FPDF_BITMAP you can inspect
      // Retrieve metadata using FPDFImageObj_GetImageMetadata
      // Calculate effective DPI based on quad coordinates
    end;
  end;
end;

یکپارچه‌سازی با PDFium Component

ساخت یک موتور پیش‌بررسی قدرتمند از پایه با استفاده از C API خام در PDFium نیازمند کدهای تکراری قابل توجه و دانش عمیق از مشخصات PDF است. استفاده از یک پوشش‌دهنده (wrapper) این کار را بسیار ساده می‌کند. با یک پوشش‌دهنده سطح بالا در دلفی، می‌توانید تکرارهای پیچیده اشاره‌گرها در C را به یک کد شیءگرای تمیز تبدیل کنید.

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

توجه: قابلیت‌های پیشرفته تجزیه PDF و پیش‌بررسی به طور کامل در کامپوننت PDFium Component VCL پشتیبانی می‌شوند.