1use crate::oxml::ns::{NS_DRAWING_MAIN, NS_PRESENTATION_MAIN};
32use crate::oxml::shape::{Connector, GraphicFrame, Group, Pic, Sp};
33use crate::oxml::txbody::TextBody;
34use crate::oxml::writer::XmlWriter;
35
36#[derive(Clone, Debug, Default)]
38pub struct Sld {
39 pub id: u32,
41 pub layout_rid: String,
43 pub name: String,
48 pub background: Option<SlideBackground>,
50 pub shapes: Vec<SlideShape>,
52 pub notes: Option<TextBody>,
54 pub transition: Option<Transition>,
56 pub ext_lst: Option<crate::oxml::shape::ExtensionList>,
58}
59
60#[derive(Clone, Debug, Default, PartialEq, Eq)]
62pub enum TransitionSpeed {
63 Slow,
65 #[default]
67 Medium,
68 Fast,
70}
71
72impl TransitionSpeed {
73 pub fn as_str(&self) -> &'static str {
75 match self {
76 TransitionSpeed::Slow => "slow",
77 TransitionSpeed::Medium => "med",
78 TransitionSpeed::Fast => "fast",
79 }
80 }
81}
82
83#[derive(Clone, Debug, Default, PartialEq, Eq)]
85pub enum TransitionType {
86 Fade { thru_blk: bool },
88 Push { dir: TransitionDirection },
90 Wipe { dir: TransitionDirection },
92 Split {
94 orient: SplitOrientation,
95 dir: TransitionDirection,
96 },
97 Cover { dir: TransitionDirection },
99 Pull { dir: TransitionDirection },
101 Cut { thru_blk: bool },
103 Zoom { dir: TransitionDirection },
105 Morph { option: MorphOption },
107 #[default]
109 None,
110}
111
112#[derive(Clone, Debug, Default, PartialEq, Eq)]
114pub enum TransitionDirection {
115 Left,
117 #[default]
119 Right,
120 Up,
122 Down,
124 LeftUp,
126 LeftDown,
128 RightUp,
130 RightDown,
132}
133
134impl TransitionDirection {
135 pub fn as_str(&self) -> &'static str {
137 match self {
138 TransitionDirection::Left => "l",
139 TransitionDirection::Right => "r",
140 TransitionDirection::Up => "u",
141 TransitionDirection::Down => "d",
142 TransitionDirection::LeftUp => "lu",
143 TransitionDirection::LeftDown => "ld",
144 TransitionDirection::RightUp => "ru",
145 TransitionDirection::RightDown => "rd",
146 }
147 }
148}
149
150#[derive(Clone, Debug, Default, PartialEq, Eq)]
152pub enum SplitOrientation {
153 #[default]
155 Horizontal,
156 Vertical,
158}
159
160impl SplitOrientation {
161 pub fn as_str(&self) -> &'static str {
163 match self {
164 SplitOrientation::Horizontal => "horz",
165 SplitOrientation::Vertical => "vert",
166 }
167 }
168}
169
170#[derive(Clone, Debug, Default, PartialEq, Eq)]
172pub enum MorphOption {
173 #[default]
175 ByObject,
176 ByWord,
178 ByChar,
180}
181
182impl MorphOption {
183 pub fn as_str(&self) -> &'static str {
185 match self {
186 MorphOption::ByObject => "byObject",
187 MorphOption::ByWord => "byWord",
188 MorphOption::ByChar => "byChar",
189 }
190 }
191}
192
193#[derive(Clone, Debug, Default)]
197pub struct Transition {
198 pub speed: TransitionSpeed,
200 pub advance_click: bool,
202 pub advance_after_ms: Option<u32>,
204 pub transition_type: TransitionType,
206}
207
208impl Transition {
209 pub fn write_xml(&self, w: &mut XmlWriter) {
211 let mut attrs: Vec<(&str, &str)> = Vec::new();
212 attrs.push(("spd", self.speed.as_str()));
213 if !self.advance_click {
214 attrs.push(("advClick", "0"));
215 }
216 let adv_tm_s;
217 if let Some(ms) = self.advance_after_ms {
218 adv_tm_s = ms.to_string();
219 attrs.push(("advTm", adv_tm_s.as_str()));
220 }
221 w.open_with("p:transition", &attrs);
222 match &self.transition_type {
224 TransitionType::Fade { thru_blk } => {
225 if *thru_blk {
226 w.empty_with("p:fade", &[("thruBlk", "1")]);
227 } else {
228 w.empty("p:fade");
229 }
230 }
231 TransitionType::Push { dir } => {
232 w.empty_with("p:push", &[("dir", dir.as_str())]);
233 }
234 TransitionType::Wipe { dir } => {
235 w.empty_with("p:wipe", &[("dir", dir.as_str())]);
236 }
237 TransitionType::Split { orient, dir } => {
238 w.empty_with(
239 "p:split",
240 &[("orient", orient.as_str()), ("dir", dir.as_str())],
241 );
242 }
243 TransitionType::Cover { dir } => {
244 w.empty_with("p:cover", &[("dir", dir.as_str())]);
245 }
246 TransitionType::Pull { dir } => {
247 w.empty_with("p:pull", &[("dir", dir.as_str())]);
248 }
249 TransitionType::Cut { thru_blk } => {
250 if *thru_blk {
251 w.empty_with("p:cut", &[("thruBlk", "1")]);
252 } else {
253 w.empty("p:cut");
254 }
255 }
256 TransitionType::Zoom { dir } => {
257 w.empty_with("p:zoom", &[("dir", dir.as_str())]);
258 }
259 TransitionType::Morph { option } => {
260 w.empty_with("p:morph", &[("option", option.as_str())]);
261 }
262 TransitionType::None => {}
263 }
264 w.close("p:transition");
265 }
266}
267
268#[derive(Clone, Debug)]
275pub enum SlideBackground {
276 Property(BackgroundProperty),
278 Reference(BackgroundReference),
280}
281
282#[derive(Clone, Debug, Default)]
284pub struct BackgroundProperty {
285 pub solid_fill: crate::oxml::color::Color,
287}
288
289#[derive(Clone, Debug, Default)]
291pub struct BackgroundReference {
292 pub idx: u32,
294 pub scheme_color: String,
296}
297
298impl SlideBackground {
299 pub fn solid(color: crate::oxml::color::Color) -> Self {
301 SlideBackground::Property(BackgroundProperty { solid_fill: color })
302 }
303
304 pub fn follow_master() -> Self {
306 SlideBackground::Reference(BackgroundReference {
307 idx: 1001,
308 scheme_color: "bg1".to_string(),
309 })
310 }
311
312 pub fn write_xml(&self, w: &mut XmlWriter) {
314 w.open("p:bg");
315 match self {
316 SlideBackground::Property(p) => {
317 w.open("p:bgPr");
318 if !matches!(p.solid_fill, crate::oxml::color::Color::None) {
319 p.solid_fill.write_solid_fill(w);
320 }
321 w.close("p:bgPr");
323 }
324 SlideBackground::Reference(r) => {
325 let idx_s = r.idx.to_string();
326 w.open_with("p:bgRef", &[("idx", idx_s.as_str())]);
327 w.empty_with("a:schemeClr", &[("val", r.scheme_color.as_str())]);
328 w.close("p:bgRef");
329 }
330 }
331 w.close("p:bg");
332 }
333}
334
335#[derive(Clone, Debug)]
336pub enum SlideShape {
337 Sp(Sp),
338 Pic(Pic),
339 CxnSp(Connector),
340 Group(Box<Group>),
341 GraphicFrame(GraphicFrame),
342}
343
344impl Sld {
345 pub fn to_xml(&self) -> String {
347 let mut w = XmlWriter::with_decl();
348 let attrs: Vec<(&str, &str)> = vec![
349 ("xmlns:a", NS_DRAWING_MAIN),
350 ("xmlns:p", NS_PRESENTATION_MAIN),
351 ("xmlns:r", crate::oxml::ns::NS_DRAWING_RELS),
352 ];
353 w.open_with("p:sld", &attrs);
354 if !self.name.trim().is_empty() {
357 w.open_with("p:cSld", &[("name", self.name.as_str())]);
358 } else {
359 w.open("p:cSld");
360 }
361 if let Some(bg) = &self.background {
363 bg.write_xml(&mut w);
364 }
365 w.open("p:spTree");
367 w.open("p:nvGrpSpPr");
369 w.empty_with("p:cNvPr", &[("id", "1"), ("name", "")]);
370 w.empty("p:cNvGrpSpPr");
371 w.empty("p:nvPr");
372 w.close("p:nvGrpSpPr");
373 w.open("p:grpSpPr");
375 w.empty("a:xfrm");
376 w.close("p:grpSpPr");
377 for shape in &self.shapes {
378 match shape {
379 SlideShape::Sp(s) => s.write_xml(&mut w),
380 SlideShape::Pic(p) => p.write_xml(&mut w),
381 SlideShape::CxnSp(c) => c.write_xml(&mut w),
382 SlideShape::Group(g) => g.write_xml(&mut w),
383 SlideShape::GraphicFrame(g) => g.write_xml(&mut w),
384 }
385 }
386 if let Some(ext) = &self.ext_lst {
388 ext.write_xml(&mut w);
389 }
390 w.close("p:spTree");
391 w.close("p:cSld");
392 w.empty("p:clrMapOvr");
394 if let Some(tr) = &self.transition {
396 tr.write_xml(&mut w);
397 }
398 w.close("p:sld");
400 w.into_string()
401 }
402
403 pub fn set_layout_rid(&mut self, rid: String) {
408 self.layout_rid = rid;
409 }
410}
411
412pub fn notes_xml(tb: &TextBody) -> String {
445 let mut w = XmlWriter::with_decl();
446 let attrs: Vec<(&str, &str)> = vec![
447 ("xmlns:a", NS_DRAWING_MAIN),
448 ("xmlns:p", NS_PRESENTATION_MAIN),
449 ("xmlns:r", crate::oxml::ns::NS_DRAWING_RELS),
450 ];
451 w.open_with("p:notes", &attrs);
452 w.open("p:cSld");
454 w.open("p:spTree");
456 w.empty_with("p:nvGrpSpPr", &[]);
457 w.empty_with("p:grpSpPr", &[]);
458 w.open("p:sp");
460 w.open("p:nvSpPr");
461 w.empty_with("p:cNvPr", &[("id", "2"), ("name", "Notes Placeholder")]);
462 w.empty_with("p:cNvSpPr", &[("txBox", "1")]);
463 w.open("p:nvPr");
464 w.empty_with("p:ph", &[("type", "body"), ("idx", "1")]);
465 w.close("p:nvPr");
466 w.close("p:nvSpPr");
467 w.open("p:spPr");
469 w.open("a:xfrm");
470 w.empty_with("a:off", &[("x", "0"), ("y", "0")]);
471 w.empty_with("a:ext", &[("cx", "6858000"), ("cy", "9144000")]);
472 w.close("a:xfrm");
473 w.open_with("a:prstGeom", &[("prst", "rect")]);
474 w.empty("a:avLst");
475 w.close("a:prstGeom");
476 w.close("p:spPr");
477 tb.write_xml(&mut w, "p:txBody");
479 w.close("p:sp");
480 w.close("p:spTree");
481 w.close("p:cSld");
482 w.empty("p:clrMapOvr");
484 w.close("p:notes");
485 w.into_string()
486}