fix(sheet/xls): track substream depth and support CONTINUE records on formula strings

This commit is contained in:
Divyanshu Gupta committed 2026-10-21 20:00:37 +05:30
1 parent e05b7fe86f
commit bfb81b72eb
1 file changed
+65 -4
+65 -4
View File
@@ -317,14 +317,41 @@ fn sheet_rows(stream: &[u8], offset: usize, g: &Globals) -> Result<Vec<Vec<Strin
// A FORMULA with a string result waits for the STRING record after it (only shared-formula,
// array and table records may come between).
let mut pending: Option<(u16, u16)> = None;
let mut depth = 1usize;
while let Some((kind, d, next)) = record(stream, pos) {
pos = next;
if kind == EOF || kind == BOF {
break;
if kind == BOF {
depth = depth.saturating_add(1);
continue;
}
if kind == EOF {
depth = depth.saturating_sub(1);
if depth == 0 {
break;
}
continue;
}
if depth > 1 {
continue;
}
if kind == STRING {
if let (Some((r, c)), Some(s)) = (pending.take(), unicode_string(d, 0)) {
put(&mut cells, r, c, &s, &mut count);
let mut parts = vec![d];
let mut p = pos;
while let Some((CONTINUE, cd, n)) = record(stream, p) {
parts.push(cd);
p = n;
}
pos = p;
if let Some((r, c)) = pending.take() {
let cch = usize::from(u16_at(parts[0], 0).unwrap_or(0));
let high = parts[0].get(2).is_some_and(|f| f & 1 != 0);
if let Some(s) = parts[0].get(3..).and_then(|first| {
let mut all_parts = vec![first];
all_parts.extend_from_slice(&parts[1..]);
Pieces::new(all_parts).chars(cch, high)
}) {
put(&mut cells, r, c, &s, &mut count);
}
}
continue;
}
@@ -619,4 +646,38 @@ mod tests {
let old = comp.into_inner().into_inner();
assert!(sheet_names(&old).unwrap_err().contains("Excel 5.0/95"));
}
#[test]
fn embedded_substream_does_not_truncate_sheet() {
let mut stream = Vec::new();
fn rec(out: &mut Vec<u8>, kind: u16, data: &[u8]) {
out.extend_from_slice(&kind.to_le_bytes());
out.extend_from_slice(&(data.len() as u16).to_le_bytes());
out.extend_from_slice(data);
}
rec(&mut stream, BOF, &[0x00, 0x06, 0x05, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let sheet_offset = 37u32;
rec(&mut stream, BOUNDSHEET, &[sheet_offset as u8, 0, 0, 0, 0, 0, 1, 0, b'S']);
rec(&mut stream, EOF, &[]);
assert_eq!(stream.len(), sheet_offset as usize);
// Worksheet BOF
rec(&mut stream, BOF, &[0x00, 0x06, 0x10, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
// Cell row 0, col 0 = NUMBER 1.0
let mut c1 = vec![0, 0, 0, 0, 0, 0];
c1.extend_from_slice(&1.0f64.to_le_bytes());
rec(&mut stream, NUMBER, &c1);
// Embedded chart substream: BOF(0x0020) ... EOF
rec(&mut stream, BOF, &[0x00, 0x06, 0x20, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
rec(&mut stream, EOF, &[]);
// Cell row 1, col 0 = NUMBER 2.0
let mut c2 = vec![1, 0, 0, 0, 0, 0];
c2.extend_from_slice(&2.0f64.to_le_bytes());
rec(&mut stream, NUMBER, &c2);
// Worksheet EOF
rec(&mut stream, EOF, &[]);
let g = globals(&stream).unwrap();
let rows = sheet_rows(&stream, sheet_offset as usize, &g).unwrap();
assert_eq!(rows, vec![vec!["1".to_string()], vec!["2".to_string()]]);
}
}