Skip to main content

html-to-pptx

把 slide/poster 的 HTML 确定性地转成可编辑 PPTX——每个视觉元素(插画、图标、箭头、装饰线、文字)都是能单独选中拖动的对象,而不是压成一张背景图;也能自动识别并转换 Editable Design 的 editor.html 和 case 的图层展开图 layers.html;并可把产物渲回图片做保真度比对。当用户要导出 pptx/ppt、问「HTML 怎么转 PowerPoint」、想让生成的海报或幻灯片能在 PowerPoint 里继续改、要把图层展开图/拆解图变成 ppt、或需要检查转换后元素是否独立可编辑时使用。

跳到安装

来源信息

仓库
yejy53/Editable-Design
最近来源活动
2026年9月1日 15:23
检测到的 SKILL.md 语言
中文
星标
1,050
分支
77

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
15 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
html-to-pptx
description
把 slide/poster 的 HTML 确定性地转成可编辑 PPTX——每个视觉元素(插画、图标、箭头、装饰线、文字)都是能单独选中拖动的对象,而不是压成一张背景图;也能自动识别并转换 Editable Design 的 editor.html 和 case 的图层展开图 layers.html;并可把产物渲回图片做保真度比对。当用户要导出 pptx/ppt、问「HTML 怎么转 PowerPoint」、想让生成的海报或幻灯片能在 PowerPoint 里继续改、要把图层展开图/拆解图变成 ppt、或需要检查转换后元素是否独立可编辑时使用。
# HTML → 可编辑 PPTX **纯确定性转换:无 LLM、无常驻服务、离线单命令。** 目标不是「截个图塞进 ppt」,而是让 PowerPoint 里每个元素都能单独选中、拖动、改字、改色。 ## 零配置入口 始终通过 `scripts/run.sh` 转换。首次调用时,它会自动找到 Codex 或系统提供的 Python 3.10+,在 Skill 内创建隔离的 `.venv`,安装 Python 依赖和 Playwright Chromium,然后继续 当前转换;以后直接复用,不让用户手动配置环境: ```bash bash scripts/run.sh <任意 HTML> -o deck.pptx ``` 不要要求用户先运行 `pip`、安装 Playwright 或处理脚本权限。若自动初始化失败,再运行 `bash scripts/setup.sh` 查看明确诊断;`HTML_TO_PPTX_PYTHON` 可用于指定兼容的 Python。 确定性转换核心已经随 Skill 固定在 `scripts/_html_to_pptx.py`,不依赖其他私有仓库。 浏览器默认保留 Chromium 沙箱;只有运行环境明确不支持沙箱时,才设置 `HTML_TO_PPTX_NO_SANDBOX=1`。 `render_check.py` 额外需要 LibreOffice(渲染 pptx)和 PyMuPDF: `brew install --cask libreoffice && .venv/bin/python -m pip install -r requirements-check.txt`。 这两项只用于视觉比对,不影响核心转换。 ## 转换 一个入口支持三类页面并自动识别: ```bash bash scripts/run.sh <任意 HTML> # → 同目录同名 .pptx bash scripts/run.sh <HTML> -o deck.pptx bash scripts/run.sh <HTML> --selector '#hero' # 画布识别失败时手动指定 bash scripts/run.sh <HTML> --mode exploded # 自动识别出错时手动指定 ``` | 输入页面 | 自动模式 | 转换前处理 | | --- | --- | --- | | `index.html`、`*.edited.html` | `plain` | 直接测量固定画布 | | `editor.html` | `editor` | 调用编辑器的 `fullHTML()`,剥掉工具栏、面板和缩放舞台 | | `layers.html` | `exploded` | 等动画落位、去除 overview、关闭响应式缩放 | 判别信号来自页面本身:编辑器暴露 `__layerEditor`/`__freeEditor.fullHTML()`,展开图包含 `.hf-exploded-board`。如果页面像编辑器但 API 没有成功加载,转换会停止并报错,不会把 编辑器 UI 悄悄写进 PPTX。 `editor.html` 使用的 `fullHTML()` 与编辑器的 “Download HTML” 按钮是同一条官方导出路径。 临时 HTML 写在原文件旁边以保留相对图片和字体路径,转换完成后立即删除。 输出示例,**先确认页面类型、画布和元素分布是否合理**: ``` 页面类型:editor(自动识别) 编辑器 fullHTML() 导出 15 KB,已剥掉编辑器外壳 ✅ /path/dragon_poster.pptx 画布 1067x1600 ([data-canvas-width]) · 形状 4 · 文本框 10 · 独立图片 13 · 元素 28 ``` ## 图层展开图(case 的 `layers.html`) `outputs/cases/*/*/layers.html` 是一张**图层展开图**:`.hf-exploded-board` 上摊开若干 `.hf-exploded-item`——整图缩略(overview)、背景底、以及各图层/分组。转成**一页** PPTX,默认丢掉 overview 那一项: ```bash bash scripts/run.sh <case>/layers.html # → 同目录 layers.pptx bash scripts/run.sh <case>/layers.html --keep-overview ``` ``` board 3060x2400 · 分块 26 (跳过 overview 1)· {'layer': 12, 'group': 13, 'background': 1} ✅ .../layers.pptx 形状 45 · 文本框 93 · 独立图片 51 · 元素 265 ``` 粒度是**元素级**而非分块级:每块内部的每行字、每张照片、每张卡片都是独立对象。 这类页面有三个特性会干扰通用转换,`to_pptx.py` 识别后会转入 `exploded_to_pptx.py`,在量画布之前一次性抹平: 1. board 会按窗口自适应缩放,而转换器又要把视口调成画布大小,两者互相触发、越缩越小。 用 `!important` 规则钉死 `transform`,压过页面脚本写的 inline style。 2. 有入场动画,要等 `[data-hf-exploded-ready='1']` 再量。 3. overview 缩略图、扫描光效、replay 按钮属于页面外壳,不是内容。 ## 验收(建议每次都跑) ```bash python3 scripts/render_check.py <index.html> <out.pptx> # → <out>.compare.png python3 scripts/render_check.py <layers.html> <layers.pptx> --exploded ``` 四联图:源 HTML / PPTX 渲回 / 每个独立对象的边界框 / 误差热力图。**第三联是关键** ——框住的每一块在 PowerPoint 里都能单独选中。若某个视觉元素没有自己的框,说明它掉进 了兜底背景,按下方「排错」处理。 终端同时给出平均像素差、超阈值占比,以及误差最大的几块区域的坐标和两侧色值。 **别只看聚合数字**:整页平均差很容易被大面积正确的背景稀释,一整张卡片的内容糊掉在 均值上可能只体现为零点几的波动,一定要顺着列出的区域坐标去看具体是哪里错了。 展开图必须加 `--exploded`,它会套用与转换器完全相同的页面预处理;否则基准图里会多出 总览图和没跑完的入场动画,比对的是两张不同的画。 **注意 LibreOffice 的渲染不等于 PowerPoint**,竖排 CJK 尤其如此:同一份 pptx, PowerPoint 排成正确的单列,LibreOffice 会把它拆成两列。已实测确认这是 LibreOffice 的 问题,不是产物的问题——别为了迁就它去改字族,那只会让 PowerPoint 里变差。 macOS 上可以让 PowerPoint 自己导 PDF 来做权威比对: ```applescript tell application "Microsoft PowerPoint" open POSIX file "/abs/path/deck.pptx" save presentation 1 in "/abs/path/out.pdf" as save as PDF end tell ``` PowerPoint 是沙箱应用,输出路径要挑它有权限写的位置;若长时间无输出,多半是它弹了 模态框,切过去点掉即可。 ### 无法严格复刻的部分 - **字重只有两档**。OOXML 的 `b` 是布尔量,CSS 的 400/500/600/650 会被压成常规或加粗。 实测「PingFang SC Semibold」这类带字重的字族名解析不出来,这条路走不通。 - **对方机器缺字体就会被替换**。我们写的是正确的字族名,但装没装不由我们决定; OOXML 的字体内嵌 python-pptx 不支持,Mac 版 PowerPoint 也不认。 - **纵向基线有零点几个百分点的偏移**。PPTX 按文本框锚定,CSS 按行盒基线,结构不同。 - **径向/conic/多层叠加渐变画不成原生填充**。没有子元素时会栅格化,仍是独立可拖动 的对象、像素精确;带子元素的容器不能栅格化(会把子元素一起吞掉),它的底色就留在 兜底背景里,视觉正确但那层底色本身不可单独选中。单层线性渐变则输出成真正的 `gradFill`,双色标带 alpha 和角度。 ## 转换规则 四分类,决定每个 DOM 元素在 pptx 里变成什么: | 角色 | 产物 | 典型对象 | | --- | --- | --- | | `text` | 可编辑文本框(含自己的底色形状、旋转角度) | 标题、标签、斜排文字 | | `shape` | 原生(圆角)矩形,子元素叠在上面 | 卡片、胶囊、空心圆圈 | | `picture` | **单独隔离截图(透明底)**,各自成一张图 | 插画、内联 SVG 图标、箭头、装饰线 | | `image` | 不重建,留在兜底背景 | 满画布渐变底、纯装饰纹理 | 关键设计:**兜底背景只装没能重建的部分**,所以永远不丢像素;重建充分时它就只剩纸张底色。 画布自动识别 `.slide-canvas` / `.poster-canvas` / `[data-canvas-width]` / `#poster`; 幻灯片尺寸按**真实宽高比**推导(长边 13.333in),1920×1080 仍是 16:9,1067×1600 保持竖版, 两轴等比例不拉伸。 已覆盖:非均匀边框(顶边强调条拆成独立细条)、只有描边的空心盒子不被填实、 `writing-mode` 竖排、字体替换后自动缩字号以免溢出。 **旋转会累积祖先的 transform**,和缩放同理。被外层 wrapper 转了角度的按钮,自身 computed `transform` 是 `none`,只读它自己会把整块导成水平的。旋转元素按「未旋转的 布局尺寸、居中于实测包围盒」放置——包围盒是斜矩形的轴对齐外接框,直接拿它当宽高会偏大。 **水平对齐取实际渲染结果,不只看 `text-align`**。用 flex `justify-content: center` 居中 的胶囊按钮从不设置 `text-align`,它继承来的值是 `start`;只读这一个属性会把文字钉在 按钮左边缘。现在 flex 行方向读 `justify-content`、列方向读 `align-items`(列布局里水平 是交叉轴),grid 优先 `justify-items` 再退回 `justify-content`;`space-between` 这类分布 关键字仍沿用 `text-align`,因为只有一段文字时它们本来就贴起始边。 **`mix-blend-mode: multiply` 的白底会烘进 alpha**。白底无 alpha 的线稿 PNG 靠 multiply 消掉自己的白(白 × 纸色 = 纸色)。PowerPoint 的图片没有混合模式,而隔离截图又是透明底, multiply 和「什么都没有」混合等于不混合,白底会原样进 pptx,成为一块白板。 灰度墨的 multiply 与「黑墨按 alpha 叠加」在数学上等价:`src*d == a*C + (1-a)*d` 取 `C=0, a=1-src`。取 `m = min(r,g,b)` 作为要抠掉的白量即可推广到彩色墨—— 压在白底上时精确,墨是灰的时候处处精确,而 `grayscale()` 滤镜留下的正是灰墨。 `darken` 同样处理,`soft-light` 之类没有等价 alpha 形式,不处理。 这条规则会让产物**偏离展开图页面、但对齐真实海报**。展开图里每个分块只含自己那一层, 纸色是另一个独立分块,所以线稿底下压根没有可乘的东西,multiply 在展开图里是失效的, 白板照样显示(实测:真海报把 multiply 强制成 normal 采样点变化 +11,展开图变化 0)。 如实复刻这种页面等于把白板抄进 pptx,所以这里选择按图层的**语义**导出。 **叠放次序**按每个元素在页面里实测的「绘制路径」排:从画布往下逐层记录 `[有效 z-index, 兄弟序号]`,字典序比较即可在最近公共祖先处分出胜负。不能把 z-index 当 成全局排序键——它只在同一个层叠上下文内给兄弟排序,拍平之后 `position:relative; z-index:1` 的卡片会排到自己子元素后面,用底色盖住自己的图标和文字。 **排版保真**:字族取 CSS 字体链里**本机真实装有**的那一个(用字形宽度探测, `document.fonts.check` 恒返回 true、不能用),字号按实测值 1:1 换算,`letter-spacing` 写成 OOXML `spc`、`line-height` 写成精确行距、斜体写成 `i`。字号只在字体确实需要替换 时才收缩,字体对得上时不打折。 **文字块拆分**:一行文字若靠 flex `gap` 或内联分隔符的 margin 撑开,合成一个文本框会 塌掉近一半宽度。这类容器里的每段裸文本会被包进 span,各自成为独立文本框,落在自己 实测的位置上。 另外三项针对「元素被祖先加工过」的补偿,缺了任何一项展开图都会明显走样: - **累积缩放**:盒子量的是变换后的尺寸,字号/边框/圆角量的却是变换前的值。二者都乘上 祖先 `transform` 的累积缩放,否则 0.5 倍裁剪窗里的 24px 标题会撑爆自己的框。 - **累积不透明度**:元素被逐个拆出来,祖先的 `opacity` 和颜色自身的 alpha 都要折进 每个对象。漏掉的话,12% 透明度的水印大字会导出成实心亮字。 - **裁剪**:伸出 `overflow:hidden` 祖先的部分要按可见切片摆放,图片按可见区截图; 完全不可见的元素直接跳过。 ## 排错 **某个元素没有独立边界框(掉进兜底背景)** 到 `outputs/.../scene.json` 查它的 `roles`。判成 `image` 通常是两种情况:面积占比 ≥ 0.9 (被当成满画布背景),或者它确实没有自己的墨迹(纯布局容器)。前者属误判, 需要调 `MAX_SHAPE_AREA_FRACTION` 或在 `classify_elements` 里加规则。 **整页只有一个对象** 源 HTML 整页画在**一个大 `<svg>`** 里。SVG 内部节点不拆——这类页面没有可分的顶层结构, 是转换器的已知限制,不是 bug。要拆得改 HTML 生成侧,让结构落在 DOM 元素上。 **编辑器 UI 被拍进了 PPTX** 说明页面被当作 `plain` 转换了。正常的 Editable Design `editor.html` 会被自动识别并调用 `fullHTML()`;如果编辑器 API 没有成功加载,改传原始 `index.html` 或编辑器下载的 `*.edited.html`。 **相对资源(css/字体/图片)丢失** 按 HTML 所在目录解析,所以在原目录里跑最稳。 ## 分发给别人 ```bash ./pack.sh # → ~/Desktop/html-to-pptx-skill.tar.gz ``` `pack.sh` 会把完整 Skill(含固定的 `scripts/_html_to_pptx.py`、依赖清单、许可和诊断脚本) 一起打包,解压后不依赖本仓库其他文件。 对方:`tar -xzf html-to-pptx-skill.tar.gz -C ~/.codex/skills/`。首次转换会自动准备隔离运行时, 无需另外安装 Python 包或 Playwright。 包里带着 `pack.sh` 自己,可继续二次分发。 ## 与 Editable Design 的关系 两者是可独立安装的同级 Skill,不是主 Skill 与内部子 Skill。Editable Design 负责完成并交付 HTML、PNG、浏览器编辑器和设计回放;本 Skill 只负责把用户指定的 HTML 转成 PowerPoint 中 可编辑的 PPTX。 当 Editable Design 已完成核心设计,如果本 Skill 可用,可以向用户简短说明“还可以导出为 可编辑 PPTX”。**只有用户明确说需要时才运行转换**,不能让 PPTX 导出阻塞或延迟核心设计交付。 转换时可使用原始 `index.html`、编辑器下载的 `*.edited.html`、正常加载的 `editor.html` 或 `layers.html`;入口会自动选择对应的预处理路径。
在 GitHub 查看