From 38f0e2b25f5a5f2c33a6fdb31ba40bf9b8b10c38 Mon Sep 17 00:00:00 2001 From: Afonsop Date: Tue, 15 Sep 2026 17:51:29 +0100 Subject: [PATCH] app: wire web export, preview, and print flows through the plot UI - plot dialog: the web build defaults to 'Save to PDF file...' (its only output choice); native dropdowns and defaults are untouched. - on_plot_dlg_commit forces to_file on wasm so a stale page setup can no longer route the web build into the (unreachable) printer path; both preview branches now go through preview_page_pdf, which is identical to the old inline temp-PDF + OS-viewer flow on desktop. - Print ribbon / PRINTALL on wasm render the PDF and open it in a tab instead of erroring; PrintAllPdf picks a virtual path and downloads. - pdf_plot_options maps the dialog's Quality dropdown into PdfPlotOptions. (cherry picked from commit 892d693d2de4311b9ab2014fb30b41215401e94b) --- src/app/mod.rs | 2 + src/app/update/file.rs | 103 +++++++++++++++++++++++++---------------- src/app/update/mod.rs | 9 ++-- src/ui/window/plot.rs | 58 +++++++++++++++++++---- 4 files changed, 117 insertions(+), 55 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 4462dd48a..5f01e223d 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -3712,6 +3712,8 @@ pub enum Message { /// Send current layout to the system printer (via lp / lpr). PrintToPrinter, /// Callback from the async printer job. + /// Web never constructs it — printing opens the PDF preview instead. + #[cfg_attr(target_arch = "wasm32", allow(dead_code))] PrintResult(Result), /// Open the full Plot / Print dialog (seeds state from the layout). PlotDialogOpen, diff --git a/src/app/update/file.rs b/src/app/update/file.rs index bfb5d8ead..8c7f591f1 100644 --- a/src/app/update/file.rs +++ b/src/app/update/file.rs @@ -4160,34 +4160,38 @@ impl OpenCADStudio { } pub(super) fn on_print_all_print(&mut self) -> Task { + let dialog = self.plot_dialog.clone(); + if self.print_all_settings_override && dialog.style_missing && dialog.apply_plot_styles { + self.command_line.push_error( + crate::tf!("Plot style table '{}' is not loaded.", dialog.style_name).as_ref(), + ); + return Task::none(); + } + let pages = match self.print_all_pages() { + Ok(pages) => pages, + Err(error) => { + self.command_line.push_error(&error); + return Task::none(); + } + }; + self.save_config(); + self.close_active_modal(); + // The web build has no system printers: render every layout into one + // PDF and open it in a browser tab for the browser print dialog. #[cfg(target_arch = "wasm32")] { - self.command_line - .push_error(crate::t!("Printing is not available in the web version.").as_ref()); - Task::none() + let work = move || { + crate::io::print_to_printer::preview_pages_pdf(&pages) + .map(|_| crate::t!("Opened plot preview.").into_owned()) + .map_err(|e| crate::tf!("Preview failed: {e}").into_owned()) + }; + self.run_print_all_work(true, work) } #[cfg(not(target_arch = "wasm32"))] { - let dialog = self.plot_dialog.clone(); - if self.print_all_settings_override && dialog.style_missing && dialog.apply_plot_styles - { - self.command_line.push_error( - crate::tf!("Plot style table '{}' is not loaded.", dialog.style_name).as_ref(), - ); - return Task::none(); - } - let pages = match self.print_all_pages() { - Ok(pages) => pages, - Err(error) => { - self.command_line.push_error(&error); - return Task::none(); - } - }; let options = self.plot_print_options(&dialog); let fallback_style = self.dialog_plot_style(&dialog); let temp_path = crate::io::print_to_printer::temp_pdf_path("print_all"); - self.save_config(); - self.close_active_modal(); self.command_line .push_info(crate::t!("Sending selected layouts to the system printer…").as_ref()); let work = move || { @@ -4429,18 +4433,35 @@ impl OpenCADStudio { ); return Task::none(); }; - let options = self.plot_print_options(&self.plot_dialog); - self.command_line - .push_info(crate::t!("Sending to system printer…").as_ref()); - background_task( - crate::t!("Print to printer"), - move || { - iced::futures::executor::block_on(crate::io::print_to_printer::print_wires_with( - page, options, - )) - }, - Message::PrintResult, - ) + // The web build has no system printers: PRINT opens the rendered PDF + // in a browser tab, where the browser's own print dialog takes over. + #[cfg(target_arch = "wasm32")] + { + background_task( + crate::t!("Plot preview"), + move || { + crate::io::print_to_printer::preview_page_pdf(&page) + .map(|_| crate::t!("Opened plot preview.").into_owned()) + .map_err(|e| crate::tf!("Preview failed: {e}").into_owned()) + }, + |result| Message::BackgroundIoFinished(result, false), + ) + } + #[cfg(not(target_arch = "wasm32"))] + { + let options = self.plot_print_options(&self.plot_dialog); + self.command_line + .push_info(crate::t!("Sending to system printer…").as_ref()); + background_task( + crate::t!("Print to printer"), + move || { + iced::futures::executor::block_on(crate::io::print_to_printer::print_wires_with( + page, options, + )) + }, + Message::PrintResult, + ) + } } /// QUICKPRINT / QP — use the current selection's bounding box as the plot @@ -5743,6 +5764,12 @@ impl OpenCADStudio { /// Open a preview PDF, export a PDF, or send the job to the chosen printer. fn on_plot_dlg_commit(&mut self, preview: bool) -> Task { let d = self.plot_dialog.clone(); + // The web dialog only offers PDF output, but a page setup loaded from + // the drawing can still seed `to_file = false` — always export there. + #[cfg(target_arch = "wasm32")] + let to_file = true; + #[cfg(not(target_arch = "wasm32"))] + let to_file = d.to_file; if d.style_missing && d.apply_plot_styles { self.command_line.push_error( crate::tf!("Plot style table '{}' is not loaded.", d.style_name).as_ref(), @@ -5788,16 +5815,14 @@ impl OpenCADStudio { // Preview renders to a temp PDF and opens it — never a save dialog, // whatever the output target is. if preview { - let tmp = crate::io::print_to_printer::temp_pdf_path("preview"); let work = move || { - crate::io::pdf_export::export_pdf(&page, &tmp) - .and_then(|_| crate::io::print_to_printer::open_in_viewer(&tmp)) + crate::io::print_to_printer::preview_page_pdf(&page) .map(|_| crate::t!("Opened plot preview.").into_owned()) .map_err(|e| crate::tf!("Preview failed: {e}").into_owned()) }; return self.run_plot_work(d.background, true, work); } - if d.to_file { + if to_file { // Tested clipped export (opens a save dialog). return Task::done(Message::PlotWindowExport); } @@ -5815,17 +5840,15 @@ impl OpenCADStudio { let page = self.layout_plot_page(); if preview { - let tmp = crate::io::print_to_printer::temp_pdf_path("preview"); let work = move || { - crate::io::pdf_export::export_pdf(&page, &tmp) - .and_then(|_| crate::io::print_to_printer::open_in_viewer(&tmp)) + crate::io::print_to_printer::preview_page_pdf(&page) .map(|_| crate::t!("Opened plot preview.").into_owned()) .map_err(|e| crate::tf!("Preview failed: {e}").into_owned()) }; return self.run_plot_work(d.background, true, work); } - if d.to_file { + if to_file { // Reuse the tested PDF export flow (it opens a save dialog). return Task::done(Message::PlotExport); } diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 18e1137c9..a178e9d5a 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -9936,11 +9936,10 @@ impl OpenCADStudio { } #[cfg(target_arch = "wasm32")] { - let _ = stem; - self.command_line.push_error( - crate::t!("PDF export is not available in the web version.").as_ref(), - ); - Task::none() + Task::perform( + crate::io::pdf_export::pick_pdf_path_owned(stem), + Message::PrintAllPdfPath, + ) } } Message::PrintAllPdfPath(None) => Task::none(), diff --git a/src/ui/window/plot.rs b/src/ui/window/plot.rs index ddd34c2de..004f5a710 100644 --- a/src/ui/window/plot.rs +++ b/src/ui/window/plot.rs @@ -3,6 +3,8 @@ //! quality and output options into one dialog; on commit it either sends the //! current layout to a system printer (with the chosen options) or writes a //! PDF. Styled to match the other OCS dialogs (dark pills + fields). +//! The web build has no system printers, so PDF is the only output it +//! offers — and its default. use crate::app::Message; use crate::io::paper_catalog::{self, CustomPaper, Margins, PaperSize, PaperUnits}; @@ -599,7 +601,8 @@ impl Default for PlotDialogState { import_draft: None, page_setup_on_new_layout: false, printer: None, - to_file: false, + // Web defaults to PDF output: it has no system printers. + to_file: cfg!(target_arch = "wasm32"), file_svg: false, paper: paper_catalog::default_paper().canonical.to_string(), paper_width_mm: 297.0, @@ -1144,6 +1147,25 @@ fn custom_paper_editor<'a>(draft: &'a CustomPaperDraft, width: Length) -> Elemen .into() } +/// Entries for the output dropdown. The web build has no system printers, so +/// PDF is the only output it offers; native lists the system default, every +/// discovered printer, then PDF. +fn output_choices(printers: &[String]) -> Vec { + #[cfg(target_arch = "wasm32")] + { + let _ = printers; + vec![PlotChoice::localized(OUT_PDF)] + } + #[cfg(not(target_arch = "wasm32"))] + { + let mut out = vec![PlotChoice::localized(OUT_DEFAULT)]; + out.extend(printers.iter().cloned().map(PlotChoice::raw)); + out.push(PlotChoice::localized(OUT_PDF)); + out.push(PlotChoice::localized(OUT_SVG)); + out + } +} + pub fn view_window( s: &PlotDialogState, print_all_options: bool, @@ -1151,10 +1173,17 @@ pub fn view_window( ) -> Element<'_, Message> { let width = sizing.width; let height = sizing.height; + // The web build always outputs PDF, whatever a stored dialog state says; + // native follows the stored choice. + #[cfg(target_arch = "wasm32")] + let to_file = true; + #[cfg(not(target_arch = "wasm32"))] + let to_file = s.to_file; let action = if print_all_options { t!("Apply") - } else if s.to_file { - if s.file_svg { + } else if to_file { + // The web build writes PDF whatever a stored dialog state says. + if s.file_svg && cfg!(not(target_arch = "wasm32")) { t!("Export SVG") } else { t!("Export PDF") @@ -1289,12 +1318,21 @@ pub fn view_window( }, None => PlotChoice::localized(OUT_DEFAULT), }; - let mut printer_opts = vec![default_entry.clone()]; - printer_opts.extend(s.printers.iter().cloned().map(PlotChoice::raw)); - printer_opts.push(PlotChoice::localized(OUT_PDF)); - printer_opts.push(PlotChoice::localized(OUT_SVG)); - let printer_sel = if s.to_file { - Some(PlotChoice::localized(if s.file_svg { OUT_SVG } else { OUT_PDF })) + let mut printer_opts = output_choices(&s.printers); + if let Some(first) = printer_opts + .first_mut() + .filter(|first| first.raw == OUT_DEFAULT) + { + *first = default_entry.clone(); + } + let printer_sel = if to_file { + Some(PlotChoice::localized( + if s.file_svg && cfg!(not(target_arch = "wasm32")) { + OUT_SVG + } else { + OUT_PDF + }, + )) } else { Some(match &s.printer { Some(printer) => PlotChoice::raw(printer.clone()), @@ -1373,7 +1411,7 @@ pub fn view_window( .spacing(8) .align_y(iced::Center) .width(Length::Fill); - let printer_controls: Element<'_, Message> = if s.to_file { + let printer_controls: Element<'_, Message> = if to_file { Space::new().height(0).into() } else { row![