1use std::collections::HashMap;
7use std::io::Cursor;
8use std::path::Path;
9
10use quick_xml::Reader;
11use quick_xml::events::Event;
12
13use crate::constants::{
14 CONTENT_TYPE_SPREADSHEET_ML_WORKSHEET, MAX_COLUMNS, MAX_FIELD_LENGTH, SOURCE_RELATIONSHIP,
15 SOURCE_RELATIONSHIP_IMAGE, SOURCE_RELATIONSHIP_SHARED_STRINGS, SOURCE_RELATIONSHIP_WORKSHEET,
16 TOTAL_ROWS,
17};
18use crate::crypt::gen_iso_passwd_hash;
19use crate::errors::Result;
20use crate::errors::{
21 ErrGroupSheets, ErrImgExt, ErrParameterInvalid, ErrSheetIdx, ErrSheetNotExist,
22 ErrUnprotectSheet, ErrUnprotectSheetPassword, ErrWorkbook, new_field_length_error,
23};
24use crate::file::{File, namespace_strict_to_transitional};
25use crate::lib_util::{
26 cell_name_to_coordinates, coordinates_to_range_ref, count_utf16_string, in_str_slice,
27 range_ref_to_coordinates, sort_coordinates,
28};
29use crate::xml::content_types::{XlsxContentTypeEntry, XlsxDefault, XlsxOverride};
30use crate::xml::workbook::{XlsxBookViews, XlsxSheet, XlsxWorkBookView};
31pub use crate::xml::worksheet::{Panes, Selection, SheetProtectionOptions};
32use crate::xml::worksheet::{
33 XlsxBreaks, XlsxBrk, XlsxColBreaks, XlsxDimension, XlsxHeaderFooter, XlsxIgnoredError,
34 XlsxIgnoredErrors, XlsxPageMargins, XlsxPageSetUp, XlsxPane, XlsxPicture, XlsxPrintOptions,
35 XlsxRowBreaks, XlsxSelection, XlsxSheetFormatPr, XlsxSheetPr, XlsxSheetProtection,
36 XlsxSheetView, XlsxSheetViews, XlsxWorksheet,
37};
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum IgnoredErrorsType {
42 EvalError = 0,
43 TwoDigitTextYear = 1,
44 NumberStoredAsText = 2,
45 Formula = 3,
46 FormulaRange = 4,
47 UnlockedFormula = 5,
48 EmptyCellReference = 6,
49 ListDataValidation = 7,
50 CalculatedColumn = 8,
51}
52
53#[derive(Debug, Default, Clone, PartialEq)]
55pub struct SheetPropsOptions {
56 pub code_name: Option<String>,
57 pub enable_format_conditions_calculation: Option<bool>,
58 pub published: Option<bool>,
59 pub auto_page_breaks: Option<bool>,
60 pub fit_to_page: Option<bool>,
61 pub tab_color_indexed: Option<i64>,
62 pub tab_color_rgb: Option<String>,
63 pub tab_color_theme: Option<i64>,
64 pub tab_color_tint: Option<f64>,
65 pub outline_pr_apply_styles: Option<bool>,
66 pub outline_pr_summary_below: Option<bool>,
67 pub outline_pr_summary_right: Option<bool>,
68 pub outline_pr_show_outline_symbols: Option<bool>,
69 pub base_col_width: Option<u8>,
70 pub default_col_width: Option<f64>,
71 pub default_row_height: Option<f64>,
72 pub custom_height: Option<bool>,
73 pub zero_height: Option<bool>,
74 pub thick_top: Option<bool>,
75 pub thick_bottom: Option<bool>,
76}
77
78#[derive(Debug, Default, Clone, PartialEq)]
80pub struct SheetViewOptions {
81 pub default_grid_color: Option<bool>,
82 pub right_to_left: Option<bool>,
83 pub show_formulas: Option<bool>,
84 pub show_grid_lines: Option<bool>,
85 pub show_outline_symbols: Option<bool>,
86 pub show_row_col_headers: Option<bool>,
87 pub show_ruler: Option<bool>,
88 pub show_white_space: Option<bool>,
89 pub show_zeros: Option<bool>,
90 pub tab_selected: Option<bool>,
91 pub top_left_cell: Option<String>,
92 pub view: Option<String>,
93 pub window_protection: Option<bool>,
94 pub zoom_scale: Option<f64>,
95}
96
97#[derive(Debug, Default, Clone, PartialEq)]
99pub struct PageLayoutOptions {
100 pub black_and_white: Option<bool>,
101 pub cell_comments: Option<String>,
102 pub copies: Option<i64>,
103 pub draft: Option<bool>,
104 pub errors: Option<String>,
105 pub first_page_number: Option<String>,
106 pub fit_to_height: Option<i64>,
107 pub fit_to_width: Option<i64>,
108 pub horizontal_dpi: Option<String>,
109 pub orientation: Option<String>,
110 pub page_order: Option<String>,
111 pub paper_size: Option<i64>,
112 pub scale: Option<i64>,
113 pub use_first_page_number: Option<bool>,
114 pub use_printer_defaults: Option<bool>,
115 pub vertical_dpi: Option<String>,
116}
117
118#[derive(Debug, Default, Clone, PartialEq)]
120pub struct PageMarginsOptions {
121 pub left: Option<f64>,
122 pub right: Option<f64>,
123 pub top: Option<f64>,
124 pub bottom: Option<f64>,
125 pub header: Option<f64>,
126 pub footer: Option<f64>,
127 pub horizontally: Option<bool>,
128 pub vertically: Option<bool>,
129}
130
131#[derive(Debug, Default, Clone, PartialEq)]
133pub struct HeaderFooterOptions {
134 pub different_first: Option<bool>,
135 pub different_odd_even: Option<bool>,
136 pub scale_with_doc: Option<bool>,
137 pub align_with_margins: Option<bool>,
138 pub odd_header: Option<String>,
139 pub odd_footer: Option<String>,
140 pub even_header: Option<String>,
141 pub even_footer: Option<String>,
142 pub first_header: Option<String>,
143 pub first_footer: Option<String>,
144}
145
146impl File {
151 pub fn get_sheet_list(&self) -> Vec<String> {
153 let wb = self.workbook_reader().ok();
154 let mut list = Vec::new();
155 if let Some(wb) = wb {
156 for sheet in &wb.sheets.sheet {
157 if let Some(name) = &sheet.name {
158 list.push(name.clone());
159 }
160 }
161 }
162 list
163 }
164
165 pub fn new_sheet(&self, name: &str) -> Result<i32> {
167 crate::excelize::check_sheet_name(name)?;
168 if let Ok(index) = self.get_sheet_index(name) {
169 return Ok(index);
170 }
171
172 let mut wb = self.workbook_reader()?;
173 let mut rels = self
174 .rels_reader(&self.get_workbook_rels_path())?
175 .unwrap_or_default();
176
177 let mut max_sheet_id = 0i64;
179 let mut max_rid = 0i32;
180 for sheet in &wb.sheets.sheet {
181 if let Some(id) = sheet.sheet_id {
182 if id > max_sheet_id {
183 max_sheet_id = id;
184 }
185 }
186 if let Some(rid) = &sheet.id {
187 if let Ok(n) = rid.trim_start_matches("rId").parse::<i32>() {
188 if n > max_rid {
189 max_rid = n;
190 }
191 }
192 }
193 }
194 for rel in &rels.relationships {
195 if let Ok(n) = rel.id.trim_start_matches("rId").parse::<i32>() {
196 if n > max_rid {
197 max_rid = n;
198 }
199 }
200 }
201 max_sheet_id += 1;
202 max_rid += 1;
203
204 let new_rid = format!("rId{max_rid}");
205 let sheet_xml = format!("xl/worksheets/sheet{max_sheet_id}.xml");
206
207 wb.sheets.sheet.push(XlsxSheet {
208 name: Some(name.to_string()),
209 sheet_id: Some(max_sheet_id),
210 id: Some(new_rid.clone()),
211 plain_id: None,
212 state: None,
213 });
214
215 rels.relationships
216 .push(crate::xml::workbook::XlsxRelationship {
217 id: new_rid,
218 r#type: SOURCE_RELATIONSHIP_WORKSHEET.to_string(),
219 target: format!("worksheets/sheet{max_sheet_id}.xml"),
220 target_mode: None,
221 });
222
223 let ws = new_worksheet();
225 self.sheet.insert(sheet_xml.clone(), ws.clone());
226 self.pkg.insert(sheet_xml.clone(), worksheet_bytes(&ws));
227 if let Some(attrs) =
230 crate::file::extract_root_namespace_attributes(crate::templates::TEMPLATE_SHEET.as_bytes())
231 {
232 self.xml_attr.insert(sheet_xml.clone(), attrs);
233 }
234 self.checked.insert(sheet_xml.clone(), true);
235
236 let mut ct = self.content_types_reader()?;
238 ct.entries
239 .push(XlsxContentTypeEntry::Override(XlsxOverride {
240 part_name: format!("/{sheet_xml}"),
241 content_type: CONTENT_TYPE_SPREADSHEET_ML_WORKSHEET.to_string(),
242 }));
243
244 ensure_shared_strings_rel(&mut rels);
246
247 *self.workbook.lock().unwrap() = Some(wb);
248 self.relationships
249 .insert(self.get_workbook_rels_path(), rels);
250 *self.content_types.lock().unwrap() = Some(ct);
251 self.sheet_map
252 .lock()
253 .unwrap()
254 .insert(name.to_string(), sheet_xml);
255
256 let mut count = self.sheet_count.lock().unwrap();
257 *count += 1;
258 Ok(max_sheet_id as i32)
259 }
260
261 pub fn get_sheet_name(&self, index: i32) -> Result<String> {
263 let wb = self.workbook_reader()?;
264 if index < 1 || index as usize > wb.sheets.sheet.len() {
265 return Err(Box::new(ErrSheetIdx));
266 }
267 Ok(wb.sheets.sheet[index as usize - 1]
268 .name
269 .clone()
270 .unwrap_or_default())
271 }
272
273 pub fn get_sheet_index(&self, name: &str) -> Result<i32> {
275 let wb = self.workbook_reader()?;
276 for (i, sheet) in wb.sheets.sheet.iter().enumerate() {
277 if sheet
278 .name
279 .as_deref()
280 .map(|n| n.eq_ignore_ascii_case(name))
281 .unwrap_or(false)
282 {
283 return Ok(i as i32 + 1);
284 }
285 }
286 Err(Box::new(ErrSheetNotExist {
287 sheet_name: name.to_string(),
288 }))
289 }
290
291 pub fn delete_sheet(&self, name: &str) -> Result<()> {
293 crate::excelize::check_sheet_name(name)?;
294 let index = self.get_sheet_index(name)?;
295 if self.get_sheet_list().len() <= 1 {
296 return Err(Box::new(ErrWorkbook));
297 }
298 let path = self
299 .get_sheet_xml_path(name)
300 .ok_or_else(|| ErrSheetNotExist {
301 sheet_name: name.to_string(),
302 })?;
303
304 self.clear_calc_cache();
305 let active_index = self.get_active_sheet_index()?;
306 let sheet_id = self.get_sheet_id(name);
307
308 let mut wb = self.workbook_reader()?;
309 wb.sheets.sheet.remove(index as usize - 1);
310
311 let rel_path = self.get_workbook_rels_path();
312 let mut rels = self.rels_reader(&rel_path)?.unwrap_or_default();
313 rels.relationships.retain(|r| {
314 let target = r.target.to_lowercase();
315 !target.contains(&format!("/{path}").to_lowercase()) && target != path.to_lowercase()
316 });
317
318 let sheet_rels = format!(
319 "xl/worksheets/_rels/{}.rels",
320 path.trim_start_matches("xl/worksheets/")
321 );
322
323 *self.workbook.lock().unwrap() = Some(wb);
324 self.relationships.insert(rel_path, rels);
325 self.sheet.remove(&path);
326 self.pkg.remove(&path);
327 self.checked.remove(&path);
328 self.relationships.remove(&sheet_rels);
329 self.pkg.remove(&sheet_rels);
330 self.sheet_map.lock().unwrap().remove(name);
331
332 self.remove_content_types_part(CONTENT_TYPE_SPREADSHEET_ML_WORKSHEET, &format!("/{path}"))?;
333 if sheet_id > 0 {
334 let _ = self.delete_calc_chain(sheet_id, "");
335 }
336
337 let mut new_active = active_index;
338 if active_index == index {
339 new_active = 1;
340 } else if active_index > index {
341 new_active = active_index - 1;
342 }
343 self.set_active_sheet(new_active)?;
344
345 let mut count = self.sheet_count.lock().unwrap();
346 *count -= 1;
347 Ok(())
348 }
349
350 pub fn get_active_sheet_index(&self) -> Result<i32> {
352 let wb = self.workbook_reader()?;
353 let views = wb.book_views.as_ref().and_then(|v| v.workbook_view.first());
354 if let Some(view) = views {
355 return Ok(view.active_tab.unwrap_or(0) as i32 + 1);
356 }
357 Ok(1)
358 }
359
360 pub fn set_active_sheet(&self, index: i32) -> Result<()> {
362 let mut wb = self.workbook_reader()?;
363 if index < 1 || index as usize > wb.sheets.sheet.len() {
364 return Err(Box::new(ErrSheetIdx));
365 }
366 if wb.book_views.is_none() {
367 wb.book_views = Some(XlsxBookViews::default());
368 }
369 let views = wb.book_views.as_mut().unwrap();
370 if views.workbook_view.is_empty() {
371 views.workbook_view.push(XlsxWorkBookView::default());
372 }
373 views.workbook_view[0].active_tab = Some(index as i64 - 1);
374 *self.workbook.lock().unwrap() = Some(wb);
375
376 let target = index as usize;
377 for (idx, name) in self.get_sheet_list().iter().enumerate() {
378 let Ok(mut ws) = self.work_sheet_reader(name) else {
379 continue;
380 };
381 if ws.sheet_views.is_none() {
382 ws.sheet_views = Some(XlsxSheetViews {
383 sheet_view: vec![XlsxSheetView::default()],
384 });
385 }
386 let sv = ws.sheet_views.as_mut().unwrap();
387 if sv.sheet_view.is_empty() {
388 sv.sheet_view.push(XlsxSheetView::default());
389 }
390 sv.sheet_view[0].tab_selected = Some(idx + 1 == target);
391 if let Some(path) = self.get_sheet_xml_path(name) {
392 self.sheet.insert(path, ws);
393 }
394 }
395 Ok(())
396 }
397
398 pub fn set_sheet_props(&self, sheet: &str, opts: &SheetPropsOptions) -> Result<()> {
400 let path = self
401 .get_sheet_xml_path(sheet)
402 .ok_or_else(|| ErrSheetNotExist {
403 sheet_name: sheet.to_string(),
404 })?;
405 let mut ws = self.work_sheet_reader(sheet)?;
406 if ws.sheet_pr.is_none() {
407 ws.sheet_pr = Some(XlsxSheetPr::default());
408 }
409 let pr = ws.sheet_pr.as_mut().unwrap();
410 if opts.code_name.is_some() {
411 pr.code_name = opts.code_name.clone();
412 }
413 if opts.enable_format_conditions_calculation.is_some() {
414 pr.enable_format_conditions_calculation = opts.enable_format_conditions_calculation;
415 }
416 if opts.published.is_some() {
417 pr.published = opts.published;
418 }
419 if let Some(indexed) = opts.tab_color_indexed {
420 if pr.tab_color.is_none() {
421 pr.tab_color = Some(crate::xml::common::XlsxColor::default());
422 }
423 pr.tab_color.as_mut().unwrap().indexed = Some(indexed);
424 }
425 if let Some(ref rgb) = opts.tab_color_rgb {
426 if pr.tab_color.is_none() {
427 pr.tab_color = Some(crate::xml::common::XlsxColor::default());
428 }
429 pr.tab_color.as_mut().unwrap().rgb = Some(rgb.clone());
430 }
431 if let Some(theme) = opts.tab_color_theme {
432 if pr.tab_color.is_none() {
433 pr.tab_color = Some(crate::xml::common::XlsxColor::default());
434 }
435 pr.tab_color.as_mut().unwrap().theme = Some(theme);
436 }
437 if let Some(tint) = opts.tab_color_tint {
438 if pr.tab_color.is_none() {
439 pr.tab_color = Some(crate::xml::common::XlsxColor::default());
440 }
441 pr.tab_color.as_mut().unwrap().tint = Some(tint);
442 }
443 let outline_needed = opts.outline_pr_apply_styles.is_some()
444 || opts.outline_pr_summary_below.is_some()
445 || opts.outline_pr_summary_right.is_some()
446 || opts.outline_pr_show_outline_symbols.is_some();
447 if outline_needed {
448 if pr.outline_pr.is_none() {
449 pr.outline_pr = Some(crate::xml::worksheet::XlsxOutlinePr::default());
450 }
451 let outline = pr.outline_pr.as_mut().unwrap();
452 if opts.outline_pr_apply_styles.is_some() {
453 outline.apply_styles = opts.outline_pr_apply_styles;
454 }
455 if opts.outline_pr_summary_below.is_some() {
456 outline.summary_below = opts.outline_pr_summary_below;
457 }
458 if opts.outline_pr_summary_right.is_some() {
459 outline.summary_right = opts.outline_pr_summary_right;
460 }
461 if opts.outline_pr_show_outline_symbols.is_some() {
462 outline.show_outline_symbols = opts.outline_pr_show_outline_symbols;
463 }
464 }
465 if opts.auto_page_breaks.is_some() || opts.fit_to_page.is_some() {
466 if pr.page_set_up_pr.is_none() {
467 pr.page_set_up_pr = Some(crate::xml::worksheet::XlsxPageSetUpPr::default());
468 }
469 let pup = pr.page_set_up_pr.as_mut().unwrap();
470 if opts.auto_page_breaks.is_some() {
471 pup.auto_page_breaks = opts.auto_page_breaks;
472 }
473 if opts.fit_to_page.is_some() {
474 pup.fit_to_page = opts.fit_to_page;
475 }
476 }
477 if ws.sheet_format_pr.is_none() {
478 ws.sheet_format_pr = Some(XlsxSheetFormatPr {
479 default_row_height: 15.0,
480 ..Default::default()
481 });
482 }
483 let sfp = ws.sheet_format_pr.as_mut().unwrap();
484 if let Some(v) = opts.base_col_width {
485 sfp.base_col_width = Some(v);
486 }
487 if let Some(v) = opts.default_col_width {
488 sfp.default_col_width = Some(v);
489 }
490 if let Some(v) = opts.default_row_height {
491 sfp.default_row_height = v;
492 }
493 if let Some(v) = opts.custom_height {
494 sfp.custom_height = Some(v);
495 }
496 if let Some(v) = opts.zero_height {
497 sfp.zero_height = Some(v);
498 }
499 if let Some(v) = opts.thick_top {
500 sfp.thick_top = Some(v);
501 }
502 if let Some(v) = opts.thick_bottom {
503 sfp.thick_bottom = Some(v);
504 }
505 self.sheet.insert(path, ws);
506 Ok(())
507 }
508
509 pub fn get_sheet_props(&self, sheet: &str) -> Result<SheetPropsOptions> {
511 let ws = self.work_sheet_reader(sheet)?;
512 let mut opts = SheetPropsOptions {
513 enable_format_conditions_calculation: Some(true),
514 published: Some(true),
515 auto_page_breaks: Some(true),
516 outline_pr_summary_below: Some(true),
517 base_col_width: Some(8),
518 ..Default::default()
519 };
520 if let Some(pr) = ws.sheet_pr {
521 opts.code_name = pr.code_name;
522 if pr.enable_format_conditions_calculation.is_some() {
523 opts.enable_format_conditions_calculation = pr.enable_format_conditions_calculation;
524 }
525 if pr.published.is_some() {
526 opts.published = pr.published;
527 }
528 if let Some(tc) = pr.tab_color {
529 opts.tab_color_indexed = tc.indexed;
530 opts.tab_color_rgb = tc.rgb;
531 opts.tab_color_theme = tc.theme;
532 opts.tab_color_tint = tc.tint;
533 }
534 if let Some(o) = pr.outline_pr {
535 opts.outline_pr_apply_styles = o.apply_styles;
536 opts.outline_pr_summary_below = o.summary_below;
537 opts.outline_pr_summary_right = o.summary_right;
538 opts.outline_pr_show_outline_symbols = o.show_outline_symbols;
539 }
540 if let Some(pup) = pr.page_set_up_pr {
541 opts.auto_page_breaks = pup.auto_page_breaks;
542 opts.fit_to_page = pup.fit_to_page;
543 }
544 }
545 if let Some(sfp) = ws.sheet_format_pr {
546 opts.base_col_width = sfp.base_col_width.or(Some(8));
547 opts.default_col_width = sfp.default_col_width;
548 opts.default_row_height = Some(sfp.default_row_height);
549 opts.custom_height = sfp.custom_height;
550 opts.zero_height = sfp.zero_height;
551 opts.thick_top = sfp.thick_top;
552 opts.thick_bottom = sfp.thick_bottom;
553 }
554 Ok(opts)
555 }
556
557 pub fn set_sheet_view(
560 &self,
561 sheet: &str,
562 view_index: i32,
563 opts: &SheetViewOptions,
564 ) -> Result<()> {
565 let path = self
566 .get_sheet_xml_path(sheet)
567 .ok_or_else(|| ErrSheetNotExist {
568 sheet_name: sheet.to_string(),
569 })?;
570 let mut ws = self.work_sheet_reader(sheet)?;
571 if ws.sheet_views.is_none() {
572 ws.sheet_views = Some(XlsxSheetViews {
573 sheet_view: vec![XlsxSheetView::default()],
574 });
575 }
576 let views = ws.sheet_views.as_mut().unwrap();
577 let len = views.sheet_view.len();
578 let idx = if view_index < 0 {
579 if view_index < -(len as i32) {
580 return Err(crate::errors::new_view_idx_error(view_index).into());
581 }
582 (len as i32 + view_index) as usize
583 } else if view_index >= len as i32 {
584 return Err(crate::errors::new_view_idx_error(view_index).into());
585 } else {
586 view_index as usize
587 };
588 let view = &mut views.sheet_view[idx];
589 if opts.default_grid_color.is_some() {
590 view.default_grid_color = opts.default_grid_color;
591 }
592 if opts.right_to_left.is_some() {
593 view.right_to_left = opts.right_to_left;
594 }
595 if opts.show_formulas.is_some() {
596 view.show_formulas = opts.show_formulas;
597 }
598 if opts.show_grid_lines.is_some() {
599 view.show_grid_lines = opts.show_grid_lines;
600 }
601 if opts.show_outline_symbols.is_some() {
602 view.show_outline_symbols = opts.show_outline_symbols;
603 }
604 if opts.show_row_col_headers.is_some() {
605 view.show_row_col_headers = opts.show_row_col_headers;
606 }
607 if opts.show_ruler.is_some() {
608 view.show_ruler = opts.show_ruler;
609 }
610 if opts.show_white_space.is_some() {
611 view.show_white_space = opts.show_white_space;
612 }
613 if opts.show_zeros.is_some() {
614 view.show_zeros = opts.show_zeros;
615 }
616 if opts.tab_selected.is_some() {
617 view.tab_selected = opts.tab_selected;
618 }
619 if opts.top_left_cell.is_some() {
620 view.top_left_cell = opts.top_left_cell.clone();
621 }
622 if let Some(ref v) = opts.view {
623 if in_str_slice(&["normal", "pageLayout", "pageBreakPreview"], v, true) != -1 {
624 view.view = opts.view.clone();
625 }
626 }
627 if opts.window_protection.is_some() {
628 view.window_protection = opts.window_protection;
629 }
630 if let Some(zoom) = opts.zoom_scale {
631 if zoom >= 10.0 && zoom <= 400.0 {
632 view.zoom_scale = Some(zoom);
633 }
634 }
635 self.sheet.insert(path, ws);
636 Ok(())
637 }
638
639 pub fn get_sheet_view(&self, sheet: &str, view_index: i32) -> Result<SheetViewOptions> {
642 let ws = self.work_sheet_reader(sheet)?;
643 let views = ws.sheet_views.unwrap_or_else(|| XlsxSheetViews {
644 sheet_view: vec![XlsxSheetView::default()],
645 });
646 let len = views.sheet_view.len();
647 let idx = if view_index < 0 {
648 if view_index < -(len as i32) {
649 return Err(crate::errors::new_view_idx_error(view_index).into());
650 }
651 (len as i32 + view_index) as usize
652 } else if view_index >= len as i32 {
653 return Err(crate::errors::new_view_idx_error(view_index).into());
654 } else {
655 view_index as usize
656 };
657 let view = &views.sheet_view[idx];
658 let mut opts = SheetViewOptions {
659 default_grid_color: Some(true),
660 show_grid_lines: Some(true),
661 show_row_col_headers: Some(true),
662 show_ruler: Some(true),
663 show_zeros: Some(true),
664 view: Some("normal".to_string()),
665 zoom_scale: Some(100.0),
666 ..Default::default()
667 };
668 if let Some(v) = view.default_grid_color {
669 opts.default_grid_color = Some(v);
670 }
671 opts.right_to_left = Some(view.right_to_left.unwrap_or(false));
672 opts.show_formulas = Some(view.show_formulas.unwrap_or(false));
673 if let Some(v) = view.show_grid_lines {
674 opts.show_grid_lines = Some(v);
675 }
676 if let Some(v) = view.show_row_col_headers {
677 opts.show_row_col_headers = Some(v);
678 }
679 if let Some(v) = view.show_ruler {
680 opts.show_ruler = Some(v);
681 }
682 if let Some(v) = view.show_white_space {
683 opts.show_white_space = Some(v);
684 }
685 if let Some(v) = view.show_zeros {
686 opts.show_zeros = Some(v);
687 }
688 opts.show_outline_symbols = Some(view.show_outline_symbols.unwrap_or(false));
689 opts.tab_selected = Some(view.tab_selected.unwrap_or(false));
690 opts.top_left_cell = Some(view.top_left_cell.clone().unwrap_or_default());
691 opts.window_protection = Some(view.window_protection.unwrap_or(false));
692 if let Some(ref v) = view.view {
693 if !v.is_empty() {
694 opts.view = Some(v.clone());
695 }
696 }
697 if let Some(zoom) = view.zoom_scale {
698 if zoom >= 10.0 && zoom <= 400.0 {
699 opts.zoom_scale = Some(zoom);
700 }
701 }
702 Ok(opts)
703 }
704
705 pub fn set_page_layout(&self, sheet: &str, opts: &PageLayoutOptions) -> Result<()> {
707 let path = self
708 .get_sheet_xml_path(sheet)
709 .ok_or_else(|| ErrSheetNotExist {
710 sheet_name: sheet.to_string(),
711 })?;
712 let mut ws = self.work_sheet_reader(sheet)?;
713 if ws.page_setup.is_none() {
714 ws.page_setup = Some(XlsxPageSetUp::default());
715 }
716 let ps = ws.page_setup.as_mut().unwrap();
717 if opts.black_and_white.is_some() {
718 ps.black_and_white = opts.black_and_white;
719 }
720 if opts.cell_comments.is_some() {
721 ps.cell_comments = opts.cell_comments.clone();
722 }
723 if opts.copies.is_some() {
724 ps.copies = opts.copies;
725 }
726 if opts.draft.is_some() {
727 ps.draft = opts.draft;
728 }
729 if opts.errors.is_some() {
730 ps.errors = opts.errors.clone();
731 }
732 if opts.first_page_number.is_some() {
733 ps.first_page_number = opts.first_page_number.clone();
734 }
735 if opts.fit_to_height.is_some() {
736 ps.fit_to_height = opts.fit_to_height;
737 }
738 if opts.fit_to_width.is_some() {
739 ps.fit_to_width = opts.fit_to_width;
740 }
741 if opts.horizontal_dpi.is_some() {
742 ps.horizontal_dpi = opts.horizontal_dpi.clone();
743 }
744 if opts.orientation.is_some() {
745 ps.orientation = opts.orientation.clone();
746 }
747 if opts.page_order.is_some() {
748 ps.page_order = opts.page_order.clone();
749 }
750 if opts.paper_size.is_some() {
751 ps.paper_size = opts.paper_size;
752 }
753 if opts.scale.is_some() {
754 ps.scale = opts.scale;
755 }
756 if opts.use_first_page_number.is_some() {
757 ps.use_first_page_number = opts.use_first_page_number;
758 }
759 if opts.use_printer_defaults.is_some() {
760 ps.use_printer_defaults = opts.use_printer_defaults;
761 }
762 if opts.vertical_dpi.is_some() {
763 ps.vertical_dpi = opts.vertical_dpi.clone();
764 }
765 self.sheet.insert(path, ws);
766 Ok(())
767 }
768
769 pub fn get_page_layout(&self, sheet: &str) -> Result<PageLayoutOptions> {
771 let ws = self.work_sheet_reader(sheet)?;
772 let mut opts = PageLayoutOptions::default();
773 if let Some(ps) = ws.page_setup {
774 opts.black_and_white = ps.black_and_white;
775 opts.cell_comments = ps.cell_comments;
776 opts.copies = ps.copies;
777 opts.draft = ps.draft;
778 opts.errors = ps.errors;
779 opts.first_page_number = ps.first_page_number;
780 opts.fit_to_height = ps.fit_to_height;
781 opts.fit_to_width = ps.fit_to_width;
782 opts.horizontal_dpi = ps.horizontal_dpi;
783 opts.orientation = ps.orientation;
784 opts.page_order = ps.page_order;
785 opts.paper_size = ps.paper_size;
786 opts.scale = ps.scale;
787 opts.use_first_page_number = ps.use_first_page_number;
788 opts.use_printer_defaults = ps.use_printer_defaults;
789 opts.vertical_dpi = ps.vertical_dpi;
790 }
791 Ok(opts)
792 }
793
794 pub fn set_page_margins(&self, sheet: &str, opts: &PageMarginsOptions) -> Result<()> {
796 let path = self
797 .get_sheet_xml_path(sheet)
798 .ok_or_else(|| ErrSheetNotExist {
799 sheet_name: sheet.to_string(),
800 })?;
801 let mut ws = self.work_sheet_reader(sheet)?;
802 let margins_needed = opts.left.is_some()
803 || opts.right.is_some()
804 || opts.top.is_some()
805 || opts.bottom.is_some()
806 || opts.header.is_some()
807 || opts.footer.is_some();
808 if margins_needed {
809 if ws.page_margins.is_none() {
810 ws.page_margins = Some(XlsxPageMargins::default());
811 }
812 let pm = ws.page_margins.as_mut().unwrap();
813 if let Some(v) = opts.left {
814 pm.left = v;
815 }
816 if let Some(v) = opts.right {
817 pm.right = v;
818 }
819 if let Some(v) = opts.top {
820 pm.top = v;
821 }
822 if let Some(v) = opts.bottom {
823 pm.bottom = v;
824 }
825 if let Some(v) = opts.header {
826 pm.header = v;
827 }
828 if let Some(v) = opts.footer {
829 pm.footer = v;
830 }
831 }
832 if opts.horizontally.is_some() || opts.vertically.is_some() {
833 if ws.print_options.is_none() {
834 ws.print_options = Some(XlsxPrintOptions::default());
835 }
836 let po = ws.print_options.as_mut().unwrap();
837 if let Some(v) = opts.horizontally {
838 po.horizontal_centered = Some(v);
839 }
840 if let Some(v) = opts.vertically {
841 po.vertical_centered = Some(v);
842 }
843 }
844 self.sheet.insert(path, ws);
845 Ok(())
846 }
847
848 pub fn get_page_margins(&self, sheet: &str) -> Result<PageMarginsOptions> {
850 let ws = self.work_sheet_reader(sheet)?;
851 let mut opts = PageMarginsOptions {
852 left: Some(0.7),
853 right: Some(0.7),
854 top: Some(0.75),
855 bottom: Some(0.75),
856 header: Some(0.3),
857 footer: Some(0.3),
858 ..Default::default()
859 };
860 if let Some(pm) = ws.page_margins {
861 opts.left = Some(pm.left);
862 opts.right = Some(pm.right);
863 opts.top = Some(pm.top);
864 opts.bottom = Some(pm.bottom);
865 opts.header = Some(pm.header);
866 opts.footer = Some(pm.footer);
867 }
868 if let Some(po) = ws.print_options {
869 opts.horizontally = po.horizontal_centered;
870 opts.vertically = po.vertical_centered;
871 }
872 Ok(opts)
873 }
874
875 pub fn set_header_footer(&self, sheet: &str, opts: &HeaderFooterOptions) -> Result<()> {
877 let path = self
878 .get_sheet_xml_path(sheet)
879 .ok_or_else(|| ErrSheetNotExist {
880 sheet_name: sheet.to_string(),
881 })?;
882 let mut ws = self.work_sheet_reader(sheet)?;
883 for (field, value) in [
884 ("OddHeader", opts.odd_header.as_deref()),
885 ("OddFooter", opts.odd_footer.as_deref()),
886 ("EvenHeader", opts.even_header.as_deref()),
887 ("EvenFooter", opts.even_footer.as_deref()),
888 ("FirstHeader", opts.first_header.as_deref()),
889 ("FirstFooter", opts.first_footer.as_deref()),
890 ] {
891 if let Some(v) = value {
892 if count_utf16_string(v) > MAX_FIELD_LENGTH {
893 return Err(new_field_length_error(field).into());
894 }
895 }
896 }
897 if ws.header_footer.is_none() {
898 ws.header_footer = Some(XlsxHeaderFooter::default());
899 }
900 let hf = ws.header_footer.as_mut().unwrap();
901 if opts.different_first.is_some() {
902 hf.different_first = opts.different_first;
903 }
904 if opts.different_odd_even.is_some() {
905 hf.different_odd_even = opts.different_odd_even;
906 }
907 if opts.scale_with_doc.is_some() {
908 hf.scale_with_doc = opts.scale_with_doc;
909 }
910 if opts.align_with_margins.is_some() {
911 hf.align_with_margins = opts.align_with_margins;
912 }
913 if opts.odd_header.is_some() {
914 hf.odd_header = opts.odd_header.clone();
915 }
916 if opts.odd_footer.is_some() {
917 hf.odd_footer = opts.odd_footer.clone();
918 }
919 if opts.even_header.is_some() {
920 hf.even_header = opts.even_header.clone();
921 }
922 if opts.even_footer.is_some() {
923 hf.even_footer = opts.even_footer.clone();
924 }
925 if opts.first_header.is_some() {
926 hf.first_header = opts.first_header.clone();
927 }
928 if opts.first_footer.is_some() {
929 hf.first_footer = opts.first_footer.clone();
930 }
931 self.sheet.insert(path, ws);
932 Ok(())
933 }
934
935 pub fn get_header_footer(&self, sheet: &str) -> Result<HeaderFooterOptions> {
937 let ws = self.work_sheet_reader(sheet)?;
938 let mut opts = HeaderFooterOptions::default();
939 if let Some(hf) = ws.header_footer {
940 opts.different_first = hf.different_first;
941 opts.different_odd_even = hf.different_odd_even;
942 opts.scale_with_doc = hf.scale_with_doc;
943 opts.align_with_margins = hf.align_with_margins;
944 opts.odd_header = hf.odd_header;
945 opts.odd_footer = hf.odd_footer;
946 opts.even_header = hf.even_header;
947 opts.even_footer = hf.even_footer;
948 opts.first_header = hf.first_header;
949 opts.first_footer = hf.first_footer;
950 }
951 Ok(opts)
952 }
953
954 pub fn set_sheet_name(&self, source: &str, target: &str) -> Result<()> {
956 crate::excelize::check_sheet_name(target)?;
957 if source.eq_ignore_ascii_case(target) {
958 return Ok(());
959 }
960 self.clear_calc_cache();
961 let mut wb = self.workbook_reader()?;
962 for k in 0..wb.sheets.sheet.len() {
963 if wb.sheets.sheet[k]
964 .name
965 .as_deref()
966 .map(|n| n.eq_ignore_ascii_case(source))
967 .unwrap_or(false)
968 {
969 wb.sheets.sheet[k].name = Some(target.to_string());
970 let mut map = self.sheet_map.lock().unwrap();
971 if let Some(path) = map.get(source).cloned() {
972 map.remove(source);
973 map.insert(target.to_string(), path);
974 }
975 }
976 }
977 if let Some(names) = wb.defined_names.as_mut() {
978 for dn in &mut names.defined_name {
979 dn.data = adjust_range_sheet_name(&dn.data, source, target);
980 }
981 }
982 *self.workbook.lock().unwrap() = Some(wb);
983 Ok(())
984 }
985
986 pub fn set_sheet_background(&self, sheet: &str, picture: &str) -> Result<()> {
988 let ext = Path::new(picture)
989 .extension()
990 .and_then(|e| e.to_str())
991 .unwrap_or("")
992 .to_string();
993 let file = std::fs::read(picture)?;
994 self.set_sheet_background_from_bytes(sheet, &ext, &file)
995 }
996
997 pub fn set_sheet_background_from_bytes(
999 &self,
1000 sheet: &str,
1001 extension: &str,
1002 picture: &[u8],
1003 ) -> Result<()> {
1004 if picture.is_empty() {
1005 return Err(Box::new(ErrParameterInvalid));
1006 }
1007 let ext = if !extension.is_empty() {
1008 format!(".{extension}")
1009 } else {
1010 String::new()
1011 };
1012 let image_type = image_extension_map()
1013 .get(&ext.to_lowercase())
1014 .cloned()
1015 .ok_or(ErrImgExt)?;
1016 let name = self.add_media(picture, &image_type);
1017 let sheet_xml_path = self
1018 .get_sheet_xml_path(sheet)
1019 .ok_or_else(|| ErrSheetNotExist {
1020 sheet_name: sheet.to_string(),
1021 })?;
1022 let sheet_rels = format!(
1023 "xl/worksheets/_rels/{}.rels",
1024 sheet_xml_path.trim_start_matches("xl/worksheets/")
1025 );
1026 let target = name.replacen("xl", "..", 1);
1027 let r_id = self.add_rels(&sheet_rels, SOURCE_RELATIONSHIP_IMAGE, &target, "");
1028 self.add_sheet_picture(sheet, r_id)?;
1029 self.add_sheet_name_space(sheet, SOURCE_RELATIONSHIP);
1030 set_content_type_part_image_extensions(self)
1031 }
1032
1033 pub fn move_sheet(&self, source: &str, target: &str) -> Result<()> {
1036 if source.eq_ignore_ascii_case(target) {
1037 return Ok(());
1038 }
1039 let mut wb = self.workbook_reader()?;
1040 let source_idx = self.get_sheet_index(source)?;
1041 let target_idx = self.get_sheet_index(target)?;
1042 if source_idx < 1 {
1043 return Err(Box::new(ErrSheetNotExist {
1044 sheet_name: source.to_string(),
1045 }));
1046 }
1047 if target_idx < 1 {
1048 return Err(Box::new(ErrSheetNotExist {
1049 sheet_name: target.to_string(),
1050 }));
1051 }
1052 let _ = self.ungroup_sheets();
1053 let active_sheet_name = self.get_sheet_name(self.get_active_sheet_index()?)?;
1054 let source_sheet = wb.sheets.sheet.remove(source_idx as usize - 1);
1055 let mut target_idx = target_idx;
1056 if target_idx > source_idx {
1057 target_idx -= 1;
1058 }
1059 wb.sheets
1060 .sheet
1061 .insert(target_idx as usize - 1, source_sheet);
1062 *self.workbook.lock().unwrap() = Some(wb);
1063 let active_idx = self.get_sheet_index(&active_sheet_name)?;
1064 self.set_active_sheet(active_idx)?;
1065 Ok(())
1066 }
1067
1068 pub fn copy_sheet(&self, from: i32, to: i32) -> Result<()> {
1070 if from < 1 || to < 1 || from == to {
1071 return Err(Box::new(ErrSheetIdx));
1072 }
1073 let from_name = self.get_sheet_name(from)?;
1074 let to_name = self.get_sheet_name(to)?;
1075 if from_name.is_empty() || to_name.is_empty() {
1076 return Err(Box::new(ErrSheetIdx));
1077 }
1078 self.clear_calc_cache();
1079 let ws = self.work_sheet_reader(&from_name)?;
1080 let mut worksheet = ws.clone();
1081 if let Some(views) = worksheet.sheet_views.as_mut() {
1082 if let Some(view) = views.sheet_view.first_mut() {
1083 view.tab_selected = Some(false);
1084 }
1085 }
1086 worksheet.drawing = None;
1087 worksheet.table_parts = None;
1088 worksheet.page_setup = None;
1089 let to_sheet_id = self.get_sheet_id(&to_name);
1090 let sheet_xml_path = format!("xl/worksheets/sheet{to_sheet_id}.xml");
1091 self.sheet.insert(sheet_xml_path.clone(), worksheet);
1092 let to_rels = format!("xl/worksheets/_rels/sheet{to_sheet_id}.xml.rels");
1093 let from_sheet_id = self.get_sheet_id(&from_name);
1094 let from_rels = format!("xl/worksheets/_rels/sheet{from_sheet_id}.xml.rels");
1095 if let Some(rels) = self.pkg.get(&from_rels) {
1096 self.pkg.insert(to_rels, rels.clone());
1097 }
1098 let from_sheet_xml_path = self.get_sheet_xml_path(&from_name).unwrap_or_default();
1099 if let Some(attrs) = self.xml_attr.get(&from_sheet_xml_path) {
1100 self.xml_attr.insert(sheet_xml_path, attrs.clone());
1101 }
1102 Ok(())
1103 }
1104
1105 pub fn set_sheet_visible(
1107 &self,
1108 sheet: &str,
1109 visible: bool,
1110 very_hidden: Option<bool>,
1111 ) -> Result<()> {
1112 crate::excelize::check_sheet_name(sheet)?;
1113 let mut wb = self.workbook_reader()?;
1114 if visible {
1115 for k in 0..wb.sheets.sheet.len() {
1116 if wb.sheets.sheet[k]
1117 .name
1118 .as_deref()
1119 .map(|n| n.eq_ignore_ascii_case(sheet))
1120 .unwrap_or(false)
1121 {
1122 wb.sheets.sheet[k].state = None;
1123 }
1124 }
1125 *self.workbook.lock().unwrap() = Some(wb);
1126 return Ok(());
1127 }
1128 let state = get_sheet_state(false, very_hidden);
1129 let mut count = 0;
1130 for s in &wb.sheets.sheet {
1131 if s.state.as_deref().unwrap_or("") != state {
1132 count += 1;
1133 }
1134 }
1135 for k in 0..wb.sheets.sheet.len() {
1136 let name = wb.sheets.sheet[k].name.clone().unwrap_or_default();
1137 let ws = self.work_sheet_reader(&name)?;
1138 let mut tab_selected = false;
1139 if let Some(views) = ws.sheet_views.as_ref() {
1140 if let Some(view) = views.sheet_view.first() {
1141 tab_selected = view.tab_selected.unwrap_or(false);
1142 }
1143 }
1144 if name.eq_ignore_ascii_case(sheet) && count > 1 && !tab_selected {
1145 wb.sheets.sheet[k].state = Some(state.to_string());
1146 }
1147 }
1148 *self.workbook.lock().unwrap() = Some(wb);
1149 Ok(())
1150 }
1151
1152 pub fn get_sheet_visible(&self, sheet: &str) -> Result<bool> {
1154 crate::excelize::check_sheet_name(sheet)?;
1155 let wb = self.workbook_reader()?;
1156 for s in &wb.sheets.sheet {
1157 if s.name
1158 .as_deref()
1159 .map(|n| n.eq_ignore_ascii_case(sheet))
1160 .unwrap_or(false)
1161 {
1162 let state = s.state.as_deref().unwrap_or("");
1163 return Ok(state.is_empty() || state.eq_ignore_ascii_case("visible"));
1164 }
1165 }
1166 Ok(false)
1167 }
1168
1169 pub fn set_panes(&self, sheet: &str, panes: &Panes) -> Result<()> {
1171 let path = self
1172 .get_sheet_xml_path(sheet)
1173 .ok_or_else(|| ErrSheetNotExist {
1174 sheet_name: sheet.to_string(),
1175 })?;
1176 let mut ws = self.work_sheet_reader(sheet)?;
1177 set_panes_ws(&mut ws, panes)?;
1178 self.sheet.insert(path, ws);
1179 Ok(())
1180 }
1181
1182 pub fn get_panes(&self, sheet: &str) -> Result<Panes> {
1184 let ws = self.work_sheet_reader(sheet)?;
1185 Ok(get_panes_ws(&ws))
1186 }
1187
1188 pub fn search_sheet(&self, sheet: &str, value: &str, reg: Option<bool>) -> Result<Vec<String>> {
1190 crate::excelize::check_sheet_name(sheet)?;
1191 let reg_search = reg.unwrap_or(false);
1192 let name = self
1193 .get_sheet_xml_path(sheet)
1194 .ok_or_else(|| ErrSheetNotExist {
1195 sheet_name: sheet.to_string(),
1196 })?;
1197 if let Some(ws) = self.sheet.get(&name) {
1198 let ws = ws.clone();
1199 self.save_file_list(&name, &worksheet_bytes(&ws));
1200 }
1201 search_sheet_impl(self, sheet, value, reg_search)
1202 }
1203
1204 pub fn protect_sheet(&self, sheet: &str, opts: &SheetProtectionOptions) -> Result<()> {
1206 let path = self
1207 .get_sheet_xml_path(sheet)
1208 .ok_or_else(|| ErrSheetNotExist {
1209 sheet_name: sheet.to_string(),
1210 })?;
1211 let mut ws = self.work_sheet_reader(sheet)?;
1212 let mut sp = XlsxSheetProtection {
1213 auto_filter: !opts.auto_filter,
1214 delete_columns: !opts.delete_columns,
1215 delete_rows: !opts.delete_rows,
1216 format_cells: !opts.format_cells,
1217 format_columns: !opts.format_columns,
1218 format_rows: !opts.format_rows,
1219 insert_columns: !opts.insert_columns,
1220 insert_hyperlinks: !opts.insert_hyperlinks,
1221 insert_rows: !opts.insert_rows,
1222 objects: !opts.edit_objects,
1223 pivot_tables: !opts.pivot_tables,
1224 scenarios: !opts.edit_scenarios,
1225 select_locked_cells: !opts.select_locked_cells,
1226 select_unlocked_cells: !opts.select_unlocked_cells,
1227 sheet: true,
1228 sort: !opts.sort,
1229 ..Default::default()
1230 };
1231 if !opts.password.is_empty() {
1232 if opts.algorithm_name.is_empty() {
1233 sp.password = Some(gen_sheet_passwd(&opts.password));
1234 } else {
1235 let (hash_value, salt_value) =
1236 gen_iso_passwd_hash(&opts.password, &opts.algorithm_name, "", 100_000)?;
1237 sp.password = None;
1238 sp.algorithm_name = Some(opts.algorithm_name.clone());
1239 sp.salt_value = Some(salt_value);
1240 sp.hash_value = Some(hash_value);
1241 sp.spin_count = Some(100_000);
1242 }
1243 }
1244 ws.sheet_protection = Some(sp);
1245 self.sheet.insert(path, ws);
1246 Ok(())
1247 }
1248
1249 pub fn unprotect_sheet(&self, sheet: &str, password: Option<&str>) -> Result<()> {
1251 let path = self
1252 .get_sheet_xml_path(sheet)
1253 .ok_or_else(|| ErrSheetNotExist {
1254 sheet_name: sheet.to_string(),
1255 })?;
1256 let mut ws = self.work_sheet_reader(sheet)?;
1257 if let Some(passwd) = password {
1258 let sp = ws.sheet_protection.as_ref().ok_or(ErrUnprotectSheet)?;
1259 if sp.algorithm_name.as_deref().unwrap_or("").is_empty() {
1260 if sp.password.as_deref().unwrap_or("") != gen_sheet_passwd(passwd) {
1261 return Err(Box::new(ErrUnprotectSheetPassword));
1262 }
1263 } else {
1264 let (hash_value, _) = gen_iso_passwd_hash(
1265 passwd,
1266 sp.algorithm_name.as_deref().unwrap_or(""),
1267 sp.salt_value.as_deref().unwrap_or(""),
1268 sp.spin_count.unwrap_or(0) as i32,
1269 )?;
1270 if sp.hash_value.as_deref().unwrap_or("") != hash_value {
1271 return Err(Box::new(ErrUnprotectSheetPassword));
1272 }
1273 }
1274 }
1275 ws.sheet_protection = None;
1276 self.sheet.insert(path, ws);
1277 Ok(())
1278 }
1279
1280 pub fn get_sheet_protection(&self, sheet: &str) -> Result<SheetProtectionOptions> {
1282 let ws = self.work_sheet_reader(sheet)?;
1283 let mut opts = SheetProtectionOptions::default();
1284 if let Some(sp) = ws.sheet_protection {
1285 opts.algorithm_name = sp.algorithm_name.unwrap_or_default();
1286 opts.auto_filter = !sp.auto_filter;
1287 opts.delete_columns = !sp.delete_columns;
1288 opts.delete_rows = !sp.delete_rows;
1289 opts.edit_objects = !sp.objects;
1290 opts.edit_scenarios = !sp.scenarios;
1291 opts.format_cells = !sp.format_cells;
1292 opts.format_columns = !sp.format_columns;
1293 opts.format_rows = !sp.format_rows;
1294 opts.insert_columns = !sp.insert_columns;
1295 opts.insert_hyperlinks = !sp.insert_hyperlinks;
1296 opts.insert_rows = !sp.insert_rows;
1297 opts.pivot_tables = !sp.pivot_tables;
1298 opts.select_locked_cells = !sp.select_locked_cells;
1299 opts.select_unlocked_cells = !sp.select_unlocked_cells;
1300 opts.sort = !sp.sort;
1301 }
1302 Ok(opts)
1303 }
1304
1305 pub fn group_sheets(&self, sheets: &[String]) -> Result<()> {
1307 let active_sheet = self.get_active_sheet_index()?;
1308 let sheet_list = self.get_sheet_list();
1309 let mut in_active_sheet = false;
1310 for (idx, sheet_name) in sheet_list.iter().enumerate() {
1311 for s in sheets {
1312 if s.eq_ignore_ascii_case(sheet_name) && (idx + 1) as i32 == active_sheet {
1313 in_active_sheet = true;
1314 }
1315 }
1316 }
1317 if !in_active_sheet {
1318 return Err(Box::new(ErrGroupSheets));
1319 }
1320 for sheet in sheets {
1321 let mut ws = self.work_sheet_reader(sheet)?;
1322 let path = self.get_sheet_xml_path(sheet).unwrap_or_default();
1323 if let Some(views) = ws.sheet_views.as_mut() {
1324 for view in &mut views.sheet_view {
1325 view.tab_selected = Some(true);
1326 }
1327 }
1328 self.sheet.insert(path, ws);
1329 }
1330 Ok(())
1331 }
1332
1333 pub fn ungroup_sheets(&self) -> Result<()> {
1335 let active_sheet = self.get_active_sheet_index()?;
1336 for (index, sheet) in self.get_sheet_list().iter().enumerate() {
1337 if active_sheet == (index + 1) as i32 {
1338 continue;
1339 }
1340 let mut ws = self.work_sheet_reader(sheet)?;
1341 let path = self.get_sheet_xml_path(sheet).unwrap_or_default();
1342 if let Some(views) = ws.sheet_views.as_mut() {
1343 for view in &mut views.sheet_view {
1344 view.tab_selected = Some(false);
1345 }
1346 }
1347 self.sheet.insert(path, ws);
1348 }
1349 Ok(())
1350 }
1351
1352 pub fn insert_page_break(&self, sheet: &str, cell: &str) -> Result<()> {
1354 let path = self
1355 .get_sheet_xml_path(sheet)
1356 .ok_or_else(|| ErrSheetNotExist {
1357 sheet_name: sheet.to_string(),
1358 })?;
1359 let mut ws = self.work_sheet_reader(sheet)?;
1360 insert_page_break_ws(&mut ws, cell)?;
1361 self.sheet.insert(path, ws);
1362 Ok(())
1363 }
1364
1365 pub fn remove_page_break(&self, sheet: &str, cell: &str) -> Result<()> {
1367 let path = self
1368 .get_sheet_xml_path(sheet)
1369 .ok_or_else(|| ErrSheetNotExist {
1370 sheet_name: sheet.to_string(),
1371 })?;
1372 let mut ws = self.work_sheet_reader(sheet)?;
1373 remove_page_break_ws(&mut ws, cell)?;
1374 self.sheet.insert(path, ws);
1375 Ok(())
1376 }
1377
1378 pub fn set_sheet_dimension(&self, sheet: &str, range_ref: &str) -> Result<()> {
1380 let path = self
1381 .get_sheet_xml_path(sheet)
1382 .ok_or_else(|| ErrSheetNotExist {
1383 sheet_name: sheet.to_string(),
1384 })?;
1385 let mut ws = self.work_sheet_reader(sheet)?;
1386 if range_ref.is_empty() {
1387 ws.dimension = None;
1388 self.sheet.insert(path, ws);
1389 return Ok(());
1390 }
1391 let parts: Vec<&str> = range_ref.split(':').collect();
1392 if parts.len() == 1 {
1393 let _ = cell_name_to_coordinates(range_ref)?;
1394 ws.dimension = Some(XlsxDimension {
1395 r#ref: range_ref.to_uppercase(),
1396 });
1397 self.sheet.insert(path, ws);
1398 return Ok(());
1399 }
1400 if parts.len() != 2 {
1401 return Err(Box::new(ErrParameterInvalid));
1402 }
1403 let mut coordinates = range_ref_to_coordinates(range_ref)?;
1404 sort_coordinates(&mut coordinates)?;
1405 let r#ref = coordinates_to_range_ref(&coordinates, false)?;
1406 ws.dimension = Some(XlsxDimension { r#ref });
1407 self.sheet.insert(path, ws);
1408 Ok(())
1409 }
1410
1411 pub fn get_sheet_dimension(&self, sheet: &str) -> Result<String> {
1413 crate::excelize::check_sheet_name(sheet)?;
1414 let name = self
1415 .get_sheet_xml_path(sheet)
1416 .ok_or_else(|| ErrSheetNotExist {
1417 sheet_name: sheet.to_string(),
1418 })?;
1419 if let Some(ws) = self.sheet.get(&name) {
1420 return Ok(ws
1421 .dimension
1422 .as_ref()
1423 .map(|d| d.r#ref.clone())
1424 .unwrap_or_default());
1425 }
1426 let data = self.read_bytes(&name);
1427 let s = String::from_utf8_lossy(&data);
1428 if let Some(start) = s.find("<dimension") {
1429 if let Some(end) = s[start..].find('>') {
1430 let tag = &s[start..start + end + 1];
1431 if let Some(pos) = tag.find("ref=\"") {
1432 let rest = &tag[pos + 6..];
1433 if let Some(q) = rest.find('"') {
1434 return Ok(rest[..q].to_string());
1435 }
1436 }
1437 }
1438 }
1439 Ok(String::new())
1440 }
1441
1442 pub fn add_ignored_errors(
1444 &self,
1445 sheet: &str,
1446 range_ref: &str,
1447 ignored_errors_type: IgnoredErrorsType,
1448 ) -> Result<()> {
1449 if range_ref.is_empty() {
1450 return Err(Box::new(ErrParameterInvalid));
1451 }
1452 let path = self
1453 .get_sheet_xml_path(sheet)
1454 .ok_or_else(|| ErrSheetNotExist {
1455 sheet_name: sheet.to_string(),
1456 })?;
1457 let mut ws = self.work_sheet_reader(sheet)?;
1458 if ws.ignored_errors.is_none() {
1459 ws.ignored_errors = Some(XlsxIgnoredErrors::default());
1460 }
1461 let ie = ignored_error_for_type(range_ref, ignored_errors_type);
1462 let ignored = ws.ignored_errors.as_mut().unwrap();
1463 if ignored
1464 .ignored_error
1465 .iter()
1466 .any(|e| e.sqref == ie.sqref && same_ignored_error_flags(e, &ie))
1467 {
1468 self.sheet.insert(path, ws);
1469 return Ok(());
1470 }
1471 ignored.ignored_error.push(ie);
1472 self.sheet.insert(path, ws);
1473 Ok(())
1474 }
1475}
1476
1477impl File {
1482 pub fn get_sheet_name_to_path_map(&self) -> Result<HashMap<String, String>> {
1484 let mut map = HashMap::new();
1485 let wb = self.workbook_reader()?;
1486 let rels = self.rels_reader(&self.get_workbook_rels_path())?;
1487 let Some(rels) = rels else {
1488 return Ok(map);
1489 };
1490 for sheet in &wb.sheets.sheet {
1491 let Some(name) = &sheet.name else { continue };
1492 let id = sheet.id.as_ref().or(sheet.plain_id.as_ref());
1493 let Some(id) = id else { continue };
1494 for rel in &rels.relationships {
1495 if &rel.id == id {
1496 let sheet_xml = self.get_worksheet_path(&rel.target);
1497 if self.pkg.contains_key(&sheet_xml) || self.temp_files.contains_key(&sheet_xml)
1498 {
1499 map.insert(name.clone(), sheet_xml);
1500 }
1501 }
1502 }
1503 }
1504 Ok(map)
1505 }
1506
1507 pub fn get_sheet_map(&self) -> HashMap<i32, String> {
1509 let mut sheet_map = HashMap::new();
1510 if let Ok(wb) = self.workbook_reader() {
1511 for sheet in &wb.sheets.sheet {
1512 if let (Some(id), Some(name)) = (sheet.sheet_id, sheet.name.clone()) {
1513 sheet_map.insert(id as i32, name);
1514 }
1515 }
1516 }
1517 sheet_map
1518 }
1519
1520 pub fn rel_id_to_sheet_id(&self, r_id: &str) -> Result<i32> {
1522 let wb = self.workbook_reader()?;
1523 for (i, sheet) in wb.sheets.sheet.iter().enumerate() {
1524 let id = sheet.id.as_deref().or(sheet.plain_id.as_deref());
1525 if id == Some(r_id) {
1526 return Ok(i as i32);
1527 }
1528 }
1529 Err(Box::new(ErrSheetIdx))
1530 }
1531}
1532
1533fn new_worksheet() -> XlsxWorksheet {
1534 XlsxWorksheet {
1535 sheet_format_pr: Some(XlsxSheetFormatPr {
1536 default_row_height: 15.0,
1537 ..Default::default()
1538 }),
1539 ..Default::default()
1540 }
1541}
1542
1543fn worksheet_bytes(ws: &XlsxWorksheet) -> Vec<u8> {
1544 use quick_xml::se::to_string;
1545 let body = to_string(ws).unwrap_or_default().into_bytes();
1546 [crate::constants::XML_HEADER.as_bytes(), &body].concat()
1547}
1548
1549impl File {
1550 pub(crate) fn add_sheet_picture(&self, sheet: &str, r_id: i32) -> Result<()> {
1552 let path = self
1553 .get_sheet_xml_path(sheet)
1554 .ok_or_else(|| ErrSheetNotExist {
1555 sheet_name: sheet.to_string(),
1556 })?;
1557 let mut ws = self.work_sheet_reader(sheet)?;
1558 ws.picture = Some(XlsxPicture {
1559 rid: Some(format!("rId{r_id}")),
1560 });
1561 self.sheet.insert(path, ws);
1562 Ok(())
1563 }
1564}
1565
1566fn set_panes_ws(ws: &mut XlsxWorksheet, panes: &Panes) -> Result<()> {
1567 let mut p = XlsxPane {
1568 active_pane: Some(panes.active_pane.clone()),
1569 top_left_cell: Some(panes.top_left_cell.clone()),
1570 x_split: Some(panes.x_split as f64),
1571 y_split: Some(panes.y_split as f64),
1572 ..Default::default()
1573 };
1574 if panes.freeze {
1575 p.state = Some("frozen".to_string());
1576 }
1577 if ws.sheet_views.is_none() {
1578 ws.sheet_views = Some(XlsxSheetViews::default());
1579 }
1580 let views = ws.sheet_views.as_mut().unwrap();
1581 if views.sheet_view.is_empty() {
1582 views.sheet_view.push(XlsxSheetView::default());
1583 }
1584 let idx = views.sheet_view.len() - 1;
1585 if !panes.freeze && !panes.split {
1586 views.sheet_view[idx].pane = None;
1587 } else {
1588 views.sheet_view[idx].pane = Some(p);
1589 }
1590 let mut selection = Vec::new();
1591 for s in &panes.selection {
1592 selection.push(XlsxSelection {
1593 active_cell: Some(s.active_cell.clone()),
1594 pane: Some(s.pane.clone()),
1595 sqref: Some(s.sqref.clone()),
1596 ..Default::default()
1597 });
1598 }
1599 views.sheet_view[idx].selection = selection;
1600 Ok(())
1601}
1602
1603fn get_panes_ws(ws: &XlsxWorksheet) -> Panes {
1604 let mut panes = Panes::default();
1605 let Some(views) = ws.sheet_views.as_ref() else {
1606 return panes;
1607 };
1608 let Some(view) = views.sheet_view.last() else {
1609 return panes;
1610 };
1611 for s in &view.selection {
1612 panes.selection.push(Selection {
1613 sqref: s.sqref.clone().unwrap_or_default(),
1614 active_cell: s.active_cell.clone().unwrap_or_default(),
1615 pane: s.pane.clone().unwrap_or_default(),
1616 });
1617 }
1618 let Some(p) = view.pane.as_ref() else {
1619 return panes;
1620 };
1621 panes.active_pane = p.active_pane.clone().unwrap_or_default();
1622 if p.state.as_deref() == Some("frozen") {
1623 panes.freeze = true;
1624 }
1625 panes.top_left_cell = p.top_left_cell.clone().unwrap_or_default();
1626 panes.x_split = p.x_split.unwrap_or(0.0) as i64;
1627 panes.y_split = p.y_split.unwrap_or(0.0) as i64;
1628 panes
1629}
1630
1631fn search_sheet_impl(file: &File, sheet: &str, value: &str, reg: bool) -> Result<Vec<String>> {
1632 let name = file
1633 .get_sheet_xml_path(sheet)
1634 .ok_or_else(|| ErrSheetNotExist {
1635 sheet_name: sheet.to_string(),
1636 })?;
1637 let raw = {
1638 let raw = file.read_xml(&name);
1639 if !raw.is_empty() {
1640 raw
1641 } else if let Some(ws) = file.sheet.get(&name) {
1642 worksheet_bytes(&ws)
1643 } else {
1644 file.read_bytes(&name)
1645 }
1646 };
1647 let data = file.apply_charset_transcoder(&raw)?;
1648 let data = namespace_strict_to_transitional(&data);
1649 let sst = file.shared_strings_reader().ok();
1650
1651 let regex = if reg {
1652 Some(regex::Regex::new(value).map_err(|e| e.to_string())?)
1653 } else {
1654 None
1655 };
1656
1657 let mut result = Vec::new();
1658 let mut merge_cells = Vec::new();
1659
1660 let mut reader = Reader::from_reader(Cursor::new(data));
1661 let mut buf = Vec::new();
1662
1663 let mut _current_row = 0;
1664 let mut in_cell = false;
1665 let mut in_v = false;
1666 let mut in_is = false;
1667 let mut in_is_t = false;
1668 let mut cell_ref = String::new();
1669 let mut cell_type = None::<String>;
1670 let mut current_val = String::new();
1671
1672 loop {
1673 match reader.read_event_into(&mut buf) {
1674 Ok(Event::Start(e)) => {
1675 let local_name = e.local_name();
1676 let name = local_name.as_ref();
1677 if name == b"row" {
1678 if let Ok(Some(attr)) = e.try_get_attribute("r") {
1679 if let Ok(r_str) = attr.decode_and_unescape_value(reader.decoder()) {
1680 _current_row = r_str.parse::<i32>().unwrap_or(0);
1681 }
1682 }
1683 } else if name == b"c" {
1684 in_cell = true;
1685 in_v = false;
1686 in_is = false;
1687 in_is_t = false;
1688 cell_ref.clear();
1689 cell_type = None;
1690 current_val.clear();
1691 if let Ok(Some(attr)) = e.try_get_attribute("r") {
1692 if let Ok(r) = attr.decode_and_unescape_value(reader.decoder()) {
1693 cell_ref = r.to_string();
1694 }
1695 }
1696 if let Ok(Some(attr)) = e.try_get_attribute("t") {
1697 if let Ok(t) = attr.decode_and_unescape_value(reader.decoder()) {
1698 cell_type = Some(t.to_string());
1699 }
1700 }
1701 } else if in_cell && name == b"v" {
1702 in_v = true;
1703 current_val.clear();
1704 } else if in_cell && name == b"is" {
1705 in_is = true;
1706 current_val.clear();
1707 } else if in_is && name == b"t" {
1708 in_is_t = true;
1709 }
1710 }
1711 Ok(Event::Empty(e)) => {
1712 let local_name = e.local_name();
1713 let name = local_name.as_ref();
1714 if name == b"c" {
1715 cell_ref.clear();
1716 cell_type = None;
1717 current_val.clear();
1718 if let Ok(Some(attr)) = e.try_get_attribute("r") {
1719 if let Ok(r) = attr.decode_and_unescape_value(reader.decoder()) {
1720 cell_ref = r.to_string();
1721 }
1722 }
1723 if let Ok(Some(attr)) = e.try_get_attribute("t") {
1724 if let Ok(t) = attr.decode_and_unescape_value(reader.decoder()) {
1725 cell_type = Some(t.to_string());
1726 }
1727 }
1728 evaluate_cell(
1729 &mut result,
1730 &cell_ref,
1731 cell_type.as_deref(),
1732 ¤t_val,
1733 sst.as_ref(),
1734 value,
1735 reg,
1736 regex.as_ref(),
1737 );
1738 } else if name == b"mergeCell" {
1739 if let Ok(Some(attr)) = e.try_get_attribute("ref") {
1740 if let Ok(r) = attr.decode_and_unescape_value(reader.decoder()) {
1741 merge_cells.push(r.to_string());
1742 }
1743 }
1744 }
1745 }
1746 Ok(Event::Text(e)) => {
1747 if in_v || in_is_t {
1748 if let Ok(text) = e.unescape() {
1749 current_val.push_str(&text);
1750 }
1751 }
1752 }
1753 Ok(Event::End(e)) => {
1754 let local_name = e.local_name();
1755 let name = local_name.as_ref();
1756 if in_cell && name == b"c" {
1757 evaluate_cell(
1758 &mut result,
1759 &cell_ref,
1760 cell_type.as_deref(),
1761 ¤t_val,
1762 sst.as_ref(),
1763 value,
1764 reg,
1765 regex.as_ref(),
1766 );
1767 in_cell = false;
1768 in_v = false;
1769 in_is = false;
1770 in_is_t = false;
1771 cell_ref.clear();
1772 cell_type = None;
1773 current_val.clear();
1774 } else if in_v && name == b"v" {
1775 in_v = false;
1776 } else if in_is && name == b"is" {
1777 in_is = false;
1778 in_is_t = false;
1779 } else if in_is_t && name == b"t" {
1780 in_is_t = false;
1781 }
1782 }
1783 Ok(Event::Eof) => break,
1784 Err(e) => return Err(Box::new(e)),
1785 _ => {}
1786 }
1787 buf.clear();
1788 }
1789
1790 if !merge_cells.is_empty() {
1791 for cell_ref in &mut result {
1792 for merge_ref in &merge_cells {
1793 if cell_in_range(cell_ref, merge_ref) {
1794 if let Some(top_left) = merge_ref.split(':').next() {
1795 *cell_ref = top_left.to_string();
1796 }
1797 }
1798 }
1799 }
1800 }
1801
1802 Ok(result)
1803}
1804
1805fn evaluate_cell(
1806 result: &mut Vec<String>,
1807 cell_ref: &str,
1808 cell_type: Option<&str>,
1809 current_val: &str,
1810 sst: Option<&crate::xml::shared_strings::XlsxSst>,
1811 value: &str,
1812 reg: bool,
1813 regex: Option<®ex::Regex>,
1814) {
1815 if cell_ref.is_empty() {
1816 return;
1817 }
1818 let val = match cell_type {
1819 Some("s") => {
1820 if let Ok(idx) = current_val.parse::<usize>() {
1821 sst.and_then(|s| s.si.get(idx))
1822 .map(|si| extract_si_text(si))
1823 .unwrap_or_default()
1824 } else {
1825 String::new()
1826 }
1827 }
1828 Some("inlineStr") => current_val.to_string(),
1829 Some("str") => current_val.to_string(),
1830 _ => current_val.to_string(),
1831 };
1832 let matched = if reg {
1833 regex.unwrap().is_match(&val)
1834 } else {
1835 val == value
1836 };
1837 if matched {
1838 result.push(cell_ref.to_string());
1839 }
1840}
1841
1842fn extract_si_text(si: &crate::xml::shared_strings::XlsxSi) -> String {
1843 if let Some(t) = &si.t {
1844 return t.val.clone();
1845 }
1846 si.r.iter()
1847 .filter_map(|r| r.t.as_ref().map(|t| t.val.clone()))
1848 .collect()
1849}
1850
1851fn cell_in_range(cell: &str, range: &str) -> bool {
1852 let parts: Vec<&str> = range.split(':').collect();
1853 if parts.len() != 2 {
1854 return false;
1855 }
1856 let (c, r) = cell_name_to_coordinates(cell).unwrap_or((0, 0));
1857 let (c1, r1) = cell_name_to_coordinates(parts[0]).unwrap_or((0, 0));
1858 let (c2, r2) = cell_name_to_coordinates(parts[1]).unwrap_or((0, 0));
1859 c >= c1 && c <= c2 && r >= r1 && r <= r2
1860}
1861
1862fn insert_page_break_ws(ws: &mut XlsxWorksheet, cell: &str) -> Result<()> {
1863 let (col, row) = cell_name_to_coordinates(cell).map_err(|e| e.to_string())?;
1864 let col = col - 1;
1865 let row = row - 1;
1866 if col == 0 && row == 0 {
1867 return Ok(());
1868 }
1869 if ws.row_breaks.is_none() {
1870 ws.row_breaks = Some(XlsxRowBreaks {
1871 breaks: XlsxBreaks::default(),
1872 });
1873 }
1874 if ws.col_breaks.is_none() {
1875 ws.col_breaks = Some(XlsxColBreaks {
1876 breaks: XlsxBreaks::default(),
1877 });
1878 }
1879 let row_breaks = ws.row_breaks.as_mut().unwrap();
1880 let col_breaks = ws.col_breaks.as_mut().unwrap();
1881 let mut row_brk = -1;
1882 let mut col_brk = -1;
1883 for (idx, brk) in row_breaks.breaks.brk.iter().enumerate() {
1884 if brk.id == Some(row as i64) {
1885 row_brk = idx as i32;
1886 }
1887 }
1888 for (idx, brk) in col_breaks.breaks.brk.iter().enumerate() {
1889 if brk.id == Some(col as i64) {
1890 col_brk = idx as i32;
1891 }
1892 }
1893 if row != 0 && row_brk == -1 {
1894 row_breaks.breaks.brk.push(XlsxBrk {
1895 id: Some(row as i64),
1896 max: Some((MAX_COLUMNS - 1) as i64),
1897 man: Some(true),
1898 ..Default::default()
1899 });
1900 row_breaks.breaks.manual_break_count =
1901 Some(row_breaks.breaks.manual_break_count.unwrap_or(0) + 1);
1902 }
1903 if col != 0 && col_brk == -1 {
1904 col_breaks.breaks.brk.push(XlsxBrk {
1905 id: Some(col as i64),
1906 max: Some((TOTAL_ROWS - 1) as i64),
1907 man: Some(true),
1908 ..Default::default()
1909 });
1910 col_breaks.breaks.manual_break_count =
1911 Some(col_breaks.breaks.manual_break_count.unwrap_or(0) + 1);
1912 }
1913 row_breaks.breaks.count = Some(row_breaks.breaks.brk.len() as i64);
1914 col_breaks.breaks.count = Some(col_breaks.breaks.brk.len() as i64);
1915 Ok(())
1916}
1917
1918fn remove_page_break_ws(ws: &mut XlsxWorksheet, cell: &str) -> Result<()> {
1919 let (col, row) = cell_name_to_coordinates(cell).map_err(|e| e.to_string())?;
1920 let col = col - 1;
1921 let row = row - 1;
1922 if col == 0 && row == 0 {
1923 return Ok(());
1924 }
1925 if ws.row_breaks.is_none() || ws.col_breaks.is_none() {
1926 return Ok(());
1927 }
1928 let row_breaks = ws.row_breaks.as_mut().unwrap();
1929 let col_breaks = ws.col_breaks.as_mut().unwrap();
1930 let row_brks = row_breaks.breaks.brk.len();
1931 let col_brks = col_breaks.breaks.brk.len();
1932 let mut remove_brk = |id: i64, brks: &mut Vec<XlsxBrk>| {
1933 let mut i = 0;
1934 while i < brks.len() {
1935 if brks[i].id == Some(id) {
1936 brks.remove(i);
1937 } else {
1938 i += 1;
1939 }
1940 }
1941 };
1942 if row_brks > 0 && row_brks == col_brks {
1943 remove_brk(row as i64, &mut row_breaks.breaks.brk);
1944 remove_brk(col as i64, &mut col_breaks.breaks.brk);
1945 row_breaks.breaks.count = Some(row_breaks.breaks.brk.len() as i64);
1946 col_breaks.breaks.count = Some(col_breaks.breaks.brk.len() as i64);
1947 row_breaks.breaks.manual_break_count =
1948 Some(row_breaks.breaks.manual_break_count.unwrap_or(1) - 1);
1949 col_breaks.breaks.manual_break_count =
1950 Some(col_breaks.breaks.manual_break_count.unwrap_or(1) - 1);
1951 return Ok(());
1952 }
1953 if row_brks > 0 && row_brks > col_brks {
1954 remove_brk(row as i64, &mut row_breaks.breaks.brk);
1955 row_breaks.breaks.count = Some(row_breaks.breaks.brk.len() as i64);
1956 row_breaks.breaks.manual_break_count =
1957 Some(row_breaks.breaks.manual_break_count.unwrap_or(1) - 1);
1958 return Ok(());
1959 }
1960 if col_brks > 0 && col_brks > row_brks {
1961 remove_brk(col as i64, &mut col_breaks.breaks.brk);
1962 col_breaks.breaks.count = Some(col_breaks.breaks.brk.len() as i64);
1963 col_breaks.breaks.manual_break_count =
1964 Some(col_breaks.breaks.manual_break_count.unwrap_or(1) - 1);
1965 }
1966 Ok(())
1967}
1968
1969fn get_sheet_state(visible: bool, very_hidden: Option<bool>) -> &'static str {
1970 if !visible && very_hidden == Some(true) {
1971 "veryHidden"
1972 } else {
1973 "hidden"
1974 }
1975}
1976
1977fn gen_sheet_passwd(plaintext: &str) -> String {
1978 let mut password: i64 = 0x0000;
1979 let mut char_pos: u32 = 1;
1980 for v in plaintext.chars() {
1981 let value = (v as i64) << char_pos;
1982 char_pos += 1;
1983 let rotated_bits = value >> 15;
1984 let value = value & 0x7fff;
1985 password ^= value | rotated_bits;
1986 }
1987 password ^= plaintext.chars().count() as i64;
1988 password ^= 0xCE4B;
1989 format!("{password:X}")
1990}
1991
1992fn image_extension_map() -> HashMap<String, String> {
1993 [
1994 (".bmp".to_string(), ".bmp".to_string()),
1995 (".emf".to_string(), ".emf".to_string()),
1996 (".emz".to_string(), ".emz".to_string()),
1997 (".gif".to_string(), ".gif".to_string()),
1998 (".ico".to_string(), ".ico".to_string()),
1999 (".jpg".to_string(), ".jpeg".to_string()),
2000 (".jpeg".to_string(), ".jpeg".to_string()),
2001 (".png".to_string(), ".png".to_string()),
2002 (".svg".to_string(), ".svg".to_string()),
2003 (".tif".to_string(), ".tiff".to_string()),
2004 (".tiff".to_string(), ".tiff".to_string()),
2005 (".wmf".to_string(), ".wmf".to_string()),
2006 (".wmz".to_string(), ".wmz".to_string()),
2007 ]
2008 .into_iter()
2009 .collect()
2010}
2011
2012pub(crate) fn set_content_type_part_image_extensions(file: &File) -> Result<()> {
2013 let image_types: HashMap<String, String> = [
2016 ("bmp".to_string(), "image/".to_string()),
2017 ("ico".to_string(), "image/x-".to_string()),
2018 ("jpeg".to_string(), "image/".to_string()),
2019 ("png".to_string(), "image/".to_string()),
2020 ("gif".to_string(), "image/".to_string()),
2021 ("svg".to_string(), "image/".to_string()),
2022 ("tiff".to_string(), "image/".to_string()),
2023 ("emf".to_string(), "image/x-".to_string()),
2024 ("wmf".to_string(), "image/x-".to_string()),
2025 ("emz".to_string(), "image/x-".to_string()),
2026 ("wmz".to_string(), "image/x-".to_string()),
2027 ]
2028 .into_iter()
2029 .collect();
2030 let mut ct = file.content_types_reader()?;
2031 for (extension, prefix) in image_types {
2032 let exists = ct.entries.iter().any(|e| {
2033 if let crate::xml::content_types::XlsxContentTypeEntry::Default(d) = e {
2034 d.extension == extension
2035 } else {
2036 false
2037 }
2038 });
2039 if !exists {
2040 let content_type = format!("{prefix}{extension}");
2041 ct.entries.push(XlsxContentTypeEntry::Default(XlsxDefault {
2042 extension: extension.clone(),
2043 content_type,
2044 }));
2045 }
2046 }
2047 *file.content_types.lock().unwrap() = Some(ct);
2048 Ok(())
2049}
2050
2051fn ignored_error_for_type(range_ref: &str, t: IgnoredErrorsType) -> XlsxIgnoredError {
2052 let base = XlsxIgnoredError {
2053 sqref: range_ref.to_string(),
2054 ..Default::default()
2055 };
2056 match t {
2057 IgnoredErrorsType::EvalError => XlsxIgnoredError {
2058 eval_error: Some(true),
2059 ..base
2060 },
2061 IgnoredErrorsType::TwoDigitTextYear => XlsxIgnoredError {
2062 two_digit_text_year: Some(true),
2063 ..base
2064 },
2065 IgnoredErrorsType::NumberStoredAsText => XlsxIgnoredError {
2066 number_stored_as_text: Some(true),
2067 ..base
2068 },
2069 IgnoredErrorsType::Formula => XlsxIgnoredError {
2070 formula: Some(true),
2071 ..base
2072 },
2073 IgnoredErrorsType::FormulaRange => XlsxIgnoredError {
2074 formula_range: Some(true),
2075 ..base
2076 },
2077 IgnoredErrorsType::UnlockedFormula => XlsxIgnoredError {
2078 unlocked_formula: Some(true),
2079 ..base
2080 },
2081 IgnoredErrorsType::EmptyCellReference => XlsxIgnoredError {
2082 empty_cell_reference: Some(true),
2083 ..base
2084 },
2085 IgnoredErrorsType::ListDataValidation => XlsxIgnoredError {
2086 list_data_validation: Some(true),
2087 ..base
2088 },
2089 IgnoredErrorsType::CalculatedColumn => XlsxIgnoredError {
2090 calculated_column: Some(true),
2091 ..base
2092 },
2093 }
2094}
2095
2096fn same_ignored_error_flags(a: &XlsxIgnoredError, b: &XlsxIgnoredError) -> bool {
2097 a.eval_error == b.eval_error
2098 && a.two_digit_text_year == b.two_digit_text_year
2099 && a.number_stored_as_text == b.number_stored_as_text
2100 && a.formula == b.formula
2101 && a.formula_range == b.formula_range
2102 && a.unlocked_formula == b.unlocked_formula
2103 && a.empty_cell_reference == b.empty_cell_reference
2104 && a.list_data_validation == b.list_data_validation
2105 && a.calculated_column == b.calculated_column
2106}
2107
2108fn adjust_range_sheet_name(data: &str, source: &str, target: &str) -> String {
2109 let mut out = data.to_string();
2111 let patterns = vec![format!("{source}!"), format!("'{source}'!")];
2112 for p in patterns {
2113 out = out.replace(&p, &format!("{target}!"));
2114 }
2115 out
2116}
2117
2118pub(crate) fn ensure_shared_strings_rel(rels: &mut crate::xml::workbook::XlsxRelationships) {
2119 for rel in &rels.relationships {
2120 if rel.r#type == SOURCE_RELATIONSHIP_SHARED_STRINGS {
2121 return;
2122 }
2123 }
2124 let mut max_rid = 0i32;
2125 for rel in &rels.relationships {
2126 if let Ok(n) = rel.id.trim_start_matches("rId").parse::<i32>() {
2127 if n > max_rid {
2128 max_rid = n;
2129 }
2130 }
2131 }
2132 rels.relationships
2133 .push(crate::xml::workbook::XlsxRelationship {
2134 id: format!("rId{}", max_rid + 1),
2135 r#type: SOURCE_RELATIONSHIP_SHARED_STRINGS.to_string(),
2136 target: "sharedStrings.xml".to_string(),
2137 target_mode: None,
2138 });
2139}
2140
2141#[cfg(test)]
2142mod tests {
2143 use super::*;
2144
2145 #[test]
2146 fn new_sheet_round_trip() {
2147 let f = File::new_with_options(crate::options::Options::default());
2148 let idx = f.new_sheet("Sheet2").unwrap();
2149 assert_eq!(idx, 2);
2150 assert_eq!(f.get_sheet_name(idx).unwrap(), "Sheet2");
2151 assert_eq!(f.get_sheet_index("Sheet2").unwrap(), idx);
2152 assert!(f.get_sheet_list().contains(&"Sheet2".to_string()));
2153 }
2154
2155 #[test]
2156 fn sheet_name_and_map_round_trip() {
2157 let f = File::new_with_options(crate::options::Options::default());
2158 f.new_sheet("Sheet2").unwrap();
2159 f.set_sheet_name("Sheet2", "NewName").unwrap();
2160 assert_eq!(f.get_sheet_index("NewName").unwrap(), 2);
2161 let map = f.get_sheet_map();
2162 assert!(map.values().any(|n| n == "NewName"));
2163 }
2164
2165 #[test]
2166 fn sheet_visible_round_trip() {
2167 let f = File::new_with_options(crate::options::Options::default());
2168 f.new_sheet("Sheet2").unwrap();
2169 assert!(f.get_sheet_visible("Sheet2").unwrap());
2170 f.set_sheet_visible("Sheet2", false, None).unwrap();
2171 assert!(!f.get_sheet_visible("Sheet2").unwrap());
2172 f.set_sheet_visible("Sheet2", true, None).unwrap();
2173 assert!(f.get_sheet_visible("Sheet2").unwrap());
2174 }
2175
2176 #[test]
2177 fn panes_round_trip() {
2178 let f = File::new_with_options(crate::options::Options::default());
2179 let panes = Panes {
2180 freeze: true,
2181 x_split: 1,
2182 top_left_cell: "B1".to_string(),
2183 active_pane: "topRight".to_string(),
2184 selection: vec![Selection {
2185 sqref: "K16".to_string(),
2186 active_cell: "K16".to_string(),
2187 pane: "topRight".to_string(),
2188 }],
2189 ..Default::default()
2190 };
2191 f.set_panes("Sheet1", &panes).unwrap();
2192 let got = f.get_panes("Sheet1").unwrap();
2193 assert_eq!(got.freeze, true);
2194 assert_eq!(got.x_split, 1);
2195 assert_eq!(got.top_left_cell, "B1");
2196 }
2197
2198 #[test]
2199 fn sheet_protection_round_trip() {
2200 let f = File::new_with_options(crate::options::Options::default());
2201 let opts = SheetProtectionOptions {
2202 password: "password".to_string(),
2203 select_locked_cells: true,
2204 ..Default::default()
2205 };
2206 f.protect_sheet("Sheet1", &opts).unwrap();
2207 let got = f.get_sheet_protection("Sheet1").unwrap();
2208 assert!(got.select_locked_cells);
2209 f.unprotect_sheet("Sheet1", Some("password")).unwrap();
2210 let got = f.get_sheet_protection("Sheet1").unwrap();
2211 assert!(!got.select_locked_cells);
2212 }
2213
2214 #[test]
2215 fn page_break_round_trip() {
2216 let f = File::new_with_options(crate::options::Options::default());
2217 f.insert_page_break("Sheet1", "B2").unwrap();
2218 let ws = f.work_sheet_reader("Sheet1").unwrap();
2219 assert!(ws.row_breaks.is_some());
2220 assert!(ws.col_breaks.is_some());
2221 f.remove_page_break("Sheet1", "B2").unwrap();
2222 let ws = f.work_sheet_reader("Sheet1").unwrap();
2223 assert!(ws.row_breaks.as_ref().unwrap().breaks.brk.is_empty());
2224 }
2225
2226 #[test]
2227 fn sheet_dimension_round_trip() {
2228 let f = File::new_with_options(crate::options::Options::default());
2229 f.set_sheet_dimension("Sheet1", "A1:D5").unwrap();
2230 assert_eq!(f.get_sheet_dimension("Sheet1").unwrap(), "A1:D5");
2231 f.set_sheet_dimension("Sheet1", "").unwrap();
2232 assert!(f.get_sheet_dimension("Sheet1").unwrap().is_empty());
2233 }
2234
2235 #[test]
2236 fn search_sheet_basic() {
2237 let f = File::new_with_options(crate::options::Options::default());
2238 f.set_cell_str("Sheet1", "A1", "hello").unwrap();
2239 f.set_cell_str("Sheet1", "B2", "world").unwrap();
2240 let refs = f.search_sheet("Sheet1", "hello", None).unwrap();
2241 assert!(refs.contains(&"A1".to_string()));
2242 }
2243
2244 #[test]
2245 fn search_sheet_shared_string() {
2246 let f = File::new_with_options(crate::options::Options::default());
2247 f.set_cell_str("Sheet1", "A1", "shared_text").unwrap();
2248 let refs = f.search_sheet("Sheet1", "shared_text", None).unwrap();
2249 assert_eq!(refs, vec!["A1".to_string()]);
2250 }
2251
2252 #[test]
2253 fn search_sheet_regex() {
2254 let f = File::new_with_options(crate::options::Options::default());
2255 f.set_cell_str("Sheet1", "A1", "hello world").unwrap();
2256 f.set_cell_str("Sheet1", "B2", "hello rust").unwrap();
2257 let refs = f.search_sheet("Sheet1", r"hello \w+", Some(true)).unwrap();
2258 assert_eq!(refs.len(), 2);
2259 assert!(refs.contains(&"A1".to_string()));
2260 assert!(refs.contains(&"B2".to_string()));
2261 }
2262
2263 #[test]
2264 fn search_sheet_inline_string() {
2265 let f = File::new_with_options(crate::options::Options::default());
2266 f.set_cell_value(
2267 "Sheet1",
2268 "A1",
2269 vec![crate::xml::common::RichTextRun {
2270 text: "inline".to_string(),
2271 ..Default::default()
2272 }],
2273 )
2274 .unwrap();
2275 let refs = f.search_sheet("Sheet1", "inline", None).unwrap();
2276 assert_eq!(refs, vec!["A1".to_string()]);
2277 }
2278
2279 #[test]
2280 fn search_sheet_rich_text_shared_string() {
2281 let f = File::new_with_options(crate::options::Options::default());
2282 let runs = vec![
2283 crate::xml::common::RichTextRun {
2284 text: "bold".to_string(),
2285 ..Default::default()
2286 },
2287 crate::xml::common::RichTextRun {
2288 text: "text".to_string(),
2289 ..Default::default()
2290 },
2291 ];
2292 f.set_cell_rich_text("Sheet1", "A1", runs).unwrap();
2293 let refs = f.search_sheet("Sheet1", "boldtext", None).unwrap();
2294 assert_eq!(refs, vec!["A1".to_string()]);
2295 }
2296
2297 #[test]
2298 fn search_sheet_merge_cells() {
2299 let f = File::new_with_options(crate::options::Options::default());
2300 f.set_cell_str("Sheet1", "A1", "merged").unwrap();
2301 f.merge_cell("Sheet1", "A1", "B2").unwrap();
2302 let refs = f.search_sheet("Sheet1", "merged", None).unwrap();
2303 assert_eq!(refs, vec!["A1".to_string()]);
2304 }
2305
2306 #[test]
2307 fn search_sheet_after_save_and_reopen() {
2308 let f = File::new_with_options(crate::options::Options::default());
2309 f.set_cell_str("Sheet1", "A1", "persisted").unwrap();
2310 let buf = f.write_to_buffer().unwrap();
2311 let f2 = File::open_reader(
2312 std::io::Cursor::new(buf),
2313 0,
2314 crate::options::Options::default(),
2315 )
2316 .unwrap();
2317 let refs = f2.search_sheet("Sheet1", "persisted", None).unwrap();
2318 assert_eq!(refs, vec!["A1".to_string()]);
2319 }
2320
2321 #[test]
2322 fn ignored_errors_round_trip() {
2323 let f = File::new_with_options(crate::options::Options::default());
2324 f.add_ignored_errors("Sheet1", "A1:A2", IgnoredErrorsType::NumberStoredAsText)
2325 .unwrap();
2326 let ws = f.work_sheet_reader("Sheet1").unwrap();
2327 let errors = ws.ignored_errors.as_ref().unwrap();
2328 assert_eq!(errors.ignored_error.len(), 1);
2329 assert!(errors.ignored_error[0].number_stored_as_text.unwrap());
2330 }
2331
2332 #[test]
2333 fn move_and_copy_sheet() {
2334 let f = File::new_with_options(crate::options::Options::default());
2335 f.new_sheet("Sheet2").unwrap();
2336 f.new_sheet("Sheet3").unwrap();
2337 f.move_sheet("Sheet3", "Sheet1").unwrap();
2338 let list = f.get_sheet_list();
2339 assert_eq!(list[0], "Sheet3");
2340 f.copy_sheet(1, 2).unwrap();
2341 }
2342
2343 #[test]
2344 fn sheet_background_from_bytes() {
2345 let f = File::new_with_options(crate::options::Options::default());
2346 let png = vec![
2348 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48,
2349 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00,
2350 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08,
2351 0x99, 0x63, 0xf8, 0x0f, 0x00, 0x00, 0x01, 0x01, 0x00, 0x05, 0x18, 0xd8, 0x4e, 0x00,
2352 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
2353 ];
2354 f.set_sheet_background_from_bytes("Sheet1", "png", &png)
2355 .unwrap();
2356 let ws = f.work_sheet_reader("Sheet1").unwrap();
2357 assert!(ws.picture.is_some());
2358 }
2359
2360 #[test]
2361 fn sheet_props_round_trip() {
2362 let f = File::new_with_options(crate::options::Options::default());
2363 let expected = SheetPropsOptions {
2364 code_name: Some("code".to_string()),
2365 enable_format_conditions_calculation: Some(true),
2366 published: Some(true),
2367 auto_page_breaks: Some(true),
2368 fit_to_page: Some(true),
2369 tab_color_indexed: Some(1),
2370 tab_color_rgb: Some("FFFF00".to_string()),
2371 tab_color_theme: Some(1),
2372 tab_color_tint: Some(1.0),
2373 outline_pr_summary_below: Some(true),
2374 outline_pr_summary_right: Some(true),
2375 base_col_width: Some(8),
2376 default_col_width: Some(10.0),
2377 default_row_height: Some(10.0),
2378 custom_height: Some(true),
2379 zero_height: Some(true),
2380 thick_top: Some(true),
2381 thick_bottom: Some(true),
2382 ..Default::default()
2383 };
2384 f.set_sheet_props("Sheet1", &expected).unwrap();
2385 let got = f.get_sheet_props("Sheet1").unwrap();
2386 assert_eq!(expected, got);
2387 }
2388
2389 #[test]
2390 fn page_margins_round_trip() {
2391 let f = File::new_with_options(crate::options::Options::default());
2392 let expected = PageMarginsOptions {
2393 left: Some(1.0),
2394 right: Some(1.0),
2395 top: Some(1.0),
2396 bottom: Some(1.0),
2397 header: Some(1.0),
2398 footer: Some(1.0),
2399 horizontally: Some(true),
2400 vertically: Some(true),
2401 };
2402 f.set_page_margins("Sheet1", &expected).unwrap();
2403 let got = f.get_page_margins("Sheet1").unwrap();
2404 assert_eq!(expected, got);
2405 }
2406
2407 #[test]
2408 fn sheet_view_round_trip() {
2409 let f = File::new_with_options(crate::options::Options::default());
2410 let expected = SheetViewOptions {
2411 default_grid_color: Some(false),
2412 right_to_left: Some(false),
2413 show_formulas: Some(false),
2414 show_grid_lines: Some(false),
2415 show_row_col_headers: Some(false),
2416 show_ruler: Some(false),
2417 show_zeros: Some(false),
2418 top_left_cell: Some("A1".to_string()),
2419 view: Some("normal".to_string()),
2420 zoom_scale: Some(120.0),
2421 tab_selected: Some(true),
2423 show_outline_symbols: Some(false),
2425 show_white_space: Some(false),
2426 window_protection: Some(false),
2427 };
2428 f.set_sheet_view("Sheet1", 0, &expected).unwrap();
2429 let got = f.get_sheet_view("Sheet1", 0).unwrap();
2430 assert_eq!(expected, got);
2431
2432 f.set_sheet_view("Sheet1", -1, &expected).unwrap();
2434 let got = f.get_sheet_view("Sheet1", -1).unwrap();
2435 assert_eq!(expected, got);
2436
2437 let invalid_view = SheetViewOptions {
2439 view: Some("invalid".to_string()),
2440 ..Default::default()
2441 };
2442 f.set_sheet_view("Sheet1", 0, &invalid_view).unwrap();
2443 let got = f.get_sheet_view("Sheet1", 0).unwrap();
2444 assert_eq!(Some("normal".to_string()), got.view);
2445
2446 let invalid_zoom = SheetViewOptions {
2448 zoom_scale: Some(5.0),
2449 ..Default::default()
2450 };
2451 f.set_sheet_view("Sheet1", 0, &invalid_zoom).unwrap();
2452 let got = f.get_sheet_view("Sheet1", 0).unwrap();
2453 assert_eq!(Some(120.0), got.zoom_scale);
2454
2455 let empty = SheetViewOptions::default();
2457 assert_eq!(
2458 "view index 1 out of range",
2459 f.set_sheet_view("Sheet1", 1, &empty)
2460 .unwrap_err()
2461 .to_string()
2462 );
2463 assert_eq!(
2464 "view index -2 out of range",
2465 f.set_sheet_view("Sheet1", -2, &empty)
2466 .unwrap_err()
2467 .to_string()
2468 );
2469 assert!(f.get_sheet_view("Sheet1", 1).is_err());
2470 assert!(f.get_sheet_view("Sheet1", -2).is_err());
2471
2472 assert!(f.set_sheet_view("SheetN", 0, &empty).is_err());
2474 assert!(f.get_sheet_view("SheetN", 0).is_err());
2475 }
2476
2477 #[test]
2478 fn sheet_view_page_break_preview() {
2479 let f = File::new_with_options(crate::options::Options::default());
2480 let opts = SheetViewOptions {
2481 view: Some("pageBreakPreview".to_string()),
2482 zoom_scale: Some(200.0),
2483 ..Default::default()
2484 };
2485 f.set_sheet_view("Sheet1", 0, &opts).unwrap();
2486 let got = f.get_sheet_view("Sheet1", 0).unwrap();
2487 assert_eq!(Some("pageBreakPreview".to_string()), got.view);
2488 assert_eq!(Some(200.0), got.zoom_scale);
2489 }
2490
2491 #[test]
2492 fn sheet_props_defaults() {
2493 let f = File::new_with_options(crate::options::Options::default());
2494 let opts = f.get_sheet_props("Sheet1").unwrap();
2495 assert_eq!(Some(true), opts.enable_format_conditions_calculation);
2496 assert_eq!(Some(true), opts.published);
2497 assert_eq!(Some(true), opts.auto_page_breaks);
2498 assert_eq!(Some(true), opts.outline_pr_summary_below);
2499 assert_eq!(Some(8), opts.base_col_width);
2500 assert_eq!(Some(15.0), opts.default_row_height);
2501 }
2502
2503 #[test]
2504 fn page_margins_defaults() {
2505 let f = File::new_with_options(crate::options::Options::default());
2506 let opts = f.get_page_margins("Sheet1").unwrap();
2507 assert_eq!(Some(0.7), opts.left);
2508 assert_eq!(Some(0.7), opts.right);
2509 assert_eq!(Some(0.75), opts.top);
2510 assert_eq!(Some(0.75), opts.bottom);
2511 assert_eq!(Some(0.3), opts.header);
2512 assert_eq!(Some(0.3), opts.footer);
2513 }
2514}