1mod cells_reader;
6
7pub use cells_reader::XlsbCellsReader;
8
9use std::borrow::Cow;
10use std::collections::BTreeMap;
11use std::io::{BufReader, Read, Seek};
12
13use log::debug;
14
15use encoding_rs::UTF_16LE;
16use quick_xml::events::attributes::Attribute;
17use quick_xml::events::Event;
18use quick_xml::name::QName;
19use quick_xml::Reader as XmlReader;
20use zip::read::{ZipArchive, ZipFile};
21use zip::result::ZipError;
22
23use crate::datatype::DataRef;
24use crate::formats::{builtin_format_by_code, detect_custom_number_format, CellFormat};
25use crate::utils::{push_column, read_f64, read_i32, read_u16, read_u32, read_usize};
26use crate::vba::VbaProject;
27use crate::{
28 Cell, Data, HeaderRow, Metadata, Range, Reader, ReaderRef, Sheet, SheetType, SheetVisible,
29 StyleRange, WorksheetLayout,
30};
31
32#[derive(Debug)]
34pub enum XlsbError {
35 Io(std::io::Error),
37 Zip(zip::result::ZipError),
39 Xml(quick_xml::Error),
41 XmlAttr(quick_xml::events::attributes::AttrError),
43 Vba(crate::vba::VbaError),
45
46 Mismatch {
48 expected: &'static str,
50 found: u16,
52 },
53 FileNotFound(String),
55 StackLen,
57
58 UnsupportedType(u16),
60 Etpg(u8),
62 IfTab(usize),
64 BErr(u8),
66 Ptg(u8),
68 CellError(u8),
70 WideStr {
72 ws_len: usize,
74 buf_len: usize,
76 },
77 Unrecognized {
79 typ: &'static str,
81 val: String,
83 },
84 Password,
86 WorksheetNotFound(String),
88 Encoding(quick_xml::encoding::EncodingError),
90}
91
92from_err!(std::io::Error, XlsbError, Io);
93from_err!(zip::result::ZipError, XlsbError, Zip);
94from_err!(quick_xml::Error, XlsbError, Xml);
95from_err!(quick_xml::events::attributes::AttrError, XlsbError, XmlAttr);
96from_err!(quick_xml::encoding::EncodingError, XlsbError, Encoding);
97from_err!(crate::vba::VbaError, XlsbError, Vba);
98
99impl std::fmt::Display for XlsbError {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 match self {
102 XlsbError::Io(e) => write!(f, "I/O error: {e}"),
103 XlsbError::Zip(e) => write!(f, "Zip error: {e}"),
104 XlsbError::Xml(e) => write!(f, "Xml error: {e}"),
105 XlsbError::XmlAttr(e) => write!(f, "Xml attribute error: {e}"),
106 XlsbError::Vba(e) => write!(f, "Vba error: {e}"),
107 XlsbError::Mismatch { expected, found } => {
108 write!(f, "Expecting {expected}, got {found:X}")
109 }
110 XlsbError::FileNotFound(file) => write!(f, "File not found: '{file}'"),
111 XlsbError::StackLen => write!(f, "Invalid stack length"),
112 XlsbError::UnsupportedType(t) => write!(f, "Unsupported type {t:X}"),
113 XlsbError::Etpg(t) => write!(f, "Unsupported etpg {t:X}"),
114 XlsbError::IfTab(t) => write!(f, "Unsupported iftab {t:X}"),
115 XlsbError::BErr(t) => write!(f, "Unsupported BErr {t:X}"),
116 XlsbError::Ptg(t) => write!(f, "Unsupported Ptf {t:X}"),
117 XlsbError::CellError(t) => write!(f, "Unsupported Cell Error code {t:X}"),
118 XlsbError::WideStr { ws_len, buf_len } => write!(
119 f,
120 "Wide str length exceeds buffer length ({ws_len} > {buf_len})",
121 ),
122 XlsbError::Unrecognized { typ, val } => {
123 write!(f, "Unrecognized {typ}: {val}")
124 }
125 XlsbError::Password => write!(f, "Workbook is password protected"),
126 XlsbError::WorksheetNotFound(name) => write!(f, "Worksheet '{name}' not found"),
127 XlsbError::Encoding(e) => write!(f, "XML encoding error: {e}"),
128 }
129 }
130}
131
132impl std::error::Error for XlsbError {
133 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
134 match self {
135 XlsbError::Io(e) => Some(e),
136 XlsbError::Zip(e) => Some(e),
137 XlsbError::Xml(e) => Some(e),
138 XlsbError::Vba(e) => Some(e),
139 _ => None,
140 }
141 }
142}
143
144#[derive(Debug, Default)]
146#[non_exhaustive]
147struct XlsbOptions {
148 pub header_row: HeaderRow,
149}
150
151pub struct Xlsb<RS> {
153 zip: ZipArchive<RS>,
154 extern_sheets: Vec<String>,
155 sheets: Vec<(String, String)>,
156 strings: Vec<String>,
157 formats: Vec<CellFormat>,
159 is_1904: bool,
160 metadata: Metadata,
161 #[cfg(feature = "picture")]
162 pictures: Option<Vec<(String, Vec<u8>)>>,
163 options: XlsbOptions,
164}
165
166impl<RS: Read + Seek> Xlsb<RS> {
167 fn read_relationships(&mut self) -> Result<BTreeMap<Vec<u8>, String>, XlsbError> {
169 let mut relationships = BTreeMap::new();
170 match self.zip.by_name("xl/_rels/workbook.bin.rels") {
171 Ok(f) => {
172 let mut xml = XmlReader::from_reader(BufReader::new(f));
173 let config = xml.config_mut();
174 config.check_end_names = false;
175 config.trim_text(false);
176 config.check_comments = false;
177 config.expand_empty_elements = true;
178 let mut buf: Vec<u8> = Vec::with_capacity(64);
179
180 loop {
181 match xml.read_event_into(&mut buf) {
182 Ok(Event::Start(e)) if e.name() == QName(b"Relationship") => {
183 let mut id = None;
184 let mut target = None;
185 for a in e.attributes() {
186 match a? {
187 Attribute {
188 key: QName(b"Id"),
189 value: v,
190 } => {
191 id = Some(v.to_vec());
192 }
193 Attribute {
194 key: QName(b"Target"),
195 value: v,
196 } => {
197 target = Some(
198 xml.decoder()
199 .decode(&v)
200 .map_err(XlsbError::Encoding)?
201 .into_owned(),
202 );
203 }
204 _ => (),
205 }
206 }
207 if let (Some(id), Some(target)) = (id, target) {
208 relationships.insert(id, target);
209 }
210 }
211 Ok(Event::Eof) => break,
212 Err(e) => return Err(XlsbError::Xml(e)),
213 _ => (),
214 }
215 buf.clear();
216 }
217 }
218 Err(ZipError::FileNotFound) => (),
219 Err(e) => return Err(XlsbError::Zip(e)),
220 }
221 Ok(relationships)
222 }
223
224 fn read_styles(&mut self) -> Result<(), XlsbError> {
226 let mut iter = match RecordIter::from_zip(&mut self.zip, "xl/styles.bin") {
227 Ok(iter) => iter,
228 Err(_) => return Ok(()), };
230 let mut buf = Vec::with_capacity(1024);
231 let mut number_formats = BTreeMap::new();
232
233 loop {
234 match iter.read_type()? {
235 0x0267 => {
236 let _len = iter.fill_buffer(&mut buf)?;
238 let len = read_usize(&buf);
239
240 for _ in 0..len {
241 let _ = iter.next_skip_blocks(0x002C, &[], &mut buf)?; let fmt_code = read_u16(&buf);
243 let fmt_str = wide_str(&buf[2..], &mut 0)?;
244 number_formats
245 .insert(fmt_code, detect_custom_number_format(fmt_str.as_ref()));
246 }
247 }
248 0x0269 => {
249 let _len = iter.fill_buffer(&mut buf)?;
251 let len = read_usize(&buf);
252 for _ in 0..len {
253 let _ = iter.next_skip_blocks(0x002F, &[], &mut buf)?; let fmt_code = read_u16(&buf[2..4]);
255 match builtin_format_by_code(fmt_code) {
256 CellFormat::DateTime => self.formats.push(CellFormat::DateTime),
257 CellFormat::TimeDelta => self.formats.push(CellFormat::TimeDelta),
258 CellFormat::Other => {
259 self.formats.push(
260 number_formats
261 .get(&fmt_code)
262 .copied()
263 .unwrap_or(CellFormat::Other),
264 );
265 }
266 }
267 }
268 break;
270 }
271 _ => (),
272 }
273 buf.clear();
274 }
275
276 Ok(())
277 }
278
279 fn read_shared_strings(&mut self) -> Result<(), XlsbError> {
281 let mut iter = match RecordIter::from_zip(&mut self.zip, "xl/sharedStrings.bin") {
282 Ok(iter) => iter,
283 Err(_) => return Ok(()), };
285 let mut buf = Vec::with_capacity(1024);
286
287 let _ = iter.next_skip_blocks(0x009F, &[], &mut buf)?; let len = read_usize(&buf[4..8]);
289
290 for _ in 0..len {
292 let _ = iter.next_skip_blocks(
293 0x0013,
294 &[
295 (0x0023, Some(0x0024)), ],
297 &mut buf,
298 )?; self.strings.push(wide_str(&buf[1..], &mut 0)?.into_owned());
300 }
301 Ok(())
302 }
303
304 fn read_workbook(
306 &mut self,
307 relationships: &BTreeMap<Vec<u8>, String>,
308 ) -> Result<(), XlsbError> {
309 let mut iter = RecordIter::from_zip(&mut self.zip, "xl/workbook.bin")?;
310 let mut buf = Vec::with_capacity(1024);
311
312 loop {
313 match iter.read_type()? {
314 0x0099 => {
315 let _ = iter.fill_buffer(&mut buf)?;
316 self.is_1904 = &buf[0] & 0x1 != 0;
317 } 0x009C => {
319 let len = iter.fill_buffer(&mut buf)?;
321 let rel_len = read_u32(&buf[8..len]);
322 if rel_len != 0xFFFF_FFFF {
323 let rel_len = rel_len as usize * 2;
324 let relid = &buf[12..12 + rel_len];
325 let relid = UTF_16LE.decode(relid).0;
327 let path = format!("xl/{}", relationships[relid.as_bytes()]);
328 let visible = match read_u32(&buf) {
330 0 => SheetVisible::Visible,
331 1 => SheetVisible::Hidden,
332 2 => SheetVisible::VeryHidden,
333 v => {
334 return Err(XlsbError::Unrecognized {
335 typ: "BoundSheet8:hsState",
336 val: v.to_string(),
337 })
338 }
339 };
340 let typ = match path.split('/').nth(1) {
341 Some("worksheets") => SheetType::WorkSheet,
342 Some("chartsheets") => SheetType::ChartSheet,
343 Some("dialogsheets") => SheetType::DialogSheet,
344 _ => {
345 return Err(XlsbError::Unrecognized {
346 typ: "BoundSheet8:dt",
347 val: path.to_string(),
348 })
349 }
350 };
351 let name = wide_str(&buf[12 + rel_len..len], &mut 0)?;
352 self.metadata.sheets.push(Sheet {
353 name: name.to_string(),
354 typ,
355 visible,
356 });
357 self.sheets.push((name.into_owned(), path));
358 }
359 }
360 0x0090 => break, _ => (),
362 }
363 buf.clear();
364 }
365
366 let mut defined_names = Vec::new();
368 loop {
369 let typ = iter.read_type()?;
370 match typ {
371 0x016A => {
372 let _len = iter.fill_buffer(&mut buf)?;
374 let cxti = read_u32(&buf[..4]) as usize;
375 if cxti < 1_000_000 {
376 self.extern_sheets.reserve(cxti);
377 }
378 let sheets = &self.sheets;
379 let extern_sheets = buf[4..]
380 .chunks(12)
381 .map(|xti| {
382 match read_i32(&xti[4..8]) {
383 -2 => "#ThisWorkbook",
384 -1 => "#InvalidWorkSheet",
385 p if p >= 0 && (p as usize) < sheets.len() => &sheets[p as usize].0,
386 _ => "#Unknown",
387 }
388 .to_string()
389 })
390 .take(cxti)
391 .collect();
392 self.extern_sheets = extern_sheets;
393 }
394 0x0027 => {
395 let len = iter.fill_buffer(&mut buf)?;
397 let mut str_len = 0;
398 let name = wide_str(&buf[9..len], &mut str_len)?.into_owned();
399 let rgce_len = read_u32(&buf[9 + str_len..]) as usize;
400 let rgce = &buf[13 + str_len..13 + str_len + rgce_len];
401 let formula = parse_formula(rgce, &self.extern_sheets, &defined_names)?;
402 defined_names.push((name, formula));
403 }
404 0x009D | 0x0225 | 0x018D | 0x0180 | 0x009A | 0x0252 | 0x0229 | 0x009B | 0x0084 => {
405 self.metadata.names = defined_names;
407 return Ok(());
408 }
409 _ => debug!("Unsupported type {typ:X}"),
410 }
411 }
412 }
413
414 pub fn worksheet_cells_reader<'a>(
416 &'a mut self,
417 name: &str,
418 ) -> Result<XlsbCellsReader<'a, RS>, XlsbError> {
419 let path = match self.sheets.iter().find(|&(n, _)| n == name) {
420 Some((_, path)) => path.clone(),
421 None => return Err(XlsbError::WorksheetNotFound(name.into())),
422 };
423 let iter = RecordIter::from_zip(&mut self.zip, &path)?;
424 XlsbCellsReader::new(
425 iter,
426 &self.formats,
427 &self.strings,
428 &self.extern_sheets,
429 &self.metadata.names,
430 self.is_1904,
431 )
432 }
433
434 #[cfg(feature = "picture")]
435 fn read_pictures(&mut self) -> Result<(), XlsbError> {
436 let mut pics = Vec::new();
437 for i in 0..self.zip.len() {
438 let mut zfile = self.zip.by_index(i)?;
439 let zname = zfile.name();
440 if zname.starts_with("xl/media") {
441 if let Some(ext) = zname.split('.').next_back() {
442 if [
443 "emf", "wmf", "pict", "jpeg", "jpg", "png", "dib", "gif", "tiff", "eps",
444 "bmp", "wpg",
445 ]
446 .contains(&ext)
447 {
448 let ext = ext.to_string();
449 let mut buf: Vec<u8> = Vec::new();
450 zfile.read_to_end(&mut buf)?;
451 pics.push((ext, buf));
452 }
453 }
454 }
455 }
456 if !pics.is_empty() {
457 self.pictures = Some(pics);
458 }
459 Ok(())
460 }
461}
462
463impl<RS: Read + Seek> Reader<RS> for Xlsb<RS> {
464 type Error = XlsbError;
465
466 fn new(mut reader: RS) -> Result<Self, XlsbError> {
467 check_for_password_protected(&mut reader)?;
468
469 let mut xlsb = Xlsb {
470 zip: ZipArchive::new(reader)?,
471 sheets: Vec::new(),
472 strings: Vec::new(),
473 extern_sheets: Vec::new(),
474 formats: Vec::new(),
475 is_1904: false,
476 metadata: Metadata::default(),
477 #[cfg(feature = "picture")]
478 pictures: None,
479 options: XlsbOptions::default(),
480 };
481 xlsb.read_shared_strings()?;
482 xlsb.read_styles()?;
483 let relationships = xlsb.read_relationships()?;
484 xlsb.read_workbook(&relationships)?;
485 #[cfg(feature = "picture")]
486 xlsb.read_pictures()?;
487
488 Ok(xlsb)
489 }
490
491 fn with_header_row(&mut self, header_row: HeaderRow) -> &mut Self {
492 self.options.header_row = header_row;
493 self
494 }
495
496 fn vba_project(&mut self) -> Result<Option<VbaProject>, XlsbError> {
497 let Some(mut f) = self.zip.by_name("xl/vbaProject.bin").ok() else {
498 return Ok(None);
499 };
500 let len = f.size() as usize;
501 let vba = VbaProject::new(&mut f, len)?;
502 Ok(Some(vba))
503 }
504
505 fn metadata(&self) -> &Metadata {
506 &self.metadata
507 }
508
509 fn worksheet_range(&mut self, name: &str) -> Result<Range<Data>, XlsbError> {
511 let rge = self.worksheet_range_ref(name)?;
512 let inner = rge.inner.into_iter().map(|v| v.into()).collect();
513 Ok(Range {
514 start: rge.start,
515 end: rge.end,
516 inner,
517 })
518 }
519
520 fn worksheet_formula(&mut self, name: &str) -> Result<Range<String>, XlsbError> {
522 let mut cells_reader = self.worksheet_cells_reader(name)?;
523 let mut cells = Vec::with_capacity(cells_reader.dimensions().len().min(1_000_000) as _);
524 while let Some(cell) = cells_reader.next_formula()? {
525 if !cell.val.is_empty() {
526 cells.push(cell);
527 }
528 }
529 Ok(Range::from_sparse(cells))
530 }
531
532 fn worksheet_style(&mut self, _name: &str) -> Result<StyleRange, XlsbError> {
533 Ok(StyleRange::empty())
535 }
536
537 fn worksheet_layout(&mut self, _name: &str) -> Result<WorksheetLayout, XlsbError> {
538 Ok(WorksheetLayout::new())
540 }
541
542 fn worksheets(&mut self) -> Vec<(String, Range<Data>)> {
544 let sheets = self
545 .sheets
546 .iter()
547 .map(|(name, _)| name.clone())
548 .collect::<Vec<_>>();
549 sheets
550 .into_iter()
551 .filter_map(|name| {
552 let ws = self.worksheet_range(&name).ok()?;
553 Some((name, ws))
554 })
555 .collect()
556 }
557
558 #[cfg(feature = "picture")]
559 fn pictures(&self) -> Option<Vec<(String, Vec<u8>)>> {
560 self.pictures.to_owned()
561 }
562}
563
564impl<RS: Read + Seek> ReaderRef<RS> for Xlsb<RS> {
565 fn worksheet_range_ref<'a>(&'a mut self, name: &str) -> Result<Range<DataRef<'a>>, XlsbError> {
566 let header_row = self.options.header_row;
567 let mut cell_reader = self.worksheet_cells_reader(name)?;
568 let len = cell_reader.dimensions().len();
569 let mut cells = Vec::new();
570 if len < 100_000 {
571 cells.reserve(len as usize);
572 }
573
574 match header_row {
575 HeaderRow::FirstNonEmptyRow => {
576 loop {
578 match cell_reader.next_cell() {
579 Ok(Some(Cell {
580 val: DataRef::Empty,
581 ..
582 })) => (),
583 Ok(Some(cell)) => cells.push(cell),
584 Ok(None) => break,
585 Err(e) => return Err(e),
586 }
587 }
588 }
589 HeaderRow::Row(header_row_idx) => {
590 loop {
592 match cell_reader.next_cell() {
593 Ok(Some(Cell {
594 val: DataRef::Empty,
595 ..
596 })) => (),
597 Ok(Some(cell)) => {
598 if cell.pos.0 >= header_row_idx {
599 cells.push(cell);
600 }
601 }
602 Ok(None) => break,
603 Err(e) => return Err(e),
604 }
605 }
606
607 if cells.first().is_some_and(|c| c.pos.0 != header_row_idx) {
610 cells.insert(
611 0,
612 Cell {
613 pos: (
614 header_row_idx,
615 cells.first().expect("cells should not be empty").pos.1,
616 ),
617 val: DataRef::Empty,
618 style: None,
619 },
620 );
621 }
622 }
623 }
624
625 Ok(Range::from_sparse(cells))
626 }
627}
628
629pub(crate) struct RecordIter<'a, RS>
630where
631 RS: Read + Seek,
632{
633 b: [u8; 1],
634 r: BufReader<ZipFile<'a, RS>>,
635}
636
637impl<'a, RS> RecordIter<'a, RS>
638where
639 RS: Read + Seek,
640{
641 fn from_zip(zip: &'a mut ZipArchive<RS>, path: &str) -> Result<RecordIter<'a, RS>, XlsbError> {
642 let zip_path = crate::xlsx::path_to_zip_path(zip, path);
643
644 match zip.by_name(&zip_path) {
645 Ok(f) => Ok(RecordIter {
646 r: BufReader::new(f),
647 b: [0],
648 }),
649 Err(ZipError::FileNotFound) => Err(XlsbError::FileNotFound(path.into())),
650 Err(e) => Err(XlsbError::Zip(e)),
651 }
652 }
653
654 fn read_u8(&mut self) -> Result<u8, std::io::Error> {
655 self.r.read_exact(&mut self.b)?;
656 Ok(self.b[0])
657 }
658
659 fn read_type(&mut self) -> Result<u16, std::io::Error> {
661 let b = self.read_u8()?;
662 let typ = if (b & 0x80) == 0x80 {
663 (b & 0x7F) as u16 + (((self.read_u8()? & 0x7F) as u16) << 7)
664 } else {
665 b as u16
666 };
667 Ok(typ)
668 }
669
670 fn fill_buffer(&mut self, buf: &mut Vec<u8>) -> Result<usize, std::io::Error> {
671 let mut b = self.read_u8()?;
672 let mut len = (b & 0x7F) as usize;
673 for i in 1..4 {
674 if (b & 0x80) == 0 {
675 break;
676 }
677 b = self.read_u8()?;
678 len += ((b & 0x7F) as usize) << (7 * i);
679 }
680 if buf.len() < len {
681 *buf = vec![0; len];
682 }
683
684 self.r.read_exact(&mut buf[..len])?;
685 Ok(len)
686 }
687
688 fn next_skip_blocks(
690 &mut self,
691 record_type: u16,
692 bounds: &[(u16, Option<u16>)],
693 buf: &mut Vec<u8>,
694 ) -> Result<usize, XlsbError> {
695 loop {
696 let typ = self.read_type()?;
697 let len = self.fill_buffer(buf)?;
698 if typ == record_type {
699 return Ok(len);
700 }
701 if let Some(end) = bounds.iter().find(|b| b.0 == typ).and_then(|b| b.1) {
702 while self.read_type()? != end {
703 let _ = self.fill_buffer(buf)?;
704 }
705 let _ = self.fill_buffer(buf)?;
706 }
707 }
708 }
709}
710
711fn wide_str<'a>(buf: &'a [u8], str_len: &mut usize) -> Result<Cow<'a, str>, XlsbError> {
712 let len = read_u32(buf) as usize;
713 if buf.len() < 4 + len * 2 {
714 return Err(XlsbError::WideStr {
715 ws_len: 4 + len * 2,
716 buf_len: buf.len(),
717 });
718 }
719 *str_len = 4 + len * 2;
720 let s = &buf[4..*str_len];
721 Ok(UTF_16LE.decode(s).0)
722}
723
724fn parse_formula(
731 mut rgce: &[u8],
732 sheets: &[String],
733 names: &[(String, String)],
734) -> Result<String, XlsbError> {
735 if rgce.is_empty() {
736 return Ok(String::new());
737 }
738
739 let mut stack = Vec::new();
740 let mut formula = String::with_capacity(rgce.len());
741 while !rgce.is_empty() {
742 let ptg = rgce[0];
743 rgce = &rgce[1..];
744 match ptg {
745 0x3a | 0x5a | 0x7a => {
746 let ixti = read_u16(&rgce[0..2]);
748 stack.push(formula.len());
749 formula.push_str(&sheets[ixti as usize]);
750 formula.push('!');
751 formula.push('$');
753 push_column(read_u16(&rgce[6..8]) as u32, &mut formula);
754 formula.push('$');
755 formula.push_str(&format!("{}", read_u32(&rgce[2..6]) + 1));
756 rgce = &rgce[8..];
757 }
758 0x3b | 0x5b | 0x7b => {
759 let ixti = read_u16(&rgce[0..2]);
761 stack.push(formula.len());
762 formula.push_str(&sheets[ixti as usize]);
763 formula.push('!');
764 formula.push('$');
766 push_column(read_u16(&rgce[10..12]) as u32, &mut formula);
767 formula.push('$');
768 formula.push_str(&format!("{}", read_u32(&rgce[2..6]) + 1));
769 formula.push(':');
770 formula.push('$');
771 push_column(read_u16(&rgce[12..14]) as u32, &mut formula);
772 formula.push('$');
773 formula.push_str(&format!("{}", read_u32(&rgce[6..10]) + 1));
774 rgce = &rgce[14..];
775 }
776 0x3c | 0x5c | 0x7c => {
777 let ixti = read_u16(&rgce[0..2]);
779 stack.push(formula.len());
780 formula.push_str(&sheets[ixti as usize]);
781 formula.push('!');
782 formula.push_str("#REF!");
783 rgce = &rgce[8..];
784 }
785 0x3d | 0x5d | 0x7d => {
786 let ixti = read_u16(&rgce[0..2]);
788 stack.push(formula.len());
789 formula.push_str(&sheets[ixti as usize]);
790 formula.push('!');
791 formula.push_str("#REF!");
792 rgce = &rgce[14..];
793 }
794 0x01 => {
795 debug!("ignoring PtgExp array/shared formula");
797 stack.push(formula.len());
798 rgce = &rgce[4..];
799 }
800 0x03..=0x11 => {
801 let e2 = stack.pop().ok_or(XlsbError::StackLen)?;
803 let e2 = formula.split_off(e2);
804 let op = match ptg {
806 0x03 => "+",
807 0x04 => "-",
808 0x05 => "*",
809 0x06 => "/",
810 0x07 => "^",
811 0x08 => "&",
812 0x09 => "<",
813 0x0A => "<=",
814 0x0B => "=",
815 0x0C => ">",
816 0x0D => ">=",
817 0x0E => "<>",
818 0x0F => " ",
819 0x10 => ",",
820 0x11 => ":",
821 _ => unreachable!(),
822 };
823 formula.push_str(op);
824 formula.push_str(&e2);
825 }
826 0x12 => {
827 let e = stack.last().ok_or(XlsbError::StackLen)?;
828 formula.insert(*e, '+');
829 }
830 0x13 => {
831 let e = stack.last().ok_or(XlsbError::StackLen)?;
832 formula.insert(*e, '-');
833 }
834 0x14 => {
835 formula.push('%');
836 }
837 0x15 => {
838 let e = stack.last().ok_or(XlsbError::StackLen)?;
839 formula.insert(*e, '(');
840 formula.push(')');
841 }
842 0x16 => {
843 stack.push(formula.len());
844 }
845 0x17 => {
846 stack.push(formula.len());
847 formula.push('\"');
848 let cch = read_u16(&rgce[0..2]) as usize;
849 formula.push_str(&UTF_16LE.decode(&rgce[2..2 + 2 * cch]).0);
850 formula.push('\"');
851 rgce = &rgce[2 + 2 * cch..];
852 }
853 0x18 => {
854 stack.push(formula.len());
855 let eptg = rgce[0];
856 rgce = &rgce[1..];
857 match eptg {
858 0x19 => rgce = &rgce[12..],
859 0x1D => rgce = &rgce[4..],
860 e => return Err(XlsbError::Etpg(e)),
861 }
862 }
863 0x19 => {
864 let eptg = rgce[0];
865 rgce = &rgce[1..];
866 match eptg {
867 0x01 | 0x02 | 0x08 | 0x20 | 0x21 | 0x40 | 0x41 | 0x80 => rgce = &rgce[2..],
868 0x04 => rgce = &rgce[10..],
869 0x10 => {
870 rgce = &rgce[2..];
871 let e = stack.last().ok_or(XlsbError::StackLen)?;
872 let e = formula.split_off(*e);
873 formula.push_str("SUM(");
874 formula.push_str(&e);
875 formula.push(')');
876 }
877 e => return Err(XlsbError::Etpg(e)),
878 }
879 }
880 0x1C => {
881 stack.push(formula.len());
882 let err = rgce[0];
883 rgce = &rgce[1..];
884 match err {
885 0x00 => formula.push_str("#NULL!"),
886 0x07 => formula.push_str("#DIV/0!"),
887 0x0F => formula.push_str("#VALUE!"),
888 0x17 => formula.push_str("#REF!"),
889 0x1D => formula.push_str("#NAME?"),
890 0x24 => formula.push_str("#NUM!"),
891 0x2A => formula.push_str("#N/A"),
892 0x2B => formula.push_str("#GETTING_DATA"),
893 e => return Err(XlsbError::BErr(e)),
894 }
895 }
896 0x1D => {
897 stack.push(formula.len());
898 formula.push_str(if rgce[0] == 0 { "FALSE" } else { "TRUE" });
899 rgce = &rgce[1..];
900 }
901 0x1E => {
902 stack.push(formula.len());
903 formula.push_str(&format!("{}", read_u16(rgce)));
904 rgce = &rgce[2..];
905 }
906 0x1F => {
907 stack.push(formula.len());
908 formula.push_str(&format!("{}", read_f64(rgce)));
909 rgce = &rgce[8..];
910 }
911 0x20 | 0x40 | 0x60 => {
912 stack.push(formula.len());
914 rgce = &rgce[14..];
915 }
916 0x21 | 0x22 | 0x41 | 0x42 | 0x61 | 0x62 => {
917 let (iftab, argc) = match ptg {
918 0x22 | 0x42 | 0x62 => {
919 let iftab = read_u16(&rgce[1..]) as usize;
920 let argc = rgce[0] as usize;
921 rgce = &rgce[3..];
922 (iftab, argc)
923 }
924 _ => {
925 let iftab = read_u16(rgce) as usize;
926 if iftab > crate::utils::FTAB_LEN {
927 return Err(XlsbError::IfTab(iftab));
928 }
929 rgce = &rgce[2..];
930 let argc = crate::utils::FTAB_ARGC[iftab] as usize;
931 (iftab, argc)
932 }
933 };
934 if stack.len() < argc {
935 return Err(XlsbError::StackLen);
936 }
937 if argc > 0 {
938 let args_start = stack.len() - argc;
939 let mut args = stack.split_off(args_start);
940 let start = args[0];
941 for s in &mut args {
942 *s -= start;
943 }
944 let fargs = formula.split_off(start);
945 stack.push(formula.len());
946 args.push(fargs.len());
947 formula.push_str(crate::utils::FTAB[iftab]);
948 formula.push('(');
949 for w in args.windows(2) {
950 formula.push_str(&fargs[w[0]..w[1]]);
951 formula.push(',');
952 }
953 formula.pop();
954 formula.push(')');
955 } else {
956 stack.push(formula.len());
957 formula.push_str(crate::utils::FTAB[iftab]);
958 formula.push_str("()");
959 }
960 }
961 0x23 | 0x43 | 0x63 => {
962 let iname = read_u32(rgce) as usize - 1; stack.push(formula.len());
964 if let Some(name) = names.get(iname) {
965 formula.push_str(&name.0);
966 }
967 rgce = &rgce[4..];
968 }
969 0x24 | 0x44 | 0x64 => {
970 let row = read_u32(rgce) + 1;
971 let col = [rgce[4], rgce[5] & 0x3F];
972 let col = read_u16(&col);
973 stack.push(formula.len());
974 if rgce[5] & 0x80 != 0x80 {
975 formula.push('$');
976 }
977 push_column(col as u32, &mut formula);
978 if rgce[5] & 0x40 != 0x40 {
979 formula.push('$');
980 }
981 formula.push_str(&format!("{row}"));
982 rgce = &rgce[6..];
983 }
984 0x25 | 0x45 | 0x65 => {
985 stack.push(formula.len());
986 formula.push('$');
987 push_column(read_u16(&rgce[8..10]) as u32, &mut formula);
988 formula.push('$');
989 formula.push_str(&format!("{}", read_u32(&rgce[0..4]) + 1));
990 formula.push(':');
991 formula.push('$');
992 push_column(read_u16(&rgce[10..12]) as u32, &mut formula);
993 formula.push('$');
994 formula.push_str(&format!("{}", read_u32(&rgce[4..8]) + 1));
995 rgce = &rgce[12..];
996 }
997 0x2A | 0x4A | 0x6A => {
998 stack.push(formula.len());
999 formula.push_str("#REF!");
1000 rgce = &rgce[6..];
1001 }
1002 0x2B | 0x4B | 0x6B => {
1003 stack.push(formula.len());
1004 formula.push_str("#REF!");
1005 rgce = &rgce[12..];
1006 }
1007 0x29 | 0x49 | 0x69 => {
1008 let cce = read_u16(rgce) as usize;
1009 rgce = &rgce[2..];
1010 let f = parse_formula(&rgce[..cce], sheets, names)?;
1011 stack.push(formula.len());
1012 formula.push_str(&f);
1013 rgce = &rgce[cce..];
1014 }
1015 0x39 | 0x59 | 0x79 => {
1016 stack.push(formula.len());
1018 formula.push_str("EXTERNAL_WB_NAME");
1019 rgce = &rgce[6..];
1020 }
1021 _ => return Err(XlsbError::Ptg(ptg)),
1022 }
1023 }
1024
1025 if stack.len() == 1 {
1026 Ok(formula)
1027 } else {
1028 Err(XlsbError::StackLen)
1029 }
1030}
1031
1032fn cell_format<'a>(formats: &'a [CellFormat], buf: &[u8]) -> Option<&'a CellFormat> {
1033 let style_ref = u32::from_le_bytes([buf[4], buf[5], buf[6], 0]);
1037
1038 formats.get(style_ref as usize)
1039}
1040
1041fn check_for_password_protected<RS: Read + Seek>(reader: &mut RS) -> Result<(), XlsbError> {
1042 let offset_end = reader.seek(std::io::SeekFrom::End(0))? as usize;
1043 reader.seek(std::io::SeekFrom::Start(0))?;
1044
1045 if let Ok(cfb) = crate::cfb::Cfb::new(reader, offset_end) {
1046 if cfb.has_directory("EncryptedPackage") {
1047 return Err(XlsbError::Password);
1048 }
1049 }
1050
1051 Ok(())
1052}