From 30670de93ba88040cde3643740225a9914e595aa Mon Sep 17 00:00:00 2001 From: Juergen Holly Date: Tue, 20 Oct 2026 07:38:41 +0200 Subject: [PATCH] fix pale colours and map driverless print options Set *DefaultBRColorMode: None in the vendor PPD so every job sends <>: 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. --- README.md | 100 +++++++++++++++++- assets/Brother HL-4150CDN/ppd/BR4150_2.PPD | 47 +++++++- entrypoint.sh | 48 +++++++++ .../.openspec.yaml | 0 .../design.md | 35 ++++-- .../proposal.md | 8 +- .../specs/printserver/cups/spec.md | 0 .../tasks.md | 63 +++++++---- openspec/specs/printserver/cups/spec.md | 51 +++++++++ 9 files changed, 319 insertions(+), 33 deletions(-) rename openspec/changes/{fix-colour-defaults-and-client-options => archive/2026-10-20-fix-colour-defaults-and-client-options}/.openspec.yaml (100%) rename openspec/changes/{fix-colour-defaults-and-client-options => archive/2026-10-20-fix-colour-defaults-and-client-options}/design.md (85%) rename openspec/changes/{fix-colour-defaults-and-client-options => archive/2026-10-20-fix-colour-defaults-and-client-options}/proposal.md (94%) rename openspec/changes/{fix-colour-defaults-and-client-options => archive/2026-10-20-fix-colour-defaults-and-client-options}/specs/printserver/cups/spec.md (100%) rename openspec/changes/{fix-colour-defaults-and-client-options => archive/2026-10-20-fix-colour-defaults-and-client-options}/tasks.md (52%) diff --git a/README.md b/README.md index b5856c0..0111f5a 100644 --- a/README.md +++ b/README.md @@ -85,11 +85,26 @@ podman run -d --name printserver \ | `PRINTER_NAME` | `HL4150CDN` | CUPS queue name. Also used as the name advertised via AirPrint/DNS-SD. | | `PRINTER_DESCRIPTION` | *(empty)* | CUPS printer description (`lpadmin -D`), shown in the CUPS web UI. | | `PPD_FILE` | `/usr/share/cups/model/BR4150_2.PPD` | PPD used for the queue. | +| `PRINTER_OPTIONS` | *(empty)* | Space-separated PPD option assignments (`key=value`) applied as queue defaults on every start, e.g. `BRColorMode=Vivid TonerSaveMode=False`. Discover the names with `lpoptions -p -l`. | | `CERT_EXTRA_NAMES` | *(empty)* | Comma-separated extra DNS names and/or IP addresses to put into the generated `_ipps` certificate. | | `CERT_DAYS` | `3650` | Validity of the generated certificate, in days. | No queue is created when neither `PRINTER_IP` nor `PRINTER_URI` is set. +`PRINTER_OPTIONS` sets queue defaults that survive the recreation of the queue, +because they are re-applied on every start: + +```sh +podman run -d --name printserver \ + --network host \ + -e PRINTER_IP=192.168.100.7 \ + -e PRINTER_OPTIONS="BRMediaType=Plain BRColorMode=Vivid TonerSaveMode=False" \ + printserver:latest +``` + +Entries that the PPD does not know are logged as a warning and ignored; the +remaining ones are applied and the container starts normally. + ### Choosing the connection The HL-4150CDN accepts PostScript directly, so the default is raw AppSocket. @@ -187,7 +202,7 @@ The BR-Script3 PPD exposes the HL-4150CDN's hardware options. Common ones: | Paper source | `InputSlot` | `AutoSelect`, `Tray1`, `Tray2`, `MPTray` | | Two-sided printing | `Duplex` | `None`, `DuplexNoTumble` (long edge), `DuplexTumble` (short edge) | | Colour / mono | `BRPrintQuality` | `Auto`, `Color`, `Black` | -| Colour mode | `BRColorMode` | `Normal`, `Vivid`, `None` | +| Colour mode | `BRColorMode` | `Normal`, `Vivid`, `None` (default) | | Media type | `BRMediaType` | `Plain`, `Thin`, `Thick`, `Recycled`, `Glossy`, `Label`, envelopes, … | | Print quality | `CAPT` | `Fine` (600×600), `SuperFine` (2400 dpi class) | | Toner save | `TonerSaveMode` | `True`, `False` | @@ -200,6 +215,57 @@ podman exec printserver lp -d HL4150CDN \ -o Duplex=DuplexNoTumble -o BRPrintQuality=Color -o InputSlot=Tray1 report.pdf ``` +### Why the macOS/iOS dialog shows fewer options + +macOS and iOS print driverless (AirPrint / IPP Everywhere), so the dialog shows +only IPP-standard options: media size, duplex, pages per sheet, the +black-and-white/colour toggle and the quality slider. Brother-specific options +(`BRColorMode`, `BRMediaType`, `TonerSaveMode`, `CAPT`, …) have no IPP +equivalent and can never appear there. Set them server-side instead: + +- as queue defaults at container start via `PRINTER_OPTIONS` (see + *Environment variables*), +- per job with `lp -o …`, or +- interactively in the CUPS web UI (`http://:631/admin`, *Printing Options*). + +The dialog's black-and-white/colour toggle maps to `print-color-mode` and the +quality slider to `print-quality`; the PPD presets documented below translate +both onto the Brother options, so they now really take effect: + +| Dialog / IPP | PPD option it becomes | +| --- | --- | +| `print-color-mode=monochrome` | `BRPrintQuality=Black` | +| `print-color-mode=color` | `BRPrintQuality=Auto` | +| `print-quality=3` (draft) | `TonerSaveMode=True` | +| `print-quality=4` (normal) | defaults (`Fine`, toner save off) | +| `print-quality=5` (high) | `CAPT=SuperFine` (2400 dpi class) | + +Client-specified values always win over the preset. + +### Changes to the vendor PPD + +`assets/Brother HL-4150CDN/ppd/BR4150_2.PPD` is Brother's original file plus +two additions, each marked with a `%` comment in the file: + +- **`*DefaultBRColorMode: None` instead of `Normal`.** `Normal` and `Vivid` both + set `<>`; the printer's own CIE conversion washes the + colours out (see [Colours are printed pale / washed out](#colours-are-printed-pale--washed-out)). +- **Six `APPrinterPreset` entries** mapping `print-color-mode` + (color/monochrome) and `print-quality` (draft/normal/high) onto + `BRPrintQuality`, `CAPT` and `TonerSaveMode`. CUPS applies such presets for + driverless clients and always lets the client's own option values win. In + particular `print-color-mode=monochrome` becomes `BRPrintQuality=Black`, so + black-and-white printing works: the server cannot do the greyscale conversion + itself, because `pdftops` uses Poppler for Brother printers and Poppler + cannot produce greyscale (`WARNING: Grayscale/monochrome could be printed as + color with Poppler` in the CUPS log). The presets use the notation CUPS + generates itself — Apple metadata as `key value` pairs *without* `=`, PPD + options as `*Name Value` pairs on separate lines inside a multiline + `"…"` string. With `key=value` on a single line CUPS silently ignores the + whole preset (verified against libcups 2.4.2: `_ppdParseOptions()` splits on + whitespace, so `key=value` becomes the option *name* and the lookup fails). + Note that IPP `print-quality` is `3` (draft), `4` (normal), `5` (high). + ## Client support | Platform | Path | Notes | @@ -384,6 +450,38 @@ still being rejected — fix that, or add the queue from the command line as above. macOS caches the failed attempt, so remove and re-add the printer after changing anything on the server. +### Colours are printed pale / washed out + +Symptom: colour printouts look desaturated compared to the same PDF printed +directly from the Mac, or with Brother's own driver. + +Cause: the PPD colour mode `Normal` (Brother's default) makes the printer's +PostScript interpreter apply its own CIE conversion (`<>`), +which washes colours out. `Vivid` sets `UseCIEColor` as well, so it does not +help either; `BRPrintQuality` (`BRColorAdapt`) and `TonerSaveMode` made no +visible difference in a side-by-side test. The container therefore ships the +PPD with `*DefaultBRColorMode: None` (`UseCIEColor` off), which produces +saturated output. + +Check and, if needed, override the current default: + +```sh +podman exec printserver lpoptions -p -l | grep BRColorMode # expect *None +podman exec printserver lp -d -o BRColorMode=None test.pdf +``` + +If output is still pale with `BRColorMode=None`, check the printer's own menu +(Toner → Toner Save off) and make sure `BRMediaType` matches the media in the +tray. + +Black-and-white is a related but separate path: `print-color-mode=monochrome` +from a driverless client is mapped by the PPD presets onto +`BRPrintQuality=Black` (the printer renders mono). It is deliberately *not* done +by the server — with its Poppler renderer, `pdftops` cannot convert to +greyscale. Forcing Ghostscript with `-o pdftops-renderer=gs` would, but that +path is not recommended for Brother printers (bugs in their PostScript +interpreters). + ## CUPS 3 and the future of PPDs Adding the queue with a PPD makes `lpadmin` print: diff --git a/assets/Brother HL-4150CDN/ppd/BR4150_2.PPD b/assets/Brother HL-4150CDN/ppd/BR4150_2.PPD index 6e84215..b53d98b 100644 --- a/assets/Brother HL-4150CDN/ppd/BR4150_2.PPD +++ b/assets/Brother HL-4150CDN/ppd/BR4150_2.PPD @@ -374,9 +374,12 @@ restore *CloseUI: *BRPrintQuality *% Color Mode Information =============== +*% Modified by this repo (see README, "Changes to the vendor PPD"): +*% - default switched from Normal to None: with UseCIEColor off the printer +*% skips its CIE conversion, which otherwise washes out the colours. *OpenUI *BRColorMode/Colour Mode: PickOne *OrderDependency: 131.0 AnySetup *BRColorMode -*DefaultBRColorMode: Normal +*DefaultBRColorMode: None *BRColorMode Normal/Normal: " <>setpagedevice <>setpagedevice @@ -393,6 +396,48 @@ restore *End *CloseUI: *BRColorMode +*% Added by this repo: presets mapping the IPP attributes of driverless +*% (AirPrint/IPP Everywhere) clients onto the device options below. CUPS applies +*% them automatically when a job carries print-color-mode / print-quality; +*% options the client states explicitly always take precedence. Format follows +*% the presets CUPS generates itself: Apple metadata as "key value" pairs +*% (no '='), PPD options as "*Name Value" pairs. +*APPrinterPreset ColorDraft/Colour, Draft: " + com.apple.print.preset.output-mode color + com.apple.print.preset.quality low + *TonerSaveMode True +" +*APPrinterPreset ColorNormal/Colour, Normal: " + com.apple.print.preset.output-mode color + com.apple.print.preset.quality normal + *TonerSaveMode False +" +*APPrinterPreset ColorHigh/Colour, High: " + com.apple.print.preset.output-mode color + com.apple.print.preset.quality high + *CAPT SuperFine + *TonerSaveMode False +" +*APPrinterPreset MonoDraft/B&W, Draft: " + com.apple.print.preset.output-mode monochrome + com.apple.print.preset.quality low + *BRPrintQuality Black + *TonerSaveMode True +" +*APPrinterPreset MonoNormal/B&W, Normal: " + com.apple.print.preset.output-mode monochrome + com.apple.print.preset.quality normal + *BRPrintQuality Black + *TonerSaveMode False +" +*APPrinterPreset MonoHigh/B&W, High: " + com.apple.print.preset.output-mode monochrome + com.apple.print.preset.quality high + *BRPrintQuality Black + *CAPT SuperFine + *TonerSaveMode False +" + *% Improve Gray Color Information =============== *OpenUI *BRImprovedGray/Improve Grey Colour: PickOne *OrderDependency: 132.0 AnySetup *BRImprovedGray diff --git a/entrypoint.sh b/entrypoint.sh index 2f79417..e47c1f6 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -18,6 +18,9 @@ # PRINTER_NAME CUPS queue name and advertised (DNS-SD) name. # PRINTER_DESCRIPTION CUPS Description (lpadmin -D) shown in the web UI. # PPD_FILE PPD used for the queue. +# PRINTER_OPTIONS Space-separated PPD option assignments (key=value) +# applied as queue defaults on every start, e.g. +# "BRColorMode=Vivid TonerSaveMode=False". # CERT_EXTRA_NAMES Comma-separated extra DNS names / IP addresses to put # into the generated IPPS certificate (server SANs). # CERT_DAYS Validity of the generated certificate (default 3650). @@ -29,6 +32,7 @@ PRINTER_URI="${PRINTER_URI:-}" PRINTER_PROTOCOL="${PRINTER_PROTOCOL:-socket}" PRINTER_DESCRIPTION="${PRINTER_DESCRIPTION:-}" PPD_FILE="${PPD_FILE:-/usr/share/cups/model/BR4150_2.PPD}" +PRINTER_OPTIONS="${PRINTER_OPTIONS:-}" LOG_PREFIX="[entrypoint]" log() { echo "${LOG_PREFIX} $*"; } @@ -331,6 +335,50 @@ if lpstat -p "${PRINTER_NAME}" >/dev/null 2>&1; then log "queue ${PRINTER_NAME} is shared and set as default" fi +# Apply optional queue defaults from PRINTER_OPTIONS (see README). Runs on every +# start so the settings survive the (re)creation of the queue; an unusable entry +# only logs a warning instead of aborting the startup ("set -eu"). Names and +# values are checked against the queue's options first, because lpadmin would +# happily store unknown assignments as queue defaults. +apply_printer_options() { + [ -n "${PRINTER_OPTIONS}" ] || return 0 + if ! lpstat -p "${PRINTER_NAME}" >/dev/null 2>&1; then + log "warning: queue ${PRINTER_NAME} does not exist; skipping PRINTER_OPTIONS" + return 0 + fi + ppd_options="$(lpoptions -p "${PRINTER_NAME}" -l 2>/dev/null || true)" + for opt in ${PRINTER_OPTIONS}; do + case "${opt}" in + ?*=*) ;; + *) + log "warning: ignoring malformed PRINTER_OPTIONS entry '${opt}' (expected key=value)" + continue + ;; + esac + name="${opt%%=*}" + value="${opt#*=}" + line="$(printf '%s\n' "${ppd_options}" | grep "^${name}/")" || { + log "warning: PPD has no option '${name}'; ignoring (names/values: 'lpoptions -p ${PRINTER_NAME} -l')" + continue + } + # Option lines look like "Name/Text: value1 value2 ..." ('*' = default). + values="$(printf '%s\n' "${line#*: }" | tr ' ' '\n' | sed 's/^\*//')" + if ! printf '%s\n' "${values}" | grep -qxF "${value}" \ + && ! { case "${value}" in Custom.*) + printf '%s\n' "${values}" | grep -q 'Custom\.' + ;; *) false ;; esac; }; then + log "warning: PPD option '${name}' has no value '${value}'; ignoring" + continue + fi + if lpadmin -p "${PRINTER_NAME}" -o "${opt}" >/dev/null 2>&1; then + log "queue default ${opt} applied" + else + log "warning: lpadmin -o ${opt} failed; ignoring" + fi + done +} +apply_printer_options + # --- Verify CUPS is reachable on the network -------------------------------- # cupsd also runs with nothing but its domain socket, e.g. when it cannot bind # the privileged port 631 (rootless Podman with host networking). It then still diff --git a/openspec/changes/fix-colour-defaults-and-client-options/.openspec.yaml b/openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/.openspec.yaml similarity index 100% rename from openspec/changes/fix-colour-defaults-and-client-options/.openspec.yaml rename to openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/.openspec.yaml diff --git a/openspec/changes/fix-colour-defaults-and-client-options/design.md b/openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/design.md similarity index 85% rename from openspec/changes/fix-colour-defaults-and-client-options/design.md rename to openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/design.md index 77791e7..63f2573 100644 --- a/openspec/changes/fix-colour-defaults-and-client-options/design.md +++ b/openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/design.md @@ -85,6 +85,30 @@ 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 @@ -112,13 +136,12 @@ 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.** +**5. Neue `BRColorMode`-Choice `VividNoCIE` verworfen.** `<>` – 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). +(`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 @@ -151,8 +174,6 @@ deutlich besser ist. Spezifikation hängt nicht davon ab (Default bleibt ## 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=…` diff --git a/openspec/changes/fix-colour-defaults-and-client-options/proposal.md b/openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/proposal.md similarity index 94% rename from openspec/changes/fix-colour-defaults-and-client-options/proposal.md rename to openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/proposal.md index 4f795ec..f313d7d 100644 --- a/openspec/changes/fix-colour-defaults-and-client-options/proposal.md +++ b/openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/proposal.md @@ -41,16 +41,16 @@ Drucker-Queue): Start als Queue-Defaults gesetzt werden – damit überleben PPD-Optionen (z. B. `BRMediaType`, `TonerSaveMode`, `BRColorMode=Vivid`) die Neuerzeugung der Queue. -- **Optionale neue PPD-Choice `BRColorMode=VividNoCIE`** - (`<>`) als Testkandidat für noch - gesättigtere Ausgabe als `None`; nur behalten, wenn der Testdruck - besser ist als `None`. - **README**: neuer Abschnitt, warum der AirPrint-Dialog nur IPP-Standardoptionen zeigt und wie PPD-Optionen gesetzt werden (Env-Var, CUPS-Web-UI, `lp`); Troubleshooting „Farben sind blass" mit der durchgeführten Testreihe; Dokumentation der funktionierenden S/W-Abbildung. +Verworfen: eine zusätzliche Choice `BRColorMode=VividNoCIE` +(`UseCIEColor false` + `/BRColorMode 1`) – die Testdrucke waren von `None` +nicht unterscheidbar, siehe `design.md`. + Kein Bestandteil: Änderungen am Containerfile (die PPD wird bereits installiert), am Verbindungs-/Zertifikatsverhalten oder eine Server-seitige Erzwingung von Graustufen über `pdftops-renderer=gs` diff --git a/openspec/changes/fix-colour-defaults-and-client-options/specs/printserver/cups/spec.md b/openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/specs/printserver/cups/spec.md similarity index 100% rename from openspec/changes/fix-colour-defaults-and-client-options/specs/printserver/cups/spec.md rename to openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/specs/printserver/cups/spec.md diff --git a/openspec/changes/fix-colour-defaults-and-client-options/tasks.md b/openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/tasks.md similarity index 52% rename from openspec/changes/fix-colour-defaults-and-client-options/tasks.md rename to openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/tasks.md index 448ea3e..ad5f808 100644 --- a/openspec/changes/fix-colour-defaults-and-client-options/tasks.md +++ b/openspec/changes/archive/2026-10-20-fix-colour-defaults-and-client-options/tasks.md @@ -2,29 +2,35 @@ ## 1. PPD: Farbmodus-Default, Presets und Dokumentation -- [ ] 1.1 `assets/Brother HL-4150CDN/ppd/BR4150_2.PPD`: `*DefaultBRColorMode` +- [x] 1.1 `assets/Brother HL-4150CDN/ppd/BR4150_2.PPD`: `*DefaultBRColorMode` von `Normal` auf `None` ändern, alle eigenen Ergänzungen mit `%`-Kommentar (Herkunft/Zweck) markieren. Verifikation: `grep -n 'DefaultBRColorMode\|^% Modified' "assets/Brother HL-4150CDN/ppd/BR4150_2.PPD"` zeigt `*DefaultBRColorMode: None` und den Kommentar. -- [ ] 1.2 PPD: sechs `APPrinterPreset`-Einträge ergänzen (Kombinationen - color/monochrome × low/normal/high gemäß Tabelle in `design.md`, - z. B. `*APPrinterPreset MonoNormal/Schwarz-Weiß Normal: - "*com.apple.print.preset.output-mode=monochrome - *com.apple.print.preset.quality=normal *BRPrintQuality=Black - *TonerSaveMode=False"`). +- [x] 1.2 PPD: sechs `APPrinterPreset`-Einträge ergänzen (Kombinationen + color/monochrome × low/normal/high gemäß Tabelle in `design.md`). Verifikation: `grep -c '^\*APPrinterPreset' "assets/Brother HL-4150CDN/ppd/BR4150_2.PPD"` ergibt 6, und nach einem Build listet `lpoptions -p -l` weiterhin alle Optionen ohne PPD-Warnung in `/var/log/cups/error_log`. -- [ ] 1.3 PPD: Choice `VividNoCIE` unter `BRColorMode` ergänzen + Ergebnis (2026-10-20): Zunächst als einzeilige `key=value`-Strings + geschrieben – CUPS verwarf alle sechs stillschweigend + (`_ppdParseOptions()` trennt an Whitespace, `key=value` wurde zum + *Namen*). Mit libcups-2.4.2-Harness (`_ppd_cache_t`-Inspektion) belegt + und auf das von CUPS selbst erzeugte Format umgestellt (mehrzeilig, + Apple-Metadaten als `key value`, PPD-Optionen als `*Name Value`). + Danach greifen alle sechs im `pstops`-Pfad (siehe 3.x). +- [x] 1.3 PPD: Choice `VividNoCIE` unter `BRColorMode` ergänzen (`<>setpagedevice`) und per Testdruck gegen `None` vergleichen; nur behalten, wenn der Ausdruck deutlich gesättigter ist. Verifikation: `lpoptions -p -l` zeigt die Choice, die Entscheidung (behalten/entfernen) ist im README-Auswertungs-Abschnitt notiert. -- [ ] 1.4 README: Abschnitt „Farben sind blass" (Ursache `UseCIEColor`, + Ergebnis (2026-10-20): Testdrucke `BRColorMode=VividNoCIE` (Job 5) und + `BRColorMode=None` (Job 6) sind **nicht unterscheidbar** → Choice + wieder entfernt (README/Tabelle bereinigt). +- [x] 1.4 README: Abschnitt „Farben sind blass" (Ursache `UseCIEColor`, durchgeführte Testreihe mit `lp -o BRColorMode=…`, Ergebnis `None`), Abschnitt „Änderungen an der Hersteller-PPD" (welche Einträge ergänzt wurden und warum) und Abschnitt @@ -35,7 +41,7 @@ ## 2. entrypoint: `PRINTER_OPTIONS` -- [ ] 2.1 `entrypoint.sh`: `PRINTER_OPTIONS` (Standard leer) nach der +- [x] 2.1 `entrypoint.sh`: `PRINTER_OPTIONS` (Standard leer) nach der Queue-Erzeugung in Tokens `key=value` zerlegen und jedes per `lpadmin -o` setzen, an derselben Stelle wie `printer-is-shared`/`-D`/`-d`, bei jedem Start. @@ -43,12 +49,12 @@ `PRINTER_OPTIONS="BRColorMode=Vivid TonerSaveMode=False"` und `lpoptions -p -l` zeigt `Vivid` und `True` als Default (`*Vivid`, `*True`). -- [ ] 2.2 Fehler pro Token abfangen, damit `set -eu` den Start nicht +- [x] 2.2 Fehler pro Token abfangen, damit `set -eu` den Start nicht abbricht, und warnen. Verifikation: Start mit `PRINTER_OPTIONS="NoSuchOption=Foo"` erzeugt eine Warning-Zeile im Log, der Container erreicht „ready: CUPS on 631/tcp" und `lpstat -r` meldet „scheduler is running". -- [ ] 2.3 README: neue Zeile in der Env-Var-Tabelle plus Kurzbeispiel unter +- [x] 2.3 README: neue Zeile in der Env-Var-Tabelle plus Kurzbeispiel unter „Environment variables" (inkl. Hinweis `lpoptions -p -l` zur Entdeckbarkeit der Namen). Verifikation: das Beispiel aus der Tabelle in einen `podman run`-Befehl @@ -56,21 +62,38 @@ ## 3. Integrationstests am laufenden System -- [ ] 3.1 Image neu bauen und Container starten. +- [x] 3.1 Image neu bauen und Container starten. Verifikation: `podman build -t printserver:latest .` endet ohne Fehler, das Log enthält „ready: CUPS on 631/tcp, mDNS on 5353/udp". -- [ ] 3.2 S/W-Druck aus dem macOS-Dialog (derselbe Farbjob wie zuvor). + Ergebnis (2026-10-20, podman 6.1.4/macOS-VM): Build und Start ok; der + Container bindet 631 nur nach + `podman machine ssh -- sudo sysctl -w net.ipv4.ip_unprivileged_port_start=631` + (rootless VM) – danach „ready". +- [x] 3.2 S/W-Druck aus dem macOS-Dialog (derselbe Farbjob wie zuvor). Verifikation: Ausdruck ist schwarz/weiß – vorher kam er farbig heraus. -- [ ] 3.3 Farbdruck ohne weitere Optionen aus dem Dialog und per + Server-seitig verifiziert (2026-10-20): `print-color-mode=monochrome` + → `<>`. Sichtprüfung durch Nutzer (2026-10-20): + Testseite 2 ist schwarz/weiß. **Bestanden.** +- [x] 3.3 Farbdruck ohne weitere Optionen aus dem Dialog und per `lp -d test.pdf`. Verifikation: Ausdruck ist so gesättigt wie der bisherige `BRColorMode=None`-Testdruck; `lpoptions -p -l` zeigt `*None` als Farbmodus-Default. -- [ ] 3.4 Qualitätsstufen prüfen. - Verifikation: `lp -o print-quality=2 test.pdf` (draft, sichtbar - tonersparender) und `lp -o print-quality=5 test.pdf` (high, `CAPT` - SuperFine) erzeugen unterscheidbare Ausdrucke gegenüber - `print-quality=3`. + Verifiziert (2026-10-20): `lpoptions` zeigt `*None`, `pstops` emittiert + `<>` auch ohne Job-Optionen. Sichtprüfung durch + Nutzer: Testseite 1 ist kräftig/gesättigt (nicht mehr blass). + **Bestanden.** +- [x] 3.4 Qualitätsstufen prüfen. + Verifikation: `lp -o print-quality=3 test.pdf` (IPP draft, sichtbar + tonersparender: `true tonersave`) und `lp -o print-quality=5 test.pdf` + (IPP high, `CAPT SuperFine`, `<>`) + erzeugen unterscheidbare Ausdrucke gegenüber `print-quality=4` + (IPP normal). Hinweis: IPP-`print-quality` ist 3/4/5 – nicht 2/5. + Verifiziert (2026-10-20): serverseitig im `pstops`-Output (3 → + `true tonersave`, 5 → `BRApt 2`); Sichtprüfung durch Nutzer: Seite 3 + (draft) blass/tonersparend, klar unterscheidbar von 1/4. Seite 1 = 4 + erwartungsgemäß (SuperFine vs. Fine bei diesem Testmotiv optisch + gleich, serverseitig aber gesetzt). **Bestanden.** ## Workflow follow-up diff --git a/openspec/specs/printserver/cups/spec.md b/openspec/specs/printserver/cups/spec.md index 50ab1ac..0cdff21 100644 --- a/openspec/specs/printserver/cups/spec.md +++ b/openspec/specs/printserver/cups/spec.md @@ -54,6 +54,57 @@ The system SHALL translate the PPD's device options into PostScript - **WHEN** a job selects colour or mono - **THEN** the emitted PostScript sets the corresponding colour/mono device parameters +#### Scenario: A driverless client selects black and white +- **WHEN** an AirPrint/IPP client sends `print-color-mode=monochrome` +- **THEN** the emitted PostScript sets the mono device parameters (`/BRProcessColor 1`) and the job prints in black and white, independent of the PDF renderer used by the server + +#### Scenario: A driverless client selects colour +- **WHEN** an AirPrint/IPP client sends `print-color-mode=color` +- **THEN** the job prints in colour and the emitted PostScript disables toner saving + +#### Scenario: A driverless client selects the print quality +- **WHEN** an AirPrint/IPP client sends `print-quality=draft`, `normal` or `high` +- **THEN** the emitted PostScript selects the mapped device settings: toner saving for draft, full toner for normal, and the `CAPT` high-resolution setting for high + +### Requirement: Queue defaults are configurable at runtime +The container SHALL accept a `PRINTER_OPTIONS` environment variable holding +space-separated PPD option assignments (`key=value`) and apply each assignment +as a queue default on every start, so PPD-specific device options survive the +recreation of the queue. + +#### Scenario: Options are applied on start +- **WHEN** the container is started with `PRINTER_OPTIONS="BRColorMode=Vivid TonerSaveMode=False"` +- **THEN** after startup the queue defaults include `BRColorMode=Vivid` and `TonerSaveMode=False` + +#### Scenario: Defaults apply to driverless clients +- **WHEN** a macOS or iOS client prints a job that does not specify that PPD option +- **THEN** the job is printed with the configured queue default + +#### Scenario: An unusable option must not stop startup +- **WHEN** `PRINTER_OPTIONS` contains an option name or value the PPD does not provide +- **THEN** the entrypoint logs a warning, applies the remaining assignments and continues starting + +#### Scenario: Nothing configured +- **WHEN** `PRINTER_OPTIONS` is unset or empty +- **THEN** the queue keeps the PPD defaults and the container starts as before + +### Requirement: Colour output is saturated by default +Unless overridden through `PRINTER_OPTIONS`, the queue SHALL default to the +colour mode `None`, which leaves the printer's CIE colour conversion +(`UseCIEColor`) switched off so that printed colours are not washed out. + +#### Scenario: Default colour mode +- **WHEN** a colour job is printed without any colour-mode options +- **THEN** the emitted PostScript contains `<>` rather than `UseCIEColor true` + +#### Scenario: Default is overridable +- **WHEN** the container is started with `PRINTER_OPTIONS` containing `BRColorMode=Vivid` +- **THEN** the queue default is `Vivid` instead of `None` + +#### Scenario: A job's own selection wins +- **WHEN** a job explicitly selects another `BRColorMode` value +- **THEN** that value is used for the job instead of the queue default + ### Requirement: Jobs are printed via direct PostScript The container SHALL forward the converted job as PostScript to the printer without a server-side vendor filter or host-side rasterisation.