Skip to content

feat(pptx): convert PowerPoint presentations slide by slide - #47

Merged
chaliy merged 20 commits into
mainfrom
claude/project-thread-foq2nw
Oct 27, 2026
Merged

chaliy merged 20 commits into
mainfrom
claude/project-thread-foq2nw

Conversation

@chaliy

@chaliy chaliy commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Михайло · project thread

What changed

PPTX is now converted to markdown behind a new pptx feature, which is included in all_formats, so the CLI and the Python, Node and WASM packages get it.

  • Slides come out in p:sldIdLst order, each as a ## Slide N: <title> section.
  • Body placeholders render as bullets, with nesting from a:pPr@lvl and buChar/buAutoNum/buNone honoured. Text boxes render as paragraphs.
  • Tables keep gridSpan/rowSpan. Links come only from external relationships. Group shapes and mc:AlternateContent are walked.
  • Speaker notes render under ### Notes.
  • Hidden slides (show="0") are skipped with a hidden_slide_skipped warning unless include_hidden_content(true), the same switch feat(security): skip hidden content by default #42 added for hidden HTML/DOCX content.
  • The title comes from the core properties, falling back to the first slide title. stats.pages is the slide count. With track_sources, each block records its slide number as the page.
  • Legacy .ppt is detected as the new Format::Ppt and rejected explicitly. Macro-enabled ms-powerpoint.* content types map to PPTX. New Error::PptxExtract.
  • The OOXML helpers that DOCX and PPTX share (relationships, inline runs, lists, table cells) move from docx.rs into a new ooxml.rs. DOCX behaviour is unchanged.
  • xml.rs attributes keep their qualified names; attr() still matches by local name, and the new attr_prefixed() is used where r:id and id must not be confused.

Why

Third part of the #11 follow-ups. fetchkit's Office backend needs PPTX.

Before / After

markkit extract --file tests/fixtures/pptx/deck.pptx

Before: Unsupported format (PPTX was detected but rejected).

After:

## Slide 1: Q3 Results

Prepared by Finance

## Slide 2: Highlights

- Revenue up 12%
  - EMEA strongest
- **Bold** [docs](https://example.com/q3)

### Notes

Mention the hiring freeze.

## Slide 3: Numbers

| Region | Revenue |
| --- | --- |
| EMEA | 1.2M |
| APAC | 0.9 \| 1.0M |

just fmt lint test check-okf test-scripts examples pass locally.

Risk

  • Medium: this is a new parser of untrusted input. It uses the same bounded ZIP and XML stack and parse budget as DOCX.
  • Security tests:
    • a zip bomb slide
    • repeated slide references, bounded by the archive total size
    • billion laughs and deeply nested groups
    • random container bytes (proptest)
  • Integration tests also cover broken slide references and internal or missing hyperlinks.
  • The fixture deck.pptx was generated with python-pptx; the script is committed.

Checklist

  • Tests added or updated (pptx_integration.rs, PPTX security tests, detection tests)
  • Backward compatibility considered (additive; Format and Error are #[non_exhaustive])

Part of #11


Generated by Claude Code

chaliy added 20 commits October 26, 2026 23:53
- Single-row sheets render the row as data under Excel column letters
- Extraction.title from XLSX core.xml, ODS meta.xml, XLS SummaryInformation
- XLSB content type / extension / container detected as Format::Xlsb, not Xlsx
- XLSX without _rels/.rels gets the standard package relationship
- Release profile uses panic = "unwind" so catch_unwind guards work
- Document the trailing newline as intended

Part of #11
- Single-row sheets render the row as data under Excel column letters
- Extraction.title from XLSX core.xml, ODS meta.xml, XLS SummaryInformation
- XLSB content type / extension / container detected as Format::Xlsb, not Xlsx
- XLSX without _rels/.rels gets the standard package relationship
- Release profile uses panic = "unwind" so catch_unwind guards work
- Document the trailing newline as intended

Part of #11
…-foq2nw

# Conflicts:
#	crates/markkit/src/extract.rs
#	crates/markkit/src/lib.rs
#	knowledge/log.md
New pptx feature (in all_formats): slides in sldIdLst order as
'## Slide N: <title>' sections, bulleted body placeholders, text boxes,
tables with spans, hyperlinks, speaker notes. Hidden slides skipped unless
include_hidden_content. Shared OOXML helpers move from docx.rs to ooxml.rs.
Legacy .ppt becomes Format::Ppt and is rejected explicitly.

Part of #11
@chaliy
chaliy merged commit 44dc78b into main Oct 27, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant