macOS: a job that names no paper tray asks for Auto Select (craft-print 1ee01a4c, #3752) (#6905)

* macOS: a job that names no paper tray asks for Auto Select (craft-print 1ee01a4c, #3752)

craft-print 1ee01a4c (storytold/craft-libs#232) sends `AP_D_InputSlot=` on macOS for a job whose
options name no tray, as the system Print dialog's Paper Feed: Auto Select does. Without it CUPS
used the PPD's default tray, and a printer whose default tray is empty held every job while CUPS
reported it completed (the Xerox AltaLink in #3752).

- The Print dialog's driver options take the queue's defaults through craft-print's
  apply_queue_defaults: option and choice names match as CUPS matches them, and on macOS the
  paper tray starts on Auto Select, which is what the job sends.
- The two tests that pin the exact `lp` arguments expect the Auto Select option on macOS.

* Tests: on macOS a CUPS queue on its driver's default tray shows Auto Select (craft-libs#232)

instances_read_the_queue_ppd_and_their_own_defaults and
a_queue_that_never_answers_is_given_up_at_the_deadline expected the PPD's default tray; on macOS
apply_queue_defaults now offers Auto Select first and as the default there, which is what a job
naming no tray sends. Found by running the print tests on hosted macOS.
This commit is contained in:
FredApps authored and GitHub committed 2026-11-10 20:17:41 +01:00
1 parent 0bffa42fc5
commit 8e6ec925c8
4 files changed
+35 -16

No files matched your search

Generated
+1 -1
View File
@@ -1243,7 +1243,7 @@ dependencies = [
[[package]]
name = "craft-print"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=1146cdc4f92e6c5c4fc3d67efbf0e1cec436a10c#1146cdc4f92e6c5c4fc3d67efbf0e1cec436a10c"
source = "git+https://github.com/storytold/craft-libs?rev=1ee01a4c58729d781638b8afbd8982726dbdefcb#1ee01a4c58729d781638b8afbd8982726dbdefcb"
dependencies = [
"craft-winprint-gdi",
"log",
+1 -1
View File
@@ -24,7 +24,7 @@ craft-keymap = { git = "https://github.com/storytold/craft-libs", version = "0.1
# the commit in `craft-i18n.rev`). Already in the tree through craft-ui.
craft-i18n = { git = "https://github.com/storytold/craft-libs", version = "0.1.0", rev = "787a7a0f972fd1d7a7618fdbeca3a5267a0407ff" }
# Shared system spooler; document composition remains in pdfcraft-print.
craft-print = { git = "https://github.com/storytold/craft-libs", version = "0.1.0", rev = "1146cdc4f92e6c5c4fc3d67efbf0e1cec436a10c" }
craft-print = { git = "https://github.com/storytold/craft-libs", version = "0.1.0", rev = "1ee01a4c58729d781638b8afbd8982726dbdefcb" }
# Whether a path stored in a document may be opened (GoToR link targets).
craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "787a7a0f972fd1d7a7618fdbeca3a5267a0407ff" }
# craft-update: app-specific pretend versions apply to startup and manual checks.
+6 -9
View File
@@ -2,8 +2,8 @@
//! stay in PdfCraft; these adapters retain the application's print-error surface.
pub use craft_print::{
Duplex, HANDLED_OPTIONS, HAS_PRINTER_PREFERENCES, Job, Printer, PrinterOption, Scaling, lp_args, lpstat_command, parse_lpoptions, parse_lpstat,
parse_lpstat_e, parse_ppd, parse_windows_printers, ppd_text, printers_parsed,
AUTO_SELECT, Duplex, HANDLED_OPTIONS, HAS_PRINTER_PREFERENCES, Job, Printer, PrinterOption, Scaling, apply_queue_defaults, lp_args,
lpstat_command, parse_lpoptions, parse_lpstat, parse_lpstat_e, parse_ppd, parse_windows_printers, ppd_text, printers_parsed,
};
#[cfg(any(windows, test))]
@@ -243,13 +243,10 @@ pub(crate) fn printer_options_via_within(
if let Some(b) = &ppd {
localize(&mut options, b, lang);
}
for (key, value) in current {
if let Some(o) = options.iter_mut().find(|o| o.key == key)
&& o.choices.iter().any(|(c, _)| *c == value)
{
o.default = value;
}
}
// The queue's current choices become the defaults (names matched as CUPS matches them). On
// macOS the paper tray also starts on Auto Select, which is what a job naming no tray asks
// for, so the printer isn't held on an empty default tray (#3752, storytold/craft-libs#232).
apply_queue_defaults(&mut options, &current);
options
}
+27 -5
View File
@@ -3,6 +3,12 @@ use std::sync::Arc;
use super::*;
use crate::spool::{Duplex, Job, Printer, lp_args, parse_lpstat, parse_windows_printers};
/// What `lp_args` adds on macOS for a job that names no paper tray: Auto Select, as the system
/// Print dialog asks (#3752, storytold/craft-libs#232). Nothing elsewhere.
fn macos_auto_tray() -> &'static str {
if cfg!(target_os = "macos") { " -o AP_D_InputSlot=" } else { "" }
}
/// `n` pages of 200×300 (page 3 is landscape 300×200 when n ≥ 3); page i shows "(Page i+1)".
/// Page 1 carries a printable square comment, a non-printing note, and a stamp.
fn fixture(n: usize) -> Document {
@@ -423,7 +429,10 @@ fn spooler_arguments_and_printer_list() {
};
assert_eq!(
lp_args(&job).join(" "),
"-d Office_Laser -n 3 -t memo.pdf -o collate=false -o sides=two-sided-long-edge -o print-color-mode=monochrome -o fit-to-page=false"
format!(
"-d Office_Laser -n 3 -t memo.pdf -o collate=false -o sides=two-sided-long-edge -o print-color-mode=monochrome -o fit-to-page=false{}",
macos_auto_tray()
)
);
assert_eq!(lp_args(&Job::default())[0], "-n", "no -d: the default printer");
}
@@ -766,7 +775,7 @@ fn landscape_sheets_ask_cups_to_turn_the_page() {
assert_eq!(cups_orientation(&[]), None);
// `lp` is given it, among the job's other options.
let job = Job { options: ask.into_iter().collect(), ..Job::default() };
assert!(lp_args(&job).join(" ").ends_with("-o fit-to-page=false -o orientation-requested=4"));
assert!(lp_args(&job).join(" ").ends_with(&format!("-o fit-to-page=false -o orientation-requested=4{}", macos_auto_tray())));
}
/// Both submission paths add the orientation through `add_cups_orientation`: after the job's
@@ -796,6 +805,13 @@ fn a_job_that_names_its_orientation_keeps_it() {
mod cups_options {
use crate::spool;
/// The paper tray a CUPS option list shows as its default: on macOS, a queue left on the driver's
/// own default tray (`driver_default`) shows Auto Select, which is what a job naming no tray asks
/// for (#3752, storytold/craft-libs#232); elsewhere, and for any other tray, `expected` as it is.
fn macos_tray<'a>(expected: &'a str, driver_default: &str) -> &'a str {
if cfg!(target_os = "macos") && expected == driver_default { spool::AUTO_SELECT } else { expected }
}
const RECORD: &str = "PDFCRAFT_LPOPTIONS_TEST_RECORD";
const ANSWER: &str = "PDFCRAFT_LPOPTIONS_TEST_ANSWER";
/// Generous: these tests are about what is read, not how long it takes.
@@ -832,7 +848,8 @@ mod cups_options {
let options = spool::printer_options_via_within("Office", &dir, command(&dir, "HANG"), std::time::Duration::from_millis(500), "en");
assert!(started.elapsed() < std::time::Duration::from_secs(20), "given up at the deadline, not when the command does");
assert_eq!(options.len(), 1, "the PPD is still read");
assert_eq!(options[0].default, "Auto", "the PPD default stands when the queue does not answer");
// On macOS the driver's default tray is shown as Auto Select, what the job then sends (#3752).
assert_eq!(options[0].default, macos_tray("Auto", "Auto"), "the PPD default stands when the queue does not answer");
std::fs::remove_dir_all(dir).unwrap();
}
@@ -886,8 +903,13 @@ mod cups_options {
let options =
spool::printer_options_via_within(destination, &dir, command(&dir, &format!("InputSlot/Paper tray: *{current}")), LIMIT, "en");
assert_eq!(options.len(), 1, "{destination}: the queue's PPD must be read");
assert_eq!(options[0].default, expected, "{destination}: only valid instance choices override the PPD default");
assert_eq!(options[0].choices, [("Auto".into(), "Automatic".into()), ("Tray2".into(), "Tray 2".into())]);
assert_eq!(options[0].default, macos_tray(expected, "Auto"), "{destination}: only valid instance choices override the PPD default");
// On macOS, a queue left on the driver's default tray also offers Auto Select first (#3752).
let mut choices: Vec<(String, String)> = vec![("Auto".into(), "Automatic".into()), ("Tray2".into(), "Tray 2".into())];
if macos_tray(expected, "Auto") != expected {
choices.insert(0, (spool::AUTO_SELECT.into(), "Auto Select".into()));
}
assert_eq!(options[0].choices, choices);
assert_eq!(std::fs::read_to_string(dir.join("args")).unwrap(), format!("-p\n{destination}\n-l"));
}
let job = spool::Job { printer: Some("Office/duplex".into()), ..Default::default() };