use crate::dml::fill::GradientFill;
use crate::error::{PptxError, PptxResult};
use crate::xml_util::{xml_escape, WriteXml};
pub(crate) fn set_slide_background_solid(slide_xml: &mut Vec<u8>, color: &str) -> PptxResult<()> {
let xml_str = std::str::from_utf8(slide_xml)?;
let bg_xml = format!(
r#"<p:bg><p:bgPr><a:solidFill><a:srgbClr val="{}"/></a:solidFill><a:effectLst/></p:bgPr></p:bg>"#,
xml_escape(color)
);
let insert_at = if let Some(pos) = xml_str.find("<p:cSld>") {
pos + "<p:cSld>".len()
} else if let Some(pos) = xml_str.find("<p:cSld ") {
let rest = &xml_str[pos..];
if let Some(close) = rest.find('>') {
pos + close + 1
} else {
return Err(PptxError::InvalidXml("malformed cSld element".to_string()));
}
} else {
return Err(PptxError::InvalidXml(
"no cSld element found in slide XML".to_string(),
));
};
let mut result = Vec::with_capacity(slide_xml.len() + bg_xml.len());
result.extend_from_slice(&slide_xml[..insert_at]);
result.extend_from_slice(bg_xml.as_bytes());
result.extend_from_slice(&slide_xml[insert_at..]);
*slide_xml = result;
Ok(())
}
pub(crate) fn set_slide_background_gradient(
slide_xml: &mut Vec<u8>,
gradient: &GradientFill,
) -> PptxResult<()> {
let xml_str = std::str::from_utf8(slide_xml)?;
let xml_str = xml_str.to_owned();
let xml_str = remove_bg_element(&xml_str);
let fill = crate::dml::fill::FillFormat::Gradient(gradient.clone());
let bg_xml = format!(
"<p:bg><p:bgPr>{}<a:effectLst/></p:bgPr></p:bg>",
fill.to_xml_string()
);
let result = insert_bg_after_csld(&xml_str, &bg_xml)?;
*slide_xml = result.into_bytes();
Ok(())
}
pub(crate) fn set_slide_background_image(
slide_xml: &mut Vec<u8>,
image_r_id: &str,
) -> PptxResult<()> {
let xml_str = std::str::from_utf8(slide_xml)?;
let xml_str = xml_str.to_owned();
let xml_str = remove_bg_element(&xml_str);
let bg_xml = format!(
r#"<p:bg><p:bgPr><a:blipFill><a:blip r:embed="{}"/><a:stretch><a:fillRect/></a:stretch></a:blipFill><a:effectLst/></p:bgPr></p:bg>"#,
xml_escape(image_r_id)
);
let result = insert_bg_after_csld(&xml_str, &bg_xml)?;
*slide_xml = result.into_bytes();
Ok(())
}
pub(crate) fn set_follow_master_background(
slide_xml: &mut Vec<u8>,
follow: bool,
) -> PptxResult<()> {
if follow {
let xml_str = std::str::from_utf8(slide_xml)?.to_owned();
let result = remove_bg_element(&xml_str);
*slide_xml = result.into_bytes();
}
Ok(())
}
pub(super) fn remove_bg_element(xml_str: &str) -> String {
xml_str
.find("<p:bg>")
.and_then(|bg_start| {
let bg_end = bg_start + xml_str[bg_start..].find("</p:bg>")? + "</p:bg>".len();
let mut result = String::with_capacity(xml_str.len() - (bg_end - bg_start));
result.push_str(&xml_str[..bg_start]);
result.push_str(&xml_str[bg_end..]);
Some(result)
})
.unwrap_or_else(|| xml_str.to_string())
}
fn insert_bg_after_csld(xml_str: &str, bg_xml: &str) -> PptxResult<String> {
let insert_pos = if let Some(pos) = xml_str.find("<p:cSld>") {
pos + "<p:cSld>".len()
} else if let Some(pos) = xml_str.find("<p:cSld ") {
let rest = &xml_str[pos..];
if let Some(close) = rest.find('>') {
pos + close + 1
} else {
return Err(PptxError::InvalidXml("malformed cSld element".to_string()));
}
} else {
return Err(PptxError::InvalidXml(
"no cSld element found in slide XML".to_string(),
));
};
let mut result = String::with_capacity(xml_str.len() + bg_xml.len());
result.push_str(&xml_str[..insert_pos]);
result.push_str(bg_xml);
result.push_str(&xml_str[insert_pos..]);
Ok(result)
}