W firmowych przepływach pracy związanych z drukowaniem, archiwizacją i zgodnością dokumentów, plik PDF nie może być po prostu "wyrenderowany". Musi zostać poddany audytowi. Plik PDF może zawierać niespłaszczone grupy przezroczystości, które powodują awarie starszych procesorów obrazów rastrowych (RIP), osadzony kod JavaScript stanowiący zagrożenie dla bezpieczeństwa lub obrazy o niskiej rozdzielczości, które będą wyglądać fatalnie po wydrukowaniu w 300 DPI.
Proces inspekcji pliku PDF przed jego wejściem do przepływu produkcyjnego jest znany jako Preflight (kontrola wstępna). W tym artykule zbadamy, jak zbudować zautomatyzowane narzędzie do weryfikacji wstępnej i audytu ryzyka w Delphi, wykorzystując niskopoziomowe możliwości parsowania silnika PDFium.
Kluczowe testy kontroli wstępnej
Standardowy audyt ryzyka zazwyczaj sprawdza następujące elementy w pliku PDF:
- Elementy interaktywne: Akcje JavaScript, akcje Launch (uruchamianie programów zewnętrznych) oraz formularze AcroForms.
- Metryki zasobów: Obecność obrazów o niskiej rozdzielczości lub brak osadzonych czcionek.
- Stan bezpieczeństwa: Szyfrowanie dokumentu, wymagania dotyczące haseł i uprawnienia dostępu (np. wyłączone drukowanie).
- Standardy dokumentów: Walidacja znaczników zgodności PDF/A lub PDF/X w metadanych dokumentu.
Ekstrakcja metadanych PDF i adnotacji przy użyciu PDFium
PDFium udostępnia solidne C API, które może być używane w Delphi. Aby przeprowadzić audyt preflight, nie tylko renderujemy stronę; przechodzimy przez całe drzewo obiektów PDF. Zobaczmy, jak iterować po stronach dokumentu i kontrolować adnotacje, aby znaleźć potencjalnie niebezpieczne akcje 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;
Sprawdzanie rozdzielczości obrazu
Innym krytycznym krokiem przed produkcją dla przepływów druku jest upewnienie się, że żaden obraz nie spada poniżej określonego progu DPI. PDFium pozwala wyodrębnić obiekty obrazów bezpośrednio ze strumienia strony. Dzieląc wymiary pikseli wyodrębnionego obrazu przez wymiary fizyczne (w punktach), jakie zajmuje on na stronie PDF, można obliczyć efektywne 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;
Integracja z PDFium Component
Budowa solidnego silnika preflight od podstaw przy użyciu surowego API C PDFium wymaga ogromnej ilości kodu powtarzalnego i głębokiej znajomości specyfikacji PDF. Użycie wrappera (opakowania) niezwykle to upraszcza. Dzięki wysokopoziomowemu wrapperowi w Delphi możesz zamienić złożone iteracje na wskaźnikach C w czysty, zorientowany obiektowo kod.
Automatyzując fazę wstępną, zapobiegasz przedostawaniu się złych plików do Twojego potoku produkcyjnego, oszczędzając czas i zasoby na renderowanie.
Uwaga: Zaawansowane funkcje parsowania PDF i preflight są w pełni obsługiwane przez PDFium Component.