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:
parent
b0421b849c
commit
8d7f3de3d7
3 changed files with 36 additions and 1 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]:
|
||||||
|
|
|
||||||
23
tests/test_qa_regressions.py
Normal file
23
tests/test_qa_regressions.py
Normal 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
|
||||||
Loading…
Reference in a new issue