PPTX Content Extraction#
The MsPowerpointDocumentBackend (docling/backend/mspowerpoint_backend.py) converts PPTX files to DoclingDocument using the python-pptx library. Each slide maps to a page, and the backend extracts text, lists, tables, images, slide notes (ContentLayer.NOTES), and comments . It runs inside a SimplePipeline — no layout model, no OCR — so every element is extracted directly from the PPTX XML.
What Gets Extracted#
| Element | Handler | Notes |
|---|---|---|
| Titles / section headers | _handle_title | PP_PLACEHOLDER.TITLE / CENTER_TITLE → DocItemLabel.TITLE; SUBTITLE → SECTION_HEADER |
| Paragraphs & bullet/numbered lists | _handle_text_elements | Level and bullet style resolved via multi-layer cascade (paragraph → txBody lstStyle → layout → slide master) |
| Tables | _handle_tables | Row/col spans via raw XML; empty cells skipped |
| Pictures | _handle_pictures | Opens blobs with PIL; UnidentifiedImageError / OSError warn-and-skip |
| Notes | _walk_linear | Stored in ContentLayer.NOTES |
| Comments | _extract_slide_comments | Stored in ContentLayer.NOTES; position-only (x,y), not linked to specific shapes |
Known Limitations#
1. DrawingML / SmartArt shapes are silently skipped#
python-pptx raises NotImplementedError for <p:sp> elements it cannot classify (freeform, autoshape, SmartArt, and other DrawingML constructs that fall outside its known shape categories). The backend catches this in _safe_shape_type and returns None, logging only at DEBUG level:
_log.debug("Skipping shape with unrecognized type: %s", shape.name)
Any text, data, or diagram content inside the unrecognized shape is dropped with no WARNING-level signal. This is a library constraint — python-pptx does not expose a SmartArt text API — not a Docling design choice.
Affected shape types include:
- SmartArt (DrawingML
<p:graphicFrame>with SmartArt namespace) - Freeform autoshapes that python-pptx cannot classify
- Connector shapes with embedded text
GROUP shapes are handled recursively : the backend iterates shape.shapes to reach children. But if an individual child shape triggers NotImplementedError, that child is still dropped silently.
2. Subtitle label bug#
In _handle_text_elements, the SUBTITLE branch assigns DocItemLabel.SECTION_HEADER but the result is never stored — the assignment is a bare expression with no effect . The paragraph then falls through and receives DocItemLabel.PARAGRAPH instead. The dedicated _handle_title path correctly assigns SECTION_HEADER for subtitles, but _handle_text_elements is called for the paragraph iteration path.
3. Image resolution not adjustable#
DPI comes from whatever is embedded in the image blob; PaginatedPipelineOptions image-scaling does not apply to PPTX extraction.
Furniture Filtering and Silent Heading Loss#
Note: The furniture filtering described below is part of the PDF pipeline (
StandardPdfPipeline+ReadingOrderModel), not the PPTX backend itself. It is documented here because users frequently encounter the same silent content loss when working with slides exported to PDF, and because the root cause is in shared pipeline infrastructure.
The ReadingOrderModel unconditionally maps any element the layout model labels PAGE_HEADER or PAGE_FOOTER to ContentLayer.FURNITURE . The layout model assigns these labels from positional cues on a single page — an element at the very top of a page is classified as PAGE_HEADER regardless of whether it repeats on other pages.
The downstream effect:
DEFAULT_CONTENT_LAYERS = {ContentLayer.BODY}means default exports (markdown, JSON) silently drop all furniture.- A non-repeating section heading that sits at the top of a page gets classified as
PAGE_HEADER, demoted toFURNITURE, and dropped from the default output with no warning. - Even including
ContentLayer.FURNITUREin the export is a blunt workaround — it re-surfaces all genuine running headers/footers too.
A second, distinct failure mode: a low-confidence PICTURE cluster can absorb nearby text cells as children in LayoutPostprocessor._process_special_clusters . Text adopted into a picture cluster is never emitted as markdown text, disappearing from all content layers, not just BODY. Enabling ContentLayer.FURNITURE does not recover it.
Proposed fix (open issue): A document-level reclassification pass in ReadingOrderModel that builds normalized per-element signatures across pages and demotes the label to FURNITURE only when the element appears on ≥2 pages . This is not yet merged.
Workarounds#
| Problem | Workaround |
|---|---|
| Non-repeating heading missing from markdown | Pass included_content_layers={ContentLayer.BODY, ContentLayer.FURNITURE} to the exporter — accepts genuine headers/footers back too |
| SmartArt / complex shape text missing | No direct workaround in Docling; pre-process with LibreOffice to render slides to images, or export PPTX to PDF and use the PDF pipeline |
Heading missing even with FURNITURE enabled | Likely absorbed by a PICTURE cluster; inspect raw DoclingDocument.pictures to confirm |
Key Source Files#
| File | Purpose |
|---|---|
docling/backend/mspowerpoint_backend.py | Full PPTX backend — shape dispatch, text/table/image/notes extraction |
docling/models/stages/reading_order/readingorder_model.py | PAGE_HEADER/PAGE_FOOTER → FURNITURE mapping |
docling/utils/layout_postprocessor.py | Cluster overlap resolution including PICTURE child adoption |