fix(print): apply cached papers immediately, refresh in background, and document functions

This commit is contained in:
Combiler01 committed 2026-11-07 11:20:24 +05:30
1 parent 25b6280ac6
commit f1e2c49d55
5 files changed
+69 -9

No files matched your search

+4
View File
@@ -191,6 +191,7 @@ fn spooled(job: &Job) -> Job {
}
}
/// Submit a PDF to the system print spooler for the configured `job`.
pub fn submit(pdf: &[u8], job: &Job) -> Result<String, crate::PrintError> {
craft_print::submit(pdf, &spooled(job)).map_err(|e| crate::PrintError::Spool(e.to_string()))
}
@@ -201,6 +202,7 @@ pub fn submit_with_progress(pdf: &[u8], job: &Job, progress: &mut dyn FnMut(usiz
craft_print::submit_with_progress(pdf, &spooled(job), progress).map_err(|e| crate::PrintError::Spool(e.to_string()))
}
/// Opens the printer driver's native Printing Preferences window (Windows `printui.dll`).
pub fn open_printer_preferences(printer: &str) -> Result<(), crate::PrintError> {
craft_print::open_printer_preferences(printer).map_err(|e| crate::PrintError::Spool(e.to_string()))
}
@@ -235,11 +237,13 @@ pub fn printer_papers(printer: &str) -> Vec<Paper> {
papers
}
/// The paper sizes supported by `printer` (fallback for non-Windows platforms).
#[cfg(not(windows))]
pub fn printer_papers(_printer: &str) -> Vec<Paper> {
Vec::new()
}
/// Match `paper_pts` against detected driver `papers` within a 1.0 pt tolerance.
#[cfg(windows)]
fn resolve_paper_name(papers: &[Paper], paper_pts: (f64, f64)) -> Paper {
let w_pts = (paper_pts.0 * 100.0).round() / 100.0;
+1
View File
@@ -36,6 +36,7 @@ fn rgb_triple(v: &serde_json::Value) -> Option<[f64; 3]> {
}
impl PdfCraftApp {
/// Sets the user's interface theme preference (Light, Dark, or System).
pub fn set_theme_preference(&mut self, preference: ThemePreference) {
self.theme_preference = preference;
if let Some(ctx) = self.ctx.clone() {
+22 -9
View File
@@ -151,6 +151,7 @@ impl Drop for WakeOnExit {
}
impl PdfCraftApp {
/// Open the Print dialog, preselecting the default printer and reading papers in the background.
pub fn open_print(&mut self) {
let Some((i, _)) = self.active_ids() else { return };
let (printers, printers_loading) = match self.printer_source.clone() {
@@ -241,6 +242,7 @@ impl PdfCraftApp {
self.print_draft.crop_pages = Some(pages);
}
/// Read the printer list in the background off the GUI thread; `poll_print` picks it up.
#[cfg(not(target_arch = "wasm32"))]
fn list_printers_in_background(&mut self, source: PrinterSource) {
// A list already being read is as fresh as a new one would be.
@@ -302,8 +304,13 @@ impl PdfCraftApp {
#[cfg(not(target_arch = "wasm32"))]
fn read_papers_in_background(&mut self, printer: String) {
if let Some((papers, default_paper)) = self.print_tasks.papers_cache.get(&printer).cloned() {
self.receive_papers(printer, papers, default_paper);
return;
let d = &mut self.print_draft;
if d.printer.as_deref() == Some(printer.as_str()) {
d.apply_printer_papers(papers);
if !d.paper_overridden {
d.apply_default_paper(default_paper);
}
}
}
if self.print_tasks.papers.iter().any(|(p, _)| *p == printer) {
return;
@@ -347,14 +354,18 @@ impl PdfCraftApp {
/// The papers and default paper read for `printer` arrived, or the read died.
#[cfg(not(target_arch = "wasm32"))]
fn receive_papers(&mut self, printer: String, papers: Vec<spool::Paper>, default_paper: Option<spool::Paper>) {
self.print_tasks.papers_cache.insert(printer.clone(), (papers.clone(), default_paper.clone()));
if !papers.is_empty() || default_paper.is_some() {
self.print_tasks.papers_cache.insert(printer.clone(), (papers.clone(), default_paper.clone()));
}
let d = &mut self.print_draft;
if d.papers_loading.as_deref() == Some(printer.as_str()) {
d.papers_loading = None;
}
if d.printer.as_deref() == Some(printer.as_str()) {
d.apply_printer_papers(papers);
if !d.paper_overridden {
if !papers.is_empty() {
d.apply_printer_papers(papers);
}
if !d.paper_overridden && default_paper.is_some() {
d.apply_default_paper(default_paper);
}
}
@@ -414,13 +425,14 @@ impl PdfCraftApp {
self.print_tasks.printers = None;
let d = &mut self.print_draft;
// A picked printer that is no longer listed falls back to the default too.
let changed = if !d.printer_picked || !offered(d.printer.as_deref(), &printers) {
let new_printer = if !d.printer_picked || !offered(d.printer.as_deref(), &printers) {
d.printer_picked = false;
d.printer = default_printer(&printers);
true
default_printer(&printers)
} else {
false
d.printer.clone()
};
let changed = d.printer != new_printer;
d.printer = new_printer;
d.printers = printers;
d.printers_loading = false;
if changed && let Some(p) = d.printer.clone() {
@@ -486,6 +498,7 @@ impl PdfCraftApp {
}
}
/// Report the outcome of a finished print job to the user.
fn report_print_job(&mut self, printer: &str, result: Result<String, String>) {
match result {
Ok(msg) if msg.is_empty() => self.notify(crate::i18n::fmt(tl!("Sent to {printer}"), &[("printer", printer)])),
@@ -361,6 +361,7 @@ impl PrintDraft {
})
}
/// Whether multiple pages per sheet should be cut and stacked into sorted piles.
fn cut_and_stack(&self) -> bool {
self.handling == Handling::Multiple && self.order == PageOrder::CutStack
}
+41
View File
@@ -941,6 +941,47 @@ fn auto_select_default_paper_preserves_user_orientation() {
assert_eq!(draft.orientation, pdfcraft_engine::print::Orientation::Landscape);
}
#[test]
fn cached_papers_apply_immediately_and_refresh_in_background() {
use pdfcraft_engine::print::spool;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
let calls = Arc::new(AtomicUsize::new(0));
let calls_clone = calls.clone();
let mut h = harness();
h.state_mut().printer_source =
Some(Arc::new(|| vec![spool::Printer { name: "Cached Printer".into(), default: true, two_sided: None, description: String::new() }]));
h.state_mut().printer_papers_source = Some(Arc::new(move |_| {
calls_clone.fetch_add(1, Ordering::SeqCst);
vec![("A4".into(), (595.28, 841.89))]
}));
h.state_mut().default_paper_source =
Some(Arc::new(|printer: &str| if printer == "Cached Printer" { Some(("A4".into(), (595.28, 841.89))) } else { None }));
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_loading || h.state().print_draft.papers_loading.is_some()) && std::time::Instant::now() < deadline {
h.run_steps(1);
std::thread::sleep(std::time::Duration::from_millis(5));
}
assert_eq!(calls.load(Ordering::SeqCst), 1);
let a4 = h.state().print_draft.paper;
h.state_mut().dialog = None;
assert!(h.state_mut().execute("print.dialog"));
assert_eq!(h.state().print_draft.paper, a4);
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
while h.state().print_draft.papers_loading.is_some() && std::time::Instant::now() < deadline {
h.run_steps(1);
std::thread::sleep(std::time::Duration::from_millis(5));
}
assert_eq!(calls.load(Ordering::SeqCst), 2);
}
#[test]
fn restored_paper_keeps_preview_and_saved_sheet_dimensions() {
use pdfcraft_engine::print::{self, PAPERS};