diff --git a/crates/automation/src/printing.rs b/crates/automation/src/printing.rs index 19cd48ef43..39cd45760e 100644 --- a/crates/automation/src/printing.rs +++ b/crates/automation/src/printing.rs @@ -6,6 +6,22 @@ use serde_json::{Value, json}; use crate::{Args, Automation, Result, ToolError, failed, write_atomic}; +/// The job's two-sided setting for `doc_print`'s `duplex`: `printer` sends none, so the printer's +/// own default applies (as the Print dialog's Printer default, #2422), except that cut and stack +/// needs single-sided sheets. +fn duplex(value: &str, cut_stack: bool) -> Result> { + Ok(match value { + "printer" if cut_stack => Some(spool::Duplex::Off), + "printer" => None, + "off" => Some(spool::Duplex::Off), + "long-edge" if cut_stack => return Err(bad("cut-and-stack printing needs duplex off (single-sided sheets)")), + "short-edge" if cut_stack => return Err(bad("cut-and-stack printing needs duplex off (single-sided sheets)")), + "long-edge" => Some(spool::Duplex::LongEdge), + "short-edge" => Some(spool::Duplex::ShortEdge), + d => return Err(bad(format!("unknown duplex {d:?}"))), + }) +} + fn bad(m: impl Into) -> ToolError { ToolError::InvalidArgs(m.into()) } @@ -92,9 +108,7 @@ impl Automation { }, l => return Err(bad(format!("unknown layout {l:?}"))), }; - if matches!(layout, Layout::Multiple { order: PageOrder::CutStack, .. }) && a.opt_str("duplex")?.unwrap_or("off") != "off" { - return Err(bad("cut-and-stack printing needs duplex off (single-sided sheets)")); - } + let duplex = duplex(a.opt_str("duplex")?.unwrap_or("off"), matches!(layout, Layout::Multiple { order: PageOrder::CutStack, .. }))?; if a.opt_str("order")? == Some("cut-stack") && !matches!(layout, Layout::Multiple { .. }) { return Err(bad("cut-stack order needs layout multiple")); } @@ -153,12 +167,7 @@ impl Automation { printer: if printer == "default" { None } else { Some(printer.to_string()) }, copies: a.opt_int("copies")?.unwrap_or(1).clamp(1, 999) as u32, collate: a.opt_bool("collate")?.unwrap_or(true), - duplex: Some(match a.opt_str("duplex")?.unwrap_or("off") { - "off" => spool::Duplex::Off, - "long-edge" => spool::Duplex::LongEdge, - "short-edge" => spool::Duplex::ShortEdge, - d => return Err(bad(format!("unknown duplex {d:?}"))), - }), + duplex, scaling: spool::Scaling::Exact, grayscale: a.opt_bool("grayscale")?.unwrap_or(false), title: name, @@ -181,3 +190,21 @@ impl Automation { Ok(out) } } + +#[cfg(test)] +mod tests { + use super::*; + + /// `printer` leaves two-sided to the printer's own default (#2422); cut and stack still asks + /// for single-sided sheets, and refuses an explicit edge. + #[test] + fn duplex_printer_leaves_two_sided_to_the_printer() { + assert_eq!(duplex("printer", false).ok(), Some(None)); + assert_eq!(duplex("printer", true).ok(), Some(Some(spool::Duplex::Off))); + assert_eq!(duplex("off", false).ok(), Some(Some(spool::Duplex::Off))); + assert_eq!(duplex("long-edge", false).ok(), Some(Some(spool::Duplex::LongEdge))); + assert_eq!(duplex("short-edge", false).ok(), Some(Some(spool::Duplex::ShortEdge))); + assert!(duplex("long-edge", true).is_err()); + assert!(duplex("sideways", false).is_err()); + } +} diff --git a/crates/automation/src/tools.rs b/crates/automation/src/tools.rs index b0c5c8cb2b..04ce1fc0a4 100644 --- a/crates/automation/src/tools.rs +++ b/crates/automation/src/tools.rs @@ -518,7 +518,7 @@ pub fn tools() -> Vec { "printer": { "type": "string" }, "copies": { "type": "integer", "minimum": 1, "maximum": 999 }, "collate": { "type": "boolean" }, - "duplex": { "type": "string", "enum": ["off", "long-edge", "short-edge"] }, + "duplex": { "type": "string", "enum": ["off", "long-edge", "short-edge", "printer"], "description": "Two-sided printing (default off); printer sends no setting, so the printer's own default applies." }, "grayscale": { "type": "boolean" }, "as_image": { "type": "boolean", "description": "Print as image (Acrobat's Advanced Print Setup): every page rendered to a picture at dpi before it is placed on the paper, for printers that skip text, shading or transparency; the print is no longer searchable text, and with grayscale the pictures are grey." }, "dpi": { "type": "number", "minimum": 72, "maximum": 600, "description": "Resolution for as_image (default 300)." }, diff --git a/crates/automation/tests/automation.rs b/crates/automation/tests/automation.rs index 624187c5ef..aa777fae67 100644 --- a/crates/automation/tests/automation.rs +++ b/crates/automation/tests/automation.rs @@ -5683,6 +5683,9 @@ fn cut_stack_printing_through_tools() { } assert!(matches!(a.call("doc_print", &json!({"doc": doc, "order": "cut-stack", "path": "refused.pdf"})), Err(ToolError::InvalidArgs(_)))); assert!(!dir.join("refused.pdf").exists()); + // The printer's own two-sided default isn't refused: cut and stack sends single-sided (#2422). + ok(&mut a, "doc_print", json!({"doc": doc, "layout": "multiple", "order": "cut-stack", "duplex": "printer", "path": "printer-default.pdf"})); + assert!(dir.join("printer-default.pdf").exists()); assert!(a.call("doc_print", &json!({"doc": doc, "layout": "multiple", "order": "cut-stack", "path": "../escaped.pdf"})).is_err()); let _ = std::fs::remove_dir_all(dir); } diff --git a/crates/ui-egui/src/i18n/ar.tsv b/crates/ui-egui/src/i18n/ar.tsv index f6b3d093d5..b07c097955 100644 --- a/crates/ui-egui/src/i18n/ar.tsv +++ b/crates/ui-egui/src/i18n/ar.tsv @@ -1219,6 +1219,7 @@ Off متوقف Flip on long edge قلب على الحافة الطويلة Flip on short edge قلب على الحافة القصيرة + Printer default افتراضي الطابعة Paper: الورق: Pages to Print الصفحات المراد طباعتها More options: خيارات أخرى: diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 90e152c27b..c749ee1ac7 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -899,6 +899,7 @@ Off Изключено Flip on long edge Обръщане по дългия ръб Flip on short edge Обръщане по късия ръб + Printer default По подразбиране за принтера Paper: Хартия: Pages to Print Страници за печат More options: Още опции: diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 1366cb2546..7baa4ac983 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -1287,6 +1287,7 @@ key Space Mezerník Off Vypnuto Flip on long edge Převrátit podle delší strany Flip on short edge Převrátit podle kratší strany + Printer default Podle tiskárny Paper: Papír: Pages to Print Stránky k tisku More options: Další možnosti: diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index ac136cd91a..861c9a2039 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -1205,6 +1205,7 @@ Off Aus Flip on long edge An langer Kante wenden Flip on short edge An kurzer Kante wenden + Printer default Druckerstandard Paper: Papier: Pages to Print Zu druckende Seiten More options: Weitere Optionen: diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 133e8ca8e6..f4ecbdf903 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -764,6 +764,7 @@ Two-sided: Εκτύπωση διπλής όψης: Flip on long edge Αναστροφή στη μεγάλη πλευρά Flip on short edge Αναστροφή στη μικρή πλευρά + Printer default Προεπιλογή εκτυπωτή Paper: Χαρτί: Pages to Print Σελίδες προς εκτύπωση More options: Περισσότερες επιλογές: diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 7c7b7c8d71..a2e5124e0d 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -1214,6 +1214,7 @@ Off Desactivado Flip on long edge Voltear por el borde largo Flip on short edge Voltear por el borde corto + Printer default Predeterminado de la impresora Paper: Papel: Pages to Print Páginas que imprimir More options: Más opciones: diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index aa83f81970..4bc3b717d1 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -1200,6 +1200,7 @@ Off Désactivé Flip on long edge Retourner sur le bord long Flip on short edge Retourner sur le bord court + Printer default Par défaut de l'imprimante Paper: Papier : Pages to Print Pages à imprimer More options: Plus d'options : diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 123be343f3..1089392348 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -1190,6 +1190,7 @@ Off Ki Flip on long edge Hosszú él mentén Flip on short edge Rövid él mentén + Printer default Nyomtató alapértéke Paper: Papír: Pages to Print Nyomtatandó oldalak More options: További beállítások: diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 71715dc5d7..f4b9b55d9d 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -1216,6 +1216,7 @@ Off Disattivato Flip on long edge Rilegatura sul lato lungo Flip on short edge Rilegatura sul lato corto + Printer default Come da stampante Paper: Carta: Pages to Print Pagine da stampare More options: Altre opzioni: diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index f73e753def..0e9b17cf97 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -1220,6 +1220,7 @@ Off オフ Flip on long edge 長辺とじ Flip on short edge 短辺とじ + Printer default プリンターの既定 Paper: 用紙: Pages to Print 印刷するページ More options: 詳細オプション: diff --git a/crates/ui-egui/src/i18n/kk.tsv b/crates/ui-egui/src/i18n/kk.tsv index 7f113d4bfc..0d88ecf969 100644 --- a/crates/ui-egui/src/i18n/kk.tsv +++ b/crates/ui-egui/src/i18n/kk.tsv @@ -1164,6 +1164,7 @@ Off Өшірулі Flip on long edge Ұзын жиек бойынша аудару Flip on short edge Қысқа жиек бойынша аудару + Printer default Принтер әдепкісі Paper: Қағаз: Pages to Print Басып шығарылатын беттер More options: Қосымша параметрлер: diff --git a/crates/ui-egui/src/i18n/lt.tsv b/crates/ui-egui/src/i18n/lt.tsv index 6448c6949e..8d1c784cf2 100644 --- a/crates/ui-egui/src/i18n/lt.tsv +++ b/crates/ui-egui/src/i18n/lt.tsv @@ -592,6 +592,7 @@ Flip horizontal Apversti horizontaliai Flip on long edge Apversti per ilgąją kraštinę Flip on short edge Apversti per trumpąją kraštinę + Printer default Pagal spausdintuvą Flip vertical Apversti vertikaliai Folders Aplankai Font Size: Šrifto dydis: diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 578b512bb7..40f799a554 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -1203,6 +1203,7 @@ Off Wyłączony Flip on long edge Odwracaj wzdłuż długiej krawędzi Flip on short edge Odwracaj wzdłuż krótkiej krawędzi + Printer default Domyślne drukarki Paper: Papier: Pages to Print Strony do wydrukowania More options: Więcej opcji: diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index ee12fe7ab2..5bce3ae4fc 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -845,6 +845,7 @@ Off Desativado Flip on long edge Virar na borda longa Flip on short edge Virar na borda curta + Printer default Padrão da impressora Paper: Papel: Pages to Print Páginas a imprimir More options: Mais opções: diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 3baec45600..62a9535d00 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -1220,6 +1220,7 @@ Off Выкл Flip on long edge Переворот по длинной стороне Flip on short edge Переворот по короткой стороне + Printer default По умолчанию для принтера Paper: Бумага: Pages to Print Страницы для печати More options: Дополнительные параметры: diff --git a/crates/ui-egui/src/i18n/sl.tsv b/crates/ui-egui/src/i18n/sl.tsv index 5ba9dc90c3..b1135979d6 100644 --- a/crates/ui-egui/src/i18n/sl.tsv +++ b/crates/ui-egui/src/i18n/sl.tsv @@ -839,6 +839,7 @@ Flip image Prezrcali sliko Flip on long edge Obrni po dolgem robu Flip on short edge Obrni po kratkem robu + Printer default Privzeto tiskalnika Flip vertical Prezrcali navpično Folders Mape Font Size: Velikost pisave: diff --git a/crates/ui-egui/src/i18n/te.tsv b/crates/ui-egui/src/i18n/te.tsv index e0d6a2b34c..0b8533c3b2 100644 --- a/crates/ui-egui/src/i18n/te.tsv +++ b/crates/ui-egui/src/i18n/te.tsv @@ -1190,6 +1190,7 @@ comment menu Edit సవరించు Off ఆఫ్ Flip on long edge పొడవైన అంచున తిరగేయి Flip on short edge పొట్టి అంచున తిరగేయి + Printer default ప్రింటర్ డిఫాల్ట్ Paper: కాగితం: Pages to Print ముద్రించాల్సిన పేజీలు More options: మరిన్ని ఎంపికలు: diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 48d34a30da..aead2b0c99 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -1177,6 +1177,7 @@ Off Kapalı Flip on long edge Uzun kenardan çevir Flip on short edge Kısa kenardan çevir + Printer default Yazıcı varsayılanı Paper: Kağıt: Pages to Print Yazdırılacak Sayfalar More options: Diğer seçenekler: diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 246a4a2c56..af7a9517ac 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -1217,6 +1217,7 @@ Off Вимкнено Flip on long edge Перевертати за довгим краєм Flip on short edge Перевертати за коротким краєм + Printer default За замовчуванням принтера Paper: Папір: Pages to Print Сторінки для друку More options: Додаткові параметри: diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 56caf38859..5ea0baa431 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -589,6 +589,7 @@ theme Appearance Giao diện Flip horizontal Lật ngang Flip on long edge Lật theo cạnh dài Flip on short edge Lật theo cạnh ngắn + Printer default Mặc định của máy in Flip vertical Lật dọc Folders Thư mục Font Size: Cỡ chữ: diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 645c0ab301..301c5868f1 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -906,6 +906,7 @@ Off 关 Flip on long edge 沿长边翻转 Flip on short edge 沿短边翻转 + Printer default 打印机默认 Paper: 纸张: Pages to Print 要打印的页面 More options: 更多选项: diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 712f0666a5..dd74c484e3 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -850,6 +850,7 @@ Off 關 Flip on long edge 沿長邊翻轉 Flip on short edge 沿短邊翻轉 + Printer default 印表機預設 Paper: 紙張: Pages to Print 要列印的頁面 More options: 更多選項: diff --git a/crates/ui-egui/src/print_ui.rs b/crates/ui-egui/src/print_ui.rs index 186d8edc43..deb31ce04c 100644 --- a/crates/ui-egui/src/print_ui.rs +++ b/crates/ui-egui/src/print_ui.rs @@ -51,7 +51,8 @@ pub struct PrintDraft { pub copies: u32, pub collate: bool, pub grayscale: bool, - pub duplex: spool::Duplex, + /// `None` = the printer's own default (its Properties), as Acrobat does. + pub duplex: Option, pub which: Which, pub range: String, /// The pages `Which::Selected` prints (0-based, in page order); empty when none were picked. @@ -108,7 +109,7 @@ impl Default for PrintDraft { copies: 1, collate: true, grayscale: false, - duplex: spool::Duplex::Off, + duplex: None, which: Which::All, range: String::new(), selected: Vec::new(), @@ -168,7 +169,7 @@ impl PrintDraft { /// The engine settings for this draft (page count and labels from the document). pub fn settings(&self, count: usize, labels: &[String]) -> Result { - if self.handling == Handling::Multiple && self.order == PageOrder::CutStack && self.duplex != spool::Duplex::Off { + if self.cut_and_stack() && matches!(self.duplex, Some(spool::Duplex::LongEdge | spool::Duplex::ShortEdge)) { return Err("Cut and stack needs Two-sided: Off. Print single-sided sheets.".into()); } let range = match self.which { @@ -211,12 +212,17 @@ impl PrintDraft { }) } + fn cut_and_stack(&self) -> bool { + self.handling == Handling::Multiple && self.order == PageOrder::CutStack + } + pub fn job(&self, title: &str) -> spool::Job { spool::Job { printer: self.printer.clone(), copies: self.copies.max(1), collate: self.collate, - duplex: Some(self.duplex), + // Cut and stack needs single-sided sheets, whatever the printer's default. + duplex: if self.cut_and_stack() { Some(spool::Duplex::Off) } else { self.duplex }, scaling: spool::Scaling::Exact, grayscale: self.grayscale, title: title.to_string(), @@ -928,9 +934,10 @@ pub(crate) fn body( "duplex", &mut d.duplex, &[ - (spool::Duplex::Off, tl!("Off")), - (spool::Duplex::LongEdge, tl!("Flip on long edge")), - (spool::Duplex::ShortEdge, tl!("Flip on short edge")), + (None, tl!("Printer default")), + (Some(spool::Duplex::Off), tl!("Off")), + (Some(spool::Duplex::LongEdge), tl!("Flip on long edge")), + (Some(spool::Duplex::ShortEdge), tl!("Flip on short edge")), ], 160.0, ); @@ -1305,11 +1312,25 @@ mod tests { #[test] fn composed_sheets_keep_exact_scaling_and_the_dialogs_duplex_choice() { - let draft = PrintDraft { duplex: spool::Duplex::LongEdge, ..PrintDraft::default() }; + let draft = PrintDraft { duplex: Some(spool::Duplex::LongEdge), ..PrintDraft::default() }; let job = draft.job("report.pdf"); assert_eq!(job.scaling, spool::Scaling::Exact); assert_eq!(job.duplex, Some(spool::Duplex::LongEdge)); - assert_eq!(PrintDraft::default().job("report.pdf").duplex, Some(spool::Duplex::Off)); + let off = PrintDraft { duplex: Some(spool::Duplex::Off), ..PrintDraft::default() }; + assert_eq!(off.job("report.pdf").duplex, Some(spool::Duplex::Off)); + } + + /// Untouched, Two-sided sends nothing, so the duplex set in the printer's Properties applies + /// (#2422); cut and stack still asks for single-sided sheets. + #[test] + fn two_sided_follows_the_printer_until_chosen() { + assert_eq!(PrintDraft::default().duplex, None); + assert_eq!(PrintDraft::default().job("report.pdf").duplex, None); + let mut cut = PrintDraft { handling: Handling::Multiple, order: PageOrder::CutStack, ..PrintDraft::default() }; + assert_eq!(cut.job("report.pdf").duplex, Some(spool::Duplex::Off)); + assert!(cut.settings(4, &[]).is_ok(), "the printer's default doesn't block cut and stack"); + cut.duplex = Some(spool::Duplex::ShortEdge); + assert!(cut.settings(4, &[]).is_err()); } /// A job being spooled holds the progress card, counting its sheets as the spooler reports diff --git a/crates/ui-egui/tests/print.rs b/crates/ui-egui/tests/print.rs index 96a7e12f14..7454c03a8d 100644 --- a/crates/ui-egui/tests/print.rs +++ b/crates/ui-egui/tests/print.rs @@ -182,11 +182,11 @@ fn cut_stack_dialog_previews_saves_and_refuses_duplex() { h.get_by_label("›").click(); h.run_steps(2); h.get_by_label("Sheet 2 of 3"); - h.state_mut().print_draft.duplex = Duplex::LongEdge; + h.state_mut().print_draft.duplex = Some(Duplex::LongEdge); h.run_steps(2); h.get_by_label_contains("Cut and stack needs Two-sided: Off"); assert!(!h.state_mut().print_now()); - h.state_mut().print_draft.duplex = Duplex::Off; + h.state_mut().print_draft.duplex = Some(Duplex::Off); h.state_mut().print_draft.printer = None; let out = std::env::temp_dir().join(format!("pdfcraft-cut-stack-ui-{}.pdf", std::process::id())); h.state_mut().save_override = Some(out.to_string_lossy().into_owned()); diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index 20a5a69e4f..c592bca6c6 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -8020,8 +8020,8 @@ milestone = "M10" status = "shipped" commands = ["print.dialog"] tools = ["doc_print"] -evidence = ["crates/print/src/tests.rs::spooler_arguments_and_printer_list"] -notes = "long/short edge via the spooler: lp -o sides=…, and the Windows driver's Duplex setting" +evidence = ["crates/print/src/tests.rs::spooler_arguments_and_printer_list", "crates/ui-egui/src/print_ui.rs::two_sided_follows_the_printer_until_chosen", "crates/automation/src/printing.rs::duplex_printer_leaves_two_sided_to_the_printer"] +notes = "long/short edge via the spooler: lp -o sides=…, and the Windows driver's Duplex setting. Two-sided starts at Printer default, as in Acrobat: nothing is sent, so the printer's own default (its Properties) applies until Off or an edge is chosen (doc_print: duplex \"printer\"). Missing: the document's /ViewerPreferences /Duplex preset isn't read yet" [[feature]] id = "print.n-up"