11. Kommandozeile

← Standardbibliothek · Inhalt · Weiter: Rezepte →

Zwölf Befehle: vier für PDFs, vier für Skripte, zwei für die Verteilung und zwei für die Lizenzierung.

Befehl Zweck
run Prüft ein PDF mit einem Skript
compare Vergleicht zwei Fassungen
watch Überwacht einen Ordner und prüft, was eintrifft
fix Wendet Änderungen an und speichert ein neues PDF
inspect Schneller Überblick über ein PDF
lint Analysiert ein Skript, ohne es auszuführen
fmt Formatiert ein Skript
doc Erzeugt die Dokumentation eines Skripts
pack Packt Profile und Daten
add Installiert ein Paket
fingerprint Geräte-Fingerabdruck
license Lizenzprüfung

Exit-Codes

Gelten für alle Befehle, die prüfen.

Code Bedeutung
0 Alles bestanden
1 Nur Warnungen
2 Prüffehler oder PDF nicht lesbar
3 Syntaxfehler im Skript
pdfl run profil.pdfl datei.pdf > bericht.json
case $? in
  0) echo "approved" ;;
  1) echo "approved with warnings" ;;
  2) echo "rejected — see bericht.json" ;;
  3) echo "error in the validation script" ;;
esac

pdfl run

Prüft ein PDF mit einem Skript.

pdfl run <skript.pdfl> <eingabe.pdf> [optionen]
Option Vorgabe Zweck
--output json|csv|html|pdf json Format des Berichts
--output-file <datei> Schreibt in eine Datei statt auf die Standardausgabe
--fail-on error|warning error Mit warning führt auch eine Warnung zu Code 2
--verbose Zusatzinformationen auf der Fehlerausgabe
pdfl run vorstufe.pdfl magazin.pdf                                     # JSON im Terminal
pdfl run vorstufe.pdfl magazin.pdf --output html --output-file bericht.html
pdfl run vorstufe.pdfl magazin.pdf --output pdf --output-file bericht.pdf
pdfl run vorstufe.pdfl magazin.pdf --output csv --output-file befunde.csv
pdfl run vorstufe.pdfl magazin.pdf --fail-on warning                   # strenger Modus

Der JSON-Bericht

{
  "script_name": "prepress.pdfl",
  "input_file": "magazine.pdf",
  "profile": "offset-magazine",
  "status": "FAIL",
  "total_pages_analyzed": 120,
  "error_count": 2,
  "warning_count": 0,
  "info_count": 0,
  "diagnostics": [
    {
      "id": "PDFL-001",
      "severity": "error",
      "check_name": "Ink coverage",
      "message": "page 7: 324% ink (limit 300%)",
      "line": 12
    }
  ]
}

Dasselbe PDF mit demselben Skript ergibt stets einen Byte für Byte identischen Bericht: Man kann ihn versionieren und Unterschiede in der CI vergleichen.


pdfl compare

Vergleicht zwei Fassungen: Text, Struktur und Metadaten.

pdfl compare <v1.pdf> <v2.pdf> [optionen]
Option Vorgabe Zweck
--output json|csv|html|pdf json Format
--output-file <datei> Schreibt in eine Datei
--normalize Ignoriert Groß-/Kleinschreibung und Leerzeichen
--ignore-dates Maskiert Datumsangaben vor dem Vergleich
--similarity-threshold <0-100> 100 Kleinste hinnehmbare Ähnlichkeit
pdfl compare freigegeben_v1.pdf erhalten_v2.pdf --normalize --ignore-dates

# Bis zu 1 % Abweichung ist erlaubt; darunter ist es ein Fehler
pdfl compare v1.pdf v2.pdf --similarity-threshold 99 \
  --output html --output-file unterschiede.html

Wie es arbeitet

  • Seiten werden nach Inhalt einander zugeordnet, nicht nach Nummer: Eine in der Mitte eingefügte Seite lässt nicht alles Folgende als Unterschied erscheinen. Funktioniert auch bei mehr als tausend Seiten.
  • Jedes Paar bekommt einen Ähnlichkeitswert und eine Auswahl der geänderten Zeilen (- entfernt, + ergänzt).
  • Eine Änderung der Metadaten ist eine Warnung; eine Textänderung unter der Schwelle ist ein Fehler, darüber eine Warnung.
  • Der Gesamtwert steht im Feld similarity des Berichts.
page 4 → 4: similarity 97.8% | -original title | +revised title

pdfl watch

Überwacht einen Ordner und prüft jedes PDF, das eintrifft oder sich ändert.

pdfl watch <ordner> --script <skript.pdfl> [optionen]
Option Vorgabe Zweck
--pattern <glob> *.pdf Welche Dateien verarbeitet werden
--exclude <glob> Welche übergangen werden
--output-dir <ordner> neben dem PDF Wohin die Berichte gehen
--depth <n> 1 Tiefe der Unterordner
--debounce <ms> 1000 Wartezeit, bis die Datei stabil ist
--report json|csv|html|pdf json Format der Berichte
--fail-fast Hält beim ersten Fehler an
--once Verarbeitet den Bestand und beendet sich
# Eingangsordner einer Druckerei, im Dauerbetrieb
pdfl watch inbox/ --script preflight.pdfl --output-dir berichte/ --report html

# Stapellauf für die CI: beendet sich mit dem schlechtesten Code
pdfl watch inbox/ --script preflight.pdfl --once
echo "result: $?"

Das debounce gibt es, weil große Dateien in Stücken ankommen: Verarbeitet wird nur eine Datei, die sich nicht mehr ändert — also nie ein halb geschriebenes PDF.

Die Berichte entstehen als <name>.report.json (oder .csv, .html, .pdf).


pdfl fix

Wendet die fix::-Operationen an und speichert ein neues PDF. Einzelheiten in Kapitel 8.

pdfl fix original.pdf normalisieren.pdfl --output out.pdf --dry-run  # nur ansehen
pdfl fix original.pdf normalisieren.pdfl --output korrigiert.pdf     # anwenden

pdfl inspect

Überblick über ein PDF, ohne Skript.

pdfl inspect <datei.pdf>
File:     magazine.pdf
Size:     26 KB (27284713 bytes)
SHA-256:  af1029842e5bfeae338ead82fb449ef851be742b1d63117c12596e3ea123a616

Pages:    120
Page size: 496 x 709 pt
Boxes:    MediaBox, TrimBox, BleedBox

Metadata:
  Title: Example Magazine
  Creator: Adobe InDesign 19.3

Fonts:    26
  ABCDEF+Helvetica — embedded
  Arial — NOT embedded
Images:   81 (minimum DPI 136, spaces: DeviceCMYK, Indexed)
Max. estimated TAC: 300% (RGB render approximation)

Warnings:
  ! there are non-embedded fonts
  ! 3 image(s) below 300 DPI

Der erste Befehl, wenn eine Datei eintrifft: In Sekunden weiß man, ob sie das Öffnen lohnt.


pdfl lint

Analysiert ein Skript, ohne es auszuführen, und meldet Qualitätsprobleme.

pdfl lint <skript.pdfl>

Was es findet:

  • Variablen, Blockparameter und Funktionen, die deklariert und nie benutzt werden (mit _ davor lässt sich die Warnung unterdrücken: _page)
  • Doppelte oder leere checks
  • Unbekannte Namensräume (text::, struct::, visual::, prepress::, codes::, fix::, data::)
  • assert / require außerhalb eines checks
  • Gebrauch von fix:: (läuft nur unter pdfl fix)
$ pdfl lint profil.pdfl
profil.pdfl: warning: variable 'LIMIT' declared and never used
profil.pdfl: warning: check "Fonts" declared 2 times

Bei Warnungen ist der Exit-Code 1 — in der CI verwendbar.


pdfl fmt

Formatiert ein Skript: zwei Leerzeichen Einrückung, einheitliche Abstände, zusammengefasste Leerzeilen. Kommentare und Einheiten (3mm bleibt 3mm) bleiben erhalten.

pdfl fmt <skript.pdfl>            # formatiert an Ort und Stelle
pdfl fmt <skript.pdfl> --check    # ändert nichts; Code 1, wenn unformatiert
# Teamstandard in der CI durchsetzen
for f in profile/*.pdfl; do pdfl fmt "$f" --check || exit 1; done

pdfl doc

Erzeugt die Dokumentation aus dem Skript selbst.

pdfl doc <skript.pdfl> [--output markdown|html]

Ausgegeben werden: das Profil, eine Tabelle der Konstanten, die Funktionen, die Importe und zu jedem check seine Etiketten und das, was er prüft (die Meldungen der assert werden zu den Beschreibungen).

pdfl doc vorstufe.pdfl > docs/vorstufen-profil.md
pdfl doc vorstufe.pdfl --output html > profil.html

Das ist das Ergebnis, das einer Produktionsleitung, die keinen Code liest, erklärt, was ein Profil prüft.


pdfl pack

Packt Skripte und Daten in ein verteilbares .pdflpkg.

pdfl pack <ordner> [--name <name>] [--version <version>] [--output <datei>]

Es sammelt rekursiv die .pdfl-, .csv-, .txt-, .json- und .xlsx-Dateien des Ordners und legt ein manifest.json bei, das den SHA-256 jeder Datei notiert. Das Packen ist deterministisch: Derselbe Ordner ergibt dieselben Bytes.

pdfl pack profile/druckerei --name druckprofil --version 1.0.0

pdfl add

Installiert ein lokales Paket und prüft dabei die Prüfsummen des Manifests.

pdfl add druckprofil.pdflpkg
# installiert nach ./pdfl_profiles/druckprofil@1.0.0/

pdfl run pdfl_profiles/druckprofil@1.0.0/vorstufe.pdfl datei.pdf

Stimmt die Prüfsumme einer Datei nicht, wird die Installation verweigert — ein beschädigtes oder verändertes Paket kommt nicht hinein.

Ferne Verzeichnisse und digitale Signaturen gehören nicht zu dieser Version: add installiert aus einer lokalen Datei.


pdfl fingerprint

Der Fingerabdruck dieses Rechners — der Wert, den Sie an Ihren Anbieter schicken, um eine Lizenz zu erhalten.

pdfl fingerprint generate

Er wird allein auf die Standardausgabe geschrieben; der erklärende Text geht auf die Fehlerausgabe, pdfl fingerprint generate 2>/dev/null liefert also einen Wert, der sich direkt weiterleiten lässt.

$ pdfl fingerprint generate
Device fingerprint for this machine:

1a7553e711c53306be083ea8927c9cbe

Send it to your vendor to be issued a license.
It changes if the operating system is reinstalled.

Der Fingerabdruck stammt aus der Maschinenkennung des Systems (/etc/machine-id, IOPlatformUUID oder MachineGuid). Eine Neuinstallation des Systems überlebt er nicht, und aus demselben Abbild geklonte virtuelle Maschinen teilen sich den Wert. Für CI und Container — wo der Rechner bei jedem Lauf verworfen wird — gibt es die seatless-Lizenz, die nicht auf das Gerät schaut.


pdfl license

Prüft eine erhaltene Lizenz. pdfl prüft nur: ausgestellt werden Lizenzen vom Anbieter, mit dem privaten Schlüssel, der nicht mit der Binärdatei ausgeliefert wird.

pdfl license check <token>
$ pdfl license check "$(cat license.txt)"
signature:  ok
customer:   Example Print Shop
expires:    2027-01-01
features:   all
device:     1a7553e711c53306be083ea8927c9cbe
this machine: matches

Beendet sich mit 0, wenn die Lizenz für diesen Rechner gilt, sonst mit 2 — auch dann, wenn die Signatur stimmt, die Lizenz aber zu einem anderen Rechner gehört; in dem Fall sagt die Meldung genau das.

Option Zweck
--pubkey <base64> Zu verwendender öffentlicher Schlüssel statt des einkompilierten

Wo eine Lizenz verlangt wird

run, fix, watch, compare und inspect halten mit Code 2 an, wenn es für diesen Rechner keine gültige Lizenz gibt. fingerprint generate verlangt nie eine — es erzeugt ja gerade den Wert, mit dem man eine anfordert. lint, fmt, doc und pack fassen nur Skripte an, nie ein PDF, und verlangen ebenfalls keine.

Das Token wird in dieser Reihenfolge gesucht:

  1. $PDFL_LICENSE — der Weg für die CI, wo es zum Secret wird
  2. ~/.config/pdfl/license
  3. pdfl.license neben der ausführbaren Datei
$ pdfl run profil.pdfl datei.pdf
error: no license found

Run 'pdfl fingerprint generate' and send the value to your vendor
to be issued a license. Then set PDFL_LICENSE, or save the token to
/home/sie/.config/pdfl/license.

Eine seatless-Lizenz hat eine Laufobergrenze, gezählt in ~/.config/pdfl/state. Dieselbe Datei hält fest, wann zuletzt gelaufen wurde, und verweigert den Lauf, wenn die Uhr zurückgestellt wird — mit einer Stunde Toleranz, damit die Zeitsynchronisation nicht dazwischenfunkt.


← Standardbibliothek · Inhalt · Weiter: Rezepte →