Kurumsal yazdırma, arşivleme ve belge uyumluluğu iş akışlarında bir PDF dosyası basitçe "işlenemez" (render). Denetlenmesi (audit) gerekir. Bir PDF, eski raster görüntü işlemcilerini (RIP'ler) çökerten düzleştirilmemiş saydamlık grupları (unflattened transparency groups), güvenlik riski oluşturan gömülü JavaScript veya 300 DPI'da yazdırıldığında korkunç görünecek düşük çözünürlüklü resimler içerebilir.
Bir PDF'i üretim iş akışına girmeden önce inceleme sürecine Uçuş Öncesi Kontrol (Preflighting) denir. Bu makalede, PDFium'un düşük seviyeli ayrıştırma (parsing) yeteneklerinden yararlanarak Delphi'de otomatik bir uçuş öncesi kontrol ve risk denetimi aracının nasıl oluşturulacağını keşfedeceğiz.
Temel Uçuş Öncesi Kontroller
Standart bir risk denetimi tipik olarak bir PDF içindeki aşağıdaki unsurları kontrol eder:
- Etkileşimli Unsurlar: JavaScript eylemleri, Başlatma (Launch) eylemleri (harici programları yürütme) ve AcroForm'lar.
- Kaynak Metrikleri: Düşük çözünürlüklü resimlerin veya eksik gömülü yazı tiplerinin varlığı.
- Güvenlik Durumu: Belge şifrelemesi, parola gereksinimleri ve erişim izinleri (örneğin, yazdırmanın devre dışı bırakılması).
- Belge Standartları: Belge meta verilerinde PDF/A veya PDF/X uygunluk işaretçilerinin doğrulanması.
PDFium ile PDF Meta Verilerini ve Ek Açıklamaları (Annotations) Çıkarma
PDFium, Delphi'nin tüketebileceği sağlam bir C API'si sağlar. Bir uçuş öncesi denetimi gerçekleştirmek için yalnızca sayfayı işlemeyiz (render); PDF nesne ağacında geziniriz. Olası riskli JavaScript eylemlerini bulmak için belge sayfalarında nasıl yineleneceğimize ve ek açıklamaları (annotations) nasıl inceleyeceğimize bakalım.
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;
Görüntü Çözünürlüğünü Kontrol Etme
Yazdırma iş akışları için bir diğer kritik uçuş öncesi adımı, hiçbir görüntünün belirli bir DPI eşiğinin altına düşmemesini sağlamaktır. PDFium, görüntü nesnelerini (image objects) doğrudan sayfa akışından çıkarmanıza olanak tanır. Çıkarılan görüntünün piksel boyutlarını, PDF sayfasında kapladığı fiziksel boyutlara (point cinsinden) bölerek etkili DPI'ı hesaplayabilirsiniz.
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;
PDFium Component ile Entegrasyon
Ham PDFium C API'sini kullanarak sıfırdan sağlam bir uçuş öncesi (preflight) motoru oluşturmak, önemli miktarda basmakalıp kod (boilerplate code) ve PDF spesifikasyonu hakkında derin bilgi gerektirir. Bir sarmalayıcı (wrapper) kullanmak bunu büyük ölçüde basitleştirir. Üst düzey bir Delphi sarmalayıcısı ile karmaşık C işaretçi (pointer) yinelemelerini temiz, nesne yönelimli koda dönüştürebilirsiniz.
Uçuş öncesi aşamasını otomatikleştirerek, hatalı dosyaların üretim hattınızı tıkamasını önler, hem zamandan hem de işleme (rendering) kaynaklarından tasarruf edersiniz.
Not: Gelişmiş PDF ayrıştırma ve uçuş öncesi kontrol yetenekleri PDFium Component tarafından tam olarak desteklenmektedir.