Technical Article

Αυτοματοποιημένος Έλεγχος PDF Preflight και Έλεγχος Κινδύνων με το PDFium

Στις εταιρικές ροές εργασίας εκτύπωσης, αρχειοθέτησης και συμμόρφωσης εγγράφων, ένα αρχείο PDF δεν μπορεί απλώς να "αποδοθεί" (rendered). Πρέπει να ελεγχθεί. Ένα PDF ενδέχεται να περιέχει μη ισοπεδωμένες (unflattened) ομάδες διαφάνειας που καταρρέουν παλαιότερους επεξεργαστές εικόνας ράστερ (RIP), ενσωματωμένη JavaScript που εγκυμονεί κινδύνους ασφαλείας, ή εικόνες χαμηλής ανάλυσης που θα φαίνονται απαίσιες όταν εκτυπωθούν στα 300 DPI.

Αυτή η διαδικασία επιθεώρησης ενός PDF πριν εισέλθει σε μια ροή παραγωγής είναι γνωστή ως Preflighting. Σε αυτό το άρθρο, θα εξερευνήσουμε πώς να κατασκευάσουμε ένα αυτοματοποιημένο εργαλείο preflight και ελέγχου κινδύνων στο Delphi, αξιοποιώντας τις δυνατότητες ανάλυσης χαμηλού επιπέδου του PDFium.

Βασικοί Έλεγχοι Preflight

Ένας τυπικός έλεγχος κινδύνου ελέγχει συνήθως για τα ακόλουθα στοιχεία μέσα σε ένα PDF:

  • Διαδραστικά Στοιχεία: Ενέργειες JavaScript, Ενέργειες εκκίνησης (Launch actions - εκτέλεση εξωτερικών προγραμμάτων) και AcroForms.
  • Μετρήσεις Πόρων: Παρουσία εικόνων χαμηλής ανάλυσης ή απουσία ενσωματωμένων γραμματοσειρών.
  • Κατάσταση Ασφαλείας: Κρυπτογράφηση εγγράφου, απαιτήσεις κωδικού πρόσβασης και δικαιώματα πρόσβασης (π.χ. απενεργοποιημένη εκτύπωση).
  • Πρότυπα Εγγράφου: Επικύρωση δεικτών συμμόρφωσης PDF/A ή PDF/X στα μεταδεδομένα του εγγράφου.

Εξαγωγή Μεταδεδομένων και Σχολίων PDF με το PDFium

Το PDFium παρέχει ένα ισχυρό C API που μπορεί να καταναλώσει το Delphi. Για να εκτελέσουμε έναν έλεγχο preflight, δεν αποδίδουμε απλώς τη σελίδα· διασχίζουμε το δέντρο αντικειμένων του PDF. Ας δούμε πώς να επαναλάβουμε τις σελίδες του εγγράφου και να επιθεωρήσουμε σχόλια για να βρούμε δυνητικά επικίνδυνες ενέργειες JavaScript.

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;

Έλεγχος Ανάλυσης Εικόνας

Ένα άλλο κρίσιμο βήμα preflight για ροές εργασίας εκτύπωσης είναι η διασφάλιση ότι καμία εικόνα δεν πέφτει κάτω από ένα συγκεκριμένο όριο DPI. Το PDFium σας επιτρέπει να εξάγετε αντικείμενα εικόνας απευθείας από τη ροή της σελίδας. Διαιρώντας τις διαστάσεις σε pixel της εξαγόμενης εικόνας με τις φυσικές διαστάσεις (σε στιγμές/points) που καταλαμβάνει στη σελίδα 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

Η κατασκευή μιας ισχυρής μηχανής preflight από το μηδέν χρησιμοποιώντας το ακατέργαστο C API του PDFium απαιτεί σημαντικό κώδικα boilerplate και βαθιά γνώση της προδιαγραφής PDF. Η χρήση ενός περιτυλίγματος (wrapper) απλοποιεί εξαιρετικά αυτή τη διαδικασία. Με ένα υψηλού επιπέδου περιτύλιγμα Delphi, μπορείτε να μετατρέψετε πολύπλοκες επαναλήψεις δεικτών (pointers) C σε καθαρό αντικειμενοστραφή (object-oriented) κώδικα.

Αυτοματοποιώντας τη φάση του preflight, αποτρέπετε τα προβληματικά αρχεία από το να μπλοκάρουν τη γραμμή παραγωγής σας, εξοικονομώντας χρόνο και πόρους απόδοσης (rendering resources).

Σημείωση: Οι προηγμένες δυνατότητες ανάλυσης και preflight PDF υποστηρίζονται πλήρως από το PDFium Component.