rdocx 0.7.0

High-level API for reading, writing, and converting DOCX documents
Documentation
# rdocx

[![CI](https://github.com/tensorbee/rdocx/actions/workflows/ci.yml/badge.svg)](https://github.com/tensorbee/rdocx/actions/workflows/ci.yml)
[![crates.io](https://img.shields.io/crates/v/rdocx.svg)](https://crates.io/crates/rdocx)
[![docs.rs](https://docs.rs/rdocx/badge.svg)](https://docs.rs/rdocx)
[![License: MIT/Apache-2.0](https://img.shields.io/crates/l/rdocx.svg)](LICENSE)
[![MSRV: 1.93](https://img.shields.io/badge/MSRV-1.93-blue.svg)](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.

| Crate | Use it when |
|---|---|
| [`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

| | rdocx | python-docx | docx2pdf | pypandoc |
|---|---|---|---|---|
| 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

| | rdocx | Apache POI | docx4j | Aspose.Words |
|---|---|---|---|---|
| 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

| | rdocx | docx-rs | docx-rust | ooxmlsdk |
|---|---|---|---|---|
| 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

| Metric | rdocx (native) | Python + LibreOffice | Java (POI + FOP) |
|---|---|---|---|
| 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).

| Family | Crate | Purpose |
|---|---|---|
| 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.