fix: Tabellen mit | in Code, Formel oder Link als HTML ausgeben (QA2-01)

GFM trennt Tabellenzellen an jedem unmaskierten senkrechten Strich, auch
innerhalb von Code-Spans und Link-Zielen. Operator-Tabellen (|, ||, |=)
verloren dadurch Zellinhalte. Solche Tabellen werden jetzt wie andere
komplexe Tabellen als bereinigtes HTML mit Warnung ausgegeben.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-10-05 23:57:42 +02:00
commit 8d7f3de3d7
3 changed files with 36 additions and 1 deletions

View file

@ -220,7 +220,8 @@ weil CommonMark keine `[^…]`-Syntax kennt.
- **Formelbasierte EPUBs:** Am besten sind EPUBs mit MathML oder TeX im `alt`-Text (z. B. Springer). - **Formelbasierte EPUBs:** Am besten sind EPUBs mit MathML oder TeX im `alt`-Text (z. B. Springer).
- **SVG-Formeln** bleiben SVG (nie gerastert) und werden im Bericht als Formel markiert. - **SVG-Formeln** bleiben SVG (nie gerastert) und werden im Bericht als Formel markiert.
- **Layouttabellen / komplexe Tabellen** (`colspan`, `rowspan`, Blockinhalte in Zellen, verschachtelte - **Layouttabellen / komplexe Tabellen** (`colspan`, `rowspan`, Blockinhalte in Zellen, verschachtelte
Tabellen) bleiben als bereinigtes HTML im Markdown erhalten, mit Warnung. Einfache Tabellen werden Tabellen, senkrechte Striche `|` in Code, Formeln oder Link-Zielen – GFM würde die Zeile dort in
zusätzliche Zellen zerlegen) bleiben als bereinigtes HTML im Markdown erhalten, mit Warnung. Einfache Tabellen werden
GFM-Pipe-Tabellen. Einzellige Layouttabellen werden als normaler Text ausgegeben. GFM-Pipe-Tabellen. Einzellige Layouttabellen werden als normaler Text ausgegeben.
- **Syntax-Highlighting:** Farb-Spans, `<strong>`/`<em>` in Listings und Zeilennummern werden entfernt. - **Syntax-Highlighting:** Farb-Spans, `<strong>`/`<em>` in Listings und Zeilennummern werden entfernt.
Einrückung, Tabs, `<`, `>`, `&`, `$`, `|`, Backslashes und Backticks bleiben exakt erhalten. Die Sprache Einrückung, Tabs, `<`, `>`, `&`, `$`, `|`, Backslashes und Backticks bleiben exakt erhalten. Die Sprache

View file

@ -694,8 +694,19 @@ class Renderer:
return f"Blockelemente ({local(e.tag)}) in Zellen" return f"Blockelemente ({local(e.tag)}) in Zellen"
if local(e.tag) == "math" and e.get("display") == "block": if local(e.tag) == "math" and e.get("display") == "block":
return "abgesetzte Formeln in Zellen" return "abgesetzte Formeln in Zellen"
if self._raw_pipe(e):
return "senkrechte Striche (|) in Code, Formel oder Link"
return "" return ""
def _raw_pipe(self, e: Any) -> bool:
"""'|' in content that is emitted unescaped (code span, math, link target, image) would split a GFM row."""
name = local(e.tag)
if is_inline_code(e) or name in ("kbd", "math"):
return "|" in text_content(e)
if name in ("a", "img", "image", "svg"):
return any("|" in v for v in e.attrib.values())
return False
# ================================================================== inline # ================================================================== inline
def inline_children(self, el: Any) -> list[Piece]: def inline_children(self, el: Any) -> list[Piece]:

View file

@ -0,0 +1,23 @@
"""Regression tests for defects found in the independent QA run (.qa/QA_REPORT.md, IDs QA2-…)."""
import re
from conftest import Book, Doc
def _pipe_rows(text: str) -> list[str]:
return [ln for ln in text.splitlines() if ln.startswith("| ")]
def test_pipe_in_inline_code_does_not_split_gfm_table(convert):
"""QA2-01: GFM splits table cells at every unescaped '|', also inside code spans and link targets."""
body = ("<h1>Operatoren</h1><table><thead><tr><th>Operator</th><th>Bedeutung</th></tr></thead><tbody>"
"<tr><td><code>||</code></td><td>logisches ODER</td></tr>"
"<tr><td><code>a | b</code></td><td><a href=\"https://example.org/a|b\">Link</a></td></tr>"
"</tbody></table>")
run = convert(Book(docs=[Doc("c.xhtml", body)]))
text = run.chapter("001")
for row in _pipe_rows(text):
cells = re.split(r"(?<!\\)\|", row.strip()[1:-1])
assert len(cells) == 2, f"Zeile zerfällt in GFM in {len(cells)} Zellen: {row}"
assert "||" in text and "a | b" in text and "logisches ODER" in text