Working Log
April 12, 2026 · View on GitHub
Changelog
2026-04-11 · Day 1 · Scaffolding + M1/M2/M3/M6 + E2E
- 创建项目骨架(
docs/,src/pptx_skill/,scripts/,tests/) - 写
pyproject.toml(python-pptx, Pillow, fonttools + pytest),AGENTS.md,README.md,.gitignore - 写
docs/prd.md定义目标、验收标准、feature list、aggressive transparency 原则 - 写
docs/rfc.md定义模块划分、公共 API 签名、CLI 命令 shape、实现里程碑(12 steps) - 写
docs/test.md定义三层测试策略 - 决定 render path 走
soffice → PDF → pdftoppm → PNG(单 slide render 的唯一可行路径) - 决定 shape id 用 OOXML cNvPr id 而非 python-pptx 的 index
- 决定 CLI 用 argparse(标准库),print 到 stderr 而非 logging
- M1 实现:
doctor命令,报告 python 依赖版本 + soffice/pdftoppm 状态 + deck 字体列表 + 问题字体 warning - M2 实现:
Deck/Slide/Shape类的 read 部分,CLI:list-slides,list-shapes,get-text,get-notes,dump-slide,dump-deck全部 working - M3 实现:
Shape.set_text(支持 paragraph/run targeting + 保留 first run 格式),Slide.set_notes,Deck.save(含 .bak 自动备份),CLI:set-text,set-notes含--dry-run。Roundtrip test pass - M6 实现:
render_slide_png,LibreOffice → PDF → pdftoppm 链路,filter macOS harmless warning,CLIrender-slide - E2E 验证成功: 用
pptx-skill set-text真的把 v5 deck slide 0 的 Part 4 改成 "两个实战案例 · 落地方式" + "15 min",用render-slide渲染出 PNG,AI 视觉确认所有改动正确、其他元素未动、layout 保真
Lessons Learned
关于 LibreOffice render
soffice --convert-to png deck.pptx只输出 slide 1,不能指定 slide index。要 render 特定 slide 必须通过 PDF 中间格式- 因此 render 功能需要两个 optional 依赖: LibreOffice(soffice)和 poppler(pdftoppm)。
doctor命令必须检查两个都在才宣告 render ready - 首次启动 soffice 有 5-10 秒的 user profile 初始化延迟,后续调用快
关于字体
- v5 deck 主要用 Calibri(英文)+ 隐式 fallback(中文)。Calibri 是 Microsoft 专有字体,LibreOffice fallback 到 Liberation Sans,字宽不同
- Georgia 在 v5 里用于大标题,是跨平台字体,不需要替换。normalize_fonts 的替换列表应该是 allowlist(只替换已知问题字体),不是 blocklist
- East Asian 字体需要通过 OOXML 的
<a:ea typeface="...">单独设置,不能和西文字体一起写
关于 v5 deck 的 clone-ability
- v5 是程序化生成的 deck(shape 命名规律
Text N/Shape N),结构非常扁平 - 所有 18 张 slide 都用一个自定义的 DEFAULT layout(不是 Office 的标准 title / content layout)
- 没有任何 SmartArt / Chart / 3D / Animation / Table 等复杂元素
- 所有 shape 都是 Rectangle + Picture 两种类型,python-pptx 完全覆盖
- 结论: v5 deck 理论上 100% 可以用 python-pptx clone,这是 PRD success criteria 1 的前提
Render smoke test(Day 1 完成)
环境验证:
soffice→/opt/homebrew/bin/soffice,LibreOffice 26.2.2.2pdftoppm→/opt/homebrew/bin/pdftoppm,poppler 26.03.0- 两个都已通过 Homebrew 装好,PATH 上可见,不需要手工 symlink
烟雾测试命令序列:
soffice --headless --convert-to pdf --outdir /tmp/pptx_smoke Sammi_Demo_Deck_v5.pptx
# → /tmp/pptx_smoke/Sammi_Demo_Deck_v5.pdf (990KB, 18 页)
pdftoppm -png -f 1 -l 1 -r 150 /tmp/pptx_smoke/Sammi_Demo_Deck_v5.pdf /tmp/pptx_smoke/slide
# → /tmp/pptx_smoke/slide-01.png (97KB, 150 DPI)
验证结果:
- 生成 PNG 尺寸 97KB,内容清晰可辨
- 品牌青绿色
#0D9488完美保留 - 黑色背景 + 白色 / 灰色文字 OK
- 中文字体 fallback 可读,字宽无明显溢出
- Georgia 大标题字体正常(跨平台字体,无需替换)
- 议程 "Part 4 · Technical Demo · 实战案例现场演示 · 30 min" 清楚可见
- 保真度结论: 比预期的高,足够 AI reality check 用途
实现细节记录:
pdftoppm的输出命名是<prefix>-<page:02d>.png,所以单页 slide 1 输出为slide-01.png而不是slide-1.png。实现时要用f"{prefix}-{page:02d}.png"匹配- 首次 soffice 调用 ~5-7 秒(含 LibreOffice user profile 初始化),后续调用会快
render-slide的实际耗时: soffice(7s)+ pdftoppm(1s)+ 移动文件(<1s)≈ 8-9 秒 for cold start- 可以优化: 一次 deck 的多次 render 应该复用同一个 PDF,不要每次都重新 convert
一个 macOS 特定的无害 warning(可以忽略):
soffice[27698:98951495] Task policy set failed: 4 ((os/kern) invalid argument)
这是 macOS 上 LibreOffice 的已知 harmless log,不影响 render 结果。CLI 应该 filter 掉这一行避免污染 stderr 输出。