1
0
Files
PPTX/openspec/changes/archive/2026-03-05-font-theme-system/specs/font-theme/spec.md
lanyuanxiaoyao bd12fce14b feat: 实现字体主题系统和东亚字体支持
实现完整的字体主题系统,支持可复用字体配置、预设类别和扩展属性。
同时修复中文字体渲染问题,确保 Source Han Sans 等东亚字体正确显示。

核心功能:
- 字体主题配置:metadata.fonts 和 fonts_default
- 三种引用方式:整体引用、继承覆盖、独立定义
- 预设字体类别:sans、serif、mono、cjk-sans、cjk-serif
- 扩展字体属性:family、underline、strikethrough、line_spacing、
  space_before、space_after、baseline、caps
- 表格字体字段:font 和 header_font 替代旧的 style.font_size
- 引用循环检测和属性继承链
- 模板字体继承支持

东亚字体修复:
- 添加 _set_font_with_eastasian() 方法
- 同时设置拉丁字体、东亚字体和复杂脚本字体
- 修复中文字符使用默认字体的问题

测试:
- 58 个单元测试覆盖所有字体系统功能
- 3 个集成测试验证端到端场景
- 移除旧语法相关测试

文档:
- 更新 README.md 添加字体主题系统使用说明
- 更新 README_DEV.md 添加技术文档
- 创建 4 个示例 YAML 文件
- 同步 delta specs 到主 specs

归档:
- 归档 font-theme-system 变更到 openspec/changes/archive/

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-05 10:38:59 +08:00

208 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Font Theme
## Purpose
字体主题系统提供可复用的字体配置管理能力,允许用户在 metadata 中定义字体配置模板,通过引用方式应用到元素,实现统一的字体样式管理。
## ADDED Requirements
### Requirement: 系统必须支持在 metadata 中定义 fonts 字段
系统 SHALL 支持在 YAML metadata 中定义 fonts 字段,用于存储可复用的字体配置。
#### Scenario: 定义 fonts 字段
- **WHEN** metadata 中定义 fonts 字段
- **THEN** 系统成功解析并存储字体配置字典
#### Scenario: fonts 字段为空字典
- **WHEN** metadata 中定义 fonts: {}
- **THEN** 系统接受空的字体配置字典
#### Scenario: fonts 字段未定义
- **WHEN** metadata 中未定义 fonts 字段
- **THEN** 系统正常处理fonts 为空字典
### Requirement: fonts 字段必须包含命名字体配置
fonts 字段 SHALL 包含一个或多个命名字体配置,每个配置是一个字体属性字典。
#### Scenario: 定义单个字体配置
- **WHEN** fonts 中定义 title: {family: "Arial", size: 44, bold: true}
- **THEN** 系统创建名为 title 的字体配置
#### Scenario: 定义多个字体配置
- **WHEN** fonts 中定义 title、subtitle、body 等多个配置
- **THEN** 系统为每个配置创建独立的字体对象
### Requirement: 系统必须支持 fonts_default 字段
系统 SHALL 支持在 metadata 中定义可选的 fonts_default 字段,指定默认字体配置的引用。
#### Scenario: 定义 fonts_default 引用
- **WHEN** metadata 中定义 fonts_default: "@body"
- **THEN** 系统将 fonts_default 解析为对 fonts.body 的引用
#### Scenario: fonts_default 未定义
- **WHEN** metadata 中未定义 fonts_default 字段
- **THEN** 系统使用 python-pptx 的默认字体
#### Scenario: fonts_default 引用不存在的配置
- **WHEN** fonts_default: "@undefined" 且 fonts.undefined 不存在
- **THEN** 系统抛出 ERROR提示引用的字体配置不存在
#### Scenario: fonts_default 必须引用 fonts 中的配置
- **WHEN** fonts_default: "Arial"(直接字体名称)
- **THEN** 系统抛出 ERROR提示 fonts_default 必须是引用格式
### Requirement: 元素 font 字段必须支持三种引用方式
元素 font 字段 SHALL 支持字符串引用(整体引用)、字典引用(继承覆盖或独立定义)两种形式。
#### Scenario: 字符串整体引用
- **WHEN** 元素定义 font: "@title"
- **THEN** 系统使用 fonts.title 的所有属性
#### Scenario: 字典继承覆盖
- **WHEN** 元素定义 font: {parent: "@title", size: 60}
- **THEN** 系统继承 fonts.title 的所有属性,覆盖 size 为 60
#### Scenario: 字典独立定义
- **WHEN** 元素定义 font: {family: "SimSun", size: 24}
- **THEN** 系统使用定义的属性,未定义的属性继承 fonts_default
#### Scenario: font 字段未定义
- **WHEN** 元素未定义 font 字段
- **THEN** 元素使用 fonts_default 或系统默认字体
### Requirement: parent 字段必须引用 fonts 中的配置
font 字典中的 parent 字段 SHALL 引用 fonts 中已定义的配置名称。
#### Scenario: parent 引用存在的配置
- **WHEN** parent: "@title" 且 fonts.title 存在
- **THEN** 系统成功继承 fonts.title 的属性
#### Scenario: parent 引用不存在的配置
- **WHEN** parent: "@undefined" 且 fonts.undefined 不存在
- **THEN** 系统抛出 ERROR提示引用的字体配置不存在
#### Scenario: parent 必须是引用格式
- **WHEN** parent: "Arial"(直接字体名称)
- **THEN** 系统抛出 ERROR提示 parent 必须是引用格式
### Requirement: 系统必须检测并拒绝引用循环
系统 SHALL 在解析字体引用时检测循环引用,检测到循环时抛出 ERROR。
#### Scenario: 直接循环引用
- **WHEN** fonts.title.parent: "@title"(引用自身)
- **THEN** 系统抛出 ERROR提示检测到自引用
#### Scenario: 间接循环引用
- **WHEN** fonts.a.parent: "@b" 且 fonts.b.parent: "@a"
- **THEN** 系统抛出 ERROR显示完整的引用循环路径
#### Scenario: 深层循环引用
- **WHEN** 引用链深度超过 10 层
- **THEN** 系统抛出 ERROR提示引用深度超限
#### Scenario: 错误信息包含引用路径
- **WHEN** 系统检测到循环引用
- **THEN** 错误信息包含完整的引用路径,如 "fonts.title -> fonts.subtitle -> fonts.title"
### Requirement: 系统必须支持属性继承链
字体属性解析 SHALL 按照优先级顺序继承parent → 当前定义 → fonts_default → 系统默认。
#### Scenario: parent 定义了基础属性
- **WHEN** fonts.title 定义 size: 44元素定义 font: {parent: "@title", bold: true}
- **THEN** 元素使用 size: 44继承、bold: true覆盖
#### Scenario: parent 未定义的属性继承 fonts_default
- **WHEN** fonts_default 定义 size: 18元素定义 font: {parent: "@title"} 且 title 未定义 size
- **THEN** 元素使用 size: 18从 fonts_default 继承)
#### Scenario: 当前定义覆盖 parent
- **WHEN** parent 定义 size: 44当前定义 size: 60
- **THEN** 元素使用 size: 60当前定义覆盖 parent
### Requirement: 模板元素必须支持继承 fonts_default
模板中未定义 font 的元素 SHALL 继承 metadata.fonts_default 配置。
#### Scenario: 模板元素未定义 font
- **WHEN** 模板元素未定义 font 字段
- **THEN** 元素继承 metadata.fonts_default 的配置
#### Scenario: 模板元素定义了 font
- **WHEN** 模板元素定义 font: "@title"
- **THEN** 元素使用 font: "@title",不继承 fonts_default
#### Scenario: fonts_default 未定义时模板元素行为
- **WHEN** 模板元素未定义 font 且 metadata 未定义 fonts_default
- **THEN** 元素使用系统默认字体
### Requirement: 表格元素必须支持 font 和 header_font 字段
表格元素 SHALL 支持 font 和 header_font 字段,分别控制数据单元格和表头的字体样式。
#### Scenario: 表格定义整体字体
- **WHEN** 表格定义 font: {family: "Arial", size: 14}
- **THEN** 数据单元格和表头都应用该字体(表头可被 header_font 覆盖)
#### Scenario: 表格定义表头字体
- **WHEN** 表格定义 header_font: {bold: true, color: "#ffffff"}
- **THEN** 表头应用该字体,数据单元格继承 font 或 fonts_default
#### Scenario: 表头字体继承表格字体
- **WHEN** 表格定义 font: {size: 14} 且 header_font: {parent: "@font", bold: true}
- **THEN** 表头继承 size: 14覆盖 bold: true
#### Scenario: 表格仅定义 header_font
- **WHEN** 表格仅定义 header_font: {bold: true}
- **THEN** 表头应用 bold: true数据单元格继承 fonts_default
### Requirement: 系统必须移除旧的表格字体语法
系统 SHALL 移除 style.font_size 和 style.header_color 字段的处理逻辑。
#### Scenario: 旧语法字段不再生效
- **WHEN** 表格定义 style: {font_size: 14, header_color: "#fff"}
- **THEN** 系统忽略这些字段,使用 font 和 header_font 替代
#### Scenario: style 字段保留用于非字体属性
- **WHEN** 表格定义 style: {header_bg: "#4a90e2"}
- **THEN** 系统正常处理背景色等非字体属性