forked from 1b-agent/opencadstudio-upstream
fix(print): send Windows plots to the chosen printer, name the default, report failures
On Windows a plot with no printer chosen went to the PDF application's bare "print" verb — whatever that application considered its default — and a viewer without a "printto" verb (Edge, store readers) silently dropped the printer the dialog had chosen. The dialog also never named the system default, and a failed printer enumeration left only "Default" and "PDF" with the reason written to a console nobody sees. - The default printer is looked up by name (GetDefaultPrinterW) both for the dialog's "System default printer (…)" entry and before every job. - A pure route decision (plan_print_route) resolves "Default" to that name and uses the PDF application only when it registered a "printto" verb for .pdf (AssocQueryStringW); otherwise the job goes straight to the spooler through the existing GDI path. The report says which: "Sent to printer: NAME (direct print | via the PDF application)". - list_printers reports the system's own error; the plot dialog shows it under the printer row and the command line warns once. A desktop without CUPS is an empty list, not an error. - PRINTERS command: default printer, listed printers (or the error) and, on Windows, the print route — a report to paste into an issue. - Properties… on the Default entry opens that printer's preferences sheet. - GDI path: the driver's dmSize is kept (its private data lives behind it), the print raster is no longer memoised for the session, and a spooled temporary PDF is removed. Tests: route planning (named / blank / no default / printto or not), the PRINTERS command; the two Windows-only GDI tests pass the resolved name. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
2b38f798c0
commit
c36ae08fd0
29 files changed
+592
-112
No files matched your search
@@ -869,6 +869,15 @@ impl OpenCADStudio {
|
||||
"PRINTALL" => {
|
||||
return Some(Task::done(Message::PrintAllOpen));
|
||||
}
|
||||
// PRINTERS — what the system reports about printing, on the
|
||||
// command line: the default printer, every printer it lists (or
|
||||
// why it could not), and how a plot will reach them. The report
|
||||
// a user pastes into a bug report instead of a screenshot.
|
||||
"PRINTERS" => {
|
||||
for line in crate::io::print_to_printer::printer_report() {
|
||||
self.command_line.push_output(&line);
|
||||
}
|
||||
}
|
||||
"EXPORT" | "EXPORTPDF" => {
|
||||
return Some(Task::done(Message::PlotExport));
|
||||
}
|
||||
@@ -2094,3 +2103,23 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod printers_command_tests {
|
||||
use crate::app::OpenCADStudio;
|
||||
|
||||
#[test]
|
||||
fn printers_reports_the_default_and_the_list() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
let before = app.command_line.history.len();
|
||||
let _ = app.run_command_line("PRINTERS");
|
||||
let lines: Vec<String> = app.command_line.history[before..]
|
||||
.iter()
|
||||
.map(|line| line.text.clone())
|
||||
.collect();
|
||||
// One line for the default printer, at least one for the list (a
|
||||
// count and names, "none", or the system's error).
|
||||
assert!(lines.len() >= 2, "{lines:?}");
|
||||
}
|
||||
}
|
||||
@@ -712,6 +712,7 @@ inventory::submit!(crate::command::CommandRegistration {
|
||||
"PERF",
|
||||
"PERSP",
|
||||
"PLOT",
|
||||
"PRINTERS",
|
||||
"PLOTSTYLE",
|
||||
"PLOTSTYLEEDITOR",
|
||||
"PLOTSTYLEPANEL",
|
||||
|
||||
+22
-3
@@ -3960,7 +3960,16 @@ impl OpenCADStudio {
|
||||
.collect();
|
||||
// Keep session-only choices while loading drawing fields from the layout.
|
||||
let d = &mut self.plot_dialog;
|
||||
d.printers = crate::io::print_to_printer::list_printers();
|
||||
match crate::io::print_to_printer::list_printers() {
|
||||
Ok(printers) => {
|
||||
d.printers = printers;
|
||||
d.printers_error = None;
|
||||
}
|
||||
Err(error) => {
|
||||
d.printers = Vec::new();
|
||||
d.printers_error = Some(error);
|
||||
}
|
||||
}
|
||||
d.default_printer = crate::io::plot_device::default_printer_name();
|
||||
d.plot_styles = crate::io::plot_style::available_ctb_names();
|
||||
d.scales = scales;
|
||||
@@ -4011,6 +4020,10 @@ impl OpenCADStudio {
|
||||
self.plot_dialog.scale_lw = false;
|
||||
}
|
||||
self.refresh_page_setups();
|
||||
if let Some(error) = self.plot_dialog.printers_error.clone() {
|
||||
self.command_line
|
||||
.push_warning(crate::tf!("Could not list printers: {error}").as_ref());
|
||||
}
|
||||
self.plot_prev = Some(previous);
|
||||
let cur = self.tabs[self.active_tab].scene.current_layout.clone();
|
||||
let layout_entry = format!("*{cur}*");
|
||||
@@ -4179,8 +4192,14 @@ impl OpenCADStudio {
|
||||
/// print job honours. Without a named printer, or where the options
|
||||
/// cannot be listed, the platform's printer settings open instead.
|
||||
fn on_printer_properties(&mut self) -> Task<Message> {
|
||||
let Some(printer) = self.plot_dialog.printer.clone().filter(|_| !self.plot_dialog.to_file)
|
||||
else {
|
||||
// "Default" is a real printer: its preferences sheet is the one to
|
||||
// open, not the system's printer list.
|
||||
let chosen = self
|
||||
.plot_dialog
|
||||
.printer
|
||||
.clone()
|
||||
.or_else(|| self.plot_dialog.default_printer.clone());
|
||||
let Some(printer) = chosen.filter(|_| !self.plot_dialog.to_file) else {
|
||||
self.open_printer_settings_fallback();
|
||||
return Task::none();
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user