use crate::error::{PartNotFoundExt, PptxError, PptxResult};
use crate::opc::constants::{content_type as CT, relationship_type as RT};
use crate::opc::part::Part;
use crate::shapes::Shape;
use crate::slide::{
new_notes_master_xml, new_notes_slide_xml, parse_notes_slide_text,
parse_notes_slide_with_part_name, set_slide_background_solid, NotesMasterRef, NotesSlide,
NotesSlideRef,
};
use crate::units::RelationshipId;
use super::Presentation;
impl Presentation {
pub fn notes_slide_or_create(
&mut self,
slide_ref: &crate::slide::SlideRef,
) -> PptxResult<NotesSlideRef> {
{
let slide_part = self
.package
.part(&slide_ref.partname)
.or_part_not_found(slide_ref.partname.as_str())?;
let notes_rels = slide_part.rels.all_by_reltype(RT::NOTES_SLIDE);
if let Some(notes_rel) = notes_rels.first() {
let partname = notes_rel.target_partname(slide_part.partname.base_uri())?;
return Ok(NotesSlideRef { partname });
}
}
let notes_partname = self
.package
.next_partname("/ppt/notesSlides/notesSlide{}.xml")?;
let notes_blob = new_notes_slide_xml();
let slide_target_ref = slide_ref.partname.relative_ref(notes_partname.base_uri());
let notes_target_ref = notes_partname.relative_ref(slide_ref.partname.base_uri());
let mut notes_part = Part::new(notes_partname.clone(), CT::PML_NOTES_SLIDE, notes_blob);
notes_part
.rels
.add_relationship(RT::SLIDE, &slide_target_ref, false);
self.package.put_part(notes_part);
let slide_part = self
.package
.part_mut(&slide_ref.partname)
.or_part_not_found(slide_ref.partname.as_str())?;
slide_part
.rels
.add_relationship(RT::NOTES_SLIDE, ¬es_target_ref, false);
Ok(NotesSlideRef {
partname: notes_partname,
})
}
pub fn notes_master(&self) -> PptxResult<Option<NotesMasterRef>> {
let pres_part = self.presentation_part()?;
let notes_master_rels = pres_part.rels.all_by_reltype(RT::NOTES_MASTER);
match notes_master_rels.first() {
Some(rel) => {
let partname = rel.target_partname(pres_part.partname.base_uri())?;
Ok(Some(NotesMasterRef {
r_id: rel.r_id.clone(),
partname,
}))
}
None => Ok(None),
}
}
pub fn notes_master_or_create(&mut self) -> PptxResult<NotesMasterRef> {
if let Some(existing) = self.notes_master()? {
return Ok(existing);
}
let nm_partname = self
.package
.next_partname("/ppt/notesMasters/notesMaster{}.xml")?;
let nm_blob = new_notes_master_xml();
let mut nm_part = Part::new(nm_partname.clone(), CT::PML_NOTES_MASTER, nm_blob);
let pres_part = self.presentation_part()?;
let theme_rels = pres_part.rels.all_by_reltype(RT::THEME);
if let Some(theme_rel) = theme_rels.first() {
let theme_partname = theme_rel.target_partname(pres_part.partname.base_uri())?;
let theme_target_ref = theme_partname.relative_ref(nm_partname.base_uri());
nm_part
.rels
.add_relationship(RT::THEME, &theme_target_ref, false);
}
self.package.put_part(nm_part);
let pres_partname = self.presentation_partname()?;
let nm_target_ref = nm_partname.relative_ref(pres_partname.base_uri());
let pres_part_mut = self
.package
.part_mut(&pres_partname)
.or_part_not_found(pres_partname.as_str())?;
let r_id_str = pres_part_mut
.rels
.add_relationship(RT::NOTES_MASTER, &nm_target_ref, false);
let r_id = RelationshipId::try_from(r_id_str.as_str())
.map_err(|_| PptxError::InvalidXml(format!("invalid notes master rId: {r_id_str}")))?;
Ok(NotesMasterRef {
r_id,
partname: nm_partname,
})
}
pub fn notes_master_xml(&self, nm_ref: &NotesMasterRef) -> PptxResult<&[u8]> {
let part = self
.package
.part(&nm_ref.partname)
.or_part_not_found(nm_ref.partname.as_str())?;
Ok(&part.blob)
}
#[must_use]
pub fn has_notes_slide(&self, slide_ref: &crate::slide::SlideRef) -> bool {
self.package
.part(&slide_ref.partname)
.is_some_and(|p| !p.rels.all_by_reltype(RT::NOTES_SLIDE).is_empty())
}
pub fn notes_slide_text(
&self,
slide_ref: &crate::slide::SlideRef,
) -> PptxResult<Option<String>> {
let slide_part = self
.package
.part(&slide_ref.partname)
.or_part_not_found(slide_ref.partname.as_str())?;
let notes_rels = slide_part.rels.all_by_reltype(RT::NOTES_SLIDE);
let notes_rel = match notes_rels.first() {
Some(r) => *r,
None => return Ok(None),
};
let notes_partname = notes_rel.target_partname(slide_part.partname.base_uri())?;
let notes_part = self
.package
.part(¬es_partname)
.or_part_not_found(notes_partname.as_str())?;
let text = parse_notes_slide_text(¬es_part.blob)?;
Ok(Some(text))
}
pub fn notes_slide(
&self,
slide_ref: &crate::slide::SlideRef,
) -> PptxResult<Option<NotesSlide>> {
let slide_part = self
.package
.part(&slide_ref.partname)
.or_part_not_found(slide_ref.partname.as_str())?;
let notes_rels = slide_part.rels.all_by_reltype(RT::NOTES_SLIDE);
let notes_rel = match notes_rels.first() {
Some(r) => *r,
None => return Ok(None),
};
let notes_partname = notes_rel.target_partname(slide_part.partname.base_uri())?;
let notes_part = self
.package
.part(¬es_partname)
.or_part_not_found(notes_partname.as_str())?;
let notes_slide = parse_notes_slide_with_part_name(
¬es_part.blob,
Some(notes_partname.as_str().to_owned()),
)?;
Ok(Some(notes_slide))
}
pub fn notes_slide_name(
&self,
slide_ref: &crate::slide::SlideRef,
) -> PptxResult<Option<String>> {
let ns = self.notes_slide(slide_ref)?;
Ok(ns.and_then(|n| n.name))
}
pub fn set_notes_slide_background_solid(
&mut self,
slide_ref: &crate::slide::SlideRef,
color: &str,
) -> PptxResult<()> {
let notes_partname = {
let slide_part = self
.package
.part(&slide_ref.partname)
.or_part_not_found(slide_ref.partname.as_str())?;
let notes_rels = slide_part.rels.all_by_reltype(RT::NOTES_SLIDE);
let notes_rel = notes_rels
.first()
.or_part_not_found("slide has no notes slide")?;
notes_rel.target_partname(slide_part.partname.base_uri())?
};
let notes_part = self
.package
.part_mut(¬es_partname)
.or_part_not_found(notes_partname.as_str())?;
set_slide_background_solid(&mut notes_part.blob, color)
}
pub fn slide_placeholders(&self, slide_ref: &crate::slide::SlideRef) -> PptxResult<Vec<Shape>> {
let slide_xml = self.slide_xml(slide_ref)?;
let tree = crate::shapes::shapetree::ShapeTree::from_slide_xml(slide_xml)?;
Ok(tree
.shapes
.into_iter()
.filter(super::super::shapes::Shape::is_placeholder)
.collect())
}
}