Skip to content

Commit

Permalink
fix parsing formula in xls
Browse files Browse the repository at this point in the history
  • Loading branch information
tafia committed Jun 15, 2023
1 parent ba6d6e6 commit 9b8fdb3
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 21 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ serde_derive = "1.0"
sha256 = "1.1"

[features]
default = []
default = ["dates"]
dates = ["chrono", "once_cell"]
picture = []
5 changes: 5 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ pub fn read_u16(s: &[u8]) -> u16 {
u16::from_le_bytes(s[..2].try_into().unwrap())
}

#[inline]
pub fn read_i16(s: &[u8]) -> i16 {
i16::from_le_bytes(s[..2].try_into().unwrap())
}

#[inline]
pub fn read_u64(s: &[u8]) -> u64 {
u64::from_le_bytes(s[..8].try_into().unwrap())
Expand Down
58 changes: 38 additions & 20 deletions src/xls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::formats::{
};
#[cfg(feature = "picture")]
use crate::utils::read_usize;
use crate::utils::{push_column, read_f64, read_i32, read_u16, read_u32};
use crate::utils::{push_column, read_f64, read_i16, read_i32, read_u16, read_u32};
use crate::vba::VbaProject;
use crate::{Cell, CellErrorType, DataType, Metadata, Range, Reader};

Expand Down Expand Up @@ -239,6 +239,13 @@ impl<RS: Read + Seek> Reader<RS> for Xls<RS> {
}
}

#[derive(Debug, Clone, Copy)]
struct Xti {
_isup_book: u16,
itab_first: i16,
_itab_last: i16,
}

impl<RS: Read + Seek> Xls<RS> {
fn parse_workbook(&mut self, mut reader: RS, mut cfb: Cfb) -> Result<(), XlsError> {
// gets workbook and worksheets stream, or early exit
Expand Down Expand Up @@ -313,12 +320,11 @@ impl<RS: Read + Seek> Xls<RS> {
0x0017 => {
// ExternSheet
let cxti = read_u16(r.data) as usize;
xtis.extend(
r.data[2..]
.chunks(6)
.take(cxti)
.map(|xti| read_u16(&xti[2..]) as usize),
);
xtis.extend(r.data[2..].chunks(6).take(cxti).map(|xti| Xti {
_isup_book: read_u16(&xti[..2]),
itab_first: read_i16(&xti[2..4]),
_itab_last: read_i16(&xti[4..]),
}));
}
0x00FC => strings = parse_sst(&mut r, &mut encoding)?, // SST
#[cfg(feature = "picture")]
Expand Down Expand Up @@ -347,16 +353,15 @@ impl<RS: Read + Seek> Xls<RS> {

let defined_names = defined_names
.into_iter()
.map(|(name, (i, f))| {
.map(|(name, (i, mut f))| {
if let Some(i) = i {
if i >= xtis.len() || xtis[i] >= sheet_names.len() {
(name, format!("#REF!{}", f))
} else {
(name, format!("{}!{}", sheet_names[xtis[i]].1, f))
}
} else {
(name, f)
let sh = xtis
.get(i)
.and_then(|xti| sheet_names.get(xti.itab_first as usize))
.map_or("#REF", |sh| &sh.1);
f = format!("{sh}!{f}");
}
(name, f)
})
.collect::<Vec<_>>();

Expand Down Expand Up @@ -397,6 +402,7 @@ impl<RS: Read + Seek> Xls<RS> {
&r.data[20..],
&fmla_sheet_names,
&defined_names,
&xtis,
&mut encoding,
)
.unwrap_or_else(|e| {
Expand Down Expand Up @@ -940,6 +946,7 @@ fn parse_formula(
mut rgce: &[u8],
sheets: &[String],
names: &[(String, String)],
xtis: &[Xti],
encoding: &mut XlsEncoding,
) -> Result<String, XlsError> {
let mut stack = Vec::new();
Expand All @@ -953,13 +960,24 @@ fn parse_formula(
0x3a | 0x5a | 0x7a => {
// PtgRef3d
let ixti = read_u16(&rgce[0..2]);
let rowu = read_u16(&rgce[2..]);
let colu = read_u16(&rgce[4..]);
let sh = xtis
.get(ixti as usize)
.and_then(|xti| sheets.get(xti.itab_first as usize))
.map_or("#REF", |sh| &sh);
stack.push(formula.len());
formula.push_str(sheets.get(ixti as usize).map_or("#REF", |s| &**s));
formula.push_str(sh);
formula.push('!');
// TODO: check with relative columns
formula.push('$');
push_column(read_u16(&rgce[4..6]) as u32, &mut formula);
write!(&mut formula, "${}", read_u16(&rgce[2..4]) as u32 + 1).unwrap();
let col = colu << 2; // first 14 bits only
if colu & 2 != 0 {
formula.push('$');
}
push_column(col as u32, &mut formula);
if colu & 1 != 0 {
formula.push('$');
}
write!(&mut formula, "{}", rowu + 1).unwrap();
rgce = &rgce[6..];
}
0x3b | 0x5b | 0x7b => {
Expand Down
13 changes: 13 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1229,3 +1229,16 @@ fn ods_number_rows_repeated() {
]
);
}

#[test]
fn xls_formula() {
setup();
let path = format!("{}/tests/xls_formula.xls", env!("CARGO_MANIFEST_DIR"));
let mut wb: Xls<_> = open_workbook(&path).unwrap();
let formula = wb.worksheet_formula("Sheet1").unwrap().unwrap();
let mut rows = formula.rows();
assert_eq!(rows.next(), Some(&["A1*2".to_owned()][..]));
assert_eq!(rows.next(), Some(&["2*Sheet2!A1".to_owned()][..]));
assert_eq!(rows.next(), Some(&["A1+Sheet2!A1".to_owned()][..]));
assert_eq!(rows.next(), None);
}
Binary file added tests/xls_formula.xls
Binary file not shown.

0 comments on commit 9b8fdb3

Please sign in to comment.