diff --git a/packages/markitdown/src/markitdown/converters/_pptx_converter.py b/packages/markitdown/src/markitdown/converters/_pptx_converter.py
index 76cf7751bc..488d4cac10 100644
--- a/packages/markitdown/src/markitdown/converters/_pptx_converter.py
+++ b/packages/markitdown/src/markitdown/converters/_pptx_converter.py
@@ -31,6 +31,18 @@
ACCEPTED_FILE_EXTENSIONS = [".pptx"]
+def _normalize_soft_line_breaks(text: str) -> str:
+ """Turn the soft line breaks python-pptx reports into newlines.
+
+ A soft line break (Shift+Enter) is an ```` inside one paragraph, and
+ python-pptx renders it as a vertical tab. A vertical tab is not Markdown: it
+ is a C0 control that renderers drop and that nothing downstream treats as
+ whitespace, so the lines on either side of it run together. The paragraphs
+ of a shape already arrive joined with newlines, so use the same separator.
+ """
+ return text.replace("\v", "\n")
+
+
class PptxConverter(DocumentConverter):
"""
Converts PPTX files to Markdown. Supports heading, tables and images with alt text.
@@ -105,10 +117,12 @@ def get_shape_content(shape, **kwargs):
# Text areas
elif shape.has_text_frame:
- text = shape.text or ""
+ text = _normalize_soft_line_breaks(shape.text or "")
if shape == title:
if text.strip():
- md_content += "# " + text.lstrip() + "\n"
+ # An ATX heading cannot span lines: a newline would
+ # close the heading and leave the rest as body text.
+ md_content += "# " + text.replace("\n", " ").strip() + "\n"
else:
md_content += text + "\n"
@@ -141,7 +155,11 @@ def get_shape_content(shape, **kwargs):
# has merely been opened, so having one says nothing about there
# being notes to read. Only head a section that has content.
notes_frame = slide.notes_slide.notes_text_frame
- notes_text = (notes_frame.text or "") if notes_frame is not None else ""
+ notes_text = (
+ _normalize_soft_line_breaks(notes_frame.text or "")
+ if notes_frame is not None
+ else ""
+ )
if notes_text.strip():
md_content += "\n\n### Notes:\n" + notes_text
md_content = md_content.strip()
@@ -309,10 +327,11 @@ def _convert_table_to_markdown(self, table, **kwargs):
for row in table.rows:
html_table += "
"
for cell in row.cells:
+ cell_text = html.escape(_normalize_soft_line_breaks(cell.text))
if first_row:
- html_table += "| " + html.escape(cell.text) + " | "
+ html_table += "" + cell_text + " | "
else:
- html_table += "" + html.escape(cell.text) + " | "
+ html_table += "" + cell_text + " | "
html_table += "
"
first_row = False
html_table += "