Skip to content

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