# yaml2pptx 使用 YAML 声明式语法创建 PowerPoint 演示文稿的工具。 ## ✨ 功能特性 - 📝 **YAML 声明式语法** - 使用简单易读的 YAML 定义演示文稿 - ✅ **智能验证** - 转换前自动检查 YAML 文件,提前发现问题 - 🎨 **模板系统** - 支持参数化模板,复用幻灯片布局 - 🧩 **丰富的元素类型** - 文本、图片、形状、表格 - 👁️ **实时预览** - 浏览器预览模式,支持热重载 - 📐 **灵活尺寸** - 支持 16:9 和 4:3 两种宽高比 - 🔧 **模块化架构** - 易于扩展和维护 ## 🚀 快速开始 ### 安装 本工具使用 [uv](https://github.com/astral-sh/uv) 管理依赖。项目依赖在 pyproject.toml 中声明,运行时会自动安装所需的 Python 包。 ### 基本用法 ```bash # 转换 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 ``` ### 实时预览 ```bash # 启动预览服务器(自动打开浏览器) 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 文件,提前发现问题: ```bash # 独立验证命令 uv run yaml2pptx.py check presentation.yaml # 使用模板时验证 uv run yaml2pptx.py check presentation.yaml --template-dir ./templates ``` 验证功能会检查: - ✅ YAML 语法和结构 - ✅ 元素是否超出页面范围 - ✅ 图片和模板文件是否存在 - ✅ 颜色格式是否正确 - ✅ 字体大小是否合理 - ✅ 表格数据是否一致 **自动验证**:转换时默认会自动验证,如果发现错误会终止转换。可以使用 `--skip-validation` 跳过验证: ```bash # 跳过自动验证 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 语法 ### 最小示例 ```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 文件: ```yaml metadata: size: "16:9" description: "2024年度项目进展总结,包含背景、成果和展望" ``` ### 使用模板 ```yaml metadata: size: "16:9" slides: - template: title-slide vars: title: "我的演示文稿" subtitle: "使用 yaml2pptx 创建" author: "张三" - template: content-slide vars: title: "功能概览" content: "yaml2pptx 支持多种元素类型..." ``` ## 🎨 元素类型 ### 文本元素 ```yaml - type: text box: [x, y, width, height] # 位置和尺寸(英寸) content: "文本内容" font: size: 18 # 字号(磅) bold: true # 粗体 italic: false # 斜体 color: "#ff0000" # 颜色 align: center # left/center/right ``` **特性**:文本框默认启用自动换行,文字超出宽度时会自动换行。 ### 图片元素 ```yaml - type: image box: [x, y, width, height] src: "path/to/image.png" # 支持相对路径和绝对路径 ``` **示例**: ```yaml slides: - elements: - type: image src: "photo.jpg" box: [1, 1, 4, 3] ``` ### 形状元素 ```yaml - type: shape box: [x, y, width, height] shape: rectangle # rectangle/ellipse/rounded_rectangle fill: "#4a90e2" # 填充颜色 line: color: "#000000" # 边框颜色 width: 2 # 边框宽度(磅) ``` ### 表格元素 ```yaml - 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` 字段: ```yaml 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`: ```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 ``` ### 使用外部模板 ```yaml slides: - template: title-slide vars: title: "我的演示文稿" subtitle: "副标题" author: "作者" ``` #### 模板 description 字段 模板文件可以包含可选的 `description` 字段,用于描述模板的用途和设计意图,仅用于文档目的: ```yaml # templates/title-slide.yaml description: "用于章节标题页的模板,包含主标题和副标题" vars: - name: title required: true elements: - type: text box: [1, 2, 8, 1] content: "{title}" font: size: 44 bold: true ``` ### 混合模式模板 混合模式允许你在使用模板的同时添加自定义元素,实现更灵活的布局组合。 #### 基本用法 在使用模板的幻灯片中,同时指定 `template` 和 `elements` 字段: ```yaml 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" ``` #### 变量共享 自定义元素可以访问模板中定义的变量: ```yaml 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 轴顺序): 1. **模板元素**(先渲染,在底层) 2. **自定义元素**(后渲染,在上层) 这意味着自定义元素会覆盖在模板元素之上。 ```yaml slides: - template: background-template # 提供背景和头部 vars: title: "标题" elements: # 这些元素会显示在模板元素之上 - type: shape box: [2, 2, 6, 3] fill: "#ffffff" # 白色框会覆盖背景 ``` #### 使用场景 **适合使用混合模式**: - 复用统一的头部/底部,自定义中间内容 - 使用模板提供的背景和品牌元素,添加页面特定内容 - 需要在标准布局基础上添加特殊元素 **示例:统一头部 + 自定义内容** ```yaml 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`,新功能 ```yaml slides: # 纯模板模式(原有行为) - template: title-slide vars: title: "标题" # 纯自定义模式(原有行为) - elements: - type: text content: "自定义内容" # 混合模式(新功能) - template: title-slide vars: title: "标题" elements: - type: text content: "额外内容" ``` #### 幻灯片 description 字段 幻灯片可以包含可选的 `description` 字段,用于描述该幻灯片的作用和内容。**`description` 内容会自动写入 PPT 备注页**,方便在演示时查看演讲说明: ```yaml slides: - description: "介绍项目背景和目标" template: title-slide vars: title: "项目背景" - description: "展示核心功能特性" elements: - type: text content: "功能特性" ``` **注意事项**: - 仅幻灯片级别的 `description` 会写入备注 - 模板的 `description` 不会继承到幻灯片备注 - `metadata.description` 用于描述整个演示文稿,不写入单个幻灯片备注 ### 条件渲染 #### 元素级条件渲染 使用 `visible` 属性控制元素显示,支持强大的条件表达式: **基本示例**: ```yaml # 简单比较 - type: text content: "有数据" visible: "{count > 0}" # 字符串比较 - type: text content: "草稿状态" visible: "{status == 'draft'}" # 非空检查(向后兼容) - type: text content: "{subtitle}" visible: "{subtitle != ''}" ``` **支持的表达式类型**: 1. **比较运算**:`==`, `!=`, `>`, `<`, `>=`, `<=` ```yaml visible: "{score >= 60}" visible: "{price <= 100}" ``` 2. **逻辑运算**:`and`, `or`, `not` ```yaml visible: "{count > 0 and status == 'active'}" visible: "{is_draft or is_preview}" visible: "{not (count == 0)}" ``` 3. **成员测试**:`in`, `not in` ```yaml visible: "{status in ['draft', 'review', 'published']}" visible: "{level in (1, 2, 3)}" visible: "{'test' in version}" # 字符串包含 ``` 4. **数学运算**:`+`, `-`, `*`, `/`, `%`, `**` ```yaml visible: "{(price * discount) > 50}" visible: "{(total / count) >= 10}" ``` 5. **内置函数**:`int()`, `float()`, `str()`, `len()`, `bool()`, `abs()`, `min()`, `max()` ```yaml visible: "{len(items) > 0}" visible: "{int(value) > 100}" ``` **复杂条件示例**: ```yaml # 范围检查 - 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` 参数控制整个幻灯片是否渲染: ```yaml 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`(元素级) | |------|-------------------|-------------------| | 作用范围 | 整个幻灯片 | 单个元素 | | 类型 | 布尔值 | 条件表达式 | | 判断时机 | 加载时(静态) | 渲染时(动态) | | 使用场景 | 临时禁用页面 | 条件显示元素 | **示例**: ```yaml 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) - **自定义模板**:创建符合你需求的模板库 详见 [开发文档](README_DEV.md)。 ## 📦 依赖项 - `python-pptx` - PowerPoint 文件生成 - `pyyaml` - YAML 解析 - `flask` - 预览服务器 - `watchdog` - 文件监听 依赖在 pyproject.toml 中声明,由 uv 自动管理,无需手动安装。 ## 🧪 测试 项目包含完整的测试套件,使用 pytest 框架。 ### 运行测试 ```bash # 安装测试依赖 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/ # 测试数据 ``` ## 🤝 贡献 欢迎贡献代码、报告问题或提出建议! 开发者请参阅 [开发文档](README_DEV.md) 了解代码结构和开发规范。 ## 📄 许可证 MIT License