feat: Leerzeichen zwischen aneinanderstoßenden Feld-Spans (QA R4)
Zwei <span>-Geschwister mit unterschiedlichen Klassen, ohne Text dazwischen und mit Großbuchstabe oder Ziffer rechts bekommen ein trennendes Leerzeichen. Initialen (D|as) und Wortteile (Haupt|bahnhof) bleiben unberührt. Im Mathebuch werden so 49 Stellen korrigiert, die anderen drei Beispielbücher bleiben unverändert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
3ac5e1a8cc
commit
5f58589c2e
3 changed files with 45 additions and 2 deletions
|
|
@ -343,8 +343,10 @@ Verlustfreiheit behauptet (`lossless: false`).
|
||||||
|
|
||||||
- Titel überschriftsloser Seiten werden aus dem Inhalt abgeleitet (Cover, Titelseite, Impressum, erste
|
- Titel überschriftsloser Seiten werden aus dem Inhalt abgeleitet (Cover, Titelseite, Impressum, erste
|
||||||
Wörter). Das ist eine Heuristik und trifft nicht jede Seite.
|
Wörter). Das ist eine Heuristik und trifft nicht jede Seite.
|
||||||
- Aneinanderstoßende Inline-Elemente, zwischen denen auch in der Quelle kein Leerraum steht, laufen
|
- Aneinanderstoßende Feld-Spans ohne Leerraum (z. B. Autor | Titel | Reihe in Springer-Kapitelköpfen)
|
||||||
zusammen (z. B. Autor- und Titelzeilen in Springer-Kapitelköpfen).
|
erhalten ein trennendes Leerzeichen. Das gilt nur bei unterschiedlichen Klassen und wenn rechts ein
|
||||||
|
Großbuchstabe oder eine Ziffer folgt. Andere direkt aneinanderstoßende Inline-Elemente bleiben
|
||||||
|
unverändert, damit Initialen und Wortteile nicht zerrissen werden.
|
||||||
- Fehlerhafte Einrückungen der Vorlage werden zeichengetreu übernommen. Druck-Fortsetzungszeichen
|
- Fehlerhafte Einrückungen der Vorlage werden zeichengetreu übernommen. Druck-Fortsetzungszeichen
|
||||||
(`↪`/`➥`) am Zeilenanfang werden dagegen standardmäßig aufgelöst (siehe `--keep-wrap-markers`).
|
(`↪`/`➥`) am Zeilenanfang werden dagegen standardmäßig aufgelöst (siehe `--keep-wrap-markers`).
|
||||||
- Formelbilder ohne TeX-Alternativtext bleiben Bilder.
|
- Formelbilder ohne TeX-Alternativtext bleiben Bilder.
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,35 @@ def normalize_doc(doc: Doc, diag: Diagnostics, title_tables: bool) -> None:
|
||||||
doc.toc_ids.update(e.get("id") for e in iter_elements(el) if e.get("id"))
|
doc.toc_ids.update(e.get("id") for e in iter_elements(el) if e.get("id"))
|
||||||
remove_keep_tail(el)
|
remove_keep_tail(el)
|
||||||
diag.info("html-toc-removed", "HTML-Inhaltsverzeichnis entfernt (ersetzt durch README.md).", doc.path)
|
diag.info("html-toc-removed", "HTML-Inhaltsverzeichnis entfernt (ersetzt durch README.md).", doc.path)
|
||||||
|
separated = _separate_field_spans(body)
|
||||||
|
if separated:
|
||||||
|
diag.info("inline-separator-added",
|
||||||
|
f"{separated} Leerzeichen zwischen aneinanderstoßenden Feld-Spans eingefügt.", doc.path)
|
||||||
|
|
||||||
|
|
||||||
|
def _separate_field_spans(body: Any) -> int:
|
||||||
|
"""Insert a space between adjacent class-styled <span> "fields" that touch without whitespace.
|
||||||
|
|
||||||
|
Publishers sometimes lay out metadata as sibling spans (author | title | series) separated only by
|
||||||
|
CSS. Narrow rule to keep drop caps ("D|as") and split words ("Haupt|bahnhof") intact: both spans
|
||||||
|
carry different classes, no text between them, both have >= 2 characters and the right one starts
|
||||||
|
with an uppercase letter or digit.
|
||||||
|
"""
|
||||||
|
count = 0
|
||||||
|
for parent in iter_elements(body):
|
||||||
|
children = [c for c in parent if is_element(c)]
|
||||||
|
for left, right in zip(children, children[1:]):
|
||||||
|
if local(left.tag) != "span" or local(right.tag) != "span" or left.getnext() is not right:
|
||||||
|
continue
|
||||||
|
if left.tail or not classes(left) or not classes(right) or classes(left) == classes(right):
|
||||||
|
continue
|
||||||
|
lt, rt = text_content(left), text_content(right)
|
||||||
|
if len(lt.strip()) < 2 or len(rt.strip()) < 2 or lt[-1].isspace() or rt[0].isspace():
|
||||||
|
continue
|
||||||
|
if rt[0].isupper() or rt[0].isdigit():
|
||||||
|
left.tail = " "
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
def _attached(el: Any, body: Any) -> bool:
|
def _attached(el: Any, body: Any) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -152,3 +152,15 @@ def test_titles_derived_from_content_when_no_other_source(convert):
|
||||||
assert "cover" in types["chapters/001-cover.md"]
|
assert "cover" in types["chapters/001-cover.md"]
|
||||||
assert "titlepage" in types["chapters/002-titelseite.md"]
|
assert "titlepage" in types["chapters/002-titelseite.md"]
|
||||||
assert "colophon" in types["chapters/003-impressum.md"]
|
assert "colophon" in types["chapters/003-impressum.md"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_adjacent_field_spans_get_a_separator(convert):
|
||||||
|
body = ('<h1>K</h1><div class="ContextInformation"><span class="Names">J. Liesen, V. Mehrmann</span>'
|
||||||
|
'<span class="BookTitles"><span class="BookTitle">Lineare Algebra</span></span>'
|
||||||
|
'<span class="Series">Springer Studium</span></div>'
|
||||||
|
'<p><span class="dropcap">D</span><span class="rest">as Wetter</span> war schlecht.</p>'
|
||||||
|
'<p><span class="a">Haupt</span><span class="b">bahnhof</span> und x<sub>i</sub>.</p>')
|
||||||
|
text = convert(Book(docs=[Doc("c.xhtml", body)])).chapter("001")
|
||||||
|
assert "J. Liesen, V. Mehrmann Lineare Algebra Springer Studium" in text
|
||||||
|
assert "Das Wetter war schlecht." in text
|
||||||
|
assert "Hauptbahnhof und x<sub>i</sub>." in text
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue