60 lines
2.5 KiB
Python
60 lines
2.5 KiB
Python
|
|
"""Pandoc path: merged file → EPUB/HTML with the filters from pandoc/ (skipped without pandoc)."""
|
||
|
|
|
||
|
|
import re
|
||
|
|
import shutil
|
||
|
|
import subprocess
|
||
|
|
import zipfile
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from conftest import Book, Doc
|
||
|
|
from epub2md.merge import main as merge_main
|
||
|
|
|
||
|
|
pytestmark = pytest.mark.skipif(shutil.which("pandoc") is None, reason="pandoc nicht installiert")
|
||
|
|
FILTERS = Path(__file__).resolve().parent.parent / "pandoc"
|
||
|
|
|
||
|
|
|
||
|
|
def merged(convert, book: Book) -> Path:
|
||
|
|
run = convert(book)
|
||
|
|
assert merge_main([str(run.out)]) == 0
|
||
|
|
return run.out / f"{run.report['input']['name'][:-5]}.md"
|
||
|
|
|
||
|
|
|
||
|
|
def pandoc(md: Path, *args: str) -> subprocess.CompletedProcess:
|
||
|
|
return subprocess.run(["pandoc", md.name, *args], cwd=md.parent, capture_output=True, text=True, check=True)
|
||
|
|
|
||
|
|
|
||
|
|
def links_and_ids(epub: Path) -> tuple[list[tuple[str, str]], dict[str, set[str]]]:
|
||
|
|
with zipfile.ZipFile(epub) as z:
|
||
|
|
docs = {n: z.read(n).decode("utf-8") for n in z.namelist() if n.endswith(".xhtml")}
|
||
|
|
ids = {n: set(re.findall(r'\bid="([^"]+)"', t)) for n, t in docs.items()}
|
||
|
|
links = [(n, h) for n, t in docs.items() for h in re.findall(r'href="([^"]+)"', t)
|
||
|
|
if not re.match(r"^[a-z]+:", h) and not h.endswith(".css")]
|
||
|
|
return links, ids
|
||
|
|
|
||
|
|
|
||
|
|
def test_epub_links_to_chapter_starts_resolve(convert):
|
||
|
|
"""Chapter start anchors must begin with a letter: Pandoc's EPUB writer breaks '#039-…' (QA3-05)."""
|
||
|
|
docs = [Doc("a.xhtml", '<h1>Kapitel A</h1><p>Siehe <a href="b.xhtml">Kapitel B</a> und <a href="c.xhtml">Bild</a>.</p>'),
|
||
|
|
Doc("b.xhtml", "<h1>Kapitel B</h1><p>Text B.</p>"),
|
||
|
|
Doc("c.xhtml", "<p>Nur eine Bildseite ohne Überschrift.</p>")]
|
||
|
|
md = merged(convert, Book(docs=docs))
|
||
|
|
pandoc(md, "--toc", "-o", "buch.epub")
|
||
|
|
links, ids = links_and_ids(md.parent / "buch.epub")
|
||
|
|
broken = []
|
||
|
|
for doc, href in links:
|
||
|
|
file, _, frag = href.partition("#")
|
||
|
|
target = str(Path(doc).parent / file).replace("\\", "/") if file else doc
|
||
|
|
target = re.sub(r"[^/]+/\.\./", "", target)
|
||
|
|
if target not in ids or (frag and frag not in ids[target]):
|
||
|
|
broken.append(f"{doc}: {href}")
|
||
|
|
assert not broken
|
||
|
|
|
||
|
|
|
||
|
|
def test_book_filter_turns_chapter_anchors_into_chapter_starts(convert):
|
||
|
|
docs = [Doc("a.xhtml", "<h1>Kapitel A</h1><p>a</p>"), Doc("b.xhtml", "<h1>Kapitel B</h1><p>b</p>")]
|
||
|
|
md = merged(convert, Book(docs=docs))
|
||
|
|
html = pandoc(md, "-s", "--lua-filter", str(FILTERS / "book.lua"), "-t", "html").stdout
|
||
|
|
assert html.count('class="chapter-start"') == 2
|
||
|
|
assert 'id="TOC"' in html and "Kapitel B" in html
|