Merge pull request #383 from KevinGriffin-new/fix/pdf-export

Never fail silently on PDF export; add EXPORTPDF <path> (#369).

The export flow handed off to an async rfd save dialog and mapped a None
result straight to Task::none(). On Linux None also means the dialog never
opened (broken XDG portal / missing zenity), and rfd reports that only via
log, so PDF export could end in total silence. The dialog-None arms now
print a message pointing at EXPORTPDF <path>, and EXPORT/EXPORTPDF take an
optional path that exports straight to a file with no dialog — the
workaround where the native dialog can't open, and a headless entry point.

Conflict: the fix (display.rs, file.rs, mod.rs, COMMANDS.md) auto-merged;
only the additive test block in automation.rs conflicted. Resolved by
keeping main's tests and adding the two #369 tests.

# Conflicts:
#	src/app/automation.rs
This commit is contained in:
Hakan Seven committed 2026-07-16 11:52:46 +03:00
commit 5b4a6719aa
5 files changed
+116 -11

No files matched your search

+34 -2
View File
@@ -365,8 +365,40 @@ impl OpenCADStudio {
"PLOT" | "PRINT" => {
return Some(Task::done(Message::PlotDialogOpen));
}
"EXPORT" | "EXPORTPDF" => {
return Some(Task::done(Message::PlotExport));
// With no argument these open the save dialog; with a path they
// export straight to it. The direct form is also the workaround for
// systems where the native save dialog cannot open at all (broken
// XDG portal / missing zenity on Linux) — there the dialog future
// resolves to None and no export would happen. (#369)
cmd if cmd == "EXPORT"
|| cmd == "EXPORTPDF"
|| cmd.starts_with("EXPORT ")
|| cmd.starts_with("EXPORTPDF ") =>
{
// Strip the keyword actually typed — EXPORTPDF first, since
// EXPORT is its prefix (see #295 for the failure mode).
let arg = cmd
.strip_prefix("EXPORTPDF")
.or_else(|| cmd.strip_prefix("EXPORT"))
.unwrap_or("")
.trim()
.trim_matches('"');
if arg.is_empty() {
return Some(Task::done(Message::PlotExport));
}
let mut path = std::path::PathBuf::from(arg);
if path.extension().is_none() {
path.set_extension("pdf");
}
// A bare filename lands next to the drawing when it has a home.
if path.is_relative() {
if let Some(dir) =
self.tabs[i].current_path.as_deref().and_then(|p| p.parent())
{
path = dir.join(path);
}
}
return Some(self.on_plot_export_path_some(path));
}
// PLOTSTYLE — load or clear CTB/STB plot style table
cmd if cmd == "PLOTSTYLE" || cmd.starts_with("PLOTSTYLE ") => {