Skip to main content

html-to-pptx

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.

Install

pip install html-to-pptx
python -m playwright install chromium

Quick start

CLI

html-to-pptx deck.html deck.pptx

Python

import asyncio
from html_to_pptx import convert

asyncio.run(convert("deck.html", "deck.pptx"))

Two-stage API

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())

HTML format

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.

What gets converted

HTML feature PPTX output
Text with fonts, colors, weight, style Editable text boxes with matching font properties
Font substitution (web fonts like Inter, Playfair, Roboto) Mapped to a metric-compatible, install-safe font in the same family so headings don't reflow
Single-line fit One-line headings/pills/buttons never wrap — the point size auto-shrinks to fit the box
CSS line-height Exact paragraph line spacing (multi-line headings keep their height)
CSS padding Reproduced as text-frame insets (bulleted/labelled text aligns correctly)
Text/box/gradient alpha Flattened over the correct backdrop (nearest ancestor bg), so translucency renders in every viewer
Translucent image overlays / scrims Baked into the underlying image so the photo stays visible
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
CSS conic-gradient (pies/donuts) Rasterized to a picture with true segments
Gradient text (-webkit-background-clip: text) Gradient text fill with a solid first-stop fallback
Inline <svg> elements Rasterized to PNG via Playwright screenshot
HTML tables (<table>) Cell-accurate text with per-column alignment preserved
Background colors Solid fills (with alpha flattening)
Rounded corners (border-radius, including %) OOXML adjustment guides on rounded rectangles
Rounded images (border-radius on <img> or wrapper) Rounded-rect geometry swap on picture shapes
object-fit: cover Centered crop (no distortion) instead of stretching
Base64-embedded images (data:image/...) Native picture shapes
Borders (with alpha) Shape outlines with color, width, and opacity
Left/top accent bars (border-left, ::before strips) Rounded to match the card's corners (offset under-component)
transform: rotate() Shape rotation about the element center
writing-mode: vertical-* Vertical PPTX text body
Flex/grid alignment (justify-content, align-items) Horizontal + vertical text anchoring
text-transform (uppercase, etc.) Transformed text content
Hyperlinks Preserved on text runs
Ordered/unordered lists (incl. custom bullet dots) Bullet/number prefixes and decorative markers
CSS ::before/::after pseudo-elements Synthetic shapes for decorative accents
white-space: pre (terminals/code) Preserved line breaks and indentation
RTL text direction Correct paragraph alignment
<br> tags in inline runs Multi-paragraph text frames
Element opacity (incl. inherited) OOXML alpha / picture transparency

How it works

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

Visual comparison tool

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

Limitations

  • 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 base64 src and inline <svg> elements become picture shapes
  • Radial / multi-layered gradients are not drawn — they fall back to the solid background color (single linear-gradient and conic-gradient are supported)
  • Animations and transitions are not represented in PPTX
  • Font availability — web fonts are mapped to a metric-compatible font in the same family (serif→Georgia, humanist sans→Segoe UI, geometric sans→Century Gothic, mono→Consolas, …). For pixel-exact display type, embed the font in PowerPoint yourself
  • Complex CSS layouts (grid, advanced flexbox) are measured as-rendered, but deeply nested layouts may lose some positioning precision

Development

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 -v

License

MIT

Metadata

Release files for html-to-pptx 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for html-to-pptx 0.3.0
File Size Uploaded
html_to_pptx-0.3.0.tar.gz 83.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for html-to-pptx 0.3.0
File Interpreter ABI Platform
html_to_pptx-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 119.6 kB

Release files / html_to_pptx-0.3.0.tar.gz

Download URL html_to_pptx-0.3.0.tar.gz
Size 83.2 kB
Tags Source
SHA-256 checksum
How to use checksums
45e7bf73d7886bcc5b58eef0d6f68bab393eeb7af9822f8814b8535c3d425293
BLAKE2b-256 checksum
How to use checksums
02a6238dd10287b4a58f7ab290e6ce01024da4498da8c1ec5b4c3bf36f419c99
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.17

Release files / html_to_pptx-0.3.0-py3-none-any.whl

Download URL html_to_pptx-0.3.0-py3-none-any.whl
Size 36.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2b8a5d5c1501e48f7894f929203d60af5a9261f240c49d0566067c6e76be37d7
BLAKE2b-256 checksum
How to use checksums
8dbdcb507698e1c779b6cafa948b216f3a4a225208fd13b3d352e11d2cac902c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.17

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page