Technical Article

Preflight PDF Otomatis dan Audit Risiko dengan PDFium

Dalam alur kerja pencetakan perusahaan, pengarsipan, dan kepatuhan dokumen, file PDF tidak bisa begitu saja "dirender." File tersebut harus diaudit. Sebuah PDF mungkin berisi grup transparansi yang tidak diratakan yang dapat membuat prosesor gambar raster (RIP) lama menjadi crash, JavaScript tertanam yang menimbulkan risiko keamanan, atau gambar resolusi rendah yang akan terlihat sangat buruk saat dicetak pada 300 DPI.

Proses pemeriksaan PDF sebelum memasuki alur kerja produksi ini dikenal sebagai Preflighting. Dalam artikel ini, kita akan menjelajahi cara membangun alat audit risiko dan preflight otomatis di Delphi dengan memanfaatkan kemampuan pem-parsing-an tingkat rendah PDFium.

Pemeriksaan Preflight Utama

Audit risiko standar biasanya memeriksa elemen-elemen berikut di dalam PDF:

  • Elemen Interaktif: Tindakan JavaScript, tindakan Peluncuran (mengeksekusi program eksternal), dan AcroForms.
  • Metrik Sumber Daya: Kehadiran gambar resolusi rendah atau font tertanam yang hilang.
  • Status Keamanan: Enkripsi dokumen, persyaratan kata sandi, dan izin akses (misalnya, pencetakan dinonaktifkan).
  • Standar Dokumen: Validasi penanda kesesuaian PDF/A atau PDF/X dalam metadata dokumen.

Mengekstrak Metadata dan Anotasi PDF dengan PDFium

PDFium menyediakan API C yang tangguh yang dapat dipakai oleh Delphi. Untuk melakukan audit preflight, kita tidak hanya merender halaman; kita berjalan menelusuri pohon objek PDF. Mari kita lihat cara menelusuri halaman dokumen dan memeriksa anotasi untuk menemukan tindakan JavaScript yang berpotensi berisiko.

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;

Memeriksa Resolusi Gambar

Langkah preflight penting lainnya untuk alur kerja cetak adalah memastikan tidak ada gambar yang berada di bawah ambang batas DPI tertentu. PDFium memungkinkan Anda untuk mengekstrak objek gambar langsung dari aliran halaman. Dengan membagi dimensi piksel gambar yang diekstrak dengan dimensi fisiknya (dalam satuan point) yang ditempatinya di halaman PDF, Anda dapat menghitung DPI efektif.

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;

Mengintegrasikan dengan PDFium Component

Membangun mesin preflight yang tangguh dari awal menggunakan API C PDFium mentah memerlukan kode boilerplate yang signifikan dan pengetahuan mendalam tentang spesifikasi PDF. Menggunakan pembungkus sangat menyederhanakan hal ini. Dengan pembungkus Delphi tingkat tinggi, Anda dapat mengubah iterasi pointer C yang kompleks menjadi kode berorientasi objek yang bersih.

Dengan mengotomatiskan fase preflight, Anda mencegah file buruk mengganggu jalur produksi Anda, menghemat waktu dan sumber daya perenderan.

Catatan: Pem-parsing-an PDF lanjutan dan kemampuan preflight didukung penuh oleh Komponen VCL PDFium Component.