From c3a9b006d2f54d7bc408b3f6b858910292e075b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Hatcher=20Andr=C3=A9s?= Date: Tue, 28 May 2024 21:52:10 +0200 Subject: [PATCH] FIX: Export views and showGridLines properly (#72) --- xlsx/src/export/mod.rs | 8 ++++- xlsx/src/export/workbook.rs | 5 ++- xlsx/src/export/worksheets.rs | 37 ++++++++++++++++++-- xlsx/tests/test.rs | 65 ++++++++++++++++++++++++++++------- 4 files changed, 98 insertions(+), 17 deletions(-) diff --git a/xlsx/src/export/mod.rs b/xlsx/src/export/mod.rs index 4dad2a3..a5973f0 100644 --- a/xlsx/src/export/mod.rs +++ b/xlsx/src/export/mod.rs @@ -68,6 +68,10 @@ pub fn save_to_xlsx(model: &Model, file_name: &str) -> Result<(), XlsxError> { pub fn save_xlsx_to_writer(model: &Model, writer: W) -> Result { let workbook = &model.workbook; + let selected_sheet = match workbook.views.get(&0) { + Some(view) => view.sheet, + _ => 0, + }; let mut zip = zip::ZipWriter::new(writer); let options = zip::write::FileOptions::default(); @@ -94,7 +98,7 @@ pub fn save_xlsx_to_writer(model: &Model, writer: W) -> Result< zip.start_file("xl/styles.xml", options)?; zip.write_all(styles::get_styles_xml(workbook).as_bytes())?; zip.start_file("xl/workbook.xml", options)?; - zip.write_all(workbook::get_workbook_xml(workbook).as_bytes())?; + zip.write_all(workbook::get_workbook_xml(workbook, selected_sheet).as_bytes())?; zip.add_directory("xl/_rels", options)?; zip.start_file("xl/_rels/workbook.xml.rels", options)?; @@ -114,11 +118,13 @@ pub fn save_xlsx_to_writer(model: &Model, writer: W) -> Result< let min_row = dimension.min_row; let max_row = dimension.max_row; let sheet_dimension_str = &format!("{column_min_str}{min_row}:{column_max_str}{max_row}"); + let is_sheet_selected = selected_sheet as usize == sheet_index; zip.write_all( worksheets::get_worksheet_xml( worksheet, &model.parsed_formulas[sheet_index], sheet_dimension_str, + is_sheet_selected, ) .as_bytes(), )?; diff --git a/xlsx/src/export/workbook.rs b/xlsx/src/export/workbook.rs index 6ccd6b8..b8d5105 100644 --- a/xlsx/src/export/workbook.rs +++ b/xlsx/src/export/workbook.rs @@ -34,7 +34,7 @@ use ironcalc_base::types::{SheetState, Workbook}; use super::escape::escape_xml; use super::xml_constants::XML_DECLARATION; -pub(crate) fn get_workbook_xml(workbook: &Workbook) -> String { +pub(crate) fn get_workbook_xml(workbook: &Workbook, selected_sheet: u32) -> String { // sheets // let mut sheets_str: Vec = vec![]; @@ -80,6 +80,9 @@ pub(crate) fn get_workbook_xml(workbook: &Workbook) -> String { let defined_names = defined_names_str.join(""); format!("{XML_DECLARATION}\n\ \ + + \ + \ {sheets}\ \ diff --git a/xlsx/src/export/worksheets.rs b/xlsx/src/export/worksheets.rs index f54ecfc..c31a0fe 100644 --- a/xlsx/src/export/worksheets.rs +++ b/xlsx/src/export/worksheets.rs @@ -55,6 +55,7 @@ pub(crate) fn get_worksheet_xml( worksheet: &Worksheet, parsed_formulas: &[Node], dimension: &str, + is_sheet_selected: bool, ) -> String { let mut sheet_data_str: Vec = vec![]; let mut cols_str: Vec = vec![]; @@ -247,6 +248,38 @@ pub(crate) fn get_worksheet_xml( format!("{cols}") }; + let tab_selected = if is_sheet_selected { + " tabSelected=\"1\"" + } else { + "" + }; + + let show_grid_lines = if !worksheet.show_grid_lines { + " showGridLines=\"0\"" + } else { + "" + }; + + let mut active_cell = "A1".to_string(); + let mut sqref = "A1".to_string(); + + let views = &worksheet.views; + if let Some(view) = views.get(&0) { + let range = view.range; + let row = view.row; + let column = view.column; + let column_name = number_to_column(column).unwrap_or("A".to_string()); + active_cell = format!("{column_name}{row}"); + + let column_start = number_to_column(range[1]).unwrap_or("A".to_string()); + let column_end = number_to_column(range[3]).unwrap_or("A".to_string()); + if range[0] == range[2] && range[1] == range[3] { + sqref = format!("{column_start}{}", range[0]); + } else { + sqref = format!("{}{}:{}{}", column_start, range[0], column_end, range[2]); + } + } + format!( "{XML_DECLARATION} \ \ \ - \ - \ + \ + \ \ \ {cols}\ diff --git a/xlsx/tests/test.rs b/xlsx/tests/test.rs index 822d32c..7d42435 100644 --- a/xlsx/tests/test.rs +++ b/xlsx/tests/test.rs @@ -51,22 +51,47 @@ fn test_example() { #[test] fn no_grid() { let model = load_from_xlsx("tests/NoGrid.xlsx", "en", "UTC").unwrap(); - let workbook = model.workbook; - let ws = &workbook.worksheets; + { + let workbook = &model.workbook; + let ws = &workbook.worksheets; - // NoGrid does not show grid lines - let no_grid_sheet = &ws[0]; - assert_eq!(no_grid_sheet.name, "NoGrid".to_string()); - assert!(!no_grid_sheet.show_grid_lines); + // NoGrid does not show grid lines + let no_grid_sheet = &ws[0]; + assert_eq!(no_grid_sheet.name, "NoGrid".to_string()); + assert!(!no_grid_sheet.show_grid_lines); - let sheet2 = &ws[1]; - assert_eq!(no_grid_sheet.name, "NoGrid".to_string()); - assert!(sheet2.show_grid_lines); + let sheet2 = &ws[1]; + assert_eq!(no_grid_sheet.name, "NoGrid".to_string()); + assert!(sheet2.show_grid_lines); - let no_grid_no_headers_sheet = &ws[2]; - assert_eq!(no_grid_sheet.name, "NoGrid".to_string()); - // There is also no headers - assert!(!no_grid_no_headers_sheet.show_grid_lines); + let no_grid_no_headers_sheet = &ws[2]; + assert_eq!(no_grid_sheet.name, "NoGrid".to_string()); + // There is also no headers + assert!(!no_grid_no_headers_sheet.show_grid_lines); + } + { + // save it and check again + let temp_file_name = "temp_file_no_grid.xlsx"; + save_to_xlsx(&model, temp_file_name).unwrap(); + let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap(); + let workbook = &model.workbook; + let ws = &workbook.worksheets; + + // NoGrid does not show grid lines + let no_grid_sheet = &ws[0]; + assert_eq!(no_grid_sheet.name, "NoGrid".to_string()); + assert!(!no_grid_sheet.show_grid_lines); + + let sheet2 = &ws[1]; + assert_eq!(no_grid_sheet.name, "NoGrid".to_string()); + assert!(sheet2.show_grid_lines); + + let no_grid_no_headers_sheet = &ws[2]; + assert_eq!(no_grid_sheet.name, "NoGrid".to_string()); + // There is also no headers + assert!(!no_grid_no_headers_sheet.show_grid_lines); + fs::remove_file(temp_file_name).unwrap(); + } } #[test] @@ -82,6 +107,20 @@ fn test_save_to_xlsx() { assert_eq!(metadata.application, "IronCalc Sheets"); // FIXME: This will need to be updated once we fix versioning assert_eq!(metadata.app_version, "10.0000"); + + let workbook = model.workbook; + let ws = &workbook.worksheets; + + assert_eq!(workbook.views[&0].sheet, 7); + + // Test selection: + // First sheet (Sheet1) + // E13 and E13:N20 + assert_eq!(ws[0].frozen_rows, 0); + assert_eq!(ws[0].frozen_columns, 0); + assert_eq!(ws[0].views[&0].row, 13); + assert_eq!(ws[0].views[&0].column, 5); + assert_eq!(ws[0].views[&0].range, [13, 5, 20, 14]); // TODO: can we show it is the 'same' model? fs::remove_file(temp_file_name).unwrap(); }