主要变更: - 将 templates_dir 参数改为 template_file,支持单个模板库 YAML 文件 - 添加模板库 YAML 验证功能 - 为模板添加 base_dir 支持,正确解析相对路径资源 - 内联模板与外部模板同名时改为警告(内联优先) - 移除模板缓存机制,直接使用模板库字典 - 更新所有相关测试以适配新的模板加载方式 此重构简化了模板管理,使模板资源的路径解析更加清晰明确。
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 ./templates.yaml
实时预览
# 启动预览服务器(自动打开浏览器)
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 ./templates.yaml
验证功能会检查:
-
- 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"
slides:
- background:
color: "#ffffff"
elements:
- type: text
box: [1, 1, 8, 1]
content: "Hello, World!"
font:
size: 44
bold: true
color: "#333333"
align: center
description 字段
metadata.description 字段用于描述整个演示文稿的概要和用途,仅用于文档目的,不影响生成的 PPTX 文件:
metadata:
size: "16:9"
description: "2024年度项目进展总结,包含背景、成果和展望"
使用模板
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" # 支持相对路径和绝对路径
示例:
slides:
- elements:
- type: image
src: "photo.jpg"
box: [1, 1, 4, 3]
形状元素
- 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"]
font:
family: "Arial"
size: 14
color: "#333333"
header_font:
bold: true
color: "#ffffff"
style:
header_bg: "#4a90e2"
字体配置:
font:表格数据单元格的字体样式header_font:表头单元格的字体样式(未定义时继承font)style.header_bg:表头背景色
- 字体主题系统
字体主题系统允许你定义可复用的字体配置,统一管理演示文稿的字体样式。
定义字体主题
在 metadata.fonts 中定义命名字体配置:
metadata:
size: "16:9"
fonts:
title:
family: "cjk-sans"
size: 44
bold: true
color: "#2c3e50"
body:
family: "sans"
size: 18
color: "#34495e"
line_spacing: 1.5
fonts_default: "@body" # 默认字体(可选)
slides:
- elements:
- type: text
content: "标题文本"
box: [1, 1, 8, 1]
font: "@title" # 引用字体主题
- type: text
content: "正文内容"
box: [1, 2.5, 8, 2]
# 未定义 font 时使用 fonts_default
预设字体类别
系统提供五种预设字体类别,自动映射到跨平台通用字体:
| 类别 | 映射字体 | 说明 |
|---|---|---|
sans |
Arial | 西文无衬线 |
serif |
Times New Roman | 西文衬线 |
mono |
Courier New | 等宽字体 |
cjk-sans |
Microsoft YaHei | 中文无衬线 |
cjk-serif |
SimSun | 中文衬线 |
使用示例:
metadata:
fonts:
body:
family: "cjk-sans" # 自动映射到 Microsoft YaHei
size: 18
字体引用方式
支持三种字体引用方式:
1. 整体引用:完全使用定义的字体配置
font: "@title"
2. 继承覆盖:继承字体配置并覆盖特定属性
font:
parent: "@title"
size: 60 # 覆盖字号
color: "#ff0000" # 覆盖颜色
3. 独立定义:完全自定义字体
font:
family: "SimSun"
size: 24
bold: true
扩展字体属性
除了基础属性(size、bold、italic、color、align),还支持:
字体样式:
family:字体族名称或预设类别underline:下划线(true/false)strikethrough:删除线(true/false)
段落属性:
line_spacing:行距倍数(如 1.5)space_before:段前间距(磅)space_after:段后间距(磅)
高级属性:
baseline:基线位置(normal/superscript/subscript)caps:大小写转换(normal/allcaps/smallcaps)
完整示例:
metadata:
fonts:
heading:
family: "cjk-sans"
size: 32
bold: true
color: "#2c3e50"
line_spacing: 1.2
space_after: 12
body:
family: "sans"
size: 18
color: "#34495e"
line_spacing: 1.5
fonts_default: "@body"
slides:
- elements:
- type: text
content: "章节标题"
box: [1, 1, 8, 1]
font: "@heading"
- type: text
content: "正文内容\n支持多行文本"
box: [1, 2, 8, 2]
font:
parent: "@body"
underline: true
- type: table
position: [1, 4]
col_widths: [3, 3]
data:
- ["列1", "列2"]
- ["数据1", "数据2"]
font: "@body"
header_font:
parent: "@body"
bold: true
color: "#ffffff"
style:
header_bg: "#3498db"
模板系统
模板允许你定义可复用的幻灯片布局。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参数
- 无需指定
-
- 内联模板不能相互引用
-
- 内联和外部模板同名时会发出警告,优先使用内联模板
何时使用内联模板
适合使用内联模板:
- 模板仅在单个文档中使用
- 快速原型开发
- 简单的模板定义(1-3 个元素)
- 文档自包含,无需外部依赖
适合使用外部模板:
- 需要跨多个文档复用
- 复杂的模板定义(>5 个元素)
- 团队共享的模板库
- 需要版本控制和独立维护
最佳实践:
-
命名规范:
- 内联模板使用描述性名称(如
title-slide,content-slide) - 避免与外部模板同名,否则会报错
- 使用一致的命名风格(kebab-case 推荐)
- 内联模板使用描述性名称(如
-
模板大小:
- 内联模板建议不超过 50 行
- 超过 50 行考虑拆分或使用外部模板
- 保持 YAML 文件可读性
-
混合使用:
- 可以在同一文档中混合使用内联和外部模板
- 通用模板使用外部模板(如标题页、结束页)
- 文档特定模板使用内联模板
-
迁移策略:
- 原型阶段使用内联模板快速迭代
- 模板稳定后,如需复用则迁移到外部模板
- 使用
--template参数指定外部模板库文件
内联模板限制
-
- 内联模板不能相互引用(会报错)
-
- 内联和外部模板同名时会发出警告,优先使用内联模板
-
- 内联模板不支持继承或组合
外部模板库
外部模板库是一个包含多个模板的 YAML 文件,适合跨文档复用和团队共享。
创建模板库文件
创建模板库文件 templates.yaml:
# 模板库元数据(可选)
description: "公司标准模板库"
version: "1.0.0"
author: "设计团队"
# 模板定义(必需)
templates:
title-slide:
description: "标题页模板"
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
content-slide:
description: "内容页模板"
vars:
- name: title
required: true
- name: content
required: true
elements:
- type: text
box: [1, 1, 8, 0.8]
content: "{title}"
font:
size: 32
bold: true
- type: text
box: [1, 2, 8, 3]
content: "{content}"
font:
size: 20
使用外部模板库
在命令行中指定模板库文件:
uv run yaml2pptx.py convert presentation.yaml output.pptx --template ./templates.yaml
在 YAML 文件中引用模板:
slides:
- template: title-slide
vars:
title: "我的演示文稿"
subtitle: "使用外部模板库"
- template: content-slide
vars:
title: "第一章"
content: "这是内容"
模板库特性
-
- 单个文件包含多个模板
-
- 支持模板库元数据(description、version、author)
-
- 每个模板可以有独立的 description
-
- 便于版本控制和分发
-
- 支持相对路径的图片资源(相对于模板库文件所在目录)
模板库文件结构
# 顶层元数据(可选)
description: "模板库描述"
version: "版本号"
author: "作者"
# 模板定义(必需)
templates:
模板名称1:
description: "模板描述(可选)"
vars: [...]
elements: [...]
模板名称2:
description: "模板描述(可选)"
vars: [...]
elements: [...]
模板 description 字段
模板可以包含可选的 description 字段,用于描述模板的用途和设计意图:
templates:
title-slide:
description: "用于章节标题页的模板,包含主标题和副标题"
vars:
- name: title
required: true
elements:
- type: text
box: [1, 2, 8, 1]
content: "{title}"
font:
size: 44
bold: true
混合模式模板
混合模式允许你在使用模板的同时添加自定义元素,实现更灵活的布局组合。
基本用法
在使用模板的幻灯片中,同时指定 template 和 elements 字段:
slides:
# 混合模式:模板 + 自定义元素
- template: standard-header
vars:
title: "混合模式示例"
theme_color: "#3949ab"
elements:
# 自定义内容区域
- type: text
box: [1, 1.5, 8, 1]
content: "这是自定义内容"
font:
size: 24
# 自定义形状
- type: shape
shape: rectangle
box: [1, 3, 8, 2]
fill: "#f5f5f5"
变量共享
自定义元素可以访问模板中定义的变量:
templates:
branded-header:
vars:
- name: title
- name: theme_color
default: "#3949ab"
elements:
- type: shape
box: [0, 0, 10, 0.8]
fill: "{theme_color}"
- type: text
box: [0.5, 0.2, 9, 0.5]
content: "{title}"
slides:
- template: branded-header
vars:
title: "我的页面"
theme_color: "#4caf50"
elements:
# 自定义元素使用模板变量
- type: shape
box: [1, 2, 8, 3]
fill: "{theme_color}" # 使用模板的 theme_color
元素渲染顺序
混合模式中,元素按以下顺序渲染(z 轴顺序):
- 模板元素(先渲染,在底层)
- 自定义元素(后渲染,在上层)
这意味着自定义元素会覆盖在模板元素之上。
slides:
- template: background-template # 提供背景和头部
vars:
title: "标题"
elements:
# 这些元素会显示在模板元素之上
- type: shape
box: [2, 2, 6, 3]
fill: "#ffffff" # 白色框会覆盖背景
使用场景
适合使用混合模式:
- 复用统一的头部/底部,自定义中间内容
- 使用模板提供的背景和品牌元素,添加页面特定内容
- 需要在标准布局基础上添加特殊元素
示例:统一头部 + 自定义内容
templates:
standard-header:
vars:
- name: title
elements:
# 统一的头部样式
- type: shape
box: [0, 0, 10, 0.8]
fill: "#3949ab"
- type: text
box: [0.5, 0.2, 9, 0.5]
content: "{title}"
font:
color: "#ffffff"
slides:
# 页面 1:头部 + 文本内容
- template: standard-header
vars:
title: "文本页面"
elements:
- type: text
box: [1, 1.5, 8, 3]
content: "页面内容..."
# 页面 2:头部 + 表格
- template: standard-header
vars:
title: "数据页面"
elements:
- type: table
position: [1, 1.5]
data: [[...]]
# 页面 3:头部 + 图片
- template: standard-header
vars:
title: "图片页面"
elements:
- type: image
box: [2, 1.5, 6, 3.5]
src: "chart.png"
向后兼容性
混合模式完全向后兼容:
- 纯模板模式:只指定
template,行为不变 - 纯自定义模式:只指定
elements,行为不变 - 混合模式:同时指定
template和elements,新功能
slides:
# 纯模板模式(原有行为)
- template: title-slide
vars:
title: "标题"
# 纯自定义模式(原有行为)
- elements:
- type: text
content: "自定义内容"
# 混合模式(新功能)
- template: title-slide
vars:
title: "标题"
elements:
- type: text
content: "额外内容"
幻灯片 description 字段
幻灯片可以包含可选的 description 字段,用于描述该幻灯片的作用和内容。description 内容会自动写入 PPT 备注页,方便在演示时查看演讲说明:
slides:
- description: "介绍项目背景和目标"
template: title-slide
vars:
title: "项目背景"
- description: "展示核心功能特性"
elements:
- type: text
content: "功能特性"
注意事项:
- 仅幻灯片级别的
description会写入备注 - 模板的
description不会继承到幻灯片备注 metadata.description用于描述整个演示文稿,不写入单个幻灯片备注
条件渲染
元素级条件渲染
使用 visible 属性控制元素显示,支持强大的条件表达式:
基本示例:
# 简单比较
- type: text
content: "有数据"
visible: "{count > 0}"
# 字符串比较
- type: text
content: "草稿状态"
visible: "{status == 'draft'}"
# 非空检查(向后兼容)
- type: text
content: "{subtitle}"
visible: "{subtitle != ''}"
支持的表达式类型:
-
比较运算:
==,!=,>,<,>=,<=visible: "{score >= 60}" visible: "{price <= 100}" -
逻辑运算:
and,or,notvisible: "{count > 0 and status == 'active'}" visible: "{is_draft or is_preview}" visible: "{not (count == 0)}" -
成员测试:
in,not invisible: "{status in ['draft', 'review', 'published']}" visible: "{level in (1, 2, 3)}" visible: "{'test' in version}" # 字符串包含 -
数学运算:
+,-,*,/,%,**visible: "{(price * discount) > 50}" visible: "{(total / count) >= 10}" -
内置函数:
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 参数说明:
- 类型:布尔值(
true或false) - 默认值:
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 |
模板库文件路径 |
convert 命令
将 YAML 文件转换为 PPTX 文件。
| 选项 | 说明 |
|---|---|
input |
输入的 YAML 文件路径(必需) |
output |
输出的 PPTX 文件路径(可选) |
--template |
模板库文件路径 |
--skip-validation |
跳过自动验证 |
--force / -f |
强制覆盖已存在文件 |
preview 命令
启动预览服务器,实时查看演示文稿效果。
| 选项 | 说明 |
|---|---|
input |
输入的 YAML 文件路径(必需) |
--template |
模板库文件路径 |
--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= 白色)
💡 使用技巧
- 开发流程:使用
--preview模式实时查看效果,确认无误后再生成 PPTX - 模板复用:为常用布局创建模板,保持演示文稿风格一致
- 相对路径:图片路径相对于 YAML 文件位置,便于项目管理
- 模板库文件:使用模板时必须指定
--template参数 - 文本换行:文本框默认启用自动换行,无需手动处理长文本
📚 完整示例
查看 temp/ 目录下的示例文件:
temp/test_refactor.yaml- 基本示例temp/template_demo.yaml- 模板使用示例temp/complex_presentation.yaml- 复杂演示文稿示例
- 常见错误
| 错误信息 | 原因 | 解决方法 |
|---|---|---|
文件不存在: xxx.yaml |
找不到输入文件 | 检查文件路径是否正确 |
YAML 语法错误: 第 X 行 |
YAML 格式错误 | 检查缩进和语法 |
模板文件不存在: xxx |
模板文件未找到 | 检查模板名称和 --template |
缺少必需变量: 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