12. Rezepte
Vollständige Fälle, direkt übernehmbar. Jeder löst ein reales Problem aus der Praxis.
12.1 Druckerei: Vorstufenprüfung eines Offset-Magazins
Abschnitt betitelt „12.1 Druckerei: Vorstufenprüfung eines Offset-Magazins“Das Problem: Der Kunde liefert die Datei; vor dem Plattenbelichten müssen Farben, Schriften, Bilder und Anschnitt geprüft werden. Ein Fehler, der später auffällt, kostet die ganze Auflage.
profile/offset.pdfl:
profile "offset-magazine" {
const TAC_GRENZE = 300% // Farbgrenze auf gestrichenem Papier const ANSCHNITT = 3mm // Vorgabe des Ausschießens const MIN_DPI = 300
check "Ink coverage" tags: ["prepress"] { // Der exakte TAC liest die in der Datei deklarierten Farben; die // Schätzung über das Rendering unterschätzt Tiefschwarz und übersieht // Überschreitungen doc.pages.each { |page| tac = prepress::calculate_exact_tac(page.number) assert tac <= TAC_GRENZE, "page #{page.number}: #{tac}% ink (limit #{TAC_GRENZE}%)" } }
check "Colors" tags: ["prepress"] { assert prepress::detect_color_mode() != "RGB", "document is in RGB — convert to CMYK"
sonder = prepress::detect_spot_colors() assert sonder.length == 0, "unquoted special ink: #{sonder.join(", ")}"
assert !prepress::detect_rich_black(), "rich black detected — use 0/0/0/100 for text" }
check "Fonts" tags: ["fonts"] { lose = prepress::detect_text_substitution() assert lose.length == 0, "fonts not embedded (text will change at the RIP): #{lose.join(", ")}" assert prepress::validate_font_size(6), "there is text below 6 pt — illegible once printed" }
check "Strokes" tags: ["prepress"] { assert !prepress::detect_hairlines(0.25), "strokes below 0.25 pt disappear in print" assert !prepress::detect_hairlines_exact(), "there is a stroke with 0 width — set a real thickness" }
check "Images" tags: ["images"] { doc.images.each { |img| assert img.dpi >= MIN_DPI, "image on page #{img.page_number}: #{round(img.dpi)} DPI (minimum #{MIN_DPI})" assert img.color_space != "DeviceRGB", "RGB image on page #{img.page_number}" } }
check "Geometry" tags: ["prepress"] { assert prepress::validate_trim_box(), "no TrimBox — imposition cannot know where to trim" assert prepress::validate_bleed_box(), "no BleedBox — no bleed is defined" assert prepress::check_page_geometry(ANSCHNITT), "bleed smaller than 3 mm on some page" }}Am Schalter:
# HTML-Bericht, der an den Kunden zurückgehtpdfl run profile/offset.pdfl kunde.pdf --output html --output-file bericht.htmlAls überwachter Ordner: Die Bedienperson legt die Datei ab, der Bericht erscheint daneben.
pdfl watch inbox/ --script profile/offset.pdfl \ --output-dir berichte/ --report html12.2 Rechtsverlag: Vertragsprüfung vor der Veröffentlichung
Abschnitt betitelt „12.2 Rechtsverlag: Vertragsprüfung vor der Veröffentlichung“Das Problem: Verträge und Policen müssen die Pflichtklauseln tragen, keinen Entwurfstext behalten, keine personenbezogenen Daten preisgeben und durchsuchbar bleiben.
profile/recht.pdfl:
profile "standard-contract" {
check "Mandatory clauses" tags: ["legal"] { // Vom Rechtsbereich gepflegtes Glossar fehlend = data::validate_against_reference("begriffe/klauseln.txt") assert fehlend.length == 0, "missing clauses: #{fehlend.join("; ")}" }
check "No drafts" tags: ["legal"] { assert text::forbid_text("DRAFT"), "document marked as draft" assert text::forbid_text("lorem ipsum"), "placeholder text present" assert text::forbid_match("X{3,}"), "unfilled fields (XXX)" }
check "Privacy" tags: ["compliance"] { // Steuernummern zählen nur mit korrekter Prüfziffer, // Beispielnummern lösen also keinen Fehlalarm aus gefunden = text::detect_personal_data() assert gefunden.length == 0, "personal data in the document: #{gefunden.join("; ")}" }
check "Numbering and initials" tags: ["legal"] { doc.pages.each { |page| fuss = region(0, 0, page.width, 60, "footer") inhalt = text::extract_from_region(page.number, fuss).trim() assert inhalt != "", "page #{page.number} has no numbering/initials in the footer" } }
check "Searchable text" tags: ["accessibility"] { assert !text::detect_rasterized_text(), "there are scanned pages — text cannot be searched or read by screen readers" }}12.3 Pharmalabor: Beipackzettel mit Chargencode
Abschnitt betitelt „12.3 Pharmalabor: Beipackzettel mit Chargencode“Das Problem: Der Beipackzettel muss die vorgeschriebenen Texte tragen, und der Strichcode muss auf das richtige Produkt zeigen. Codes zwischen Produkten zu vertauschen, ist der teuerste Fehler dieser Branche.
profile/beipackzettel.pdfl:
profile "regulated-insert" {
check "Mandatory texts" tags: ["regulatory"] { fehlend = data::validate_against_reference("datenbanken/pflichttexte.txt") assert fehlend.length == 0, "mandatory texts missing: #{fehlend.join("; ")}" }
check "Legibility" tags: ["regulatory"] { assert prepress::validate_font_size(6), "there is text below 6 pt" }
check "Barcode" tags: ["codes", "critical"] { assert codes::detect_barcodes(), "insert has no barcode"
code = codes::decode_barcode(1) assert codes::validate_barcode_checksum(1), "invalid check digit: #{code}"
// Diese Prüfung fängt den teuersten Fehler ab: // der Code des einen Produkts mit dem Text eines anderen assert codes::compare_barcode_with_text(), "the code number does not appear in the insert text" }
check "Approved product" tags: ["data", "critical"] { code = codes::decode_barcode(1) produkt = data::query_gtin(code) assert produkt, "GTIN #{code} is not in the product database"
name = produkt.get(2) assert text::require_text(name), "the name '#{name}' does not appear on the insert" print("product verified:", name) }
check "Code position" tags: ["layout"] { bereich = region(400, 20, 180, 90, "barcode area") assert codes::validate_barcode_position(bereich), "code outside the reserved area — risk of being trimmed off" }}PDFL_DATA_DIR=./datenbanken pdfl run profile/beipackzettel.pdfl beipackzettel_v3.pdf12.4 Freigabe: Vergleich mit der freigegebenen Fassung
Abschnitt betitelt „12.4 Freigabe: Vergleich mit der freigegebenen Fassung“Das Problem: Der Kunde hat v1 freigegeben. v2 kommt mit „wir haben nur ein Wort geändert“. Das zu glauben, wird teuer.
# HTML, das zeigt, was sich tatsächlich geändert hatpdfl compare freigegeben/katalog_v1.pdf erhalten/katalog_v2.pdf \ --normalize --ignore-dates \ --output html --output-file unterschiede.html
echo "exit: $?" # 0 identisch · 1 nur Metadaten · 2 Inhalt geändertUm auch das Aussehen zu prüfen, nicht nur den Text:
profile "visual-fidelity" {
const FREIGEGEBEN = "freigegeben/katalog_v1.pdf"
check "Pages visually identical" tags: ["approval"] { doc.pages.each { |page| ssim = visual::measure_ssim(page.number, FREIGEGEBEN) assert ssim > 0.99, "page #{page.number} changed visually (SSIM #{ssim}, #{visual::pixel_diff(page.number, FREIGEGEBEN)}% of pixels)" } }
check "No image replaced" tags: ["approval"] { doc.pages.each { |page| assert !visual::detect_image_replacement(page.number, FREIGEGEBEN), "page #{page.number}: image swapped compared to the approved version" } }}Und ein Ordner zum Ansehen, für die Person, die den Nachdruck freigibt:
pdfl pixelcompare freigegeben/katalog_v1.pdf erhalten/katalog_v2.pdf \ --max-diff 0.05 --viewer pruefung/ --output-file pixels.json
zip -r pruefung.zip pruefung/ # eine index.html und drei PNGs je Seite, sonst nichtsDer Ordner braucht weder Server noch Netz: Wer index.html öffnet, sieht das
Original, die neue Datei und beide zusammen mit den eingefärbten Unterschieden,
und er öffnet auf den Seiten, die sich unterscheiden — bei einem Katalog, in
dem sich zwei von neunzig Seiten geändert haben, geht es genau um diese zwei.
Mausbewegung schiebt die neue Datei über die alte; das Rad zoomt alle drei
Bereiche gemeinsam und Ziehen bewegt sie, eine Haarlinie lässt sich also bei
8× klären, ohne die Seite zu verlassen.
12.5 CI/CD: Prüfung im Stapel
Abschnitt betitelt „12.5 CI/CD: Prüfung im Stapel“Das Problem: Jede Datei, die ins Repository kommt, muss die Vorstufenprüfung bestehen, ohne dass jemand sie von Hand startet.
.github/workflows/preflight.yml:
name: PDF preflight
on: [push, pull_request]
jobs: validate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Install pdfl env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatisches Actions-Token, keine Einrichtung run: | gh release download --repo kotarokikuchi/pdflang \ --pattern 'pdfl_*_amd64.deb' sudo dpkg -i pdfl_*_amd64.deb
- name: Check the scripts themselves run: | for f in profile/*.pdfl; do pdfl lint "$f" pdfl fmt "$f" --check done
- name: Preflight every PDF run: | pdfl watch dateien/ --script profile/offset.pdfl \ --output-dir berichte/ --once
- name: Publish the reports if: always() uses: actions/upload-artifact@v4 with: name: berichte path: berichte/
# Ein Artefakt muss jemand öffnen gehen. Eine Anmerkung am Pull Request # nicht. - name: Befunde am Pull Request run: | pdfl run profile/offset.pdfl dateien/umschlag.pdf \ --output sarif --output-file pdfl.sarif continue-on-error: true # Exit 2 heißt abgelehnte Datei; der Upload muss trotzdem laufen - uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: pdfl.sarif12.6 Eine Verlagsdatei für die Druckerei aufbereiten
Abschnitt betitelt „12.6 Eine Verlagsdatei für die Druckerei aufbereiten“check "Preconditions" { require doc.page_count > 0 assert !struct::check_encryption(), "file is encrypted — ask for the open version"}
// Produktionsrahmen, die der Verlag nicht gesetzt hatfix::set_trim_box(8.5, 8.5, 586.5, 833.5)fix::set_bleed_box(0, 0, 595, 842)
// Aufräumenfix::remove_annotations() // Korrekturkommentarefix::remove_attachments() // Anhänge, die nur beschwerenfix::flatten_layers() // verhindert versehentlich zugeschaltete Ebenenfix::remove_unused_resources()pdfl fix verlag.pdf profile/vorbereiten.pdfl --output druck.pdf --dry-run # prüfenpdfl fix verlag.pdf profile/vorbereiten.pdfl --output druck.pdf # anwendenpdfl run profile/offset.pdfl druck.pdf # validieren12.7 Ein Profil im Team verteilen
Abschnitt betitelt „12.7 Ein Profil im Team verteilen“Das Problem: Fünf Arbeitsplätze sollen genau dasselbe Profil und dieselben Daten benutzen, ohne dass jemand daran dreht.
# Auf dem Rechner, der das Profil pflegtpdfl pack profile/ --name druckprofil --version 1.2.0
# Auf den Produktionsrechnernpdfl add druckprofil.pdflpkg# installiert nach ./pdfl_profiles/[email protected]/, jede Prüfsumme geprüft
Wurde das Paket unterwegs verändert, verweigert add die Installation.
12.8 Einer problematischen Datei auf den Grund gehen
Abschnitt betitelt „12.8 Einer problematischen Datei auf den Grund gehen“Vorgehen, wenn unklar ist, woher das Problem kommt:
# 1. Überblick in Sekundenpdfl inspect verdaechtig.pdf
# 2. Untersuchungsskript, nur print()cat > untersuchung.pdfl <<'EOF'check "X-ray" { print("exact TAC:", prepress::calculate_exact_tac(), "%") print("estimated TAC:", prepress::calculate_tac(), "%") print("spots:", prepress::detect_spot_colors().join(", ")) print("rich black?", prepress::detect_rich_black()) print("overprint ok?", prepress::validate_overprint_settings()) print("loose fonts:", prepress::detect_text_substitution().join(", "))
doc.images.each { |img| print("image page", img.page_number, ":", img.width, "x", img.height, "@", round(img.dpi), "DPI", img.color_space) }}EOF
pdfl run untersuchung.pdfl verdaechtig.pdf > /dev/null# print() schreibt auf die Fehlerausgabe: Man wirft den Bericht weg# und behält nur die Ergebnisse der Untersuchung12.9 Ein Profil prüfen, bevor es jemanden eine Auflage kostet
Abschnitt betitelt „12.9 Ein Profil prüfen, bevor es jemanden eine Auflage kostet“Problem: ein Profil ist Code, und jemand ändert es. Ein Grenzwert wandert, ein Check wird umbenannt, und niemand merkt es, bis eine Datei, die hätte abgelehnt werden müssen, auf die Platte ging.
Behalten Sie die Dateien, die Ihnen die Regel beigebracht haben, und halten Sie fest, was das Profil über sie sagt:
profile/druckerei/ vorstufe.pdfl tests/ freigegeben.pdf # bestand, und muss weiter bestehen freigegeben.expected.json farbe_324.pdf # die Datei, die im März einen Nachdruck kostete farbe_324.expected.json schriften_nicht_eingebettet.pdf schriften_nicht_eingebettet.expected.json# Einmal, wenn die Fälle die richtigen sindpdfl test profile/druckerei/vorstufe.pdfl --update
# Von da an — in der CI und vor jedem Commit am Profilpdfl test profile/druckerei/vorstufe.pdfl --jobs 0Eine abgelehnte Datei ist ein ebenso guter Fall wie eine freigegebene: aufgezeichnet wird der ganze Bericht, der Test scheitert also genauso laut, wenn das Profil die 324% Farbauftrag nicht mehr bemängelt, wie wenn es eine einwandfreie Datei zu bemängeln beginnt.
- name: Die Profile finden noch, was sie fanden run: pdfl test profile/druckerei/vorstufe.pdfl --jobs 0Lesen Sie den Fehlschlag, bevor Sie neu aufzeichnen. --update ist der Moment,
in dem Sie entscheiden, dass das neue Verhalten richtig ist — einen anderen gibt
es nicht.