Commit Graph
49 Commits
Author SHA1 Message Date
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 5f00550cf7 print: respell a QName value written after whitespace
A pretty-printed ticket puts whitespace before the value, so the prefix
starts after it; without this the ticket fell back to Windows' spelling.
2026-10-28 04:28:55 +00:00
skylar taylor-barrick cb53221767 fix(print): stop relying on inflate error output; bump miniz_oxide to 0.9 (#348)
* fix(print): do not treat early inflate errors as partial output

* chore(deps): bump miniz_oxide to 0.9
2026-10-25 20:37:49 -04:00
skylar 8e42b9ff4e fix(print): keep miniz_oxide at 0.8 2026-10-25 16:52:57 -06:00
skylar ce020c5811 chore(deps): bump roxmltree, miniz_oxide, serde_json, fixed_decimal
craft-print: roxmltree 0.20 -> 0.21 (locked 0.21.2, fixes a stack
overflow on deeply nested XML) and miniz_oxide 0.8 -> 0.9.
Lockfile only: serde_json 1.0.154, fixed_decimal 0.7.3.
2026-10-25 16:35:43 -06:00
Claude 23bd96375a print: the live driver test compares the vendor prefixes instead of re-running respell
Review follow-up. The test asserted `respell(validated, driver_default) == None`.
respell also returns None when it refuses a ticket, and in_drivers_spelling then
keeps the ticket as it is, so a ticket still spelled `ns0000` passed.

The test now compares the vendor namespace declarations of the ticket driver_ticket
returns with those of the driver's own default ticket and fails when a namespace the
driver declares is under another prefix. The comparison is
`ticket::prefixes_unlike_the_drivers` (test-only), unit-tested here: it flags the ticket
as the merge returns it, passes the respelled one and the driver's own, and flags a
ticket respell refused, where `respell(..) == None` can't tell it from a correct one.

Checked against a Brother HL-L2390DW: the test passes, and fails with
`(brother namespace, "ns0000", "brpsk")` when in_drivers_spelling is made to return
the merged ticket.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FQ5PzHVUcntEHU97zniM5U
2026-10-25 06:34:58 +00:00
Claude 766f1c6f2d print: respell leaves a ticket alone when an element or attribute name would move
Review follow-up. When two vendor namespaces trade prefixes, both prefixes stay
declared, so a name written with one of them as an element or attribute name
(`<ns0000:Feature/>`, `ns0000:flag="1"`) still parsed and resolved after the
root declarations changed, but to the other namespace. respell only rewrites
`name`/`constrained` values and QName values, so such a name would not follow.

respell now returns None when any element or attribute is named in a namespace
whose prefix changes. Names in a namespace that keeps its prefix do not block
the respelling, and the real Brother tickets (vendor names only in `name`
attributes and QName values) are respelled as before.

Tests: regression cases for element and attribute names in a swap and in a
prefix chain, and a control for names in an untouched namespace. Each ticket
is asserted well-formed with every prefix resolving, so the case is refused
for the name, not for a parse failure. The refusal tests fail with the guard
disabled.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FQ5PzHVUcntEHU97zniM5U
2026-10-25 02:58:18 +00: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 fb59c587df print: respell refuses a nested redeclaration; test the vendor-element case for real
Review of #307:

- respell rewrites the root's namespace declarations, so an element below the
  root that declares either prefix of a rename again (ns0000 or brpsk) would make
  a name resolve to another namespace once the prefixes change. It now returns
  None for such a ticket, and a test covers both prefixes.
- The vendor-element test replaced only an opening tag, left the closing tag
  behind and so never parsed: it passed because respell refused the broken XML,
  not because of what it was meant to check. Every refusal fixture is now
  asserted well-formed, and the element is replaced whole. Doing so showed the
  element guard was redundant: an element or attribute name in a renamed
  namespace leaves an undeclared prefix, which the final parse of the result
  already refuses. The guard is removed and the test now exercises that check.
- Doc comments on the new constants and tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FQ5PzHVUcntEHU97znim5u
2026-10-25 02:06:11 +00: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 13bb99663e print: DocumentDuplex counts as a driver's duplex feature 2026-10-24 23:37:08 +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
aiongg 941316b7ce Merge storytold/craft-libs#141 (Windows ticket names a colour only for grayscale) 2026-10-24 23:17:33 +00:00
aiongg 286212626e Merge remote-tracking branch 'upstream/main' into print-verify-capabilities 2026-10-24 23:16:52 +00:00
Dylan M. Taylor 4f85efb2b9 security: bound print dialog name resolution, PPD labels, lpstat merge and inherited geometry
- 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)
2026-10-22 21:49:46 -04:00
Bishal De 2bbef53f29 craft-print, craft-ui: show printers by name, not CUPS queue name (#192)
* 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.
2026-10-20 09:08:21 -06: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
Justin CapellaandClaude Sonnet 5.5 18c4d3146a Accept a one-sided request on a queue whose driver has no duplex feature
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>
2026-10-14 20:11:20 -07: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 ee70afc29a Pin driver copy-validity qualification fix 2026-10-13 02:49:34 +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
skylartaylor 4af92ad6aa feat(print): bridge native page processing to submitter callbacks 2026-10-13 01:20:21 +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 697b473c0b Stop PDF work immediately after a parser budget is exhausted 2026-10-12 23:27:53 +02:00
skylar taylor-barrick c22935270b Parse duplicate PDF dictionary keys in linear time 2026-10-12 23:22:05 +02:00
skylar taylor-barrick 85ef8b92ac Bound PDF parsing and preserve indirect page content arrays 2026-10-12 23:14:06 +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 32bcac12db Launch Windows PowerShell from its system path 2026-10-11 22:09:14 +02:00
skylar taylor-barrick bef3211753 Target the local computer explicitly when removing the test port 2026-10-11 22:03:37 +02:00
skylar taylor-barrick c7c01aff6d Use a removable local port name for the Windows queue test 2026-10-11 22:00:25 +02:00
skylar taylor-barrick da752825c7 Verify Windows queue cleanup without removing an absent output file 2026-10-11 21:56:44 +02:00
skylar taylor-barrick cf93331318 Use PrinterName to inspect and clean the Windows test queue 2026-10-11 21:53:59 +02:00
skylar taylor-barrick 379d99f817 Compare PowerShell geometry JSON as numeric values 2026-10-11 21:51:02 +02:00
skylar taylor-barrick c30d7d4e13 Avoid interactive FILE port prompts in the Windows queue test 2026-10-11 21:50:09 +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 c22bb1b0ce Require a paused temporary queue in native Windows print regression 2026-10-11 07:13:40 +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