バッチプリフライトツールはウィンドウのないコンソールプログラムです。PDFフォルダを指定すると、指定した適合規格に対して各ファイルを検証し、見つかった内容を機械可読な形式で記録します。誰もリアルタイムで監視する必要はありません。cronやWindowsタスクスケジューラーによって深夜2時に動作し、その出力を最初に必要とするのは終了コードを読むスケジューラーか、数週間後にレポートを開く監査担当者です。これにより「正しい」の意味が変わります。PDFium Componentのプリフライトエンジンは、Delphi、C++Builder、LazarusのソースコードPDFライブラリであり、検証の呼び出し自体をほぼ簡単にします。ツールが真に役立つかを決めるのは、その呼び出しを囲む部分です。すなわち、どのプロファイルを検証したか、終了コードがスケジューラーに何を伝えるか、そして誰かが探すときに問題を捉えていたはずのレポートがまだ存在するかどうかです
契約:スケジューラーが実際に見られるもの
CIランナーまたはWindowsタスクスケジューラーは、ツールから2つのものだけを確認します。終了コードと、残されたファイルです。ログ行、コンソールの色、進行状況の出力、それらはすべて生放送で見ている人間のためのものであり、深夜2時には誰もいません。そのため、APIに触れる前に終了コードの語彙を固定し、シンプルに保ってください:
0:すべてのファイルがすべての要求されたプロファイルに適合した1:少なくとも1つのファイルで検証の指摘事項が発生した2:少なくとも1つのファイルでツール自体が失敗した(破損入力、ロック、クラッシュ)
コード1と2の区別は、チームがスキップして後で後悔するものです。開けない破損したPDFは検証の失敗ではありません。コード1に折り込むと、大量の破損したスキャンが適合性の崩壊としてダッシュボードに突如現れ、誰かが実際には発生しなかった規格上のリグレッションを追いかけることになります。本当の原因は上流の壊れたスキャナーです
契約にはさらに2つの事項が必要です。1つ目はファイルごとのタイムアウトです。病的なPDF、数千ページで深くネストされたオブジェクト構造を持つものは、1回の検証パスを数分間保留できますが、夜間ウィンドウにはその余裕はありません。期限でそのファイルのジョブを強制終了し、ツール障害としてカウントして、バッチを続行させてください。2つ目は検疫ディレクトリです。タイムアウトまたは開けない入力ファイルをその場に残すのではなく、別の場所に移動してください。数か月後、そのディレクトリには実際の顧客が送る最悪のドキュメントが静かに蓄積されますが、そのコーパスは手書きの合成サンプルよりもリリーステストにとって価値があります
規格の選択と適合レベルが重要な理由
TPdfPreflightStandard列挙型は、実際に使われるファミリーをカバーします。ISO 19005アーカイブ適合性のためのppsPdfA、ISO 14289アクセシビリティのためのppsPdfUa、印刷交換のためのppsPdfX、さらにエンジニアリング、ラスター、可変データ作業のためのppsPdfE、ppsPdfR、ppsPdfVTです。ファミリー内では、エンジンはドキュメントが主張する適合レベルを読み取り、結果のConformanceNameで規格ごとに報告します。ファミリーの指定だけでは通常不十分です。なぜなら、レベルこそが本当の違いが生じる場所だからです。PDF/A-2bは視覚的再現性のみを約束します。PDF/A-3aは論理構造タグ付けの要求を追加し、埋め込みソースファイルを許可します。これはタグツリーをまったく持たないスキャン済み資料にとってはるかに高い基準です。いずれかの方向で間違えると、バッチはあなたに嘘をつきます。保持ポリシーが実際にはPDF/A-2bを求めているのに、構造タグの欠如でファイルを失敗させると、誰も修正しない指摘事項でレポートがいっぱいになります。レベルを確認せずにPDF/Aラベルを受け入れると、約束した基準より弱い基準を満たすドキュメントを承認してしまいます。政府バイヤーからのアクセシビリティ義務はこれにPDF/UAを積み重ねることが多いですが、BuildPdfPreflightReport(FPdfPreflightReportユニットから)は規格のセットを取るため、実行コストは追加されません:
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
1回の呼び出しで両方の規格を評価し、単一の統合レポートレコードを返します
空の指摘事項リストが合格でない理由
レポートは規格ごとに指摘事項を列挙しており、空の問題リストは「実際に実行された規格で問題が見つからなかった」ことのみを意味します。これは「あなたが気にする規格にファイルが適合している」という主張よりも狭いものであり、この2つの間の差異がバッチプリフライトが静かに腐っていく場所です。ppsPdfAをセットから削除する設定の誤字は、完全にクリーンなファイルとまったく同じ空の問題リストを生成します。そのため、沈黙を疑わしいものとして扱ってください。Report.Resultsを調べ、確認するつもりだったすべての規格について2つのことをアサートしてください。そのためのResult Entryが存在すること、そしてStatus = pfsPassに裏付けられたIsCompliantフラグがtrueであることです。どの規格が評価されたかを確認せずに「指摘事項なし」を「アーカイブ準備完了」と同一視する夜間ジョブは、不適合ファイルのフォルダが何か月も通り抜ける典型的な方法であり、外部監査者がveraPDFで1つを開いてアーカイブ全体が疑問視されるまで続きます
2つ目の罠は、指摘事項が何であるかに潜んでいます。各TPdfPreflightIssueはCode、Category、Description、Recommendationを持ち、ページやオブジェクトではなく、違反したルールを指名します。これはフィードバックループに影響を与える設計上の選択です。レポートは生成チームにどのクラスの欠陥が存在するかを伝えます。埋め込まれていないフォントや欠けているXMP識別子などです。特定の問題オブジェクトを見つけることは、バリデーターの仕事ではなく、下流の修正ツールの仕事です。安定したCode値に対してレポートコンシューマーを構築してください。警告なしにリリース間で言い換えられる可能性のある人間が読める説明テキストに対してではありません
機械のためと担当者のためのレポートファイル
レポートレコードは同じ指摘事項を5つの形式で書き出します。SaveJsonToFile、SaveCsvToFile、SaveHtmlToFile、SaveTextToFile、SaveMarkdownToFile、そしてディスクではなくメモリ内に文字列が必要な場合のための各ToJsonスタイル関数です。1つを選ぼうとする衝動に抵抗してください。パイプライン用にJSONを書き出してください。CIがそれをジョブレコードに添付し、テキストをスクレイピングせずに問題コードと規格ごとのステータスを解析できるようにするためです。担当者のためにHTMLを書き出してください。ツールなしでどのブラウザでも開けるからです。この2つを合わせてもファイルごとに1行余分になるだけで、深夜2時に生のJSONブロブをリバースエンジニアリングしてどのファイルが壊れたかを学ぶというバッチ処理で最悪のタスクから担当エンジニアを救います。フォーマットの選択より重要な1つの規律があります。レポート名をタイムスタンプからではなく、入力ファイル名から導出することです。そうしないと、2つの並行実行のレポートが混在し、入力にマッチさせることができなくなります
重大度のしきい値はコードではなく設定に属します。代替説明のないアノテーションはPDF/UA提出ポータルでは厳しい失敗であり、内部アーカイブでは無視できる注意事項ですが、両方において指摘事項は同一です。ポリシーが再コンパイルなしに変更できるようにプロファイルごとに失敗レベルを公開し、そのジョブサマリー自体に有効だったレベルをスタンプしてください。来四半期には誰も先10月のバッチがどのしきい値で実行されたかを覚えていないでしょう。そのメモリが生き残る唯一の場所はサマリーです
1つの不良PDFがバッチ全体を沈没させないようにファイルを分離する
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;
そのループには3つの意図的な選択があります。ファイルごとに新鮮なTPdfを使うことで、エンジン状態を破壊する1つのドキュメントがそれに続くファイルを汚染できないことを保証します。明示的なActiveチェックがその場所にある理由は、Active := Trueがロードエラーを発生させる代わりに飲み込むためです。ガードを外すと、切り詰められたファイルが検証呼び出しに漂い込み、下流で誤解を招くメッセージで失敗します。内側のtry..exceptは意図的にファイルごとのスコープ内にあり、単一の例外が失敗カウンターをインクリメントしてループを続行させます。ファイル5,000が破損していても、4,999の良いファイルのクリーンなレポートが欲しいのです。そして両方のレポート形式は判定が集計される前にディスクに書き込まれます。つまり、後でサマリーロジックのバグが誤カウントを引き起こしても、証拠は残ります
終了コードのマッピングはプロジェクトファイルの数行に絞り込まれます:
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.
プリフライトがやってくれないこと
エンジンは検出するだけであり、修復はしません。埋め込まれていないフォントやデバイス依存の色空間に関する指摘事項は、ファイルを生成する担当者への作業指示書であり、バリデーターにはそれをその場でパッチする手段がありません。そのため、フィードバックループを意図的に計画してください。レポートは生成チームが実際に読む場所に届く必要があります。そうでなければ、誰かがついに適合率が改善されない理由を聞くまで、毎晩同じ指摘事項が繰り返されます。また、外部監査者が先に確認する前に、独立したバリデーター(PDF/AのveraPDFやPDF/XのAcrobatプリフライト)と実際の顧客ファイルのサンプルを相互確認する価値があります。2つのエンジンが実際の顧客ファイルで意見が分かれた場合、そのドキュメントは厄介者ではありません。まさにリリーステストが見逃していたリグレッションケースです。保存し、名前をつけ、すべてのビルドで実行してください
もう1つのペアリングを知っておく価値があります。同じ検証エンジンがレビューUI内のインタラクティブなチェックを駆動するため、このヘッドレスCLIとアナリスト向けのPDF受け入れレビューワークベンチは、時間の経過とともに乖離するのではなく、単一の検証語彙を共有できます。そして[ppsPdfA, ppsPdfUa]が同じパスでアクセシビリティを評価するため、バッチのPDF/UA側はDelphiでアクセシブルなPDFリーダーを構築するのようなビューアー側の作業ときれいに整合します。プロファイル、レポート形式、完全なプリフライトAPIはPDFium Componentの製品ページに記載されています