# rdocx
[](https://github.com/tensorbee/rdocx/actions/workflows/ci.yml)
[](https://crates.io/crates/rdocx)
[](https://docs.rs/rdocx)
[](LICENSE)
[](https://blog.rust-lang.org/2026/01/09/Rust-1.93.0.html)
A pure Rust DOCX library — create, read, and modify Word documents programmatically. Additionally, render pixel-identical PDFs and export to HTML and Markdown, all from the same document object. No LibreOffice, no unoconv, no C dependencies.
## Why rdocx?
Most DOCX solutions in the ecosystem shell out to LibreOffice or wrap C/C++ libraries. rdocx is written entirely in Rust, so it compiles to a single binary with zero runtime dependencies. It works everywhere Rust does — including WASM.
The core focus is **DOCX**: a high-level, python-docx-inspired API for building and editing Word documents with paragraphs, tables, images, headers/footers, styles, and lists. On top of that, rdocx includes a built-in layout engine that paginates your document and can render it to **PDF** (with font subsetting, bookmarks, and selectable text) or export to **HTML** and **Markdown** — so you get faithful output in every format without leaving Rust.
## DOCX Features
- **Read & write** DOCX files with a high-level API
- **Tables** with merged cells, borders, shading, and content-based column sizing
- **Images** — inline and anchored, with header/footer background images
- **Headers & footers** with first-page support and per-section overrides
- **Styles** — paragraph and character styles, theme color resolution
- **Lists** with automatic numbering ID management
- **Custom lists** with independent definitions, per-level number formats, and
configurable starting values
- **Composable links and line breaks** inside paragraphs and table cells
- **Template engine** with placeholder replacement (plain text and regex)
- **TOC generation** with internal hyperlinks and dot-leader tabs
- **Document merging** with style deduplication and numbering remapping
## Output Formats
- **PDF** — built-in layout engine with text shaping (rustybuzz), Unicode line breaking, multi-section pagination, font subsetting, ToUnicode CMap, bookmarks, and images
- **HTML** — semantic mapping from OOXML with CSS styling and base64-embedded images
- **Markdown** — GFM-compatible output with pipe tables and formatting
- **PNG** — page-to-image rendering via tiny-skia rasterizer
## Extras
- **WASM support** via standalone `rdocx-wasm` crate
- **CLI tool** (`rdocx-cli`) — inspect, convert, diff, replace, validate, render
## Installation
```toml
[dependencies]
rdocx = "0.6"
```
Bundled metric-compatible fonts are always available through the deterministic
rendering methods. The default feature adds system font discovery. Disable it
when an application should use only deterministic bundled fonts:
```toml
[dependencies]
rdocx = { version = "0.6", default-features = false }
```
## Quick Start
### Create a document
```rust,no_run
use rdocx::{Document, Length};
let mut doc = Document::new();
doc.add_paragraph("Hello, World!");
let mut para = doc.add_paragraph("");
para.add_run("Bold text").bold(true);
para.add_run(" and ");
para.add_run("italic text").italic(true);
doc.add_table(3, 4);
doc.save("output.docx").unwrap();
```
### Read a document
```rust,no_run
use rdocx::Document;
let doc = Document::open("report.docx").unwrap();
for para in doc.paragraphs() {
println!("{}", para.text());
}
for table in doc.tables() {
for row_index in 0..table.row_count() {
let Some(row) = table.row(row_index) else {
continue;
};
for cell_index in 0..row.cell_count() {
let Some(cell) = row.cell(cell_index) else {
continue;
};
print!("{}\t", cell.text());
}
println!();
}
}
```
### Convert to PDF
```rust,no_run
use rdocx::Document;
let doc = Document::open("report.docx").unwrap();
doc.save_pdf("report.pdf").unwrap();
// Or get bytes directly
let pdf_bytes = doc.to_pdf().unwrap();
```
### Convert to HTML / Markdown
```rust,no_run
use rdocx::Document;
let doc = Document::open("report.docx").unwrap();
let html = doc.to_html();
let markdown = doc.to_markdown();
```
### Template replacement
```rust,no_run
use rdocx::Document;
use std::collections::HashMap;
let mut doc = Document::open("template.docx").unwrap();
let mut replacements = HashMap::new();
replacements.insert("{{name}}", "Jane Doe");
replacements.insert("{{date}}", "2025-01-15");
doc.replace_all(&replacements);
doc.save("filled.docx").unwrap();
```
### Merge documents
```rust,no_run
use rdocx::{Document, SectionBreak};
let mut doc = Document::open("part1.docx").unwrap();
let part2 = Document::open("part2.docx").unwrap();
doc.append_with_break(&part2, SectionBreak::NextPage);
doc.save("combined.docx").unwrap();
```
### Custom lists, links, and fixed table columns
```rust,no_run
use rdocx::{Document, Length, ListLevel};
let mut doc = Document::new();
let list_id = doc.add_list_definition(&[
ListLevel::bullet(),
ListLevel::decimal().start(3),
]);
doc.add_paragraph("A bullet").set_numbering(list_id, 0);
doc.add_paragraph("Starts at three")
.set_numbering(list_id, 1);
let relationship_id = doc.add_hyperlink_relationship("https://docs.rs/rdocx");
let mut paragraph = doc.add_paragraph("");
paragraph
.add_hyperlink("rdocx API documentation", &relationship_id)
.bold(true);
paragraph.add_line_break();
paragraph.add_run("Continue on a new line.");
let mut table = doc.add_table(1, 2);
assert!(table.set_column_width(0, Length::inches(2.0)));
assert!(table.set_column_width(1, Length::inches(3.0)));
doc.save("authoring.docx")?;
# Ok::<(), rdocx::Error>(())
```
## Stable crate family
Most applications should depend only on `rdocx`. The companion crates expose
lower-level boundaries for tools that already own parsed WordprocessingML or
layout data.
| [`rdocx`](https://docs.rs/rdocx) | Creating, reading, editing, or rendering complete DOCX packages |
| [`rdocx-oxml`](https://docs.rs/rdocx-oxml) | Working directly with typed WordprocessingML elements |
| [`rdocx-layout`](https://docs.rs/rdocx-layout) | Paginating an already assembled `LayoutInput` |
| [`rdocx-html`](https://docs.rs/rdocx-html) | Converting parsed Word content to HTML or Markdown |
| [`rdocx-cli`](https://crates.io/crates/rdocx-cli) | Inspecting, converting, validating, or rendering documents from a shell |
| [`rdocx-opc`](https://docs.rs/rdocx-opc) | Maintaining legacy imports while migrating to `oxml-opc` |
| [`rdocx-pdf`](https://docs.rs/rdocx-pdf) | Maintaining legacy imports while migrating to `oxml-pdf` |
## CLI
Install the CLI:
```sh
cargo install rdocx-cli
```
```sh
# Inspect document structure
rdocx inspect report.docx
# Extract plain text
rdocx text report.docx
# Convert to PDF
rdocx convert report.docx --to pdf -o report.pdf
# Convert to HTML or Markdown
rdocx convert report.docx --to html -o report.html
rdocx convert report.docx --to md -o report.md
# Find and replace text
rdocx replace report.docx --placeholder "Draft" --value "Final" -o final.docx
# Diff two documents
rdocx diff v1.docx v2.docx
```
## How rdocx Compares
### vs. Python Libraries
| Create DOCX | Yes | Yes | -- | -- |
| Read DOCX | Yes | Yes | -- | -- |
| DOCX to PDF | Yes (built-in) | No | Via MS Word | Via Pandoc + LaTeX |
| DOCX to HTML | Yes (built-in) | No | No | Yes (lossy) |
| DOCX to Markdown | Yes (built-in) | No | No | Yes (lossy) |
| Layout engine | Yes | None | Delegates to Word | Delegates to LaTeX |
| External runtime | **None** | None (but no PDF) | **MS Word required** | **Pandoc + LaTeX** |
| Install size | **4 MB binary** | ~5 MB | ~31 KB + Word | 300-650 MB |
| Runs in Docker / CI | Yes | Yes (no PDF) | No | Yes (huge image) |
| WASM / browser | Yes | No | No | No |
**python-docx** is the most popular DOCX library in any language (~14M PyPI downloads/month), but it has **zero conversion capabilities** — no PDF, no HTML, no Markdown. Users who need PDF must bolt on a separate tool like LibreOffice (~500 MB) or a commercial API. rdocx gives you the same read/write API *plus* built-in conversion in a single 4 MB binary.
### vs. Java Libraries
| Create DOCX | Yes | Yes | Yes | Yes |
| Read DOCX | Yes | Yes | Yes | Yes |
| PDF (built-in) | Yes | No | Via FOP (limited) | Yes (high fidelity) |
| HTML (built-in) | Yes | No | Yes | Yes |
| License | MIT / Apache-2.0 | Apache-2.0 | Apache-2.0 | **$1,199+** |
| Total dependency size | **4 MB** | 18-28 MB + JRE | 50-80 MB + JRE | 14 MB + JRE |
| Typical memory (moderate doc) | **10-50 MB** | 256 MB - 1 GB | 256 MB - 2 GB | 50-300 MB |
| Cold start | **< 10 ms** | 2-5 sec | 2-5 sec | 2-5 sec |
| Runtime required | None | JVM (~200 MB) | JVM (~200 MB) | JVM (~200 MB) |
Java solutions carry the JVM's baseline overhead: 50-100 MB of RAM before a single document is loaded, and 2-5 second cold starts from class loading. Apache POI has **no built-in PDF** at all. docx4j's FOP pipeline is acknowledged by its own maintainer as limited in fidelity. Aspose has excellent PDF output but costs $1,199+ per developer. rdocx delivers comparable capabilities as a zero-dependency native binary.
### vs. Other Rust Crates
| Create DOCX | Yes | Yes | Yes | Low-level |
| Read DOCX | Yes | Yes | Yes | Low-level |
| Round-trip preservation | Yes | Limited | Limited | N/A |
| Tables, images, headers | Yes | Yes | Basic | Raw XML |
| PDF conversion | **Yes** | No | No | No |
| HTML / Markdown export | **Yes** | No | No | No |
| Layout engine | **Yes** | No | No | No |
| Page-to-image rendering | **Yes** | No | No | No |
| Template engine | **Yes** | No | No | No |
| Document merging | **Yes** | No | No | No |
| Regex find/replace | **Yes** | No | No | No |
| CLI tool | **Yes** | No | No | No |
| WASM | Yes | Yes | No | No |
**docx-rs** (1M+ downloads, 500+ stars) is the most popular Rust DOCX crate, but it is a read/write library only — no conversion, no layout engine, no PDF. The same is true for every other Rust DOCX crate. rdocx is the only Rust crate that combines DOCX read/write with a built-in layout engine and multi-format output (PDF, HTML, Markdown, PNG).
### Resource Footprint
| Binary / install size | **4 MB** | ~500 MB | ~250 MB (JARs + JRE) |
| Memory (moderate document) | **10-50 MB** | ~200-500 MB | ~300 MB - 1.5 GB |
| Cold start | **< 10 ms** | ~2-4 sec (LibreOffice) | ~2-5 sec (JVM) |
| Serverless / Lambda friendly | Yes | Difficult | Difficult |
| Docker image overhead | **~10 MB** (musl static) | ~500 MB+ | ~250 MB+ |
| WASM compatible | Yes | No | No |
## Crate Architecture
Migration guidance for the shared crate cutover is in the
[Unreleased CHANGELOG](CHANGELOG.md#unreleased).
| Shared | `oxml-core` | Units, XML helpers, and document properties |
| Shared | `oxml-opc` | OPC package, relationships, and content types |
| Shared | `oxml-media` | Image detection, dimensions, and media naming |
| Shared | `oxml-drawing` | DrawingML colors, geometry, fills, lines, effects, themes, and text bodies |
| Shared | `oxml-layout` | Layout output types, fonts, and line breaking |
| Shared | `oxml-pdf` | PDF and PNG rendering backends |
| Shared | `oxml-sml` | Minimal SpreadsheetML writer for chart workbooks |
| Word | `rdocx` | High-level Document API |
| Word | `rdocx-opc` | Deprecated compatibility shim over `oxml-opc` |
| Word | `rdocx-oxml` | WordprocessingML types and `oxml-core` compatibility re-exports |
| Word | `rdocx-layout` | Word flow engine, pagination, blocks, tables, and style resolution |
| Word | `rdocx-pdf` | Deprecated compatibility shim over `oxml-pdf` |
| Word | `rdocx-html` | HTML and Markdown conversion |
| Word | `rdocx-cli` | CLI binary |
| Word | `rdocx-wasm` | WASM bindings |
| PowerPoint | `rpptx` | High-level Presentation API |
| PowerPoint | `rpptx-oxml` | PresentationML types |
| PowerPoint | `rpptx-layout` | Inheritance resolver and flattener |
| PowerPoint | `rpptx-render` | Slide renderer |
| PowerPoint | `rpptx-chart` | ChartML model and renderer |
## Minimum Supported Rust Version
1.93 (edition 2024)
## License
Licensed under either of
- MIT license ([LICENSE](LICENSE) or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
at your option.