实现完整的字体主题系统,支持可复用字体配置、预设类别和扩展属性。 同时修复中文字体渲染问题,确保 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>
7.1 KiB
7.1 KiB
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 系统正常处理背景色等非字体属性