Druckdaten als Code: Visitenkarte & Flyer als PDF/X-4

Wie ich Visitenkarte, Flyer, Postkarte und Mauerschild per Python setze: Schrift in Pfaden, nur CMYK, PDF/X-4 mit FOGRA51 – und Prüfungen im Skript.

Vorder- und Rückseite der neuen syfrt.de-Visitenkarte in der dunklen Variante: Wortmarke SYFRT., Claim, Kontaktdaten und QR-Code

Meine alten Werbemittel lagen als SVG-Dateien vor: Text mit dem Schriftnamen „Archivo“, aber ohne eingebettete Schrift, Farben in RGB, Verläufe, auf dem Flyer Schrift ab 4,5 pt. Für den Bildschirm reicht das. Eine Druckerei rendert so etwas mit Ersatzschrift oder lehnt es im Preflight ab.

Als ich diese Woche den Claim von syfrt.de geändert habe, hätte ich jedes Werbemittel einzeln anfassen müssen. Stattdessen setze ich Visitenkarte, Flyer A5, Postkarte A6 und Mauerschild jetzt per Python aus einer Quelle. Ein Aufruf erzeugt alle Druck-PDFs und die Bildschirm-Vorschauen.

Das Ziel: Dateien, die jedes Preflight besteht

  • nur DeviceCMYK, keine RGB-Werte,
  • keine Schriften im PDF – jeder Buchstabe ist ein Pfad,
  • TrimBox = Endformat, BleedBox = Datenformat mit Beschnitt, je Druckerei passend,
  • PDF/X-4 mit eingebettetem Farbprofil der Druckbedingung (PSO Coated v3, FOGRA51).

Schrift wird zu Pfaden

Die Schrift liegt ohnehin im Projekt: Archivo als variable Schrift aus node_modules (über @fontsource-variable/archivo), mit Gewichts- und Breitenachse. HarfBuzz setzt den Text mit Kerning, fontTools zeichnet jede Glyphe als Kontur:

import uharfbuzz as hb
from fontTools.pens.transformPen import TransformPen

font = hb.Font(face)
font.set_variations({"wght": 700, "wdth": 110})
puffer = hb.Buffer()
puffer.add_str("Sascha Seyfert")
puffer.guess_segment_properties()
hb.shape(font, puffer, {"kern": True, "liga": True})

f = pt * (25.4 / 72) / face.upem        # Font-Einheiten -> mm
lauf = 0
for info, pos in zip(puffer.glyph_infos, puffer.glyph_positions):
    font.draw_glyph_with_pen(info.codepoint,
                             TransformPen(stift, (f, 0, 0, -f, x + lauf * f, y)))
    lauf += pos.x_advance

Die Wortmarke SYFRT. kommt nicht aus der Schrift, sondern aus den freigegebenen Logo-Pfaden; der QR-Code entsteht mit segno als einzelne Modul-Quadrate. Am Ende besteht jede Seite nur aus Flächen und Linien mit festen CMYK-Werten.

Farben, die im Druck halten

ElementCMYKWarum
Hintergrund dunkel60/40/40/100Tiefschwarz, 240 % Gesamtfarbauftrag – unter der Grenze von 300 % des Profils
syfrt-Rot0/92/68/19Richtwert aus dem Corporate-Design-Handbuch, per Proof festlegen
Schrift auf Hell0/0/0/100nur Schwarzplatte – kein Passer-Risiko bei kleiner Schrift
Haarlinie auf Dunkel0/0/0/80eine Platte statt vier ausgesparter Farben

reportlab zeichnet die Pfade, setzt Trim- und BleedBox und schreibt pro Druckerei eine Datei. Eine Eigenheit musste ich nachbessern: reportlab legt auf jeder Seite eine ungenutzte Helvetica als Ressource an. Das Preflight meldet dann „Schrift nicht eingebettet“, obwohl gar kein Text im PDF steht. pikepdf entfernt die Reste nach dem Speichern.

PDF/X-4 in wenigen Zeilen

Das Farbprofil PSO Coated v3 stellt die European Color Initiative (ECI) bereit. Eingebettet wird es als OutputIntent, dazu kommen XMP-Metadaten mit pdfxid:GTS_PDFXVersion = PDF/X-4 und die Angabe Trapped = False:

profil = doc.make_stream(Path("marketing/icc/PSOcoated_v3.icc").read_bytes())
profil.N = 4
doc.Root.OutputIntents = pikepdf.Array([pikepdf.Dictionary(
    Type=pikepdf.Name.OutputIntent, S=pikepdf.Name.GTS_PDFX,
    OutputConditionIdentifier="FOGRA51", RegistryName="http://www.color.org",
    Info="PSO Coated v3", DestOutputProfile=profil)])
doc.docinfo["/Trapped"] = pikepdf.Name("/False")
doc.save(pfad, min_version="1.6")

Der Preis: Jedes PDF wird um rund 1,7 MB größer, weil das Profil mitreist. Und eine ehrliche Einschränkung: Geprüft habe ich die Dateien strukturell (Version, OutputIntent, Metadaten, Farboperatoren), nicht mit einem Acrobat-Preflight. Das ersetzt der Proof der Druckerei.

Prüfungen im Skript statt Kontrolle mit dem Auge

Was beim Satz schiefgehen kann, prüft das Skript bei jedem Lauf und bricht sonst ab:

  • Sicherheitsabstand: Kein Inhalt näher als 5 mm am Schnitt (Visitenkarte 6,7 mm, Flyer 13,9 mm).
  • Dialogpost-Postkarte: Frankierzone oben rechts (74 × 40 mm) und Codierzone unten (15 mm) bleiben leer.
  • Mauerschild: Um jede der vier Bohrungen bleiben 15 mm frei für die Abstandshalter.
  • QR-Codes: Jede Seite wird gerendert und der QR-Code mit OpenCV wieder ausgelesen.
for x0, y0, x1, y1 in ZONEN_DIALOGPOST:   # Frankier- und Codierzone
    for px, py in alle_punkte(elemente):
        assert not (x0 <= px <= x1 and y0 <= py <= y1), "Inhalt in freizuhaltender Zone"

Die QR-Prüfung hat tatsächlich etwas gefunden: Auf der Dialogpost-Rückseite war der Code mit 18 mm für die lange Adresse mit Tracking-Parametern zu klein – Version 7, 0,34 mm je Modul, bei 300 dpi für die Prüfung nicht mehr lesbar. Mit 21 mm (0,40 mm je Modul) liest das Skript ihn zuverlässig – mit dem Handy prüfe ich das am Proof noch einmal.

Satz ohne Hurenkinder

Fließtext umbricht das Skript selbst. Zwei Regeln reichen für sauberen Flattersatz: Steht in der letzten Zeile nur ein Wort, wird die Satzbreite schrittweise verringert, bis sich die Zeilen ausgleichen. Und geschützte Leerzeichen halten zusammen, was zusammengehört – Kundennamen wie „Physiotherapie Anja Kretschmar“ und der Mittelpunkt, der nie am Zeilenanfang stehen soll.

Was es bringt

Aus derselben Quelle entstehen jetzt 18 Druck-PDFs (zwei Druckereien, dunkle und helle Varianten, Lackmaske für die Visitenkarte, Dialogpost-Postkarte, zwei Schild-Ausführungen) und die Vorschauen für Website und Handbuch. Als ich die gemeinsamen Bausteine aus dem Visitenkarten-Skript in ein Modul ausgelagert habe, habe ich die Ergebnisse vorher und nachher Pixel für Pixel verglichen – identisch. Ändert sich der Claim, eine Telefonnummer oder ein Farbwert, ist das eine Zeile Code und ein neuer Lauf.

Genau so gehe ich auch bei Websites vor: eine Quelle, automatische Prüfungen, nachvollziehbare Änderungen. Wenn Sie Druckdaten oder Werbemittel haben, die zur Website passen sollen, schreiben Sie mir.

Häufige Fragen

Warum Schrift in Pfade umwandeln, statt sie einzubetten?

Pfade sind für die Druckerei nur noch Flächen: Es gibt keine fehlende oder ersetzte Schrift mehr im Preflight. Der Nachteil – Text ist im PDF nicht mehr bearbeitbar – fällt weg, wenn das Skript die Quelle ist und das PDF bei jeder Änderung neu entsteht.

Was macht ein PDF zum PDF/X-4?

PDF/X-4 (ISO 15930-7) verlangt unter anderem ein eingebettetes Farbprofil der Druckbedingung als OutputIntent, Trim- und BleedBox auf jeder Seite, eine Kennzeichnung in den XMP-Metadaten und eine Angabe zum Trapping. Transparenzen sind erlaubt, Verschlüsselung nicht.

Wie viel Beschnitt brauchen Druckdaten?

Das gibt die Druckerei je Produkt vor. Für meine Visitenkarte verlangt WIRmachenDRUCK 3 mm und Flyeralarm 1 mm je Seite – deshalb erzeugt das Skript je Druckerei eine eigene Datei. Vor dem Upload immer mit dem aktuellen Datenblatt abgleichen.


Fragen zum Thema? Schreiben Sie mir – ich antworte in der Regel innerhalb eines Werktags.