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                    tc.content = append_text(TextContent::Text(String::new()), tc.content);
1441                }
1442                Event::Start(xml_tag) if xml_tag.name().as_ref() == b"text:p" => {
1443                    let new_txt = read_text_or_tag(ctx, xml, xml_tag, false)?;
1444                    tc.content = append_text(new_txt, tc.content);
1445                }
1446
1447                Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:annotation" => {
1448                    let annotation = read_annotation(ctx, xml, xml_tag)?;
1449                    cell.get_or_insert_with(CellData::default)
1450                        .extra_mut()
1451                        .annotation = Some(annotation);
1452                }
1453                Event::Start(xml_tag) if xml_tag.name().as_ref() == b"draw:frame" => {
1454                    let draw_frame = read_draw_frame(ctx, xml, xml_tag)?;
1455                    cell.get_or_insert_with(CellData::default)
1456                        .extra_mut()
1457                        .draw_frames
1458                        .push(draw_frame);
1459                }
1460
1461                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
1462                    break;
1463                }
1464                Event::Eof => {
1465                    break;
1466                }
1467                _ => {
1468                    unused_event("read_table_cell", &evt)?;
1469                }
1470            }
1471
1472            buf.clear();
1473        }
1474        ctx.push_buf(buf);
1475    }
1476
1477    let have_data = if let Some(mut cell) = cell {
1478        // composes a Value
1479        set_value(tc, &mut cell)?;
1480
1481        // store cell-data
1482        if ignore_cell(ctx, default_cellstyle, &cell) {
1483            false
1484        } else {
1485            cell.repeat = repeat;
1486            sheet.add_cell_data(row, col, cell);
1487            true
1488        }
1489    } else {
1490        false
1491    };
1492
1493    Ok((repeat, have_data))
1494}
1495
1496#[allow(clippy::if_same_then_else)]
1497#[inline]
1498fn ignore_cell(
1499    ctx: &mut OdsContext,
1500    default_cellstyle: Option<&CellStyleRef>,
1501    cell: &CellData,
1502) -> bool {
1503    if cell.is_void(default_cellstyle) {
1504        return true;
1505    }
1506    if ctx.ignore_empty_cells && cell.is_empty() {
1507        return true;
1508    }
1509    false
1510}
1511
1512fn append_text(new_txt: TextContent, mut content: TextContent) -> TextContent {
1513    // There can be multiple text:p elements within the cell.
1514    content = match content {
1515        TextContent::Empty => new_txt,
1516        TextContent::Text(txt) => {
1517            // Have a destructured text:p from before.
1518            // Wrap up and create list.
1519            let p = TextP::new().text(txt).into_xmltag();
1520            let mut vec = vec![p];
1521
1522            match new_txt {
1523                TextContent::Empty => {}
1524                TextContent::Text(txt) => {
1525                    let p2 = TextP::new().text(txt).into_xmltag();
1526                    vec.push(p2);
1527                }
1528                TextContent::Xml(xml) => {
1529                    vec.push(xml);
1530                }
1531                TextContent::XmlVec(_) => {
1532                    unreachable!();
1533                }
1534            }
1535            TextContent::XmlVec(vec)
1536        }
1537        TextContent::Xml(xml) => {
1538            let mut vec = vec![xml];
1539            match new_txt {
1540                TextContent::Empty => {}
1541                TextContent::Text(txt) => {
1542                    let p2 = TextP::new().text(txt).into_xmltag();
1543                    vec.push(p2);
1544                }
1545                TextContent::Xml(xml) => {
1546                    vec.push(xml);
1547                }
1548                TextContent::XmlVec(_) => {
1549                    unreachable!();
1550                }
1551            }
1552            TextContent::XmlVec(vec)
1553        }
1554        TextContent::XmlVec(mut vec) => {
1555            match new_txt {
1556                TextContent::Empty => {}
1557                TextContent::Text(txt) => {
1558                    let p2 = TextP::new().text(txt).into_xmltag();
1559                    vec.push(p2);
1560                }
1561                TextContent::Xml(xml) => {
1562                    vec.push(xml);
1563                }
1564                TextContent::XmlVec(_) => {
1565                    unreachable!();
1566                }
1567            }
1568            TextContent::XmlVec(vec)
1569        }
1570    };
1571
1572    content
1573}
1574
1575#[inline(always)]
1576fn set_value(tc: ReadTableCell, cell: &mut CellData) -> Result<(), OdsError> {
1577    match tc.val_type {
1578        ValueType::Empty => {
1579            // noop
1580        }
1581        ValueType::Boolean => {
1582            if let Some(v) = tc.val_bool {
1583                cell.value = Value::Boolean(v);
1584            } else {
1585                return Err(OdsError::Parse("no boolean value", None));
1586            }
1587        }
1588        ValueType::Number => {
1589            if let Some(v) = tc.val_float {
1590                cell.value = Value::Number(v);
1591            } else {
1592                return Err(OdsError::Parse("no float value", None));
1593            }
1594        }
1595        ValueType::Percentage => {
1596            if let Some(v) = tc.val_float {
1597                cell.value = Value::Percentage(v);
1598            } else {
1599                return Err(OdsError::Parse("no float value", None));
1600            }
1601        }
1602        ValueType::Currency => {
1603            if let Some(v) = tc.val_float {
1604                if let Some(c) = tc.val_currency {
1605                    cell.value = Value::Currency(v, c.into_boxed_str());
1606                } else {
1607                    cell.value = Value::Currency(v, "".into());
1608                }
1609            } else {
1610                return Err(OdsError::Parse("no float value", None));
1611            }
1612        }
1613        ValueType::Text => {
1614            if let Some(v) = tc.val_string {
1615                cell.value = Value::Text(v);
1616            } else {
1617                match tc.content {
1618                    TextContent::Empty => {
1619                        // noop
1620                    }
1621                    TextContent::Text(txt) => {
1622                        cell.value = Value::Text(txt);
1623                    }
1624                    TextContent::Xml(xml) => {
1625                        cell.value = Value::TextXml(vec![xml]);
1626                    }
1627                    TextContent::XmlVec(vec) => {
1628                        cell.value = Value::TextXml(vec);
1629                    }
1630                }
1631            }
1632        }
1633        ValueType::TextXml => {
1634            unreachable!();
1635        }
1636        ValueType::DateTime => {
1637            if let Some(v) = tc.val_datetime {
1638                cell.value = Value::DateTime(v);
1639            } else {
1640                return Err(OdsError::Parse("no datetime value", None));
1641            }
1642        }
1643        ValueType::TimeDuration => {
1644            if let Some(v) = tc.val_duration {
1645                cell.value = Value::TimeDuration(v);
1646            } else {
1647                return Err(OdsError::Parse("no duration value", None));
1648            }
1649        }
1650    }
1651
1652    Ok(())
1653}
1654
1655fn read_annotation(
1656    ctx: &mut OdsContext,
1657    xml: &mut OdsXmlReader<'_>,
1658    super_tag: &BytesStart<'_>,
1659) -> Result<Box<Annotation>, OdsError> {
1660    let mut annotation = Box::new(Annotation::new_empty());
1661
1662    for attr in super_tag.attributes().with_checks(false) {
1663        match attr? {
1664            attr if attr.key.as_ref() == b"office:display" => {
1665                annotation.set_display(parse_bool(&attr.value)?);
1666            }
1667            attr if attr.key.as_ref() == b"office:name" => {
1668                annotation.set_name(attr.decode_and_unescape_value(ctx.decoder)?);
1669            }
1670            attr => {
1671                let k = from_utf8(attr.key.as_ref())?;
1672                let v = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
1673                annotation.attrmap_mut().push_attr(k, v);
1674            }
1675        }
1676    }
1677
1678    let mut buf = ctx.pop_buf();
1679    loop {
1680        let evt = xml.read_event_into(&mut buf)?;
1681        let empty_tag = matches!(evt, Event::Empty(_));
1682        if cfg!(feature = "dump_xml") {
1683            println!("read_annotation {:?}", evt);
1684        }
1685        match &evt {
1686            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:annotation" => {
1687                break;
1688            }
1689
1690            Event::Start(xml_tag) | Event::Empty(xml_tag)
1691                if xml_tag.name().as_ref() == b"dc:creator" =>
1692            {
1693                annotation.set_creator(read_text(ctx, xml, xml_tag, empty_tag, parse_string)?);
1694            }
1695            Event::Start(xml_tag) | Event::Empty(xml_tag)
1696                if xml_tag.name().as_ref() == b"dc:date" =>
1697            {
1698                annotation.set_date(read_text(ctx, xml, xml_tag, empty_tag, parse_datetime)?);
1699            }
1700            Event::Start(xml_tag) | Event::Empty(xml_tag)
1701                if xml_tag.name().as_ref() == b"text:list"
1702                    || xml_tag.name().as_ref() == b"text:p" =>
1703            {
1704                annotation.push_text(read_xml(ctx, xml, xml_tag, empty_tag)?);
1705            }
1706
1707            Event::Eof => {
1708                break;
1709            }
1710            _ => {
1711                unused_event("read_annotation", &evt)?;
1712            }
1713        }
1714
1715        buf.clear();
1716    }
1717    ctx.push_buf(buf);
1718
1719    Ok(annotation)
1720}
1721
1722fn read_draw_frame(
1723    ctx: &mut OdsContext,
1724    xml: &mut OdsXmlReader<'_>,
1725    super_tag: &BytesStart<'_>,
1726) -> Result<DrawFrame, OdsError> {
1727    let mut draw_frame = DrawFrame::new();
1728
1729    copy_attr2(ctx, draw_frame.attrmap_mut(), super_tag)?;
1730
1731    let mut buf = ctx.pop_buf();
1732    loop {
1733        let evt = xml.read_event_into(&mut buf)?;
1734        let empty_tag = matches!(evt, Event::Empty(_));
1735        if cfg!(feature = "dump_xml") {
1736            println!("read_draw_frame {:?}", evt);
1737        }
1738        match &evt {
1739            Event::End(xml_tag) if xml_tag.name().as_ref() == b"draw:frame" => {
1740                break;
1741            }
1742            Event::Empty(xml_tag) | Event::Start(xml_tag)
1743                if xml_tag.name().as_ref() == b"draw:image" =>
1744            {
1745                draw_frame.push_content(DrawFrameContent::Image(read_image(
1746                    ctx, xml, xml_tag, empty_tag,
1747                )?));
1748            }
1749            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"svg:desc" => {}
1750            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"svg:desc" => {
1751                if let Some(v) = read_text(ctx, xml, xml_tag, empty_tag, parse_string)? {
1752                    draw_frame.set_desc(v);
1753                }
1754            }
1755            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"svg:title" => {}
1756            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"svg:title" => {
1757                if let Some(v) = read_text(ctx, xml, xml_tag, empty_tag, parse_string)? {
1758                    draw_frame.set_title(v);
1759                }
1760            }
1761            Event::Eof => {
1762                break;
1763            }
1764            _ => {
1765                unused_event("read_draw_frame", &evt)?;
1766            }
1767        }
1768
1769        buf.clear();
1770    }
1771    ctx.push_buf(buf);
1772
1773    Ok(draw_frame)
1774}
1775
1776fn read_image(
1777    ctx: &mut OdsContext,
1778    xml: &mut OdsXmlReader<'_>,
1779    super_tag: &BytesStart<'_>,
1780    empty_tag: bool,
1781) -> Result<DrawImage, OdsError> {
1782    let mut draw_image = DrawImage::new();
1783
1784    copy_attr2(ctx, draw_image.attrmap_mut(), super_tag)?;
1785
1786    if !empty_tag {
1787        let mut buf = ctx.pop_buf();
1788        loop {
1789            let evt = xml.read_event_into(&mut buf)?;
1790            let empty_tag = matches!(evt, Event::Empty(_));
1791            if cfg!(feature = "dump_xml") {
1792                println!("read_image {:?}", evt);
1793            }
1794            match &evt {
1795                Event::End(xml_tag) if xml_tag.name().as_ref() == b"draw:image" => {
1796                    break;
1797                }
1798
1799                Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:binary-data" => {
1800                    if let Some(v) = read_text(ctx, xml, xml_tag, empty_tag, parse_string)? {
1801                        draw_image.set_binary_base64(v);
1802                    }
1803                }
1804                Event::Start(xml_tag) | Event::Empty(xml_tag)
1805                    if xml_tag.name().as_ref() == b"text:list"
1806                        || xml_tag.name().as_ref() == b"text:p" =>
1807                {
1808                    draw_image.push_text(read_xml(ctx, xml, xml_tag, empty_tag)?);
1809                }
1810
1811                Event::Eof => {
1812                    break;
1813                }
1814                _ => {
1815                    unused_event("read_image", &evt)?;
1816                }
1817            }
1818
1819            buf.clear();
1820        }
1821        ctx.push_buf(buf);
1822    }
1823
1824    Ok(draw_image)
1825}
1826
1827fn read_scripts(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
1828    let mut buf = ctx.pop_buf();
1829    loop {
1830        let evt = xml.read_event_into(&mut buf)?;
1831        if cfg!(feature = "dump_xml") {
1832            println!("read_scripts {:?}", evt);
1833        }
1834        match &evt {
1835            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:scripts" => {
1836                break;
1837            }
1838
1839            Event::Start(xml_tag) | Event::Empty(xml_tag)
1840                if xml_tag.name().as_ref() == b"office:script" =>
1841            {
1842                let script = read_script(ctx, xml, xml_tag)?;
1843                ctx.book.add_script(script);
1844            }
1845
1846            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:event-listeners" => {}
1847            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:event-listeners" => {}
1848
1849            Event::Start(xml_tag) | Event::Empty(xml_tag)
1850                if xml_tag.name().as_ref() == b"script:event-listener" =>
1851            {
1852                let evt_listener = read_event_listener(ctx, xml_tag)?;
1853                ctx.book.add_event_listener(evt_listener);
1854            }
1855            Event::End(xml_tag) if xml_tag.name().as_ref() == b"script:event-listener" => {}
1856
1857            Event::Eof => {
1858                break;
1859            }
1860            _ => {
1861                unused_event("read_scripts", &evt)?;
1862            }
1863        }
1864
1865        buf.clear();
1866    }
1867    ctx.push_buf(buf);
1868
1869    Ok(())
1870}
1871
1872fn read_script(
1873    ctx: &mut OdsContext,
1874    xml: &mut OdsXmlReader<'_>,
1875    super_tag: &BytesStart<'_>,
1876) -> Result<Script, OdsError> {
1877    let v = read_xml(ctx, xml, super_tag, false)?;
1878    let script: Script = Script {
1879        script_lang: v
1880            .get_attr("script:language")
1881            .map(|v| v.to_string())
1882            .unwrap_or_default(),
1883        script: v.into_mixed_vec(),
1884    };
1885    Ok(script)
1886}
1887
1888// reads the page-layout tag
1889fn read_event_listener(
1890    ctx: &mut OdsContext,
1891    super_tag: &BytesStart<'_>,
1892) -> Result<EventListener, OdsError> {
1893    let mut evt = EventListener::new();
1894    for attr in super_tag.attributes().with_checks(false) {
1895        match attr? {
1896            attr if attr.key.as_ref() == b"script:event-name" => {
1897                evt.event_name = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
1898            }
1899            attr if attr.key.as_ref() == b"script:language" => {
1900                evt.script_lang = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
1901            }
1902            attr if attr.key.as_ref() == b"script:macro-name" => {
1903                evt.macro_name = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
1904            }
1905            attr if attr.key.as_ref() == b"xlink:actuate" => {
1906                evt.actuate =
1907                    parse_xlink_actuate(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
1908            }
1909            attr if attr.key.as_ref() == b"xlink:href" => {
1910                evt.href = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
1911            }
1912            attr if attr.key.as_ref() == b"xlink:type" => {
1913                evt.link_type =
1914                    parse_xlink_type(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
1915            }
1916            attr => {
1917                unused_attr("read_event_listener", super_tag.name().as_ref(), &attr)?;
1918            }
1919        }
1920    }
1921    Ok(evt)
1922}
1923
1924// reads a font-face
1925fn read_office_font_face_decls(
1926    ctx: &mut OdsContext,
1927    xml: &mut OdsXmlReader<'_>,
1928    origin: StyleOrigin,
1929) -> Result<(), OdsError> {
1930    let mut font: FontFaceDecl = FontFaceDecl::new_empty();
1931    font.set_origin(origin);
1932
1933    let mut buf = ctx.pop_buf();
1934    loop {
1935        let evt = xml.read_event_into(&mut buf)?;
1936        if cfg!(feature = "dump_xml") {
1937            println!(" read_fonts {:?}", evt);
1938        }
1939        match &evt {
1940            Event::Start(xml_tag) | Event::Empty(xml_tag)
1941                if xml_tag.name().as_ref() == b"style:font-face" =>
1942            {
1943                let name = copy_style_attr(ctx, font.attrmap_mut(), xml_tag)?;
1944                font.set_name(name);
1945                ctx.book.add_font(font);
1946
1947                font = FontFaceDecl::new_empty();
1948                font.set_origin(StyleOrigin::Content);
1949            }
1950            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:font-face-decls" => {
1951                break;
1952            }
1953            Event::Eof => {
1954                break;
1955            }
1956            _ => {
1957                unused_event("read_fonts", &evt)?;
1958            }
1959        }
1960
1961        buf.clear();
1962    }
1963    ctx.push_buf(buf);
1964
1965    Ok(())
1966}
1967
1968// reads the page-layout tag
1969fn read_page_style(
1970    ctx: &mut OdsContext,
1971    xml: &mut OdsXmlReader<'_>,
1972    super_tag: &BytesStart<'_>,
1973) -> Result<(), OdsError> {
1974    let mut pl = PageStyle::new_empty();
1975    for attr in super_tag.attributes().with_checks(false) {
1976        match attr? {
1977            attr if attr.key.as_ref() == b"style:name" => {
1978                let value = attr.decode_and_unescape_value(ctx.decoder)?;
1979                pl.set_name(value);
1980            }
1981            attr if attr.key.as_ref() == b"style:page-usage" => {
1982                let value = attr.decode_and_unescape_value(ctx.decoder)?;
1983                pl.master_page_usage = Some(value.to_string());
1984            }
1985            attr => {
1986                unused_attr("read_page_style", super_tag.name().as_ref(), &attr)?;
1987            }
1988        }
1989    }
1990
1991    let mut headerstyle = false;
1992    let mut footerstyle = false;
1993
1994    let mut buf = ctx.pop_buf();
1995    loop {
1996        let evt = xml.read_event_into(&mut buf)?;
1997        if cfg!(feature = "dump_xml") {
1998            println!(" read_page_layout {:?}", evt);
1999        }
2000        match &evt {
2001            Event::Start(xml_tag) | Event::Empty(xml_tag)
2002                if xml_tag.name().as_ref() == b"style:page-layout-properties" =>
2003            {
2004                copy_attr2(ctx, pl.style_mut(), xml_tag)?;
2005            }
2006            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:page-layout-properties" => {}
2007
2008            Event::Start(xml_tag) | Event::Empty(xml_tag)
2009                if xml_tag.name().as_ref() == b"style:header-style" =>
2010            {
2011                headerstyle = true;
2012            }
2013            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:header-style" => {
2014                headerstyle = false;
2015            }
2016
2017            Event::Start(xml_tag) | Event::Empty(xml_tag)
2018                if xml_tag.name().as_ref() == b"style:footer-style" =>
2019            {
2020                footerstyle = true;
2021            }
2022            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:footer-style" => {
2023                footerstyle = false;
2024            }
2025
2026            Event::Start(xml_tag) | Event::Empty(xml_tag)
2027                if xml_tag.name().as_ref() == b"style:header-footer-properties" =>
2028            {
2029                if headerstyle {
2030                    copy_attr2(ctx, pl.headerstyle_mut().style_mut(), xml_tag)?;
2031                }
2032                if footerstyle {
2033                    copy_attr2(ctx, pl.footerstyle_mut().style_mut(), xml_tag)?;
2034                }
2035            }
2036            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:header-footer-properties" => {
2037            }
2038
2039            Event::Start(xml_tag) | Event::Empty(xml_tag)
2040                if xml_tag.name().as_ref() == b"style:background-image" =>
2041            {
2042                // noop for now. sets the background transparent.
2043            }
2044
2045            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:page-layout" => {
2046                break;
2047            }
2048            Event::Text(_) => (),
2049            Event::Eof => break,
2050            _ => {
2051                unused_event("read_page_layout", &evt)?;
2052            }
2053        }
2054
2055        buf.clear();
2056    }
2057    ctx.push_buf(buf);
2058
2059    ctx.book.add_pagestyle(pl);
2060
2061    Ok(())
2062}
2063
2064fn read_validations(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
2065    let mut valid = Validation::new();
2066
2067    let mut buf = ctx.pop_buf();
2068    loop {
2069        let evt = xml.read_event_into(&mut buf)?;
2070        let empty_tag = matches!(evt, Event::Empty(_));
2071        if cfg!(feature = "dump_xml") {
2072            println!(" read_validations {:?}", evt);
2073        }
2074        match &evt {
2075            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:content-validation" => {
2076                read_validation(ctx, &mut valid, xml_tag)?;
2077                ctx.book.add_validation(valid);
2078                valid = Validation::new();
2079            }
2080            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"table:content-validation" => {
2081                read_validation(ctx, &mut valid, xml_tag)?;
2082            }
2083            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:content-validation" => {
2084                ctx.book.add_validation(valid);
2085                valid = Validation::new();
2086            }
2087
2088            Event::Start(xml_tag) | Event::Empty(xml_tag)
2089                if xml_tag.name().as_ref() == b"table:error-message" =>
2090            {
2091                read_validation_error(ctx, xml, &mut valid, xml_tag, empty_tag)?;
2092            }
2093
2094            Event::Start(xml_tag) | Event::Empty(xml_tag)
2095                if xml_tag.name().as_ref() == b"table:help-message" =>
2096            {
2097                read_validation_help(ctx, xml, &mut valid, xml_tag, empty_tag)?;
2098            }
2099
2100            Event::End(xml_tag) if xml_tag.name().as_ref() == b"table:content-validations" => {
2101                break;
2102            }
2103
2104            Event::Text(_) => (),
2105            Event::Eof => break,
2106            _ => {
2107                unused_event("read_validations", &evt)?;
2108            }
2109        }
2110    }
2111    ctx.push_buf(buf);
2112
2113    Ok(())
2114}
2115
2116fn read_validation_help(
2117    ctx: &mut OdsContext,
2118    xml: &mut OdsXmlReader<'_>,
2119    valid: &mut Validation,
2120    super_tag: &BytesStart<'_>,
2121    empty_tag: bool,
2122) -> Result<(), OdsError> {
2123    let mut vh = ValidationHelp::new();
2124
2125    for attr in super_tag.attributes().with_checks(false) {
2126        match attr? {
2127            attr if attr.key.as_ref() == b"table:display" => {
2128                vh.set_display(parse_bool(&attr.value)?);
2129            }
2130            attr if attr.key.as_ref() == b"table:title" => {
2131                vh.set_title(Some(
2132                    attr.decode_and_unescape_value(ctx.decoder)?.to_string(),
2133                ));
2134            }
2135            attr => {
2136                unused_attr("read_validations", super_tag.name().as_ref(), &attr)?;
2137            }
2138        }
2139    }
2140    let txt = read_text_or_tag(ctx, xml, super_tag, empty_tag)?;
2141    match txt {
2142        TextContent::Empty => {}
2143        TextContent::Xml(txt) => {
2144            vh.set_text(Some(txt));
2145        }
2146        _ => {
2147            return Err(OdsError::Ods(format!(
2148                "table:help-message invalid {:?}",
2149                txt
2150            )));
2151        }
2152    }
2153
2154    valid.set_help(Some(vh));
2155    Ok(())
2156}
2157
2158fn read_validation_error(
2159    ctx: &mut OdsContext,
2160    xml: &mut OdsXmlReader<'_>,
2161    valid: &mut Validation,
2162    super_tag: &BytesStart<'_>,
2163    empty_tag: bool,
2164) -> Result<(), OdsError> {
2165    let mut ve = ValidationError::new();
2166
2167    for attr in super_tag.attributes().with_checks(false) {
2168        match attr? {
2169            attr if attr.key.as_ref() == b"table:display" => {
2170                ve.set_display(parse_bool(&attr.value)?);
2171            }
2172            attr if attr.key.as_ref() == b"table:message-type" => {
2173                let mt = match attr.value.as_ref() {
2174                    b"stop" => MessageType::Error,
2175                    b"warning" => MessageType::Warning,
2176                    b"information" => MessageType::Info,
2177                    _ => {
2178                        return Err(OdsError::Parse(
2179                            "unknown message-type",
2180                            Some(attr.decode_and_unescape_value(ctx.decoder)?.into()),
2181                        ));
2182                    }
2183                };
2184                ve.set_msg_type(mt);
2185            }
2186            attr if attr.key.as_ref() == b"table:title" => {
2187                ve.set_title(Some(
2188                    attr.decode_and_unescape_value(ctx.decoder)?.to_string(),
2189                ));
2190            }
2191            attr => {
2192                unused_attr("read_validations", super_tag.name().as_ref(), &attr)?;
2193            }
2194        }
2195    }
2196    let txt = read_text_or_tag(ctx, xml, super_tag, empty_tag)?;
2197    match txt {
2198        TextContent::Empty => {}
2199        TextContent::Xml(txt) => {
2200            ve.set_text(Some(txt));
2201        }
2202        _ => {
2203            return Err(OdsError::Ods(format!(
2204                "table:error-message invalid {:?}",
2205                txt
2206            )));
2207        }
2208    }
2209
2210    valid.set_err(Some(ve));
2211
2212    Ok(())
2213}
2214
2215fn read_validation(
2216    ctx: &mut OdsContext,
2217    valid: &mut Validation,
2218    super_tag: &BytesStart<'_>,
2219) -> Result<(), OdsError> {
2220    for attr in super_tag.attributes().with_checks(false) {
2221        match attr? {
2222            attr if attr.key.as_ref() == b"table:name" => {
2223                valid.set_name(attr.decode_and_unescape_value(ctx.decoder)?);
2224            }
2225            attr if attr.key.as_ref() == b"table:condition" => {
2226                // split off 'of:' prefix
2227                let v = attr.decode_and_unescape_value(ctx.decoder)?;
2228                valid.set_condition(Condition::new(v.split_at(3).1));
2229            }
2230            attr if attr.key.as_ref() == b"table:allow-empty-cell" => {
2231                valid.set_allow_empty(parse_bool(&attr.value)?);
2232            }
2233            attr if attr.key.as_ref() == b"table:base-cell-address" => {
2234                let v = attr.decode_and_unescape_value(ctx.decoder)?;
2235                valid.set_base_cell(parse_cellref(&v)?);
2236            }
2237            attr if attr.key.as_ref() == b"table:display-list" => {
2238                valid.set_display(attr.value.as_ref().try_into()?);
2239            }
2240            attr => {
2241                unused_attr("read_validation", super_tag.name().as_ref(), &attr)?;
2242            }
2243        }
2244    }
2245    Ok(())
2246}
2247
2248// read the master-styles tag
2249fn read_office_master_styles(
2250    ctx: &mut OdsContext,
2251    xml: &mut OdsXmlReader<'_>,
2252    origin: StyleOrigin,
2253) -> Result<(), OdsError> {
2254    let mut buf = ctx.pop_buf();
2255    loop {
2256        let evt = xml.read_event_into(&mut buf)?;
2257        if cfg!(feature = "dump_xml") {
2258            println!(" read_master_styles {:?}", evt);
2259        }
2260        match &evt {
2261            Event::Start(xml_tag) | Event::Empty(xml_tag)
2262                if xml_tag.name().as_ref() == b"style:master-page" =>
2263            {
2264                read_master_page(ctx, xml, origin, xml_tag)?;
2265            }
2266            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:master-styles" => {
2267                break;
2268            }
2269            Event::Text(_) => (),
2270            Event::Eof => break,
2271            _ => {
2272                unused_event("read_master_styles", &evt)?;
2273            }
2274        }
2275
2276        buf.clear();
2277    }
2278    ctx.push_buf(buf);
2279
2280    Ok(())
2281}
2282
2283// read the master-page tag
2284fn read_master_page(
2285    ctx: &mut OdsContext,
2286    xml: &mut OdsXmlReader<'_>,
2287    _origin: StyleOrigin,
2288    super_tag: &BytesStart<'_>,
2289) -> Result<(), OdsError> {
2290    let mut masterpage = MasterPage::new_empty();
2291
2292    for attr in super_tag.attributes().with_checks(false) {
2293        match attr? {
2294            attr if attr.key.as_ref() == b"style:name" => {
2295                masterpage.set_name(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
2296            }
2297            attr if attr.key.as_ref() == b"style:page-layout-name" => {
2298                masterpage
2299                    .set_pagestyle(&attr.decode_and_unescape_value(ctx.decoder)?.as_ref().into());
2300            }
2301            attr if attr.key.as_ref() == b"style:display-name" => {
2302                masterpage
2303                    .set_display_name(attr.decode_and_unescape_value(ctx.decoder)?.as_ref().into());
2304            }
2305            attr if attr.key.as_ref() == b"style:next-style-name" => {
2306                let v = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
2307                masterpage.set_next_masterpage(&MasterPageRef::from(v));
2308            }
2309            attr => {
2310                unused_attr("read_master_page", super_tag.name().as_ref(), &attr)?;
2311            }
2312        }
2313    }
2314
2315    let mut buf = ctx.pop_buf();
2316    loop {
2317        let evt = xml.read_event_into(&mut buf)?;
2318        if cfg!(feature = "dump_xml") {
2319            println!(" read_master_page {:?}", evt);
2320        }
2321        match &evt {
2322            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:header" => {}
2323            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:header" => {
2324                masterpage.set_header(read_headerfooter(ctx, xml, xml_tag)?);
2325            }
2326            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:header-first" => {}
2327            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:header-first" => {
2328                masterpage.set_header_first(read_headerfooter(ctx, xml, xml_tag)?);
2329            }
2330            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:header-left" => {}
2331            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:header-left" => {
2332                masterpage.set_header_left(read_headerfooter(ctx, xml, xml_tag)?);
2333            }
2334            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:footer" => {}
2335            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:footer" => {
2336                masterpage.set_footer(read_headerfooter(ctx, xml, xml_tag)?);
2337            }
2338            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:footer-first" => {}
2339            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:footer-first" => {
2340                masterpage.set_footer_first(read_headerfooter(ctx, xml, xml_tag)?);
2341            }
2342            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"style:footer-left" => {}
2343            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"style:footer-left" => {
2344                masterpage.set_footer_left(read_headerfooter(ctx, xml, xml_tag)?);
2345            }
2346            Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:master-page" => {
2347                break;
2348            }
2349            Event::Eof => break,
2350            _ => {
2351                unused_event("read_master_page", &evt)?;
2352            }
2353        }
2354
2355        buf.clear();
2356    }
2357    ctx.push_buf(buf);
2358
2359    ctx.book.add_masterpage(masterpage);
2360
2361    Ok(())
2362}
2363
2364// reads any header or footer tags
2365fn read_headerfooter(
2366    ctx: &mut OdsContext,
2367    xml: &mut OdsXmlReader<'_>,
2368    super_tag: &BytesStart<'_>,
2369) -> Result<HeaderFooter, OdsError> {
2370    let mut hf = HeaderFooter::new();
2371    let mut content = TextContent::Empty;
2372
2373    for attr in super_tag.attributes().with_checks(false) {
2374        match attr? {
2375            attr if attr.key.as_ref() == b"style:display" => {
2376                hf.set_display(parse_bool(&attr.value)?);
2377            }
2378            attr => {
2379                unused_attr("read_headerfooter", super_tag.name().as_ref(), &attr)?;
2380            }
2381        }
2382    }
2383
2384    let mut buf = ctx.pop_buf();
2385    loop {
2386        let evt = xml.read_event_into(&mut buf)?;
2387        let empty_tag = matches!(evt, Event::Empty(_));
2388        if cfg!(feature = "dump_xml") {
2389            println!(" read_headerfooter {:?}", evt);
2390        }
2391        match &evt {
2392            Event::Start(xml_tag) | Event::Empty(xml_tag)
2393                if xml_tag.name().as_ref() == b"style:region-left" =>
2394            {
2395                let reg = read_xml(ctx, xml, xml_tag, empty_tag)?;
2396                hf.set_left(reg.into_vec()?);
2397            }
2398            Event::Start(xml_tag) | Event::Empty(xml_tag)
2399                if xml_tag.name().as_ref() == b"style:region-center" =>
2400            {
2401                let reg = read_xml(ctx, xml, xml_tag, empty_tag)?;
2402                hf.set_center(reg.into_vec()?);
2403            }
2404            Event::Start(xml_tag) | Event::Empty(xml_tag)
2405                if xml_tag.name().as_ref() == b"style:region-right" =>
2406            {
2407                let reg = read_xml(ctx, xml, xml_tag, empty_tag)?;
2408                hf.set_right(reg.into_vec()?);
2409            }
2410
2411            Event::Start(xml_tag) | Event::Empty(xml_tag)
2412                if xml_tag.name().as_ref() == b"text:p" =>
2413            {
2414                let new_txt = read_text_or_tag(ctx, xml, xml_tag, empty_tag)?;
2415                content = append_text(new_txt, content);
2416            }
2417            Event::Start(xml_tag) | Event::Empty(xml_tag)
2418                if xml_tag.name().as_ref() == b"text:h" =>
2419            {
2420                let new_txt = read_text_or_tag(ctx, xml, xml_tag, empty_tag)?;
2421                content = append_text(new_txt, content);
2422            }
2423            // no other tags supported for now. they have never been seen in the wild.
2424            Event::Text(_) => (),
2425            Event::End(xml_tag) => {
2426                if xml_tag.name() == super_tag.name() {
2427                    hf.set_content(match content {
2428                        TextContent::Empty => Vec::new(),
2429                        TextContent::Text(v) => vec![TextP::new().text(v).into()],
2430                        TextContent::Xml(v) => vec![v],
2431                        TextContent::XmlVec(v) => v,
2432                    });
2433                    break;
2434                }
2435            }
2436            Event::Eof => break,
2437            _ => {
2438                unused_event("read_headerfooter", &evt)?;
2439            }
2440        }
2441
2442        buf.clear();
2443    }
2444    ctx.push_buf(buf);
2445
2446    Ok(hf)
2447}
2448
2449// reads the office-styles tag
2450fn read_office_styles(
2451    ctx: &mut OdsContext,
2452    xml: &mut OdsXmlReader<'_>,
2453    origin: StyleOrigin,
2454) -> Result<(), OdsError> {
2455    let mut buf = ctx.pop_buf();
2456    loop {
2457        let evt = xml.read_event_into(&mut buf)?;
2458        let empty_tag = matches!(evt, Event::Empty(_));
2459        if cfg!(feature = "dump_xml") {
2460            println!(" read_styles_tag {:?}", evt);
2461        }
2462        match &evt {
2463            Event::Start(xml_tag) | Event::Empty(xml_tag)
2464                if xml_tag.name().as_ref() == b"style:style" =>
2465            {
2466                read_style_style(ctx, xml, origin, StyleUse::Named, xml_tag, empty_tag)?;
2467            }
2468            Event::Start(xml_tag) | Event::Empty(xml_tag)
2469                if xml_tag.name().as_ref() == b"style:default-style" =>
2470            {
2471                read_style_style(ctx, xml, origin, StyleUse::Default, xml_tag, empty_tag)?;
2472            }
2473            Event::Start(xml_tag) | Event::Empty(xml_tag)
2474                if xml_tag.name().as_ref() == b"number:boolean-style"
2475                    || xml_tag.name().as_ref() == b"number:date-style"
2476                    || xml_tag.name().as_ref() == b"number:time-style"
2477                    || xml_tag.name().as_ref() == b"number:number-style"
2478                    || xml_tag.name().as_ref() == b"number:currency-style"
2479                    || xml_tag.name().as_ref() == b"number:percentage-style"
2480                    || xml_tag.name().as_ref() == b"number:text-style" =>
2481            {
2482                read_value_format(ctx, xml, origin, StyleUse::Named, xml_tag)?;
2483            }
2484            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:styles" => {
2485                break;
2486            }
2487            Event::Text(_) => (),
2488            Event::Eof => break,
2489            _ => {
2490                unused_event("read_styles_tag", &evt)?;
2491            }
2492        }
2493
2494        buf.clear();
2495    }
2496    ctx.push_buf(buf);
2497
2498    Ok(())
2499}
2500
2501// read the automatic-styles tag
2502fn read_office_automatic_styles(
2503    ctx: &mut OdsContext,
2504    xml: &mut OdsXmlReader<'_>,
2505    origin: StyleOrigin,
2506) -> Result<(), OdsError> {
2507    let mut buf = ctx.pop_buf();
2508    loop {
2509        let evt = xml.read_event_into(&mut buf)?;
2510        let empty_tag = matches!(evt, Event::Empty(_));
2511        if cfg!(feature = "dump_xml") {
2512            println!(" read_auto_styles {:?}", evt);
2513        }
2514        match &evt {
2515            Event::Start(xml_tag) | Event::Empty(xml_tag)
2516                if xml_tag.name().as_ref() == b"style:style" =>
2517            {
2518                read_style_style(ctx, xml, origin, StyleUse::Automatic, xml_tag, empty_tag)?;
2519            }
2520            Event::Start(xml_tag) | Event::Empty(xml_tag)
2521                if xml_tag.name().as_ref() == b"number:boolean-style"
2522                    || xml_tag.name().as_ref() == b"number:date-style"
2523                    || xml_tag.name().as_ref() == b"number:time-style"
2524                    || xml_tag.name().as_ref() == b"number:number-style"
2525                    || xml_tag.name().as_ref() == b"number:currency-style"
2526                    || xml_tag.name().as_ref() == b"number:percentage-style"
2527                    || xml_tag.name().as_ref() == b"number:text-style" =>
2528            {
2529                read_value_format(ctx, xml, origin, StyleUse::Automatic, xml_tag)?;
2530            }
2531
2532            Event::Start(xml_tag) | Event::Empty(xml_tag)
2533                if xml_tag.name().as_ref() == b"style:page-layout" =>
2534            {
2535                read_page_style(ctx, xml, xml_tag)?;
2536            }
2537
2538            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:automatic-styles" => {
2539                break;
2540            }
2541            Event::Text(_) => (),
2542            Event::Eof => break,
2543            _ => {
2544                unused_event("read_auto_styles", &evt)?;
2545            }
2546        }
2547
2548        buf.clear();
2549    }
2550    ctx.push_buf(buf);
2551
2552    Ok(())
2553}
2554
2555// Reads any of the number:xxx tags
2556fn read_value_format(
2557    ctx: &mut OdsContext,
2558    xml: &mut OdsXmlReader<'_>,
2559    origin: StyleOrigin,
2560    styleuse: StyleUse,
2561    super_tag: &BytesStart<'_>,
2562) -> Result<(), OdsError> {
2563    match super_tag.name().as_ref() {
2564        b"number:boolean-style" => {
2565            let mut valuestyle = ValueFormatBoolean::new_empty();
2566            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2567            ctx.book.add_boolean_format(valuestyle);
2568        }
2569        b"number:date-style" => {
2570            let mut valuestyle = ValueFormatDateTime::new_empty();
2571            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2572            ctx.book.add_datetime_format(valuestyle);
2573        }
2574        b"number:time-style" => {
2575            let mut valuestyle = ValueFormatTimeDuration::new_empty();
2576            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2577            ctx.book.add_timeduration_format(valuestyle);
2578        }
2579        b"number:number-style" => {
2580            let mut valuestyle = ValueFormatNumber::new_empty();
2581            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2582            ctx.book.add_number_format(valuestyle);
2583        }
2584        b"number:currency-style" => {
2585            let mut valuestyle = ValueFormatCurrency::new_empty();
2586            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2587            ctx.book.add_currency_format(valuestyle);
2588        }
2589        b"number:percentage-style" => {
2590            let mut valuestyle = ValueFormatPercentage::new_empty();
2591            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2592            ctx.book.add_percentage_format(valuestyle);
2593        }
2594        b"number:text-style" => {
2595            let mut valuestyle = ValueFormatText::new_empty();
2596            read_value_format_parts(ctx, xml, origin, styleuse, &mut valuestyle, super_tag)?;
2597            ctx.book.add_text_format(valuestyle);
2598        }
2599        _ => {
2600            if cfg!(feature = "dump_unused") {
2601                println!(
2602                    " read_value_format unused {}",
2603                    from_utf8(super_tag.name().as_ref())?
2604                );
2605            }
2606        }
2607    }
2608
2609    Ok(())
2610}
2611
2612// Reads any of the number:xxx tags
2613fn read_value_format_parts<T: ValueFormatTrait>(
2614    ctx: &mut OdsContext,
2615    xml: &mut OdsXmlReader<'_>,
2616    origin: StyleOrigin,
2617    styleuse: StyleUse,
2618    valuestyle: &mut T,
2619    super_tag: &BytesStart<'_>,
2620) -> Result<(), OdsError> {
2621    //
2622    valuestyle.set_origin(origin);
2623    valuestyle.set_styleuse(styleuse);
2624    let name = copy_style_attr(ctx, valuestyle.attrmap_mut(), super_tag)?;
2625    valuestyle.set_name(name.as_str());
2626
2627    let mut buf = ctx.pop_buf();
2628    loop {
2629        let evt = xml.read_event_into(&mut buf)?;
2630        let empty_tag = matches!(evt, Event::Empty(_));
2631        if cfg!(feature = "dump_xml") {
2632            println!(" read_value_format_parts {:?}", evt);
2633        }
2634        match &evt {
2635            Event::Start(xml_tag) | Event::Empty(xml_tag)
2636                if xml_tag.name().as_ref() == b"number:boolean" =>
2637            {
2638                valuestyle.push_part(read_part(
2639                    ctx,
2640                    xml,
2641                    xml_tag,
2642                    empty_tag,
2643                    FormatPartType::Boolean,
2644                )?);
2645            }
2646            Event::Start(xml_tag) | Event::Empty(xml_tag)
2647                if xml_tag.name().as_ref() == b"number:number" =>
2648            {
2649                valuestyle.push_part(read_part_number(
2650                    ctx,
2651                    xml,
2652                    xml_tag,
2653                    empty_tag,
2654                    FormatPartType::Number,
2655                )?);
2656            }
2657            Event::Start(xml_tag) | Event::Empty(xml_tag)
2658                if xml_tag.name().as_ref() == b"number:fraction" =>
2659            {
2660                valuestyle.push_part(read_part(
2661                    ctx,
2662                    xml,
2663                    xml_tag,
2664                    empty_tag,
2665                    FormatPartType::Fraction,
2666                )?);
2667            }
2668            Event::Start(xml_tag) | Event::Empty(xml_tag)
2669                if xml_tag.name().as_ref() == b"number:scientific-number" =>
2670            {
2671                valuestyle.push_part(read_part(
2672                    ctx,
2673                    xml,
2674                    xml_tag,
2675                    empty_tag,
2676                    FormatPartType::ScientificNumber,
2677                )?);
2678            }
2679            Event::Start(xml_tag) | Event::Empty(xml_tag)
2680                if xml_tag.name().as_ref() == b"number:text"
2681                    || xml_tag.name().as_ref() == b"loext:text" =>
2682            {
2683                valuestyle.push_part(read_part_text(
2684                    ctx,
2685                    xml,
2686                    xml_tag,
2687                    empty_tag,
2688                    FormatPartType::Text,
2689                )?);
2690            }
2691
2692            Event::Start(xml_tag) | Event::Empty(xml_tag)
2693                if xml_tag.name().as_ref() == b"number:am-pm" =>
2694            {
2695                valuestyle.push_part(read_part(
2696                    ctx,
2697                    xml,
2698                    xml_tag,
2699                    empty_tag,
2700                    FormatPartType::AmPm,
2701                )?);
2702            }
2703            Event::Start(xml_tag) | Event::Empty(xml_tag)
2704                if xml_tag.name().as_ref() == b"number:day" =>
2705            {
2706                valuestyle.push_part(read_part(
2707                    ctx,
2708                    xml,
2709                    xml_tag,
2710                    empty_tag,
2711                    FormatPartType::Day,
2712                )?);
2713            }
2714            Event::Start(xml_tag) | Event::Empty(xml_tag)
2715                if xml_tag.name().as_ref() == b"number:day-of-week" =>
2716            {
2717                valuestyle.push_part(read_part(
2718                    ctx,
2719                    xml,
2720                    xml_tag,
2721                    empty_tag,
2722                    FormatPartType::DayOfWeek,
2723                )?);
2724            }
2725            Event::Start(xml_tag) | Event::Empty(xml_tag)
2726                if xml_tag.name().as_ref() == b"number:era" =>
2727            {
2728                valuestyle.push_part(read_part(
2729                    ctx,
2730                    xml,
2731                    xml_tag,
2732                    empty_tag,
2733                    FormatPartType::Era,
2734                )?);
2735            }
2736            Event::Start(xml_tag) | Event::Empty(xml_tag)
2737                if xml_tag.name().as_ref() == b"number:hours" =>
2738            {
2739                valuestyle.push_part(read_part(
2740                    ctx,
2741                    xml,
2742                    xml_tag,
2743                    empty_tag,
2744                    FormatPartType::Hours,
2745                )?);
2746            }
2747            Event::Start(xml_tag) | Event::Empty(xml_tag)
2748                if xml_tag.name().as_ref() == b"number:minutes" =>
2749            {
2750                valuestyle.push_part(read_part(
2751                    ctx,
2752                    xml,
2753                    xml_tag,
2754                    empty_tag,
2755                    FormatPartType::Minutes,
2756                )?);
2757            }
2758            Event::Start(xml_tag) | Event::Empty(xml_tag)
2759                if xml_tag.name().as_ref() == b"number:month" =>
2760            {
2761                valuestyle.push_part(read_part(
2762                    ctx,
2763                    xml,
2764                    xml_tag,
2765                    empty_tag,
2766                    FormatPartType::Month,
2767                )?);
2768            }
2769            Event::Start(xml_tag) | Event::Empty(xml_tag)
2770                if xml_tag.name().as_ref() == b"number:quarter" =>
2771            {
2772                valuestyle.push_part(read_part(
2773                    ctx,
2774                    xml,
2775                    xml_tag,
2776                    empty_tag,
2777                    FormatPartType::Quarter,
2778                )?);
2779            }
2780            Event::Start(xml_tag) | Event::Empty(xml_tag)
2781                if xml_tag.name().as_ref() == b"number:seconds" =>
2782            {
2783                valuestyle.push_part(read_part(
2784                    ctx,
2785                    xml,
2786                    xml_tag,
2787                    empty_tag,
2788                    FormatPartType::Seconds,
2789                )?);
2790            }
2791            Event::Start(xml_tag) | Event::Empty(xml_tag)
2792                if xml_tag.name().as_ref() == b"number:week-of-year" =>
2793            {
2794                valuestyle.push_part(read_part(
2795                    ctx,
2796                    xml,
2797                    xml_tag,
2798                    empty_tag,
2799                    FormatPartType::WeekOfYear,
2800                )?);
2801            }
2802            Event::Start(xml_tag) | Event::Empty(xml_tag)
2803                if xml_tag.name().as_ref() == b"number:year" =>
2804            {
2805                valuestyle.push_part(read_part(
2806                    ctx,
2807                    xml,
2808                    xml_tag,
2809                    empty_tag,
2810                    FormatPartType::Year,
2811                )?);
2812            }
2813
2814            Event::Start(xml_tag) | Event::Empty(xml_tag)
2815                if xml_tag.name().as_ref() == b"number:currency-symbol" =>
2816            {
2817                valuestyle.push_part(read_part_text(
2818                    ctx,
2819                    xml,
2820                    xml_tag,
2821                    empty_tag,
2822                    FormatPartType::CurrencySymbol,
2823                )?);
2824            }
2825            Event::Start(xml_tag) | Event::Empty(xml_tag)
2826                if xml_tag.name().as_ref() == b"number:fill-character"
2827                    || xml_tag.name().as_ref() == b"loext:fill-character" =>
2828            {
2829                valuestyle.push_part(read_part_text(
2830                    ctx,
2831                    xml,
2832                    xml_tag,
2833                    empty_tag,
2834                    FormatPartType::FillCharacter,
2835                )?);
2836            }
2837            Event::Start(xml_tag) | Event::Empty(xml_tag)
2838                if xml_tag.name().as_ref() == b"number:text-content" =>
2839            {
2840                valuestyle.push_part(read_part(
2841                    ctx,
2842                    xml,
2843                    xml_tag,
2844                    empty_tag,
2845                    FormatPartType::TextContent,
2846                )?);
2847            }
2848
2849            Event::Start(xml_tag) | Event::Empty(xml_tag)
2850                if xml_tag.name().as_ref() == b"style:map" =>
2851            {
2852                valuestyle.push_stylemap(read_value_stylemap(ctx, xml_tag)?);
2853            }
2854            Event::Start(xml_tag) | Event::Empty(xml_tag)
2855                if xml_tag.name().as_ref() == b"style:text-properties" =>
2856            {
2857                copy_attr2(ctx, valuestyle.textstyle_mut(), xml_tag)?;
2858            }
2859            Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
2860                break;
2861            }
2862            Event::Eof => break,
2863            _ => {
2864                unused_event("read_value_format_parts", &evt)?;
2865            }
2866        }
2867
2868        buf.clear();
2869    }
2870    ctx.push_buf(buf);
2871
2872    Ok(())
2873}
2874
2875fn read_part(
2876    ctx: &mut OdsContext,
2877    xml: &mut OdsXmlReader<'_>,
2878    super_tag: &BytesStart<'_>,
2879    empty_tag: bool,
2880    part_type: FormatPartType,
2881) -> Result<FormatPart, OdsError> {
2882    let mut part = FormatPart::new(part_type);
2883    copy_attr2(ctx, part.attrmap_mut(), super_tag)?;
2884
2885    if !empty_tag {
2886        let mut buf = ctx.pop_buf();
2887        loop {
2888            let evt = xml.read_event_into(&mut buf)?;
2889            if cfg!(feature = "dump_xml") {
2890                println!(" read_part {:?}", evt);
2891            }
2892            match &evt {
2893                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
2894                    break;
2895                }
2896                Event::Eof => {
2897                    break;
2898                }
2899                _ => {
2900                    unused_event("read_part", &evt)?;
2901                }
2902            }
2903        }
2904        ctx.push_buf(buf);
2905    }
2906
2907    Ok(part)
2908}
2909
2910// value format part with text content
2911fn read_part_text(
2912    ctx: &mut OdsContext,
2913    xml: &mut OdsXmlReader<'_>,
2914    super_tag: &BytesStart<'_>,
2915    empty_tag: bool,
2916    part_type: FormatPartType,
2917) -> Result<FormatPart, OdsError> {
2918    let mut part = FormatPart::new(part_type);
2919    copy_attr2(ctx, part.attrmap_mut(), super_tag)?;
2920
2921    if !empty_tag {
2922        let mut buf = ctx.pop_buf();
2923        loop {
2924            let evt = xml.read_event_into(&mut buf)?;
2925            if cfg!(feature = "dump_xml") {
2926                println!(" read_part_text {:?}", evt);
2927            }
2928            match &evt {
2929                Event::GeneralRef(xml_ref) => {
2930                    part.append_content(entity(xml_ref)?);
2931                }
2932                Event::Text(xml_text) => {
2933                    part.append_content(xml_text.decode()?);
2934                }
2935                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
2936                    break;
2937                }
2938                Event::Eof => {
2939                    break;
2940                }
2941                _ => {
2942                    unused_event("read_part_text", &evt)?;
2943                }
2944            }
2945        }
2946        ctx.push_buf(buf);
2947    }
2948
2949    Ok(part)
2950}
2951
2952fn read_part_number(
2953    ctx: &mut OdsContext,
2954    xml: &mut OdsXmlReader<'_>,
2955    super_tag: &BytesStart<'_>,
2956    empty_tag: bool,
2957    part_type: FormatPartType,
2958) -> Result<FormatPart, OdsError> {
2959    let mut part = FormatPart::new(part_type);
2960    copy_attr2(ctx, part.attrmap_mut(), super_tag)?;
2961
2962    if !empty_tag {
2963        let mut buf = ctx.pop_buf();
2964        loop {
2965            let evt = xml.read_event_into(&mut buf)?;
2966            if cfg!(feature = "dump_xml") {
2967                println!(" read_part_embedded_text {:?}", evt);
2968            }
2969            match &evt {
2970                Event::Start(xml_tag) | Event::Empty(xml_tag)
2971                    if xml_tag.name().as_ref() == b"number:embedded-text" =>
2972                {
2973                    for attr in xml_tag.attributes().with_checks(false) {
2974                        let attr = attr?;
2975                        match attr.key.as_ref() {
2976                            b"number:position" => {
2977                                part.set_position(parse_i32(&attr.value)?);
2978                            }
2979                            _ => {
2980                                unused_attr(
2981                                    "read_part_embedded_text",
2982                                    xml_tag.name().as_ref(),
2983                                    &attr,
2984                                )?;
2985                            }
2986                        }
2987                    }
2988                }
2989                Event::End(xml_tag) if xml_tag.name().as_ref() == b"number:embedded-text" => {}
2990                Event::Text(xml_text) => {
2991                    part.set_content(parse_string(xml_text.as_ref())?);
2992                }
2993                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
2994                    break;
2995                }
2996                Event::Eof => {
2997                    break;
2998                }
2999                _ => {
3000                    unused_event("read_part_embedded_text", &evt)?;
3001                }
3002            }
3003        }
3004        ctx.push_buf(buf);
3005    }
3006
3007    Ok(part)
3008}
3009
3010// style:style tag
3011#[allow(clippy::too_many_arguments)]
3012fn read_style_style(
3013    ctx: &mut OdsContext,
3014    xml: &mut OdsXmlReader<'_>,
3015    origin: StyleOrigin,
3016    style_use: StyleUse,
3017    super_tag: &BytesStart<'_>,
3018    empty_tag: bool,
3019) -> Result<(), OdsError> {
3020    for attr in super_tag.attributes().with_checks(false) {
3021        match attr? {
3022            attr if attr.key.as_ref() == b"style:family" => {
3023                match attr.value.as_ref() {
3024                    b"table" => read_tablestyle(ctx, xml, origin, style_use, super_tag, empty_tag)?,
3025                    b"table-column" => {
3026                        read_colstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?
3027                    }
3028                    b"table-row" => {
3029                        read_rowstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?
3030                    }
3031                    b"table-cell" => {
3032                        read_cellstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?
3033                    }
3034                    b"graphic" => {
3035                        read_graphicstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?
3036                    }
3037                    b"paragraph" => {
3038                        read_paragraphstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?
3039                    }
3040                    b"text" => read_textstyle(ctx, xml, origin, style_use, super_tag, empty_tag)?,
3041                    b"ruby" => read_rubystyle(ctx, xml, origin, style_use, super_tag, empty_tag)?,
3042                    value => {
3043                        return Err(OdsError::Ods(format!(
3044                            "style:family unknown {} ",
3045                            from_utf8(value)?
3046                        )));
3047                    }
3048                };
3049            }
3050            _ => {
3051                // not read here
3052            }
3053        }
3054    }
3055
3056    Ok(())
3057}
3058
3059// style:style tag
3060#[allow(clippy::collapsible_else_if)]
3061#[allow(clippy::too_many_arguments)]
3062fn read_tablestyle(
3063    ctx: &mut OdsContext,
3064    xml: &mut OdsXmlReader<'_>,
3065    origin: StyleOrigin,
3066    style_use: StyleUse,
3067    super_tag: &BytesStart<'_>,
3068    empty_tag: bool,
3069) -> Result<(), OdsError> {
3070    let mut style = TableStyle::new_empty();
3071    style.set_origin(origin);
3072    style.set_styleuse(style_use);
3073    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3074    style.set_name(name);
3075
3076    // In case of an empty xml-tag we are done here.
3077    if empty_tag {
3078        ctx.book.add_tablestyle(style);
3079    } else {
3080        let mut buf = ctx.pop_buf();
3081        loop {
3082            let evt = xml.read_event_into(&mut buf)?;
3083            if cfg!(feature = "dump_xml") {
3084                println!(" read_table_style {:?}", evt);
3085            }
3086            match &evt {
3087                Event::Start(xml_tag) | Event::Empty(xml_tag) => match xml_tag.name().as_ref() {
3088                    b"style:table-properties" => copy_attr2(ctx, style.tablestyle_mut(), xml_tag)?,
3089                    _ => {
3090                        unused_event("read_table_style", &evt)?;
3091                    }
3092                },
3093                Event::Text(_) => (),
3094                Event::End(xml_tag) => {
3095                    if xml_tag.name().as_ref() == super_tag.name().as_ref() {
3096                        ctx.book.add_tablestyle(style);
3097                        break;
3098                    } else {
3099                        unused_event("read_table_style", &evt)?;
3100                    }
3101                }
3102                Event::Eof => break,
3103                _ => {
3104                    unused_event("read_table_style", &evt)?;
3105                }
3106            }
3107        }
3108
3109        ctx.push_buf(buf);
3110    }
3111
3112    Ok(())
3113}
3114
3115// style:style tag
3116#[allow(clippy::collapsible_else_if)]
3117#[allow(clippy::too_many_arguments)]
3118fn read_rowstyle(
3119    ctx: &mut OdsContext,
3120    xml: &mut OdsXmlReader<'_>,
3121    origin: StyleOrigin,
3122    style_use: StyleUse,
3123    super_tag: &BytesStart<'_>,
3124    empty_tag: bool,
3125) -> Result<(), OdsError> {
3126    let mut style = RowStyle::new_empty();
3127    style.set_origin(origin);
3128    style.set_styleuse(style_use);
3129    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3130    style.set_name(name);
3131
3132    // In case of an empty xml-tag we are done here.
3133    if empty_tag {
3134        ctx.book.add_rowstyle(style);
3135    } else {
3136        let mut buf = ctx.pop_buf();
3137        loop {
3138            let evt = xml.read_event_into(&mut buf)?;
3139            if cfg!(feature = "dump_xml") {
3140                println!(" read_rowstyle {:?}", evt);
3141            }
3142            match &evt {
3143                Event::Start(xml_tag) | Event::Empty(xml_tag) => match xml_tag.name().as_ref() {
3144                    b"style:table-row-properties" => {
3145                        copy_attr2(ctx, style.rowstyle_mut(), xml_tag)?
3146                    }
3147                    _ => {
3148                        unused_event("read_rowstyle", &evt)?;
3149                    }
3150                },
3151                Event::Text(_) => (),
3152                Event::End(xml_tag) => {
3153                    if xml_tag.name() == super_tag.name() {
3154                        ctx.book.add_rowstyle(style);
3155                        break;
3156                    } else {
3157                        unused_event("read_rowstyle", &evt)?;
3158                    }
3159                }
3160                Event::Eof => break,
3161                _ => {
3162                    unused_event("read_rowstyle", &evt)?;
3163                }
3164            }
3165        }
3166        ctx.push_buf(buf);
3167    }
3168
3169    Ok(())
3170}
3171
3172// style:style tag
3173#[allow(clippy::collapsible_else_if)]
3174#[allow(clippy::too_many_arguments)]
3175fn read_colstyle(
3176    ctx: &mut OdsContext,
3177    xml: &mut OdsXmlReader<'_>,
3178    origin: StyleOrigin,
3179    style_use: StyleUse,
3180    super_tag: &BytesStart<'_>,
3181    empty_tag: bool,
3182) -> Result<(), OdsError> {
3183    let mut style = ColStyle::new_empty();
3184    style.set_origin(origin);
3185    style.set_styleuse(style_use);
3186    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3187    style.set_name(name);
3188
3189    // In case of an empty xml-tag we are done here.
3190    if empty_tag {
3191        ctx.book.add_colstyle(style);
3192    } else {
3193        let mut buf = ctx.pop_buf();
3194        loop {
3195            let evt = xml.read_event_into(&mut buf)?;
3196            if cfg!(feature = "dump_xml") {
3197                println!(" read_colstyle {:?}", evt);
3198            }
3199            match &evt {
3200                Event::Start(xml_tag) | Event::Empty(xml_tag) => match xml_tag.name().as_ref() {
3201                    b"style:table-column-properties" => {
3202                        copy_attr2(ctx, style.colstyle_mut(), xml_tag)?
3203                    }
3204                    _ => {
3205                        unused_event("read_colstyle", &evt)?;
3206                    }
3207                },
3208                Event::Text(_) => (),
3209                Event::End(xml_tag) => {
3210                    if xml_tag.name() == super_tag.name() {
3211                        ctx.book.add_colstyle(style);
3212                        break;
3213                    } else {
3214                        unused_event("read_colstyle", &evt)?;
3215                    }
3216                }
3217                Event::Eof => break,
3218                _ => {
3219                    unused_event("read_colstyle", &evt)?;
3220                }
3221            }
3222        }
3223
3224        ctx.push_buf(buf);
3225    }
3226    Ok(())
3227}
3228
3229// style:style tag
3230#[allow(clippy::collapsible_else_if)]
3231#[allow(clippy::too_many_arguments)]
3232fn read_cellstyle(
3233    ctx: &mut OdsContext,
3234    xml: &mut OdsXmlReader<'_>,
3235    origin: StyleOrigin,
3236    style_use: StyleUse,
3237    super_tag: &BytesStart<'_>,
3238    empty_tag: bool,
3239) -> Result<(), OdsError> {
3240    let mut style = CellStyle::new_empty();
3241    style.set_origin(origin);
3242    style.set_styleuse(style_use);
3243    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3244    style.set_name(name);
3245
3246    // In case of an empty xml-tag we are done here.
3247    if empty_tag {
3248        ctx.book.add_cellstyle(style);
3249    } else {
3250        let mut buf = ctx.pop_buf();
3251        loop {
3252            let evt = xml.read_event_into(&mut buf)?;
3253            if cfg!(feature = "dump_xml") {
3254                println!(" read_cellstyle {:?}", evt);
3255            }
3256            match &evt {
3257                Event::Start(xml_tag) | Event::Empty(xml_tag)
3258                    if xml_tag.name().as_ref() == b"style:table-cell-properties" =>
3259                {
3260                    copy_attr2(ctx, style.cellstyle_mut(), xml_tag)?;
3261                }
3262                Event::Start(xml_tag) | Event::Empty(xml_tag)
3263                    if xml_tag.name().as_ref() == b"style:text-properties" =>
3264                {
3265                    copy_attr2(ctx, style.textstyle_mut(), xml_tag)?;
3266                }
3267                Event::Start(xml_tag) | Event::Empty(xml_tag)
3268                    if xml_tag.name().as_ref() == b"style:paragraph-properties" =>
3269                {
3270                    copy_attr2(ctx, style.paragraphstyle_mut(), xml_tag)?;
3271                }
3272                Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:paragraph-properties" => {
3273                }
3274                // Event::Start(xml_tag) | Event::Empty(xml_tag)
3275                //     if xml_tag.name().as_ref() == b"style:graphic-properties" =>
3276                // {
3277                //     copy_attr(style.graphic_mut(), xml, xml_tag)?;
3278                // }
3279                Event::Start(xml_tag) | Event::Empty(xml_tag)
3280                    if xml_tag.name().as_ref() == b"style:map" =>
3281                {
3282                    style.push_stylemap(read_stylemap(ctx, xml_tag)?);
3283                }
3284                // todo: tab-stops
3285                // b"style:tab-stops" => (),
3286                // b"style:tab-stop" => {
3287                //     let mut ts = TabStop::new();
3288                //     copy_attr(&mut ts, xml, xml_tag)?;
3289                //     style.paragraph_mut().add_tabstop(ts);
3290                // }
3291                Event::Text(_) => (),
3292                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
3293                    ctx.book.add_cellstyle(style);
3294                    break;
3295                }
3296                Event::Eof => break,
3297                _ => {
3298                    unused_event("read_cellstyle", &evt)?;
3299                }
3300            }
3301        }
3302        ctx.push_buf(buf);
3303    }
3304
3305    Ok(())
3306}
3307
3308// style:style tag
3309#[allow(clippy::collapsible_else_if)]
3310#[allow(clippy::too_many_arguments)]
3311fn read_paragraphstyle(
3312    ctx: &mut OdsContext,
3313    xml: &mut OdsXmlReader<'_>,
3314    origin: StyleOrigin,
3315    style_use: StyleUse,
3316    super_tag: &BytesStart<'_>,
3317    empty_tag: bool,
3318) -> Result<(), OdsError> {
3319    let mut style = ParagraphStyle::new_empty();
3320    style.set_origin(origin);
3321    style.set_styleuse(style_use);
3322    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3323    style.set_name(name);
3324
3325    // In case of an empty xml-tag we are done here.
3326    if empty_tag {
3327        ctx.book.add_paragraphstyle(style);
3328    } else {
3329        let mut buf = ctx.pop_buf();
3330        loop {
3331            let evt = xml.read_event_into(&mut buf)?;
3332            if cfg!(feature = "dump_xml") {
3333                println!(" read_paragraphstyle {:?}", evt);
3334            }
3335            match &evt {
3336                Event::Start(xml_tag) | Event::Empty(xml_tag)
3337                    if xml_tag.name().as_ref() == b"style:text-properties" =>
3338                {
3339                    copy_attr2(ctx, style.textstyle_mut(), xml_tag)?;
3340                }
3341                Event::Start(xml_tag) | Event::Empty(xml_tag)
3342                    if xml_tag.name().as_ref() == b"style:paragraph-properties" =>
3343                {
3344                    copy_attr2(ctx, style.paragraphstyle_mut(), xml_tag)?;
3345                }
3346                Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:paragraph-properties" => {
3347                }
3348                // b"style:graphic-properties" => copy_attr(style.graphic_mut(), xml, xml_tag)?,
3349                // b"style:map" => style.push_stylemap(read_stylemap(xml, xml_tag)?),
3350                Event::Start(xml_tag) | Event::Empty(xml_tag)
3351                    if xml_tag.name().as_ref() == b"style:tab-stops" => {}
3352                Event::End(xml_tag) if xml_tag.name().as_ref() == b"style:tab-stops" => {}
3353                Event::Start(xml_tag) | Event::Empty(xml_tag)
3354                    if xml_tag.name().as_ref() == b"style:tab-stop" =>
3355                {
3356                    let mut ts = TabStop::new();
3357                    copy_attr2(ctx, ts.attrmap_mut(), xml_tag)?;
3358                    style.add_tabstop(ts);
3359                }
3360
3361                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
3362                    ctx.book.add_paragraphstyle(style);
3363                    break;
3364                }
3365
3366                Event::Text(_) => (),
3367                Event::Eof => break,
3368                _ => {
3369                    unused_event("read_paragraphstyle", &evt)?;
3370                }
3371            }
3372        }
3373        ctx.push_buf(buf);
3374    }
3375
3376    Ok(())
3377}
3378
3379// style:style tag
3380#[allow(clippy::collapsible_else_if)]
3381#[allow(clippy::too_many_arguments)]
3382fn read_textstyle(
3383    ctx: &mut OdsContext,
3384    xml: &mut OdsXmlReader<'_>,
3385    origin: StyleOrigin,
3386    style_use: StyleUse,
3387    super_tag: &BytesStart<'_>,
3388    empty_tag: bool,
3389) -> Result<(), OdsError> {
3390    let mut style = TextStyle::new_empty();
3391    style.set_origin(origin);
3392    style.set_styleuse(style_use);
3393    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3394    style.set_name(name);
3395
3396    // In case of an empty xml-tag we are done here.
3397    if empty_tag {
3398        ctx.book.add_textstyle(style);
3399    } else {
3400        let mut buf = ctx.pop_buf();
3401        loop {
3402            let evt = xml.read_event_into(&mut buf)?;
3403            if cfg!(feature = "dump_xml") {
3404                println!(" read_textstyle {:?}", evt);
3405            }
3406            match &evt {
3407                Event::Start(xml_tag) | Event::Empty(xml_tag)
3408                    if xml_tag.name().as_ref() == b"style:text-properties" =>
3409                {
3410                    copy_attr2(ctx, style.textstyle_mut(), xml_tag)?;
3411                }
3412                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
3413                    ctx.book.add_textstyle(style);
3414                    break;
3415                }
3416                Event::Text(_) => (),
3417                Event::Eof => break,
3418                _ => {
3419                    unused_event("read_textstyle", &evt)?;
3420                }
3421            }
3422        }
3423        ctx.push_buf(buf);
3424    }
3425
3426    Ok(())
3427}
3428
3429// style:style tag
3430#[allow(clippy::collapsible_else_if)]
3431#[allow(clippy::too_many_arguments)]
3432fn read_rubystyle(
3433    ctx: &mut OdsContext,
3434    xml: &mut OdsXmlReader<'_>,
3435    origin: StyleOrigin,
3436    style_use: StyleUse,
3437    super_tag: &BytesStart<'_>,
3438    empty_tag: bool,
3439) -> Result<(), OdsError> {
3440    let mut style = RubyStyle::new_empty();
3441    style.set_origin(origin);
3442    style.set_styleuse(style_use);
3443    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3444    style.set_name(name);
3445
3446    // In case of an empty xml-tag we are done here.
3447    if empty_tag {
3448        ctx.book.add_rubystyle(style);
3449    } else {
3450        let mut buf = ctx.pop_buf();
3451        loop {
3452            let evt = xml.read_event_into(&mut buf)?;
3453            if cfg!(feature = "dump_xml") {
3454                println!(" read_rubystyle {:?}", evt);
3455            }
3456            match &evt {
3457                Event::Start(xml_tag) | Event::Empty(xml_tag)
3458                    if xml_tag.name().as_ref() == b"style:ruby-properties" =>
3459                {
3460                    copy_attr2(ctx, style.rubystyle_mut(), xml_tag)?;
3461                }
3462                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
3463                    ctx.book.add_rubystyle(style);
3464                    break;
3465                }
3466                Event::Text(_) => (),
3467                Event::Eof => break,
3468                _ => {
3469                    unused_event("read_rubystyle", &evt)?;
3470                }
3471            }
3472        }
3473        ctx.push_buf(buf);
3474    }
3475
3476    Ok(())
3477}
3478
3479// style:style tag
3480#[allow(clippy::collapsible_else_if)]
3481#[allow(clippy::too_many_arguments)]
3482fn read_graphicstyle(
3483    ctx: &mut OdsContext,
3484    xml: &mut OdsXmlReader<'_>,
3485    origin: StyleOrigin,
3486    style_use: StyleUse,
3487    super_tag: &BytesStart<'_>,
3488    empty_tag: bool,
3489) -> Result<(), OdsError> {
3490    let mut style = GraphicStyle::new_empty();
3491    style.set_origin(origin);
3492    style.set_styleuse(style_use);
3493    let name = copy_style_attr(ctx, style.attrmap_mut(), super_tag)?;
3494    style.set_name(name);
3495
3496    // In case of an empty xml-tag we are done here.
3497    if empty_tag {
3498        ctx.book.add_graphicstyle(style);
3499    } else {
3500        let mut buf = ctx.pop_buf();
3501        loop {
3502            let evt = xml.read_event_into(&mut buf)?;
3503            if cfg!(feature = "dump_xml") {
3504                println!(" read_graphicstyle {:?}", evt);
3505            }
3506            match &evt {
3507                Event::Start(xml_tag) | Event::Empty(xml_tag)
3508                    if xml_tag.name().as_ref() == b"style:graphic-properties" =>
3509                {
3510                    copy_attr2(ctx, style.graphicstyle_mut(), xml_tag)?;
3511                }
3512                Event::Start(xml_tag) | Event::Empty(xml_tag)
3513                    if xml_tag.name().as_ref() == b"style:paragraph-properties" =>
3514                {
3515                    copy_attr2(ctx, style.paragraphstyle_mut(), xml_tag)?;
3516                }
3517                Event::Start(xml_tag) | Event::Empty(xml_tag)
3518                    if xml_tag.name().as_ref() == b"style:text-properties" =>
3519                {
3520                    copy_attr2(ctx, style.textstyle_mut(), xml_tag)?;
3521                }
3522                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
3523                    ctx.book.add_graphicstyle(style);
3524                    break;
3525                }
3526                Event::Text(_) => (),
3527                Event::Eof => break,
3528                _ => {
3529                    unused_event("read_graphicstyle", &evt)?;
3530                }
3531            }
3532        }
3533        ctx.push_buf(buf);
3534    }
3535
3536    Ok(())
3537}
3538
3539// style:map inside a number style.
3540fn read_value_stylemap(
3541    ctx: &mut OdsContext,
3542    super_tag: &BytesStart<'_>,
3543) -> Result<ValueStyleMap, OdsError> {
3544    let mut sm = ValueStyleMap::default();
3545    for attr in super_tag.attributes().with_checks(false) {
3546        match attr? {
3547            attr if attr.key.as_ref() == b"style:condition" => {
3548                sm.set_condition(ValueCondition::new(
3549                    attr.decode_and_unescape_value(ctx.decoder)?.to_string(),
3550                ));
3551            }
3552            attr if attr.key.as_ref() == b"style:apply-style-name" => {
3553                sm.set_applied_style(attr.decode_and_unescape_value(ctx.decoder)?);
3554            }
3555            attr => {
3556                unused_attr("read_value_stylemap", super_tag.name().as_ref(), &attr)?;
3557            }
3558        }
3559    }
3560
3561    Ok(sm)
3562}
3563
3564fn read_stylemap(ctx: &mut OdsContext, super_tag: &BytesStart<'_>) -> Result<StyleMap, OdsError> {
3565    let mut sm = StyleMap::new_empty();
3566    for attr in super_tag.attributes().with_checks(false) {
3567        match attr? {
3568            attr if attr.key.as_ref() == b"style:condition" => {
3569                sm.set_condition(Condition::new(
3570                    attr.decode_and_unescape_value(ctx.decoder)?.to_string(),
3571                ));
3572            }
3573            attr if attr.key.as_ref() == b"style:apply-style-name" => {
3574                let name = attr.decode_and_unescape_value(ctx.decoder)?;
3575                sm.set_applied_style(AnyStyleRef::from(name.as_ref()));
3576            }
3577            attr if attr.key.as_ref() == b"style:base-cell-address" => {
3578                let v = attr.decode_and_unescape_value(ctx.decoder)?;
3579                sm.set_base_cell(Some(parse_cellref(v.as_ref())?));
3580            }
3581            attr => {
3582                unused_attr("read_stylemap", super_tag.name().as_ref(), &attr)?;
3583            }
3584        }
3585    }
3586
3587    Ok(sm)
3588}
3589
3590/// Copies all attributes to the map, excluding "style:name" which is returned.
3591fn copy_style_attr(
3592    ctx: &mut OdsContext,
3593    attrmap: &mut AttrMap2,
3594    super_tag: &BytesStart<'_>,
3595) -> Result<String, OdsError> {
3596    let mut name = None;
3597
3598    for attr in super_tag.attributes().with_checks(false) {
3599        match attr? {
3600            attr if attr.key.as_ref() == b"style:name" => {
3601                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
3602            }
3603            attr => {
3604                let k = from_utf8(attr.key.as_ref())?;
3605                let v = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
3606                attrmap.push_attr(k, v);
3607            }
3608        }
3609    }
3610
3611    Ok(name.unwrap_or_default())
3612}
3613
3614/// Copies all attributes to the given map.
3615fn copy_attr2(
3616    ctx: &mut OdsContext,
3617    attrmap: &mut AttrMap2,
3618    super_tag: &BytesStart<'_>,
3619) -> Result<(), OdsError> {
3620    for attr in super_tag.attributes().with_checks(false) {
3621        let attr = attr?;
3622
3623        let k = from_utf8(attr.key.as_ref())?;
3624        let v = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
3625        attrmap.push_attr(k, v);
3626    }
3627
3628    Ok(())
3629}
3630
3631fn read_ods_styles(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
3632    let mut buf = ctx.pop_buf();
3633    loop {
3634        let evt = xml.read_event_into(&mut buf)?;
3635        if cfg!(feature = "dump_xml") {
3636            println!(" read_styles {:?}", evt);
3637        }
3638        match &evt {
3639            Event::Decl(_) => {}
3640            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:document-styles" => {
3641                let (_, xmlns) = read_namespaces_and_version(ctx, xml_tag)?;
3642                ctx.book.xmlns.insert("styles.xml".to_string(), xmlns);
3643            }
3644            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:document-styles" => {
3645                // noop
3646            }
3647            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:font-face-decls" => {
3648                read_office_font_face_decls(ctx, xml, StyleOrigin::Styles)?
3649            }
3650            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:styles" => {
3651                read_office_styles(ctx, xml, StyleOrigin::Styles)?
3652            }
3653            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:automatic-styles" => {
3654                read_office_automatic_styles(ctx, xml, StyleOrigin::Styles)?
3655            }
3656            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:master-styles" => {
3657                read_office_master_styles(ctx, xml, StyleOrigin::Styles)?
3658            }
3659            Event::Eof => {
3660                break;
3661            }
3662            _ => {
3663                unused_event("read_styles", &evt)?;
3664            }
3665        }
3666
3667        buf.clear();
3668    }
3669    ctx.push_buf(buf);
3670
3671    Ok(())
3672}
3673
3674#[allow(unused_variables)]
3675pub(crate) fn default_settings() -> Detach<Config> {
3676    let mut dc = Detach::new(Config::new());
3677    let p0 = dc.create_path(&[("ooo:view-settings", ConfigItemType::Set)]);
3678    p0.insert("VisibleAreaTop", 0);
3679    p0.insert("VisibleAreaLeft", 0);
3680    p0.insert("VisibleAreaWidth", 2540);
3681    p0.insert("VisibleAreaHeight", 1270);
3682
3683    let p0 = dc.create_path(&[
3684        ("ooo:view-settings", ConfigItemType::Set),
3685        ("Views", ConfigItemType::Vec),
3686        ("0", ConfigItemType::Entry),
3687    ]);
3688    p0.insert("ViewId", "view1");
3689    let p0 = dc.create_path(&[
3690        ("ooo:view-settings", ConfigItemType::Set),
3691        ("Views", ConfigItemType::Vec),
3692        ("0", ConfigItemType::Entry),
3693        ("Tables", ConfigItemType::Map),
3694    ]);
3695    let p0 = dc.create_path(&[
3696        ("ooo:view-settings", ConfigItemType::Set),
3697        ("Views", ConfigItemType::Vec),
3698        ("0", ConfigItemType::Entry),
3699    ]);
3700    p0.insert("ActiveTable", "");
3701    p0.insert("HorizontalScrollbarWidth", 702);
3702    p0.insert("ZoomType", 0i16);
3703    p0.insert("ZoomValue", 100);
3704    p0.insert("PageViewZoomValue", 60);
3705    p0.insert("ShowPageBreakPreview", false);
3706    p0.insert("ShowZeroValues", true);
3707    p0.insert("ShowNotes", true);
3708    p0.insert("ShowGrid", true);
3709    p0.insert("GridColor", 12632256);
3710    p0.insert("ShowPageBreaks", false);
3711    p0.insert("HasColumnRowHeaders", true);
3712    p0.insert("HasSheetTabs", true);
3713    p0.insert("IsOutlineSymbolsSet", true);
3714    p0.insert("IsValueHighlightingEnabled", false);
3715    p0.insert("IsSnapToRaster", false);
3716    p0.insert("RasterIsVisible", false);
3717    p0.insert("RasterResolutionX", 1000);
3718    p0.insert("RasterResolutionY", 1000);
3719    p0.insert("RasterSubdivisionX", 1);
3720    p0.insert("RasterSubdivisionY", 1);
3721    p0.insert("IsRasterAxisSynchronized", true);
3722    p0.insert("AnchoredTextOverflowLegacy", false);
3723
3724    let p0 = dc.create_path(&[("ooo:configuration-settings", ConfigItemType::Set)]);
3725    p0.insert("HasSheetTabs", true);
3726    p0.insert("ShowNotes", true);
3727    p0.insert("EmbedComplexScriptFonts", true);
3728    p0.insert("ShowZeroValues", true);
3729    p0.insert("ShowGrid", true);
3730    p0.insert("GridColor", 12632256);
3731    p0.insert("ShowPageBreaks", false);
3732    p0.insert("IsKernAsianPunctuation", false);
3733    p0.insert("LinkUpdateMode", 3i16);
3734    p0.insert("HasColumnRowHeaders", true);
3735    p0.insert("EmbedLatinScriptFonts", true);
3736    p0.insert("IsOutlineSymbolsSet", true);
3737    p0.insert("EmbedLatinScriptFonts", true);
3738    p0.insert("IsOutlineSymbolsSet", true);
3739    p0.insert("IsSnapToRaster", false);
3740    p0.insert("RasterIsVisible", false);
3741    p0.insert("RasterResolutionX", 1000);
3742    p0.insert("RasterResolutionY", 1000);
3743    p0.insert("RasterSubdivisionX", 1);
3744    p0.insert("RasterSubdivisionY", 1);
3745    p0.insert("IsRasterAxisSynchronized", true);
3746    p0.insert("AutoCalculate", true);
3747    p0.insert("ApplyUserData", true);
3748    p0.insert("PrinterName", "");
3749    p0.insert("PrinterSetup", ConfigValue::Base64Binary("".to_string()));
3750    p0.insert("SaveThumbnail", true);
3751    p0.insert("CharacterCompressionType", 0i16);
3752    p0.insert("SaveVersionOnClose", false);
3753    p0.insert("UpdateFromTemplate", true);
3754    p0.insert("AllowPrintJobCancel", true);
3755    p0.insert("LoadReadonly", false);
3756    p0.insert("IsDocumentShared", false);
3757    p0.insert("EmbedFonts", false);
3758    p0.insert("EmbedOnlyUsedFonts", false);
3759    p0.insert("EmbedAsianScriptFonts", true);
3760    p0.insert("SyntaxStringRef", 7i16);
3761
3762    dc
3763}
3764
3765fn read_ods_metadata(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
3766    let mut buf = ctx.pop_buf();
3767
3768    loop {
3769        let evt = xml.read_event_into(&mut buf)?;
3770        if cfg!(feature = "dump_xml") {
3771            println!("read_ods_metadata {:?}", evt);
3772        }
3773
3774        match &evt {
3775            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:document-meta" => {
3776                let (_, xmlns) = read_namespaces_and_version(ctx, xml_tag)?;
3777                ctx.book.xmlns.insert("meta.xml".to_string(), xmlns);
3778            }
3779            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:document-meta" => {}
3780
3781            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:meta" => {
3782                read_office_meta(ctx, xml)?;
3783            }
3784
3785            Event::Decl(_) => {}
3786            Event::Eof => {
3787                break;
3788            }
3789            _ => {
3790                unused_event("read_ods_metadata", &evt)?;
3791            }
3792        }
3793
3794        buf.clear();
3795    }
3796    ctx.push_buf(buf);
3797
3798    Ok(())
3799}
3800
3801fn read_office_meta(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
3802    let mut buf = ctx.pop_buf();
3803
3804    loop {
3805        let evt = xml.read_event_into(&mut buf)?;
3806        if cfg!(feature = "dump_xml") {
3807            println!("read_metadata {:?}", evt);
3808        }
3809
3810        match &evt {
3811            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:meta" => {
3812                break;
3813            }
3814
3815            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:generator" => {
3816                ctx.book.metadata.generator = read_metadata_value(
3817                    ctx,
3818                    xml,
3819                    xml_tag,
3820                    |v| Ok(from_utf8(v)?.to_string()),
3821                    String::new,
3822                )?;
3823            }
3824            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:title" => {
3825                ctx.book.metadata.title = read_metadata_value(
3826                    ctx,
3827                    xml,
3828                    xml_tag,
3829                    |v| Ok(from_utf8(v)?.to_string()),
3830                    String::new,
3831                )?;
3832            }
3833            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:description" => {
3834                ctx.book.metadata.description = read_metadata_value(
3835                    ctx,
3836                    xml,
3837                    xml_tag,
3838                    |v| Ok(from_utf8(v)?.to_string()),
3839                    String::new,
3840                )?;
3841            }
3842            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:subject" => {
3843                ctx.book.metadata.subject = read_metadata_value(
3844                    ctx,
3845                    xml,
3846                    xml_tag,
3847                    |v| Ok(from_utf8(v)?.to_string()),
3848                    String::new,
3849                )?;
3850            }
3851            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:keyword" => {
3852                ctx.book.metadata.keyword = read_metadata_value(
3853                    ctx,
3854                    xml,
3855                    xml_tag,
3856                    |v| Ok(from_utf8(v)?.to_string()),
3857                    String::new,
3858                )?;
3859            }
3860            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:initial-creator" => {
3861                ctx.book.metadata.initial_creator = read_metadata_value(
3862                    ctx,
3863                    xml,
3864                    xml_tag,
3865                    |v| Ok(from_utf8(v)?.to_string()),
3866                    String::new,
3867                )?;
3868            }
3869            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:creator" => {
3870                ctx.book.metadata.creator = read_metadata_value(
3871                    ctx,
3872                    xml,
3873                    xml_tag,
3874                    |v| Ok(from_utf8(v)?.to_string()),
3875                    String::new,
3876                )?;
3877            }
3878            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:printed-by" => {
3879                ctx.book.metadata.printed_by = read_metadata_value(
3880                    ctx,
3881                    xml,
3882                    xml_tag,
3883                    |v| Ok(from_utf8(v)?.to_string()),
3884                    String::new,
3885                )?;
3886            }
3887            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:creation-date" => {
3888                ctx.book.metadata.creation_date = read_metadata_value(
3889                    ctx,
3890                    xml,
3891                    xml_tag,
3892                    |v| Ok(Some(parse_datetime(v)?)),
3893                    || None,
3894                )?;
3895            }
3896            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:date" => {
3897                ctx.book.metadata.date = read_metadata_value(
3898                    ctx,
3899                    xml,
3900                    xml_tag,
3901                    |v| Ok(Some(parse_datetime(v)?)),
3902                    || None,
3903                )?;
3904            }
3905            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:print-date" => {
3906                ctx.book.metadata.print_date = read_metadata_value(
3907                    ctx,
3908                    xml,
3909                    xml_tag,
3910                    |v| Ok(Some(parse_datetime(v)?)),
3911                    || None,
3912                )?;
3913            }
3914            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"dc:language" => {
3915                ctx.book.metadata.language = read_metadata_value(
3916                    ctx,
3917                    xml,
3918                    xml_tag,
3919                    |v| Ok(from_utf8(v)?.to_string()),
3920                    String::new,
3921                )?;
3922            }
3923            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:editing-cycles" => {
3924                ctx.book.metadata.editing_cycles =
3925                    read_metadata_value(ctx, xml, xml_tag, parse_u32, || 0)?;
3926            }
3927            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:editing-duration" => {
3928                ctx.book.metadata.editing_duration =
3929                    read_metadata_value(ctx, xml, xml_tag, parse_duration, Duration::default)?;
3930            }
3931
3932            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:template" => {
3933                ctx.book.metadata.template = read_metadata_template(ctx, xml_tag)?;
3934            }
3935            Event::End(xml_tag) if xml_tag.name().as_ref() == b"meta:template" => {}
3936
3937            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:auto-reload" => {
3938                ctx.book.metadata.auto_reload = read_metadata_auto_reload(ctx, xml_tag)?;
3939            }
3940            Event::End(xml_tag) if xml_tag.name().as_ref() == b"meta:auto-reload" => {}
3941
3942            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:hyperlink-behaviour" => {
3943                ctx.book.metadata.hyperlink_behaviour =
3944                    read_metadata_hyperlink_behaviour(ctx, xml_tag)?;
3945            }
3946            Event::End(xml_tag) if xml_tag.name().as_ref() == b"meta:hyperlink-behaviour" => {}
3947
3948            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:document-statistic" => {
3949                ctx.book.metadata.document_statistics =
3950                    read_metadata_document_statistics(ctx, xml_tag)?;
3951            }
3952            Event::End(xml_tag) if xml_tag.name().as_ref() == b"meta:document-statistic" => {}
3953
3954            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"meta:user-defined" => {
3955                let userdefined = read_metadata_user_defined(ctx, xml, xml_tag)?;
3956                ctx.book.metadata.user_defined.push(userdefined);
3957            }
3958
3959            Event::Empty(_) => {}
3960            Event::Text(_) => {}
3961            Event::Eof => {
3962                break;
3963            }
3964            _ => {
3965                unused_event("read_metadata", &evt)?;
3966            }
3967        }
3968
3969        buf.clear();
3970    }
3971    ctx.push_buf(buf);
3972
3973    Ok(())
3974}
3975
3976fn read_metadata_template(
3977    ctx: &mut OdsContext,
3978    tag: &BytesStart<'_>,
3979) -> Result<MetaTemplate, OdsError> {
3980    let mut template = MetaTemplate::default();
3981
3982    for attr in tag.attributes().with_checks(false) {
3983        match attr? {
3984            attr if attr.key.as_ref() == b"meta:date" => {
3985                template.date = Some(parse_datetime(
3986                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
3987                )?);
3988            }
3989            attr if attr.key.as_ref() == b"xlink:actuate" => {
3990                template.actuate = Some(parse_xlink_actuate(
3991                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
3992                )?);
3993            }
3994            attr if attr.key.as_ref() == b"xlink:href" => {
3995                template.href = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string())
3996            }
3997            attr if attr.key.as_ref() == b"xlink:title" => {
3998                template.title = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string())
3999            }
4000            attr if attr.key.as_ref() == b"xlink:type" => {
4001                template.link_type = Some(parse_xlink_type(
4002                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4003                )?);
4004            }
4005            attr => {
4006                unused_attr("read_metadata_template", tag.name().as_ref(), &attr)?;
4007            }
4008        }
4009    }
4010
4011    Ok(template)
4012}
4013
4014fn read_metadata_auto_reload(
4015    ctx: &mut OdsContext,
4016    tag: &BytesStart<'_>,
4017) -> Result<MetaAutoReload, OdsError> {
4018    let mut auto_reload = MetaAutoReload::default();
4019
4020    for attr in tag.attributes().with_checks(false) {
4021        match attr? {
4022            attr if attr.key.as_ref() == b"meta:delay" => {
4023                auto_reload.delay = Some(parse_duration(
4024                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4025                )?);
4026            }
4027            attr if attr.key.as_ref() == b"xlink:actuate" => {
4028                auto_reload.actuate = Some(parse_xlink_actuate(
4029                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4030                )?);
4031            }
4032            attr if attr.key.as_ref() == b"xlink:href" => {
4033                auto_reload.href = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string())
4034            }
4035            attr if attr.key.as_ref() == b"xlink:show" => {
4036                auto_reload.show = Some(parse_xlink_show(
4037                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4038                )?);
4039            }
4040            attr if attr.key.as_ref() == b"xlink:type" => {
4041                auto_reload.link_type = Some(parse_xlink_type(
4042                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4043                )?);
4044            }
4045            attr => {
4046                unused_attr("read_metadata_auto_reload", tag.name().as_ref(), &attr)?;
4047            }
4048        }
4049    }
4050
4051    Ok(auto_reload)
4052}
4053
4054fn read_metadata_hyperlink_behaviour(
4055    ctx: &mut OdsContext,
4056    tag: &BytesStart<'_>,
4057) -> Result<MetaHyperlinkBehaviour, OdsError> {
4058    let mut hyperlink_behaviour = MetaHyperlinkBehaviour::default();
4059
4060    for attr in tag.attributes().with_checks(false) {
4061        match attr? {
4062            attr if attr.key.as_ref() == b"office:targetframe-name" => {
4063                hyperlink_behaviour.target_frame_name =
4064                    Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4065            }
4066            attr if attr.key.as_ref() == b"xlink:show" => {
4067                hyperlink_behaviour.show = Some(parse_xlink_show(
4068                    attr.decode_and_unescape_value(ctx.decoder)?.as_bytes(),
4069                )?);
4070            }
4071            attr => {
4072                unused_attr(
4073                    "read_metadata_hyperlink_behaviour",
4074                    tag.name().as_ref(),
4075                    &attr,
4076                )?;
4077            }
4078        }
4079    }
4080
4081    Ok(hyperlink_behaviour)
4082}
4083
4084fn read_metadata_document_statistics(
4085    ctx: &mut OdsContext,
4086    tag: &BytesStart<'_>,
4087) -> Result<MetaDocumentStatistics, OdsError> {
4088    let mut document_statistics = MetaDocumentStatistics::default();
4089
4090    for attr in tag.attributes().with_checks(false) {
4091        match attr? {
4092            attr if attr.key.as_ref() == b"meta:cell-count" => {
4093                document_statistics.cell_count =
4094                    parse_u32(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
4095            }
4096            attr if attr.key.as_ref() == b"meta:object-count" => {
4097                document_statistics.object_count =
4098                    parse_u32(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
4099            }
4100            attr if attr.key.as_ref() == b"meta:ole-object-count" => {
4101                document_statistics.ole_object_count =
4102                    parse_u32(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
4103            }
4104            attr if attr.key.as_ref() == b"meta:table-count" => {
4105                document_statistics.table_count =
4106                    parse_u32(attr.decode_and_unescape_value(ctx.decoder)?.as_bytes())?;
4107            }
4108            attr => {
4109                unused_attr(
4110                    "read_metadata_document_statistics",
4111                    tag.name().as_ref(),
4112                    &attr,
4113                )?;
4114            }
4115        }
4116    }
4117
4118    Ok(document_statistics)
4119}
4120
4121fn read_metadata_user_defined(
4122    ctx: &mut OdsContext,
4123    xml: &mut OdsXmlReader<'_>,
4124    tag: &BytesStart<'_>,
4125) -> Result<MetaUserDefined, OdsError> {
4126    let mut user_defined = MetaUserDefined::default();
4127    let mut value_type = None;
4128    for attr in tag.attributes().with_checks(false) {
4129        match attr? {
4130            attr if attr.key.as_ref() == b"meta:name" => {
4131                user_defined.name = attr.decode_and_unescape_value(ctx.decoder)?.to_string();
4132            }
4133            attr if attr.key.as_ref() == b"meta:value-type" => {
4134                value_type = Some(
4135                    match attr.decode_and_unescape_value(ctx.decoder)?.as_ref() {
4136                        "boolean" => "boolean",
4137                        "date" => "date",
4138                        "float" => "float",
4139                        "time" => "time",
4140                        _ => "string",
4141                    },
4142                );
4143            }
4144            attr => {
4145                unused_attr("read_meta_user_defined", tag.name().as_ref(), &attr)?;
4146            }
4147        }
4148    }
4149
4150    let mut buf = ctx.pop_buf();
4151    loop {
4152        let evt = xml.read_event_into(&mut buf)?;
4153        if cfg!(feature = "dump_xml") {
4154            println!("read_meta_user_defined {:?}", evt);
4155        }
4156
4157        match &evt {
4158            Event::End(xml_tag) if xml_tag.name() == tag.name() => {
4159                break;
4160            }
4161            Event::Text(xml_text) => {
4162                user_defined.value = match value_type {
4163                    Some("boolean") => MetaValue::Boolean(parse_bool(xml_text)?),
4164                    Some("date") => MetaValue::Datetime(parse_datetime(xml_text)?),
4165                    Some("float") => MetaValue::Float(parse_f64(xml_text)?),
4166                    Some("time") => MetaValue::TimeDuration(parse_duration(xml_text)?),
4167                    _ => MetaValue::String(xml_text.decode()?.to_string()),
4168                };
4169            }
4170            Event::Eof => {
4171                break;
4172            }
4173            _ => {
4174                unused_event("read_meta_user_defined", &evt)?;
4175            }
4176        }
4177
4178        buf.clear();
4179    }
4180    ctx.push_buf(buf);
4181
4182    Ok(user_defined)
4183}
4184
4185// Parse a metadata value.
4186fn read_metadata_value<T>(
4187    ctx: &mut OdsContext,
4188    xml: &mut OdsXmlReader<'_>,
4189    tag: &BytesStart<'_>,
4190    parse: fn(&[u8]) -> Result<T, OdsError>,
4191    default: fn() -> T,
4192) -> Result<T, OdsError> {
4193    let mut buf = ctx.pop_buf();
4194    let mut text = String::new();
4195    loop {
4196        let evt = xml.read_event_into(&mut buf)?;
4197        if cfg!(feature = "dump_xml") {
4198            println!("read_metadata_value {:?}", evt);
4199        }
4200
4201        match &evt {
4202            Event::End(xml_tag) if xml_tag.name() == tag.name() => {
4203                break;
4204            }
4205            Event::GeneralRef(xml_ref) => {
4206                text.push_str(entity(xml_ref)?.as_ref());
4207            }
4208            Event::Text(xml_text) => {
4209                text.push_str(xml_text.decode()?.as_ref());
4210            }
4211            Event::Eof => {
4212                break;
4213            }
4214            _ => {
4215                unused_event("read_metadata_value", &evt)?;
4216            }
4217        }
4218
4219        buf.clear();
4220    }
4221    ctx.push_buf(buf);
4222
4223    if !text.is_empty() {
4224        parse(text.as_bytes())
4225    } else {
4226        Ok(default())
4227    }
4228}
4229
4230fn read_ods_settings(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
4231    let mut buf = ctx.pop_buf();
4232    loop {
4233        let evt = xml.read_event_into(&mut buf)?;
4234        if cfg!(feature = "dump_xml") {
4235            println!(" read_settings {:?}", evt);
4236        }
4237
4238        match &evt {
4239            Event::Decl(_) => {}
4240
4241            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:document-settings" => {
4242                let (_, xmlns) = read_namespaces_and_version(ctx, xml_tag)?;
4243                ctx.book.xmlns.insert("settings.xml".to_string(), xmlns);
4244            }
4245            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:document-settings" => {}
4246
4247            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"office:settings" => {
4248                read_office_settings(ctx, xml)?;
4249            }
4250
4251            Event::Eof => {
4252                break;
4253            }
4254            _ => {
4255                unused_event("read_settings", &evt)?;
4256            }
4257        }
4258
4259        buf.clear();
4260    }
4261    ctx.push_buf(buf);
4262
4263    Ok(())
4264}
4265
4266// read the automatic-styles tag
4267fn read_office_settings(ctx: &mut OdsContext, xml: &mut OdsXmlReader<'_>) -> Result<(), OdsError> {
4268    let mut config = Config::new();
4269
4270    let mut buf = ctx.pop_buf();
4271    loop {
4272        let evt = xml.read_event_into(&mut buf)?;
4273        if cfg!(feature = "dump_xml") {
4274            println!(" read_office_settings {:?}", evt);
4275        }
4276        match &evt {
4277            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-set" => {
4278                let (name, set) = read_config_item_set(ctx, xml, xml_tag)?;
4279                config.insert(name, set);
4280            }
4281
4282            Event::End(xml_tag) if xml_tag.name().as_ref() == b"office:settings" => {
4283                break;
4284            }
4285            Event::Eof => break,
4286            _ => {
4287                unused_event("read_office_settings", &evt)?;
4288            }
4289        }
4290
4291        buf.clear();
4292    }
4293    ctx.push_buf(buf);
4294
4295    ctx.book.config = Detach::new(config);
4296
4297    Ok(())
4298}
4299
4300// read the automatic-styles tag
4301fn read_config_item_set(
4302    ctx: &mut OdsContext,
4303    xml: &mut OdsXmlReader<'_>,
4304    super_tag: &BytesStart<'_>,
4305) -> Result<(String, ConfigItem), OdsError> {
4306    let mut name = None;
4307    let mut config_set = ConfigItem::new_set();
4308
4309    for attr in super_tag.attributes().with_checks(false) {
4310        match attr? {
4311            attr if attr.key.as_ref() == b"config:name" => {
4312                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4313            }
4314            attr => {
4315                unused_attr("read_config_item_set", super_tag.name().as_ref(), &attr)?;
4316            }
4317        }
4318    }
4319
4320    let name = if let Some(name) = name {
4321        name
4322    } else {
4323        return Err(OdsError::Ods("config-item-set without name".to_string()));
4324    };
4325
4326    let mut buf = ctx.pop_buf();
4327    loop {
4328        let evt = xml.read_event_into(&mut buf)?;
4329        if cfg!(feature = "dump_xml") {
4330            println!(" read_office_item_set {:?}", evt);
4331        }
4332        match &evt {
4333            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"config:config-item" => {}
4334            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item" => {
4335                let (name, val) = read_config_item(ctx, xml, xml_tag)?;
4336                config_set.insert(name, val);
4337            }
4338            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-set" => {
4339                let (name, val) = read_config_item_set(ctx, xml, xml_tag)?;
4340                config_set.insert(name, val);
4341            }
4342            Event::Start(xml_tag)
4343                if xml_tag.name().as_ref() == b"config:config-item-map-indexed" =>
4344            {
4345                let (name, val) = read_config_item_map_indexed(ctx, xml, xml_tag)?;
4346                config_set.insert(name, val);
4347            }
4348            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-named" => {
4349                let (name, val) = read_config_item_map_named(ctx, xml, xml_tag)?;
4350                config_set.insert(name, val);
4351            }
4352            Event::End(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-set" => {
4353                break;
4354            }
4355            Event::Eof => break,
4356            _ => {
4357                unused_event("read_config_item_set", &evt)?;
4358            }
4359        }
4360
4361        buf.clear();
4362    }
4363    ctx.push_buf(buf);
4364
4365    Ok((name, config_set))
4366}
4367
4368// read the automatic-styles tag
4369fn read_config_item_map_indexed(
4370    ctx: &mut OdsContext,
4371    xml: &mut OdsXmlReader<'_>,
4372    super_tag: &BytesStart<'_>,
4373) -> Result<(String, ConfigItem), OdsError> {
4374    let mut name = None;
4375    let mut config_vec = ConfigItem::new_vec();
4376
4377    for attr in super_tag.attributes().with_checks(false) {
4378        match attr? {
4379            attr if attr.key.as_ref() == b"config:name" => {
4380                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4381            }
4382            attr => {
4383                unused_attr(
4384                    "read_config_item_map_indexed",
4385                    super_tag.name().as_ref(),
4386                    &attr,
4387                )?;
4388            }
4389        }
4390    }
4391
4392    let name = if let Some(name) = name {
4393        name
4394    } else {
4395        return Err(OdsError::Ods(
4396            "config-item-map-indexed without name".to_string(),
4397        ));
4398    };
4399
4400    let mut index = 0;
4401
4402    let mut buf = ctx.pop_buf();
4403    loop {
4404        let evt = xml.read_event_into(&mut buf)?;
4405        if cfg!(feature = "dump_xml") {
4406            println!(" read_office_item_set {:?}", evt);
4407        }
4408        match &evt {
4409            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-entry" => {
4410                let (_, entry) = read_config_item_map_entry(ctx, xml, xml_tag)?;
4411                config_vec.insert(index.to_string(), entry);
4412                index += 1;
4413            }
4414            Event::End(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-indexed" => {
4415                break;
4416            }
4417            Event::Eof => break,
4418            _ => {
4419                unused_event("read_config_item_map_indexed", &evt)?;
4420            }
4421        }
4422
4423        buf.clear();
4424    }
4425    ctx.push_buf(buf);
4426
4427    Ok((name, config_vec))
4428}
4429
4430// read the automatic-styles tag
4431fn read_config_item_map_named(
4432    ctx: &mut OdsContext,
4433    xml: &mut OdsXmlReader<'_>,
4434    super_tag: &BytesStart<'_>,
4435) -> Result<(String, ConfigItem), OdsError> {
4436    let mut name = None;
4437    let mut config_map = ConfigItem::new_map();
4438
4439    for attr in super_tag.attributes().with_checks(false) {
4440        match attr? {
4441            attr if attr.key.as_ref() == b"config:name" => {
4442                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4443            }
4444            attr => {
4445                unused_attr(
4446                    "read_config_item_map_named",
4447                    super_tag.name().as_ref(),
4448                    &attr,
4449                )?;
4450            }
4451        }
4452    }
4453
4454    let name = if let Some(name) = name {
4455        name
4456    } else {
4457        return Err(OdsError::Ods(
4458            "config-item-map-named without name".to_string(),
4459        ));
4460    };
4461
4462    let mut buf = ctx.pop_buf();
4463    loop {
4464        let evt = xml.read_event_into(&mut buf)?;
4465        if cfg!(feature = "dump_xml") {
4466            println!(" read_config_item_map_named {:?}", evt);
4467        }
4468        match &evt {
4469            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-entry" => {
4470                let (name, entry) = read_config_item_map_entry(ctx, xml, xml_tag)?;
4471
4472                let name = if let Some(name) = name {
4473                    name
4474                } else {
4475                    return Err(OdsError::Ods(
4476                        "config-item-map-entry without name".to_string(),
4477                    ));
4478                };
4479
4480                config_map.insert(name, entry);
4481            }
4482            Event::End(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-named" => {
4483                break;
4484            }
4485            Event::Eof => break,
4486            _ => {
4487                unused_event("read_config_item_map_named", &evt)?;
4488            }
4489        }
4490
4491        buf.clear();
4492    }
4493    ctx.push_buf(buf);
4494
4495    Ok((name, config_map))
4496}
4497
4498// read the automatic-styles tag
4499fn read_config_item_map_entry(
4500    ctx: &mut OdsContext,
4501    xml: &mut OdsXmlReader<'_>,
4502    super_tag: &BytesStart<'_>,
4503) -> Result<(Option<String>, ConfigItem), OdsError> {
4504    let mut name = None;
4505    let mut config_set = ConfigItem::new_entry();
4506
4507    for attr in super_tag.attributes().with_checks(false) {
4508        match attr? {
4509            attr if attr.key.as_ref() == b"config:name" => {
4510                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4511            }
4512            attr => {
4513                unused_attr(
4514                    "read_config_item_map_entry",
4515                    super_tag.name().as_ref(),
4516                    &attr,
4517                )?;
4518            }
4519        }
4520    }
4521
4522    let mut buf = ctx.pop_buf();
4523    loop {
4524        let evt = xml.read_event_into(&mut buf)?;
4525        if cfg!(feature = "dump_xml") {
4526            println!(" read_config_item_map_entry {:?}", evt);
4527        }
4528        match &evt {
4529            Event::Empty(xml_tag) if xml_tag.name().as_ref() == b"config:config-item" => {}
4530            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item" => {
4531                let (name, val) = read_config_item(ctx, xml, xml_tag)?;
4532                config_set.insert(name, ConfigItem::from(val));
4533            }
4534
4535            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-set" => {
4536                let (name, val) = read_config_item_set(ctx, xml, xml_tag)?;
4537                config_set.insert(name, val);
4538            }
4539
4540            Event::Start(xml_tag)
4541                if xml_tag.name().as_ref() == b"config:config-item-map-indexed" =>
4542            {
4543                let (name, val) = read_config_item_map_indexed(ctx, xml, xml_tag)?;
4544                config_set.insert(name, val);
4545            }
4546
4547            Event::Start(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-named" => {
4548                let (name, val) = read_config_item_map_named(ctx, xml, xml_tag)?;
4549                config_set.insert(name, val);
4550            }
4551            Event::End(xml_tag) if xml_tag.name().as_ref() == b"config:config-item-map-entry" => {
4552                break;
4553            }
4554
4555            Event::Eof => break,
4556            _ => {
4557                unused_event("read_config_item_map_entry", &evt)?;
4558            }
4559        }
4560
4561        buf.clear();
4562    }
4563    ctx.push_buf(buf);
4564
4565    Ok((name, config_set))
4566}
4567
4568// read the automatic-styles tag
4569fn read_config_item(
4570    ctx: &mut OdsContext,
4571    xml: &mut OdsXmlReader<'_>,
4572    super_tag: &BytesStart<'_>,
4573) -> Result<(String, ConfigValue), OdsError> {
4574    #[derive(PartialEq)]
4575    enum ConfigValueType {
4576        None,
4577        Base64Binary,
4578        Boolean,
4579        DateTime,
4580        Double,
4581        Int,
4582        Long,
4583        Short,
4584        String,
4585    }
4586
4587    let mut name = None;
4588    let mut val_type = ConfigValueType::None;
4589    let mut config_val = None;
4590
4591    for attr in super_tag.attributes().with_checks(false) {
4592        match attr? {
4593            attr if attr.key.as_ref() == b"config:name" => {
4594                name = Some(attr.decode_and_unescape_value(ctx.decoder)?.to_string());
4595            }
4596            attr if attr.key.as_ref() == b"config:type" => {
4597                val_type = match attr.value.as_ref() {
4598                    b"base64Binary" => ConfigValueType::Base64Binary,
4599                    b"boolean" => ConfigValueType::Boolean,
4600                    b"datetime" => ConfigValueType::DateTime,
4601                    b"double" => ConfigValueType::Double,
4602                    b"int" => ConfigValueType::Int,
4603                    b"long" => ConfigValueType::Long,
4604                    b"short" => ConfigValueType::Short,
4605                    b"string" => ConfigValueType::String,
4606                    x => {
4607                        return Err(OdsError::Ods(format!(
4608                            "unknown config:type {}",
4609                            from_utf8(x)?
4610                        )));
4611                    }
4612                };
4613            }
4614            attr => {
4615                unused_attr("read_config_item", super_tag.name().as_ref(), &attr)?;
4616            }
4617        }
4618    }
4619
4620    let name = if let Some(name) = name {
4621        name
4622    } else {
4623        return Err(OdsError::Ods(
4624            "config value without config:name".to_string(),
4625        ));
4626    };
4627
4628    if val_type == ConfigValueType::None {
4629        return Err(OdsError::Ods(
4630            "config value without config:type".to_string(),
4631        ));
4632    };
4633
4634    let mut value = ctx.pop_buf();
4635    let mut buf = ctx.pop_buf();
4636    loop {
4637        let evt = xml.read_event_into(&mut buf)?;
4638        match &evt {
4639            Event::GeneralRef(xml_ref) => {
4640                value.write_all(entity(xml_ref)?.as_bytes())?;
4641            }
4642            Event::Text(xml_text) => {
4643                value.write_all(xml_text.decode()?.as_bytes())?;
4644            }
4645            Event::End(xml_tag) if xml_tag.name().as_ref() == b"config:config-item" => {
4646                let value = <Cow<'_, [u8]> as From<&Vec<u8>>>::from(value.as_ref());
4647                match val_type {
4648                    ConfigValueType::None => {}
4649                    ConfigValueType::Base64Binary => {
4650                        config_val =
4651                            Some(ConfigValue::Base64Binary(from_utf8(&value)?.to_string()));
4652                    }
4653                    ConfigValueType::Boolean => {
4654                        config_val = Some(ConfigValue::Boolean(parse_bool(&value)?));
4655                    }
4656                    ConfigValueType::DateTime => {
4657                        config_val = Some(ConfigValue::DateTime(parse_datetime(&value)?));
4658                    }
4659                    ConfigValueType::Double => {
4660                        config_val = Some(ConfigValue::Double(parse_f64(&value)?));
4661                    }
4662                    ConfigValueType::Int => {
4663                        config_val = Some(ConfigValue::Int(parse_i32(&value)?));
4664                    }
4665                    ConfigValueType::Long => {
4666                        config_val = Some(ConfigValue::Long(parse_i64(&value)?));
4667                    }
4668                    ConfigValueType::Short => {
4669                        config_val = Some(ConfigValue::Short(parse_i16(&value)?));
4670                    }
4671                    ConfigValueType::String => {
4672                        config_val =
4673                            Some(ConfigValue::String(from_utf8(value.as_ref())?.to_string()));
4674                    }
4675                }
4676                break;
4677            }
4678            Event::Eof => {
4679                break;
4680            }
4681            _ => {
4682                unused_event("read_config_item", &evt)?;
4683            }
4684        }
4685
4686        if cfg!(feature = "dump_xml") {
4687            println!(" read_config_item {:?}", evt);
4688        }
4689        buf.clear();
4690    }
4691    ctx.push_buf(buf);
4692    ctx.push_buf(value);
4693
4694    let config_val = if let Some(config_val) = config_val {
4695        config_val
4696    } else {
4697        return Err(OdsError::Ods("config-item without value???".to_string()));
4698    };
4699
4700    Ok((name, config_val))
4701}
4702
4703// Reads a part of the XML as XmlTag's.
4704fn read_xml(
4705    ctx: &mut OdsContext,
4706    xml: &mut OdsXmlReader<'_>,
4707    super_tag: &BytesStart<'_>,
4708    empty_tag: bool,
4709) -> Result<XmlTag, OdsError> {
4710    let mut stack = ctx.pop_xml_buf();
4711
4712    let mut tag = XmlTag::new(from_utf8(super_tag.name().as_ref())?);
4713    copy_attr2(ctx, tag.attrmap_mut(), super_tag)?;
4714    stack.push(tag);
4715
4716    if !empty_tag {
4717        let mut buf = ctx.pop_buf();
4718        loop {
4719            let evt = xml.read_event_into(&mut buf)?;
4720            if cfg!(feature = "dump_xml") {
4721                println!(" read_xml {:?}", evt);
4722            }
4723            match &evt {
4724                Event::Start(xml_tag) => {
4725                    let mut tag = XmlTag::new(from_utf8(xml_tag.name().as_ref())?);
4726                    copy_attr2(ctx, tag.attrmap_mut(), xml_tag)?;
4727                    stack.push(tag);
4728                }
4729                Event::End(xml_tag) => {
4730                    if xml_tag.name() == super_tag.name() {
4731                        break;
4732                    } else {
4733                        let tag = stack.pop().expect("valid stack");
4734                        if let Some(parent) = stack.last_mut() {
4735                            parent.add_tag(tag);
4736                        } else {
4737                            unreachable!()
4738                        }
4739                    }
4740                }
4741                Event::Empty(xml_tag) => {
4742                    let mut emptytag = XmlTag::new(from_utf8(xml_tag.name().as_ref())?);
4743                    copy_attr2(ctx, emptytag.attrmap_mut(), xml_tag)?;
4744
4745                    if let Some(parent) = stack.last_mut() {
4746                        parent.add_tag(emptytag);
4747                    } else {
4748                        unreachable!()
4749                    }
4750                }
4751                Event::GeneralRef(xml_ref) => {
4752                    if let Some(parent) = stack.last_mut() {
4753                        parent.add_text(entity(xml_ref)?.as_ref());
4754                    } else {
4755                        unreachable!()
4756                    }
4757                }
4758                Event::Text(xml_text) => {
4759                    if let Some(parent) = stack.last_mut() {
4760                        parent.add_text(xml_text.decode()?.as_ref());
4761                    } else {
4762                        unreachable!()
4763                    }
4764                }
4765                Event::Eof => {
4766                    break;
4767                }
4768                _ => {
4769                    unused_event("read_xml", &evt)?;
4770                }
4771            }
4772            buf.clear();
4773        }
4774
4775        ctx.push_buf(buf);
4776    }
4777
4778    let tag = stack.pop().unwrap();
4779    ctx.push_xml_buf(stack);
4780    Ok(tag)
4781}
4782
4783fn read_text_or_tag(
4784    ctx: &mut OdsContext,
4785    xml: &mut OdsXmlReader<'_>,
4786    super_tag: &BytesStart<'_>,
4787    empty_tag: bool,
4788) -> Result<TextContent, OdsError> {
4789    let mut stack = ctx.pop_xml_buf();
4790    let mut cellcontent = TextContent::Empty;
4791
4792    // The toplevel element is passed in with the xml_tag.
4793    // It is only created if there are further xml tags in the
4794    // element. If there is only text this is not needed.
4795    let create_toplevel = |ctx: &mut OdsContext, t: Option<String>| -> Result<XmlTag, OdsError> {
4796        // No parent tag on the stack. Create the parent.
4797        let mut toplevel = XmlTag::new(from_utf8(super_tag.name().as_ref())?);
4798        copy_attr2(ctx, toplevel.attrmap_mut(), super_tag)?;
4799        if let Some(t) = t {
4800            toplevel.add_text(t);
4801        }
4802        Ok(toplevel)
4803    };
4804
4805    if !empty_tag {
4806        let mut buf = ctx.pop_buf();
4807        loop {
4808            let evt = xml.read_event_into(&mut buf)?;
4809            if cfg!(feature = "dump_xml") {
4810                println!(" read_xml {:?}", evt);
4811            }
4812            match &evt {
4813                Event::Start(xml_tag) => {
4814                    match cellcontent {
4815                        TextContent::Empty => {
4816                            stack.push(create_toplevel(ctx, None)?);
4817                        }
4818                        TextContent::Text(old_txt) => {
4819                            stack.push(create_toplevel(ctx, Some(old_txt))?);
4820                        }
4821                        TextContent::Xml(parent) => {
4822                            stack.push(parent);
4823                        }
4824                        TextContent::XmlVec(_) => {
4825                            unreachable!()
4826                        }
4827                    }
4828
4829                    // Set the new tag.
4830                    let mut new_tag = XmlTag::new(from_utf8(xml_tag.name().as_ref())?);
4831                    copy_attr2(ctx, new_tag.attrmap_mut(), xml_tag)?;
4832                    cellcontent = TextContent::Xml(new_tag)
4833                }
4834                Event::Empty(xml_tag) => {
4835                    match cellcontent {
4836                        TextContent::Empty => {
4837                            stack.push(create_toplevel(ctx, None)?);
4838                        }
4839                        TextContent::Text(txt) => {
4840                            stack.push(create_toplevel(ctx, Some(txt))?);
4841                        }
4842                        TextContent::Xml(parent) => {
4843                            stack.push(parent);
4844                        }
4845                        TextContent::XmlVec(_) => {
4846                            unreachable!()
4847                        }
4848                    }
4849                    if let Some(mut parent) = stack.pop() {
4850                        // Create the tag and append it immediately to the parent.
4851                        let mut emptytag = XmlTag::new(from_utf8(xml_tag.name().as_ref())?);
4852                        copy_attr2(ctx, emptytag.attrmap_mut(), xml_tag)?;
4853                        parent.add_tag(emptytag);
4854
4855                        cellcontent = TextContent::Xml(parent);
4856                    } else {
4857                        unreachable!()
4858                    }
4859                }
4860                Event::GeneralRef(xml_ref) => {
4861                    let v = entity(xml_ref)?;
4862
4863                    cellcontent = match cellcontent {
4864                        TextContent::Empty => {
4865                            // Fresh plain text string.
4866                            TextContent::Text(v.to_string())
4867                        }
4868                        TextContent::Text(mut old_txt) => {
4869                            // We have a previous plain text string. Append to it.
4870                            old_txt.push_str(&v);
4871                            TextContent::Text(old_txt)
4872                        }
4873                        TextContent::Xml(mut xml) => {
4874                            // There is already a tag. Append the text to its children.
4875                            xml.add_text(v);
4876                            TextContent::Xml(xml)
4877                        }
4878                        TextContent::XmlVec(_) => {
4879                            unreachable!()
4880                        }
4881                    };
4882                }
4883                Event::Text(xml_text) => {
4884                    let v = xml_text.decode()?;
4885
4886                    cellcontent = match cellcontent {
4887                        TextContent::Empty => {
4888                            // Fresh plain text string.
4889                            TextContent::Text(v.to_string())
4890                        }
4891                        TextContent::Text(mut old_txt) => {
4892                            // We have a previous plain text string. Append to it.
4893                            old_txt.push_str(&v);
4894                            TextContent::Text(old_txt)
4895                        }
4896                        TextContent::Xml(mut xml) => {
4897                            // There is already a tag. Append the text to its children.
4898                            xml.add_text(v);
4899                            TextContent::Xml(xml)
4900                        }
4901                        TextContent::XmlVec(_) => {
4902                            unreachable!()
4903                        }
4904                    };
4905                }
4906                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
4907                    if !stack.is_empty() {
4908                        return Err(OdsError::Ods(format!(
4909                            "XML corrupted. Endtag {} occured before all elements are closed: {:?}",
4910                            from_utf8(super_tag.name().as_ref())?,
4911                            stack
4912                        )));
4913                    }
4914                    break;
4915                }
4916                Event::End(xml_tag) => {
4917                    cellcontent = match cellcontent {
4918                        TextContent::Empty | TextContent::Text(_) => {
4919                            return Err(OdsError::Ods(format!(
4920                                "XML corrupted. Endtag {} occured without start tag",
4921                                from_utf8(xml_tag.name().as_ref())?
4922                            )));
4923                        }
4924                        TextContent::Xml(tag) => {
4925                            if let Some(mut parent) = stack.pop() {
4926                                parent.add_tag(tag);
4927                                TextContent::Xml(parent)
4928                            } else {
4929                                return Err(OdsError::Ods(format!(
4930                                    "XML corrupted. Endtag {} occured without start tag",
4931                                    from_utf8(xml_tag.name().as_ref())?
4932                                )));
4933                            }
4934                        }
4935                        TextContent::XmlVec(_) => {
4936                            unreachable!()
4937                        }
4938                    }
4939                }
4940
4941                Event::Eof => {
4942                    break;
4943                }
4944
4945                _ => {
4946                    unused_event("read_text_or_tag", &evt)?;
4947                }
4948            }
4949        }
4950        ctx.push_buf(buf);
4951    }
4952
4953    ctx.push_xml_buf(stack);
4954
4955    Ok(cellcontent)
4956}
4957
4958/// Read simple text content.
4959/// Fail on any tag other than the end-tag to the supertag.
4960fn read_text<T, E>(
4961    ctx: &mut OdsContext,
4962    xml: &mut OdsXmlReader<'_>,
4963    super_tag: &BytesStart<'_>,
4964    empty_tag: bool,
4965    parse: fn(&[u8]) -> Result<T, E>,
4966) -> Result<Option<T>, OdsError>
4967where
4968    OdsError: From<E>,
4969{
4970    if empty_tag {
4971        Ok(None)
4972    } else {
4973        let mut result_buf = ctx.pop_buf();
4974        let mut buf = ctx.pop_buf();
4975        loop {
4976            let evt = xml.read_event_into(&mut buf)?;
4977            if cfg!(feature = "dump_xml") {
4978                println!(" read_text {:?}", evt);
4979            }
4980            match &evt {
4981                Event::GeneralRef(xml_ref) => {
4982                    result_buf.extend_from_slice(entity(xml_ref)?.as_bytes());
4983                }
4984                Event::Text(xml_text) => {
4985                    result_buf.extend_from_slice(xml_text.as_ref());
4986                }
4987                Event::End(xml_tag) if xml_tag.name() == super_tag.name() => {
4988                    break;
4989                }
4990                Event::Empty(xml_tag) | Event::Start(xml_tag) => {
4991                    return Err(OdsError::Ods(from_utf8(xml_tag.as_ref())?.to_string()));
4992                }
4993                Event::End(xml_tag) => {
4994                    return Err(OdsError::Ods(from_utf8(xml_tag.as_ref())?.to_string()));
4995                }
4996                Event::Eof => {
4997                    break;
4998                }
4999                _ => {
5000                    unused_event("read_text", &evt)?;
5001                }
5002            }
5003        }
5004        ctx.push_buf(buf);
5005
5006        let result = parse(&result_buf)?;
5007        ctx.push_buf(result_buf);
5008
5009        Ok(Some(result))
5010    }
5011}
5012
5013fn entity(xml_ref: &BytesRef) -> Result<Cow<'static, str>, quick_xml::Error> {
5014    if let Some(cc) = xml_ref.resolve_char_ref()? {
5015        Ok(Cow::Owned(format!("{}", cc)))
5016    } else {
5017        let xml_ref = xml_ref.decode()?;
5018        match xml_ref.as_ref() {
5019            "lt" => Ok(Cow::Borrowed("<")),
5020            "gt" => Ok(Cow::Borrowed(">")),
5021            "amp" => Ok(Cow::Borrowed("&")),
5022            "apos" => Ok(Cow::Borrowed("'")),
5023            "quot" => Ok(Cow::Borrowed("\"")),
5024            other => Ok(Cow::Owned(format!("&{};", other))),
5025        }
5026    }
5027}
5028
5029#[inline(always)]
5030fn unused_attr(func: &str, tag: &[u8], attr: &Attribute<'_>) -> Result<(), OdsError> {
5031    if cfg!(feature = "dump_unused") {
5032        let tag = from_utf8(tag)?;
5033        let key = from_utf8(attr.key.as_ref())?;
5034        let value = from_utf8(attr.value.as_ref())?;
5035        println!("unused attr: {} '{}' ({}:{})", func, tag, key, value);
5036    }
5037    Ok(())
5038}
5039
5040#[inline(always)]
5041fn unused_event(func: &str, evt: &Event<'_>) -> Result<(), OdsError> {
5042    if cfg!(feature = "dump_unused") {
5043        match &evt {
5044            Event::Text(xml_text) => {
5045                if !xml_text.decode()?.trim().is_empty() {
5046                    println!("unused text: {} ({:?})", func, evt);
5047                }
5048            }
5049            _ => {
5050                println!("unused event: {} ({:?})", func, evt);
5051            }
5052        }
5053    }
5054    Ok(())
5055}