11. Ligne de commande

← Bibliothèque standard · Sommaire · Suivant : recettes →

Douze commandes : quatre pour les PDF, quatre pour les scripts, deux pour la distribution et deux pour les licences.

Commande Rôle
run Valide un PDF avec un script
compare Compare deux versions
watch Surveille un dossier et valide ce qui arrive
fix Applique des modifications et enregistre un nouveau PDF
inspect Vue d'ensemble rapide d'un PDF
lint Analyse un script sans l'exécuter
fmt Met en forme un script
doc Génère la documentation d'un script
pack Empaquette profils et données
add Installe un paquet
fingerprint Empreinte de la machine
license Vérification de licence

Codes de sortie

Communs à toutes les commandes qui valident.

Code Signification
0 Tout est passé
1 Avertissements seulement
2 Erreurs de validation, ou PDF illisible
3 Erreur de syntaxe dans le script
pdfl run profil.pdfl fichier.pdf > rapport.json
case $? in
  0) echo "approved" ;;
  1) echo "approved with warnings" ;;
  2) echo "rejected — see rapport.json" ;;
  3) echo "error in the validation script" ;;
esac

pdfl run

Valide un PDF avec un script.

pdfl run <script.pdfl> <entree.pdf> [options]
Option Défaut Rôle
--output json|csv|html|pdf json Format du rapport
--output-file <fichier> Écrit dans un fichier au lieu de la sortie standard
--fail-on error|warning error Avec warning, un avertissement donne aussi le code 2
--verbose Informations supplémentaires sur la sortie d'erreur
pdfl run prepresse.pdfl magazine.pdf                                     # JSON au terminal
pdfl run prepresse.pdfl magazine.pdf --output html --output-file rapport.html
pdfl run prepresse.pdfl magazine.pdf --output pdf --output-file rapport.pdf
pdfl run prepresse.pdfl magazine.pdf --output csv --output-file constats.csv
pdfl run prepresse.pdfl magazine.pdf --fail-on warning                   # mode strict

Le rapport JSON

{
  "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
    }
  ]
}

Le même PDF avec le même script produit toujours un rapport identique octet pour octet : on peut le versionner et comparer les différences en CI.


pdfl compare

Compare deux versions : texte, structure et métadonnées.

pdfl compare <v1.pdf> <v2.pdf> [options]
Option Défaut Rôle
--output json|csv|html|pdf json Format
--output-file <fichier> Écrit dans un fichier
--normalize Ignore casse et espaces
--ignore-dates Masque les dates avant de comparer
--similarity-threshold <0-100> 100 Similarité minimale acceptable
pdfl compare approuve_v1.pdf recu_v2.pdf --normalize --ignore-dates

# Tolère jusqu'à 1 % d'écart ; en dessous, c'est une erreur
pdfl compare v1.pdf v2.pdf --similarity-threshold 99 \
  --output html --output-file differences.html

Comment ça marche

  • Les pages sont mises en correspondance par leur contenu, pas par leur numéro : une page insérée au milieu ne fait pas signaler tout ce qui suit. Fonctionne sur des documents de plus de mille pages.
  • Chaque paire reçoit un score de similarité et un échantillon des lignes qui changent (- retirée, + ajoutée).
  • Un changement de métadonnées est un avertissement ; un changement de texte sous le seuil est une erreur, au-dessus un avertissement.
  • Le score global figure dans le champ similarity du rapport.
page 4 → 4: similarity 97.8% | -original title | +revised title

pdfl watch

Surveille un dossier et valide chaque PDF qui arrive ou change.

pdfl watch <dossier> --script <script.pdfl> [options]
Option Défaut Rôle
--pattern <glob> *.pdf Quels fichiers traiter
--exclude <glob> Quels fichiers ignorer
--output-dir <dossier> à côté du PDF Où écrire les rapports
--depth <n> 1 Profondeur des sous-dossiers
--debounce <ms> 1000 Attente que le fichier se stabilise
--report json|csv|html|pdf json Format des rapports
--fail-fast S'arrête à la première erreur
--once Traite l'existant puis quitte
# Dossier de réception d'une imprimerie, en continu
pdfl watch inbox/ --script preflight.pdfl --output-dir rapports/ --report html

# Traitement par lot pour la CI : sort avec le pire code rencontré
pdfl watch inbox/ --script preflight.pdfl --once
echo "result: $?"

Le debounce existe parce qu'un gros fichier arrive par morceaux : on ne traite qu'un fichier qui a cessé de changer, donc jamais un PDF à moitié écrit.

Les rapports s'écrivent en <nom>.report.json (ou .csv, .html, .pdf).


pdfl fix

Applique les opérations fix:: et enregistre un nouveau PDF. Détails au chapitre 8.

pdfl fix original.pdf normaliser.pdfl --output out.pdf --dry-run  # voir seulement
pdfl fix original.pdf normaliser.pdfl --output corrige.pdf        # appliquer

pdfl inspect

Vue d'ensemble d'un PDF, sans script.

pdfl inspect <fichier.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

La première commande à lancer quand un fichier arrive : en quelques secondes on sait s'il vaut la peine d'être ouvert.


pdfl lint

Analyse un script sans l'exécuter et signale les problèmes de qualité.

pdfl lint <script.pdfl>

Ce qu'il détecte :

  • Variables, paramètres de bloc et fonctions déclarés et jamais utilisés (préfixez par _ pour taire l'avertissement : _page)
  • Checks en double ou vides
  • Espaces de noms inconnus (text::, struct::, visual::, prepress::, codes::, fix::, data::)
  • assert / require hors d'un check
  • Usage de fix:: (qui ne tourne que sous pdfl fix)
$ pdfl lint profil.pdfl
profil.pdfl: warning: variable 'LIMIT' declared and never used
profil.pdfl: warning: check "Fonts" declared 2 times

En présence d'avertissements, le code de sortie est 1 — utilisable en CI.


pdfl fmt

Met en forme un script : indentation de deux espaces, espacement cohérent, lignes vides compactées. Commentaires et unités (3mm reste 3mm) sont conservés.

pdfl fmt <script.pdfl>            # met en forme sur place
pdfl fmt <script.pdfl> --check    # ne modifie rien ; code 1 si non formaté
# Imposer la norme de l'équipe en CI
for f in profils/*.pdfl; do pdfl fmt "$f" --check || exit 1; done

pdfl doc

Génère la documentation à partir du script lui-même.

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

Il produit : le profil, un tableau des constantes, les fonctions, les imports, et pour chaque check ses étiquettes et ce qu'il valide (les messages des assert deviennent les descriptions).

pdfl doc prepresse.pdfl > docs/profil-prepresse.md
pdfl doc prepresse.pdfl --output html > profil.html

C'est le livrable qui explique ce que valide un profil à un responsable de fabrication qui ne lit pas le code.


pdfl pack

Empaquette scripts et données dans un .pdflpkg distribuable.

pdfl pack <dossier> [--name <nom>] [--version <version>] [--output <fichier>]

Il collecte récursivement les .pdfl, .csv, .txt, .json et .xlsx du dossier et ajoute un manifest.json qui note le SHA-256 de chaque fichier. L'empaquetage est déterministe : le même dossier produit les mêmes octets.

pdfl pack profils/imprimerie --name profil-impression --version 1.0.0

pdfl add

Installe un paquet local en vérifiant les empreintes du manifeste.

pdfl add profil-impression.pdflpkg
# installe dans ./pdfl_profiles/profil-impression@1.0.0/

pdfl run pdfl_profiles/profil-impression@1.0.0/prepresse.pdfl fichier.pdf

Si l'empreinte d'un fichier ne correspond pas, l'installation est refusée — un paquet corrompu ou altéré n'entre pas.

Dépôts distants et signatures numériques ne font pas partie de cette version : add installe depuis un fichier local.


pdfl fingerprint

L'empreinte de cette machine — la valeur à envoyer au fournisseur pour obtenir une licence.

pdfl fingerprint generate

Elle est écrite seule sur la sortie standard ; le texte d'accompagnement va sur la sortie d'erreur, donc pdfl fingerprint generate 2>/dev/null donne une valeur prête à passer dans un tube.

$ 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.

L'empreinte vient de l'identifiant machine du système (/etc/machine-id, IOPlatformUUID ou MachineGuid). Elle ne survit pas à une réinstallation du système, et des machines virtuelles clonées de la même image partagent la valeur. Pour la CI et les conteneurs — où la machine est détruite à chaque exécution — il y a la licence seatless, qui ne regarde pas la machine.


pdfl license

Vérifie une licence reçue. pdfl ne fait que vérifier : les licences sont émises par le fournisseur, avec la clé privée, qui n'accompagne pas le binaire.

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

Sort avec 0 si la licence vaut pour cette machine et 2 sinon — y compris quand la signature est bonne mais que la licence appartient à une autre machine, cas où le message le dit explicitement.

Option Rôle
--pubkey <base64> Clé publique à utiliser, au lieu de celle compilée

Où une licence est exigée

run, fix, watch, compare et inspect s'arrêtent avec le code 2 s'il n'y a pas de licence valable pour cette machine. fingerprint generate n'en exige jamais — c'est lui qui produit la valeur nécessaire pour en demander une. lint, fmt, doc et pack ne touchent que des scripts, jamais un PDF, et n'en exigent pas non plus.

Le jeton est cherché dans cet ordre :

  1. $PDFL_LICENSE — la voie pour la CI, où il devient un secret
  2. ~/.config/pdfl/license
  3. pdfl.license à côté de l'exécutable
$ pdfl run profil.pdfl fichier.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/vous/.config/pdfl/license.

Une licence seatless a un plafond d'exécutions, compté dans ~/.config/pdfl/state. Le même fichier retient la date de la dernière exécution et refuse de tourner si l'horloge recule — avec une heure de tolérance, pour ne pas se heurter à la synchronisation de l'heure.


← Bibliothèque standard · Sommaire · Suivant : recettes →