Webfold
Menu

Format · PPTX

PowerPoint (PPTX) to Markdown: slides in presentation order, with titles, text, tables and notes.

Webfold writes each PPTX slide as a '## Slide N' section in presentation order with its title, text, tables, hyperlinks and speaker notes: on 7 PPTX fixtures its hot median is 40.8 µs against 237.0 µs for anydoc, another document converter, measured on 24 September before the rewrite; a chart's title and values are written as a table, SmartArt, comments and images are not read, and the rewritten reader is in the main branch, not in the 0.1.0 release.

Reads a PPTX package and writes each slide as a '## Slide N' section in the order the presentation shows them: the title, the text one paragraph at a time, tables as Markdown tables, hyperlinks, and the speaker notes. Footers, dates and slide numbers are dropped. A chart's title and its series values are written as a table. SmartArt, comments and images are not read.

Last updated , the newest measurement or check on record for this page.

In Webfold's main branch; not in the 0.1.0 release

Status

  • The rewritten DOCX, PPTX and XLSX readers are in Webfold's main branch. Webfold pins superdocuments at revision 68c216f, which contains them. Webfold built from main reads files this way; the 0.1.0 release predates it. Everything below describes the new readers.

See it work

How do I try it from wherever I work?

1webfold convert deck.pptx

Input

What input does PPTX reading accept?

PPTX packages

Routed by the pptx extension to superdocuments/src/ooxml/pptx.rs. The slide list is the sldIdLst of the presentation part, resolved through that part's relationships. Only if no slide resolves does the reader fall back to the numbers in ppt/slides/slideN.xml file names.

Slide order and numbering

Slides are numbered by position in the presentation, which is the number a person sees. A moved slide keeps its old file name, so the file-name number would be wrong.

Titles

A shape whose placeholder type is title or ctrTitle gives the slide's title, written as 'Slide N: Title'. Text without a title placeholder is body text.

Body text

Each a:p paragraph is written as its own paragraph, with a:br written as a line break, bold and italic as ** and *, and hyperlinks as [text](url) from the slide's relationships. An explicit bullet (buChar) gives '- ' and an explicit number (buAutoNum) a counted '1.', indented four spaces per level. A body paragraph with no bullet setting of its own takes the bullet of its slide layout's placeholder, then of the master's body style, level by level.

Tables

A graphic frame's a:tbl becomes a pipe table whose first row is the header. Cells carrying hMerge or vMerge are empty, and paragraphs inside a cell are joined with <br>.

Speaker notes

The slide's notesSlide relationship gives the notes part. Only its body placeholder is read, and the text is written as '**Notes:** ...' after the slide's content.

What is dropped

Footer, date, header and slide-number placeholders are not written. The Fallback half of mc:AlternateContent is skipped so equations and similar are not read twice. A slide with no title, body or notes is left out, though it still counts in the numbering.

Output

What comes out?

Markdown string

Slides are separated by a '---' rule. Each starts with '## Slide N' or '## Slide N: Title', followed by body paragraphs, tables and the notes.

No structured type

The README lists no public structured output for PPTX. convert_streaming and the cache call the same reader as convert_bytes.

How it works

What does the code do, step by step?

  1. 1

    Dispatch

    convert_bytes sends pptx to ooxml::pptx::convert. The Webfold command line sends .pptx to webfold's convert_path, which calls convert_bytes in the superdocuments revision Webfold pins (68c216f).

  2. 2

    Bound the archive

    The same ZIP limits as the other OOXML readers: 4,096 entries, 32 MiB per declared entry, 128 MiB declared total.

  3. 3

    Order the slides

    presentation.xml is read for its sldId elements. Each has two attributes named id; the one that matches a slide relationship is used, because the other is the slide's numeric id.

  4. 4

    Read each slide

    Events from the slide part feed a paragraph, a table and a placeholder tracker. A paragraph ends as the title, a body line, a list item, or a table-cell line.

  5. 5

    Read the notes

    The notes part is read with the same code, keeping only the body placeholder.

  6. 6

    Write

    Each non-empty slide is written with its position as the number.

Measured

What did we measure, and how?

PPTX: Webfold against anydoc on 7 public fixtures

Run of 2026-09-24

How fast does Webfold convert these files to Markdown, in process and from a fresh process, against anydoc on the same bytes?

Corpus. 7 public fixtures in PPTX from the anydoc repository (tests/fixtures; MIT licence per its README), out of 45 across ten formats. Fixture hashes: document-fixture-sha256-2026-09-24.txt.

ToolVersionRole
Webfoldgit 614483614e4e (document::convert_bytes, in process)Subject. The readers as of that revision, not the newer ones described on this page.
anydocgit 261fc257d17c (anydoc::to_markdown_bytes, Rust library)Comparison converter, linked into the same binary and given identical bytes.
MetricHow it is computed
Hot medianPer fixture: the median of 7 samples, each the mean time of 20 back-to-back in-process conversions of the same bytes, output consumed so it is not optimised away. The cell is the median of those per-fixture medians over the format's fixtures.
Cold medianPer fixture: the median of 7 timed launches (after 2 discarded) of a fresh runner process that reads the file and converts it once, with a warm operating-system cache. Wall time, process start included. The cell is the median of the per-fixture medians.
Converted without errorThe call returned a result. The run records conversion success and output size only; it has no gold text and no fidelity score.
FormatFixturesConverted without error, Webfold / anydocHot median, WebfoldHot median, anydocHot, anydoc over WebfoldWebfold faster, hotCold median, WebfoldCold median, anydocWebfold faster, cold
PPTX7 (6 hand-made)7 / 740.8 µs237.0 µs5.8 times7 of 72.90 ms3.64 ms7 of 7
1# webfold-bench is private until the public release of Webfold. Run this from a checkout of it.
2git clone https://github.com/firecrawl/anydoc
3cd webfold-bench/bench/document-h2h
4cargo run --release -- --fixtures ../../../anydoc/tests/fixtures --out document-results.json --warmup 2 --samples 7 --iterations 20

Reproduce: webfold-bench/bench/document-h2h/src/main.rs; revisions and hashes: webfold-bench/bench/results/document-rust-cold-hot-2026-09-24.provenance.json. Figures read from webfold-bench/bench/results/document-rust-cold-hot-2026-09-24.json and document-fixture-sha256-2026-09-24.txt.

Limits of this measurement

  • Webfold wrote nothing for one of the seven PPTX fixtures (handmade-altpath.pptx: 0 bytes, against 24 bytes from anydoc), and that file still counts as converted without error. Output size is not output correctness.
  • Fixtures are tiny: 0.4 to 25 KB each, and most are hand-made feature probes from the anydoc repository, not real-world documents.
  • Speed on success only. Nothing here says the output is right: there is no gold text and no fidelity score, and a conversion that returns almost nothing still counts as converted.
  • One host (Intel Core i5-1340P, rustc 1.96.1) and one run, 2026-09-24. Hot timings are in process and exclude start-up; cold timings include process start, which dominates them (about 2 to 3 ms for both tools), so the cold gap is small.
  • The run measured the Webfold revision named above. The harness manifest pins a later Webfold revision (391c3e290b19) through git dependencies, so a rerun at the pin measures newer readers; to reproduce these figures, use the revisions listed in the provenance file. The readers written since the measured revision have not been benchmarked.

Document conversion benchmark →

Evidence

How do we know each claim?

Each row says how we know. Read from the code means the behaviour is in the implementation at the path shown. Covered by a test names a test in the repository; this page does not claim it was run for this release. Measured points to a benchmark with its protocol and limits.

ClaimHow we knowWhereDetail
Slides follow the presentation order, not the file numbers; notes and a table are readCovered by a testsuperdocuments/tests/office_real.rs (pptx_follows_presentation_order_and_reads_notes_and_tables); fixture tests/fixtures/office/basic.pptxRan 2026-11-08, passing. The python-pptx fixture has its two slides reordered, so 'Second slide' (slide2.xml) comes first. The first run of this test failed: the reader took sldId's numeric id instead of r:id and fell back to file order. That is fixed, and is why the test uses a real reordered file.
A LibreOffice-written deck reads the same, with explicit bullets as list itemsCovered by a testsuperdocuments/tests/office_real.rs (libreoffice_written_xlsx_and_pptx_read_the_same_content); fixture tests/fixtures/office/lo.pptxRan 2026-11-08, passing. Written by LibreOffice from basic.odp.
Entity references in slide text surviveCovered by a testsuperdocuments/tests/office_real.rs (entity_references_are_kept_in_every_xml_format)Ran 2026-11-08, passing: 'Intro & Goals' and 'Point <A>'.
A minimal slide with no presentation part still convertsCovered by a testsuperdocuments/src/document.rs (office_packages_convert_through_public_api)Ran 2026-11-08, passing. This is the file-name fallback.
ZIP limits are shared with the DOCX and spreadsheet readersRead from the codesuperdocuments/src/document.rs (archive, entry)One function for all OOXML readers.
Only the pptx extension is routed to the PPTX readerRead from the codesuperdocuments/src/document.rs (convert_bytes); webfold/src/cli/document.rs:130-140No ppt arm exists.

What are the limits?

Know the limits

  • A chart is written as its title and a table with one column per series. SmartArt and comments are not read. Pictures are written as !alt on their slide, and document::extract_images returns the bytes, type, dimensions, alt text and slide of each one.
  • Text that only the layout or master carries is not read. Bullets are: a paragraph with no bullet setting of its own takes its layout placeholder's bullet, then the master's, level by level.
  • No file written by Microsoft PowerPoint is among the test fixtures. The real files come from python-pptx and LibreOffice.
  • Reading order inside a slide is the order of shapes in the file, not their position on the slide, so a deck whose shape order differs from its visual order reads out of visual order.
  • Footers, dates and slide numbers are dropped on purpose; a deck that keeps real content there loses it.
  • A slide with nothing to write is left out of the output but still counts in the numbering, so the numbers can skip.
  • Legacy .ppt is not supported.

Compared

How does it compare with other tools?

Compared withWhat the evidence saysSource
doclingDocling's supported-formats page lists PPTX as an input that needs no LibreOffice, and PPT as needing LibreOffice.Docling supported formats
markerMarker's README lists PPTX among its inputs and writes Markdown and JSON, but it needs PyTorch and its model weights carry OpenRAIL-M terms with commercial limits.marker README; marker LICENSE

Where should I read next?

FAQ

Frequently asked questions

Are speaker notes included?

Yes. The slide's notes part is read and its body text is written as 'Notes:' after the slide.

Why do my slides come out in the wrong order?

They should not any more: the order comes from the presentation part's slide list, and the numbers are positions. The older reader used file numbers and got a moved slide wrong.

Are tables kept as tables?

Yes. A table becomes a Markdown pipe table; merged cells are empty at the covered positions.

Does it read the old .ppt format?

No. Only the pptx extension is routed.

Does the Webfold command line do this today?

Yes, in Webfold's main branch: it pins a superdocuments revision (68c216f) that has the new readers. The 0.1.0 release predates it.

Read PPTX with Webfold.

Run the command-line tool today, and join the waitlist for the hosted API.