Skip to content

fix(linux): enable Flatpak printing through XDG Print Portal (#1905) - #3066

Open
robertobulltech wants to merge 1 commit into
storytold:mainfrom
robertobulltech:fix/flatpak-print-portal-1905
Open

robertobulltech wants to merge 1 commit into
storytold:mainfrom
robertobulltech:fix/flatpak-print-portal-1905

Conversation

@robertobulltech

@robertobulltech robertobulltech commented Oct 17, 2026 •

Copy link
Copy Markdown

What this changes

Flatpak has neither the CUPS client commands nor the CUPS socket, so discovery returned no printers and PdfCraft offered only Save as PDF. Linux Flatpak printing now uses org.freedesktop.portal.Print without additional sandbox permissions. Native Linux retains CUPS; Windows, macOS and browser backends retain their existing paths.

Fixes #1905.

Architecture and workflow

  • A Linux-only transport uses the existing maintained zbus dependency with default-features = false and async-io; the other dependencies already existed in the lockfile. ashpd was evaluated; using zbus directly keeps the request handle, response subscription, cancellation and bounded cleanup under the application's control without another portal-client dependency. craft-print itself and its pin are unchanged.
  • /.flatpak-info or a nonempty FLATPAK_ID selects the portal. Flatpak does not enumerate or query CUPS, and the system dialog is a separate destination rather than a fabricated printer.
  • PreparePrint → Print(token) opens one host dialog. The specification also permits direct Print, which was tried first: the tested KDE 6.7.5 backend returned response 2 immediately, with no dialog. PreparePrint followed by token-based Print worked. KDE's observed reverse: "no" response is accepted alongside "false".
  • PdfCraft composes its finished PDF once. Identity page/N-up/scale hints are supplied; conflicting returned page/layout/scale/reverse choices and unsupported file formats are refused before handoff. Host copies, collate, duplex, paper and destination settings belong to the portal token.
  • Permission-checked print snapshots move composition/rasterization onto the worker. The PDF is written to an anonymous file and reopened read-only through /proc/self/fd; no pathname is given to the host. Descriptors are dropped on every outcome.
  • Both phases subscribe before invoking the method and await Response. Cancellation, missing/unsupported services, malformed responses/tokens, service loss and timeouts are errors/cancellation rather than success. Close is bounded, pending jobs cancel on shutdown, completion repaints the GUI, and overlapping system requests are refused. Acceptance explicitly does not mean physical completion.

UI and automation

Flatpak offers System print dialog… even with zero enumerated printers, plus the existing Save as PDF. Preview, page selection, orientation, sizing, N-up, booklet, poster, comments/forms and Print as image retain the existing composition path. Grayscale uses the existing image renderer. Driver Properties/copies/collate/duplex are disabled/delegated, and paper is labelled as the PDF sheet size. The system dialog should keep All pages, one page per sheet and 100% scale. The progress card can cancel a pending request.

printers reports system_dialog: true with an empty printer list. doc_print accepts printer: "system-dialog" or "default", returns status: "accepted"/"cancelled", and rejects physical-option arguments that must be selected in the host dialog. Atomic PDF saving and automation root confinement remain intact.

How you verified it

Functional tests: 139 passed, scoped to the changed printing paths:

Scope Result
pdfcraft-print 36 passed
engine --lib print 11 passed, including permissions and a snapshot after source closure
UI print/control integration 14 + 59 passed
UI --lib print_ui 8 passed
automation printing 3 passed
automation roots 8 passed

The private dbus-daemon/mock service receives the actual imposed PDF through a read-only Unix FD and tests early responses, both-phase cancellation, malformed/failed responses and tokens, incompatible settings, unavailable/disappearing services, timeout/Close, cleanup and consecutive requests. These are functional tests, not compilation-only checks.

flatpak_offers_system_printing_without_cups was copied unchanged into an archive of base 924c4da4ac2879ac9107c6c82e20ac6be5570f0a: it compiled and failed because the system dialog option was absent. The same test passes with the fix, including saving a valid PDF.

  • cargo fmt --check
  • Scoped Clippy, -j 2 --all-targets -- -D warnings: print, engine, automation, ui-egui, desktop app
  • Relevant Rust unit/integration/UI/automation tests above
  • cargo xtask layers, assets, parity
  • cargo check -p pdfcraft-web --target wasm32-unknown-unknown -j 2, Rust 1.95 (existing render/UI dead-code warnings)
  • Bash syntax, Desktop/AppStream validation, parsed Flatpak manifest consistency, actual Flatpak export/bundle/install/run

Remote CI: GitHub Actions currently reports action_required with no jobs started (CI run); upstream access for this contributor is READ. A maintainer must unblock the upstream workflows. The passing results above are local checks, not a claim that remote CI ran.

Real KDE/Wayland and Flatpak evidence

On CachyOS, Plasma/Wayland, xdg-desktop-portal 1.22.1, KDE portal 6.7.5 and Freedesktop Platform 25.08.18:

  1. Built and opened the native app with the synthetic four-page PDF and --control. The native CUPS/Save dialog is retained; this host has no CUPS queues.
  2. Packaged and installed the modified app/CLI in an actual Flatpak sandbox with exactly the manifests' finish-args. Confirmed no lp, lpstat, lpoptions, CUPS socket or /etc/cups.
  3. Used the opt-in control channel inside the sandbox to inspect the system option, select pages 2–4, two pages per sheet and landscape Letter, then open KDE's Stampa dialog.
  4. KDE's built-in Stampa su file (PDF) wrote the result. The output is byte-for-byte identical to the CLI's expected print-ready PDF: 2 pages, 792 × 612 pt, 1,374 bytes; SHA-256 83213200b1525b8f30e8498e09aaa41a90e22537c6677ae2bffba1170197512e. External PDF inspection/rendering confirmed green/blue on sheet 1 and magenta on sheet 2; source page 1 is excluded.
  5. Repeated successfully after host cancellation and through the sandboxed doc_print tool. The output path was outside the sandbox's visible filesystem, demonstrating portal delivery rather than direct host filesystem access.
  6. Verified host Cancel, the app's progress Cancel and shutdown during a pending request: host dialog closes, no output/new success notice. The GUI responds while pending. The final anonymous-file implementation held one read-only /tmp/#… (deleted) PDF descriptor while waiting and none after the response.

Detailed validation and reproducible command scopes. Private logs and synthetic outputs are retained locally; no third-party desktop screenshots, PDF binaries or fonts are committed.

Security, provenance and limitations

  • No new unsafe or non-test unwrap/expect/panic paths; existing panic guards are retained and composition has a final guard.
  • No host spawning, unrestricted host access, CUPS permissions or network permissions. Both manifests parse identically to the base; their changes are comments only.
  • Control/MCP remain opt-in, loopback/authenticated; root confinement tests pass.
  • No Adobe-derived assets, copied GPL/AGPL code, committed fonts or new binary fixtures. Behaviour comes from the public specification and black-box D-Bus/desktop observations. Asset checks pass.
  • Printing parity notes/evidence and release/hardware/printing documentation updated without increasing readiness estimates.

Limits: physical printer output, duplex, paper calibration, collation and driver compatibility are not hardware-validated. A portal cancellation cannot guarantee recalling a physical job already handed to the host. Windows/macOS runtime printing was not rerun. Wayland uses an unparented, nonmodal dialog because eframe cannot safely export an xdg-foreign handle; X11 identifiers are supplied where available. Physical paper/scale choices remain the host driver's responsibility; PostScript/SVG file output is unsupported.

The local test bundle used stripped debug binaries linked against the runtime's libm because CachyOS's newer glibc exports symbols absent in runtime 25.08. No linker workaround or bundled glibc is committed; release binaries already target Ubuntu 22.04. The full offline Flathub source build and optional release OCR/Japanese-font inputs were not exercised by this printing validation. No physical printer was required.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flatpak: Print dialog finds no printers (no CUPS tools or socket in the sandbox) – tracking issue for the print portal

1 participant