#[path = "xlsx_parts.rs"]
mod xlsx_parts;
use crate::chart::data::{BubbleChartData, CategoryChartData, XyChartData};
use crate::error::PptxResult;
use xlsx_parts::{build_xlsx, shared_string_index, CellValue};
pub fn generate_category_xlsx(data: &CategoryChartData) -> PptxResult<Vec<u8>> {
let categories = data.categories();
let series = data.series();
let mut shared_strings: Vec<String> = Vec::new();
for s in series {
shared_strings.push(s.name().to_string());
}
for cat in categories {
shared_strings.push(cat.clone());
}
let mut rows: Vec<Vec<CellValue>> = Vec::new();
let mut header = vec![CellValue::Empty];
for s in series {
let idx = shared_string_index(&shared_strings, s.name()).ok_or_else(|| {
crate::error::PptxError::InvalidXml(format!(
"series name {:?} not found in shared strings",
s.name()
))
})?;
header.push(CellValue::SharedString(idx));
}
rows.push(header);
for (cat_idx, cat_label) in categories.iter().enumerate() {
let cat_ss_idx = shared_string_index(&shared_strings, cat_label).ok_or_else(|| {
crate::error::PptxError::InvalidXml(format!(
"category label {cat_label:?} not found in shared strings"
))
})?;
let mut row = vec![CellValue::SharedString(cat_ss_idx)];
for s in series {
if let Some(Some(v)) = s.values().get(cat_idx) {
row.push(CellValue::Number(*v));
} else {
row.push(CellValue::Empty);
}
}
rows.push(row);
}
build_xlsx(&rows, &shared_strings)
}
pub fn generate_xy_xlsx(data: &XyChartData) -> PptxResult<Vec<u8>> {
let series = data.series();
let mut shared_strings: Vec<String> = Vec::new();
for s in series {
shared_strings.push(format!("{} X", s.name()));
shared_strings.push(format!("{} Y", s.name()));
}
let max_points = series
.iter()
.map(|s| s.data_points().len())
.max()
.unwrap_or(0);
let mut rows: Vec<Vec<CellValue>> = Vec::new();
let mut header = Vec::new();
for (i, _s) in series.iter().enumerate() {
header.push(CellValue::SharedString(i * 2));
header.push(CellValue::SharedString(i * 2 + 1));
}
rows.push(header);
for pt_idx in 0..max_points {
let mut row = Vec::new();
for s in series {
if let Some(dp) = s.data_points().get(pt_idx) {
row.push(CellValue::Number(dp.x));
row.push(CellValue::Number(dp.y));
} else {
row.push(CellValue::Empty);
row.push(CellValue::Empty);
}
}
rows.push(row);
}
build_xlsx(&rows, &shared_strings)
}
pub fn generate_bubble_xlsx(data: &BubbleChartData) -> PptxResult<Vec<u8>> {
let series = data.series();
let mut shared_strings: Vec<String> = Vec::new();
for s in series {
shared_strings.push(format!("{} X", s.name()));
shared_strings.push(format!("{} Y", s.name()));
shared_strings.push(format!("{} Size", s.name()));
}
let max_points = series
.iter()
.map(|s| s.data_points().len())
.max()
.unwrap_or(0);
let mut rows: Vec<Vec<CellValue>> = Vec::new();
let mut header = Vec::new();
for (i, _s) in series.iter().enumerate() {
header.push(CellValue::SharedString(i * 3));
header.push(CellValue::SharedString(i * 3 + 1));
header.push(CellValue::SharedString(i * 3 + 2));
}
rows.push(header);
for pt_idx in 0..max_points {
let mut row = Vec::new();
for s in series {
if let Some(dp) = s.data_points().get(pt_idx) {
row.push(CellValue::Number(dp.x));
row.push(CellValue::Number(dp.y));
row.push(CellValue::Number(dp.size));
} else {
row.push(CellValue::Empty);
row.push(CellValue::Empty);
row.push(CellValue::Empty);
}
}
rows.push(row);
}
build_xlsx(&rows, &shared_strings)
}
#[cfg(test)]
#[path = "xlsx_tests.rs"]
mod tests;