# 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"). **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` nur unter Vorbehalt.** `<>` – Kombi, die es bisher nicht gibt (`None` lässt `/BRColorMode` unangetastet, `Vivid` erzwingt CIE). Wird als Testdruck gegen `None` verglichen; nur übernommen, wenn der Ausdruck deutlich besser ist. Spezifikation hängt nicht davon ab (Default bleibt `None`, Überschreibung ist ohnehin möglich). ## 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 -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 `VividNoCIE` mehr Sättigung liefert als `None` – wird im Tasks-Test entschieden; ändert weder Spec (Default bleibt `None`) noch Aufbau. - 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).