Inhaltsverzeichnis einheitlich: Einträge als Absätze statt "\"-Zeilen

- Im Inhaltsverzeichnis wird jeder Eintrag ein eigener Absatz, auch wenn
  er als zeilengetreu erkannt wurde (vorher gemischte Abstände im EPUB)
- Zeilen, die wie Gliederungseinträge aussehen ("Teil V: …", "32 …"),
  werden auch ohne Überschrift "Inhalt" zu Absätzen (Vorschau Band Zwei)
- Tests, README

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-10-02 17:32:20 +02:00
commit f0e0562451

View file

@ -131,6 +131,11 @@ PART_TITLE = re.compile(
r"^(?:teil|buch|band|part|book)\s+(?:[ivxlcdm]+|\d+)\b",
re.IGNORECASE,
)
# Zeile, die wie ein Gliederungseintrag aussieht ("Teil V: …", "32 Oberst …").
OUTLINE_ENTRY = re.compile(
r"^(?:\d{1,3}\.?\s+\S|(?:teil|buch|band|part|book|kapitel|chapter)\s+(?:[ivxlcdm]+|\d+)\b)",
re.IGNORECASE,
)
NUMBERED_TITLE = re.compile(
r"^(?:\d+\.?|kapitel\s+\d+|chapter\s+\d+)\s",
re.IGNORECASE,
@ -374,6 +379,19 @@ def normalize_markdown(
# Mehrzeiliger Absatz: Absatzende; einzelne Zeile: harter Umbruch
line += PARAGRAPH_END if end_mark.group(2) else HARD_BREAK
if in_contents and line.endswith((HARD_BREAK, PARAGRAPH_END)):
# Inhaltsverzeichnis: jeder Eintrag ein eigener Absatz (einheitlich,
# auch wenn das Verzeichnis in der PDF zeilengetreu erscheint).
line = line.rstrip(HARD_BREAK + PARAGRAPH_END)
elif line.endswith((HARD_BREAK, PARAGRAPH_END)) and OUTLINE_ENTRY.match(
clean_inline(line.rstrip(HARD_BREAK + PARAGRAPH_END))
):
# Verzeichnis ohne Überschrift "Inhalt" (z. B. "Vorschau Band Zwei"):
# Einträge als eigene Absätze statt als Zeilen mit "\".
flush_paragraph()
standalone(clean_inline(line.rstrip(HARD_BREAK + PARAGRAPH_END)))
continue
if line.endswith(PARAGRAPH_END):
content = line.removesuffix(PARAGRAPH_END)
if not paragraph and result and result[-1].endswith(HARD_BREAK):