Python
Fast PowerPoint reading in Python.
pip install pptxboss puts a PowerPoint engine written in Rust behind a small Python API: slide text, speaker notes, tables, chart data, SmartArt, comments, sections, properties, pictures and embedded objects, Markdown output, a structured report of anything skipped, and a verifier with 72 clause-numbered ECMA-376 rules. Legacy .ppt decks open through the same class. Prebuilt abi3 wheels for CPython 3.12 and later on Linux x86_64 and macOS arm64, no C toolchain, MIT OR Apache-2.0.
.pyi stubs with the exact signatures.Text
Extract the text of a deck in three lines.
Document opens a path or bytes. Text comes out in z-order, which ECMA-376 makes the reading order, one paragraph per line, table rows tab-separated, groups descended; date, footer and slide-number placeholders and hidden shapes are left out unless asked for. Whole-deck calls spread slides across cores; threads=1 keeps everything on the calling thread.
import pptxboss
doc = pptxboss.Document("deck.pptx") # or "legacy.ppt"; threads=1 stays on one core
print(doc.slide_count, doc.slide_size)
text = doc.text() # whole deck, slides separated by a blank line
per_slide = doc.slide_texts() # one string per slide, read in parallel
chosen = doc.slide_texts(indexes=[2, 0]) # zero-based, in that order
for slide in doc: # slides parse lazily
print(slide.number, slide.title, slide.text(furniture=True))Notes, tables, charts and the rest
Read everything the file holds.
Every slide exposes its notes, tables with spans and merges, comments with authors and replies, charts with their cached series, SmartArt items depth-first, pictures and embedded objects. Bytes of images and objects are read only when asked for, so a deck’s media costs nothing until then. Core and application properties and sections come from the document.
slide = doc[3]
slide.notes() # speaker notes or None
slide.tables() # rows of cell texts
for comment in slide.comments(): # author, date, text; replies flagged
print(comment.author, comment.text)
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)
for image in slide.images(): # pictures with their image parts
data = slide.image_bytes(image) # read only now
for obj in slide.embedded_objects(): # p:oleObj with prog_id and part
data = slide.object_bytes(obj)
doc.core_properties() # title, creator, created, modified, ...
doc.app_properties() # application, company, slide and word counts
doc.sections() # name and zero-based slide indexesMarkdown
The deck as Markdown.
A ## heading per slide, bullets with their levels, GFM tables, images, chart tables and diagram outlines, slides separated by a rule; notes and comments become block quotes on request. More on the Markdown output →
md = doc.markdown(notes=True) # a heading per slide, bullets, GFM tables
md = doc.markdown(indexes=[0, 1], comments=True, images=False)Reporting
Know what was skipped.
The reader is lenient: broken archives, content types and relationships are worked around, and what cannot be read is skipped. Every skip is returned to the caller. ExtractReport lists failed slides, notes, comments and frames, hidden slides skipped and unknown graphics, and is_complete is true only when nothing was dropped for a reason other than the options.
text, warnings = doc.text_reporting() # what was skipped, one line each
text, report = doc.extract(indexes=[0]) # the same as an ExtractReport
if not report.is_complete:
print(report.failed_slides, report.unknown_graphics)
package = doc.package() # the raw package: parts, content types, rels
package.defects # what the lenient reader worked aroundVerifier
Check a deck against ECMA-376.
check() returns findings sorted most severe first, each with a stable code, a severity, the clause it enforces, the part and a message. check_report() adds the counts and the number of parts checked. Decks saved by PowerPoint pass with no findings.
for finding in pptxboss.check("deck.pptx"): # most severe first
print(finding.severity, finding.code, finding.clause, finding.message)
report = pptxboss.check_report("deck.pptx")
report.is_clean, report.errors, report.warnings, report.parts_checked
for rule in pptxboss.rules():
print(rule.code, rule.severity, rule.clause, rule.summary)Speed
Why it is fast.
Text extraction over 631 test-suite decks, 20× python-pptx.
On one thread, 2.5× the next fastest Rust engine.
To read the text of a 43-slide, 42 MB deck.
Calls that read the archive release the GIL and run on a private reader over the same archive, so calls from different threads run in parallel, and extracting text never reads the media parts. Method and full tables →
Scope
What it does not do.
- Editing existing decks. pptxboss reads decks and creates new ones from Rust or the CLI; the Python package reads and verifies.
- Rendering slides to images.
- Password-protected files are detected and refused with an error that says so, not decrypted.
- Legacy .ppt decks give text, titles, notes, hidden flags, slide size and pictures; their tables, charts, comments and properties are not read, and the verifier covers ECMA-376 packages only.