fix(print): support default-paper injection, clear driver error on retry, and fix legacy settings restore

This commit is contained in:
Combiler01 committed 2026-11-04 14:18:52 +05:30
1 parent 290f775316
commit 04fa9910ca
8 files changed
+89 -14

No files matched your search

+1
View File
@@ -113,6 +113,7 @@ impl PdfCraftApp {
updates: updates::Updates::default(),
printer_source: None,
options_source: None,
default_paper_source: None,
print_tasks: print_ui::PrintTasks::default(),
palette_open: false,
palette_query: String::new(),
+2 -2
View File
@@ -293,11 +293,11 @@ impl PdfCraftApp {
});
if let Some(paper) = matched_pos {
self.print_draft.paper = paper;
self.print_draft.paper_overridden = v.get("print_paper_overridden").and_then(serde_json::Value::as_bool).unwrap_or(true);
self.print_draft.paper_overridden = v.get("print_paper_overridden").and_then(serde_json::Value::as_bool).unwrap_or(false);
} else if let Some(size) = saved_size {
self.print_draft.papers.push((name.to_string(), size));
self.print_draft.paper = self.print_draft.papers.len() - 1;
self.print_draft.paper_overridden = v.get("print_paper_overridden").and_then(serde_json::Value::as_bool).unwrap_or(true);
self.print_draft.paper_overridden = v.get("print_paper_overridden").and_then(serde_json::Value::as_bool).unwrap_or(false);
}
}
if let Some(on) = v["highlight_fields"].as_bool() {
+1 -1
View File
@@ -1325,7 +1325,7 @@ fn dialog_print(ui: &mut egui::Ui, app: &mut PdfCraftApp, req: &mut Pending) ->
let view = &mut app.views[i];
view.set_print_grayscale(app.print_draft.grayscale);
view.queue_print_previews(&rasters);
let (go, cancel) = crate::print_ui::body(ui, &mut app.print_draft, &t, &sizes, &labels, &mut |p| {
let (go, cancel) = crate::print_ui::body(ui, &mut app.print_draft, &t, &sizes, &labels, app.default_paper_source.as_ref(), &mut |p| {
view.need_thumbnail(p, true);
view.page_preview(p)
});
+3 -1
View File
@@ -58,7 +58,7 @@ mod editing;
mod home;
mod hotbar;
mod panels;
pub use print_ui::{Handling as PrintHandling, OptionsSource, PrintDraft, PrinterSource, Which as PrintWhich, system_printers};
pub use print_ui::{DefaultPaperSource, Handling as PrintHandling, OptionsSource, PrintDraft, PrinterSource, Which as PrintWhich, system_printers};
pub use redact_ui::{HiddenDraft, PagesDraft as RedactPagesDraft, RedactPrefs, SearchDraft as RedactSearchDraft, SearchMode as RedactSearchMode};
pub mod i18n;
pub mod theme;
@@ -341,6 +341,8 @@ pub struct PdfCraftApp {
pub printer_source: Option<PrinterSource>,
/// Where Properties… reads a printer's driver options: the spooler's unless a test stands in.
pub options_source: Option<OptionsSource>,
/// Where a printer's configured default paper comes from: DEVMODE / driver settings unless a test stands in.
pub default_paper_source: Option<DefaultPaperSource>,
// A browser has no background print work: the type is empty there.
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
pub(crate) print_tasks: print_ui::PrintTasks,
+10 -2
View File
@@ -33,6 +33,10 @@ pub type PrinterSource = std::sync::Arc<dyn Fn() -> Vec<spool::Printer> + Send +
/// UI language) unless a test stands in. Called off the GUI thread.
pub type OptionsSource = std::sync::Arc<dyn Fn(&str) -> Vec<spool::PrinterOption> + Send + Sync>;
/// Where a printer's configured default paper comes from: the spooler's DEVMODE / driver settings
/// unless a test stands in.
pub type DefaultPaperSource = std::sync::Arc<dyn Fn(&str) -> Option<(String, (f64, f64))> + Send + Sync>;
/// The system spooler's printers, as the desktop app's [`PrinterSource`].
pub fn system_printers() -> PrinterSource {
std::sync::Arc::new(spool::printers)
@@ -173,11 +177,15 @@ impl PdfCraftApp {
};
self.print_draft.update_printer_papers();
if !self.print_draft.paper_overridden {
self.print_draft.auto_select_default_paper();
self.auto_select_default_paper();
}
self.dialog = Some(crate::Dialog::Print);
}
pub(crate) fn auto_select_default_paper(&mut self) {
self.print_draft.auto_select_default_paper_with(self.default_paper_source.as_ref());
}
/// Label printing: find each printed page's printed area (a quick low-resolution render, as
/// Fit Visible does) and which way its text runs. Only the pages being printed are read, at
/// most [`LABEL_CROP_PAGES`] of them (the rest print whole); read again when they change.
@@ -319,7 +327,7 @@ impl PdfCraftApp {
d.printer = default_printer(&printers);
d.update_printer_papers();
if !d.paper_overridden {
d.auto_select_default_paper();
d.auto_select_default_paper_with(self.default_paper_source.as_ref());
}
}
d.printers = printers;
@@ -87,6 +87,7 @@ pub(crate) fn body(
t: &Tokens,
sizes: &[(f64, f64)],
labels: &[String],
default_paper_source: Option<&super::DefaultPaperSource>,
thumb: &mut dyn FnMut(usize) -> Option<egui::TextureId>,
) -> (bool, bool) {
ui.set_width(820.0);
@@ -141,6 +142,7 @@ pub(crate) fn body(
&& let Some(p) = d.printer.clone()
{
if spool::HAS_PRINTER_PREFERENCES {
d.driver_error = None;
match spool::show_printer_properties(&p) {
Ok(Some((name, size))) => {
d.apply_driver_paper(name, size);
@@ -166,7 +168,7 @@ pub(crate) fn body(
d.driver_error = None;
d.update_printer_papers();
if !d.paper_overridden {
d.auto_select_default_paper();
d.auto_select_default_paper_with(default_paper_source);
}
}
ui.end_row();
@@ -236,6 +238,7 @@ pub(crate) fn body(
if page_setup.clicked()
&& let Some(p) = d.printer.clone()
{
d.driver_error = None;
match spool::show_printer_properties(&p) {
Ok(Some((name, size))) => {
d.apply_driver_paper(name, size);
+15 -6
View File
@@ -212,12 +212,21 @@ impl PrintDraft {
/// Auto-select the printer's configured default paper (from DEVMODE / driver settings).
pub fn auto_select_default_paper(&mut self) {
if let Some(ref printer) = self.printer
&& let Some((name, size)) = spool::printer_default_paper(printer)
{
let overridden = self.paper_overridden;
self.apply_driver_paper(name, size);
self.paper_overridden = overridden;
self.auto_select_default_paper_with(None);
}
/// Auto-select the printer's default paper using an optional source (or DEVMODE / driver settings).
pub fn auto_select_default_paper_with(&mut self, source: Option<&super::DefaultPaperSource>) {
if self.handling == Handling::Label {
return;
}
if let Some(ref printer) = self.printer {
let detected = if let Some(source) = source { source(printer) } else { spool::printer_default_paper(printer) };
if let Some((name, size)) = detected {
let overridden = self.paper_overridden;
self.apply_driver_paper(name, size);
self.paper_overridden = overridden;
}
}
}
+53 -1
View File
@@ -805,6 +805,19 @@ fn paper_choice_survives_cancel_and_settings_restore() {
let mut restored_default = PdfCraftApp::new();
restored_default.restore(&default_saved);
assert!(!restored_default.print_draft.paper_overridden);
// Opening print with a printer default paper selects that paper when paper_overridden is false.
let mut default_h = harness();
use pdfcraft_engine::print::spool;
use std::sync::Arc;
default_h.state_mut().printer_source =
Some(Arc::new(|| vec![spool::Printer { name: "Office Printer".into(), default: true, two_sided: None, description: String::new() }]));
default_h.state_mut().default_paper_source =
Some(Arc::new(|printer: &str| if printer == "Office Printer" { Some(("A4".into(), (595.28, 841.89))) } else { None }));
assert!(default_h.state_mut().execute("print.dialog"));
default_h.run_steps(3);
assert_eq!(default_h.state().print_draft.paper, a4);
assert!(!default_h.state().print_draft.paper_overridden);
let mut restarted = harness_with_settings(Some(saved));
assert!(restarted.state_mut().execute("print.dialog"));
restarted.run_steps(3);
@@ -832,6 +845,7 @@ fn paper_restore_accepts_canonical_names_and_ignores_invalid_settings() {
let mut app = PdfCraftApp::new();
app.restore(&serde_json::json!({"print_paper": name}).to_string());
assert_eq!(app.print_draft.paper, index);
assert!(!app.print_draft.paper_overridden);
assert_eq!(serde_json::from_str::<serde_json::Value>(&app.persist()).unwrap()["print_paper"], *name);
}
for invalid in [
@@ -863,11 +877,49 @@ fn paper_restore_accepts_canonical_names_and_ignores_invalid_settings() {
assert_eq!(serde_json::from_str::<serde_json::Value>(&app.persist()).unwrap()["print_paper"], serde_json::Value::Null);
}
#[test]
fn legacy_settings_restore_unsets_override_and_open_selects_printer_default() {
use pdfcraft_engine::print::{PAPERS, spool};
use std::sync::Arc;
let letter = PAPERS.iter().position(|(name, _)| *name == "US Letter").unwrap();
let a4 = PAPERS.iter().position(|(name, _)| *name == "A4").unwrap();
let mut h = harness();
let legacy = serde_json::json!({
"print_paper": "US Letter",
})
.to_string();
h.state_mut().restore(&legacy);
assert_eq!(h.state().print_draft.paper, letter);
assert!(!h.state().print_draft.paper_overridden, "legacy settings restore leaves paper_overridden as false");
h.state_mut().printer_source =
Some(Arc::new(|| vec![spool::Printer { name: "Office Printer".into(), default: true, two_sided: None, description: String::new() }]));
h.state_mut().default_paper_source =
Some(Arc::new(|printer: &str| if printer == "Office Printer" { Some(("A4".into(), (595.28, 841.89))) } else { None }));
assert!(h.state_mut().execute("print.dialog"));
h.run_steps(3);
assert_eq!(h.state().print_draft.paper, a4);
assert!(!h.state().print_draft.paper_overridden);
}
#[test]
fn auto_select_default_paper_leaves_label_stock_unchanged() {
let mut draft = pdfcraft_ui_egui::PrintDraft {
printer: Some("Office Printer".into()),
handling: pdfcraft_ui_egui::PrintHandling::Label,
paper: pdfcraft_engine::print::LABEL_PAPER,
..Default::default()
};
let source: pdfcraft_ui_egui::DefaultPaperSource = std::sync::Arc::new(move |_| Some(("A4".into(), (595.28, 841.89))));
draft.auto_select_default_paper_with(Some(&source));
assert_eq!(draft.paper, pdfcraft_engine::print::LABEL_PAPER);
}
#[test]
fn restored_paper_keeps_preview_and_saved_sheet_dimensions() {
use pdfcraft_engine::print::{self, PAPERS};
let a4 = PAPERS.iter().find(|(name, _)| *name == "A4").unwrap().1;
let mut h = harness_with_settings(Some(r#"{"print_paper":"A4"}"#.into()));
let mut h = harness_with_settings(Some(r#"{"print_paper":"A4","print_paper_overridden":true}"#.into()));
assert!(h.state_mut().execute("print.dialog"));
h.run_steps(3);
h.get_by_value("A4");