146 lines
5.2 KiB
Python
146 lines
5.2 KiB
Python
|
|
"""--split, --from-md, YAML-Kopf und --jpeg-quality."""
|
|||
|
|
|
|||
|
|
import zipfile
|
|||
|
|
|
|||
|
|
import pytest
|
|||
|
|
|
|||
|
|
import pdf2md as p
|
|||
|
|
from conftest import needs_pandoc
|
|||
|
|
from test_e2e import run
|
|||
|
|
|
|||
|
|
BOOK = """# Das Buch
|
|||
|
|
|
|||
|
|
## Inhaltsverzeichnis
|
|||
|
|
|
|||
|
|
Teil I
|
|||
|
|
|
|||
|
|
1 Anfang
|
|||
|
|
|
|||
|
|
## Teil I: Der Anfang
|
|||
|
|
|
|||
|
|
Einleitung zum Teil.
|
|||
|
|
|
|||
|
|
### 1 Anfang
|
|||
|
|
|
|||
|
|
Text eins.
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
# kein Kapitel, nur ein Kommentar
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2 Weiter
|
|||
|
|
|
|||
|
|
Text zwei.
|
|||
|
|
|
|||
|
|
## Impressum
|
|||
|
|
|
|||
|
|
Alle Rechte vorbehalten.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_frontmatter_round_trip():
|
|||
|
|
meta = {"title": "Ein \"Buch\": Teil 1", "author": "Max Muster", "chapter": 3, "leer": ""}
|
|||
|
|
text = p.make_frontmatter(meta) + "# Text\n"
|
|||
|
|
parsed, body = p.split_frontmatter(text)
|
|||
|
|
assert parsed == {"title": "Ein \"Buch\": Teil 1", "author": "Max Muster", "chapter": 3}
|
|||
|
|
assert body == "# Text\n"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_text_without_frontmatter_is_unchanged():
|
|||
|
|
assert p.split_frontmatter("---\n\nText\n") == ({}, "---\n\nText\n")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_slugify():
|
|||
|
|
assert p.slugify("2 Ein Brief – eine Liste – ein Vermächtnis") == "2-ein-brief-eine-liste-ein-vermaechtnis"
|
|||
|
|
assert p.slugify("Größe & Maß") == "groesse-mass"
|
|||
|
|
assert p.slugify("!!!") == "abschnitt"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_split_by_chapters():
|
|||
|
|
sections = p.split_markdown(BOOK, "kapitel")
|
|||
|
|
assert [s["title"] for s in sections] == [
|
|||
|
|
"Vorspann", "Teil I: Der Anfang", "1 Anfang", "2 Weiter", "Impressum",
|
|||
|
|
]
|
|||
|
|
assert sections[0]["body"].startswith("# Das Buch") and "## Inhaltsverzeichnis" in sections[0]["body"]
|
|||
|
|
assert sections[2]["part"] == "Teil I: Der Anfang" and sections[2]["chapter"] == 1
|
|||
|
|
assert "# kein Kapitel" in sections[2]["body"] # Codeblock wird nicht geschnitten
|
|||
|
|
assert "".join(s["body"] for s in sections).replace("\n", "") == BOOK.replace("\n", "")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_split_by_parts():
|
|||
|
|
sections = p.split_markdown(BOOK, "teile")
|
|||
|
|
assert [s["title"] for s in sections] == ["Vorspann", "Teil I: Der Anfang", "Impressum"]
|
|||
|
|
assert "### 2 Weiter" in sections[1]["body"]
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_write_split_and_read_back(tmp_path):
|
|||
|
|
sections = p.split_markdown(BOOK, "kapitel")
|
|||
|
|
files = p.write_split(tmp_path / "buch", sections, {"title": "Das Buch", "author": "Max"})
|
|||
|
|
assert [f.name for f in files][:3] == ["00_vorspann.md", "01_teil-i-der-anfang.md", "02_1-anfang.md"]
|
|||
|
|
meta, body, base = p.read_markdown_source(tmp_path / "buch")
|
|||
|
|
assert meta["title"] == "Das Buch" and base == tmp_path / "buch"
|
|||
|
|
assert body.replace("\n", "") == BOOK.replace("\n", "")
|
|||
|
|
head, _ = p.split_frontmatter(files[2].read_text())
|
|||
|
|
assert head == {"title": "1 Anfang", "part": "Teil I: Der Anfang", "chapter": 1,
|
|||
|
|
"order": 2, "book": "Das Buch", "author": "Max"}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_missing_index_entry_is_reported(tmp_path):
|
|||
|
|
folder = tmp_path / "buch"
|
|||
|
|
p.write_split(folder, p.split_markdown(BOOK, "teile"), {"title": "X"})
|
|||
|
|
(folder / "01_teil-i-der-anfang.md").unlink()
|
|||
|
|
with pytest.raises(RuntimeError, match="nicht vorhanden"):
|
|||
|
|
p.read_markdown_source(folder)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# --- Ende zu Ende ------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
def test_cli_split_writes_folder(pdfs, tmp_path):
|
|||
|
|
run(pdfs["digital"], tmp_path / "buch.md", "--split", "kapitel", "--images")
|
|||
|
|
folder = tmp_path / "buch"
|
|||
|
|
names = sorted(f.name for f in folder.glob("*.md"))
|
|||
|
|
assert names[0] == "00_vorspann.md" and "index.md" in names
|
|||
|
|
assert any(n.endswith("_1-der-notar.md") for n in names)
|
|||
|
|
assert (folder / "images" / "cover.jpg").is_file()
|
|||
|
|
text = "".join(f.read_text() for f in folder.glob("*.md"))
|
|||
|
|
assert "](images/" in text and "_images/" not in text
|
|||
|
|
|
|||
|
|
|
|||
|
|
@needs_pandoc
|
|||
|
|
def test_from_md_folder_follows_index_order(pdfs, tmp_path):
|
|||
|
|
run(pdfs["digital"], tmp_path / "buch.md", "--split", "kapitel", "--epub")
|
|||
|
|
index = tmp_path / "buch" / "index.md"
|
|||
|
|
lines = index.read_text().split("\n")
|
|||
|
|
i = next(n for n, line in enumerate(lines) if "1-der-notar" in line)
|
|||
|
|
j = next(n for n, line in enumerate(lines) if "2-ein-brief" in line)
|
|||
|
|
lines[i], lines[j] = lines[j], lines[i]
|
|||
|
|
index.write_text("\n".join(lines))
|
|||
|
|
|
|||
|
|
run("--from-md", tmp_path / "buch", "--epub-file", tmp_path / "neu.epub")
|
|||
|
|
with zipfile.ZipFile(tmp_path / "neu.epub") as z:
|
|||
|
|
nav = z.read("EPUB/nav.xhtml").decode()
|
|||
|
|
opf = z.read("EPUB/content.opf").decode()
|
|||
|
|
assert nav.index("2 Ein Brief") < nav.index("1 Der Notar")
|
|||
|
|
assert "Erika Muster" in opf and "cover" in opf
|
|||
|
|
|
|||
|
|
|
|||
|
|
@needs_pandoc
|
|||
|
|
def test_from_md_file_uses_frontmatter_and_overrides(pdfs, tmp_path):
|
|||
|
|
run(pdfs["digital"], tmp_path / "buch.md", "--images")
|
|||
|
|
md = tmp_path / "buch.md"
|
|||
|
|
md.write_text(md.read_text().replace("Der Regen", "Der starke Regen"))
|
|||
|
|
run("--from-md", md, "--title", "Neuer Titel")
|
|||
|
|
with zipfile.ZipFile(tmp_path / "buch.epub") as z:
|
|||
|
|
opf = z.read("EPUB/content.opf").decode()
|
|||
|
|
text = "".join(z.read(n).decode() for n in z.namelist() if n.endswith(".xhtml"))
|
|||
|
|
assert ">Neuer Titel<" in opf and "Erika Muster" in opf and "cover" in opf
|
|||
|
|
assert "Der starke Regen" in text and "title:" not in text
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_jpeg_quality_changes_image_size(pdfs, tmp_path):
|
|||
|
|
def size(quality):
|
|||
|
|
out = tmp_path / f"q{quality}" / "buch.md"
|
|||
|
|
run(pdfs["digital"], out, "--images", "--jpeg-quality", str(quality))
|
|||
|
|
return sum(f.stat().st_size for f in (out.parent / "buch_images").glob("*.jpg"))
|
|||
|
|
|
|||
|
|
assert size(30) < size(95)
|