Skip to main content

Pure-Rust Word (.docx / OOXML) structural parser with strong table support and local image OCR, via PyO3.

Project description

docspine

PyPI

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. A parsed document can also be exported to PDF (to_pdf() / save_pdf()) with flowed layout and pagination through the shared pure-Rust pdf-typeset engine from pdfspine — no LibreOffice, no cloud converter.

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)
PDF export: to_pdf() / save_pdf() — flowed layout + pagination; per-section page geometry (sectPr), styles.xml + theme effective styles, numbering engine, table fidelity (borders/merges/margins/vAlign, cross-page) working
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.

Install

pip install docspine

docspine is on PyPI. OCR works out of the box: the PP-OCRv5 weights ship in the shared ocrspine-models data package — a runtime dependency pip pulls in automatically — so a plain pip install docspine finds the OCR models with no extra setup (it no longer needs a sibling ../ocrspine/models checkout or OCRSPINE_MODELS). To build from source instead, see below.

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"])

Export to PDF

doc = docspine.open("report.docx")
doc.save_pdf("report.pdf")         # flowed layout + pagination
pdf_bytes = doc.to_pdf()           # or in-memory bytes

# Optional: map a requested font family to a local font file (or to another
# installed family), layered on top of the built-in substitution table:
doc.save_pdf("report.pdf", font_map={"Calibri": "/path/to/Carlito.ttf"})

Per-section page geometry from sectPr is honored (paper size, orientation, margins per section). Rendering is deterministic and fully offline. Missing fonts degrade gracefully: an available face is substituted and a Python UserWarning is emitted once per warning kind — the export never fails on a missing font.

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.
  doc-render  docx -> PDF renderer over the shared pdf-typeset engine (from pdfspine).
  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.
  • Richer styling (theme colors, styles.xml inheritance), headers/footers, footnotes/endnotes, comments, fields, hyperlinks targets, SmartArt/charts.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

docspine-0.2.0.tar.gz (147.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

docspine-0.2.0-cp311-abi3-win_amd64.whl (10.4 MB view details)

Uploaded CPython 3.11+Windows x86-64

docspine-0.2.0-cp311-abi3-manylinux_2_28_aarch64.whl (9.5 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.28+ ARM64

docspine-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (10.5 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64

docspine-0.2.0-cp311-abi3-macosx_11_0_arm64.whl (9.2 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

docspine-0.2.0-cp311-abi3-macosx_10_12_x86_64.whl (10.1 MB view details)

Uploaded CPython 3.11+macOS 10.12+ x86-64

File details

Details for the file docspine-0.2.0.tar.gz.

File metadata

  • Download URL: docspine-0.2.0.tar.gz
  • Upload date:
  • Size: 147.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for docspine-0.2.0.tar.gz
Algorithm Hash digest
SHA256 71481fc2d97fe15ac9ddddc27184a84f992ee3a791b8a0d0b2904cd02132c001
MD5 91e5258924c9ff474f7543f5fc0787be
BLAKE2b-256 b363ca98f709f74861e465d671624fa67f94a778f16841681bf8ce05616ca01b

See more details on using hashes here.

File details

Details for the file docspine-0.2.0-cp311-abi3-win_amd64.whl.

File metadata

  • Download URL: docspine-0.2.0-cp311-abi3-win_amd64.whl
  • Upload date:
  • Size: 10.4 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

Hashes for docspine-0.2.0-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 3c8e8050fdc0125ead2788f81d0acf9105f693640f9589141837017cd54feac6
MD5 5ba4435192eaf087a61cb474e9bf3e56
BLAKE2b-256 fcadc9811a9a31f8f408eb5bbd9b5158b85d06416aea414458a86c9acf4270fc

See more details on using hashes here.

File details

Details for the file docspine-0.2.0-cp311-abi3-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for docspine-0.2.0-cp311-abi3-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 ff9ed4c5b317e35b806532fe8bdb18b385e406384147269e25eeb98373fff516
MD5 326a9d89714c0add1b5adba99b3e4703
BLAKE2b-256 01bd4144b606dac9769828a9163b2c571846229575f6a5b9dfca848942185380

See more details on using hashes here.

File details

Details for the file docspine-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for docspine-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 07a1846383d4e50ad41e69dcc1f502f9a8a2fbe0c2731c834015f4a0ecf56862
MD5 9cf5458f4fb03ae71f2de8efc8d8179b
BLAKE2b-256 7ca728c7ccf60c7444e190197df9fac8be6c1d7a29cdb1a3c18f5db7589db19c

See more details on using hashes here.

File details

Details for the file docspine-0.2.0-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for docspine-0.2.0-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 170f40cf16c92da1dc44d7dd712d5f72a34a5d537ee836c4ac6295e979fb896e
MD5 0afc275f5a0b79b2107fb1725107b64c
BLAKE2b-256 bdb45b2260ffb679a0607e82acd5dd2b3f3cef579f1af28782a81cefa3a14acb

See more details on using hashes here.

File details

Details for the file docspine-0.2.0-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for docspine-0.2.0-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 1746ea5d853e748eca90f373cd985afd45033953732630344473f0d64c01b21f
MD5 440b4e7ddb2a3b1246f7feb54b5f08bc
BLAKE2b-256 e89cefc28718917f8aa151e0208be80fb73949ea6f4f332b2ef9600b3d355d64

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page