use crate::error::PptxError;
use crate::objects::SlideObject;
use crate::packaging::assemble_pptx;
use crate::slide::Slide;
use crate::types::PresLayout;
#[derive(Debug, Clone)]
pub struct SlideLayout {
pub name: String,
pub width: Option<f64>,
pub height: Option<f64>,
}
#[derive(Debug, Clone, Default)]
pub struct SlideMasterDef {
pub title: String,
pub background_color: Option<String>,
pub objects: Vec<SlideObject>,
}
#[derive(Debug, Clone, Default)]
pub struct ThemeProps {
pub head_font_face: Option<String>,
pub body_font_face: Option<String>,
}
#[derive(Debug, Clone)]
pub struct SectionDef {
pub name: String,
pub start_slide: u32,
}
#[derive(Debug)]
pub struct Presentation {
pub author: String,
pub company: String,
pub title: String,
pub subject: String,
pub revision: String,
pub rtl_mode: bool,
pub layout: PresLayout,
pub theme: Option<ThemeProps>,
pub(crate) slides: Vec<Slide>,
pub(crate) slide_layouts: Vec<SlideLayout>,
pub(crate) master: Option<SlideMasterDef>,
pub(crate) sections: Vec<SectionDef>,
slide_id_counter: u32,
}
impl Default for Presentation {
fn default() -> Self {
Presentation {
author: String::new(),
company: String::new(),
title: String::new(),
subject: String::new(),
revision: "1".to_string(),
rtl_mode: false,
layout: PresLayout::default(),
theme: None,
slides: Vec::new(),
slide_layouts: vec![SlideLayout { name: "DEFAULT".to_string(), width: None, height: None }],
master: None,
sections: Vec::new(),
slide_id_counter: 0,
}
}
}
impl Presentation {
pub fn new() -> Self {
Presentation::default()
}
pub fn with_layout(layout: PresLayout) -> Self {
Presentation { layout, ..Default::default() }
}
pub fn author(mut self, a: impl Into<String>) -> Self { self.author = a.into(); self }
pub fn company(mut self, c: impl Into<String>) -> Self { self.company = c.into(); self }
pub fn title(mut self, t: impl Into<String>) -> Self { self.title = t.into(); self }
pub fn subject(mut self, s: impl Into<String>) -> Self { self.subject = s.into(); self }
pub fn revision(mut self, r: impl Into<String>) -> Self { self.revision = r.into(); self }
pub fn rtl(mut self) -> Self { self.rtl_mode = true; self }
pub fn theme(mut self, t: ThemeProps) -> Self { self.theme = Some(t); self }
pub fn define_layout(&mut self, name: impl Into<String>, width: Option<f64>, height: Option<f64>) -> &mut Self {
self.slide_layouts.push(SlideLayout { name: name.into(), width, height });
self
}
pub fn define_master(&mut self, def: SlideMasterDef) -> &mut Self {
self.master = Some(def);
self
}
pub fn add_section(&mut self, name: impl Into<String>) -> &mut Self {
let next_slide_num = self.slide_id_counter + 1;
self.sections.push(SectionDef { name: name.into(), start_slide: next_slide_num });
self
}
pub fn add_slide(&mut self) -> &mut Slide {
self.slide_id_counter += 1;
let slide_num = self.slide_id_counter;
let slide_id = 255 + slide_num; let r_id = slide_num + 1; let slide = Slide::new(slide_num, slide_id, r_id);
self.slides.push(slide);
self.slides.last_mut().unwrap()
}
pub fn slide_count(&self) -> usize {
self.slides.len()
}
pub fn slide(&self, idx: usize) -> Option<&Slide> {
self.slides.get(idx)
}
pub fn slide_mut(&mut self, idx: usize) -> Option<&mut Slide> {
self.slides.get_mut(idx)
}
pub fn write(&self) -> Result<Vec<u8>, PptxError> {
assemble_pptx(self)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn write_to_file(&self, path: &str) -> Result<(), PptxError> {
let bytes = self.write()?;
std::fs::write(path, &bytes).map_err(PptxError::Io)
}
pub fn write_validated(&self) -> Result<Vec<u8>, PptxError> {
let bytes = self.write()?;
let issues = crate::validate::validate(&bytes);
let errors: Vec<_> = issues.into_iter()
.filter(|i| i.severity == crate::validate::Severity::Error)
.collect();
if !errors.is_empty() {
return Err(PptxError::ValidationFailed(errors));
}
Ok(bytes)
}
}