feat: epub2md – EPUB nach kapitelweisem Markdown
Kommandozeilenwerkzeug, das DRM-freie EPUB-2/3-Dateien offline in kapitelweise Markdown-Dateien umwandelt. Erzeugt werden außerdem Medien, metadata.yaml, manifest.json, conversion-report.json und Diagnose-Dateien. - Spine-basierte Kapitelbildung mit --chapter-level, epub:type-Abschnitten, Fortsetzungsdateien und Erkennung von Layout-Überschriften - Code mit exakter Einrückung, sicheren Fences und konservativer Spracherkennung - MathML->TeX (konservativ), TeX aus alt-Texten, SVG-/Bildformeln bleiben Bilder - Fußnoten-Modi end-of-chapter, end-of-book, inline und keep-links - Interne Links/Anker werden umgeschrieben, Linkcheck über die Ausgabe - Sicher: kein Entpacken, Schutz vor Path Traversal und XXE, DRM-Erkennung, atomares Schreiben, --force nur für frühere Ausgaben - 82 pytest-Tests mit synthetischen EPUBs und Invariantentests für Beispiele/ Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
02449734a2
1 changed files with 7726 additions and 0 deletions
292
README.md
Normal file
292
README.md
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
# epub2md
|
||||
|
||||
Kommandozeilenwerkzeug, das **DRM-freie EPUB-Dateien** (EPUB 2 und EPUB 3) lokal und offline in eine
|
||||
strukturierte Sammlung **kapitelweiser Markdown-Dateien** umwandelt – mit extrahierten Medien,
|
||||
YAML-Metadaten, Inhaltsverzeichnis, maschinenlesbarem Manifest und einem Konvertierungsbericht.
|
||||
|
||||
Gedacht für die Weiterverarbeitung mit Markdown-Renderern, Pandoc, Obsidian/Zettelkasten, Git,
|
||||
RAG-/Embedding-Pipelines, Volltextsuche und lokale LLMs.
|
||||
|
||||
## Grenzen
|
||||
|
||||
- **Kein DRM:** Verschlüsselte EPUBs (`META-INF/encryption.xml` mit verschlüsselten Inhalten) werden mit
|
||||
Exit-Code 2 abgelehnt. Es wird kein Kopierschutz umgangen. Reine Font-Obfuscation ist erlaubt.
|
||||
- **Keine OCR:** Gescannte Bücher (Seiten als Bilder) liefern nur Bildreferenzen, keinen Text.
|
||||
- **Formeln als Bild/SVG** werden nie in TeX „geraten“. Sie bleiben als Bild/SVG erhalten und werden im
|
||||
Bericht gemeldet. TeX entsteht nur aus echtem MathML, TeX-Annotationen oder TeX im `alt`-Text.
|
||||
- **Layout-Heuristiken** (z. B. Überschriften, die als Tabellen gesetzt sind) sind konservativ und
|
||||
erfassen nicht jedes Verlagslayout.
|
||||
|
||||
## Installation (Ubuntu 24.04)
|
||||
|
||||
```bash
|
||||
sudo apt install python3 python3-venv
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt # Laufzeit: nur lxml
|
||||
pip install -r requirements-dev.txt # Tests: pytest, PyYAML
|
||||
```
|
||||
|
||||
Laufzeitabhängigkeit ist ausschließlich **lxml** (sicheres XML/HTML-Parsing). Alles andere ist
|
||||
Standardbibliothek. Pandoc, Calibre, Java oder Node.js werden **nicht** benötigt. Pandoc ist nur für
|
||||
die optionale Weiterverarbeitung der Ausgabe nützlich.
|
||||
|
||||
## Aufruf
|
||||
|
||||
```bash
|
||||
python3 epub2md.py INPUT.epub OUTPUT_VERZEICHNIS [Optionen]
|
||||
# alternativ
|
||||
python3 -m epub2md INPUT.epub OUTPUT_VERZEICHNIS [Optionen]
|
||||
```
|
||||
|
||||
Beispiele:
|
||||
|
||||
```bash
|
||||
# Programmierbuch mit Standardoptionen
|
||||
python3 epub2md.py \
|
||||
Beispiele/programmierbuch.epub \
|
||||
output/programmierbuch \
|
||||
--format gfm \
|
||||
--math auto \
|
||||
--footnotes end-of-chapter \
|
||||
--extract-media \
|
||||
--frontmatter \
|
||||
--toc \
|
||||
--verbose
|
||||
|
||||
# Roman mit Teilen (h1) und Kapiteln (h2): ein File pro Kapitel
|
||||
python3 epub2md.py roman.epub output/roman --chapter-level 2
|
||||
|
||||
# Nur analysieren, nichts schreiben
|
||||
python3 epub2md.py buch.epub output/buch --dry-run
|
||||
|
||||
# Erneut konvertieren und frühere Ausgabe ersetzen
|
||||
python3 epub2md.py buch.epub output/buch --force --report berichte/buch.json
|
||||
```
|
||||
|
||||
## Optionen
|
||||
|
||||
| Option | Standard | Wirkung |
|
||||
| --- | --- | --- |
|
||||
| `--format {gfm,commonmark}` | `gfm` | `gfm`: Pipe-Tabellen, `~~durchgestrichen~~`, `[^fn]`-Fußnoten. `commonmark`: Tabellen als HTML, `<del>`, Fußnoten als Anker-Links |
|
||||
| `--extract-media` / `--no-extract-media` | an | Medien nach `media/` extrahieren. Ohne Extraktion stehen Bildreferenzen als Text `[Bild: alt – Pfad]` da (keine kaputten Links) |
|
||||
| `--copy-original` / `--no-copy-original` | aus | Original-EPUB nach `source/` kopieren |
|
||||
| `--chapter-level N` | `1` | Überschriftenebene (1–6), ab der eine neue Kapiteldatei beginnt |
|
||||
| `--toc` / `--no-toc` | an | `README.md` mit Inhaltsverzeichnis, Statistik und Reproduktionsbefehl |
|
||||
| `--frontmatter` / `--no-frontmatter` | an | YAML-Frontmatter in jeder Kapiteldatei |
|
||||
| `--footnotes {keep-links,end-of-chapter,end-of-book,inline}` | `end-of-chapter` | siehe [Fußnoten](#fußnoten-modi) |
|
||||
| `--math {auto,tex,mathml,image}` | `auto` | siehe [Mathematik](#math-modi) |
|
||||
| `--images {relative,flat,original-path}` | `relative` | `relative`: `media/images/0001-name.ext`; `flat`: `media/name.ext`; `original-path`: `media/<Pfad im EPUB>` |
|
||||
| `--slug-language {de,en,none}` | `de` | Dateinamen: `de` ä→ae, ß→ss; `en` ä→a, ß→ss; `none` Unicode bleibt |
|
||||
| `--keep-html` | aus | Unbekannte/nicht sicher konvertierbare Elemente als bereinigtes HTML erhalten (sonst: Text übernommen + Eintrag in `diagnostics/unsupported-elements.md`) |
|
||||
| `--strict` | aus | Abbruch bei kritischen Fehlern (fehlende/unlesbare Spine-Dokumente, unsichere ZIP-Pfade, ungültige Spine-Verweise) |
|
||||
| `--force` | aus | Bestehendes Ausgabeverzeichnis ersetzen – siehe [Sicherheit](#sicherheit-und-fehlerbehandlung) |
|
||||
| `--verbose`, `-v` | aus | Verarbeitungsschritte und Warnungen auf stderr |
|
||||
| `--dry-run` | aus | EPUB analysieren, geplante Kapiteldateien anzeigen, **nichts** schreiben (auch kein `--report`) |
|
||||
| `--report DATEI.json` | – | Bericht zusätzlich an diesen Ort schreiben. `OUTPUT/conversion-report.json` entsteht immer |
|
||||
| `--version` | – | Programmversion |
|
||||
|
||||
### Rückgabecodes
|
||||
|
||||
| Code | Bedeutung |
|
||||
| --- | --- |
|
||||
| 0 | Erfolgreich, ggf. mit nicht-kritischen Warnungen |
|
||||
| 1 | Ungültige Parameter, Eingabedatei fehlt/nicht lesbar, Ausgabeverzeichnis belegt |
|
||||
| 2 | ZIP-/Container-Fehler, DRM, unsichere ZIP-Pfade (mit `--strict`) |
|
||||
| 3 | OPF-/Spine-/XHTML-Fehler (z. B. leere Spine; mit `--strict` auch einzelne defekte Dokumente) |
|
||||
| 4 | Unerwarteter interner Fehler (Traceback mit `--verbose`) |
|
||||
|
||||
## Ausgabestruktur
|
||||
|
||||
```text
|
||||
output/mein-buch/
|
||||
├── README.md # Titel, Metadaten, Inhaltsverzeichnis, Statistik, Reproduktion
|
||||
├── metadata.yaml # Buchmetadaten (nur vorhandene Felder, nichts erfunden)
|
||||
├── manifest.json # Kapitel mit Anker-Listen, Wortzahl, Code/Bild/Formel-Zählern – für RAG
|
||||
├── conversion-report.json # vollständiger Bericht (Optionen, SHA-256, Warnungen, Formeln, Links …)
|
||||
├── source/mein-buch.epub # nur mit --copy-original
|
||||
├── media/
|
||||
│ ├── images/0001-cover.jpg # stabile, kollisionsfreie Namen; jede Quelle genau einmal
|
||||
│ ├── audio/ video/ other/ # Audio, Video, Schriften, CSS …
|
||||
├── chapters/
|
||||
│ ├── 001-cover.md
|
||||
│ ├── 002-vorwort.md
|
||||
│ ├── 003-1-einleitung.md
|
||||
│ ├── …
|
||||
│ └── 999-anmerkungen.md # nur bei --footnotes end-of-book
|
||||
└── diagnostics/
|
||||
├── warnings.md # Warnungen/Hinweise gruppiert, für Menschen
|
||||
└── unsupported-elements.md
|
||||
```
|
||||
|
||||
- Dateinamen: fortlaufend dreistellig nummeriert (`001-…`) plus Slug aus dem Kapiteltitel. Gleiche Titel
|
||||
bekommen eindeutige Namen, weil die Nummer immer Teil des Namens ist.
|
||||
- Kapitel-Frontmatter (YAML, `---`-Begrenzer): `book_title`, `book_author`, `language`,
|
||||
`chapter_number`, `chapter_title`, `source_epub`, `source_href` (+ `source_hrefs`, wenn ein Kapitel aus
|
||||
mehreren XHTML-Dateien besteht), `epub_type`, `conversion_status` (`converted` oder
|
||||
`converted-with-warnings`).
|
||||
- Spezialabschnitte (Glossar, Literatur, Sach-/Personenregister, Anhang, Endnoten) erhalten eigene
|
||||
Dateien und werden in `README.md` unter „Besondere Abschnitte“ verlinkt.
|
||||
- Nicht-lineare Spine-Dokumente (`linear="no"`, z. B. Bild-Zoomseiten) bleiben in Spine-Reihenfolge
|
||||
erhalten und sind im Inhaltsverzeichnis als *Zusatzseite* markiert.
|
||||
|
||||
## Wie Kapitel gebildet werden
|
||||
|
||||
1. Die **OPF-Spine** bestimmt die Lesereihenfolge (nicht Dateinamen, nicht das Inhaltsverzeichnis).
|
||||
2. Überschriften `h1`…`hN` (N = `--chapter-level`) und semantische Abschnitte (`epub:type`/`role`:
|
||||
chapter, part, appendix, glossary, bibliography, index, preface, …) beginnen neue Kapiteldateien,
|
||||
auch mitten in einer XHTML-Datei.
|
||||
3. Eine XHTML-Datei, die nur mit einer tieferen Überschrift beginnt (z. B. eine Datei pro Abschnitt
|
||||
`1.2`), wird an das laufende Kapitel angehängt.
|
||||
4. Dateien ohne Überschrift (Cover, Titelei, Widmung) werden eigene Kapitel. Ihr Titel kommt aus
|
||||
NAV/NCX, `<title>`, `epub:type` oder dem Dateinamen.
|
||||
5. Ein HTML-Inhaltsverzeichnis wird übersprungen, wenn NAV/NCX vorhanden ist (keine doppelte Ausgabe).
|
||||
Links darauf zeigen auf `README.md`.
|
||||
6. Überschriftenebenen werden je Datei verschoben: Der Kapiteltitel ist `#`, alle weiteren beginnen bei `##`.
|
||||
|
||||
Verlagsspezifische Heuristiken: Layout-Tabellen mit Klassen wie `Head1`/`Title_document` werden zu
|
||||
Überschriften. `<pre>` mit `<br/>`-Zeilen und `div.*programlisting* > span.pre` werden zu Codeblöcken.
|
||||
Literaturlisten mit eigenen Labels (`[BryL06]`) werden nicht doppelt nummeriert.
|
||||
|
||||
**Tipp:** Hat ein Buch Teile als `h1` und Kapitel als `h2` (typisch bei Romanen), erzeugt
|
||||
`--chapter-level 2` eine Datei pro Kapitel. Mit dem Standard `1` entsteht eine Datei pro Teil.
|
||||
|
||||
## Math-Modi
|
||||
|
||||
| Modus | MathML | TeX in `alt`/Annotation | SVG-/Bildformel ohne TeX |
|
||||
| --- | --- | --- | --- |
|
||||
| `auto` | konservativ → TeX `$…$` / `$$…$$`; bei Unklarheit MathML behalten | TeX `$…$` / `$$…$$` | Bild + Warnung |
|
||||
| `tex` | wie auto, aber Begrenzer `\(…\)` / `\[…\]` (nie `$`) | TeX `\(…\)` / `\[…\]` | Bild + Warnung |
|
||||
| `mathml` | MathML als eingebettetes XML (Namespace normalisiert) | Bild (TeX bleibt im Alt-Text) | Bild |
|
||||
| `image` | `altimg`-Bild falls vorhanden, sonst MathML + Warnung | Bild | Bild |
|
||||
|
||||
- Die MathML→TeX-Umsetzung unterstützt `mi, mn, mo, mtext, mspace, mrow, mstyle, mfrac, msqrt, mroot,
|
||||
msup, msub, msubsup, mover, munder, munderover, mfenced, mtable/mtr/mtd, semantics, maction`.
|
||||
Bei allem anderen (z. B. `menclose`, `mmultiscripts`, `mlabeledtr`) bleibt das MathML erhalten. Lieber
|
||||
MathML als falsches TeX.
|
||||
- TeX-Annotationen (`<annotation encoding="application/x-tex">`) haben Vorrang.
|
||||
- Im `alt`-Text doppelt escapte Zeichen (`&amp;` → `&`) werden repariert.
|
||||
- Gleichungsnummern wie `(3.7)` bleiben als Text sichtbar, Gleichungsanker als `<a id>` erhalten.
|
||||
- Formelbilder, die durch TeX ersetzt wurden, werden nicht extrahiert. Die Anzahl steht im Bericht
|
||||
(`formula_images_replaced_by_tex`).
|
||||
- **Qualität hängt vom EPUB ab:** Echtes MathML/TeX liefert semantische Formeln. Reine SVG-/Bitmap-Formeln
|
||||
bleiben Bilder.
|
||||
|
||||
## Fußnoten-Modi
|
||||
|
||||
Erkannt werden `epub:type="noteref|footnote|endnote|rearnote"`, `role="doc-noteref|doc-footnote|doc-endnote"`,
|
||||
Klassen wie `footnote`/`endnote` und Links auf solche Ziele. Rücklinks innerhalb der Note werden entfernt.
|
||||
Die Note erscheint nicht zusätzlich im Fließtext.
|
||||
|
||||
| Modus | Ergebnis |
|
||||
| --- | --- |
|
||||
| `end-of-chapter` | `Text[^fn-001]` und `[^fn-001]: …` am Kapitelende. Mehrere Absätze, Code, Formeln und Links bleiben erhalten. Mehrfachverweise nutzen dieselbe Definition |
|
||||
| `end-of-book` | `chapters/999-anmerkungen.md` mit Ankern. Verweise `<sup>[1](999-anmerkungen.md#fn-001)</sup>`, Rücklinks `↩` zu jeder Verweisstelle |
|
||||
| `inline` | `Text (Anmerkung: …)`. Noten mit Blockinhalt (Code, Listen, Tabellen) fallen auf `end-of-chapter` zurück (Hinweis im Bericht) |
|
||||
| `keep-links` | Originalstruktur: Note bleibt an ihrer Stelle, Verweise sind normale Links auf Anker |
|
||||
|
||||
Mit `--format commonmark` werden Fußnoten als Anker-Links mit Abschnitt „Anmerkungen“ ausgegeben,
|
||||
weil CommonMark keine `[^…]`-Syntax kennt.
|
||||
|
||||
## Qualitätshinweise
|
||||
|
||||
- **Formelbasierte EPUBs:** Am besten sind EPUBs mit MathML oder TeX im `alt`-Text (z. B. Springer).
|
||||
- **SVG-Formeln** bleiben SVG (nie gerastert) und werden im Bericht als Formel markiert.
|
||||
- **Layouttabellen / komplexe Tabellen** (`colspan`, `rowspan`, Blockinhalte in Zellen, verschachtelte
|
||||
Tabellen) bleiben als bereinigtes HTML im Markdown erhalten, mit Warnung. Einfache Tabellen werden
|
||||
GFM-Pipe-Tabellen. Einzellige Layouttabellen werden als normaler Text ausgegeben.
|
||||
- **Syntax-Highlighting:** Farb-Spans, `<strong>`/`<em>` in Listings und Zeilennummern werden entfernt.
|
||||
Einrückung, Tabs, `<`, `>`, `&`, `$`, `|`, Backslashes und Backticks bleiben exakt erhalten. Die Sprache
|
||||
kommt aus Klassen/Attributen (`language-python`, `data-lang`, …) oder starken Inhaltssignalen
|
||||
(`#include`, `def …:`, Shebang, …). Ist sie unsicher, bleibt das Label leer.
|
||||
- **Gescannte Texte:** nur Bilder, kein Text (keine OCR).
|
||||
- **DRM:** wird erkannt und abgelehnt.
|
||||
- **Register:** Buchstabengruppen werden zu `##`-Überschriften, Untereinträge erhalten `– `. Interne
|
||||
Registerlinks werden auf Kapiteldatei + Anker umgeschrieben. Seitenzahlen bleiben als Text sichtbar.
|
||||
|
||||
## Weiterverarbeitung mit Pandoc
|
||||
|
||||
```bash
|
||||
pandoc output/programmierbuch/chapters/*.md \
|
||||
--metadata-file=output/programmierbuch/metadata.yaml \
|
||||
-o programmierbuch.html
|
||||
|
||||
pandoc output/mathematikbuch/chapters/*.md \
|
||||
--metadata-file=output/mathematikbuch/metadata.yaml \
|
||||
--pdf-engine=xelatex \
|
||||
-o mathematikbuch.pdf
|
||||
```
|
||||
|
||||
- Für HTML mit Formeln `--mathml`, `--mathjax` oder `--katex` verwenden. Pandocs Standard-HTML kann
|
||||
Matrizen nicht in Unicode umsetzen, meldet „Could not convert TeX math“ und belässt das TeX.
|
||||
- Ausgaben mit `--math tex` (`\(…\)`) brauchen `-f markdown+tex_math_single_backslash`.
|
||||
- Das YAML-Frontmatter jeder Kapiteldatei wird von Pandoc als Metadaten gelesen. Für ein Gesamtdokument
|
||||
ggf. `--no-frontmatter` verwenden.
|
||||
|
||||
## Bericht und Diagnose
|
||||
|
||||
`conversion-report.json` enthält u. a. Programmversion, Zeitstempel, Eingabedatei mit SHA-256, EPUB-Version,
|
||||
Optionen, Metadaten, Kapitel mit Statistik, alle Medien, Codeblöcke (Sprache + Erkennungsart),
|
||||
Formeln (Herkunft + Darstellung), Fußnoten (Modus, erkannt/referenziert/ausgegeben), Spezialabschnitte,
|
||||
Warnungen, nicht-fatale Fehler, nicht aufgelöste interne Links, nicht unterstützte Elemente,
|
||||
übersprungene Dokumente und das Ergebnis eines **Linkchecks über die erzeugte Ausgabe**. Dabei wird
|
||||
jeder relative Link und jedes Bild auf Zieldatei und Anker geprüft.
|
||||
|
||||
`status` ist `success`, `success-with-warnings` oder `success-with-errors`. Bei Warnungen wird nie
|
||||
Verlustfreiheit behauptet (`lossless: false`).
|
||||
|
||||
## Sicherheit und Fehlerbehandlung
|
||||
|
||||
- Das EPUB wird **nie** pauschal entpackt. Einträge werden einzeln gelesen. Absolute Pfade und `..`
|
||||
werden ignoriert und gemeldet (mit `--strict`: Abbruch). Alle Ausgabedateien werden vor dem Schreiben
|
||||
gegen das Zielverzeichnis geprüft.
|
||||
- Schutz gegen ZIP-Bomben: Größenlimit je Eintrag und Prüfung des Kompressionsverhältnisses.
|
||||
- XML wird mit lxml **ohne DTD-Laden, ohne Entity-Auflösung und ohne Netzwerk** geparst (kein XXE).
|
||||
HTML-Entities (` ` …) werden vorab in Zeichenreferenzen umgesetzt.
|
||||
- Ungültiges UTF-8 wird mit Ersatzzeichen gelesen und als Warnung gemeldet.
|
||||
- Keine Netzwerkzugriffe, keine Shell-Aufrufe, keine Telemetrie.
|
||||
- Die Ausgabe entsteht in einem temporären Verzeichnis neben dem Ziel und wird erst am Ende an ihren Platz
|
||||
verschoben. Bei Fehlern wird das temporäre Verzeichnis gelöscht, und es bleibt keine halbe Ausgabe zurück.
|
||||
- **Bestehendes Ausgabeverzeichnis:** Ist es nicht leer, bricht das Programm ab (Exit 1). `--force` ersetzt
|
||||
es nur, wenn es eine frühere epub2md-Ausgabe ist (`conversion-report.json`/`manifest.json` vorhanden),
|
||||
nicht das Arbeits- oder Home-Verzeichnis ist und die Eingabedatei nicht darin liegt.
|
||||
|
||||
## Architektur
|
||||
|
||||
```text
|
||||
epub2md.py Einstiegspunkt
|
||||
epub2md/
|
||||
├── cli.py argparse, Exit-Codes
|
||||
├── pipeline.py Ablaufsteuerung, Schreiben (atomar), Bericht/Manifest
|
||||
├── epub_reader.py ZIP-Sicherheit, DRM-Check, container.xml, OPF, NAV, NCX, Guide
|
||||
├── normalize.py Laden der Spine-Dokumente, Layout-Heuristiken, Seitenumbrüche, HTML-TOC
|
||||
├── footnotes.py Erkennung/Auslagerung von Fuß- und Endnoten
|
||||
├── chapters.py Kapitelbildung (Spine + Überschriften + epub:type), Klassifikation
|
||||
├── references.py Ziel-IDs → Kapiteldatei + Anker, Link-Auflösung
|
||||
├── html_converter.py rekursiver XHTML→Markdown-Renderer (Blöcke, Inline, Tabellen, Listen, Bilder)
|
||||
├── codeblocks.py Codeerkennung, exakte Textextraktion, Spracherkennung, Fences
|
||||
├── mathml.py konservative MathML→TeX-Konvertierung, TeX-Erkennung
|
||||
├── media.py Medienregistry mit stabilen Namen
|
||||
├── linkcheck.py Validierung aller relativen Links/Anker der Ausgabe
|
||||
├── writer.py README, metadata.yaml, Frontmatter, Diagnose-Markdown
|
||||
├── diagnostics.py zentrale Sammlung von Warnungen/Fehlern
|
||||
├── models.py Dataclasses (Kapitel, Medien, Noten, Metadaten …)
|
||||
└── utils.py Slugs, YAML-Emitter, Pfadhelfer
|
||||
```
|
||||
|
||||
Statt BeautifulSoup wird direkt lxml verwendet: Es ist für die XHTML-Bäume ausreichend, schneller und
|
||||
ermöglicht die sichere Parser-Konfiguration (XXE). Die Konvertierung ist deterministisch. Gleiche
|
||||
Eingabe und Optionen ergeben identische Kapiteldateien.
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
python3 -m pytest -q
|
||||
```
|
||||
|
||||
Die Tests erzeugen synthetische Minimal-EPUBs (EPUB 2/3, NAV/NCX, Code, Tabellen, MathML, SVG,
|
||||
Fußnoten, Glossar/Literatur/Register, unsichere ZIP-Pfade, DRM, XXE, defekte Container, Kodierung) und
|
||||
prüfen die tatsächliche CLI-Ausgabe. Liegen EPUBs unter `Beispiele/`, werden sie zusätzlich auf
|
||||
Struktur-Invarianten geprüft: keine kaputten Links, balancierte Code-Fences, gültiges YAML, alle Medien
|
||||
vorhanden. Dabei werden keine Buchtexte verglichen. `Beispiele/` ist per `.gitignore` vom Repository
|
||||
ausgeschlossen.
|
||||
Loading…
Reference in a new issue