Pure-Rust Word (.docx / OOXML) structural parser with strong table support and local image OCR, via PyO3.
Project description
docspine
A pure-Rust Word (.docx) parser with Python bindings (PyO3 / maturin,
abi3-py311). A .docx file is OOXML — a zip archive of XML parts — and docspine
walks word/document.xml directly to produce a structured, information-preserving
model: paragraphs (styled runs), tables (rows, cells, merges, fills, nesting),
and embedded pictures. Tables are a first-class focus. Embedded images can
additionally be OCR'd locally, offline, and deterministically via the sibling
ocrspine crate (PP-OCRv5 through
tract-onnx — no cloud, no network), and an image that is a table can be
reconstructed into a grid from its OCR word boxes.
docspine is the document-engine sibling of pdfspine
(PDF) and pptspine (PowerPoint),
all sharing the same ocrspine OCR core.
Capabilities
| Area | Status |
|---|---|
| Body blocks: paragraphs + tables in document order | parsed |
| Paragraphs: runs, text, style name, alignment, list level | parsed |
| Run styling: font, size, bold, italic, underline, color | parsed |
| Tables: rows, cells, cell paragraphs | parsed |
Table merges: gridSpan (horizontal) |
parsed |
Table merges: vMerge restart / continue (vertical) |
parsed |
| Nested tables (a table inside a cell) | parsed |
| Cell shading/fill, cell width (dxa), table grid columns | parsed |
| Row height, header rows | parsed |
Embedded pictures: r:embed rel → media name + raw bytes + EMU extent |
parsed |
| Image OCR (embedded pictures → words + boxes) | working (ocr_image) |
| Image-table reconstruction from OCR boxes → grid | working (reconstruct_image_table) |
Legacy binary .doc (OLE/CFB) |
probe + typed downgrade (full body deferred) |
Parsing is tolerant: unknown elements are skipped, missing attributes become
None, and malformed input yields a typed DocError rather than a panic.
docx first; legacy .doc deferred
Modern .docx (OOXML) is the primary target. The old binary .doc is a
Microsoft compound document (OLE/CFB): rebuilding its body from the binary FIB +
piece table is large, fiddly, and shares almost nothing with the docx path. So
docspine ships detection + a clean typed downgrade today (a .doc byte
stream yields DocUnsupportedError, and probe_doc reports the CFB streams when
built with the legacy-doc feature); full .doc body reconstruction is a
follow-up, not a blocker.
Build (from the package root)
uv venv .venv
VIRTUAL_ENV="$(pwd)/.venv" uv pip install maturin pytest
# Structural parsing needs no models. The OCR path resolves models from a
# sibling ../ocrspine/models by default (or set OCRSPINE_MODELS).
OCRSPINE_MODELS="$(cd ../ocrspine && pwd)/models" \
VIRTUAL_ENV="$(pwd)/.venv" .venv/bin/maturin develop --release
Use from Python
import docspine
doc = docspine.open("report.docx")
print(doc.block_count)
for block in doc.body(): # list[dict], introspectable
if block["kind"] == "paragraph":
for run in block["runs"]:
print(run["text"], run["bold"], run["color"])
elif block["kind"] == "table":
for row in block["rows"]:
for cell in row["cells"]:
print(cell["text"], "span", cell["grid_span"], cell["v_merge"])
# Run OCR on raw image bytes (PNG/JPEG), offline:
items = docspine.ocr_image(open("scan.png", "rb").read())
print(" ".join(i["text"] for i in items))
# Reconstruct a table that lives inside an image into a grid:
for table in docspine.reconstruct_image_table(open("table.png", "rb").read()):
for cell in table["cells"]:
print(cell["row"], cell["col"], cell["text"])
Rust workspace
crates/
doc-core domain model + geometry (twip/EMU) + typed DocError. No IO/zip/XML.
doc-parse OOXML reader: zip extract + quick-xml walk -> Document.
Legacy binary .doc probing behind the `legacy-doc` feature.
doc-ocr image-OCR bridge over ocrspine (PaddleOcr) + image-table reconstruction.
py-bindings PyO3 _core extension (the FFI chokepoint); `ocr` feature gates OCR.
Deferred / follow-up
- Full legacy binary
.doc(OLE/CFB /[MS-DOC]) body reconstruction (FIB, piece table, CHPX/PAPX). Today: detection + typed downgrade +probe_doc. - Bundling the PP-OCRv5 ONNX weights into the published wheel (a CI task).
- Richer styling (theme colors, styles.xml inheritance), headers/footers, footnotes/endnotes, comments, fields, hyperlinks targets, SmartArt/charts.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file docspine-0.1.0.tar.gz.
File metadata
- Download URL: docspine-0.1.0.tar.gz
- Upload date:
- Size: 54.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c55cff7deaee996ff4f9fb1f6db8fe3b9571eaa41e5581c9b015ca82fc45106d
|
|
| MD5 |
5e60fc35164b23903a726df2f3e60da5
|
|
| BLAKE2b-256 |
a1e63333dd38f22cdbc9ba080c7e7d998a971aec6d9ba9d1a21d43f5205c872b
|
File details
Details for the file docspine-0.1.0-cp311-abi3-win_amd64.whl.
File metadata
- Download URL: docspine-0.1.0-cp311-abi3-win_amd64.whl
- Upload date:
- Size: 6.1 MB
- Tags: CPython 3.11+, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6890b6f1645d196f336d44f841cee134a8828cffa4c1aaaafd7d016252d076b7
|
|
| MD5 |
1d5b3d15cd71b8be5de0c41f0e529791
|
|
| BLAKE2b-256 |
b33b6df8fdfac569c4835b4e1c21464546a4c650d7ff982884583df45b3a55b6
|
File details
Details for the file docspine-0.1.0-cp311-abi3-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: docspine-0.1.0-cp311-abi3-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 5.2 MB
- Tags: CPython 3.11+, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09d4c76bbc811f11c581cf9a7287a7151f6443fa717b16db7356a097f42c90b8
|
|
| MD5 |
fa3530ef3c24431079dfff7c12d28235
|
|
| BLAKE2b-256 |
a56bb51fc8abd88697ec1ff8d224803388edd69bba16097482997dcb0148eb1d
|
File details
Details for the file docspine-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: docspine-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 6.1 MB
- Tags: CPython 3.11+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9cf2f00e2921d3cae46735469707a964b0e97483dcad936ab5761ff715e0922
|
|
| MD5 |
05420a144b08a5a22ff6406e608eaf85
|
|
| BLAKE2b-256 |
16b031bf775ced0b179c8e8d1325d5a5780c0b9a7665eb54e1f15bf56f8ccb9e
|
File details
Details for the file docspine-0.1.0-cp311-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: docspine-0.1.0-cp311-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 4.9 MB
- Tags: CPython 3.11+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f2b71e68f22ea7b1e200f1121fd54e8335d664c5c03cfc10033e79b31d0dde2
|
|
| MD5 |
57d08ae1870c8dbc76260a1bda47993e
|
|
| BLAKE2b-256 |
ca0addf09dcbb060ccbbf0bbd96fa3d396a90cbca4130c2da8f80943a30d66b3
|