use super::*;
use crate::common::{ xml_utils::*, namespaces::excel };
use crate::error::Result;
impl CellStyle {
pub fn to_xml(&self) -> XmlElement {
let mut style = XmlElement::new("xf");
if self.font.is_some() {
style.add_attribute("applyFont", "1");
}
if self.fill.is_some() {
style.add_attribute("applyFill", "1");
}
if self.border.is_some() {
style.add_attribute("applyBorder", "1");
}
if let Some(alignment) = &self.alignment {
style.add_attribute("applyAlignment", "1");
let align_elem = alignment_to_xml(alignment);
style.add_child(align_elem);
}
if self.number_format.is_some() {
style.add_attribute("applyNumberFormat", "1");
}
if let Some(protection) = &self.protection {
style.add_attribute("applyProtection", "1");
let prot_elem = protection_to_xml(protection);
style.add_child(prot_elem);
}
style
}
}
impl Font {
pub fn to_xml(&self) -> XmlElement {
let mut font = XmlElement::new("font");
let mut name = XmlElement::new("name");
name.add_attribute("val", &self.name);
font.add_child(name);
let mut size = XmlElement::new("sz");
size.add_attribute("val", &self.size.to_string());
font.add_child(size);
if self.bold {
font.add_child(XmlElement::new("b"));
}
if self.italic {
font.add_child(XmlElement::new("i"));
}
if let Some(underline) = &self.underline {
let mut u = XmlElement::new("u");
match underline {
UnderlineStyle::Single => u.add_attribute("val", "single"),
UnderlineStyle::Double => u.add_attribute("val", "double"),
UnderlineStyle::SingleAccounting => u.add_attribute("val", "singleAccounting"),
UnderlineStyle::DoubleAccounting => u.add_attribute("val", "doubleAccounting"),
}
font.add_child(u);
}
if let Some(color) = &self.color {
let mut clr = XmlElement::new("color");
clr.add_attribute("rgb", &color.rgb);
font.add_child(clr);
}
font
}
}
impl Border {
pub fn to_xml(&self) -> XmlElement {
let mut border = XmlElement::new("border");
let left = border_style_to_xml("left", &self.left);
border.add_child(left);
let right = border_style_to_xml("right", &self.right);
border.add_child(right);
let top = border_style_to_xml("top", &self.top);
border.add_child(top);
let bottom = border_style_to_xml("bottom", &self.bottom);
border.add_child(bottom);
let diagonal = border_style_to_xml("diagonal", &self.diagonal);
border.add_child(diagonal);
border
}
}
impl Fill {
pub fn to_xml(&self) -> XmlElement {
let mut fill = XmlElement::new("fill");
let mut pattern_fill = XmlElement::new("patternFill");
match self.pattern_type {
PatternType::None => pattern_fill.add_attribute("patternType", "none"),
PatternType::Solid => pattern_fill.add_attribute("patternType", "solid"),
PatternType::MediumGray => pattern_fill.add_attribute("patternType", "mediumGray"),
PatternType::DarkGray => pattern_fill.add_attribute("patternType", "darkGray"),
PatternType::LightGray => pattern_fill.add_attribute("patternType", "lightGray"),
}
if let Some(fg_color) = &self.fg_color {
let mut fgColor = XmlElement::new("fgColor");
fgColor.add_attribute("rgb", &fg_color.rgb);
pattern_fill.add_child(fgColor);
}
if let Some(bg_color) = &self.bg_color {
let mut bgColor = XmlElement::new("bgColor");
bgColor.add_attribute("rgb", &bg_color.rgb);
pattern_fill.add_child(bgColor);
}
fill.add_child(pattern_fill);
fill
}
}
impl StylesManager {
pub fn generate_styles_xml(&self) -> Result<String> {
let mut root = XmlElement::new("styleSheet");
root.add_attribute("xmlns", excel::STYLES);
let (fonts, fills, borders) = self.collect_style_components();
let mut fonts_elem = XmlElement::new("fonts");
fonts_elem.add_attribute("count", &fonts.len().to_string());
for font in fonts {
fonts_elem.add_child(font.to_xml());
}
root.add_child(fonts_elem);
let mut fills_elem = XmlElement::new("fills");
fills_elem.add_attribute("count", &fills.len().to_string());
for fill in fills {
fills_elem.add_child(fill.to_xml());
}
root.add_child(fills_elem);
let mut borders_elem = XmlElement::new("borders");
borders_elem.add_attribute("count", &borders.len().to_string());
for border in borders {
borders_elem.add_child(border.to_xml());
}
root.add_child(borders_elem);
let mut cell_styles = XmlElement::new("cellStyles");
cell_styles.add_attribute("count", &self.styles.len().to_string());
for style in &self.styles {
cell_styles.add_child(style.to_xml());
}
root.add_child(cell_styles);
let generator = XmlGenerator::new();
generator.generate_string(&root)
}
fn collect_style_components(&self) -> (Vec<&Font>, Vec<&Fill>, Vec<&Border>) {
let mut fonts = Vec::new();
let mut fills = Vec::new();
let mut borders = Vec::new();
for style in &self.styles {
if let Some(font) = &style.font {
if !fonts.contains(&font) {
fonts.push(font);
}
}
if let Some(fill) = &style.fill {
if !fills.contains(&fill) {
fills.push(fill);
}
}
if let Some(border) = &style.border {
if !borders.contains(&border) {
borders.push(border);
}
}
}
(fonts, fills, borders)
}
}
fn alignment_to_xml(alignment: &Alignment) -> XmlElement {
let mut elem = XmlElement::new("alignment");
let horizontal = match alignment.horizontal {
HorizontalAlignment::Left => "left",
HorizontalAlignment::Center => "center",
HorizontalAlignment::Right => "right",
HorizontalAlignment::Fill => "fill",
HorizontalAlignment::Justify => "justify",
HorizontalAlignment::CenterContinuous => "centerContinuous",
HorizontalAlignment::Distributed => "distributed",
};
elem.add_attribute("horizontal", horizontal);
let vertical = match alignment.vertical {
VerticalAlignment::Top => "top",
VerticalAlignment::Center => "center",
VerticalAlignment::Bottom => "bottom",
VerticalAlignment::Justify => "justify",
VerticalAlignment::Distributed => "distributed",
};
elem.add_attribute("vertical", vertical);
if alignment.wrap_text {
elem.add_attribute("wrapText", "1");
}
if alignment.text_rotation != 0 {
elem.add_attribute("textRotation", &alignment.text_rotation.to_string());
}
if alignment.indent > 0 {
elem.add_attribute("indent", &alignment.indent.to_string());
}
elem
}
fn protection_to_xml(protection: &Protection) -> XmlElement {
let mut elem = XmlElement::new("protection");
if protection.locked {
elem.add_attribute("locked", "1");
}
if protection.hidden {
elem.add_attribute("hidden", "1");
}
elem
}
fn border_style_to_xml(name: &str, style: &BorderStyle) -> XmlElement {
let mut elem = XmlElement::new(name);
if let Some(line_style) = &style.style {
let style_str = match line_style {
LineStyle::None => "none",
LineStyle::Thin => "thin",
LineStyle::Medium => "medium",
LineStyle::Thick => "thick",
LineStyle::Double => "double",
LineStyle::Dotted => "dotted",
LineStyle::Dashed => "dashed",
};
elem.add_attribute("style", style_str);
}
if let Some(color) = &style.color {
let mut color_elem = XmlElement::new("color");
color_elem.add_attribute("rgb", &color.rgb);
elem.add_child(color_elem);
}
elem
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_font_xml() {
let font = Font {
name: "Arial".to_string(),
size: 12.0,
bold: true,
italic: false,
underline: Some(UnderlineStyle::Single),
color: Some(Color::from_rgb(0, 0, 0)),
};
let xml = font.to_xml();
let generator = XmlGenerator::new();
let xml_str = generator.generate_string(&xml).unwrap();
assert!(xml_str.contains("Arial"));
assert!(xml_str.contains("12"));
assert!(xml_str.contains("<b"));
assert!(xml_str.contains("FF000000"));
}
#[test]
fn test_styles_xml() {
let mut manager = StylesManager::new();
manager.add_style(CellStyle::default_header());
manager.add_style(CellStyle::default_body());
let xml = manager.generate_styles_xml().unwrap();
assert!(xml.contains("styleSheet"));
assert!(xml.contains("fonts"));
assert!(xml.contains("fills"));
assert!(xml.contains("borders"));
assert!(xml.contains("cellStyles"));
}
}