Nei flussi di lavoro aziendali di stampa, archiviazione e conformità dei documenti, un file PDF non può essere semplicemente "renderizzato". Deve essere verificato. Un PDF potrebbe contenere gruppi di trasparenze non convertite che mandano in crash i vecchi Raster Image Processor (RIP), JavaScript incorporato che rappresenta un rischio per la sicurezza, o immagini a bassa risoluzione che appariranno scadenti se stampate a 300 DPI.
Questo processo di ispezione di un PDF prima che entri in un flusso di lavoro di produzione è noto come Preflight. In questo articolo, esploreremo come creare uno strumento automatizzato di preflight e audit dei rischi in Delphi sfruttando le capacità di parsing a basso livello di PDFium.
Controlli di Preflight Chiave
Un tipico audit dei rischi controlla i seguenti elementi all'interno di un PDF:
- Elementi Interattivi: azioni JavaScript, azioni di Avvio (esecuzione di programmi esterni) e AcroForm.
- Metriche delle Risorse: presenza di immagini a bassa risoluzione o font incorporati mancanti.
- Stato di Sicurezza: crittografia del documento, requisiti della password e permessi di accesso (ad es. stampa disabilitata).
- Standard del Documento: convalida dei marcatori di conformità PDF/A o PDF/X nei metadati del documento.
Estrarre Metadati e Annotazioni PDF con PDFium
PDFium fornisce un'API C robusta che può essere utilizzata da Delphi. Per eseguire un audit di preflight, non ci limitiamo a renderizzare la pagina; esaminiamo l'albero degli oggetti PDF. Vediamo come scorrere le pagine del documento e ispezionare le annotazioni per trovare azioni JavaScript potenzialmente rischiose.
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;
Controllo della Risoluzione delle Immagini
Un altro passaggio critico del preflight per i flussi di stampa è garantire che nessuna immagine scenda al di sotto di una soglia DPI specifica. PDFium consente di estrarre gli oggetti immagine direttamente dal flusso della pagina. Dividendo le dimensioni in pixel dell'immagine estratta per le dimensioni fisiche (in punti) che occupa sulla pagina PDF, è possibile calcolare i DPI effettivi.
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;
Integrazione con PDFium Component
Costruire da zero un motore di preflight robusto utilizzando l'API C nativa di PDFium richiede una notevole quantità di codice boilerplate e una profonda conoscenza delle specifiche PDF. L'uso di un wrapper semplifica immensamente tutto questo. Con un wrapper Delphi di alto livello, puoi trasformare le complesse iterazioni dei puntatori C in codice pulito orientato agli oggetti.
Automatizzando la fase di preflight, si evita che i file non validi intasino la pipeline di produzione, risparmiando tempo e risorse di rendering.
Nota: le funzionalità avanzate di parsing e preflight dei PDF sono pienamente supportate dal Componente VCL PDFium Component.