Commit Graph
28 Commits
Author SHA1 Message Date
Juan JuradoandFredApps 1ee01a4c58 craft-print: ask macOS for Auto Select when a job names no tray (#232)
* craft-print: ask macOS for Auto Select when a job names no tray

CUPS gives a job that names no tray the PPD's default tray. On a queue
whose default tray is empty (a Xerox AltaLink defaulting to Tray 1), the
printer holds every job ("Tray 1 is empty") while CUPS reports it
completed, so nothing prints. Preview prints because the macOS Print
dialog's Paper Feed: Auto Select sends AP_D_InputSlot with an empty value,
which drops the PPD's default tray (storytold/pdfcraft#3752).

- lp_args sends `-o AP_D_InputSlot=` on macOS when the job's options name
  no InputSlot. A tray the job names wins; checked with cupsfilter on a
  synthetic PPD: with both, CUPS emits the InputSlot's code.
- apply_queue_defaults (now shared with apps that read options
  themselves, such as PdfCraft) gives the driver's tray option an
  AUTO_SELECT choice, labelled "Auto Select", first and as the default,
  so a dialog shows what lp_args sends. It is left out when the user's
  lpoptions defaults name another tray, since lp adds that tray to every
  job and it wins.

Linux jobs are unchanged: AP_D_InputSlot is Apple's.

* craft-print: match queue defaults to the PPD without regard to case

CUPS treats PPD option and choice names as case-insensitive. A queue
default spelled otherwise than the PPD (inputslot=tray2 for InputSlot
Tray2) was ignored, so on macOS apply_queue_defaults showed Auto Select
although lp would send Tray2. Keys and choices now match case-
insensitively and keep the PPD's spelling. lpoptions -l prints the PPD's
spelling, so this matters for callers passing defaults from elsewhere.

Review: storytold/craft-libs#232 (CodeRabbit).

* README: keep both the macOS tray and the Flatpak notes (the merge left the conflict markers in)

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 19:15:15 +01:00
art804andFredApps b6b8a55b2c craft-print: two_sided(printer) reads one printer's two-sided support (#133)
printer_queues() (#618) lists the queues without asking any driver, so
every printer's two_sided is unknown there. A Print dialog listing from it
still needs to know whether the printer the user picks takes two-sided
settings, without asking every other driver as printers() does: Windows
takes about 48 s to give up on a printer it can't reach, such as a WSD
printer on another network (storytold/pdfcraft#2192).

two_sided(printer) finds that queue and reads its driver's capabilities
with the same check printers() makes, so the answers match. It is None
when there is no such queue, when the capabilities can't be read, and off
Windows, as printers() reports there.

The paused-printer test now checks that two_sided(name) gives printers()'s
answer for the test queue from exactly one driver query, and a new test
checks that a name no queue has is unknown. The query counting moves into
a small helper, which keeps windows_jobs_reach_the_spooler under the
cognitive-complexity limit.

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 00:03:35 +01:00
robertobulltechandrobertobulltech 92451193ba fix(print): honor DocumentDuplex-only Windows drivers (#411)
* fix(print): honor DocumentDuplex-only Windows drivers (#5130)

* fix(print): select duplex scope by two-sided options (#5130)

* docs(print): clarify duplex ticket selection and validation

* fix(print): select duplex scope for the requested option

* fix(print): require standard namespace for duplex options

* docs(print): document duplex regression

* docs(print): describe both offered duplex scopes

---------

Co-authored-by: robertobulltech <341520377+robertobulltech@users.noreply.github.com>
2026-11-09 23:48:52 +01:00
ashandClaude Opus 5.5 a3e0b5a944 craft-print: a job can be cancelled until it starts reaching the spooler (#622)
* craft-print: a job can be cancelled until it starts reaching the spooler

On Windows a job asks the printer's driver for its capabilities before
anything is sent. A networked printer in power save can hold that query
for a long time, and an app had no way to give up on the job without
risking it printing later (storytold/wordcraft#5710; CodeRabbit on
storytold/wordcraft#5732).

A Cancel token, passed to the new submit_cancellable, moves a job from
pending to cancelled or to sending. The job checks it at the last point
before anything reaches the spooler: on Windows after the driver has
answered, before the job file is written; on macOS and Linux before lp
starts. A cancelled job returns an error and is never sent; a job already
being sent completes. submit and submit_with_progress are unchanged, and
Error keeps its one variant so callers that match on it still compile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-print: a job turns to sending with the provider held; the job file waits for it

Review: marked as sending before it took the PrintTicket provider, a job
could then wait behind another job's driver call that a sleeping printer
held, and an app that waits only for jobs being sent would wait on it.
The job now turns to sending only once it holds the provider, right
before printing, and writes its file only after that, so a cancelled job
leaves no copy of the document behind.

A test-only hold in the driver call lets the Windows spooler test cancel
a job while its driver doesn't answer: the cancel wins and, once the
driver answers, the job stops instead of reaching the paused queue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-print: the DEVMODE conversion comes before a job turns to sending

CodeRabbit on #622: winprint converts the ticket to a DEVMODE, a driver
call a sleeping printer can stall, before it creates the native job, so
a job stalled there had already turned to sending and couldn't be
cancelled. The conversion now runs first, with the provider held, while
the job can still be cancelled; the provider stays held until the first
copy's own conversion, so no other job comes between them.

Tests: the Windows spooler test also cancels a job held in that
conversion; the CUPS test gives a spooler that can't start, so any
attempt to start it would change the error. README: cancellation in the
progress section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* craft-print: clarify cancellation during winprint conversion repeat

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:32:35 -05:00
arnav-hiwarkarandClaude Opus 5.5 62b0fe5edb craft-print: printer_queues lists the queues without asking any driver
On Windows, printers() reads each driver's PrintCapabilities to report
two-sided support before it returns. A networked printer in power save
can hold that query for a long time, so a Print dialog showed no printer
at all, not even the ones that were awake (storytold/wordcraft#5710,
storytold/wordcraft#5645, storytold/wordcraft#5413).

printer_queues() returns the same queues and default from EnumPrinters
and GetDefaultPrinter with two_sided unknown, and makes no driver call.
Submission still checks the job's options with the driver. On other
platforms it is printers(). printers() is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 04:54:11 +05:30
Dylan M. Taylor 733a038aa5 craft-print: log why the printer list is empty when lpstat can't run; document Flatpak needs (#429)
* craft-print: log why the printer list is empty when lpstat can't run; document Flatpak needs

Flatpak apps saw "No printers found" with working CUPS printers: the Freedesktop
runtime has libcups but not lpstat/lp, and the sandbox had neither the CUPS socket
nor Avahi (driverless printers come from lpstat -e). Log the spawn failure and say
in the README what a Flatpak must bundle and grant.

* craft-print: log a failed lpstat -p -d; README separates what each Flatpak piece is for

Review (CodeRabbit): a non-zero lpstat exit was dropped silently, and the README
said any missing piece empties the printer list when only lpstat and the socket do.

* craft-print README: driver options need the PPD, remote servers need network, socket paths are remapped

Review (CodeRabbit): printer_options reads /etc/cups/ppd before lpoptions, so it is
the PPD, not lpoptions, that a Flatpak lacks; a client.conf server is reached only
with --share=network.

* craft-print README: scope the CUPS_SERVER override to one app; describe the stricter socket wrapper

Review (CodeRabbit): flatpak override without an app id applies to every app.
2026-10-29 18:48:26 -04:00
aiongg b4afa6d22a Merge pull request #307 from rbulboaca/fix/windows-print-vendor-ticket-prefix
print: give the job ticket the driver's own prefix for its vendor nam…
2026-10-28 00:47:44 -04:00
aiongg 865c9af7fd print: collated copies that can't be one job print as a job per copy (#308) 2026-10-24 22:25:28 -04:00
aiongg 2869b8cc10 print: collated copies on a Windows driver that can't collate repeat the pages in one job (#305) 2026-10-24 22:11:16 -04:00
Claude 0348d2990f print: give the job ticket the driver's own prefix for its vendor namespace
Windows' PTMergeAndValidatePrintTicket names a vendor namespace with a generated
prefix (ns0000) as soon as the merged ticket uses a name from it. driver_ticket
merges the driver's own paper option from its capabilities, which carries vendor
properties, so a Brother HL-L2390DW's ticket came back with ns0000 where the
driver's own default ticket says brpsk. XML-wise the two are the same; the
driver's print pipeline reads the prefix as text. The job was accepted and then
ID2D1PrintControl::AddPage (or Close, in older builds) failed with 0x80040003,
reported by winprint as "Render error" / "Failed to close print control: No such
interface supported".

The same ticket sent raw with brpsk prints; with only the prefix changed to
ns0000 it fails the same way, so the prefix is the whole difference.

driver_ticket now re-spells the validated ticket with the prefixes of the driver's
own default ticket (an empty ticket, validated), through ticket::respell: pure
XML, tested on every platform, and a no-op unless a vendor namespace is spelled
differently. It rewrites only the root declarations and the vendor names in name
/ constrained attributes and QName values, handles two namespaces trading
prefixes, and leaves the ticket as it is when a rename could be ambiguous or leave
a prefix undeclared.

Verified on a Brother HL-L2390DW: the job spools, and pdfcraft head's doc_print
that failed at AddPage prints.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FQ5PzHVUcntEHU97znim5u
2026-10-25 01:35:49 +00:00
aiongg 00f0656e36 print: refuse a Windows job option only when the driver offers it and dropped it
The driver's PrintCapabilities tell a driver that dropped a requested value
from one that never had the feature. Grayscale on a driver with no colour
option and uncollated copies on one that can't collate are accepted, like
one-sided on a queue without duplex. Collated copies on a driver that can't
collate are refused with their own message. The capabilities are read only
when the strict check fails.
2026-10-24 23:23:12 +00:00
magdale76andClaude Opus 5.5 bfbf82b360 fix(print): a black and white chosen in the printer's preferences applies
The Windows job ticket always named a colour: Monochrome when the job
asked for grayscale, Color otherwise. Merged over the user's printing
preferences, that Color replaced a black and white the user had picked
in the driver's own Properties dialog, so the job came out in colour
(storytold/pdfcraft#2422).

The ticket now names a colour only when grayscale is asked for. Without
it the driver validates the ticket from the user's preferences, as the
CUPS backend and `verify_job_options` already assumed.

Checked on an HP OfficeJet 250 (PCL-3 driver) with its preferences set
to black and white: the validated ticket had psk:Color and the DEVMODE
dmColor 2 before, psk:Monochrome and dmColor 1 after. With colour
preferences it is psk:Color either way.

- `the_ticket_carries_the_job_options`: no PageOutputColor without
  grayscale (fails on the old ticket).
- `the_preferences_colour_reaches_a_job_without_grayscale` (ignored,
  needs CRAFT_PRINT_TEST_PRINTER; sends no job): the job's validated
  colour equals the preferences' colour. Fails on the old ticket with the
  queue set to black and white; passes on HP OfficeJet 250, Microsoft
  Print to PDF and OneNote.
- The Windows command preview says `color=(driver default)`, like
  `duplex=(queue default)`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 10:17:48 +02:00
skylar taylor-barrick 3d37da389a Merge current core print progress while retaining submission guards 2026-10-13 05:03:53 +02:00
skylar taylor-barrick d873514e8a Document driver option refusal and equivalent grayscale capability 2026-10-13 04:50:19 +02:00
skylar taylor-barrick 3929c3d14c Refuse explicit print options dropped by Windows drivers 2026-10-13 04:48:42 +02:00
skylartaylor 6ba4d0b97a Refuse annotation visibility the native renderer cannot print correctly 2026-10-13 03:29:55 +02:00
skylartaylor 8fb89562d1 Expose shared safe driver settings and RGB print transport 2026-10-13 02:15:46 +02:00
skylartaylor ccb1eae613 fix(print): refuse fitting that would discard annotations 2026-10-13 01:47:19 +02:00
skylartaylorandTKIPisalegacycipher ebc4d8cfbd Add caller-thread progress for native sheet processing
Co-authored-by: TKIPisalegacycipher <git@chir.us>
2026-10-13 01:20:21 +02:00
skylar taylor-barrick 02d08fe9cc fix(print): pin native completion repair and document bounded PDF handling 2026-10-12 23:12:41 +02:00
754f4d70e0 Print on Windows in-process, without PowerShell
craft-print's Windows backend ran an embedded PowerShell script for the printer list and every
job. Under App Control for Business or AppLocker script enforcement that script runs in
Constrained Language Mode and every job fails (storytold/pdfcraft#1107). The backend now runs
in this process and starts no program:

- Printers come from EnumPrinters and the default from GetDefaultPrinter; Printer::two_sided
  from each driver's PrintCapabilities. The list is sorted, each name once.
- A job is drawn as vectors by the in-box Windows.Data.Pdf with Direct2D into the XPS print
  API (winprint). Copies, collation, duplex (left out for duplex: None), colour, orientation
  and the driver's own paper option go in a PrintTicket the driver validates; a ticket the
  driver changed (paper, orientation, scaling) is refused before anything is sent. The driver
  paper named like the standard size is preferred (pdfcraft#599).
- A job has one ticket, so one paper shape. A small lenient reader (sheets.rs) reads the
  print-ready PDF's sheets and, when needed, appends an incremental update: /Rotate for sheets
  of the other orientation, and for Scaling::ShrinkToFit a reframe onto the driver's matching
  or default paper (shrunk inside its widest hardware margin, never enlarged, centred).
  Exact size refuses mixed paper sizes and paper the driver lacks.
- command_preview describes the in-process job on Windows ("xps-print", key=value options).
- windows_print.ps1 and the PowerShell helpers are gone. open_printer_preferences (rundll32)
  and the CUPS path are unchanged, as is the public API.

The end-to-end test puts a paused Generic / Text Only printer on LPT1: (no port to remove
afterwards; a paused FILE: queue never takes the job) and checks a mixed-orientation exact job
and a shrink-to-fit job each land as one job with all sheets. The reader is tested on every
platform with classic and compressed cross-references and thousands of truncated and corrupted
files. Windows-only dependencies: winprint =0.2.1 (default features off), winsafe 0.0.29,
roxmltree 0.20, miniz_oxide 0.8.

Ported from PdfCraft's native printing branch (pdfcraft#1132), which carries #599's approach.

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>
2026-10-11 18:03:54 -07:00
skylar taylor-barrick d991bf6792 Report Windows driver duplex capability to print callers 2026-10-12 01:04:26 +02:00
skylar taylor-barrick c15c501567 Require Windows CI to exercise the paused print queue 2026-10-11 21:46:00 +02:00
skylar taylor-barrick b71ad7c8f1 Preserve queue duplex defaults and add explicit shrink-to-fit 2026-10-11 21:44:37 +02:00
skylar taylor-barrick 711b7934c9 Enable cross-platform craft-print CI and manual runs 2026-10-11 19:48:56 +02:00
skylar taylor-barrick 9e03fd49cc Bound Windows custom paper dimensions to DEVMODE capacity 2026-10-11 07:11:07 +02:00
skylar taylor-barrick e7e12f7d0f Document cross-platform print CI setup and pending native validation 2026-10-11 07:08:48 +02:00
skylar taylor-barrick eb99775517 Extract craft-print PDF spooler with Windows size and duplex fixes 2026-10-11 07:07:56 +02:00