Em fluxos de trabalho de impressão corporativa, arquivamento e conformidade de documentos, um arquivo PDF não pode simplesmente ser "renderizado". Ele deve ser auditado. Um PDF pode conter grupos de transparência não achatados que travam processadores de imagem raster (RIPs) legados, JavaScript incorporado que representa um risco de segurança ou imagens de baixa resolução que ficarão horríveis quando impressas em 300 DPI.
Este processo de inspecionar um PDF antes que ele entre em um fluxo de trabalho de produção é conhecido como Preflighting. Neste artigo, exploraremos como construir uma ferramenta automatizada de preflight e auditoria de risco no Delphi, aproveitando os recursos de análise de baixo nível do PDFium.
Principais Verificações de Preflight
Uma auditoria de risco padrão normalmente verifica os seguintes elementos em um PDF:
- Elementos Interativos: Ações de JavaScript, ações de Lançamento (execução de programas externos) e AcroForms.
- Métricas de Recursos: Presença de imagens de baixa resolução ou fontes incorporadas ausentes.
- Status de Segurança: Criptografia de documento, requisitos de senha e permissões de acesso (por exemplo, impressão desativada).
- Padrões de Documento: Validação de marcadores de conformidade PDF/A ou PDF/X nos metadados do documento.
Extraindo Metadados e Anotações de PDF com PDFium
O PDFium fornece uma API C robusta que o Delphi pode consumir. Para realizar uma auditoria de preflight, nós não renderizamos apenas a página; nós percorremos a árvore de objetos do PDF. Vejamos como iterar pelas páginas do documento e inspecionar anotações para encontrar ações JavaScript potencialmente perigosas.
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;
Verificando a Resolução da Imagem
Outra etapa crítica do preflight para fluxos de trabalho de impressão é garantir que nenhuma imagem caia abaixo de um limite de DPI específico. O PDFium permite que você extraia objetos de imagem diretamente do fluxo da página. Dividindo as dimensões de pixel da imagem extraída pelas dimensões físicas (em pontos) que ela ocupa na página PDF, você pode calcular o DPI efetivo.
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;
Integração com PDFium Component
Construir um mecanismo de preflight robusto do zero usando a API C pura do PDFium requer um código repetitivo significativo e um profundo conhecimento da especificação do PDF. O uso de um wrapper simplifica isso imensamente. Com um wrapper Delphi de alto nível, você pode transformar iterações complexas de ponteiros C em um código limpo orientado a objetos.
Ao automatizar a fase de preflight, você evita que arquivos ruins prejudiquem seu pipeline de produção, economizando tempo e recursos de renderização.
Nota: Análise avançada de PDF e recursos de preflight são totalmente suportados pelo PDFium Component.