Convert HTML slide decks into editable PowerPoint files.
Unlike screenshot-based approaches that produce flat images, html-to-pptx measures every DOM element in a headless browser and maps it to a native PPTX shape — text boxes you can edit, images you can resize, backgrounds you can restyle. The output is a real presentation, not a picture of one.
pip install html-to-pptx
python -m playwright install chromiumhtml-to-pptx deck.html deck.pptximport asyncio
from html_to_pptx import convert
asyncio.run(convert("deck.html", "deck.pptx"))For more control, separate the measurement and rendering steps:
import asyncio
from html_to_pptx import extract_measurements, render_pptx
async def main():
# Stage 1: measure DOM elements in a headless browser
measurements = await extract_measurements("deck.html")
# Stage 2: render measurements to python-pptx shapes
prs = render_pptx(measurements)
# Modify the presentation before saving
prs.slides[0].shapes[0].text = "Custom title"
prs.save("deck.pptx")
asyncio.run(main())Your HTML file should contain <section class="slide"> elements at a 1920×1080 canvas:
<!DOCTYPE html>
<html>
<head>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #000; overflow: hidden; }
.slide {
width: 1920px;
height: 1080px;
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
font-family: sans-serif;
}
.slide.active { display: flex; }
</style>
</head>
<body>
<section class="slide active" style="background-color: #1e293b;">
<h1 style="font-size: 64px; color: #f8fafc;">Hello World</h1>
</section>
<section class="slide" style="background-color: #ffffff;">
<p style="font-size: 24px; color: #334155;">Second slide</p>
</section>
</body>
</html>See examples/demo.html for a full five-slide deck.
| HTML feature | PPTX output |
|---|---|
| Text with fonts, colors, weight, style | Editable text boxes with matching font properties |
Mixed inline content (<p>text <strong>bold</strong> more</p>) |
Multi-run paragraphs with per-run styling |
| CSS linear-gradient backgrounds | OOXML gradient fills with angle and stop positions |
Gradient text (-webkit-background-clip: text) |
Gradient text fill (or solid fallback) |
Inline <svg> elements |
Rasterized to PNG via Playwright screenshot |
| Background colors | Solid fills (with alpha transparency) |
Rounded corners (border-radius, including %) |
OOXML adjustment guides on rounded rectangles |
Rounded images (border-radius on <img>) |
Rounded-rect geometry swap on picture shapes |
Base64-embedded images (data:image/...) |
Native picture shapes |
| Borders (with alpha) | Shape outlines with color, width, and opacity |
| Left-border accent bars | Separate narrow rectangle shapes |
text-transform (uppercase, etc.) |
Transformed text content |
| Hyperlinks | Preserved on text runs |
| Ordered/unordered lists | Bullet/number prefixes with marker colors |
CSS ::before/::after pseudo-elements |
Synthetic shapes for decorative accents |
| RTL text direction | Correct paragraph alignment |
<br> tags in inline runs |
Multi-paragraph text frames |
| Element opacity | OOXML alpha on fill colors |
HTML file
│
▼
┌─────────────────────────┐
│ Headless Chromium │ ← Playwright loads the HTML at 1920×1080
│ (Playwright) │
│ │
│ For each .slide: │
│ • Show slide in isolation│
│ • Clear CSS transforms │
│ • Measure every visible │
│ element recursively: │
│ position, size, color,│
│ font, text, images │
└───────────┬─────────────┘
│
▼
Measurement tree (JSON)
│
▼
┌─────────────────────────┐
│ python-pptx renderer │
│ │
│ For each element: │
│ • Text leaf → text box │
│ • Image → picture shape │
│ • Background → rectangle│
│ • Border → outline/shape│
│ │
│ Coordinates: │
│ CSS px → inches using │
│ 13.333/1920 ratio │
└───────────┬─────────────┘
│
▼
Editable .pptx
Verify conversion quality with side-by-side comparisons:
pip install html-to-pptx[compare]
html-to-pptx-compare deck.html -o results/This screenshots the original HTML, converts to PPTX, renders the PPTX back to images via LibreOffice, and creates side-by-side comparison PNGs. Requires LibreOffice on PATH.
Output structure:
results/deck/
├── html_slide_0.png # ground truth (HTML screenshot)
├── html_slide_1.png
├── pptx_slide_0.png # converter output (PPTX rendered via LibreOffice)
├── pptx_slide_1.png
├── compare_slide_0.png # side-by-side comparison
├── compare_slide_1.png
└── output.pptx # the generated file
- External images are not fetched — use base64
data:URIs for embedded images - CSS
background-image: url(...)on containers is not converted — only<img>tags with base64srcand inline<svg>elements become picture shapes - Radial gradients are not supported — only
linear-gradient - Animations and transitions are not represented in PPTX
- Font availability — the PPTX references font names from the HTML; if those fonts aren't installed on the machine opening the file, PowerPoint will substitute
- Complex CSS layouts (grid, advanced flexbox) are measured as-rendered, but deeply nested layouts may lose some positioning precision
git clone https://github.com/Design-Arena/html-to-pptx.git
cd html-to-pptx
pip install -e ".[dev]"
python -m playwright install chromium
pytest -vMIT