1
0

feat: 实现模板库metadata和跨域字体引用系统

实现了统一的metadata结构和字体作用域系统,支持文档和模板库之间的单向字体引用。

主要变更:
- 模板库必须包含metadata字段(包括size、fonts、fonts_default)
- 实现文档和模板库的size一致性校验
- 实现字体作用域系统(文档可引用模板库字体,反之不可)
- 实现跨域循环引用检测
- 实现fonts_default级联规则(模板库→文档→系统默认)
- 添加错误代码常量(SIZE_MISMATCH、FONT_NOT_FOUND等)
- 更新文档和开发者指南

测试覆盖:
- 新增33个测试(单元测试20个,集成测试13个)
- 所有457个测试通过

Breaking Changes:
- 模板库文件必须包含metadata字段
- 模板库metadata.size为必填字段
- 文档和模板库的size必须一致

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 18:12:05 +08:00
parent f1aae96a04
commit 98098dc911
25 changed files with 2794 additions and 141 deletions

View File

@@ -252,52 +252,6 @@ templates:
assert len(result["elements"]) == 1
assert result["elements"][0].content == "Test"
def test_backward_compat_template_only(self, temp_dir, sample_template):
"""测试向后兼容:纯模板模式"""
yaml_content = """
slides:
- elements: []
templates:
test-template:
vars:
- name: title
elements:
- type: text
content: "{title}"
box: [0, 0, 1, 1]
font: {}
"""
yaml_path = temp_dir / "test.yaml"
yaml_path.write_text(yaml_content)
pres = Presentation(str(yaml_path))
slide_data = {
"template": "test-template",
"vars": {"title": "Test"}
}
result = pres.render_slide(slide_data)
# 应该只有模板元素
assert len(result["elements"]) == 1
assert result["elements"][0].content == "Test"
def test_backward_compat_custom_only(self, sample_yaml):
"""测试向后兼容:纯自定义元素模式"""
pres = Presentation(str(sample_yaml))
slide_data = {
"elements": [
{"type": "text", "content": "Custom", "box": [0, 0, 1, 1], "font": {}}
]
}
result = pres.render_slide(slide_data)
# 应该只有自定义元素
assert len(result["elements"]) == 1
assert result["elements"][0].content == "Custom"
def test_hybrid_mode_with_inline_template(self, temp_dir):
"""测试内联模板与自定义元素混合使用"""
@@ -485,3 +439,140 @@ class TestSlideDescription:
assert len(result["elements"]) == 1
assert result["elements"][0].content == "Test Content"
class TestSizeConsistency:
"""Size 一致性校验测试类"""
def test_size_consistency_16_9(self, temp_dir):
"""测试文档和模板库 size 都是 16:9 时正常加载"""
# 创建文档
doc_content = """
metadata:
size: "16:9"
slides:
- elements: []
"""
doc_path = temp_dir / "doc.yaml"
doc_path.write_text(doc_content)
# 创建模板库
template_content = """
metadata:
size: "16:9"
templates:
test:
elements: []
"""
template_path = temp_dir / "templates.yaml"
template_path.write_text(template_content)
# 应该正常加载
pres = Presentation(str(doc_path), str(template_path))
assert pres.size == "16:9"
def test_size_consistency_4_3(self, temp_dir):
"""测试文档和模板库 size 都是 4:3 时正常加载"""
# 创建文档
doc_content = """
metadata:
size: "4:3"
slides:
- elements: []
"""
doc_path = temp_dir / "doc.yaml"
doc_path.write_text(doc_content)
# 创建模板库
template_content = """
metadata:
size: "4:3"
templates:
test:
elements: []
"""
template_path = temp_dir / "templates.yaml"
template_path.write_text(template_content)
# 应该正常加载
pres = Presentation(str(doc_path), str(template_path))
assert pres.size == "4:3"
def test_size_mismatch_error(self, temp_dir):
"""测试文档和模板库 size 不一致时抛出错误"""
# 创建文档
doc_content = """
metadata:
size: "16:9"
slides:
- elements: []
"""
doc_path = temp_dir / "doc.yaml"
doc_path.write_text(doc_content)
# 创建模板库
template_content = """
metadata:
size: "4:3"
templates:
test:
elements: []
"""
template_path = temp_dir / "templates.yaml"
template_path.write_text(template_content)
# 应该抛出错误
with pytest.raises(YAMLError, match="文档尺寸.*与模板库尺寸.*不一致"):
Presentation(str(doc_path), str(template_path))
def test_template_library_missing_metadata(self, temp_dir):
"""测试模板库缺少 metadata 时抛出错误"""
# 创建文档
doc_content = """
metadata:
size: "16:9"
slides:
- elements: []
"""
doc_path = temp_dir / "doc.yaml"
doc_path.write_text(doc_content)
# 创建模板库(缺少 metadata
template_content = """
templates:
test:
elements: []
"""
template_path = temp_dir / "templates.yaml"
template_path.write_text(template_content)
# 应该抛出错误
with pytest.raises(YAMLError, match="模板库必须包含 metadata 字段"):
Presentation(str(doc_path), str(template_path))
def test_template_library_missing_size(self, temp_dir):
"""测试模板库 metadata 缺少 size 时抛出错误"""
# 创建文档
doc_content = """
metadata:
size: "16:9"
slides:
- elements: []
"""
doc_path = temp_dir / "doc.yaml"
doc_path.write_text(doc_content)
# 创建模板库metadata 缺少 size
template_content = """
metadata:
description: "测试模板库"
templates:
test:
elements: []
"""
template_path = temp_dir / "templates.yaml"
template_path.write_text(template_content)
# 应该抛出错误
with pytest.raises(YAMLError, match="metadata 缺少必填字段 'size'"):
Presentation(str(doc_path), str(template_path))