easydoc-writer
DOCX document writer with fluent builder API and atomic file output

English | 简体中文
Overview · Capabilities · Architecture ·
Quick Start · API ·
Upstream · Quality
Status: alpha pre-release (latest on crates.io)
MSRV: Rust 1.88
Edition: 2024
Resolver: 3
Maturity: Alpha -- public API may change
Last verified: 2026-08-11
1. Overview
easydoc-writer is a Rust crate for generating DOCX documents from a semantic document model (DocumentContent) or via a fluent builder API. It is part of the easydoc-rust workspace and corresponds to the write layer of Java EasyExcel (com.alibaba.excel).
| Dimension |
Value |
| Crate |
easydoc-writer |
| Status |
Alpha pre-release (latest on crates.io) |
| MSRV / Edition |
1.88 / 2024 |
| Unsafe policy |
forbid (workspace lint) |
| License |
Apache-2.0 |
1.1 What It Is
- A DOCX generator built on
docx-rs that renders the DocumentContent semantic model into OOXML.
- Provides a fluent
DocBuilder API for programmatic document construction.
- Supports atomic file writes via temporary file + persist (no partial output on failure).
- Includes a
DocEditor for text replacement in existing DOCX files.
- Lifecycle hooks (
DocWriteHandler) for before/after document, paragraph, and table callbacks.
1.2 What It Is Not
- Not a DOCX reader -- use
easydoc-reader for reading.
- Not a Markdown converter -- use
easydoc-markdown for conversion.
- Not a full OOXML style engine -- advanced formatting (columns, watermarks, macros) is out of scope.
- Not a template engine -- use
easydoc-template for placeholder-based template filling.
2. Capabilities
2.1 Write Capability Matrix
| Element |
Write |
Round-trip Fidelity |
Evidence |
| Paragraphs |
Stable |
High |
content_renderer.rs tests |
| Headings (H1-H6) |
Stable |
High |
content_renderer.rs tests |
| Tables (column span) |
Stable |
High |
content_renderer.rs tests |
| Images (binary embedding) |
Stable |
High |
content_renderer.rs tests |
| Lists (ordered / unordered, multi-level) |
Stable |
High |
content_renderer.rs tests |
| Hyperlinks (URL) |
Stable |
High |
content_renderer.rs tests |
| Code blocks |
Stable |
Partial |
Rendered as monospace paragraphs |
| Page / column breaks |
Stable |
High |
content_renderer.rs tests |
| Text styles (bold / italic / strikethrough) |
Stable |
High |
content_renderer.rs tests |
| Footnotes / endnotes |
Stable |
Partial |
Rendered as indented paragraphs |
| TextBox |
Stable |
Partial |
Content rendered as nested blocks |
| Sections |
Stable |
Partial |
Content rendered as sub-blocks |
| Thematic breaks |
Stable |
Partial |
Rendered as page breaks |
| Math formulas (OMML) |
Not supported |
N/A |
Use easydoc-markdown for OMML to LaTeX |
2.2 Edit Capability Matrix
| Operation |
Status |
Notes |
| Open existing DOCX |
Stable |
DocEditor::open() |
| Text replacement (placeholder) |
Stable |
replace_text(find, replace) |
| Save (overwrite) |
Stable |
Atomic via office_oxide |
2.3 Status Definitions
| Status |
Definition |
| Stable |
Public API, tests, and documentation complete |
| Partial |
Only explicitly listed subset available |
| N/A |
Not available |
3. Architecture
DocumentContent (semantic model from easydoc-core)
│
▼
content_renderer::render_document_content()
│
▼
docx-rs Docx builder (OOXML construction)
│
▼
DocWriteExecutor::save()
│
▼
AtomicFile (temp file + persist)
│
▼
Output .docx file
3.1 Crate Dependencies
flowchart TB
USER["Application"] --> WRITER["easydoc-writer"]
WRITER --> CORE["easydoc-core"]
WRITER --> OOXML["easydoc-ooxml"]
WRITER --> DOCX["docx-rs"]
WRITER --> OXIDE["office_oxide"]
3.2 Key Types
| Type |
Role |
DocBuilder |
Fluent builder for programmatic DOCX creation |
DocWriteExecutor |
Executes the build and saves to file |
DocEditor |
Opens existing DOCX for text replacement |
TableWriteBuilder |
Fluent builder for table construction |
DocWriteHandler |
Lifecycle callback trait (before/after hooks) |
render_document_content() |
Renders DocumentContent to docx_rs::Docx |
render_with_handler() |
Renders with lifecycle handler callbacks |
3.3 Handler Lifecycle
before_document
├── before_paragraph / after_paragraph (per paragraph)
├── before_table / after_table (per table)
│ ├── before_cell / after_cell (per cell)
└── (other blocks)
after_document
4. Quick Start
4.1 Installation
[dependencies]
easydoc-writer = "0.1.0-alpha"
4.2 Fluent Builder
use easydoc_writer::DocBuilder;
use easydoc_core::HeadingLevel;
fn main() -> Result<(), Box<dyn std::error::Error>> {
DocBuilder::new("report.docx")
.title("Quarterly Report")
.author("Alice")
.add_heading("Introduction", HeadingLevel::H1)
.add_paragraph(
easydoc_writer::Paragraph::new()
.add_run(easydoc_writer::Run::new("This is the introduction."))
)
.add_heading("Results", HeadingLevel::H2)
.add_table(easydoc_writer::Table::from_data(&vec![
vec!["Metric", "Value"],
vec!["Revenue", "$1.2M"],
]))
.build()?
.save()?;
Ok(())
}
4.3 Render from Semantic Model
use easydoc_core::{DocumentContent, DocumentBlock, DocumentTextRun};
use easydoc_writer::content_renderer::render_document_content;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let content = DocumentContent {
blocks: vec![
DocumentBlock::Heading {
level: 1,
runs: vec![DocumentTextRun {
text: "Hello World".into(),
..Default::default()
}],
},
DocumentBlock::Paragraph(vec![DocumentTextRun {
text: "Generated from semantic model.".into(),
..Default::default()
}]),
],
..Default::default()
};
let docx = render_document_content(&content)?;
Ok(())
}
4.4 Edit Existing Document
use easydoc_writer::DocEditor;
fn main() -> Result<(), Box<dyn std::error::Error>> {
DocEditor::open("template.docx".as_ref())?
.replace_text("{name}", "Alice")
.replace_text("{date}", "2026-08-11")
.save()?;
Ok(())
}
5. API Reference
5.1 Core API
| Function / Type |
Purpose |
DocBuilder::new(path) |
Create builder targeting output path |
builder.title(t) |
Set document title |
builder.author(a) |
Set document author |
builder.add_heading(text, level) |
Add heading paragraph |
builder.add_paragraph(p) |
Add paragraph |
builder.add_table(t) |
Add table |
builder.add_image(img) |
Add image |
builder.add_page_break() |
Add page break |
builder.build()?.save() |
Build and save atomically |
DocEditor::open(path) |
Open existing DOCX for editing |
editor.replace_text(find, replace) |
Replace text placeholders |
editor.save() |
Save modified document |
render_document_content(content) |
Render DocumentContent to Docx |
render_with_handler(content, handler) |
Render with lifecycle hooks |
5.2 Error Model
| Error Variant |
Scenario |
Source |
DocError::Io |
File I/O failure |
std::io::Error |
DocError::Document |
Document open/render failure |
office_oxide, docx-rs |
6. Upstream Compatibility
6.1 Java EasyExcel Mapping
This crate corresponds to the write layer of Java EasyExcel:
| Upstream Component |
Rust Equivalent |
Notes |
ExcelBuilderImpl |
DocBuilder |
Fluent builder pattern |
ExcelWriter |
DocWriteExecutor |
Executes and saves |
WriteHandler |
DocWriteHandler |
Lifecycle callbacks |
Hutool Word07Writer (edit) |
DocEditor |
Text replacement in existing files |
| Upstream Capability |
Rust Status |
Evidence |
| Fluent document building |
Stable |
DocBuilder API |
| Semantic model rendering |
Stable |
render_document_content() |
| Atomic file output |
Stable |
AtomicFile in easydoc-ooxml |
| Lifecycle handler hooks |
Stable |
DocWriteHandler trait |
| Text replacement editing |
Stable |
DocEditor::replace_text() |
6.2 Differences from Java
- No reflection: Rust uses typed structs instead of Java reflection for data binding.
- Atomic writes: All file output uses temp-file + persist; Java EasyExcel does not guarantee this.
- Handler model: Rust handlers use trait methods with explicit contexts; Java uses interface implementations.
- Style system: Rust style configuration is struct-based (
ParagraphStyle, TableStyle, FontConfig); Java uses builder chains.
7. Quality & Testing
7.1 Unsafe Policy
This crate uses #![deny(unsafe_code)]. The workspace enforces unsafe_code = "forbid" via [workspace.lints.rust].
7.2 Test Categories
| Category |
Scope |
Tool |
| Unit tests |
Renderer, builder, editor, handler lifecycle |
cargo test |
| Integration tests |
Full document generation + ZIP validation |
cargo test |
7.3 Building & Testing
cargo check -p easydoc-writer
cargo test -p easydoc-writer
cargo clippy -p easydoc-writer -- -D warnings
cargo doc -p easydoc-writer --no-deps
Back to top · docs.rs · crates.io · Issues