Insert a picture into a word processing document
Pictures require an image part, a relationship from the main document part, and DrawingML markup in the document body.
Package model
The body markup references an image relationship id. The relationship resolves to an image part under the package.
The image bytes are stored outside document.xml. The body contains a run with drawing markup, and that DrawingML references the relationship id for the image part. The graphic object data element can contain application-specific graphic data under a uri, so the picture markup must use the expected DrawingML picture structure for Word to render it.
<w:r>
<w:drawing>
<!-- inline or anchored DrawingML that references r:embed="rId..." -->
</w:drawing>
</w:r>
Rust workflow
Use the main document part as the insertion point. Add an image part, write the image bytes, and insert DrawingML that references the image relationship id.
#![allow(unused)] fn main() { pub fn insert_picture( path: &Path, relationship_id: &str, content_type: &str, image_bytes: &[u8], ) -> Result<Vec<u8>, Box<dyn std::error::Error>> { let mut document = WordprocessingDocument::new_from_file_with_settings(path, lazy_settings())?; let main_part = document.main_document_part()?; let image_part = main_part.add_image_part_with_id(&mut document, content_type.to_string(), relationship_id)?; image_part.set_data(&mut document, image_bytes.to_vec())?; let document_xml = main_part.data_as_str(&document)?.unwrap_or_default(); let document_xml = ensure_relationship_namespace(document_xml); let picture = picture_paragraph_xml(relationship_id, 990_000, 792_000); let updated_xml = insert_before_section_properties(&document_xml, &picture)?; main_part.set_data(&mut document, updated_xml.into_bytes())?; let mut buffer = Cursor::new(Vec::new()); document.save(&mut buffer)?; Ok(buffer.into_inner()) } }
When inspecting or updating existing picture relationships, preserve the relationship ids that body DrawingML references:
#![allow(unused)] fn main() { pub fn list_image_relationship_ids(path: &Path) -> Result<Vec<String>, Box<dyn std::error::Error>> { let document = WordprocessingDocument::new_from_file_with_settings(path, lazy_settings())?; let main_part = document.main_document_part()?; Ok( main_part .related_parts_of_type::<_, ImagePart>(&document) .map(|related| related.relationship_id().to_string()) .collect(), ) } }
In ooxmlsdk, MainDocumentPart::image_parts(&document) traverses existing image parts, while related_parts_of_type::<_, ImagePart>(&document) keeps the relationship id with each image part.