UPDATE: Dynamic arrays!
This commit is contained in:
@@ -22,7 +22,7 @@ use itertools::Itertools;
|
||||
|
||||
use ironcalc_base::{
|
||||
expressions::{
|
||||
parser::{stringify::to_excel_string, Node},
|
||||
parser::{static_analysis::StaticResult, stringify::to_excel_string, Node},
|
||||
types::CellReferenceRC,
|
||||
utils::number_to_column,
|
||||
},
|
||||
@@ -56,7 +56,7 @@ fn get_formula_attribute(
|
||||
|
||||
pub(crate) fn get_worksheet_xml(
|
||||
worksheet: &Worksheet,
|
||||
parsed_formulas: &[Node],
|
||||
parsed_formulas: &[(Node, StaticResult)],
|
||||
dimension: &str,
|
||||
is_sheet_selected: bool,
|
||||
) -> String {
|
||||
@@ -104,7 +104,7 @@ pub(crate) fn get_worksheet_xml(
|
||||
let style = get_cell_style_attribute(*s);
|
||||
row_data_str.push(format!("<c r=\"{cell_name}\"{style}/>"));
|
||||
}
|
||||
Cell::BooleanCell { v, s } => {
|
||||
Cell::SpillBooleanCell { v, s, .. } | Cell::BooleanCell { v, s } => {
|
||||
// <c r="A8" t="b" s="1">
|
||||
// <v>1</v>
|
||||
// </c>
|
||||
@@ -114,7 +114,7 @@ pub(crate) fn get_worksheet_xml(
|
||||
"<c r=\"{cell_name}\" t=\"b\"{style}><v>{b}</v></c>"
|
||||
));
|
||||
}
|
||||
Cell::NumberCell { v, s } => {
|
||||
Cell::SpillNumberCell { v, s, .. } | Cell::NumberCell { v, s } => {
|
||||
// Normally the type number is left out. Example:
|
||||
// <c r="C6" s="1">
|
||||
// <v>3</v>
|
||||
@@ -122,7 +122,7 @@ pub(crate) fn get_worksheet_xml(
|
||||
let style = get_cell_style_attribute(*s);
|
||||
row_data_str.push(format!("<c r=\"{cell_name}\"{style}><v>{v}</v></c>"));
|
||||
}
|
||||
Cell::ErrorCell { ei, s } => {
|
||||
Cell::SpillErrorCell { ei, s, .. } | Cell::ErrorCell { ei, s } => {
|
||||
let style = get_cell_style_attribute(*s);
|
||||
row_data_str.push(format!(
|
||||
"<c r=\"{cell_name}\" t=\"e\"{style}><v>{ei}</v></c>"
|
||||
@@ -153,7 +153,7 @@ pub(crate) fn get_worksheet_xml(
|
||||
worksheet.get_name(),
|
||||
*row_index,
|
||||
*column_index,
|
||||
&parsed_formulas[*f as usize],
|
||||
&parsed_formulas[*f as usize].0,
|
||||
);
|
||||
|
||||
let b = i32::from(*v);
|
||||
@@ -172,7 +172,7 @@ pub(crate) fn get_worksheet_xml(
|
||||
worksheet.get_name(),
|
||||
*row_index,
|
||||
*column_index,
|
||||
&parsed_formulas[*f as usize],
|
||||
&parsed_formulas[*f as usize].0,
|
||||
);
|
||||
let style = get_cell_style_attribute(*s);
|
||||
|
||||
@@ -189,14 +189,14 @@ pub(crate) fn get_worksheet_xml(
|
||||
worksheet.get_name(),
|
||||
*row_index,
|
||||
*column_index,
|
||||
&parsed_formulas[*f as usize],
|
||||
&parsed_formulas[*f as usize].0,
|
||||
);
|
||||
let style = get_cell_style_attribute(*s);
|
||||
let escaped_v = escape_xml(v);
|
||||
|
||||
row_data_str.push(format!(
|
||||
"<c r=\"{cell_name}\" t=\"str\"{style}><f>{formula}</f><v>{escaped_v}</v></c>"
|
||||
));
|
||||
"<c r=\"{cell_name}\" t=\"str\"{style}><f>{formula}</f><v>{escaped_v}</v></c>"
|
||||
));
|
||||
}
|
||||
Cell::CellFormulaError {
|
||||
f,
|
||||
@@ -213,13 +213,135 @@ pub(crate) fn get_worksheet_xml(
|
||||
worksheet.get_name(),
|
||||
*row_index,
|
||||
*column_index,
|
||||
&parsed_formulas[*f as usize],
|
||||
&parsed_formulas[*f as usize].0,
|
||||
);
|
||||
let style = get_cell_style_attribute(*s);
|
||||
row_data_str.push(format!(
|
||||
"<c r=\"{cell_name}\" t=\"e\"{style}><f>{formula}</f><v>{ei}</v></c>"
|
||||
));
|
||||
}
|
||||
Cell::SpillStringCell { v, s, .. } => {
|
||||
// inline string
|
||||
// <c r="A1" t="str">
|
||||
let style = get_cell_style_attribute(*s);
|
||||
let escaped_v = escape_xml(v);
|
||||
row_data_str.push(format!(
|
||||
"<c r=\"{cell_name}\" t=\"str\"{style}><v>{escaped_v}</v></c>"
|
||||
));
|
||||
}
|
||||
Cell::DynamicCellFormula { .. } => {
|
||||
panic!("Model needs to be evaluated before saving!");
|
||||
}
|
||||
Cell::DynamicCellFormulaBoolean { f, v, s, r, a: _ } => {
|
||||
// <c r="A1" s="3" cm="1">
|
||||
// <f t="array" ref="A1:A10">A1:A10</f>
|
||||
// <v>1</v>
|
||||
// </c>
|
||||
let style = get_cell_style_attribute(*s);
|
||||
let range = format!(
|
||||
"{}{}:{}{}",
|
||||
column_name,
|
||||
row_index,
|
||||
number_to_column(r.0 + column_index).unwrap(),
|
||||
r.1 + row_index
|
||||
);
|
||||
|
||||
let formula = get_formula_attribute(
|
||||
worksheet.get_name(),
|
||||
*row_index,
|
||||
*column_index,
|
||||
&parsed_formulas[*f as usize].0,
|
||||
);
|
||||
|
||||
let b = i32::from(*v);
|
||||
row_data_str.push(format!(
|
||||
r#"<c r="{cell_name}" t="b" s="{style}" cm="1"><f t="array" ref="{range}">{formula}</f><v>{b}</v></c>"#
|
||||
));
|
||||
}
|
||||
Cell::DynamicCellFormulaNumber { f, v, s, r, a: _ } => {
|
||||
// <c r="C4" s="3" cm="1">
|
||||
// <f t="array" ref="C4:C10">C4:C10</f>
|
||||
// <v>123</v>
|
||||
// </c>
|
||||
let style = get_cell_style_attribute(*s);
|
||||
let range = format!(
|
||||
"{}{}:{}{}",
|
||||
column_name,
|
||||
row_index,
|
||||
number_to_column(r.0 + column_index).unwrap(),
|
||||
r.1 + row_index
|
||||
);
|
||||
|
||||
let formula = get_formula_attribute(
|
||||
worksheet.get_name(),
|
||||
*row_index,
|
||||
*column_index,
|
||||
&parsed_formulas[*f as usize].0,
|
||||
);
|
||||
|
||||
row_data_str.push(format!(
|
||||
r#"<c r="{cell_name}" s="{style}" cm="1"><f t="array" ref="{range}">{formula}</f><v>{v}</v></c>"#
|
||||
));
|
||||
}
|
||||
Cell::DynamicCellFormulaString { f, v, s, r, a: _ } => {
|
||||
// <c r="C6" t="str" s="5" cm="1">
|
||||
// <f t="array" ref="C6:C10">C6:C10</f>
|
||||
// <v>Hello world!</v>
|
||||
// </c>
|
||||
let style = get_cell_style_attribute(*s);
|
||||
let range = format!(
|
||||
"{}{}:{}{}",
|
||||
column_name,
|
||||
row_index,
|
||||
number_to_column(r.0 + column_index).unwrap(),
|
||||
r.1 + row_index
|
||||
);
|
||||
|
||||
let formula = get_formula_attribute(
|
||||
worksheet.get_name(),
|
||||
*row_index,
|
||||
*column_index,
|
||||
&parsed_formulas[*f as usize].0,
|
||||
);
|
||||
let escaped_v = escape_xml(v);
|
||||
|
||||
row_data_str.push(format!(
|
||||
r#"<c r="{cell_name}" t="str" s="{style}" cm="1"><f t="array" ref="{range}">{formula}</f><v>{escaped_v}</v></c>"#
|
||||
));
|
||||
}
|
||||
Cell::DynamicCellFormulaError {
|
||||
f,
|
||||
ei,
|
||||
s,
|
||||
o: _,
|
||||
m: _,
|
||||
r,
|
||||
a: _,
|
||||
} => {
|
||||
// <c r="C6" t="e" s="4" cm="1">
|
||||
// <f t="array" ref="C6:C10">C6:C10</f>
|
||||
// <v>#DIV/0!</v>
|
||||
// </c>
|
||||
let style = get_cell_style_attribute(*s);
|
||||
let range = format!(
|
||||
"{}{}:{}{}",
|
||||
column_name,
|
||||
row_index,
|
||||
number_to_column(r.0 + column_index).unwrap(),
|
||||
r.1 + row_index
|
||||
);
|
||||
|
||||
let formula = get_formula_attribute(
|
||||
worksheet.get_name(),
|
||||
*row_index,
|
||||
*column_index,
|
||||
&parsed_formulas[*f as usize].0,
|
||||
);
|
||||
|
||||
row_data_str.push(format!(
|
||||
r#"<c r="{cell_name}" t="e" s="{style}" cm="1"><f t="array" ref="{range}">{formula}</f><v>{ei}</v></c>"#
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
let row_style_str = match row_style_dict.get(row_index) {
|
||||
|
||||
@@ -304,13 +304,15 @@ fn from_a1_to_rc(
|
||||
context: String,
|
||||
tables: HashMap<String, Table>,
|
||||
defined_names: Vec<DefinedNameS>,
|
||||
is_array: bool,
|
||||
) -> Result<String, XlsxError> {
|
||||
let mut parser = Parser::new(worksheets.to_owned(), defined_names, tables);
|
||||
let cell_reference =
|
||||
parse_reference(&context).map_err(|error| XlsxError::Xml(error.to_string()))?;
|
||||
let mut t = parser.parse(&formula, &cell_reference);
|
||||
add_implicit_intersection(&mut t, true);
|
||||
|
||||
if !is_array {
|
||||
add_implicit_intersection(&mut t, true);
|
||||
}
|
||||
Ok(to_rc_format(&t))
|
||||
}
|
||||
|
||||
@@ -838,6 +840,7 @@ pub(super) fn load_sheet<R: Read + std::io::Seek>(
|
||||
};
|
||||
|
||||
let cell_metadata = cell.attribute("cm");
|
||||
let is_dynamic_array = cell_metadata == Some("1");
|
||||
|
||||
// type, the default type being "n" for number
|
||||
// If the cell does not have a value is an empty cell
|
||||
@@ -904,6 +907,7 @@ pub(super) fn load_sheet<R: Read + std::io::Seek>(
|
||||
context,
|
||||
tables.clone(),
|
||||
defined_names.clone(),
|
||||
is_dynamic_array,
|
||||
)?;
|
||||
match index_map.get(&si) {
|
||||
Some(index) => {
|
||||
@@ -952,7 +956,6 @@ pub(super) fn load_sheet<R: Read + std::io::Seek>(
|
||||
return Err(XlsxError::NotImplemented("data table formulas".to_string()));
|
||||
}
|
||||
"array" | "normal" => {
|
||||
let is_dynamic_array = cell_metadata == Some("1");
|
||||
if formula_type == "array" && !is_dynamic_array {
|
||||
// Dynamic formulas in Excel are formulas of type array with the cm=1, those we support.
|
||||
// On the other hand the old CSE formulas or array formulas are not supported in IronCalc for the time being
|
||||
@@ -967,6 +970,7 @@ pub(super) fn load_sheet<R: Read + std::io::Seek>(
|
||||
context,
|
||||
tables.clone(),
|
||||
defined_names.clone(),
|
||||
is_dynamic_array,
|
||||
)?;
|
||||
|
||||
match get_formula_index(&formula, &shared_formulas) {
|
||||
|
||||
BIN
xlsx/tests/calc_tests/simple_spill.xlsx
Normal file
BIN
xlsx/tests/calc_tests/simple_spill.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user