M10: Windows two-sided binds on the chosen edge; note drivers that ignore Two-sided (#1154)

The print script mapped "Flip on long edge" to System.Drawing's Duplex.Horizontal and
"short edge" to Vertical. Those are DEVMODE's DMDUP_HORIZONTAL (short-edge binding) and
DMDUP_VERTICAL (long-edge binding), so every Windows two-sided job bound on the other edge:
a PCL driver received @PJL SET BINDING = TOP for long edge. They are swapped back.

Win32_Printer's Capabilities say whether a driver prints two-sided (3, Duplex Printing). The
printer list already comes from Win32_Printer, so it now carries that as Printer::two_sided
(None on CUPS), the printers tool reports it, and the Print dialog notes when the selected
printer's driver doesn't take two-sided settings and points to Properties…. Such drivers keep
their own setting even with Two-sided Off. No vendor-specific names are used.

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
This commit is contained in:
authored and GitHub committed 2026-10-11 18:41:02 -04:00
1 parent 287fce8f9c
commit fb97ce958d
26 files changed
+140 -41

No files matched your search

+19 -19
View File
@@ -7,7 +7,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Greek UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "7ed6a7c482dab19fe1b54a7704902610e79e9bc60f19115ce8af7b1ebca504ff"
sha256 = "1049c27ed90afac974f371c7e7e3bff94a2baebde2d820adc4c4c84e822e4cdd"
# ATTRIBUTION.toml: one entry per asset (AGENTS.md §1). The source of truth for provenance and licences.
# ATTRIBUTION.md is generated from this file: `cargo xtask assets --write`. Checked by `cargo xtask assets` (part of `xtask ci`).
@@ -30,7 +30,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Simplified Chinese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "e5141febe07d89c4c4c7b2d2f87abf71ad1ab8dc4a7880d03805db6f0022118b"
sha256 = "c30f72a1e902ddee4cb902560aaa91dfd3567490fd64db972f74a64fdb9af62c"
[[asset]]
path = "crates/ui-egui/src/i18n/zh-hant.tsv"
@@ -41,7 +41,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Traditional Chinese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "6dff4fded1106659f7c0b1c7e2c671c02e59313ab58875fabf591a9356098767"
sha256 = "ad2c35209b7dbcd366038e6a179d695937ef1c3e333f64eb803de738766fb3e9"
[[asset]]
path = "crates/ui-egui/src/i18n/cs.tsv"
@@ -52,7 +52,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Czech UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "1978cfd55b08fe9905e00ddd16a10d3d0f5371143e2f00a58a77affcc1d874c3"
sha256 = "5d5456a0e51a3287ada0faec559dc137197159e64b4df8e7ee22ac8c563a9de9"
[[asset]]
path = "crates/ui-egui/src/i18n/pt-br.tsv"
@@ -63,7 +63,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Brazilian Portuguese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "928435778bd8c703083087d401851ab01dc038db3ff568e572b6cf646f805b03"
sha256 = "f0b1e7ac0bf248cf09bf2d038e9224c24d2edb4c3b3ba92b3806f9384161e77b"
[[asset]]
path = "crates/ui-egui/src/i18n/es.tsv"
@@ -74,7 +74,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Spanish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "4f95f2dc524177a5a6261f2f6df6def2cd061596bddac20b264cf6051978b3e3"
sha256 = "468443e23821e248c934e222b245e87f926c38786575b6290d33f959d39a20bf"
[[asset]]
path = "crates/ui-egui/src/i18n/fr.tsv"
@@ -85,7 +85,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "French UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "7c0df34f12030b1dc266413839dbb507bc890cc8847f6b2eac324ed54ff3fc19"
sha256 = "575b7487137ef8c4b4ae33fc7bdbd50370fa28078e41727216046fdc5b89b81a"
[[asset]]
path = "crates/ui-egui/src/i18n/de.tsv"
@@ -96,7 +96,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "German UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "77f4ec8eb9cba7e2f2aad0883a91a62ca871140135c586f32c725f66774a0632"
sha256 = "30f526fc7a1c14737bbaea272f45f123ffe61107188fe74375bd51b4808c9575"
[[asset]]
path = "crates/ui-egui/src/i18n/te.tsv"
@@ -118,7 +118,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Italian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "3d1af7e703706bf4c814062871b47308b70f358356c3b17782dde3eaaa66abb7"
sha256 = "8404b7ae758b3cddb28efd8f27405d260cf4193e3fecf68ea9422aebd4d063b4"
[[asset]]
path = "crates/ui-egui/src/i18n/pl.tsv"
@@ -129,7 +129,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Polish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "d03048fe760c7daf08c0d8ed8470633c0eaca8f68c41d1c8ce889813c7f6fab1"
sha256 = "0ae768bcb0d7d3c24f5636d85df1532e626452ff97397312270379501a025c52"
[[asset]]
path = "crates/ui-egui/src/i18n/ru.tsv"
@@ -140,7 +140,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Russian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "69e07b82ae409de7544f538fedc8e56cdb9f4092be7f45cc7366bddbc03ce93f"
sha256 = "7b0798851885f59105046e861f682f529f5666b0ef8edc3fb3ba6255459d80ff"
[[asset]]
path = "crates/ui-egui/src/i18n/hu.tsv"
@@ -151,7 +151,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Hungarian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "0a71d58a88ea532358d3b133eba3aae217ad4bcbb763b6235b137615ce226d2b"
sha256 = "b1c44f5f1b6deadcdbe216019750f24beeac5533b6f519be4e361c97b57d5ac1"
[[asset]]
path = "crates/ui-egui/src/i18n/bg.tsv"
@@ -162,7 +162,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Bulgarian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "1e5172befbe995c800cd69e9d6abc02c92544b62d8e6dac0f4a0c26cd0b1efbb"
sha256 = "9dc87fafc98f9d30c4bafa8014f19b32b0e83a0608b2202e647988abb4e6303d"
[[asset]]
path = "crates/ui-egui/src/i18n/kk.tsv"
@@ -173,7 +173,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Kazakh UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "dd76d58abda6153e5c0b9fcc761d652cf2ed96522fab18c9ce87e9cec594043a"
sha256 = "af1cf608a1561057c530d5716ea8aac27dacf9fe4ab49741743b980f587a1409"
[[asset]]
path = "crates/ui-egui/src/i18n/ja.tsv"
@@ -184,7 +184,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Japanese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "6c1ca7f6d53454c69b3e362a2a09c5c28d04b615c53936c639e3b43c880552d3"
sha256 = "f0e5375a5ee6b1151e312ba333f1a9a9ce0c55b27928da9bd6cff1ba95b8a5a1"
[[asset]]
path = "crates/ui-egui/src/i18n/uk.tsv"
@@ -195,7 +195,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Ukrainian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "9151c1049289e318409965c5ad061448db9f35a7e8f14a754ba83616e3d87930"
sha256 = "9663c11a1f02c55bafb37ed2b12fc4d8542a2f2ed1c6dfdefd0b1f0629208cb8"
[[asset]]
path = "crates/ui-egui/src/i18n/ar.tsv"
@@ -206,7 +206,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Arabic UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "096be489498c16dbf85fcd6d2d0d96590736a8b0bbe3b3a5b557e2627c89f9c1"
sha256 = "3bfd85b5f6d3ab75573fa7511487561d8ee8b37a69dbf361c5b142cd50fb38e6"
[[asset]]
path = "crates/ui-egui/src/i18n/tr.tsv"
@@ -217,7 +217,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Turkish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "e72215bc4bf21f598bf82a6ac55551bdef47d62cceedea239d89c0febd9cb1b7"
sha256 = "e9f46d2738530d4114b9e0a3e3c51bbd2df9bc51cb84506c10f499baa566650d"
[[asset]]
path = "assets/icons/accessibility.svg"
@@ -3339,4 +3339,4 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Slovene UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "7cc8bf51e042d07a397dbebf0747b496879e721a6644f1af197cfb3cbcbe7904"
sha256 = "855e743c3b21f4d8e158d30c5757ead4f2b6b8bf830c87115ae99e6d16da76a3"
+10 -1
View File
@@ -12,7 +12,16 @@ fn bad(m: impl Into<String>) -> ToolError {
impl Automation {
pub(crate) fn printers(&self) -> Result<Value> {
let list: Vec<Value> = spool::printers().into_iter().map(|p| json!({ "name": p.name, "default": p.default })).collect();
let list: Vec<Value> = spool::printers()
.into_iter()
.map(|p| {
let mut v = json!({ "name": p.name, "default": p.default });
if let Some(two_sided) = p.two_sided {
v["two_sided"] = json!(two_sided);
}
v
})
.collect();
Ok(json!({ "count": list.len(), "printers": list }))
}
+23 -10
View File
@@ -12,6 +12,10 @@ use crate::PrintError;
pub struct Printer {
pub name: String,
pub default: bool,
/// Whether the driver says it can print two-sided, where the spooler tells (Windows: the
/// printer's `Duplex Printing` capability). A driver without it ignores Two-sided and uses
/// its own setting. `None` when unknown (CUPS maps `sides` itself).
pub two_sided: Option<bool>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
@@ -53,7 +57,7 @@ pub fn parse_lpstat(out: &str) -> Vec<Printer> {
out.lines()
.filter_map(|l| l.strip_prefix("printer "))
.filter_map(|l| l.split_whitespace().next())
.map(|n| Printer { name: n.to_string(), default: default == Some(n) })
.map(|n| Printer { name: n.to_string(), default: default == Some(n), two_sided: None })
.collect()
}
@@ -340,7 +344,7 @@ pub fn printers_parsed(queues: &str, available: Option<&str>) -> Vec<Printer> {
if let Some(names) = available {
for name in parse_lpstat_e(names) {
if !printers.iter().any(|p| p.name == name) {
printers.push(Printer { default: default == Some(name.as_str()), name });
printers.push(Printer { default: default == Some(name.as_str()), name, two_sided: None });
}
}
}
@@ -367,23 +371,32 @@ pub(crate) fn powershell_command(script: &str) -> std::process::Command {
c
}
/// The Windows printer list: one line per printer, `*` on the system default. UTF-8, through
/// the output encoding the script sets first. Only the two properties the list needs are
/// The Windows printer list: one line per printer, `*` on the system default, then a tab and
/// `2` when the driver reports the Duplex Printing capability (3), `1` when it doesn't. UTF-8,
/// through the output encoding the script sets first. Only the properties the list needs are
/// pulled, so a machine with many printers doesn't wait for the rest of each record.
#[cfg(windows)]
const POWERSHELL_PRINTERS: &str = "[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false); Get-CimInstance Win32_Printer -Property Name, Default | ForEach-Object { if ($_.Default) { '*' + $_.Name } else { $_.Name } }";
const POWERSHELL_PRINTERS: &str = "[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false); Get-CimInstance Win32_Printer -Property Name, Default, Capabilities | ForEach-Object { $sides = if (@($_.Capabilities) -contains 3) { '2' } else { '1' }; if ($_.Default) { '*' + $_.Name + [char]9 + $sides } else { $_.Name + [char]9 + $sides } }";
/// The Print dialog's printers from the Windows printer list: one per line, `*` before the
/// system default (a Windows queue name may not contain `*`). The first `*` wins when a broken
/// spooler marks more than one, so the dialog still has a single default to preselect.
/// system default (a Windows queue name may not contain `*`), and after a tab `2` or `1` for
/// whether the driver can print two-sided. The first `*` wins when a broken spooler marks more
/// than one, so the dialog still has a single default to preselect.
pub fn parse_windows_printers(out: &str) -> Vec<Printer> {
let mut printers: Vec<Printer> = out
.lines()
.map(|l| l.strip_suffix('\r').unwrap_or(l))
.filter(|l| !l.is_empty())
.map(|l| match l.strip_prefix('*') {
Some(name) => Printer { name: name.to_string(), default: true },
None => Printer { name: l.to_string(), default: false },
.map(|l| {
let (l, two_sided) = match l.rsplit_once('\t') {
Some((name, "2")) => (name, Some(true)),
Some((name, "1")) => (name, Some(false)),
_ => (l, None),
};
match l.strip_prefix('*') {
Some(name) => Printer { name: name.to_string(), default: true, two_sided },
None => Printer { name: l.to_string(), default: false, two_sided },
}
})
.collect();
let mut seen_default = false;
+28 -8
View File
@@ -234,7 +234,10 @@ fn spooler_arguments_and_printer_list() {
let out = "printer Office_Laser is idle. enabled since Thu Oct 1 09:00:00 2026\nprinter Label_Writer disabled since …\nsystem default destination: Office_Laser\n";
assert_eq!(
parse_lpstat(out),
[spool::Printer { name: "Office_Laser".into(), default: true }, spool::Printer { name: "Label_Writer".into(), default: false }]
[
spool::Printer { name: "Office_Laser".into(), default: true, two_sided: None },
spool::Printer { name: "Label_Writer".into(), default: false, two_sided: None }
]
);
assert!(parse_lpstat("lpstat: No destinations added.\nno system default destination\n").is_empty());
let job = Job {
@@ -274,16 +277,19 @@ fn driverless_printers_join_the_list_without_a_queue() {
let queues = "printer Office_Laser is idle. enabled since Thu Oct 1 09:00:00 2026\nsystem default destination: Office_Laser\n";
assert_eq!(
spool::printers_parsed(queues, Some("Basement_Color\nOffice_Laser\n")),
[spool::Printer { name: "Office_Laser".into(), default: true }, spool::Printer { name: "Basement_Color".into(), default: false },],
[
spool::Printer { name: "Office_Laser".into(), default: true, two_sided: None },
spool::Printer { name: "Basement_Color".into(), default: false, two_sided: None },
],
"queues first, then the driverless destination; the queue listed twice is not doubled"
);
// An old spooler without `-e` (CUPS < 1.7) keeps its queues; only the driverless names are lost.
assert_eq!(spool::printers_parsed(queues, None), [spool::Printer { name: "Office_Laser".into(), default: true }]);
assert_eq!(spool::printers_parsed(queues, None), [spool::Printer { name: "Office_Laser".into(), default: true, two_sided: None }]);
// The default can itself be driverless (`lpoptions -d` names a destination no queue exists
// for): it keeps its marker, so the dialog preselects the user's default, not the first queue.
assert_eq!(
spool::printers_parsed("system default destination: Basement_Color\n", Some("Basement_Color\n")),
[spool::Printer { name: "Basement_Color".into(), default: true }],
[spool::Printer { name: "Basement_Color".into(), default: true, two_sided: None }],
"a default destination without a queue is still the default"
);
assert!(spool::parse_lpstat_e("\n \n").is_empty(), "no destinations is empty, not [\"\"]");
@@ -443,12 +449,26 @@ fn windows_printers_carry_one_default_and_keep_their_names() {
// Windows marks the system default with a `*` no queue name may contain; names hold spaces,
// parentheses and non-ASCII letters, and the lines end with CRLF.
let printers = parse_windows_printers("*Microsoft Print to PDF\r\nHP LaserJet 400 (Copy 2)\r\nКопир Canon iR\r\n\r\n*XPS via USB\r\n");
assert_eq!(printers[0], Printer { name: "Microsoft Print to PDF".into(), default: true });
assert_eq!(printers[1], Printer { name: "HP LaserJet 400 (Copy 2)".into(), default: false });
assert_eq!(printers[2], Printer { name: "Копир Canon iR".into(), default: false });
assert_eq!(printers[0], Printer { name: "Microsoft Print to PDF".into(), default: true, two_sided: None });
assert_eq!(printers[1], Printer { name: "HP LaserJet 400 (Copy 2)".into(), default: false, two_sided: None });
assert_eq!(printers[2], Printer { name: "Копир Canon iR".into(), default: false, two_sided: None });
// A spooler marking two defaults: the first still wins, so the dialog has one to preselect.
assert_eq!(printers[3], Printer { name: "XPS via USB".into(), default: false });
assert_eq!(printers[3], Printer { name: "XPS via USB".into(), default: false, two_sided: None });
assert_eq!(parse_windows_printers("\r\n"), Vec::new());
// After a tab, whether the driver reports two-sided printing; a tab inside a name stays.
let sided = parse_windows_printers("*Office PCL\t2\r\nOffice PS\t1\r\nTab\tName\t2\r\n");
assert_eq!(sided[0], Printer { name: "Office PCL".into(), default: true, two_sided: Some(true) });
assert_eq!(sided[1], Printer { name: "Office PS".into(), default: false, two_sided: Some(false) });
assert_eq!(sided[2], Printer { name: "Tab\tName".into(), default: false, two_sided: Some(true) });
}
#[test]
fn the_windows_print_script_binds_long_edge_on_the_long_edge() {
// System.Drawing's Duplex values are DEVMODE's: Vertical binds a portrait page on its long
// edge, Horizontal on its short edge (checked against a PCL driver's @PJL SET BINDING).
let script = include_str!("windows_print.ps1").replace("\r\n", "\n");
assert!(script.contains("'long-edge' { [System.Drawing.Printing.Duplex]::Vertical }"));
assert!(script.contains("'short-edge' { [System.Drawing.Printing.Duplex]::Horizontal }"));
}
#[test]
+4 -2
View File
@@ -97,9 +97,11 @@ if ($env:PDFCRAFT_PRINT_PRINTER) {
}
$settings.Copies = [Math]::Min(32767, [Math]::Max(1, [int]$env:PDFCRAFT_PRINT_COPIES))
$settings.Collate = ($env:PDFCRAFT_PRINT_COLLATE -eq '1')
# DEVMODE's names are about the binding edge of a portrait page: Vertical (DMDUP_VERTICAL)
# binds on the long edge, Horizontal (DMDUP_HORIZONTAL) on the short edge.
$settings.Duplex = switch ($env:PDFCRAFT_PRINT_DUPLEX) {
'long-edge' { [System.Drawing.Printing.Duplex]::Horizontal }
'short-edge' { [System.Drawing.Printing.Duplex]::Vertical }
'long-edge' { [System.Drawing.Printing.Duplex]::Vertical }
'short-edge' { [System.Drawing.Printing.Duplex]::Horizontal }
default { [System.Drawing.Printing.Duplex]::Simplex }
}
if ($env:PDFCRAFT_PRINT_GRAYSCALE -eq '1') { $printer.DefaultPageSettings.Color = $false }
+1
View File
@@ -2270,3 +2270,4 @@ scroll speed Fast سريعة
Original الأصل
Greyscale تدرج رمادي
Black and white أبيض وأسود
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… لا يقبل برنامج تشغيل هذه الطابعة إعدادات الطباعة على الوجهين من البرامج الأخرى، لذا يُطبَّق إعداده الخاص. غيّره من خصائص…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -2212,3 +2212,4 @@ scroll speed Fast Висока
Original Оригинал
Greyscale Нива на сивото
Black and white Черно-бяло
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Драйверът на този принтер не приема настройки за двустранен печат от други програми и прилага собствената си настройка. Променете я от Свойства…
Can't render this file because it contains an unexpected character in line 8 and column 16.
+1
View File
@@ -2324,5 +2324,6 @@ scroll speed Fast Rychlá
Original Originál
Greyscale Stupně šedi
Black and white Černobílé
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Ovladač této tiskárny nepřijímá nastavení oboustranného tisku z jiných programů, proto platí jeho vlastní nastavení. Změňte ho ve Vlastnosti…
This field's font can't draw “{c}” ({code}), so PdfCraft shows ? in its place. The value is saved, and other PDF viewers redraw the field from it. Písmo tohoto pole neumí zobrazit „{c}“ ({code}), proto PdfCraft místo něj ukazuje ?. Hodnota se uloží a jiné prohlížeče PDF pole podle ní překreslí.
The fonts PdfCraft writes with can't draw “{c}” ({code}), so it shows as ? in every PDF viewer. The text is saved. Písma, kterými PdfCraft píše, neumí zobrazit „{c}“ ({code}), proto se ve všech prohlížečích PDF zobrazí jako ?. Text se uloží.
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -2250,3 +2250,4 @@ scroll speed Fast Schnell
Original Original
Greyscale Graustufen
Black and white Schwarzweiß
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Der Treiber dieses Druckers übernimmt keine Einstellungen für beidseitigen Druck von anderen Programmen, daher gilt seine eigene Einstellung. Ändern unter Eigenschaften…
Can't render this file because it contains an unexpected character in line 6 and column 51.
+1
View File
@@ -2266,5 +2266,6 @@ scroll speed Fast Γρήγορη
Original Πρωτότυπο
Greyscale Κλίμακα του γκρι
Black and white Ασπρόμαυρο
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Το πρόγραμμα οδήγησης αυτού του εκτυπωτή δεν δέχεται ρυθμίσεις εκτύπωσης διπλής όψης από άλλα προγράμματα, οπότε ισχύει η δική του ρύθμιση. Αλλάξτε τη στις Ιδιότητες…
This field's font can't draw “{c}” ({code}), so PdfCraft shows ? in its place. The value is saved, and other PDF viewers redraw the field from it. Η γραμματοσειρά αυτού του πεδίου δεν μπορεί να σχεδιάσει το «{c}» ({code}), γι' αυτό το PdfCraft δείχνει ? στη θέση του. Η τιμή αποθηκεύεται και άλλα προγράμματα προβολής PDF επανασχεδιάζουν το πεδίο από αυτήν.
The fonts PdfCraft writes with can't draw “{c}” ({code}), so it shows as ? in every PDF viewer. The text is saved. Οι γραμματοσειρές με τις οποίες γράφει το PdfCraft δεν μπορούν να σχεδιάσουν το «{c}» ({code}), γι' αυτό εμφανίζεται ως ? σε κάθε πρόγραμμα προβολής PDF. Το κείμενο αποθηκεύεται.
Can't render this file because it contains an unexpected character in line 477 and column 12.
+1
View File
@@ -2300,3 +2300,4 @@ scroll speed Fast Rápida
Original Original
Greyscale Escala de grises
Black and white Blanco y negro
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… El controlador de esta impresora no acepta los ajustes de doble cara de otros programas, así que se aplica su propio ajuste. Cámbielo en Propiedades…
Can't render this file because it contains an unexpected character in line 6 and column 60.
+1
View File
@@ -2249,3 +2249,4 @@ scroll speed Fast Rapide
Original Original
Greyscale Niveaux de gris
Black and white Noir et blanc
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Le pilote de cette imprimante n'accepte pas les réglages recto verso des autres programmes ; son propre réglage s'applique. Modifiez-le dans Propriétés…
Can't render this file because it contains an unexpected character in line 6 and column 39.
+1
View File
@@ -2248,3 +2248,4 @@ scroll speed Fast Gyors
Original Eredeti
Greyscale Szürkeárnyalatos
Black and white Fekete-fehér
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… A nyomtató illesztőprogramja nem fogadja el más programoktól a kétoldalas nyomtatás beállítását, ezért a saját beállítása érvényes. Módosítás: Tulajdonságok…
Can't render this file because it contains an unexpected character in line 7 and column 44.
+1
View File
@@ -2258,3 +2258,4 @@ scroll speed Fast Veloce
Original Originale
Greyscale Scala di grigi
Black and white Bianco e nero
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Il driver di questa stampante non accetta le impostazioni fronte-retro da altri programmi, quindi vale la sua impostazione. Modificala in Proprietà…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -2249,3 +2249,4 @@ scroll speed Fast 速い
Original オリジナル
Greyscale グレースケール
Black and white 白黒
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… このプリンターのドライバーは他のプログラムからの両面印刷の設定を受け付けないため、ドライバー自身の設定が使われます。変更先: プロパティ…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -2248,5 +2248,6 @@ scroll speed Fast Жылдам
Original Түпнұсқа
Greyscale Сұр реңктер
Black and white Ақ-қара
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Бұл принтердің драйвері басқа бағдарламалардан екі жақты басып шығару баптауларын қабылдамайды, сондықтан өз баптауы қолданылады. Оны мына жерден өзгертіңіз: Сипаттар…
This field's font can't draw “{c}” ({code}), so PdfCraft shows ? in its place. The value is saved, and other PDF viewers redraw the field from it. Бұл өрістің қаріпі «{c}» ({code}) таңбасын сала алмайды, сондықтан PdfCraft оның орнына ? көрсетеді. Мән сақталады, ал басқа PDF қарау құралдары өрісті соған қарап қайта салады.
The fonts PdfCraft writes with can't draw “{c}” ({code}), so it shows as ? in every PDF viewer. The text is saved. PdfCraft жазатын қаріптер «{c}» ({code}) таңбасын сала алмайды, сондықтан ол барлық PDF қарау құралдарында ? болып көрінеді. Мәтін сақталады.
Can't render this file because it contains an unexpected character in line 813 and column 12.
+1
View File
@@ -2232,3 +2232,4 @@ scroll speed Fast Szybka
Original Oryginał
Greyscale Skala szarości
Black and white Czarno-biały
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Sterownik tej drukarki nie przyjmuje ustawień druku dwustronnego z innych programów, więc obowiązuje jego własne ustawienie. Zmień je w oknie Właściwości…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -2263,3 +2263,4 @@ scroll speed Fast Rápida
Original Original
Greyscale Escala de cinza
Black and white Preto e branco
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… O driver desta impressora não aceita configurações de frente e verso de outros programas, então vale a configuração dele. Altere em Propriedades…
Can't render this file because it contains an unexpected character in line 9 and column 16.
+1
View File
@@ -2162,3 +2162,4 @@ scroll speed Fast Высокая
Original Оригинал
Greyscale Оттенки серого
Black and white Чёрно-белый
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Драйвер этого принтера не принимает настройки двусторонней печати от других программ, поэтому действует его собственная настройка. Измените её в окне Свойства…
Can't render this file because it contains an unexpected character in line 9 and column 16.
+1
View File
@@ -2255,5 +2255,6 @@ scroll speed Fast Hitro
Original Izvirnik
Greyscale Sivine
Black and white Črno-belo
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Gonilnik tega tiskalnika ne sprejema nastavitev obojestranskega tiskanja iz drugih programov, zato velja njegova lastna nastavitev. Spremenite jo v Lastnosti…
This field's font can't draw “{c}” ({code}), so PdfCraft shows ? in its place. The value is saved, and other PDF viewers redraw the field from it. Pisava tega polja ne more prikazati »{c}« ({code}), zato PdfCraft namesto njega prikaže ?. Vrednost se shrani, drugi pregledovalniki PDF pa polje znova narišejo iz nje.
The fonts PdfCraft writes with can't draw “{c}” ({code}), so it shows as ? in every PDF viewer. The text is saved. Pisave, s katerimi piše PdfCraft, ne morejo prikazati »{c}« ({code}), zato se v vseh pregledovalnikih PDF prikaže kot ?. Besedilo se shrani.
Can't render this file because it contains an unexpected character in line 9 and column 16.
+1
View File
@@ -2246,5 +2246,6 @@ scroll speed Fast Hızlı
Original Orijinal
Greyscale Gri tonlamalı
Black and white Siyah beyaz
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Bu yazıcının sürücüsü diğer programlardan gelen çift taraflı yazdırma ayarlarını kabul etmez, bu yüzden kendi ayarı geçerlidir. Değiştirmek için: Özellikler…
This field's font can't draw “{c}” ({code}), so PdfCraft shows ? in its place. The value is saved, and other PDF viewers redraw the field from it. Bu alanın yazı tipi “{c}” ({code}) karakterini çizemiyor, bu yüzden PdfCraft yerine ? gösteriyor. Değer kaydedilir ve diğer PDF görüntüleyiciler alanı ondan yeniden çizer.
The fonts PdfCraft writes with can't draw “{c}” ({code}), so it shows as ? in every PDF viewer. The text is saved. PdfCraft'ın yazdığı yazı tipleri “{c}” ({code}) karakterini çizemiyor, bu yüzden her PDF görüntüleyicide ? olarak görünür. Metin kaydedilir.
Can't render this file because it contains an unexpected character in line 811 and column 12.
+1
View File
@@ -2271,3 +2271,4 @@ scroll speed Fast Швидка
Original Оригінал
Greyscale Відтінки сірого
Black and white Чорно-білий
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… Драйвер цього принтера не приймає налаштування двобічного друку від інших програм, тому діє його власне налаштування. Змініть його у вікні Властивості…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -2213,3 +2213,4 @@ scroll speed Fast 快
Original 原始
Greyscale 灰度
Black and white 黑白
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… 此打印机的驱动程序不接受其他程序的双面打印设置,因此使用其自身设置。更改位置:属性…
Can't render this file because it contains an unexpected character in line 9 and column 16.
+1
View File
@@ -2126,3 +2126,4 @@ scroll speed Fast 快
Original 原始
Greyscale 灰階
Black and white 黑白
This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties… 此印表機的驅動程式不接受其他程式的雙面列印設定,因此使用其自身設定。變更位置:屬性…
Can't render this file because it contains an unexpected character in line 9 and column 16.
+7
View File
@@ -617,6 +617,13 @@ pub(crate) fn body(
});
ui.end_row();
});
// Windows says which drivers can't print two-sided as they're asked; they keep their
// own setting, even when Two-sided is Off.
if let Some(p) = d.printers.iter().find(|p| d.printer.as_ref() == Some(&p.name))
&& p.two_sided == Some(false)
{
ui.label(egui::RichText::new(tl!("This printer's driver doesn't take two-sided settings from other programs, so its own setting applies. Change it in Properties…")).small().color(t.text_muted));
}
if let Some(e) = &d.driver_error {
ui.label(egui::RichText::new(crate::i18n::fmt(tl!("Couldn't open the printer's preferences: {e}"), &[("e", e)])).small().color(t.text_muted));
}
+30 -1
View File
@@ -205,7 +205,10 @@ fn printers_listed_in_the_background_preselect_the_default_unless_one_was_picked
let wait = gate.clone();
h.state_mut().printer_source = Some(Arc::new(move || {
let _open = wait.lock();
vec![Printer { name: "Office".into(), default: false }, Printer { name: "Front Desk".into(), default: true }]
vec![
Printer { name: "Office".into(), default: false, two_sided: None },
Printer { name: "Front Desk".into(), default: true, two_sided: None },
]
}));
assert!(h.state_mut().execute("print.dialog"));
h.run_steps(2);
@@ -282,3 +285,29 @@ fn custom_page_cannot_silently_match_a_standard_paper() {
assert_eq!(h.state().print_draft.paper, before.paper);
assert_eq!(h.state().print_draft.size, before.size);
}
/// A driver Windows reports without two-sided printing keeps its own setting whatever Two-sided
/// says (some print both sides with it Off), so the dialog says where to change it.
#[test]
fn a_printer_without_two_sided_printing_points_to_its_properties() {
use pdfcraft_engine::print::spool::Printer;
let mut h = harness();
h.state_mut().printer_source = Some(std::sync::Arc::new(|| {
vec![
Printer { name: "Office PCL".into(), default: true, two_sided: Some(true) },
Printer { name: "Office PS".into(), default: false, two_sided: Some(false) },
]
}));
assert!(h.state_mut().execute("print.dialog"));
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
while h.state().print_draft.printers.is_empty() && std::time::Instant::now() < deadline {
h.run_steps(1);
std::thread::sleep(std::time::Duration::from_millis(5));
}
h.run_steps(2);
let note = "doesn't take two-sided settings";
assert!(h.query_by_label_contains(note).is_none(), "a driver that prints two-sided as asked");
h.state_mut().print_draft.printer = Some("Office PS".into());
h.run_steps(2);
h.get_by_label_contains(note);
}