diff --git a/crates/ui-egui/src/app/prefs.rs b/crates/ui-egui/src/app/prefs.rs index 94088a090e..489c216162 100644 --- a/crates/ui-egui/src/app/prefs.rs +++ b/crates/ui-egui/src/app/prefs.rs @@ -159,6 +159,8 @@ impl PdfCraftApp { .map(|(name, _)| name.as_str()) .or_else(|| pdfcraft_engine::print::PAPERS.get(self.print_draft.paper).map(|(name, _)| *name)) ); + let size = self.print_draft.paper_size(); + v["print_paper_size"] = serde_json::json!([size.0, size.1]); v.to_string() } @@ -277,6 +279,13 @@ impl PdfCraftApp { self.print_draft.paper = paper; } else if let Some(paper) = pdfcraft_engine::print::PAPERS.iter().position(|(canonical, _)| *canonical == name) { self.print_draft.paper = paper; + } else if let Some(size) = v.get("print_paper_size").and_then(|s| { + let w = s.get(0)?.as_f64()?; + let h = s.get(1)?.as_f64()?; + if w >= 72.0 && h >= 72.0 && w.is_finite() && h.is_finite() { Some((w, h)) } else { None } + }) { + self.print_draft.papers.push((name.to_string(), size)); + self.print_draft.paper = self.print_draft.papers.len() - 1; } } if let Some(on) = v["highlight_fields"].as_bool() { diff --git a/crates/ui-egui/src/workflows/print_ui.rs b/crates/ui-egui/src/workflows/print_ui.rs index 96e5fc39e1..262c84077a 100644 --- a/crates/ui-egui/src/workflows/print_ui.rs +++ b/crates/ui-egui/src/workflows/print_ui.rs @@ -172,7 +172,7 @@ impl PdfCraftApp { ..keep }; self.print_draft.update_printer_papers(); - if !picked && self.print_draft.paper == 0 { + if !picked && !self.print_draft.paper_overridden && self.print_draft.paper == 0 { self.print_draft.auto_select_default_paper(); } self.dialog = Some(crate::Dialog::Print); @@ -318,7 +318,7 @@ impl PdfCraftApp { d.printer_picked = false; d.printer = default_printer(&printers); d.update_printer_papers(); - if d.paper == 0 { + if !d.paper_overridden && d.paper == 0 { d.auto_select_default_paper(); } } diff --git a/crates/ui-egui/src/workflows/print_ui/body.rs b/crates/ui-egui/src/workflows/print_ui/body.rs index 2a140e79d5..edff10b9b6 100644 --- a/crates/ui-egui/src/workflows/print_ui/body.rs +++ b/crates/ui-egui/src/workflows/print_ui/body.rs @@ -165,7 +165,9 @@ pub(crate) fn body( d.show_driver_options = false; d.driver_error = None; d.update_printer_papers(); - d.auto_select_default_paper(); + if !d.paper_overridden { + d.auto_select_default_paper(); + } } ui.end_row(); // The spooler answered with no printer at all (#160): say so, rather than @@ -223,9 +225,11 @@ pub(crate) fn body( let page_setup_tip = if spool::HAS_PRINTER_PREFERENCES { tl!("Opens the printer driver's page setup: paper size, orientation and source.") } else { - tl!("The printer driver's page setup: paper size, orientation and source.") + tl!("Page setup dialog is not available on this system.") }; - let page_setup = ui.add_enabled(d.printer.is_some() && !browser, egui::Button::new(tl!("Page Setup…"))).on_hover_text(page_setup_tip); + let page_setup = ui + .add_enabled(d.printer.is_some() && !browser && spool::HAS_PRINTER_PREFERENCES, egui::Button::new(tl!("Page Setup…"))) + .on_hover_text(page_setup_tip); if page_setup.clicked() && let Some(p) = d.printer.clone() { @@ -238,6 +242,9 @@ pub(crate) fn body( } } }); + if d.paper != before { + d.paper_overridden = true; + } // Label stock gets the label layout (cut, turned upright, fitted). if d.paper != before && d.paper == print::LABEL_PAPER { d.paper_before_label = Some(before); diff --git a/crates/ui-egui/src/workflows/print_ui/draft.rs b/crates/ui-egui/src/workflows/print_ui/draft.rs index 6a86e0aae1..47d388122c 100644 --- a/crates/ui-egui/src/workflows/print_ui/draft.rs +++ b/crates/ui-egui/src/workflows/print_ui/draft.rs @@ -61,6 +61,8 @@ pub struct PrintDraft { pub paper: usize, /// The paper chosen before Label put label stock in, so leaving Label gives it back (#3564). pub paper_before_label: Option, + /// Whether the user has explicitly chosen or overridden the paper size. + pub paper_overridden: bool, /// The list of papers available for selection (standard PAPERS plus any detected from the driver). pub papers: Vec<(String, (f64, f64))>, /// The sheet shown in the preview (0-based). @@ -122,6 +124,7 @@ impl Default for PrintDraft { dpi: 300.0, paper: 0, paper_before_label: None, + paper_overridden: false, papers: default_papers(), sheet: 0, current_page: 0, @@ -144,14 +147,17 @@ pub fn default_papers() -> Vec<(String, (f64, f64))> { } impl PrintDraft { + /// The display name of the currently selected paper. pub fn paper_name(&self) -> &str { self.papers.get(self.paper).map(|p| p.0.as_str()).or_else(|| PAPERS.get(self.paper).map(|p| p.0)).unwrap_or(PAPERS[0].0) } + /// The dimensions in points `(width, height)` of the currently selected paper. pub fn paper_size(&self) -> (f64, f64) { self.papers.get(self.paper).map(|p| p.1).or_else(|| PAPERS.get(self.paper).map(|p| p.1)).unwrap_or(PAPERS[0].1) } + /// Find the index of a paper in `self.papers` matching `size`, allowing for orientation. pub fn matching_paper(&self, size: (f64, f64)) -> Option { print::matching_paper(size).or_else(|| { self.papers.iter().position(|(_, p)| { @@ -160,17 +166,16 @@ impl PrintDraft { }) } + /// Refresh `self.papers` with driver-reported media sizes, preserving the current selection. pub fn update_printer_papers(&mut self) { let mut papers = default_papers(); if let Some(ref printer) = self.printer { let detected = spool::printer_papers(printer); for (name, size) in detected { let norm = (size.0.min(size.1), size.0.max(size.1)); - if !papers.iter().any(|(n, s)| { - n.eq_ignore_ascii_case(&name) || { - let s_norm = (s.0.min(s.1), s.0.max(s.1)); - (s_norm.0 - norm.0).abs() <= 0.5 && (s_norm.1 - norm.1).abs() <= 0.5 - } + if !papers.iter().any(|(_, s)| { + let s_norm = (s.0.min(s.1), s.0.max(s.1)); + (s_norm.0 - norm.0).abs() <= 0.5 && (s_norm.1 - norm.1).abs() <= 0.5 }) { papers.push((name, size)); } @@ -184,7 +189,8 @@ impl PrintDraft { } else if let Some(pos) = self.papers.iter().position(|(_, s)| (s.0 - current_size.0).abs() <= 0.5 && (s.1 - current_size.1).abs() <= 0.5) { self.paper = pos; } else { - self.paper = self.paper.min(self.papers.len().saturating_sub(1)); + self.papers.push((current_name, current_size)); + self.paper = self.papers.len() - 1; } } @@ -199,16 +205,26 @@ impl PrintDraft { /// Apply paper size and orientation chosen in printer properties or page setup. pub fn apply_driver_paper(&mut self, name: String, size: (f64, f64)) { + if !(size.0 >= 72.0 && size.1 >= 72.0 && size.0.is_finite() && size.1.is_finite()) { + return; + } + self.paper_overridden = true; if let Some(pos) = self.matching_paper(size) { self.paper = pos; } else if !name.is_empty() { - if let Some(pos) = self.papers.iter().position(|(n, _)| n.eq_ignore_ascii_case(&name)) { + let norm = (size.0.min(size.1), size.0.max(size.1)); + if let Some(pos) = self.papers.iter().position(|(n, s)| { + n.eq_ignore_ascii_case(&name) && { + let s_norm = (s.0.min(s.1), s.0.max(s.1)); + (s_norm.0 - norm.0).abs() <= 1.0 && (s_norm.1 - norm.1).abs() <= 1.0 + } + }) { self.paper = pos; } else { self.papers.push((name, size)); self.paper = self.papers.len() - 1; } - } else if size.0 > 0.0 && size.1 > 0.0 { + } else { let custom_name = format!("{:.1} x {:.1} pt", size.0, size.1); self.papers.push((custom_name, size)); self.paper = self.papers.len() - 1;