Technischer Artikel

Batch-PDF-Preflight-Berichte in Delphi mit der PDFium Component CLI

Ein Batch-Preflight-Tool ist ein Konsolenprogramm ohne Fenster, das auf einen Ordner mit PDFs gerichtet ist, jedes gegen die von Ihnen genannten Konformitätsstandards validiert und einen maschinenlesbaren Beweis dafür hinterlässt, was es gefunden hat. Niemand sitzt da und beobachtet es. Es läuft um zwei Uhr morgens unter cron oder der Windows-Aufgabenplanung oder als Schranke in einer CI-Pipeline, und die nächste Person, die sich für seine Ausgabe interessiert, ist entweder ein Scheduler, der einen Exit-Code liest, oder ein Prüfer, der Wochen später einen Bericht öffnet. Das ändert die Bedeutung von „richtig“. Die Preflight-Engine der PDFium Component, einer Quellcode-PDF-Bibliothek für Delphi, C++Builder und Lazarus, macht die Validierungsaufrufe selbst fast trivial. Die Arbeit, die darüber entscheidet, ob das Tool seinen Zweck erfüllt, dreht sich um diese Aufrufe: Welches Profil Sie überprüft haben, was der Exit-Code dem Scheduler mitgeteilt hat und ob der Bericht, der einen Fehler hätte aufdecken können, noch existiert, wenn jemand danach sucht

Der Vertrag: was ein Scheduler tatsächlich sehen kann

Ein CI-Runner oder die Windows-Aufgabenplanung sieht genau zwei Dinge von Ihrem Tool: den Exit-Code und alle Dateien, die es hinterlassen hat. Protokollzeilen, Konsolenfarben, Fortschrittsausgabe: all das ist für einen Menschen gedacht, der live zuschaut, und um zwei Uhr morgens tut das niemand. Legen Sie also das Exit-Code-Vokabular fest, bevor Sie die API anführen, und halten Sie es unspektakulär:

  • : Jede Datei entsprach jedem angeforderten Profil
  • : Mindestens eine Datei hat Validierungsbefunde erzeugt
  • : Das Tool selbst ist bei mindestens einer Datei fehlgeschlagen (beschädigte Eingabe, Sperre, Absturz)

Die Unterscheidung zwischen den Codes 1 und 2 ist diejenige, die Teams oft übergehen und später bereuen. Ein beschädigtes PDF, das sich nicht öffnen lässt, ist kein Validierungsfehler. Wenn Sie dies in Code 1 aufnehmen, taucht eine Wagenladung beschädigter Scans in Ihren Dashboards als plötzlicher Konformitätseinbruch auf und jemand wird losgeschickt, um eine angebliche Verschlechterung der Standards zu verfolgen, die nie stattgefunden hat, während das eigentliche Problem ein defekter Scanner im Vorfeld ist

Zwei weitere Punkte gehören zum Vertrag. Der erste ist ein Timeout pro Datei. Ein pathologisches PDF mit Tausenden von Seiten und tief verschachtelten Objektstrukturen kann einen einzelnen Validierungsdurchlauf für Minuten blockieren, und ein nächtliches Zeitfenster hat keine Geduld dafür. Brechen Sie die Verarbeitung dieser Datei bei Fristablauf ab, werten Sie sie als Tool-Fehler und lassen Sie den Batch weiterlaufen. Der zweite Punkt ist ein Quarantäneverzeichnis: Verschieben Sie jede Eingabe, bei der das Timeout abgelaufen ist oder die sich nicht öffnen lässt, anstatt sie an Ort und Stelle zu belassen. Im Laufe einiger Monate sammeln sich in diesem Verzeichnis unauffällig die schlimmsten Dokumente an, die Ihre tatsächlichen Kunden senden, und dieser Korpus ist für Release-Tests mehr wert als jede synthetische Stichprobe, die Sie von Hand erstellen könnten

Standards auswählen und warum die Konformitätsstufe wichtig ist

Die Enumeration deckt die in der Praxis auftretenden Familien ab: für die Archivierungskonformität nach ISO 19005, für Barrierefreiheit nach ISO 14289, für den Druckdatenaustausch sowie , und für Konstruktions-, Raster- und variable Datenarbeiten. Innerhalb einer Familie liest die Engine die Konformitätsstufe, die das Dokument beansprucht, und gibt sie pro Standard im des Ergebnisses aus. Die Benennung der Familie reicht selten aus, da der tatsächliche Unterschied in der Stufe liegt. PDF/A-2b verspricht visuelle Reproduzierbarkeit und nicht mehr. PDF/A-3a fügt die Anforderung nach logischem Struktur-Tagging hinzu und erlaubt eingebettete Quelldateien, was für gescanntes Material, das überhaupt keinen Tag-Baum hat, eine weitaus höhere Hürde darstellt. Wenn Sie dies in eine der beiden Richtungen falsch machen, lügt Sie der Batch an. Wenn Ihre Aufbewahrungsrichtlinie eigentlich PDF/A-2b erfordert, Sie aber Dateien wegen fehlender Struktur-Tags fehlschlagen lassen, füllt sich der Bericht mit Befunden, die niemand jemals beheben wird. Wenn Sie jedes PDF/A-Label akzeptieren, ohne die Stufe zu überprüfen, zeichnen Sie Dokumente ab, die einen geringeren Standard erfüllen, als Sie versprochen haben. Barrierefreiheitsanforderungen von staatlichen Käufern stapeln zunehmend PDF/UA oben drauf, was für den Durchlauf keine zusätzlichen Kosten verursacht, da (aus der Unit ) eine Menge von Standards akzeptiert:

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

Ein einziger Aufruf bewertet beide Standards und gibt einen einzelnen konsolidierten Berichtsdatensatz zurück

Warum eine leere Befundliste kein Bestanden ist

Der Bericht listet Befunde pro Standard auf, und eine leere Problemliste bedeutet nur: „Es wurden keine Probleme in den Standards gefunden, die tatsächlich ausgeführt wurden.“ Das ist eine engere Behauptung als „Die Datei entspricht dem Standard, der Sie interessiert“, und die Lücke zwischen den beiden ist der Punkt, an dem der Batch-Preflight im Stillen versagt. Ein Konfigurationsfehler, der aus der Menge streicht, erzeugt genau die gleiche leere Problemliste wie eine absolut saubere Datei. Betrachten Sie Stillschweigen also als verdächtig. Gehen Sie durch und stellen Sie für jeden Standard, den Sie überprüfen wollten, zwei Dinge sicher: dass überhaupt ein Ergebniseintrag dafür existiert und dass das -Flag, das durch gestützt wird, wahr ist. Ein nächtlicher Job, der „keine Befunde“ mit „archivbereit“ gleichsetzt, ohne jemals zu bestätigen, welche Standards bewertet wurden, ist der klassische Weg, auf dem ein Ordner mit nicht konformen Dateien monatelang durchrutscht, bis ein externer Prüfer eine davon mit veraPDF öffnet und das gesamte Archiv in Frage gestellt wird

Eine zweite Falle verbirgt sich darin, was ein Befund überhaupt ist. Jedes enthält einen , eine , eine und eine und benennt die Regel, die verletzt wurde, nicht eine Seite oder ein Objekt. Das ist eine Designentscheidung mit Konsequenzen für die Feedbackschleife. Der Bericht teilt dem produzierenden Team mit, welche Klasse von Defekten vorliegt – etwa eine nicht eingebettete Schriftart oder eine fehlende XMP-Kennung –, und das Finden des spezifischen beanstandeten Objekts ist die Aufgabe des Behebungstools im weiteren Verlauf, nicht die des Validators. Bauen Sie Ihre Berichtskonsumenten gegen die stabilen -Werte auf, niemals gegen den menschenlesbaren Beschreibungstext, der zwischen Releases ohne Vorwarnung umformuliert werden kann

Berichtsdateien für Maschinen und für den Bereitschaftsdienst

Der Berichtsdatensatz schreibt dieselben Befunde in fünf Formaten: , , , und , jeweils mit einer entsprechenden Funktion im -Stil, wenn Sie den String im Speicher anstatt auf der Festplatte benötigen. Widerstehen Sie dem Drang, sich für eines zu entscheiden. Schreiben Sie JSON für die Pipeline, damit die CI es an den Job-Datensatz anhängen und Problemcodes sowie Status pro Standard analysieren kann, ohne Text zu parsen. Schreiben Sie HTML für den Menschen, der angefunkt wird, da es in jedem Browser ohne jegliche Tools geöffnet werden kann. Beide zusammen kosten eine zusätzliche Zeile pro Datei und ersparen Ihrem Bereitschaftsingenieur die schlimmste Aufgabe bei der Stapelverarbeitung: um zwei Uhr morgens einen rohen JSON-Blob per Reverse Engineering zu untersuchen, um herauszufinden, welche Datei den Fehler verursacht hat. Eine Disziplin ist wichtiger als die Wahl des Formats: Leiten Sie jeden Berichtsnamen vom Eingabedatenamen ab, niemals von einem Zeitstempel, da sonst bei zwei parallelen Durchläufen Berichte vermischt werden, die Sie ihren Eingaben nicht mehr zuordnen können

Schweregradschwellenwerte gehören eher in die Konfiguration als in den Code. Eine Anmerkung ohne alternative Beschreibung ist ein harter Fehler für ein PDF/UA-Einreichungsportal und ein ignorierbarer Hinweis für ein internes Archiv, dennoch ist es in beiden Fällen identisch. Stellen Sie ein Fail-on-Level pro Profil bereit, damit sich die Richtlinie ohne Neukompilierung ändern lässt, und stempeln Sie das Level, das in Kraft war, direkt in die Job-Zusammenfassung ein. Im nächsten Quartal wird sich niemand mehr daran erinnern, unter welchem Schwellenwert der Batch vom letzten Oktober lief, und die Zusammenfassung ist der einzige Ort, an dem diese Information überlebt

Dateien isolieren, damit ein schlechtes PDF nicht den Batch versenkt

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;

In dieser Schleife stecken drei bewusste Entscheidungen. Ein frisches pro Datei garantiert, dass ein Dokument, das den Status der Engine beschädigt, die darauffolgenden Dateien nicht vergiften kann. Die explizite -Prüfung hat ihre Berechtigung, da Ladefehler verschluckt, anstatt sie auszulösen; lässt man diese Absicherung weg, treibt eine abgeschnittene Datei in den Validierungsaufruf hinein, bevor sie irgendwo im weiteren Verlauf mit einer irreführenden Fehlermeldung scheitert. Das innere befindet sich absichtlich im Gültigkeitsbereich pro Datei, sodass eine einzelne Ausnahme den Fehlerzähler erhöht und die Schleife fortgesetzt wird. Sie möchten saubere Berichte für die 4.999 guten Dateien, auch wenn Datei 5.000 zerschreddert ist. Und beide Berichtsformate werden auf die Festplatte geschrieben, bevor das Urteil ausgezählt wird, was bedeutet, dass der Beweis auch dann überlebt, wenn ein Fehler später in der Zusammenfassungslogik eine Fehlzählung verursacht

Die Exit-Code-Zuordnung reduziert sich dann auf wenige Zeilen in der Projektdatei:

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.

Was Preflight nicht für Sie tun wird

Die Engine erkennt; sie repariert nicht. Ein Befund über eine nicht eingebettete Schriftart oder einen geräteabhängigen Farbraum ist ein Arbeitsauftrag für denjenigen, der die Dateien erstellt, und der Validator hat keine Möglichkeit, ihn an Ort und Stelle zu patchen. Planen Sie die Feedbackschleife daher bewusst. Berichte müssen dort landen, wo das produzierende Team sie auch tatsächlich liest, andernfalls tauchen dieselben Befunde jede Nacht wieder auf, bis sich endlich jemand fragt, warum sich die Konformitätsrate nie verbessert. Es zahlt sich auch aus, eine Stichprobe von Urteilen gegen einen unabhängigen Validator gegenzuprüfen – etwa veraPDF für PDF/A oder Acrobats Preflight für PDF/X –, bevor ein externer Prüfer sie für Sie gegencheckt. Wenn zwei Engines bei einer echten Kundendatei unterschiedlicher Meinung sind, ist dieses Dokument kein Ärgernis; es ist genau der Regressionsfall, der bei Ihren Release-Tests gefehlt hat. Behalten Sie es, benennen Sie es und führen Sie es bei jedem Build aus

Eine weitere Kombination ist gut zu wissen. Dieselbe Validierungs-Engine steuert auch die interaktiven Prüfungen in einer Review-Benutzeroberfläche, sodass dieses Headless-CLI und ein auf Analysten ausgerichteter PDF Intake Review Workbench ein gemeinsames Validierungsvokabular teilen können, anstatt mit der Zeit auseinanderzudriften. Und weil die Barrierefreiheit im selben Durchgang bewertet, reiht sich die PDF/UA-Seite des Batches nahtlos an viewer-seitige Arbeiten wie das Erstellen eines barrierefreien PDF-Readers in Delphi an. Profile, Berichtsformate und die vollständige Preflight-API sind auf der Produktseite der dokumentiert