A vállalati nyomtatási, archiválási és dokumentummegfelelőségi munkafolyamatokban egy PDF fájlt nem lehet egyszerűen csak "renderelni". Auditálni kell. Egy PDF tartalmazhat nem lelapított (unflattened) átlátszósági csoportokat, amelyek összeomlasztják a régebbi raszteres képfeldolgozókat (RIP), beágyazott JavaScriptet, amely biztonsági kockázatot jelent, vagy alacsony felbontású képeket, amelyek szörnyen fognak kinézni 300 DPI-s nyomtatáskor.
Egy PDF-nek a termelési munkafolyamatba lépés előtti ellenőrzési folyamatát Preflight-nak (előzetes ellenőrzésnek) nevezzük. Ebben a cikkben megvizsgáljuk, hogyan építhető automatizált preflight és kockázatellenőrző eszköz Delphiben a PDFium alacsony szintű elemzési (parsing) képességeinek kihasználásával.
Főbb Preflight ellenőrzések
Egy szabványos kockázati audit általában a következő elemeket ellenőrzi a PDF-ben:
- Interaktív elemek: JavaScript műveletek, Indítási (Launch) műveletek (külső programok végrehajtása) és AcroFormok.
- Erőforrás metrikák: Alacsony felbontású képek jelenléte vagy hiányzó beágyazott betűtípusok.
- Biztonsági állapot: Dokumentumtitkosítás, jelszókövetelmények és hozzáférési jogosultságok (pl. nyomtatás letiltva).
- Dokumentumszabványok: A PDF/A vagy PDF/X megfelelőségi jelölők érvényesítése a dokumentum metaadataiban.
PDF metaadatok és annotációk kinyerése a PDFiummal
A PDFium egy robusztus C API-t biztosít, amelyet a Delphi képes használni. A preflight audit elvégzéséhez nem csak rendereljük az oldalt; végigjárjuk a PDF objektumfát. Nézzük meg, hogyan lehet végigiterálni a dokumentum oldalain, és megvizsgálni az annotációkat a potenciálisan kockázatos JavaScript műveletek megtalálása érdekében.
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;
Képfelbontás ellenőrzése
Egy másik kritikus preflight lépés a nyomtatási munkafolyamatok esetében annak biztosítása, hogy egyetlen kép se essen egy meghatározott DPI küszöbérték alá. A PDFium lehetővé teszi a képobjektumok közvetlen kinyerését az oldalstream-ből. Ha elosztja a kinyert kép pixelméretét a PDF-oldalon elfoglalt fizikai méretével (pontokban megadva), kiszámíthatja az effektív DPI-t.
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;
Integráció a PDFium Component-szal
Egy robusztus preflight motor nulláról történő felépítése a nyers PDFium C API használatával jelentős mennyiségű "boilerplate" kódot és a PDF specifikáció mélyreható ismeretét igényli. Egy wrapper (burkoló) használata ezt mérhetetlenül leegyszerűsíti. Egy magas szintű Delphi wrapperrel a bonyolult C pointer iterációkat tiszta, objektumorientált kóddá alakíthatja.
A preflight fázis automatizálásával megakadályozhatja, hogy a hibás fájlok eltömítsék a termelési folyamatot, időt és renderelési erőforrásokat takarítva meg ezáltal.
Megjegyzés: A fejlett PDF-elemzési és preflight képességeket teljes mértékben támogatja a PDFium Component.