v2.0.0.2 - M12 Native Print Spool #210

Merged
gronod merged 21 commits from develop into main 2026-09-18 15:14:49 +01:00
Owner
No description provided.
gronod added this to the M12 — Native print spool milestone 2026-09-17 18:35:38 +01:00
gronod added 15 commits 2026-09-17 18:35:40 +01:00
docs(agents): add issue ticket style and dependency tracking guidelines
macOS CI / package (push) Skipped
macOS CI / build-and-test (push) Skipped
0513b272cb
refactor(print): extract PMTicketBridge from PrintPanelService
macOS CI / build-and-test (push) Skipped
ee5da4a637
Centralise every Core Printing (PM*) call in one @MainActor bridge so
the upcoming ticket serialise/restore path and native spooler share a
single documented ownership policy. Pure move — no behaviour change.
Serialise PMPrintSettings/PMPageFormat to XML data plus a plist-safe
NSPrintInfo dictionary snapshot (PrintTicket), restore via
PM*CreateWithDataRepresentation -> PMCopy* -> PMSessionValidate* ->
updateFromPM*, and refuse cross-queue replay (R3).

PrintPanelService.showProperties now returns PanelCaptureResult; the
view model stores tickets in capturedTickets keyed by queue.

Also fixes a latent over-release: PMSessionGetCurrentPrinter hands back
the session's own printer (borrowed) — the extracted currentPrinterID
was PMRelease-ing it, which dangled the session and crashed AppKit's
_printerInPrintSession path and session teardown.
TargetRasterLoader decodes printtarg TIFFs via ImageIO and re-tags
into DeviceGray/DeviceRGB/DeviceCMYK around the decoder's own
dataProvider — no resample, no bit-depth change (D10). Skip-only
alpha padding (noneSkipLast/First, which ImageIO emits for opaque
RGB TIFFs) is accepted; real coverage alpha, indexed, float and
non-1/3/4-component layouts are rejected as unsupportedLayout.
DPI metadata drives pointSize (px/dpi*72) with a 72 dpi fallback +
warn; manifestDrift reports >0.5 mm disagreement with the printtarg
manifest (D9).

TargetPageCanvasView is the headless flipped canvas NSPrintOperation
paginates: rectForPage stacks pages top-down, destinationRect anchors
1:1 at the page's top-left snapped to 0.001 pt, and draw() forces
interpolation/antialias/font-smoothing off per page with a drawProbe
seam for tests.

PDF harness (NativeSpoolPDFTests, skippable via ICCERY_SKIP_PRINT_PDF=1)
ran on this host: run() -> true, 2 pages at A4 media box, and the
fixture's 72 pt patch verified 1:1 at the painted top-left (printer
unprintable-margin offset tolerated per R10).
Replace the LpArgs argv builder with the deterministic
TicketWriteResolver (locked 1-10 write order, Stage 2 overrides win
unconditionally, no raw, nil overrides emit no write) and spool via a
headless NSPrintOperation over TargetPageCanvasView instead of the
CUPS lp binary. RecordingTargetSpooler (DEBUG) logs deterministic
request lines to ICCERY_TEST_SPOOL_LOG for tests. Single spool job
toggle switches printAllPages between per-page requests and one
multi-page job. CupsService keeps lpstat/lpoptions enumeration but
loses printTarget; ProcessID.lp* renamed cupsLp*; LpArgs, its tests
and the lp fixture deleted.
- CupsParsers: lpstatDeviceURIs / lpoptionsMakeAndModel /
  detectAirPrint — the six docs/14 §10.2 rules (apple-airprint:// URI,
  *APAirPrint: True, Apple+AirPrint make-and-model, ipps://+PPD
  mention, ipp:// + _universal mDNS subtype, cupsFilter2→image/urf).
- CupsService.listPrinters: one tolerated lpstat -v spawn feeds
  detection; lpoptions -p yields displayName + makeAndModel in a
  single spawn; Printer.isAirPrint populated per queue.
- Printer.isAirPrint defaults false with a tolerant decoder.
- Stage2View: live airPrintWarningBadge — "AirPrint queue —
  unmanaged colour cannot be guaranteed."
- Fixtures: lpstat -v marks Mock_Canon_Pro via _universal subtype;
  lpoptions emits printer-make-and-model.
- Tests: six rule fixtures + USB negative + a no-fallback-respawn
  listPrinters case; Milestone12AirPrintUITests tracks the badge
  across queue selection.

Gate: full xcodebuild test green (515 unit + 70 UI).
- Create docs/issue-printer-quality-ignored.md: lifecycle trace, ticket-loss and override-inversion root causes with file:line evidence, dismissed argv tokenisation hypothesis, locked D1-D13 table.
- docs/11: retitle walkthrough to UI click to NSPrintOperation; replace build_lp_args/print_target with the S1-S14 native-spool trace; add layers 5' (Quartz vocabulary) and 7 (ticket serialise/restore); mark lp flag table and macOS test list historical v1.
- docs/14: decision table rows (Spool, ColorSync ticket, Geometry, Interpolation, AirPrint) marked adopted in v2.0 via #201; note section 7 Quartz vocabulary now live in ICCery proper.
- AGENTS.md: spooler documented in app target (D1); new Print spool section (lp eradicated, parsers retained per D4, ICCERY_TEST_SPOOL_LOG seam); ColorSync SPI rule replaced by the D2 single-path dual-vocabulary rule.
- Eradication sweep: label remaining lp/LpArgs references historical v1 in docs/10, docs/13, docs/25, docs/26; drop deleted LpArgs from README.
Merge pull request 'docs(print): native spool documentation update + issue #201 analysis (#201 Phase 6)' (#208) from feat/201-docs into milestone/m12-native-spool
macOS CI / build-and-test (push) Skipped
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 32m12s
60f3aa3991
chore: bump MARKETING_VERSION for M12 native spool release
macOS CI / build-and-test (push) Failing after 4m51s
macOS CI / package (push) Skipped
2407abfd9e
gronod force-pushed develop from 273c319152 to 2407abfd9e 2026-09-17 21:53:30 +01:00 Compare
gronod added 2 commits 2026-09-17 21:59:49 +01:00
test(print): flip canvas coordinate space to bottom-up for NSPrintOperation compatibility
macOS CI / build-and-test (push) Skipped
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 34s
226e7b2a78
- TargetPageCanvasView.isFlipped: true → false (bottom-up CoreGraphics origin)
- TargetCanvasGeometryTests: assertion updated to XCTAssertFalse(view.isFlipped)
fix(print): anchor raster at page top in bottom-up canvas space (#211)
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 11m49s
macOS CI / build-and-test (push) Failing after 44m23s
macOS CI / package (push) Skipped
ddb9245f44
226e7b2 un-flipped the canvas but left destinationRect anchored at
pageRect.minY — the page's BOTTOM edge in bottom-up space — so
sub-page targets printed at the foot of the sheet and oversize
targets clipped their top rows. Anchor at pageRect.maxY - height,
restoring the spec'd top-left anchor (docs/14 §6, D9).

Adds the missing regression coverage: the geometry test now asserts
the top anchor with a sub-page raster, and the PDF harness gains a
banded (red-over-blue) fixture that fails on either a mirrored draw
or a bottom anchor — solid fills could catch neither.
gronod added 1 commit 2026-09-18 07:05:39 +01:00
fix(print): filter Stage 2 quality picker by selected media type (#214)
macOS CI / build-and-test (push) Successful in 44m15s
macOS CI / package (push) Successful in 3m27s
f7099a9808
lpoptions -l only exposes the global quality list, so the picker offered every token for every media — the driver then rejected invalid pairs (e.g. Fast Economy on Premium Semigloss) at print time. Neither the Epson nor the Canon PPD carries standard *UIConstraints, so resolve the media-to-quality map from each driver's own data: Epson InkjetPrinter2 *EPIJUIConstraint forbid-rules in the machine bundle's PDEData.dat (non-media LHS terms evaluated against lpoptions defaults), Canon BJPrinter's binary cnb_<TableID>0.tbl record table via *CNIJNameTblPath/*CNIJTableID, and generic PPD *UIConstraints pairs.

MediaQualityConstraints picks the first non-empty source and fails open — queues without constraint data keep the full list. PrinterCapabilities gains qualityIDsByMediaType plus filtered qualities(forMediaType:)/allowsQuality accessors; the view model exposes availableQualities, clamps the pick on media change / seeding / panel apply-back, and substitutes a stale quality defensively in makeRequest so an invalid pair can never reach the ticket.
gronod added 1 commit 2026-09-18 09:55:32 +01:00
docs(agents): add remote test build procedure for CI runner host
macOS CI / package (push) Canceled after 0s
macOS CI / build-and-test (push) Canceled after 36m50s
77c065f433
- SSH access: localadmin@192.168.0.172, credentials in ~/Projects/build-host.env
- sshpass usage via SSHPASS env var (never command-line)
- Remote checkout sync: git checkout + scp uncommitted changes
- xcodegen PATH workaround: prepend ~/.local/xcodegen/2.38.0/xcodegen/bin (#109)
- Full xcodebuild build-for-testing + codesign + test-without-building sequence mirroring .gitea/workflows/macos.yml
- Unit tests only (ICCeryCoreTests) —
gronod added 1 commit 2026-09-18 10:59:34 +01:00
test(print): scroll stage-2 via synthesized scroll wheel (#215)
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (push) Canceled after 0s
macOS CI / package (push) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 24m1s
ed91ebf807
press(forDuration:thenDragTo:) is inert on macOS — content-drag
scrolling is iOS-only, so the helper burned its 20s budget with the
gallery frame never moving. The scroll wheel is the mechanism the
platform supports: one scroll(byDeltaX:deltaY:) call on a coordinate
inside app.scrollViews["stage-2"] per poll iteration, negative deltaY
to move content up off the Dock zone.
gronod added 1 commit 2026-09-18 11:07:03 +01:00
chore: bump MARKETING_VERSION to 2.0.3
macOS CI / package (push) Successful in 3m37s
macOS CI / build-and-test (push) Skipped
f7f6a8bd87
gronod merged commit f7f6a8bd87 into main 2026-09-18 15:14:49 +01:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gronod/iccery-v2-mac#210