1
0
lanyuanxiaoyao 5b367f7ef3 fix: 修复多行文本渲染时字号只应用于第一段的bug
当使用 YAML 多行字符串语法定义文本内容时,python-pptx 会自动
将包含换行符的文本分割成多个段落。修改 _render_text 方法使其
遍历所有段落并应用相同的字体样式(大小、粗体、斜体、颜色、对齐)。

主要变更:
- renderers/pptx_renderer.py: 将 p = tf.paragraphs[0] 改为 for p in tf.paragraphs
- tests/unit/test_renderers/test_pptx_renderer.py: 新增多行文本测试用例
- openspec/specs/element-rendering/spec.md: 更新 spec 文档
- openspec/changes/archive/: 归档完成的变更
2026-03-04 12:18:23 +08:00
2026-03-04 10:31:26 +08:00
2026-03-04 10:29:21 +08:00
2026-03-04 10:29:21 +08:00
2026-03-04 10:29:21 +08:00

yaml2pptx

使用 YAML 声明式语法创建 PowerPoint 演示文稿的工具。

功能特性

  • 📝 YAML 声明式语法 - 使用简单易读的 YAML 定义演示文稿
  • 智能验证 - 转换前自动检查 YAML 文件,提前发现问题
  • 🎨 模板系统 - 支持参数化模板,复用幻灯片布局
  • 🧩 丰富的元素类型 - 文本、图片、形状、表格
  • 👁️ 实时预览 - 浏览器预览模式,支持热重载
  • 📐 灵活尺寸 - 支持 16:9 和 4:3 两种宽高比
  • 🔧 模块化架构 - 易于扩展和维护

🚀 快速开始

安装

本工具使用 uv 管理依赖。项目依赖在 pyproject.toml 中声明,运行时会自动安装所需的 Python 包。

基本用法

# 转换 YAML 为 PPTX
uv run yaml2pptx.py convert presentation.yaml output.pptx

# 自动生成输出文件名
uv run yaml2pptx.py convert presentation.yaml

# 使用模板
uv run yaml2pptx.py convert presentation.yaml output.pptx --template-dir ./templates

实时预览

# 启动预览服务器(自动打开浏览器)
uv run yaml2pptx.py preview presentation.yaml

# 指定端口
uv run yaml2pptx.py preview presentation.yaml --port 8080

# 允许局域网访问
uv run yaml2pptx.py preview presentation.yaml --host 0.0.0.0

# 不自动打开浏览器
uv run yaml2pptx.py preview presentation.yaml --no-browser

预览模式会自动监听文件变化,修改 YAML 文件后浏览器会自动刷新。

验证功能

在转换前验证 YAML 文件,提前发现问题:

# 独立验证命令
uv run yaml2pptx.py check presentation.yaml

# 使用模板时验证
uv run yaml2pptx.py check presentation.yaml --template-dir ./templates

验证功能会检查:

  • YAML 语法和结构
  • 元素是否超出页面范围
  • 图片和模板文件是否存在
  • 颜色格式是否正确
  • 字体大小是否合理
  • 表格数据是否一致

自动验证:转换时默认会自动验证,如果发现错误会终止转换。可以使用 --skip-validation 跳过验证:

# 跳过自动验证
uv run yaml2pptx.py convert presentation.yaml --skip-validation

验证结果示例

🔍 正在检查 YAML 文件...

❌ 错误 (2):
  [幻灯片 2, 元素 1] 无效的颜色格式: red (应为 #RRGGBB)
  [幻灯片 3, 元素 2] 图片文件不存在: logo.png

⚠️  警告 (1):
  [幻灯片 1, 元素 1] 元素右边界超出: 10.50 > 10

检查完成: 发现 2 个错误, 1 个警告
  • ERROR:阻止转换的严重问题(文件不存在、语法错误等)
  • WARNING:影响视觉效果的问题(元素超出页面、字体太小等)
  • INFO:优化建议

📖 YAML 语法

最小示例

metadata:
  size: "16:9"  # 或 "4:3"
  dpi: 96       # 可选DPI 配置,默认 96

slides:
  - background:
      color: "#ffffff"
    elements:
      - type: text
        box: [1, 1, 8, 1]
        content: "Hello, World!"
        font:
          size: 44
          bold: true
          color: "#333333"
          align: center

DPI 配置

metadata.dpi 参数控制图片处理的分辨率,影响图片质量和文件大小:

  • 默认值96 DPI标准屏幕分辨率
  • 建议范围72-300 DPI
  • 常用值
    • 72 DPI网页显示
    • 96 DPI标准屏幕默认
    • 150 DPI高质量打印
    • 300 DPI专业印刷
metadata:
  size: "16:9"
  dpi: 150  # 高质量输出

使用模板

metadata:
  size: "16:9"

slides:
  - template: title-slide
    vars:
      title: "我的演示文稿"
      subtitle: "使用 yaml2pptx 创建"
      author: "张三"

  - template: content-slide
    vars:
      title: "功能概览"
      content: "yaml2pptx 支持多种元素类型..."

🎨 元素类型

文本元素

- type: text
  box: [x, y, width, height]  # 位置和尺寸(英寸)
  content: "文本内容"
  font:
    size: 18              # 字号(磅)
    bold: true            # 粗体
    italic: false         # 斜体
    color: "#ff0000"      # 颜色
    align: center         # left/center/right

特性:文本框默认启用自动换行,文字超出宽度时会自动换行。

图片元素

- type: image
  box: [x, y, width, height]
  src: "path/to/image.png"  # 支持相对路径和绝对路径
  fit: contain              # 可选:图片适配模式
  background: "#f0f0f0"     # 可选:背景色(仅对 contain 和 center 模式有效)

图片适配模式

fit 参数控制图片如何适配到指定的 box 区域,支持以下模式:

  • stretch(默认):拉伸图片以填满整个 box可能改变宽高比
  • contain:保持宽高比,完整显示图片在 box 内,可能留白
  • cover:保持宽高比,填满整个 box可能裁剪图片
  • center:不缩放,居中显示,超出部分裁剪

示例

slides:
  - elements:
      # 默认 stretch 模式
      - type: image
        src: "photo.jpg"
        box: [1, 1, 4, 3]

      # contain 模式,带灰色背景
      - type: image
        src: "photo.jpg"
        box: [5, 1, 4, 3]
        fit: contain
        background: "#f0f0f0"

      # cover 模式,填满区域
      - type: image
        src: "photo.jpg"
        box: [1, 4, 4, 3]
        fit: cover

      # center 模式,不缩放
      - type: image
        src: "icon.png"
        box: [5, 4, 2, 2]
        fit: center
        background: "#ffffff"

背景色

background 参数为图片添加背景色,支持 #RRGGBB#RGB 格式:

  • 仅对 containcenter 模式有效
  • 当图片小于 box 或保持宽高比时,背景色填充留白区域
  • 默认为透明(无背景色)
- type: image
  src: "logo.png"
  box: [1, 1, 3, 2]
  fit: contain
  background: "#ffffff"  # 白色背景

形状元素

- type: shape
  box: [x, y, width, height]
  shape: rectangle  # rectangle/ellipse/rounded_rectangle
  fill: "#4a90e2"   # 填充颜色
  line:
    color: "#000000"  # 边框颜色
    width: 2          # 边框宽度(磅)

表格元素

- type: table
  position: [x, y]
  col_widths: [2, 2, 2]  # 每列宽度(英寸)
  data:
    - ["表头1", "表头2", "表头3"]
    - ["数据1", "数据2", "数据3"]
    - ["数据4", "数据5", "数据6"]
  style:
    font_size: 14
    header_bg: "#4a90e2"
    header_color: "#ffffff"

📋 模板系统

模板允许你定义可复用的幻灯片布局。yaml2pptx 支持两种模板方式:

  • 外部模板:独立的 YAML 文件,适合跨文档复用
  • 内联模板:在源文件中定义,适合单文档使用

内联模板

内联模板允许你在 YAML 源文件中直接定义模板,无需创建单独的模板文件。

定义内联模板

在 YAML 文件顶层添加 templates 字段:

metadata:
  size: "16:9"

templates:
  title-slide:
    vars:
      - name: title
        required: true
      - name: subtitle
        required: false
        default: ""
    elements:
      - type: text
        box: [1, 2, 8, 1]
        content: "{title}"
        font:
          size: 44
          bold: true
          align: center
      - type: text
        box: [1, 3.5, 8, 0.5]
        content: "{subtitle}"
        visible: "{subtitle != ''}"
        font:
          size: 24
          align: center

slides:
  - template: title-slide
    vars:
      title: "我的演示文稿"
      subtitle: "使用内联模板"

内联模板特性

  • 支持变量替换和条件渲染
  • 可以与外部模板混合使用
  • 无需指定 --template-dir 参数
  • ⚠️ 内联模板不能相互引用
  • ⚠️ 内联和外部模板不能同名(会报错)

何时使用内联模板

适合使用内联模板

  • 模板仅在单个文档中使用
  • 快速原型开发
  • 简单的模板定义1-3 个元素)
  • 文档自包含,无需外部依赖

适合使用外部模板

  • 需要跨多个文档复用
  • 复杂的模板定义(>5 个元素)
  • 团队共享的模板库
  • 需要版本控制和独立维护

最佳实践

  1. 命名规范

    • 内联模板使用描述性名称(如 title-slide, content-slide
    • 避免与外部模板同名,否则会报错
    • 使用一致的命名风格kebab-case 推荐)
  2. 模板大小

    • 内联模板建议不超过 50 行
    • 超过 50 行考虑拆分或使用外部模板
    • 保持 YAML 文件可读性
  3. 混合使用

    • 可以在同一文档中混合使用内联和外部模板
    • 通用模板使用外部模板(如标题页、结束页)
    • 文档特定模板使用内联模板
  4. 迁移策略

    • 原型阶段使用内联模板快速迭代
    • 模板稳定后,如需复用则迁移到外部模板
    • 使用 --template-dir 参数指定外部模板目录

内联模板限制

  • ⚠️ 内联模板不能相互引用(会报错)
  • ⚠️ 内联和外部模板不能同名(会报错)
  • ⚠️ 内联模板不支持继承或组合

创建外部模板

创建模板文件 templates/title-slide.yaml

vars:
  - name: title
    required: true
  - name: subtitle
    required: false
    default: ""
  - name: author
    required: false
    default: ""

elements:
  - type: text
    box: [1, 2, 8, 1]
    content: "{title}"
    font:
      size: 44
      bold: true
      align: center

  - type: text
    box: [1, 3.5, 8, 0.5]
    content: "{subtitle}"
    visible: "{subtitle != ''}"  # 条件渲染
    font:
      size: 24
      align: center

  - type: text
    box: [1, 5, 8, 0.5]
    content: "{author}"
    font:
      size: 18
      align: center

使用外部模板

slides:
  - template: title-slide
    vars:
      title: "我的演示文稿"
      subtitle: "副标题"
      author: "作者"

条件渲染

元素级条件渲染

使用 visible 属性控制元素显示,支持强大的条件表达式:

基本示例

# 简单比较
- type: text
  content: "有数据"
  visible: "{count > 0}"

# 字符串比较
- type: text
  content: "草稿状态"
  visible: "{status == 'draft'}"

# 非空检查(向后兼容)
- type: text
  content: "{subtitle}"
  visible: "{subtitle != ''}"

支持的表达式类型

  1. 比较运算==, !=, >, <, >=, <=

    visible: "{score >= 60}"
    visible: "{price <= 100}"
    
  2. 逻辑运算and, or, not

    visible: "{count > 0 and status == 'active'}"
    visible: "{is_draft or is_preview}"
    visible: "{not (count == 0)}"
    
  3. 成员测试in, not in

    visible: "{status in ['draft', 'review', 'published']}"
    visible: "{level in (1, 2, 3)}"
    visible: "{'test' in version}"  # 字符串包含
    
  4. 数学运算+, -, *, /, %, **

    visible: "{(price * discount) > 50}"
    visible: "{(total / count) >= 10}"
    
  5. 内置函数int(), float(), str(), len(), bool(), abs(), min(), max()

    visible: "{len(items) > 0}"
    visible: "{int(value) > 100}"
    

复杂条件示例

# 范围检查
- type: text
  content: "评分: {score}"
  visible: "{score >= 60 and score <= 100}"

# 多条件组合
- type: text
  content: "管理员或高分用户"
  visible: "{is_admin or (score >= 90)}"

# 嵌套条件
- type: text
  content: "符合条件"
  visible: "{((count > 0) and (status == 'active')) or (is_admin and (level >= 3))}"

页面级启用控制

使用 enabled 参数控制整个幻灯片是否渲染:

slides:
  # 正常渲染的幻灯片
  - template: title-slide
    vars:
      title: "主标题"

  # 临时禁用的幻灯片(开发调试)
  - enabled: false
    template: work-in-progress
    vars:
      title: "未完成的内容"

  # 继续渲染后续幻灯片
  - template: content-slide
    vars:
      title: "内容页"

enabled 参数说明

  • 类型:布尔值(truefalse
  • 默认值:true(未指定时默认启用)
  • 用途:临时禁用幻灯片,无需删除或注释 YAML 内容
  • 场景开发调试、版本控制、A/B 测试

enabled vs visible 的区别

特性 enabled(页面级) visible(元素级)
作用范围 整个幻灯片 单个元素
类型 布尔值 条件表达式
判断时机 加载时(静态) 渲染时(动态)
使用场景 临时禁用页面 条件显示元素

示例

slides:
  # 页面启用,但副标题元素可能隐藏
  - enabled: true
    template: title-slide
    vars:
      title: "标题"
      subtitle: ""  # 空字符串,元素级 visible 会隐藏副标题

  # 整页禁用,不渲染
  - enabled: false
    elements:
      - type: text
        content: "这一页不会出现在最终 PPTX 中"
        box: [1, 1, 8, 1]
        font: {size: 44}

🎯 命令行选项

check 命令

验证 YAML 文件的正确性。

选项 说明
input 输入的 YAML 文件路径(必需)
--template-dir 模板文件目录

convert 命令

将 YAML 文件转换为 PPTX 文件。

选项 说明
input 输入的 YAML 文件路径(必需)
output 输出的 PPTX 文件路径(可选)
--template-dir 模板文件目录
--skip-validation 跳过自动验证
--force / -f 强制覆盖已存在文件

preview 命令

启动预览服务器,实时查看演示文稿效果。

选项 说明
input 输入的 YAML 文件路径(必需)
--template-dir 模板文件目录
--port 服务器端口(默认:随机端口 30000-40000
--host 主机地址默认127.0.0.1
--no-browser 不自动打开浏览器

📐 坐标系统

  • 单位:英寸 (inch)
  • 原点:幻灯片左上角 (0, 0)
  • 方向X 轴向右Y 轴向下

幻灯片尺寸

  • 16:9 → 10" × 5.625"
  • 4:3 → 10" × 7.5"

示例box: [1, 2, 8, 1] 表示:

  • 左上角位置:(1", 2")
  • 尺寸:宽 8",高 1"

🎨 颜色格式

支持两种十六进制格式:

  • 短格式#RGB(如 #fff = 白色)
  • 完整格式#RRGGBB(如 #ffffff = 白色)

💡 使用技巧

  1. 开发流程:使用 --preview 模式实时查看效果,确认无误后再生成 PPTX
  2. 模板复用:为常用布局创建模板,保持演示文稿风格一致
  3. 相对路径:图片路径相对于 YAML 文件位置,便于项目管理
  4. 模板目录:使用模板时必须指定 --template-dir 参数
  5. 文本换行:文本框默认启用自动换行,无需手动处理长文本

📚 完整示例

查看 temp/ 目录下的示例文件:

  • temp/test_refactor.yaml - 基本示例
  • temp/template_demo.yaml - 模板使用示例
  • temp/complex_presentation.yaml - 复杂演示文稿示例

⚠️ 常见错误

错误信息 原因 解决方法
文件不存在: xxx.yaml 找不到输入文件 检查文件路径是否正确
YAML 语法错误: 第 X 行 YAML 格式错误 检查缩进和语法
模板文件不存在: xxx 模板文件未找到 检查模板名称和 --template-dir
缺少必需变量: xxx 未提供必需的模板变量 vars 中提供该变量
图片文件未找到: xxx 图片文件不存在 检查图片路径

🔧 扩展性

yaml2pptx 采用模块化架构,易于扩展:

  • 添加新元素类型:定义新的元素数据类和渲染方法
  • 添加新渲染器:支持输出到其他格式(如 PDF
  • 自定义模板:创建符合你需求的模板库

详见 开发文档

📦 依赖项

  • python-pptx - PowerPoint 文件生成
  • pyyaml - YAML 解析
  • flask - 预览服务器
  • watchdog - 文件监听

依赖在 pyproject.toml 中声明,由 uv 自动管理,无需手动安装。

🧪 测试

项目包含完整的测试套件,使用 pytest 框架。

运行测试

# 安装测试依赖
uv pip install -e ".[dev]"

# 运行所有测试
uv run pytest

# 运行特定类型的测试
uv run pytest tests/unit/       # 单元测试
uv run pytest tests/integration/  # 集成测试
uv run pytest tests/e2e/        # 端到端测试

# 运行特定测试文件
uv run pytest tests/unit/test_elements.py

# 显示详细输出
uv run pytest -v

# 显示测试覆盖率
uv run pytest --cov=. --cov-report=html

测试结构

tests/
├── unit/           # 单元测试 - 测试各模块独立功能
├── integration/    # 集成测试 - 测试模块间协作
├── e2e/            # 端到端测试 - 测试完整用户场景
└── fixtures/       # 测试数据

🤝 贡献

欢迎贡献代码、报告问题或提出建议!

开发者请参阅 开发文档 了解代码结构和开发规范。

📄 许可证

MIT License

Description
No description provided
Readme 1.1 MiB
Languages
Python 100%