这次的目标是:根据一张参考图,生成一页可编辑的 PPTX,文字可改、图形可替换,版式尽量贴近原图。
我的思路
不直接让 GPT “画一张图”,而是让它走一条更可控的链路:
参考图 → 版式识别 → 字体/图片环境检查 → python-pptx 生成 → 校验 → 输出可编辑 .pptx这样生成的不是截图,而是真正的 PPTX 文件,打开后可以继续改文字、换配色、调整图形。
关键步骤
1. 先看工作区里有什么文件
pwd&&rg--files-g'*.png'-g'*.jpg'-g'*.pptx'-g'AGENTS.md'.这一步主要是确认参考图、已有 PPTX、配置文件是否都在工作区里。
2. 查 PPT 生成 API 文档
cat"$CODEX_PRIMARY_RUNTIME_NODE_MODULES/@oai/artifact-tool/docs/presentations/api/API_DOCS.md"cat"$CODEX_PRIMARY_RUNTIME_NODE_MODULES/@oai/artifact-tool/docs/presentations/api/references/rich-text.spec.md"这里会看生成 PPT 时支持哪些元素,比如标题、正文、形状、图片、备注等。
3. 检查中文字体
fc-list :lang=zh family|sort-u中文 PPT 很容易踩字体坑,所以要先确认环境里有没有可用的中文字体。
4. 查找 CJK / 黑体类字体
rg--files"$CODEX_PRIMARY_RUNTIME_ROOT"-g'*.ttf'-g'*.otf'-g'*.ttc'\|rg'CJK|[Ss]ans[Ss][Cc]|[Ss]ans[Hh]ans|[Aa]rial|[Mm]icrosoft|[Ww]qy'如果要用中文排版,优先找 Noto Sans SC、思源黑体、微软雅黑、WenQuanYi 这类字体。
5. 查看运行时字体加载逻辑
ls"$CODEX_PRIMARY_RUNTIME_ROOT/dependencies"rg--files"$CODEX_PRIMARY_RUNTIME_ROOT"|rg-i'font|cjk'|tail-35sed-n'1,160p'/root/.codex/skills/builtins/presentations/container_tools/runtime_helpers.mjs这一步是为了确认 PPT 生成流程里,字体是怎么注册、怎么加载的。
6. 检查字体相关代码
rg-n'font|Font'/root/.codex/skills/builtins/presentations/container_tools/artifact_tool_utils.mjs|head-45ls/usr/share/fonts rg--files/usr/share/fonts|head-15rg--files"$CODEX_PRIMARY_RUNTIME_NODE_MODULES"|rg-i'CJK|NotoSansSC|\.ttc'|head-15如果环境里没有合适的中文字体,生成的中文可能会显示异常,所以这里要重点确认。
7. 查看构建校验结果
catbuild/validation.json用来确认之前的生成或校验有没有报错。
8. 分析 artifact 工具内部逻辑
frompathlibimportPathimportos,re p=Path(os.environ['CODEX_PRIMARY_RUNTIME_NODE_MODULES'])/'@oai/artifact-tool/dist/artifact_tool.mjs's=p.read_text()fortermin['FONT','fontPaths','loadFonts','registerFont','fontDirectory','FONTCONFIG','GlobalFonts']:matches=list(re.finditer(term,s))print(term,len(matches))forminmatches[:3]:print(s[max(0,m.start()-130):m.end()+220])这段脚本用来快速定位 PPT 生成工具里和字体相关的函数。
9. 查看核心生成函数
frompathlibimportPathimportos s=(Path(os.environ['CODEX_PRIMARY_RUNTIME_NODE_MODULES'])/'@oai/artifact-tool/dist/artifact_tool.mjs').read_text()i=s.find('function hqt()')print(s[i-1400:i+6500])找到核心函数后,可以判断它是否支持文本框、形状、图片、备注等元素。
10. 执行生成
"$CODEX_PRIMARY_RUNTIME_NODE"build/create.mjs>build/run.log2>&1tail-c1300build/run.log这里真正触发 PPTX 生成,并把日志输出到build/run.log。
11. 校验字体文件
fromfontTools.ttLibimportTTFont f=TTFont('fonts/DroidSansFallback.ttf')print(f['OS/2'].__dict__)print(f['hhea'].__dict__)如果生成结果里中文显示异常,可以用fontTools检查字体是否完整、是否包含中文字形。
12. 查看最终日志
tail-n3build/run.log|cut-c1-200确认最后几步有没有报错。
13. 应用补丁并输出文件
{"path":"/connector_openai_library/implicit_link::connector_openai_library/create_library_file","args":{"file":"/workspace/scratch/0b5208d9034e/output/入职感悟与成长体会_可编辑.pptx","library_artifact_type":"slides"}}这一步把生成的 PPTX 注册为可下载或可引用的产物。
14. 写入扩展属性
python3 /workspace/scratch/0b5208d9034e/build/library_file_transfer.py apply-xattrs\'/workspace/scratch/0b5208d9034e/output/入职感悟与成长体会_可编辑.pptx'\'libfile_9d7bf7ba58e48191907930c0f4174794'<<'JSON' [{"name":"user.library-file-version","value":"0"}] JSON给输出文件加上版本标记,方便后续追踪。
最终输出
生成后的文件路径是:
/workspace/scratch/0b5208d9034e/output/入职感悟与成长体会_可编辑.pptx它是一个真正的 PPTX,不是图片。打开后可以继续编辑文字、调整图形、替换配色。
踩坑提醒
- 中文优先确认字体:没有中文字体时,中文可能变成方框或默认字体。
- 不要只靠截图还原:截图只能参考版式,真正可编辑要靠 PPTX 元素生成。
- 生成后要校验:至少打开确认文字、图片、版式是否正常。
- 复杂版式建议分步:先做标题区,再做内容区,最后加图形和图片。
命令和脚本都按语言加了语法高亮。
1. 查看工作区文件
pwd&&rg--files-g'*.png'-g'*.jpg'-g'*.pptx'-g'AGENTS.md'.2. 查看 PPT 生成 API 文档
cat"$CODEX_PRIMARY_RUNTIME_NODE_MODULES/@oai/artifact-tool/docs/presentations/api/API_DOCS.md"cat"$CODEX_PRIMARY_RUNTIME_NODE_MODULES/@oai/artifact-tool/docs/presentations/api/references/rich-text.spec.md"3. 检查中文字体
fc-list :lang=zh family|sort-u4. 查找 CJK / 黑体类字体
rg--files"$CODEX_PRIMARY_RUNTIME_ROOT"-g'*.ttf'-g'*.otf'-g'*.ttc'\|rg'CJK|[Ss]ans[Ss][Cc]|[Ss]ans[Hh]ans|[Aa]rial|[Mm]icrosoft|[Ww]qy'5. 查看运行时字体加载逻辑
ls"$CODEX_PRIMARY_RUNTIME_ROOT/dependencies"rg--files"$CODEX_PRIMARY_RUNTIME_ROOT"|rg-i'font|cjk'|tail-35sed-n'1,160p'/root/.codex/skills/builtins/presentations/container_tools/runtime_helpers.mjs6. 检查字体相关代码
rg-n'font|Font'/root/.codex/skills/builtins/presentations/container_tools/artifact_tool_utils.mjs|head-45ls/usr/share/fonts rg--files/usr/share/fonts|head-15rg--files"$CODEX_PRIMARY_RUNTIME_NODE_MODULES"|rg-i'CJK|NotoSansSC|\.ttc'|head-157. 查看构建校验结果
catbuild/validation.json8. 分析 artifact 工具内部逻辑
frompathlibimportPathimportos,re p=Path(os.environ['CODEX_PRIMARY_RUNTIME_NODE_MODULES'])/'@oai/artifact-tool/dist/artifact_tool.mjs's=p.read_text()fortermin['FONT','fontPaths','loadFonts','registerFont','fontDirectory','FONTCONFIG','GlobalFonts']:matches=list(re.finditer(term,s))print(term,len(matches))forminmatches[:3]:print(s[max(0,m.start()-130):m.end()+220])9. 查看核心生成函数
frompathlibimportPathimportos s=(Path(os.environ['CODEX_PRIMARY_RUNTIME_NODE_MODULES'])/'@oai/artifact-tool/dist/artifact_tool.mjs').read_text()i=s.find('function hqt()')print(s[i-1400:i+6500])10. 执行生成
"$CODEX_PRIMARY_RUNTIME_NODE"build/create.mjs>build/run.log2>&1tail-c1300build/run.log11. 校验字体文件
fromfontTools.ttLibimportTTFont f=TTFont('fonts/DroidSansFallback.ttf')print(f['OS/2'].__dict__)print(f['hhea'].__dict__)12. 查看最终日志
tail-n3build/run.log|cut-c1-20013. 应用补丁并输出文件
{"path":"/connector_openai_library/implicit_link::connector_openai_library/create_library_file","args":{"file":"/workspace/scratch/0b5208d9034e/output/入职感悟与成长体会_可编辑.pptx","library_artifact_type":"slides"}}14. 写入扩展属性
python3 /workspace/scratch/0b5208d9034e/build/library_file_transfer.py apply-xattrs\'/workspace/scratch/0b5208d9034e/output/入职感悟与成长体会_可编辑.pptx'\'libfile_9d7bf7ba58e48191907930c0f4174794'<<'JSON' [{"name":"user.library-file-version","value":"0"}] JSON