use std::io::Read; use ironcalc_base::types::{DefinedName, SheetState}; use roxmltree::Node; use crate::error::XlsxError; use super::{ util::get_attribute, worksheets::{Sheet, WorkbookXML}, }; pub(super) fn load_workbook( archive: &mut zip::read::ZipArchive, ) -> Result { let mut file = archive.by_name("xl/workbook.xml")?; let mut text = String::new(); file.read_to_string(&mut text)?; let doc = roxmltree::Document::parse(&text)?; let mut defined_names = Vec::new(); let mut sheets = Vec::new(); // Get the sheets let sheet_nodes: Vec = doc .descendants() .filter(|n| n.has_tag_name("sheet")) .collect(); for sheet in sheet_nodes { let name = get_attribute(&sheet, "name")?.to_string(); let sheet_id = get_attribute(&sheet, "sheetId")?.to_string(); let sheet_id = sheet_id.parse::()?; let id = get_attribute( &sheet, ( "http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id", ), )? .to_string(); let state = match sheet.attribute("state") { Some("visible") | None => SheetState::Visible, Some("hidden") => SheetState::Hidden, Some("veryHidden") => SheetState::VeryHidden, Some(state) => return Err(XlsxError::Xml(format!("Unknown sheet state: {}", state))), }; sheets.push(Sheet { name, sheet_id, id, state, }); } // Get the defined names let name_nodes: Vec = doc .descendants() .filter(|n| n.has_tag_name("definedName")) .collect(); for node in name_nodes { let name = get_attribute(&node, "name")?.to_string(); let formula = node.text().unwrap_or("").to_string(); // NOTE: In Excel the `localSheetId` is just the index of the worksheet and unrelated to the sheetId let sheet_id = match node.attribute("localSheetId") { Some(s) => { let index = s.parse::()?; Some(sheets[index].sheet_id) } None => None, }; defined_names.push(DefinedName { name, formula, sheet_id, }) } // read the relationships file Ok(WorkbookXML { worksheets: sheets, defined_names, }) }