## Context 当前 `renderers/pptx_renderer.py` 中的 `_render_text` 方法使用 `tf.paragraphs[0]` 来应用字体样式。当文本内容包含换行符时(如 YAML 多行字符串语法 `|`),python-pptx 会自动创建多个段落,但样式只应用到第一个段落。 现有代码问题: ```python # 创建文本框 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` 并对每个段落应用样式,而不是其他方案。 **替代方案考虑**: 1. **逐段设置内容**(`add_paragraph()`):需要重写整个文本设置逻辑,改动较大 2. **只对第一个段落设置**(当前行为):存在 bug,不可接受 3. **遍历所有段落**(选择):最小改动,符合当前设计语义 **理由**: - 代码改动最小,只需将 `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 修复。