Add a comment to a slide in a presentation
Comments are stored in comment parts related to slides, with author information stored separately at the presentation level. Adding a comment is therefore a package graph update, not just a text edit in the slide XML.
This page describes modern PowerPoint comments. Classic comments use a different, older package shape and should be tested separately before reusing the same writer logic.
Comment parts
A comment list can look like this:
<p:cmLst xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
<p:cm authorId="0" dt="2026-05-04T10:00:00Z" idx="1">
<p:pos x="10" y="20"/>
<p:text>Review this slide.</p:text>
</p:cm>
</p:cmLst>
The matching author entry is stored in a presentation-level comment authors part.
Add-comment workflow
The package update has these steps:
- Open the presentation and get the presentation part.
- Find or create the presentation-level comment authors part.
- Find an existing author by name and initials, or append a new author and assign an ID.
- Locate the target slide by slide ID or slide relationship.
- Find or create the slide's PowerPoint comments part.
- Append the comment with author ID, timestamp, position, and text.
- Ensure the slide has the extension list entries required by modern comments.
Rust workflow
Create or reuse the classic comment authors part, create or reuse the slide comments part, then append a comment:
#![allow(unused)] fn main() { pub fn add_comment_to_slide( path: &Path, slide_index: usize, author_name: &str, initials: &str, text: &str, ) -> Result<Vec<u8>, Box<dyn std::error::Error>> { let mut document = PresentationDocument::new_from_file_with_settings(path, lazy_settings())?; let presentation_part = document.presentation_part()?; let authors_part = if let Some(part) = presentation_part.comment_authors_part(&document) { part } else { presentation_part.add_new_part_auto_id::<_, CommentAuthorsPart>(&mut document)? }; let authors_xml = authors_part .data_as_str(&document)? .map(str::to_string) .filter(|xml| !xml.trim().is_empty()) .unwrap_or_else(empty_comment_authors_xml); let (updated_authors_xml, author_id, comment_index) = upsert_comment_author(&authors_xml, author_name, initials)?; authors_part.set_data(&mut document, updated_authors_xml.into_bytes())?; let Some(slide_part) = presentation_part.slide_parts(&document).nth(slide_index) else { return Err("slide index out of range".into()); }; let comments_part = if let Some(part) = slide_part.slide_comments_part(&document) { part } else { slide_part.add_new_part_auto_id::<_, SlideCommentsPart>(&mut document)? }; let comments_xml = comments_part .data_as_str(&document)? .map(str::to_string) .filter(|xml| !xml.trim().is_empty()) .unwrap_or_else(empty_slide_comments_xml); let updated_comments_xml = append_slide_comment(&comments_xml, author_id, comment_index, text)?; comments_part.set_data(&mut document, updated_comments_xml.into_bytes())?; let mut buffer = Cursor::new(Vec::new()); document.save(&mut buffer)?; Ok(buffer.into_inner()) } }
Modern PowerPoint comments can also use newer Office extension parts. Keep modern threaded comments separate from the classic p:cmLst path shown above.
When matching author data, use the exact name and initials PowerPoint stores in the file, usually the values from PowerPoint Options under the General tab.