Skip to main content

calamine_styles/
ods.rs

1// SPDX-License-Identifier: MIT
2//
3// Copyright 2016-2025, Johann Tuffe.
4
5//! A module to parse Open Document Spreadsheets
6//!
7/// # Reference
8///
9/// OASIS Open Document Format for Office Application 1.2 ([ODF 1.2]).
10///
11/// [ODF 1.2]: http://docs.oasis-open.org/office/v1.2/OpenDocument-v1.2.pdf
12///
13use std::collections::{BTreeMap, HashMap};
14use std::io::{BufReader, Read, Seek};
15
16use log::warn;
17use quick_xml::events::attributes::Attributes;
18use quick_xml::events::Event;
19use quick_xml::name::QName;
20use quick_xml::Reader as XmlReader;
21use zip::read::{ZipArchive, ZipFile};
22use zip::result::ZipError;
23
24use crate::utils::unescape_entity_to_buffer;
25use crate::vba::VbaProject;
26use crate::{
27    Data, DataType, HeaderRow, Metadata, Range, Reader, Sheet, SheetType, SheetVisible, StyleRange,
28    WorksheetLayout,
29};
30use std::marker::PhantomData;
31
32const MIMETYPE: &[u8] = b"application/vnd.oasis.opendocument.spreadsheet";
33
34/// Maximum number of rows allowed in an ODS file (matches XLSX limit).
35const MAX_ROWS: u32 = 1_048_576;
36
37/// Maximum number of columns allowed in an ODS file (matches XLSX limit).
38const MAX_COLUMNS: u32 = 16_384;
39
40/// Maximum number of cells to prevent memory exhaustion from malicious files.
41const MAX_CELLS: usize = 100_000_000;
42
43type OdsReader<'a, RS> = XmlReader<BufReader<ZipFile<'a, RS>>>;
44
45/// An enum for ods specific errors
46#[derive(Debug)]
47pub enum OdsError {
48    /// Io error
49    Io(std::io::Error),
50    /// Zip error
51    Zip(zip::result::ZipError),
52    /// Xml error
53    Xml(quick_xml::Error),
54    /// Xml attribute error
55    XmlAttr(quick_xml::events::attributes::AttrError),
56    /// Error while parsing string
57    Parse(std::string::ParseError),
58    /// Error while parsing integer
59    ParseInt(std::num::ParseIntError),
60    /// Error while parsing float
61    ParseFloat(std::num::ParseFloatError),
62    /// Error while parsing bool
63    ParseBool(std::str::ParseBoolError),
64
65    /// Invalid MIME
66    InvalidMime(Vec<u8>),
67    /// File not found
68    FileNotFound(&'static str),
69    /// Unexpected end of file
70    Eof(&'static str),
71    /// Unexpected error
72    Mismatch {
73        /// Expected
74        expected: &'static str,
75        /// Found
76        found: String,
77    },
78    /// Workbook is password protected
79    Password,
80    /// Worksheet not found
81    WorksheetNotFound(String),
82
83    /// XML attribute error
84    AttrError(quick_xml::events::attributes::AttrError),
85    /// XML encoding error
86    EncodingError(quick_xml::encoding::EncodingError),
87    /// File exceeds maximum cell count
88    CellLimitExceeded {
89        /// Number of cells requested
90        requested: usize,
91        /// Maximum allowed cells
92        max: usize,
93    },
94}
95
96/// Ods reader options
97#[derive(Debug, Default)]
98#[non_exhaustive]
99struct OdsOptions {
100    pub header_row: HeaderRow,
101}
102
103from_err!(std::io::Error, OdsError, Io);
104from_err!(zip::result::ZipError, OdsError, Zip);
105from_err!(quick_xml::Error, OdsError, Xml);
106from_err!(std::str::ParseBoolError, OdsError, ParseBool);
107from_err!(std::num::ParseFloatError, OdsError, ParseFloat);
108from_err!(std::num::ParseIntError, OdsError, ParseInt);
109from_err!(quick_xml::events::attributes::AttrError, OdsError, XmlAttr);
110from_err!(quick_xml::encoding::EncodingError, OdsError, Xml);
111
112impl std::fmt::Display for OdsError {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        match self {
115            OdsError::Io(e) => write!(f, "I/O error: {e}"),
116            OdsError::Zip(e) => write!(f, "Zip error: {e:?}"),
117            OdsError::Xml(e) => write!(f, "Xml error: {e}"),
118            OdsError::XmlAttr(e) => write!(f, "Xml attribute error: {e}"),
119            OdsError::Parse(e) => write!(f, "Parse string error: {e}"),
120            OdsError::ParseInt(e) => write!(f, "Parse integer error: {e}"),
121            OdsError::ParseFloat(e) => write!(f, "Parse float error: {e}"),
122            OdsError::ParseBool(e) => write!(f, "Parse bool error: {e}"),
123            OdsError::InvalidMime(mime) => write!(f, "Invalid MIME type: {mime:?}"),
124            OdsError::FileNotFound(file) => write!(f, "'{file}' file not found in archive"),
125            OdsError::Eof(node) => write!(f, "Expecting '{node}' node, found end of xml file"),
126            OdsError::Mismatch { expected, found } => {
127                write!(f, "Expecting '{expected}', found '{found}'")
128            }
129            OdsError::Password => write!(f, "Workbook is password protected"),
130            OdsError::WorksheetNotFound(name) => write!(f, "Worksheet '{name}' not found"),
131            OdsError::AttrError(e) => write!(f, "XML attribute Error: {e}"),
132            OdsError::EncodingError(e) => write!(f, "XML encoding Error: {e}"),
133            OdsError::CellLimitExceeded { requested, max } => {
134                write!(
135                    f,
136                    "Cell limit exceeded ({requested} cells requested, max {max})"
137                )
138            }
139        }
140    }
141}
142
143impl std::error::Error for OdsError {
144    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
145        match self {
146            OdsError::Io(e) => Some(e),
147            OdsError::Zip(e) => Some(e),
148            OdsError::Xml(e) => Some(e),
149            OdsError::Parse(e) => Some(e),
150            OdsError::ParseInt(e) => Some(e),
151            OdsError::ParseFloat(e) => Some(e),
152            OdsError::AttrError(e) => Some(e),
153            OdsError::EncodingError(e) => Some(e),
154            _ => None,
155        }
156    }
157}
158
159/// An `OpenDocument` Spreadsheet document parser
160///
161/// # Reference
162///
163/// OASIS Open Document Format for Office Application 1.2 ([ODF 1.2]).
164///
165/// [ODF 1.2]: http://docs.oasis-open.org/office/v1.2/OpenDocument-v1.2.pdf
166///
167pub struct Ods<RS> {
168    sheets: BTreeMap<String, (Range<Data>, Range<String>)>,
169    metadata: Metadata,
170    marker: PhantomData<RS>,
171    #[cfg(feature = "picture")]
172    pictures: Option<Vec<(String, Vec<u8>)>>,
173    /// Reader options
174    options: OdsOptions,
175}
176
177impl<RS> Reader<RS> for Ods<RS>
178where
179    RS: Read + Seek,
180{
181    type Error = OdsError;
182
183    fn new(reader: RS) -> Result<Self, OdsError> {
184        let mut zip = ZipArchive::new(reader)?;
185
186        // check mimetype
187        match zip.by_name("mimetype") {
188            Ok(mut f) => {
189                let mut buf = [0u8; 46];
190                f.read_exact(&mut buf)?;
191                if &buf[..] != MIMETYPE {
192                    return Err(OdsError::InvalidMime(buf.to_vec()));
193                }
194            }
195            Err(ZipError::FileNotFound) => return Err(OdsError::FileNotFound("mimetype")),
196            Err(e) => return Err(OdsError::Zip(e)),
197        }
198
199        check_for_password_protected(&mut zip)?;
200
201        #[cfg(feature = "picture")]
202        let pictures = read_pictures(&mut zip)?;
203
204        let Content {
205            sheets,
206            sheets_metadata,
207            defined_names,
208        } = parse_content(zip)?;
209        let metadata = Metadata {
210            sheets: sheets_metadata,
211            names: defined_names,
212        };
213
214        Ok(Ods {
215            marker: PhantomData,
216            metadata,
217            sheets,
218            #[cfg(feature = "picture")]
219            pictures,
220            options: OdsOptions::default(),
221        })
222    }
223
224    fn with_header_row(&mut self, header_row: HeaderRow) -> &mut Self {
225        self.options.header_row = header_row;
226        self
227    }
228
229    /// Gets `VbaProject`
230    fn vba_project(&mut self) -> Result<Option<VbaProject>, OdsError> {
231        Ok(None)
232    }
233
234    /// Read sheets from workbook.xml and get their corresponding path from relationships
235    fn metadata(&self) -> &Metadata {
236        &self.metadata
237    }
238
239    /// Read worksheet data in corresponding worksheet path
240    fn worksheet_range(&mut self, name: &str) -> Result<Range<Data>, OdsError> {
241        let sheet = self
242            .sheets
243            .get(name)
244            .ok_or_else(|| OdsError::WorksheetNotFound(name.into()))?
245            .0
246            .to_owned();
247
248        match self.options.header_row {
249            HeaderRow::FirstNonEmptyRow => Ok(sheet),
250            HeaderRow::Row(header_row_idx) => {
251                // If `header_row` is a row index, adjust the range
252                if let (Some(start), Some(end)) = (sheet.start(), sheet.end()) {
253                    Ok(sheet.range((header_row_idx, start.1), end))
254                } else {
255                    Ok(sheet)
256                }
257            }
258        }
259    }
260
261    fn worksheets(&mut self) -> Vec<(String, Range<Data>)> {
262        self.sheets
263            .iter()
264            .map(|(name, (range, _formula))| (name.to_owned(), range.clone()))
265            .collect()
266    }
267
268    /// Read worksheet data in corresponding worksheet path
269    fn worksheet_formula(&mut self, name: &str) -> Result<Range<String>, OdsError> {
270        self.sheets
271            .get(name)
272            .ok_or_else(|| OdsError::WorksheetNotFound(name.into()))
273            .map(|r| r.1.to_owned())
274    }
275
276    fn worksheet_style(&mut self, _name: &str) -> Result<StyleRange, OdsError> {
277        // TODO: Implement ODS style parsing
278        Ok(StyleRange::empty())
279    }
280
281    fn worksheet_layout(&mut self, _name: &str) -> Result<WorksheetLayout, OdsError> {
282        // ODS doesn't support column width/row height information in the same way as XLSX
283        Ok(WorksheetLayout::new())
284    }
285
286    #[cfg(feature = "picture")]
287    fn pictures(&self) -> Option<Vec<(String, Vec<u8>)>> {
288        self.pictures.to_owned()
289    }
290}
291
292struct Content {
293    sheets: BTreeMap<String, (Range<Data>, Range<String>)>,
294    sheets_metadata: Vec<Sheet>,
295    defined_names: Vec<(String, String)>,
296}
297
298/// Check password protection
299fn check_for_password_protected<RS: Read + Seek>(zip: &mut ZipArchive<RS>) -> Result<(), OdsError> {
300    let mut reader = match zip.by_name("META-INF/manifest.xml") {
301        Ok(f) => {
302            let mut r = XmlReader::from_reader(BufReader::new(f));
303            let config = r.config_mut();
304            config.check_end_names = false;
305            config.trim_text(false);
306            config.check_comments = false;
307            config.expand_empty_elements = true;
308            r
309        }
310        Err(ZipError::FileNotFound) => return Err(OdsError::FileNotFound("META-INF/manifest.xml")),
311        Err(e) => return Err(OdsError::Zip(e)),
312    };
313
314    let mut buf = Vec::new();
315    let mut inner = Vec::new();
316    loop {
317        match reader.read_event_into(&mut buf) {
318            Ok(Event::Start(e)) if e.name() == QName(b"manifest:file-entry") => {
319                loop {
320                    match reader.read_event_into(&mut inner) {
321                        Ok(Event::Start(e)) if e.name() == QName(b"manifest:encryption-data") => {
322                            return Err(OdsError::Password)
323                        }
324                        Ok(Event::Eof) => break,
325                        Err(e) => return Err(OdsError::Xml(e)),
326                        _ => (),
327                    }
328                }
329                inner.clear();
330            }
331            Ok(Event::Eof) => break,
332            Err(e) => return Err(OdsError::Xml(e)),
333            _ => (),
334        }
335        buf.clear();
336    }
337
338    Ok(())
339}
340
341/// Parses content.xml and store the result in `self.content`
342fn parse_content<RS: Read + Seek>(mut zip: ZipArchive<RS>) -> Result<Content, OdsError> {
343    let mut reader = match zip.by_name("content.xml") {
344        Ok(f) => {
345            let mut r = XmlReader::from_reader(BufReader::new(f));
346            let config = r.config_mut();
347            config.check_end_names = false;
348            config.trim_text(false);
349            config.check_comments = false;
350            config.expand_empty_elements = true;
351            r
352        }
353        Err(ZipError::FileNotFound) => return Err(OdsError::FileNotFound("content.xml")),
354        Err(e) => return Err(OdsError::Zip(e)),
355    };
356    let mut buf = Vec::with_capacity(1024);
357    let mut sheets = BTreeMap::new();
358    let mut defined_names = Vec::new();
359    let mut sheets_metadata = Vec::new();
360    let mut styles = HashMap::new();
361    let mut style_name: Option<String> = None;
362    loop {
363        match reader.read_event_into(&mut buf) {
364            Ok(Event::Start(e)) if e.name() == QName(b"style:style") => {
365                style_name = e
366                    .try_get_attribute(b"style:name")?
367                    .map(|a| a.decode_and_unescape_value(reader.decoder()))
368                    .transpose()?
369                    .map(|x| x.to_string());
370            }
371            Ok(Event::Start(e))
372                if style_name.is_some() && e.name() == QName(b"style:table-properties") =>
373            {
374                let visible = match e.try_get_attribute(b"table:display")? {
375                    Some(a) => {
376                        if a.decode_and_unescape_value(reader.decoder())?.parse()? {
377                            SheetVisible::Visible
378                        } else {
379                            SheetVisible::Hidden
380                        }
381                    }
382                    None => SheetVisible::Visible,
383                };
384                styles.insert(style_name.clone(), visible);
385            }
386            Ok(Event::Start(e)) if e.name() == QName(b"table:table") => {
387                let visible = styles
388                    .get(
389                        &e.try_get_attribute(b"table:style-name")?
390                            .map(|a| a.decode_and_unescape_value(reader.decoder()))
391                            .transpose()?
392                            .map(|x| x.to_string()),
393                    )
394                    .cloned()
395                    .unwrap_or(SheetVisible::Visible);
396                if let Some(a) = e
397                    .attributes()
398                    .filter_map(|a| a.ok())
399                    .find(|a| a.key == QName(b"table:name"))
400                {
401                    let name = a.decode_and_unescape_value(reader.decoder())?.to_string();
402                    let (range, formulas) = read_table(&mut reader)?;
403                    sheets_metadata.push(Sheet {
404                        name: name.clone(),
405                        typ: SheetType::WorkSheet,
406                        visible,
407                    });
408                    sheets.insert(name, (range, formulas));
409                }
410            }
411            Ok(Event::Start(e)) if e.name() == QName(b"table:named-expressions") => {
412                defined_names = read_named_expressions(&mut reader)?;
413            }
414            Ok(Event::Eof) => break,
415            Err(e) => return Err(OdsError::Xml(e)),
416            _ => (),
417        }
418        buf.clear();
419    }
420    Ok(Content {
421        sheets,
422        sheets_metadata,
423        defined_names,
424    })
425}
426
427fn read_table<RS>(reader: &mut OdsReader<'_, RS>) -> Result<(Range<Data>, Range<String>), OdsError>
428where
429    RS: Read + Seek,
430{
431    let mut cells = Vec::new();
432    let mut rows_repeats = Vec::new();
433    let mut formulas = Vec::new();
434    let mut cols = Vec::new();
435    let mut buf = Vec::with_capacity(1024);
436    let mut row_buf = Vec::with_capacity(1024);
437    let mut cell_buf = Vec::with_capacity(1024);
438    let mut total_rows = 0;
439    cols.push(0);
440    loop {
441        match reader.read_event_into(&mut buf) {
442            Ok(Event::Start(e)) if e.name() == QName(b"table:table-row") => {
443                let row_repeats = match e.try_get_attribute(b"table:number-rows-repeated")? {
444                    Some(c) => c.decode_and_unescape_value(reader.decoder())?.parse()?,
445                    None => 1,
446                };
447
448                // Cap row_repeats so total rows don't exceed MAX_ROWS
449                let remaining_rows = (MAX_ROWS as usize).saturating_sub(total_rows);
450                let capped_repeats = row_repeats.min(remaining_rows);
451                if capped_repeats < row_repeats {
452                    warn!(
453                        "ods row repeat count capped ({row_repeats} -> {capped_repeats}, max rows {MAX_ROWS})"
454                    );
455                }
456                total_rows = total_rows.saturating_add(capped_repeats);
457
458                read_row(
459                    reader,
460                    &mut row_buf,
461                    &mut cell_buf,
462                    &mut cells,
463                    &mut formulas,
464                )?;
465                cols.push(cells.len());
466                rows_repeats.push(capped_repeats);
467            }
468            Ok(Event::End(e)) if e.name() == QName(b"table:table") => break,
469            Err(e) => return Err(OdsError::Xml(e)),
470            Ok(_) => (),
471        }
472        buf.clear();
473    }
474    Ok((
475        get_range(cells, &cols, &rows_repeats)?,
476        get_range(formulas, &cols, &rows_repeats)?,
477    ))
478}
479
480fn is_empty_row<T: Default + Clone + PartialEq>(row: &[T]) -> bool {
481    row.iter().all(|x| x == &T::default())
482}
483
484fn get_range<T: Default + Clone + PartialEq>(
485    mut cells: Vec<T>,
486    cols: &[usize],
487    rows_repeats: &[usize],
488) -> Result<Range<T>, OdsError> {
489    // find smallest area with non empty Cells
490    let mut row_min = None;
491    let mut row_max = 0;
492    let mut col_min = usize::MAX;
493    let mut col_max = 0;
494    let mut first_empty_rows_repeated = 0;
495    {
496        for (i, w) in cols.windows(2).enumerate() {
497            let row = &cells[w[0]..w[1]];
498            if let Some(p) = row.iter().position(|c| c != &T::default()) {
499                if row_min.is_none() {
500                    row_min = Some(i);
501                    first_empty_rows_repeated =
502                        rows_repeats.iter().take(i).sum::<usize>().saturating_sub(i);
503                }
504                row_max = i;
505                if p < col_min {
506                    col_min = p;
507                }
508                if let Some(p) = row.iter().rposition(|c| c != &T::default()) {
509                    if p > col_max {
510                        col_max = p;
511                    }
512                }
513            }
514        }
515    }
516    let Some(row_min) = row_min else {
517        return Ok(Range::default());
518    };
519
520    // rebuild cells into its smallest non empty area
521    let row_width = col_max + 1 - col_min;
522    let cells_len = (row_max + 1 - row_min) * row_width;
523    {
524        let mut new_cells = Vec::with_capacity(cells_len.min(MAX_CELLS));
525        let empty_cells = vec![T::default(); col_max + 1];
526        let mut empty_row_repeats = 0_usize;
527        let mut consecutive_empty_rows = 0_usize;
528        for (w, row_repeats) in cols
529            .windows(2)
530            .skip(row_min)
531            .take(row_max + 1)
532            .zip(rows_repeats.iter().skip(row_min).take(row_max + 1))
533        {
534            let row = &cells[w[0]..w[1]];
535            let row_repeats = *row_repeats;
536
537            if is_empty_row(row) {
538                empty_row_repeats = empty_row_repeats.saturating_add(row_repeats);
539                consecutive_empty_rows += 1;
540                continue;
541            }
542
543            if empty_row_repeats > 0 {
544                // Check if expanding empty rows would exceed MAX_CELLS
545                let cells_to_add = empty_row_repeats.saturating_mul(row_width);
546                if new_cells.len().saturating_add(cells_to_add) > MAX_CELLS {
547                    return Err(OdsError::CellLimitExceeded {
548                        requested: new_cells.len().saturating_add(cells_to_add),
549                        max: MAX_CELLS,
550                    });
551                }
552                row_max = row_max + empty_row_repeats - consecutive_empty_rows;
553                for _ in 0..empty_row_repeats {
554                    new_cells.extend_from_slice(&empty_cells[col_min..]);
555                }
556                empty_row_repeats = 0;
557                consecutive_empty_rows = 0;
558            }
559
560            if row_repeats > 1 {
561                row_max = row_max + row_repeats - 1;
562            }
563
564            // Check if expanding this row would exceed MAX_CELLS
565            let cells_to_add = row_repeats.saturating_mul(row_width);
566            if new_cells.len().saturating_add(cells_to_add) > MAX_CELLS {
567                return Err(OdsError::CellLimitExceeded {
568                    requested: new_cells.len().saturating_add(cells_to_add),
569                    max: MAX_CELLS,
570                });
571            }
572
573            for _ in 0..row_repeats {
574                match row.len().cmp(&(col_max + 1)) {
575                    std::cmp::Ordering::Less => {
576                        new_cells.extend_from_slice(&row[col_min..]);
577                        new_cells.extend_from_slice(&empty_cells[row.len()..]);
578                    }
579                    std::cmp::Ordering::Equal => {
580                        new_cells.extend_from_slice(&row[col_min..]);
581                    }
582                    std::cmp::Ordering::Greater => {
583                        new_cells.extend_from_slice(&row[col_min..=col_max]);
584                    }
585                }
586            }
587        }
588        cells = new_cells;
589    }
590    let row_min = row_min + first_empty_rows_repeated;
591    let row_max = row_max + first_empty_rows_repeated;
592    Ok(Range {
593        start: (row_min as u32, col_min as u32),
594        end: (row_max as u32, col_max as u32),
595        inner: cells,
596    })
597}
598
599fn read_row<RS>(
600    reader: &mut OdsReader<'_, RS>,
601    row_buf: &mut Vec<u8>,
602    cell_buf: &mut Vec<u8>,
603    cells: &mut Vec<Data>,
604    formulas: &mut Vec<String>,
605) -> Result<(), OdsError>
606where
607    RS: Read + Seek,
608{
609    let mut empty_col_repeats = 0;
610    let row_start = cells.len();
611    loop {
612        row_buf.clear();
613        match reader.read_event_into(row_buf) {
614            Ok(Event::Start(e))
615                if e.name() == QName(b"table:table-cell")
616                    || e.name() == QName(b"table:covered-table-cell") =>
617            {
618                let mut repeats = 1;
619                for a in e.attributes() {
620                    let a = a?;
621                    if a.key == QName(b"table:number-columns-repeated") {
622                        repeats = reader.decoder().decode(&a.value)?.parse()?;
623                        break;
624                    }
625                }
626
627                let (value, formula, is_closed) = get_datatype(reader, e.attributes(), cell_buf)?;
628
629                // Cap empty_col_repeats to not exceed MAX_COLUMNS
630                let current_cols = cells.len() - row_start;
631                let remaining = (MAX_COLUMNS as usize).saturating_sub(current_cols);
632                let capped_empty = empty_col_repeats.min(remaining);
633                if capped_empty < empty_col_repeats {
634                    warn!(
635                        "ods column repeat count capped ({empty_col_repeats} -> {capped_empty}, max columns {MAX_COLUMNS})"
636                    );
637                }
638
639                for _ in 0..capped_empty {
640                    cells.push(Data::Empty);
641                    formulas.push("".to_string());
642                }
643                empty_col_repeats = 0;
644
645                // Cap repeats to not exceed MAX_COLUMNS
646                let current_cols = cells.len() - row_start;
647                let remaining = (MAX_COLUMNS as usize).saturating_sub(current_cols);
648                let capped_repeats = repeats.min(remaining);
649                if capped_repeats < repeats {
650                    warn!(
651                        "ods column repeat count capped ({repeats} -> {capped_repeats}, max columns {MAX_COLUMNS})"
652                    );
653                }
654
655                if value.is_empty() && formula.is_empty() {
656                    empty_col_repeats = capped_repeats;
657                } else {
658                    for _ in 0..capped_repeats {
659                        cells.push(value.clone());
660                        formulas.push(formula.clone());
661                    }
662                }
663                if !is_closed {
664                    reader.read_to_end_into(e.name(), cell_buf)?;
665                }
666            }
667            Ok(Event::End(e)) if e.name() == QName(b"table:table-row") => break,
668            Err(e) => return Err(OdsError::Xml(e)),
669            Ok(e) => {
670                return Err(OdsError::Mismatch {
671                    expected: "table-cell",
672                    found: format!("{e:?}"),
673                });
674            }
675        }
676    }
677    Ok(())
678}
679
680/// Converts table-cell element into a `Data`
681///
682/// ODF 1.2-19.385
683fn get_datatype<RS>(
684    reader: &mut OdsReader<'_, RS>,
685    atts: Attributes<'_>,
686    buf: &mut Vec<u8>,
687) -> Result<(Data, String, bool), OdsError>
688where
689    RS: Read + Seek,
690{
691    let mut is_string = false;
692    let mut is_value_set = false;
693    let mut val = Data::Empty;
694    let mut formula = String::new();
695    for a in atts {
696        let a = a?;
697        match a.key {
698            QName(b"office:value") if !is_value_set => {
699                let v = reader.decoder().decode(&a.value)?;
700                val = Data::Float(v.parse()?);
701                is_value_set = true;
702            }
703            QName(b"office:string-value" | b"office:date-value" | b"office:time-value")
704                if !is_value_set =>
705            {
706                let attr = a.decode_and_unescape_value(reader.decoder())?.to_string();
707                val = match a.key {
708                    QName(b"office:date-value") => Data::DateTimeIso(attr),
709                    QName(b"office:time-value") => Data::DurationIso(attr),
710                    _ => Data::String(attr),
711                };
712                is_value_set = true;
713            }
714            QName(b"office:boolean-value") if !is_value_set => {
715                let b = &*a.value == b"TRUE" || &*a.value == b"true";
716                val = Data::Bool(b);
717                is_value_set = true;
718            }
719            QName(b"office:value-type") if !is_value_set => is_string = &*a.value == b"string",
720            QName(b"table:formula") => {
721                formula = a.decode_and_unescape_value(reader.decoder())?.to_string();
722            }
723            _ => (),
724        }
725    }
726    if !is_value_set && is_string {
727        // If the value type is string and the office:string-value attribute
728        // is not present, the element content defines the value.
729        let mut s = String::new();
730        let mut first_paragraph = true;
731        loop {
732            buf.clear();
733            match reader.read_event_into(buf) {
734                Ok(Event::Text(t)) => {
735                    s.push_str(&t.xml10_content()?);
736                }
737                Ok(Event::GeneralRef(e)) => {
738                    unescape_entity_to_buffer(&e, &mut s)?;
739                }
740                Ok(Event::End(e))
741                    if e.name() == QName(b"table:table-cell")
742                        || e.name() == QName(b"table:covered-table-cell") =>
743                {
744                    return Ok((Data::String(s), formula, true));
745                }
746                Ok(Event::Start(e)) if e.name() == QName(b"office:annotation") => loop {
747                    match reader.read_event_into(buf) {
748                        Ok(Event::End(e)) if e.name() == QName(b"office:annotation") => {
749                            break;
750                        }
751                        Err(e) => return Err(OdsError::Xml(e)),
752                        _ => (),
753                    }
754                },
755                Ok(Event::Start(e)) if e.name() == QName(b"text:p") => {
756                    if first_paragraph {
757                        first_paragraph = false;
758                    } else {
759                        s.push('\n');
760                    }
761                }
762                Ok(Event::Start(e)) if e.name() == QName(b"text:s") => {
763                    let count = match e.try_get_attribute("text:c")? {
764                        Some(c) => c.decode_and_unescape_value(reader.decoder())?.parse()?,
765                        None => 1,
766                    };
767                    for _ in 0..count {
768                        s.push(' ');
769                    }
770                }
771                Err(e) => return Err(OdsError::Xml(e)),
772                Ok(Event::Eof) => return Err(OdsError::Eof("table:table-cell")),
773                _ => (),
774            }
775        }
776    } else {
777        Ok((val, formula, false))
778    }
779}
780
781fn read_named_expressions<RS>(
782    reader: &mut OdsReader<'_, RS>,
783) -> Result<Vec<(String, String)>, OdsError>
784where
785    RS: Read + Seek,
786{
787    let mut defined_names = Vec::new();
788    let mut buf = Vec::with_capacity(512);
789    loop {
790        buf.clear();
791        match reader.read_event_into(&mut buf) {
792            Ok(Event::Start(e))
793                if e.name() == QName(b"table:named-range")
794                    || e.name() == QName(b"table:named-expression") =>
795            {
796                let mut name = String::new();
797                let mut formula = String::new();
798                for a in e.attributes() {
799                    let a = a?;
800                    match a.key {
801                        QName(b"table:name") => {
802                            name = a.decode_and_unescape_value(reader.decoder())?.to_string();
803                        }
804                        QName(b"table:cell-range-address" | b"table:expression") => {
805                            formula = a.decode_and_unescape_value(reader.decoder())?.to_string();
806                        }
807                        _ => (),
808                    }
809                }
810                defined_names.push((name, formula));
811            }
812            Ok(Event::End(e))
813                if e.name() == QName(b"table:named-range")
814                    || e.name() == QName(b"table:named-expression") => {}
815            Ok(Event::End(e)) if e.name() == QName(b"table:named-expressions") => break,
816            Err(e) => return Err(OdsError::Xml(e)),
817            Ok(e) => {
818                return Err(OdsError::Mismatch {
819                    expected: "table:named-expressions",
820                    found: format!("{e:?}"),
821                });
822            }
823        }
824    }
825    Ok(defined_names)
826}
827
828// Read pictures.
829#[cfg(feature = "picture")]
830#[allow(clippy::type_complexity)]
831fn read_pictures<RS: Read + Seek>(
832    zip: &mut ZipArchive<RS>,
833) -> Result<Option<Vec<(String, Vec<u8>)>>, OdsError> {
834    let mut pics = Vec::new();
835    for i in 0..zip.len() {
836        let mut zfile = zip.by_index(i)?;
837        let zname = zfile.name();
838        // no Thumbnails
839        if zname.starts_with("Pictures") {
840            if let Some(ext) = zname.split('.').next_back() {
841                if [
842                    "emf", "wmf", "pict", "jpeg", "jpg", "png", "dib", "gif", "tiff", "eps", "bmp",
843                    "wpg",
844                ]
845                .contains(&ext)
846                {
847                    let ext = ext.to_string();
848                    let mut buf: Vec<u8> = Vec::new();
849                    zfile.read_to_end(&mut buf)?;
850                    pics.push((ext, buf));
851                }
852            }
853        }
854    }
855    if pics.is_empty() {
856        Ok(None)
857    } else {
858        Ok(Some(pics))
859    }
860}