[Bug/Critical] Epson printer quality options limited to default only — only "Normal" shown, 6 other options missing #180

Open
opened 2026-09-15 10:03:25 +01:00 by gronod · 2 comments
Owner

Summary

The Epson driver (e.g., Epson XP-55 Series) offers 7 quality options — Fast Economy (301), Economy (302), Normal (303), Draft (308), Fine (304), Quality (305), Best Quality (307) — but ICCery v2 Mac does not enumerate them: CupsService.capabilities(from:ppd:) performs no quality extraction at all, so only the driver default ("Normal" / 303) can ever apply.

Note: no quality picker exists in Stage 2 until #183 lands — the enumeration gap becomes user-visible once it does.

This same limitation isn't present in TargetPrint (ICCery-CPU) which doesn't restrict available quality settings and correctly shows all 7 options.

Root Cause Analysis

What lpoptions -l Actually Returns

EPIJ_Qual/Print Quality: 301 302 *303 308 304 305 307

All 7 options ARE present in the CUPS lpoptions -l output. The issue is not with CUPS — it's with ICCery v2 Mac's parsing code.

Where the Bug Lives

  1. CupsService.capabilities(from:ppd:) (Packages/ICCeryCore/Sources/ICCeryCore/Print/CupsService.swift:105-136) — the actual blocker

    • Only handles InputSlot/MediaSource, PageSize/MediaSize, and CupsParsers.mediaTypeKeys
    • No quality option extraction at all
  2. No quality-key detection roster exists in CupsParsers

    • CupsOptionsFilter.relevantKeys (CupsOptionsFilter.swift:16-32) is a capture-relevance roster — it decides which -o options survive into captured cupsOptions, not which options get enumerated. EPIJ_Qual already survives isRelevant via the permissive non-com.* rule (CupsOptionsFilter.swift:44-52), so the roster is not the blocker — a new detection roster + extraction is
    • EPIJ_Quality (in relevantKeys, CupsOptionsFilter.swift:28) is a phantom/alias — real Epson PPDs use EPIJ_Qual. Keep EPIJ_Quality (harmless for capture); treat EPIJ_Qual as canonical in the new quality-key roster
  3. Test fixture mismatch (Tests/ICCeryCoreTests/CupsParserTests.swift:33)

    • Uses cupsPrintQuality/cupsPrintQuality: Draft *Normal High (3 options)
    • Real Epson driver uses EPIJ_Qual with 7 options

Comparison with ICCery-CPU (TargetPrint)

ICCery-CPU correctly handles this in CUPSManager.discoverPPDOptions() (Sources/Printing/CUPSManager.swift:190-196):

let quality = discoverOptionChoices(
    in: ppdText,
    candidateKeywords: ["CNIJPrintQuality", "EPIJ_Qual", "cupsPrintQuality", "PrintQuality", "CNIJPrintMode2", "Quality", "StpQuality"],
    openUITargets: ["print quality", "quality"]
)

It includes EPIJ_Qual in candidate keywords AND scans *OpenUI translations.

PPD Evidence (Epson XP-55 Series)

/etc/cups/ppd/Epson_XP_55_Series.ppd:
*OpenUI *EPIJ_Qual/Print Quality: PickOne
*DefaultEPIJ_Qual: 303
*EPIJ_Qual 301/Fast Economy: ""
*EPIJ_Qual 302/Economy: ""
*EPIJ_Qual 303/Normal: ""
*EPIJ_Qual 308/Draft: ""
*EPIJ_Qual 304/Fine: ""
*EPIJ_Qual 305/Quality: ""
*EPIJ_Qual 307/Best Quality: ""

Proposed Fix Options

Option 1: Minimal Fix — quality extraction + detection roster

  • Add quality option extraction to CupsService.capabilities() (or a new function) — this is the actual blocker
  • Add a quality-key detection roster with EPIJ_Qual canonical, ordered vendor-first / weakest-last (OutputMode/Resolution last — Epson OutputMode is a colour-mode key, misdetect risk); EPIJ_Qual may also be added to CupsOptionsFilter.relevantKeys for explicitness (it already survives isRelevant)
  • Allows existing lpoptions -l path to capture all quality options

Option 2: Full PPD Parsing (aligns with ICCery-CPU)

  • Parse PPD directly for quality choices with human labels
  • More accurate but adds complexity

Option 3: Hybrid Approach (Recommended)

  • Use lpoptions -l for basic enumeration
  • Add PPD parsing specifically for vendor-specific quality/media keys
  • Matches ICCery-CPU's discoverOptionChoices approach

Acceptance Criteria

  • All 7 Epson quality options appear in ICCery's quality UI — codes 301, 302, 303, 304, 305, 307, 308 (no 306 exists; 308 = Draft)
  • Quality options display with human-readable labels (Fast Economy, Economy, Normal, Draft, Fine, Quality, Best Quality)
  • Works for all Epson printers using EPIJ_Qual key
  • Test fixture updated to reflect real EPIJ_Qual output

Dependencies

  • Depends on: #183 (Stage 2 quality picker UI must exist before this bug fix can show all options)
  • Blocks: #186 (printer properties dialog needs all quality options to pre-select)

References

  • ICCery-CPU: Sources/Printing/CUPSManager.swift (discoverOptionChoices, optionDetails)
  • ICCery v2 Mac: Packages/ICCeryCore/Sources/ICCeryCore/Print/CupsOptionsFilter.swift
  • ICCery v2 Mac: Packages/ICCeryCore/Sources/ICCeryCore/Print/CupsService.swift
  • ICCery v2 Mac: Packages/ICCeryCore/Sources/ICCeryCore/Print/CupsParsers.swift
  • Test: Tests/ICCeryCoreTests/CupsParserTests.swift
## Summary The Epson driver (e.g., Epson XP-55 Series) offers 7 quality options — Fast Economy (301), Economy (302), Normal (303), Draft (308), Fine (304), Quality (305), Best Quality (307) — but ICCery v2 Mac does not enumerate them: `CupsService.capabilities(from:ppd:)` performs **no quality extraction at all**, so only the driver default ("Normal" / 303) can ever apply. Note: no quality picker exists in Stage 2 until #183 lands — the enumeration gap becomes user-visible once it does. This same limitation isn't present in TargetPrint (ICCery-CPU) which doesn't restrict available quality settings and correctly shows all 7 options. ## Root Cause Analysis ### What `lpoptions -l` Actually Returns ``` EPIJ_Qual/Print Quality: 301 302 *303 308 304 305 307 ``` All 7 options ARE present in the CUPS `lpoptions -l` output. The issue is not with CUPS — it's with ICCery v2 Mac's parsing code. ### Where the Bug Lives 1. **`CupsService.capabilities(from:ppd:)`** (Packages/ICCeryCore/Sources/ICCeryCore/Print/CupsService.swift:105-136) — **the actual blocker** - Only handles `InputSlot`/`MediaSource`, `PageSize`/`MediaSize`, and `CupsParsers.mediaTypeKeys` - No quality option extraction at all 2. **No quality-key detection roster exists in `CupsParsers`** - `CupsOptionsFilter.relevantKeys` (CupsOptionsFilter.swift:16-32) is a **capture-relevance** roster — it decides which `-o` options survive into captured `cupsOptions`, not which options get enumerated. `EPIJ_Qual` already survives `isRelevant` via the permissive non-`com.*` rule (CupsOptionsFilter.swift:44-52), so the roster is not the blocker — a new detection roster + extraction is - `EPIJ_Quality` (in `relevantKeys`, CupsOptionsFilter.swift:28) is a **phantom/alias** — real Epson PPDs use `EPIJ_Qual`. Keep `EPIJ_Quality` (harmless for capture); treat `EPIJ_Qual` as canonical in the new quality-key roster 3. **Test fixture mismatch** (Tests/ICCeryCoreTests/CupsParserTests.swift:33) - Uses `cupsPrintQuality/cupsPrintQuality: Draft *Normal High` (3 options) - Real Epson driver uses `EPIJ_Qual` with 7 options ### Comparison with ICCery-CPU (TargetPrint) ICCery-CPU correctly handles this in `CUPSManager.discoverPPDOptions()` (Sources/Printing/CUPSManager.swift:190-196): ```swift let quality = discoverOptionChoices( in: ppdText, candidateKeywords: ["CNIJPrintQuality", "EPIJ_Qual", "cupsPrintQuality", "PrintQuality", "CNIJPrintMode2", "Quality", "StpQuality"], openUITargets: ["print quality", "quality"] ) ``` It includes `EPIJ_Qual` in candidate keywords AND scans `*OpenUI` translations. ### PPD Evidence (Epson XP-55 Series) ``` /etc/cups/ppd/Epson_XP_55_Series.ppd: *OpenUI *EPIJ_Qual/Print Quality: PickOne *DefaultEPIJ_Qual: 303 *EPIJ_Qual 301/Fast Economy: "" *EPIJ_Qual 302/Economy: "" *EPIJ_Qual 303/Normal: "" *EPIJ_Qual 308/Draft: "" *EPIJ_Qual 304/Fine: "" *EPIJ_Qual 305/Quality: "" *EPIJ_Qual 307/Best Quality: "" ``` ## Proposed Fix Options ### Option 1: Minimal Fix — quality extraction + detection roster - Add quality option extraction to `CupsService.capabilities()` (or a new function) — this is the actual blocker - Add a quality-key detection roster with `EPIJ_Qual` canonical, ordered vendor-first / weakest-last (`OutputMode`/`Resolution` last — Epson `OutputMode` is a colour-mode key, misdetect risk); `EPIJ_Qual` may also be added to `CupsOptionsFilter.relevantKeys` for explicitness (it already survives `isRelevant`) - Allows existing `lpoptions -l` path to capture all quality options ### Option 2: Full PPD Parsing (aligns with ICCery-CPU) - Parse PPD directly for quality choices with human labels - More accurate but adds complexity ### Option 3: Hybrid Approach (Recommended) - Use `lpoptions -l` for basic enumeration - Add PPD parsing specifically for vendor-specific quality/media keys - Matches ICCery-CPU's `discoverOptionChoices` approach ## Acceptance Criteria - [ ] All 7 Epson quality options appear in ICCery's quality UI — codes **301, 302, 303, 304, 305, 307, 308** (no 306 exists; 308 = Draft) - [ ] Quality options display with human-readable labels (Fast Economy, Economy, Normal, Draft, Fine, Quality, Best Quality) - [ ] Works for all Epson printers using `EPIJ_Qual` key - [ ] Test fixture updated to reflect real `EPIJ_Qual` output ## Dependencies - **Depends on:** #183 (Stage 2 quality picker UI must exist before this bug fix can show all options) - **Blocks:** #186 (printer properties dialog needs all quality options to pre-select) ## References - ICCery-CPU: `Sources/Printing/CUPSManager.swift` (discoverOptionChoices, optionDetails) - ICCery v2 Mac: `Packages/ICCeryCore/Sources/ICCeryCore/Print/CupsOptionsFilter.swift` - ICCery v2 Mac: `Packages/ICCeryCore/Sources/ICCeryCore/Print/CupsService.swift` - ICCery v2 Mac: `Packages/ICCeryCore/Sources/ICCeryCore/Print/CupsParsers.swift` - Test: `Tests/ICCeryCoreTests/CupsParserTests.swift`
gronod added the Kind/Bug
Priority
Critical
1
Project/ICCery-v2Bug/Backend
labels 2026-09-15 10:03:25 +01:00
gronod added this to the M11 — Printer settings completeness & dialog binding milestone 2026-09-15 11:58:22 +01:00
Author
Owner

Planning errata (verified on develop@736fd08):

  1. AC "(301–307)" — the seven codes are 301, 302, 303, 304, 305, 307, 308; no 306 exists and 308 (Draft) is included (matches the body's own lpoptions -l evidence: EPIJ_Qual/Print Quality: 301 302 *303 308 304 305 307).
  2. RCA framing: CupsOptionsFilter.relevantKeys is a capture-relevance roster, not an enumeration roster — EPIJ_Qual already survives isRelevant via the permissive non-com.* rule (CupsOptionsFilter.swift:44–52). The actual blockers are: CupsService.capabilities(from:ppd:) has no quality extraction at all (CupsService.swift:105–136 handles InputSlot/MediaSource, PageSize/MediaSize, and mediaTypeKeys only), and no quality-key detection roster exists in CupsParsers.
  3. "only 'Normal' shown in ICCery" — no quality picker exists in Stage 2 until #183 lands; the enumeration gap becomes user-visible only then.
  4. EPIJ_Quality (in relevantKeys, CupsOptionsFilter.swift:28) is a phantom/alias — real Epson PPDs use EPIJ_Qual. Keep EPIJ_Quality (harmless for capture); treat EPIJ_Qual as canonical in the new quality-key roster.
Planning errata (verified on develop@736fd08): 1. AC "(301–307)" — the seven codes are **301, 302, 303, 304, 305, 307, 308**; no 306 exists and 308 (Draft) is included (matches the body's own `lpoptions -l` evidence: `EPIJ_Qual/Print Quality: 301 302 *303 308 304 305 307`). 2. RCA framing: `CupsOptionsFilter.relevantKeys` is a **capture-relevance** roster, not an enumeration roster — `EPIJ_Qual` already survives `isRelevant` via the permissive non-`com.*` rule (CupsOptionsFilter.swift:44–52). The actual blockers are: `CupsService.capabilities(from:ppd:)` has **no quality extraction at all** (CupsService.swift:105–136 handles InputSlot/MediaSource, PageSize/MediaSize, and mediaTypeKeys only), and no quality-key detection roster exists in `CupsParsers`. 3. "only 'Normal' shown in ICCery" — no quality picker exists in Stage 2 until #183 lands; the enumeration gap becomes user-visible only then. 4. `EPIJ_Quality` (in `relevantKeys`, CupsOptionsFilter.swift:28) is a phantom/alias — real Epson PPDs use `EPIJ_Qual`. Keep `EPIJ_Quality` (harmless for capture); treat `EPIJ_Qual` as canonical in the new quality-key roster.
Author
Owner

Errata above incorporated into the issue body — the ticket itself is now the corrected contract.

Errata above incorporated into the issue body — the ticket itself is now the corrected contract.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: gronod/iccery-v2-mac#180