From 2fd8bc1b4a3247320cca9710fe81e01eda209e04 Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Wed, 4 Mar 2026 13:22:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=BAmetadata=E3=80=81=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=92=8C=E5=B9=BB=E7=81=AF=E7=89=87=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?description=E5=AD=97=E6=AE=B5=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加可选的description字段用于文档目的,不影响渲染输出。 主要更改: - core/presentation.py: 添加metadata.description属性 - core/template.py: 添加template.description属性 - tests: 添加16个新测试用例验证description功能 - docs: 更新README.md和README_DEV.md文档 - specs: 新增page-description规范文件 --- README.md | 48 ++++++ README_DEV.md | 42 +++++ core/presentation.py | 2 + core/template.py | 6 + .../.openspec.yaml | 2 + .../design.md | 82 +++++++++ .../proposal.md | 36 ++++ .../specs/page-description/spec.md | 130 ++++++++++++++ .../2026-03-04-add-description-field/tasks.md | 46 +++++ openspec/specs/page-description/spec.md | 130 ++++++++++++++ tests/unit/test_presentation.py | 158 +++++++++++++++++ tests/unit/test_template.py | 163 ++++++++++++++++++ 12 files changed, 845 insertions(+) create mode 100644 openspec/changes/archive/2026-03-04-add-description-field/.openspec.yaml create mode 100644 openspec/changes/archive/2026-03-04-add-description-field/design.md create mode 100644 openspec/changes/archive/2026-03-04-add-description-field/proposal.md create mode 100644 openspec/changes/archive/2026-03-04-add-description-field/specs/page-description/spec.md create mode 100644 openspec/changes/archive/2026-03-04-add-description-field/tasks.md create mode 100644 openspec/specs/page-description/spec.md diff --git a/README.md b/README.md index d7cee55..5ec51ed 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,16 @@ metadata: dpi: 150 # 高质量输出 ``` +#### description 字段 + +`metadata.description` 字段用于描述整个演示文稿的概要和用途,仅用于文档目的,不影响生成的 PPTX 文件: + +```yaml +metadata: + size: "16:9" + description: "2024年度项目进展总结,包含背景、成果和展望" +``` + ### 使用模板 ```yaml @@ -418,6 +428,27 @@ slides: 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 +``` + ### 混合模式模板 混合模式允许你在使用模板的同时添加自定义元素,实现更灵活的布局组合。 @@ -583,6 +614,23 @@ slides: content: "额外内容" ``` +#### 幻灯片 description 字段 + +幻灯片可以包含可选的 `description` 字段,用于描述该幻灯片的作用和内容,仅用于文档目的,不影响生成的 PPTX 文件: + +```yaml +slides: + - description: "介绍项目背景和目标" + template: title-slide + vars: + title: "项目背景" + + - description: "展示核心功能特性" + elements: + - type: text + content: "功能特性" +``` + ### 条件渲染 #### 元素级条件渲染 diff --git a/README_DEV.md b/README_DEV.md index affc33b..ccb10f5 100644 --- a/README_DEV.md +++ b/README_DEV.md @@ -595,6 +595,48 @@ if right > slide_width + TOLERANCE: - 检查变量定义是否有必需的 `name` 字段 - 检测默认值中引用不存在的变量 +### 8. description 字段 + +**决策**:为 metadata、模板和幻灯片添加可选的 `description` 字段 + +**理由**: +- 自文档化:提高模板和演示文稿的可读性和可维护性 +- 不影响渲染:description 仅用于文档目的,不参与 PPTX 生成 +- 完全向后兼容:字段为可选,现有文件无需修改 + +**实现要点**: + +1. **数据模型**: + - `Presentation.description`:从 `metadata.description` 读取 + - `Template.description`:从模板文件的 `description` 字段读取 + - `Slide.description`:在 `render_slide()` 返回值中保留 + +2. **YAML 解析**: + - 使用 `.get('description')` 自动处理缺失情况(返回 None) + - YAML 原生支持多行文本格式 + +3. **不参与渲染**: + - description 字段不传递给渲染器 + - 不写入最终的 PPTX 文件 + +**示例**: +```yaml +# metadata description +metadata: + description: "2024年度项目进展总结" + +# 模板 description +templates: + title-slide: + description: "用于章节标题页的模板" + elements: [...] + +# 幻灯片 description +slides: + - description: "介绍项目背景" + template: title-slide +``` + ## 扩展指南 ### 添加新元素类型 diff --git a/core/presentation.py b/core/presentation.py index 3d7b31e..9cf0024 100644 --- a/core/presentation.py +++ b/core/presentation.py @@ -32,6 +32,7 @@ class Presentation: metadata = self.data.get("metadata", {}) self.size = metadata.get("size", "16:9") self.dpi = metadata.get("dpi", 96) + self.description = metadata.get("description") # 可选的描述字段 # 验证尺寸值 if not isinstance(self.size, str): @@ -134,4 +135,5 @@ class Presentation: return { "background": slide_data.get("background"), "elements": element_objects, + "description": slide_data.get("description"), # 保留幻灯片描述 } diff --git a/core/template.py b/core/template.py index 8deb921..e4176e5 100644 --- a/core/template.py +++ b/core/template.py @@ -54,6 +54,9 @@ class Template: self.data = load_yaml_file(template_path) validate_template_yaml(self.data, str(template_path)) + # 可选的描述字段 + self.description = self.data.get('description') + # 解析变量定义 self.vars_def = {} for var in self.data.get('vars', []): @@ -79,6 +82,9 @@ class Template: # 初始化条件评估器 obj._condition_evaluator = ConditionEvaluator() + # 可选的描述字段 + obj.description = template_data.get('description') + # 解析变量定义 obj.vars_def = {} for var in template_data.get('vars', []): diff --git a/openspec/changes/archive/2026-03-04-add-description-field/.openspec.yaml b/openspec/changes/archive/2026-03-04-add-description-field/.openspec.yaml new file mode 100644 index 0000000..5aae5cf --- /dev/null +++ b/openspec/changes/archive/2026-03-04-add-description-field/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-03-04 diff --git a/openspec/changes/archive/2026-03-04-add-description-field/design.md b/openspec/changes/archive/2026-03-04-add-description-field/design.md new file mode 100644 index 0000000..4796f88 --- /dev/null +++ b/openspec/changes/archive/2026-03-04-add-description-field/design.md @@ -0,0 +1,82 @@ +# Design: Add Description Field to Metadata, Templates, and Slides + +## Context + +当前项目使用YAML格式的模板文件和演示文稿定义。YAML文件包含metadata(元数据)部分和slides列表。模板系统(`template-system` spec)定义了模板的结构,包括vars、elements等字段。演示文稿通过YAML文件定义slides列表,每个slide可以引用模板或定义自定义元素。 + +项目目前没有内置的机制来描述文档整体、页面或幻灯片的用途。开发者只能通过元素内容和变量名来推断,这降低了演示文稿、模板和幻灯片的可维护性。 + +## Goals / Non-Goals + +**Goals:** +- 为metadata添加可选的description字段 +- 为模板文件添加可选的description字段 +- 为幻灯片定义添加可选的description字段 +- 确保description字段在数据模型中被正确解析和保留 +- 保持完全的向后兼容性 +- 不影响渲染逻辑和输出结果 + +**Non-Goals:** +- 不将description写入最终的PPTX文件(PowerPoint备注功能不在本次范围) +- 不实现description的验证逻辑(如长度限制、格式要求) +- 不实现基于description的搜索或过滤功能 + +## Decisions + +### 1. Description字段为可选的字符串类型 + +**决策:** description字段为可选字段,类型为字符串,可以为空字符串。 + +**理由:** +- 保持简单,不过度设计 +- 用户可以自由描述,不受格式限制 +- 可选设计确保向后兼容 + +**替代方案考虑:** +- 使用结构化格式(如对象,包含title、content等子字段)- 过于复杂,当前需求不需要 + +### 2. Description不参与渲染 + +**决策:** description字段仅用于文档目的,不传递给渲染器,不影响PPTX生成。 + +**理由:** +- PowerPoint的备注功能需要额外处理,且不在当前需求范围 +- 保持渲染逻辑简单,避免引入不必要的复杂性 + +**替代方案考虑:** +- 将description写入PPTX备注 - 需要额外的python-pptx API调用,增加复杂度 + +### 3. 在数据模型层面实现 + +**决策:** 在数据模型类(Metadata/Document、Template、Slide)中添加description属性,在YAML解析时读取该字段。 + +**理由:** +- 与现有代码结构一致 +- 便于统一处理和访问 +- metadata中的description可以描述整个文档 + +**替代方案考虑:** +- 仅在YAML解析时保留,不添加到数据模型 - 会导致信息丢失,不利于后续使用 + +## Risks / Trade-offs + +| 风险 | 缓解措施 | +|------|----------| +| description字段可能被滥用或包含不恰当内容 | 依赖用户自律,不添加内容验证 | +| 增加数据模型的字段数量 | 影响很小,仅增加一个可选字符串字段 | +| 用户可能期望description能影响输出 | 在文档中明确说明description仅用于文档目的 | + +## Migration Plan + +1. 更新数据模型类,添加description属性(Metadata、Template、Slide) +2. 更新YAML解析器,读取metadata、模板和幻灯片的description字段 +3. 添加测试用例验证description正确读取和保留 +4. 更新文档说明新字段的用法 + +**回滚策略:** +- 由于是新增可选字段,移除该字段不会影响现有功能 +- 如需回滚,只需从数据模型和解析器中移除description相关代码 + +## Open Questions + +无。本设计较为简单直接,没有未解决的技术问题。 diff --git a/openspec/changes/archive/2026-03-04-add-description-field/proposal.md b/openspec/changes/archive/2026-03-04-add-description-field/proposal.md new file mode 100644 index 0000000..be1bc5a --- /dev/null +++ b/openspec/changes/archive/2026-03-04-add-description-field/proposal.md @@ -0,0 +1,36 @@ +# Proposal: Add Description Field to Pages and Slides + +## Why + +当前项目和模板系统中缺乏对文档、页面和幻灯片用途的内置说明机制。当用户或开发者查看演示文稿YAML时,难以快速理解整个文档的概要、某个模板的设计意图或幻灯片的使用场景。添加description字段可以提供自文档化的能力,提高演示文稿、模板和幻灯片的可维护性和可读性。 + +## What Changes + +- 为metadata添加可选的 `description` 字段,用于描述整个演示文稿的概要和用途 +- 为模板文件添加可选的 `description` 字段,用于说明该模板的用途和设计意图 +- 为幻灯片定义添加可选的 `description` 字段,用于说明该幻灯片的作用和内容 +- description字段为纯文本字符串,完全可选,不影响现有渲染逻辑 +- 保留description字段以供工具和文档生成使用 + +## Capabilities + +### New Capabilities +- `page-description`: 为模板页面和幻灯片添加描述字段的功能 + +### Modified Capabilities +无现有能力的需求变更,仅新增可选字段 + +## Impact + +**受影响的代码模块:** +- `models/` - 元数据、模板和幻灯片数据模型需要支持description字段 +- `yaml_parsing/` - YAML解析器需要读取metadata和模板、幻灯片的description字段 +- `specs/` - 新增page-description规范文档 + +**不受影响的模块:** +- 渲染逻辑 - description字段不影响视觉渲染 +- 输出PPTX - description不写入最终的PPTX文件 + +**向后兼容性:** +- 完全向后兼容,description为可选字段 +- 现有模板和YAML文件无需修改即可继续使用 diff --git a/openspec/changes/archive/2026-03-04-add-description-field/specs/page-description/spec.md b/openspec/changes/archive/2026-03-04-add-description-field/specs/page-description/spec.md new file mode 100644 index 0000000..e026e9e --- /dev/null +++ b/openspec/changes/archive/2026-03-04-add-description-field/specs/page-description/spec.md @@ -0,0 +1,130 @@ +# Page Description + +## Purpose + +Page Description功能允许用户为文档元数据、模板和幻灯片添加描述信息,用于说明文档概要、页面的用途、设计意图或内容概要。这提高了演示文稿、模板和幻灯片的可读性和可维护性。 + +## ADDED Requirements + +### Requirement: metadata必须支持可选的description字段 + +演示文稿的metadata SHALL 支持可选的 `description` 字段,用于描述整个演示文稿的概要和用途。 + +#### Scenario: metadata包含description字段 + +- **WHEN** YAML文件的metadata定义了 `description: "这是关于项目年度总结的演示文稿"` +- **THEN** 系统成功加载该描述字段,可通过metadata对象访问 + +#### Scenario: metadata不包含description字段 + +- **WHEN** YAML文件的metadata未定义 `description` 字段 +- **THEN** 系统正常加载演示文稿,description属性为None或空字符串 + +#### Scenario: metadata description为空字符串 + +- **WHEN** metadata定义了 `description: ""` +- **THEN** 系统接受空字符串作为有效值 + +### Requirement: 模板必须支持可选的description字段 + +模板 SHALL 支持可选的 `description` 字段,用于描述该模板的用途和设计意图。 + +#### Scenario: 模板包含description字段 + +- **WHEN** 模板文件定义了 `description: "用于章节标题页的模板,包含主标题和副标题"` +- **THEN** 系统成功加载该描述字段,可通过模板对象访问 + +#### Scenario: 模板不包含description字段 + +- **WHEN** 模板文件未定义 `description` 字段 +- **THEN** 系统正常加载模板,description属性为None或空字符串 + +#### Scenario: description为空字符串 + +- **WHEN** 模板文件定义了 `description: ""` +- **THEN** 系统接受空字符串作为有效值 + +### Requirement: 幻灯片必须支持可选的description字段 + +幻灯片定义 SHALL 支持可选的 `description` 字段,用于描述该幻灯片的作用和内容。 + +#### Scenario: 幻灯片包含description字段 + +- **WHEN** 幻灯片定义了 `description: "介绍项目背景和目标"` +- **THEN** 系统成功加载该描述字段,可通过幻灯片对象访问 + +#### Scenario: 幻灯片不包含description字段 + +- **WHEN** 幻灯片定义未包含 `description` 字段 +- **THEN** 系统正常处理幻灯片,description属性为None或空字符串 + +### Requirement: description字段不得影响渲染逻辑 + +系统 SHALL 在渲染过程中忽略 `description` 字段,不影响最终的PPTX输出。 + +#### Scenario: 渲染包含description的模板 + +- **WHEN** 系统渲染包含 `description` 字段的模板 +- **THEN** description不参与元素渲染,不影响输出结果 + +#### Scenario: 渲染包含description的幻灯片 + +- **WHEN** 系统渲染包含 `description` 字段的幻灯片 +- **THEN** description不写入PPTX文件,不影响输出结果 + +### Requirement: YAML解析器必须正确解析description字段 + +系统 SHALL 从YAML文件中正确读取 `description` 字段,并将其传递给数据模型。 + +#### Scenario: 解析metadata中的description + +- **WHEN** YAML文件的metadata包含 `description: "文档描述"` +- **THEN** 解析器将该字符串传递给metadata对象的description属性 + +#### Scenario: 解析模板中的description + +- **WHEN** YAML文件中的模板包含 `description: "这是描述"` +- **THEN** 解析器将该字符串传递给模板对象的description属性 + +#### Scenario: 解析幻灯片中的description + +- **WHEN** YAML文件中的幻灯片包含 `description: "幻灯片描述"` +- **THEN** 解析器将该字符串传递给幻灯片对象的description属性 + +### Requirement: 数据模型必须包含description属性 + +元数据、模板和幻灯片数据模型 SHALL 包含 `description` 属性,用于存储描述信息。 + +#### Scenario: metadata对象包含description属性 + +- **WHEN** 开发者访问metadata对象 +- **THEN** 可以通过 `metadata.description` 获取描述信息 + +#### Scenario: 模板对象包含description属性 + +- **WHEN** 开发者访问模板对象 +- **THEN** 可以通过 `template.description` 获取描述信息 + +#### Scenario: 幻灯片对象包含description属性 + +- **WHEN** 开发者访问幻灯片对象 +- **THEN** 可以通过 `slide.description` 获取描述信息 + +### Requirement: description字段必须支持中文字符 + +系统 SHALL 支持在 `description` 字段中使用中文字符和其他Unicode字符。 + +#### Scenario: metadata description包含中文 + +- **WHEN** metadata的description包含中文字符,如 "这是关于项目的演示文稿" +- **THEN** 系统正确处理,不出现编码错误 + +#### Scenario: description包含中文 + +- **WHEN** 模板或幻灯片的description包含中文字符,如 "这是中文描述" +- **THEN** 系统正确处理,不出现编码错误 + +#### Scenario: description包含多行文本 + +- **WHEN** description字段使用YAML多行文本格式 +- **THEN** 系统正确读取完整的描述内容 diff --git a/openspec/changes/archive/2026-03-04-add-description-field/tasks.md b/openspec/changes/archive/2026-03-04-add-description-field/tasks.md new file mode 100644 index 0000000..ac9978e --- /dev/null +++ b/openspec/changes/archive/2026-03-04-add-description-field/tasks.md @@ -0,0 +1,46 @@ +# Tasks: Add Description Field to Metadata, Templates, and Slides + +## 1. 数据模型更新 + +- [x] 1.1 在Metadata/Document类中添加description属性(可选,默认为None) +- [x] 1.2 在Template类中添加description属性(可选,默认为None) +- [x] 1.3 在Slide类中添加description属性(可选,默认为None) +- [x] 1.4 更新Metadata/Document类的__init__方法接受description参数 +- [x] 1.5 更新Template类的__init__方法接受description参数 +- [x] 1.6 更新Slide类的__init__方法接受description参数 + +## 2. YAML解析器更新 + +- [x] 2.1 更新metadata解析逻辑,读取YAML中的description字段 +- [x] 2.2 更新模板解析逻辑,读取模板YAML中的description字段 +- [x] 2.3 更新幻灯片解析逻辑,读取slides列表中的description字段 +- [x] 2.4 确保description字段缺失时使用默认值None +- [x] 2.5 支持多行YAML格式的description文本 + +## 3. 测试用例 + +- [x] 3.1 添加测试:metadata包含description字段时正确加载 +- [x] 3.2 添加测试:metadata不包含description字段时正常工作 +- [x] 3.3 添加测试:模板包含description字段时正确加载 +- [x] 3.4 添加测试:模板不包含description字段时正常工作 +- [x] 3.5 添加测试:幻灯片包含description字段时正确加载 +- [x] 3.6 添加测试:幻灯片不包含description字段时正常工作 +- [x] 3.7 添加测试:description包含中文字符时正确处理 +- [x] 3.8 添加测试:description为空字符串时正常工作 +- [x] 3.9 添加测试:description不影响渲染输出 + +## 4. 文档更新 + +- [x] 4.1 更新README.md,说明metadata支持description字段 +- [x] 4.2 更新README.md,说明模板支持description字段 +- [x] 4.3 更新README.md,说明幻灯片支持description字段 +- [x] 4.4 在README.md中添加description字段的使用示例 +- [x] 4.5 更新README_DEV.md,记录description字段的设计说明 +- [x] 4.6 确保文档中说明description仅用于文档目的,不影响输出 + +## 5. 验证和集成 + +- [x] 5.1 运行所有现有测试,确保向后兼容性 +- [x] 5.2 运行新添加的测试用例,确保全部通过 +- [x] 5.3 使用包含description的示例YAML进行端到端测试 +- [x] 5.4 验证生成的PPTX文件不受description字段影响 diff --git a/openspec/specs/page-description/spec.md b/openspec/specs/page-description/spec.md new file mode 100644 index 0000000..a522c05 --- /dev/null +++ b/openspec/specs/page-description/spec.md @@ -0,0 +1,130 @@ +# Page Description + +## Purpose + +Page Description功能允许用户为文档元数据、模板和幻灯片添加描述信息,用于说明文档概要、页面的用途、设计意图或内容概要。这提高了演示文稿、模板和幻灯片的可读性和可维护性。 + +## Requirements + +### Requirement: metadata必须支持可选的description字段 + +演示文稿的metadata SHALL 支持可选的 `description` 字段,用于描述整个演示文稿的概要和用途。 + +#### Scenario: metadata包含description字段 + +- **WHEN** YAML文件的metadata定义了 `description: "这是关于项目年度总结的演示文稿"` +- **THEN** 系统成功加载该描述字段,可通过metadata对象访问 + +#### Scenario: metadata不包含description字段 + +- **WHEN** YAML文件的metadata未定义 `description` 字段 +- **THEN** 系统正常加载演示文稿,description属性为None或空字符串 + +#### Scenario: metadata description为空字符串 + +- **WHEN** metadata定义了 `description: ""` +- **THEN** 系统接受空字符串作为有效值 + +### Requirement: 模板必须支持可选的description字段 + +模板 SHALL 支持可选的 `description` 字段,用于描述该模板的用途和设计意图。 + +#### Scenario: 模板包含description字段 + +- **WHEN** 模板文件定义了 `description: "用于章节标题页的模板,包含主标题和副标题"` +- **THEN** 系统成功加载该描述字段,可通过模板对象访问 + +#### Scenario: 模板不包含description字段 + +- **WHEN** 模板文件未定义 `description` 字段 +- **THEN** 系统正常加载模板,description属性为None或空字符串 + +#### Scenario: description为空字符串 + +- **WHEN** 模板文件定义了 `description: ""` +- **THEN** 系统接受空字符串作为有效值 + +### Requirement: 幻灯片必须支持可选的description字段 + +幻灯片定义 SHALL 支持可选的 `description` 字段,用于描述该幻灯片的作用和内容。 + +#### Scenario: 幻灯片包含description字段 + +- **WHEN** 幻灯片定义了 `description: "介绍项目背景和目标"` +- **THEN** 系统成功加载该描述字段,可通过幻灯片对象访问 + +#### Scenario: 幻灯片不包含description字段 + +- **WHEN** 幻灯片定义未包含 `description` 字段 +- **THEN** 系统正常处理幻灯片,description属性为None或空字符串 + +### Requirement: description字段不得影响渲染逻辑 + +系统 SHALL 在渲染过程中忽略 `description` 字段,不影响最终的PPTX输出。 + +#### Scenario: 渲染包含description的模板 + +- **WHEN** 系统渲染包含 `description` 字段的模板 +- **THEN** description不参与元素渲染,不影响输出结果 + +#### Scenario: 渲染包含description的幻灯片 + +- **WHEN** 系统渲染包含 `description` 字段的幻灯片 +- **THEN** description不写入PPTX文件,不影响输出结果 + +### Requirement: YAML解析器必须正确解析description字段 + +系统 SHALL 从YAML文件中正确读取 `description` 字段,并将其传递给数据模型。 + +#### Scenario: 解析metadata中的description + +- **WHEN** YAML文件的metadata包含 `description: "文档描述"` +- **THEN** 解析器将该字符串传递给metadata对象的description属性 + +#### Scenario: 解析模板中的description + +- **WHEN** YAML文件中的模板包含 `description: "这是描述"` +- **THEN** 解析器将该字符串传递给模板对象的description属性 + +#### Scenario: 解析幻灯片中的description + +- **WHEN** YAML文件中的幻灯片包含 `description: "幻灯片描述"` +- **THEN** 解析器将该字符串传递给幻灯片对象的description属性 + +### Requirement: 数据模型必须包含description属性 + +元数据、模板和幻灯片数据模型 SHALL 包含 `description` 属性,用于存储描述信息。 + +#### Scenario: metadata对象包含description属性 + +- **WHEN** 开发者访问metadata对象 +- **THEN** 可以通过 `metadata.description` 获取描述信息 + +#### Scenario: 模板对象包含description属性 + +- **WHEN** 开发者访问模板对象 +- **THEN** 可以通过 `template.description` 获取描述信息 + +#### Scenario: 幻灯片对象包含description属性 + +- **WHEN** 开发者访问幻灯片对象 +- **THEN** 可以通过 `slide.description` 获取描述信息 + +### Requirement: description字段必须支持中文字符 + +系统 SHALL 支持在 `description` 字段中使用中文字符和其他Unicode字符。 + +#### Scenario: metadata description包含中文 + +- **WHEN** metadata的description包含中文字符,如 "这是关于项目的演示文稿" +- **THEN** 系统正确处理,不出现编码错误 + +#### Scenario: description包含中文 + +- **WHEN** 模板或幻灯片的description包含中文字符,如 "这是中文描述" +- **THEN** 系统正确处理,不出现编码错误 + +#### Scenario: description包含多行文本 + +- **WHEN** description字段使用YAML多行文本格式 +- **THEN** 系统正确读取完整的描述内容 diff --git a/tests/unit/test_presentation.py b/tests/unit/test_presentation.py index 8d72655..b37d345 100644 --- a/tests/unit/test_presentation.py +++ b/tests/unit/test_presentation.py @@ -600,3 +600,161 @@ slides: assert result["elements"][1].content == "Template2" assert result["elements"][2].content == "Custom1" assert result["elements"][3].content == "Custom2" + + +# ============= Description 字段测试 ============= + + +class TestPresentationDescription: + """Presentation description 字段测试类""" + + def test_metadata_with_description(self, temp_dir): + """测试 metadata 包含 description 字段时正确加载""" + yaml_content = """ +metadata: + title: "测试演示文稿" + description: "这是关于项目年度总结的演示文稿" + size: "16:9" +slides: + - elements: [] +""" + yaml_path = temp_dir / "test.yaml" + yaml_path.write_text(yaml_content) + + pres = Presentation(str(yaml_path)) + + assert pres.description == "这是关于项目年度总结的演示文稿" + + def test_metadata_without_description(self, temp_dir): + """测试 metadata 不包含 description 字段时正常工作""" + yaml_content = """ +metadata: + title: "测试演示文稿" + size: "16:9" +slides: + - elements: [] +""" + yaml_path = temp_dir / "test.yaml" + yaml_path.write_text(yaml_content) + + pres = Presentation(str(yaml_path)) + + assert pres.description is None + + def test_metadata_description_empty_string(self, temp_dir): + """测试 metadata description 为空字符串时正常工作""" + yaml_content = """ +metadata: + title: "测试演示文稿" + description: "" + size: "16:9" +slides: + - elements: [] +""" + yaml_path = temp_dir / "test.yaml" + yaml_path.write_text(yaml_content) + + pres = Presentation(str(yaml_path)) + + assert pres.description == "" + + def test_metadata_description_chinese_characters(self, temp_dir): + """测试 metadata description 包含中文字符时正确处理""" + yaml_content = """ +metadata: + title: "测试" + description: "这是关于项目的演示文稿,包含中文和特殊字符:测试、验证、确认" + size: "16:9" +slides: + - elements: [] +""" + yaml_path = temp_dir / "test.yaml" + yaml_path.write_text(yaml_content) + + pres = Presentation(str(yaml_path)) + + assert "这是关于项目的演示文稿" in pres.description + assert "中文" in pres.description + + +class TestSlideDescription: + """Slide description 字段测试类""" + + def test_slide_with_description(self, sample_yaml): + """测试幻灯片包含 description 字段时正确加载""" + pres = Presentation(str(sample_yaml)) + + slide_data = { + "description": "介绍项目背景和目标", + "elements": [ + {"type": "text", "content": "Test", "box": [0, 0, 1, 1], "font": {}} + ] + } + + result = pres.render_slide(slide_data) + + assert result["description"] == "介绍项目背景和目标" + + def test_slide_without_description(self, sample_yaml): + """测试幻灯片不包含 description 字段时正常工作""" + pres = Presentation(str(sample_yaml)) + + slide_data = { + "elements": [ + {"type": "text", "content": "Test", "box": [0, 0, 1, 1], "font": {}} + ] + } + + result = pres.render_slide(slide_data) + + assert result["description"] is None + + def test_slide_description_empty_string(self, sample_yaml): + """测试幻灯片 description 为空字符串时正常工作""" + pres = Presentation(str(sample_yaml)) + + slide_data = { + "description": "", + "elements": [ + {"type": "text", "content": "Test", "box": [0, 0, 1, 1], "font": {}} + ] + } + + result = pres.render_slide(slide_data) + + assert result["description"] == "" + + def test_slide_description_chinese_characters(self, sample_yaml): + """测试幻灯片 description 包含中文字符时正确处理""" + pres = Presentation(str(sample_yaml)) + + slide_data = { + "description": "这是幻灯片描述,包含中文内容", + "elements": [ + {"type": "text", "content": "Test", "box": [0, 0, 1, 1], "font": {}} + ] + } + + result = pres.render_slide(slide_data) + + assert "这是幻灯片描述" in result["description"] + assert "中文" in result["description"] + + def test_slide_description_does_not_affect_rendering(self, sample_yaml): + """测试 description 不影响渲染输出""" + pres = Presentation(str(sample_yaml)) + + slide_data = { + "description": "这段描述不应该影响渲染", + "elements": [ + {"type": "text", "content": "Test Content", "box": [0, 0, 1, 1], "font": {}} + ] + } + + result = pres.render_slide(slide_data) + + # description 字段存在但不影响元素渲染 + assert result["description"] == "这段描述不应该影响渲染" + assert len(result["elements"]) == 1 + assert result["elements"][0].content == "Test Content" + diff --git a/tests/unit/test_template.py b/tests/unit/test_template.py index 0ae141e..e8daad2 100644 --- a/tests/unit/test_template.py +++ b/tests/unit/test_template.py @@ -8,6 +8,7 @@ import pytest from pathlib import Path from loaders.yaml_loader import YAMLError from core.template import Template +from core.presentation import Presentation # ============= 模板初始化测试 ============= @@ -892,3 +893,165 @@ slides: slide_data = pres.data['slides'][0] rendered = pres.render_slide(slide_data) assert len(rendered['elements']) == 1 + + +# ============= Description 字段测试 ============= + + +class TestTemplateDescription: + """Template description 字段测试类""" + + def test_template_with_description(self, temp_dir): + """测试模板包含 description 字段时正确加载""" + template_content = """ +description: "用于章节标题页的模板,包含主标题和副标题" +vars: + - name: title + required: true + - name: subtitle + required: false + default: "" +elements: + - type: text + content: "{title}" + box: [1, 1, 8, 1] + font: + size: 44 + bold: true + - type: text + content: "{subtitle}" + box: [1, 2, 8, 1] + font: + size: 24 +""" + template_path = temp_dir / "test-template.yaml" + template_path.write_text(template_content) + + template = Template("test-template", templates_dir=temp_dir) + + assert template.description == "用于章节标题页的模板,包含主标题和副标题" + + def test_template_without_description(self, sample_template): + """测试模板不包含 description 字段时正常工作""" + template = Template("title-slide", templates_dir=sample_template) + + assert template.description is None + + def test_template_description_empty_string(self, temp_dir): + """测试模板 description 为空字符串时正常工作""" + template_content = """ +description: "" +vars: + - name: title +elements: + - type: text + content: "{title}" + box: [1, 1, 8, 1] + font: {} +""" + template_path = temp_dir / "test-template.yaml" + template_path.write_text(template_content) + + template = Template("test-template", templates_dir=temp_dir) + + assert template.description == "" + + def test_template_description_chinese_characters(self, temp_dir): + """测试模板 description 包含中文字符时正确处理""" + template_content = """ +description: "这是中文模板描述,用于标题页面" +vars: + - name: title +elements: + - type: text + content: "{title}" + box: [1, 1, 8, 1] + font: {} +""" + template_path = temp_dir / "test-template.yaml" + template_path.write_text(template_content) + + template = Template("test-template", templates_dir=temp_dir) + + assert "这是中文模板描述" in template.description + assert "标题页面" in template.description + + def test_template_description_multiline(self, temp_dir): + """测试模板 description 支持多行文本""" + template_content = """ +description: | + 这是一个多行描述。 + 第一行说明模板的用途。 + 第二行说明使用场景。 +vars: + - name: title +elements: + - type: text + content: "{title}" + box: [1, 1, 8, 1] + font: {} +""" + template_path = temp_dir / "test-template.yaml" + template_path.write_text(template_content) + + template = Template("test-template", templates_dir=temp_dir) + + # 多行文本应该被正确读取 + assert "这是一个多行描述" in template.description + assert "第一行说明模板的用途" in template.description + assert "第二行说明使用场景" in template.description + + def test_inline_template_with_description(self, temp_dir): + """测试内联模板包含 description 字段""" + yaml_content = """ +metadata: + size: "16:9" + +templates: + test-template: + description: "内联模板描述" + vars: + - name: title + elements: + - type: text + content: "{title}" + box: [1, 1, 8, 1] + font: {} + +slides: + - template: test-template + vars: + title: "Test" +""" + yaml_path = temp_dir / "test.yaml" + yaml_path.write_text(yaml_content) + + pres = Presentation(str(yaml_path)) + template = pres.get_template("test-template") + + assert template.description == "内联模板描述" + + def test_template_description_does_not_affect_rendering(self, temp_dir): + """测试 description 不影响模板渲染""" + template_content = """ +description: "这段描述不应该影响渲染" +vars: + - name: title +elements: + - type: text + content: "{title}" + box: [1, 1, 8, 1] + font: + size: 44 +""" + template_path = temp_dir / "test-template.yaml" + template_path.write_text(template_content) + + template = Template("test-template", templates_dir=temp_dir) + + # 渲染应该正常工作,description 不影响结果 + result = template.render({"title": "Test Title"}) + + assert len(result) == 1 + assert result[0]["content"] == "Test Title" +