Technical Article

Automatizovaný preflight PDF a audit rizik pomocí PDFium

V pracovních postupech podnikového tisku, archivace a dodržování předpisů pro dokumenty nelze soubor PDF jednoduše "vykreslit". Musí být podroben auditu. PDF může obsahovat nesloučené skupiny průhledností, které zhroutí starší rastrové obrazové procesory (RIP), vložený JavaScript, který představuje bezpečnostní riziko, nebo obrázky s nízkým rozlišením, které budou při tisku na 300 DPI vypadat příšerně.

Tento proces kontroly PDF před jeho vstupem do produkčního pracovního postupu je známý jako Preflight. V tomto článku prozkoumáme, jak v Delphi vytvořit automatizovaný nástroj pro preflight a audit rizik využitím nízkoúrovňových schopností analýzy PDFium.

Klíčové kontroly preflight

Standardní audit rizik obvykle v PDF kontroluje následující prvky:

  • Interaktivní prvky: Akce JavaScriptu, akce spouštění (spouštění externích programů) a AcroForms.
  • Metriky prostředků: Přítomnost obrázků s nízkým rozlišením nebo chybějící vložená písma.
  • Stav zabezpečení: Šifrování dokumentu, požadavky na heslo a přístupová oprávnění (např. zakázaný tisk).
  • Standardy dokumentu: Ověření značek shody s PDF/A nebo PDF/X v metadatech dokumentu.

Extrakce metadat a anotací PDF pomocí PDFium

PDFium poskytuje robustní C API, které lze použít v Delphi. K provedení preflight auditu nestačí stránku jen vykreslit, musíme projít strom objektů PDF. Podívejme se, jak iterovat přes stránky dokumentu a kontrolovat anotace, abychom nalezli potenciálně rizikové akce JavaScriptu.

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;

Kontrola rozlišení obrazu

Dalším kritickým krokem preflight pro tiskové postupy je zajištění toho, aby žádný obrázek neklesl pod specifickou hranici DPI. PDFium umožňuje extrahovat obrazové objekty přímo ze streamu stránky. Vydělením rozměrů extrahovaného obrázku v pixelech jeho fyzickými rozměry (v bodech), které na stránce PDF zabírá, můžete vypočítat efektivní 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;

Integrace s PDFium Component

Vytvoření robustního modulu pro preflight od nuly pomocí nativního C API PDFium vyžaduje značné množství standardního kódu a hluboké znalosti specifikace PDF. Použití wrapperu to nesmírně zjednodušuje. Pomocí vysokoúrovňového wrapperu pro Delphi můžete přeměnit složité iterace ukazatelů v jazyce C na čistý objektově orientovaný kód.

Automatizací fáze preflight zabráníte tomu, aby vadné soubory zablokovaly váš produkční proces, čímž ušetříte čas i prostředky na vykreslování.

Poznámka: Pokročilá analýza PDF a schopnosti preflight jsou plně podporovány komponentou PDFium Component.