1use std::cmp::min;
6use std::collections::BTreeMap;
7use std::fmt::{self, Write};
8use std::io::{Read, Seek, SeekFrom};
9
10use log::debug;
11
12use crate::cfb::{Cfb, XlsEncoding};
13use crate::formats::{
14 builtin_format_by_code, detect_custom_number_format, format_excel_f64, format_excel_i64,
15 CellFormat,
16};
17#[cfg(feature = "picture")]
18use crate::utils::read_usize;
19use crate::utils::{push_column, read_f64, read_i16, read_i32, read_u16, read_u32};
20use crate::vba::VbaProject;
21use crate::{
22 Cell, CellErrorType, Data, Dimensions, HeaderRow, Metadata, Range, Reader, Sheet, SheetType,
23 SheetVisible,
24};
25
26#[derive(Debug)]
27pub enum XlsError {
29 Io(std::io::Error),
31 Cfb(crate::cfb::CfbError),
33 Vba(crate::vba::VbaError),
35
36 StackLen,
38 Unrecognized {
40 typ: &'static str,
42 val: u8,
44 },
45 Password,
47 Len {
49 expected: usize,
51 found: usize,
53 typ: &'static str,
55 },
56 ContinueRecordTooShort,
58 EoStream(&'static str),
60
61 InvalidFormula {
63 stack_size: usize,
65 },
66 IfTab(usize),
68 Etpg(u8),
70 NoVba,
72
73 #[cfg(feature = "picture")]
75 #[cfg_attr(docsrs, doc(cfg(feature = "picture")))]
76 Art(&'static str),
77
78 WorksheetNotFound(String),
80 InvalidFormat {
82 ifmt: u16,
84 },
85}
86
87from_err!(std::io::Error, XlsError, Io);
88from_err!(crate::cfb::CfbError, XlsError, Cfb);
89from_err!(crate::vba::VbaError, XlsError, Vba);
90
91impl std::fmt::Display for XlsError {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 match self {
94 XlsError::Io(e) => write!(f, "I/O error: {e}"),
95 XlsError::Cfb(e) => write!(f, "Cfb error: {e}"),
96 XlsError::Vba(e) => write!(f, "Vba error: {e}"),
97 XlsError::StackLen => write!(f, "Invalid stack length"),
98 XlsError::Unrecognized { typ, val } => write!(f, "Unrecognized {typ}: 0x{val:0X}"),
99 XlsError::Password => write!(f, "Workbook is password protected"),
100 XlsError::Len {
101 expected,
102 found,
103 typ,
104 } => write!(
105 f,
106 "Invalid {typ} length, expected at least {expected}, found {found}",
107 ),
108 XlsError::ContinueRecordTooShort => write!(
109 f,
110 "Continued record too short while reading extended string"
111 ),
112 XlsError::EoStream(s) => write!(f, "End of stream '{s}'"),
113 XlsError::InvalidFormula { stack_size } => {
114 write!(f, "Invalid formula (stack size: {stack_size})")
115 }
116 XlsError::IfTab(iftab) => write!(f, "Invalid iftab {iftab:X}"),
117 XlsError::Etpg(etpg) => write!(f, "Invalid etpg {etpg:X}"),
118 XlsError::NoVba => write!(f, "No VBA project"),
119 #[cfg(feature = "picture")]
120 XlsError::Art(s) => write!(f, "Invalid art record '{s}'"),
121 XlsError::WorksheetNotFound(name) => write!(f, "Worksheet '{name}' not found"),
122 XlsError::InvalidFormat { ifmt } => write!(f, "Invalid ifmt value: '{ifmt}'"),
123 }
124 }
125}
126
127impl std::error::Error for XlsError {
128 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
129 match self {
130 XlsError::Io(e) => Some(e),
131 XlsError::Cfb(e) => Some(e),
132 XlsError::Vba(e) => Some(e),
133 _ => None,
134 }
135 }
136}
137
138#[derive(Debug, Clone, Default)]
140#[non_exhaustive]
141pub struct XlsOptions {
142 pub force_codepage: Option<u16>,
151 pub header_row: HeaderRow,
153}
154
155struct SheetData {
156 range: Range<Data>,
157 formula: Range<String>,
158 merge_cells: Vec<Dimensions>,
159}
160
161pub struct Xls<RS> {
163 sheets: BTreeMap<String, SheetData>,
164 metadata: Metadata,
165 cfb: Cfb,
166 reader: RS,
167 options: XlsOptions,
168 formats: Vec<CellFormat>,
169 is_1904: bool,
170 #[cfg(feature = "picture")]
171 pictures: Option<Vec<(String, Vec<u8>)>>,
172}
173
174fn cfb<RS: Seek + Read>(reader: &mut RS) -> Result<Cfb, XlsError> {
175 let offset_end = reader.seek(SeekFrom::End(0))? as usize;
176 reader.seek(SeekFrom::Start(0))?;
177 let cfb = Cfb::new(reader, offset_end)?;
178 Ok(cfb)
179}
180
181impl<RS: Read + Seek> Xls<RS> {
182 pub fn new_with_options(mut reader: RS, options: XlsOptions) -> Result<Self, XlsError> {
198 let cfb = cfb(&mut reader)?;
199
200 debug!("cfb loaded");
201
202 let mut xls = Xls {
203 sheets: BTreeMap::new(),
204 cfb,
205 reader,
206 metadata: Metadata::default(),
207 options,
208 is_1904: false,
209 formats: Vec::new(),
210 #[cfg(feature = "picture")]
211 pictures: None,
212 };
213
214 xls.parse_workbook()?;
215
216 debug!("xls parsed");
217
218 Ok(xls)
219 }
220
221 #[doc(hidden)]
223 #[deprecated(
224 since = "0.36.0",
225 note = "use `merge_cells_by_sheet_name()` or `merge_cells_by_sheet_id()` instead"
226 )]
227 pub fn worksheet_merge_cells(&self, name: &str) -> Option<Vec<Dimensions>> {
228 self.sheets.get(name).map(|r| r.merge_cells.clone())
229 }
230
231 #[doc(hidden)]
234 #[deprecated(
235 since = "0.36.0",
236 note = "use `merge_cells_by_sheet_name()` or `merge_cells_by_sheet_id()` instead"
237 )]
238 pub fn worksheet_merge_cells_at(&self, n: usize) -> Option<Vec<Dimensions>> {
239 let sheet = self.metadata().sheets.get(n)?;
240
241 #[allow(deprecated)]
242 self.worksheet_merge_cells(&sheet.name)
243 }
244
245 pub fn merge_cells_by_sheet_name(&self, name: &str) -> Result<Vec<Dimensions>, XlsError> {
306 self.sheets
307 .get(name)
308 .map(|r| r.merge_cells.clone())
309 .ok_or_else(|| XlsError::WorksheetNotFound(name.into()))
310 }
311
312 pub fn merge_cells_by_sheet_id(&self, id: usize) -> Result<Vec<Dimensions>, XlsError> {
365 let name = self
366 .metadata()
367 .sheets
368 .get(id)
369 .map(|sheet| sheet.name.clone())
370 .ok_or_else(|| XlsError::WorksheetNotFound(format!("Sheet index {id} out of range")))?;
371
372 self.merge_cells_by_sheet_name(&name)
373 }
374
375 pub fn has_1904_epoch(&self) -> bool {
393 self.is_1904
394 }
395}
396
397impl<RS: Read + Seek> Reader<RS> for Xls<RS> {
398 type Error = XlsError;
399
400 fn new(reader: RS) -> Result<Self, XlsError> {
401 Self::new_with_options(reader, XlsOptions::default())
402 }
403
404 fn with_header_row(&mut self, header_row: HeaderRow) -> &mut Self {
405 self.options.header_row = header_row;
406 self
407 }
408
409 fn vba_project(&mut self) -> Result<Option<VbaProject>, XlsError> {
410 if !self.cfb.has_directory("_VBA_PROJECT_CUR") {
412 return Ok(None);
413 }
414 let vba = VbaProject::from_cfb(&mut self.reader, &mut self.cfb)?;
415 Ok(Some(vba))
416 }
417
418 fn metadata(&self) -> &Metadata {
420 &self.metadata
421 }
422
423 fn worksheet_range(&mut self, name: &str) -> Result<Range<Data>, XlsError> {
424 let sheet = self
425 .sheets
426 .get(name)
427 .map(|r| r.range.clone())
428 .ok_or_else(|| XlsError::WorksheetNotFound(name.into()))?;
429
430 match self.options.header_row {
431 HeaderRow::FirstNonEmptyRow => Ok(sheet),
432 HeaderRow::Row(header_row_idx) => {
433 if let (Some(start), Some(end)) = (sheet.start(), sheet.end()) {
435 Ok(sheet.range((header_row_idx, start.1), end))
436 } else {
437 Ok(sheet)
438 }
439 }
440 }
441 }
442
443 fn worksheets(&mut self) -> Vec<(String, Range<Data>)> {
444 self.sheets
445 .iter()
446 .map(|(name, sheet)| (name.to_owned(), sheet.range.clone()))
447 .collect()
448 }
449
450 fn worksheet_formula(&mut self, name: &str) -> Result<Range<String>, XlsError> {
451 self.sheets
452 .get(name)
453 .ok_or_else(|| XlsError::WorksheetNotFound(name.into()))
454 .map(|r| r.formula.clone())
455 }
456
457 #[cfg(feature = "picture")]
458 fn pictures(&self) -> Option<Vec<(String, Vec<u8>)>> {
459 self.pictures.to_owned()
460 }
461}
462
463#[derive(Debug, Clone, Copy)]
464struct Xti {
465 _isup_book: u16,
466 itab_first: i16,
467 _itab_last: i16,
468}
469
470impl<RS: Read + Seek> Xls<RS> {
471 fn parse_workbook(&mut self) -> Result<(), XlsError> {
472 use super::cfb::CfbError::StreamNotFound;
473 let stream = match self
475 .cfb
476 .get_stream("Workbook", &mut self.reader)
477 .or_else(|_| self.cfb.get_stream("Book", &mut self.reader))
478 .or_else(|_| self.cfb.get_stream("WORKBOOK", &mut self.reader))
479 .or_else(|_| self.cfb.get_stream("BOOK", &mut self.reader))
480 {
481 Ok(s) => s,
482 Err(StreamNotFound(_)) => return Err(StreamNotFound("Workbook".to_string()).into()),
483 Err(e) => return Err(e.into()),
484 };
485
486 let mut sheet_names = Vec::new();
487 let mut strings = Vec::new();
488 let mut defined_names = Vec::new();
489 let mut xtis = Vec::new();
490 let mut formats = BTreeMap::new();
491 let mut xfs = Vec::new();
492 let mut biff = Biff::Biff8; let codepage = self.options.force_codepage.unwrap_or(1200);
494 let mut encoding = XlsEncoding::from_codepage(codepage)?;
495 #[cfg(feature = "picture")]
496 let mut draw_group: Vec<u8> = Vec::new();
497 {
498 let wb = &stream;
499 let records = RecordIter { stream: wb };
500 for record in records {
501 let mut r = record?;
504 match r.typ {
505 0x002F if read_u16(r.data) != 0 => return Err(XlsError::Password),
507 0x0042 if self.options.force_codepage.is_none() => {
509 encoding = XlsEncoding::from_codepage(read_u16(r.data))?;
510 }
511 0x013D => {
513 let sheet_len = r.data.len() / 2;
514 sheet_names.reserve(sheet_len);
515 self.metadata.sheets.reserve(sheet_len);
516 }
517 0x0022 if read_u16(r.data) == 1 => {
519 self.is_1904 = true;
520 }
521 0x041E => match parse_format(&mut r, &encoding, biff) {
523 Ok((idx, format)) => {
524 formats.insert(idx, format);
525 }
526 Err(e) => log::warn!("{e}"),
527 },
528 0x00E0 => {
530 xfs.push(parse_xf(&r)?);
531 }
532 0x0085 => {
534 let (pos, sheet) = parse_sheet_metadata(&mut r, &encoding, biff)?;
535 self.metadata.sheets.push(sheet.clone());
536 sheet_names.push((pos, sheet.name));
537 }
538 0x0809 => {
540 let bof = parse_bof(&mut r)?;
541 biff = bof.biff;
542 }
543 0x0018 => defined_names.push(parse_lbl(&r, &encoding, biff)?),
545 0x0017 => xtis.extend(parse_extern_sheet(&r, biff)),
547 0x00FC => strings = parse_sst(&mut r, &encoding)?,
549 #[cfg(feature = "picture")]
550 0x00EB => {
552 draw_group.extend(r.data);
553 draw_group.extend(r.cont.iter().flat_map(|v| *v));
554 }
555 0x000A => break,
557 _ => (),
558 }
559 }
560 }
561
562 if matches!(biff, Biff::Biff2 | Biff::Biff3 | Biff::Biff4 | Biff::Biff5) && xtis.is_empty()
565 {
566 xtis = (0..sheet_names.len())
567 .map(|i| Xti {
568 _isup_book: 0,
569 itab_first: i as i16,
570 _itab_last: i as i16,
571 })
572 .collect();
573 }
574 self.formats = xfs
575 .into_iter()
576 .map(|fmt| match formats.get(&fmt) {
577 Some(s) => *s,
578 _ => builtin_format_by_code(fmt),
579 })
580 .collect();
581
582 debug!("formats: {:?}", self.formats);
583
584 let defined_names = defined_names
585 .into_iter()
586 .map(|(name, (i, mut f))| {
587 if let Some(i) = i {
588 let sh = xtis
589 .get(i)
590 .and_then(|xti| sheet_names.get(xti.itab_first as usize))
591 .map_or("#REF", |sh| &sh.1);
592 f = format!("{sh}!{f}");
593 }
594 (name, f)
595 })
596 .collect::<Vec<_>>();
597
598 debug!("defined_names: {defined_names:?}");
599
600 let mut sheets = BTreeMap::new();
601 let fmla_sheet_names = sheet_names
602 .iter()
603 .map(|(_, n)| n.clone())
604 .collect::<Vec<_>>();
605 for (pos, name) in sheet_names {
606 let sh = &stream[pos..];
607 let records = RecordIter { stream: sh };
608 let mut cells = Vec::new();
609 let mut formulas = Vec::new();
610 let mut fmla_pos = (0, 0);
611 let mut merge_cells = Vec::new();
612 let mut seen_sheet_bof = false;
615 let mut substream_depth = 0usize;
616 for record in records {
617 let r = record?;
618 if substream_depth > 0 {
619 match r.typ {
620 0x0809 => substream_depth += 1,
621 0x000A => substream_depth -= 1,
622 _ => (),
623 }
624 continue;
625 }
626 match r.typ {
627 0x0809 => {
629 if seen_sheet_bof {
630 substream_depth += 1;
631 } else {
632 seen_sheet_bof = true;
633 }
634 }
635 0x0200 => {
637 let Dimensions { start, end } = parse_dimensions(r.data)?;
638 let rows = (end.0 - start.0 + 1) as usize;
639 let cols = (end.1 - start.1 + 1) as usize;
640 cells.reserve(rows.saturating_mul(cols));
641 }
642 0x0203 => cells.push(parse_number(r.data, &self.formats, self.is_1904)?), 0x0204 => cells.push(parse_label(r.data, &encoding, biff)?), 0x00D6 => cells.push(parse_label(r.data, &encoding, biff)?), 0x0205 => cells.push(parse_bool_err(r.data)?), 0x0207 => {
648 let val = Data::String(parse_string(r.data, &encoding, biff)?);
650 cells.push(Cell::new(fmla_pos, val));
651 }
652 0x027E => cells.push(parse_rk(r.data, &self.formats, self.is_1904)?), 0x00FD => cells.extend(parse_label_sst(r.data, &strings)?), 0x00BD => parse_mul_rk(r.data, &mut cells, &self.formats, self.is_1904)?, 0x00E5 => parse_merge_cells(r.data, &mut merge_cells)?, 0x000A => break, 0x0006 => {
658 if r.data.len() < 20 {
660 return Err(XlsError::Len {
661 expected: 20,
662 found: r.data.len(),
663 typ: "Formula",
664 });
665 }
666 let row = read_u16(r.data);
667 let col = read_u16(&r.data[2..]);
668 let format = self.formats.get(read_u16(&r.data[4..]) as usize);
669 fmla_pos = (row as u32, col as u32);
670 if let Some(val) =
671 parse_formula_value(&r.data[6..14], format, self.is_1904)?
672 {
673 cells.push(Cell::new(fmla_pos, val));
676 }
677 let fmla = parse_formula(
678 &r.data[20..],
679 &fmla_sheet_names,
680 &defined_names,
681 &xtis,
682 &encoding,
683 biff,
684 )
685 .unwrap_or_else(|e| {
686 debug!("{e}");
687 format!(
688 "Unrecognised formula \
689 for cell ({row}, {col}): {e:?}"
690 )
691 });
692 formulas.push(Cell::new(fmla_pos, fmla));
693 }
694 _ => (),
695 }
696 }
697 let range = Range::from_sparse(cells);
698 let formula = Range::from_sparse(formulas);
699 sheets.insert(
700 name,
701 SheetData {
702 range,
703 formula,
704 merge_cells,
705 },
706 );
707 }
708
709 self.sheets = sheets;
710 self.metadata.names = defined_names;
711
712 #[cfg(feature = "picture")]
713 if !draw_group.is_empty() {
714 let pics = parse_pictures(&draw_group)?;
715 if !pics.is_empty() {
716 self.pictures = Some(pics);
717 }
718 }
719
720 Ok(())
721 }
722}
723
724struct Bof {
726 biff: Biff,
728}
729
730#[derive(Clone, Copy, Debug, PartialEq, Eq)]
732enum Biff {
733 Biff2,
734 Biff3,
735 Biff4,
736 Biff5,
737 Biff8,
738 }
741
742fn parse_bof(r: &mut Record<'_>) -> Result<Bof, XlsError> {
744 let mut dt = 0;
745 let biff_version = read_u16(&r.data[..2]);
746
747 if r.data.len() >= 4 {
748 dt = read_u16(&r.data[2..]);
749 }
750
751 let biff = match biff_version {
752 0x0200 | 0x0002 | 0x0007 => Biff::Biff2,
753 0x0300 => Biff::Biff3,
754 0x0400 => Biff::Biff4,
755 0x0500 => Biff::Biff5,
756 0x0600 => Biff::Biff8,
757 0 => {
758 if dt == 0x1000 {
759 Biff::Biff5
760 } else {
761 Biff::Biff8
762 }
763 }
764 _ => Biff::Biff8,
765 };
766
767 Ok(Bof { biff })
768}
769
770fn parse_sheet_metadata(
772 r: &mut Record<'_>,
773 encoding: &XlsEncoding,
774 biff: Biff,
775) -> Result<(usize, Sheet), XlsError> {
776 let pos = read_u32(r.data) as usize;
777 let visible = match r.data[4] & 0b0011_1111 {
778 0x00 => SheetVisible::Visible,
779 0x01 => SheetVisible::Hidden,
780 0x02 => SheetVisible::VeryHidden,
781 e => {
782 return Err(XlsError::Unrecognized {
783 typ: "BoundSheet8:hsState",
784 val: e,
785 });
786 }
787 };
788 let typ = match r.data[5] {
789 0x00 => SheetType::WorkSheet,
790 0x01 => SheetType::MacroSheet,
791 0x02 => SheetType::ChartSheet,
792 0x06 => SheetType::Vba,
793 e => {
794 return Err(XlsError::Unrecognized {
795 typ: "BoundSheet8:dt",
796 val: e,
797 });
798 }
799 };
800 r.data = &r.data[6..];
801 let mut name = parse_short_string(r, encoding, biff)?;
802 name.retain(|c| c != '\0');
803 Ok((pos, Sheet { name, typ, visible }))
804}
805
806fn parse_lbl(
807 r: &Record<'_>,
808 encoding: &XlsEncoding,
809 biff: Biff,
810) -> Result<(String, (Option<usize>, String)), XlsError> {
811 let cch = r.data[3] as usize;
812 let cce = read_u16(&r.data[4..]) as usize;
813 let mut name = String::with_capacity(cch);
814 match biff {
815 Biff::Biff2 | Biff::Biff3 | Biff::Biff4 | Biff::Biff5 => {
816 encoding.decode_to(&r.data[14..], cch, &mut name, None);
818 }
819 Biff::Biff8 => {
820 read_unicode_string_no_cch(encoding, &r.data[14..], cch, &mut name)?;
821 }
822 }
823 let rgce = &r.data[r.data.len() - cce..];
824 let formula = parse_defined_names(rgce, biff)?;
825 Ok((name, formula))
826}
827
828fn parse_extern_sheet(r: &Record<'_>, biff: Biff) -> Vec<Xti> {
829 match biff {
830 Biff::Biff8 => {
831 let cxti = read_u16(r.data) as usize;
833 r.data[2..]
834 .as_chunks::<6>()
835 .0
836 .iter()
837 .take(cxti)
838 .map(|xti| Xti {
839 _isup_book: read_u16(&xti[..2]),
840 itab_first: read_i16(&xti[2..4]),
841 _itab_last: read_i16(&xti[4..]),
842 })
843 .collect()
844 }
845 Biff::Biff2 | Biff::Biff3 | Biff::Biff4 | Biff::Biff5 => Vec::new(),
848 }
849}
850
851fn parse_number(r: &[u8], formats: &[CellFormat], is_1904: bool) -> Result<Cell<Data>, XlsError> {
852 if r.len() < 14 {
853 return Err(XlsError::Len {
854 typ: "number",
855 expected: 14,
856 found: r.len(),
857 });
858 }
859 let row = read_u16(r) as u32;
860 let col = read_u16(&r[2..]) as u32;
861 let v = read_f64(&r[6..]);
862 let format = formats.get(read_u16(&r[4..]) as usize);
863
864 Ok(Cell::new((row, col), format_excel_f64(v, format, is_1904)))
865}
866
867fn parse_bool_err(r: &[u8]) -> Result<Cell<Data>, XlsError> {
868 if r.len() < 8 {
869 return Err(XlsError::Len {
870 typ: "BoolErr",
871 expected: 8,
872 found: r.len(),
873 });
874 }
875 let row = read_u16(r);
876 let col = read_u16(&r[2..]);
877 let pos = (row as u32, col as u32);
878 match r[7] {
879 0x00 => Ok(Cell::new(pos, Data::Bool(r[6] != 0))),
880 0x01 => Ok(Cell::new(pos, parse_err(r[6])?)),
881 e => Err(XlsError::Unrecognized {
882 typ: "fError",
883 val: e,
884 }),
885 }
886}
887
888fn parse_err(e: u8) -> Result<Data, XlsError> {
889 match e {
890 0x00 => Ok(Data::Error(CellErrorType::Null)),
891 0x07 => Ok(Data::Error(CellErrorType::Div0)),
892 0x0F => Ok(Data::Error(CellErrorType::Value)),
893 0x17 => Ok(Data::Error(CellErrorType::Ref)),
894 0x1D => Ok(Data::Error(CellErrorType::Name)),
895 0x24 => Ok(Data::Error(CellErrorType::Num)),
896 0x2A => Ok(Data::Error(CellErrorType::NA)),
897 0x2B => Ok(Data::Error(CellErrorType::GettingData)),
898 e => Err(XlsError::Unrecognized {
899 typ: "error",
900 val: e,
901 }),
902 }
903}
904
905fn parse_rk(r: &[u8], formats: &[CellFormat], is_1904: bool) -> Result<Cell<Data>, XlsError> {
906 if r.len() < 10 {
907 return Err(XlsError::Len {
908 typ: "rk",
909 expected: 10,
910 found: r.len(),
911 });
912 }
913 let row = read_u16(r);
914 let col = read_u16(&r[2..]);
915
916 Ok(Cell::new(
917 (row as u32, col as u32),
918 rk_num(&r[4..10], formats, is_1904),
919 ))
920}
921
922fn parse_merge_cells(r: &[u8], merge_cells: &mut Vec<Dimensions>) -> Result<(), XlsError> {
923 let count = read_u16(r);
924
925 for i in 0..count {
926 let offset: usize = (2 + i * 8).into();
927
928 let rf = read_u16(&r[offset..]);
929 let rl = read_u16(&r[offset + 2..]);
930 let cf = read_u16(&r[offset + 4..]);
931 let cl = read_u16(&r[offset + 6..]);
932
933 merge_cells.push(Dimensions {
934 start: (rf.into(), cf.into()),
935 end: (rl.into(), cl.into()),
936 });
937 }
938
939 Ok(())
940}
941
942fn parse_mul_rk(
943 r: &[u8],
944 cells: &mut Vec<Cell<Data>>,
945 formats: &[CellFormat],
946 is_1904: bool,
947) -> Result<(), XlsError> {
948 if r.len() < 6 {
949 return Err(XlsError::Len {
950 typ: "rk",
951 expected: 6,
952 found: r.len(),
953 });
954 }
955
956 let row = read_u16(r);
957 let col_first = read_u16(&r[2..]);
958 let col_last = read_u16(&r[r.len() - 2..]);
959
960 if r.len() != 6 + 6 * (col_last - col_first + 1) as usize {
961 return Err(XlsError::Len {
962 typ: "rk",
963 expected: 6 + 6 * (col_last - col_first + 1) as usize,
964 found: r.len(),
965 });
966 }
967
968 for (col, rk) in (col_first as u32..).zip(r[4..r.len() - 2].chunks(6)) {
969 cells.push(Cell::new((row as u32, col), rk_num(rk, formats, is_1904)));
970 }
971 Ok(())
972}
973
974fn rk_num(rk: &[u8], formats: &[CellFormat], is_1904: bool) -> Data {
975 let d100 = (rk[2] & 1) != 0;
976 let is_int = (rk[2] & 2) != 0;
977 let format = formats.get(read_u16(rk) as usize);
978
979 let mut v = [0u8; 8];
980 v[4..].copy_from_slice(&rk[2..]);
981 v[4] &= 0xFC;
982 if is_int {
983 let v = (read_i32(&v[4..8]) >> 2) as i64;
984 if d100 && v % 100 != 0 {
985 format_excel_f64(v as f64 / 100.0, format, is_1904)
986 } else {
987 format_excel_i64(if d100 { v / 100 } else { v }, format, is_1904)
988 }
989 } else {
990 let v = read_f64(&v);
991 format_excel_f64(if d100 { v / 100.0 } else { v }, format, is_1904)
992 }
993}
994
995fn parse_short_string(
997 r: &mut Record<'_>,
998 encoding: &XlsEncoding,
999 biff: Biff,
1000) -> Result<String, XlsError> {
1001 let biff8 = matches!(biff, Biff::Biff8);
1002 if r.data.is_empty() || r.data.len() < 2 && biff8 {
1003 return Err(XlsError::Len {
1004 typ: "short string",
1005 expected: if biff8 { 2 } else { 1 },
1006 found: r.data.len(),
1007 });
1008 }
1009
1010 let cch = r.data[0] as usize;
1011 r.data = &r.data[1..];
1012 let mut high_byte = None;
1013
1014 if biff8 {
1015 high_byte = Some(r.data[0] & 0x1 != 0);
1016 r.data = &r.data[1..];
1017 }
1018
1019 let mut s = String::with_capacity(cch);
1020 encoding.decode_to(r.data, cch, &mut s, high_byte);
1021 Ok(s)
1022}
1023
1024fn parse_string(r: &[u8], encoding: &XlsEncoding, biff: Biff) -> Result<String, XlsError> {
1026 let (mut high_byte, expected) = match biff {
1027 Biff::Biff2 | Biff::Biff3 | Biff::Biff4 | Biff::Biff5 => (None, 2),
1028 Biff::Biff8 => (Some(false), 3),
1029 };
1030 if r.len() < expected {
1031 if 2 == r.len() && read_u16(r) == 0 {
1032 return Ok(String::new());
1034 }
1035 return Err(XlsError::Len {
1036 typ: "string",
1037 expected,
1038 found: r.len(),
1039 });
1040 }
1041 high_byte = high_byte.map(|_| r[2] & 0x1 != 0);
1043
1044 let cch = read_u16(r) as usize;
1045 let mut s = String::with_capacity(cch);
1046 encoding.decode_to(&r[expected..], cch, &mut s, high_byte);
1047 Ok(s)
1048}
1049
1050fn parse_label(r: &[u8], encoding: &XlsEncoding, biff: Biff) -> Result<Cell<Data>, XlsError> {
1051 if r.len() < 6 {
1052 return Err(XlsError::Len {
1053 typ: "label",
1054 expected: 6,
1055 found: r.len(),
1056 });
1057 }
1058 let row = read_u16(r);
1059 let col = read_u16(&r[2..]);
1060 let _ixfe = read_u16(&r[4..]);
1061 Ok(Cell::new(
1062 (row as u32, col as u32),
1063 Data::String(parse_string(&r[6..], encoding, biff)?),
1064 ))
1065}
1066
1067fn parse_label_sst(r: &[u8], strings: &[String]) -> Result<Option<Cell<Data>>, XlsError> {
1068 if r.len() < 10 {
1069 return Err(XlsError::Len {
1070 typ: "label sst",
1071 expected: 10,
1072 found: r.len(),
1073 });
1074 }
1075 let row = read_u16(r);
1076 let col = read_u16(&r[2..]);
1077 let i = read_u32(&r[6..]) as usize;
1078 if let Some(s) = strings.get(i) {
1079 return Ok(Some(Cell::new(
1080 (row as u32, col as u32),
1081 Data::String(s.clone()),
1082 )));
1083 }
1084 Ok(None)
1085}
1086
1087fn parse_dimensions(r: &[u8]) -> Result<Dimensions, XlsError> {
1088 let (rf, rl, mut cf, cl) = match r.len() {
1089 10 => (
1090 read_u16(&r[0..2]) as u32,
1091 read_u16(&r[2..4]) as u32,
1092 read_u16(&r[4..6]) as u32,
1093 read_u16(&r[6..8]) as u32,
1094 ),
1095 14 | 16 => (
1098 read_u32(&r[0..4]),
1099 read_u32(&r[4..8]),
1100 read_u16(&r[8..10]) as u32,
1101 read_u16(&r[10..12]) as u32,
1102 ),
1103 _ => {
1104 return Err(XlsError::Len {
1105 typ: "dimensions",
1106 expected: 14,
1107 found: r.len(),
1108 });
1109 }
1110 };
1111 if 0xFF < cf || cl < cf {
1115 cf = 0;
1116 }
1117 if 1 <= rl && 1 <= cl {
1118 Ok(Dimensions {
1119 start: (rf, cf),
1120 end: (rl - 1, cl - 1),
1121 })
1122 } else {
1123 Ok(Dimensions {
1124 start: (rf, cf),
1125 end: (rf, cf),
1126 })
1127 }
1128}
1129
1130fn parse_sst(r: &mut Record<'_>, encoding: &XlsEncoding) -> Result<Vec<String>, XlsError> {
1134 if r.data.len() < 8 {
1135 return Err(XlsError::Len {
1136 typ: "sst",
1137 expected: 8,
1138 found: r.data.len(),
1139 });
1140 }
1141 let mut sst = vec![];
1142
1143 r.data = &r.data[8..];
1145
1146 while !r.data.is_empty() || r.continue_record() {
1147 sst.push(read_rich_extended_string(r, encoding)?);
1148 }
1149
1150 Ok(sst)
1151}
1152
1153fn parse_xf(r: &Record<'_>) -> Result<u16, XlsError> {
1157 if r.data.len() < 4 {
1158 return Err(XlsError::Len {
1159 typ: "xf",
1160 expected: 4,
1161 found: r.data.len(),
1162 });
1163 }
1164
1165 Ok(read_u16(&r.data[2..]))
1166}
1167
1168fn parse_format(
1172 r: &mut Record<'_>,
1173 encoding: &XlsEncoding,
1174 biff: Biff,
1175) -> Result<(u16, CellFormat), XlsError> {
1176 if r.data.len() < 2 {
1177 return Err(XlsError::Len {
1178 typ: "format",
1179 expected: 2,
1180 found: r.data.len(),
1181 });
1182 }
1183 let ifmt = read_u16(r.data);
1184 match ifmt {
1185 5..=8 | 23..=26 | 41..=44 | 63..=66 | 164..=382 => (),
1186 _ => return Err(XlsError::InvalidFormat { ifmt }),
1187 }
1188
1189 let s = parse_string(&r.data[2..], encoding, biff)?;
1190 Ok((ifmt, detect_custom_number_format(&s)))
1191}
1192
1193fn read_rich_extended_string(
1197 r: &mut Record<'_>,
1198 encoding: &XlsEncoding,
1199) -> Result<String, XlsError> {
1200 if r.data.is_empty() {
1201 return Ok(String::new());
1203 }
1204 if r.data.len() < 3 {
1205 return Err(XlsError::Len {
1206 typ: "rich extended string",
1207 expected: 3,
1208 found: r.data.len(),
1209 });
1210 }
1211
1212 let cch = read_u16(r.data) as usize;
1213 let flags = r.data[2];
1214
1215 r.data = &r.data[3..];
1216
1217 let high_byte = flags & 0x1 != 0;
1218
1219 let mut c_run = 0;
1221
1222 let mut cb_ext_rst = 0;
1224
1225 if flags & 0x8 != 0 {
1227 c_run = read_u16(r.data) as usize;
1228 r.data = &r.data[2..];
1229 }
1230
1231 if flags & 0x4 != 0 {
1233 cb_ext_rst = read_i32(r.data) as usize;
1234 r.data = &r.data[4..];
1235 }
1236
1237 let s = read_dbcs(encoding, cch, r, high_byte)?;
1239
1240 r.skip(c_run * 4)?;
1242
1243 r.skip(cb_ext_rst)?;
1245
1246 Ok(s)
1247}
1248
1249fn read_dbcs(
1250 encoding: &XlsEncoding,
1251 mut len: usize,
1252 r: &mut Record<'_>,
1253 mut high_byte: bool,
1254) -> Result<String, XlsError> {
1255 let mut s = String::with_capacity(len);
1256 while len > 0 {
1257 let (l, at) = encoding.decode_to(r.data, len, &mut s, Some(high_byte));
1258 r.data = &r.data[at..];
1259 len -= l;
1260 if len > 0 {
1261 if r.continue_record() {
1262 high_byte = r.data[0] & 0x1 != 0;
1263 r.data = &r.data[1..];
1264 } else {
1265 return Err(XlsError::EoStream("dbcs"));
1266 }
1267 }
1268 }
1269 Ok(s)
1270}
1271
1272fn read_unicode_string_no_cch(
1278 encoding: &XlsEncoding,
1279 buf: &[u8],
1280 len: usize,
1281 s: &mut String,
1282) -> Result<usize, XlsError> {
1283 let Some(&flags) = buf.first() else {
1284 return Err(XlsError::Len {
1285 expected: 1,
1286 found: 0,
1287 typ: "XLUnicodeStringNoCch",
1288 });
1289 };
1290 let high_byte = flags & 0x1 != 0;
1291 let consumed = 1 + if high_byte { 2 * len } else { len };
1292 if buf.len() < consumed {
1293 return Err(XlsError::Len {
1294 expected: consumed,
1295 found: buf.len(),
1296 typ: "XLUnicodeStringNoCch",
1297 });
1298 }
1299 encoding.decode_to(&buf[1..consumed], len, s, Some(high_byte));
1300 Ok(consumed)
1301}
1302
1303struct Record<'a> {
1304 typ: u16,
1305 data: &'a [u8],
1306 cont: Vec<&'a [u8]>,
1307}
1308
1309impl<'a> Record<'a> {
1310 fn continue_record(&mut self) -> bool {
1311 if self.cont.is_empty() {
1312 false
1313 } else {
1314 self.data = self.cont.remove(0);
1315 true
1316 }
1317 }
1318
1319 fn skip(&mut self, mut len: usize) -> Result<(), XlsError> {
1320 while len > 0 {
1321 if self.data.is_empty() && !self.continue_record() {
1322 return Err(XlsError::ContinueRecordTooShort);
1323 }
1324 let l = min(len, self.data.len());
1325 let (_, next) = self.data.split_at(l);
1326 self.data = next;
1327 len -= l;
1328 }
1329 Ok(())
1330 }
1331}
1332
1333impl fmt::Debug for Record<'_> {
1335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1336 writeln!(
1337 f,
1338 "\nRecord = 0x{:04X}, Length = 0x{:04X}, {}",
1339 self.typ,
1340 self.data.len(),
1341 self.data.len()
1342 )?;
1343
1344 let mut iter = self.data.chunks(16);
1345 for chunk in iter.by_ref() {
1346 for byte in chunk {
1347 write!(f, "{byte:02X} ")?;
1348 }
1349 writeln!(f)?;
1350 }
1351
1352 Ok(())
1353 }
1354}
1355
1356struct RecordIter<'a> {
1357 stream: &'a [u8],
1358}
1359
1360impl<'a> Iterator for RecordIter<'a> {
1361 type Item = Result<Record<'a>, XlsError>;
1362 fn next(&mut self) -> Option<Self::Item> {
1363 if self.stream.len() < 4 {
1364 return if self.stream.is_empty() {
1365 None
1366 } else {
1367 Some(Err(XlsError::EoStream("record type and length")))
1368 };
1369 }
1370 let t = read_u16(self.stream);
1371 let mut len = read_u16(&self.stream[2..]) as usize;
1372 if self.stream.len() < len + 4 {
1373 return Some(Err(XlsError::EoStream("record length")));
1374 }
1375 let (data, next) = self.stream.split_at(len + 4);
1376 self.stream = next;
1377 let d = &data[4..];
1378
1379 let mut cont = Vec::new();
1381 if next.len() > 4 && read_u16(next) == 0x003C {
1382 while self.stream.len() > 4 && read_u16(self.stream) == 0x003C {
1383 len = read_u16(&self.stream[2..]) as usize;
1384 if self.stream.len() < len + 4 {
1385 return Some(Err(XlsError::EoStream("continue record length")));
1386 }
1387 let sp = self.stream.split_at(len + 4);
1388 cont.push(&sp.0[4..]);
1389 self.stream = sp.1;
1390 }
1391 }
1392
1393 Some(Ok(Record {
1394 typ: t,
1395 data: d,
1396 cont,
1397 }))
1398 }
1399}
1400
1401fn write_absolute_cell_ref(f: &mut String, col: u32, row: u32) {
1403 f.push('$');
1404 push_column(col, f);
1405 write!(f, "${}", row + 1).unwrap();
1406}
1407
1408fn parse_defined_names(rgce: &[u8], biff: Biff) -> Result<(Option<usize>, String), XlsError> {
1412 if rgce.is_empty() {
1413 return Ok((None, "empty rgce".to_string()));
1415 }
1416 let ptg = rgce[0];
1417 let res = match ptg {
1418 0x3a | 0x5a | 0x7a => {
1419 let (sheet_idx, row, col) = match biff {
1421 Biff::Biff2 | Biff::Biff3 | Biff::Biff4 | Biff::Biff5 => {
1422 (
1424 read_u16(&rgce[11..13]) as usize,
1425 read_u16(&rgce[15..17]) as u32 & 0x3FFF,
1426 rgce[17] as u32,
1427 )
1428 }
1429 Biff::Biff8 => {
1430 (
1432 read_u16(&rgce[1..3]) as usize,
1433 read_u16(&rgce[3..5]) as u32,
1434 read_u16(&rgce[5..7]) as u32,
1435 )
1436 }
1437 };
1438 let mut f = String::new();
1440 write_absolute_cell_ref(&mut f, col, row);
1441 (Some(sheet_idx), f)
1442 }
1443 0x3b | 0x5b | 0x7b => {
1444 let (sheet_idx, row_first, row_last, col_first, col_last) = match biff {
1446 Biff::Biff2 | Biff::Biff3 | Biff::Biff4 | Biff::Biff5 => {
1447 (
1449 read_u16(&rgce[11..13]) as usize,
1450 read_u16(&rgce[15..17]) as u32 & 0x3FFF,
1451 read_u16(&rgce[17..19]) as u32 & 0x3FFF,
1452 rgce[19] as u32,
1453 rgce[20] as u32,
1454 )
1455 }
1456 Biff::Biff8 => {
1457 (
1459 read_u16(&rgce[1..3]) as usize,
1460 read_u16(&rgce[3..5]) as u32,
1461 read_u16(&rgce[5..7]) as u32,
1462 read_u16(&rgce[7..9]) as u32,
1463 read_u16(&rgce[9..11]) as u32,
1464 )
1465 }
1466 };
1467 let mut f = String::new();
1469 write_absolute_cell_ref(&mut f, col_first, row_first);
1470 f.push(':');
1471 write_absolute_cell_ref(&mut f, col_last, row_last);
1472 (Some(sheet_idx), f)
1473 }
1474 0x3c | 0x5c | 0x7c | 0x3d | 0x5d | 0x7d => {
1475 let sheet_idx = match biff {
1477 Biff::Biff8 => read_u16(&rgce[1..3]) as usize,
1478 Biff::Biff2 | Biff::Biff3 | Biff::Biff4 | Biff::Biff5 => {
1479 read_u16(&rgce[11..13]) as usize
1480 }
1481 };
1482 (Some(sheet_idx), "#REF!".to_string())
1483 }
1484 _ => (None, format!("Unsupported ptg: {ptg:x}")),
1485 };
1486 Ok(res)
1487}
1488
1489fn parse_formula(
1493 mut rgce: &[u8],
1494 sheets: &[String],
1495 names: &[(String, String)],
1496 xtis: &[Xti],
1497 encoding: &XlsEncoding,
1498 biff: Biff,
1499) -> Result<String, XlsError> {
1500 let is_pre_biff8 = matches!(biff, Biff::Biff2 | Biff::Biff3 | Biff::Biff4 | Biff::Biff5);
1501 let mut stack = Vec::new();
1502 let mut formula = String::with_capacity(rgce.len());
1503 let cce = read_u16(rgce) as usize;
1504 rgce = &rgce[2..2 + cce];
1505 while !rgce.is_empty() {
1506 let ptg = rgce[0];
1507 rgce = &rgce[1..];
1508 match ptg {
1509 0x3a | 0x5a | 0x7a => {
1510 let ixti = read_u16(&rgce[0..2]);
1512 let rowu = read_u16(&rgce[2..]);
1513 let colu = read_u16(&rgce[4..]);
1514 let sh = xtis
1515 .get(ixti as usize)
1516 .and_then(|xti| sheets.get(xti.itab_first as usize))
1517 .map_or("#REF", |sh| sh);
1518 stack.push(formula.len());
1519 formula.push_str(sh);
1520 formula.push('!');
1521 let col = colu << 2; if colu & 2 != 0 {
1523 formula.push('$');
1524 }
1525 push_column(col as u32, &mut formula);
1526 if colu & 1 != 0 {
1527 formula.push('$');
1528 }
1529 write!(&mut formula, "{}", rowu + 1).unwrap();
1530 rgce = &rgce[6..];
1531 }
1532 0x3b | 0x5b | 0x7b => {
1533 let ixti = read_u16(&rgce[0..2]);
1535 stack.push(formula.len());
1536 formula.push_str(sheets.get(ixti as usize).map_or("#REF", |s| &**s));
1537 formula.push('!');
1538 formula.push('$');
1540 push_column(read_u16(&rgce[6..8]) as u32, &mut formula);
1541 write!(&mut formula, "${}:$", read_u16(&rgce[2..4]) as u32 + 1).unwrap();
1542 push_column(read_u16(&rgce[8..10]) as u32, &mut formula);
1543 write!(&mut formula, "${}", read_u16(&rgce[4..6]) as u32 + 1).unwrap();
1544 rgce = &rgce[10..];
1545 }
1546 0x3c | 0x5c | 0x7c => {
1547 let ixti = read_u16(&rgce[0..2]);
1549 stack.push(formula.len());
1550 formula.push_str(sheets.get(ixti as usize).map_or("#REF", |s| &**s));
1551 formula.push('!');
1552 formula.push_str("#REF!");
1553 rgce = &rgce[6..];
1554 }
1555 0x3d | 0x5d | 0x7d => {
1556 let ixti = read_u16(&rgce[0..2]);
1558 stack.push(formula.len());
1559 formula.push_str(sheets.get(ixti as usize).map_or("#REF", |s| &**s));
1560 formula.push('!');
1561 formula.push_str("#REF!");
1562 rgce = &rgce[10..];
1563 }
1564 0x01 => {
1565 debug!("ignoring PtgExp array/shared formula");
1567 stack.push(formula.len());
1568 if rgce.len() < 4 {
1571 return Err(XlsError::Len {
1572 expected: 4,
1573 found: rgce.len(),
1574 typ: "PtgExp",
1575 });
1576 }
1577 rgce = &rgce[4..];
1578 }
1579 0x03..=0x11 => {
1580 let e2 = stack.pop().ok_or(XlsError::StackLen)?;
1582 let op = match ptg {
1584 0x03 => "+",
1585 0x04 => "-",
1586 0x05 => "*",
1587 0x06 => "/",
1588 0x07 => "^",
1589 0x08 => "&",
1590 0x09 => "<",
1591 0x0A => "<=",
1592 0x0B => "=",
1593 0x0C => ">",
1594 0x0D => ">=",
1595 0x0E => "<>",
1596 0x0F => " ",
1597 0x10 => ",",
1598 0x11 => ":",
1599 _ => unreachable!(),
1600 };
1601 let e2 = formula.split_off(e2);
1602 write!(&mut formula, "{op}{e2}").unwrap();
1603 }
1604 0x12 => {
1605 let e = stack.last().ok_or(XlsError::StackLen)?;
1606 formula.insert(*e, '+');
1607 }
1608 0x13 => {
1609 let e = stack.last().ok_or(XlsError::StackLen)?;
1610 formula.insert(*e, '-');
1611 }
1612 0x14 => {
1613 formula.push('%');
1614 }
1615 0x15 => {
1616 let e = stack.last().ok_or(XlsError::StackLen)?;
1617 formula.insert(*e, '(');
1618 formula.push(')');
1619 }
1620 0x16 => {
1621 stack.push(formula.len());
1622 }
1623 0x17 => {
1624 stack.push(formula.len());
1625 formula.push('\"');
1626 let Some(&cch) = rgce.first() else {
1628 return Err(XlsError::Len {
1629 expected: 1,
1630 found: 0,
1631 typ: "PtgStr",
1632 });
1633 };
1634 let consumed =
1635 read_unicode_string_no_cch(encoding, &rgce[1..], cch as usize, &mut formula)?;
1636 formula.push('\"');
1637 rgce = &rgce[1 + consumed..];
1638 }
1639 0x18 => {
1640 rgce = &rgce[5..];
1641 }
1642 0x19 => {
1643 let etpg = rgce[0];
1644 rgce = &rgce[1..];
1645 match etpg {
1646 0x01 | 0x02 | 0x08 | 0x20 | 0x21 => rgce = &rgce[2..],
1647 0x04 => {
1648 let n = read_u16(&rgce[..2]) as usize + 1;
1650 rgce = &rgce[2 + 2 * n..]; }
1652 0x10 => {
1653 rgce = &rgce[2..];
1654 let e = *stack.last().ok_or(XlsError::StackLen)?;
1655 let e = formula.split_off(e);
1656 write!(&mut formula, "SUM({e})").unwrap();
1657 }
1658 0x40 | 0x41 => {
1659 let e = *stack.last().ok_or(XlsError::StackLen)?;
1661 let space = match rgce[0] {
1662 0x00 | 0x02 | 0x04 | 0x06 => ' ',
1663 0x01 | 0x03 | 0x05 => '\r',
1664 val => {
1665 return Err(XlsError::Unrecognized {
1666 typ: "PtgAttrSpaceType",
1667 val,
1668 });
1669 }
1670 };
1671 let cch = rgce[1];
1672 for _ in 0..cch {
1673 formula.insert(e, space);
1674 }
1675 rgce = &rgce[2..];
1676 }
1677 e => return Err(XlsError::Etpg(e)),
1678 }
1679 }
1680 0x1C => {
1681 stack.push(formula.len());
1682 let err = rgce[0];
1683 rgce = &rgce[1..];
1684 match err {
1685 0x00 => formula.push_str("#NULL!"),
1686 0x07 => formula.push_str("#DIV/0!"),
1687 0x0F => formula.push_str("#VALUE!"),
1688 0x17 => formula.push_str("#REF!"),
1689 0x1D => formula.push_str("#NAME?"),
1690 0x24 => formula.push_str("#NUM!"),
1691 0x2A => formula.push_str("#N/A"),
1692 0x2B => formula.push_str("#GETTING_DATA"),
1693 e => {
1694 return Err(XlsError::Unrecognized {
1695 typ: "BErr",
1696 val: e,
1697 });
1698 }
1699 }
1700 }
1701 0x1D => {
1702 stack.push(formula.len());
1703 formula.push_str(if rgce[0] == 0 { "FALSE" } else { "TRUE" });
1704 rgce = &rgce[1..];
1705 }
1706 0x1E => {
1707 stack.push(formula.len());
1708 write!(&mut formula, "{}", read_u16(rgce)).unwrap();
1709 rgce = &rgce[2..];
1710 }
1711 0x1F => {
1712 stack.push(formula.len());
1713 write!(&mut formula, "{}", read_f64(rgce)).unwrap();
1714 rgce = &rgce[8..];
1715 }
1716 0x20 | 0x40 | 0x60 => {
1717 stack.push(formula.len());
1719 formula.push_str("{PtgArray}");
1720 rgce = &rgce[7..];
1721 }
1722 0x21 | 0x22 | 0x41 | 0x42 | 0x61 | 0x62 => {
1723 let (iftab, argc) = match ptg {
1724 0x22 | 0x42 | 0x62 => {
1725 let iftab = read_u16(&rgce[1..]) as usize;
1726 let argc = rgce[0] as usize;
1727 rgce = &rgce[3..];
1728 (iftab, argc)
1729 }
1730 _ => {
1731 let iftab = read_u16(rgce) as usize;
1732 if iftab >= crate::utils::FTAB_LEN {
1733 return Err(XlsError::IfTab(iftab));
1734 }
1735 rgce = &rgce[2..];
1736 let argc = crate::utils::FTAB_ARGC[iftab] as usize;
1737 (iftab, argc)
1738 }
1739 };
1740 if stack.len() < argc {
1741 return Err(XlsError::StackLen);
1742 }
1743 if argc > 0 {
1744 let args_start = stack.len() - argc;
1745 let mut args = stack.split_off(args_start);
1746 let start = args[0];
1747 for s in &mut args {
1748 *s -= start;
1749 }
1750 let fargs = formula.split_off(start);
1751 stack.push(formula.len());
1752 args.push(fargs.len());
1753 formula.push_str(
1754 crate::utils::FTAB
1755 .get(iftab)
1756 .ok_or(XlsError::IfTab(iftab))?,
1757 );
1758 formula.push('(');
1759 for w in args.windows(2) {
1760 formula.push_str(&fargs[w[0]..w[1]]);
1761 formula.push(',');
1762 }
1763 formula.pop();
1764 formula.push(')');
1765 } else {
1766 stack.push(formula.len());
1767 formula.push_str(
1768 crate::utils::FTAB
1769 .get(iftab)
1770 .ok_or(XlsError::IfTab(iftab))?,
1771 );
1772 formula.push_str("()");
1773 }
1774 }
1775 0x23 | 0x43 | 0x63 => {
1776 let iname = read_u32(rgce) as usize - 1; stack.push(formula.len());
1778 formula.push_str(names.get(iname).map_or("#REF!", |n| &*n.0));
1779 rgce = &rgce[4..];
1780 }
1781 0x24 | 0x44 | 0x64 => {
1782 stack.push(formula.len());
1783 if is_pre_biff8 {
1784 let rw_raw = read_u16(rgce);
1786 let row = (rw_raw & 0x3FFF) + 1;
1787 let col = rgce[2];
1788 if rw_raw & 0x4000 == 0 {
1789 formula.push('$');
1790 }
1791 push_column(col as u32, &mut formula);
1792 if rw_raw & 0x8000 == 0 {
1793 formula.push('$');
1794 }
1795 formula.push_str(&format!("{row}"));
1796 rgce = &rgce[3..];
1797 } else {
1798 let row = read_u16(rgce) + 1;
1799 let col = read_u16(&[rgce[2], rgce[3] & 0x3F]);
1800 if rgce[3] & 0x80 != 0x80 {
1801 formula.push('$');
1802 }
1803 push_column(col as u32, &mut formula);
1804 if rgce[3] & 0x40 != 0x40 {
1805 formula.push('$');
1806 }
1807 formula.push_str(&format!("{row}"));
1808 rgce = &rgce[4..];
1809 }
1810 }
1811 0x25 | 0x45 | 0x65 => {
1812 stack.push(formula.len());
1813 if is_pre_biff8 {
1814 let row_first = (read_u16(&rgce[0..2]) & 0x3FFF) as u32 + 1;
1816 let row_last = (read_u16(&rgce[2..4]) & 0x3FFF) as u32 + 1;
1817 let col_first = rgce[4] as u32;
1818 let col_last = rgce[5] as u32;
1819 formula.push('$');
1820 push_column(col_first, &mut formula);
1821 write!(&mut formula, "${row_first}:$").unwrap();
1822 push_column(col_last, &mut formula);
1823 write!(&mut formula, "${row_last}").unwrap();
1824 rgce = &rgce[6..];
1825 } else {
1826 let col_first = read_u16(&[rgce[4], rgce[5] & 0x3F]);
1828 let col_last = read_u16(&[rgce[6], rgce[7] & 0x3F]);
1829 let row_first = read_u16(&rgce[0..2]) as u32 + 1;
1830 let row_last = read_u16(&rgce[2..4]) as u32 + 1;
1831 if rgce[5] & 0x80 != 0x80 {
1832 formula.push('$');
1833 }
1834 push_column(col_first as u32, &mut formula);
1835 if rgce[5] & 0x40 != 0x40 {
1836 formula.push('$');
1837 }
1838 write!(&mut formula, "{row_first}:").unwrap();
1839 if rgce[7] & 0x80 != 0x80 {
1840 formula.push('$');
1841 }
1842 push_column(col_last as u32, &mut formula);
1843 if rgce[7] & 0x40 != 0x40 {
1844 formula.push('$');
1845 }
1846 write!(&mut formula, "{row_last}").unwrap();
1847 rgce = &rgce[8..];
1848 }
1849 }
1850 0x2A | 0x4A | 0x6A => {
1851 stack.push(formula.len());
1852 formula.push_str("#REF!");
1853 rgce = &rgce[if is_pre_biff8 { 3 } else { 4 }..];
1854 }
1855 0x2B | 0x4B | 0x6B => {
1856 stack.push(formula.len());
1857 formula.push_str("#REF!");
1858 rgce = &rgce[if is_pre_biff8 { 6 } else { 8 }..];
1859 }
1860 0x39 | 0x59 => {
1861 stack.push(formula.len());
1863 formula.push_str("[PtgNameX]");
1864 rgce = &rgce[6..];
1865 }
1866 _ => {
1867 return Err(XlsError::Unrecognized {
1868 typ: "ptg",
1869 val: ptg,
1870 });
1871 }
1872 }
1873 }
1874 if stack.len() == 1 {
1875 Ok(formula)
1876 } else {
1877 Err(XlsError::InvalidFormula {
1878 stack_size: stack.len(),
1879 })
1880 }
1881}
1882
1883fn parse_formula_value(
1885 r: &[u8],
1886 format: Option<&CellFormat>,
1887 is_1904: bool,
1888) -> Result<Option<Data>, XlsError> {
1889 match *r {
1890 [0x00, .., 0xFF, 0xFF] => Ok(None),
1892 [0x01, _, b, .., 0xFF, 0xFF] => Ok(Some(Data::Bool(b != 0))),
1893 [0x02, _, e, .., 0xFF, 0xFF] => parse_err(e).map(Some),
1894 [0x03, _, .., 0xFF, 0xFF] => Ok(Some(Data::String("".to_string()))),
1896 [e, .., 0xFF, 0xFF] => Err(XlsError::Unrecognized {
1897 typ: "error",
1898 val: e,
1899 }),
1900 _ => Ok(Some(format_excel_f64(read_f64(r), format, is_1904))),
1901 }
1902}
1903
1904#[cfg(feature = "picture")]
1906struct ArtRecord<'a> {
1907 instance: u16,
1908 typ: u16,
1909 data: &'a [u8],
1910}
1911
1912#[cfg(feature = "picture")]
1913struct ArtRecordIter<'a> {
1914 stream: &'a [u8],
1915}
1916
1917#[cfg(feature = "picture")]
1918impl<'a> Iterator for ArtRecordIter<'a> {
1919 type Item = Result<ArtRecord<'a>, XlsError>;
1920 fn next(&mut self) -> Option<Self::Item> {
1921 if self.stream.len() < 8 {
1922 return if self.stream.is_empty() {
1923 None
1924 } else {
1925 Some(Err(XlsError::EoStream("art record header")))
1926 };
1927 }
1928 let ver_ins = read_u16(self.stream);
1929 let instance = ver_ins >> 4;
1930 let typ = read_u16(&self.stream[2..]);
1931 if typ < 0xF000 {
1932 return Some(Err(XlsError::Art("type range 0xF000 - 0xFFFF")));
1933 }
1934 let len = read_usize(&self.stream[4..]);
1935 if self.stream.len() < len + 8 {
1936 return Some(Err(XlsError::EoStream("art record length")));
1937 }
1938 let (d, next) = self.stream.split_at(len + 8);
1939 self.stream = next;
1940 let data = &d[8..];
1941
1942 Some(Ok(ArtRecord {
1943 instance,
1944 typ,
1945 data,
1946 }))
1947 }
1948}
1949
1950#[cfg(feature = "picture")]
1952fn parse_pictures(stream: &[u8]) -> Result<Vec<(String, Vec<u8>)>, XlsError> {
1953 let mut pics = Vec::new();
1954 let records = ArtRecordIter { stream };
1955 for record in records {
1956 let r = record?;
1957 match r.typ {
1958 0xF000 | 0xF001 => pics.extend(parse_pictures(r.data)?),
1961 0xF007 => {
1963 let skip = 36 + r.data[33] as usize;
1964 pics.extend(parse_pictures(&r.data[skip..])?);
1965 }
1966 0xF01A | 0xF01B | 0xF01C | 0xF01D | 0xF01E | 0xF01F | 0xF029 | 0xF02A => {
1968 let ext_skip = match r.typ {
1969 0xF01A => {
1971 let skip = match r.instance {
1972 0x3D4 => 50usize,
1973 0x3D5 => 66,
1974 _ => unreachable!(),
1975 };
1976 Ok(("emf", skip))
1977 }
1978 0xF01B => {
1980 let skip = match r.instance {
1981 0x216 => 50usize,
1982 0x217 => 66,
1983 _ => unreachable!(),
1984 };
1985 Ok(("wmf", skip))
1986 }
1987 0xF01C => {
1989 let skip = match r.instance {
1990 0x542 => 50usize,
1991 0x543 => 66,
1992 _ => unreachable!(),
1993 };
1994 Ok(("pict", skip))
1995 }
1996 0xF01D | 0xF02A => {
1998 let skip = match r.instance {
1999 0x46A | 0x6E2 => 17usize,
2000 0x46B | 0x6E3 => 33,
2001 _ => unreachable!(),
2002 };
2003 Ok(("jpg", skip))
2004 }
2005 0xF01E => {
2007 let skip = match r.instance {
2008 0x6E0 => 17usize,
2009 0x6E1 => 33,
2010 _ => unreachable!(),
2011 };
2012 Ok(("png", skip))
2013 }
2014 0xF01F => {
2016 let skip = match r.instance {
2017 0x7A8 => 17usize,
2018 0x7A9 => 33,
2019 _ => unreachable!(),
2020 };
2021 Ok(("dib", skip))
2022 }
2023 0xF029 => {
2025 let skip = match r.instance {
2026 0x6E4 => 17usize,
2027 0x6E5 => 33,
2028 _ => unreachable!(),
2029 };
2030 Ok(("tiff", skip))
2031 }
2032 _ => Err(XlsError::Art("picture type not support")),
2033 };
2034 let ext_skip = ext_skip?;
2035 pics.push((ext_skip.0.to_string(), Vec::from(&r.data[ext_skip.1..])));
2036 }
2037 _ => {}
2038 }
2039 }
2040 Ok(pics)
2041}
2042
2043#[cfg(test)]
2044mod tests {
2045 use super::*;
2046
2047 #[test]
2048 fn test_parse_string() {
2049 let enc = XlsEncoding::from_codepage(1252).unwrap();
2050 parse_string(&[0, 1], &enc, Biff::Biff8).unwrap_err();
2051 }
2052}