当使用 YAML 多行字符串语法定义文本内容时,python-pptx 会自动 将包含换行符的文本分割成多个段落。修改 _render_text 方法使其 遍历所有段落并应用相同的字体样式(大小、粗体、斜体、颜色、对齐)。 主要变更: - renderers/pptx_renderer.py: 将 p = tf.paragraphs[0] 改为 for p in tf.paragraphs - tests/unit/test_renderers/test_pptx_renderer.py: 新增多行文本测试用例 - openspec/specs/element-rendering/spec.md: 更新 spec 文档 - openspec/changes/archive/: 归档完成的变更
3.5 KiB
3.5 KiB
Context
当前 renderers/pptx_renderer.py 中的 _render_text 方法使用 tf.paragraphs[0] 来应用字体样式。当文本内容包含换行符时(如 YAML 多行字符串语法 |),python-pptx 会自动创建多个段落,但样式只应用到第一个段落。
现有代码问题:
# 创建文本框
textbox = slide.shapes.add_textbox(x, y, w, h)
tf = textbox.text_frame
tf.text = elem.content # 包含换行符时,python-pptx 会创建多个段落
# 只对第一个段落应用样式 - 问题所在
p = tf.paragraphs[0]
if 'size' in elem.font:
p.font.size = Pt(elem.font['size']) # 只有第一个段落生效
测试验证(见 temp/test_multiline_behavior.py):
- 当设置
tf.text = "第一行\n第二行\n第三行"后 len(tf.paragraphs)返回 3- 只有
paragraphs[0].font.size被设置,其余为None
Goals / Non-Goals
Goals:
- 修复多行文本渲染时,字体样式只应用于第一段的问题
- 确保所有字体样式属性(size, bold, italic, color, align)应用到所有段落
- 保持单行文本的渲染行为不变
- 添加完整的单元测试覆盖
Non-Goals:
- 不支持不同段落使用不同样式(当前架构是一个文本框一个样式配置)
- 不修改 HTML 渲染器(HTML 渲染器无此问题)
Decisions
决策1:遍历所有段落应用样式
选择遍历 tf.paragraphs 并对每个段落应用样式,而不是其他方案。
替代方案考虑:
- 逐段设置内容(
add_paragraph()):需要重写整个文本设置逻辑,改动较大 - 只对第一个段落设置(当前行为):存在 bug,不可接受
- 遍历所有段落(选择):最小改动,符合当前设计语义
理由:
- 代码改动最小,只需将
p = tf.paragraphs[0]改为for p in tf.paragraphs: - 符合当前设计语义:一个文本框使用统一的字体样式配置
- 向后兼容:单行文本只有一个段落,行为不变
决策2:不增加新的配置选项
不添加类似 apply_style_to_all_paragraphs 的配置选项。
理由:
- 这是 bug 修复,不是新功能
- 当前设计中,一个文本框就应该使用统一样式
- 添加配置会增加复杂度,且用途有限
决策3:测试策略
在单元测试中新增多行文本测试用例,使用 Mock 验证样式应用到所有段落。
理由:
- 现有测试只验证
paragraphs[0]的样式 - 需要验证修复后的行为正确
- 测试应覆盖多种换行情况(2行、3行、多行)
Risks / Trade-offs
风险1:现有测试可能失败
风险:现有测试中 Mock 的 paragraphs 只有一个元素,改为遍历后可能通过但不完整。
缓解:新增专门的多行文本测试,Mock 返回多个段落对象。
风险2:性能影响
风险:遍历所有段落可能对极多段落的文本有轻微性能影响。
评估:影响可忽略不计。通常文本框段落数量有限(<100),遍历开销极小。
风险3:空段落行为
风险:如果存在空段落(如连续换行),可能产生意外行为。
评估:python-pptx 会自动处理空段落,样式设置到空段落无副作用。
Migration Plan
无需迁移计划。这是 bug 修复:
- 向后兼容:单行文本行为完全不变
- 多行文本:从错误行为变为正确行为
- 无 API 变更
- 无配置变更
Open Questions
无。这是一个明确且范围有限的 bug 修复。