use super::Document;
use crate::style::{Family, Fill, PageLayout};
use crate::xml::{Element, Ns};
use crate::{Error, media_type};
pub struct Presentation {
pub document: Document,
}
pub struct Slide<'a> {
pub element: &'a Element,
pub name: Option<&'a str>,
pub master_page: Option<&'a str>,
}
impl Slide<'_> {
pub fn style_name(&self) -> Option<&str> {
self.element.attr(&Ns::Draw, "style-name")
}
pub fn notes(&self) -> Option<&Element> {
self.element.child(&Ns::Presentation, "notes")
}
pub fn shapes(&self) -> impl Iterator<Item = &Element> {
self.element
.elements()
.filter(|e| !e.is(&Ns::Presentation, "notes") && !e.is(&Ns::Office, "forms"))
}
}
impl Presentation {
pub fn read(bytes: &[u8]) -> Result<Self, Error> {
let document = Document::read(bytes, media_type::PRESENTATION_ANY)?;
Ok(Self { document })
}
pub fn slides(&self) -> Vec<Slide<'_>> {
let Some(body) = self.document.body_of("presentation") else {
return Vec::new();
};
body.elements()
.filter(|e| e.is(&Ns::Draw, "page"))
.map(|element| Slide {
element,
name: element.attr(&Ns::Draw, "name"),
master_page: element.attr(&Ns::Draw, "master-page-name"),
})
.collect()
}
pub fn master(&self, slide: &Slide<'_>) -> Option<&Element> {
self.document.styles.master_page(slide.master_page?)
}
pub fn background(&self, slide: &Slide<'_>) -> Fill {
let own = slide
.style_name()
.map(|name| self.document.styles.resolve(&Family::DrawingPage, name));
if own
.as_ref()
.and_then(|properties| properties.background_visible)
== Some(false)
{
return Fill::None;
}
if let Some(fill) = own.map(|properties| properties.graphic.fill())
&& fill != Fill::None
{
return fill;
}
self.master(slide)
.and_then(|master| master.attr(&Ns::Draw, "style-name"))
.map(|name| self.document.styles.resolve(&Family::DrawingPage, name))
.map_or(Fill::None, |properties| properties.graphic.fill())
}
pub fn background_objects(&self, slide: &Slide<'_>) -> Vec<&Element> {
let shown = slide
.style_name()
.map(|name| self.document.styles.resolve(&Family::DrawingPage, name))
.and_then(|properties| properties.background_objects_visible);
if shown == Some(false) {
return Vec::new();
}
let Some(master) = self.master(slide) else {
return Vec::new();
};
master
.elements()
.filter(|e| e.attr(&Ns::Draw, "layer") == Some("backgroundobjects"))
.filter(|e| e.attr(&Ns::Presentation, "class").is_none())
.collect()
}
pub fn page_layout(&self, slide: &Slide<'_>) -> PageLayout {
slide
.master_page
.and_then(|name| self.document.styles.page_layout_of(name))
.cloned()
.unwrap_or_else(presentation_default)
}
}
fn presentation_default() -> PageLayout {
let portrait = PageLayout::default();
PageLayout {
width: portrait.height,
height: portrait.width,
margin: portrait.margin,
}
}