Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/markitdown/src/markitdown/converters/_xlsx_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ def convert(
sheets = _read_xlsx_sheets(file_stream)
md_content = ""
for s in sheets:
# A completely empty sheet has no columns. pandas then emits a
# column-less HTML table that markdownify turns into broken syntax
# ("|\n| |"). Skip it. A header-only sheet still has columns and
# already renders as a well-formed empty table.
if sheets[s].columns.empty:
continue
md_content += f"## {s}\n"
html_content = sheets[s].to_html(index=False)
md_content += (
Expand Down Expand Up @@ -196,6 +202,10 @@ def convert(
sheets = pd.read_excel(file_stream, sheet_name=None, engine="xlrd")
md_content = ""
for s in sheets:
# Same empty-sheet guard as XlsxConverter: no columns means pandas
# would emit a column-less table that is not valid Markdown.
if sheets[s].columns.empty:
continue
md_content += f"## {s}\n"
html_content = sheets[s].to_html(index=False)
md_content += (
Expand Down
60 changes: 60 additions & 0 deletions packages/markitdown/tests/test_xlsx_empty_sheet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import io

from openpyxl import Workbook

from markitdown import MarkItDown, StreamInfo


def _xlsx_bytes(workbook: Workbook) -> bytes:
buf = io.BytesIO()
workbook.save(buf)
return buf.getvalue()


def test_completely_empty_sheet_is_skipped() -> None:
"""A sheet with no columns must not emit malformed table syntax."""
workbook = Workbook()
workbook.active.title = "HasData"
workbook.active["A1"] = "col"
workbook.active["A2"] = "v"
workbook.create_sheet("CompletelyEmpty")

result = MarkItDown().convert_stream(
io.BytesIO(_xlsx_bytes(workbook)),
stream_info=StreamInfo(extension=".xlsx"),
)

assert "## HasData" in result.markdown
assert "| col |" in result.markdown
assert "| v |" in result.markdown
assert "CompletelyEmpty" not in result.markdown
assert "|\n| |" not in result.markdown


def test_header_only_sheet_keeps_empty_table() -> None:
"""A sheet with a header row but no data already produces a valid table."""
workbook = Workbook()
workbook.active.title = "HeaderOnly"
workbook.active["A1"] = "col"

result = MarkItDown().convert_stream(
io.BytesIO(_xlsx_bytes(workbook)),
stream_info=StreamInfo(extension=".xlsx"),
)

assert "## HeaderOnly" in result.markdown
assert "| col |" in result.markdown
assert "| --- |" in result.markdown


def test_single_completely_empty_workbook_emits_nothing() -> None:
workbook = Workbook()
workbook.active.title = "Empty"

result = MarkItDown().convert_stream(
io.BytesIO(_xlsx_bytes(workbook)),
stream_info=StreamInfo(extension=".xlsx"),
)

assert result.markdown.strip() == ""
assert "|" not in result.markdown