From 94af08ad200b5c034cd3508d6446eb6a2ddba686 Mon Sep 17 00:00:00 2001 From: aviiciii Date: Sat, 17 Oct 2026 01:23:26 +0530 Subject: [PATCH] XLSX: new workbooks get (fixes #1699) A workbook GridCraft created had no in workbook.xml; it now names GridCraft, and keeps it on later saves. A workbook read from an Excel file still keeps its as it was, appName="xl" included. Observed in Excel 16.114: with the calcFeatures extension present, Excel uses the saved cell values only when appName is "xl". appName="gridcraft", "Calc" or no appName makes it recalculate everything on open, which brings back #799 (lookups into closed workbooks open as #CALC!). The application that last saved the file is recorded in docProps/app.xml GridCraft, which GridCraft already writes on every save and which Excel's recalculation ignores. --- crates/xlsx/src/tests/passthrough_tests.rs | 15 ++++++++++++--- crates/xlsx/src/write.rs | 5 ++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/xlsx/src/tests/passthrough_tests.rs b/crates/xlsx/src/tests/passthrough_tests.rs index d844daa05..f3bf89919 100644 --- a/crates/xlsx/src/tests/passthrough_tests.rs +++ b/crates/xlsx/src/tests/passthrough_tests.rs @@ -819,7 +819,16 @@ fn file_version_and_calc_features_survive_a_save() { assert!(text.contains(&format!("{calc_features}")), "{text}"); } - // A workbook that had neither gets neither. - let plain = Unpacked::new(&write_xlsx(&gridcraft_model::Workbook::new()).unwrap()).text("xl/workbook.xml"); - assert!(!plain.contains("fileVersion") && !plain.contains("extLst"), "{plain}"); + // A workbook that had neither gets a naming GridCraft (#1699), kept on the next + // save, and no calcFeatures: Excel recalculates it on open. + let new = write_xlsx(&gridcraft_model::Workbook::new()).unwrap(); + let (reread, _) = read_xlsx(&new).unwrap(); + for saved in [new, write_xlsx(&reread).unwrap()] { + let u = Unpacked::new(&saved); + u.assert_valid(); + let plain = u.text("xl/workbook.xml"); + let root = xml::parse(plain.as_bytes()).unwrap(); + assert_eq!(root.children.first().map(|e| e.name.as_str()), Some("fileVersion"), "{plain}"); + assert!(plain.contains(r#""#) && !plain.contains("extLst"), "{plain}"); + } } diff --git a/crates/xlsx/src/write.rs b/crates/xlsx/src/write.rs index 06761f01d..a4c5d1d70 100644 --- a/crates/xlsx/src/write.rs +++ b/crates/xlsx/src/write.rs @@ -314,7 +314,10 @@ pub fn write_xlsx(wb: &Workbook, kind: XlsxKind) -> Result, IoError> { } let mut w = format!("{XML_DECL}"); - w.push_str(&crate::passthrough::workbook_elements(wb, "fileVersion")); + // A workbook read from a file keeps its as it was: Excel trusts the saved values + // only when appName is "xl" (#799). One without names GridCraft (#1699), and Excel recalculates it. + let file_version = crate::passthrough::workbook_elements(wb, "fileVersion"); + w.push_str(if file_version.is_empty() { "" } else { &file_version }); w.push_str(if wb.date_system == gridcraft_core::DateSystem::D1904 { "" } else { "" }); if wb.protected_structure { let pw = wb.structure_password.as_ref().map(|h| crate::sheet_write::password_attrs(h, "workbook")).unwrap_or_default();