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.
8.7 KiB
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-RendererCUPS_PDFTOPS_RENDERER=hybrid;hybridwechselt 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 nurWARNING: Grayscale/monochrome could be printed as color with Popplergeloggt. Deshalb ist ein serverseitiger Grauzwang überpdftops-renderer=gszwar technisch möglich, aber ausgeschlossen (siehe Decisions).- CUPS-Optionsspalte:
cupsMarkOptions()(cups/ppd-mark.c) bildetprint-color-modeundprint-qualityausschließlich über Presets ab, die_ppdCacheCreateWithPPD()(cups/ppd-cache.c) ausAPPrinterPreset-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 nutztBRPrintQuality=Black), daher wirdprint-color-mode=monochromeheute stillschweigend verworfen. PPD-spezifische Optionen wieBRColorMode/BRMediaType/TonerSaveModesind 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-sharedund-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=draftdruckt sichtbar weniger Toner („blass")] → bewusst die Semantik des Reglers; dokumentiert, per Job und überPRINTER_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_OPTIONSwird bei falscher Schreibweise ignoriert] → Warning im Log,lpoptions -p <queue> -lals 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
- Image neu bauen (
podman build), Container neu starten (Queue wird ohnehin neu erzeugt bzw. Defaults überschrieben). - Akzeptanztest: S/W-Druck aus dem macOS-Dialog, Farbdruck-Vergleich mit
und ohne
PRINTER_OPTIONS. - Rollback: Image auf den vorherigen Commit bauen; Einträge in der PPD
sind mit demselben Revert rückgängig zu machen,
PRINTER_OPTIONSbei Bedarf leer lassen.
Open Questions
- Ob macOS/iOS den Qualitätsregler als
print-qualitysendet – falls nicht, bleiben dieprint-quality-Presets inert,print-color-mode-Presets wirken unabhängig davon (Server-seitig perlp -o print-quality=…testbar).