Editing Office Documents
EditableDocument is the read-modify-write counterpart to Document. It preserves every unmodified OPC part — images, charts, styles, themes, relationships — verbatim, so your edits don’t reflow surrounding content or invalidate downstream consumers.
Editing is supported for DOCX, XLSX, and PPTX. Legacy DOC, XLS, and PPT are read-only — convert them with save_as first if you need to edit.
What you can do
| Operation | DOCX | XLSX | PPTX | Method |
|---|---|---|---|---|
| Replace text in body / placeholders | ✓ | — | ✓ | replace_text |
| Set a cell value (string / number / bool / empty) | — | ✓ | — | set_cell |
| Save to disk | ✓ | ✓ | ✓ | save |
| Save to bytes | ✓ | ✓ | ✓ | save_to_bytes |
replace_text returns 0 when called on an XLSX (no <w:t> or <a:t> elements exist there); use set_cell for spreadsheets instead.
Open, edit, save
Python
from office_oxide import EditableDocument
with EditableDocument.open("template.docx") as ed:
ed.replace_text("{{name}}", "Alice")
ed.replace_text("{{date}}", "2026-04-19")
ed.save("filled.docx")
Rust
use office_oxide::edit::EditableDocument;
let mut ed = EditableDocument::open("template.docx")?;
ed.replace_text("{{name}}", "Alice");
ed.replace_text("{{date}}", "2026-04-19");
ed.save("filled.docx")?;
JavaScript
import { EditableDocument } from 'office-oxide';
using ed = EditableDocument.open('template.docx');
ed.replaceText('{{name}}', 'Alice');
ed.replaceText('{{date}}', '2026-04-19');
ed.save('filled.docx');
Go
ed, err := officeoxide.OpenEditable("template.docx")
defer ed.Close()
ed.ReplaceText("{{name}}", "Alice")
ed.ReplaceText("{{date}}", "2026-04-19")
ed.Save("filled.docx")
C#
using var ed = EditableDocument.Open("template.docx");
ed.ReplaceText("{{name}}", "Alice");
ed.ReplaceText("{{date}}", "2026-04-19");
ed.Save("filled.docx");
Save to bytes (for upload / streaming)
Python
with EditableDocument.open("template.docx") as ed:
ed.replace_text("{{name}}", "Alice")
bytes_out = ed.save_to_bytes()
# upload bytes_out to S3 / send over HTTP / etc.
Rust
let mut ed = EditableDocument::open("template.docx")?;
ed.replace_text("{{name}}", "Alice");
let mut buf = std::io::Cursor::new(Vec::new());
ed.write_to(&mut buf)?;
let bytes: Vec<u8> = buf.into_inner();
JavaScript
using ed = EditableDocument.open('template.docx');
ed.replaceText('{{name}}', 'Alice');
const bytes = ed.saveToBytes(); // Uint8Array
What “preserve OPC parts” actually means
OOXML files are ZIP archives containing dozens of XML parts plus binary parts (images, fonts, embedded objects). Naïve editors reserialize every part on save, which can:
- Reorder relationships and break links
- Drop extension parts the editor doesn’t understand (custom XML, AlternateContent fallbacks)
- Reformat XML in ways that break downstream diffs and signatures
EditableDocument rewrites only the parts you touched. Everything else is copied byte-for-byte. The result is the smallest possible diff between input and output — friendlier to version control, signature verification, and round-tripping through other tools.
When to use the format-specific API instead
EditableDocument covers the 80% case (templating, batch fill-in, cell writes). For richer edits — adding paragraphs, inserting tables, adjusting styles, building slides from scratch — drop into the per-format APIs:
use office_oxide::docx::edit::DocxEditor;
let mut docx = DocxEditor::open("report.docx")?;
docx.append_paragraph("New section", Some("Heading2"));
docx.save("report.docx")?;
The per-format editors are documented under each binding’s reference (Rust: docx::edit, xlsx::edit, pptx::edit).
See also
- Replace text — the
replace_textAPI in depth - Set XLSX cells — cell types, formats, and edge cases
- Conversion — turn legacy DOC/XLS/PPT into editable OOXML first