Pure-Rust PowerPoint (.pptx / OOXML) structural parser with local image OCR, via PyO3.
Project description
pptspine
A pure-Rust PowerPoint (.pptx) parser with Python bindings (PyO3 / maturin,
abi3-py311). A .pptx file is OOXML — a zip archive of XML parts — and pptspine
walks that XML directly to produce a structured, information-preserving model:
slides, text frames (paragraphs + styled runs), tables (cells, merges, fills),
pictures, and autoshapes. A parsed deck can also be exported to PDF
(to_pdf() / save_pdf(), one page per slide) through the shared pure-Rust
pdf-typeset engine from the sibling
pdfspine — no LibreOffice, no
cloud converter. 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).
Capabilities
| Area | Status |
|---|---|
| Slides + slide size | parsed |
| Text frames: paragraphs, runs, text | parsed |
| Run styling: font, size, bold, italic, solid-fill color | parsed |
| Paragraph level + alignment | parsed |
| Tables: rows, cells, cell text | parsed |
Table merges: gridSpan / rowSpan / hMerge / vMerge |
parsed |
| Cell solid-fill color | parsed |
Pictures: r:embed rel → media name; raw bytes via Presentation.image_bytes() |
parsed |
| Autoshapes: geometry name, fill, stroke, optional text | parsed (best-effort) |
Groups (p:grpSp): recursive |
parsed |
Speaker notes (notesSlide → Slide.notes) |
parsed |
Structured export: to_text() / to_markdown() (GFM + HTML tables for merges) |
working |
PDF export: to_pdf() / save_pdf() — one page per slide; placeholder/theme inheritance, shape transforms (rot/flip/adj/dash/srcRect), group affine, tables, slide backgrounds, body-anchor/autofit |
working |
| Image OCR (embedded pictures → words + boxes) | working (ocr_image) |
| Image-table geometry reconstruction from OCR boxes | deferred (stub) |
Parsing is tolerant: unknown elements are skipped, missing attributes become
None, and malformed input yields a typed PptError rather than a panic.
Install
pip install pptspine
pptspine 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 the wheel itself
ships no 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
# ../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 pptspine
pres = pptspine.open("deck.pptx")
print(pres.slide_count, pres.slide_size) # e.g. 2 (9144000, 6858000) # EMU
for slide in pres.slides():
for shape in slide.shapes(): # list[dict], introspectable
if shape["kind"] == "text":
for para in shape["paragraphs"]:
for run in para["runs"]:
print(run["text"], run["bold"], run["color"])
elif shape["kind"] == "table":
for row in shape["rows"]:
print([cell["text"] for cell in row])
elif shape["kind"] == "picture":
print("image:", shape["media"])
# Structured export + speaker notes:
print(pres.to_text()) # slides joined by "--- slide N ---"
print(pres.to_markdown()) # one section per slide; GFM / HTML tables
print(pres.slides()[0].text) # all text on a slide (convenience)
print(pres.slides()[0].notes) # speaker notes, or None
# Run OCR on raw image bytes (PNG/JPEG), offline:
items = pptspine.ocr_image(open("scan.png", "rb").read())
print(" ".join(i["text"] for i in items))
# End-to-end: pull an embedded image's bytes and OCR them, offline:
for shape in pres.slides()[0].shapes():
if shape["kind"] == "picture" and shape["media"]:
data = pres.image_bytes(shape["media"]) # bytes | None
if data:
print([i["text"] for i in pptspine.ocr_image(data)])
Export to PDF
pres = pptspine.open("deck.pptx")
pres.save_pdf("deck.pdf") # one PDF page per slide
pdf_bytes = pres.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:
pres.save_pdf("deck.pdf", font_map={"Aptos": "/path/to/Aptos.ttf"})
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/
ppt-core domain model + geometry (EMU) + typed PptError. No IO/zip/XML.
ppt-parse OOXML reader: zip extract + quick-xml walk -> Presentation.
ppt-ocr image-OCR bridge over ocrspine (PaddleOcr).
ppt-render slide -> PDF renderer over the shared pdf-typeset engine (from pdfspine).
py-bindings PyO3 _core extension (the FFI chokepoint).
Deferred / follow-up
- Image-table geometry reconstruction from OCR boxes
(
ppt_ocr::reconstruct_table_from_image, currently a typedUnsupportedstub). - Richer color models (theme/scheme colors, gradients), hyperlinks, charts, SmartArt, comments.
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 pptspine-0.2.0.tar.gz.
File metadata
- Download URL: pptspine-0.2.0.tar.gz
- Upload date:
- Size: 129.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5b07747a23116f2f5a62720349ae1c7bf54d7f7b49df878c1e7903250b353cc
|
|
| MD5 |
503ea37ac9c82d6b47fbb132e892c375
|
|
| BLAKE2b-256 |
045b4fb0fc5008efaf77bde6fb95779f38e02b2fc524e13a4c0a3d2c91efdbd2
|
File details
Details for the file pptspine-0.2.0-cp311-abi3-win_amd64.whl.
File metadata
- Download URL: pptspine-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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9997247864174f81d88aff720994f70e58db063a00b1bea5e7c7681874c1486c
|
|
| MD5 |
624129891831159b7deb5a882637fffd
|
|
| BLAKE2b-256 |
4bae38d6e588550d1ca163206e2360b1881087563e8190118f4d29b1eedf0e14
|
File details
Details for the file pptspine-0.2.0-cp311-abi3-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: pptspine-0.2.0-cp311-abi3-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 9.5 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 |
e9f30813e224ae4c14bf00bc2f486073482fd8859e16d49d0ede24ba2e810c5b
|
|
| MD5 |
12e860267cc966fb6f6ff1803a07f22e
|
|
| BLAKE2b-256 |
bdd10b9ad9284821b2cc522cc165b55f610e1ceba1602d051e14315719cab865
|
File details
Details for the file pptspine-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: pptspine-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 10.5 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 |
eb4f9faff2421020a3afe1c077e8ace3cd24b559b2cab49651fd14e19c077ac2
|
|
| MD5 |
c34729ba2bc0832b32646dcd37d162b4
|
|
| BLAKE2b-256 |
12557783b0db7064b280b6fef4c124fb81272e7d66b4c0fa3274ca6fc945b6e3
|
File details
Details for the file pptspine-0.2.0-cp311-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: pptspine-0.2.0-cp311-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 9.3 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 |
bd1e997e78896cb542d002a09842f2e6e7a8485ccab19beaf529c25d7c39e80f
|
|
| MD5 |
705833add02bf4833e86d9222e3ab6fe
|
|
| BLAKE2b-256 |
14baf4714a06476c24ed1d75ccf60bc459ea2ec6670ceb81b040f8b58f3d4c89
|
File details
Details for the file pptspine-0.2.0-cp311-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: pptspine-0.2.0-cp311-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 10.9 MB
- Tags: CPython 3.11+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
799eed1dcd53683d4067b8b652d3d95b5bf33ca703c3b405f2ad49c69da484aa
|
|
| MD5 |
34a238e25cc70259d30dc100bdbb8a6f
|
|
| BLAKE2b-256 |
fa3f020de4fe5c11ba252a6a16d3c23ca68163e38922d390ccc75024ed0ae79e
|