use quick_xml::events::Event;
use quick_xml::Reader;
use crate::dml::color::{ColorFormat, HslColor, PresetColor, SystemColor, ThemeColor};
use crate::enums::dml::{MsoThemeColorIndex, PresetColorVal, SystemColorVal};
use crate::error::PptxResult;
use crate::text::font::RgbColor;
use crate::xml_util::{attr_value, local_name_str};
pub(super) fn parse_color_from_xml(xml: &[u8]) -> PptxResult<Option<ColorFormat>> {
let mut reader = Reader::from_reader(xml);
reader.config_mut().trim_text(true);
let mut buf = Vec::new();
loop {
buf.clear();
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e) | Event::Empty(ref e)) => {
let qn = e.name();
let local = local_name_str(qn.as_ref());
match local {
"srgbClr" => {
if let Some(val) = attr_value(e, b"val")? {
if let Ok(rgb) = RgbColor::from_hex(&val) {
return Ok(Some(ColorFormat::Rgb(rgb)));
}
}
}
"schemeClr" => {
if let Some(val) = attr_value(e, b"val")? {
if let Some(tc) = MsoThemeColorIndex::from_xml_str(&val) {
let brightness =
parse_theme_brightness_from_remaining(&mut reader)?;
if let Ok(theme) = ThemeColor::new(tc, brightness) {
return Ok(Some(ColorFormat::Theme(theme)));
}
}
}
}
"sysClr" => {
let val = attr_value(e, b"val")?.map_or(SystemColorVal::WindowText, |c| {
SystemColorVal::from_xml_str(&c)
});
let last_color =
attr_value(e, b"lastClr")?.map(std::borrow::Cow::into_owned);
return Ok(Some(ColorFormat::System(SystemColor { val, last_color })));
}
"hslClr" => {
#[allow(clippy::cast_precision_loss)]
let hue = attr_value(e, b"hue")?
.and_then(|s| s.parse::<i64>().ok())
.map_or(0.0, |v| v as f64 / 60000.0);
#[allow(clippy::cast_precision_loss)]
let sat = attr_value(e, b"sat")?
.and_then(|s| s.parse::<i64>().ok())
.map_or(0.0, |v| v as f64 / 1000.0);
#[allow(clippy::cast_precision_loss)]
let lum = attr_value(e, b"lum")?
.and_then(|s| s.parse::<i64>().ok())
.map_or(0.0, |v| v as f64 / 1000.0);
if let Ok(hsl) = HslColor::new(hue, sat, lum) {
return Ok(Some(ColorFormat::Hsl(hsl)));
}
}
"prstClr" => {
let val = attr_value(e, b"val")?
.map_or(PresetColorVal::Black, |c| PresetColorVal::from_xml_str(&c));
return Ok(Some(ColorFormat::Preset(PresetColor { val })));
}
_ => {}
}
}
Ok(Event::Eof) | Err(_) => break,
_ => {}
}
}
Ok(None)
}
fn parse_theme_brightness_from_remaining(reader: &mut Reader<&[u8]>) -> PptxResult<Option<f64>> {
let mut buf = Vec::new();
let mut lum_mod: Option<f64> = None;
let mut lum_off: Option<f64> = None;
let mut depth = 1u32;
loop {
buf.clear();
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) => {
depth += 1;
let qn = e.name();
let local = local_name_str(qn.as_ref());
match local {
"lumMod" => {
#[allow(clippy::cast_precision_loss)]
{
lum_mod = attr_value(e, b"val")?
.and_then(|s| s.parse::<i64>().ok())
.map(|v| v as f64 / 100_000.0);
}
}
"lumOff" => {
#[allow(clippy::cast_precision_loss)]
{
lum_off = attr_value(e, b"val")?
.and_then(|s| s.parse::<i64>().ok())
.map(|v| v as f64 / 100_000.0);
}
}
_ => {}
}
}
Ok(Event::Empty(ref e)) => {
let qn = e.name();
let local = local_name_str(qn.as_ref());
match local {
"lumMod" => {
#[allow(clippy::cast_precision_loss)]
{
lum_mod = attr_value(e, b"val")?
.and_then(|s| s.parse::<i64>().ok())
.map(|v| v as f64 / 100_000.0);
}
}
"lumOff" => {
#[allow(clippy::cast_precision_loss)]
{
lum_off = attr_value(e, b"val")?
.and_then(|s| s.parse::<i64>().ok())
.map(|v| v as f64 / 100_000.0);
}
}
_ => {}
}
}
Ok(Event::End(_)) => {
depth -= 1;
if depth == 0 {
break;
}
}
Ok(Event::Eof) | Err(_) => break,
_ => {}
}
}
Ok(match (lum_mod, lum_off) {
(Some(_mod_val), Some(off_val)) if off_val > 0.0 => {
Some(off_val)
}
(Some(mod_val), None) if mod_val < 1.0 => {
Some(-(1.0 - mod_val))
}
_ => None,
})
}