1use std::borrow::Cow;
2use std::cmp::min;
3use std::collections::BTreeMap;
4use std::fmt::Write;
5use std::io::{Read, Seek, SeekFrom};
6use std::marker::PhantomData;
7
8use log::debug;
9
10use crate::cfb::{Cfb, XlsEncoding};
11use crate::formats::{
12 builtin_format_by_code, detect_custom_number_format, format_excel_f64, format_excel_i64,
13 CellFormat,
14};
15#[cfg(feature = "picture")]
16use crate::utils::read_usize;
17use crate::utils::{push_column, read_f64, read_i16, read_i32, read_u16, read_u32};
18use crate::vba::VbaProject;
19use crate::{
20 Cell, CellErrorType, Data, Dimensions, HeaderRow, Metadata, Range, Reader, Sheet, SheetType,
21 SheetVisible,
22};
23
24#[derive(Debug)]
25pub enum XlsError {
27 Io(std::io::Error),
29 Cfb(crate::cfb::CfbError),
31 Vba(crate::vba::VbaError),
33
34 StackLen,
36 Unrecognized {
38 typ: &'static str,
40 val: u8,
42 },
43 Password,
45 Len {
47 expected: usize,
49 found: usize,
51 typ: &'static str,
53 },
54 ContinueRecordTooShort,
56 EoStream(&'static str),
58
59 InvalidFormula {
61 stack_size: usize,
63 },
64 IfTab(usize),
66 Etpg(u8),
68 NoVba,
70 #[cfg(feature = "picture")]
72 Art(&'static str),
73 WorksheetNotFound(String),
75}
76
77from_err!(std::io::Error, XlsError, Io);
78from_err!(crate::cfb::CfbError, XlsError, Cfb);
79from_err!(crate::vba::VbaError, XlsError, Vba);
80
81impl std::fmt::Display for XlsError {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 match self {
84 XlsError::Io(e) => write!(f, "I/O error: {e}"),
85 XlsError::Cfb(e) => write!(f, "Cfb error: {e}"),
86 XlsError::Vba(e) => write!(f, "Vba error: {e}"),
87 XlsError::StackLen => write!(f, "Invalid stack length"),
88 XlsError::Unrecognized { typ, val } => write!(f, "Unrecognized {typ}: 0x{val:0X}"),
89 XlsError::Password => write!(f, "Workbook is password protected"),
90 XlsError::Len {
91 expected,
92 found,
93 typ,
94 } => write!(
95 f,
96 "Invalid {typ} length, expected {expected} maximum, found {found}",
97 ),
98 XlsError::ContinueRecordTooShort => write!(
99 f,
100 "Continued record too short while reading extended string"
101 ),
102 XlsError::EoStream(s) => write!(f, "End of stream '{s}'"),
103 XlsError::InvalidFormula { stack_size } => {
104 write!(f, "Invalid formula (stack size: {stack_size})")
105 }
106 XlsError::IfTab(iftab) => write!(f, "Invalid iftab {iftab:X}"),
107 XlsError::Etpg(etpg) => write!(f, "Invalid etpg {etpg:X}"),
108 XlsError::NoVba => write!(f, "No VBA project"),
109 #[cfg(feature = "picture")]
110 XlsError::Art(s) => write!(f, "Invalid art record '{s}'"),
111 XlsError::WorksheetNotFound(name) => write!(f, "Worksheet '{name}' not found"),
112 }
113 }
114}
115
116impl std::error::Error for XlsError {
117 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
118 match self {
119 XlsError::Io(e) => Some(e),
120 XlsError::Cfb(e) => Some(e),
121 XlsError::Vba(e) => Some(e),
122 _ => None,
123 }
124 }
125}
126
127#[derive(Debug, Clone, Default)]
129#[non_exhaustive]
130pub struct XlsOptions {
131 pub force_codepage: Option<u16>,
140 pub header_row: HeaderRow,
142}
143
144struct SheetData {
145 range: Range<Data>,
146 formula: Range<String>,
147 merge_cells: Vec<Dimensions>,
148}
149
150pub struct Xls<RS> {
152 sheets: BTreeMap<String, SheetData>,
153 vba: Option<VbaProject>,
154 metadata: Metadata,
155 marker: PhantomData<RS>,
156 options: XlsOptions,
157 formats: Vec<CellFormat>,
158 is_1904: bool,
159 #[cfg(feature = "picture")]
160 pictures: Option<Vec<(String, Vec<u8>)>>,
161}
162
163impl<RS: Read + Seek> Xls<RS> {
164 pub fn new_with_options(mut reader: RS, options: XlsOptions) -> Result<Self, XlsError> {
180 let mut cfb = {
181 let offset_end = reader.seek(SeekFrom::End(0))? as usize;
182 reader.seek(SeekFrom::Start(0))?;
183 Cfb::new(&mut reader, offset_end)?
184 };
185
186 debug!("cfb loaded");
187
188 let vba = if cfb.has_directory("_VBA_PROJECT_CUR") {
190 Some(VbaProject::from_cfb(&mut reader, &mut cfb)?)
191 } else {
192 None
193 };
194
195 debug!("vba ok");
196
197 let mut xls = Xls {
198 sheets: BTreeMap::new(),
199 vba,
200 marker: PhantomData,
201 metadata: Metadata::default(),
202 options,
203 is_1904: false,
204 formats: Vec::new(),
205 #[cfg(feature = "picture")]
206 pictures: None,
207 };
208
209 xls.parse_workbook(reader, cfb)?;
210
211 debug!("xls parsed");
212
213 Ok(xls)
214 }
215
216 pub fn worksheet_merge_cells(&self, name: &str) -> Option<Vec<Dimensions>> {
218 self.sheets.get(name).map(|r| r.merge_cells.clone())
219 }
220
221 pub fn worksheet_merge_cells_at(&self, n: usize) -> Option<Vec<Dimensions>> {
224 let sheet = self.metadata().sheets.get(n)?;
225
226 self.worksheet_merge_cells(&sheet.name)
227 }
228}
229
230impl<RS: Read + Seek> Reader<RS> for Xls<RS> {
231 type Error = XlsError;
232
233 fn new(reader: RS) -> Result<Self, XlsError> {
234 Self::new_with_options(reader, XlsOptions::default())
235 }
236
237 fn with_header_row(&mut self, header_row: HeaderRow) -> &mut Self {
238 self.options.header_row = header_row;
239 self
240 }
241
242 fn vba_project(&mut self) -> Option<Result<Cow<'_, VbaProject>, XlsError>> {
243 self.vba.as_ref().map(|vba| Ok(Cow::Borrowed(vba)))
244 }
245
246 fn metadata(&self) -> &Metadata {
248 &self.metadata
249 }
250
251 fn worksheet_range(&mut self, name: &str) -> Result<Range<Data>, XlsError> {
252 let sheet = self
253 .sheets
254 .get(name)
255 .map(|r| r.range.clone())
256 .ok_or_else(|| XlsError::WorksheetNotFound(name.into()))?;
257
258 match self.options.header_row {
259 HeaderRow::FirstNonEmptyRow => Ok(sheet),
260 HeaderRow::Row(header_row_idx) => {
261 if let (Some(start), Some(end)) = (sheet.start(), sheet.end()) {
263 Ok(sheet.range((header_row_idx, start.1), end))
264 } else {
265 Ok(sheet)
266 }
267 }
268 }
269 }
270
271 fn worksheets(&mut self) -> Vec<(String, Range<Data>)> {
272 self.sheets
273 .iter()
274 .map(|(name, sheet)| (name.to_owned(), sheet.range.clone()))
275 .collect()
276 }
277
278 fn worksheet_formula(&mut self, name: &str) -> Result<Range<String>, XlsError> {
279 self.sheets
280 .get(name)
281 .ok_or_else(|| XlsError::WorksheetNotFound(name.into()))
282 .map(|r| r.formula.clone())
283 }
284
285 #[cfg(feature = "picture")]
286 fn pictures(&self) -> Option<Vec<(String, Vec<u8>)>> {
287 self.pictures.to_owned()
288 }
289}
290
291#[derive(Debug, Clone, Copy)]
292struct Xti {
293 _isup_book: u16,
294 itab_first: i16,
295 _itab_last: i16,
296}
297
298impl<RS: Read + Seek> Xls<RS> {
299 fn parse_workbook(&mut self, mut reader: RS, mut cfb: Cfb) -> Result<(), XlsError> {
300 let stream = cfb
302 .get_stream("Workbook", &mut reader)
303 .or_else(|_| cfb.get_stream("Book", &mut reader))?;
304
305 let mut sheet_names = Vec::new();
306 let mut strings = Vec::new();
307 let mut defined_names = Vec::new();
308 let mut xtis = Vec::new();
309 let mut formats = BTreeMap::new();
310 let mut xfs = Vec::new();
311 let mut biff = Biff::Biff8; let codepage = self.options.force_codepage.unwrap_or(1200);
313 let mut encoding = XlsEncoding::from_codepage(codepage)?;
314 #[cfg(feature = "picture")]
315 let mut draw_group: Vec<u8> = Vec::new();
316 {
317 let wb = &stream;
318 let records = RecordIter { stream: wb };
319 for record in records {
320 let mut r = record?;
321 match r.typ {
322 0x002F if read_u16(r.data) != 0 => return Err(XlsError::Password),
324 0x0042 => {
326 if self.options.force_codepage.is_none() {
327 encoding = XlsEncoding::from_codepage(read_u16(r.data))?
328 }
329 }
330 0x013D => {
331 let sheet_len = r.data.len() / 2;
332 sheet_names.reserve(sheet_len);
333 self.metadata.sheets.reserve(sheet_len);
334 }
335 0x0022 => {
337 if read_u16(r.data) == 1 {
338 self.is_1904 = true
339 }
340 }
341 0x041E => {
343 let (idx, format) = parse_format(&mut r, &encoding)?;
344 formats.insert(idx, format);
345 }
346 0x00E0 => {
348 xfs.push(parse_xf(&r)?);
349 }
350 0x0085 => {
352 let (pos, sheet) = parse_sheet_metadata(&mut r, &encoding, biff)?;
353 self.metadata.sheets.push(sheet.clone());
354 sheet_names.push((pos, sheet.name)); }
356 0x0809 => {
358 let bof = parse_bof(&mut r)?;
359 biff = bof.biff;
360 }
361 0x0018 => {
362 let cch = r.data[3] as usize;
364 let cce = read_u16(&r.data[4..]) as usize;
365 let mut name = String::new();
366 read_unicode_string_no_cch(&encoding, &r.data[14..], &cch, &mut name);
367 let rgce = &r.data[r.data.len() - cce..];
368 let formula = parse_defined_names(rgce)?;
369 defined_names.push((name, formula));
370 }
371 0x0017 => {
372 let cxti = read_u16(r.data) as usize;
374 xtis.extend(r.data[2..].chunks(6).take(cxti).map(|xti| Xti {
375 _isup_book: read_u16(&xti[..2]),
376 itab_first: read_i16(&xti[2..4]),
377 _itab_last: read_i16(&xti[4..]),
378 }));
379 }
380 0x00FC => strings = parse_sst(&mut r, &encoding)?, #[cfg(feature = "picture")]
382 0x00EB => {
383 draw_group.extend(r.data);
385 if let Some(cont) = r.cont {
386 draw_group.extend(cont.iter().flat_map(|v| *v));
387 }
388 }
389 0x000A => break, _ => (),
391 }
392 }
393 }
394
395 self.formats = xfs
396 .into_iter()
397 .map(|fmt| match formats.get(&fmt) {
398 Some(s) => *s,
399 _ => builtin_format_by_code(fmt),
400 })
401 .collect();
402
403 debug!("formats: {:?}", self.formats);
404
405 let defined_names = defined_names
406 .into_iter()
407 .map(|(name, (i, mut f))| {
408 if let Some(i) = i {
409 let sh = xtis
410 .get(i)
411 .and_then(|xti| sheet_names.get(xti.itab_first as usize))
412 .map_or("#REF", |sh| &sh.1);
413 f = format!("{sh}!{f}");
414 }
415 (name, f)
416 })
417 .collect::<Vec<_>>();
418
419 debug!("defined_names: {:?}", defined_names);
420
421 let mut sheets = BTreeMap::new();
422 let fmla_sheet_names = sheet_names
423 .iter()
424 .map(|(_, n)| n.clone())
425 .collect::<Vec<_>>();
426 for (pos, name) in sheet_names {
427 let sh = &stream[pos..];
428 let records = RecordIter { stream: sh };
429 let mut cells = Vec::new();
430 let mut formulas = Vec::new();
431 let mut fmla_pos = (0, 0);
432 let mut merge_cells = Vec::new();
433 for record in records {
434 let r = record?;
435 match r.typ {
436 0x0200 => {
438 let Dimensions { start, end } = parse_dimensions(r.data)?;
439 let rows = (end.0 - start.0 + 1) as usize;
440 let cols = (end.1 - start.1 + 1) as usize;
441 cells.reserve(rows.saturating_mul(cols));
442 }
443 0x0203 => cells.push(parse_number(r.data, &self.formats, self.is_1904)?), 0x0204 => cells.extend(parse_label(r.data, &encoding, biff)?), 0x0205 => cells.push(parse_bool_err(r.data)?), 0x0207 => {
448 let val = Data::String(parse_string(r.data, &encoding, biff)?);
450 cells.push(Cell::new(fmla_pos, val))
451 }
452 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 => {
458 if r.data.len() < 20 {
460 return Err(XlsError::Len {
461 expected: 20,
462 found: r.data.len(),
463 typ: "Formula",
464 });
465 }
466 let row = read_u16(r.data);
467 let col = read_u16(&r.data[2..]);
468 fmla_pos = (row as u32, col as u32);
469 if let Some(val) = parse_formula_value(&r.data[6..14])? {
470 cells.push(Cell::new(fmla_pos, val));
473 }
474 let fmla = parse_formula(
475 &r.data[20..],
476 &fmla_sheet_names,
477 &defined_names,
478 &xtis,
479 &encoding,
480 )
481 .unwrap_or_else(|e| {
482 debug!("{}", e);
483 format!(
484 "Unrecognised formula \
485 for cell ({}, {}): {:?}",
486 row, col, e
487 )
488 });
489 formulas.push(Cell::new(fmla_pos, fmla));
490 }
491 _ => (),
492 }
493 }
494 let range = Range::from_sparse(cells);
495 let formula = Range::from_sparse(formulas);
496 sheets.insert(
497 name,
498 SheetData {
499 range,
500 formula,
501 merge_cells,
502 },
503 );
504 }
505
506 self.sheets = sheets;
507 self.metadata.names = defined_names;
508
509 #[cfg(feature = "picture")]
510 if !draw_group.is_empty() {
511 let pics = parse_pictures(&draw_group)?;
512 if !pics.is_empty() {
513 self.pictures = Some(pics);
514 }
515 }
516
517 Ok(())
518 }
519}
520
521struct Bof {
523 biff: Biff,
525}
526
527#[derive(Clone, Copy)]
529enum Biff {
530 Biff2,
531 Biff3,
532 Biff4,
533 Biff5,
534 Biff8,
535 }
538
539fn parse_bof(r: &mut Record<'_>) -> Result<Bof, XlsError> {
541 let mut dt = 0;
542 let biff_version = read_u16(&r.data[..2]);
543
544 if r.data.len() >= 4 {
545 dt = read_u16(&r.data[2..]);
546 };
547
548 let biff = match biff_version {
549 0x0200 | 0x0002 | 0x0007 => Biff::Biff2,
550 0x0300 => Biff::Biff3,
551 0x0400 => Biff::Biff4,
552 0x0500 => Biff::Biff5,
553 0x0600 => Biff::Biff8,
554 0 => {
555 if dt == 0x1000 {
556 Biff::Biff5
557 } else {
558 Biff::Biff8
559 }
560 }
561 _ => Biff::Biff8,
562 };
563
564 Ok(Bof { biff })
565}
566
567fn parse_sheet_metadata(
569 r: &mut Record<'_>,
570 encoding: &XlsEncoding,
571 biff: Biff,
572) -> Result<(usize, Sheet), XlsError> {
573 let pos = read_u32(r.data) as usize;
574 let visible = match r.data[4] & 0b0011_1111 {
575 0x00 => SheetVisible::Visible,
576 0x01 => SheetVisible::Hidden,
577 0x02 => SheetVisible::VeryHidden,
578 e => {
579 return Err(XlsError::Unrecognized {
580 typ: "BoundSheet8:hsState",
581 val: e,
582 });
583 }
584 };
585 let typ = match r.data[5] {
586 0x00 => SheetType::WorkSheet,
587 0x01 => SheetType::MacroSheet,
588 0x02 => SheetType::ChartSheet,
589 0x06 => SheetType::Vba,
590 e => {
591 return Err(XlsError::Unrecognized {
592 typ: "BoundSheet8:dt",
593 val: e,
594 });
595 }
596 };
597 r.data = &r.data[6..];
598 let mut name = parse_short_string(r, encoding, biff)?;
599 name.retain(|c| c != '\0');
600 Ok((pos, Sheet { name, visible, typ }))
601}
602
603fn parse_number(r: &[u8], formats: &[CellFormat], is_1904: bool) -> Result<Cell<Data>, XlsError> {
604 if r.len() < 14 {
605 return Err(XlsError::Len {
606 typ: "number",
607 expected: 14,
608 found: r.len(),
609 });
610 }
611 let row = read_u16(r) as u32;
612 let col = read_u16(&r[2..]) as u32;
613 let v = read_f64(&r[6..]);
614 let format = formats.get(read_u16(&r[4..]) as usize);
615
616 Ok(Cell::new((row, col), format_excel_f64(v, format, is_1904)))
617}
618
619fn parse_bool_err(r: &[u8]) -> Result<Cell<Data>, XlsError> {
620 if r.len() < 8 {
621 return Err(XlsError::Len {
622 typ: "BoolErr",
623 expected: 8,
624 found: r.len(),
625 });
626 }
627 let row = read_u16(r);
628 let col = read_u16(&r[2..]);
629 let pos = (row as u32, col as u32);
630 match r[7] {
631 0x00 => Ok(Cell::new(pos, Data::Bool(r[6] != 0))),
632 0x01 => Ok(Cell::new(pos, parse_err(r[6])?)),
633 e => Err(XlsError::Unrecognized {
634 typ: "fError",
635 val: e,
636 }),
637 }
638}
639
640fn parse_err(e: u8) -> Result<Data, XlsError> {
641 match e {
642 0x00 => Ok(Data::Error(CellErrorType::Null)),
643 0x07 => Ok(Data::Error(CellErrorType::Div0)),
644 0x0F => Ok(Data::Error(CellErrorType::Value)),
645 0x17 => Ok(Data::Error(CellErrorType::Ref)),
646 0x1D => Ok(Data::Error(CellErrorType::Name)),
647 0x24 => Ok(Data::Error(CellErrorType::Num)),
648 0x2A => Ok(Data::Error(CellErrorType::NA)),
649 0x2B => Ok(Data::Error(CellErrorType::GettingData)),
650 e => Err(XlsError::Unrecognized {
651 typ: "error",
652 val: e,
653 }),
654 }
655}
656
657fn parse_rk(r: &[u8], formats: &[CellFormat], is_1904: bool) -> Result<Cell<Data>, XlsError> {
658 if r.len() < 10 {
659 return Err(XlsError::Len {
660 typ: "rk",
661 expected: 10,
662 found: r.len(),
663 });
664 }
665 let row = read_u16(r);
666 let col = read_u16(&r[2..]);
667
668 Ok(Cell::new(
669 (row as u32, col as u32),
670 rk_num(&r[4..10], formats, is_1904),
671 ))
672}
673
674fn parse_merge_cells(r: &[u8], merge_cells: &mut Vec<Dimensions>) -> Result<(), XlsError> {
675 let count = read_u16(r);
676
677 for i in 0..count {
678 let offset: usize = (2 + i * 8).into();
679
680 let rf = read_u16(&r[offset..]);
681 let rl = read_u16(&r[offset + 2..]);
682 let cf = read_u16(&r[offset + 4..]);
683 let cl = read_u16(&r[offset + 6..]);
684
685 merge_cells.push(Dimensions {
686 start: (rf.into(), cf.into()),
687 end: (rl.into(), cl.into()),
688 })
689 }
690
691 Ok(())
692}
693
694fn parse_mul_rk(
695 r: &[u8],
696 cells: &mut Vec<Cell<Data>>,
697 formats: &[CellFormat],
698 is_1904: bool,
699) -> Result<(), XlsError> {
700 if r.len() < 6 {
701 return Err(XlsError::Len {
702 typ: "rk",
703 expected: 6,
704 found: r.len(),
705 });
706 }
707
708 let row = read_u16(r);
709 let col_first = read_u16(&r[2..]);
710 let col_last = read_u16(&r[r.len() - 2..]);
711
712 if r.len() != 6 + 6 * (col_last - col_first + 1) as usize {
713 return Err(XlsError::Len {
714 typ: "rk",
715 expected: 6 + 6 * (col_last - col_first + 1) as usize,
716 found: r.len(),
717 });
718 }
719
720 let mut col = col_first as u32;
721
722 for rk in r[4..r.len() - 2].chunks(6) {
723 cells.push(Cell::new((row as u32, col), rk_num(rk, formats, is_1904)));
724 col += 1;
725 }
726 Ok(())
727}
728
729fn rk_num(rk: &[u8], formats: &[CellFormat], is_1904: bool) -> Data {
730 let d100 = (rk[2] & 1) != 0;
731 let is_int = (rk[2] & 2) != 0;
732 let format = formats.get(read_u16(rk) as usize);
733
734 let mut v = [0u8; 8];
735 v[4..].copy_from_slice(&rk[2..]);
736 v[4] &= 0xFC;
737 if is_int {
738 let v = (read_i32(&v[4..8]) >> 2) as i64;
739 if d100 && v % 100 != 0 {
740 format_excel_f64(v as f64 / 100.0, format, is_1904)
741 } else {
742 format_excel_i64(if d100 { v / 100 } else { v }, format, is_1904)
743 }
744 } else {
745 let v = read_f64(&v);
746 format_excel_f64(if d100 { v / 100.0 } else { v }, format, is_1904)
747 }
748}
749
750fn parse_short_string(
752 r: &mut Record<'_>,
753 encoding: &XlsEncoding,
754 biff: Biff,
755) -> Result<String, XlsError> {
756 if r.data.len() < 2 {
757 return Err(XlsError::Len {
758 typ: "short string",
759 expected: 2,
760 found: r.data.len(),
761 });
762 }
763
764 let cch = r.data[0] as usize;
765 r.data = &r.data[1..];
766 let mut high_byte = None;
767
768 if matches!(biff, Biff::Biff8) {
769 high_byte = Some(r.data[0] & 0x1 != 0);
770 r.data = &r.data[1..];
771 }
772
773 let mut s = String::with_capacity(cch);
774 let _ = encoding.decode_to(r.data, cch, &mut s, high_byte);
775 Ok(s)
776}
777
778fn parse_string(r: &[u8], encoding: &XlsEncoding, biff: Biff) -> Result<String, XlsError> {
780 if r.len() < 4 {
781 return Err(XlsError::Len {
782 typ: "string",
783 expected: 4,
784 found: r.len(),
785 });
786 }
787 let cch = read_u16(r) as usize;
788
789 let (high_byte, start) = match biff {
790 Biff::Biff2 | Biff::Biff3 | Biff::Biff4 | Biff::Biff5 => (None, 2),
791 _ => (Some(r[2] & 0x1 != 0), 3),
792 };
793
794 let mut s = String::with_capacity(cch);
795 let _ = encoding.decode_to(&r[start..], cch, &mut s, high_byte);
796 Ok(s)
797}
798
799fn parse_label(
800 r: &[u8],
801 encoding: &XlsEncoding,
802 biff: Biff,
803) -> Result<Option<Cell<Data>>, XlsError> {
804 if r.len() < 6 {
805 return Err(XlsError::Len {
806 typ: "label",
807 expected: 6,
808 found: r.len(),
809 });
810 }
811 let row = read_u16(r);
812 let col = read_u16(&r[2..]);
813 let _ixfe = read_u16(&r[4..]);
814 Ok(Some(Cell::new(
815 (row as u32, col as u32),
816 Data::String(parse_string(&r[6..], encoding, biff)?),
817 )))
818}
819
820fn parse_label_sst(r: &[u8], strings: &[String]) -> Result<Option<Cell<Data>>, XlsError> {
821 if r.len() < 10 {
822 return Err(XlsError::Len {
823 typ: "label sst",
824 expected: 10,
825 found: r.len(),
826 });
827 }
828 let row = read_u16(r);
829 let col = read_u16(&r[2..]);
830 let i = read_u32(&r[6..]) as usize;
831 if let Some(s) = strings.get(i) {
832 if !s.is_empty() {
833 return Ok(Some(Cell::new(
834 (row as u32, col as u32),
835 Data::String(s.clone()),
836 )));
837 }
838 }
839 Ok(None)
840}
841
842fn parse_dimensions(r: &[u8]) -> Result<Dimensions, XlsError> {
843 let (rf, rl, cf, cl) = match r.len() {
844 10 => (
845 read_u16(&r[0..2]) as u32,
846 read_u16(&r[2..4]) as u32,
847 read_u16(&r[4..6]) as u32,
848 read_u16(&r[6..8]) as u32,
849 ),
850 14 => (
851 read_u32(&r[0..4]),
852 read_u32(&r[4..8]),
853 read_u16(&r[8..10]) as u32,
854 read_u16(&r[10..12]) as u32,
855 ),
856 _ => {
857 return Err(XlsError::Len {
858 typ: "dimensions",
859 expected: 14,
860 found: r.len(),
861 });
862 }
863 };
864 if 1 <= rl && 1 <= cl {
865 Ok(Dimensions {
866 start: (rf, cf),
867 end: (rl - 1, cl - 1),
868 })
869 } else {
870 Ok(Dimensions {
871 start: (rf, cf),
872 end: (rf, cf),
873 })
874 }
875}
876
877fn parse_sst(r: &mut Record<'_>, encoding: &XlsEncoding) -> Result<Vec<String>, XlsError> {
878 if r.data.len() < 8 {
879 return Err(XlsError::Len {
880 typ: "sst",
881 expected: 8,
882 found: r.data.len(),
883 });
884 }
885 let len: usize = read_i32(&r.data[4..8]).try_into().unwrap();
886 let mut sst = Vec::with_capacity(len);
887 r.data = &r.data[8..];
888
889 for _ in 0..len {
890 sst.push(read_rich_extended_string(r, encoding)?);
891 }
892 Ok(sst)
893}
894
895fn parse_xf(r: &Record<'_>) -> Result<u16, XlsError> {
899 if r.data.len() < 4 {
900 return Err(XlsError::Len {
901 typ: "xf",
902 expected: 4,
903 found: r.data.len(),
904 });
905 }
906
907 Ok(read_u16(&r.data[2..]))
908}
909
910fn parse_format(r: &mut Record<'_>, encoding: &XlsEncoding) -> Result<(u16, CellFormat), XlsError> {
914 if r.data.len() < 4 {
915 return Err(XlsError::Len {
916 typ: "format",
917 expected: 4,
918 found: r.data.len(),
919 });
920 }
921
922 let idx = read_u16(r.data);
923
924 let cch = read_u16(&r.data[2..]) as usize;
925 let high_byte = r.data[4] & 0x1 != 0;
926 r.data = &r.data[5..];
927 let mut s = String::with_capacity(cch);
928 encoding.decode_to(r.data, cch, &mut s, Some(high_byte));
929
930 Ok((idx, detect_custom_number_format(&s)))
931}
932
933fn read_rich_extended_string(
937 r: &mut Record<'_>,
938 encoding: &XlsEncoding,
939) -> Result<String, XlsError> {
940 if r.data.is_empty() && !r.continue_record() || r.data.len() < 3 {
941 return Err(XlsError::Len {
942 typ: "rich extended string",
943 expected: 3,
944 found: r.data.len(),
945 });
946 }
947
948 let cch = read_u16(r.data) as usize;
949 let flags = r.data[2];
950
951 r.data = &r.data[3..];
952
953 let high_byte = flags & 0x1 != 0;
954
955 let mut c_run = 0;
957
958 let mut cb_ext_rst = 0;
960
961 if flags & 0x8 != 0 {
963 c_run = read_u16(r.data) as usize;
964 r.data = &r.data[2..];
965 }
966
967 if flags & 0x4 != 0 {
969 cb_ext_rst = read_i32(r.data) as usize;
970 r.data = &r.data[4..];
971 }
972
973 let s = read_dbcs(encoding, cch, r, high_byte)?;
975
976 r.skip(c_run * 4)?;
978
979 r.skip(cb_ext_rst)?;
981
982 Ok(s)
983}
984
985fn read_dbcs(
986 encoding: &XlsEncoding,
987 mut len: usize,
988 r: &mut Record<'_>,
989 mut high_byte: bool,
990) -> Result<String, XlsError> {
991 let mut s = String::with_capacity(len);
992 while len > 0 {
993 let (l, at) = encoding.decode_to(r.data, len, &mut s, Some(high_byte));
994 r.data = &r.data[at..];
995 len -= l;
996 if len > 0 {
997 if r.continue_record() {
998 high_byte = r.data[0] & 0x1 != 0;
999 r.data = &r.data[1..];
1000 } else {
1001 return Err(XlsError::EoStream("dbcs"));
1002 }
1003 }
1004 }
1005 Ok(s)
1006}
1007
1008fn read_unicode_string_no_cch(encoding: &XlsEncoding, buf: &[u8], len: &usize, s: &mut String) {
1009 encoding.decode_to(&buf[1..=*len], *len, s, Some(buf[0] & 0x1 != 0));
1010}
1011
1012struct Record<'a> {
1013 typ: u16,
1014 data: &'a [u8],
1015 cont: Option<Vec<&'a [u8]>>,
1016}
1017
1018impl<'a> Record<'a> {
1019 fn continue_record(&mut self) -> bool {
1020 match self.cont {
1021 None => false,
1022 Some(ref mut v) => {
1023 if v.is_empty() {
1024 false
1025 } else {
1026 self.data = v.remove(0);
1027 true
1028 }
1029 }
1030 }
1031 }
1032
1033 fn skip(&mut self, mut len: usize) -> Result<(), XlsError> {
1034 while len > 0 {
1035 if self.data.is_empty() && !self.continue_record() {
1036 return Err(XlsError::ContinueRecordTooShort);
1037 }
1038 let l = min(len, self.data.len());
1039 let (_, next) = self.data.split_at(l);
1040 self.data = next;
1041 len -= l;
1042 }
1043 Ok(())
1044 }
1045}
1046
1047struct RecordIter<'a> {
1048 stream: &'a [u8],
1049}
1050
1051impl<'a> Iterator for RecordIter<'a> {
1052 type Item = Result<Record<'a>, XlsError>;
1053 fn next(&mut self) -> Option<Self::Item> {
1054 if self.stream.len() < 4 {
1055 return if self.stream.is_empty() {
1056 None
1057 } else {
1058 Some(Err(XlsError::EoStream("record type and length")))
1059 };
1060 }
1061 let t = read_u16(self.stream);
1062 let mut len = read_u16(&self.stream[2..]) as usize;
1063 if self.stream.len() < len + 4 {
1064 return Some(Err(XlsError::EoStream("record length")));
1065 }
1066 let (data, next) = self.stream.split_at(len + 4);
1067 self.stream = next;
1068 let d = &data[4..];
1069
1070 let cont = if next.len() > 4 && read_u16(next) == 0x003C {
1072 let mut cont = Vec::new();
1073 while self.stream.len() > 4 && read_u16(self.stream) == 0x003C {
1074 len = read_u16(&self.stream[2..]) as usize;
1075 if self.stream.len() < len + 4 {
1076 return Some(Err(XlsError::EoStream("continue record length")));
1077 }
1078 let sp = self.stream.split_at(len + 4);
1079 cont.push(&sp.0[4..]);
1080 self.stream = sp.1;
1081 }
1082 Some(cont)
1083 } else {
1084 None
1085 };
1086
1087 Some(Ok(Record {
1088 typ: t,
1089 data: d,
1090 cont,
1091 }))
1092 }
1093}
1094
1095fn parse_defined_names(rgce: &[u8]) -> Result<(Option<usize>, String), XlsError> {
1099 if rgce.is_empty() {
1100 return Ok((None, "empty rgce".to_string()));
1102 }
1103 let ptg = rgce[0];
1104 let res = match ptg {
1105 0x3a | 0x5a | 0x7a => {
1106 let ixti = read_u16(&rgce[1..3]) as usize;
1108 let mut f = String::new();
1109 f.push('$');
1111 push_column(read_u16(&rgce[5..7]) as u32, &mut f);
1112 f.push('$');
1113 f.push_str(&format!("{}", read_u16(&rgce[3..5]) as u32 + 1));
1114 (Some(ixti), f)
1115 }
1116 0x3b | 0x5b | 0x7b => {
1117 let ixti = read_u16(&rgce[1..3]) as usize;
1119 let mut f = String::new();
1120 f.push('$');
1122 push_column(read_u16(&rgce[7..9]) as u32, &mut f);
1123 f.push('$');
1124 f.push_str(&format!("{}", read_u16(&rgce[3..5]) as u32 + 1));
1125 f.push(':');
1126 f.push('$');
1127 push_column(read_u16(&rgce[9..11]) as u32, &mut f);
1128 f.push('$');
1129 f.push_str(&format!("{}", read_u16(&rgce[5..7]) as u32 + 1));
1130 (Some(ixti), f)
1131 }
1132 0x3c | 0x5c | 0x7c | 0x3d | 0x5d | 0x7d => {
1133 let ixti = read_u16(&rgce[1..3]) as usize;
1135 (Some(ixti), "#REF!".to_string())
1136 }
1137 _ => (None, format!("Unsupported ptg: {:x}", ptg)),
1138 };
1139 Ok(res)
1140}
1141
1142fn parse_formula(
1146 mut rgce: &[u8],
1147 sheets: &[String],
1148 names: &[(String, String)],
1149 xtis: &[Xti],
1150 encoding: &XlsEncoding,
1151) -> Result<String, XlsError> {
1152 let mut stack = Vec::new();
1153 let mut formula = String::with_capacity(rgce.len());
1154 let cce = read_u16(rgce) as usize;
1155 rgce = &rgce[2..2 + cce];
1156 while !rgce.is_empty() {
1157 let ptg = rgce[0];
1158 rgce = &rgce[1..];
1159 match ptg {
1160 0x3a | 0x5a | 0x7a => {
1161 let ixti = read_u16(&rgce[0..2]);
1163 let rowu = read_u16(&rgce[2..]);
1164 let colu = read_u16(&rgce[4..]);
1165 let sh = xtis
1166 .get(ixti as usize)
1167 .and_then(|xti| sheets.get(xti.itab_first as usize))
1168 .map_or("#REF", |sh| sh);
1169 stack.push(formula.len());
1170 formula.push_str(sh);
1171 formula.push('!');
1172 let col = colu << 2; if colu & 2 != 0 {
1174 formula.push('$');
1175 }
1176 push_column(col as u32, &mut formula);
1177 if colu & 1 != 0 {
1178 formula.push('$');
1179 }
1180 write!(&mut formula, "{}", rowu + 1).unwrap();
1181 rgce = &rgce[6..];
1182 }
1183 0x3b | 0x5b | 0x7b => {
1184 let ixti = read_u16(&rgce[0..2]);
1186 stack.push(formula.len());
1187 formula.push_str(sheets.get(ixti as usize).map_or("#REF", |s| &**s));
1188 formula.push('!');
1189 formula.push('$');
1191 push_column(read_u16(&rgce[6..8]) as u32, &mut formula);
1192 write!(&mut formula, "${}:$", read_u16(&rgce[2..4]) as u32 + 1).unwrap();
1193 push_column(read_u16(&rgce[8..10]) as u32, &mut formula);
1194 write!(&mut formula, "${}", read_u16(&rgce[4..6]) as u32 + 1).unwrap();
1195 rgce = &rgce[10..];
1196 }
1197 0x3c | 0x5c | 0x7c => {
1198 let ixti = read_u16(&rgce[0..2]);
1200 stack.push(formula.len());
1201 formula.push_str(sheets.get(ixti as usize).map_or("#REF", |s| &**s));
1202 formula.push('!');
1203 formula.push_str("#REF!");
1204 rgce = &rgce[6..];
1205 }
1206 0x3d | 0x5d | 0x7d => {
1207 let ixti = read_u16(&rgce[0..2]);
1209 stack.push(formula.len());
1210 formula.push_str(sheets.get(ixti as usize).map_or("#REF", |s| &**s));
1211 formula.push('!');
1212 formula.push_str("#REF!");
1213 rgce = &rgce[10..];
1214 }
1215 0x01 => {
1216 debug!("ignoring PtgExp array/shared formula");
1218 stack.push(formula.len());
1219 rgce = &rgce[4..];
1220 }
1221 0x03..=0x11 => {
1222 let e2 = stack.pop().ok_or(XlsError::StackLen)?;
1224 let op = match ptg {
1226 0x03 => "+",
1227 0x04 => "-",
1228 0x05 => "*",
1229 0x06 => "/",
1230 0x07 => "^",
1231 0x08 => "&",
1232 0x09 => "<",
1233 0x0A => "<=",
1234 0x0B => "=",
1235 0x0C => ">",
1236 0x0D => ">=",
1237 0x0E => "<>",
1238 0x0F => " ",
1239 0x10 => ",",
1240 0x11 => ":",
1241 _ => unreachable!(),
1242 };
1243 let e2 = formula.split_off(e2);
1244 write!(&mut formula, "{}{}", op, e2).unwrap();
1245 }
1246 0x12 => {
1247 let e = stack.last().ok_or(XlsError::StackLen)?;
1248 formula.insert(*e, '+');
1249 }
1250 0x13 => {
1251 let e = stack.last().ok_or(XlsError::StackLen)?;
1252 formula.insert(*e, '-');
1253 }
1254 0x14 => {
1255 formula.push('%');
1256 }
1257 0x15 => {
1258 let e = stack.last().ok_or(XlsError::StackLen)?;
1259 formula.insert(*e, '(');
1260 formula.push(')');
1261 }
1262 0x16 => {
1263 stack.push(formula.len());
1264 }
1265 0x17 => {
1266 stack.push(formula.len());
1267 formula.push('\"');
1268 let cch = rgce[0] as usize;
1269 read_unicode_string_no_cch(encoding, &rgce[1..], &cch, &mut formula);
1270 formula.push('\"');
1271 rgce = &rgce[2 + cch..];
1272 }
1273 0x18 => {
1274 rgce = &rgce[5..];
1275 }
1276 0x19 => {
1277 let etpg = rgce[0];
1278 rgce = &rgce[1..];
1279 match etpg {
1280 0x01 | 0x02 | 0x08 | 0x20 | 0x21 => rgce = &rgce[2..],
1281 0x04 => {
1282 let n = read_u16(&rgce[..2]) as usize + 1;
1284 rgce = &rgce[2 + 2 * n..]; }
1286 0x10 => {
1287 rgce = &rgce[2..];
1288 let e = *stack.last().ok_or(XlsError::StackLen)?;
1289 let e = formula.split_off(e);
1290 write!(&mut formula, "SUM({})", e).unwrap();
1291 }
1292 0x40 | 0x41 => {
1293 let e = *stack.last().ok_or(XlsError::StackLen)?;
1295 let space = match rgce[0] {
1296 0x00 | 0x02 | 0x04 | 0x06 => ' ',
1297 0x01 | 0x03 | 0x05 => '\r',
1298 val => {
1299 return Err(XlsError::Unrecognized {
1300 typ: "PtgAttrSpaceType",
1301 val,
1302 });
1303 }
1304 };
1305 let cch = rgce[1];
1306 for _ in 0..cch {
1307 formula.insert(e, space);
1308 }
1309 rgce = &rgce[2..];
1310 }
1311 e => return Err(XlsError::Etpg(e)),
1312 }
1313 }
1314 0x1C => {
1315 stack.push(formula.len());
1316 let err = rgce[0];
1317 rgce = &rgce[1..];
1318 match err {
1319 0x00 => formula.push_str("#NULL!"),
1320 0x07 => formula.push_str("#DIV/0!"),
1321 0x0F => formula.push_str("#VALUE!"),
1322 0x17 => formula.push_str("#REF!"),
1323 0x1D => formula.push_str("#NAME?"),
1324 0x24 => formula.push_str("#NUM!"),
1325 0x2A => formula.push_str("#N/A"),
1326 0x2B => formula.push_str("#GETTING_DATA"),
1327 e => {
1328 return Err(XlsError::Unrecognized {
1329 typ: "BErr",
1330 val: e,
1331 });
1332 }
1333 }
1334 }
1335 0x1D => {
1336 stack.push(formula.len());
1337 formula.push_str(if rgce[0] == 0 { "FALSE" } else { "TRUE" });
1338 rgce = &rgce[1..];
1339 }
1340 0x1E => {
1341 stack.push(formula.len());
1342 write!(&mut formula, "{}", read_u16(rgce)).unwrap();
1343 rgce = &rgce[2..];
1344 }
1345 0x1F => {
1346 stack.push(formula.len());
1347 write!(&mut formula, "{}", read_f64(rgce)).unwrap();
1348 rgce = &rgce[8..];
1349 }
1350 0x20 | 0x40 | 0x60 => {
1351 stack.push(formula.len());
1353 formula.push_str("{PtgArray}");
1354 rgce = &rgce[7..];
1355 }
1356 0x21 | 0x22 | 0x41 | 0x42 | 0x61 | 0x62 => {
1357 let (iftab, argc) = match ptg {
1358 0x22 | 0x42 | 0x62 => {
1359 let iftab = read_u16(&rgce[1..]) as usize;
1360 let argc = rgce[0] as usize;
1361 rgce = &rgce[3..];
1362 (iftab, argc)
1363 }
1364 _ => {
1365 let iftab = read_u16(rgce) as usize;
1366 if iftab > crate::utils::FTAB_LEN {
1367 return Err(XlsError::IfTab(iftab));
1368 }
1369 rgce = &rgce[2..];
1370 let argc = crate::utils::FTAB_ARGC[iftab] as usize;
1371 (iftab, argc)
1372 }
1373 };
1374 if stack.len() < argc {
1375 return Err(XlsError::StackLen);
1376 }
1377 if argc > 0 {
1378 let args_start = stack.len() - argc;
1379 let mut args = stack.split_off(args_start);
1380 let start = args[0];
1381 for s in &mut args {
1382 *s -= start;
1383 }
1384 let fargs = formula.split_off(start);
1385 stack.push(formula.len());
1386 args.push(fargs.len());
1387 formula.push_str(
1388 crate::utils::FTAB
1389 .get(iftab)
1390 .ok_or(XlsError::IfTab(iftab))?,
1391 );
1392 formula.push('(');
1393 for w in args.windows(2) {
1394 formula.push_str(&fargs[w[0]..w[1]]);
1395 formula.push(',');
1396 }
1397 formula.pop();
1398 formula.push(')');
1399 } else {
1400 stack.push(formula.len());
1401 formula.push_str(crate::utils::FTAB[iftab]);
1402 formula.push_str("()");
1403 }
1404 }
1405 0x23 | 0x43 | 0x63 => {
1406 let iname = read_u32(rgce) as usize - 1; stack.push(formula.len());
1408 formula.push_str(names.get(iname).map_or("#REF!", |n| &*n.0));
1409 rgce = &rgce[4..];
1410 }
1411 0x24 | 0x44 | 0x64 => {
1412 stack.push(formula.len());
1413 let row = read_u16(rgce) + 1;
1414 let col = read_u16(&[rgce[2], rgce[3] & 0x3F]);
1415 if rgce[3] & 0x80 != 0x80 {
1416 formula.push('$');
1417 }
1418 push_column(col as u32, &mut formula);
1419 if rgce[3] & 0x40 != 0x40 {
1420 formula.push('$');
1421 }
1422 formula.push_str(&format!("{}", row));
1423 rgce = &rgce[4..];
1424 }
1425 0x25 | 0x45 | 0x65 => {
1426 stack.push(formula.len());
1427 formula.push('$');
1428 push_column(read_u16(&rgce[4..6]) as u32, &mut formula);
1429 write!(&mut formula, "${}:$", read_u16(&rgce[0..2]) as u32 + 1).unwrap();
1430 push_column(read_u16(&rgce[6..8]) as u32, &mut formula);
1431 write!(&mut formula, "${}", read_u16(&rgce[2..4]) as u32 + 1).unwrap();
1432 rgce = &rgce[8..];
1433 }
1434 0x2A | 0x4A | 0x6A => {
1435 stack.push(formula.len());
1436 formula.push_str("#REF!");
1437 rgce = &rgce[4..];
1438 }
1439 0x2B | 0x4B | 0x6B => {
1440 stack.push(formula.len());
1441 formula.push_str("#REF!");
1442 rgce = &rgce[8..];
1443 }
1444 0x39 | 0x59 => {
1445 stack.push(formula.len());
1447 formula.push_str("[PtgNameX]");
1448 rgce = &rgce[6..];
1449 }
1450 _ => {
1451 return Err(XlsError::Unrecognized {
1452 typ: "ptg",
1453 val: ptg,
1454 });
1455 }
1456 }
1457 }
1458 if stack.len() == 1 {
1459 Ok(formula)
1460 } else {
1461 Err(XlsError::InvalidFormula {
1462 stack_size: stack.len(),
1463 })
1464 }
1465}
1466
1467fn parse_formula_value(r: &[u8]) -> Result<Option<Data>, XlsError> {
1469 match *r {
1470 [0x00, .., 0xFF, 0xFF] => Ok(None),
1472 [0x01, _, b, .., 0xFF, 0xFF] => Ok(Some(Data::Bool(b != 0))),
1473 [0x02, _, e, .., 0xFF, 0xFF] => parse_err(e).map(Some),
1474 [0x03, _, .., 0xFF, 0xFF] => Ok(Some(Data::String("".to_string()))),
1476 [e, .., 0xFF, 0xFF] => Err(XlsError::Unrecognized {
1477 typ: "error",
1478 val: e,
1479 }),
1480 _ => Ok(Some(Data::Float(read_f64(r)))),
1481 }
1482}
1483
1484#[cfg(feature = "picture")]
1486struct ArtRecord<'a> {
1487 instance: u16,
1488 typ: u16,
1489 data: &'a [u8],
1490}
1491
1492#[cfg(feature = "picture")]
1493struct ArtRecordIter<'a> {
1494 stream: &'a [u8],
1495}
1496
1497#[cfg(feature = "picture")]
1498impl<'a> Iterator for ArtRecordIter<'a> {
1499 type Item = Result<ArtRecord<'a>, XlsError>;
1500 fn next(&mut self) -> Option<Self::Item> {
1501 if self.stream.len() < 8 {
1502 return if self.stream.is_empty() {
1503 None
1504 } else {
1505 Some(Err(XlsError::EoStream("art record header")))
1506 };
1507 }
1508 let ver_ins = read_u16(self.stream);
1509 let instance = ver_ins >> 4;
1510 let typ = read_u16(&self.stream[2..]);
1511 if typ < 0xF000 {
1512 return Some(Err(XlsError::Art("type range 0xF000 - 0xFFFF")));
1513 }
1514 let len = read_usize(&self.stream[4..]);
1515 if self.stream.len() < len + 8 {
1516 return Some(Err(XlsError::EoStream("art record length")));
1517 }
1518 let (d, next) = self.stream.split_at(len + 8);
1519 self.stream = next;
1520 let data = &d[8..];
1521
1522 Some(Ok(ArtRecord {
1523 instance,
1524 typ,
1525 data,
1526 }))
1527 }
1528}
1529
1530#[cfg(feature = "picture")]
1532fn parse_pictures(stream: &[u8]) -> Result<Vec<(String, Vec<u8>)>, XlsError> {
1533 let mut pics = Vec::new();
1534 let records = ArtRecordIter { stream };
1535 for record in records {
1536 let r = record?;
1537 match r.typ {
1538 0xF000 | 0xF001 => pics.extend(parse_pictures(r.data)?),
1541 0xF007 => {
1543 let skip = 36 + r.data[33] as usize;
1544 pics.extend(parse_pictures(&r.data[skip..])?);
1545 }
1546 0xF01A | 0xF01B | 0xF01C | 0xF01D | 0xF01E | 0xF01F | 0xF029 | 0xF02A => {
1548 let ext_skip = match r.typ {
1549 0xF01A => {
1551 let skip = match r.instance {
1552 0x3D4 => 50usize,
1553 0x3D5 => 66,
1554 _ => unreachable!(),
1555 };
1556 Ok(("emf", skip))
1557 }
1558 0xF01B => {
1560 let skip = match r.instance {
1561 0x216 => 50usize,
1562 0x217 => 66,
1563 _ => unreachable!(),
1564 };
1565 Ok(("wmf", skip))
1566 }
1567 0xF01C => {
1569 let skip = match r.instance {
1570 0x542 => 50usize,
1571 0x543 => 66,
1572 _ => unreachable!(),
1573 };
1574 Ok(("pict", skip))
1575 }
1576 0xF01D | 0xF02A => {
1578 let skip = match r.instance {
1579 0x46A | 0x6E2 => 17usize,
1580 0x46B | 0x6E3 => 33,
1581 _ => unreachable!(),
1582 };
1583 Ok(("jpg", skip))
1584 }
1585 0xF01E => {
1587 let skip = match r.instance {
1588 0x6E0 => 17usize,
1589 0x6E1 => 33,
1590 _ => unreachable!(),
1591 };
1592 Ok(("png", skip))
1593 }
1594 0xF01F => {
1596 let skip = match r.instance {
1597 0x7A8 => 17usize,
1598 0x7A9 => 33,
1599 _ => unreachable!(),
1600 };
1601 Ok(("dib", skip))
1602 }
1603 0xF029 => {
1605 let skip = match r.instance {
1606 0x6E4 => 17usize,
1607 0x6E5 => 33,
1608 _ => unreachable!(),
1609 };
1610 Ok(("tiff", skip))
1611 }
1612 _ => Err(XlsError::Art("picture type not support")),
1613 };
1614 let ext_skip = ext_skip?;
1615 pics.push((ext_skip.0.to_string(), Vec::from(&r.data[ext_skip.1..])));
1616 }
1617 _ => {}
1618 }
1619 }
1620 Ok(pics)
1621}