mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-10 21:11:52 +00:00
fix(print): support default-paper injection, clear driver error on retry, and fix legacy settings restore
This commit is contained in:
1 parent
290f775316
commit
04fa9910ca
8 files changed
+89
-14
No files matched your search
@@ -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(),
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in new issue
Block a user