Skip to main content

pptxboss

A PowerPoint engine written from scratch in Rust: read .pptx and legacy .ppt decks, extract text, notes, tables, charts and images, render Markdown, verify against ECMA-376, create decks. One core, a CLI, and pythonic bindings.

CI python-ci Rust 2021 MIT OR Apache-2.0


Reading a PowerPoint file should not require PowerPoint, a Java runtime, or a pure-Python XML tree. pptxboss is a clean-room reader built from the ECMA-376 specification (Office Open XML): safe Rust, no C dependencies, no bindings to another engine, one core behind the CLI and the Python extension. It is a lenient reader: real decks are damaged, so it compensates for junk before the archive, accepts data descriptors and Zip64 records, reads UTF-16 parts and interleaved pieces, tolerates broken content types and relationships, recovers the slide list when the presentation part does not list it, and skips what it cannot read instead of refusing, reporting every skip.

Highlights

  • Clean-room engine: implemented from ECMA-376 Parts 1 to 4 in safe Rust. The ZIP container, DEFLATE decoder, CRC-32, XML tokenizer, Open Packaging Conventions and PresentationML model are all in-tree; the reader has no compression dependency.
  • Reads only what it needs: the central directory is parsed once, then parts are read with positioned reads. Extracting text from a 42 MB deck never touches its 40 MB of media.
  • Fast on one core, faster on all: a deck opens with one positioned read for small files and one per part otherwise, and slides are spread across cores with a work-stealing counter, each worker with private caches over a shared archive. --threads 1 keeps everything on the calling thread and is still the fastest engine measured (benchmarks).
  • Strict and Transitional alike: both namespace families resolve to the same element ids, and mc:AlternateContent is resolved per Part 3.
  • Two views of a package: Package keeps every defect as written for the verifier; Document reads around them and says what it skipped.
  • The whole deck, not just the slides: speaker notes, comments of both flavours (the 2006 comments part and the threaded 2018 one) with their authors, sections, core and application properties, embedded objects with their bytes, pictures with their image parts, hyperlinks, and alternative text on request. Chart titles, series, categories and values and the text of SmartArt diagrams come out with the slide text.
  • Markdown output: pptxboss markdown renders a deck as a heading per slide, bullets with their levels, GFM tables, images, chart tables and diagram outlines, with notes and comments as block quotes on request.
  • A lean verifier: pptxboss check runs 72 structural rules from ECMA-376 Parts 1 and 2 over the container, part names, content types, relationships, required parts, id ranges and XML well-formedness. Every finding carries a stable code, a severity and the clause it enforces. Real PowerPoint output verifies clean; the rules were calibrated against 790 public test decks.
  • Fastest measured: 9,868 files/s extracting text over a 631-file public corpus, and 7,942 files/s when held to one thread: 3.1x and 2.5x the next fastest Rust engine, 16x to 20x the most-used Python library, with chart and SmartArt text included that no other engine measured produces, and paragraph-for-paragraph agreement on every gated file (benchmarks).
  • Reads Strict decks: the Open XML SDK's Strict-namespace test decks, which most readers refuse, read and verify like any other.
  • Reads legacy .ppt too: the PowerPoint 97-2003 binary format (compound file, persist directory, OfficeArt drawings) is read from the MS-CFB, MS-PPT and MS-ODRAW specifications into the same slide model, so every reading command and API works on it unchanged.

Install

pip install pptxboss          # Python package with the extension module
cargo install pptxboss-cli    # the pptxboss binary

Usage

pptxboss info deck.pptx             # slide count, size, one line per slide
pptxboss text deck.pptx             # slide text, slides separated by blank lines
pptxboss text --notes --headings deck.pptx
pptxboss text --comments --alt-text deck.pptx   # comments after each slide; alt text of pictures
pptxboss markdown --notes deck.pptx # the deck as Markdown, notes as block quotes
pptxboss text --json deck.pptx      # [{"number": 1, "text": "..."}, ...]
pptxboss text --slides 2-4,7 deck.pptx  # only those slides, in that order; also on info and markdown
pptxboss text --threads 1 deck.pptx # cap the worker threads (default: every core)
pptxboss check deck.pptx            # verify against ECMA-376; exit 1 on errors
pptxboss check --json --quiet deck.pptx
pptxboss rules                      # every rule with its code, severity and clause
pptxboss create text out.pptx --title "Hello" --bullet "one" --bullet "two" --notes "say hi"
pptxboss create md out.pptx deck.md # '#' title slide, '##' content slides, list items, Notes:
pptxboss create blank out.pptx --slides 3

Text semantics: shapes in z-order (which ECMA-376 makes the reading order), paragraphs one per line, line breaks preserved, fields included, table rows one per line with tab-separated cells, groups descended, chart titles and data as rows, diagram nodes one per line, hidden shapes and date/footer/slide-number placeholders left out unless asked for. Text is never inherited from a layout or master, so empty placeholders stay empty.

import pptxboss

doc = pptxboss.Document("deck.pptx")   # threads=1 to stay on one core
print(doc.slide_count, doc.slide_size)
for slide in doc:                      # slides parse lazily
    print(slide.number, slide.title)
    print(slide.text())                # z-order, one paragraph per line
    print(slide.notes())               # speaker notes or None
    for table in slide.tables():       # rows of cell texts
        print(table)
    for image in slide.images():       # pictures with their image parts
        data = slide.image_bytes(image)
    for comment in slide.comments():   # author, date, text; replies flagged
        print(comment.author, comment.text)
    for obj in slide.embedded_objects():  # p:oleObj with prog_id and part
        data = slide.object_bytes(obj)
    for chart in slide.charts():       # title, kinds, series with categories and values
        print(chart.title, [s.name for s in chart.series])
    for diagram in slide.diagrams():   # SmartArt as (level, text) items
        print(diagram.items)
print(doc.markdown(notes=True))        # the deck as Markdown
props = doc.core_properties()          # title, creator, created, modified, ...
for section in doc.sections():         # name and zero-based slide indexes
    print(section.name, section.slides)
text, warnings = doc.text_reporting()  # whole deck, plus what was skipped

for finding in pptxboss.check("deck.pptx"):   # the verifier, most severe first
    print(finding.severity, finding.code, finding.clause, finding.message)
use pptxboss_core::Document;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let doc = Document::open("deck.pptx")?;
    for slide in doc.slides() {
        let slide = slide?;
        println!("{}: {}", slide.number(), slide.title().unwrap_or_default());
        println!("{}", slide.text());
        if let Some(notes) = slide.notes_text()? {
            println!("notes: {notes}");
        }
    }
    let (text, report) = doc.text_reporting(&Default::default());
    for warning in report.warnings() {
        eprintln!("warning: {warning}");
    }
    println!("{text}");
    Ok(())
}

Create decks

use pptxboss_write::{Presentation, Rect, Slide};

let deck = Presentation::new()
    .slide(Slide::title_slide("Quarterly review", Some("Q3 2026")))
    .slide(Slide::titled("Highlights").bullet("Revenue up").sub_bullet("in every region", 1).notes("Pause here"))
    .slide(Slide::titled("Numbers").table(Rect::inches(1.0, 1.8, 11.0, 2.0), vec![vec!["Region".into(), "Growth".into()], vec!["EMEA".into(), "12%".into()]], true));
deck.write_to("review.pptx")?;

Output is deterministic (fixed timestamps, fixed part order), reads back through pptxboss-core, and passes pptxboss check with no findings.

Benchmarks

pptxboss is the fastest library measured, on one thread as well as on all cores: 2.5x to 3x the next fastest Rust engine and 16x to 20x python-pptx, with paragraph-for-paragraph agreement on every file that passes the gate.

Text extraction from Python over the 737 .pptx files of the LibreOffice, Apache POI, python-pptx, pandoc and Open XML SDK test suites, best of 3 per file after a warm-up pass, aggregated over the 631 files every engine handled, Apple M3 Pro. A file counts only when pptxboss reports nothing skipped and its per-slide paragraphs match python-pptx after whitespace normalization: 636 files pass, and not one is excluded for a disagreement. The 101 exclusions are 86 Strict-namespace decks python-pptx cannot open, 11 fuzzer-minimized archives, one encrypted deck, and two fuzzer cases pptxboss reports as unreadable.

Library files/s slides/s
pptxboss, all cores 9,868 21,424
pptxboss, one thread (threads=1) 7,942 17,243
office-oxide 3,163 6,867
kreuzberg 1,866 4,051
undoc 1,831 3,975
python-pptx 488 1,060
markitdown 71 154
Method and fine print

Every engine is called from Python through its own adapter. pptxboss spreads a deck's slides across cores unless threads=1 holds it to the calling thread; the one-thread row is the like-for-like comparison, since the other Rust engines run one thread per file (office-oxide's wheel was measured at 0.9 to 1.1 CPU seconds per wall second). The pptxboss rows include chart and SmartArt text, which none of the other engines produce; on this corpus that costs pptxboss 13% of its one-thread time, and text(charts=False, diagrams=False) leaves it out. The test-suite corpus is small files, so the rows are dominated by per-file cost: opening the file, one positioned read for the whole archive when it is small, parsing the directory, the relationships and a few slides.

On two real-world PowerPoint decks (7 and 43 slides, 3 MB and 42 MB) the same harness, best of 40:

Deck office-oxide pptxboss, one thread pptxboss, all cores
43 slides, wall 4.89 ms 1.38 ms 0.44 ms
43 slides, CPU 5.19 ms 1.41 ms 2.60 ms
7 slides, wall 1.05 ms 0.28 ms 0.16 ms
7 slides, CPU 1.10 ms 0.28 ms 0.52 ms

In-process, the 43-slide deck opens in 0.13 ms with positioned reads, tokenizes its 443 KiB of slide XML in 0.6 ms, and yields its text in 1.5 ms on one thread and 0.5 ms on twelve.

Legacy .ppt decks: over the 153 readable files of the LibreOffice and Apache POI .ppt test suites (41 MB), the same harness reads text with pptxboss in 15 ms against 61 ms for office-oxide, best of 3 per file, both on one thread: a legacy deck stays on the calling thread by default, since spreading its slides across cores measured slower on 146 of 152 decks. The words agree on 87 of the 89 decks with real content; the other two have a broken user-edit chain that pptxboss recovers only partially. office-oxide includes master placeholder text, pptxboss never does.

The gate compares pptxboss against python-pptx only, because the other engines do not expose per-slide paragraphs. Absolute numbers depend on the machine and on the cores macOS schedules the process on: an earlier session on the same machine gave every engine 7x to 9x lower rates with the ratios between engines within 20% of these. Reproduce with benchmarks/bench.py after benchmarks/corpora/fetch_public.sh. Engine versions are recorded in benchmarks/results.json.

What's inside

Crate What it does
pptxboss-core ZIP container with positioned reads, DEFLATE decoder, CRC-32, XML pull tokenizer, OPC package model, PresentationML document model, charts, diagrams, comments, properties, text and Markdown extraction; compound-file container and the PowerPoint 97-2003 binary reader
pptxboss-check The verifier: 72 clause-numbered rules over package and presentation structure
pptxboss-write Creates decks: titles, bullets, paragraphs, text boxes, tables, pictures, notes; Markdown to slides; deterministic output that verifies clean
pptxboss-cli The pptxboss binary
pptxboss-py The pptxboss._pptxboss extension module behind the Python package
pptxboss-testkit In-memory ZIP and deck builders for tests; not published

Limitations

  • Password-protected files (encrypted packages and encrypted .ppt) are detected and refused with a clear error, not decrypted.
  • Legacy .ppt decks give their text, titles, notes, hidden flags, slide size and pictures, and are read whole into memory; their tables, charts, comments and properties are not read, the verifier covers ECMA-376 packages only, so check refuses them, and PowerPoint 95 files are refused.
  • Interleaved ("piece") items are reassembled, but no public test deck uses them; the only evidence is the testkit fixture built from the OPC text.
  • No rendering of slides to images.

Development

cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
maturin develop && pytest
make ci

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project shall be dual licensed as above, without any additional terms or conditions.

Download files

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

Source Distribution

pptxboss-0.2.0.tar.gz (228.9 kB view details)

Uploaded Source

Built Distributions

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

pptxboss-0.2.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (856.4 kB view details)

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

pptxboss-0.2.0-cp312-abi3-macosx_11_0_arm64.whl (777.7 kB view details)

Uploaded CPython 3.12+macOS 11.0+ ARM64

File details

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

File metadata

  • Download URL: pptxboss-0.2.0.tar.gz
  • Upload date:
  • Size: 228.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pptxboss-0.2.0.tar.gz
Algorithm Hash digest
SHA256 4f44ed05a516cf5d0caaddceab538e2ffecc89f303fc06414bff7730a7163b38
MD5 bdc7996f4d030c331591b21e3235b0d4
BLAKE2b-256 3cf8c8ebf7f37e0f14adad4082279fe8ceb9b603be42c56b69e8d89c2013a7f3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pptxboss-0.2.0.tar.gz:

Publisher: release-please.yaml on 4thel00z/pptxboss

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pptxboss-0.2.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pptxboss-0.2.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7e3bb7abf27323c3dcba8bacde0dbfac0ea598eaeed55081190e189149498e39
MD5 863fca87930f3db25f6d1873b4107ca6
BLAKE2b-256 d6de1306993c6fa557d66f0c86ca4715441dea72e3cd56cfed4ca9687a7b73c5

See more details on using hashes here.

Provenance

The following attestation bundles were made for pptxboss-0.2.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release-please.yaml on 4thel00z/pptxboss

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pptxboss-0.2.0-cp312-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pptxboss-0.2.0-cp312-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 900ee81e2dca96eb10ea033a48beb69d428e4a0a0a9233f23dcb6f72f29ff4fb
MD5 b08a16c771d296b78544a7342bc0e76b
BLAKE2b-256 a817f38114a0e86e733e13bab99f25b79f13d0b6d72d098e0df03287fcdf4283

See more details on using hashes here.

Provenance

The following attestation bundles were made for pptxboss-0.2.0-cp312-abi3-macosx_11_0_arm64.whl:

Publisher: release-please.yaml on 4thel00z/pptxboss

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.3.0

3 files

0.2.1

3 files

This release

0.2.0 This release

3 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page