- --ocr always / schiefe OCR-Ebenen: alte unsichtbare Textebene vor der
OCR entfernen (nur in der Kopie im Speicher); vorher blieb der alte
Text erhalten bzw. stand doppelt da ("bliht" statt "blüht")
- --keep-lines: jede PDF-Zeile bleibt eine Zeile, Strophen als Absätze,
Einrückungen bleiben, nichts wird zusammengefügt (auch keine Trennung)
- OCR-Text selbst erkennen (unsichtbar, wie pymupdf4llm 1.28 intern);
Buchkante "|" entfernen, Rauschzeilen und Fetzen neben dem Textbereich
verwerfen, Zeilen mit verschiedenen Grundlinien trennen, Überschriften
ohne Buchstaben/Ziffern verwerfen
- Fraktur: frk+deu dokumentiert, Grenzen in TODO
- Tests: Scan mit falscher OCR-Ebene, Gedicht in schmaler Spalte,
OCR-Rauschen; README, Bedienungsanleitung, TODO
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
15 KiB
Bedienungsanleitung: pdf2md.py – PDF → Markdown → EPUB
pdf2md.py wandelt PDF-Bücher in sauberes Markdown um: echte Absätze, Überschriften mit
richtiger Ebene, Bilder und ohne Seitenzahlen. Daraus entsteht auf Wunsch direkt ein EPUB für
den PocketBook-Reader. Das funktioniert mit digitalen PDFs (Text lässt sich markieren) ebenso wie
mit gescannten PDFs (nur Seitenbilder, der Text wird per OCR erkannt).
Es gibt zwei Arbeitswege:
- Weg A: PDF → EPUB in einem Schritt, wenn das Ergebnis ohne Nacharbeit passt.
- Weg B: PDF → Markdown → von Hand bearbeiten → EPUB, wenn du Überschriften, Text oder Bilder noch korrigieren willst.
- Weg C: wie Weg B, aber jeder Teil bzw. jedes Kapitel in einer eigenen Datei – zum Umsortieren, für RAG oder andere Formate.
1. Installation und Prüfung
Einmalig unter Ubuntu 24.04:
sudo apt install tesseract-ocr tesseract-ocr-deu pandoc
git clone https://kitux.de/forgejo/dschlueter/pdf2md.git
cd pdf2md # Ordner des Repos
pip install -r requirements.txt
tesseract-ocr+tesseract-ocr-deubraucht man nur für gescannte PDFs.pandocbraucht man nur für das EPUB.
Prüfen, ob alles da ist:
tesseract --list-langs # muss "deu" enthalten
pandoc --version # Version 3.x
python3 pdf2md.py --help # zeigt alle Optionen
2. Weg A – PDF → EPUB in einem Schritt
cd pdf2md # Ordner des Repos
python3 pdf2md.py --epub Operation_Ljutsch_Band_1_-_Reinhard_Otto_Kranz.pdf Output/band1.md
Das Programm meldet zuerst, was es in der PDF gefunden hat:
706 Seiten: 705 Text, 1 Scan/Bild, 0 mit OCR-Ebene, 0 leer → 0 Seiten werden per OCR gelesen
Hinweis: 1 Bildseite(n) ohne Text bleiben ohne OCR (Fotos/Cover oder --ocr never).
[1/15] Seiten 1–50 (Text) fertig
...
| Begriff | Bedeutung |
|---|---|
| Text | Seiten mit echter Textebene, keine OCR nötig |
| Scan/Bild | Seiten, die nur aus einem Bild bestehen (Scan, Foto, Cover) |
| mit OCR-Ebene | Scans, die schon unsichtbaren Text enthalten (z. B. von ocrmypdf) |
| „Hinweis: … Bildseite(n)“ | Bildseiten, die bewusst nicht per OCR gelesen werden, meist das Cover |
Danach liegen in Output/:
| Datei | Inhalt |
|---|---|
band1.md |
das Buch als Markdown |
band1_images/ |
Bilder als JPEG, dazu cover.jpg (Seite 1 der PDF) |
band1.epub |
das fertige E-Book (Titel und Autor aus der PDF, siehe unten) |
Ein Band mit ca. 700 Seiten dauert etwa anderthalb Minuten.
Titel und Autor im EPUB stammen aus den Metadaten der PDF. Fehlen sie, wertet das Programm
den Dateinamen nach dem Muster Autor_-_Titel.pdf aus: Katja_Adler_-_Rolle_rückwärts.pdf ergibt
Titel „Rolle rückwärts“ und Autor „Katja Adler“. Ohne - im Namen wird der ganze Dateiname
(mit Leerzeichen statt _) zum Titel. Es lohnt sich also, PDFs vorher passend umzubenennen.
3. Weg B – Markdown von Hand bearbeiten, dann EPUB
Schritt 1: Markdown erzeugen
Wie bei Weg A, also mit --epub. So entstehen gleich das Cover (cover.jpg) und ein erstes
EPUB zum Vergleichen:
cd pdf2md # Ordner des Repos
python3 pdf2md.py --epub Operation_Ljutsch_Band_1_-_Reinhard_Otto_Kranz.pdf Output/band1.md
Wenn du kein Probe-EPUB brauchst, reicht --images statt --epub (das Cover wird trotzdem
abgelegt).
Am Anfang von band1.md steht ein YAML-Kopf mit den Angaben für das EPUB:
---
title: "Operation Ljutsch"
author: "Reinhard Otto Kranz"
lang: "de"
cover: "band1_images/cover.jpg"
---
Hier kannst du Titel, Autor, Sprache oder Cover ändern. Werte immer in "…" schreiben.
Schritt 2: band1.md bearbeiten
Öffne die Datei in einem beliebigen Texteditor und halte dich an diese Regeln:
| Element | Schreibweise | Wirkung im EPUB |
|---|---|---|
| Teil | ## Teil I: Das Vermächtnis |
eigener Abschnitt im Inhaltsverzeichnis |
| Kapitel | ### 1 Der Notar |
eigenes Kapitel (eigene Datei im EPUB), unter dem Teil eingeordnet |
| Absatz | durch eine Leerzeile vom nächsten getrennt | neuer Absatz |
| Zeilenumbruch innerhalb eines Absatzes | einfacher Umbruch | wird zu einem Leerzeichen |
| Zeile erzwingen (Register, Vers, Adresse) | \ am Zeilenende |
neue Zeile im selben Absatz |
| Einrückung außerhalb von Code | geschützte Leerzeichen (U+00A0) am Zeilenanfang | bleibt erhalten (normale Leerzeichen verschwinden) |
| Code | zwischen zwei Zeilen ```, Einrückung mit Leerzeichen |
Schreibmaschinenschrift, Einrückung bleibt, lange Zeilen werden umbrochen |
| Bild |  auf eigener Zeile |
Bild an dieser Stelle |
| kursiv / fett | _Wort_ / **Wort** |
kursiv / fett |
| Sonderzeichen wörtlich | \*, \_, \[, \<, \$ … (so erzeugt das Programm sie auch) |
Zeichen erscheint wie geschrieben, ohne Formatierung |
| Link | <https://beispiel.de/seite> oder [Text](https://beispiel.de/seite) |
anklickbarer Link |
Wichtig:
- Der Ordner
band1_images/muss nebenband1.mdliegen bleiben. Benennst du die Markdown-Datei um, ändern sich die Bildpfade nicht, die Bilder bleiben gültig. - Bildzeilen nicht verändern, nur löschen, wenn das Bild weg soll.
- Nach
#immer ein Leerzeichen:## Titel, nicht##Titel.
Beispiel: Eine falsch erkannte Überschrift korrigieren.
Vorher (ein Satz, der als Überschrift erkannt wurde, und ein Kapiteltitel ohne Überschrift):
## Er ging nach Hause.
**4 Flucht aufs Land**
Am nächsten Morgen ...
Nachher:
Er ging nach Hause.
### 4 Flucht aufs Land
Am nächsten Morgen ...
Schritt 3: EPUB aus dem bearbeiteten Markdown erzeugen
cd pdf2md # Ordner des Repos
python3 pdf2md.py --from-md Output/band1.md
Das erzeugt Output/band1.epub mit denselben Einstellungen wie --epub. Titel, Autor, Sprache
und Cover kommen aus dem YAML-Kopf; --title, --author, --lang oder --epub-file PFAD
überschreiben sie bei Bedarf.
Schritt 2 und 3 kannst du beliebig oft wiederholen. Achtung: Schritt 1 erneut auszuführen
überschreibt deine Änderungen in band1.md. Leg dir vorher eine Kopie an oder nimm einen anderen
Dateinamen.
4. Weg C – Teile oder Kapitel als Einzeldateien
cd pdf2md # Ordner des Repos
python3 pdf2md.py --epub --split kapitel Operation_Ljutsch_Band_1_-_Reinhard_Otto_Kranz.pdf Output/band1.md
Statt band1.md entsteht der Ordner Output/band1/:
band1/
index.md Reihenfolge der Dateien + YAML-Kopf des Buchs
00_vorspann.md Titelei und Inhaltsverzeichnis
01_vorwort-berlin-2015.md
02_teil-i-das-vermaechtnis.md Teil-Überschrift (und ggf. Einleitung)
03_1-der-notar.md
…
40_impressum.md
images/ Bilder und cover.jpg
--split kapitelschneidet an Teilen und Kapiteln,--split teilenur an Teilen (ein Teil mit all seinen Kapiteln pro Datei).- Jede Datei beginnt mit einem YAML-Kopf, z. B.:
--- title: "3 Die Grauen nehmen die Spur auf" part: "Teil I: Das Vermächtnis" chapter: 3 order: 5 source_pages: [40, 43] # Seiten der PDF-Datei (nicht die gedruckten Seitenzahlen) book: "Operation Ljutsch" author: "Reinhard Otto Kranz" --- - Erkennt das Programm keine Gliederung, teilt es nach Länge (ca. 30 000 Zeichen, an
Absatzgrenzen) in
Abschnitt 1,Abschnitt 2… und meldet das. - Umsortieren: in
index.mddie Zeilen verschieben. Weglassen: Zeile löschen. Dateien dürfen umbenannt werden, wenn der Link inindex.mdmitgeändert wird. - EPUB neu bauen:
python3 pdf2md.py --from-md Output/band1/→Output/band1.epub(Kapitel in der Reihenfolge vonindex.md).
5. Gescannte PDFs
Bei Scans liest das Programm den Text per OCR (Tesseract). Der Aufruf ist derselbe:
python3 pdf2md.py --epub scan.pdf Output/scan.md
| Situation | Option |
|---|---|
| Buch in Deutsch (Standard) | – (--ocr-language deu) |
| Deutsch und Englisch gemischt | --ocr-language deu+eng (Sprachpaket tesseract-ocr-eng nötig) |
| mehr oder weniger parallele OCR-Prozesse | --jobs N (Standard: halbe Kernzahl, höchstens 8) |
| OCR erzwingen, auch für vorhandene OCR-Ebenen | --ocr always (alte Ebene wird verworfen) |
| Gedichtband, Liederbuch | zusätzlich --keep-lines (siehe unten) |
| Fraktur | --ocr-language frk+deu (Paket tesseract-ocr-frk nötig; „ſ“ bleibt erhalten) |
| englisches Buch | --ocr-language eng (Sprachpaket tesseract-ocr-eng nötig; EPUB-Sprache wird automatisch en) |
| keine OCR | --ocr never oder --no-ocr |
So entscheidet das Programm von selbst (--ocr auto):
- Scanseiten werden per OCR gelesen. Richtwert: ca. 1 Sekunde pro Seite mit 4 Prozessen, also ca. 5 Minuten für 300 Seiten.
- Scans mit vorhandener OCR-Ebene (z. B. von
ocrmypdf): Eine gerade Ebene wird übernommen, das geht sehr schnell. Eine schiefe Ebene (Scan nicht entzerrt) wird neu erkannt, weil ihre Lesereihenfolge oft falsch ist. Alte OCR-Ebenen (z. B. aus Digitalisaten älterer Bücher) sind oft schlecht: Kolumnentitel werden zu Überschriften, Buchstaben sind falsch erkannt. Dann mit--ocr alwaysneu erkennen lassen; die alte Ebene wird dabei verworfen. - Rauschen vom Scanrand (Buchstabenfetzen neben dem Textbereich) wird entfernt.
Gedichtbände und Liederbücher: Füllen die Verse die Zeile fast, kann das Programm sie nicht
von Fließtext unterscheiden und fügt sie zusammen. Mit --keep-lines bleibt jede Zeile eine
Zeile, Strophen werden durch Leerzeilen getrennt, Einrückungen bleiben; nichts wird
zusammengefügt, auch keine Silbentrennung:
python3 pdf2md.py --epub --ocr always --keep-lines Neue_Gedichte_-_Rainer_Maria_Rilke.pdf Output/Neue_Gedichte_-_Rainer_Maria_Rilke.md
- Reine Bildseiten in einem sonst digitalen PDF (Fotos, Cover) werden nur übernommen, wenn eine Probe-OCR echten Fließtext liefert. Sonst bleiben sie Bild, und das Programm meldet einen „Hinweis“.
Kolumnentitel und Seitenzahlen gedruckter Bücher werden automatisch entfernt, getrennte Wörter („Geheim-|dienst“) zusammengefügt.
6. Alle Optionen
| Option | Bedeutung |
|---|---|
pdf |
Eingabe-PDF (entfällt bei --from-md) |
markdown |
Ausgabe-Datei; ohne Angabe: gleicher Name wie die PDF mit .md |
--ocr auto|always|never |
OCR-Entscheidung (Standard auto, siehe Abschnitt 5) |
--no-ocr |
Kurzform für --ocr never |
--ocr-language |
Tesseract-Sprache(n), z. B. deu+eng (Standard deu) |
--ocr-dpi |
Auflösung für die OCR (Standard 300) |
--jobs N |
parallele OCR-Prozesse (Standard: halbe Kernzahl, höchstens 8) |
--images |
Bilder in <name>_images/ speichern und im Markdown verlinken |
--image-format jpg|png |
Bildformat (Standard jpg; png verlustfrei, aber ca. 3× größer) |
--jpeg-quality 1-100 |
JPEG-Qualität (Standard 85; 95 fast verlustfrei, Bilder ca. 1,7× größer) |
--epub |
zusätzlich EPUB erzeugen (schaltet --images ein) |
--epub-file PFAD |
anderer Pfad für das EPUB |
--split teile|kapitel |
Teile bzw. Kapitel als Einzeldateien (Abschnitt 4) |
--from-md PFAD |
nur EPUB aus .md-Datei oder --split-Ordner (Abschnitt 3, 4) |
--title, --author |
Titel/Autor im EPUB vorgeben |
--lang |
Sprache im EPUB (Standard: aus --ocr-language abgeleitet, deu→de, eng→en; sonst de) |
--keep-lines |
jede Zeile der PDF bleibt eine Zeile (Gedichte, Liedtexte); nichts wird zusammengefügt |
--keep-headers |
Kolumnentitel/Seitenköpfe nicht entfernen |
--keep-footers |
Seitenzahlen/Seitenfüße nicht entfernen |
7. Ergebnis prüfen
- Überschriften auflisten. So sieht man schnell, ob Teile und Kapitel stimmen:
Erwartet:grep -n "^#" Output/band1.md## Teil …und darunter### 1 …,### 2 …usw. Ganze Sätze als Überschrift (endend mit.oder«) sind Erkennungsfehler, die du in Weg B korrigierst. Die Ebenen kommen aus der Gliederung (Lesezeichen) der PDF; ist sie flach, stehen alle Kapitel auf einer Ebene. - Stichproben am Anfang, in der Mitte und am Ende des Markdowns mit der PDF vergleichen: Fehlen Absätze, sind Absätze zusammengeklebt, stehen Bilder an der richtigen Stelle?
- EPUB auf dem PocketBook testen: Inhaltsverzeichnis, Kapitelsprünge, Bilder, Cover.
- Für Feinarbeit direkt am EPUB eignen sich Sigil oder der Calibre-Editor („Buch bearbeiten“).
8. Häufige Probleme
| Meldung / Problem | Lösung |
|---|---|
Tesseract-Sprachdaten fehlen: deu – installieren mit: … |
den angezeigten Befehl ausführen, z. B. sudo apt install tesseract-ocr-deu |
Tesseract nicht gefunden |
sudo apt install tesseract-ocr tesseract-ocr-deu |
pandoc nicht gefunden |
sudo apt install pandoc |
| Bilder fehlen im EPUB (Weg B) | pandoc im Ordner der .md starten (cd Output) bzw. --resource-path auf diesen Ordner setzen; der _images-Ordner muss daneben liegen |
| Gescannter Text fehlt, Meldung „Hinweis: … Bildseite(n)“ | die Seite wurde für ein Foto gehalten → --ocr always |
| Satz als Überschrift oder Kapitel ohne Überschrift | in Weg B von Hand korrigieren (Abschnitt 3) |
Überschrift besteht nur aus einer Zahl (## 90, ## XX) |
wird normalerweise automatisch entfernt; bleibt sie, weil direkt eine Überschrift folgt, in Weg B löschen |
| Buchstaben fehlen („Offzielle“, „treff ch“) | Ligatur (fi, fl) ohne Textzuordnung in der PDF; von Hand korrigieren |
| Überschrift gesperrt („M e i n k l e i nes Stück“) | Buchstaben einzeln gesetzt in der PDF; von Hand korrigieren |
| Link führt ins Leere oder ist abgeschnitten | URL in der PDF nachsehen und in <…> korrigieren; manche PDFs enthalten selbst abgeschnittene Linkziele |
| Register, Verse oder Code zu einem Absatz verklebt | Zeilen in Weg B mit \ am Ende trennen bzw. Code in ``` einschließen; das Programm erkennt nur Umbrüche, bei denen das nächste Wort noch in die Zeile gepasst hätte |
Fließtext zerfällt in einzelne Zeilen mit \ |
in Weg B die \ am Zeilenende entfernen |
| Formeln erscheinen als Bilder | bei Mathebüchern normal; dafür ist das Programm nicht gebaut |
| Alter Scan: falsche Überschriften, viele Lesefehler | --ocr always (vorhandene OCR-Ebene ignorieren) |
| Gedicht wird zu Fließtext (Verse zusammengefügt) | --keep-lines |
| Auf Scans fehlen Leerzeichen („deutetin“) | OCR-Fehler; höhere Auflösung versuchen (--ocr-dpi 400) oder von Hand korrigieren |
| EPUB zu groß | --image-format jpg (Standard) verwenden; PNG ist ca. 3× größer; --jpeg-quality 70 spart weiter |
--from-md: „In index.md genannt, aber nicht vorhanden“ |
Datei umbenannt oder gelöscht → Link in index.md anpassen |
Meine Änderungen in der .md sind weg |
Schritt 1 wurde erneut ausgeführt → vorher Kopie anlegen |