mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-10 21:11:52 +00:00
fix(print): read papers in background, restore properties handler, and pass dialog owner
This commit is contained in:
1 parent
04fa9910ca
commit
25b6280ac6
9 files changed
+247
-87
No files matched your search
@@ -43,4 +43,4 @@ pub use settings::{
|
||||
#[cfg(test)]
|
||||
use sheets::text_direction;
|
||||
pub use sheets::{LABEL_MARGIN, MARGIN, Placement, Sheet, layout, matching_paper, text_reads_up};
|
||||
pub use spool::{open_printer_preferences, printer_default_paper, printer_papers, show_printer_properties};
|
||||
pub use spool::{open_printer_preferences, printer_default_paper, printer_paper_info, printer_papers, show_printer_properties};
|
||||
+29
-23
@@ -240,34 +240,47 @@ pub fn printer_papers(_printer: &str) -> Vec<Paper> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
/// The default paper size configured for `printer` in the printer properties / DEVMODE.
|
||||
/// Returns `Some((name, (width, height)))` if readable.
|
||||
#[cfg(windows)]
|
||||
pub fn printer_default_paper(printer: &str) -> Option<Paper> {
|
||||
let settings = craft_print::rgb::default_settings(printer).ok()?;
|
||||
let page = craft_print::rgb::page(printer, Some(&settings)).ok()?;
|
||||
let w_pts = (page.paper.0 * 100.0).round() / 100.0;
|
||||
let h_pts = (page.paper.1 * 100.0).round() / 100.0;
|
||||
let papers = printer_papers(printer);
|
||||
fn resolve_paper_name(papers: &[Paper], paper_pts: (f64, f64)) -> Paper {
|
||||
let w_pts = (paper_pts.0 * 100.0).round() / 100.0;
|
||||
let h_pts = (paper_pts.1 * 100.0).round() / 100.0;
|
||||
let name = papers
|
||||
.iter()
|
||||
.find(|(_, (w, h))| ((*w - w_pts).abs() <= 1.0 && (*h - h_pts).abs() <= 1.0) || ((*w - h_pts).abs() <= 1.0 && (*h - w_pts).abs() <= 1.0))
|
||||
.map(|(n, _)| n.clone())
|
||||
.unwrap_or_default();
|
||||
Some((name, (w_pts, h_pts)))
|
||||
(name, (w_pts, h_pts))
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub fn printer_default_paper(_printer: &str) -> Option<Paper> {
|
||||
None
|
||||
/// The default paper size configured for `printer` in the printer properties / DEVMODE.
|
||||
/// Returns `Some((name, (width, height)))` if readable.
|
||||
pub fn printer_default_paper(printer: &str) -> Option<Paper> {
|
||||
printer_paper_info(printer).1
|
||||
}
|
||||
|
||||
/// Reads the supported paper sizes and the default paper size configured for `printer`.
|
||||
pub fn printer_paper_info(printer: &str) -> (Vec<Paper>, Option<Paper>) {
|
||||
let papers = printer_papers(printer);
|
||||
#[cfg(windows)]
|
||||
let default_paper = {
|
||||
let settings = craft_print::rgb::default_settings(printer).ok();
|
||||
let page = settings.as_ref().and_then(|s| craft_print::rgb::page(printer, Some(s)).ok());
|
||||
page.map(|p| resolve_paper_name(&papers, p.paper))
|
||||
};
|
||||
#[cfg(not(windows))]
|
||||
let default_paper = {
|
||||
let _ = &papers;
|
||||
None
|
||||
};
|
||||
(papers, default_paper)
|
||||
}
|
||||
|
||||
/// Shows the printer driver's Print Settings / DocumentProperties dialog (Windows).
|
||||
/// If the user clicks OK, returns the updated paper size.
|
||||
pub fn show_printer_properties(printer: &str) -> Result<Option<Paper>, crate::PrintError> {
|
||||
pub fn show_printer_properties(printer: &str, parent: Option<isize>) -> Result<Option<Paper>, crate::PrintError> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let devmode = match craft_print::rgb::settings_dialog(printer, None, None) {
|
||||
let devmode = match craft_print::rgb::settings_dialog(printer, None, parent) {
|
||||
Ok(Some(dm)) => dm,
|
||||
Ok(None) => return Ok(None),
|
||||
Err(_) => {
|
||||
@@ -276,19 +289,12 @@ pub fn show_printer_properties(printer: &str) -> Result<Option<Paper>, crate::Pr
|
||||
}
|
||||
};
|
||||
let page = craft_print::rgb::page(printer, Some(&devmode)).map_err(|e| crate::PrintError::Spool(e.to_string()))?;
|
||||
let w_pts = (page.paper.0 * 100.0).round() / 100.0;
|
||||
let h_pts = (page.paper.1 * 100.0).round() / 100.0;
|
||||
let papers = printer_papers(printer);
|
||||
let name = papers
|
||||
.iter()
|
||||
.find(|(_, (w, h))| ((*w - w_pts).abs() <= 1.0 && (*h - h_pts).abs() <= 1.0) || ((*w - h_pts).abs() <= 1.0 && (*h - w_pts).abs() <= 1.0))
|
||||
.map(|(n, _)| n.clone())
|
||||
.unwrap_or_default();
|
||||
Ok(Some((name, (w_pts, h_pts))))
|
||||
Ok(Some(resolve_paper_name(&papers, page.paper)))
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let _ = printer;
|
||||
let _ = (printer, parent);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,7 @@ impl PdfCraftApp {
|
||||
printer_source: None,
|
||||
options_source: None,
|
||||
default_paper_source: None,
|
||||
printer_papers_source: None,
|
||||
print_tasks: print_ui::PrintTasks::default(),
|
||||
palette_open: false,
|
||||
palette_query: String::new(),
|
||||
|
||||
@@ -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, app.default_paper_source.as_ref(), &mut |p| {
|
||||
let (go, cancel) = crate::print_ui::body(ui, &mut app.print_draft, &t, &sizes, &labels, &mut |p| {
|
||||
view.need_thumbnail(p, true);
|
||||
view.page_preview(p)
|
||||
});
|
||||
|
||||
@@ -58,7 +58,10 @@ mod editing;
|
||||
mod home;
|
||||
mod hotbar;
|
||||
mod panels;
|
||||
pub use print_ui::{DefaultPaperSource, Handling as PrintHandling, OptionsSource, PrintDraft, PrinterSource, Which as PrintWhich, system_printers};
|
||||
pub use print_ui::{
|
||||
DefaultPaperSource, Handling as PrintHandling, OptionsSource, PrintDraft, PrinterPapersSource, 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;
|
||||
@@ -343,6 +346,8 @@ pub struct PdfCraftApp {
|
||||
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>,
|
||||
/// Where a printer's supported paper sizes come from: driver capabilities unless a test stands in.
|
||||
pub printer_papers_source: Option<PrinterPapersSource>,
|
||||
// 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,
|
||||
|
||||
@@ -37,11 +37,20 @@ pub type OptionsSource = std::sync::Arc<dyn Fn(&str) -> Vec<spool::PrinterOption
|
||||
/// unless a test stands in.
|
||||
pub type DefaultPaperSource = std::sync::Arc<dyn Fn(&str) -> Option<(String, (f64, f64))> + Send + Sync>;
|
||||
|
||||
/// Where a printer's supported paper sizes come from: the spooler driver capabilities
|
||||
/// unless a test stands in.
|
||||
pub type PrinterPapersSource = std::sync::Arc<dyn Fn(&str) -> Vec<spool::Paper> + Send + Sync>;
|
||||
|
||||
/// The system spooler's printers, as the desktop app's [`PrinterSource`].
|
||||
pub fn system_printers() -> PrinterSource {
|
||||
std::sync::Arc::new(spool::printers)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
type PaperInfo = (Vec<spool::Paper>, Option<spool::Paper>);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
type PendingPaperInfo = (String, std::sync::mpsc::Receiver<PaperInfo>);
|
||||
|
||||
/// The Print dialog's work in the background: the printer list being read, and the jobs being
|
||||
/// spooled, oldest first.
|
||||
#[derive(Default)]
|
||||
@@ -56,6 +65,12 @@ pub(crate) struct PrintTasks {
|
||||
/// The driver options being read, per printer (`read_options_in_background`).
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
options: Vec<(String, std::sync::mpsc::Receiver<Vec<spool::PrinterOption>>)>,
|
||||
/// The papers and default paper being read in the background, per printer (`read_papers_in_background`).
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
papers: Vec<PendingPaperInfo>,
|
||||
/// Cached papers and default paper per printer so switching printers avoids re-querying.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
papers_cache: std::collections::HashMap<String, PaperInfo>,
|
||||
/// The browser print being drawn, a sheet a frame (`web_print`).
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
web_job: Option<crate::web_print::WebPrintJob>,
|
||||
@@ -168,24 +183,20 @@ impl PdfCraftApp {
|
||||
printers_loading,
|
||||
printer_picked: picked,
|
||||
units: self.units,
|
||||
printer,
|
||||
printer: printer.clone(),
|
||||
current_page: current,
|
||||
sheet: 0,
|
||||
selected,
|
||||
which,
|
||||
..keep
|
||||
};
|
||||
self.print_draft.update_printer_papers();
|
||||
if !self.print_draft.paper_overridden {
|
||||
self.auto_select_default_paper();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if let Some(p) = printer {
|
||||
self.read_papers_in_background(p);
|
||||
}
|
||||
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.
|
||||
@@ -286,12 +297,93 @@ impl PdfCraftApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read `printer`'s papers and default paper off the GUI thread (#5861); `poll_print` picks them up.
|
||||
/// A read already under way for the same printer is left to finish.
|
||||
#[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;
|
||||
}
|
||||
if self.print_tasks.papers.iter().any(|(p, _)| *p == printer) {
|
||||
return;
|
||||
}
|
||||
self.print_draft.papers_loading = Some(printer.clone());
|
||||
let papers_source = self.printer_papers_source.clone();
|
||||
let default_source = self.default_paper_source.clone();
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
let wake = WakeOnExit(self.ctx.clone());
|
||||
let name = printer.clone();
|
||||
let spawned = std::thread::Builder::new().name("printer papers".into()).spawn(move || {
|
||||
let _wake = wake;
|
||||
let tx = tx;
|
||||
let info = match (&papers_source, &default_source) {
|
||||
(None, None) => spool::printer_paper_info(&name),
|
||||
(papers_s, def_s) => {
|
||||
let papers = match papers_s {
|
||||
Some(s) => s(&name),
|
||||
None => spool::printer_papers(&name),
|
||||
};
|
||||
let def = match def_s {
|
||||
Some(s) => s(&name),
|
||||
None => spool::printer_default_paper(&name),
|
||||
};
|
||||
(papers, def)
|
||||
}
|
||||
};
|
||||
let _ = tx.send(info);
|
||||
});
|
||||
match spawned {
|
||||
Ok(_) => self.print_tasks.papers.push((printer, rx)),
|
||||
Err(_) => {
|
||||
let d = &mut self.print_draft;
|
||||
if d.papers_loading.as_deref() == Some(printer.as_str()) {
|
||||
d.papers_loading = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()));
|
||||
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 {
|
||||
d.apply_default_paper(default_paper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pick up a printer list, driver options or a spooled job that finished (each frame).
|
||||
pub(crate) fn poll_print(&mut self) {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
self.poll_web_print();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
if let Some(printer) = self.print_draft.read_papers_for.take() {
|
||||
self.read_papers_in_background(printer);
|
||||
}
|
||||
let mut arrived_papers = Vec::new();
|
||||
self.print_tasks.papers.retain(|(printer, rx)| match rx.try_recv() {
|
||||
Ok((papers, def)) => {
|
||||
arrived_papers.push((printer.clone(), papers, def));
|
||||
false
|
||||
}
|
||||
Err(std::sync::mpsc::TryRecvError::Empty) => true,
|
||||
Err(std::sync::mpsc::TryRecvError::Disconnected) => {
|
||||
arrived_papers.push((printer.clone(), Vec::new(), None));
|
||||
false
|
||||
}
|
||||
});
|
||||
for (printer, papers, def) in arrived_papers {
|
||||
self.receive_papers(printer, papers, def);
|
||||
}
|
||||
if let Some(printer) = self.print_draft.read_options_for.take() {
|
||||
self.read_options_in_background(printer);
|
||||
}
|
||||
@@ -322,16 +414,18 @@ 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.
|
||||
if !d.printer_picked || !offered(d.printer.as_deref(), &printers) {
|
||||
let changed = if !d.printer_picked || !offered(d.printer.as_deref(), &printers) {
|
||||
d.printer_picked = false;
|
||||
d.printer = default_printer(&printers);
|
||||
d.update_printer_papers();
|
||||
if !d.paper_overridden {
|
||||
d.auto_select_default_paper_with(self.default_paper_source.as_ref());
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
d.printers = printers;
|
||||
d.printers_loading = false;
|
||||
if changed && let Some(p) = d.printer.clone() {
|
||||
self.read_papers_in_background(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut finished = Vec::new();
|
||||
|
||||
@@ -87,7 +87,6 @@ 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);
|
||||
@@ -142,14 +141,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);
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(e) => d.driver_error = Some(e.to_string()),
|
||||
}
|
||||
d.driver_error = spool::open_printer_preferences(&p).err().map(|e| e.to_string());
|
||||
} else {
|
||||
if d.driver_options.as_ref().is_none_or(|(read_for, _)| *read_for != p) && d.driver_options_loading.as_deref() != Some(p.as_str()) {
|
||||
// Read off the GUI thread (#1818): `poll_print` starts it next frame.
|
||||
@@ -166,9 +158,8 @@ pub(crate) fn body(
|
||||
d.printer_picked = true;
|
||||
d.show_driver_options = false;
|
||||
d.driver_error = None;
|
||||
d.update_printer_papers();
|
||||
if !d.paper_overridden {
|
||||
d.auto_select_default_paper_with(default_paper_source);
|
||||
if let Some(p) = d.printer.clone() {
|
||||
d.read_papers_for = Some(p);
|
||||
}
|
||||
}
|
||||
ui.end_row();
|
||||
@@ -239,7 +230,11 @@ pub(crate) fn body(
|
||||
&& let Some(p) = d.printer.clone()
|
||||
{
|
||||
d.driver_error = None;
|
||||
match spool::show_printer_properties(&p) {
|
||||
#[cfg(windows)]
|
||||
let parent = winsafe::HWND::GetActiveWindow().map(|h| h.ptr() as isize);
|
||||
#[cfg(not(windows))]
|
||||
let parent = None;
|
||||
match spool::show_printer_properties(&p, parent) {
|
||||
Ok(Some((name, size))) => {
|
||||
d.apply_driver_paper(name, size);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,10 @@ pub struct PrintDraft {
|
||||
pub driver_options_loading: Option<String>,
|
||||
/// Properties… asked for this printer's options; the app starts the read on the next frame.
|
||||
pub read_options_for: Option<String>,
|
||||
/// The printer whose papers and default paper are being read in the background.
|
||||
pub papers_loading: Option<String>,
|
||||
/// A printer change requested reading papers; the app starts the read on the next frame.
|
||||
pub read_papers_for: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for PrintDraft {
|
||||
@@ -137,6 +141,8 @@ impl Default for PrintDraft {
|
||||
crop_pages: None,
|
||||
driver_options_loading: None,
|
||||
read_options_for: None,
|
||||
papers_loading: None,
|
||||
read_papers_for: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,21 +174,33 @@ impl PrintDraft {
|
||||
|
||||
/// Refresh `self.papers` with driver-reported media sizes, preserving the current selection.
|
||||
pub fn update_printer_papers(&mut self) {
|
||||
self.update_printer_papers_with(None);
|
||||
}
|
||||
|
||||
/// Refresh `self.papers` with driver-reported media sizes from an optional source or the driver.
|
||||
pub fn update_printer_papers_with(&mut self, source: Option<&super::PrinterPapersSource>) {
|
||||
let detected = if let Some(ref printer) = self.printer {
|
||||
if let Some(source) = source { source(printer) } else { spool::printer_papers(printer) }
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
self.apply_printer_papers(detected);
|
||||
}
|
||||
|
||||
/// Apply driver-reported media sizes to `self.papers`, preserving the current selection.
|
||||
pub fn apply_printer_papers(&mut self, detected: Vec<(String, (f64, f64))>) {
|
||||
let before_label = self.paper_before_label.and_then(|idx| self.papers.get(idx).cloned());
|
||||
let mut papers = default_papers();
|
||||
if let Some(ref printer) = self.printer {
|
||||
let detected = spool::printer_papers(printer);
|
||||
for (name, size) in detected {
|
||||
if !(size.0 >= 72.0 && size.1 >= 72.0 && size.0.is_finite() && size.1.is_finite()) {
|
||||
continue;
|
||||
}
|
||||
let norm = (size.0.min(size.1), size.0.max(size.1));
|
||||
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));
|
||||
}
|
||||
for (name, size) in detected {
|
||||
if !(size.0 >= 72.0 && size.1 >= 72.0 && size.0.is_finite() && size.1.is_finite()) {
|
||||
continue;
|
||||
}
|
||||
let norm = (size.0.min(size.1), size.0.max(size.1));
|
||||
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));
|
||||
}
|
||||
}
|
||||
let current_name = self.paper_name().to_string();
|
||||
@@ -217,27 +235,34 @@ impl PrintDraft {
|
||||
|
||||
/// 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;
|
||||
self.apply_default_paper(detected);
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply the detected default paper.
|
||||
pub fn apply_default_paper(&mut self, detected: Option<(String, (f64, f64))>) {
|
||||
if let Some((name, size)) = detected
|
||||
&& let Some(pos) = self.find_or_add_paper(name, size)
|
||||
{
|
||||
if self.handling == Handling::Label {
|
||||
if !self.paper_overridden {
|
||||
self.paper_before_label = Some(pos);
|
||||
}
|
||||
} else {
|
||||
self.paper = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply paper size and orientation chosen in printer properties or page setup.
|
||||
pub fn apply_driver_paper(&mut self, name: String, size: (f64, f64)) {
|
||||
/// Locate `(name, size)` in `self.papers`, or append it if missing. Returns the paper index.
|
||||
pub fn find_or_add_paper(&mut self, name: String, size: (f64, f64)) -> Option<usize> {
|
||||
if !(size.0 >= 72.0 && size.1 >= 72.0 && size.0.is_finite() && size.1.is_finite()) {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
self.paper_overridden = true;
|
||||
if let Some(pos) = self.matching_paper(size) {
|
||||
self.paper = pos;
|
||||
Some(pos)
|
||||
} else if !name.is_empty() {
|
||||
let norm = (size.0.min(size.1), size.0.max(size.1));
|
||||
if let Some(pos) = self.papers.iter().position(|(n, s)| {
|
||||
@@ -246,20 +271,28 @@ impl PrintDraft {
|
||||
(s_norm.0 - norm.0).abs() <= 1.0 && (s_norm.1 - norm.1).abs() <= 1.0
|
||||
}
|
||||
}) {
|
||||
self.paper = pos;
|
||||
Some(pos)
|
||||
} else {
|
||||
self.papers.push((name, size));
|
||||
self.paper = self.papers.len() - 1;
|
||||
Some(self.papers.len() - 1)
|
||||
}
|
||||
} 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;
|
||||
Some(self.papers.len() - 1)
|
||||
}
|
||||
if size.0 > size.1 {
|
||||
self.orientation = Orientation::Landscape;
|
||||
} else if size.1 > size.0 {
|
||||
self.orientation = Orientation::Portrait;
|
||||
}
|
||||
|
||||
/// 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 let Some(pos) = self.find_or_add_paper(name, size) {
|
||||
self.paper_overridden = true;
|
||||
self.paper = pos;
|
||||
if size.0 > size.1 {
|
||||
self.orientation = Orientation::Landscape;
|
||||
} else if size.1 > size.0 {
|
||||
self.orientation = Orientation::Portrait;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -812,10 +812,17 @@ fn paper_choice_survives_cancel_and_settings_restore() {
|
||||
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().printer_papers_source = Some(Arc::new(|_| vec![("A4".into(), (595.28, 841.89))]));
|
||||
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);
|
||||
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
|
||||
while (default_h.state().print_draft.printers_loading || default_h.state().print_draft.papers_loading.is_some())
|
||||
&& std::time::Instant::now() < deadline
|
||||
{
|
||||
default_h.run_steps(1);
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
}
|
||||
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));
|
||||
@@ -894,10 +901,15 @@ fn legacy_settings_restore_unsets_override_and_open_selects_printer_default() {
|
||||
|
||||
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().printer_papers_source = Some(Arc::new(|_| vec![("A4".into(), (595.28, 841.89))]));
|
||||
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);
|
||||
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!(h.state().print_draft.paper, a4);
|
||||
assert!(!h.state().print_draft.paper_overridden);
|
||||
}
|
||||
@@ -910,9 +922,23 @@ fn auto_select_default_paper_leaves_label_stock_unchanged() {
|
||||
paper: pdfcraft_engine::print::LABEL_PAPER,
|
||||
..Default::default()
|
||||
};
|
||||
let a4 = pdfcraft_engine::print::PAPERS.iter().position(|(name, _)| *name == "A4").unwrap();
|
||||
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);
|
||||
assert_eq!(draft.paper_before_label, Some(a4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_select_default_paper_preserves_user_orientation() {
|
||||
let mut draft = pdfcraft_ui_egui::PrintDraft {
|
||||
printer: Some("Office Printer".into()),
|
||||
orientation: pdfcraft_engine::print::Orientation::Landscape,
|
||||
..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.orientation, pdfcraft_engine::print::Orientation::Landscape);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user