Files
juergen 30670de93b
ci/woodpecker/push/build Pipeline was successful
fix pale colours and map driverless print options
Set *DefaultBRColorMode: None in the vendor PPD so every job sends
<</UseCIEColor false>>: Brother's Normal/Vivid modes enable the printer's
own CIE conversion, which washed the colours out.

Add six APPrinterPreset entries mapping print-color-mode and
print-quality from driverless (AirPrint/IPP) clients onto the Brother
device options; they use the notation CUPS generates itself (Apple
metadata as 'key value' pairs, PPD options as '*Name Value'), because
key=value strings are silently discarded. print-quality is 3/4/5.

Add the PRINTER_OPTIONS env var: whitespace-separated key=value pairs
applied as queue defaults on every start via lpadmin -o, validated
against the queue's PPD, with warnings instead of a failed startup.

Document all of it in the README. The VividNoCIE candidate was dropped
after test prints showed no difference from None. Archive the OpenSpec
change and sync the printserver/cups spec with the two new
requirements.
2026-10-20 08:28:08 +02:00

8.7 KiB
Raw Permalink Blame History

Design

Context

Motivation und Messergebnisse stehen in proposal.md; hier nur die technischen Randbedingungen, die den Ansatz bestimmen:

  • Jobkette für einen AirPrint-Job: macOS/iOS → IPP (PDF) → pdftopdf → pdftops → pstops (PPD-Optionen, JCL/PJL) → socket://9100:9100 → Drucker.
  • pdftops (cups-filters 1.28) läuft mit Default-Renderer CUPS_PDFTOPS_RENDERER = hybrid; hybrid wechselt für Brother ausdrücklich auf Poppler, um Bugs im PS-Interpreter des Druckers zu umgehen (filter/pdftops.c, „Switching to Poppler's pdftops instead of Ghostscript for Brother …"). Poppler kann keine Graustufen erzeugen – es wird nur WARNING: Grayscale/monochrome could be printed as color with Poppler geloggt. Deshalb ist ein serverseitiger Grauzwang über pdftops-renderer=gs zwar technisch möglich, aber ausgeschlossen (siehe Decisions).
  • CUPS-Optionsspalte: cupsMarkOptions() (cups/ppd-mark.c) bildet print-color-mode und print-quality ausschließlich über Presets ab, die _ppdCacheCreateWithPPD() (cups/ppd-cache.c) aus APPrinterPreset-Attributen der PPD – oder als Fallback aus einem bekannten Grau-Optionsschlüssel (ColorModel=Gray, BRMonoColor=Mono, HPColorMode=grayscale, …) – aufbaut. Die BR-Script3-PPD enthält keines davon (Brother nutzt BRPrintQuality=Black), daher wird print-color-mode=monochrome heute stillschweigend verworfen. PPD-spezifische Optionen wie BRColorMode/BRMediaType/TonerSaveMode sind darüber hinaus keine IPP-Attribute und erscheinen im driverless AirPrint-Dialog grundsätzlich nicht.
  • Die Queue wird bei jedem Containerstart neu erzeugt (kein Persistenz-Volume vorausgesetzt), Queue-Defaults müssen deshalb bei jedem Start erneut gesetzt werden – so wie bereits printer-is-shared und -D.

Goals / Non-Goals

Goals:

  • Gesättigte Farbausgabe als Zustand (Default BRColorMode=None).
  • S/W und Qualitätsstufe von macOS/iOS wirken geräteitig, ohne von der PDF-Renderer-Wahl abzuhängen.
  • PPD-Optionen, die der Dialog nicht zeigen kann, dauerhaft über die Umgebung konfigurierierbar machen (PRINTER_OPTIONS).
  • Nichts am Containerfile, an Discovery/Zertifikat oder am Windows-Weg ändern.

Non-Goals:

  • Brother-spezifische Optionen (Vivid, Medientyp, Toner Save) im AirPrint-Dialog sichtbar machen – mangels IPP-Äquivalent nicht darstellbar; stattdessen serverseitige Defaults + Doku.
  • Serverseitige Graustufen-Konvertierung (Poppler-Pfad) oder Wechsel des Renderers.
  • Änderung an der Drucker- oder Panel-Konfiguration selbst.

Decisions

1. Abbildung über APPrinterPreset in der PPD (statt ColorModel-Option, Renderer-Wechsel oder entrypoint-Logik).

Sechs Presets – Kombination aus com.apple.print.preset.output-mode (color/monochrome) und com.apple.print.preset.quality (low/normal/high) – deren PPD-Optionen CUPS bei passendem Job-Attribut automatisch mitmarkiert:

Preset (mode/quality) PPD-Optionen
color / low TonerSaveMode=True
color / normal TonerSaveMode=False
color / high CAPT=SuperFine TonerSaveMode=False
monochrome / low BRPrintQuality=Black TonerSaveMode=True
monochrome / normal BRPrintQuality=Black TonerSaveMode=False
monochrome / high BRPrintQuality=Black CAPT=SuperFine TonerSaveMode=False

Alternativen verworfen: (a) ColorModel-Option mit Choice Gray anlegen – löst zwar ebenfalls das Mono-Preset, verändert aber zusätzlich die Werbung/Defaults von print-color-mode und die cupsColorSpace-Semantik in pstops/Raster-Pfaden; (b) pdftops-renderer=gs als Queue-Default – bringt den Grauzwang zurück, riskiert aber genau die PS-Interpreter-Bugs, wegen derer hybrid bei Brother Poppler wählt; (c) Grauzwang im entrypoint per lpadmin -o – unmöglich, da die Konvertierung im Filter passiert und Poppler sie nicht kann.

CUPS vergibt Preset-Optionen nur, wenn der Client den Namen nicht selbst geschickt hat – clientseitige Auswahl gewinnt also weiterhin (ppd-mark.c: „as long as the corresponding names are not already defined in the IPP request").

Preset-Notation (2026-10-20 empirisch geklärt). Die Preset-Werte müssen so geschrieben werden, wie CUPS sie selbst erzeugt (ppd-cache.c, Funktion cupsFilePrintf fp "*APPrinterPreset %s: \""): eine mehrzeilige Zeichenkette, in der Apple-Metadaten als key value-Paare ohne = und PPD-Optionen als *Name Value-Paare stehen, jeweils durch Leerzeichen getrennt:

*APPrinterPreset MonoNormal/B&W, Normal: "
  com.apple.print.preset.output-mode monochrome
  com.apple.print.preset.quality normal
  *BRPrintQuality Black
  *TonerSaveMode False
"

_ppdCacheCreateWithPPD() parst den Wert mit _ppdParseOptions(), das an Whitespace trennt – key=value in einer Zeile würde den Namen key=value ergeben und der Lookup (cupsGetOption("com.apple.print.preset. quality", …)) schlüge fehl. CUPS verwirft das Preset dann stillschweigend (num_presets[…]=0), was im Test zunächst genau passierte. Verifiziert mit einem libcups-2.4.2-Harness (_ppd_cache_t inspiziert) und im pstops-Filterpfad. Zusätzlich: IPP-print-quality ist 3/4/5 (Draft/Normal/High), nicht 2/5.

2. PPD direkt im Repository ändern (nicht zur Build-Zeit patchen).

Die Asset-Datei assets/Brother HL-4150CDN/ppd/BR4150_2.PPD wird bereits gecommitted und vom Containerfile unverändert nach /usr/share/cups/model kopiert. Die Änderungen (*DefaultBRColorMode: None, Presets, ggf. neue Choice) werden als PPD-konforme Einträge mit %-Kommentar ergänzt, die Herkunft des Files bleibt dokumentiert. Alternative verworfen: sed-Patch im Containerfile – zweite Quellwahrheit, schwerer zu reviewen, und COPY + RUN-Logik müsste ohnehin getestet werden.

3. Farbmodus-Default in der PPD statt als Entry-Point-Sonderfall.

*DefaultBRColorMode: None wirkt für jede aus dieser PPD erzeugte Queue, auch für manuell angelegte, ohne dass entrypoint.sh etwas davon wissen muss. PRINTER_OPTIONS bleibt damit generisch (Default leer) und dient nur der Konfiguration, nicht dem Fix.

4. PRINTER_OPTIONS-Parsing und Anwendung.

Leerzeichengetrennte key=value-Tokens (Brother-Choice-Werte enthalten keine Leerzeichen; getrennt wird auch an Zeilenumbrüchen). Jedes Token wird einzeln als lpadmin -o gesetzt, direkt an der Stelle, an der heute printer-is-shared, -D und -d gesetzt werden, und bei jedem Start – damit gilt es auch für eine persistente Queue. Fehler pro Token abfangen (|| log warning), da set -eu sonst den Start abbricht; unbekannte Namen werden verworfen, ohne die übrigen zu blockieren.

5. Neue BRColorMode-Choice VividNoCIE verworfen.

<</UseCIEColor false /BRColorMode 1>> – Kombi, die es bisher nicht gibt (None lässt /BRColorMode unangetastet, Vivid erzwingt CIE). Als Testdruck gegen None verglichen (2026-10-20): nicht unterscheidbar, daher nicht übernommen. None bleibt der einzige geänderte Default.

Risks / Trade-offs

  • [PPD weicht dauerhaft vom Brother-Original ab] → Änderungen mit %-Kommentar markieren, der README-Abschnitt „Änderungen an der PPD" nennt Grund und Stelle; Upstream-Vergleich bleibt durch die unveränderte Herkunft im Header möglich.
  • [print-quality=draft druckt sichtbar weniger Toner („blass")] → bewusst die Semantik des Reglers; dokumentiert, per Job und über PRINTER_OPTIONS überschreibbar.
  • [Mono wird jetzt geräteitig gerendert (/BRProcessColor 1), nicht mehr gar nicht] → Grau-Wiedergabe des Druckers ist die einzige realistische Route mit Poppler-Renderer; Akzeptanztest im Tasks.
  • [PRINTER_OPTIONS wird bei falscher Schreibweise ignoriert] → Warning im Log, lpoptions -p <queue> -l als Referenz in der Doku genannt.
  • [Persistentes Volume: Defaults könnten aus altem Stand zurückkommen] → Neu-Setzen bei jedem Start überschreibt sie deterministisch.
  • [MacOS-Caches bei Drucker-Änderungen] → keine Client-Änderung nötig; Server-Attribute bleiben gleich, nur Job-Verarbeitung ändert sich.

Migration Plan

  1. Image neu bauen (podman build), Container neu starten (Queue wird ohnehin neu erzeugt bzw. Defaults überschrieben).
  2. Akzeptanztest: S/W-Druck aus dem macOS-Dialog, Farbdruck-Vergleich mit und ohne PRINTER_OPTIONS.
  3. Rollback: Image auf den vorherigen Commit bauen; Einträge in der PPD sind mit demselben Revert rückgängig zu machen, PRINTER_OPTIONS bei Bedarf leer lassen.

Open Questions

  • Ob macOS/iOS den Qualitätsregler als print-quality sendet – falls nicht, bleiben die print-quality-Presets inert, print-color-mode-Presets wirken unabhängig davon (Server-seitig per lp -o print-quality=… testbar).