diff --git a/haystack/components/converters/pptx.py b/haystack/components/converters/pptx.py index f4d9fed086..2ec673c332 100644 --- a/haystack/components/converters/pptx.py +++ b/haystack/components/converters/pptx.py @@ -101,7 +101,13 @@ def _process_paragraph(self, paragraph: "_Paragraph") -> str: if self.link_format == "none": return paragraph.text parts = [] - for run in paragraph.runs: + runs = {run._r: run for run in paragraph.runs} + for content in paragraph._element.content_children: + run = runs.get(content) + if run is None: + if content.text == "\v": + parts.append("\v") + continue if run.hyperlink and run.hyperlink.address: if self.link_format == "markdown": parts.append(f"[{run.text}]({run.hyperlink.address})") diff --git a/releasenotes/notes/fix-pptx-soft-line-breaks-97f4237b1266d42f.yaml b/releasenotes/notes/fix-pptx-soft-line-breaks-97f4237b1266d42f.yaml new file mode 100644 index 0000000000..7f435f7d93 --- /dev/null +++ b/releasenotes/notes/fix-pptx-soft-line-breaks-97f4237b1266d42f.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixed ``PPTXToDocument`` dropping soft line breaks from paragraphs when ``link_format`` is + ``"markdown"`` or ``"plain"``. Soft line breaks are now preserved as vertical tabs + (``"\v"``), matching the behavior of the default ``"none"`` format. diff --git a/test/components/converters/test_pptx_to_document.py b/test/components/converters/test_pptx_to_document.py index 647a2a3571..679bf0a478 100644 --- a/test/components/converters/test_pptx_to_document.py +++ b/test/components/converters/test_pptx_to_document.py @@ -2,11 +2,14 @@ # # SPDX-License-Identifier: Apache-2.0 +import io import logging import os from pathlib import Path import pytest +from pptx import Presentation +from pptx.util import Inches from haystack.components.converters.pptx import PPTXToDocument from haystack.dataclasses import ByteStream @@ -123,3 +126,17 @@ def test_no_link_extraction(self, test_files_path): assert "https://example.com" not in content assert "Example" in content + + @pytest.mark.parametrize("link_format", ["markdown", "plain"]) + def test_soft_line_break_preserved(self, link_format): + presentation = Presentation() + slide = presentation.slides.add_slide(presentation.slide_layouts[6]) + text_frame = slide.shapes.add_textbox(Inches(1), Inches(1), Inches(7), Inches(2)).text_frame + text_frame.text = "Account: Alice\vBalance: 100" + buffer = io.BytesIO() + presentation.save(buffer) + + converter = PPTXToDocument(link_format=link_format) + output = converter.run(sources=[ByteStream(data=buffer.getvalue())]) + + assert output["documents"][0].content == "Account: Alice\vBalance: 100"