ppt_rs/generator/
template.rs1use std::collections::HashMap;
4use std::path::Path;
5
6use crate::exc::{PptxError, Result};
7use crate::opc::Package;
8
9#[derive(Clone, Debug, Default)]
11pub struct PptxTemplate {
12 parts: HashMap<String, Vec<u8>>,
13 layout_count: usize,
14}
15
16impl PptxTemplate {
17 pub fn load(path: impl AsRef<Path>) -> Result<Self> {
19 let pkg = Package::open(path)?;
20 Self::from_package(&pkg)
21 }
22
23 pub fn from_package(pkg: &Package) -> Result<Self> {
25 let mut parts = HashMap::new();
26 for path in pkg.part_paths() {
27 if path.starts_with("ppt/theme/")
28 || path.starts_with("ppt/slideMasters/")
29 || path.starts_with("ppt/slideLayouts/")
30 || path == "ppt/tableStyles.xml"
31 {
32 if let Some(data) = pkg.get_part(path) {
33 parts.insert(path.to_string(), data.to_vec());
34 }
35 }
36 }
37
38 if !parts.keys().any(|p| p.starts_with("ppt/slideMasters/")) {
39 return Err(PptxError::InvalidValue(
40 "template missing ppt/slideMasters/".into(),
41 ));
42 }
43
44 let layout_count = parts
45 .keys()
46 .filter(|p| {
47 p.starts_with("ppt/slideLayouts/slideLayout")
48 && p.ends_with(".xml")
49 && !p.contains("_rels")
50 })
51 .count()
52 .max(1);
53
54 Ok(PptxTemplate { parts, layout_count })
55 }
56
57 pub fn layout_count(&self) -> usize {
58 self.layout_count
59 }
60
61 pub fn parts(&self) -> &HashMap<String, Vec<u8>> {
62 &self.parts
63 }
64
65 pub fn has_layout(&self, n: usize) -> bool {
66 self.parts
67 .contains_key(&format!("ppt/slideLayouts/slideLayout{n}.xml"))
68 }
69
70 pub fn resolve_layout_number(&self, requested: usize) -> usize {
72 if self.has_layout(requested) {
73 requested
74 } else {
75 1
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use crate::generator::create_pptx;
84
85 #[test]
86 fn load_template_from_generated_deck() {
87 let bytes = create_pptx("Tpl", 1).unwrap();
88 let dir = std::env::temp_dir().join("ppt_rs_template_test.pptx");
89 std::fs::write(&dir, &bytes).unwrap();
90 let tpl = PptxTemplate::load(&dir).unwrap();
91 assert!(tpl.layout_count() >= 1);
92 assert!(tpl.has_layout(1));
93 std::fs::remove_file(dir).ok();
94 }
95}