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