From 7caa54bd731f84d685299702c11c0be339715b97 Mon Sep 17 00:00:00 2001 From: Yash Raj Pandey Date: Thu, 1 Oct 2026 11:12:21 -0400 Subject: [PATCH] fix(md): keep inline HTML and autolinks inside GFM table cells Marko parses
, and other inline tags as InlineHTML, and as AutoLink. _iterate_elements has no branch for either, so in a table cell they reach the fallback branch, which closes the table. The rest of the row leaks into the body as text. Keep both in the open table. The text inside a tag pair and the URL of an autolink are RawText children, so they reach the cell as before. A
adds a line break to the cell, as the HTML backend does. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Yash Raj Pandey --- docling/backend/md_backend.py | 13 +++++++++++++ tests/test_backend_markdown.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/docling/backend/md_backend.py b/docling/backend/md_backend.py index b64b879bca..a9e583b85a 100644 --- a/docling/backend/md_backend.py +++ b/docling/backend/md_backend.py @@ -132,6 +132,7 @@ class MarkdownDocumentBackend(DeclarativeDocumentBackend): _ENTITY_RE = re.compile(r"&(#\d+|#x[0-9a-fA-F]+|\w+);") _DELIMITER_CELL_RE = re.compile(r":?-+:?") _PIPE_ENTITY = "|" + _BR_TAG_RE = re.compile(r"]", re.IGNORECASE) @staticmethod def _split_table_row(row: str) -> list[str]: @@ -810,6 +811,18 @@ def _iterate_elements( # noqa: C901 formatting=formatting, hyperlink=hyperlink, ) + + elif self.in_table and isinstance( + element, marko.inline.InlineHTML | marko.inline.AutoLink + ): + _log.debug(" - Inline HTML or autolink in a table: %s", element.children) + # The text inside a tag pair and the URL of an autolink arrive as + # RawText children, so only a
adds to the cell: a line break, + # as the HTML backend gives for
in a cell. + if isinstance(element, marko.inline.InlineHTML) and self._BR_TAG_RE.match( + str(element.children) + ): + self._append_table_text("\n") else: if not isinstance(element, str): self._close_table(doc) diff --git a/tests/test_backend_markdown.py b/tests/test_backend_markdown.py index 842eb494b5..679ce8dc2d 100644 --- a/tests/test_backend_markdown.py +++ b/tests/test_backend_markdown.py @@ -272,6 +272,35 @@ def test_convert_table_keeps_inline_code_spans(): ] +def test_convert_table_keeps_inline_html_and_autolinks(): + """Inline HTML and an autolink inside a GFM table cell are part of that cell. + + A
is a line break in the cell, the text inside other tags stays + without the tags, and an autolink keeps its URL. + """ + markdown = """| Name
(unit) | Value | +| --- | --- | +| CO2 | Ctrl+C | +| a
b
c
d | see now | +""" + conv_result = get_converter().convert_string(markdown, format=InputFormat.MD) + assert conv_result.status == ConversionStatus.SUCCESS + + assert conv_result.document.texts == [] + assert len(conv_result.document.tables) == 1 + table_data = conv_result.document.tables[0].data + assert table_data.num_rows == 3 + assert table_data.num_cols == 2 + assert [cell.text for cell in table_data.table_cells] == [ + "Name\n(unit)", + "Value", + "CO2", + "Ctrl+C", + "a\nb\nc\nd", + "see https://x.org now", + ] + + def test_convert_table_has_no_duplicate_cells(): """ Regression test: