epub2md/tests/test_styles.py
dschlueter 5f7c352df1 feat: Hervorhebungen aus dem CSS des Buches übernehmen (P1)
Neues Modul epub2md/css.py: Es wertet die Stylesheets des Buches aus
(Typ-, Klassen- und ID-Selektoren, Nachfahre/Kind, Spezifität,
style-Attribute; @media und Pseudoklassen werden ignoriert). Daraus werden:
- fett/kursiv → **/*, unterstrichen → <u>, durchgestrichen → ~~,
  vertical-align super/sub → <sup>/<sub>
- Monospace-Einzelwörter → Inline-Code
- display:none → Element entfällt
Das gilt im Fließtext, für ganze Absätze und in HTML-Tabellen (dort als
HTML-Tags). Direkt aneinanderstoßende gleiche Hervorhebungen werden
zusammengefasst ('*x**y*' brach Markdown; Mathebuch: 305 → 249 Stellen).
Beispiele: C-Buch 799× kursiv und 648× fett, die vorher verloren gingen.
Abschaltbar mit --no-css-emphasis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 04:52:36 +02:00

68 lines
3.9 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Typography and layout: invisible characters, CSS emphasis, scene breaks, boxes, styles, page markers."""
from conftest import Book, Doc
ZW, SHY = "​", "­"
def test_p6_invisible_characters_removed_outside_code(convert):
body = (f"<h1>Kapitel 1.{ZW}1</h1><p>Lese{SHY}rinnen und 1.{ZW}1 sowie{ZW}Wort.</p>"
f"<pre>a{ZW}b</pre><p><code>x{SHY}y</code></p>")
run = convert(Book(docs=[Doc("c.xhtml", body)]))
text = run.chapter("001")
assert "Leserinnen und 1.1 sowieWort." in text
assert "# Kapitel 1.1" in text and 'chapter_title: "Kapitel 1.1"' in text
assert f"a{ZW}b" in text and f"x{SHY}y" in text # code stays verbatim
assert run.report["text_cleanup"]["invisible_chars_removed"] == 4
kept = convert(Book(docs=[Doc("c.xhtml", body)]), "--keep-invisible-chars").chapter("001")
assert f"Lese{SHY}rinnen" in kept
CSS = b""".i { font-style: italic } .b { font-weight: bold } span.u { text-decoration: underline }
.s { text-decoration: line-through } .hoch { vertical-align: super } .weg { display: none }
li p.first > span.b { font-weight: normal; font-family: monospace } .schrift-c { font-family: "Courier New", monospace }
p.zitat { font-style: italic } h2.t { font-weight: bold; font-style: italic } .url { text-decoration: underline }
@media amzn-kf8 { .i { font-style: normal } }"""
def styled(body: str, *args: str, convert=None):
return convert(Book(docs=[Doc("c.xhtml", body)], media={"style.css": CSS}), *args)
def test_p1_css_classes_become_markdown_emphasis(convert):
body = ('<h1>K</h1><p>Ein <span class="i">kursives</span> und <span class="b">fettes</span> Wort, '
'<span class="u">unterstrichen</span>, <span class="s">gestrichen</span>, x<span class="hoch">2</span>, '
'<span class="weg">[»] </span>sichtbar, <span class="schrift-c">printf</span> und '
'<span class="schrift-c">zwei Wörter</span>, <span style="font-weight:700">inline</span>, '
'<span class="url"><a href="https://example.org">example.org</a></span>.</p>'
'<ul><li><p class="first"><span class="b">nicht fett im Listenkopf</span></p></li></ul>'
'<p class="zitat">Ein ganzer kursiver Absatz mit <em>Betonung</em>.</p>'
'<h2 class="t">Überschrift bleibt</h2><p><em class="i">nur einmal</em></p><pre><span class="b">code</span></pre>')
run = styled(body, convert=convert)
text = run.chapter("001")
assert "Ein *kursives* und **fettes** Wort, <u>unterstrichen</u>, ~~gestrichen~~, x<sup>2</sup>, sichtbar," in text
assert "`printf` und zwei Wörter, **inline**, [example.org](https://example.org)." in text
assert "[»]" not in text and "\\[»\\]" not in text
assert "- nicht fett im Listenkopf" in text
assert "*Ein ganzer kursiver Absatz mit Betonung.*" in text
assert "## Überschrift bleibt" in text and "*nur einmal*" in text and "**nur einmal**" not in text
assert "```\ncode\n```" in text
assert run.report["css"]["hidden_elements"] == 1
assert run.report["css"]["applied"]["italic"] >= 2
plain = styled(body, "--no-css-emphasis", convert=convert).chapter("001")
assert "Ein kursives und fettes Wort" in plain
def test_p1_css_emphasis_inside_html_tables(convert):
body = ('<h1>T</h1><table><tr><td colspan="2"><span class="i">kursiv</span> und <span class="b">fett</span></td></tr>'
'<tr><td>a</td><td>b</td></tr></table>')
text = styled(body, convert=convert).chapter("001")
assert "<em>kursiv</em> und <strong>fett</strong>" in text
def test_adjacent_emphasis_is_merged(convert):
body = ('<h1>K</h1><p>Fläche (A=r<sup>2</sup><span class="i">×</span><span class="i">π</span>) und '
'<em>x</em><em>y</em> sowie <strong>a</strong><strong>b</strong> und <em>c</em> <em>d</em>.</p>')
text = styled(body, convert=convert).chapter("001")
assert "<sup>2</sup>*×π*" in text
assert "*xy*" in text and "**ab**" in text and "*c* *d*" in text