Reply to a comment in a presentation
Comment replies are more complex than simple comment insertion because PowerPoint files may use modern comment extension parts in addition to classic PresentationML comment lists.
Model notes
Classic comments are stored as <p:cm/> entries in a slide comment part. Modern comments and replies can involve Office extension namespaces and additional relationship targets. A reply must preserve author identity, timestamps, threading metadata, and the relationship between the slide and its comment parts.
Reply workflow
The upstream modern-comments sample follows this shape:
- Open the presentation and find or create the comment authors part.
- Match the reply author by name and initials, or create a new author record.
- Locate the first or target slide part and read its comment parts.
- Iterate existing comments and choose the comment that should receive a reply.
- Find or create that comment's reply list.
- Append the reply text with the author ID and timestamp.
Rust workflow
Classic PresentationML comments do not have a reply subtree. For classic comments, add another comment by the reply author and preserve the author/comment ids:
#![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()) } }
For modern threaded comments, use the modern PowerPoint comment and author parts (PowerPointCommentPart and PowerPointAuthorsPart) and preserve the threading metadata. Prefer modifying a fixture that already contains a modern comment and reply so the package structure is known-good.