Technical Article

Preflight PDF automatizat și auditarea riscurilor cu PDFium

În fluxurile de lucru pentru imprimare enterprise, arhivare și conformitate a documentelor, un fișier PDF nu poate fi pur și simplu „randat”. Acesta trebuie să fie auditat. Un PDF ar putea conține grupuri de transparență neaplatizate care blochează procesoarele de imagini raster (RIP) mai vechi, JavaScript încorporat care prezintă un risc de securitate sau imagini cu rezoluție redusă care vor arăta groaznic la imprimarea la 300 DPI.

Acest proces de inspectare a unui PDF înainte ca acesta să intre într-un flux de lucru de producție este cunoscut sub numele de Preflighting. În acest articol, vom explora cum să construim un instrument automatizat de preflight și de auditare a riscurilor în Delphi, valorificând capacitățile de parsare de nivel scăzut ale PDFium.

Verificări preflight cheie

Un audit standard al riscurilor verifică de obicei următoarele elemente dintr-un PDF:

  • Elemente interactive: Acțiuni JavaScript, acțiuni Launch (executarea de programe externe) și AcroForms.
  • Valori ale resurselor: Prezența imaginilor de rezoluție redusă sau a fonturilor încorporate lipsă.
  • Stare de securitate: Criptarea documentului, cerințele pentru parole și permisiunile de acces (de exemplu, imprimarea dezactivată).
  • Standarde ale documentului: Validarea markerilor de conformitate PDF/A sau PDF/X în metadatele documentului.

Extragerea metadatelor și a adnotărilor PDF cu PDFium

PDFium oferă un API C robust pe care Delphi îl poate consuma. Pentru a efectua un audit preflight, nu randăm pur și simplu pagina; parcurgem arborele de obiecte PDF. Să vedem cum să iterăm prin paginile documentului și să inspectăm adnotările pentru a găsi acțiuni JavaScript potențial riscante.

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;

Verificarea rezoluției imaginii

Un alt pas critic de preflight pentru fluxurile de lucru de imprimare este asigurarea faptului că nicio imagine nu scade sub un prag DPI specific. PDFium vă permite să extrageți obiecte imagine direct din fluxul paginii. Împărțind dimensiunile în pixeli ale imaginii extrase la dimensiunile fizice (în puncte) pe care le ocupă pe pagina PDF, puteți calcula DPI-ul efectiv.

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;

Integrarea cu PDFium Component

Construirea unui motor de preflight robust de la zero folosind API-ul C PDFium brut necesită un cod repetitiv semnificativ și o cunoaștere profundă a specificației PDF. Utilizarea unui wrapper simplifică enorm acest lucru. Cu un wrapper Delphi de nivel înalt, puteți transforma iterațiile complexe de pointeri C într-un cod curat orientat pe obiecte.

Automatizând faza de preflight, preveniți ca fișierele defecte să vă blocheze pipeline-ul de producție, economisind atât timp, cât și resurse de randare.

Notă: Capacitățile avansate de parsare și preflight PDF sunt pe deplin suportate de PDFium Component.