wechat-robot-skills/skills/pptx/SKILL.md

433 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: pptx
description: "创建、读取、编辑、复制页面、转换、校验和渲染本地或远程 HTTPS PowerPoint 演示文稿与模板,并下载 PPT 任务所需且不超过 25 MiB 的图片、音视频、压缩包和其他 HTTPS 附件,按需识别页面图片、截图和图表中的文字。用户提到 PPT、PPTX、PowerPoint、演示文稿、幻灯片、路演稿、汇报材料、演讲者备注、模板、版式或图片文字 OCR,或提供 HTTPS 演示文稿地址、.pptx、.potx、.ppsx、.ppt 文件时使用;支持源演示文稿安全下载、结构化创建、保留 Run 格式的文本替换、页面删除/重排/复制、Markdown 提取、本地 RapidOCR、旧格式转换、受控 OOXML 解包/打包、关系与图表校验及逐页视觉检查。若主要交付物不是演示文稿且不需要读取或修改 PPT 内容,则不要使用。"
---
# PowerPoint 演示文稿处理
## 强制执行规则
当前智能体不能直接执行 shell、任意 Python、Node.js 或系统命令。只能通过 `execute_skill_script` 调用本 Skill 中真实存在的固定 Python 脚本。
- 只调用下表列出的可执行脚本;不执行 `scripts/` 目录、`scripts/_pptx_common.py`、`scripts/_presentation_builder.js` 或 `scripts/_icon_renderer.js`。
- 不把 `python3`、`node`、`soffice`、`libreoffice`、`pdftoppm`、`zip`、`unzip`、`rm` 或其他系统命令作为脚本参数。
- PptxGenJS、React Icons、Sharp、LibreOffice、Poppler 和 ZIP 操作只允许由固定脚本在内部调用。
- 每次检查脚本返回的 JSON;只有 `ok` 为 `true` 时才继续。`validate_presentation.py` 还必须返回 `status: valid`、`issue_count: 0`。
- 只在需要读取图片、截图或视觉图表中的文字时调用 `ocr_presentation.py`。只使用 `slides[]` 中 `usable_for_summary: true` 的 `text`;低置信度结果不得作为可靠正文。
- 不覆盖用户提供的源文件。PPT 最终文件一律写入 `/usr/local/src/ppt/`,下载缓存、中间文件和渲染结果一律写入 `/usr/local/src/ppt/tmp/<任务名>/`。始终传绝对路径;固定脚本会自动创建目录并拒绝该根目录之外的输出。
- 远程 PowerPoint 源文件只交给 `download_presentation.py`;任务所需的远程图片、视频、音频、压缩包或其他附件只交给 `download_attachment.py`。不要在回复、日志摘要或文件名中复述可能含敏感查询参数的完整 URL。
- 环境已预置全部依赖,不安装软件包,也不提示用户安装依赖。
## 脚本清单
| 脚本 | 用途 | 底层能力 |
| --- | --- | --- |
| `scripts/download_presentation.py` | 下载并校验远程 HTTPS 演示文稿 | Python `urllib`、安全 OOXML 解析、`python-pptx` |
| `scripts/download_attachment.py` | 下载图片、音视频、压缩包等通用 HTTPS 附件 | Python `urllib`、HEAD 大小探测、流式硬限制 |
| `scripts/inspect_presentation.py` | 分段读取页面、文本、表格、图表、图片和备注 | `python-pptx`、安全 OOXML 解析 |
| `scripts/ocr_presentation.py` | 按页识别图片、截图和视觉图表中的文字 | RapidOCR、ONNX Runtime、LibreOffice、Poppler |
| `scripts/extract_presentation.py` | 提取整份演示文稿为 Markdown | `markitdown[pptx]` |
| `scripts/create_presentation.py` | 按受控 JSON 创建专业 PPTX | PptxGenJS |
| `scripts/edit_presentation.py` | 文本替换、删除/重排页面、修改属性 | `python-pptx` |
| `scripts/duplicate_slide.py` | 复制现有 PPTX 页面并维护包关系 | `lxml`、Python `zipfile` |
| `scripts/render_icon.py` | 把允许的 React Icons 图标渲染为 PNG | React、React DOM、React Icons、Sharp |
| `scripts/convert_presentation.py` | `.ppt/.potx/.ppsx/.pptx` 转 PPTX 或 PDF | LibreOffice |
| `scripts/unpack_presentation.py` | 安全解包 OOXML 供高级编辑 | Python `zipfile` |
| `scripts/pack_presentation.py` | 把 OOXML 目录安全打包为演示文稿 | Python `zipfile`、`python-pptx` |
| `scripts/validate_presentation.py` | 校验 ZIP、XML、关系、页面、图表、边界和可渲染性 | `defusedxml`、`lxml`、`python-pptx`、LibreOffice |
| `scripts/render_presentation.py` | 把演示文稿渲染为逐页 PNG、联系表和 PDF | LibreOffice、Poppler、Pillow |
## 标准流程
1. 输入是 HTTPS 地址时,先调用 `download_presentation.py` 下载到本次任务临时目录;本地文件直接进入下一步。
2. 旧版 `.ppt` 先调用 `convert_presentation.py` 转为 `.pptx`。需要把 `.potx/.ppsx` 当普通演示文稿编辑时,也先转为 `.pptx`。
3. 编辑、总结或套用模板前先调用 `inspect_presentation.py`;内容较多时再调用 `extract_presentation.py` 获取完整 Markdown。
4. 需要读取截图、扫描页或图片中的文字时,从 `image_slides` 选择相关页调用 `ocr_presentation.py`。不要默认 OCR 全部页面,也不要用 OCR 覆盖可靠的原生文本。
5. 从零创建使用 `create_presentation.py`;常规文本与页面编辑使用 `edit_presentation.py`;复用模板页面使用 `duplicate_slide.py`。
6. 只有固定脚本不能完成的精细模板编辑,才使用 `unpack_presentation.py` → 最小化编辑 OOXML → `pack_presentation.py`。
7. 创建或修改后必须调用 `validate_presentation.py --check-render`。基于模板制作时同时传 `--original <原模板>`。
8. 再调用 `render_presentation.py --contact-sheet` 渲染全部页面;若 `has_more: true`,用 `next_slide` 继续,直到检查完整份演示文稿。
9. 逐页检查内容、顺序、溢出、重叠、间距、对齐、对比度、占位文本和备注。发现问题后修复并重新校验、重新渲染受影响页面。
10. 文件结构、内容和视觉检查全部通过后才交付。
## 下载远程演示文稿
只接受 HTTPS 地址。完整保留 URL 及查询参数传给脚本,但不要在回复或输出文件名中暴露查询参数。
```text
--url 'https://example.com/deck.pptx?signature=...' --output '/usr/local/src/ppt/tmp/<任务名>/source.pptx'
```
可选参数:
- `--timeout <1-600>`:连接和读取超时秒数,默认 `60`。
- `--max-bytes <字节数>`:默认且最高 25 MiB(26214400 字节),只允许设置更小的限制。
- `--overwrite`:只覆盖本次任务生成的旧缓存。
`output` 扩展名必须与远程内容的真实格式一致,支持 `.pptx/.potx/.ppsx/.ppt`。脚本限制重定向只能继续使用 HTTPS,流式限制大小,先写临时文件,再原子发布。
## 下载通用附件
需要下载作为 PowerPoint 任务素材的图片、视频、音频、压缩包或其他文件时,调用 `scripts/download_attachment.py`:
```text
--url 'https://example.com/asset.bin?signature=...' --output '/usr/local/src/ppt/tmp/<任务名>/asset.bin'
```
只接受 HTTPS 地址,`output` 可使用任意附件扩展名。可选参数只有 `--timeout <1-600>`(默认 `60`)和 `--overwrite`。附件上限固定为 25 MiB(26214400 字节),不可调高:脚本先用 HEAD 探测远端声明大小,再检查 GET 响应声明,并在流式接收时持续兜底计数;任一阶段发现超限都会返回 `ok: false` 和明确的“已拒绝下载”错误,且不会发布部分文件。
成功结果包含 `path`、实际 `size_bytes`、`declared_size_bytes`、`size_limit_bytes`、`size_probe` 和 `content_type`。本脚本不校验文件业务格式;远程 PowerPoint 源文件仍使用 `download_presentation.py`。
## 检查和提取内容
调用结构检查:
```text
--input 'source.pptx' --start-slide 1 --max-slides 30
```
常用参数:
- `--include-runs`:需要检查局部字体、粗体、字号或跨 Run 文本替换时使用。
- `--max-shapes <1-1000>`、`--max-table-cells <1-10000>`:限制单次结构输出。
- `--max-chars <1000-1000000>`:限制 JSON 中返回的正文量。
重点检查 `slide_size`、`slides[].layout`、形状边界、表格、图表系列、图片类型、`slides[].media`、`image_slides`、`speaker_notes`、批注摘要和 `external_relationships`。`media.image_count` 表示页面中的图片数量,`media.image_area_ratio` 是图片大致占页比例,`media.native_text_char_count` 是可直接提取的原生文字量。长演示文稿根据 `selection.has_more` 和 `next_slide` 分段读取。
需要连续正文时调用:
```text
--input 'source.pptx' --output '/usr/local/src/ppt/tmp/<任务名>/content.md'
```
Markdown 适合检查遗漏、错字和顺序,不代表页面版式。
## 识别图片中的文字
只有图片、截图、扫描页或视觉图表中的文字对任务有意义时才调用:
```text
--input 'source.pptx' --slides '2,5-6'
```
`slides` 必须明确指定页码,单次最多 4 页。脚本先通过 LibreOffice 和 Poppler 临时渲染选定页面,再使用本地 RapidOCR 识别;渲染图片会自动删除,不联网,也不调用大模型识图。
脚本会根据原生文本框的位置和文字相似度过滤重复内容,因此 `slides[].text` 只返回原生文本之外的可靠图片文字。检查:
- `status: good` 且 `usable_for_summary: true`:可以把 `text` 补充到同页原生文本中。
- `status: no_image_text`:未发现额外图片文字,不是错误。
- `status: sparse` 或 `low_confidence`:不要使用返回文字;根据 `needs_review` 人工核验。
- `filtered_native_line_count`:被识别为原生文本并去重的 OCR 行数。
- `picture_count`、`chart_count` 和 `image_area_ratio`:用于理解本页视觉内容规模。
默认 260 DPI。小字可使用 `--dpi 300-400`;单页预计像素过大时降低 DPI。可用 `--max-chars` 控制输出;若 `has_more: true`,根据 `next_slide`、`next_offset` 和 `remaining_slides` 继续。只有 `next_offset > 0` 时才传 `--start-offset`,且此时 `slides` 只能包含该页。
## 创建演示文稿
调用:
```text
--output '/usr/local/src/ppt/result.pptx' --spec '<JSON对象>'
```
内容较长时先把 JSON 写入任务临时目录,再传 `--spec-file`。目标是本次任务旧产物且确认可覆盖时才传 `--overwrite`。
顶层结构:
```json
{
"layout": "LAYOUT_WIDE",
"properties": {
"title": "2026 年产品路线图",
"author": "示例公司",
"subject": "产品规划"
},
"theme": {
"head_font": "Noto Sans CJK SC",
"body_font": "Noto Sans CJK SC",
"language": "zh-CN"
},
"slides": []
}
```
支持布局:
| `layout` | 画布尺寸 |
| --- | --- |
| `LAYOUT_WIDE` | 13.333 × 7.5 英寸 |
| `LAYOUT_16X9` | 10 × 5.625 英寸 |
| `LAYOUT_4X3` | 10 × 7.5 英寸 |
每页使用:
```json
{
"background": "0F172A",
"speaker_notes": "本页讲解约 45 秒。",
"elements": []
}
```
坐标和尺寸 `x/y/w/h` 均使用英寸。颜色必须是不带 `#` 的 6 位十六进制值;不要把透明度拼入 8 位颜色,透明度使用 `transparency: 0-100`。所有可见元素必须位于画布内。
### 文本
```json
{
"type": "text",
"text": "从洞察到增长",
"options": {
"x": 0.7,
"y": 0.6,
"w": 8.8,
"h": 0.7,
"fontFace": "Noto Sans CJK SC",
"fontSize": 30,
"bold": true,
"color": "F8FAFC",
"margin": 0,
"breakLine": false
}
}
```
局部格式使用 `runs`:
```json
{
"type": "text",
"runs": [
{"text": "收入 ", "options": {"bold": true}},
{"text": "+28%", "options": {"bold": true, "color": "22C55E"}}
],
"options": {
"x": 0.8,
"y": 2.0,
"w": 4.0,
"h": 0.6,
"fontSize": 24,
"margin": 0
}
}
```
列表不要输入字面量 `•`。每个列表项使用独立 Run,并设置 `bullet: true`;除最后一项外设置 `breakLine: true`,项目间距用 `paraSpaceAfter`。
### 形状、图片和图标
形状:
```json
{
"type": "shape",
"shape": "roundRect",
"options": {
"x": 0.8,
"y": 1.7,
"w": 3.6,
"h": 2.2,
"fill": {"color": "E0F2FE"},
"line": {"color": "BAE6FD", "width": 1},
"shadow": {"type": "outer", "color": "0F172A", "opacity": 0.15, "blur": 2, "angle": 45, "distance": 1}
}
}
```
常用形状名包括 `rect`、`roundRect`、`ellipse`、`line`、`chevron`、`triangle` 和 `hexagon`。阴影 `offset/distance` 不得为负数;向上投影时改变角度。
图片:
```json
{
"type": "image",
"path": "/absolute/path/chart.png",
"options": {"x": 7.2, "y": 1.4, "w": 5.2, "h": 4.8}
}
```
需要图标时先调用:
```text
--library fi --name FiTrendingUp --color 2563EB --size 256 --output '/usr/local/src/ppt/tmp/<任务名>/trend.png'
```
允许的图标库:`fa6`、`fi`、`hi2`、`io5`、`lu`、`md`、`ri`、`tb`。把生成的 PNG 作为普通图片元素插入。
### 图表
```json
{
"type": "chart",
"chart_type": "bar",
"data": [
{
"name": "收入",
"labels": ["Q1", "Q2", "Q3", "Q4"],
"values": [120, 148, 176, 215]
}
],
"options": {
"x": 0.8,
"y": 1.6,
"w": 6.0,
"h": 4.7,
"catAxisLabelFontSize": 12,
"valAxisLabelFontSize": 11,
"showLegend": false,
"showValue": true,
"dataLabelPosition": "outEnd",
"chartColors": ["2563EB"]
}
}
```
支持 `area/bar/bar3d/bubble/bubble3d/doughnut/line/pie/radar/scatter`。PowerPoint 原生支持的图表必须保留为可编辑图表;只有桑基图、网络图等没有对应原生类型的可视化才使用图片。堆积条形图或柱形图的数据标签只能使用 `ctr/inEnd/inBase`,不能使用 `outEnd`。
### 表格
```json
{
"type": "table",
"rows": [
[
{"text": "指标", "options": {"bold": true, "color": "FFFFFF", "fill": "1E3A8A"}},
{"text": "本期", "options": {"bold": true, "color": "FFFFFF", "fill": "1E3A8A"}}
],
["收入", "2,150 万元"],
["增长率", "28.0%"]
],
"options": {
"x": 0.8,
"y": 1.8,
"w": 6.2,
"h": 2.2,
"border": {"type": "solid", "color": "CBD5E1", "pt": 1},
"fontFace": "Noto Sans CJK SC",
"fontSize": 14,
"margin": 0.08
}
}
```
## 编辑现有演示文稿
调用:
```text
--input 'source.pptx' --output '/usr/local/src/ppt/edited.pptx' --spec '<JSON对象>'
```
JSON 顶层只有 `operations`,按数组顺序执行:
| `operations[].type` | 关键字段 |
| --- | --- |
| `replace_text` | `find`、`replace`;可选 `slides/match_case/whole_word/count/required/include_notes` |
| `set_properties` | `properties`;其中 `revision` 必须是正整数 |
| `delete_slides` | `slides[]`,页码从 1 开始 |
| `reorder_slides` | `order[]`,必须完整且不重复地列出当前全部页码 |
示例:
```json
{
"operations": [
{
"type": "replace_text",
"find": "2025 年",
"replace": "2026 年",
"match_case": true,
"required": true
},
{
"type": "reorder_slides",
"order": [1, 3, 2, 4]
}
]
}
```
文本替换会处理同一段落内跨多个 Run 的匹配,并尽量保留替换起点和结尾的格式。输入含批注、ActiveX、宏或嵌入对象时,常规编辑脚本会停止,改用受控 OOXML 流程,避免静默丢失内容。输入含外部链接时默认停止;只有用户明确接受风险后才传 `--allow-external-links`。
## 复制模板页面
只对 `.pptx` 使用:
```text
--input 'template.pptx' --output '/usr/local/src/ppt/tmp/<任务名>/expanded.pptx' --slide 2 --after 4
```
`slide` 是复制来源,`after` 是插入位置;省略 `after` 时紧跟来源页插入。脚本会更新页面清单、关系和内容类型,并移除不能安全共享的备注/批注关系。
复制页仍可能与原页共享图表、SmartArt 或嵌入对象部件。若返回的 `shared_relationships` 非空,修改这些对象前先做 OOXML 级独立复制;否则改动一页可能同时影响另一页。
## 高级 OOXML 编辑
固定编辑脚本无法表达且确实需要精细模板操作时:
1. 调用 `unpack_presentation.py --input <pptx> --output-dir <空目录>`。
2. 先完成页面复制、删除和重排,再修改页面内容。
3. 只最小化编辑相关 XML;不要重排、格式化或重写无关部件。
4. 每个列表项保留独立的 `<a:p>`;保留相邻 `<a:pPr>` 以继承缩进与间距;不要在文本中写字面量项目符号。
5. 有前后空格的 `<a:t>` 设置 `xml:space="preserve"`。
6. 调用 `pack_presentation.py --input-dir <目录> --output <新pptx>`。
7. 立即调用 `validate_presentation.py --original <源pptx> --check-render`。
不得手工复制单个 `slideN.xml`。新页面必须同时登记到 `ppt/presentation.xml`、`ppt/_rels/presentation.xml.rels` 和 `[Content_Types].xml`,并处理页面关系。
## 设计和排版要求
- 先确定与主题相关的配色和一个贯穿全稿的视觉母题。一个主色承担约三分之二视觉权重,搭配 1–2 个辅助色和一个强调色。
- 封面、章节页和结尾页可以使用深色背景,内容页使用浅色背景;同一套演示文稿保持一致。
- 每页至少包含一种有信息作用的视觉元素:图片、图表、图标、流程、时间线或重点数字。不要只放标题和大段项目符号。
- 交替使用双栏、卡片网格、半幅图片、对比栏和流程布局;不要连续复用同一种版式。
- 中文正文优先使用 `Noto Sans CJK SC`,拉丁正文优先使用 Arial 或 Calibri。字体会在最终用户的 PowerPoint 中渲染,LibreOffice 预览可能发生替换;非预置字体至少保留约 10% 宽度余量。
- 标题通常为 32–44 pt,分区标题 20–26 pt,正文 14–18 pt,注释 10–12 pt。正文左对齐;只对短标题或数字做居中。
- 页面边缘至少留 0.5 英寸,内容块间距至少 0.3 英寸。同类元素使用一致的栅格、间距和对齐。
- 文本框需要与图形边缘精确对齐时设置 `margin: 0`;字符间距使用 `charSpacing`,不要使用无效的 `letterSpacing`。
- 不用标题下划线、整页装饰色条或卡片单侧色边充当“设计感”。优先使用留白、轻微底色、阴影、图片裁切和图标层次。
- 不默认使用与主题无关的蓝色或米黄色;不交付低对比、越界、截断或互相重叠的元素。
- 演讲者备注只写入 `speaker_notes`,不要伪装成页面上的隐藏文本。
## 校验与视觉检查
结构校验:
```text
--input '/usr/local/src/ppt/result.pptx' --check-render
```
模板派生结果:
```text
--input '/usr/local/src/ppt/result.pptx' --original 'template.pptx' --check-render
```
必须满足:
- `status: valid`
- `issue_count: 0`
- `archive.missing_required_parts` 和 `duplicate_members` 为空
- `render.pdf_pages` 与 `slide_count` 一致
警告也必须逐项评估,特别是 `placeholder_text`、空页面、孤立页面部件和外部关系。
渲染全部页面:
```text
--input '/usr/local/src/ppt/result.pptx' --output-dir '/usr/local/src/ppt/tmp/<任务名>/rendered' --contact-sheet --include-pdf
```
默认 150 DPI、单次最多 30 页。可用 `--start-slide/--end-slide/--max-slides` 分批,复杂图表或小字可把 `--dpi` 提高到 180–220。联系表用于快速检查整体节奏,逐页 PNG 用于最终 QA。
逐页检查:
- 文本是否被截断、溢出或因字体替换异常换行。
- 图形、文字、页脚和来源是否重叠。
- 页面边距、列宽、卡片尺寸、基线和间距是否一致。
- 图标与文字是否有足够对比度,图表标签是否可读。
- 模板占位内容、示例数据和多余装饰是否全部清除。
- 页面顺序、标题层级、图表数值、演讲者备注和来源是否正确。
第一次渲染发现问题是正常的;修复后必须重新运行结构校验,并重新生成受影响页面的预览。