PPT 模板创建器

此技能创建技能,而非演示文稿。 当用户想要将其 PowerPoint 模板转换为可重用的技能以便日后生成演示文稿时使用此技能。如果用户只是想创建演示文稿,请使用 pptx 技能。

生成的技能包括:

关于通用技能构建最佳实践,请参考 skill-creator 技能。此技能专注于 PPT 特定模式。

工作流程

  1. 用户提供模板(.pptx 或 .potx)
  2. 分析模板 - 提取布局、占位符、尺寸
  3. 初始化技能 - 使用 skill-creator 技能设置技能结构
  4. 添加模板 - 将 .pptx 复制到 assets/template.pptx
  5. 编写 SKILL.md - 遵循下面的模板,填写 PPT 特定细节
  6. 创建示例 - 生成示例演示文稿以验证
  7. 打包 - 使用 skill-creator 技能打包为 .skill 文件

步骤 2:分析模板

关键:提取精确的占位符位置 - 这决定了内容区域边界。

from pptx import Presentation

prs = Presentation(template_path)
print(f"尺寸:{prs.slide_width/914400:.2f}\" x {prs.slide_height/914400:.2f}\"")
print(f"布局数:{len(prs.slide_layouts)}")

for idx, layout in enumerate(prs.slide_layouts):
    print(f"\n[{idx}] {layout.name}:")
    for ph in layout.placeholders:
        try:
            ph_idx = ph.placeholder_format.idx
            ph_type = ph.placeholder_format.type
            # 重要:提取精确位置(英寸)
            left = ph.left / 914400
            top = ph.top / 914400
            width = ph.width / 914400
            height = ph.height / 914400
            print(f"    idx={ph_idx}, type={ph_type}")
            print(f"        x={left:.2f}\", y={top:.2f}\", w={width:.2f}\", h={height:.2f}\"")
        except:
            pass

要记录的关键测量值:

找到真正的内容起始位置

关键: 内容区域并不总是在副标题占位符之后立即开始。许多模板在副标题和内容区域之间有视觉边框、线条或保留空间。

最佳方法: 查看布局 2 或类似的"内容"布局,这些布局有 OBJECT 占位符 — 这个占位符的 y 位置指示内容应该实际从哪里开始。

# 找到 OBJECT 占位符以确定真正的内容起始位置
for idx, layout in enumerate(prs.slide_layouts):
    for ph in layout.placeholders:
        try:
            if ph.placeholder_format.type == 7:  # OBJECT 类型
                top = ph.top / 914400
                print(f"布局 [{idx}] {layout.name}:OBJECT 从 y={top:.2f}\" 开始")
                # 这个 y 值就是您的内容应该开始的位置!
        except:
            pass

示例: 一个模板可能有:

使用 OBJECT 占位符的 y 位置作为内容起始点,而不是副标题的结束位置。

步骤 5:编写 SKILL.md

生成的技能应有此结构:

[公司]-ppt-template/
├── SKILL.md
└── assets/
    └── template.pptx

生成的 SKILL.md 模板

生成的 SKILL.md 必须是自包含的,包含所有嵌入的说明。使用此模板,从您的分析中填写括号内的值:

---
name: [公司]-ppt-template
description: [公司] PowerPoint 模板,用于创建演示文稿。当创建[公司]品牌的融资演示稿、董事会材料或客户演示文稿时使用。
---

# [公司] PPT 模板

模板:`assets/template.pptx`[]\" x []\",[N] 个布局)

## 创建演示文稿

```python
from pptx import Presentation

prs = Presentation("path/to/skill/assets/template.pptx")

# 先删除所有现有幻灯片
while len(prs.slides) > 0:
    rId = prs.slides._sldIdLst[0].rId
    prs.part.drop_rel(rId)
    del prs.slides._sldIdLst[0]

# 从布局添加幻灯片
slide = prs.slides.add_slide(prs.slide_layouts[LAYOUT_IDX])
```

## 关键布局

| 索引 | 名称 | 用途 |
|------|------|------|
| [0] | [布局名称] | [封面/标题幻灯片] |
| [N] | [布局名称] | [带要点的内容] |
| [N] | [布局名称] | [双列布局] |

## 占位符映射

**关键:为每个占位符包含精确位置(x、y 坐标)。**

### 布局 [N]:[名称]
| idx | 类型 | 位置 | 用途 |
|-----|------|------|------|
| [idx] | 标题(1)| y=[Y]" | 幻灯片标题 |
| [idx] | 正文(2)| y=[Y]" | 副标题/描述 |
| [idx] | 正文(2)| y=[Y]" | 页脚 |
| [idx] | 正文(2)| y=[Y]" | 来源/注释 |

### 内容区域边界

**记录自定义形状/表格/图表的安全内容区域:**

```
内容区域(布局 [N]):
- 左边距:[X]"(内容从这里开始)
- 顶部:[Y]"(副标题占位符下方)
- 宽度:[W]"
- 高度:[H]"(在页脚之前结束)

对于四象限布局:
- 左列:x=[X]",宽度=[W]"
- 右列:x=[X]",宽度=[W]"
- 顶行:y=[Y]",高度=[H]"
- 底行:y=[Y]",高度=[H]"
```

**为什么这很重要:** 自定义内容(文本框、表格、图表)必须保持在这些边界内,以避免与模板占位符(如标题、页脚和来源行)重叠。

## 填充内容

**不要手动添加项目符号字符** — 幻灯片母版处理格式。

```python
# 填充标题
for shape in slide.shapes:
    if hasattr(shape, 'placeholder_format'):
        if shape.placeholder_format.type == 1:  # 标题
            shape.text = "幻灯片标题"

# 带层级填充内容(级别 0 = 标题,级别 1 = 项目符号)
for shape in slide.shapes:
    if hasattr(shape, 'placeholder_format'):
        idx = shape.placeholder_format.idx
        if idx == [CONTENT_IDX]:
            tf = shape.text_frame
            for para in tf.paragraphs:
                para.clear()

            content = [
                ("部分标题", 0),
                ("第一个项目符号", 1),
                ("第二个项目符号", 1),
            ]

            tf.paragraphs[0].text = content[0][0]
            tf.paragraphs[0].level = content[0][1]
            for text, level in content[1:]:
                p = tf.add_paragraph()
                p.text = text
                p.level = level
```

## 示例:封面幻灯片

```python
slide = prs.slides.add_slide(prs.slide_layouts[[COVER_IDX]])
for shape in slide.shapes:
    if hasattr(shape, 'placeholder_format'):
        idx = shape.placeholder_format.idx
        if idx == [TITLE_IDX]:
            shape.text = "公司名称"
        elif idx == [SUBTITLE_IDX]:
            shape.text = "演示文稿标题 | 日期"
```

## 示例:内容幻灯片

```python
slide = prs.slides.add_slide(prs.slide_layouts[[CONTENT_IDX]])
for shape in slide.shapes:
    if hasattr(shape, 'placeholder_format'):
        ph_type = shape.placeholder_format.type
        idx = shape.placeholder_format.idx
        if ph_type == 1:
            shape.text = "执行摘要"
        elif idx == [BODY_IDX]:
            tf = shape.text_frame
            for para in tf.paragraphs:
                para.clear()
            content = [
                ("关键发现", 0),
                ("收入同比增长 40% 达到 $5000 万", 1),
                ("扩展到 3 个新市场", 1),
                ("建议", 0),
                ("继续推进战略举措", 1),
            ]
            tf.paragraphs[0].text = content[0][0]
            tf.paragraphs[0].level = content[0][1]
            for text, level in content[1:]:
                p = tf.add_paragraph()
                p.text = text
                p.level = level
```

步骤 6:创建示例输出

生成示例演示文稿以验证技能有效。将其与技能一起保存以供参考。

生成技能的 PPT 特定规则

  1. 模板放在 assets/ — 始终打包 .pptx 文件
  2. 自包含的 SKILL.md — 所有说明嵌入,无外部引用
  3. 无手动项目符号 — 使用 paragraph.level 实现层级
  4. 先删除幻灯片 — 在添加新幻灯片之前始终清除现有幻灯片
  5. 按 idx 记录占位符 — 占位符 idx 值是特定于模板的