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]