fn current_timestamp() -> String {
let duration = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_else(|_| std::time::Duration::from_secs(0));
let secs = duration.as_secs();
let days = secs / 86400;
let seconds = secs % 86400;
let year = 1970 + days / 365;
let remaining_days = days % 365;
let month = 1 + remaining_days / 30;
let day = 1 + remaining_days % 30;
let hours = seconds / 3600;
let minutes = (seconds % 3600) / 60;
let secs = seconds % 60;
format!(
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
year, month, day, hours, minutes, secs
)
}
pub fn create_core_props_xml(title: &str) -> String {
let now = current_timestamp();
format!(
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dc:title>{title}</dc:title>
<dc:creator>pptx-rs</dc:creator>
<cp:lastModifiedBy>pptx-rs</cp:lastModifiedBy>
<cp:revision>1</cp:revision>
<dcterms:created xsi:type="dcterms:W3CDTF">{now}</dcterms:created>
<dcterms:modified xsi:type="dcterms:W3CDTF">{now}</dcterms:modified>
</cp:coreProperties>"#
)
}
fn xml_escape(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for c in s.chars() {
match c {
'&' => out.push_str("&"),
'<' => out.push_str("<"),
'>' => out.push_str(">"),
'"' => out.push_str("""),
'\'' => out.push_str("'"),
_ => out.push(c),
}
}
out
}
fn heading_pairs_and_titles(slide_titles: &[String]) -> String {
let count = slide_titles.len();
let mut titles_xml = String::with_capacity(count * 32);
for title in slide_titles {
titles_xml.push_str("<vt:lpstr>");
titles_xml.push_str(&xml_escape(title));
titles_xml.push_str("</vt:lpstr>");
}
format!(
r#"<HeadingPairs><vt:vector size="2" baseType="variant"><vt:variant><vt:lpstr>Slide Titles</vt:lpstr></vt:variant><vt:variant><vt:i4>{count}</vt:i4></vt:variant></vt:vector></HeadingPairs><TitlesOfParts><vt:vector size="{count}" baseType="lpstr">{titles_xml}</vt:vector></TitlesOfParts>"#
)
}
pub fn create_app_props_xml(slides: usize, notes_count: usize, slide_titles: &[String]) -> String {
let titles: Vec<String> = if slide_titles.len() >= slides {
slide_titles.iter().take(slides).cloned().collect()
} else {
let mut v: Vec<String> = slide_titles.to_vec();
for i in v.len()..slides {
v.push(format!("Slide {}", i + 1));
}
v
};
let heading_and_titles = heading_pairs_and_titles(&titles);
format!(
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
<TotalTime>0</TotalTime>
<Words>0</Words>
<Application>pptx-rs</Application>
<PresentationFormat>On-screen Show (4:3)</PresentationFormat>
<Paragraphs>0</Paragraphs>
<Slides>{slides}</Slides>
<Notes>{notes_count}</Notes>
<HiddenSlides>0</HiddenSlides>
<MMClips>0</MMClips>
<ScaleCrop>false</ScaleCrop>
{heading_and_titles}
<LinksUpToDate>false</LinksUpToDate>
<SharedDoc>false</SharedDoc>
<HyperlinksChanged>false</HyperlinksChanged>
<AppVersion>1.0000</AppVersion>
</Properties>"#
)
}