Working with presentation slides
A slide is stored as a separate PresentationML part, usually under ppt/slides/. In ooxmlsdk, slide parts are reached from the presentation part with slide_parts(&document).
The slide list in p:presentation defines normal presentation order. Custom shows can define a separate ordering by listing slide relationship IDs in a p:custShow slide list.
Slide structure
The root element of a slide part is <p:sld/>. It contains common slide data, optional transition and timing data, color map overrides, and extension data.
For a shape, p:nvSpPr and p:spPr are required by the generated schema. The text body p:txBody is optional, but when it is present it must contain a:bodyPr; a:lstStyle is optional.
<p:sld
xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<p:cSld>
<p:spTree>
<p:nvGrpSpPr>
<p:cNvPr id="1" name=""/>
<p:cNvGrpSpPr/>
<p:nvPr/>
</p:nvGrpSpPr>
<p:grpSpPr/>
<p:sp>
<p:nvSpPr>
<p:cNvPr id="2" name="Title 1"/>
<p:cNvSpPr/>
<p:nvPr/>
</p:nvSpPr>
<p:spPr/>
<p:txBody>
<a:bodyPr/>
<a:lstStyle/>
<a:p><a:r><a:t>Slide title</a:t></a:r></a:p>
</p:txBody>
</p:sp>
</p:spTree>
</p:cSld>
</p:sld>
Common child elements include:
| PresentationML element | Purpose |
|---|---|
<p:cSld/> | Common slide data, including shapes and text |
<p:clrMapOvr/> | Color map override for this slide |
<p:transition/> | Transition from the previous slide |
<p:timing/> | Animation and timing data |
<p:extLst/> | Extension data |
p:cSld stores the slide-specific common data, including the shape tree. p:clrMapOvr can inherit the master color mapping with a:masterClrMapping or define an override mapping.
Reading slide content
For inspection tasks, read each SlidePart through the package and parse the text or relationships you need:
#![allow(unused)] fn main() { pub fn get_slide_text( path: &Path, slide_index: usize, ) -> Result<Vec<String>, Box<dyn std::error::Error>> { let document = PresentationDocument::new_from_file_with_settings(path, lazy_settings())?; let presentation_part = document.presentation_part()?; let Some(slide_part) = presentation_part.slide_parts(&document).nth(slide_index) else { return Ok(Vec::new()); }; let xml = slide_part.data_as_str(&document)?.unwrap_or_default(); Ok(extract_drawing_text(xml)) } }
The companion example that reads every slide uses the same traversal:
#![allow(unused)] fn main() { pub fn get_all_slide_text(path: &Path) -> Result<Vec<Vec<String>>, Box<dyn std::error::Error>> { let document = PresentationDocument::new_from_file_with_settings(path, lazy_settings())?; let presentation_part = document.presentation_part()?; let mut slides = Vec::new(); let slide_parts: Vec<_> = presentation_part.slide_parts(&document).collect(); for slide_part in slide_parts { let xml = slide_part.data_as_str(&document)?.unwrap_or_default(); slides.push(extract_drawing_text(xml)); } Ok(slides) } }
Relationship model
Slide parts can own relationships to layouts, images, charts, embedded packages, media, comments, and hyperlinks. Prefer typed accessors such as slide_layout_part(&document) when the target is a known part type. For reference relationships like external hyperlinks, use relationship iterators such as hyperlink_relationships(&document).
When a feature is not yet represented by a high-level helper in ooxmlsdk, keep the package model intact: read the part data, make a focused XML change, and save the package only after the resulting part graph is still valid.
A valid slide is normally associated with a slide layout, and that layout is associated with a slide master. When creating or copying slides, preserve those relationships rather than copying only ppt/slides/slideN.xml.