diff --git a/Cargo.lock b/Cargo.lock index 03f455a7..e7ae1d0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7142,7 +7142,7 @@ dependencies = [ [[package]] name = "winprint" version = "0.2.1" -source = "git+https://github.com/storytold/winprint.rs?rev=3c978dfcf3d460577fabd87aeea7a84ffdcfb396#3c978dfcf3d460577fabd87aeea7a84ffdcfb396" +source = "git+https://github.com/storytold/winprint.rs?rev=bc06a7c48b768eb27a828c7986f6d2868b245ee0#bc06a7c48b768eb27a828c7986f6d2868b245ee0" dependencies = [ "flate2", "fmt-derive", diff --git a/crates/print/Cargo.toml b/crates/print/Cargo.toml index 7dc5bb62..b9ae1fda 100644 --- a/crates/print/Cargo.toml +++ b/crates/print/Cargo.toml @@ -35,7 +35,7 @@ log = "0.4" # ticket (winprint.rs#12, storytold/pdfcraft#3823), and reads whitespace-padded integers in # PrintCapabilities (winprint.rs#3, storytold/pdfcraft#2430). Pinned to an immutable revision; # review before bumping. -winprint = { git = "https://github.com/storytold/winprint.rs", rev = "3c978dfcf3d460577fabd87aeea7a84ffdcfb396", default-features = false } +winprint = { git = "https://github.com/storytold/winprint.rs", rev = "bc06a7c48b768eb27a828c7986f6d2868b245ee0", default-features = false } # GetDefaultPrinter (MIT). winsafe = { version = "0.0.29", default-features = false, features = ["winspool"] } # The job file's private folder. diff --git a/crates/print/src/lib.rs b/crates/print/src/lib.rs index 569cad28..01c05726 100644 --- a/crates/print/src/lib.rs +++ b/crates/print/src/lib.rs @@ -653,6 +653,23 @@ pub fn two_sided(printer: &str) -> Option { } } +/// Whether `printer`'s driver renders XPS itself: on Windows, `Some(true)` for v4 drivers and v3 +/// drivers with an XPS rendering filter, `Some(false)` for GDI drivers, which the XPS print API +/// reaches through Windows' XPS-to-GDI conversion. A page with transparency can come out wrong or +/// fail to print there (storytold/pdfcraft#6155, #5838, #5940), so an app may flatten it first. +/// `None` when it can't be read or no queue has that name, and on other systems. +pub fn xps_driver(printer: &str) -> Option { + #[cfg(windows)] + { + windows::xps_driver_named(printer) + } + #[cfg(not(windows))] + { + let _ = printer; + None + } +} + /// The Print dialog's printers from lpstat's two answers: the spooler's queues (`-p -d`), then /// the driverless destinations of `available` (`-e`) that have no queue — CUPS builds a temporary /// queue when `lp` sends them a job. Each destination once, queues first; the default still comes diff --git a/crates/print/src/tests/windows_native.rs b/crates/print/src/tests/windows_native.rs index feae2bb8..7882a057 100644 --- a/crates/print/src/tests/windows_native.rs +++ b/crates/print/src/tests/windows_native.rs @@ -454,6 +454,14 @@ fn windows_powershell(script: &str, printer: &str) -> Result { /// them; the test uses PowerShell only to add, inspect and remove the printer. Its driver is the /// inbox "Generic / Text Only"; a machine that cannot add one (no rights, no driver) skips the /// test, unless `CRAFT_PRINT_REQUIRE_QUEUE=1` (CI) makes it mandatory. +/// "Generic / Text Only" is a GDI (v3) driver: the XPS print API reaches it through Windows' +/// XPS-to-GDI conversion, where an app may flatten transparency first (storytold/pdfcraft#6155). +#[cfg(windows)] +fn assert_gdi_driver(name: &str) { + assert_eq!(spool::xps_driver(name), Some(false), "the test queue's driver is a GDI driver"); + assert_eq!(spool::xps_driver("Craft Test Print: no such queue"), None); +} + #[cfg(windows)] #[test] fn windows_jobs_reach_the_spooler() { @@ -474,6 +482,7 @@ fn windows_jobs_reach_the_spooler() { return; } let (listed, probed) = driver_queries(|| spool::printers().into_iter().find(|p| p.name == name)); + assert_gdi_driver(name); // The list a Print dialog shows first: the same queue, named without asking its driver (a // networked printer in power save can hold that query, storytold/wordcraft#5710). let (queue, unprobed) = driver_queries(|| spool::printer_queues().into_iter().find(|p| p.name == name)); diff --git a/crates/print/src/windows.rs b/crates/print/src/windows.rs index 67fbfdf8..d8f10db8 100644 --- a/crates/print/src/windows.rs +++ b/crates/print/src/windows.rs @@ -68,6 +68,12 @@ pub(crate) fn two_sided_named(name: &str) -> Option { PrinterDevice::all().ok()?.iter().find(|d| d.name() == name).and_then(two_sided) } +/// Whether the driver of the queue `name` renders XPS itself (`PRINTER_DRIVER_XPS`); `None` +/// when no queue has that name or its driver can't be read. +pub(crate) fn xps_driver_named(name: &str) -> Option { + PrinterDevice::all().ok()?.iter().find(|d| d.name() == name)?.is_xps_driver().ok() +} + #[cfg(test)] thread_local! { /// The driver capability queries printer discovery made on this thread, so a test can show