mod parser;
#[cfg(test)]
mod tests;
use std::fmt::Write;
use crate::error::{PptxError, PptxResult};
use crate::text::font::RgbColor;
pub use parser::parse_theme_color_scheme;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ThemeColorScheme {
pub dk1: RgbColor,
pub dk2: RgbColor,
pub lt1: RgbColor,
pub lt2: RgbColor,
pub accent1: RgbColor,
pub accent2: RgbColor,
pub accent3: RgbColor,
pub accent4: RgbColor,
pub accent5: RgbColor,
pub accent6: RgbColor,
pub hlink: RgbColor,
pub fol_hlink: RgbColor,
}
impl Default for ThemeColorScheme {
fn default() -> Self {
Self {
dk1: RgbColor::new(0, 0, 0),
dk2: RgbColor::new(31, 73, 125),
lt1: RgbColor::new(255, 255, 255),
lt2: RgbColor::new(238, 236, 225),
accent1: RgbColor::new(79, 129, 189),
accent2: RgbColor::new(192, 80, 77),
accent3: RgbColor::new(155, 187, 89),
accent4: RgbColor::new(128, 100, 162),
accent5: RgbColor::new(75, 172, 198),
accent6: RgbColor::new(247, 150, 70),
hlink: RgbColor::new(0, 0, 255),
fol_hlink: RgbColor::new(128, 0, 128),
}
}
}
impl ThemeColorScheme {
#[must_use]
pub fn by_name(&self, name: &str) -> Option<RgbColor> {
match name {
"dk1" | "tx1" => Some(self.dk1),
"dk2" | "tx2" => Some(self.dk2),
"lt1" | "bg1" => Some(self.lt1),
"lt2" | "bg2" => Some(self.lt2),
"accent1" => Some(self.accent1),
"accent2" => Some(self.accent2),
"accent3" => Some(self.accent3),
"accent4" => Some(self.accent4),
"accent5" => Some(self.accent5),
"accent6" => Some(self.accent6),
"hlink" => Some(self.hlink),
"folHlink" => Some(self.fol_hlink),
_ => None,
}
}
#[must_use]
pub fn to_xml_string(&self) -> String {
fn write_slot(xml: &mut String, tag: &str, color: RgbColor) {
write!(
xml,
r#"<a:{tag}><a:srgbClr val="{hex}"/></a:{tag}>"#,
tag = tag,
hex = color.to_hex(),
)
.unwrap_or_else(|_| unreachable!("fmt::Write for String is infallible"));
}
let mut xml = String::from(r#"<a:clrScheme name="Office">"#);
write_slot(&mut xml, "dk1", self.dk1);
write_slot(&mut xml, "lt1", self.lt1);
write_slot(&mut xml, "dk2", self.dk2);
write_slot(&mut xml, "lt2", self.lt2);
write_slot(&mut xml, "accent1", self.accent1);
write_slot(&mut xml, "accent2", self.accent2);
write_slot(&mut xml, "accent3", self.accent3);
write_slot(&mut xml, "accent4", self.accent4);
write_slot(&mut xml, "accent5", self.accent5);
write_slot(&mut xml, "accent6", self.accent6);
write_slot(&mut xml, "hlink", self.hlink);
write_slot(&mut xml, "folHlink", self.fol_hlink);
xml.push_str("</a:clrScheme>");
xml
}
}
pub fn update_theme_color_scheme(
theme_xml: &[u8],
scheme: &ThemeColorScheme,
) -> PptxResult<Vec<u8>> {
let xml_str = std::str::from_utf8(theme_xml)?;
let start = xml_str
.find("<a:clrScheme")
.ok_or_else(|| PptxError::InvalidXml("No <a:clrScheme> found in theme XML".to_string()))?;
let end_tag = "</a:clrScheme>";
let end = xml_str[start..]
.find(end_tag)
.ok_or_else(|| PptxError::InvalidXml("No </a:clrScheme> found in theme XML".to_string()))?;
let end_pos = start + end + end_tag.len();
let mut result = String::with_capacity(xml_str.len());
result.push_str(&xml_str[..start]);
result.push_str(&scheme.to_xml_string());
result.push_str(&xml_str[end_pos..]);
Ok(result.into_bytes())
}