Move a paragraph from one presentation to another
Paragraph text in slides is stored inside DrawingML text bodies, usually under <a:p/>. Moving a paragraph between presentations means copying XML from one slide part to another and ensuring any referenced resources still exist.
Shape text body
A shape text body contains all visible text and visible text properties for the shape. It can contain multiple paragraphs, and each paragraph can contain multiple runs.
Its schema shape is:
| Child element | Meaning |
|---|---|
a:bodyPr | Body properties |
a:lstStyle | Text list styles |
a:p | Text paragraphs |
Paragraph markup
<a:p xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:r>
<a:t>Paragraph text</a:t>
</a:r>
</a:p>
If the paragraph uses only text properties, copying the <a:p/> subtree can be enough. If it references hyperlinks, images, embedded objects, comments, or custom extension data, the target package also needs matching relationships and parts.
The upstream sample opens both presentations, gets the first slide from each, finds the first text body in each slide, deep-clones the first source paragraph into the target text body, and replaces the source paragraph with a placeholder paragraph. The save operation must persist both packages.
Rust workflow
Move the first plain DrawingML paragraph from the first source slide into the first target slide:
#![allow(unused)] fn main() { pub fn move_first_paragraph_between_presentations( source_path: &Path, target_path: &Path, ) -> Result<(Vec<u8>, Vec<u8>), Box<dyn std::error::Error>> { let mut source = PresentationDocument::new_from_file_with_settings(source_path, lazy_settings())?; let mut target = PresentationDocument::new_from_file_with_settings(target_path, lazy_settings())?; let source_presentation_part = source.presentation_part()?; let target_presentation_part = target.presentation_part()?; let Some(source_slide_part) = source_presentation_part.slide_parts(&source).next() else { return Err("source presentation has no slides".into()); }; let Some(target_slide_part) = target_presentation_part.slide_parts(&target).next() else { return Err("target presentation has no slides".into()); }; let source_xml = source_slide_part.data_as_str(&source)?.unwrap_or_default(); let (updated_source_xml, paragraph_xml) = remove_first_drawing_paragraph(source_xml)?; source_slide_part.set_data(&mut source, updated_source_xml.into_bytes())?; let target_xml = target_slide_part.data_as_str(&target)?.unwrap_or_default(); let updated_target_xml = append_drawing_paragraph(target_xml, ¶graph_xml)?; target_slide_part.set_data(&mut target, updated_target_xml.into_bytes())?; let mut source_buffer = Cursor::new(Vec::new()); source.save(&mut source_buffer)?; let mut target_buffer = Cursor::new(Vec::new()); target.save(&mut target_buffer)?; Ok((source_buffer.into_inner(), target_buffer.into_inner())) } }
This example handles plain text paragraphs. If the paragraph contains hyperlinks, media, embedded objects, comments, or custom extension data, copy the referenced relationships and parts into the target package before saving.