PPTX (Anthropic, official)
## what it does
tl;dr: one of the four document skills Anthropic ships under anthropics/skills (with docx, pdf, xlsx) — the SKILL.md is the router, the heavy lifting lives in scripts/ and in a tight dependency set. Create = pptxgenjs; edit = unzip → edit ppt/slides/slideN.xml → rezip; read = markitdown plus scripts/thumbnail.py for a visual grid.
What it actually does
The frontmatter triggers for any .pptx or .potx mention as input, output, or both: create decks, read/parse/extract text, edit existing decks, split/combine, work with templates/layouts/speaker notes/comments. The skill tells the agent which tool to reach for; it does not run on its own.
| Task | Tool |
|---|---|
| Create a new deck | pptxgenjs script |
| Edit an existing deck or build from template | unzip → edit ppt/slides/slideN.xml → zip |
| Read content | markitdown deck.pptx (one block per slide under <!-- Slide number: N --> markers) |
| Visual grid of slides | python scripts/thumbnail.py deck.pptx [prefix] |
Bundled scripts
Paths are relative to the skill directory. Everything else is plain Python / node / shell.
| Script | What it does |
|---|---|
scripts/thumbnail.py deck.pptx [prefix] | Labeled grid of every slide, for picking template layouts. .pptx only — copy .potx to .pptx first. Pass prefix — it defaults to thumbnails and silently overwrites grids from prior decks in the same directory. |
scripts/add_slide.py unpacked/ slide2.xml [--after slideN.xml] | Duplicate a slide (or a slideLayoutN.xml) with all the package bookkeeping. Also takes a .pptx directly with -o out.pptx (without -o it rewrites the input deck in place). |
scripts/clean.py unpacked/ | Delete slides, media, and rels no longer referenced. Run after <p:sldIdLst> is final — anything missing from the list, including a slide you just wrote, gets removed. |
scripts/office/validate.py deck.pptx [--original src.pptx] | Schema, relationship, content-type, chart, and slide checks; each failure names its fix. Pass --original for any template-derived deck so the template’s own XSD errors don’t read as yours. |
scripts/office/soffice.py --headless --convert-to pdf deck.pptx | LibreOffice wrapper — bare soffice hangs in this sandbox. |
Creating with pptxgenjs — gotchas (from SKILL.md)
pptxgenjs is preinstalled — do not run npm install first; write the script and require('pptxgenjs') directly. Only fall back to npm install pptxgenjs if that require fails.
- Set
pres.layoutbefore adding slides. The default canvas isLAYOUT_16x9= 10” × 5.625”, not 13.3” wide. Coordinates past the edge are written, not clamped — the shape just isn’t on the slide.LAYOUT_WIDEis 13.3” × 7.5”. - Hex colors: never
#, never 8 digits.color: "FF0000". Both"#FF0000"and alpha baked into the hex ("00000020") corrupt the file. Translucency istransparency: 0-100on fills/images andopacity: 0.0-1.0on shadows — each is silently ignored on the other. pptxgenjsmutates option objects in place (converts values to EMU on first use). Never share oneshadow/options object across twoadd*calls — build a fresh object each time.- Shadow
offsetmust be ≥ 0. Negative offsets corrupt the file. To cast upward, useangle: 270with a positive offset. letterSpacingis silently ignored — the real option ischarSpacing.- Lists:
bullet: trueon each item, never a literal•(renders double bullets). SetbreakLine: trueon every array item except the last. Space bulleted paragraphs withparaSpaceAfter, notlineSpacing(huge gaps). - One
new pptxgen()per output file — never reuse an instance. rectRadiusonly works onROUNDED_RECTANGLE, notRECTANGLE.- Gradient fills aren’t supported — use a gradient image as the background.
- Text boxes have built-in internal padding — set
margin: 0whenever text must align with a shape/line/icon at the same x. - Speaker notes go in
slide.addNotes("...")(plain text, once per slide), never in a text box on the slide. - Keep charts native via
addChart()— only chart types PowerPoint has no native form for (Sankey, network, chord) go in as images. - Stacked bar/column
dataLabelPositionmust bectr,inEnd, orinBase.outEndcorrupts the file. - Combo series using
secondaryValAxis/secondaryCatAxisneeds bothvalAxesandcatAxesdeclared on chart options — pptxgenjs otherwise writes axis ids it never declares, and PowerPoint discards the chart as corrupt. - After
writeFile(), runpython scripts/office/validate.py deck.pptx. It reports the chart faults above and the slide-XML defects PowerPoint refuses, and names the fix for each. Fix in the generator, not by hand-editing packed XML. - Never reorder children of
<p:presentation>—<p:notesMasterIdLst>lives right after<p:sldIdLst>; move the element and the same deck becomes unopenable. - Icons: render
react-iconsto SVG viaReactDOMServer.renderToStaticMarkup, rasterize withsharpat ≥256px, insert viaaddImage({ data: "image/png;base64," + buf.toString("base64") })(theimage/png;base64,prefix is required).
Editing existing decks
python3 -c "import sys,zipfile; zipfile.ZipFile(sys.argv[1]).extractall('unpacked')" deck.pptx
python scripts/add_slide.py unpacked/ slide2.xml --after slide2.xml # duplicate a slide; prints the new slide's path
# reorder / delete slides = edit <p:sldIdLst> in ppt/presentation.xml
python scripts/clean.py unpacked/ # after deletions: removes orphaned slides/media/rels
# edit slide content in ppt/slides/slideN.xml
(cd unpacked && rm -f ../out.pptx && zip -Xr ../out.pptx .) # zip from INSIDE the dir; rm first or deleted parts survive
python scripts/office/validate.py out.pptx --original deck.pptx
- Do all structural work (add/delete/reorder) before editing any slide’s content.
add_slide.pycopies a slide file verbatim, so duplicating after you edit clones the edited content;clean.pydeletes any slide missing from<p:sldIdLst>, including one you just wrote. - Never copy a slide file by hand.
add_slide.pydoes every registration a new slide needs and reports what it made. A duplicated slide still references its source’s chart/SmartArt/embedded-object parts — editing one slide’s chart changes the other’s. python-pptxwon’t: duplicate a slide (its only entry point isadd_slide(layout)), preserve formatting throughtext_frame.text = "..."(collapses the paragraph to one unstyled run — assignrun.textinstead), or read SVG/EMF most template art uses (add_pictureraisesUnidentifiedImageError).- Legacy
.pptmust be converted first:python scripts/office/soffice.py --headless --convert-to pptx file.ppt..potxtemplates unpack and pack identically — keep the.potxextension on the output.
QA loop (required)
The first render usually has real defects — overlaps, overflow, misalignment. Find and fix those, re-render only the slides you changed, and stop.
markitdown output.pptx # content QA
python scripts/office/validate.py output.pptx # schema QA
python scripts/office/validate.py output.pptx --original src.pptx # template-derived: baselined against template
python scripts/office/soffice.py --headless --convert-to pdf output.pptx # visual QA: PDF first
rm -f slide-*.jpg
pdftoppm -jpeg -r 150 output.pdf slide # then rasterize
ls -1 "$PWD"/slide-*.jpg
Look at the rendered images, not the generator. Common user-visible defects to scan for: text overflow/cut-off, overlapping elements, source citations or footers colliding with content above, gaps < 0.3”, uneven gaps, slide-edge margins < 0.5”, misaligned columns, low-contrast text or icons, template decoration mispositioned after a title wrap, narrow text boxes that wrap excessively, leftover placeholders. For template-based decks, also markitdown output.pptx | grep -iE "\bx{3,}\b|lorem|ipsum|\bTODO|\[insert|this.*(page|slide).*layout" — any hits are leftover placeholder text.
Gotchas
pptxgenjscharts break PowerPoint in ways Python and LibreOffice happily accept —validate.pyis the only thing that surfaces those. Don’t trust a deck that “opened fine in LibreOffice.”scripts/thumbnail.pydefaults tothumbnailsfor the prefix and silently overwrites grids from any other deck in the same directory. Pass a deck-named prefix every time, or the first deck’s grid is gone.add_slide.pyrewrites the input deck in place unless you pass-o. No backup, no confirmation.- LibreOffice (
soffice) hangs in this sandbox without thescripts/office/soffice.pywrapper. Use the wrapper. markitdownplaceholders leak through template fills — grep forxxx|lorem|ipsum|TODO|[insert|this.*(page|slide).*layoutafter every fill.- License is Proprietary.
LICENSE.txtrestricts redistribution and derivative works. You use the skill; you don’t fork it. .pptxis a ZIP of XML. Azip -Xrfrom outside the unpacked dir leaves deleted parts in the archive and PowerPoint reads them as conflicting. Always zip from inside.pdftoppmzero-padding follows page count —slide-1.jpgfor decks under 10,slide-01.jpgfor 10–99,slide-001.jpgfor 100+.
Dependencies (per SKILL.md)
pptxgenjs (npm, preinstalled — install only if require('pptxgenjs') fails) · markitdown[pptx], Pillow, defusedxml, lxml (pip — text dump, thumbnail, clean, validate) · LibreOffice (soffice, auto-configured via scripts/office/soffice.py) · pdftoppm (Poppler)
Install
# Claude Code (marketplace)
plugin marketplace add anthropics/skills # register the repo as a marketplace
/plugin install document-skills@anthropic-agent-skills # installs pdf + docx + pptx + xlsx
# Or, just the pptx skill, directly
/plugin install pptx@anthropic-agent-skills
// On the API — attach via container.skills; requires code_execution_20250825
{
"model": "claude-opus-5",
"max_tokens": 4096,
"container": {
"skills": [
{ "type": "anthropic", "skill_id": "pptx", "version": "latest" }
]
},
"messages": [
{ "role": "user", "content": "Build a 6-slide pitch deck for /mnt/seed-round.md" }
],
"tools": [
{ "type": "code_execution_20250825", "name": "code_execution" }
]
}
Related
- Anthropic PDF (official skill) — sibling document skill, same Anthropic
document-skillsplugin, samecode_execution_20250825requirement - Anthropic DOCX (official skill) — sibling document skill, same Anthropic shelf
- Anthropic Algorithmic Art — same
anthropics/skillsrepo, creative shelf - UI Skills catalog (Anthropic) — broader Anthropic skills surface
## compatibility
## embed this badge
