spreadsheet_ods/io/
read.rs

1use crate::sheet_::Header;
2use std::borrow::Cow;
3use std::convert::{TryFrom, TryInto};
4use std::fs::File;
5use std::io::{BufRead, BufReader, Cursor, Read, Seek, Write};
6use std::mem;
7use std::path::Path;
8use std::str::from_utf8;
9
10use chrono::{Duration, NaiveDateTime};
11use quick_xml::events::attributes::Attribute;
12use quick_xml::events::{BytesRef, BytesStart, Event};
13use quick_xml::{Decoder, Reader};
14use zip::ZipArchive;
15
16use crate::attrmap2::AttrMap2;
17use crate::cell_::CellData;
18use crate::condition::{Condition, ValueCondition};
19use crate::config::{Config, ConfigItem, ConfigItemType, ConfigValue};
20use crate::draw::{Annotation, DrawFrame, DrawFrameContent, DrawImage};
21use crate::ds::detach::Detach;
22use crate::error::OdsError;
23use crate::format::{FormatPart, FormatPartType, ValueFormatTrait, ValueStyleMap};
24use crate::io::parse::{
25    parse_bool, parse_currency, parse_datetime, parse_duration, parse_f64, parse_i16, parse_i32,
26    parse_i64, parse_string, parse_u32, parse_visibility, parse_xlink_actuate, parse_xlink_show,
27    parse_xlink_type,
28};
29use crate::io::NamespaceMap;
30use crate::manifest::Manifest;
31use crate::metadata::{
32    MetaAutoReload, MetaDocumentStatistics, MetaHyperlinkBehaviour, MetaTemplate, MetaUserDefined,
33    MetaValue,
34};
35use crate::refs::{parse_cellranges, parse_cellref};
36use crate::sheet::{Grouped, SplitMode};
37use crate::sheet_::{dedup_colheader, CellDataIter, CellDataIterMut, ColHeader, RowHeader};
38use crate::style::stylemap::StyleMap;
39use crate::style::tabstop::TabStop;
40use crate::style::{
41    AnyStyleRef, ColStyle, ColStyleRef, FontFaceDecl, GraphicStyle, HeaderFooter, MasterPage,
42    MasterPageRef, PageStyle, ParagraphStyle, RowStyle, RowStyleRef, RubyStyle, StyleOrigin,
43    StyleUse, TableStyle, TableStyleRef, TextStyle,
44};
45use crate::text::{TextP, TextTag};
46use crate::validation::{MessageType, Validation, ValidationError, ValidationHelp, ValidationRef};
47use crate::workbook::{EventListener, Script};
48use crate::xmltree::XmlTag;
49use crate::{
50    CellStyle, CellStyleRef, Length, Sheet, Value, ValueFormatBoolean, ValueFormatCurrency,
51    ValueFormatDateTime, ValueFormatNumber, ValueFormatPercentage, ValueFormatText,
52    ValueFormatTimeDuration, ValueType, WorkBook,
53};
54
55type OdsXmlReader<'a> = quick_xml::Reader<&'a mut dyn BufRead>;
56
57/// Read options for ods-files.
58#[derive(Debug, Default)]
59pub struct OdsOptions {
60    // parse the content only.
61    content_only: bool,
62    // expand duplicated cells
63    use_repeat_for_cells: bool,
64    // ignore empty cells.
65    ignore_empty_cells: bool,
66}
67
68impl OdsOptions {
69    /// Parse the content only.
70    ///
71    /// Doesn't buffer any extra files and ignores styles etc.
72    /// This saves quite some time if only the cell-data is needed.
73    pub fn content_only(mut self) -> Self {
74        self.content_only = true;
75        self
76    }
77
78    /// Parse everything.
79    ///
80    /// Reads styles and buffers extra files.
81    /// This is the default. If the data will be written again this options
82    /// should be used.
83    pub fn read_styles(mut self) -> Self {
84        self.content_only = false;
85        self
86    }
87
88    /// The value of table:number-columns-repeated is stored as part of the
89    /// cell-data, and the cell-data is not duplicated. The cell-data can
90    /// only be found at the original row/col.
91    ///
92    /// This can save a bit of time when reading, but makes working with the
93    /// data harder. Keeping track of overlapping cells makes this tricky.
94    pub fn use_repeat_for_cells(mut self) -> Self {
95        self.use_repeat_for_cells = true;
96        self
97    }
98
99    /// Cells are cloned based on their table:number-columns-repeated.
100    ///
101    /// This is the default behaviour. The cell-data can be found at each row/col
102    /// that the repeat count includes.
103    ///
104    /// Most of the time the repeat-count is used for empty cells to fill the
105    /// required structure. These completely empty cells are always dumped.
106    ///
107    /// See: ignore_empty_cells().
108    pub fn use_clone_for_cells(mut self) -> Self {
109        self.use_repeat_for_cells = false;
110        self
111    }
112
113    /// Ignores cells without value and formula.
114    ///
115    /// This can be useful, if only the data is needed. If you store such
116    /// a spreadsheet you will lose cell-formating, spans etc.
117    pub fn ignore_empty_cells(mut self) -> Self {
118        self.ignore_empty_cells = true;
119        self
120    }
121
122    /// Reads cells without value and formula.
123    ///
124    /// This is the default behaviour. As such cells can have a style,
125    /// annotations etc. it is recommended to use this option.
126    ///
127    /// Cells without any information, that are only structural are always
128    /// ignored.
129    pub fn read_empty_cells(mut self) -> Self {
130        self.ignore_empty_cells = false;
131        self
132    }
133
134    /// Reads a .ods file.
135    pub fn read_ods<T: Read + Seek>(&self, read: T) -> Result<WorkBook, OdsError> {
136        let zip = ZipArchive::new(read)?;
137        if self.content_only {
138            read_ods_impl_content_only(zip, self)
139        } else {
140            read_ods_impl(zip, self)
141        }
142    }
143
144    /// Reads a flat .fods file.
145    pub fn read_fods<T: BufRead>(&self, mut read: T) -> Result<WorkBook, OdsError> {
146        if self.content_only {
147            read_fods_impl_content_only(&mut read, self)
148        } else {
149            read_fods_impl(&mut read, self)
150        }
151    }
152}
153
154/// Reads an ODS-file from a buffer
155pub fn read_ods_buf(buf: &[u8]) -> Result<WorkBook, OdsError> {
156    let read = Cursor::new(buf);
157    OdsOptions::default().read_ods(read)
158}
159
160/// Reads an ODS-file from a reader
161pub fn read_ods_from<T: Read + Seek>(read: T) -> Result<WorkBook, OdsError> {
162    OdsOptions::default().read_ods(read)
163}
164
165/// Reads an ODS-file.
166pub fn read_ods<P: AsRef<Path>>(path: P) -> Result<WorkBook, OdsError> {
167    let read = BufReader::new(File::open(path.as_ref())?);
168    OdsOptions::default().read_ods(read)
169}
170
171/// Reads an FODS-file from a buffer
172pub fn read_fods_buf(buf: &[u8]) -> Result<WorkBook, OdsError> {
173    let mut read = Cursor::new(buf);
174    OdsOptions::default().read_fods(&mut read)
175}
176
177/// Reads an FODS-file from a reader
178pub fn read_fods_from<T: Read>(read: T) -> Result<WorkBook, OdsError> {
179    let read = BufReader::new(read);
180    OdsOptions::default().read_fods(read)
181}
182
183/// Reads an FODS-file.
184pub fn read_fods<P: AsRef<Path>>(path: P) -> Result<WorkBook, OdsError> {
185    let read = BufReader::new(File::open(path.as_ref())?);
186    OdsOptions::default().read_fods(read)
187}
188
189struct OdsContext {
190    book: WorkBook,
191
192    decoder: Decoder,
193
194    #[allow(dead_code)]
195    content_only: bool,
196    use_repeat_for_cells: bool,
197    ignore_empty_cells: bool,
198
199    buffers: Vec<Vec<u8>>,
200    xml_buffer: Vec<XmlTag>,
201    col_group_buffer: Vec<Grouped>,
202    row_group_buffer: Vec<Grouped>,
203}
204
205impl OdsContext {
206    fn new(options: &OdsOptions) -> Self {
207        Self {
208            book: Default::default(),
209            decoder: Reader::from_reader(Vec::<u8>::new().as_slice()).decoder(),
210            content_only: options.content_only,
211            use_repeat_for_cells: options.use_repeat_for_cells,
212            ignore_empty_cells: options.ignore_empty_cells,
213
214            buffers: vec![],
215            xml_buffer: vec![],
216            col_group_buffer: vec![],
217            row_group_buffer: vec![],
218        }
219    }
220
221    fn pop_xml_buf(&mut self) -> Vec<XmlTag> {
222        mem::take(&mut self.xml_buffer)
223    }
224
225    fn push_xml_buf(&mut self, mut buf: Vec<XmlTag>) {
226        buf.clear();
227        self.xml_buffer = buf;
228    }
229
230    fn pop_colgroup_buf(&mut self) -> Vec<Grouped> {
231        mem::take(&mut self.col_group_buffer)
232    }
233
234    fn push_colgroup_buf(&mut self, mut buf: Vec<Grouped>) {
235        buf.clear();
236        self.col_group_buffer = buf;
237    }
238
239    fn pop_rowgroup_buf(&mut self) -> Vec<Grouped> {
240        mem::take(&mut self.row_group_buffer)
241    }
242
243    fn push_rowgroup_buf(&mut self, mut buf: Vec<Grouped>) {
244        buf.clear();
245        self.row_group_buffer = buf;
246    }
247
248    // Return a temporary buffer.
249    fn pop_buf(&mut self) -> Vec<u8> {
250        self.buffers.pop().unwrap_or_default()
251    }
252
253    // Give back a buffer to be reused later.
254    fn push_buf(&mut self, mut buf: Vec<u8>) {
255        buf.clear();
256        self.buffers.push(buf);
257    }
258}
259
260fn read_fods_impl(read: &mut dyn BufRead, options: &OdsOptions) -> Result<WorkBook, OdsError> {
261    let mut ctx = OdsContext::new(options);
262    let mut xml = quick_xml::Reader::from_reader(read);
263
264    let mut buf = ctx.pop_buf();
265    loop {
266        let evt = xml.read_event_into(&mut buf)?;
267        if cfg!(feature = "dump_xml") {
268            println!("read_fods_content {:?}", evt);
269        }
270
271        match &evt {
272            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:document" => {
273                let (version, xmlns) = read_namespaces_and_version(&mut ctx, xml_tag)?;
274                ctx.book.xmlns.insert("fods.xml".to_string(), xmlns);
275                if let Some(version) = version {
276                    ctx.book.set_version(version);
277                }
278            }
279            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:document" => {}
280
281            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:meta" => {
282                read_office_meta(&mut ctx, &mut xml)?;
283            }
284            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:settings" => {
285                read_office_settings(&mut ctx, &mut xml)?;
286            }
287            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:scripts" => {
288                read_scripts(&mut ctx, &mut xml)?
289            }
290            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:font-face-decls" => {
291                read_office_font_face_decls(&mut ctx, &mut xml, StyleOrigin::Content)?
292            }
293            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:styles" => {
294                read_office_styles(&mut ctx, &mut xml, StyleOrigin::Content)?
295            }
296            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:automatic-styles" => {
297                read_office_automatic_styles(&mut ctx, &mut xml, StyleOrigin::Content)?
298            }
299            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:master-styles" => {
300                read_office_master_styles(&mut ctx, &mut xml, StyleOrigin::Content)?
301            }
302            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:body" => {
303                read_office_body(&mut ctx, &mut xml)?;
304            }
305
306            Event::Decl(_) => {}
307            Event::Eof => {
308                break;
309            }
310            _ => {
311                unused_event("read_fods_content", &evt)?;
312            }
313        }
314    }
315    ctx.push_buf(buf);
316
317    calculations(&mut ctx)?;
318
319    // We do some data duplication here, to make everything easier to use.
320    calc_derived(&mut ctx.book)?;
321
322    Ok(ctx.book)
323}
324
325fn read_fods_impl_content_only(
326    read: &mut dyn BufRead,
327    options: &OdsOptions,
328) -> Result<WorkBook, OdsError> {
329    let mut ctx = OdsContext::new(options);
330    let mut xml: quick_xml::Reader<&mut dyn BufRead> = quick_xml::Reader::from_reader(read);
331
332    let mut buf = ctx.pop_buf();
333    loop {
334        let evt = xml.read_event_into(&mut buf)?;
335        if cfg!(feature = "dump_xml") {
336            println!("read_fods_content_only {:?}", evt);
337        }
338
339        match &evt {
340            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:body" => {
341                read_office_body(&mut ctx, &mut xml)?;
342            }
343            Event::Eof => {
344                break;
345            }
346            _ => {
347                // a lot is ignored.
348            }
349        }
350    }
351    ctx.push_buf(buf);
352
353    calculations(&mut ctx)?;
354
355    Ok(ctx.book)
356}
357
358/// Reads an ODS-file.
359fn read_ods_impl<R: Read + Seek>(
360    mut zip: ZipArchive<R>,
361    options: &OdsOptions,
362) -> Result<WorkBook, OdsError> {
363    let mut ctx = OdsContext::new(options);
364
365    if let Ok(z) = zip.by_name("META-INF/manifest.xml") {
366        let mut read = BufReader::new(z);
367        let read: &mut dyn BufRead = &mut read;
368        let mut xml = quick_xml::Reader::from_reader(read);
369
370        read_ods_manifest(&mut ctx, &mut xml)?;
371    }
372
373    read_ods_extras(&mut ctx, &mut zip)?;
374
375    if let Ok(z) = zip.by_name("meta.xml") {
376        let mut read = BufReader::new(z);
377        let read: &mut dyn BufRead = &mut read;
378        let mut xml = quick_xml::Reader::from_reader(read);
379
380        read_ods_metadata(&mut ctx, &mut xml)?;
381    }
382
383    if let Ok(z) = zip.by_name("settings.xml") {
384        let mut read = BufReader::new(z);
385        let read: &mut dyn BufRead = &mut read;
386        let mut xml = quick_xml::Reader::from_reader(read);
387        read_ods_settings(&mut ctx, &mut xml)?;
388    }
389
390    if let Ok(z) = zip.by_name("styles.xml") {
391        let mut read = BufReader::new(z);
392        let read: &mut dyn BufRead = &mut read;
393        let mut xml = quick_xml::Reader::from_reader(read);
394        read_ods_styles(&mut ctx, &mut xml)?;
395    }
396
397    {
398        let mut read = BufReader::new(zip.by_name("content.xml")?);
399        let read: &mut dyn BufRead = &mut read;
400        let mut xml = quick_xml::Reader::from_reader(read);
401        read_ods_content(&mut ctx, &mut xml)?;
402    }
403
404    calculations(&mut ctx)?;
405
406    // We do some data duplication here, to make everything easier to use.
407    calc_derived(&mut ctx.book)?;
408
409    Ok(ctx.book)
410}
411
412/// Reads an ODS-file.
413fn read_ods_impl_content_only<R: Read + Seek>(
414    mut zip: ZipArchive<R>,
415    options: &OdsOptions,
416) -> Result<WorkBook, OdsError> {
417    let mut ctx = OdsContext::new(options);
418
419    let mut read = BufReader::new(zip.by_name("content.xml")?);
420    let read: &mut dyn BufRead = &mut read;
421    let mut xml = quick_xml::Reader::from_reader(read);
422
423    // todo: this still reads styles etc from content.xml
424    read_ods_content(&mut ctx, &mut xml)?;
425
426    calculations(&mut ctx)?;
427
428    Ok(ctx.book)
429}
430
431fn read_ods_extras<R: Read + Seek>(
432    ctx: &mut OdsContext,
433    zip: &mut ZipArchive<R>,
434) -> Result<(), OdsError> {
435    // now the data if needed ...
436    for manifest in ctx.book.manifest.values_mut().filter(|v| !v.is_dir()) {
437        if !matches!(
438            manifest.full_path.as_str(),
439            "/" | "settings.xml" | "styles.xml" | "content.xml" | "meta.xml"
440        ) {
441            let mut ze = zip.by_name(manifest.full_path.as_str())?;
442            let mut buf = Vec::new();
443            ze.read_to_end(&mut buf)?;
444            manifest.buffer = Some(buf);
445        }
446    }
447
448    Ok(())
449}
450
451fn read_ods_manifest(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
452    let mut buf = ctx.pop_buf();
453    loop {
454        let evt = xml.read_event_into(&mut buf)?;
455        match &evt {
456            Event::Decl(_) => {}
457
458            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"manifest:manifest" => {}
459            Event::End(xml_tag) if xml_tag.name().as_ref() == b"manifest:manifest" => {}
460
461            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"manifest:file-entry" => {
462                let mut manifest = Manifest::default();
463
464                for attr in xml_tag.attributes().with_checks(false) {
465                    let attr = attr?;
466
467                    if attr.key.as_ref() == b"manifest:full-path" {
468                        manifest.full_path =
469                            attr.decode_and_unescape_value(ctx.decoder)?.to_string();
470                    } else if attr.key.as_ref() == b"manifest:version" {
471                        manifest.version =
472                            Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
473                    } else if attr.key.as_ref() == b"manifest:media-type" {
474                        manifest.media_type =
475                            attr.decode_and_unescape_value(ctx.decoder)?.to_string();
476                    }
477                }
478
479                // some files shouldn't be in the manifest
480                if manifest.full_path != "mimetype" && manifest.full_path != "META-INF/manifest.xml"
481                {
482                    ctx.book.add_manifest(manifest);
483                }
484            }
485            Event::Eof => {
486                break;
487            }
488            _ => {
489                unused_event("read_manifest", &evt)?;
490            }
491        }
492        buf.clear();
493    }
494    ctx.push_buf(buf);
495    Ok(())
496}
497
498// Clone cell-data.
499fn calculations(ctx: &mut OdsContext) -> Result<(), OdsError> {
500    for i in 0..ctx.book.num_sheets() {
501        dedup_colheader(ctx.book.sheet_mut(i))?;
502        if ctx.use_repeat_for_cells {
503            calc_repeat_sheet(ctx.book.sheet_mut(i))?;
504        } else {
505            calc_cloned_sheet(ctx.book.sheet_mut(i))?;
506        }
507    }
508    Ok(())
509}
510
511// Cleanup repeat cell-data.
512fn calc_repeat_sheet(sheet: &mut Sheet) -> Result<(), OdsError> {
513    let mut dropped = Vec::new();
514
515    // clone by row-repeat
516
517    // last two rows often have insane repeat values. clear now.
518    for (_row, rh) in sheet.row_header.iter_mut().rev().take(5) {
519        if rh.repeat > 1000 {
520            rh.repeat = 1;
521        }
522    }
523
524    // clone by cell-repeat
525    let mut it = CellDataIterMut::new(sheet.data.range_mut(..));
526    loop {
527        let Some(((row, col), data)) = it.next() else {
528            break;
529        };
530
531        if data.repeat > 1 {
532            let last_in_row = if let Some((next_row, _next_col)) = it.peek_cell() {
533                row != next_row
534            } else {
535                true
536            };
537            if last_in_row && data.is_empty() {
538                // skip on empty last cell. this is just an editing artifact.
539                dropped.push((row, col));
540                continue;
541            }
542        }
543    }
544    for (row, col) in dropped {
545        sheet.data.remove(&(row, col));
546    }
547
548    Ok(())
549}
550
551// Clone cell-data.
552fn calc_cloned_sheet(sheet: &mut Sheet) -> Result<(), OdsError> {
553    let mut cloned = Vec::new();
554    let mut dropped = Vec::new();
555
556    // clone by row-repeat
557
558    // last two rows often have insane repeat values. clear now.
559    for (_row, rh) in sheet.row_header.iter_mut().rev().take(5) {
560        if rh.repeat > 1000 {
561            rh.repeat = 1;
562        }
563    }
564    // duplicate by row-repeat
565    for (row, rh) in sheet.row_header.iter().filter(|(_, v)| v.repeat > 1) {
566        // get one row
567        let cit = CellDataIter::new(sheet.data.range((*row, 0)..(row + 1, 0)));
568        for ((row, col), data) in cit {
569            for i in 1..rh.repeat {
570                cloned.push((row + i, col, data.clone()));
571            }
572        }
573    }
574    for (row, col, data) in cloned.drain(..) {
575        sheet.data.insert((row, col), data);
576    }
577    // after the previous operation the repeat value is reduced to a span where
578    // the header-values are valid. no longer denotes repeated row-data.
579    for (_row, rh) in sheet.row_header.iter_mut() {
580        mem::swap(&mut rh.repeat, &mut rh.span);
581    }
582
583    // clone by cell-repeat
584
585    let mut it = CellDataIterMut::new(sheet.data.range_mut(..));
586    loop {
587        let Some(((row, col), data)) = it.next() else {
588            break;
589        };
590
591        if data.repeat > 1 {
592            let repeat = mem::replace(&mut data.repeat, 1);
593
594            let last_in_row = if let Some((next_row, _next_col)) = it.peek_cell() {
595                row != next_row
596            } else {
597                true
598            };
599            if last_in_row && data.is_empty() {
600                // skip on empty last cell. this is just an editing artifact.
601                dropped.push((row, col));
602                continue;
603            }
604
605            for i in 1..repeat {
606                cloned.push((row, col + i, data.clone()));
607            }
608        }
609    }
610    for (row, col) in dropped {
611        sheet.data.remove(&(row, col));
612    }
613    for (row, col, data) in cloned {
614        sheet.data.insert((row, col), data);
615    }
616
617    Ok(())
618}
619
620// Sets some values from the styles on the corresponding data fields.
621fn calc_derived(book: &mut WorkBook) -> Result<(), OdsError> {
622    let v = book
623        .config
624        .get_value(&["ooo:view-settings", "Views", "0", "ActiveTable"]);
625    if let Some(ConfigValue::String(n)) = v {
626        book.config_mut().active_table = n.clone();
627    }
628    let v = book
629        .config
630        .get_value(&["ooo:view-settings", "Views", "0", "HasSheetTabs"]);
631    if let Some(ConfigValue::Boolean(n)) = v {
632        book.config_mut().has_sheet_tabs = *n;
633    }
634    let v = book
635        .config
636        .get_value(&["ooo:view-settings", "Views", "0", "ShowGrid"]);
637    if let Some(ConfigValue::Boolean(n)) = v {
638        book.config_mut().show_grid = *n;
639    }
640    let v = book
641        .config
642        .get_value(&["ooo:view-settings", "Views", "0", "ShowPageBreaks"]);
643    if let Some(ConfigValue::Boolean(n)) = v {
644        book.config_mut().show_page_breaks = *n;
645    }
646
647    for i in 0..book.num_sheets() {
648        let mut sheet = book.detach_sheet(i);
649
650        // Set the column widths.
651        for ch in sheet.col_header.values_mut() {
652            if let Some(style_name) = &ch.style {
653                if let Some(style) = book.colstyle(style_name) {
654                    if style.use_optimal_col_width()? {
655                        ch.width = Length::Default;
656                    } else {
657                        ch.width = style.col_width()?;
658                    }
659                }
660            }
661        }
662
663        // Set the row heights
664        for rh in sheet.row_header.values_mut() {
665            if let Some(style_name) = &rh.style {
666                if let Some(style) = book.rowstyle(style_name) {
667                    if style.use_optimal_row_height()? {
668                        rh.height = Length::Default;
669                    } else {
670                        rh.height = style.row_height()?;
671                    }
672                }
673            }
674        }
675
676        let v = book.config.get(&[
677            "ooo:view-settings",
678            "Views",
679            "0",
680            "Tables",
681            sheet.name().as_str(),
682        ]);
683
684        if let Some(cc) = v {
685            if let Some(ConfigValue::Int(n)) = cc.get_value_rec(&["CursorPositionX"]) {
686                sheet.config_mut().cursor_x = *n as u32;
687            }
688            if let Some(ConfigValue::Int(n)) = cc.get_value_rec(&["CursorPositionY"]) {
689                sheet.config_mut().cursor_y = *n as u32;
690            }
691            if let Some(ConfigValue::Short(n)) = cc.get_value_rec(&["HorizontalSplitMode"]) {
692                sheet.config_mut().hor_split_mode = SplitMode::try_from(*n)?;
693            }
694            if let Some(ConfigValue::Short(n)) = cc.get_value_rec(&["VerticalSplitMode"]) {
695                sheet.config_mut().vert_split_mode = SplitMode::try_from(*n)?;
696            }
697            if let Some(ConfigValue::Int(n)) = cc.get_value_rec(&["HorizontalSplitPosition"]) {
698                sheet.config_mut().hor_split_pos = *n as u32;
699            }
700            if let Some(ConfigValue::Int(n)) = cc.get_value_rec(&["VerticalSplitPosition"]) {
701                sheet.config_mut().vert_split_pos = *n as u32;
702            }
703            if let Some(ConfigValue::Short(n)) = cc.get_value_rec(&["ActiveSplitRange"]) {
704                sheet.config_mut().active_split_range = *n;
705            }
706            if let Some(ConfigValue::Int(n)) = cc.get_value_rec(&["PositionLeft"]) {
707                sheet.config_mut().position_left = *n as u32;
708            }
709            if let Some(ConfigValue::Int(n)) = cc.get_value_rec(&["PositionRight"]) {
710                sheet.config_mut().position_right = *n as u32;
711            }
712            if let Some(ConfigValue::Int(n)) = cc.get_value_rec(&["PositionTop"]) {
713                sheet.config_mut().position_top = *n as u32;
714            }
715            if let Some(ConfigValue::Int(n)) = cc.get_value_rec(&["PositionBottom"]) {
716                sheet.config_mut().position_bottom = *n as u32;
717            }
718            if let Some(ConfigValue::Short(n)) = cc.get_value_rec(&["ZoomType"]) {
719                sheet.config_mut().zoom_type = *n;
720            }
721            if let Some(ConfigValue::Int(n)) = cc.get_value_rec(&["ZoomValue"]) {
722                sheet.config_mut().zoom_value = *n;
723            }
724            if let Some(ConfigValue::Boolean(n)) = cc.get_value_rec(&["ShowGrid"]) {
725                sheet.config_mut().show_grid = *n;
726            }
727        }
728
729        book.attach_sheet(sheet);
730    }
731
732    Ok(())
733}
734
735// Reads the content.xml
736fn read_ods_content(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
737    let mut buf = ctx.pop_buf();
738    loop {
739        let evt = xml.read_event_into(&mut buf)?;
740        if cfg!(feature = "dump_xml") {
741            println!(" read_ods_content {:?}", evt);
742        }
743        match &evt {
744            Event::Decl(_) => {}
745
746            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:document-content" => {
747                let (version, xmlns) = read_namespaces_and_version(ctx, xml_tag)?;
748                if let Some(version) = version {
749                    ctx.book.set_version(version);
750                }
751                ctx.book.xmlns.insert("content.xml".to_string(), xmlns);
752            }
753            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:document-content" => {}
754
755            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"office:scripts" => {}
756            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:scripts" => {
757                read_scripts(ctx, xml)?
758            }
759            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:font-face-decls" => {
760                read_office_font_face_decls(ctx, xml, StyleOrigin::Content)?
761            }
762            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:styles" => {
763                read_office_styles(ctx, xml, StyleOrigin::Content)?
764            }
765            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:automatic-styles" => {
766                read_office_automatic_styles(ctx, xml, StyleOrigin::Content)?
767            }
768            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:master-styles" => {
769                read_office_master_styles(ctx, xml, StyleOrigin::Content)?
770            }
771            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:body" => {
772                read_office_body(ctx, xml)?;
773            }
774
775            Event::Eof => {
776                break;
777            }
778            _ => {
779                unused_event("read_ods_content", &evt)?;
780            }
781        }
782
783        buf.clear();
784    }
785    ctx.push_buf(buf);
786
787    Ok(())
788}
789
790// Reads the content.xml
791fn read_office_body(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
792    let mut buf = ctx.pop_buf();
793    loop {
794        let evt = xml.read_event_into(&mut buf)?;
795        let empty_tag = matches!(evt, Event::Empty(_));
796        if cfg!(feature = "dump_xml") {
797            println!("read_office_body {:?}", evt);
798        }
799        match &evt {
800            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:body" => {}
801            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:body" => {
802                break;
803            }
804
805            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:spreadsheet" => {}
806            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:spreadsheet" => {}
807
808            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:content-validations" => {
809                read_validations(ctx, xml)?
810            }
811            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:table" => {
812                read_table(ctx, xml, xml_tag)?
813            }
814
815            // from the prelude
816            Event::Empty(xml_tag) | Event::Start(xml_tag)
817                if xml_tag.name().as_ref() == b"table:calculation-settings"
818                    || xml_tag.name().as_ref() == b"table:label-ranges"
819                    || xml_tag.name().as_ref() == b"table:tracked-changes"
820                    || xml_tag.name().as_ref() == b"text:alphabetical-index-auto-mark-file"
821                    || xml_tag.name().as_ref() == b"text:dde-connection-decls"
822                    || xml_tag.name().as_ref() == b"text:sequence-decls"
823                    || xml_tag.name().as_ref() == b"text:user-field-decls"
824                    || xml_tag.name().as_ref() == b"text:variable-decls" =>
825            {
826                let v = read_xml(ctx, xml, xml_tag, empty_tag)?;
827                ctx.book.extra.push(v);
828            }
829            // from the epilogue
830            Event::Empty(xml_tag) | Event::Start(xml_tag)
831                if xml_tag.name().as_ref() == b"table:consolidation"
832                    || xml_tag.name().as_ref() == b"table:data-pilot-tables"
833                    || xml_tag.name().as_ref() == b"table:database-ranges"
834                    || xml_tag.name().as_ref() == b"table:dde-links"
835                    || xml_tag.name().as_ref() == b"table:named-expressions"
836                    || xml_tag.name().as_ref() == b"calcext:conditional-formats" =>
837            {
838                let v = read_xml(ctx, xml, xml_tag, empty_tag)?;
839                ctx.book.extra.push(v);
840            }
841            // from the prelude
842            Event::End(xml_tag)
843                if xml_tag.name().as_ref() == b"table:calculation-settings"
844                    || xml_tag.name().as_ref() == b"table:label-ranges"
845                    || xml_tag.name().as_ref() == b"table:tracked-changes"
846                    || xml_tag.name().as_ref() == b"text:alphabetical-index-auto-mark-file"
847                    || xml_tag.name().as_ref() == b"text:dde-connection-decls"
848                    || xml_tag.name().as_ref() == b"text:sequence-decls"
849                    || xml_tag.name().as_ref() == b"text:user-field-decls"
850                    || xml_tag.name().as_ref() == b"text:variable-decls" => {}
851            // from the epilogue
852            Event::End(xml_tag)
853                if xml_tag.name().as_ref() == b"table:consolidation"
854                    || xml_tag.name().as_ref() == b"table:data-pilot-tables"
855                    || xml_tag.name().as_ref() == b"table:database-ranges"
856                    || xml_tag.name().as_ref() == b"table:dde-links"
857                    || xml_tag.name().as_ref() == b"table:named-expressions" => {}
858
859            Event::Eof => {
860                break;
861            }
862            _ => {
863                unused_event("read_office_body", &evt)?;
864            }
865        }
866
867        buf.clear();
868    }
869    ctx.push_buf(buf);
870
871    Ok(())
872}
873
874fn read_namespaces_and_version(
875    ctx: &mut OdsContext,
876    super_tag: &BytesStart<'_>,
877) -> Result<(Option<String>, NamespaceMap), OdsError> {
878    let mut version = None;
879    let mut xmlns = NamespaceMap::new();
880
881    for attr in super_tag.attributes().with_checks(false) {
882        match attr? {
883            attr if attr.key.as_ref() == b"office:version" => {
884                version = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
885            }
886            attr if attr.key.as_ref().starts_with(b"xmlns:") => {
887                let k = from_utf8(attr.key.as_ref())?.to_string();
888                let v = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
889                xmlns.insert(k, v);
890            }
891            attr if attr.key.as_ref() == b"office:mimetype" => {
892                if attr.decode_and_unescape_value(ctx.decoder)?
893                    != "application/vnd.oasis.opendocument.spreadsheet"
894                {
895                    return Err(OdsError::Parse(
896                        "invalid content-type",
897                        Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string()),
898                    ));
899                }
900            }
901            attr => {
902                unused_attr(
903                    "read_namespaces_and_version",
904                    super_tag.name().as_ref(),
905                    &attr,
906                )?;
907            }
908        }
909    }
910    Ok((version, xmlns))
911}
912
913// Reads the table.
914fn read_table(
915    ctx: &mut OdsContext,
916    xml: &mut OdsXmlReader<'_>,
917    super_tag: &BytesStart<'_>,
918) -> Result<(), OdsError> {
919    let mut sheet = Sheet::new("");
920
921    read_table_attr(ctx, &mut sheet, super_tag)?;
922
923    // Cell
924    let mut row: u32 = 0;
925    let mut col: u32 = 0;
926    let mut col_data: bool = false;
927
928    // Columns
929    let mut col_range_from = 0;
930    let mut col_group = ctx.pop_colgroup_buf();
931
932    // Rows
933    let mut row_repeat: u32 = 1;
934    let mut row_range_from = 0;
935    let mut row_group = ctx.pop_rowgroup_buf();
936
937    let mut buf = ctx.pop_buf();
938    loop {
939        let evt = xml.read_event_into(&mut buf)?;
940        let empty_tag = matches!(evt, Event::Empty(_));
941        if cfg!(feature = "dump_xml") {
942            println!(" read_table {:?}", evt);
943        }
944        match &evt {
945            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:table" => {
946                break;
947            }
948
949            // Prelude
950            Event::Start(xml_tag) | Event::Empty(xml_tag)
951                if xml_tag.name().as_ref() == b"table:title"
952                    || xml_tag.name().as_ref() == b"table:desc"
953                    || xml_tag.name().as_ref() == b"table:table-source"
954                    || xml_tag.name().as_ref() == b"office:dde-source"
955                    || xml_tag.name().as_ref() == b"table:scenario"
956                    || xml_tag.name().as_ref() == b"office:forms"
957                    || xml_tag.name().as_ref() == b"table:shapes" =>
958            {
959                sheet.extra.push(read_xml(ctx, xml, xml_tag, empty_tag)?);
960            }
961            Event::End(xml_tag)
962                if xml_tag.name().as_ref() == b"table:title"
963                    || xml_tag.name().as_ref() == b"table:desc"
964                    || xml_tag.name().as_ref() == b"table:table-source"
965                    || xml_tag.name().as_ref() == b"office:dde-source"
966                    || xml_tag.name().as_ref() == b"table:scenario"
967                    || xml_tag.name().as_ref() == b"office:forms"
968                    || xml_tag.name().as_ref() == b"table:shapes" => {}
969
970            // Epilogue
971            Event::Start(xml_tag) | Event::Empty(xml_tag)
972                if xml_tag.name().as_ref() == b"table:named-expressions"
973                    || xml_tag.name().as_ref() == b"calcext:conditional-formats" =>
974            {
975                sheet.extra.push(read_xml(ctx, xml, xml_tag, empty_tag)?);
976            }
977            Event::End(xml_tag)
978                if xml_tag.name().as_ref() == b"table:named-expressions"
979                    || xml_tag.name().as_ref() == b"calcext:conditional-formats" => {}
980
981            //
982            // table columns
983            //
984            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:table-column-group" => {
985                let v = read_table_column_group_attr(col, xml_tag)?;
986                col_group.push(v);
987            }
988            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:table-column-group" => {
989                if let Some(mut v) = col_group.pop() {
990                    v.set_to(col - 1);
991                    sheet.group_cols.push(v);
992                }
993            }
994
995            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:table-header-columns" => {
996                col_range_from = col;
997            }
998            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:table-header-columns" => {
999                if let Some(header_cols) = &mut sheet.header_cols {
1000                    header_cols.to = col - 1;
1001                } else {
1002                    sheet.header_cols = Some(Header {
1003                        from: col_range_from,
1004                        to: col - 1,
1005                    });
1006                }
1007            }
1008
1009            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:table-columns" => {}
1010            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:table-columns" => {}
1011
1012            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"table:table-column" => {
1013                let col_repeat = read_table_col_attr(ctx, &mut sheet, xml_tag, col)?;
1014                col += col_repeat;
1015            }
1016
1017            //
1018            // table rows
1019            //
1020            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:table-row-group" => {
1021                let v = read_table_row_group_attr(row, xml_tag)?;
1022                row_group.push(v);
1023            }
1024            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:table-row-group" => {
1025                if let Some(mut v) = row_group.pop() {
1026                    v.set_to(row - 1);
1027                    sheet.group_rows.push(v);
1028                } else {
1029                    // there are no unbalanced tags.
1030                }
1031            }
1032
1033            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:table-header-rows" => {
1034                row_range_from = row;
1035            }
1036            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:table-header-rows" => {
1037                if let Some(header_rows) = &mut sheet.header_rows {
1038                    header_rows.to = row - 1;
1039                } else {
1040                    sheet.header_rows = Some(Header {
1041                        from: row_range_from,
1042                        to: row - 1,
1043                    });
1044                }
1045            }
1046
1047            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:table-rows" => {
1048                // noop
1049            }
1050            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:table-rows" => {
1051                // noop
1052            }
1053            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:table-row" => {
1054                col = 0;
1055                row_repeat = read_table_row_attr(ctx, &mut sheet, row, xml_tag)?;
1056            }
1057            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:table-row" => {
1058                if col_data {
1059                    // row-repeat is ignored unless there is any cell-data in that row.
1060                    sheet.set_row_repeat(row, row_repeat);
1061                }
1062                row += row_repeat;
1063                row_repeat = 1;
1064                col_data = false;
1065            }
1066
1067            //
1068            // table cells
1069            //
1070            Event::Empty(xml_tag) | Event::Start(xml_tag)
1071                if xml_tag.name().as_ref() == b"table:table-cell"
1072                    || xml_tag.name().as_ref() == b"table:covered-table-cell" =>
1073            {
1074                let (cell_repeat, have_data) =
1075                    read_table_cell(ctx, xml, &mut sheet, row, col, xml_tag, empty_tag)?;
1076                col += cell_repeat;
1077                col_data |= have_data;
1078            }
1079
1080            _ => {
1081                unused_event("read_table", &evt)?;
1082            }
1083        }
1084        buf.clear();
1085    }
1086
1087    ctx.push_buf(buf);
1088    ctx.push_colgroup_buf(col_group);
1089    ctx.push_rowgroup_buf(row_group);
1090
1091    ctx.book.push_sheet(sheet);
1092
1093    Ok(())
1094}
1095
1096// Reads the table attributes.
1097fn read_table_attr(
1098    ctx: &mut OdsContext,
1099    sheet: &mut Sheet,
1100    super_tag: &BytesStart<'_>,
1101) -> Result<(), OdsError> {
1102    for attr in super_tag.attributes().with_checks(false) {
1103        match attr? {
1104            attr if attr.key.as_ref() == b"table:name" => {
1105                sheet.set_name(attr.decode_and_unescape_value(ctx.decoder)?);
1106            }
1107            attr if attr.key.as_ref() == b"table:style-name" => {
1108                let name = &attr.decode_and_unescape_value(ctx.decoder)?;
1109                sheet.style = Some(TableStyleRef::from(name.as_ref()));
1110            }
1111            attr if attr.key.as_ref() == b"table:print" => {
1112                sheet.set_print(parse_bool(&attr.value)?);
1113            }
1114            attr if attr.key.as_ref() == b"table:display" => {
1115                sheet.set_display(parse_bool(&attr.value)?);
1116            }
1117            attr if attr.key.as_ref() == b"table:print-ranges" => {
1118                let v = attr.decode_and_unescape_value(ctx.decoder)?;
1119                sheet.print_ranges = parse_cellranges(v.as_ref())?;
1120            }
1121            attr => {
1122                unused_attr("read_table_attr", super_tag.name().as_ref(), &attr)?;
1123            }
1124        }
1125    }
1126
1127    Ok(())
1128}
1129
1130// Reads table-row attributes. Returns the repeat-count.
1131fn read_table_row_attr(
1132    ctx: &mut OdsContext,
1133    sheet: &mut Sheet,
1134    row: u32,
1135    super_tag: &BytesStart<'_>,
1136) -> Result<u32, OdsError> {
1137    let mut row_repeat: u32 = 1;
1138    let mut row_header = None;
1139
1140    for attr in super_tag.attributes().with_checks(false) {
1141        match attr? {
1142            // table:default-cell-style-name 19.615, table:visibility 19.749 and xml:id 19.914.
1143            attr if attr.key.as_ref() == b"table:number-rows-repeated" => {
1144                row_repeat = parse_u32(&attr.value)?;
1145            }
1146            attr if attr.key.as_ref() == b"table:style-name" => {
1147                let name = attr.decode_and_unescape_value(ctx.decoder)?;
1148                row_header.get_or_insert_with(RowHeader::default).style =
1149                    Some(RowStyleRef::from(name.as_ref()));
1150            }
1151            attr if attr.key.as_ref() == b"table:default-cell-style-name" => {
1152                let name = attr.decode_and_unescape_value(ctx.decoder)?;
1153                row_header.get_or_insert_with(RowHeader::default).cellstyle =
1154                    Some(CellStyleRef::from(name.as_ref()));
1155            }
1156            attr if attr.key.as_ref() == b"table:visibility" => {
1157                let visible = parse_visibility(&attr.value)?;
1158                row_header.get_or_insert_with(RowHeader::default).visible = visible;
1159            }
1160            attr => {
1161                unused_attr("read_table_row_attr", super_tag.name().as_ref(), &attr)?;
1162            }
1163        }
1164    }
1165
1166    if let Some(mut row_header) = row_header {
1167        row_header.repeat = row_repeat;
1168        sheet.row_header.insert(row, row_header);
1169    }
1170
1171    Ok(row_repeat)
1172}
1173
1174// Reads the table:table-column-group attributes.
1175fn read_table_column_group_attr(
1176    table_col: u32,
1177    super_tag: &BytesStart<'_>,
1178) -> Result<Grouped, OdsError> {
1179    let mut display = true;
1180
1181    for attr in super_tag.attributes().with_checks(false) {
1182        match attr? {
1183            attr if attr.key.as_ref() == b"table:display" => {
1184                display = parse_bool(&attr.value)?;
1185            }
1186            attr => {
1187                unused_attr(
1188                    "read_table_column_group_attr",
1189                    super_tag.name().as_ref(),
1190                    &attr,
1191                )?;
1192            }
1193        }
1194    }
1195
1196    Ok(Grouped {
1197        from: table_col,
1198        to: 0,
1199        display,
1200    })
1201}
1202
1203// Reads the table:table-row-group attributes.
1204fn read_table_row_group_attr(row: u32, super_tag: &BytesStart<'_>) -> Result<Grouped, OdsError> {
1205    let mut display = true;
1206
1207    for attr in super_tag.attributes().with_checks(false) {
1208        match attr? {
1209            attr if attr.key.as_ref() == b"table:display" => {
1210                display = parse_bool(&attr.value)?;
1211            }
1212            attr => {
1213                unused_attr(
1214                    "read_table_row_group_attr",
1215                    super_tag.name().as_ref(),
1216                    &attr,
1217                )?;
1218            }
1219        }
1220    }
1221
1222    Ok(Grouped {
1223        from: row,
1224        to: 0,
1225        display,
1226    })
1227}
1228
1229// Reads the table-column attributes. Creates as many copies as indicated.
1230fn read_table_col_attr(
1231    ctx: &mut OdsContext,
1232    sheet: &mut Sheet,
1233    super_tag: &BytesStart<'_>,
1234    table_col: u32,
1235) -> Result<u32, OdsError> {
1236    let mut col_repeat = 1;
1237    let mut col_header = None;
1238
1239    for attr in super_tag.attributes().with_checks(false) {
1240        match attr? {
1241            attr if attr.key.as_ref() == b"table:number-columns-repeated" => {
1242                col_repeat = parse_u32(&attr.value)?;
1243            }
1244            attr if attr.key.as_ref() == b"table:style-name" => {
1245                let name = attr.decode_and_unescape_value(ctx.decoder)?;
1246                col_header.get_or_insert_with(ColHeader::default).style =
1247                    Some(ColStyleRef::from(name.as_ref()));
1248            }
1249            attr if attr.key.as_ref() == b"table:default-cell-style-name" => {
1250                let name = attr.decode_and_unescape_value(ctx.decoder)?;
1251                col_header.get_or_insert_with(ColHeader::default).cellstyle =
1252                    Some(CellStyleRef::from(name.as_ref()));
1253            }
1254            attr if attr.key.as_ref() == b"table:visibility" => {
1255                let visible = parse_visibility(&attr.value)?;
1256                col_header.get_or_insert_with(ColHeader::default).visible = visible;
1257            }
1258            attr => {
1259                unused_attr("read_table_col_attr", super_tag.name().as_ref(), &attr)?;
1260            }
1261        }
1262    }
1263
1264    if let Some(mut col_header) = col_header {
1265        col_header.span = col_repeat;
1266        sheet.col_header.insert(table_col, col_header);
1267    }
1268
1269    Ok(col_repeat)
1270}
1271
1272#[derive(Debug)]
1273#[allow(variant_size_differences)]
1274enum TextContent {
1275    Empty,
1276    Text(String),
1277    Xml(TextTag),
1278    XmlVec(Vec<TextTag>),
1279}
1280
1281#[derive(Debug)]
1282struct ReadTableCell {
1283    val_type: ValueType,
1284    val_datetime: Option<NaiveDateTime>,
1285    val_duration: Option<Duration>,
1286    val_float: Option<f64>,
1287    val_bool: Option<bool>,
1288    val_string: Option<String>,
1289    val_currency: Option<String>,
1290
1291    content: TextContent,
1292}
1293
1294fn read_table_cell(
1295    ctx: &mut OdsContext,
1296    xml: &mut OdsXmlReader<'_>,
1297    sheet: &mut Sheet,
1298    row: u32,
1299    col: u32,
1300    super_tag: &BytesStart<'_>,
1301    empty_tag: bool,
1302) -> Result<(u32, bool), OdsError> {
1303    let mut cell = None;
1304    let mut repeat = 1;
1305
1306    // find default-cell-style for this column.
1307    let default_cellstyle = if let Some(ch) = sheet.valid_col_header(col) {
1308        ch.cellstyle.as_ref()
1309    } else {
1310        None
1311    };
1312
1313    let mut tc = ReadTableCell {
1314        val_type: ValueType::Empty,
1315        val_datetime: None,
1316        val_duration: None,
1317        val_float: None,
1318        val_bool: None,
1319        val_string: None,
1320        val_currency: None,
1321        content: TextContent::Empty,
1322    };
1323
1324    for attr in super_tag.attributes().with_checks(false) {
1325        match attr? {
1326            attr if attr.key.as_ref() == b"table:number-columns-repeated" => {
1327                repeat = parse_u32(&attr.value)?;
1328            }
1329            attr if attr.key.as_ref() == b"table:number-rows-spanned" => {
1330                let row_span = parse_u32(&attr.value)?;
1331                if row_span > 1 {
1332                    cell.get_or_insert_with(CellData::default)
1333                        .extra_mut()
1334                        .span
1335                        .row_span = row_span;
1336                }
1337            }
1338            attr if attr.key.as_ref() == b"table:number-columns-spanned" => {
1339                let col_span = parse_u32(&attr.value)?;
1340                if col_span > 1 {
1341                    cell.get_or_insert_with(CellData::default)
1342                        .extra_mut()
1343                        .span
1344                        .col_span = col_span;
1345                }
1346            }
1347            attr if attr.key.as_ref() == b"table:number-matrix-rows-spanned" => {
1348                let row_span = parse_u32(&attr.value)?;
1349                if row_span > 1 {
1350                    cell.get_or_insert_with(CellData::default)
1351                        .extra_mut()
1352                        .matrix_span
1353                        .row_span = row_span;
1354                }
1355            }
1356            attr if attr.key.as_ref() == b"table:number-matrix-columns-spanned" => {
1357                let col_span = parse_u32(&attr.value)?;
1358                if col_span > 1 {
1359                    cell.get_or_insert_with(CellData::default)
1360                        .extra_mut()
1361                        .matrix_span
1362                        .col_span = col_span;
1363                }
1364            }
1365            attr if attr.key.as_ref() == b"table:content-validation-name" => {
1366                let name = attr.decode_and_unescape_value(ctx.decoder)?;
1367                cell.get_or_insert_with(CellData::default)
1368                    .extra_mut()
1369                    .validation_name = Some(ValidationRef::from(name.as_ref()));
1370            }
1371            attr if attr.key.as_ref() == b"calcext:value-type" => {
1372                // not used. office:value-type seems to be good enough.
1373            }
1374            attr if attr.key.as_ref() == b"office:value-type" => {
1375                cell.get_or_insert_with(CellData::default);
1376                tc.val_type = match attr.value.as_ref() {
1377                    b"string" => ValueType::Text,
1378                    b"float" => ValueType::Number,
1379                    b"percentage" => ValueType::Percentage,
1380                    b"date" => ValueType::DateTime,
1381                    b"time" => ValueType::TimeDuration,
1382                    b"boolean" => ValueType::Boolean,
1383                    b"currency" => ValueType::Currency,
1384                    other => {
1385                        return Err(OdsError::Parse(
1386                            "Unknown cell-type {:?}",
1387                            Some(from_utf8(other)?.into()),
1388                        ));
1389                    }
1390                }
1391            }
1392            attr if attr.key.as_ref() == b"office:date-value" => {
1393                cell.get_or_insert_with(CellData::default);
1394                tc.val_datetime = Some(parse_datetime(&attr.value)?);
1395            }
1396            attr if attr.key.as_ref() == b"office:time-value" => {
1397                cell.get_or_insert_with(CellData::default);
1398                tc.val_duration = Some(parse_duration(&attr.value)?);
1399            }
1400            attr if attr.key.as_ref() == b"office:value" => {
1401                cell.get_or_insert_with(CellData::default);
1402                tc.val_float = Some(parse_f64(&attr.value)?);
1403            }
1404            attr if attr.key.as_ref() == b"office:boolean-value" => {
1405                cell.get_or_insert_with(CellData::default);
1406                tc.val_bool = Some(parse_bool(&attr.value)?);
1407            }
1408            attr if attr.key.as_ref() == b"office:string-value" => {
1409                cell.get_or_insert_with(CellData::default);
1410                tc.val_string = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
1411            }
1412            attr if attr.key.as_ref() == b"office:currency" => {
1413                cell.get_or_insert_with(CellData::default);
1414                tc.val_currency = Some(parse_currency(&attr.value)?);
1415            }
1416            attr if attr.key.as_ref() == b"table:formula" => {
1417                cell.get_or_insert_with(CellData::default).formula =
1418                    Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
1419            }
1420            attr if attr.key.as_ref() == b"table:style-name" => {
1421                let name = attr.decode_and_unescape_value(ctx.decoder)?;
1422                cell.get_or_insert_with(CellData::default).style =
1423                    Some(CellStyleRef::from(name.as_ref()));
1424            }
1425            attr => {
1426                unused_attr("read_table_cell2", super_tag.name().as_ref(), &attr)?;
1427            }
1428        }
1429    }
1430
1431    if !empty_tag {
1432        let mut buf = ctx.pop_buf();
1433        loop {
1434            let evt = xml.read_event_into(&mut buf)?;
1435            if cfg!(feature = "dump_xml") {
1436                println!(" read_table_cell {:?}", evt);
1437            }
1438            match &evt {
1439                Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"text:p" => {}
1440                Event::Start(xml_tag) if xml_tag.name().as_ref() == b"text:p" => {
1441                    let new_txt = read_text_or_tag(ctx, xml, xml_tag, false)?;
1442                    tc.content = append_text(new_txt, tc.content);
1443                }
1444
1445                Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:annotation" => {
1446                    let annotation = read_annotation(ctx, xml, xml_tag)?;
1447                    cell.get_or_insert_with(CellData::default)
1448                        .extra_mut()
1449                        .annotation = Some(annotation);
1450                }
1451                Event::Start(xml_tag) if xml_tag.name().as_ref() == b"draw:frame" => {
1452                    let draw_frame = read_draw_frame(ctx, xml, xml_tag)?;
1453                    cell.get_or_insert_with(CellData::default)
1454                        .extra_mut()
1455                        .draw_frames
1456                        .push(draw_frame);
1457                }
1458
1459                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
1460                    break;
1461                }
1462                Event::Eof => {
1463                    break;
1464                }
1465                _ => {
1466                    unused_event("read_table_cell", &evt)?;
1467                }
1468            }
1469
1470            buf.clear();
1471        }
1472        ctx.push_buf(buf);
1473    }
1474
1475    let have_data = if let Some(mut cell) = cell {
1476        // composes a Value
1477        set_value(tc, &mut cell)?;
1478
1479        // store cell-data
1480        if ignore_cell(ctx, default_cellstyle, &cell) {
1481            false
1482        } else {
1483            cell.repeat = repeat;
1484            sheet.add_cell_data(row, col, cell);
1485            true
1486        }
1487    } else {
1488        false
1489    };
1490
1491    Ok((repeat, have_data))
1492}
1493
1494#[allow(clippy::if_same_then_else)]
1495#[inline]
1496fn ignore_cell(
1497    ctx: &mut OdsContext,
1498    default_cellstyle: Option<&CellStyleRef>,
1499    cell: &CellData,
1500) -> bool {
1501    if cell.is_void(default_cellstyle) {
1502        return true;
1503    }
1504    if ctx.ignore_empty_cells && cell.is_empty() {
1505        return true;
1506    }
1507    false
1508}
1509
1510fn append_text(new_txt: TextContent, mut content: TextContent) -> TextContent {
1511    // There can be multiple text:p elements within the cell.
1512    content = match content {
1513        TextContent::Empty => new_txt,
1514        TextContent::Text(txt) => {
1515            // Have a destructured text:p from before.
1516            // Wrap up and create list.
1517            let p = TextP::new().text(txt).into_xmltag();
1518            let mut vec = vec![p];
1519
1520            match new_txt {
1521                TextContent::Empty => {}
1522                TextContent::Text(txt) => {
1523                    let p2 = TextP::new().text(txt).into_xmltag();
1524                    vec.push(p2);
1525                }
1526                TextContent::Xml(xml) => {
1527                    vec.push(xml);
1528                }
1529                TextContent::XmlVec(_) => {
1530                    unreachable!();
1531                }
1532            }
1533            TextContent::XmlVec(vec)
1534        }
1535        TextContent::Xml(xml) => {
1536            let mut vec = vec![xml];
1537            match new_txt {
1538                TextContent::Empty => {}
1539                TextContent::Text(txt) => {
1540                    let p2 = TextP::new().text(txt).into_xmltag();
1541                    vec.push(p2);
1542                }
1543                TextContent::Xml(xml) => {
1544                    vec.push(xml);
1545                }
1546                TextContent::XmlVec(_) => {
1547                    unreachable!();
1548                }
1549            }
1550            TextContent::XmlVec(vec)
1551        }
1552        TextContent::XmlVec(mut vec) => {
1553            match new_txt {
1554                TextContent::Empty => {}
1555                TextContent::Text(txt) => {
1556                    let p2 = TextP::new().text(txt).into_xmltag();
1557                    vec.push(p2);
1558                }
1559                TextContent::Xml(xml) => {
1560                    vec.push(xml);
1561                }
1562                TextContent::XmlVec(_) => {
1563                    unreachable!();
1564                }
1565            }
1566            TextContent::XmlVec(vec)
1567        }
1568    };
1569
1570    content
1571}
1572
1573#[inline(always)]
1574fn set_value(tc: ReadTableCell, cell: &mut CellData) -> Result<(), OdsError> {
1575    match tc.val_type {
1576        ValueType::Empty => {
1577            // noop
1578        }
1579        ValueType::Boolean => {
1580            if let Some(v) = tc.val_bool {
1581                cell.value = Value::Boolean(v);
1582            } else {
1583                return Err(OdsError::Parse("no boolean value", None));
1584            }
1585        }
1586        ValueType::Number => {
1587            if let Some(v) = tc.val_float {
1588                cell.value = Value::Number(v);
1589            } else {
1590                return Err(OdsError::Parse("no float value", None));
1591            }
1592        }
1593        ValueType::Percentage => {
1594            if let Some(v) = tc.val_float {
1595                cell.value = Value::Percentage(v);
1596            } else {
1597                return Err(OdsError::Parse("no float value", None));
1598            }
1599        }
1600        ValueType::Currency => {
1601            if let Some(v) = tc.val_float {
1602                if let Some(c) = tc.val_currency {
1603                    cell.value = Value::Currency(v, c.into_boxed_str());
1604                } else {
1605                    cell.value = Value::Currency(v, "".into());
1606                }
1607            } else {
1608                return Err(OdsError::Parse("no float value", None));
1609            }
1610        }
1611        ValueType::Text => {
1612            if let Some(v) = tc.val_string {
1613                cell.value = Value::Text(v);
1614            } else {
1615                match tc.content {
1616                    TextContent::Empty => {
1617                        // noop
1618                    }
1619                    TextContent::Text(txt) => {
1620                        cell.value = Value::Text(txt);
1621                    }
1622                    TextContent::Xml(xml) => {
1623                        cell.value = Value::TextXml(vec![xml]);
1624                    }
1625                    TextContent::XmlVec(vec) => {
1626                        cell.value = Value::TextXml(vec);
1627                    }
1628                }
1629            }
1630        }
1631        ValueType::TextXml => {
1632            unreachable!();
1633        }
1634        ValueType::DateTime => {
1635            if let Some(v) = tc.val_datetime {
1636                cell.value = Value::DateTime(v);
1637            } else {
1638                return Err(OdsError::Parse("no datetime value", None));
1639            }
1640        }
1641        ValueType::TimeDuration => {
1642            if let Some(v) = tc.val_duration {
1643                cell.value = Value::TimeDuration(v);
1644            } else {
1645                return Err(OdsError::Parse("no duration value", None));
1646            }
1647        }
1648    }
1649
1650    Ok(())
1651}
1652
1653fn read_annotation(
1654    ctx: &mut OdsContext,
1655    xml: &mut OdsXmlReader<'_>,
1656    super_tag: &BytesStart<'_>,
1657) -> Result<Box<Annotation>, OdsError> {
1658    let mut annotation = Box::new(Annotation::new_empty());
1659
1660    for attr in super_tag.attributes().with_checks(false) {
1661        match attr? {
1662            attr if attr.key.as_ref() == b"office:display" => {
1663                annotation.set_display(parse_bool(&attr.value)?);
1664            }
1665            attr if attr.key.as_ref() == b"office:name" => {
1666                annotation.set_name(attr.decode_and_unescape_value(ctx.decoder)?);
1667            }
1668            attr => {
1669                let k = from_utf8(attr.key.as_ref())?;
1670                let v = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
1671                annotation.attrmap_mut().push_attr(k, v);
1672            }
1673        }
1674    }
1675
1676    let mut buf = ctx.pop_buf();
1677    loop {
1678        let evt = xml.read_event_into(&mut buf)?;
1679        let empty_tag = matches!(evt, Event::Empty(_));
1680        if cfg!(feature = "dump_xml") {
1681            println!("read_annotation {:?}", evt);
1682        }
1683        match &evt {
1684            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:annotation" => {
1685                break;
1686            }
1687
1688            Event::Start(xml_tag) | Event::Empty(xml_tag)
1689                if xml_tag.name().as_ref() == b"dc:creator" =>
1690            {
1691                annotation.set_creator(read_text(ctx, xml, xml_tag, empty_tag, parse_string)?);
1692            }
1693            Event::Start(xml_tag) | Event::Empty(xml_tag)
1694                if xml_tag.name().as_ref() == b"dc:date" =>
1695            {
1696                annotation.set_date(read_text(ctx, xml, xml_tag, empty_tag, parse_datetime)?);
1697            }
1698            Event::Start(xml_tag) | Event::Empty(xml_tag)
1699                if xml_tag.name().as_ref() == b"text:list"
1700                    || xml_tag.name().as_ref() == b"text:p" =>
1701            {
1702                annotation.push_text(read_xml(ctx, xml, xml_tag, empty_tag)?);
1703            }
1704
1705            Event::Eof => {
1706                break;
1707            }
1708            _ => {
1709                unused_event("read_annotation", &evt)?;
1710            }
1711        }
1712
1713        buf.clear();
1714    }
1715    ctx.push_buf(buf);
1716
1717    Ok(annotation)
1718}
1719
1720fn read_draw_frame(
1721    ctx: &mut OdsContext,
1722    xml: &mut OdsXmlReader<'_>,
1723    super_tag: &BytesStart<'_>,
1724) -> Result<DrawFrame, OdsError> {
1725    let mut draw_frame = DrawFrame::new();
1726
1727    copy_attr2(ctx, draw_frame.attrmap_mut(), super_tag)?;
1728
1729    let mut buf = ctx.pop_buf();
1730    loop {
1731        let evt = xml.read_event_into(&mut buf)?;
1732        let empty_tag = matches!(evt, Event::Empty(_));
1733        if cfg!(feature = "dump_xml") {
1734            println!("read_draw_frame {:?}", evt);
1735        }
1736        match &evt {
1737            Event::End(xml_tag) if xml_tag.name().as_ref() == b"draw:frame" => {
1738                break;
1739            }
1740            Event::Empty(xml_tag) | Event::Start(xml_tag)
1741                if xml_tag.name().as_ref() == b"draw:image" =>
1742            {
1743                draw_frame.push_content(DrawFrameContent::Image(read_image(
1744                    ctx, xml, xml_tag, empty_tag,
1745                )?));
1746            }
1747            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"svg:desc" => {}
1748            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"svg:desc" => {
1749                if let Some(v) = read_text(ctx, xml, xml_tag, empty_tag, parse_string)? {
1750                    draw_frame.set_desc(v);
1751                }
1752            }
1753            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"svg:title" => {}
1754            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"svg:title" => {
1755                if let Some(v) = read_text(ctx, xml, xml_tag, empty_tag, parse_string)? {
1756                    draw_frame.set_title(v);
1757                }
1758            }
1759            Event::Eof => {
1760                break;
1761            }
1762            _ => {
1763                unused_event("read_draw_frame", &evt)?;
1764            }
1765        }
1766
1767        buf.clear();
1768    }
1769    ctx.push_buf(buf);
1770
1771    Ok(draw_frame)
1772}
1773
1774fn read_image(
1775    ctx: &mut OdsContext,
1776    xml: &mut OdsXmlReader<'_>,
1777    super_tag: &BytesStart<'_>,
1778    empty_tag: bool,
1779) -> Result<DrawImage, OdsError> {
1780    let mut draw_image = DrawImage::new();
1781
1782    copy_attr2(ctx, draw_image.attrmap_mut(), super_tag)?;
1783
1784    if !empty_tag {
1785        let mut buf = ctx.pop_buf();
1786        loop {
1787            let evt = xml.read_event_into(&mut buf)?;
1788            let empty_tag = matches!(evt, Event::Empty(_));
1789            if cfg!(feature = "dump_xml") {
1790                println!("read_image {:?}", evt);
1791            }
1792            match &evt {
1793                Event::End(xml_tag) if xml_tag.name().as_ref() == b"draw:image" => {
1794                    break;
1795                }
1796
1797                Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:binary-data" => {
1798                    if let Some(v) = read_text(ctx, xml, xml_tag, empty_tag, parse_string)? {
1799                        draw_image.set_binary_base64(v);
1800                    }
1801                }
1802                Event::Start(xml_tag) | Event::Empty(xml_tag)
1803                    if xml_tag.name().as_ref() == b"text:list"
1804                        || xml_tag.name().as_ref() == b"text:p" =>
1805                {
1806                    draw_image.push_text(read_xml(ctx, xml, xml_tag, empty_tag)?);
1807                }
1808
1809                Event::Eof => {
1810                    break;
1811                }
1812                _ => {
1813                    unused_event("read_image", &evt)?;
1814                }
1815            }
1816
1817            buf.clear();
1818        }
1819        ctx.push_buf(buf);
1820    }
1821
1822    Ok(draw_image)
1823}
1824
1825fn read_scripts(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
1826    let mut buf = ctx.pop_buf();
1827    loop {
1828        let evt = xml.read_event_into(&mut buf)?;
1829        if cfg!(feature = "dump_xml") {
1830            println!("read_scripts {:?}", evt);
1831        }
1832        match &evt {
1833            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:scripts" => {
1834                break;
1835            }
1836
1837            Event::Start(xml_tag) | Event::Empty(xml_tag)
1838                if xml_tag.name().as_ref() == b"office:script" =>
1839            {
1840                let script = read_script(ctx, xml, xml_tag)?;
1841                ctx.book.add_script(script);
1842            }
1843
1844            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:event-listeners" => {}
1845            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:event-listeners" => {}
1846
1847            Event::Start(xml_tag) | Event::Empty(xml_tag)
1848                if xml_tag.name().as_ref() == b"script:event-listener" =>
1849            {
1850                let evt_listener = read_event_listener(ctx, xml_tag)?;
1851                ctx.book.add_event_listener(evt_listener);
1852            }
1853            Event::End(xml_tag) if xml_tag.name().as_ref() == b"script:event-listener" => {}
1854
1855            Event::Eof => {
1856                break;
1857            }
1858            _ => {
1859                unused_event("read_scripts", &evt)?;
1860            }
1861        }
1862
1863        buf.clear();
1864    }
1865    ctx.push_buf(buf);
1866
1867    Ok(())
1868}
1869
1870fn read_script(
1871    ctx: &mut OdsContext,
1872    xml: &mut OdsXmlReader<'_>,
1873    super_tag: &BytesStart<'_>,
1874) -> Result<Script, OdsError> {
1875    let v = read_xml(ctx, xml, super_tag, false)?;
1876    let script: Script = Script {
1877        script_lang: v
1878            .get_attr("script:language")
1879            .map(|v| v.to_string())
1880            .unwrap_or_default(),
1881        script: v.into_mixed_vec(),
1882    };
1883    Ok(script)
1884}
1885
1886// reads the page-layout tag
1887fn read_event_listener(
1888    ctx: &mut OdsContext,
1889    super_tag: &BytesStart<'_>,
1890) -> Result<EventListener, OdsError> {
1891    let mut evt = EventListener::new();
1892    for attr in super_tag.attributes().with_checks(false) {
1893        match attr? {
1894            attr if attr.key.as_ref() == b"script:event-name" => {
1895                evt.event_name = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
1896            }
1897            attr if attr.key.as_ref() == b"script:language" => {
1898                evt.script_lang = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
1899            }
1900            attr if attr.key.as_ref() == b"script:macro-name" => {
1901                evt.macro_name = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
1902            }
1903            attr if attr.key.as_ref() == b"xlink:actuate" => {
1904                evt.actuate =
1905                    parse_xlink_actuate(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
1906            }
1907            attr if attr.key.as_ref() == b"xlink:href" => {
1908                evt.href = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
1909            }
1910            attr if attr.key.as_ref() == b"xlink:type" => {
1911                evt.link_type =
1912                    parse_xlink_type(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
1913            }
1914            attr => {
1915                unused_attr("read_event_listener", super_tag.name().as_ref(), &attr)?;
1916            }
1917        }
1918    }
1919    Ok(evt)
1920}
1921
1922// reads a font-face
1923fn read_office_font_face_decls(
1924    ctx: &mut OdsContext,
1925    xml: &mut OdsXmlReader<'_>,
1926    origin: StyleOrigin,
1927) -> Result<(), OdsError> {
1928    let mut font: FontFaceDecl = FontFaceDecl::new_empty();
1929    font.set_origin(origin);
1930
1931    let mut buf = ctx.pop_buf();
1932    loop {
1933        let evt = xml.read_event_into(&mut buf)?;
1934        if cfg!(feature = "dump_xml") {
1935            println!(" read_fonts {:?}", evt);
1936        }
1937        match &evt {
1938            Event::Start(xml_tag) | Event::Empty(xml_tag)
1939                if xml_tag.name().as_ref() == b"style:font-face" =>
1940            {
1941                let name = copy_style_attr(ctx, font.attrmap_mut(), xml_tag)?;
1942                font.set_name(name);
1943                ctx.book.add_font(font);
1944
1945                font = FontFaceDecl::new_empty();
1946                font.set_origin(StyleOrigin::Content);
1947            }
1948            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:font-face-decls" => {
1949                break;
1950            }
1951            Event::Eof => {
1952                break;
1953            }
1954            _ => {
1955                unused_event("read_fonts", &evt)?;
1956            }
1957        }
1958
1959        buf.clear();
1960    }
1961    ctx.push_buf(buf);
1962
1963    Ok(())
1964}
1965
1966// reads the page-layout tag
1967fn read_page_style(
1968    ctx: &mut OdsContext,
1969    xml: &mut OdsXmlReader<'_>,
1970    super_tag: &BytesStart<'_>,
1971) -> Result<(), OdsError> {
1972    let mut pl = PageStyle::new_empty();
1973    for attr in super_tag.attributes().with_checks(false) {
1974        match attr? {
1975            attr if attr.key.as_ref() == b"style:name" => {
1976                let value = attr.decode_and_unescape_value(ctx.decoder)?;
1977                pl.set_name(value);
1978            }
1979            attr if attr.key.as_ref() == b"style:page-usage" => {
1980                let value = attr.decode_and_unescape_value(ctx.decoder)?;
1981                pl.master_page_usage = Some(value.to_string());
1982            }
1983            attr => {
1984                unused_attr("read_page_style", super_tag.name().as_ref(), &attr)?;
1985            }
1986        }
1987    }
1988
1989    let mut headerstyle = false;
1990    let mut footerstyle = false;
1991
1992    let mut buf = ctx.pop_buf();
1993    loop {
1994        let evt = xml.read_event_into(&mut buf)?;
1995        if cfg!(feature = "dump_xml") {
1996            println!(" read_page_layout {:?}", evt);
1997        }
1998        match &evt {
1999            Event::Start(xml_tag) | Event::Empty(xml_tag)
2000                if xml_tag.name().as_ref() == b"style:page-layout-properties" =>
2001            {
2002                copy_attr2(ctx, pl.style_mut(), xml_tag)?;
2003            }
2004            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:page-layout-properties" => {}
2005
2006            Event::Start(xml_tag) | Event::Empty(xml_tag)
2007                if xml_tag.name().as_ref() == b"style:header-style" =>
2008            {
2009                headerstyle = true;
2010            }
2011            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:header-style" => {
2012                headerstyle = false;
2013            }
2014
2015            Event::Start(xml_tag) | Event::Empty(xml_tag)
2016                if xml_tag.name().as_ref() == b"style:footer-style" =>
2017            {
2018                footerstyle = true;
2019            }
2020            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:footer-style" => {
2021                footerstyle = false;
2022            }
2023
2024            Event::Start(xml_tag) | Event::Empty(xml_tag)
2025                if xml_tag.name().as_ref() == b"style:header-footer-properties" =>
2026            {
2027                if headerstyle {
2028                    copy_attr2(ctx, pl.headerstyle_mut().style_mut(), xml_tag)?;
2029                }
2030                if footerstyle {
2031                    copy_attr2(ctx, pl.footerstyle_mut().style_mut(), xml_tag)?;
2032                }
2033            }
2034            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:header-footer-properties" => {
2035            }
2036
2037            Event::Start(xml_tag) | Event::Empty(xml_tag)
2038                if xml_tag.name().as_ref() == b"style:background-image" =>
2039            {
2040                // noop for now. sets the background transparent.
2041            }
2042
2043            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:page-layout" => {
2044                break;
2045            }
2046            Event::Text(_) => (),
2047            Event::Eof => break,
2048            _ => {
2049                unused_event("read_page_layout", &evt)?;
2050            }
2051        }
2052
2053        buf.clear();
2054    }
2055    ctx.push_buf(buf);
2056
2057    ctx.book.add_pagestyle(pl);
2058
2059    Ok(())
2060}
2061
2062fn read_validations(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
2063    let mut valid = Validation::new();
2064
2065    let mut buf = ctx.pop_buf();
2066    loop {
2067        let evt = xml.read_event_into(&mut buf)?;
2068        let empty_tag = matches!(evt, Event::Empty(_));
2069        if cfg!(feature = "dump_xml") {
2070            println!(" read_validations {:?}", evt);
2071        }
2072        match &evt {
2073            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:content-validation" => {
2074                read_validation(ctx, &mut valid, xml_tag)?;
2075                ctx.book.add_validation(valid);
2076                valid = Validation::new();
2077            }
2078            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:content-validation" => {
2079                read_validation(ctx, &mut valid, xml_tag)?;
2080            }
2081            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:content-validation" => {
2082                ctx.book.add_validation(valid);
2083                valid = Validation::new();
2084            }
2085
2086            Event::Start(xml_tag) | Event::Empty(xml_tag)
2087                if xml_tag.name().as_ref() == b"table:error-message" =>
2088            {
2089                read_validation_error(ctx, xml, &mut valid, xml_tag, empty_tag)?;
2090            }
2091
2092            Event::Start(xml_tag) | Event::Empty(xml_tag)
2093                if xml_tag.name().as_ref() == b"table:help-message" =>
2094            {
2095                read_validation_help(ctx, xml, &mut valid, xml_tag, empty_tag)?;
2096            }
2097
2098            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:content-validations" => {
2099                break;
2100            }
2101
2102            Event::Text(_) => (),
2103            Event::Eof => break,
2104            _ => {
2105                unused_event("read_validations", &evt)?;
2106            }
2107        }
2108    }
2109    ctx.push_buf(buf);
2110
2111    Ok(())
2112}
2113
2114fn read_validation_help(
2115    ctx: &mut OdsContext,
2116    xml: &mut OdsXmlReader<'_>,
2117    valid: &mut Validation,
2118    super_tag: &BytesStart<'_>,
2119    empty_tag: bool,
2120) -> Result<(), OdsError> {
2121    let mut vh = ValidationHelp::new();
2122
2123    for attr in super_tag.attributes().with_checks(false) {
2124        match attr? {
2125            attr if attr.key.as_ref() == b"table:display" => {
2126                vh.set_display(parse_bool(&attr.value)?);
2127            }
2128            attr if attr.key.as_ref() == b"table:title" => {
2129                vh.set_title(Some(
2130                    attr.decode_and_unescape_value(ctx.decoder)?.to_string(),
2131                ));
2132            }
2133            attr => {
2134                unused_attr("read_validations", super_tag.name().as_ref(), &attr)?;
2135            }
2136        }
2137    }
2138    let txt = read_text_or_tag(ctx, xml, super_tag, empty_tag)?;
2139    match txt {
2140        TextContent::Empty => {}
2141        TextContent::Xml(txt) => {
2142            vh.set_text(Some(txt));
2143        }
2144        _ => {
2145            return Err(OdsError::Ods(format!(
2146                "table:help-message invalid {:?}",
2147                txt
2148            )));
2149        }
2150    }
2151
2152    valid.set_help(Some(vh));
2153    Ok(())
2154}
2155
2156fn read_validation_error(
2157    ctx: &mut OdsContext,
2158    xml: &mut OdsXmlReader<'_>,
2159    valid: &mut Validation,
2160    super_tag: &BytesStart<'_>,
2161    empty_tag: bool,
2162) -> Result<(), OdsError> {
2163    let mut ve = ValidationError::new();
2164
2165    for attr in super_tag.attributes().with_checks(false) {
2166        match attr? {
2167            attr if attr.key.as_ref() == b"table:display" => {
2168                ve.set_display(parse_bool(&attr.value)?);
2169            }
2170            attr if attr.key.as_ref() == b"table:message-type" => {
2171                let mt = match attr.value.as_ref() {
2172                    b"stop" => MessageType::Error,
2173                    b"warning" => MessageType::Warning,
2174                    b"information" => MessageType::Info,
2175                    _ => {
2176                        return Err(OdsError::Parse(
2177                            "unknown message-type",
2178                            Some(attr.decode_and_unescape_value(ctx.decoder)?.into()),
2179                        ));
2180                    }
2181                };
2182                ve.set_msg_type(mt);
2183            }
2184            attr if attr.key.as_ref() == b"table:title" => {
2185                ve.set_title(Some(
2186                    attr.decode_and_unescape_value(ctx.decoder)?.to_string(),
2187                ));
2188            }
2189            attr => {
2190                unused_attr("read_validations", super_tag.name().as_ref(), &attr)?;
2191            }
2192        }
2193    }
2194    let txt = read_text_or_tag(ctx, xml, super_tag, empty_tag)?;
2195    match txt {
2196        TextContent::Empty => {}
2197        TextContent::Xml(txt) => {
2198            ve.set_text(Some(txt));
2199        }
2200        _ => {
2201            return Err(OdsError::Ods(format!(
2202                "table:error-message invalid {:?}",
2203                txt
2204            )));
2205        }
2206    }
2207
2208    valid.set_err(Some(ve));
2209
2210    Ok(())
2211}
2212
2213fn read_validation(
2214    ctx: &mut OdsContext,
2215    valid: &mut Validation,
2216    super_tag: &BytesStart<'_>,
2217) -> Result<(), OdsError> {
2218    for attr in super_tag.attributes().with_checks(false) {
2219        match attr? {
2220            attr if attr.key.as_ref() == b"table:name" => {
2221                valid.set_name(attr.decode_and_unescape_value(ctx.decoder)?);
2222            }
2223            attr if attr.key.as_ref() == b"table:condition" => {
2224                // split off 'of:' prefix
2225                let v = attr.decode_and_unescape_value(ctx.decoder)?;
2226                valid.set_condition(Condition::new(v.split_at(3).1));
2227            }
2228            attr if attr.key.as_ref() == b"table:allow-empty-cell" => {
2229                valid.set_allow_empty(parse_bool(&attr.value)?);
2230            }
2231            attr if attr.key.as_ref() == b"table:base-cell-address" => {
2232                let v = attr.decode_and_unescape_value(ctx.decoder)?;
2233                valid.set_base_cell(parse_cellref(&v)?);
2234            }
2235            attr if attr.key.as_ref() == b"table:display-list" => {
2236                valid.set_display(attr.value.as_ref().try_into()?);
2237            }
2238            attr => {
2239                unused_attr("read_validation", super_tag.name().as_ref(), &attr)?;
2240            }
2241        }
2242    }
2243    Ok(())
2244}
2245
2246// read the master-styles tag
2247fn read_office_master_styles(
2248    ctx: &mut OdsContext,
2249    xml: &mut OdsXmlReader<'_>,
2250    origin: StyleOrigin,
2251) -> Result<(), OdsError> {
2252    let mut buf = ctx.pop_buf();
2253    loop {
2254        let evt = xml.read_event_into(&mut buf)?;
2255        if cfg!(feature = "dump_xml") {
2256            println!(" read_master_styles {:?}", evt);
2257        }
2258        match &evt {
2259            Event::Start(xml_tag) | Event::Empty(xml_tag)
2260                if xml_tag.name().as_ref() == b"style:master-page" =>
2261            {
2262                read_master_page(ctx, xml, origin, xml_tag)?;
2263            }
2264            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:master-styles" => {
2265                break;
2266            }
2267            Event::Text(_) => (),
2268            Event::Eof => break,
2269            _ => {
2270                unused_event("read_master_styles", &evt)?;
2271            }
2272        }
2273
2274        buf.clear();
2275    }
2276    ctx.push_buf(buf);
2277
2278    Ok(())
2279}
2280
2281// read the master-page tag
2282fn read_master_page(
2283    ctx: &mut OdsContext,
2284    xml: &mut OdsXmlReader<'_>,
2285    _origin: StyleOrigin,
2286    super_tag: &BytesStart<'_>,
2287) -> Result<(), OdsError> {
2288    let mut masterpage = MasterPage::new_empty();
2289
2290    for attr in super_tag.attributes().with_checks(false) {
2291        match attr? {
2292            attr if attr.key.as_ref() == b"style:name" => {
2293                masterpage.set_name(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
2294            }
2295            attr if attr.key.as_ref() == b"style:page-layout-name" => {
2296                masterpage
2297                    .set_pagestyle(&attr.decode_and_unescape_value(ctx.decoder)?.as_ref().into());
2298            }
2299            attr if attr.key.as_ref() == b"style:display-name" => {
2300                masterpage
2301                    .set_display_name(attr.decode_and_unescape_value(ctx.decoder)?.as_ref().into());
2302            }
2303            attr if attr.key.as_ref() == b"style:next-style-name" => {
2304                let v = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
2305                masterpage.set_next_masterpage(&MasterPageRef::from(v));
2306            }
2307            attr => {
2308                unused_attr("read_master_page", super_tag.name().as_ref(), &attr)?;
2309            }
2310        }
2311    }
2312
2313    let mut buf = ctx.pop_buf();
2314    loop {
2315        let evt = xml.read_event_into(&mut buf)?;
2316        if cfg!(feature = "dump_xml") {
2317            println!(" read_master_page {:?}", evt);
2318        }
2319        match &evt {
2320            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:header" => {}
2321            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:header" => {
2322                masterpage.set_header(read_headerfooter(ctx, xml, xml_tag)?);
2323            }
2324            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:header-first" => {}
2325            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:header-first" => {
2326                masterpage.set_header_first(read_headerfooter(ctx, xml, xml_tag)?);
2327            }
2328            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:header-left" => {}
2329            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:header-left" => {
2330                masterpage.set_header_left(read_headerfooter(ctx, xml, xml_tag)?);
2331            }
2332            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:footer" => {}
2333            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:footer" => {
2334                masterpage.set_footer(read_headerfooter(ctx, xml, xml_tag)?);
2335            }
2336            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:footer-first" => {}
2337            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:footer-first" => {
2338                masterpage.set_footer_first(read_headerfooter(ctx, xml, xml_tag)?);
2339            }
2340            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:footer-left" => {}
2341            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:footer-left" => {
2342                masterpage.set_footer_left(read_headerfooter(ctx, xml, xml_tag)?);
2343            }
2344            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:master-page" => {
2345                break;
2346            }
2347            Event::Eof => break,
2348            _ => {
2349                unused_event("read_master_page", &evt)?;
2350            }
2351        }
2352
2353        buf.clear();
2354    }
2355    ctx.push_buf(buf);
2356
2357    ctx.book.add_masterpage(masterpage);
2358
2359    Ok(())
2360}
2361
2362// reads any header or footer tags
2363fn read_headerfooter(
2364    ctx: &mut OdsContext,
2365    xml: &mut OdsXmlReader<'_>,
2366    super_tag: &BytesStart<'_>,
2367) -> Result<HeaderFooter, OdsError> {
2368    let mut hf = HeaderFooter::new();
2369    let mut content = TextContent::Empty;
2370
2371    for attr in super_tag.attributes().with_checks(false) {
2372        match attr? {
2373            attr if attr.key.as_ref() == b"style:display" => {
2374                hf.set_display(parse_bool(&attr.value)?);
2375            }
2376            attr => {
2377                unused_attr("read_headerfooter", super_tag.name().as_ref(), &attr)?;
2378            }
2379        }
2380    }
2381
2382    let mut buf = ctx.pop_buf();
2383    loop {
2384        let evt = xml.read_event_into(&mut buf)?;
2385        let empty_tag = matches!(evt, Event::Empty(_));
2386        if cfg!(feature = "dump_xml") {
2387            println!(" read_headerfooter {:?}", evt);
2388        }
2389        match &evt {
2390            Event::Start(xml_tag) | Event::Empty(xml_tag)
2391                if xml_tag.name().as_ref() == b"style:region-left" =>
2392            {
2393                let reg = read_xml(ctx, xml, xml_tag, empty_tag)?;
2394                hf.set_left(reg.into_vec()?);
2395            }
2396            Event::Start(xml_tag) | Event::Empty(xml_tag)
2397                if xml_tag.name().as_ref() == b"style:region-center" =>
2398            {
2399                let reg = read_xml(ctx, xml, xml_tag, empty_tag)?;
2400                hf.set_center(reg.into_vec()?);
2401            }
2402            Event::Start(xml_tag) | Event::Empty(xml_tag)
2403                if xml_tag.name().as_ref() == b"style:region-right" =>
2404            {
2405                let reg = read_xml(ctx, xml, xml_tag, empty_tag)?;
2406                hf.set_right(reg.into_vec()?);
2407            }
2408
2409            Event::Start(xml_tag) | Event::Empty(xml_tag)
2410                if xml_tag.name().as_ref() == b"text:p" =>
2411            {
2412                let new_txt = read_text_or_tag(ctx, xml, xml_tag, empty_tag)?;
2413                content = append_text(new_txt, content);
2414            }
2415            Event::Start(xml_tag) | Event::Empty(xml_tag)
2416                if xml_tag.name().as_ref() == b"text:h" =>
2417            {
2418                let new_txt = read_text_or_tag(ctx, xml, xml_tag, empty_tag)?;
2419                content = append_text(new_txt, content);
2420            }
2421            // no other tags supported for now. they have never been seen in the wild.
2422            Event::Text(_) => (),
2423            Event::End(xml_tag) => {
2424                if xml_tag.name() == super_tag.name() {
2425                    hf.set_content(match content {
2426                        TextContent::Empty => Vec::new(),
2427                        TextContent::Text(v) => vec![TextP::new().text(v).into()],
2428                        TextContent::Xml(v) => vec![v],
2429                        TextContent::XmlVec(v) => v,
2430                    });
2431                    break;
2432                }
2433            }
2434            Event::Eof => break,
2435            _ => {
2436                unused_event("read_headerfooter", &evt)?;
2437            }
2438        }
2439
2440        buf.clear();
2441    }
2442    ctx.push_buf(buf);
2443
2444    Ok(hf)
2445}
2446
2447// reads the office-styles tag
2448fn read_office_styles(
2449    ctx: &mut OdsContext,
2450    xml: &mut OdsXmlReader<'_>,
2451    origin: StyleOrigin,
2452) -> Result<(), OdsError> {
2453    let mut buf = ctx.pop_buf();
2454    loop {
2455        let evt = xml.read_event_into(&mut buf)?;
2456        let empty_tag = matches!(evt, Event::Empty(_));
2457        if cfg!(feature = "dump_xml") {
2458            println!(" read_styles_tag {:?}", evt);
2459        }
2460        match &evt {
2461            Event::Start(xml_tag) | Event::Empty(xml_tag)
2462                if xml_tag.name().as_ref() == b"style:style" =>
2463            {
2464                read_style_style(ctx, xml, origin, StyleUse::Named, xml_tag, empty_tag)?;
2465            }
2466            Event::Start(xml_tag) | Event::Empty(xml_tag)
2467                if xml_tag.name().as_ref() == b"style:default-style" =>
2468            {
2469                read_style_style(ctx, xml, origin, StyleUse::Default, xml_tag, empty_tag)?;
2470            }
2471            Event::Start(xml_tag) | Event::Empty(xml_tag)
2472                if xml_tag.name().as_ref() == b"number:boolean-style"
2473                    || xml_tag.name().as_ref() == b"number:date-style"
2474                    || xml_tag.name().as_ref() == b"number:time-style"
2475                    || xml_tag.name().as_ref() == b"number:number-style"
2476                    || xml_tag.name().as_ref() == b"number:currency-style"
2477                    || xml_tag.name().as_ref() == b"number:percentage-style"
2478                    || xml_tag.name().as_ref() == b"number:text-style" =>
2479            {
2480                read_value_format(ctx, xml, origin, StyleUse::Named, xml_tag)?;
2481            }
2482            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:styles" => {
2483                break;
2484            }
2485            Event::Text(_) => (),
2486            Event::Eof => break,
2487            _ => {
2488                unused_event("read_styles_tag", &evt)?;
2489            }
2490        }
2491
2492        buf.clear();
2493    }
2494    ctx.push_buf(buf);
2495
2496    Ok(())
2497}
2498
2499// read the automatic-styles tag
2500fn read_office_automatic_styles(
2501    ctx: &mut OdsContext,
2502    xml: &mut OdsXmlReader<'_>,
2503    origin: StyleOrigin,
2504) -> Result<(), OdsError> {
2505    let mut buf = ctx.pop_buf();
2506    loop {
2507        let evt = xml.read_event_into(&mut buf)?;
2508        let empty_tag = matches!(evt, Event::Empty(_));
2509        if cfg!(feature = "dump_xml") {
2510            println!(" read_auto_styles {:?}", evt);
2511        }
2512        match &evt {
2513            Event::Start(xml_tag) | Event::Empty(xml_tag)
2514                if xml_tag.name().as_ref() == b"style:style" =>
2515            {
2516                read_style_style(ctx, xml, origin, StyleUse::Automatic, xml_tag, empty_tag)?;
2517            }
2518            Event::Start(xml_tag) | Event::Empty(xml_tag)
2519                if xml_tag.name().as_ref() == b"number:boolean-style"
2520                    || xml_tag.name().as_ref() == b"number:date-style"
2521                    || xml_tag.name().as_ref() == b"number:time-style"
2522                    || xml_tag.name().as_ref() == b"number:number-style"
2523                    || xml_tag.name().as_ref() == b"number:currency-style"
2524                    || xml_tag.name().as_ref() == b"number:percentage-style"
2525                    || xml_tag.name().as_ref() == b"number:text-style" =>
2526            {
2527                read_value_format(ctx, xml, origin, StyleUse::Automatic, xml_tag)?;
2528            }
2529
2530            Event::Start(xml_tag) | Event::Empty(xml_tag)
2531                if xml_tag.name().as_ref() == b"style:page-layout" =>
2532            {
2533                read_page_style(ctx, xml, xml_tag)?;
2534            }
2535
2536            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:automatic-styles" => {
2537                break;
2538            }
2539            Event::Text(_) => (),
2540            Event::Eof => break,
2541            _ => {
2542                unused_event("read_auto_styles", &evt)?;
2543            }
2544        }
2545
2546        buf.clear();
2547    }
2548    ctx.push_buf(buf);
2549
2550    Ok(())
2551}
2552
2553// Reads any of the number:xxx tags
2554fn read_value_format(
2555    ctx: &mut OdsContext,
2556    xml: &mut OdsXmlReader<'_>,
2557    origin: StyleOrigin,
2558    styleuse: StyleUse,
2559    super_tag: &BytesStart<'_>,
2560) -> Result<(), OdsError> {
2561    match super_tag.name().as_ref() {
2562        b"number:boolean-style" => {
2563            let mut valuestyle = ValueFormatBoolean::new_empty();
2564            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2565            ctx.book.add_boolean_format(valuestyle);
2566        }
2567        b"number:date-style" => {
2568            let mut valuestyle = ValueFormatDateTime::new_empty();
2569            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2570            ctx.book.add_datetime_format(valuestyle);
2571        }
2572        b"number:time-style" => {
2573            let mut valuestyle = ValueFormatTimeDuration::new_empty();
2574            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2575            ctx.book.add_timeduration_format(valuestyle);
2576        }
2577        b"number:number-style" => {
2578            let mut valuestyle = ValueFormatNumber::new_empty();
2579            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2580            ctx.book.add_number_format(valuestyle);
2581        }
2582        b"number:currency-style" => {
2583            let mut valuestyle = ValueFormatCurrency::new_empty();
2584            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2585            ctx.book.add_currency_format(valuestyle);
2586        }
2587        b"number:percentage-style" => {
2588            let mut valuestyle = ValueFormatPercentage::new_empty();
2589            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2590            ctx.book.add_percentage_format(valuestyle);
2591        }
2592        b"number:text-style" => {
2593            let mut valuestyle = ValueFormatText::new_empty();
2594            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2595            ctx.book.add_text_format(valuestyle);
2596        }
2597        _ => {
2598            if cfg!(feature = "dump_unused") {
2599                println!(
2600                    " read_value_format unused {}",
2601                    from_utf8(super_tag.name().as_ref())?
2602                );
2603            }
2604        }
2605    }
2606
2607    Ok(())
2608}
2609
2610// Reads any of the number:xxx tags
2611fn read_value_format_parts<T: ValueFormatTrait>(
2612    ctx: &mut OdsContext,
2613    xml: &mut OdsXmlReader<'_>,
2614    origin: StyleOrigin,
2615    styleuse: StyleUse,
2616    valuestyle: &mut T,
2617    super_tag: &BytesStart<'_>,
2618) -> Result<(), OdsError> {
2619    //
2620    valuestyle.set_origin(origin);
2621    valuestyle.set_styleuse(styleuse);
2622    let name = copy_style_attr(ctx, valuestyle.attrmap_mut(), super_tag)?;
2623    valuestyle.set_name(name.as_str());
2624
2625    let mut buf = ctx.pop_buf();
2626    loop {
2627        let evt = xml.read_event_into(&mut buf)?;
2628        let empty_tag = matches!(evt, Event::Empty(_));
2629        if cfg!(feature = "dump_xml") {
2630            println!(" read_value_format_parts {:?}", evt);
2631        }
2632        match &evt {
2633            Event::Start(xml_tag) | Event::Empty(xml_tag)
2634                if xml_tag.name().as_ref() == b"number:boolean" =>
2635            {
2636                valuestyle.push_part(read_part(
2637                    ctx,
2638                    xml,
2639                    xml_tag,
2640                    empty_tag,
2641                    FormatPartType::Boolean,
2642                )?);
2643            }
2644            Event::Start(xml_tag) | Event::Empty(xml_tag)
2645                if xml_tag.name().as_ref() == b"number:number" =>
2646            {
2647                valuestyle.push_part(read_part_number(
2648                    ctx,
2649                    xml,
2650                    xml_tag,
2651                    empty_tag,
2652                    FormatPartType::Number,
2653                )?);
2654            }
2655            Event::Start(xml_tag) | Event::Empty(xml_tag)
2656                if xml_tag.name().as_ref() == b"number:fraction" =>
2657            {
2658                valuestyle.push_part(read_part(
2659                    ctx,
2660                    xml,
2661                    xml_tag,
2662                    empty_tag,
2663                    FormatPartType::Fraction,
2664                )?);
2665            }
2666            Event::Start(xml_tag) | Event::Empty(xml_tag)
2667                if xml_tag.name().as_ref() == b"number:scientific-number" =>
2668            {
2669                valuestyle.push_part(read_part(
2670                    ctx,
2671                    xml,
2672                    xml_tag,
2673                    empty_tag,
2674                    FormatPartType::ScientificNumber,
2675                )?);
2676            }
2677            Event::Start(xml_tag) | Event::Empty(xml_tag)
2678                if xml_tag.name().as_ref() == b"number:text"
2679                    || xml_tag.name().as_ref() == b"loext:text" =>
2680            {
2681                valuestyle.push_part(read_part_text(
2682                    ctx,
2683                    xml,
2684                    xml_tag,
2685                    empty_tag,
2686                    FormatPartType::Text,
2687                )?);
2688            }
2689
2690            Event::Start(xml_tag) | Event::Empty(xml_tag)
2691                if xml_tag.name().as_ref() == b"number:am-pm" =>
2692            {
2693                valuestyle.push_part(read_part(
2694                    ctx,
2695                    xml,
2696                    xml_tag,
2697                    empty_tag,
2698                    FormatPartType::AmPm,
2699                )?);
2700            }
2701            Event::Start(xml_tag) | Event::Empty(xml_tag)
2702                if xml_tag.name().as_ref() == b"number:day" =>
2703            {
2704                valuestyle.push_part(read_part(
2705                    ctx,
2706                    xml,
2707                    xml_tag,
2708                    empty_tag,
2709                    FormatPartType::Day,
2710                )?);
2711            }
2712            Event::Start(xml_tag) | Event::Empty(xml_tag)
2713                if xml_tag.name().as_ref() == b"number:day-of-week" =>
2714            {
2715                valuestyle.push_part(read_part(
2716                    ctx,
2717                    xml,
2718                    xml_tag,
2719                    empty_tag,
2720                    FormatPartType::DayOfWeek,
2721                )?);
2722            }
2723            Event::Start(xml_tag) | Event::Empty(xml_tag)
2724                if xml_tag.name().as_ref() == b"number:era" =>
2725            {
2726                valuestyle.push_part(read_part(
2727                    ctx,
2728                    xml,
2729                    xml_tag,
2730                    empty_tag,
2731                    FormatPartType::Era,
2732                )?);
2733            }
2734            Event::Start(xml_tag) | Event::Empty(xml_tag)
2735                if xml_tag.name().as_ref() == b"number:hours" =>
2736            {
2737                valuestyle.push_part(read_part(
2738                    ctx,
2739                    xml,
2740                    xml_tag,
2741                    empty_tag,
2742                    FormatPartType::Hours,
2743                )?);
2744            }
2745            Event::Start(xml_tag) | Event::Empty(xml_tag)
2746                if xml_tag.name().as_ref() == b"number:minutes" =>
2747            {
2748                valuestyle.push_part(read_part(
2749                    ctx,
2750                    xml,
2751                    xml_tag,
2752                    empty_tag,
2753                    FormatPartType::Minutes,
2754                )?);
2755            }
2756            Event::Start(xml_tag) | Event::Empty(xml_tag)
2757                if xml_tag.name().as_ref() == b"number:month" =>
2758            {
2759                valuestyle.push_part(read_part(
2760                    ctx,
2761                    xml,
2762                    xml_tag,
2763                    empty_tag,
2764                    FormatPartType::Month,
2765                )?);
2766            }
2767            Event::Start(xml_tag) | Event::Empty(xml_tag)
2768                if xml_tag.name().as_ref() == b"number:quarter" =>
2769            {
2770                valuestyle.push_part(read_part(
2771                    ctx,
2772                    xml,
2773                    xml_tag,
2774                    empty_tag,
2775                    FormatPartType::Quarter,
2776                )?);
2777            }
2778            Event::Start(xml_tag) | Event::Empty(xml_tag)
2779                if xml_tag.name().as_ref() == b"number:seconds" =>
2780            {
2781                valuestyle.push_part(read_part(
2782                    ctx,
2783                    xml,
2784                    xml_tag,
2785                    empty_tag,
2786                    FormatPartType::Seconds,
2787                )?);
2788            }
2789            Event::Start(xml_tag) | Event::Empty(xml_tag)
2790                if xml_tag.name().as_ref() == b"number:week-of-year" =>
2791            {
2792                valuestyle.push_part(read_part(
2793                    ctx,
2794                    xml,
2795                    xml_tag,
2796                    empty_tag,
2797                    FormatPartType::WeekOfYear,
2798                )?);
2799            }
2800            Event::Start(xml_tag) | Event::Empty(xml_tag)
2801                if xml_tag.name().as_ref() == b"number:year" =>
2802            {
2803                valuestyle.push_part(read_part(
2804                    ctx,
2805                    xml,
2806                    xml_tag,
2807                    empty_tag,
2808                    FormatPartType::Year,
2809                )?);
2810            }
2811
2812            Event::Start(xml_tag) | Event::Empty(xml_tag)
2813                if xml_tag.name().as_ref() == b"number:currency-symbol" =>
2814            {
2815                valuestyle.push_part(read_part_text(
2816                    ctx,
2817                    xml,
2818                    xml_tag,
2819                    empty_tag,
2820                    FormatPartType::CurrencySymbol,
2821                )?);
2822            }
2823            Event::Start(xml_tag) | Event::Empty(xml_tag)
2824                if xml_tag.name().as_ref() == b"number:fill-character"
2825                    || xml_tag.name().as_ref() == b"loext:fill-character" =>
2826            {
2827                valuestyle.push_part(read_part_text(
2828                    ctx,
2829                    xml,
2830                    xml_tag,
2831                    empty_tag,
2832                    FormatPartType::FillCharacter,
2833                )?);
2834            }
2835            Event::Start(xml_tag) | Event::Empty(xml_tag)
2836                if xml_tag.name().as_ref() == b"number:text-content" =>
2837            {
2838                valuestyle.push_part(read_part(
2839                    ctx,
2840                    xml,
2841                    xml_tag,
2842                    empty_tag,
2843                    FormatPartType::TextContent,
2844                )?);
2845            }
2846
2847            Event::Start(xml_tag) | Event::Empty(xml_tag)
2848                if xml_tag.name().as_ref() == b"style:map" =>
2849            {
2850                valuestyle.push_stylemap(read_value_stylemap(ctx, xml_tag)?);
2851            }
2852            Event::Start(xml_tag) | Event::Empty(xml_tag)
2853                if xml_tag.name().as_ref() == b"style:text-properties" =>
2854            {
2855                copy_attr2(ctx, valuestyle.textstyle_mut(), xml_tag)?;
2856            }
2857            Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
2858                break;
2859            }
2860            Event::Eof => break,
2861            _ => {
2862                unused_event("read_value_format_parts", &evt)?;
2863            }
2864        }
2865
2866        buf.clear();
2867    }
2868    ctx.push_buf(buf);
2869
2870    Ok(())
2871}
2872
2873fn read_part(
2874    ctx: &mut OdsContext,
2875    xml: &mut OdsXmlReader<'_>,
2876    super_tag: &BytesStart<'_>,
2877    empty_tag: bool,
2878    part_type: FormatPartType,
2879) -> Result<FormatPart, OdsError> {
2880    let mut part = FormatPart::new(part_type);
2881    copy_attr2(ctx, part.attrmap_mut(), super_tag)?;
2882
2883    if !empty_tag {
2884        let mut buf = ctx.pop_buf();
2885        loop {
2886            let evt = xml.read_event_into(&mut buf)?;
2887            if cfg!(feature = "dump_xml") {
2888                println!(" read_part {:?}", evt);
2889            }
2890            match &evt {
2891                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
2892                    break;
2893                }
2894                Event::Eof => {
2895                    break;
2896                }
2897                _ => {
2898                    unused_event("read_part", &evt)?;
2899                }
2900            }
2901        }
2902        ctx.push_buf(buf);
2903    }
2904
2905    Ok(part)
2906}
2907
2908// value format part with text content
2909fn read_part_text(
2910    ctx: &mut OdsContext,
2911    xml: &mut OdsXmlReader<'_>,
2912    super_tag: &BytesStart<'_>,
2913    empty_tag: bool,
2914    part_type: FormatPartType,
2915) -> Result<FormatPart, OdsError> {
2916    let mut part = FormatPart::new(part_type);
2917    copy_attr2(ctx, part.attrmap_mut(), super_tag)?;
2918
2919    if !empty_tag {
2920        let mut buf = ctx.pop_buf();
2921        loop {
2922            let evt = xml.read_event_into(&mut buf)?;
2923            if cfg!(feature = "dump_xml") {
2924                println!(" read_part_text {:?}", evt);
2925            }
2926            match &evt {
2927                Event::GeneralRef(xml_ref) => {
2928                    part.append_content(entity(xml_ref)?);
2929                }
2930                Event::Text(xml_text) => {
2931                    part.append_content(xml_text.decode()?);
2932                }
2933                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
2934                    break;
2935                }
2936                Event::Eof => {
2937                    break;
2938                }
2939                _ => {
2940                    unused_event("read_part_text", &evt)?;
2941                }
2942            }
2943        }
2944        ctx.push_buf(buf);
2945    }
2946
2947    Ok(part)
2948}
2949
2950fn read_part_number(
2951    ctx: &mut OdsContext,
2952    xml: &mut OdsXmlReader<'_>,
2953    super_tag: &BytesStart<'_>,
2954    empty_tag: bool,
2955    part_type: FormatPartType,
2956) -> Result<FormatPart, OdsError> {
2957    let mut part = FormatPart::new(part_type);
2958    copy_attr2(ctx, part.attrmap_mut(), super_tag)?;
2959
2960    if !empty_tag {
2961        let mut buf = ctx.pop_buf();
2962        loop {
2963            let evt = xml.read_event_into(&mut buf)?;
2964            if cfg!(feature = "dump_xml") {
2965                println!(" read_part_embedded_text {:?}", evt);
2966            }
2967            match &evt {
2968                Event::Start(xml_tag) | Event::Empty(xml_tag)
2969                    if xml_tag.name().as_ref() == b"number:embedded-text" =>
2970                {
2971                    for attr in xml_tag.attributes().with_checks(false) {
2972                        let attr = attr?;
2973                        match attr.key.as_ref() {
2974                            b"number:position" => {
2975                                part.set_position(parse_i32(&attr.value)?);
2976                            }
2977                            _ => {
2978                                unused_attr(
2979                                    "read_part_embedded_text",
2980                                    xml_tag.name().as_ref(),
2981                                    &attr,
2982                                )?;
2983                            }
2984                        }
2985                    }
2986                }
2987                Event::End(xml_tag) if xml_tag.name().as_ref() == b"number:embedded-text" => {}
2988                Event::Text(xml_text) => {
2989                    part.set_content(parse_string(xml_text.as_ref())?);
2990                }
2991                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
2992                    break;
2993                }
2994                Event::Eof => {
2995                    break;
2996                }
2997                _ => {
2998                    unused_event("read_part_embedded_text", &evt)?;
2999                }
3000            }
3001        }
3002        ctx.push_buf(buf);
3003    }
3004
3005    Ok(part)
3006}
3007
3008// style:style tag
3009#[allow(clippy::too_many_arguments)]
3010fn read_style_style(
3011    ctx: &mut OdsContext,
3012    xml: &mut OdsXmlReader<'_>,
3013    origin: StyleOrigin,
3014    style_use: StyleUse,
3015    super_tag: &BytesStart<'_>,
3016    empty_tag: bool,
3017) -> Result<(), OdsError> {
3018    for attr in super_tag.attributes().with_checks(false) {
3019        match attr? {
3020            attr if attr.key.as_ref() == b"style:family" => {
3021                match attr.value.as_ref() {
3022                    b"table" => read_tablestyle(ctx, xml, origin, style_use, super_tag, empty_tag)?,
3023                    b"table-column" => {
3024                        read_colstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?
3025                    }
3026                    b"table-row" => {
3027                        read_rowstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?
3028                    }
3029                    b"table-cell" => {
3030                        read_cellstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?
3031                    }
3032                    b"graphic" => {
3033                        read_graphicstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?
3034                    }
3035                    b"paragraph" => {
3036                        read_paragraphstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?
3037                    }
3038                    b"text" => read_textstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?,
3039                    b"ruby" => read_rubystyle(ctx, xml, origin, style_use, super_tag, empty_tag)?,
3040                    value => {
3041                        return Err(OdsError::Ods(format!(
3042                            "style:family unknown {} ",
3043                            from_utf8(value)?
3044                        )));
3045                    }
3046                };
3047            }
3048            _ => {
3049                // not read here
3050            }
3051        }
3052    }
3053
3054    Ok(())
3055}
3056
3057// style:style tag
3058#[allow(clippy::collapsible_else_if)]
3059#[allow(clippy::too_many_arguments)]
3060fn read_tablestyle(
3061    ctx: &mut OdsContext,
3062    xml: &mut OdsXmlReader<'_>,
3063    origin: StyleOrigin,
3064    style_use: StyleUse,
3065    super_tag: &BytesStart<'_>,
3066    empty_tag: bool,
3067) -> Result<(), OdsError> {
3068    let mut style = TableStyle::new_empty();
3069    style.set_origin(origin);
3070    style.set_styleuse(style_use);
3071    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3072    style.set_name(name);
3073
3074    // In case of an empty xml-tag we are done here.
3075    if empty_tag {
3076        ctx.book.add_tablestyle(style);
3077    } else {
3078        let mut buf = ctx.pop_buf();
3079        loop {
3080            let evt = xml.read_event_into(&mut buf)?;
3081            if cfg!(feature = "dump_xml") {
3082                println!(" read_table_style {:?}", evt);
3083            }
3084            match &evt {
3085                Event::Start(xml_tag) | Event::Empty(xml_tag) => match xml_tag.name().as_ref() {
3086                    b"style:table-properties" => copy_attr2(ctx, style.tablestyle_mut(), xml_tag)?,
3087                    _ => {
3088                        unused_event("read_table_style", &evt)?;
3089                    }
3090                },
3091                Event::Text(_) => (),
3092                Event::End(xml_tag) => {
3093                    if xml_tag.name().as_ref() == super_tag.name().as_ref() {
3094                        ctx.book.add_tablestyle(style);
3095                        break;
3096                    } else {
3097                        unused_event("read_table_style", &evt)?;
3098                    }
3099                }
3100                Event::Eof => break,
3101                _ => {
3102                    unused_event("read_table_style", &evt)?;
3103                }
3104            }
3105        }
3106
3107        ctx.push_buf(buf);
3108    }
3109
3110    Ok(())
3111}
3112
3113// style:style tag
3114#[allow(clippy::collapsible_else_if)]
3115#[allow(clippy::too_many_arguments)]
3116fn read_rowstyle(
3117    ctx: &mut OdsContext,
3118    xml: &mut OdsXmlReader<'_>,
3119    origin: StyleOrigin,
3120    style_use: StyleUse,
3121    super_tag: &BytesStart<'_>,
3122    empty_tag: bool,
3123) -> Result<(), OdsError> {
3124    let mut style = RowStyle::new_empty();
3125    style.set_origin(origin);
3126    style.set_styleuse(style_use);
3127    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3128    style.set_name(name);
3129
3130    // In case of an empty xml-tag we are done here.
3131    if empty_tag {
3132        ctx.book.add_rowstyle(style);
3133    } else {
3134        let mut buf = ctx.pop_buf();
3135        loop {
3136            let evt = xml.read_event_into(&mut buf)?;
3137            if cfg!(feature = "dump_xml") {
3138                println!(" read_rowstyle {:?}", evt);
3139            }
3140            match &evt {
3141                Event::Start(xml_tag) | Event::Empty(xml_tag) => match xml_tag.name().as_ref() {
3142                    b"style:table-row-properties" => {
3143                        copy_attr2(ctx, style.rowstyle_mut(), xml_tag)?
3144                    }
3145                    _ => {
3146                        unused_event("read_rowstyle", &evt)?;
3147                    }
3148                },
3149                Event::Text(_) => (),
3150                Event::End(xml_tag) => {
3151                    if xml_tag.name() == super_tag.name() {
3152                        ctx.book.add_rowstyle(style);
3153                        break;
3154                    } else {
3155                        unused_event("read_rowstyle", &evt)?;
3156                    }
3157                }
3158                Event::Eof => break,
3159                _ => {
3160                    unused_event("read_rowstyle", &evt)?;
3161                }
3162            }
3163        }
3164        ctx.push_buf(buf);
3165    }
3166
3167    Ok(())
3168}
3169
3170// style:style tag
3171#[allow(clippy::collapsible_else_if)]
3172#[allow(clippy::too_many_arguments)]
3173fn read_colstyle(
3174    ctx: &mut OdsContext,
3175    xml: &mut OdsXmlReader<'_>,
3176    origin: StyleOrigin,
3177    style_use: StyleUse,
3178    super_tag: &BytesStart<'_>,
3179    empty_tag: bool,
3180) -> Result<(), OdsError> {
3181    let mut style = ColStyle::new_empty();
3182    style.set_origin(origin);
3183    style.set_styleuse(style_use);
3184    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3185    style.set_name(name);
3186
3187    // In case of an empty xml-tag we are done here.
3188    if empty_tag {
3189        ctx.book.add_colstyle(style);
3190    } else {
3191        let mut buf = ctx.pop_buf();
3192        loop {
3193            let evt = xml.read_event_into(&mut buf)?;
3194            if cfg!(feature = "dump_xml") {
3195                println!(" read_colstyle {:?}", evt);
3196            }
3197            match &evt {
3198                Event::Start(xml_tag) | Event::Empty(xml_tag) => match xml_tag.name().as_ref() {
3199                    b"style:table-column-properties" => {
3200                        copy_attr2(ctx, style.colstyle_mut(), xml_tag)?
3201                    }
3202                    _ => {
3203                        unused_event("read_colstyle", &evt)?;
3204                    }
3205                },
3206                Event::Text(_) => (),
3207                Event::End(xml_tag) => {
3208                    if xml_tag.name() == super_tag.name() {
3209                        ctx.book.add_colstyle(style);
3210                        break;
3211                    } else {
3212                        unused_event("read_colstyle", &evt)?;
3213                    }
3214                }
3215                Event::Eof => break,
3216                _ => {
3217                    unused_event("read_colstyle", &evt)?;
3218                }
3219            }
3220        }
3221
3222        ctx.push_buf(buf);
3223    }
3224    Ok(())
3225}
3226
3227// style:style tag
3228#[allow(clippy::collapsible_else_if)]
3229#[allow(clippy::too_many_arguments)]
3230fn read_cellstyle(
3231    ctx: &mut OdsContext,
3232    xml: &mut OdsXmlReader<'_>,
3233    origin: StyleOrigin,
3234    style_use: StyleUse,
3235    super_tag: &BytesStart<'_>,
3236    empty_tag: bool,
3237) -> Result<(), OdsError> {
3238    let mut style = CellStyle::new_empty();
3239    style.set_origin(origin);
3240    style.set_styleuse(style_use);
3241    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3242    style.set_name(name);
3243
3244    // In case of an empty xml-tag we are done here.
3245    if empty_tag {
3246        ctx.book.add_cellstyle(style);
3247    } else {
3248        let mut buf = ctx.pop_buf();
3249        loop {
3250            let evt = xml.read_event_into(&mut buf)?;
3251            if cfg!(feature = "dump_xml") {
3252                println!(" read_cellstyle {:?}", evt);
3253            }
3254            match &evt {
3255                Event::Start(xml_tag) | Event::Empty(xml_tag)
3256                    if xml_tag.name().as_ref() == b"style:table-cell-properties" =>
3257                {
3258                    copy_attr2(ctx, style.cellstyle_mut(), xml_tag)?;
3259                }
3260                Event::Start(xml_tag) | Event::Empty(xml_tag)
3261                    if xml_tag.name().as_ref() == b"style:text-properties" =>
3262                {
3263                    copy_attr2(ctx, style.textstyle_mut(), xml_tag)?;
3264                }
3265                Event::Start(xml_tag) | Event::Empty(xml_tag)
3266                    if xml_tag.name().as_ref() == b"style:paragraph-properties" =>
3267                {
3268                    copy_attr2(ctx, style.paragraphstyle_mut(), xml_tag)?;
3269                }
3270                Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:paragraph-properties" => {
3271                }
3272                // Event::Start(xml_tag) | Event::Empty(xml_tag)
3273                //     if xml_tag.name().as_ref() == b"style:graphic-properties" =>
3274                // {
3275                //     copy_attr(style.graphic_mut(), xml, xml_tag)?;
3276                // }
3277                Event::Start(xml_tag) | Event::Empty(xml_tag)
3278                    if xml_tag.name().as_ref() == b"style:map" =>
3279                {
3280                    style.push_stylemap(read_stylemap(ctx, xml_tag)?);
3281                }
3282                // todo: tab-stops
3283                // b"style:tab-stops" => (),
3284                // b"style:tab-stop" => {
3285                //     let mut ts = TabStop::new();
3286                //     copy_attr(&mut ts, xml, xml_tag)?;
3287                //     style.paragraph_mut().add_tabstop(ts);
3288                // }
3289                Event::Text(_) => (),
3290                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
3291                    ctx.book.add_cellstyle(style);
3292                    break;
3293                }
3294                Event::Eof => break,
3295                _ => {
3296                    unused_event("read_cellstyle", &evt)?;
3297                }
3298            }
3299        }
3300        ctx.push_buf(buf);
3301    }
3302
3303    Ok(())
3304}
3305
3306// style:style tag
3307#[allow(clippy::collapsible_else_if)]
3308#[allow(clippy::too_many_arguments)]
3309fn read_paragraphstyle(
3310    ctx: &mut OdsContext,
3311    xml: &mut OdsXmlReader<'_>,
3312    origin: StyleOrigin,
3313    style_use: StyleUse,
3314    super_tag: &BytesStart<'_>,
3315    empty_tag: bool,
3316) -> Result<(), OdsError> {
3317    let mut style = ParagraphStyle::new_empty();
3318    style.set_origin(origin);
3319    style.set_styleuse(style_use);
3320    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3321    style.set_name(name);
3322
3323    // In case of an empty xml-tag we are done here.
3324    if empty_tag {
3325        ctx.book.add_paragraphstyle(style);
3326    } else {
3327        let mut buf = ctx.pop_buf();
3328        loop {
3329            let evt = xml.read_event_into(&mut buf)?;
3330            if cfg!(feature = "dump_xml") {
3331                println!(" read_paragraphstyle {:?}", evt);
3332            }
3333            match &evt {
3334                Event::Start(xml_tag) | Event::Empty(xml_tag)
3335                    if xml_tag.name().as_ref() == b"style:text-properties" =>
3336                {
3337                    copy_attr2(ctx, style.textstyle_mut(), xml_tag)?;
3338                }
3339                Event::Start(xml_tag) | Event::Empty(xml_tag)
3340                    if xml_tag.name().as_ref() == b"style:paragraph-properties" =>
3341                {
3342                    copy_attr2(ctx, style.paragraphstyle_mut(), xml_tag)?;
3343                }
3344                Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:paragraph-properties" => {
3345                }
3346                // b"style:graphic-properties" => copy_attr(style.graphic_mut(), xml, xml_tag)?,
3347                // b"style:map" => style.push_stylemap(read_stylemap(xml, xml_tag)?),
3348                Event::Start(xml_tag) | Event::Empty(xml_tag)
3349                    if xml_tag.name().as_ref() == b"style:tab-stops" => {}
3350                Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:tab-stops" => {}
3351                Event::Start(xml_tag) | Event::Empty(xml_tag)
3352                    if xml_tag.name().as_ref() == b"style:tab-stop" =>
3353                {
3354                    let mut ts = TabStop::new();
3355                    copy_attr2(ctx, ts.attrmap_mut(), xml_tag)?;
3356                    style.add_tabstop(ts);
3357                }
3358
3359                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
3360                    ctx.book.add_paragraphstyle(style);
3361                    break;
3362                }
3363
3364                Event::Text(_) => (),
3365                Event::Eof => break,
3366                _ => {
3367                    unused_event("read_paragraphstyle", &evt)?;
3368                }
3369            }
3370        }
3371        ctx.push_buf(buf);
3372    }
3373
3374    Ok(())
3375}
3376
3377// style:style tag
3378#[allow(clippy::collapsible_else_if)]
3379#[allow(clippy::too_many_arguments)]
3380fn read_textstyle(
3381    ctx: &mut OdsContext,
3382    xml: &mut OdsXmlReader<'_>,
3383    origin: StyleOrigin,
3384    style_use: StyleUse,
3385    super_tag: &BytesStart<'_>,
3386    empty_tag: bool,
3387) -> Result<(), OdsError> {
3388    let mut style = TextStyle::new_empty();
3389    style.set_origin(origin);
3390    style.set_styleuse(style_use);
3391    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3392    style.set_name(name);
3393
3394    // In case of an empty xml-tag we are done here.
3395    if empty_tag {
3396        ctx.book.add_textstyle(style);
3397    } else {
3398        let mut buf = ctx.pop_buf();
3399        loop {
3400            let evt = xml.read_event_into(&mut buf)?;
3401            if cfg!(feature = "dump_xml") {
3402                println!(" read_textstyle {:?}", evt);
3403            }
3404            match &evt {
3405                Event::Start(xml_tag) | Event::Empty(xml_tag)
3406                    if xml_tag.name().as_ref() == b"style:text-properties" =>
3407                {
3408                    copy_attr2(ctx, style.textstyle_mut(), xml_tag)?;
3409                }
3410                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
3411                    ctx.book.add_textstyle(style);
3412                    break;
3413                }
3414                Event::Text(_) => (),
3415                Event::Eof => break,
3416                _ => {
3417                    unused_event("read_textstyle", &evt)?;
3418                }
3419            }
3420        }
3421        ctx.push_buf(buf);
3422    }
3423
3424    Ok(())
3425}
3426
3427// style:style tag
3428#[allow(clippy::collapsible_else_if)]
3429#[allow(clippy::too_many_arguments)]
3430fn read_rubystyle(
3431    ctx: &mut OdsContext,
3432    xml: &mut OdsXmlReader<'_>,
3433    origin: StyleOrigin,
3434    style_use: StyleUse,
3435    super_tag: &BytesStart<'_>,
3436    empty_tag: bool,
3437) -> Result<(), OdsError> {
3438    let mut style = RubyStyle::new_empty();
3439    style.set_origin(origin);
3440    style.set_styleuse(style_use);
3441    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3442    style.set_name(name);
3443
3444    // In case of an empty xml-tag we are done here.
3445    if empty_tag {
3446        ctx.book.add_rubystyle(style);
3447    } else {
3448        let mut buf = ctx.pop_buf();
3449        loop {
3450            let evt = xml.read_event_into(&mut buf)?;
3451            if cfg!(feature = "dump_xml") {
3452                println!(" read_rubystyle {:?}", evt);
3453            }
3454            match &evt {
3455                Event::Start(xml_tag) | Event::Empty(xml_tag)
3456                    if xml_tag.name().as_ref() == b"style:ruby-properties" =>
3457                {
3458                    copy_attr2(ctx, style.rubystyle_mut(), xml_tag)?;
3459                }
3460                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
3461                    ctx.book.add_rubystyle(style);
3462                    break;
3463                }
3464                Event::Text(_) => (),
3465                Event::Eof => break,
3466                _ => {
3467                    unused_event("read_rubystyle", &evt)?;
3468                }
3469            }
3470        }
3471        ctx.push_buf(buf);
3472    }
3473
3474    Ok(())
3475}
3476
3477// style:style tag
3478#[allow(clippy::collapsible_else_if)]
3479#[allow(clippy::too_many_arguments)]
3480fn read_graphicstyle(
3481    ctx: &mut OdsContext,
3482    xml: &mut OdsXmlReader<'_>,
3483    origin: StyleOrigin,
3484    style_use: StyleUse,
3485    super_tag: &BytesStart<'_>,
3486    empty_tag: bool,
3487) -> Result<(), OdsError> {
3488    let mut style = GraphicStyle::new_empty();
3489    style.set_origin(origin);
3490    style.set_styleuse(style_use);
3491    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3492    style.set_name(name);
3493
3494    // In case of an empty xml-tag we are done here.
3495    if empty_tag {
3496        ctx.book.add_graphicstyle(style);
3497    } else {
3498        let mut buf = ctx.pop_buf();
3499        loop {
3500            let evt = xml.read_event_into(&mut buf)?;
3501            if cfg!(feature = "dump_xml") {
3502                println!(" read_graphicstyle {:?}", evt);
3503            }
3504            match &evt {
3505                Event::Start(xml_tag) | Event::Empty(xml_tag)
3506                    if xml_tag.name().as_ref() == b"style:graphic-properties" =>
3507                {
3508                    copy_attr2(ctx, style.graphicstyle_mut(), xml_tag)?;
3509                }
3510                Event::Start(xml_tag) | Event::Empty(xml_tag)
3511                    if xml_tag.name().as_ref() == b"style:paragraph-properties" =>
3512                {
3513                    copy_attr2(ctx, style.paragraphstyle_mut(), xml_tag)?;
3514                }
3515                Event::Start(xml_tag) | Event::Empty(xml_tag)
3516                    if xml_tag.name().as_ref() == b"style:text-properties" =>
3517                {
3518                    copy_attr2(ctx, style.textstyle_mut(), xml_tag)?;
3519                }
3520                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
3521                    ctx.book.add_graphicstyle(style);
3522                    break;
3523                }
3524                Event::Text(_) => (),
3525                Event::Eof => break,
3526                _ => {
3527                    unused_event("read_graphicstyle", &evt)?;
3528                }
3529            }
3530        }
3531        ctx.push_buf(buf);
3532    }
3533
3534    Ok(())
3535}
3536
3537// style:map inside a number style.
3538fn read_value_stylemap(
3539    ctx: &mut OdsContext,
3540    super_tag: &BytesStart<'_>,
3541) -> Result<ValueStyleMap, OdsError> {
3542    let mut sm = ValueStyleMap::default();
3543    for attr in super_tag.attributes().with_checks(false) {
3544        match attr? {
3545            attr if attr.key.as_ref() == b"style:condition" => {
3546                sm.set_condition(ValueCondition::new(
3547                    attr.decode_and_unescape_value(ctx.decoder)?.to_string(),
3548                ));
3549            }
3550            attr if attr.key.as_ref() == b"style:apply-style-name" => {
3551                sm.set_applied_style(attr.decode_and_unescape_value(ctx.decoder)?);
3552            }
3553            attr => {
3554                unused_attr("read_value_stylemap", super_tag.name().as_ref(), &attr)?;
3555            }
3556        }
3557    }
3558
3559    Ok(sm)
3560}
3561
3562fn read_stylemap(ctx: &mut OdsContext, super_tag: &BytesStart<'_>) -> Result<StyleMap, OdsError> {
3563    let mut sm = StyleMap::new_empty();
3564    for attr in super_tag.attributes().with_checks(false) {
3565        match attr? {
3566            attr if attr.key.as_ref() == b"style:condition" => {
3567                sm.set_condition(Condition::new(
3568                    attr.decode_and_unescape_value(ctx.decoder)?.to_string(),
3569                ));
3570            }
3571            attr if attr.key.as_ref() == b"style:apply-style-name" => {
3572                let name = attr.decode_and_unescape_value(ctx.decoder)?;
3573                sm.set_applied_style(AnyStyleRef::from(name.as_ref()));
3574            }
3575            attr if attr.key.as_ref() == b"style:base-cell-address" => {
3576                let v = attr.decode_and_unescape_value(ctx.decoder)?;
3577                sm.set_base_cell(Some(parse_cellref(v.as_ref())?));
3578            }
3579            attr => {
3580                unused_attr("read_stylemap", super_tag.name().as_ref(), &attr)?;
3581            }
3582        }
3583    }
3584
3585    Ok(sm)
3586}
3587
3588/// Copies all attributes to the map, excluding "style:name" which is returned.
3589fn copy_style_attr(
3590    ctx: &mut OdsContext,
3591    attrmap: &mut AttrMap2,
3592    super_tag: &BytesStart<'_>,
3593) -> Result<String, OdsError> {
3594    let mut name = None;
3595
3596    for attr in super_tag.attributes().with_checks(false) {
3597        match attr? {
3598            attr if attr.key.as_ref() == b"style:name" => {
3599                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
3600            }
3601            attr => {
3602                let k = from_utf8(attr.key.as_ref())?;
3603                let v = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
3604                attrmap.push_attr(k, v);
3605            }
3606        }
3607    }
3608
3609    Ok(name.unwrap_or_default())
3610}
3611
3612/// Copies all attributes to the given map.
3613fn copy_attr2(
3614    ctx: &mut OdsContext,
3615    attrmap: &mut AttrMap2,
3616    super_tag: &BytesStart<'_>,
3617) -> Result<(), OdsError> {
3618    for attr in super_tag.attributes().with_checks(false) {
3619        let attr = attr?;
3620
3621        let k = from_utf8(attr.key.as_ref())?;
3622        let v = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
3623        attrmap.push_attr(k, v);
3624    }
3625
3626    Ok(())
3627}
3628
3629fn read_ods_styles(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
3630    let mut buf = ctx.pop_buf();
3631    loop {
3632        let evt = xml.read_event_into(&mut buf)?;
3633        if cfg!(feature = "dump_xml") {
3634            println!(" read_styles {:?}", evt);
3635        }
3636        match &evt {
3637            Event::Decl(_) => {}
3638            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:document-styles" => {
3639                let (_, xmlns) = read_namespaces_and_version(ctx, xml_tag)?;
3640                ctx.book.xmlns.insert("styles.xml".to_string(), xmlns);
3641            }
3642            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:document-styles" => {
3643                // noop
3644            }
3645            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:font-face-decls" => {
3646                read_office_font_face_decls(ctx, xml, StyleOrigin::Styles)?
3647            }
3648            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:styles" => {
3649                read_office_styles(ctx, xml, StyleOrigin::Styles)?
3650            }
3651            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:automatic-styles" => {
3652                read_office_automatic_styles(ctx, xml, StyleOrigin::Styles)?
3653            }
3654            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:master-styles" => {
3655                read_office_master_styles(ctx, xml, StyleOrigin::Styles)?
3656            }
3657            Event::Eof => {
3658                break;
3659            }
3660            _ => {
3661                unused_event("read_styles", &evt)?;
3662            }
3663        }
3664
3665        buf.clear();
3666    }
3667    ctx.push_buf(buf);
3668
3669    Ok(())
3670}
3671
3672#[allow(unused_variables)]
3673pub(crate) fn default_settings() -> Detach<Config> {
3674    let mut dc = Detach::new(Config::new());
3675    let p0 = dc.create_path(&[("ooo:view-settings", ConfigItemType::Set)]);
3676    p0.insert("VisibleAreaTop", 0);
3677    p0.insert("VisibleAreaLeft", 0);
3678    p0.insert("VisibleAreaWidth", 2540);
3679    p0.insert("VisibleAreaHeight", 1270);
3680
3681    let p0 = dc.create_path(&[
3682        ("ooo:view-settings", ConfigItemType::Set),
3683        ("Views", ConfigItemType::Vec),
3684        ("0", ConfigItemType::Entry),
3685    ]);
3686    p0.insert("ViewId", "view1");
3687    let p0 = dc.create_path(&[
3688        ("ooo:view-settings", ConfigItemType::Set),
3689        ("Views", ConfigItemType::Vec),
3690        ("0", ConfigItemType::Entry),
3691        ("Tables", ConfigItemType::Map),
3692    ]);
3693    let p0 = dc.create_path(&[
3694        ("ooo:view-settings", ConfigItemType::Set),
3695        ("Views", ConfigItemType::Vec),
3696        ("0", ConfigItemType::Entry),
3697    ]);
3698    p0.insert("ActiveTable", "");
3699    p0.insert("HorizontalScrollbarWidth", 702);
3700    p0.insert("ZoomType", 0i16);
3701    p0.insert("ZoomValue", 100);
3702    p0.insert("PageViewZoomValue", 60);
3703    p0.insert("ShowPageBreakPreview", false);
3704    p0.insert("ShowZeroValues", true);
3705    p0.insert("ShowNotes", true);
3706    p0.insert("ShowGrid", true);
3707    p0.insert("GridColor", 12632256);
3708    p0.insert("ShowPageBreaks", false);
3709    p0.insert("HasColumnRowHeaders", true);
3710    p0.insert("HasSheetTabs", true);
3711    p0.insert("IsOutlineSymbolsSet", true);
3712    p0.insert("IsValueHighlightingEnabled", false);
3713    p0.insert("IsSnapToRaster", false);
3714    p0.insert("RasterIsVisible", false);
3715    p0.insert("RasterResolutionX", 1000);
3716    p0.insert("RasterResolutionY", 1000);
3717    p0.insert("RasterSubdivisionX", 1);
3718    p0.insert("RasterSubdivisionY", 1);
3719    p0.insert("IsRasterAxisSynchronized", true);
3720    p0.insert("AnchoredTextOverflowLegacy", false);
3721
3722    let p0 = dc.create_path(&[("ooo:configuration-settings", ConfigItemType::Set)]);
3723    p0.insert("HasSheetTabs", true);
3724    p0.insert("ShowNotes", true);
3725    p0.insert("EmbedComplexScriptFonts", true);
3726    p0.insert("ShowZeroValues", true);
3727    p0.insert("ShowGrid", true);
3728    p0.insert("GridColor", 12632256);
3729    p0.insert("ShowPageBreaks", false);
3730    p0.insert("IsKernAsianPunctuation", false);
3731    p0.insert("LinkUpdateMode", 3i16);
3732    p0.insert("HasColumnRowHeaders", true);
3733    p0.insert("EmbedLatinScriptFonts", true);
3734    p0.insert("IsOutlineSymbolsSet", true);
3735    p0.insert("EmbedLatinScriptFonts", true);
3736    p0.insert("IsOutlineSymbolsSet", true);
3737    p0.insert("IsSnapToRaster", false);
3738    p0.insert("RasterIsVisible", false);
3739    p0.insert("RasterResolutionX", 1000);
3740    p0.insert("RasterResolutionY", 1000);
3741    p0.insert("RasterSubdivisionX", 1);
3742    p0.insert("RasterSubdivisionY", 1);
3743    p0.insert("IsRasterAxisSynchronized", true);
3744    p0.insert("AutoCalculate", true);
3745    p0.insert("ApplyUserData", true);
3746    p0.insert("PrinterName", "");
3747    p0.insert("PrinterSetup", ConfigValue::Base64Binary("".to_string()));
3748    p0.insert("SaveThumbnail", true);
3749    p0.insert("CharacterCompressionType", 0i16);
3750    p0.insert("SaveVersionOnClose", false);
3751    p0.insert("UpdateFromTemplate", true);
3752    p0.insert("AllowPrintJobCancel", true);
3753    p0.insert("LoadReadonly", false);
3754    p0.insert("IsDocumentShared", false);
3755    p0.insert("EmbedFonts", false);
3756    p0.insert("EmbedOnlyUsedFonts", false);
3757    p0.insert("EmbedAsianScriptFonts", true);
3758    p0.insert("SyntaxStringRef", 7i16);
3759
3760    dc
3761}
3762
3763fn read_ods_metadata(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
3764    let mut buf = ctx.pop_buf();
3765
3766    loop {
3767        let evt = xml.read_event_into(&mut buf)?;
3768        if cfg!(feature = "dump_xml") {
3769            println!("read_ods_metadata {:?}", evt);
3770        }
3771
3772        match &evt {
3773            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:document-meta" => {
3774                let (_, xmlns) = read_namespaces_and_version(ctx, xml_tag)?;
3775                ctx.book.xmlns.insert("meta.xml".to_string(), xmlns);
3776            }
3777            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:document-meta" => {}
3778
3779            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:meta" => {
3780                read_office_meta(ctx, xml)?;
3781            }
3782
3783            Event::Decl(_) => {}
3784            Event::Eof => {
3785                break;
3786            }
3787            _ => {
3788                unused_event("read_ods_metadata", &evt)?;
3789            }
3790        }
3791
3792        buf.clear();
3793    }
3794    ctx.push_buf(buf);
3795
3796    Ok(())
3797}
3798
3799fn read_office_meta(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
3800    let mut buf = ctx.pop_buf();
3801
3802    loop {
3803        let evt = xml.read_event_into(&mut buf)?;
3804        if cfg!(feature = "dump_xml") {
3805            println!("read_metadata {:?}", evt);
3806        }
3807
3808        match &evt {
3809            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:meta" => {
3810                break;
3811            }
3812
3813            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:generator" => {
3814                ctx.book.metadata.generator = read_metadata_value(
3815                    ctx,
3816                    xml,
3817                    xml_tag,
3818                    |v| Ok(from_utf8(v)?.to_string()),
3819                    String::new,
3820                )?;
3821            }
3822            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:title" => {
3823                ctx.book.metadata.title = read_metadata_value(
3824                    ctx,
3825                    xml,
3826                    xml_tag,
3827                    |v| Ok(from_utf8(v)?.to_string()),
3828                    String::new,
3829                )?;
3830            }
3831            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:description" => {
3832                ctx.book.metadata.description = read_metadata_value(
3833                    ctx,
3834                    xml,
3835                    xml_tag,
3836                    |v| Ok(from_utf8(v)?.to_string()),
3837                    String::new,
3838                )?;
3839            }
3840            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:subject" => {
3841                ctx.book.metadata.subject = read_metadata_value(
3842                    ctx,
3843                    xml,
3844                    xml_tag,
3845                    |v| Ok(from_utf8(v)?.to_string()),
3846                    String::new,
3847                )?;
3848            }
3849            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:keyword" => {
3850                ctx.book.metadata.keyword = read_metadata_value(
3851                    ctx,
3852                    xml,
3853                    xml_tag,
3854                    |v| Ok(from_utf8(v)?.to_string()),
3855                    String::new,
3856                )?;
3857            }
3858            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:initial-creator" => {
3859                ctx.book.metadata.initial_creator = read_metadata_value(
3860                    ctx,
3861                    xml,
3862                    xml_tag,
3863                    |v| Ok(from_utf8(v)?.to_string()),
3864                    String::new,
3865                )?;
3866            }
3867            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:creator" => {
3868                ctx.book.metadata.creator = read_metadata_value(
3869                    ctx,
3870                    xml,
3871                    xml_tag,
3872                    |v| Ok(from_utf8(v)?.to_string()),
3873                    String::new,
3874                )?;
3875            }
3876            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:printed-by" => {
3877                ctx.book.metadata.printed_by = read_metadata_value(
3878                    ctx,
3879                    xml,
3880                    xml_tag,
3881                    |v| Ok(from_utf8(v)?.to_string()),
3882                    String::new,
3883                )?;
3884            }
3885            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:creation-date" => {
3886                ctx.book.metadata.creation_date = read_metadata_value(
3887                    ctx,
3888                    xml,
3889                    xml_tag,
3890                    |v| Ok(Some(parse_datetime(v)?)),
3891                    || None,
3892                )?;
3893            }
3894            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:date" => {
3895                ctx.book.metadata.date = read_metadata_value(
3896                    ctx,
3897                    xml,
3898                    xml_tag,
3899                    |v| Ok(Some(parse_datetime(v)?)),
3900                    || None,
3901                )?;
3902            }
3903            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:print-date" => {
3904                ctx.book.metadata.print_date = read_metadata_value(
3905                    ctx,
3906                    xml,
3907                    xml_tag,
3908                    |v| Ok(Some(parse_datetime(v)?)),
3909                    || None,
3910                )?;
3911            }
3912            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:language" => {
3913                ctx.book.metadata.language = read_metadata_value(
3914                    ctx,
3915                    xml,
3916                    xml_tag,
3917                    |v| Ok(from_utf8(v)?.to_string()),
3918                    String::new,
3919                )?;
3920            }
3921            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:editing-cycles" => {
3922                ctx.book.metadata.editing_cycles =
3923                    read_metadata_value(ctx, xml, xml_tag, parse_u32, || 0)?;
3924            }
3925            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:editing-duration" => {
3926                ctx.book.metadata.editing_duration =
3927                    read_metadata_value(ctx, xml, xml_tag, parse_duration, Duration::default)?;
3928            }
3929
3930            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:template" => {
3931                ctx.book.metadata.template = read_metadata_template(ctx, xml_tag)?;
3932            }
3933            Event::End(xml_tag) if xml_tag.name().as_ref() == b"meta:template" => {}
3934
3935            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:auto-reload" => {
3936                ctx.book.metadata.auto_reload = read_metadata_auto_reload(ctx, xml_tag)?;
3937            }
3938            Event::End(xml_tag) if xml_tag.name().as_ref() == b"meta:auto-reload" => {}
3939
3940            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:hyperlink-behaviour" => {
3941                ctx.book.metadata.hyperlink_behaviour =
3942                    read_metadata_hyperlink_behaviour(ctx, xml_tag)?;
3943            }
3944            Event::End(xml_tag) if xml_tag.name().as_ref() == b"meta:hyperlink-behaviour" => {}
3945
3946            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:document-statistic" => {
3947                ctx.book.metadata.document_statistics =
3948                    read_metadata_document_statistics(ctx, xml_tag)?;
3949            }
3950            Event::End(xml_tag) if xml_tag.name().as_ref() == b"meta:document-statistic" => {}
3951
3952            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:user-defined" => {
3953                let userdefined = read_metadata_user_defined(ctx, xml, xml_tag)?;
3954                ctx.book.metadata.user_defined.push(userdefined);
3955            }
3956
3957            Event::Empty(_) => {}
3958            Event::Text(_) => {}
3959            Event::Eof => {
3960                break;
3961            }
3962            _ => {
3963                unused_event("read_metadata", &evt)?;
3964            }
3965        }
3966
3967        buf.clear();
3968    }
3969    ctx.push_buf(buf);
3970
3971    Ok(())
3972}
3973
3974fn read_metadata_template(
3975    ctx: &mut OdsContext,
3976    tag: &BytesStart<'_>,
3977) -> Result<MetaTemplate, OdsError> {
3978    let mut template = MetaTemplate::default();
3979
3980    for attr in tag.attributes().with_checks(false) {
3981        match attr? {
3982            attr if attr.key.as_ref() == b"meta:date" => {
3983                template.date = Some(parse_datetime(
3984                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
3985                )?);
3986            }
3987            attr if attr.key.as_ref() == b"xlink:actuate" => {
3988                template.actuate = Some(parse_xlink_actuate(
3989                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
3990                )?);
3991            }
3992            attr if attr.key.as_ref() == b"xlink:href" => {
3993                template.href = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string())
3994            }
3995            attr if attr.key.as_ref() == b"xlink:title" => {
3996                template.title = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string())
3997            }
3998            attr if attr.key.as_ref() == b"xlink:type" => {
3999                template.link_type = Some(parse_xlink_type(
4000                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4001                )?);
4002            }
4003            attr => {
4004                unused_attr("read_metadata_template", tag.name().as_ref(), &attr)?;
4005            }
4006        }
4007    }
4008
4009    Ok(template)
4010}
4011
4012fn read_metadata_auto_reload(
4013    ctx: &mut OdsContext,
4014    tag: &BytesStart<'_>,
4015) -> Result<MetaAutoReload, OdsError> {
4016    let mut auto_reload = MetaAutoReload::default();
4017
4018    for attr in tag.attributes().with_checks(false) {
4019        match attr? {
4020            attr if attr.key.as_ref() == b"meta:delay" => {
4021                auto_reload.delay = Some(parse_duration(
4022                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4023                )?);
4024            }
4025            attr if attr.key.as_ref() == b"xlink:actuate" => {
4026                auto_reload.actuate = Some(parse_xlink_actuate(
4027                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4028                )?);
4029            }
4030            attr if attr.key.as_ref() == b"xlink:href" => {
4031                auto_reload.href = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string())
4032            }
4033            attr if attr.key.as_ref() == b"xlink:show" => {
4034                auto_reload.show = Some(parse_xlink_show(
4035                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4036                )?);
4037            }
4038            attr if attr.key.as_ref() == b"xlink:type" => {
4039                auto_reload.link_type = Some(parse_xlink_type(
4040                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4041                )?);
4042            }
4043            attr => {
4044                unused_attr("read_metadata_auto_reload", tag.name().as_ref(), &attr)?;
4045            }
4046        }
4047    }
4048
4049    Ok(auto_reload)
4050}
4051
4052fn read_metadata_hyperlink_behaviour(
4053    ctx: &mut OdsContext,
4054    tag: &BytesStart<'_>,
4055) -> Result<MetaHyperlinkBehaviour, OdsError> {
4056    let mut hyperlink_behaviour = MetaHyperlinkBehaviour::default();
4057
4058    for attr in tag.attributes().with_checks(false) {
4059        match attr? {
4060            attr if attr.key.as_ref() == b"office:targetframe-name" => {
4061                hyperlink_behaviour.target_frame_name =
4062                    Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4063            }
4064            attr if attr.key.as_ref() == b"xlink:show" => {
4065                hyperlink_behaviour.show = Some(parse_xlink_show(
4066                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4067                )?);
4068            }
4069            attr => {
4070                unused_attr(
4071                    "read_metadata_hyperlink_behaviour",
4072                    tag.name().as_ref(),
4073                    &attr,
4074                )?;
4075            }
4076        }
4077    }
4078
4079    Ok(hyperlink_behaviour)
4080}
4081
4082fn read_metadata_document_statistics(
4083    ctx: &mut OdsContext,
4084    tag: &BytesStart<'_>,
4085) -> Result<MetaDocumentStatistics, OdsError> {
4086    let mut document_statistics = MetaDocumentStatistics::default();
4087
4088    for attr in tag.attributes().with_checks(false) {
4089        match attr? {
4090            attr if attr.key.as_ref() == b"meta:cell-count" => {
4091                document_statistics.cell_count =
4092                    parse_u32(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
4093            }
4094            attr if attr.key.as_ref() == b"meta:object-count" => {
4095                document_statistics.object_count =
4096                    parse_u32(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
4097            }
4098            attr if attr.key.as_ref() == b"meta:ole-object-count" => {
4099                document_statistics.ole_object_count =
4100                    parse_u32(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
4101            }
4102            attr if attr.key.as_ref() == b"meta:table-count" => {
4103                document_statistics.table_count =
4104                    parse_u32(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
4105            }
4106            attr => {
4107                unused_attr(
4108                    "read_metadata_document_statistics",
4109                    tag.name().as_ref(),
4110                    &attr,
4111                )?;
4112            }
4113        }
4114    }
4115
4116    Ok(document_statistics)
4117}
4118
4119fn read_metadata_user_defined(
4120    ctx: &mut OdsContext,
4121    xml: &mut OdsXmlReader<'_>,
4122    tag: &BytesStart<'_>,
4123) -> Result<MetaUserDefined, OdsError> {
4124    let mut user_defined = MetaUserDefined::default();
4125    let mut value_type = None;
4126    for attr in tag.attributes().with_checks(false) {
4127        match attr? {
4128            attr if attr.key.as_ref() == b"meta:name" => {
4129                user_defined.name = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
4130            }
4131            attr if attr.key.as_ref() == b"meta:value-type" => {
4132                value_type = Some(
4133                    match attr.decode_and_unescape_value(ctx.decoder)?.as_ref() {
4134                        "boolean" => "boolean",
4135                        "date" => "date",
4136                        "float" => "float",
4137                        "time" => "time",
4138                        _ => "string",
4139                    },
4140                );
4141            }
4142            attr => {
4143                unused_attr("read_meta_user_defined", tag.name().as_ref(), &attr)?;
4144            }
4145        }
4146    }
4147
4148    let mut buf = ctx.pop_buf();
4149    loop {
4150        let evt = xml.read_event_into(&mut buf)?;
4151        if cfg!(feature = "dump_xml") {
4152            println!("read_meta_user_defined {:?}", evt);
4153        }
4154
4155        match &evt {
4156            Event::End(xml_tag) if xml_tag.name() == tag.name() => {
4157                break;
4158            }
4159            Event::Text(xml_text) => {
4160                user_defined.value = match value_type {
4161                    Some("boolean") => MetaValue::Boolean(parse_bool(xml_text)?),
4162                    Some("date") => MetaValue::Datetime(parse_datetime(xml_text)?),
4163                    Some("float") => MetaValue::Float(parse_f64(xml_text)?),
4164                    Some("time") => MetaValue::TimeDuration(parse_duration(xml_text)?),
4165                    _ => MetaValue::String(xml_text.decode()?.to_string()),
4166                };
4167            }
4168            Event::Eof => {
4169                break;
4170            }
4171            _ => {
4172                unused_event("read_meta_user_defined", &evt)?;
4173            }
4174        }
4175
4176        buf.clear();
4177    }
4178    ctx.push_buf(buf);
4179
4180    Ok(user_defined)
4181}
4182
4183// Parse a metadata value.
4184fn read_metadata_value<T>(
4185    ctx: &mut OdsContext,
4186    xml: &mut OdsXmlReader<'_>,
4187    tag: &BytesStart<'_>,
4188    parse: fn(&[u8]) -> Result<T, OdsError>,
4189    default: fn() -> T,
4190) -> Result<T, OdsError> {
4191    let mut buf = ctx.pop_buf();
4192    let mut text = String::new();
4193    loop {
4194        let evt = xml.read_event_into(&mut buf)?;
4195        if cfg!(feature = "dump_xml") {
4196            println!("read_metadata_value {:?}", evt);
4197        }
4198
4199        match &evt {
4200            Event::End(xml_tag) if xml_tag.name() == tag.name() => {
4201                break;
4202            }
4203            Event::GeneralRef(xml_ref) => {
4204                text.push_str(entity(xml_ref)?.as_ref());
4205            }
4206            Event::Text(xml_text) => {
4207                text.push_str(xml_text.decode()?.as_ref());
4208            }
4209            Event::Eof => {
4210                break;
4211            }
4212            _ => {
4213                unused_event("read_metadata_value", &evt)?;
4214            }
4215        }
4216
4217        buf.clear();
4218    }
4219    ctx.push_buf(buf);
4220
4221    if !text.is_empty() {
4222        parse(text.as_bytes())
4223    } else {
4224        Ok(default())
4225    }
4226}
4227
4228fn read_ods_settings(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
4229    let mut buf = ctx.pop_buf();
4230    loop {
4231        let evt = xml.read_event_into(&mut buf)?;
4232        if cfg!(feature = "dump_xml") {
4233            println!(" read_settings {:?}", evt);
4234        }
4235
4236        match &evt {
4237            Event::Decl(_) => {}
4238
4239            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:document-settings" => {
4240                let (_, xmlns) = read_namespaces_and_version(ctx, xml_tag)?;
4241                ctx.book.xmlns.insert("settings.xml".to_string(), xmlns);
4242            }
4243            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:document-settings" => {}
4244
4245            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:settings" => {
4246                read_office_settings(ctx, xml)?;
4247            }
4248
4249            Event::Eof => {
4250                break;
4251            }
4252            _ => {
4253                unused_event("read_settings", &evt)?;
4254            }
4255        }
4256
4257        buf.clear();
4258    }
4259    ctx.push_buf(buf);
4260
4261    Ok(())
4262}
4263
4264// read the automatic-styles tag
4265fn read_office_settings(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
4266    let mut config = Config::new();
4267
4268    let mut buf = ctx.pop_buf();
4269    loop {
4270        let evt = xml.read_event_into(&mut buf)?;
4271        if cfg!(feature = "dump_xml") {
4272            println!(" read_office_settings {:?}", evt);
4273        }
4274        match &evt {
4275            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-set" => {
4276                let (name, set) = read_config_item_set(ctx, xml, xml_tag)?;
4277                config.insert(name, set);
4278            }
4279
4280            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:settings" => {
4281                break;
4282            }
4283            Event::Eof => break,
4284            _ => {
4285                unused_event("read_office_settings", &evt)?;
4286            }
4287        }
4288
4289        buf.clear();
4290    }
4291    ctx.push_buf(buf);
4292
4293    ctx.book.config = Detach::new(config);
4294
4295    Ok(())
4296}
4297
4298// read the automatic-styles tag
4299fn read_config_item_set(
4300    ctx: &mut OdsContext,
4301    xml: &mut OdsXmlReader<'_>,
4302    super_tag: &BytesStart<'_>,
4303) -> Result<(String, ConfigItem), OdsError> {
4304    let mut name = None;
4305    let mut config_set = ConfigItem::new_set();
4306
4307    for attr in super_tag.attributes().with_checks(false) {
4308        match attr? {
4309            attr if attr.key.as_ref() == b"config:name" => {
4310                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4311            }
4312            attr => {
4313                unused_attr("read_config_item_set", super_tag.name().as_ref(), &attr)?;
4314            }
4315        }
4316    }
4317
4318    let name = if let Some(name) = name {
4319        name
4320    } else {
4321        return Err(OdsError::Ods("config-item-set without name".to_string()));
4322    };
4323
4324    let mut buf = ctx.pop_buf();
4325    loop {
4326        let evt = xml.read_event_into(&mut buf)?;
4327        if cfg!(feature = "dump_xml") {
4328            println!(" read_office_item_set {:?}", evt);
4329        }
4330        match &evt {
4331            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"config:config-item" => {}
4332            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item" => {
4333                let (name, val) = read_config_item(ctx, xml, xml_tag)?;
4334                config_set.insert(name, val);
4335            }
4336            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-set" => {
4337                let (name, val) = read_config_item_set(ctx, xml, xml_tag)?;
4338                config_set.insert(name, val);
4339            }
4340            Event::Start(xml_tag)
4341                if xml_tag.name().as_ref() == b"config:config-item-map-indexed" =>
4342            {
4343                let (name, val) = read_config_item_map_indexed(ctx, xml, xml_tag)?;
4344                config_set.insert(name, val);
4345            }
4346            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-named" => {
4347                let (name, val) = read_config_item_map_named(ctx, xml, xml_tag)?;
4348                config_set.insert(name, val);
4349            }
4350            Event::End(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-set" => {
4351                break;
4352            }
4353            Event::Eof => break,
4354            _ => {
4355                unused_event("read_config_item_set", &evt)?;
4356            }
4357        }
4358
4359        buf.clear();
4360    }
4361    ctx.push_buf(buf);
4362
4363    Ok((name, config_set))
4364}
4365
4366// read the automatic-styles tag
4367fn read_config_item_map_indexed(
4368    ctx: &mut OdsContext,
4369    xml: &mut OdsXmlReader<'_>,
4370    super_tag: &BytesStart<'_>,
4371) -> Result<(String, ConfigItem), OdsError> {
4372    let mut name = None;
4373    let mut config_vec = ConfigItem::new_vec();
4374
4375    for attr in super_tag.attributes().with_checks(false) {
4376        match attr? {
4377            attr if attr.key.as_ref() == b"config:name" => {
4378                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4379            }
4380            attr => {
4381                unused_attr(
4382                    "read_config_item_map_indexed",
4383                    super_tag.name().as_ref(),
4384                    &attr,
4385                )?;
4386            }
4387        }
4388    }
4389
4390    let name = if let Some(name) = name {
4391        name
4392    } else {
4393        return Err(OdsError::Ods(
4394            "config-item-map-indexed without name".to_string(),
4395        ));
4396    };
4397
4398    let mut index = 0;
4399
4400    let mut buf = ctx.pop_buf();
4401    loop {
4402        let evt = xml.read_event_into(&mut buf)?;
4403        if cfg!(feature = "dump_xml") {
4404            println!(" read_office_item_set {:?}", evt);
4405        }
4406        match &evt {
4407            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-entry" => {
4408                let (_, entry) = read_config_item_map_entry(ctx, xml, xml_tag)?;
4409                config_vec.insert(index.to_string(), entry);
4410                index += 1;
4411            }
4412            Event::End(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-indexed" => {
4413                break;
4414            }
4415            Event::Eof => break,
4416            _ => {
4417                unused_event("read_config_item_map_indexed", &evt)?;
4418            }
4419        }
4420
4421        buf.clear();
4422    }
4423    ctx.push_buf(buf);
4424
4425    Ok((name, config_vec))
4426}
4427
4428// read the automatic-styles tag
4429fn read_config_item_map_named(
4430    ctx: &mut OdsContext,
4431    xml: &mut OdsXmlReader<'_>,
4432    super_tag: &BytesStart<'_>,
4433) -> Result<(String, ConfigItem), OdsError> {
4434    let mut name = None;
4435    let mut config_map = ConfigItem::new_map();
4436
4437    for attr in super_tag.attributes().with_checks(false) {
4438        match attr? {
4439            attr if attr.key.as_ref() == b"config:name" => {
4440                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4441            }
4442            attr => {
4443                unused_attr(
4444                    "read_config_item_map_named",
4445                    super_tag.name().as_ref(),
4446                    &attr,
4447                )?;
4448            }
4449        }
4450    }
4451
4452    let name = if let Some(name) = name {
4453        name
4454    } else {
4455        return Err(OdsError::Ods(
4456            "config-item-map-named without name".to_string(),
4457        ));
4458    };
4459
4460    let mut buf = ctx.pop_buf();
4461    loop {
4462        let evt = xml.read_event_into(&mut buf)?;
4463        if cfg!(feature = "dump_xml") {
4464            println!(" read_config_item_map_named {:?}", evt);
4465        }
4466        match &evt {
4467            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-entry" => {
4468                let (name, entry) = read_config_item_map_entry(ctx, xml, xml_tag)?;
4469
4470                let name = if let Some(name) = name {
4471                    name
4472                } else {
4473                    return Err(OdsError::Ods(
4474                        "config-item-map-entry without name".to_string(),
4475                    ));
4476                };
4477
4478                config_map.insert(name, entry);
4479            }
4480            Event::End(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-named" => {
4481                break;
4482            }
4483            Event::Eof => break,
4484            _ => {
4485                unused_event("read_config_item_map_named", &evt)?;
4486            }
4487        }
4488
4489        buf.clear();
4490    }
4491    ctx.push_buf(buf);
4492
4493    Ok((name, config_map))
4494}
4495
4496// read the automatic-styles tag
4497fn read_config_item_map_entry(
4498    ctx: &mut OdsContext,
4499    xml: &mut OdsXmlReader<'_>,
4500    super_tag: &BytesStart<'_>,
4501) -> Result<(Option<String>, ConfigItem), OdsError> {
4502    let mut name = None;
4503    let mut config_set = ConfigItem::new_entry();
4504
4505    for attr in super_tag.attributes().with_checks(false) {
4506        match attr? {
4507            attr if attr.key.as_ref() == b"config:name" => {
4508                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4509            }
4510            attr => {
4511                unused_attr(
4512                    "read_config_item_map_entry",
4513                    super_tag.name().as_ref(),
4514                    &attr,
4515                )?;
4516            }
4517        }
4518    }
4519
4520    let mut buf = ctx.pop_buf();
4521    loop {
4522        let evt = xml.read_event_into(&mut buf)?;
4523        if cfg!(feature = "dump_xml") {
4524            println!(" read_config_item_map_entry {:?}", evt);
4525        }
4526        match &evt {
4527            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"config:config-item" => {}
4528            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item" => {
4529                let (name, val) = read_config_item(ctx, xml, xml_tag)?;
4530                config_set.insert(name, ConfigItem::from(val));
4531            }
4532
4533            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-set" => {
4534                let (name, val) = read_config_item_set(ctx, xml, xml_tag)?;
4535                config_set.insert(name, val);
4536            }
4537
4538            Event::Start(xml_tag)
4539                if xml_tag.name().as_ref() == b"config:config-item-map-indexed" =>
4540            {
4541                let (name, val) = read_config_item_map_indexed(ctx, xml, xml_tag)?;
4542                config_set.insert(name, val);
4543            }
4544
4545            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-named" => {
4546                let (name, val) = read_config_item_map_named(ctx, xml, xml_tag)?;
4547                config_set.insert(name, val);
4548            }
4549            Event::End(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-entry" => {
4550                break;
4551            }
4552
4553            Event::Eof => break,
4554            _ => {
4555                unused_event("read_config_item_map_entry", &evt)?;
4556            }
4557        }
4558
4559        buf.clear();
4560    }
4561    ctx.push_buf(buf);
4562
4563    Ok((name, config_set))
4564}
4565
4566// read the automatic-styles tag
4567fn read_config_item(
4568    ctx: &mut OdsContext,
4569    xml: &mut OdsXmlReader<'_>,
4570    super_tag: &BytesStart<'_>,
4571) -> Result<(String, ConfigValue), OdsError> {
4572    #[derive(PartialEq)]
4573    enum ConfigValueType {
4574        None,
4575        Base64Binary,
4576        Boolean,
4577        DateTime,
4578        Double,
4579        Int,
4580        Long,
4581        Short,
4582        String,
4583    }
4584
4585    let mut name = None;
4586    let mut val_type = ConfigValueType::None;
4587    let mut config_val = None;
4588
4589    for attr in super_tag.attributes().with_checks(false) {
4590        match attr? {
4591            attr if attr.key.as_ref() == b"config:name" => {
4592                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4593            }
4594            attr if attr.key.as_ref() == b"config:type" => {
4595                val_type = match attr.value.as_ref() {
4596                    b"base64Binary" => ConfigValueType::Base64Binary,
4597                    b"boolean" => ConfigValueType::Boolean,
4598                    b"datetime" => ConfigValueType::DateTime,
4599                    b"double" => ConfigValueType::Double,
4600                    b"int" => ConfigValueType::Int,
4601                    b"long" => ConfigValueType::Long,
4602                    b"short" => ConfigValueType::Short,
4603                    b"string" => ConfigValueType::String,
4604                    x => {
4605                        return Err(OdsError::Ods(format!(
4606                            "unknown config:type {}",
4607                            from_utf8(x)?
4608                        )));
4609                    }
4610                };
4611            }
4612            attr => {
4613                unused_attr("read_config_item", super_tag.name().as_ref(), &attr)?;
4614            }
4615        }
4616    }
4617
4618    let name = if let Some(name) = name {
4619        name
4620    } else {
4621        return Err(OdsError::Ods(
4622            "config value without config:name".to_string(),
4623        ));
4624    };
4625
4626    if val_type == ConfigValueType::None {
4627        return Err(OdsError::Ods(
4628            "config value without config:type".to_string(),
4629        ));
4630    };
4631
4632    let mut value = ctx.pop_buf();
4633    let mut buf = ctx.pop_buf();
4634    loop {
4635        let evt = xml.read_event_into(&mut buf)?;
4636        match &evt {
4637            Event::GeneralRef(xml_ref) => {
4638                value.write_all(entity(xml_ref)?.as_bytes())?;
4639            }
4640            Event::Text(xml_text) => {
4641                value.write_all(xml_text.decode()?.as_bytes())?;
4642            }
4643            Event::End(xml_tag) if xml_tag.name().as_ref() == b"config:config-item" => {
4644                let value = <Cow<'_, [u8]> as From<&Vec<u8>>>::from(value.as_ref());
4645                match val_type {
4646                    ConfigValueType::None => {}
4647                    ConfigValueType::Base64Binary => {
4648                        config_val =
4649                            Some(ConfigValue::Base64Binary(from_utf8(&value)?.to_string()));
4650                    }
4651                    ConfigValueType::Boolean => {
4652                        config_val = Some(ConfigValue::Boolean(parse_bool(&value)?));
4653                    }
4654                    ConfigValueType::DateTime => {
4655                        config_val = Some(ConfigValue::DateTime(parse_datetime(&value)?));
4656                    }
4657                    ConfigValueType::Double => {
4658                        config_val = Some(ConfigValue::Double(parse_f64(&value)?));
4659                    }
4660                    ConfigValueType::Int => {
4661                        config_val = Some(ConfigValue::Int(parse_i32(&value)?));
4662                    }
4663                    ConfigValueType::Long => {
4664                        config_val = Some(ConfigValue::Long(parse_i64(&value)?));
4665                    }
4666                    ConfigValueType::Short => {
4667                        config_val = Some(ConfigValue::Short(parse_i16(&value)?));
4668                    }
4669                    ConfigValueType::String => {
4670                        config_val =
4671                            Some(ConfigValue::String(from_utf8(value.as_ref())?.to_string()));
4672                    }
4673                }
4674                break;
4675            }
4676            Event::Eof => {
4677                break;
4678            }
4679            _ => {
4680                unused_event("read_config_item", &evt)?;
4681            }
4682        }
4683
4684        if cfg!(feature = "dump_xml") {
4685            println!(" read_config_item {:?}", evt);
4686        }
4687        buf.clear();
4688    }
4689    ctx.push_buf(buf);
4690    ctx.push_buf(value);
4691
4692    let config_val = if let Some(config_val) = config_val {
4693        config_val
4694    } else {
4695        return Err(OdsError::Ods("config-item without value???".to_string()));
4696    };
4697
4698    Ok((name, config_val))
4699}
4700
4701// Reads a part of the XML as XmlTag's.
4702fn read_xml(
4703    ctx: &mut OdsContext,
4704    xml: &mut OdsXmlReader<'_>,
4705    super_tag: &BytesStart<'_>,
4706    empty_tag: bool,
4707) -> Result<XmlTag, OdsError> {
4708    let mut stack = ctx.pop_xml_buf();
4709
4710    let mut tag = XmlTag::new(from_utf8(super_tag.name().as_ref())?);
4711    copy_attr2(ctx, tag.attrmap_mut(), super_tag)?;
4712    stack.push(tag);
4713
4714    if !empty_tag {
4715        let mut buf = ctx.pop_buf();
4716        loop {
4717            let evt = xml.read_event_into(&mut buf)?;
4718            if cfg!(feature = "dump_xml") {
4719                println!(" read_xml {:?}", evt);
4720            }
4721            match &evt {
4722                Event::Start(xml_tag) => {
4723                    let mut tag = XmlTag::new(from_utf8(xml_tag.name().as_ref())?);
4724                    copy_attr2(ctx, tag.attrmap_mut(), xml_tag)?;
4725                    stack.push(tag);
4726                }
4727                Event::End(xml_tag) => {
4728                    if xml_tag.name() == super_tag.name() {
4729                        break;
4730                    } else {
4731                        let tag = stack.pop().expect("valid stack");
4732                        if let Some(parent) = stack.last_mut() {
4733                            parent.add_tag(tag);
4734                        } else {
4735                            unreachable!()
4736                        }
4737                    }
4738                }
4739                Event::Empty(xml_tag) => {
4740                    let mut emptytag = XmlTag::new(from_utf8(xml_tag.name().as_ref())?);
4741                    copy_attr2(ctx, emptytag.attrmap_mut(), xml_tag)?;
4742
4743                    if let Some(parent) = stack.last_mut() {
4744                        parent.add_tag(emptytag);
4745                    } else {
4746                        unreachable!()
4747                    }
4748                }
4749                Event::GeneralRef(xml_ref) => {
4750                    if let Some(parent) = stack.last_mut() {
4751                        parent.add_text(entity(xml_ref)?.as_ref());
4752                    } else {
4753                        unreachable!()
4754                    }
4755                }
4756                Event::Text(xml_text) => {
4757                    if let Some(parent) = stack.last_mut() {
4758                        parent.add_text(xml_text.decode()?.as_ref());
4759                    } else {
4760                        unreachable!()
4761                    }
4762                }
4763                Event::Eof => {
4764                    break;
4765                }
4766                _ => {
4767                    unused_event("read_xml", &evt)?;
4768                }
4769            }
4770            buf.clear();
4771        }
4772
4773        ctx.push_buf(buf);
4774    }
4775
4776    let tag = stack.pop().unwrap();
4777    ctx.push_xml_buf(stack);
4778    Ok(tag)
4779}
4780
4781fn read_text_or_tag(
4782    ctx: &mut OdsContext,
4783    xml: &mut OdsXmlReader<'_>,
4784    super_tag: &BytesStart<'_>,
4785    empty_tag: bool,
4786) -> Result<TextContent, OdsError> {
4787    let mut stack = ctx.pop_xml_buf();
4788    let mut cellcontent = TextContent::Empty;
4789
4790    // The toplevel element is passed in with the xml_tag.
4791    // It is only created if there are further xml tags in the
4792    // element. If there is only text this is not needed.
4793    let create_toplevel = |ctx: &mut OdsContext, t: Option<String>| -> Result<XmlTag, OdsError> {
4794        // No parent tag on the stack. Create the parent.
4795        let mut toplevel = XmlTag::new(from_utf8(super_tag.name().as_ref())?);
4796        copy_attr2(ctx, toplevel.attrmap_mut(), super_tag)?;
4797        if let Some(t) = t {
4798            toplevel.add_text(t);
4799        }
4800        Ok(toplevel)
4801    };
4802
4803    if !empty_tag {
4804        let mut buf = ctx.pop_buf();
4805        loop {
4806            let evt = xml.read_event_into(&mut buf)?;
4807            if cfg!(feature = "dump_xml") {
4808                println!(" read_xml {:?}", evt);
4809            }
4810            match &evt {
4811                Event::Start(xml_tag) => {
4812                    match cellcontent {
4813                        TextContent::Empty => {
4814                            stack.push(create_toplevel(ctx, None)?);
4815                        }
4816                        TextContent::Text(old_txt) => {
4817                            stack.push(create_toplevel(ctx, Some(old_txt))?);
4818                        }
4819                        TextContent::Xml(parent) => {
4820                            stack.push(parent);
4821                        }
4822                        TextContent::XmlVec(_) => {
4823                            unreachable!()
4824                        }
4825                    }
4826
4827                    // Set the new tag.
4828                    let mut new_tag = XmlTag::new(from_utf8(xml_tag.name().as_ref())?);
4829                    copy_attr2(ctx, new_tag.attrmap_mut(), xml_tag)?;
4830                    cellcontent = TextContent::Xml(new_tag)
4831                }
4832                Event::Empty(xml_tag) => {
4833                    match cellcontent {
4834                        TextContent::Empty => {
4835                            stack.push(create_toplevel(ctx, None)?);
4836                        }
4837                        TextContent::Text(txt) => {
4838                            stack.push(create_toplevel(ctx, Some(txt))?);
4839                        }
4840                        TextContent::Xml(parent) => {
4841                            stack.push(parent);
4842                        }
4843                        TextContent::XmlVec(_) => {
4844                            unreachable!()
4845                        }
4846                    }
4847                    if let Some(mut parent) = stack.pop() {
4848                        // Create the tag and append it immediately to the parent.
4849                        let mut emptytag = XmlTag::new(from_utf8(xml_tag.name().as_ref())?);
4850                        copy_attr2(ctx, emptytag.attrmap_mut(), xml_tag)?;
4851                        parent.add_tag(emptytag);
4852
4853                        cellcontent = TextContent::Xml(parent);
4854                    } else {
4855                        unreachable!()
4856                    }
4857                }
4858                Event::GeneralRef(xml_ref) => {
4859                    let v = entity(xml_ref)?;
4860
4861                    cellcontent = match cellcontent {
4862                        TextContent::Empty => {
4863                            // Fresh plain text string.
4864                            TextContent::Text(v.to_string())
4865                        }
4866                        TextContent::Text(mut old_txt) => {
4867                            // We have a previous plain text string. Append to it.
4868                            old_txt.push_str(&v);
4869                            TextContent::Text(old_txt)
4870                        }
4871                        TextContent::Xml(mut xml) => {
4872                            // There is already a tag. Append the text to its children.
4873                            xml.add_text(v);
4874                            TextContent::Xml(xml)
4875                        }
4876                        TextContent::XmlVec(_) => {
4877                            unreachable!()
4878                        }
4879                    };
4880                }
4881                Event::Text(xml_text) => {
4882                    let v = xml_text.decode()?;
4883
4884                    cellcontent = match cellcontent {
4885                        TextContent::Empty => {
4886                            // Fresh plain text string.
4887                            TextContent::Text(v.to_string())
4888                        }
4889                        TextContent::Text(mut old_txt) => {
4890                            // We have a previous plain text string. Append to it.
4891                            old_txt.push_str(&v);
4892                            TextContent::Text(old_txt)
4893                        }
4894                        TextContent::Xml(mut xml) => {
4895                            // There is already a tag. Append the text to its children.
4896                            xml.add_text(v);
4897                            TextContent::Xml(xml)
4898                        }
4899                        TextContent::XmlVec(_) => {
4900                            unreachable!()
4901                        }
4902                    };
4903                }
4904                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
4905                    if !stack.is_empty() {
4906                        return Err(OdsError::Ods(format!(
4907                            "XML corrupted. Endtag {} occured before all elements are closed: {:?}",
4908                            from_utf8(super_tag.name().as_ref())?,
4909                            stack
4910                        )));
4911                    }
4912                    break;
4913                }
4914                Event::End(xml_tag) => {
4915                    cellcontent = match cellcontent {
4916                        TextContent::Empty | TextContent::Text(_) => {
4917                            return Err(OdsError::Ods(format!(
4918                                "XML corrupted. Endtag {} occured without start tag",
4919                                from_utf8(xml_tag.name().as_ref())?
4920                            )));
4921                        }
4922                        TextContent::Xml(tag) => {
4923                            if let Some(mut parent) = stack.pop() {
4924                                parent.add_tag(tag);
4925                                TextContent::Xml(parent)
4926                            } else {
4927                                return Err(OdsError::Ods(format!(
4928                                    "XML corrupted. Endtag {} occured without start tag",
4929                                    from_utf8(xml_tag.name().as_ref())?
4930                                )));
4931                            }
4932                        }
4933                        TextContent::XmlVec(_) => {
4934                            unreachable!()
4935                        }
4936                    }
4937                }
4938
4939                Event::Eof => {
4940                    break;
4941                }
4942
4943                _ => {
4944                    unused_event("read_text_or_tag", &evt)?;
4945                }
4946            }
4947        }
4948        ctx.push_buf(buf);
4949    }
4950
4951    ctx.push_xml_buf(stack);
4952
4953    Ok(cellcontent)
4954}
4955
4956/// Read simple text content.
4957/// Fail on any tag other than the end-tag to the supertag.
4958fn read_text<T, E>(
4959    ctx: &mut OdsContext,
4960    xml: &mut OdsXmlReader<'_>,
4961    super_tag: &BytesStart<'_>,
4962    empty_tag: bool,
4963    parse: fn(&[u8]) -> Result<T, E>,
4964) -> Result<Option<T>, OdsError>
4965where
4966    OdsError: From<E>,
4967{
4968    if empty_tag {
4969        Ok(None)
4970    } else {
4971        let mut result_buf = ctx.pop_buf();
4972        let mut buf = ctx.pop_buf();
4973        loop {
4974            let evt = xml.read_event_into(&mut buf)?;
4975            if cfg!(feature = "dump_xml") {
4976                println!(" read_text {:?}", evt);
4977            }
4978            match &evt {
4979                Event::GeneralRef(xml_ref) => {
4980                    result_buf.extend_from_slice(entity(xml_ref)?.as_bytes());
4981                }
4982                Event::Text(xml_text) => {
4983                    result_buf.extend_from_slice(xml_text.as_ref());
4984                }
4985                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
4986                    break;
4987                }
4988                Event::Empty(xml_tag) | Event::Start(xml_tag) => {
4989                    return Err(OdsError::Ods(from_utf8(xml_tag.as_ref())?.to_string()));
4990                }
4991                Event::End(xml_tag) => {
4992                    return Err(OdsError::Ods(from_utf8(xml_tag.as_ref())?.to_string()));
4993                }
4994                Event::Eof => {
4995                    break;
4996                }
4997                _ => {
4998                    unused_event("read_text", &evt)?;
4999                }
5000            }
5001        }
5002        ctx.push_buf(buf);
5003
5004        let result = parse(&result_buf)?;
5005        ctx.push_buf(result_buf);
5006
5007        Ok(Some(result))
5008    }
5009}
5010
5011fn entity(xml_ref: &BytesRef) -> Result<Cow<'static, str>, quick_xml::Error> {
5012    if let Some(cc) = xml_ref.resolve_char_ref()? {
5013        Ok(Cow::Owned(format!("{}", cc)))
5014    } else {
5015        let xml_ref = xml_ref.decode()?;
5016        match xml_ref.as_ref() {
5017            "lt" => Ok(Cow::Borrowed("<")),
5018            "gt" => Ok(Cow::Borrowed(">")),
5019            "amp" => Ok(Cow::Borrowed("&")),
5020            "apos" => Ok(Cow::Borrowed("'")),
5021            "quot" => Ok(Cow::Borrowed("\"")),
5022            other => Ok(Cow::Owned(format!("&{};", other))),
5023        }
5024    }
5025}
5026
5027#[inline(always)]
5028fn unused_attr(func: &str, tag: &[u8], attr: &Attribute<'_>) -> Result<(), OdsError> {
5029    if cfg!(feature = "dump_unused") {
5030        let tag = from_utf8(tag)?;
5031        let key = from_utf8(attr.key.as_ref())?;
5032        let value = from_utf8(attr.value.as_ref())?;
5033        println!("unused attr: {} '{}' ({}:{})", func, tag, key, value);
5034    }
5035    Ok(())
5036}
5037
5038#[inline(always)]
5039fn unused_event(func: &str, evt: &Event<'_>) -> Result<(), OdsError> {
5040    if cfg!(feature = "dump_unused") {
5041        match &evt {
5042            Event::Text(xml_text) => {
5043                if !xml_text.decode()?.trim().is_empty() {
5044                    println!("unused text: {} ({:?})", func, evt);
5045                }
5046            }
5047            _ => {
5048                println!("unused event: {} ({:?})", func, evt);
5049            }
5050        }
5051    }
5052    Ok(())
5053}