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 (gradients), hyperlinks, charts, SmartArt, comments.
Metadata
Release files for pptspine 0.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pptspine-0.5.1.tar.gz | 139.0 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| pptspine-0.5.1-cp311-abi3-win_amd64.whl | CPython 3.11 | abi3 | Windows x86-64 | Details |
| pptspine-0.5.1-cp311-abi3-manylinux_2_28_aarch64.whl | CPython 3.11 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| pptspine-0.5.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.11 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| pptspine-0.5.1-cp311-abi3-macosx_11_0_arm64.whl | CPython 3.11 | abi3 | macOS 11.0+ ARM64 | Details |
| pptspine-0.5.1-cp311-abi3-macosx_10_12_x86_64.whl | CPython 3.11 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 50.0 MB
Release files / pptspine-0.5.1.tar.gz
| Download URL | pptspine-0.5.1.tar.gz |
|---|---|
| Size | 139.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ca7677f82db4d7c30e4218b55f2125d016f68e0af2c7ab07b18b8de97c233a2a
|
|
BLAKE2b-256 checksum How to use checksums |
b3bbd675a4fd043c37ba08e0479135e36a944e63b5f7c003b8fdc8fb25324001
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / pptspine-0.5.1-cp311-abi3-win_amd64.whl
| Download URL | pptspine-0.5.1-cp311-abi3-win_amd64.whl |
|---|---|
| Size | 10.5 MB |
| Tags | CPython 3.11 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
0e28f808dc95245a19ab92806684b830a2c7cc2ac531d0d656d4591d44044840
|
|
BLAKE2b-256 checksum How to use checksums |
a96cb8ab0e0a4adcfd51be917792167ce45b891dd9a383d7e817989641cd403e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / pptspine-0.5.1-cp311-abi3-manylinux_2_28_aarch64.whl
| Download URL | pptspine-0.5.1-cp311-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 9.5 MB |
| Tags | CPython 3.11 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
39a4712b2dcafa92154ff38d320029a521ec93f3d27573ba93bdc1e53726b1f7
|
|
BLAKE2b-256 checksum How to use checksums |
a1d6d78a4e25eb8796240fa7689ffd8c7edda2e8cb14200447134db89a805e1b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / pptspine-0.5.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | pptspine-0.5.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 10.5 MB |
| Tags | CPython 3.11 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
066780bcbeec871655e570f66e2429bd3f31ea07619d13bbc583da125991fd86
|
|
BLAKE2b-256 checksum How to use checksums |
0e9461250946bf6bfb8751ed5764a637bff06170752ba65e480f0ee1794a3f84
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / pptspine-0.5.1-cp311-abi3-macosx_11_0_arm64.whl
| Download URL | pptspine-0.5.1-cp311-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 9.3 MB |
| Tags | CPython 3.11 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
abe887d2104aaaa129e7938dce0d122f49bcedf5d7c6c1a0f741025813fa8d34
|
|
BLAKE2b-256 checksum How to use checksums |
07e5c69abd8917bf92dd62f989b5cbedbb1aed58a78726ff51c1c3ab00b3ec89
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / pptspine-0.5.1-cp311-abi3-macosx_10_12_x86_64.whl
| Download URL | pptspine-0.5.1-cp311-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 10.1 MB |
| Tags | CPython 3.11 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
c0ef3c6951ff5d5e20931d30ad656b105187c9a12e02d51d8ed7483f4d9d3f2d
|
|
BLAKE2b-256 checksum How to use checksums |
5ee567bb5329c34b724d29f3b20612c9f92807406a2e004486fae42f9014936c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|