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.
- craft-ui shown_names resolves label collisions with hash-map counts and a worklist, so a
crafted cascade is linear instead of cubic (CodeRabbit CWE-407, ui/src/printing.rs:32)
- parse_ppd caps group, option and choice labels at 256 characters before each option copies
its group (CodeRabbit CWE-400, print/src/lib.rs:232)
- printers_parsed tracks seen destinations in a HashSet (CodeRabbit CWE-407, lib.rs:417)
- sheets::Reader::rect checks an inline array's length before reading it and borrows direct
objects instead of deep-cloning the inherited MediaBox/CropBox/Rotate for every page
(CodeRabbit CWE-400, print/src/sheets.rs:774)
* print, ui: a printer is shown by its name, not its CUPS queue name
CUPS queue names can't hold dots or spaces, so macOS adds the printer at
192.0.2.15 as the queue `_192_0_2_15`, and the Print dialog showed that
underscored name. The name the user knows is the queue's description
(CUPS `printer-info`).
craft-print now lists queues with `lpstat -l -p -d` (same lines, plus
each queue's indented details) and keeps the first `Description:` as
`Printer::description`, capped at 256 characters. Windows names are
already human, so they leave it empty.
craft-ui's `Printer` gains `label`; the dialog shows it, or the name when
it's empty, and `shown_names` adds the queue name in brackets when two
printers would show the same text. Selection and jobs still use `name`.
Both structs gain a field, so apps that build them by hand add it
(`..Default::default()` works for craft-ui's) when they update the pin.
* print, ui: an empty first description stays first, and shown names never collide
parse_lpstat tracks whether a queue has had its Description line, so an
empty first one isn't replaced by a later one. shown_names checks the
final texts: printers whose bracketed text another label already reads
show their queue names instead.
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>
The Windows path refused any explicit duplex request whose validated PrintTicket lacked the duplex feature. The Print dialog asks for one-sided by default, so every queue without a duplex unit refused with default settings, although one-sided is all such a queue can do and nothing was dropped.
A one-sided request is now satisfied when the driver's PrintCapabilities offer no duplex feature at all. Two-sided requests without the feature, a driver that offers the feature but returns a ticket without the setting, and capabilities that cannot be read are still refused. Copies, collation, colour, paper, orientation and scaling are checked as before.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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>