What happens
Converting an old (2013-era) .pptx deck raises AttributeError and the whole file is dropped:
ERROR mdd.converters.pptx: error converting <REDACTED>/<REDACTED>.pptx
Traceback (most recent call last):
File "src/mdd/converters/pptx.py", line 32, in convert
convert_pptx(src, dest)
File "src/mdd/convert/pptx.py", line 421, in convert_pptx
_render_slide(
...
File "src/mdd/convert/pptx.py", line 304, in _render_slide
str(title_shape.text).strip()
^^^^^^^^^^^^^^^^
AttributeError: 'PlaceholderPicture' object has no attribute 'text'
Why
_render_slide assumes slide.shapes.title is a text placeholder:
https://github.com/schubergphilis/mdd/blob/main/src/mdd/convert/pptx.py#L302-L307
title_shape: Any = slide.shapes.title
slide_title: str = (
str(title_shape.text).strip()
if title_shape is not None
else ""
)
python-pptx's shapes.title returns whatever placeholder sits at idx == 0, not
necessarily a title text placeholder. In this deck that slot holds a picture
placeholder, so python-pptx hands back a PlaceholderPicture, which has no
.text — only text-frame shapes do. Older decks (and decks built from custom
layouts) hit this regularly; the layout puts a logo or screenshot in the idx-0
placeholder.
Note this is not only about the title: a PlaceholderPicture title also means
the picture is silently lost today even if the AttributeError were swallowed,
because _render_shape skips any shape matching title_shape before it gets a
chance to be extracted as an image.
Expected
A picture in the title placeholder should not abort conversion of the deck. The
slide should fall back to the Slide N heading (the existing slide_title or f"Slide {slide_num}" path), and the picture should still be extracted as an
attachment like any other picture.
Suggested fix
Guard the title read on the shape actually having a text frame, and only treat
the title shape as "already rendered" when it contributed the heading:
title_shape: Any = slide.shapes.title
if title_shape is not None and getattr(title_shape, "has_text_frame", False):
slide_title = str(title_shape.text).strip()
else:
title_shape = None # not a text title; let the shape loop render it normally
slide_title = ""
Setting title_shape = None is what keeps the picture: _render_shape /
_render_text_frame both skip via _same_shape(shape, title_shape), and
_same_shape already tolerates None.
Test coverage
Add a fixture deck whose slide layout places a picture in the idx == 0
placeholder and assert:
- conversion succeeds,
- the heading is
## Slide 1,
- the picture is written to the attachments dir and linked.
Environment
- mdd
main (at time of report: 3f7f017)
- python-pptx as pinned in
uv.lock
- source deck: a 2013
.pptx synced from SharePoint (path and deck title redacted)
What happens
Converting an old (2013-era)
.pptxdeck raisesAttributeErrorand the whole file is dropped:Why
_render_slideassumesslide.shapes.titleis a text placeholder:https://github.com/schubergphilis/mdd/blob/main/src/mdd/convert/pptx.py#L302-L307
python-pptx's
shapes.titlereturns whatever placeholder sits atidx == 0, notnecessarily a title text placeholder. In this deck that slot holds a picture
placeholder, so python-pptx hands back a
PlaceholderPicture, which has no.text— only text-frame shapes do. Older decks (and decks built from customlayouts) hit this regularly; the layout puts a logo or screenshot in the idx-0
placeholder.
Note this is not only about the title: a
PlaceholderPicturetitle also meansthe picture is silently lost today even if the
AttributeErrorwere swallowed,because
_render_shapeskips any shape matchingtitle_shapebefore it gets achance to be extracted as an image.
Expected
A picture in the title placeholder should not abort conversion of the deck. The
slide should fall back to the
Slide Nheading (the existingslide_title or f"Slide {slide_num}"path), and the picture should still be extracted as anattachment like any other picture.
Suggested fix
Guard the title read on the shape actually having a text frame, and only treat
the title shape as "already rendered" when it contributed the heading:
Setting
title_shape = Noneis what keeps the picture:_render_shape/_render_text_frameboth skip via_same_shape(shape, title_shape), and_same_shapealready toleratesNone.Test coverage
Add a fixture deck whose slide layout places a picture in the
idx == 0placeholder and assert:
## Slide 1,Environment
main(at time of report: 3f7f017)uv.lock.pptxsynced from SharePoint (path and deck title redacted)