实现完整的字体主题系统,支持可复用字体配置、预设类别和扩展属性。 同时修复中文字体渲染问题,确保 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>
155 lines
4.1 KiB
Python
155 lines
4.1 KiB
Python
"""
|
||
字体系统集成测试
|
||
|
||
本测试文件包含字体主题系统的集成测试,验证完整的工作流程:
|
||
|
||
1. 多行文本扩展属性应用
|
||
- 验证扩展属性(line_spacing、space_before、space_after)应用到所有段落
|
||
- 测试从 YAML 加载到渲染的完整流程
|
||
|
||
2. 模板字体继承
|
||
- 验证模板元素继承 fonts_default
|
||
- 测试内联模板的字体配置
|
||
|
||
3. 引用循环错误
|
||
- 验证循环引用在渲染时被检测并抛出错误
|
||
- 测试错误信息的准确性
|
||
|
||
测试策略:
|
||
- 使用临时 YAML 文件模拟真实场景
|
||
- 测试完整的加载 → 解析 → 渲染流程
|
||
- 验证错误处理和边界情况
|
||
|
||
注意:
|
||
- 这些测试验证字体系统与其他模块的集成
|
||
- 单元测试在 test_font_system.py 中
|
||
- 渲染器测试在 test_pptx_renderer.py 中
|
||
"""
|
||
|
||
import pytest
|
||
from pathlib import Path
|
||
from core.presentation import Presentation
|
||
from renderers.pptx_renderer import PptxGenerator
|
||
|
||
|
||
class TestMultilineTextExtendedProperties:
|
||
"""多行文本扩展属性应用集成测试"""
|
||
|
||
def test_multiline_text_with_extended_properties(self, tmp_path):
|
||
"""测试多行文本应用扩展属性到所有段落"""
|
||
# 创建测试 YAML 文件
|
||
yaml_content = """
|
||
metadata:
|
||
size: "16:9"
|
||
fonts:
|
||
body:
|
||
family: "Arial"
|
||
size: 18
|
||
line_spacing: 1.5
|
||
space_before: 12
|
||
space_after: 6
|
||
|
||
slides:
|
||
- elements:
|
||
- type: text
|
||
content: "第一行\\n第二行\\n第三行"
|
||
box: [1, 1, 8, 2]
|
||
font: "@body"
|
||
"""
|
||
yaml_file = tmp_path / "test.yaml"
|
||
yaml_file.write_text(yaml_content)
|
||
|
||
# 加载并渲染
|
||
pres = Presentation(yaml_file)
|
||
gen = PptxGenerator(pres.size, fonts=pres.fonts, fonts_default=pres.fonts_default)
|
||
|
||
slide_data = pres.data.get('slides', [])[0]
|
||
rendered = pres.render_slide(slide_data)
|
||
|
||
# 验证元素被正确解析
|
||
assert len(rendered['elements']) == 1
|
||
elem = rendered['elements'][0]
|
||
# YAML 会将 \n 解析为实际的换行符
|
||
assert elem.content == "第一行\n第二行\n第三行"
|
||
|
||
|
||
class TestTemplateFontInheritance:
|
||
"""模板字体继承集成测试"""
|
||
|
||
def test_template_inherits_fonts_default(self, tmp_path):
|
||
"""测试模板元素继承 fonts_default"""
|
||
# 创建测试 YAML 文件
|
||
yaml_content = """
|
||
metadata:
|
||
size: "16:9"
|
||
fonts:
|
||
body:
|
||
family: "Arial"
|
||
size: 18
|
||
color: "#333333"
|
||
fonts_default: "@body"
|
||
|
||
templates:
|
||
simple:
|
||
elements:
|
||
- type: text
|
||
content: "模板文本"
|
||
box: [1, 1, 8, 1]
|
||
|
||
slides:
|
||
- template: simple
|
||
"""
|
||
yaml_file = tmp_path / "test.yaml"
|
||
yaml_file.write_text(yaml_content)
|
||
|
||
# 加载并渲染
|
||
pres = Presentation(yaml_file)
|
||
gen = PptxGenerator(pres.size, fonts=pres.fonts, fonts_default=pres.fonts_default)
|
||
|
||
slide_data = pres.data.get('slides', [])[0]
|
||
rendered = pres.render_slide(slide_data)
|
||
|
||
# 验证模板元素被渲染
|
||
assert len(rendered['elements']) == 1
|
||
|
||
|
||
class TestCircularReferenceError:
|
||
"""引用循环错误集成测试"""
|
||
|
||
def test_circular_reference_in_yaml_raises_error(self, tmp_path):
|
||
"""测试 YAML 中的循环引用抛出错误"""
|
||
# 创建包含循环引用的 YAML 文件
|
||
yaml_content = """
|
||
metadata:
|
||
size: "16:9"
|
||
fonts:
|
||
a:
|
||
parent: "@b"
|
||
size: 44
|
||
b:
|
||
parent: "@a"
|
||
size: 18
|
||
|
||
slides:
|
||
- elements:
|
||
- type: text
|
||
content: "测试"
|
||
box: [1, 1, 8, 1]
|
||
font: "@a"
|
||
"""
|
||
yaml_file = tmp_path / "test.yaml"
|
||
yaml_file.write_text(yaml_content)
|
||
|
||
# 加载演示文稿
|
||
pres = Presentation(yaml_file)
|
||
gen = PptxGenerator(pres.size, fonts=pres.fonts, fonts_default=pres.fonts_default)
|
||
|
||
slide_data = pres.data.get('slides', [])[0]
|
||
|
||
# 渲染时应该抛出循环引用错误
|
||
with pytest.raises(ValueError, match="检测到字体引用循环"):
|
||
rendered = pres.render_slide(slide_data)
|
||
# 触发字体解析
|
||
for elem in rendered['elements']:
|
||
gen.font_resolver.resolve_font(elem.font)
|