Adopt the canonical craft-print revision from craft-libs #28 so Windows printing uses the in-process Windows.Data.Pdf/XPS backend instead of PowerShell. Keep exact paper, orientation and scaling validation and the existing PDFCraft composition and permission checks.
Pin the reviewed winprint fork, scope its compile-time licence/advisory exceptions, and distinguish the earlier WDAC Sandbox exercise from the final pinned revision's Windows CI coverage. Physical printer output remains unverified.
Add worker-barrier regressions for Home-selected tool handoff and startup document ordering.
Fixes#1107
Co-authored-by: durtyrobbin <76844303+durtyrobbin@users.noreply.github.com>
Co-authored-by: Dimitar Klaturov <7221384+bulgariamitko@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: skylar taylor-barrick <hi@skylar.cc>
* Gates: fix two clippy failures on main
- ocr_ui: "Also recognize pages that already have text" used egui's own
checkbox, which clippy.toml disallows (#1184 landed beside #1173);
use widgets::checkbox like every other box.
- camera_edges: Rust 1.97's manual_range_contains; the rewrite keeps the
old answer for a NaN angle (false) with an is_finite check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ojKsSCHWk2AuyDTFMHCd7
* M10: print from the web build through the browser's print dialog (gap 4)
The web build had no printer: spool::submit refuses on wasm, so Print
could only download the print-ready PDF. Now its Print dialog offers
"Browser's print dialog" (the default there), which reaches every
printer the operating system knows.
- web_print: PdfCraft draws the print-ready PDF's sheets itself
(engine Exporter), one sheet a frame since wasm has no threads:
300 dpi up to 100 sheets, 200 up to 300, 150 up to 1,000, at most
64 MP a sheet and 1 GiB of PNGs a job. The PNGs become blob URLs
and print from a hidden srcdoc iframe, one sheet per page, margin 0.
- Chromium prints every page at the default @page size, so a sheet of
the paper's size turned sideways is rotated 90 degrees CCW onto it
(as drivers do); other sizes get named @page rules.
- Copies, two-sided and colour are greyed out (the browser's dialog
sets them). If print() is refused (e.g. a sandboxed frame), the
print-ready PDF downloads instead, with the reason.
- pdfcraft-print stays free of web APIs; six new strings in all 20
catalogs (ATTRIBUTION hashes refreshed); docs and parity updated,
gap 4 closed.
Verified in headless Chromium against a trunk build: three sheets at
300 dpi, the landscape sheet turned, no blank trailing page, and the
download fallback. Firefox and Safari not yet tried.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ojKsSCHWk2AuyDTFMHCd7
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* M10.5: Restore architectural paper presets and PDF scale controls
Port the missing paper catalog and scale controls from PR #599 onto the merged Windows printing implementation. Keep the spooler and background discovery from #847/#1007. Document the feature-by-feature audit and verification limits.
Assisted-by: OpenAI Codex
* Drop the provenance write-up and the screenshot example (the kittest covers the dialog)
* Refresh catalog hashes
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* M10: Windows printing prints at actual size, off the UI thread (follow-up to #847)
- Sheets print at 100% on the driver's paper closest to their size, landscape
when wide, instead of being shrunk into .NET's default 1-inch margins
(about 76% on Letter) and never rotated. Each sheet is capped at ~64 MP so a
poster-sized sheet can't exhaust memory.
- PowerShell runs with CREATE_NO_WINDOW: the release app has no console, so
Windows opened one over the app for the whole job.
- The Print dialog lists printers in the background (the desktop app's
printer_source) and spools jobs on a worker thread, so Ctrl+P and long jobs
no longer freeze the UI. A list arriving late preselects the default only if
the user hasn't picked a printer.
- Parity evidence named a test that #847 renamed; xtask parity passes again.
The raster-only limit is now stated in the parity notes and README.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M10: refresh catalog hashes in ATTRIBUTION.toml for the new print strings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Print: Windows lists its printers and spools jobs (#756)
The Print dialog's printer list comes from Win32_Printer through PowerShell's
CIM (wmic is gone from Windows 11), and jobs are spooled by the embedded
windows_print.ps1: the in-box Windows.Data.Pdf renders each sheet at the
printer's own resolution and System.Drawing.Printing carries copies,
collation, duplex and colour as the job's settings. Both run through
powershell, so the Rust code touches no Win32 API. Fixes#756, with the
printer-not-showing duplicates #160, #244, #293, #465 and #644; CIM approach
from #452. On CI a paused Generic / Text Only test printer catches the job in
its queue; a parser test keeps the script valid PowerShell.
* Print: fix the Windows await helper and render the job on CI (dry run)
Real-machine testing found [System.WindowsRuntimeSystemExtensions]::GetMethods()
failing with 'does not contain a method named GetMethods': GetMethods is an
instance method of [Type], so it is called with '.', the form the canonical
WinRT-await snippets use, not '::'. The first CI run missed it because the
end-to-end test skipped silently when the runner refused the test printer, so
the script had never actually run.
The script now honours PDFCRAFT_PRINT_DRYRUN=1: render every sheet with
Windows.Data.Pdf to PNGs in the temporary folder and print nothing. The test
runs that half unconditionally, so CI exercises the whole WinRT path —
projection, await helpers, render — on any machine, while the paused-printer
half stays best-effort and prints why it skipped. Win32_Printer enumeration
asks only for Name and Default, so a machine with nine printers waits less.
* Tests: the find-options wait in the viewer test is a deadline too
4280f83 converted the frame-count waits in this test to deadlines after the
same 12-of-15 race under a full workspace test run, but left the first wait —
the one for the 'page' matches — on the old 60-frame loop, and it flaked the
same way on PR #847's CI. Same fix: wait up to 20 s for the 15 matches, then
assert.
* Print: list driverless CUPS destinations, not only permanent queues
The Print dialog's printer list came from `lpstat -p -d`, which lists only
permanent queues. A driverless IPP printer discovered on the network — the
standard setup on modern Linux desktops — has no permanent queue: only
`lpstat -e` (CUPS >= 1.7) names it, and CUPS builds a temporary queue when
`lp` sends it a job. Until now such printers were invisible and the dialog
showed only Save as PDF.
Enumerate `lpstat -e` alongside `lpstat -p -d` and merge the answers: queues
first, deduplicated, default still from `-d`; a spooler without `-e` keeps
today's behaviour and loses only the driverless names. The .deb/.rpm now
recommend cups-client so a machine without a print stack is not silently
printerless.
Fixes#652
Co-authored-by: ZCode (GLM-5.3) <noreply@z.ai>
* Print: fix the off-unix build and a driverless default's marker
`lpstat_e_command` is called only from `printers`' unix block, so off unix
it was dead code and CI's Windows clippy gate (`-D warnings`) failed to
build the crate. Gate it like its caller.
The default destination can itself be driverless — `lpoptions -d` names a
destination no queue exists for — and the merge appended it from
`lpstat -e` with `default: false`, so the dialog preselected the first
queue instead of the user's default. The `-d` default now marks the
appended entry too; the test covers it, and CRLF in `-e` output.
Co-authored-by: ZCode (GLM-5.3) <noreply@z.ai>
---------
Co-authored-by: ZCode (GLM-5.3) <noreply@z.ai>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Security: pipe print jobs to lp instead of a shared temp folder
On macOS, Linux and FreeBSD, Print wrote each job to
<temp>/pdfcraft-print-<pid>/job-<time>.pdf and passed that name to lp.
The folder name is predictable and was made with create_dir_all, which
accepts a folder another local user created first in a shared /tmp and
follows a symlink planted there; the job was then written with fs::write.
That user could read every printed document, or swap the file before lp
read it. The folder was also never removed.
The job now goes to lp on stdin, so nothing is written to disk. lp_args
no longer takes a file. The job is fed on its own thread while lp's
output is collected, so neither side can block on a full pipe; if that
thread can't be started, lp is stopped and an error returned rather than
a panic. When lp refuses a job before reading it (an unknown printer)
the user sees lp's message rather than a broken pipe. CUPS ends its reply
for a job read from stdin with "(0 file(s))", which reads as if nothing
was sent, so that is dropped from the message shown.
submit_via takes the spooler command so tests can run this test binary
as a stand-in lp on every platform; the new test failed on the old code
(it received the temp file as an argument and nothing on stdin).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Security: create a new digital ID file fresh, owner-only
Configure New Digital ID > Create picked the first free name with
`!p.exists()` and then wrote the .p12 with fs::write. That is
check-then-write: a file or link planted at the name between the two is
written through. exists() also follows links, so a dangling symlink
looked free and the private key was created wherever it pointed. The
file got default permissions, readable by other local users on most
Unix systems.
save_new_id_file now creates the file with create_new, never opening
anything already at the name, and with mode 0o600 on Unix. A taken name
moves on to the next ("<name> 2.p12", ...), including a folder, which
Windows reports as access denied rather than already exists. A failed
write removes the partial file.
The dangling-link test failed on the old code (the key was written
through the link); the hard-link and folder test guards the retry loop,
and a Unix-only test checks the 0o600 mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Security: never write the --control token through a planted link
write_control_file opened the --control path with create + truncate. A
symlink or hard link already at that path was followed: its target was
truncated and overwritten with the control token before set_permissions
ran, so the token could land in a file another user can read, and any
file the link pointed at was destroyed.
The JSON now goes to a fresh file next to the path, with a random name
(create_new, 0o600 on Unix, another name if one is taken, including a
folder on Windows), which is then renamed over the path. The rename
replaces a link instead of following it, a control file left by an
earlier run is still replaced as before, and a reader polling for the
file never sees it half-written. A failed write or rename removes the
temporary file.
The hard-link and symlink tests failed on the old code (the token was
written into the linked file); a third test covers replacing a previous
run's file, and the owner-only mode on Unix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Contributors: credit jcl-bit as James Locke
My own entry in contributors/people.toml (AGENTS.md, Contributor
credits), added in my PR as asked there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The app, crates, binaries, bundle id, packaging, icons, screenshots and docs are now
PdfCraft / pdfcraft (repo storytold/pdfcraft). On first launch the desktop app moves the
PrintCraft settings and crash-recovery folders to the new name, and both apps read settings
saved under the old storage key, so upgrades keep recent files, preferences and unsaved work.
The signing test fixtures keep their PrintCraft subject names (they are binary).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>