Technical Article

Automatizirani PDF preflight i revizija rizika pomoću PDFiuma

U poslovnim tijekovima rada za ispis, arhiviranje i usklađenost dokumenata, PDF datoteka ne može se jednostavno "prikazati" (renderirati). Ona mora biti revidirana. PDF može sadržavati neizravnate (unflattened) grupe transparentnosti koje uzrokuju pad naslijeđenih procesora rasterske slike (RIP-ova), ugrađeni JavaScript koji predstavlja sigurnosni rizik ili slike niske razlučivosti koje će izgledati užasno ispisane na 300 DPI.

Ovaj proces pregleda PDF-a prije nego što uđe u produkcijski tijek rada poznat je kao Preflighting. U ovom ćemo članku istražiti kako izgraditi automatizirani alat za preflight i reviziju rizika u Delphiju korištenjem mogućnosti parsiranja niske razine koje pruža PDFium.

Ključne preflight provjere

Standardna revizija rizika obično provjerava sljedeće elemente unutar PDF-a:

  • Interaktivni elementi: JavaScript akcije, Launch akcije (pokretanje vanjskih programa) i AcroForms.
  • Metrika resursa: Prisutnost slika niske razlučivosti ili nedostajući ugrađeni fontovi.
  • Sigurnosni status: Enkripcija dokumenta, zahtjevi za lozinkom i dopuštenja pristupa (npr. ispis onemogućen).
  • Standardi dokumenta: Validacija PDF/A ili PDF/X oznaka sukladnosti u metapodacima dokumenta.

Izdvajanje PDF metapodataka i zabilješki pomoću PDFiuma

PDFium pruža robusni C API koji Delphi može konzumirati. Da bismo izvršili preflight reviziju, ne samo da renderiramo stranicu; prolazimo kroz stablo PDF objekata. Pogledajmo kako iterirati kroz stranice dokumenta i pregledati zabilješke kako bismo pronašli potencijalno rizične JavaScript akcije.

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;

Provjera razlučivosti slike

Još jedan kritičan preflight korak za tijekove rada ispisa osigurava da nijedna slika ne padne ispod određenog DPI praga. PDFium vam omogućuje izdvajanje slikovnih objekata izravno iz toka stranice. Dijeljenjem dimenzija u pikselima izdvojene slike s fizičkim dimenzijama (u točkama) koje zauzima na PDF stranici, možete izračunati efektivni 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;

Integracija s PDFium Componentom

Izgradnja robusnog preflight motora od nule pomoću sirovog PDFium C API-ja zahtijeva značajnu količinu standardnog (boilerplate) koda i duboko poznavanje PDF specifikacije. Korištenje omotača (wrapper) to neizmjerno pojednostavljuje. Uz Delphi omotač visoke razine, složene iteracije C pokazivača možete pretvoriti u čist objektno orijentirani kod.

Automatizacijom faze preflighta sprječavate loše datoteke da začepe vaš proizvodni cjevovod, štedeći vrijeme i resurse za renderiranje.

Napomena: Napredno PDF parsiranje i mogućnosti preflighta u potpunosti su podržani u PDFium Component.