PPTX 设计系统逆向 + 统一重排(Windows)
处理"用户给定稿页做模板,把后续混乱复制内容重排进同一视觉体系"的完整流程。
核心心法
用户实际应用的颜色 ≠ theme1.xml 的主题色。 这是本类任务最容易踩的坑。
theme 里的 accent 色往往是从别处继承的默认值;真正要复刻的是 slide XML 里
逐个 <a:srgbClr val="..."> 出现频次最高的那几组色。必须统计频次,不要读主题。
步骤 0:Windows bash 前置(必须)
本机 bash 工具下 GNU coreutils 不在 PATH,所有命令必须加前缀:
export PATH="/usr/bin:/bin:$PATH"
否则 ls / dirname / sed 全部报 command not found。
(stderr 里那条 shell-runtime-bash-env.sh: line 3: dirname: command not found 是无害噪音,忽略。)
步骤 1:解析 PPTX(不依赖 python-pptx)
直接用 zipfile + ElementTree 读 OOXML。
命名空间:
a = http://schemas.openxmlformats.org/drawingml/2006/main
p = http://schemas.openxmlformats.org/presentationml/2006/main
- 幻灯片列表:
z.namelist()里匹配ppt/slides/slide\d+\.xml$ - 文本:遍历
{p}sp→{a}p→{a}t,拼接t.text - 画布尺寸:
ppt/presentation.xml的sldSz(EMU;914400 EMU = 1 英寸)
写三个脚本(放在 .workbuddy/tools/):
- extract_pptx.py —— 全文转储到 txt,作为内容底稿
- extract_design.py —— 统计 srgbClr / typeface 频次(关键!)
- extract_geo.py —— 逐 shape 的 name/位置/尺寸/preset-geometry/fill
步骤 2:确定设计令牌
根据 extract_design.py 的频次表 + 用户定稿页的视觉,人工确认:
- 主色 primary、强调色 accent1/2、中性色、背景色
- 字体(中文 + 拉丁)
写入 docs/page-global-config.json,并在 frontend/index.html 里替换
--primary-color 等 CSS 变量占位符。同时加 Google Fonts 兜底:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700;900&display=swap" rel="stylesheet">
步骤 3:内容取舍(重要)
不要机械照搬页数。 先判定哪些页是垃圾:
- 纯 XXX / TBD 占位符页 → 删
- 纯英文页(当用户要求"都要是中文")→ 转中文重写
- Demo / 示例 图片占位页 → 删或补真实内容
- 用户明确说"这是我写好的"的页 → 内容逐字保留
产出一份 docs/product/chapters.md:逐页记录
页码 | 页面类型 | 标题 | 副标题 | 选用模板 | 内容结构 | 密度 | 叙事角色。
步骤 4:生成 slide 文件
技术栈:Vite + Tailwind(skill 的 ppt-implement 脚手架)。
硬性约定:
- 每个 slide 一个文件 frontend/src/slides/slide-N.js
- 格式:window.slideDataMap.set(N, \<div>...</div>`);—— 框架自动加载,无需手动注册
- **关键布局一律用内联style="..."**,不要依赖 Tailwind class(构建阶段会 stripclass` 属性)
- 外层 div 固定:
class="w-[1440px] h-[810px] shadow-2xl relative overflow-hidden slide-bg"
style="font-family:'Microsoft YaHei','微软雅黑','Noto Sans SC',sans-serif;"
- 画布 1440×810 px(16:9)
写完立刻批量校验:
for i in $(seq 1 N); do node --check frontend/src/slides/slide-$i.js || echo "FAIL $i"; done
# div 配平
O=$(grep -o '<div' f.js|wc -l); C=$(grep -o '</div>' f.js|wc -l)
步骤 5:截图(Windows 专项绕过)
不要直接用 skill 自带的 screenshot-ppt.py —— 它有两处 Windows 不兼容:
1. ensure_venv() 找 .venv/bin/python3,Windows 实际是 .venv/Scripts/python.exe
2. 会尝试 pip install playwright 重装
绕过:用系统 Python(本机 C:/Python314/python.exe 已装 playwright),自写脚本。
若报 Executable doesn't exist at ...chromium_headless_shell-XXXX(版本不匹配),
加 channel="msedge" 回退——本机 Edge 一定存在,秒启动。
browser = p.chromium.launch(headless=True, channel="msedge") # 回退链见 references
context = browser.new_context(viewport={"width":1440,"height":810}, device_scale_factor=2)
page.goto(f"{URL}?page={i}", wait_until="networkidle")
page.locator("#ppt-viewport").screenshot(path=f"page-{i}.png")
Vite 会随 shell 会话退出而死 → 必须 run_in_background=true 独立启动,
加 --strictPort,然后 curl -o /dev/null -w "%{http_code}" 确认 200。
步骤 6:肉眼复查(不可跳过!)
截图不只是给 poster 用的——必须逐页 Read 看。 自动校验查不出排版问题。
本类任务 100% 会遇到的通病:内容只占上半页,下半页大片空白。
修复手法:给已有区块补一段支撑内容(流程链、对照表、四步法网格),
并同时调整已有区块的高度/内边距,否则会重叠。
改完单页后只重截那一页验证,最后再全量跑一次。
步骤 7:单文件 HTML 合并(可选交付)
skill 自带的 frontend/scripts/build-slides.js 在 Windows 上可能静默 exit=127
(fs.cpSync 拷大 assets 目录时被杀)。自写轻量版:
不拷 assets、不跑 npx/juice,直接拼 HTML + 手写 utility shim。
必须覆盖的 shim(因为 cleanInlineStyles() 会 strip 掉所有 class):
.absolute .relative .top-0 .left-0 .right-0 .bottom-0
.h-full .w-full .flex .grid .rounded-full .overflow-hidden
.w-\[1440px\] .h-\[810px\] .shadow-2xl .slide-bg .slide-page
交付清单
frontend/src/slides/slide-1..N.jsfrontend/public/assets/images/posters/pages/page-1..N.pngdocs/pages.json(pageKey / title / url / poster / pageNum)docs/product/{material,chapters,features}.mddocs/page-global-config.jsonfrontend/dist-slides/all-slides.html(自包含单文件)
常见坑速查
| 现象 | 原因 | 解法 |
|---|---|---|
| 颜色对不上 | 读了 theme1.xml | 统计 slideXML 里 srgbClr 频次 |
ls: command not found |
PATH 未设 | 加 PortableGit 前缀 |
No module named pptx |
未装 python-pptx | 用 zipfile+ElementTree |
| playwright 找不到 venv | POSIX 路径假设 | 用系统 Python |
| chromium 版本不匹配 | 本地版本旧 | channel="msedge" |
| Vite 502 | 随会话退出 | run_in_background + strictPort |
| 构建 exit=127 静默 | cpSync 大 assets 被杀 | 自写轻量 build 脚本 |
| 下半页大片空白 | 内容密度不足 | 截图肉眼查,补支撑区块 |
| 新增区块和旧内容重叠 | 只加不调 | 同时缩已有区块高度/内边距 |