Technical Article

Contrôle en amont automatisé des PDF et audit des risques avec PDFium

Dans les flux de travail d'impression d'entreprise, d'archivage et de conformité des documents, un fichier PDF ne peut pas simplement être "rendu". Il doit être audité. Un PDF peut contenir des groupes de transparence non aplatis qui font planter les anciens processeurs d'images raster (RIP), du JavaScript intégré qui pose un risque de sécurité, ou des images basse résolution qui auront un rendu terrible une fois imprimées à 300 DPI.

Ce processus d'inspection d'un PDF avant qu'il n'entre dans un flux de production est connu sous le nom de Contrôle en amont (Preflighting). Dans cet article, nous allons explorer comment créer un outil automatisé de contrôle en amont et d'audit des risques dans Delphi en exploitant les capacités d'analyse de bas niveau de PDFium.

Contrôles en amont clés

Un audit des risques standard vérifie généralement les éléments suivants dans un PDF :

  • Éléments interactifs : Actions JavaScript, actions de lancement (Launch actions, exécution de programmes externes) et AcroForms.
  • Métriques de ressources : Présence d'images basse résolution ou polices intégrées manquantes.
  • État de la sécurité : Chiffrement du document, exigences de mot de passe et autorisations d'accès (par exemple, impression désactivée).
  • Normes de document : Validation des marqueurs de conformité PDF/A ou PDF/X dans les métadonnées du document.

Extraction des métadonnées et des annotations PDF avec PDFium

PDFium fournit une API C robuste que Delphi peut consommer. Pour effectuer un audit de contrôle en amont, nous ne nous contentons pas de rendre la page ; nous parcourons l'arborescence des objets PDF. Voyons comment itérer à travers les pages du document et inspecter les annotations pour trouver des actions JavaScript potentiellement risquées.

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;

Vérification de la résolution d'image

Une autre étape critique du contrôle en amont pour les flux de travail d'impression consiste à s'assurer qu'aucune image ne tombe en dessous d'un seuil DPI spécifique. PDFium vous permet d'extraire des objets image directement à partir du flux de page. En divisant les dimensions en pixels de l'image extraite par les dimensions physiques (en points) qu'elle occupe sur la page PDF, vous pouvez calculer le DPI effectif.

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;

Intégration avec PDFium Component

Construire un moteur de contrôle en amont robuste à partir de zéro à l'aide de l'API C brute de PDFium nécessite un code standard (boilerplate) important et une connaissance approfondie de la spécification PDF. L'utilisation d'un wrapper simplifie énormément cela. Avec un wrapper Delphi de haut niveau, vous pouvez transformer des itérations de pointeurs C complexes en un code propre orienté objet.

En automatisant la phase de contrôle en amont, vous évitez que de mauvais fichiers ne bloquent votre pipeline de production, ce qui permet d'économiser du temps et des ressources de rendu.

Remarque : L'analyse avancée des PDF et les fonctionnalités de contrôle en amont sont entièrement prises en charge par le composant VCL PDFium Component.

\n