Slide Surgery: What python-pptx Can't Do, Over One API Call
Duplicate, delete and reorder slides in an existing .pptx — the three oldest open python-pptx feature requests — now one HTTP call. Formatting untouched.
Posted by
Related reading
Your AI Agent Can Now Edit, Merge, Translate and PDF Decks — 8 MCP Tools
The PowerPoint Engine MCP server grew from 3 tools to 8: replace text in an existing deck, do slide surgery, merge presentations, convert to PDF and translate — all chainable in one agent conversation.
Convert PPTX to PDF Over One API Call
python-pptx can't export PDF — it's a file-format library, not a renderer. The usual workarounds are PowerPoint COM automation or babysitting LibreOffice yourself. Now it's one HTTP call.
Markdown Tables and Combo Charts, Natively in PowerPoint
A markdown table becomes a styled, editable PowerPoint table. A combo chart with a secondary axis takes five lines. No python-pptx XML archaeology required.
If you automate PowerPoint in Python, you know the ritual: you reach for python-pptx, it does 90% of the job beautifully, and then you hit the same wall everyone hits. You can't duplicate a slide. You can't delete one. You can't reorder them. Those aren't obscure gaps — they are the oldest open feature requests in the entire library:
- #132 Slide.duplicate() — open since 2014, the most-commented issue in the repo.
- #67 delete a slide — open since 2013.
- #68 reorder slides — also 2013. Still open.
We just shipped all three as one endpoint: POST /api/powerpoint/edit.
One call, a chain of operations
Upload your .pptx and a list of operations. They run in order, each on the result of the previous one. Slide numbers are 1-based, exactly as you see them in PowerPoint:
curl -s https://powerpointengine.io/api/powerpoint/edit \
-F 'file=@deck.pptx' \
-F 'operations=[
{"op": "duplicate", "slide": 2},
{"op": "move", "slide": 5, "to": 1},
{"op": "delete", "slide": 3}
]' | jq -r '.result.downloadUrl'duplicate inserts the copy right after the source (or anywhere, with "insertAfter": 0 for the start of the deck). Every shape, chart, image, layout and master of your file stays byte-for-byte untouched — we operate on the OOXML package directly instead of parsing your deck into an object model and re-serializing it.
Why this is hard in an object model
Cloning one slide means updating four parts of the package in lockstep: the slide list in presentation.xml, the relationship table, [Content_Types].xml, and the slide part itself with its own relationships (minus the speaker notes, which legally belong to exactly one slide). An object-model library has to keep all of that consistent through its public API — which is why the issue has 89 comments and no merge. A server that owns the whole file for the duration of one request can just do it correctly.
And yes, we eat our own dog food with a sense of humor: every file our test suite edits is verified by opening it with python-pptx itself.
The template loop, finally
The #1 reason people wanted Slide.duplicate() was the classic reporting pattern: one designed template slide, N records of data. Combine /edit with our structure-preserving text replacement and that loop is two calls:
- /edit — duplicate the template slide once per record.
- /replace — fill {{placeholders}} in each copy. They're found even when PowerPoint has split them across text runs — the other famous python-pptx pain.
Bonus round: two more "impossible" ones
While we were in the neighborhood, we shipped the next two most-requested gaps from the same issue tracker.
Merge presentations (#403, #1036, #175)
POST /api/powerpoint/merge appends the slides of 2–5 decks in upload order — and every deck keeps its own design. Each copied slide brings its layout; the layout brings its master; the master brings its theme; charts and images travel along. Merge a corporate deck with a modern one and you get both masters, intact:
curl -s https://powerpointengine.io/api/powerpoint/merge \
-F 'files=@intro.pptx' \
-F 'files=@results.pptx' \
-F 'files=@outro.pptx' | jq -r '.result.downloadUrl'Replace an image in place (#116)
Picture.replace_image() has been requested since 2014. Add an image:<shape name> field to a /replace call and the picture swaps its pixels while keeping position, size, cropping and effects — matched by the shape's name or alt text:
curl -s https://powerpointengine.io/api/powerpoint/replace \
-F 'file=@deck.pptx' \
-F 'replacements={"client":"ACME"}' \
-F 'image:Logo=@acme-logo.png'Works for AI agents too
Everything above is also available to AI agents: the agent skill documents the endpoint, and our MCP server handles generation. An agent can generate a deck, then rearrange it, then fill in data — without a single line of COM automation or a Windows box.
Try it on the live tool or read the API docs. Free tier included — bring a deck you already have.