В корпоративных рабочих процессах печати, архивирования и обеспечения соответствия документов требованиям PDF-файл нельзя просто «отрендерить». Он должен пройти аудит. PDF-файл может содержать несведенные группы прозрачности, которые приводят к сбою устаревших растровых процессоров изображений (RIP), встроенный JavaScript, представляющий угрозу безопасности, или изображения с низким разрешением, которые будут ужасно выглядеть при печати с разрешением 300 DPI.
Этот процесс проверки PDF перед его попаданием в производственный рабочий процесс известен как предпечатная проверка (Preflighting). В этой статье мы рассмотрим, как создать автоматизированный инструмент предпечатной проверки и аудита рисков в Delphi, используя возможности низкоуровневого парсинга PDFium.
Ключевые предпечатные проверки
Стандартный аудит рисков обычно проверяет наличие следующих элементов в PDF:
- Интерактивные элементы: действия JavaScript, действия запуска Launch (выполнение внешних программ) и формы AcroForms.
- Метрики ресурсов: наличие изображений с низким разрешением или отсутствующие встроенные шрифты.
- Статус безопасности: шифрование документа, требования к паролю и разрешения доступа (например, печать отключена).
- Стандарты документов: проверка маркеров соответствия PDF/A или PDF/X в метаданных документа.
Извлечение метаданных и аннотаций PDF с помощью PDFium
PDFium предоставляет надежный API C, который может использовать Delphi. Чтобы выполнить аудит предпечатной проверки, мы не просто рендерим страницу, мы обходим дерево объектов PDF. Давайте посмотрим, как выполнять итерацию по страницам документа и проверять аннотации для поиска потенциально опасных действий 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;
Проверка разрешения изображения
Еще одним критическим шагом предпечатной проверки для рабочих процессов печати является обеспечение того, чтобы ни одно изображение не было ниже определенного порога DPI. PDFium позволяет извлекать объекты изображений непосредственно из потока страницы. Разделив размеры в пикселях извлеченного изображения на физические размеры (в пунктах), которые оно занимает на странице PDF, вы можете рассчитать эффективный 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;
Интеграция с PDFium Component
Создание надежного механизма предпечатной проверки с нуля с использованием исходного C API PDFium требует значительного количества шаблонного кода и глубоких знаний спецификации PDF. Использование обертки (wrapper) невероятно упрощает эту задачу. С помощью высокоуровневой обертки Delphi вы можете превратить сложные итерации указателей C в чистый объектно-ориентированный код.
Автоматизируя этап предпечатной проверки, вы предотвращаете засорение вашего производственного конвейера плохими файлами, экономя время и ресурсы рендеринга.
Примечание. Расширенные возможности парсинга и предпечатной проверки PDF полностью поддерживаются в компоненте PDFium Component VCL.