实现完整的字体主题系统,支持可复用字体配置、预设类别和扩展属性。 同时修复中文字体渲染问题,确保 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>
208 lines
6.5 KiB
Markdown
208 lines
6.5 KiB
Markdown
# Font Extended
|
||
|
||
## Purpose
|
||
|
||
扩展字体属性在现有 size、bold、italic、color、align 基础上,新增 family、underline、strikethrough、line_spacing、space_before、space_after、baseline、caps 等属性,提供更完整的字体样式控制能力。
|
||
|
||
## Requirements
|
||
|
||
### Requirement: 元素 font 必须支持 family 属性
|
||
|
||
font 字段 SHALL 支持 family 属性,用于指定字体族名称。
|
||
|
||
#### Scenario: 设置字体族
|
||
|
||
- **WHEN** 元素定义 font: {family: "Arial"}
|
||
- **THEN** 系统将字体族设置为 Arial
|
||
|
||
#### Scenario: family 字段为 None
|
||
|
||
- **WHEN** 元素定义 font: {size: 18}(未定义 family)
|
||
- **THEN** 系统使用继承或默认的字体族
|
||
|
||
### Requirement: 元素 font 必须支持 underline 属性
|
||
|
||
font 字段 SHALL 支持 underline 属性,控制文本是否带下划线。
|
||
|
||
#### Scenario: 启用下划线
|
||
|
||
- **WHEN** 元素定义 font: {underline: true}
|
||
- **THEN** 系统为文本添加下划线
|
||
|
||
#### Scenario: 禁用下划线
|
||
|
||
- **WHEN** 元素定义 font: {underline: false}
|
||
- **THEN** 系统不为文本添加下划线
|
||
|
||
#### Scenario: underline 未定义
|
||
|
||
- **WHEN** 元素定义 font: {size: 18}(未定义 underline)
|
||
- **THEN** 系统使用继承或默认的下划线设置
|
||
|
||
### Requirement: 元素 font 必须支持 strikethrough 属性
|
||
|
||
font 字段 SHALL 支持 strikethrough 属性,控制文本是否带删除线。
|
||
|
||
#### Scenario: 启用删除线
|
||
|
||
- **WHEN** 元素定义 font: {strikethrough: true}
|
||
- **THEN** 系统为文本添加删除线
|
||
|
||
#### Scenario: 禁用删除线
|
||
|
||
- **WHEN** 元素定义 font: {strikethrough: false}
|
||
- **THEN** 系统不为文本添加删除线
|
||
|
||
#### Scenario: strikethrough 未定义
|
||
|
||
- **WHEN** 元素定义 font: {size: 18}(未定义 strikethrough)
|
||
- **THEN** 系统使用继承或默认的删除线设置
|
||
|
||
### Requirement: 元素 font 必须支持 line_spacing 属性
|
||
|
||
font 字段 SHALL 支持 line_spacing 属性,控制行距倍数。
|
||
|
||
#### Scenario: 设置行距倍数
|
||
|
||
- **WHEN** 元素定义 font: {line_spacing: 1.5}
|
||
- **THEN** 系统将行距设置为 1.5 倍
|
||
|
||
#### Scenario: line_spacing 为 1.0
|
||
|
||
- **WHEN** 元素定义 font: {line_spacing: 1.0}
|
||
- **THEN** 系统使用单倍行距
|
||
|
||
#### Scenario: line_spacing 未定义
|
||
|
||
- **WHEN** 元素定义 font: {size: 18}(未定义 line_spacing)
|
||
- **THEN** 系统使用继承或默认的行距设置
|
||
|
||
### Requirement: 元素 font 必须支持 space_before 属性
|
||
|
||
font 字段 SHALL 支持 space_before 属性,控制段前间距(单位:磅)。
|
||
|
||
#### Scenario: 设置段前间距
|
||
|
||
- **WHEN** 元素定义 font: {space_before: 12}
|
||
- **THEN** 系统将段前间距设置为 12 磅
|
||
|
||
#### Scenario: space_before 为 0
|
||
|
||
- **WHEN** 元素定义 font: {space_before: 0}
|
||
- **THEN** 系统不添加段前间距
|
||
|
||
#### Scenario: space_before 未定义
|
||
|
||
- **WHEN** 元素定义 font: {size: 18}(未定义 space_before)
|
||
- **THEN** 系统使用继承或默认的段前间距
|
||
|
||
### Requirement: 元素 font 必须支持 space_after 属性
|
||
|
||
font 字段 SHALL 支持 space_after 属性,控制段后间距(单位:磅)。
|
||
|
||
#### Scenario: 设置段后间距
|
||
|
||
- **WHEN** 元素定义 font: {space_after: 12}
|
||
- **THEN** 系统将段后间距设置为 12 磅
|
||
|
||
#### Scenario: space_after 为 0
|
||
|
||
- **WHEN** 元素定义 font: {space_after: 0}
|
||
- **THEN** 系统不添加段后间距
|
||
|
||
#### Scenario: space_after 未定义
|
||
|
||
- **WHEN** 元素定义 font: {size: 18}(未定义 space_after)
|
||
- **THEN** 系统使用继承或默认的段后间距
|
||
|
||
### Requirement: 元素 font 必须支持 baseline 属性
|
||
|
||
font 字段 SHALL 支持 baseline 属性,控制文本基线位置(normal、superscript、subscript)。
|
||
|
||
#### Scenario: 设置为上标
|
||
|
||
- **WHEN** 元素定义 font: {baseline: "superscript"}
|
||
- **THEN** 系统将文本设置为上标
|
||
|
||
#### Scenario: 设置为下标
|
||
|
||
- **WHEN** 元素定义 font: {baseline: "subscript"}
|
||
- **THEN** 系统将文本设置为下标
|
||
|
||
#### Scenario: 设置为正常基线
|
||
|
||
- **WHEN** 元素定义 font: {baseline: "normal"}
|
||
- **THEN** 系统使用正常基线位置
|
||
|
||
#### Scenario: baseline 未定义
|
||
|
||
- **WHEN** 元素定义 font: {size: 18}(未定义 baseline)
|
||
- **THEN** 系统使用正常基线位置
|
||
|
||
#### Scenario: baseline 值无效
|
||
|
||
- **WHEN** 元素定义 font: {baseline: "invalid"}
|
||
- **THEN** 系统抛出 ERROR,提示 baseline 必须是 normal、superscript 或 subscript
|
||
|
||
### Requirement: 元素 font 必须支持 caps 属性
|
||
|
||
font 字段 SHALL 支持 caps 属性,控制文本大小写转换(normal、allcaps、smallcaps)。
|
||
|
||
#### Scenario: 设置为全大写
|
||
|
||
- **WHEN** 元素定义 font: {caps: "allcaps"}
|
||
- **THEN** 系统将文本转换为大写
|
||
|
||
#### Scenario: 设置为小型大写
|
||
|
||
- **WHEN** 元素定义 font: {caps: "smallcaps"}
|
||
- **THEN** 系统将文本转换为小型大写字母
|
||
|
||
#### Scenario: 设置为正常大小写
|
||
|
||
- **WHEN** 元素定义 font: {caps: "normal"}
|
||
- **THEN** 系统保持文本原始大小写
|
||
|
||
#### Scenario: caps 未定义
|
||
|
||
- **WHEN** 元素定义 font: {size: 18}(未定义 caps)
|
||
- **THEN** 系统保持文本原始大小写
|
||
|
||
#### Scenario: caps 值无效
|
||
|
||
- **WHEN** 元素定义 font: {caps: "invalid"}
|
||
- **THEN** 系统抛出 ERROR,提示 caps 必须是 normal、allcaps 或 smallcaps
|
||
|
||
### Requirement: 多行文本必须将所有属性应用到每个段落
|
||
|
||
当文本内容包含换行符时,系统 SHALL 将所有字体属性(包括扩展属性)应用到文本框中的每个段落。
|
||
|
||
#### Scenario: 多行文本应用扩展属性
|
||
|
||
- **WHEN** 文本内容包含换行符且定义 font: {size: 12, underline: true, line_spacing: 1.5}
|
||
- **THEN** 系统将所有属性(size、underline、line_spacing)应用到所有段落
|
||
|
||
#### Scenario: 多行文本每个段落样式一致
|
||
|
||
- **WHEN** 文本包含多个换行符且定义了 font 属性
|
||
- **THEN** 每个段落的字体样式都应一致
|
||
|
||
### Requirement: 扩展属性必须支持继承机制
|
||
|
||
扩展属性 SHALL 遵循与基础属性相同的继承机制:parent → 当前定义 → fonts_default → 系统默认。
|
||
|
||
#### Scenario: 扩展属性从 parent 继承
|
||
|
||
- **WHEN** parent 定义 underline: true,当前定义未指定 underline
|
||
- **THEN** 元素使用 underline: true(从 parent 继承)
|
||
|
||
#### Scenario: 扩展属性从 fonts_default 继承
|
||
|
||
- **WHEN** fonts_default 定义 line_spacing: 1.5,元素未指定 line_spacing
|
||
- **THEN** 元素使用 line_spacing: 1.5(从 fonts_default 继承)
|
||
|
||
#### Scenario: 当前定义覆盖继承的扩展属性
|
||
|
||
- **WHEN** parent 定义 space_before: 12,当前定义 space_before: 24
|
||
- **THEN** 元素使用 space_before: 24(当前定义覆盖)
|