Technical Article

Автоматизирана предварителна проверка на PDF и одит на риска с PDFium

В работните процеси за корпоративен печат, архивиране и съответствие на документи (compliance), PDF файлът не може просто да бъде "изобразен". Той трябва да бъде одитиран. Даден PDF файл може да съдържа неоптимизирани групи за прозрачност, които сриват стари процесори за растерни изображения (RIP), вграден JavaScript, който представлява риск за сигурността, или изображения с ниска резолюция, които ще изглеждат ужасно при печат на 300 DPI.

Този процес на проверка на PDF файл преди влизането му в производствен работен процес е известен като Предварителна проверка (Preflighting). В тази статия ще разгледаме как да изградим автоматизиран инструмент за предварителна проверка и одит на риска в Delphi, възползвайки се от възможностите за синтактичен анализ на ниско ниво на PDFium.

Ключови предварителни проверки

Стандартният одит на риска обикновено проверява за следните елементи в рамките на PDF:

  • Интерактивни елементи: Действия с JavaScript, действия за стартиране (Launch actions - изпълнение на външни програми) и AcroForms.
  • Метрики на ресурсите: Наличие на изображения с ниска разделителна способност или липсващи вградени шрифтове.
  • Състояние на сигурността: Криптиране на документа, изисквания за парола и разрешения за достъп (например забранен печат).
  • Стандарти за документи: Валидиране на маркерите за съответствие с PDF/A или PDF/X в метаданните на документа.

Извличане на метаданни и анотации от PDF с PDFium

PDFium предоставя стабилен C API, който 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.