From dcda5a52cea63d3823370df377f820d9890526a7 Mon Sep 17 00:00:00 2001 From: MarekSpodBiedry <73122968+MarekSpodBiedry@users.noreply.github.com> Date: Sat, 10 Oct 2026 19:04:04 +0200 Subject: [PATCH] Deliver Export as PDF as a browser download (#141) --- crates/ui-egui/src/lib.rs | 18 ++++++++++++++++++ crates/ui-egui/tests/file_menu.rs | 15 +++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index c3efe3b..457b326 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -214,6 +214,11 @@ impl SheetApp { } let r = self.session.run(id, params); self.after_engine(); + if id == "file.exportPdf" + && let Ok(r) = &r + { + self.deliver_pdf(r); + } r } @@ -234,6 +239,19 @@ impl SheetApp { ctx.copy_text(text.to_string()); } + /// Web: `file.exportPdf` without a path returns the PDF as base64; hand it to the browser as a download. + fn deliver_pdf(&self, result: &Json) { + if result.get("path").is_some() { + return; + } + if let Some(dl) = &self.services.download + && let Some(b) = result.get("base64").and_then(Json::as_str).and_then(gridcraft_engine::io::base64_decode) + { + let title = self.session.active().map(|d| d.display_title()).unwrap_or_else(|| "Book1".into()); + dl(&std::path::Path::new(&title).with_extension("pdf").to_string_lossy(), &b); + } + } + /// Runs a command and shows its error in a message box (for menu/ribbon clicks). pub fn run_or_alert(&mut self, id: &str, params: Json) { if let Err(e) = self.run(id, params) { diff --git a/crates/ui-egui/tests/file_menu.rs b/crates/ui-egui/tests/file_menu.rs index 5f6ca7c..f3b07f4 100644 --- a/crates/ui-egui/tests/file_menu.rs +++ b/crates/ui-egui/tests/file_menu.rs @@ -74,3 +74,18 @@ fn save_actions_include_the_cell_draft_and_close_keeps_the_unsaved_prompt() { assert_eq!(h.state().dialog.as_ref().map(|d| d.name()), Some("saveChanges")); assert_eq!(h.state().session.active().unwrap().wb.active().unwrap().value(CellRef::new(0, 0)), Value::Number(123.0)); } + +#[test] +fn export_as_pdf_is_delivered_as_a_download() { + let files = Arc::new(Mutex::new(Vec::<(String, Vec)>::new())); + let capture = files.clone(); + let mut h = harness(Services { + download: Some(Box::new(move |name, bytes| capture.lock().unwrap().push((name.to_string(), bytes.to_vec())))), + ..Default::default() + }); + h.state_mut().run("file.exportPdf", serde_json::json!({})).unwrap(); + let got = files.lock().unwrap(); + assert_eq!(got.len(), 1, "Export as PDF hands a file to the browser"); + assert!(got[0].0.ends_with(".pdf")); + assert!(got[0].1.starts_with(b"%PDF-")); +}