From ad2e3ac9de1a1ce011229a2c77924c8a201d8d51 Mon Sep 17 00:00:00 2001 From: Harrison Booth <73434116+Harrison-Booth@users.noreply.github.com> Date: Fri, 23 Oct 2026 16:37:19 -0500 Subject: [PATCH] Check duplicate .xls style names in linear time --- crates/xlsx/src/xls/styles.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/xlsx/src/xls/styles.rs b/crates/xlsx/src/xls/styles.rs index 6e254c20c..8bd4ca9f8 100644 --- a/crates/xlsx/src/xls/styles.rs +++ b/crates/xlsx/src/xls/styles.rs @@ -377,11 +377,18 @@ pub(super) fn build(r: &StyleRecords, formats: &HashMap, wb: &mut W // User-defined named styles. Excel 2007 and later write every built-in style into each .xls // (about fifty, their colours approximated to the palette); GridCraft defines those itself. let mut named: Vec<(String, Style)> = Vec::new(); + // Names are compared ASCII-case-insensitively; a set keeps a hostile file's many names linear. + let mut seen: std::collections::HashSet = std::collections::HashSet::from(["normal".to_string()]); for ns in &r.styles { - if ns.builtin.is_some() || ns.name.eq_ignore_ascii_case("Normal") || named.iter().any(|(n, _)| n.eq_ignore_ascii_case(&ns.name)) { + if ns.builtin.is_some() { + continue; + } + let key = ns.name.to_ascii_lowercase(); + if seen.contains(&key) { continue; } if let Some(xf) = r.xfs.get(usize::from(ns.ixf)) { + seen.insert(key); named.push((ns.name.clone(), xf_style(xf, &fonts, formats, &table))); } }