[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M7DMFT0633SY3B2P9M92EEKE":3,"skill-same:01M7DMFT0633SY3B2P9M92EEKE":84},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":18,"linkOnly":19,"markdownContent":20,"markdownBody":21,"frontMatter":22,"refName":34,"commitSha":35,"skillDirectory":36,"forksCount":37,"repositoryHtmlUrl":38,"repositoryHomepageUrl":39,"ownerHtmlUrl":40,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":41,"install":42,"resources":55},"01M7DMFT0633SY3B2P9M92EEKE","pptx-html-fidelity-audit",null,"Audit a python-pptx export against its source HTML deck, identify layout\u002Fcontent drift (footer overflow, cropped content, missing italic\u002Fem, 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\u002Faudit\u002Fverify\u002Ffix 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.","andremolnar\u002Fopen-open-design","andremolnar","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F919644?v=4","01M7DHE1X4QH6SJBHR92VG5N84","01M7DHE1XRHK1WQV34AM9M328E",1,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fpptx-html-fidelity-audit\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: pptx-html-fidelity-audit\ndescription: Audit a python-pptx export against its source HTML deck, identify layout\u002Fcontent drift (footer overflow, cropped content, missing italic\u002Fem, 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\u002Faudit\u002Fverify\u002Ffix 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.\ntriggers:\n  - \"pptx fidelity\"\n  - \"pptx audit\"\n  - \"ppt 跑掉\"\n  - \"字型不對\"\n  - \"footer overlap\"\n  - \"verify pptx\"\n  - \"html to pptx\"\nod:\n  mode: utility\n  scenario: engineering\n---\n\n# PPTX ↔ HTML Fidelity Audit\n\nA 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.\n\n## When this skill applies\n\nThe user has:\n\n- A source HTML slide deck (typically a single-file deck with `\u003Csection class=\"slide\">` blocks):\n\n  ```html\n  \u003Csection class=\"slide light\">\n    \u003Cdiv class=\"chrome\">2026 · Q2 review\u003C\u002Fdiv>\n    \u003Cspan class=\"kicker\">Pillar 03\u003C\u002Fspan>\n    \u003Ch2 class=\"h-xl\">Shipping \u003Cem>velocity\u003C\u002Fem> doubled\u003C\u002Fh2>\n    \u003Cp class=\"lead\">…\u003C\u002Fp>\n    \u003Cdiv class=\"foot\">page 5 \u002F 14\u003C\u002Fdiv>\n  \u003C\u002Fsection>\n  ```\n\n- A PPTX file generated from that deck via python-pptx (or similar).\n- 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.\n\nIf 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.\n\n## Why this is hard (and why a skill helps)\n\nPPTX 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:\n\n1. **Footer overflow** — content's `top + height` crosses into the footer row.\n2. **Off-canvas content** — bottom of last block exceeds `7.5\"` (16:9 canvas).\n3. **Italic loss** — `\u003Cem>` in HTML never gets `run.font.italic = True`.\n4. **Hero slides not centered** — vertical-stack slides use `MARGIN_TOP` instead of computing center.\n5. **Box bounds intruding** — the text fits, but the *shape's bounding box* is oversized and visually crosses the rail.\n6. **Tag\u002Fstyling loss** — colored chrome rows, kicker uppercase tracking, mono-vs-serif assignments quietly fall back to defaults.\n\nEvery one of these is a *layout discipline* problem, not a content problem. Once you adopt the discipline, they stop happening.\n\n---\n\n## Workflow\n\nThe 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.\n\n### Step 1 — Extract ground truth from the PPTX\n\nRun `scripts\u002Fextract_pptx.py \u003Cpath-to.pptx> > pptx_dump.json`. The script walks every shape on every slide and dumps text, position (`top` \u002F `left`), size (`width` \u002F `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.\n\nFor 14-slide decks, the dump is ~30–60 KB and human-readable.\n\n### Step 2 — Walk the HTML structure\n\nRead the source HTML and enumerate `\u003Csection class=\"slide\">` blocks. For each, note:\n\n- The slide's theme (`light` \u002F `dark` \u002F `hero light` \u002F `hero dark`).\n- The `chrome` row text (top metadata).\n- The `kicker` (small uppercase eyebrow above the headline).\n- The headline (h-hero \u002F h-xl \u002F etc.) and any sub-head.\n- The body copy and any structured blocks (pipeline steps, cards, pillars, observation cards).\n- The `foot` row (bottom metadata).\n- Any `\u003Cem>` or italic-styled spans — italic is the silent regression.\n\nMap each HTML slide to a PPTX slide index. For decks following the convention \"slide 1 = cover, slide N = closing\", the mapping is positional.\n\n### Step 3 — Build the audit table\n\nFor 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:\n\n```\n| Slide | Issue | Severity |\n|---|---|---|\n| 1 cover | meta-row 底端 6.95\" 蓋過 footer (6.7\") | 🔴 |\n| 5 checklist | row B 步驟描述底端 7.2\" 切到 footer | 🔴 |\n| 8 3E | 收束段落直接坐在 footer 起點 | 🔴 |\n| 9 on-day | step 描述底端剛好碰 footer，無安全距 | 🟠 |\n| 多處 | em (Playfair italic) 未保留 | 🟡 |\n```\n\nSeverity rubric:\n\n- 🔴 **critical** — content cropped, text invisible, footer overlap, off-canvas. Must fix.\n- 🟠 **high** — content visible but visual hierarchy broken, no breathing room, hero not centered. Should fix.\n- 🟡 **medium** — italic\u002Fem missing, font fallback wrong, color drift. Fix in this pass.\n- 🟢 **low** — minor spacing\u002Falignment, sub-pixel offsets. Note but don't block.\n\nAfter 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.\n\n### Step 4 — Re-export with footer-rail + cursor-flow layout discipline\n\nThis is the load-bearing technique. See `references\u002Flayout-discipline.md` for the full rules; the summary:\n\n**Define the rails up front, once, for the whole deck:**\n\n```python\nfrom pptx.util import Inches\n\nCANVAS_W       = Inches(13.333)   # 16:9\nCANVAS_H       = Inches(7.5)\nMARGIN_X       = Inches(0.6)\nMARGIN_TOP     = Inches(0.5)\nCONTENT_MAX_Y  = Inches(6.70)     # NOTHING in content area may cross this\nFOOTER_TOP     = Inches(6.85)     # footer row pinned here, edge-to-edge\n```\n\n> **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.py` via `--content-max-y` \u002F `--canvas-h` \u002F `--canvas-w`. See `references\u002Flayout-discipline.md` §1 for the full constant table.\n\n\n**Use a cursor for content blocks instead of pinning each block at an absolute y:**\n\n```python\nclass Cursor:\n    \"\"\"Advances down the slide; refuses to cross the footer rail.\"\"\"\n    def __init__(self, y_start, cap=CONTENT_MAX_Y):\n        self.y = y_start\n        self.cap = cap\n    def take(self, h, gap=Inches(0.12)):  # ~1 line of whitespace at 14pt; tighten\u002Floosen per design system\n        top = self.y\n        self.y = top + h + gap\n        if self.y > self.cap:\n            raise OverflowError(\n                f\"cursor at {self.y} exceeds footer rail {self.cap}; \"\n                f\"reduce block height or split slide\"\n            )\n        return top\n```\n\nFor 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.\n\n**Hero (vertically-centered) slides use a budget instead of a cursor:**\n\n```python\ndef hero_layout(blocks):\n    \"\"\"blocks = list of (height, gap_after) tuples in reading order.\"\"\"\n    total = sum(h + g for h, g in blocks)\n    y_start = (CANVAS_H - total) \u002F 2\n    return Cursor(y_start)\n```\n\nThat single change kills \"hero slide content sticks to top\" — the most common hero defect.\n\n**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.\n\n**Preserve italic \u002F em explicitly:**\n\n```python\ndef add_run(p, text, font, size_pt, italic=False, bold=False, color=None):\n    r = p.add_run()\n    r.text = text\n    r.font.name = font\n    r.font.size = Pt(size_pt)\n    r.font.italic = italic\n    r.font.bold = bold\n    if color:\n        r.font.color.rgb = color\n    return r\n```\n\nWhen walking HTML, detect `\u003Cem>` \u002F `\u003Ci>` \u002F 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.\n\nFor deeper font issues that the layout rails can't catch — variable-font traps where PowerPoint silently swaps to Calibri \u002F Microsoft JhengHei, missing `\u003Ca:ea>` slot causing CJK runs to fall back, fake-italic on Han characters — read `references\u002Ffont-discipline.md`. The five layers there cover everything `verify_layout.py` can't see.\n\n### Step 5 — Verify post-export\n\nAfter writing the new `.pptx`, run `scripts\u002Fverify_layout.py \u003Cpath-to.pptx>`. The script:\n\n- Walks every shape on every slide.\n- Asserts `top + height ≤ CONTENT_MAX_Y` for content shapes (footer\u002Fpage-number shapes are allowed below the rail).\n- Asserts `top + height ≤ CANVAS_H` for all shapes (no off-canvas).\n- Asserts `left + width ≤ CANVAS_W` and `left ≥ 0`.\n- Reports violations as a single block: slide index, shape name, observed bottom, rail.\n\nZero 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.\n\n---\n\n## Output to the user\n\nAfter Step 5 passes, report:\n\n1. **Audit table** — the table from Step 3.\n2. **Root causes** — 1-paragraph systemic explanation.\n3. **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\").\n4. **Verification** — \"0 rail violations across N slides, file size X KB\".\n5. **Path** — absolute path to the re-exported `.pptx`.\n\nThe user is reading for two reasons: confirming the visible bugs are fixed, and trusting the systemic fix is right. Cover both.\n\n---\n\n## Bundled resources\n\n- `scripts\u002Fextract_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.\n- `scripts\u002Fverify_layout.py` — post-export rail checker. Returns nonzero exit code on violations so it slots into a CI pipeline if needed.\n- `references\u002Flayout-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).\n- `references\u002Ffont-discipline.md` — five-layer font audit: mapping, presence, variable-vs-static traps, the three XML language slots (`latin` \u002F `ea` \u002F `cs`), CJK + Latin italic interaction.\n- `references\u002Faudit-table-template.md` — copy-pasteable table template with severity legend.\n\nRead the references when:\n\n- The deck has slide types beyond what the SKILL.md covers (multi-column dashboards, embedded images, charts) → `layout-discipline.md`.\n- The audit shows 🟡 typography issues — italic missing, CJK falling back, unexpected `Calibri` \u002F `Microsoft JhengHei` in the XML → `font-discipline.md`.\n- You want to drop the audit table directly into a report or markdown deliverable → `audit-table-template.md`.\n\n---\n\n## Anti-patterns to avoid\n\n- **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.\n- **Trusting the original export script's intent.** Always run the extractor against the actual file. Drift between intent and reality is the bug.\n- **Skipping verification because \"it looked fine in PowerPoint preview\".** Preview anti-aliasing hides 1–2 mm overflows. The script doesn't.\n- **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. See `references\u002Ffont-discipline.md` Layer 5 for the implementation pattern.\n- **Using `MARGIN_TOP` for hero slides.** Hero slides need *budget centering*, not top-anchored. This is the most common hero defect and the cheapest to fix.\n\n---\n\n## Why geometry-based verification, not visual diff\n\nAn earlier iteration of this skill leaned on visual diffing — render the\n.pptx through Keynote → PDF → PNG, screenshot the HTML through Chrome\nheadless, stitch them side-by-side with `magick`. It worked, but with\nthree sharp drawbacks:\n\n- **Platform lock-in.** Keynote AppleScript is macOS-only; `magick` and\n  font-discovery commands vary across OSes; CI pipelines on Linux can't\n  reproduce the chain.\n- **Imprecision.** A 1-2 mm overflow gets anti-aliased away in a PNG\n  preview. The human eye misses it; the script catches it as a hard\n  numeric violation.\n- **Setup cost.** Every contributor needs the full graphics toolchain\n  installed before they can audit. Geometry checks need only\n  `python-pptx`.\n\nGeometry-based verification gives up one thing the visual diff is good\nat: catching cases where shape positions are correct but the rendered\nglyph looks wrong (font fallback, kerning bugs, missing weight). When\nthat case appears, fall back to a manual screenshot review — the\nfive-layer audit in `references\u002Ffont-discipline.md` covers most of the\nunderlying causes.\n","# PPTX ↔ HTML Fidelity Audit\n\nA 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.\n\n## When this skill applies\n\nThe user has:\n\n- A source HTML slide deck (typically a single-file deck with `\u003Csection class=\"slide\">` blocks):\n\n  ```html\n  \u003Csection class=\"slide light\">\n    \u003Cdiv class=\"chrome\">2026 · Q2 review\u003C\u002Fdiv>\n    \u003Cspan class=\"kicker\">Pillar 03\u003C\u002Fspan>\n    \u003Ch2 class=\"h-xl\">Shipping \u003Cem>velocity\u003C\u002Fem> doubled\u003C\u002Fh2>\n    \u003Cp class=\"lead\">…\u003C\u002Fp>\n    \u003Cdiv class=\"foot\">page 5 \u002F 14\u003C\u002Fdiv>\n  \u003C\u002Fsection>\n  ```\n\n- A PPTX file generated from that deck via python-pptx (or similar).\n- 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.\n\nIf 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.\n\n## Why this is hard (and why a skill helps)\n\nPPTX 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:\n\n1. **Footer overflow** — content's `top + height` crosses into the footer row.\n2. **Off-canvas content** — bottom of last block exceeds `7.5\"` (16:9 canvas).\n3. **Italic loss** — `\u003Cem>` in HTML never gets `run.font.italic = True`.\n4. **Hero slides not centered** — vertical-stack slides use `MARGIN_TOP` instead of computing center.\n5. **Box bounds intruding** — the text fits, but the *shape's bounding box* is oversized and visually crosses the rail.\n6. **Tag\u002Fstyling loss** — colored chrome rows, kicker uppercase tracking, mono-vs-serif assignments quietly fall back to defaults.\n\nEvery one of these is a *layout discipline* problem, not a content problem. Once you adopt the discipline, they stop happening.\n\n---\n\n## Workflow\n\nThe 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.\n\n### Step 1 — Extract ground truth from the PPTX\n\nRun `scripts\u002Fextract_pptx.py \u003Cpath-to.pptx> > pptx_dump.json`. The script walks every shape on every slide and dumps text, position (`top` \u002F `left`), size (`width` \u002F `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.\n\nFor 14-slide decks, the dump is ~30–60 KB and human-readable.\n\n### Step 2 — Walk the HTML structure\n\nRead the source HTML and enumerate `\u003Csection class=\"slide\">` blocks. For each, note:\n\n- The slide's theme (`light` \u002F `dark` \u002F `hero light` \u002F `hero dark`).\n- The `chrome` row text (top metadata).\n- The `kicker` (small uppercase eyebrow above the headline).\n- The headline (h-hero \u002F h-xl \u002F etc.) and any sub-head.\n- The body copy and any structured blocks (pipeline steps, cards, pillars, observation cards).\n- The `foot` row (bottom metadata).\n- Any `\u003Cem>` or italic-styled spans — italic is the silent regression.\n\nMap each HTML slide to a PPTX slide index. For decks following the convention \"slide 1 = cover, slide N = closing\", the mapping is positional.\n\n### Step 3 — Build the audit table\n\nFor 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:\n\n```\n| Slide | Issue | Severity |\n|---|---|---|\n| 1 cover | meta-row 底端 6.95\" 蓋過 footer (6.7\") | 🔴 |\n| 5 checklist | row B 步驟描述底端 7.2\" 切到 footer | 🔴 |\n| 8 3E | 收束段落直接坐在 footer 起點 | 🔴 |\n| 9 on-day | step 描述底端剛好碰 footer，無安全距 | 🟠 |\n| 多處 | em (Playfair italic) 未保留 | 🟡 |\n```\n\nSeverity rubric:\n\n- 🔴 **critical** — content cropped, text invisible, footer overlap, off-canvas. Must fix.\n- 🟠 **high** — content visible but visual hierarchy broken, no breathing room, hero not centered. Should fix.\n- 🟡 **medium** — italic\u002Fem missing, font fallback wrong, color drift. Fix in this pass.\n- 🟢 **low** — minor spacing\u002Falignment, sub-pixel offsets. Note but don't block.\n\nAfter 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.\n\n### Step 4 — Re-export with footer-rail + cursor-flow layout discipline\n\nThis is the load-bearing technique. See `references\u002Flayout-discipline.md` for the full rules; the summary:\n\n**Define the rails up front, once, for the whole deck:**\n\n```python\nfrom pptx.util import Inches\n\nCANVAS_W       = Inches(13.333)   # 16:9\nCANVAS_H       = Inches(7.5)\nMARGIN_X       = Inches(0.6)\nMARGIN_TOP     = Inches(0.5)\nCONTENT_MAX_Y  = Inches(6.70)     # NOTHING in content area may cross this\nFOOTER_TOP     = Inches(6.85)     # footer row pinned here, edge-to-edge\n```\n\n> **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.py` via `--content-max-y` \u002F `--canvas-h` \u002F `--canvas-w`. See `references\u002Flayout-discipline.md` §1 for the full constant table.\n\n\n**Use a cursor for content blocks instead of pinning each block at an absolute y:**\n\n```python\nclass Cursor:\n    \"\"\"Advances down the slide; refuses to cross the footer rail.\"\"\"\n    def __init__(self, y_start, cap=CONTENT_MAX_Y):\n        self.y = y_start\n        self.cap = cap\n    def take(self, h, gap=Inches(0.12)):  # ~1 line of whitespace at 14pt; tighten\u002Floosen per design system\n        top = self.y\n        self.y = top + h + gap\n        if self.y > self.cap:\n            raise OverflowError(\n                f\"cursor at {self.y} exceeds footer rail {self.cap}; \"\n                f\"reduce block height or split slide\"\n            )\n        return top\n```\n\nFor 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.\n\n**Hero (vertically-centered) slides use a budget instead of a cursor:**\n\n```python\ndef hero_layout(blocks):\n    \"\"\"blocks = list of (height, gap_after) tuples in reading order.\"\"\"\n    total = sum(h + g for h, g in blocks)\n    y_start = (CANVAS_H - total) \u002F 2\n    return Cursor(y_start)\n```\n\nThat single change kills \"hero slide content sticks to top\" — the most common hero defect.\n\n**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.\n\n**Preserve italic \u002F em explicitly:**\n\n```python\ndef add_run(p, text, font, size_pt, italic=False, bold=False, color=None):\n    r = p.add_run()\n    r.text = text\n    r.font.name = font\n    r.font.size = Pt(size_pt)\n    r.font.italic = italic\n    r.font.bold = bold\n    if color:\n        r.font.color.rgb = color\n    return r\n```\n\nWhen walking HTML, detect `\u003Cem>` \u002F `\u003Ci>` \u002F 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.\n\nFor deeper font issues that the layout rails can't catch — variable-font traps where PowerPoint silently swaps to Calibri \u002F Microsoft JhengHei, missing `\u003Ca:ea>` slot causing CJK runs to fall back, fake-italic on Han characters — read `references\u002Ffont-discipline.md`. The five layers there cover everything `verify_layout.py` can't see.\n\n### Step 5 — Verify post-export\n\nAfter writing the new `.pptx`, run `scripts\u002Fverify_layout.py \u003Cpath-to.pptx>`. The script:\n\n- Walks every shape on every slide.\n- Asserts `top + height ≤ CONTENT_MAX_Y` for content shapes (footer\u002Fpage-number shapes are allowed below the rail).\n- Asserts `top + height ≤ CANVAS_H` for all shapes (no off-canvas).\n- Asserts `left + width ≤ CANVAS_W` and `left ≥ 0`.\n- Reports violations as a single block: slide index, shape name, observed bottom, rail.\n\nZero 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.\n\n---\n\n## Output to the user\n\nAfter Step 5 passes, report:\n\n1. **Audit table** — the table from Step 3.\n2. **Root causes** — 1-paragraph systemic explanation.\n3. **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\").\n4. **Verification** — \"0 rail violations across N slides, file size X KB\".\n5. **Path** — absolute path to the re-exported `.pptx`.\n\nThe user is reading for two reasons: confirming the visible bugs are fixed, and trusting the systemic fix is right. Cover both.\n\n---\n\n## Bundled resources\n\n- `scripts\u002Fextract_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.\n- `scripts\u002Fverify_layout.py` — post-export rail checker. Returns nonzero exit code on violations so it slots into a CI pipeline if needed.\n- `references\u002Flayout-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).\n- `references\u002Ffont-discipline.md` — five-layer font audit: mapping, presence, variable-vs-static traps, the three XML language slots (`latin` \u002F `ea` \u002F `cs`), CJK + Latin italic interaction.\n- `references\u002Faudit-table-template.md` — copy-pasteable table template with severity legend.\n\nRead the references when:\n\n- The deck has slide types beyond what the SKILL.md covers (multi-column dashboards, embedded images, charts) → `layout-discipline.md`.\n- The audit shows 🟡 typography issues — italic missing, CJK falling back, unexpected `Calibri` \u002F `Microsoft JhengHei` in the XML → `font-discipline.md`.\n- You want to drop the audit table directly into a report or markdown deliverable → `audit-table-template.md`.\n\n---\n\n## Anti-patterns to avoid\n\n- **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.\n- **Trusting the original export script's intent.** Always run the extractor against the actual file. Drift between intent and reality is the bug.\n- **Skipping verification because \"it looked fine in PowerPoint preview\".** Preview anti-aliasing hides 1–2 mm overflows. The script doesn't.\n- **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. See `references\u002Ffont-discipline.md` Layer 5 for the implementation pattern.\n- **Using `MARGIN_TOP` for hero slides.** Hero slides need *budget centering*, not top-anchored. This is the most common hero defect and the cheapest to fix.\n\n---\n\n## Why geometry-based verification, not visual diff\n\nAn earlier iteration of this skill leaned on visual diffing — render the\n.pptx through Keynote → PDF → PNG, screenshot the HTML through Chrome\nheadless, stitch them side-by-side with `magick`. It worked, but with\nthree sharp drawbacks:\n\n- **Platform lock-in.** Keynote AppleScript is macOS-only; `magick` and\n  font-discovery commands vary across OSes; CI pipelines on Linux can't\n  reproduce the chain.\n- **Imprecision.** A 1-2 mm overflow gets anti-aliased away in a PNG\n  preview. The human eye misses it; the script catches it as a hard\n  numeric violation.\n- **Setup cost.** Every contributor needs the full graphics toolchain\n  installed before they can audit. Geometry checks need only\n  `python-pptx`.\n\nGeometry-based verification gives up one thing the visual diff is good\nat: catching cases where shape positions are correct but the rendered\nglyph looks wrong (font fallback, kerning bugs, missing weight). When\nthat case appears, fall back to a manual screenshot review — the\nfive-layer audit in `references\u002Ffont-discipline.md` covers most of the\nunderlying causes.\n",{"od":23,"name":5,"triggers":26,"description":7},{"mode":24,"scenario":25},"utility","engineering",[27,28,29,30,31,32,33],"pptx fidelity","pptx audit","ppt 跑掉","字型不對","footer overlap","verify pptx","html to pptx","main","3a6721b94dec7506e324023f2855495c9c54d0d2","skills\u002Fpptx-html-fidelity-audit",0,"https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design","https:\u002F\u002Fopen-design.ai","https:\u002F\u002Fgithub.com\u002Fandremolnar","2026-10-30 01:33:11",{"source":8,"skillName":5,"commands":43,"prompt":53,"zipAvailable":54},[44,47,50],{"tool":45,"command":46},"npx","npx skills add andremolnar\u002Fopen-open-design --skill pptx-html-fidelity-audit",{"tool":48,"command":49},"bunx","bunx skills add andremolnar\u002Fopen-open-design --skill pptx-html-fidelity-audit",{"tool":51,"command":52},"pnpm","pnpm dlx skills add andremolnar\u002Fopen-open-design --skill pptx-html-fidelity-audit","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fpptx-html-fidelity-audit\u002FSKILL.md",true,[56,62,66,70,76,80],{"path":57,"resourceType":58,"resourceTypeDesc":59,"sizeBytes":60,"sourceHtmlUrl":61},"references\u002Faudit-table-template.md","REFERENCE","参考文档",3009,"https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fpptx-html-fidelity-audit\u002Freferences\u002Faudit-table-template.md",{"path":63,"resourceType":58,"resourceTypeDesc":59,"sizeBytes":64,"sourceHtmlUrl":65},"references\u002Ffont-discipline.md",16232,"https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fpptx-html-fidelity-audit\u002Freferences\u002Ffont-discipline.md",{"path":67,"resourceType":58,"resourceTypeDesc":59,"sizeBytes":68,"sourceHtmlUrl":69},"references\u002Flayout-discipline.md",14895,"https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fpptx-html-fidelity-audit\u002Freferences\u002Flayout-discipline.md",{"path":71,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":74,"sourceHtmlUrl":75},"scripts\u002F.gitignore","SCRIPT","脚本",19,"https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fpptx-html-fidelity-audit\u002Fscripts\u002F.gitignore",{"path":77,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":78,"sourceHtmlUrl":79},"scripts\u002Fextract_pptx.py",4384,"https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fpptx-html-fidelity-audit\u002Fscripts\u002Fextract_pptx.py",{"path":81,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":82,"sourceHtmlUrl":83},"scripts\u002Fverify_layout.py",5644,"https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fpptx-html-fidelity-audit\u002Fscripts\u002Fverify_layout.py",[85,92,99,106,113,122],{"skillNo":86,"name":87,"nameZh":6,"description":88,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":91,"publishedAt":18},"01M7DMPDFNBD52ZP0GNY046Z2A","Beta Skill","Instruction-only fixture skill.","https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Ftools\u002Frelease\u002Ftests\u002Ffixtures\u002Fcatalog\u002Fskills\u002Fbeta\u002FSKILL.md",[],[],{"skillNo":93,"name":94,"nameZh":6,"description":95,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":98,"publishedAt":18},"01M7DMPC4JJ8ENX7VWTMP38Y9C","Alpha Skill","First fixture skill with a runnable demo.","https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Ftools\u002Frelease\u002Ftests\u002Ffixtures\u002Fcatalog\u002Fskills\u002Falpha\u002FSKILL.md",[],[],{"skillNo":100,"name":101,"nameZh":6,"description":102,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":103,"categories":104,"keywords":105,"publishedAt":18},"01M7DMP9J5BX307VWVD67J0BBM","Deck One","A small deck template fixture.","https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Ftools\u002Frelease\u002Ftests\u002Ffixtures\u002Fcatalog\u002Fdesign-templates\u002Fdeck-one\u002FSKILL.md",[],[],{"skillNo":107,"name":108,"nameZh":6,"description":109,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":110,"categories":111,"keywords":112,"publishedAt":18},"01M7DMP7GNYTFXGK9RDZ2X723P","youtube-clipper","YouTube clip generation and editing with automated workflows — pull source video, slice highlights, add captions, and export.","https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-clipper\u002FSKILL.md",[],[],{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":117,"licenseFileUrl":118,"sourceHtmlUrl":119,"categories":120,"keywords":121,"publishedAt":18},"01M7DMP2WHMNKM0P5BXRYAG8D1","writing-guidelines","Review docs\u002Fprose for Writing Guidelines compliance. Use when asked to \"review my docs\", \"check writing style\", \"audit prose\", \"review docs voice and tone\", or \"check this page against the writing handbook\".","MIT","https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fwriting-guidelines\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fwriting-guidelines\u002FSKILL.md",[],[],{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":128,"publishedAt":18},"01M7DMP0GVJQ37CGXYQDD20VWS","wpds","WordPress Design System. Apply WordPress's official design tokens, typography, and component patterns to themes and sites.","https:\u002F\u002Fgithub.com\u002Fandremolnar\u002Fopen-open-design\u002Fblob\u002Fmain\u002Fskills\u002Fwpds\u002FSKILL.md",[],[]]