use anyhow::bail;
use quick_xml::events::Event;
use std::io::{Read, Seek};
use zip::ZipArchive;
use crate::excel::xml_reader;
use super::{
color::custom_color::{load_custom_color_list, XlsxCustomColorList},
default::object_defaults::XlsxObjectDefaults,
scheme::extra_color_scheme::{load_extra_color_scheme_list, XlsxExtraColorSchemeList},
theme_elements::XlsxThemeElements,
};
#[cfg(feature = "drawing")]
use super::{
effect::{effect_reference::XlsxEffectReference, effect_style::XlsxEffectStyle},
fill::{fill_reference::XlsxFillReference, XlsxFillStyleEnum},
line::{line_reference::XlsxLineReference, outline::XlsxOutline},
scheme::format_scheme::XlsxFormatScheme,
text::font::{font_reference::XlsxFontReference, XlsxFontBase},
};
#[derive(Debug, Clone, PartialEq)]
pub struct XlsxTheme {
pub custom_color_list: Option<Box<XlsxCustomColorList>>,
pub extra_color_scheme_list: Option<Box<XlsxExtraColorSchemeList>>,
pub object_defaults: Option<Box<XlsxObjectDefaults>>,
pub theme_elements: Option<Box<XlsxThemeElements>>,
pub name: Option<String>,
}
impl XlsxTheme {
pub(crate) fn load(
zip: &mut ZipArchive<impl Read + Seek>,
path: Vec<String>,
) -> anyhow::Result<Self> {
let mut theme = Self {
name: None,
custom_color_list: None,
extra_color_scheme_list: None,
theme_elements: None,
object_defaults: None,
};
let Some(path) = path.first() else {
return Ok(theme);
};
let Some(mut reader) = xml_reader(zip, path) else {
return Ok(theme);
};
let mut buf = Vec::new();
loop {
buf.clear();
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"extLst" => {
let _ = reader.read_to_end_into(e.to_end().to_owned().name(), &mut Vec::new());
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"theme" => {
let attributes = e.attributes();
for a in attributes {
match a {
Ok(a) => {
let string_value = String::from_utf8(a.value.to_vec())?;
match a.key.local_name().as_ref() {
b"name" => {
theme.name = Some(string_value);
break;
}
_ => {}
}
}
Err(error) => {
bail!(error.to_string())
}
}
}
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"custClrLst" => {
let colors = load_custom_color_list(&mut reader)?;
theme.custom_color_list = Some(Box::new(colors));
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"extraClrSchemeLst" => {
let schemes = load_extra_color_scheme_list(&mut reader)?;
theme.extra_color_scheme_list = Some(Box::new(schemes));
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"objectDefaults" => {
let defaults: XlsxObjectDefaults = XlsxObjectDefaults::load(&mut reader)?;
theme.object_defaults = Some(Box::new(defaults));
}
Ok(Event::Start(ref e)) if e.local_name().as_ref() == b"themeElements" => {
let theme_elements = XlsxThemeElements::load(&mut reader)?;
theme.theme_elements = Some(Box::new(theme_elements));
}
Ok(Event::End(ref e)) if e.local_name().as_ref() == b"theme" => break,
Ok(Event::Eof) => break,
Err(e) => bail!(e.to_string()),
_ => (),
}
}
return Ok(theme);
}
}
#[cfg(feature = "drawing")]
impl XlsxTheme {
fn get_format_scheme(&self) -> Option<XlsxFormatScheme> {
let Some(theme_elements) = self.theme_elements.clone() else {
return None;
};
return theme_elements.format_scheme.clone();
}
fn u64_to_usize(u64: u64) -> anyhow::Result<usize> {
return Ok(TryInto::<usize>::try_into(u64)?);
}
pub(crate) fn get_line_from_ref(
&self,
reference: Option<XlsxLineReference>,
) -> Option<XlsxOutline> {
let Some(reference) = reference else {
return None;
};
let Some(index) = reference.index else {
return None;
};
let Some(format_scheme) = self.get_format_scheme() else {
return None;
};
let Some(line_style_list) = format_scheme.line_style_lst else {
return None;
};
let Ok(index) = Self::u64_to_usize(index) else {
return None;
};
if index > line_style_list.len() - 1 {
return None;
};
return Some(line_style_list[index].clone());
}
pub(crate) fn get_fill_from_ref(
&self,
reference: Option<XlsxFillReference>,
) -> Option<XlsxFillStyleEnum> {
let Some(reference) = reference else {
return None;
};
let Some(index) = reference.index else {
return None;
};
let Some(format_scheme) = self.get_format_scheme() else {
return None;
};
let Ok(index) = Self::u64_to_usize(index) else {
return None;
};
if index == 0 || index == 1000 {
return Some(XlsxFillStyleEnum::NoFill(true));
}
if (1..=999).contains(&index) {
let Some(style_list) = format_scheme.fill_style_lst else {
return None;
};
let Some(index) = index.checked_sub(1) else {
return None;
};
if index > style_list.len() - 1 {
return None;
};
return Some(style_list[index].clone());
}
let Some(index) = index.checked_sub(1001) else {
return None;
};
let Some(style_list) = format_scheme.bg_fill_style_lst else {
return None;
};
if index > style_list.len() - 1 {
return None;
};
return Some(style_list[index].clone());
}
pub(crate) fn get_effect_from_ref(
&self,
reference: Option<XlsxEffectReference>,
) -> Option<XlsxEffectStyle> {
let Some(reference) = reference else {
return None;
};
let Some(index) = reference.index else {
return None;
};
let Some(format_scheme) = self.get_format_scheme() else {
return None;
};
let Some(style_list) = format_scheme.effect_style_lst else {
return None;
};
let Ok(index) = Self::u64_to_usize(index) else {
return None;
};
if index > style_list.len() - 1 {
return None;
};
return Some(style_list[index].clone());
}
pub(crate) fn get_font_from_ref(
&self,
reference: Option<XlsxFontReference>,
) -> Option<XlsxFontBase> {
let Some(reference) = reference else {
return None;
};
let Some(index) = reference.index else {
return None;
};
let Some(theme_elements) = self.theme_elements.clone() else {
return None;
};
let Some(font_scheme) = theme_elements.font_scheme.clone() else {
return None;
};
if index == "major" {
return font_scheme.major_font;
}
if index == "minor" {
return font_scheme.minor_font;
};
return None;
}
}