pdf2md.py wandelt eine PDF-Datei in eine oder kapitelweise mehrere MD-Dateien um (zur Weiterverarbeitung, z. B. HTML oder für RAG) und kann daraus komplette EPUB-Dateien für den Pocketbook-Reader (und andere Reader) erzeugen.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Dieter Schlüter f0e0562451 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>
2026-10-02 17:32:20 +02:00
tests Inhaltsverzeichnis einheitlich: Einträge als Absätze statt "\"-Zeilen 2026-10-02 17:32:20 +02:00
.gitignore Bilder standardmäßig als JPEG (--image-format), Output/ ignorieren 2026-10-02 00:47:42 +02:00
BEDIENUNGSANLEITUNG.md Zeilengetreue Übernahme von Code, Registern, Versen und Adressen 2026-10-02 16:39:06 +02:00
CLAUDE.md Zeilengetreue Übernahme von Code, Registern, Versen und Adressen 2026-10-02 16:39:06 +02:00
pdf2md.py Inhaltsverzeichnis einheitlich: Einträge als Absätze statt "\"-Zeilen 2026-10-02 17:32:20 +02:00
pytest.ini pdf2md v5: gescannte und digitale PDFs, EPUB-Export, Tests 2026-10-02 00:33:21 +02:00
README.md Inhaltsverzeichnis einheitlich: Einträge als Absätze statt "\"-Zeilen 2026-10-02 17:32:20 +02:00
requirements.txt Initial commit: pdf2md v1–v4 und Recherche 2026-10-01 23:46:54 +02:00
TODO.md Zeilengetreue Übernahme von Code, Registern, Versen und Adressen 2026-10-02 16:39:06 +02:00

ljutsch – PDF → Markdown → EPUB

Werkzeug, um PDF-Bücher in sauberes Markdown und anschließend in ein EPUB für den PocketBook-Reader umzuwandeln – mit Überschriften, Bildern und echten Absätzen. Funktioniert für digitale PDFs (Textebene vorhanden) ebenso wie für gescannte PDFs (nur Bilder, per OCR) und für Scans, die bereits eine OCR-Ebene haben (z. B. von ocrmypdf).

Entwickelt an Operation Ljutsch (Band 1 und 2), außerdem erprobt an einem deutschen Sachbuch, einem englischen Handbuch, einem Mathematikbuch, einem Bericht mit Fußnoten und einem englischen Scan von 1893 (Ergebnisse siehe Grenzen).

Installation (Ubuntu 24.04)

sudo apt install tesseract-ocr tesseract-ocr-deu pandoc
pip install -r requirements.txt     # pymupdf4llm, pymupdf-layout, pytest

tesseract-ocr wird nur für Scans gebraucht, pandoc nur für --epub.

Verwendung

Ausführliche Schritt-für-Schritt-Anleitung, auch für den Weg „Markdown von Hand bearbeiten, dann EPUB“: BEDIENUNGSANLEITUNG.md.

# digitales oder gescanntes PDF -> Markdown (OCR wird automatisch entschieden)
python3 pdf2md.py buch.pdf buch.md

# zusätzlich Bilder und EPUB (Titel/Autor aus den PDF-Metadaten bzw. dem Dateinamen
# "Autor_-_Titel.pdf", Seite 1 als Cover)
python3 pdf2md.py --epub buch.pdf buch.md

# Scan in Deutsch und Englisch, 8 parallele OCR-Prozesse
python3 pdf2md.py --ocr-language deu+eng --jobs 8 scan.pdf scan.md

# englisches Buch; alter Scan mit schlechter OCR-Ebene neu erkennen
python3 pdf2md.py --epub --ocr-language eng --ocr always scan.pdf scan.md   # EPUB-Sprache: en
Option Bedeutung
--ocr auto (Standard) Scanseiten per OCR lesen; vorhandene, gerade OCR-Ebenen übernehmen, schiefe neu erkennen; reine Bildseiten in digitalen PDFs (Fotos, Cover) nur nach erfolgreicher Probe-OCR
--ocr always / --ocr never (--no-ocr) alle Seiten neu erkennen / keine OCR
--ocr-language, --ocr-dpi Tesseract-Sprache(n) (deu), Auflösung (300)
--jobs N parallele OCR-Prozesse (Standard: halbe Kernzahl, höchstens 8); reine Textdokumente laufen immer sequenziell
--images Grafiken in <name>_images/ speichern und verlinken
--image-format jpg|png Bildformat (Standard jpg; png verlustfrei, aber ca. 3× größer)
--epub, --epub-file PFAD EPUB 3 mit pandoc erzeugen (schaltet --images ein)
--lang Sprache im EPUB (Standard: aus --ocr-language, deu→de, eng→en)
--keep-headers, --keep-footers Kolumnentitel bzw. Seitenzahlen nicht entfernen

Was die Nachbearbeitung macht

  • Fließtext oder zeilengetreu: Ein Zeilenumbruch gilt als weich (vom Satzprogramm), wenn das erste Wort der nächsten Zeile nicht mehr in die Zeile gepasst hätte – dann werden die Zeilen zu Fließtext verbunden. Sonst hat ihn der Autor gesetzt (hart), und die Zeile bleibt samt Einrückung erhalten. So bleiben Code (auch in Proportionalschrift, als ```-Block mit Einrückung), Register, Glossare, Verse, Adressen, Lebensläufe zeilengetreu (Markdown: \ am Zeilenende, Einrückung als geschütztes Leerzeichen). Codeblöcke über eine Seitengrenze werden zusammengeführt; lange Codezeilen bricht der Reader um.
  • Absätze: eine Absatzgrenze gilt nach Satzende (. ! ? …, auch mit « “ » usw. dahinter) oder an einem harten Umbruch; auf Scans werden Absätze am Einzug erkannt.
  • Silbentrennung: Geheim-|dienst → Geheimdienst, Militär-|Geheimdienst bleibt mit Bindestrich, Arbeits- und bleibt. Weiche Trennstriche (U+00AD) und ¬ am Zeilenende werden ebenfalls aufgelöst (wach­|senden → wachsenden).
  • Überschriften: Ebenen aus den PDF-Lesezeichen (Teile ##, Kapitel ###); Satzreste, die fälschlich als Überschrift erkannt wurden, kommen zurück in den Text.
  • Inhaltsverzeichnis: zusammengeklebte Einträge werden getrennt; jeder Eintrag wird ein eigener Absatz (auch bei Verzeichnissen ohne Überschrift „Inhalt“, z. B. „Vorschau Band Zwei“), nie eine Zeile mit \. Liegt das Verzeichnis als Tabelle vor, bleibt die Tabelle erhalten.
  • OCR-Ebenen in Courier werden nicht als Programmcode (`…`) gesetzt.
  • Links: URLs werden als <https://…> geschrieben und sind damit in Markdown-Viewern und im EPUB anklickbar. Beim Zeilenumbruch zerbrochene URLs (deutsche-|sehen, …/ youtube-…) werden mit den Linkzielen der PDF abgeglichen und repariert; Tracking-Parameter (utm_…) entfallen. Ohne Linkziel wird die URL aus dem gedruckten Text zusammengesetzt.
  • Seitenzahlen, Kolumnentitel, Text in Fotos werden entfernt.

Tests

python3 -m pytest            # ca. 30 s; OCR-/EPUB-Tests werden ohne Tesseract/pandoc übersprungen

Die Test-PDFs (digital, gescannt, mit OCR-Ebene, gemischt, Fotoseite) werden zur Laufzeit synthetisch erzeugt.

Grenzen

  • Die Ergebnisqualität hängt von pymupdf4llm/pymupdf-layout ab. Einige Schwächen der Bibliothek gleicht pdf2md.py per Patch aus (Bindestriche und weiche Trennstriche am Zeilenende, Absätze in Scan-Blöcken, OCR-Ebenen in Courier). Eine weitere ist bekannt: Bei Schriften mit Ligaturen (ff, fi) mit übergroßer Glyphenbox kann pymupdf4llm einzelne Zeilen verlieren. Bei den Ljutsch-Bänden tritt das nicht auf (geprüft per Wortabgleich PDF ↔ Markdown).
  • OCR-Fehler (z. B. fehlende Leerzeichen) werden nicht korrigiert. Bei alten Scans mit mitgelieferter OCR-Ebene liefert --ocr always meist deutlich besseren Text.
  • Mathematische Formeln werden als Bilder übernommen; für Mathebücher ist ein anderes Werkzeug besser geeignet (z. B. Marker, Nougat).
  • Was am PDF liegt, bleibt: fehlende Ligaturen ohne Textzuordnung („Offzielle“), gesperrt gesetzte Überschriften („M e i n k l e i nes …“), flache PDF-Gliederungen (alle Kapitel auf einer Ebene).
  • Links mit anderem Text als der URL (z. B. „hier“) werden nicht übernommen.
  • Zeilengetreue Erkennung: Verszeilen mit Satzzeichen am Ende werden zu eigenen Absätzen statt zu Zeilen; einzelne Konfigurations- oder Ausgabezeilen neben Code ([dependencies]) stehen als Zeilen statt im Codeblock.
  • Reine Zahlen können als Überschrift erscheinen (Fußnotenzeichen, römische Seitenzahlen). Weitere bekannte Schwächen: TODO.md.
  • Bilder werden als JPEG (Qualität 95) gespeichert; EPUB Band 1 ca. 5 MB.

Dateien

  • pdf2md.py – aktuelle Version
  • tests/ – pytest-Suite