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

@@ -66,13 +66,15 @@ class Template:
self.elements = self.data.get('elements', [])
@classmethod
def from_data(cls, template_data, template_name, base_dir=None):
def from_data(cls, template_data, template_name, base_dir=None, scope="document", font_resolver=None):
"""从字典创建模板(内联模板或外部模板)
Args:
template_data: 模板数据字典
template_name: 模板名称
base_dir: 资源路径解析的基础目录(外部模板使用模板库文件所在目录,内联模板使用文档目录)
scope: 作用域("document""template"
font_resolver: 字体解析器(用于解析字体引用)
Returns:
Template 对象
@@ -80,6 +82,8 @@ class Template:
obj = cls.__new__(cls)
obj.data = template_data
obj.base_dir = base_dir # 保存 base_dir 用于资源路径解析
obj.scope = scope # 保存作用域
obj.font_resolver = font_resolver # 保存字体解析器
# 初始化条件评估器
obj._condition_evaluator = ConditionEvaluator()
@@ -231,6 +235,37 @@ class Template:
# 深度解析元素中的所有变量引用
rendered_elem = self.resolve_element(elem, vars_values)
# 解析字体引用(如果有 font_resolver
if self.font_resolver and isinstance(rendered_elem, dict):
# 处理普通元素的 font 字段
if 'font' in rendered_elem:
font_config = rendered_elem['font']
# 如果是字符串引用或字典,使用 FontResolver 解析
if isinstance(font_config, (str, dict)):
try:
resolved_font = self.font_resolver.resolve_font(font_config)
rendered_elem['font'] = resolved_font
except ValueError as e:
raise YAMLError(f"字体解析失败: {str(e)}")
elif rendered_elem.get('type') in ['text', 'table']:
# 元素未定义 font使用 fonts_default如果有
if self.font_resolver.fonts_default:
try:
resolved_font = self.font_resolver.resolve_font(None)
rendered_elem['font'] = resolved_font
except ValueError as e:
raise YAMLError(f"字体解析失败: {str(e)}")
# 处理表格元素的 header_font 字段
if rendered_elem.get('type') == 'table' and 'header_font' in rendered_elem:
header_font_config = rendered_elem['header_font']
if isinstance(header_font_config, (str, dict)):
try:
resolved_header_font = self.font_resolver.resolve_font(header_font_config)
rendered_elem['header_font'] = resolved_header_font
except ValueError as e:
raise YAMLError(f"表格 header_font 解析失败: {str(e)}")
# 如果是图片元素且有相对路径,解析为绝对路径
if isinstance(rendered_elem, dict) and rendered_elem.get('type') == 'image':
src = rendered_elem.get('src')