Trong các quy trình in ấn doanh nghiệp, lưu trữ và tuân thủ tài liệu, một tệp PDF không thể chỉ đơn giản là được "kết xuất" (rendered). Nó phải được kiểm toán. Một tệp PDF có thể chứa các nhóm độ trong suốt (transparency groups) chưa được làm phẳng gây treo các bộ xử lý hình ảnh raster (RIP) cũ, chứa JavaScript nhúng gây rủi ro bảo mật hoặc hình ảnh độ phân giải thấp sẽ trông rất tệ khi in ở 300 DPI.
Quá trình kiểm tra tệp PDF trước khi nó đi vào quy trình sản xuất được gọi là Kiểm tra trước (Preflighting). Trong bài viết này, chúng ta sẽ khám phá cách xây dựng một công cụ kiểm toán rủi ro và kiểm tra trước tự động trong Delphi bằng cách khai thác khả năng phân tích cú pháp cấp thấp của PDFium.
Các bước kiểm tra trước chính
Một cuộc kiểm toán rủi ro tiêu chuẩn thường kiểm tra các yếu tố sau trong một tệp PDF:
- Các phần tử tương tác: Các hành động JavaScript, hành động Launch (khởi chạy chương trình bên ngoài) và AcroForms.
- Số liệu tài nguyên: Sự hiện diện của hình ảnh độ phân giải thấp hoặc thiếu phông chữ nhúng.
- Trạng thái bảo mật: Mã hóa tài liệu, yêu cầu mật khẩu và quyền truy cập (ví dụ: vô hiệu hóa tính năng in).
- Tiêu chuẩn tài liệu: Xác thực các dấu hiệu tuân thủ PDF/A hoặc PDF/X trong siêu dữ liệu tài liệu.
Trích xuất siêu dữ liệu PDF và chú thích với PDFium
PDFium cung cấp một API C mạnh mẽ mà Delphi có thể sử dụng. Để thực hiện kiểm toán trước, chúng ta không chỉ kết xuất trang; chúng ta duyệt qua cây đối tượng PDF. Hãy xem cách lặp qua các trang tài liệu và kiểm tra các chú thích (annotations) để tìm các hành động JavaScript có khả năng gây rủi ro.
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;
Kiểm tra độ phân giải hình ảnh
Một bước kiểm tra trước quan trọng khác đối với quy trình in là đảm bảo không có hình ảnh nào giảm xuống dưới một ngưỡng DPI cụ thể. PDFium cho phép bạn trích xuất trực tiếp các đối tượng hình ảnh từ luồng trang. Bằng cách chia kích thước pixel của hình ảnh được trích xuất cho kích thước vật lý (tính bằng điểm) mà nó chiếm trên trang PDF, bạn có thể tính toán DPI hiệu quả.
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;
Tích hợp với PDFium Component
Xây dựng một công cụ kiểm tra trước mạnh mẽ từ đầu bằng API C của PDFium thô đòi hỏi mã nguyên mẫu (boilerplate) đáng kể và kiến thức sâu về thông số kỹ thuật PDF. Việc sử dụng một trình bọc (wrapper) đơn giản hóa điều này rất nhiều. Với một trình bọc Delphi bậc cao, bạn có thể biến các vòng lặp con trỏ C phức tạp thành mã hướng đối tượng rõ ràng.
Bằng cách tự động hóa giai đoạn kiểm tra trước, bạn ngăn chặn các tệp xấu làm tắc nghẽn quy trình sản xuất của mình, tiết kiệm cả thời gian và tài nguyên kết xuất.
Lưu ý: Các khả năng kiểm tra trước và phân tích cú pháp PDF nâng cao được hỗ trợ đầy đủ bởi PDFium Component.