From d66ed11253bc750527c110355786987c28557ade Mon Sep 17 00:00:00 2001
From: midasmatterhorn <248507632+midasmatterhorn@users.noreply.github.com>
Date: Wed, 14 Oct 2026 23:58:57 +0300
Subject: [PATCH] xlsx: keep every property of the default cell format when
importing a file
---
crates/xlsx/src/read.rs | 6 ++++--
crates/xlsx/src/tests/fixtures.rs | 30 ++++++++++++++++++++++++++++++
2 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/crates/xlsx/src/read.rs b/crates/xlsx/src/read.rs
index 1a479081c..c65008bcc 100644
--- a/crates/xlsx/src/read.rs
+++ b/crates/xlsx/src/read.rs
@@ -123,9 +123,11 @@ pub fn read_xlsx(bytes: &[u8]) -> Result<(Workbook, ReadReport), IoError> {
if let Some(x) = cx.optional_xml(&r.target)? {
let StylesIn { xfs, dxfs, palette, named, num_fmts } = read_styles(&x, &checkboxes);
cx.num_fmts = num_fmts;
- // Cell format 0 is the Normal style: its font is what every unformatted cell shows.
+ // Cell format 0 is the Normal cell format: everything on it (font, number format,
+ // fill, borders, alignment, protection) is what a cell without its own format shows
+ // (#1013).
if let Some(normal) = xfs.first() {
- cx.styles = StyleTable::with_default(Style { font: normal.font.clone(), ..Style::default() });
+ cx.styles = StyleTable::with_default(normal.clone());
}
cx.xf_map = xfs.into_iter().enumerate().map(|(i, s)| if i == 0 { StyleId::DEFAULT } else { cx.styles.intern(s) }).collect();
cx.dxfs = dxfs;
diff --git a/crates/xlsx/src/tests/fixtures.rs b/crates/xlsx/src/tests/fixtures.rs
index 39ea30951..ee3297b3b 100644
--- a/crates/xlsx/src/tests/fixtures.rs
+++ b/crates/xlsx/src/tests/fixtures.rs
@@ -553,6 +553,36 @@ fn text_box_formatting_is_read_and_written() {
assert_eq!(g.text_outline.map(|o| o.1), Some(1.5));
}
+/// #1013: cell format 0 is the workbook's Normal cell format. A file that gives it a number
+/// format, fill, alignment or protection must keep all of it for cells that omit their style (or
+/// say s="0"), not only the font.
+#[test]
+fn the_default_cell_format_keeps_every_property_of_xf_zero() {
+ let styles = r#"
+
+
+
+
+
+ "#;
+ let rels = r#""#;
+ let body = r#"55
"#;
+ let (wb, _) = read_xlsx(&minimal(body, &[("xl/styles.xml", styles)], rels, "")).unwrap();
+ let s = wb.sheet(0).unwrap();
+ let check = |st: &gridcraft_model::Style, when: &str| {
+ assert_eq!(st.num_fmt.as_str(), "0.00", "number format {when}");
+ assert_eq!(st.fill.pattern, PatternType::Solid, "fill {when}");
+ assert_eq!(st.fill.fg, Color::Rgb(0x00B050), "fill colour {when}");
+ assert_eq!(st.align.h, HAlign::Center, "alignment {when}");
+ assert!(!st.protection.locked, "protection {when}");
+ };
+ check(wb.styles.get(StyleId::DEFAULT), "after reading");
+ assert_eq!(s.cell(at("A1")).unwrap().style, StyleId::DEFAULT);
+ assert_eq!(s.cell(at("B1")).unwrap().style, StyleId::DEFAULT);
+ let (back, _) = read_xlsx(&crate::write_xlsx(&wb).unwrap()).unwrap();
+ check(back.styles.get(StyleId::DEFAULT), "after saving");
+}
+
/// `_xludf.` marks a user-defined call even when a built-in has the same name; dropping it turns
/// the call into the built-in, and `_xludf.EOMONTH()` into a call Excel repairs away (#726).
#[test]