| name | structured-html-to-pptx |
| description | Convert structured HTML presentation documents (HTML decks where each slide is a div with class "slide" containing semantic CSS classes like .page-header, .action-title, .lead-in, .two-col, .info-table, .takeaway, .diagram) into fully editable PowerPoint .pptx files. Use this skill whenever a user wants to turn an HTML slide deck into PPTX — especially when they mention "HTML to PPTX", "資料をパワポにして", "ホームページの形式の資料をPowerPointに", "slide HTML to PPTX", "convert HTML deck", "編集可能なPPTX", or reference an HTML file that contains slide-shaped DOM with these class names. The output PPTX has native editable textboxes, native tables with proper borders, embedded SVG diagrams as PNG, and inline date-tag pills with red highlight backgrounds. |
Structured HTML → PPTX Converter
Convert HTML presentation documents into fully editable PowerPoint files. The
output preserves text editability (every text element is a real PPT textbox),
uses native PPT tables with proper borders, embeds SVG diagrams as PNG, and
renders inline date-tag pills with red highlighted background.
When to use this skill
Use this skill the moment a user asks to convert a structured HTML slide deck
to PPTX. Signal phrases:
- "HTMLをPPTXに変換", "this HTML to a PowerPoint", "convert HTML deck"
- "資料をパワポにして", "ホームページの形式の資料をPowerPointに"
- "編集可能なPPTXにしたい" with an HTML attachment
- The user attaches an HTML file with
<div class="slide"> elements and asks
for a presentation file
If the user's HTML lacks the expected class structure (.slide, .page-header,
.info-table, etc.), this skill is the wrong tool — fall back to general
HTML-to-PPTX conversion or generate slides from scratch. See
references/html_format.md for the exact format expected.
How it works (3-phase pipeline)
HTML (input)
│
├─ Phase 1: measure.py
│ └─ Playwright launches Chromium, opens HTML at 1320×800,
│ walks every slide's DOM, captures bbox + computed style
│ + structured runs (incl. inline pills) → measurements.json
│
├─ Phase 2: convert_svgs.py
│ └─ Extracts every <svg> and converts to PNG via cairosvg.
│ Replaces glyphs missing from Noto Sans CJK JP (⋮→︙, ✕→×).
│
└─ Phase 3: build.py
└─ Reads measurements.json, generates .pptx via python-pptx.
Renders shapes + lines + native tables (with cell borders
in OOXML schema-correct order!) + textboxes + SVG images.
The fastest path is to run scripts/pipeline.py which orchestrates all three.
Entry point — one shot conversion
For most cases just run:
cd /path/to/working-dir
python <skill_path>/scripts/pipeline.py path/to/input.html -o output.pptx
The script handles dependencies (will pip-install cairosvg if missing) and
runs all three phases. Intermediate artifacts (measurements.json, SVG PNGs)
land in a _pptx_build/ subdir for debugging — pass --keep-intermediate
to retain them, otherwise they're cleaned up.
Required environment
- Python ≥3.10 with:
playwright, python-pptx, cairosvg, beautifulsoup4, pillow, lxml, fonttools
playwright install chromium (only first time)
- Fonts on the conversion box: Noto Sans CJK JP, DejaVu Sans (used for the
cmap detection / fallback character substitution)
- LibreOffice (
soffice) only if you want to QA-render to PDF/JPG; the .pptx
itself doesn't need it
The pipeline.py script self-checks these and tells the user what to install
if anything is missing.
What the skill does NOT handle
- Generic HTML → PPTX (the builder relies on the structured-deck class names;
unrecognized blocks render as plain text without intended styling)
- HTML files where slides are not enclosed in
<div class="slide"> elements
- SVG features beyond basic shapes/text/lines/paths (no filters, no clipPath
chains)
- Animations / transitions
- 50+ MB images embedded as data-URIs (will work but slow)
Critical implementation details (do NOT skip)
These were hard-won fixes during development. If you're modifying the build
code, read references/troubleshooting.md first — almost every change risks
regressing one of these.
1. Position by measurement, never by calculation.
Use the Playwright-measured bbox of every element. Do NOT compute
"action-title is at y=44, lead-in at y=44+30+12" — fonts render slightly
differently in PPT than browsers and any computed layout drifts.
2. OOXML schema order in <a:tcPr>.
Cell borders (lnL, lnR, lnT, lnB) MUST come before solidFill in the
XML. PowerPoint silently ignores borders if solidFill precedes them.
LibreOffice tolerates either order, so LO QA passes while PPT shows no
borders. Always run reorder_tcPr(cell) after every cell modification.
3. CJK font missing-glyph replacement.
Noto Sans CJK JP doesn't contain ⋮ (U+22EE) or ✕ (U+2715). cairosvg
renders these as a missing-glyph icon. Replace pre-conversion: ⋮ → ︙
(U+FE19), ✕ → × (U+00D7). The convert_svgs.py script uses fontTools to
read the CJK cmap and identifies any other missing glyphs automatically.
4. Inline pills (date-tag-512), not floating shapes.
The HTML's <span class="date-tag-512">...</span> looks tempting to render
as a separate rounded rectangle at its measured bbox. Don't — it floats and
disconnects from surrounding text when the cell wraps differently. Instead,
capture pills as inline runs with isPill=true flag, then in the builder
render them with <a:highlight val="FEE2E2"/> + red bold + Inter font. This
keeps them in the text flow.
5. compact-light/medium/heavy/extreme classes shift the page-header.
Slides with these classes have reduced page-header padding so the priority
box ends up at y=15-17 instead of y=22. Normalize the page-header layout in
the builder (priority y=22 h=25, action-title y=56) regardless of measured
values, otherwise label heights vary across slides.
6. Force role-based fontSize.
Use a fixed ROLE_FONT_SIZE table (action-title=22, lead-in=12, etc.). HTML's
CSS may apply per-slide overrides via compact-* or per-element styles that
produce inconsistent visual sizes; the role table enforces uniformity.
7. Native PPT tables can't contain other tables.
For info-table cells with nested content (banner divs, nested tables, lists),
capture the cell's block children as overlay elements positioned by measured
bbox. The outer cell renders empty (no text), and overlays sit on top in
z-order. The nested table is its own native table at its measured position.
8. Whitespace normalization in extractRuns.
HTML inline whitespace (newlines + indentation) collapses to a single space
per CSS. If you don't text.replace(/\s+/g, ' ') in the JS extraction,
you'll get spurious empty paragraphs at the top of textboxes that push text
down ~30px.
Reference files — read when relevant
references/design_spec.md — full design spec; read when modifying the
layout strategy or color palette
references/troubleshooting.md — diagnostic table for common visual bugs
(text shifted down, missing borders, broken SVGs, pill misalignment, etc.)
references/html_format.md — the expected HTML structure: required CSS
classes, role of each element, inline styling classes (accent-em, new-512,
date-tag-512). Read this if the input HTML is from a new variant of the
template.
Sample data
samples/sample_minimal.html — a 3-slide HTML with cover + two-col + table,
exercising all key features (action-title, lead-in, content-block, takeaway,
diagram, info-table, inline pill). Use this for quick sanity testing.
Output expectations
The generated .pptx, opened in PowerPoint:
- All text is editable (click to edit)
- Tables have visible borders: thick blue (1.5pt #2563EB) under header row,
thin gray (0.5pt #E5E5E5) between data rows; nested tables have all-side
borders (0.5pt #D0D0D0)
- date-tag-style inline pills appear as inline red bold text with light pink
highlight, in the natural text flow
- SVG diagrams are embedded as 1600px-wide PNGs, scaled to fit their containers
- Slide size: 13.333" × 7.5" (16:9 widescreen)
- Page header layout (item-id, priority box, action-title, lead-in, bottom
border) is uniform across all slides regardless of content variations or
compact-* classes