pptx-html-fidelity-audit
nexu-io/open-design
Audit python-pptx exports against source HTML decks and fix layout drift with footer-rail discipline.
What is pptx-html-fidelity-audit?
Compares a PPTX file generated from an HTML slide deck against the source HTML to identify and fix visual regressions like footer overflow, cropped content, missing italic styling, and off-rhythm spacing. Use this when you have both a source HTML deck and its PPTX export and need to verify or repair fidelity between them.
- Extract and analyze PPTX shape positions, text, and typography via pptx_dump.json
- Map HTML slide structure to PPTX slides and identify layout drift (footer overlap, content cropping, italic loss, hero centering)
- Build a severity-ranked audit table (critical/high/medium/low) with root-cause analysis
- Re-export PPTX using cursor-flow layout discipline with enforced footer-rail boundaries
- Verify re-exported deck against layout constraints to prevent regressions
How to install pptx-html-fidelity-audit
npx skills add https://github.com/nexu-io/open-design --skill pptx-html-fidelity-audit- Source HTML slide deck (single file with <section class="slide"> blocks)
- PPTX file generated from that HTML deck via python-pptx or similar tool
- Python environment with pptx library installed
- scripts/extract_pptx.py available to dump PPTX structure
How to use pptx-html-fidelity-audit
- 1.Run scripts/extract_pptx.py <path-to.pptx> > pptx_dump.json to extract ground truth of the PPTX export
- 2.Read the source HTML and enumerate each <section class="slide"> block, noting theme, chrome, kicker, headline, body, and footer
- 3.Map each HTML slide to its PPTX slide index and build an audit table listing issues by slide with severity (critical/high/medium/low)
- 4.Identify 2โ3 systemic root causes (e.g., no footer rail enforced, hero stacks pinned to top, italic never propagated)
- 5.Re-export the PPTX using cursor-flow layout discipline: define CANVAS_H, CONTENT_MAX_Y, FOOTER_TOP constants once, use a Cursor class to advance blocks down the slide, and raise OverflowError if any block crosses the footer rail
- 6.Verify the re-exported deck against layout constraints to confirm all issues are resolved
Use cases
- Compare a deck.html and deck.pptx side-by-side to debug visual differences and identify which elements drifted
- Fix a PPTX where content bleeds into the footer or text is cropped at the bottom edge
- Restore italic/em styling that was lost in the python-pptx export
- Verify hero slides are properly centered vertically instead of pinned to the top
- Audit multi-slide decks for systemic layout issues before re-exporting with corrected discipline
- Presentation engineers using python-pptx to generate PPTX from HTML source
- Designers debugging visual fidelity between HTML and PowerPoint versions of a deck
- Teams maintaining slide decks with strict layout and typography requirements
pptx-html-fidelity-audit FAQ
This skill requires both artifacts. If you only have one, first generate the missing one (export HTML to PPTX, or reverse-engineer HTML from PPTX) or ask the user to provide it.
The python-pptx export script must explicitly set run.font.italic = True for each <em> or italic span in the HTML. If the export script doesn't propagate italic, it silently falls back to regular weight. The audit catches this and the re-export fixes it by checking for <em> tags and applying italic to the corresponding runs.
Instead of pinning each block at a hand-picked absolute y-coordinate (which works for one slide and breaks for others), the cursor advances down the slide block-by-block, enforcing that no block crosses a CONTENT_MAX_Y boundary. This makes overflows loud build errors instead of silent visual bugs, and prevents regressions on future exports.
Define CANVAS_W, CANVAS_H, MARGIN_X, MARGIN_TOP, CONTENT_MAX_Y, and FOOTER_TOP constants at the top of your export script. The defaults suit 16:9 with a slim footer; override them if your design system uses a different canvas size or footer height, and pass the same values to verify_layout.py via --content-max-y / --canvas-h flags.
๐ด critical = content cropped or invisible, must fix; ๐ high = visual hierarchy broken or no breathing room, should fix; ๐ก medium = italic/font/color drift, fix in this pass; ๐ข low = minor spacing or sub-pixel offsets, note but don't block.
Full instructions (SKILL.md)
Source of truth, from nexu-io/open-design.
name: pptx-html-fidelity-audit description: Audit a python-pptx export against its source HTML deck, identify layout/content drift (footer overflow, cropped content, missing italic/em, lost styling, off-rhythm spacing), and re-export with strict footer-rail + cursor-flow layout discipline. Use this skill whenever the user has a .pptx that was generated from an HTML slide deck and asks to compare/audit/verify/fix the export โ including phrases like "compare ppt with html", "fidelity audit", "fix the pptx", "ppt is cut off", "footer overlap", "italic missing in pptx", "re-export the deck", "pptx-html-fidelity-audit", or any case where a python-pptx โ HTML round-trip needs verification or repair. Also trigger when the user shows you a deck.html and a deck.pptx side by side and is debugging visual differences. triggers:
- "pptx fidelity"
- "pptx audit"
- "ppt ่ทๆ"
- "ๅญๅไธๅฐ"
- "footer overlap"
- "verify pptx"
- "html to pptx" od: mode: utility scenario: engineering
PPTX โ HTML Fidelity Audit
A repeatable workflow for catching the ways a python-pptx export silently drifts from its HTML source โ and fixing them with a layout discipline that prevents the same regressions on the next pass.
When this skill applies
The user has:
-
A source HTML slide deck (typically a single-file deck with
<section class="slide">blocks):<section class="slide light"> <div class="chrome">2026 ยท Q2 review</div> <span class="kicker">Pillar 03</span> <h2 class="h-xl">Shipping <em>velocity</em> doubled</h2> <p class="lead">โฆ</p> <div class="foot">page 5 / 14</div> </section> -
A PPTX file generated from that deck via python-pptx (or similar).
-
A suspicion (or visible evidence) that the PPTX doesn't match the HTML โ text bleeding into the footer, italic words gone flat, hero slides not centered, sections cropped, tag styling lost.
If the user only has one of those two artifacts, this skill doesn't apply yet โ first generate the missing one, or ask the user to provide it.
Why this is hard (and why a skill helps)
PPTX is a fixed-canvas, absolute-positioned medium. HTML is a fluid, flow-based medium. A naive python-pptx export pins each block at hand-picked (top, left) coordinates, which works for the first slide it was tested on and silently fails for every other slide whose content has different intrinsic height. The result is the most common drift modes:
- Footer overflow โ content's
top + heightcrosses into the footer row. - Off-canvas content โ bottom of last block exceeds
7.5"(16:9 canvas). - Italic loss โ
<em>in HTML never getsrun.font.italic = True. - Hero slides not centered โ vertical-stack slides use
MARGIN_TOPinstead of computing center. - Box bounds intruding โ the text fits, but the shape's bounding box is oversized and visually crosses the rail.
- Tag/styling loss โ colored chrome rows, kicker uppercase tracking, mono-vs-serif assignments quietly fall back to defaults.
Every one of these is a layout discipline problem, not a content problem. Once you adopt the discipline, they stop happening.
Workflow
The audit is five steps. Don't skip any of them โ the discipline only works if the audit produces a real list of issues to drive the re-export. A fix-without-audit pass tends to leave half the issues alive.
Step 1 โ Extract ground truth from the PPTX
Run scripts/extract_pptx.py <path-to.pptx> > pptx_dump.json. The script walks every shape on every slide and dumps text, position (top / left), size (width / height), and per-run typography (font name, size pt, bold, italic, color). This is the actual state of the export โ don't trust the export script's intent, trust the dump.
For 14-slide decks, the dump is ~30โ60 KB and human-readable.
Step 2 โ Walk the HTML structure
Read the source HTML and enumerate <section class="slide"> blocks. For each, note:
- The slide's theme (
light/dark/hero light/hero dark). - The
chromerow text (top metadata). - The
kicker(small uppercase eyebrow above the headline). - The headline (h-hero / h-xl / etc.) and any sub-head.
- The body copy and any structured blocks (pipeline steps, cards, pillars, observation cards).
- The
footrow (bottom metadata). - Any
<em>or italic-styled spans โ italic is the silent regression.
Map each HTML slide to a PPTX slide index. For decks following the convention "slide 1 = cover, slide N = closing", the mapping is positional.
Step 3 โ Build the audit table
For each slide, walk shapes from the dump and check against expected layout rules. Use this exact table format โ the severity column is what drives the fix priority:
| Slide | Issue | Severity |
|---|---|---|
| 1 cover | meta-row ๅบ็ซฏ 6.95" ่้ footer (6.7") | ๐ด |
| 5 checklist | row B ๆญฅ้ฉๆ่ฟฐๅบ็ซฏ 7.2" ๅๅฐ footer | ๐ด |
| 8 3E | ๆถๆๆฎต่ฝ็ดๆฅๅๅจ footer ่ตท้ป | ๐ด |
| 9 on-day | step ๆ่ฟฐๅบ็ซฏๅๅฅฝ็ขฐ footer๏ผ็กๅฎๅ
จ่ท | ๐ |
| ๅค่ | em (Playfair italic) ๆชไฟ็ | ๐ก |
Severity rubric:
- ๐ด critical โ content cropped, text invisible, footer overlap, off-canvas. Must fix.
- ๐ high โ content visible but visual hierarchy broken, no breathing room, hero not centered. Should fix.
- ๐ก medium โ italic/em missing, font fallback wrong, color drift. Fix in this pass.
- ๐ข low โ minor spacing/alignment, sub-pixel offsets. Note but don't block.
After the table, write a short root-cause section: 90 % of the issues usually come from 2โ3 systemic causes (e.g. "no footer rail enforced", "hero stacks pinned to MARGIN_TOP instead of centered", "italic never propagated"). Naming the systemic causes makes the re-export script much smaller and more correct.
Step 4 โ Re-export with footer-rail + cursor-flow layout discipline
This is the load-bearing technique. See references/layout-discipline.md for the full rules; the summary:
Define the rails up front, once, for the whole deck:
from pptx.util import Inches
CANVAS_W = Inches(13.333) # 16:9
CANVAS_H = Inches(7.5)
MARGIN_X = Inches(0.6)
MARGIN_TOP = Inches(0.5)
CONTENT_MAX_Y = Inches(6.70) # NOTHING in content area may cross this
FOOTER_TOP = Inches(6.85) # footer row pinned here, edge-to-edge
Customizing the rails. The defaults above suit a 16:9 canvas with a slim footer. If your design system uses a wider footer or a 4:3 canvas, override these constants in your export script and pass the same values to
verify_layout.pyvia--content-max-y/--canvas-h/--canvas-w. Seereferences/layout-discipline.mdยง1 for the full constant table.
Use a cursor for content blocks instead of pinning each block at an absolute y:
class Cursor:
"""Advances down the slide; refuses to cross the footer rail."""
def __init__(self, y_start, cap=CONTENT_MAX_Y):
self.y = y_start
self.cap = cap
def take(self, h, gap=Inches(0.12)): # ~1 line of whitespace at 14pt; tighten/loosen per design system
top = self.y
self.y = top + h + gap
if self.y > self.cap:
raise OverflowError(
f"cursor at {self.y} exceeds footer rail {self.cap}; "
f"reduce block height or split slide"
)
return top
For each slide, instantiate Cursor(MARGIN_TOP) and take(height) each block in reading order. The slide refuses to render if any block would cross the rail, so overflows become loud build errors instead of silent visual bugs.
Hero (vertically-centered) slides use a budget instead of a cursor:
def hero_layout(blocks):
"""blocks = list of (height, gap_after) tuples in reading order."""
total = sum(h + g for h, g in blocks)
y_start = (CANVAS_H - total) / 2
return Cursor(y_start)
That single change kills "hero slide content sticks to top" โ the most common hero defect.
Tighten box height to fit text + minimal padding. PowerPoint reveals shape bounds when they overlap (selection halos, Z-order conflicts), and an oversized box can visually cross the footer rail even when the text inside doesn't. Compute box height from text metrics + ~0.05" pad, not from generous wrappers.
Preserve italic / em explicitly:
def add_run(p, text, font, size_pt, italic=False, bold=False, color=None):
r = p.add_run()
r.text = text
r.font.name = font
r.font.size = Pt(size_pt)
r.font.italic = italic
r.font.bold = bold
if color:
r.font.color.rgb = color
return r
When walking HTML, detect <em> / <i> / inline style font-style: italic and pass italic=True. Use the EN serif face (Playfair Display, Source Serif, or fallback Georgia) for italic display copy โ the CJK serif typically has no italic and looks broken if you try to italicize it.
For deeper font issues that the layout rails can't catch โ variable-font traps where PowerPoint silently swaps to Calibri / Microsoft JhengHei, missing <a:ea> slot causing CJK runs to fall back, fake-italic on Han characters โ read references/font-discipline.md. The five layers there cover everything verify_layout.py can't see.
Step 5 โ Verify post-export
After writing the new .pptx, run scripts/verify_layout.py <path-to.pptx>. The script:
- Walks every shape on every slide.
- Asserts
top + height โค CONTENT_MAX_Yfor content shapes (footer/page-number shapes are allowed below the rail). - Asserts
top + height โค CANVAS_Hfor all shapes (no off-canvas). - Asserts
left + width โค CANVAS_Wandleft โฅ 0. - Reports violations as a single block: slide index, shape name, observed bottom, rail.
Zero violations is the gate for "this re-export is shippable". Don't claim the audit is fixed without running the verifier โ the human eye misses 1โ2 mm overflow at zoom-out, the script doesn't.
Output to the user
After Step 5 passes, report:
- Audit table โ the table from Step 3.
- Root causes โ 1-paragraph systemic explanation.
- Fix list โ terse list of what was changed and why (e.g. "hero slides switched to budget centering", "all content blocks routed through Cursor", "em runs explicitly italic").
- Verification โ "0 rail violations across N slides, file size X KB".
- Path โ absolute path to the re-exported
.pptx.
The user is reading for two reasons: confirming the visible bugs are fixed, and trusting the systemic fix is right. Cover both.
Bundled resources
scripts/extract_pptx.pyโ dump every shape on every slide as JSON. Run before the audit. Important: also run on the original export to compare, and on the re-exported one to confirm.scripts/verify_layout.pyโ post-export rail checker. Returns nonzero exit code on violations so it slots into a CI pipeline if needed.references/layout-discipline.mdโ the full footer-rail + cursor-flow rule set with code snippets for each common slide type (hero, content, pipeline, two-column, observation grid).references/font-discipline.mdโ five-layer font audit: mapping, presence, variable-vs-static traps, the three XML language slots (latin/ea/cs), CJK + Latin italic interaction.references/audit-table-template.mdโ copy-pasteable table template with severity legend.
Read the references when:
- The deck has slide types beyond what the SKILL.md covers (multi-column dashboards, embedded images, charts) โ
layout-discipline.md. - The audit shows ๐ก typography issues โ italic missing, CJK falling back, unexpected
Calibri/Microsoft JhengHeiin the XML โfont-discipline.md. - You want to drop the audit table directly into a report or markdown deliverable โ
audit-table-template.md.
Anti-patterns to avoid
- Patching individual slides without naming the systemic cause. If you fix slide 5 by lowering its block by 0.2", you'll be back fixing slide 9, 11, and 14 next. Find the rule that produced all four problems.
- Trusting the original export script's intent. Always run the extractor against the actual file. Drift between intent and reality is the bug.
- Skipping verification because "it looked fine in PowerPoint preview". Preview anti-aliasing hides 1โ2 mm overflows. The script doesn't.
- Italicizing scripts that have no italic tradition. CJK, Arabic, Hebrew, Devanagari, Thai, and Khmer all produce a synthesized slant when forced into
italic=True, and the result looks mechanically deformed. Italicize only runs whose primary script supports italic โ Latin, Cyrillic, Greek. Seereferences/font-discipline.mdLayer 5 for the implementation pattern. - Using
MARGIN_TOPfor hero slides. Hero slides need budget centering, not top-anchored. This is the most common hero defect and the cheapest to fix.
Why geometry-based verification, not visual diff
An earlier iteration of this skill leaned on visual diffing โ render the
.pptx through Keynote โ PDF โ PNG, screenshot the HTML through Chrome
headless, stitch them side-by-side with magick. It worked, but with
three sharp drawbacks:
- Platform lock-in. Keynote AppleScript is macOS-only;
magickand font-discovery commands vary across OSes; CI pipelines on Linux can't reproduce the chain. - Imprecision. A 1-2 mm overflow gets anti-aliased away in a PNG preview. The human eye misses it; the script catches it as a hard numeric violation.
- Setup cost. Every contributor needs the full graphics toolchain
installed before they can audit. Geometry checks need only
python-pptx.
Geometry-based verification gives up one thing the visual diff is good
at: catching cases where shape positions are correct but the rendered
glyph looks wrong (font fallback, kerning bugs, missing weight). When
that case appears, fall back to a manual screenshot review โ the
five-layer audit in references/font-discipline.md covers most of the
underlying causes.
Related skills
More from nexu-io/open-design and the wider catalog.
ui-ux-pro-max
Catalog entry for UI/UX Pro Max design patternsโfull workflow requires upstream installation.
web-design-guidelines
Web design guidelines and standards covering layout, typography, color, motion, and accessibility.
frontend-design
Create production-grade frontend interfaces with strong visual direction, polished typography, and working code.
design-brief
Parse design briefs into concrete specs using I-Lang protocol to eliminate ambiguity.
hatch-pet
Create Codex-compatible animated pet spritesheets from art, screenshots, or references with deterministic assembly and packaging.
creative-director
AI creative director with 20+ methodologies and recursive self-assessment for campaign concepts and design critique.