Artikel Teknis

Laporan Preflight PDF Batch di Delphi dengan PDFium Component CLI

Alat preflight batch adalah program konsol tanpa jendela yang diarahkan ke folder berisi PDF, memvalidasi setiap file terhadap standar kesesuaian yang ditentukan, dan meninggalkan bukti yang dapat dibaca mesin tentang apa yang ditemukan. Tidak ada orang yang menunggunya. Alat ini berjalan pukul dua dini hari di bawah cron atau Windows Task Scheduler, atau sebagai gerbang dalam pipeline CI, dan orang berikutnya yang peduli dengan outputnya adalah penjadwal yang membaca exit code atau auditor yang membuka laporan beberapa minggu kemudian. Hal ini mengubah arti "benar". Engine preflight PDFium Component, pustaka PDF kode sumber untuk Delphi, C++Builder, dan Lazarus, membuat panggilan validasi itu sendiri hampir sepele. Pekerjaan yang menentukan apakah alat ini bernilai terletak di sekitar panggilan tersebut: profil mana yang diperiksa, apa yang dikatakan exit code kepada penjadwal, dan apakah laporan yang seharusnya menangkap kesalahan masih ada saat seseorang mencarinya

Kontrak: apa yang sebenarnya dapat dilihat oleh penjadwal

CI runner atau Windows Task Scheduler hanya melihat dua hal dari alat Anda: exit code dan file apa pun yang ditinggalkannya. Baris log, warna konsol, output progres: semua itu untuk manusia yang menonton secara langsung, dan pukul dua dini hari tidak ada yang menonton. Jadi tetapkan kosakata exit code sebelum menyentuh API, dan buat sesederhana mungkin:

  • 0: setiap file sesuai dengan setiap profil yang diminta
  • 1: setidaknya satu file menghasilkan temuan validasi
  • 2: alat itu sendiri gagal pada setidaknya satu file (input rusak, kunci, crash)

Perbedaan antara kode 1 dan 2 adalah yang sering diabaikan tim dan kemudian disesali. PDF yang rusak yang tidak bisa dibuka bukanlah kegagalan validasi. Gabungkan ke dalam kode 1 dan setumpuk pemindaian yang rusak akan muncul di dasbor Anda sebagai keruntuhan kesesuaian yang tiba-tiba, membuat seseorang mengejar regresi standar yang tidak pernah terjadi, padahal cerita sebenarnya adalah pemindai yang rusak di hulu

Dua item lagi termasuk dalam kontrak. Yang pertama adalah batas waktu per file. PDF yang patologis, ribuan halaman dengan struktur objek yang sangat bersarang, dapat menahan satu jalur validasi selama beberapa menit, dan jendela malam tidak memiliki kesabaran untuk itu. Hentikan pekerjaan file tersebut pada batas waktu, hitung sebagai kegagalan alat, dan teruskan batch. Yang kedua adalah direktori karantina: pindahkan setiap input yang kehabisan waktu atau tidak bisa dibuka ke samping, bukan dibiarkan di tempat. Setelah beberapa bulan direktori tersebut secara diam-diam mengumpulkan dokumen terburuk yang dikirim pelanggan nyata Anda, dan korpus tersebut lebih berharga bagi pengujian rilis daripada sampel sintetis mana pun yang bisa Anda tulis sendiri

Memilih standar, dan mengapa tingkat kesesuaian penting

Enumerasi TPdfPreflightStandard mencakup keluarga yang muncul dalam praktik: ppsPdfA untuk kesesuaian pengarsipan ISO 19005, ppsPdfUa untuk aksesibilitas ISO 14289, ppsPdfX untuk pertukaran cetak, ditambah ppsPdfE, ppsPdfR, dan ppsPdfVT untuk pekerjaan rekayasa, raster, dan data variabel. Dalam satu keluarga, engine membaca tingkat kesesuaian yang diklaim dokumen dan melaporkannya per standar dalam ConformanceName hasil. Menyebutkan keluarga saja jarang cukup, karena tingkatan adalah tempat perbedaan nyata berada. PDF/A-2b menjanjikan reprodusibilitas visual dan tidak lebih. PDF/A-3a menambahkan permintaan untuk penandaan struktur logis dan mengizinkan file sumber tertanam, yang merupakan standar yang jauh lebih sulit dipenuhi untuk materi yang dipindai yang tidak memiliki pohon tag sama sekali. Salah dalam arah mana pun dan batch berbohong kepada Anda. Jika kebijakan penyimpanan Anda sebenarnya menginginkan PDF/A-2b tetapi Anda gagalkan file karena tag struktur yang hilang, laporan akan penuh dengan temuan yang tidak akan pernah diperbaiki siapa pun. Terima label PDF/A apa pun tanpa memeriksa tingkat dan Anda menyetujui dokumen yang memenuhi standar yang lebih lemah dari yang Anda janjikan. Mandat aksesibilitas dari pembeli pemerintah semakin banyak menumpuk PDF/UA di atas semua ini, yang tidak menambah biaya pada jalannya karena BuildPdfPreflightReport (dari unit FPdfPreflightReport) mengambil satu set standar:

Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);

Satu panggilan mengevaluasi kedua standar dan mengembalikan satu catatan laporan yang dikonsolidasikan

Mengapa daftar temuan kosong bukan berarti lolos

Laporan menghitung temuan per standar, dan daftar masalah yang kosong hanya berarti "tidak ada masalah yang ditemukan dalam standar yang benar-benar dijalankan." Itu adalah klaim yang lebih sempit dari "file sesuai dengan standar yang Anda pedulikan," dan celah antara keduanya adalah tempat preflight batch diam-diam membusuk. Kesalahan ketik konfigurasi yang menghapus ppsPdfA dari set menghasilkan daftar masalah kosong yang persis sama dengan file yang benar-benar bersih. Jadi perlakukan keheningan sebagai hal yang mencurigakan. Telusuri Report.Results dan tegaskan dua hal untuk setiap standar yang ingin Anda periksa: bahwa entri hasil untuk itu ada sama sekali, dan bahwa flag IsCompliant-nya, didukung oleh Status = pfsPass, bernilai benar. Pekerjaan malam yang menyamakan "tidak ada temuan" dengan "siap arsip" tanpa pernah mengonfirmasi standar mana yang dievaluasi adalah cara klasik folder berisi file yang tidak sesuai berlalu selama berbulan-bulan, sampai auditor eksternal membuka satu dengan veraPDF dan seluruh arsip dipertanyakan

Perangkap kedua tersembunyi dalam apa sebenarnya sebuah temuan. Setiap TPdfPreflightIssue membawa Code, Category, Description, dan Recommendation, dan menyebutkan aturan yang dilanggar, bukan halaman atau objek. Itu adalah pilihan desain dengan konsekuensi untuk loop umpan balik. Laporan memberi tahu tim produksi kelas cacat apa yang ada, font yang tidak tertanam atau pengidentifikasi XMP yang hilang, dan menemukan objek yang menyinggung secara spesifik adalah pekerjaan alat perbaikan di hilir, bukan validator. Bangun konsumen laporan Anda terhadap nilai Code yang stabil, jangan pernah terhadap teks deskripsi yang dapat dibaca manusia, yang bisa diubah antar rilis tanpa peringatan

File laporan untuk mesin dan untuk orang yang bertugas

Catatan laporan menulis temuan yang sama dalam lima format: SaveJsonToFile, SaveCsvToFile, SaveHtmlToFile, SaveTextToFile, dan SaveMarkdownToFile, masing-masing dengan fungsi gaya ToJson yang sesuai ketika Anda ingin string dalam memori bukan di disk. Tahan dorongan untuk memilih satu. Tulis JSON untuk pipeline, sehingga CI dapat melampirkannya ke catatan pekerjaan dan mengurai kode masalah dan status per standar tanpa mengikis teks. Tulis HTML untuk manusia yang dipanggil, karena itu terbuka di browser mana pun tanpa alat sama sekali. Keduanya bersama-sama menghabiskan satu baris ekstra per file dan membebaskan insinyur on-call Anda dari tugas terburuk tunggal dalam pemrosesan batch, yaitu merekayasa balik blob JSON mentah pukul dua dini hari untuk mengetahui file mana yang rusak. Satu disiplin lebih penting dari pilihan format: turunkan setiap nama laporan dari nama file input, jangan pernah dari cap waktu, atau dua jalankan paralel akan mencampur laporan yang tidak dapat lagi Anda cocokkan kembali ke input mereka

Ambang keparahan termasuk dalam konfigurasi bukan dalam kode. Anotasi tanpa deskripsi alternatif adalah kegagalan keras untuk portal pengiriman PDF/UA dan catatan yang dapat diabaikan untuk arsip internal, namun itu adalah temuan yang identik di keduanya. Tampilkan level fail-on per profil sehingga kebijakan dapat berubah tanpa kompilasi ulang, dan cap level yang berlaku ke dalam ringkasan pekerjaan itu sendiri. Kuartal berikutnya tidak ada yang akan ingat ambang mana yang digunakan batch Oktober lalu, dan ringkasan adalah satu-satunya tempat memori itu bertahan

Mengisolasi file agar satu PDF buruk tidak menenggelamkan batch

procedure RunPreflightBatch(const InputDir, ReportDir: string;
  out FilesWithFindings, ToolFailures: Integer);
var
  SR: TSearchRec;
  Pdf: TPdf;
  Report: TPdfPreflightReport;
begin
  FilesWithFindings := 0;
  ToolFailures := 0;
  if FindFirst(InputDir + '*.pdf', faAnyFile, SR) = 0 then
  try
    repeat
      Pdf := TPdf.Create(nil);   // fresh instance per file: no state bleed
      try
        try
          Pdf.FileName := InputDir + SR.Name;
          Pdf.Active := True;
          if not Pdf.Active then  // load failures are silent, not raised
            raise EPdfError.Create('Cannot open ' + SR.Name);
          Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
          Report.SaveJsonToFile(ReportDir + ChangeFileExt(SR.Name, '.json'));
          Report.SaveHtmlToFile(ReportDir + ChangeFileExt(SR.Name, '.html'));
          if Report.TotalIssueCount > 0 then
            Inc(FilesWithFindings);
        except
          on E: Exception do
          begin
            Inc(ToolFailures);   // exit-code-2 territory, not a validation verdict
            WriteLn(ErrOutput, SR.Name + ': ' + E.Message);
          end;
        end;
      finally
        Pdf.Free;
      end;
    until FindNext(SR) <> 0;
  finally
    FindClose(SR);
  end;
end;

Tiga pilihan yang disengaja ada dalam loop tersebut. TPdf baru per file menjamin bahwa satu dokumen yang merusak status engine tidak dapat meracuni file yang mengikutinya. Pemeriksaan Active yang eksplisit mendapat tempatnya karena Active := True menelan kesalahan pemuatan alih-alih memunculkannya; hilangkan penjaga dan file yang terpotong melayang ke dalam panggilan validasi sebelum gagal di suatu tempat di hilir dengan pesan yang menyesatkan. try..except dalam tinggal di dalam lingkup per file dengan sengaja, sehingga satu pengecualian menambah penghitung kegagalan dan loop berlanjut. Anda ingin laporan bersih untuk 4.999 file yang baik bahkan ketika file ke-5.000 rusak. Dan kedua format laporan ditulis ke disk sebelum putusan dihitung, yang berarti bukti tetap ada bahkan jika bug di kemudian hari dalam logika ringkasan salah menghitung

Pemetaan exit code kemudian menyusut menjadi beberapa baris dalam file proyek:

begin
  RunPreflightBatch(ParamStr(1), ParamStr(2), Findings, Failures);
  if Failures > 0 then
    Halt(2)
  else if Findings > 0 then
    Halt(1);
  // falling through exits with 0: every file conformed
end.

Apa yang tidak dapat dilakukan preflight untuk Anda

Engine mendeteksi; tidak memperbaiki. Temuan tentang font yang tidak tertanam atau ruang warna yang bergantung pada perangkat adalah perintah kerja bagi siapa pun yang menghasilkan file, dan validator tidak memiliki cara untuk mempatchnya di tempat. Jadi rencanakan loop umpan balik dengan sengaja. Laporan harus sampai di tempat tim produksi benar-benar membacanya, atau temuan yang sama akan muncul kembali setiap malam sampai seseorang akhirnya bertanya mengapa tingkat kesesuaian tidak pernah meningkat. Ada juga baiknya untuk mencocokkan sampel putusan dengan validator independen, veraPDF untuk PDF/A atau preflight Acrobat untuk PDF/X, sebelum auditor eksternal mencocokkannya untuk Anda. Ketika dua engine tidak sepakat pada file pelanggan nyata, dokumen itu bukan gangguan; itu persis kasus regresi yang hilang dari pengujian rilis Anda. Simpan, beri nama, dan jalankan pada setiap build

Satu pasangan lagi layak diketahui. Engine validasi yang sama mendorong pemeriksaan interaktif dalam UI tinjauan, sehingga CLI tanpa kepala ini dan meja kerja tinjauan asupan PDF yang menghadap analis dapat berbagi satu kosakata validasi alih-alih menyimpang seiring waktu. Dan karena [ppsPdfA, ppsPdfUa] mengevaluasi aksesibilitas dalam satu tahap yang sama, sisi PDF/UA dari batch sejalan dengan pekerjaan sisi viewer seperti membangun pembaca PDF yang dapat diakses di Delphi. Profil, format laporan, dan API preflight lengkap didokumentasikan di halaman produk untuk PDFium Component