ppt_rs/parts/
presentation.rs1use super::base::{ContentType, Part, PartType};
6use super::relationships::{RelationshipType, Relationships};
7use crate::exc::PptxError;
8use crate::oxml::XmlParser;
9
10#[derive(Debug, Clone)]
12pub struct SlideRef {
13 pub id: u32,
14 pub r_id: String,
15}
16
17#[derive(Debug, Clone)]
19pub struct PresentationPart {
20 path: String,
21 slide_refs: Vec<SlideRef>,
22 slide_master_r_id: Option<String>,
23 theme_r_id: Option<String>,
24 slide_width: u32,
25 slide_height: u32,
26}
27
28impl PresentationPart {
29 pub fn new() -> Self {
31 PresentationPart {
32 path: "ppt/presentation.xml".to_string(),
33 slide_refs: Vec::new(),
34 slide_master_r_id: None,
35 theme_r_id: None,
36 slide_width: 9144000, slide_height: 6858000, }
39 }
40
41 pub fn set_slide_master(&mut self, r_id: &str) {
43 self.slide_master_r_id = Some(r_id.to_string());
44 }
45
46 pub fn set_theme(&mut self, r_id: &str) {
48 self.theme_r_id = Some(r_id.to_string());
49 }
50
51 pub fn add_slide(&mut self, r_id: &str) -> u32 {
53 let id = 256 + self.slide_refs.len() as u32 + 1;
54 self.slide_refs.push(SlideRef {
55 id,
56 r_id: r_id.to_string(),
57 });
58 id
59 }
60
61 pub fn slide_count(&self) -> usize {
63 self.slide_refs.len()
64 }
65
66 pub fn slides(&self) -> &[SlideRef] {
68 &self.slide_refs
69 }
70
71 pub fn set_dimensions(&mut self, width: u32, height: u32) {
73 self.slide_width = width;
74 self.slide_height = height;
75 }
76
77 pub fn create_relationships(&self) -> Relationships {
79 let mut rels = Relationships::new();
80
81 if let Some(ref r_id) = self.slide_master_r_id {
83 rels.add_with_id(
84 r_id,
85 RelationshipType::SlideMaster,
86 "slideMasters/slideMaster1.xml",
87 );
88 } else {
89 rels.add(
90 RelationshipType::SlideMaster,
91 "slideMasters/slideMaster1.xml",
92 );
93 }
94
95 if let Some(ref r_id) = self.theme_r_id {
97 rels.add_with_id(r_id, RelationshipType::Theme, "theme/theme1.xml");
98 } else {
99 rels.add(RelationshipType::Theme, "theme/theme1.xml");
100 }
101
102 for (i, slide_ref) in self.slide_refs.iter().enumerate() {
104 rels.add_with_id(
105 &slide_ref.r_id,
106 RelationshipType::Slide,
107 &format!("slides/slide{}.xml", i + 1),
108 );
109 }
110
111 rels
112 }
113}
114
115impl Default for PresentationPart {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121impl Part for PresentationPart {
122 fn path(&self) -> &str {
123 &self.path
124 }
125
126 fn part_type(&self) -> PartType {
127 PartType::Presentation
128 }
129
130 fn content_type(&self) -> ContentType {
131 ContentType::Presentation
132 }
133
134 fn to_xml(&self) -> Result<String, PptxError> {
135 let mut slide_id_list = String::new();
136 for slide_ref in &self.slide_refs {
137 slide_id_list.push_str(&format!(
138 "\n<p:sldId id=\"{}\" r:id=\"{}\"/>",
139 slide_ref.id, slide_ref.r_id
140 ));
141 }
142
143 let master_r_id = self.slide_master_r_id.as_deref().unwrap_or("rId1");
144
145 let xml = format!(
146 r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
147<p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" saveSubsetFonts="1">
148<p:sldMasterIdLst>
149<p:sldMasterId id="2147483648" r:id="{}"/>
150</p:sldMasterIdLst>
151<p:sldIdLst>{}
152</p:sldIdLst>
153<p:sldSz cx="{}" cy="{}" type="screen4x3"/>
154<p:notesSz cx="{}" cy="{}"/>
155</p:presentation>"#,
156 master_r_id,
157 slide_id_list,
158 self.slide_width,
159 self.slide_height,
160 self.slide_height,
161 self.slide_width,
162 );
163
164 Ok(xml)
165 }
166
167 fn from_xml(xml: &str) -> Result<Self, PptxError> {
168 let root = XmlParser::parse_str(xml)?;
169 let mut part = PresentationPart::new();
170
171 for sld_id in root.find_all_descendants("sldId") {
173 if let (Some(id), Some(r_id)) = (sld_id.attr("id"), sld_id.attr("r:id")) {
174 if let Ok(id_num) = id.parse::<u32>() {
175 part.slide_refs.push(SlideRef {
176 id: id_num,
177 r_id: r_id.to_string(),
178 });
179 }
180 }
181 }
182
183 if let Some(master_id) = root.find_descendant("sldMasterId") {
185 part.slide_master_r_id = master_id.attr("r:id").map(|s| s.to_string());
186 }
187
188 if let Some(sld_sz) = root.find_descendant("sldSz") {
190 if let Some(cx) = sld_sz.attr("cx").and_then(|s| s.parse().ok()) {
191 part.slide_width = cx;
192 }
193 if let Some(cy) = sld_sz.attr("cy").and_then(|s| s.parse().ok()) {
194 part.slide_height = cy;
195 }
196 }
197
198 Ok(part)
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn test_presentation_part_new() {
208 let part = PresentationPart::new();
209 assert_eq!(part.slide_count(), 0);
210 assert_eq!(part.path(), "ppt/presentation.xml");
211 }
212
213 #[test]
214 fn test_add_slides() {
215 let mut part = PresentationPart::new();
216 part.add_slide("rId3");
217 part.add_slide("rId4");
218
219 assert_eq!(part.slide_count(), 2);
220 assert_eq!(part.slides()[0].r_id, "rId3");
221 }
222
223 #[test]
224 fn test_to_xml() {
225 let mut part = PresentationPart::new();
226 part.set_slide_master("rId1");
227 part.add_slide("rId3");
228
229 let xml = part.to_xml().unwrap();
230 assert!(xml.contains("p:presentation"));
231 assert!(xml.contains("p:sldId"));
232 assert!(xml.contains("rId3"));
233 }
234
235 #[test]
236 fn test_from_xml() {
237 let xml = r#"<?xml version="1.0"?>
238 <p:presentation xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
239 xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
240 <p:sldMasterIdLst>
241 <p:sldMasterId id="2147483648" r:id="rId1"/>
242 </p:sldMasterIdLst>
243 <p:sldIdLst>
244 <p:sldId id="257" r:id="rId3"/>
245 <p:sldId id="258" r:id="rId4"/>
246 </p:sldIdLst>
247 <p:sldSz cx="9144000" cy="6858000"/>
248 </p:presentation>"#;
249
250 let part = PresentationPart::from_xml(xml).unwrap();
251 assert_eq!(part.slide_count(), 2);
252 assert_eq!(part.slides()[0].id, 257);
253 }
254}