added SVG export feature

This commit is contained in:
francesco fantoni committed 2026-10-10 14:41:31 +02:00
1 parent 4708283382
commit 666b5d561a
10 files changed
+1274 -124

No files matched your search

+7
View File
@@ -895,6 +895,13 @@ impl OpenCADStudio {
"EXPORT" | "EXPORTPDF" => {
return Some(Task::done(Message::PlotExport));
}
// EXPORTSVG — the same direct export, but the save dialog
// proposes the SVG device's defaults (`stem.svg`).
"EXPORTSVG" => {
self.plot_dialog.to_file = true;
self.plot_dialog.file_svg = true;
return Some(Task::done(Message::PlotExport));
}
// PLOTSTYLE — load or clear CTB/STB plot style table
cmd if cmd == "PLOTSTYLE" || cmd.starts_with("PLOTSTYLE ") => {
let sub = cmd
+1
View File
@@ -475,6 +475,7 @@ inventory::submit!(crate::command::CommandRegistration {
// Standard aliases for existing commands.
"BMAKE",
"EXPORTPDF",
"EXPORTSVG",
"PRINTALL",
"DDIM",
// Inquiry: list the whole drawing database.
+43 -17
View File
@@ -474,8 +474,11 @@ impl OpenCADStudio {
use crate::io::pdf_export::PdfPageInput;
let path = string(req, "path")?;
if !path.to_ascii_lowercase().ends_with(".pdf") {
return Err(failure("invalid_path", "plot writes .pdf files"));
let lower = path.to_ascii_lowercase();
// SVG shares the plot page with PDF: the writer follows the extension.
let is_svg = lower.ends_with(".svg");
if !is_svg && !lower.ends_with(".pdf") {
return Err(failure("invalid_path", "plot writes .pdf or .svg files"));
}
self.stamp_plot_fields();
let i = self.active_tab;
@@ -688,22 +691,37 @@ impl OpenCADStudio {
self.tabs[i].scene.camera_generation = original_camera_generation;
let pages = built.map_err(|e| failure("plot_failed", e))?;
if req["per_page"].as_bool().unwrap_or(false) {
// One PDF per layout: <stem>-<Layout>.pdf beside the requested
// One file per layout: <stem>-<Layout>.pdf/.svg beside the requested
// path (sheet-by-sheet delivery).
let stem = path.strip_suffix(".pdf").unwrap_or(path).to_owned();
let stem = path
.strip_suffix(".pdf")
.or_else(|| path.strip_suffix(".PDF"))
.or_else(|| path.strip_suffix(".svg"))
.or_else(|| path.strip_suffix(".SVG"))
.unwrap_or(path)
.to_owned();
let ext = if is_svg { "svg" } else { "pdf" };
let mut files = Vec::with_capacity(pages.len());
for (name, page) in targets.iter().zip(&pages) {
let safe: String = name
.chars()
.map(|c| if c.is_alphanumeric() || c == '-' || c == '_' { c } else { '_' })
.collect();
let file = format!("{stem}-{safe}.pdf");
crate::io::pdf_export::export_pdf_pages(
std::slice::from_ref(page),
std::path::Path::new(&file),
loaded_style.as_ref(),
)
.map_err(|e| failure("plot_failed", e))?;
let file = format!("{stem}-{safe}.{ext}");
let result = if is_svg {
crate::io::svg_export::export_svg_pages(
std::slice::from_ref(page),
std::path::Path::new(&file),
loaded_style.as_ref(),
)
} else {
crate::io::pdf_export::export_pdf_pages(
std::slice::from_ref(page),
std::path::Path::new(&file),
loaded_style.as_ref(),
)
};
result.map_err(|e| failure("plot_failed", e))?;
files.push(json!({"layout": name, "path": file}));
}
self.set_control_result(json!({ "files": files, "pages": pages.len() }));
@@ -712,12 +730,20 @@ impl OpenCADStudio {
// Pages may drop the style when a stored page setup overwrites the
// dialog's style fields, so the explicit request style rides along as
// the export-level fallback.
crate::io::pdf_export::export_pdf_pages(
&pages,
std::path::Path::new(path),
loaded_style.as_ref(),
)
.map_err(|e| failure("plot_failed", e))?;
let result = if is_svg {
crate::io::svg_export::export_svg_pages(
&pages,
std::path::Path::new(path),
loaded_style.as_ref(),
)
} else {
crate::io::pdf_export::export_pdf_pages(
&pages,
std::path::Path::new(path),
loaded_style.as_ref(),
)
};
result.map_err(|e| failure("plot_failed", e))?;
self.set_control_result(json!({
"path": path,
"pages": pages.len(),
+51 -3
View File
@@ -3832,7 +3832,17 @@ impl OpenCADStudio {
};
let worker_path = path.clone();
let work = move || {
crate::io::pdf_export::export_pdf(&page, &worker_path)
// SVG shares the plot page with PDF: pick the writer from the
// chosen extension.
let result = if worker_path
.extension()
.is_some_and(|ext| ext.eq_ignore_ascii_case("svg"))
{
crate::io::svg_export::export_svg(&page, &worker_path)
} else {
crate::io::pdf_export::export_pdf(&page, &worker_path)
};
result
.map(|_| crate::tf!("Exported: {}", worker_path.display()).into_owned())
.map_err(|e| crate::tf!("Export failed: {e}").into_owned())
};
@@ -3859,7 +3869,15 @@ impl OpenCADStudio {
let worker_path = path.clone();
self.close_active_modal();
let work = move || {
crate::io::pdf_export::export_pdf(&page, &worker_path)
let result = if worker_path
.extension()
.is_some_and(|ext| ext.eq_ignore_ascii_case("svg"))
{
crate::io::svg_export::export_svg(&page, &worker_path)
} else {
crate::io::pdf_export::export_pdf(&page, &worker_path)
};
result
.map(|_| {
crate::tf!(
"Plotted window to {}",
@@ -4856,7 +4874,7 @@ impl OpenCADStudio {
msg: crate::ui::window::plot::PlotDlgMsg,
) -> Task<Message> {
use crate::ui::window::plot::{
PlotDlgMsg as M, PlotFlag, OUT_DEFAULT, OUT_PDF, STYLE_NONE,
PlotDlgMsg as M, PlotFlag, OUT_DEFAULT, OUT_PDF, OUT_SVG, STYLE_NONE,
};
match msg {
M::Close => {
@@ -4872,11 +4890,17 @@ impl OpenCADStudio {
self.plot_dialog.printer_editor = None;
if s == OUT_PDF {
self.plot_dialog.to_file = true;
self.plot_dialog.file_svg = false;
} else if s == OUT_SVG {
self.plot_dialog.to_file = true;
self.plot_dialog.file_svg = true;
} else if s == OUT_DEFAULT {
self.plot_dialog.to_file = false;
self.plot_dialog.file_svg = false;
self.plot_dialog.printer = None;
} else {
self.plot_dialog.to_file = false;
self.plot_dialog.file_svg = false;
self.plot_dialog.printer = Some(s);
}
self.save_config();
@@ -5371,6 +5395,7 @@ impl OpenCADStudio {
let is_model = self.tabs[self.active_tab].scene.current_layout == "Model";
let d = &mut self.plot_dialog;
d.to_file = true;
d.file_svg = false;
let a4 = crate::io::paper_catalog::default_paper();
d.paper = a4.canonical.to_string();
d.orientation = "Landscape".into();
@@ -5593,14 +5618,17 @@ impl OpenCADStudio {
match crate::io::plot_device::PlotDevice::from_stored_name(&ps.printer_name).0 {
crate::io::plot_device::PlotDevice::Pdf => {
d.to_file = true;
d.file_svg = false;
d.printer = None;
}
crate::io::plot_device::PlotDevice::Printer(name) => {
d.to_file = false;
d.file_svg = false;
d.printer = Some(name);
}
crate::io::plot_device::PlotDevice::None => {
d.to_file = false;
d.file_svg = false;
d.printer = None;
}
}
@@ -6714,6 +6742,26 @@ mod plot_paper_tests {
assert_eq!(ps.margins, codec::objects::PaperMargin::new(5.0, 17.0, 6.0, 18.0));
}
#[test]
fn svg_device_is_session_state_and_stores_as_pdf() {
let mut app = app_with_printer_named_sheet();
let _ = app.on_plot_dialog_open();
// The SVG device behaves like file output…
let _ = app.on_plot_dlg(PlotDlgMsg::Printer(crate::ui::window::plot::OUT_SVG.into()));
assert!(app.plot_dialog.to_file);
assert!(app.plot_dialog.file_svg);
// …but switching back to PDF (or a printer) clears it.
let _ = app.on_plot_dlg(PlotDlgMsg::Printer(crate::ui::window::plot::OUT_PDF.into()));
assert!(app.plot_dialog.to_file);
assert!(!app.plot_dialog.file_svg);
// The drawing never learns about SVG: page setups only know printers
// and PDF drivers, so an SVG plot stores the PDF driver spelling.
let _ = app.on_plot_dlg(PlotDlgMsg::Printer(crate::ui::window::plot::OUT_SVG.into()));
let _ = app.on_plot_dlg(PlotDlgMsg::SetCurrent);
let ps = app.tabs[app.active_tab].scene.plot_settings_for("Layout1").unwrap();
assert_eq!(ps.printer_name, "DWG To PDF.pc3");
}
#[test]
fn legacy_pdf_label_is_upgraded_on_save() {
let mut app = app_with_printer_named_sheet();
+12 -8
View File
@@ -9824,14 +9824,14 @@ impl OpenCADStudio {
return Task::done(Message::PrintAllPdfPath(None));
};
iced::window::run(window_id, move |parent| {
crate::io::pdf_export::pick_pdf_path_owned(stem, parent)
crate::io::pdf_export::pick_plot_path_owned(stem, parent, false)
})
.map(Message::PrintAllPdfPath)
}
#[cfg(target_os = "windows")]
{
Task::perform(
crate::io::pdf_export::pick_pdf_path_async(stem),
crate::io::pdf_export::pick_plot_path_async(stem, false),
Message::PrintAllPdfPath,
)
}
@@ -9870,27 +9870,30 @@ impl OpenCADStudio {
.and_then(|p: &std::path::Path| p.file_stem())
.map(|s: &std::ffi::OsStr| s.to_string_lossy().into_owned())
.unwrap_or_else(|| "drawing".into());
// The save dialog follows the dialog's file device, so the
// SVG choice proposes `stem.svg` up front.
let svg = self.plot_dialog.file_svg;
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "windows")))]
{
let Some(window_id) = self.main_window else {
return Task::done(Message::PlotExportPath(None));
};
iced::window::run(window_id, move |parent| {
crate::io::pdf_export::pick_pdf_path_owned(stem, parent)
crate::io::pdf_export::pick_plot_path_owned(stem, parent, svg)
})
.map(Message::PlotExportPath)
}
#[cfg(target_os = "windows")]
{
Task::perform(
crate::io::pdf_export::pick_pdf_path_async(stem),
crate::io::pdf_export::pick_plot_path_async(stem, svg),
Message::PlotExportPath,
)
}
#[cfg(target_arch = "wasm32")]
{
Task::perform(
crate::io::pdf_export::pick_pdf_path_owned(stem),
crate::io::pdf_export::pick_plot_path_owned(stem),
Message::PlotExportPath,
)
}
@@ -9906,27 +9909,28 @@ impl OpenCADStudio {
.and_then(|p: &std::path::Path| p.file_stem())
.map(|s: &std::ffi::OsStr| s.to_string_lossy().into_owned())
.unwrap_or_else(|| "drawing".into());
let svg = self.plot_dialog.file_svg;
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "windows")))]
{
let Some(window_id) = self.main_window else {
return Task::done(Message::PlotWindowExportPath(None));
};
iced::window::run(window_id, move |parent| {
crate::io::pdf_export::pick_pdf_path_owned(stem, parent)
crate::io::pdf_export::pick_plot_path_owned(stem, parent, svg)
})
.map(Message::PlotWindowExportPath)
}
#[cfg(target_os = "windows")]
{
Task::perform(
crate::io::pdf_export::pick_pdf_path_async(stem),
crate::io::pdf_export::pick_plot_path_async(stem, svg),
Message::PlotWindowExportPath,
)
}
#[cfg(target_arch = "wasm32")]
{
Task::perform(
crate::io::pdf_export::pick_pdf_path_owned(stem),
crate::io::pdf_export::pick_plot_path_owned(stem),
Message::PlotWindowExportPath,
)
}
+2
View File
@@ -12,6 +12,8 @@ pub mod ole_embed;
#[cfg(not(target_arch = "wasm32"))]
pub mod single_instance;
pub mod pdf_export;
pub mod plot;
pub mod svg_export;
pub mod plot_style;
pub mod print_to_printer;
pub mod recovery;
+44 -93
View File
@@ -13,7 +13,6 @@ use crate::scene::model::hatch_model::HatchModel;
#[cfg(not(target_arch = "wasm32"))]
use crate::scene::model::hatch_model::HatchPattern;
use crate::scene::WireModel;
use crate::scene::model::image_model::ImageModel;
#[cfg(not(target_arch = "wasm32"))]
use crate::scene::model::wire_model::SearchableTextRun;
#[cfg(not(target_arch = "wasm32"))]
@@ -25,27 +24,13 @@ use printpdf::{
};
use std::path::Path;
#[derive(Clone, Debug)]
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
pub struct PlotWire {
pub wire: WireModel,
pub draw_depth: f32,
}
impl std::ops::Deref for PlotWire {
type Target = WireModel;
fn deref(&self) -> &Self::Target {
&self.wire
}
}
/// Decoded image geometry plus inherited block/viewport clip boundaries.
#[derive(Clone, Debug)]
pub struct PlotImage {
pub image: ImageModel,
pub clips: Vec<Vec<[f64; 2]>>,
}
// Shared plot model: `PlotWire`, `PlotImage`, `PlotContent`, `PlotGroupSplits`
// and friends live in `super::plot`. The `Pdf*` names remain as aliases so
// the scene, the print pipeline and every other call site keep compiling.
pub use super::plot::{
PlotContent, PlotGroupSplits, PlotImage, PlotOptions as PdfPlotOptions,
PlotPage as PdfPageInput, PlotWire,
};
// The web build has no `printpdf` (it pulls a wasm-incompatible `memchr` via
// lopdf → nom_locate) and no filesystem, so PDF export is native-only; the web
@@ -65,7 +50,7 @@ pub fn export_pdf_pages(
}
#[cfg(target_arch = "wasm32")]
pub async fn pick_pdf_path_owned(_stem: String) -> Option<std::path::PathBuf> {
pub async fn pick_plot_path_owned(_stem: String) -> Option<std::path::PathBuf> {
None
}
@@ -82,63 +67,6 @@ const LW_PX_TO_PT: f32 = MM_TO_PT / (96.0 / 25.4);
#[cfg(not(target_arch = "wasm32"))]
const SCREEN_DOT_MM: f32 = 25.4 / 96.0;
/// Output controls shared by preview, PDF export, and printer rendering.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PdfPlotOptions {
pub object_lineweights: bool,
pub scale_lineweights: bool,
pub transparency: bool,
pub stamp: bool,
pub merge_lines: bool,
}
/// End indexes of the first paper/model render group in each flat input list.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct PlotGroupSplits {
pub wires: usize,
pub hatches: usize,
pub wipeouts: usize,
pub images: usize,
}
#[derive(Default)]
pub struct PlotContent {
pub wires: std::sync::Arc<Vec<PlotWire>>,
pub hatches: Vec<HatchModel>,
pub wipeouts: Vec<HatchModel>,
pub images: Vec<PlotImage>,
pub group_splits: PlotGroupSplits,
}
/// Owned geometry and settings for one PDF page.
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
pub struct PdfPageInput {
pub content: PlotContent,
/// Page dimensions in mm, after any 90/270-degree rotation.
pub paper_w: f64,
pub paper_h: f64,
/// Absolute-world offsets stay f64 to preserve local detail at UTM coordinates.
pub offset_x: f64,
pub offset_y: f64,
pub rotation_deg: i32,
pub scale: f32,
pub clip: Option<(f32, f32, f32, f32)>,
pub options: PdfPlotOptions,
pub plot_style: Option<PlotStyleTable>,
}
impl Default for PdfPlotOptions {
fn default() -> Self {
Self {
object_lineweights: true,
scale_lineweights: false,
transparency: false,
stamp: false,
merge_lines: false,
}
}
}
// ── Public entry point ────────────────────────────────────────────────────
/// Export one page to a PDF file.
@@ -381,21 +309,33 @@ fn image_triangle_matrix(p: [[f32; 2]; 3], uv: [[f32; 2]; 3]) -> Option<[f32; 6]
])
}
/// Show a parented PDF save-file dialog and return the chosen path.
/// Show a parented plot save-file dialog and return the chosen path.
///
/// The parent comes from `iced::window::run`, keeping the portal request tied
/// to the visible app window on Wayland instead of silently resolving to
/// `None` on desktops that reject a parentless save dialog (#537).
/// `svg` selects the SVG device's defaults (name, title, first filter).
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "windows")))]
pub fn pick_pdf_path_owned(
pub fn pick_plot_path_owned(
stem: String,
parent: &dyn iced::window::Window,
svg: bool,
) -> Option<std::path::PathBuf> {
let path = crate::sys::blocking_file_dialog()
.set_parent(parent)
.set_title(crate::t!("Export as PDF").as_ref())
.set_file_name(&format!("{stem}.pdf"))
.add_filter(crate::t!("PDF Files").as_ref(), &["pdf"])
let dialog = crate::sys::blocking_file_dialog().set_parent(parent);
let dialog = if svg {
dialog
.set_title(crate::t!("Export as SVG").as_ref())
.set_file_name(&format!("{stem}.svg"))
.add_filter(crate::t!("SVG Files").as_ref(), &["svg"])
.add_filter(crate::t!("PDF Files").as_ref(), &["pdf"])
} else {
dialog
.set_title(crate::t!("Export as PDF").as_ref())
.set_file_name(&format!("{stem}.pdf"))
.add_filter(crate::t!("PDF Files").as_ref(), &["pdf"])
.add_filter(crate::t!("SVG Files").as_ref(), &["svg"])
};
let path = dialog
.add_filter(crate::t!("All Files").as_ref(), &["*"])
.save_file()
?;
@@ -403,7 +343,7 @@ pub fn pick_pdf_path_owned(
Some(path)
}
/// Windows: pick the PDF destination with the async dialog on a worker
/// Windows: pick the plot destination with the async dialog on a worker
/// thread. The parented blocking dialog ran `IFileDialog::Show` on the UI
/// thread inside the window callback, and when the target name already
/// existed the overwrite-confirmation popup is a second nested modal that
@@ -411,11 +351,22 @@ pub fn pick_pdf_path_owned(
/// backend runs the dialog off-thread; it is the same pattern the DWG
/// Save As flow uses, whose confirm popup works.
#[cfg(all(not(target_arch = "wasm32"), target_os = "windows"))]
pub async fn pick_pdf_path_async(stem: String) -> Option<std::path::PathBuf> {
let handle = crate::sys::file_dialog()
.set_title(crate::t!("Export as PDF").as_ref())
.set_file_name(format!("{stem}.pdf"))
.add_filter(crate::t!("PDF Files").as_ref(), &["pdf"])
pub async fn pick_plot_path_async(stem: String, svg: bool) -> Option<std::path::PathBuf> {
let dialog = crate::sys::file_dialog();
let dialog = if svg {
dialog
.set_title(crate::t!("Export as SVG").as_ref())
.set_file_name(format!("{stem}.svg"))
.add_filter(crate::t!("SVG Files").as_ref(), &["svg"])
.add_filter(crate::t!("PDF Files").as_ref(), &["pdf"])
} else {
dialog
.set_title(crate::t!("Export as PDF").as_ref())
.set_file_name(format!("{stem}.pdf"))
.add_filter(crate::t!("PDF Files").as_ref(), &["pdf"])
.add_filter(crate::t!("SVG Files").as_ref(), &["svg"])
};
let handle = dialog
.add_filter(crate::t!("All Files").as_ref(), &["*"])
.save_file()
.await?;
+91
View File
@@ -0,0 +1,91 @@
// Shared plot model — the paper-space geometry every plot backend consumes.
//
// `PdfPageInput`/`PdfPlotOptions` used to live in `pdf_export`, which made the
// scene, the print pipeline and now the SVG exporter all import from the PDF
// backend. The structs themselves are backend-agnostic (wires, hatches,
// images, page size/offset/clip), so they live here; `pdf_export` re-exports
// the historical `Pdf*` names as aliases so existing call sites keep working.
use crate::io::plot_style::PlotStyleTable;
use crate::scene::WireModel;
use crate::scene::model::hatch_model::HatchModel;
use crate::scene::model::image_model::ImageModel;
#[derive(Clone, Debug)]
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
pub struct PlotWire {
pub wire: WireModel,
pub draw_depth: f32,
}
impl std::ops::Deref for PlotWire {
type Target = WireModel;
fn deref(&self) -> &Self::Target {
&self.wire
}
}
/// Decoded image geometry plus inherited block/viewport clip boundaries.
#[derive(Clone, Debug)]
pub struct PlotImage {
pub image: ImageModel,
pub clips: Vec<Vec<[f64; 2]>>,
}
/// Output controls shared by preview, plot exports, and printer rendering.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PlotOptions {
pub object_lineweights: bool,
pub scale_lineweights: bool,
pub transparency: bool,
pub stamp: bool,
pub merge_lines: bool,
}
/// End indexes of the first paper/model render group in each flat input list.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct PlotGroupSplits {
pub wires: usize,
pub hatches: usize,
pub wipeouts: usize,
pub images: usize,
}
#[derive(Default)]
pub struct PlotContent {
pub wires: std::sync::Arc<Vec<PlotWire>>,
pub hatches: Vec<HatchModel>,
pub wipeouts: Vec<HatchModel>,
pub images: Vec<PlotImage>,
pub group_splits: PlotGroupSplits,
}
/// Owned geometry and settings for one plot page.
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
pub struct PlotPage {
pub content: PlotContent,
/// Page dimensions in mm, after any 90/270-degree rotation.
pub paper_w: f64,
pub paper_h: f64,
/// Absolute-world offsets stay f64 to preserve local detail at UTM coordinates.
pub offset_x: f64,
pub offset_y: f64,
pub rotation_deg: i32,
pub scale: f32,
pub clip: Option<(f32, f32, f32, f32)>,
pub options: PlotOptions,
pub plot_style: Option<PlotStyleTable>,
}
impl Default for PlotOptions {
fn default() -> Self {
Self {
object_lineweights: true,
scale_lineweights: false,
transparency: false,
stamp: false,
merge_lines: false,
}
}
}
+1007
View File
File diff suppressed because it is too large. Load diff
+16 -3
View File
@@ -22,6 +22,7 @@ use std::fmt;
/// Sentinel entries in the printer dropdown (not real printer names).
pub const OUT_DEFAULT: &str = "System default printer";
pub const OUT_PDF: &str = "Save to PDF file…";
pub const OUT_SVG: &str = "Save to SVG file…";
/// Top-of-list entries: no page setup (defaults + PDF), and the last-used
/// settings captured when the dialog opened.
@@ -449,8 +450,13 @@ pub struct PlotDialogState {
pub page_setup_on_new_layout: bool,
/// Chosen printer name, or `None` for the system default.
pub printer: Option<String>,
/// Output goes to a PDF file instead of a printer.
/// Output goes to a file instead of a printer.
pub to_file: bool,
/// File output is SVG instead of PDF (only meaningful when `to_file`).
/// Session state, like the printer choice: never written into the
/// drawing's page setup, which only knows printers and PDF drivers.
#[serde(default)]
pub file_svg: bool,
pub paper: String,
#[serde(skip)]
pub paper_width_mm: f64,
@@ -560,6 +566,7 @@ impl Default for PlotDialogState {
page_setup_on_new_layout: false,
printer: None,
to_file: false,
file_svg: false,
paper: paper_catalog::default_paper().canonical.to_string(),
paper_width_mm: 297.0,
paper_height_mm: 210.0,
@@ -616,6 +623,7 @@ impl PlotDialogState {
pub fn copy_settings_from(&mut self, o: &PlotDialogState) {
self.printer = o.printer.clone();
self.to_file = o.to_file;
self.file_svg = o.file_svg;
self.paper = o.paper.clone();
self.paper_width_mm = o.paper_width_mm;
self.paper_height_mm = o.paper_height_mm;
@@ -1077,7 +1085,11 @@ pub fn view_window(
let action = if print_all_options {
t!("Apply")
} else if s.to_file {
t!("Export PDF")
if s.file_svg {
t!("Export SVG")
} else {
t!("Export PDF")
}
} else {
t!("Print")
};
@@ -1217,8 +1229,9 @@ pub fn view_window(
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(OUT_PDF))
Some(PlotChoice::localized(if s.file_svg { OUT_SVG } else { OUT_PDF }))
} else {
Some(match &s.printer {
Some(printer) => PlotChoice::raw(printer.clone()),