ci/woodpecker/push/build Pipeline was successful
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.
181 lines
8.7 KiB
Markdown
181 lines
8.7 KiB
Markdown
# 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).
|