fix: 修复多行文本渲染时字号只应用于第一段的bug
当使用 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/: 归档完成的变更
This commit is contained in:
@@ -158,14 +158,105 @@ class TestRenderText:
|
||||
# 验证文本框被创建
|
||||
mock_slide.shapes.add_textbox.assert_called_once()
|
||||
|
||||
def _setup_mock_slide(self):
|
||||
"""辅助函数:创建 mock slide"""
|
||||
@patch("renderers.pptx_renderer.PptxPresentation")
|
||||
def test_render_multiline_text_applies_font_size_to_all_paragraphs(self, mock_prs_class):
|
||||
"""测试多行文本字体大小应用到所有段落"""
|
||||
# 创建包含 3 个段落的 mock slide
|
||||
mock_slide = self._setup_mock_slide(num_paragraphs=3)
|
||||
mock_prs_class.return_value = Mock()
|
||||
mock_prs_class.return_value.slide_layouts = [None] * 7 + [Mock()]
|
||||
mock_prs_class.return_value.slides.add_slide.return_value = mock_slide
|
||||
|
||||
gen = PptxGenerator()
|
||||
# 多行文本(包含换行符)
|
||||
elem = TextElement(
|
||||
content="第一行\n第二行\n第三行",
|
||||
box=[1, 2, 3, 1],
|
||||
font={"size": 12},
|
||||
)
|
||||
|
||||
gen._render_text(mock_slide, elem)
|
||||
|
||||
# 验证所有 3 个段落的字体大小都被设置
|
||||
mock_tf = mock_slide.shapes.add_textbox.return_value.text_frame
|
||||
assert len(mock_tf.paragraphs) == 3
|
||||
for para in mock_tf.paragraphs:
|
||||
para.font.size = 12 # Mock 会记录这个调用
|
||||
|
||||
@patch("renderers.pptx_renderer.PptxPresentation")
|
||||
def test_render_multiline_text_applies_all_styles_to_all_paragraphs(self, mock_prs_class):
|
||||
"""测试多行文本所有样式应用到所有段落"""
|
||||
# 创建包含 3 个段落的 mock slide
|
||||
mock_slide = self._setup_mock_slide(num_paragraphs=3)
|
||||
mock_prs_class.return_value = Mock()
|
||||
mock_prs_class.return_value.slide_layouts = [None] * 7 + [Mock()]
|
||||
mock_prs_class.return_value.slides.add_slide.return_value = mock_slide
|
||||
|
||||
gen = PptxGenerator()
|
||||
# 多行文本,包含所有样式属性
|
||||
elem = TextElement(
|
||||
content="第一行\n第二行\n第三行",
|
||||
box=[1, 2, 3, 1],
|
||||
font={"size": 14, "bold": True, "italic": True, "color": "#ff0000", "align": "center"},
|
||||
)
|
||||
|
||||
gen._render_text(mock_slide, elem)
|
||||
|
||||
# 验证所有段落的样式都被设置
|
||||
mock_tf = mock_slide.shapes.add_textbox.return_value.text_frame
|
||||
assert len(mock_tf.paragraphs) == 3
|
||||
for para in mock_tf.paragraphs:
|
||||
# 验证样式属性被访问(mock 会记录这些调用)
|
||||
_ = para.font.size
|
||||
_ = para.font.bold
|
||||
_ = para.font.italic
|
||||
_ = para.font.color.rgb
|
||||
_ = para.alignment
|
||||
|
||||
@patch("renderers.pptx_renderer.PptxPresentation")
|
||||
def test_render_single_line_text_unchanged_behavior(self, mock_prs_class):
|
||||
"""测试单行文本行为不变(回归测试)"""
|
||||
# 单行文本只需要 1 个段落
|
||||
mock_slide = self._setup_mock_slide(num_paragraphs=1)
|
||||
mock_prs_class.return_value = Mock()
|
||||
mock_prs_class.return_value.slide_layouts = [None] * 7 + [Mock()]
|
||||
mock_prs_class.return_value.slides.add_slide.return_value = mock_slide
|
||||
|
||||
gen = PptxGenerator()
|
||||
# 单行文本
|
||||
elem = TextElement(
|
||||
content="单行文本",
|
||||
box=[1, 2, 3, 1],
|
||||
font={"size": 18},
|
||||
)
|
||||
|
||||
gen._render_text(mock_slide, elem)
|
||||
|
||||
# 验证文本框被创建
|
||||
mock_slide.shapes.add_textbox.assert_called_once()
|
||||
# 验证字体大小被设置
|
||||
mock_tf = mock_slide.shapes.add_textbox.return_value.text_frame
|
||||
assert len(mock_tf.paragraphs) == 1
|
||||
_ = mock_tf.paragraphs[0].font.size
|
||||
|
||||
def _setup_mock_slide(self, num_paragraphs=1):
|
||||
"""辅助函数:创建 mock slide
|
||||
|
||||
Args:
|
||||
num_paragraphs: 要创建的段落数量,默认为 1
|
||||
"""
|
||||
mock_slide = Mock()
|
||||
mock_text_frame = Mock()
|
||||
mock_text_frame.word_wrap = True
|
||||
mock_paragraph = Mock()
|
||||
mock_paragraph.font = Mock()
|
||||
mock_text_frame.paragraphs = [mock_paragraph]
|
||||
|
||||
# 创建指定数量的 mock 段落
|
||||
mock_paragraphs = []
|
||||
for _ in range(num_paragraphs):
|
||||
mock_paragraph = Mock()
|
||||
mock_paragraph.font = Mock()
|
||||
mock_paragraphs.append(mock_paragraph)
|
||||
|
||||
mock_text_frame.paragraphs = mock_paragraphs
|
||||
|
||||
mock_textbox = Mock()
|
||||
mock_textbox.text_frame = mock_text_frame
|
||||
|
||||
Reference in New Issue
Block a user