sheetkit_core/workbook/
mod.rs1use std::collections::{HashMap, HashSet};
8use std::io::{Read as _, Write as _};
9use std::path::Path;
10
11use serde::Serialize;
12use sheetkit_xml::chart::ChartSpace;
13use sheetkit_xml::comments::Comments;
14use sheetkit_xml::content_types::{
15 mime_types, ContentTypeDefault, ContentTypeOverride, ContentTypes,
16};
17use sheetkit_xml::drawing::{MarkerType, WsDr};
18use sheetkit_xml::relationships::{self, rel_types, Relationship, Relationships};
19use sheetkit_xml::shared_strings::Sst;
20use sheetkit_xml::styles::StyleSheet;
21use sheetkit_xml::workbook::{WorkbookProtection, WorkbookXml};
22use sheetkit_xml::worksheet::{Cell, CellFormula, CellTypeTag, DrawingRef, Row, WorksheetXml};
23use zip::write::SimpleFileOptions;
24use zip::CompressionMethod;
25
26use crate::cell::CellValue;
27use crate::cell_ref_shift::shift_cell_references_in_text;
28use crate::chart::ChartConfig;
29use crate::comment::CommentConfig;
30use crate::conditional::ConditionalFormatRule;
31use crate::error::{Error, Result};
32use crate::image::ImageConfig;
33use crate::pivot::{PivotTableConfig, PivotTableInfo};
34use crate::protection::WorkbookProtectionConfig;
35use crate::sst::SharedStringTable;
36use crate::utils::cell_ref::{cell_name_to_coordinates, column_name_to_number};
37use crate::utils::constants::MAX_CELL_CHARS;
38use crate::validation::DataValidationConfig;
39use crate::workbook_paths::{
40 default_relationships, relationship_part_path, relative_relationship_target,
41 resolve_relationship_target,
42};
43
44mod cell_ops;
45mod data;
46mod drawing;
47mod features;
48mod io;
49mod sheet_ops;
50
51const XML_DECLARATION: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>"#;
53
54pub struct Workbook {
56 content_types: ContentTypes,
57 package_rels: Relationships,
58 workbook_xml: WorkbookXml,
59 workbook_rels: Relationships,
60 worksheets: Vec<(String, WorksheetXml)>,
61 stylesheet: StyleSheet,
62 sst_runtime: SharedStringTable,
63 sheet_comments: Vec<Option<Comments>>,
65 charts: Vec<(String, ChartSpace)>,
67 raw_charts: Vec<(String, Vec<u8>)>,
69 drawings: Vec<(String, WsDr)>,
71 images: Vec<(String, Vec<u8>)>,
73 #[allow(dead_code)]
75 worksheet_drawings: HashMap<usize, usize>,
76 worksheet_rels: HashMap<usize, Relationships>,
78 drawing_rels: HashMap<usize, Relationships>,
80 core_properties: Option<sheetkit_xml::doc_props::CoreProperties>,
82 app_properties: Option<sheetkit_xml::doc_props::ExtendedProperties>,
84 custom_properties: Option<sheetkit_xml::doc_props::CustomProperties>,
86 pivot_tables: Vec<(String, sheetkit_xml::pivot_table::PivotTableDefinition)>,
88 pivot_cache_defs: Vec<(String, sheetkit_xml::pivot_cache::PivotCacheDefinition)>,
90 pivot_cache_records: Vec<(String, sheetkit_xml::pivot_cache::PivotCacheRecords)>,
92 theme_xml: Option<Vec<u8>>,
94 theme_colors: sheetkit_xml::theme::ThemeColors,
96 sheet_sparklines: Vec<Vec<crate::sparkline::SparklineConfig>>,
98 sheet_vml: Vec<Option<Vec<u8>>>,
101 sheet_name_index: HashMap<String, usize>,
104}
105
106impl Workbook {
107 pub(crate) fn sheet_index(&self, sheet: &str) -> Result<usize> {
109 self.sheet_name_index
110 .get(sheet)
111 .copied()
112 .ok_or_else(|| Error::SheetNotFound {
113 name: sheet.to_string(),
114 })
115 }
116
117 pub(crate) fn worksheet_mut(&mut self, sheet: &str) -> Result<&mut WorksheetXml> {
119 let idx = self.sheet_index(sheet)?;
120 Ok(&mut self.worksheets[idx].1)
121 }
122
123 pub(crate) fn worksheet_ref(&self, sheet: &str) -> Result<&WorksheetXml> {
125 let idx = self.sheet_index(sheet)?;
126 Ok(&self.worksheets[idx].1)
127 }
128
129 pub fn worksheet_xml_ref(&self, sheet: &str) -> Result<&WorksheetXml> {
131 self.worksheet_ref(sheet)
132 }
133
134 pub fn sst_ref(&self) -> &SharedStringTable {
136 &self.sst_runtime
137 }
138
139 pub(crate) fn rebuild_sheet_index(&mut self) {
142 self.sheet_name_index.clear();
143 for (i, (name, _)) in self.worksheets.iter().enumerate() {
144 self.sheet_name_index.insert(name.clone(), i);
145 }
146 }
147
148 pub(crate) fn sheet_part_path(&self, sheet_idx: usize) -> String {
151 if let Some(sheet_entry) = self.workbook_xml.sheets.sheets.get(sheet_idx) {
152 if let Some(rel) = self
153 .workbook_rels
154 .relationships
155 .iter()
156 .find(|r| r.id == sheet_entry.r_id && r.rel_type == rel_types::WORKSHEET)
157 {
158 return resolve_relationship_target("xl/workbook.xml", &rel.target);
159 }
160 }
161 format!("xl/worksheets/sheet{}.xml", sheet_idx + 1)
162 }
163}