pdf2md/tests/test_classify.py
Dieter Schlüter b4b911d37f pdf2md.py als fester Name, v1–v4 nach History/ verschoben
- pdf2md_v5.py → pdf2md.py; Verweise in Doku und Tests angepasst
- pdf2md_v1.py … pdf2md_v4.py nach History/ verschoben
- ljutsch_meta.xml entfernt (nicht mehr verwendet)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:12:36 +02:00

144 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Seitenklassifizierung und OCR-Planung."""
import pymupdf
import pytest
import pdf2md as p
from conftest import needs_tesseract
def kinds_of(path):
with pymupdf.open(path) as doc:
return [p.classify_page(page) for page in doc]
def test_digital_pages_are_text(pdfs):
assert kinds_of(pdfs["digital"]) == [p.PAGE_TEXT] * 4
def test_scanned_pages_are_scans(pdfs):
assert kinds_of(pdfs["scanned"]) == [p.PAGE_SCAN] * 4
@needs_tesseract
def test_scans_with_ocr_layer_are_detected(pdfs):
assert kinds_of(pdfs["scanned_ocr"]) == [p.PAGE_OCR_LAYER] * 4
def test_mixed_document(pdfs):
assert kinds_of(pdfs["mixed"]) == [p.PAGE_TEXT, p.PAGE_TEXT, p.PAGE_SCAN, p.PAGE_SCAN]
def test_blank_page_is_empty(pdfs):
assert kinds_of(pdfs["blank"]) == [p.PAGE_EMPTY]
def test_plan_never_uses_no_ocr(pdfs):
with pymupdf.open(pdfs["scanned"]) as doc:
_, plan = p.plan_pages(doc, "never", "deu", 300)
assert plan == [p.RUN_PLAIN] * 4
@needs_tesseract
def test_plan_auto_scanned(pdfs):
with pymupdf.open(pdfs["scanned"]) as doc:
_, plan = p.plan_pages(doc, "auto", "deu", 300)
assert plan == [p.RUN_OCR] * 4
@needs_tesseract
def test_plan_auto_reuses_existing_ocr_layer(pdfs):
with pymupdf.open(pdfs["scanned_ocr"]) as doc:
_, plan = p.plan_pages(doc, "auto", "deu", 300)
assert plan == [p.RUN_REUSE] * 4
def _scan_with_text_layer(path, angle):
"""Scanseite mit unsichtbarer, um `angle` Grad gedrehter Textebene."""
doc = pymupdf.open()
page = doc.new_page(width=420, height=595)
pix = pymupdf.Pixmap(pymupdf.csGRAY, pymupdf.IRect(0, 0, 200, 280), 0)
pix.set_rect(pix.irect, (230,))
page.insert_image(page.rect, pixmap=pix)
for i in range(20):
point = pymupdf.Point(50, 80 + 20 * i)
page.insert_text(
point,
"Eine Zeile der vorhandenen OCR-Ebene",
render_mode=3,
morph=(point, pymupdf.Matrix(1, 1).prerotate(-angle)),
)
doc.save(path)
return path
@needs_tesseract
@pytest.mark.parametrize("angle, expected", [(0, p.RUN_REUSE), (0.5, p.RUN_OCR)])
def test_plan_auto_redoes_skewed_ocr_layer(tmp_path, angle, expected):
path = _scan_with_text_layer(tmp_path / f"layer_{angle}.pdf", angle)
with pymupdf.open(path) as doc:
kinds, plan = p.plan_pages(doc, "auto", "deu", 300)
assert kinds == [p.PAGE_OCR_LAYER]
assert plan == [expected]
@needs_tesseract
def test_plan_auto_mixed_probes_scan_pages(pdfs):
# 2 Text- zu 2 Scanseiten gilt nicht als Scan-Dokument; die Scanseiten
# werden per Probe-OCR geprüft und bestehen sie.
with pymupdf.open(pdfs["mixed"]) as doc:
_, plan = p.plan_pages(doc, "auto", "deu", 300)
assert plan == [p.RUN_PLAIN, p.RUN_PLAIN, p.RUN_OCR, p.RUN_OCR]
@needs_tesseract
def test_plan_auto_skips_photo_page(pdfs):
with pymupdf.open(pdfs["photo"]) as doc:
kinds, plan = p.plan_pages(doc, "auto", "deu", 300)
assert kinds[-1] == p.PAGE_SCAN
assert plan == [p.RUN_PLAIN] * 5
@needs_tesseract
def test_plan_always(pdfs):
with pymupdf.open(pdfs["digital"]) as doc:
_, plan = p.plan_pages(doc, "always", "deu", 300)
assert plan == [p.RUN_OCR] * 4
def test_missing_language_gives_clear_error():
if not pymupdf.get_tessdata():
pytest.skip("Tesseract nicht installiert")
with pytest.raises(RuntimeError, match="tesseract-ocr-xyz"):
p.check_ocr_language("deu+xyz")
@pytest.mark.parametrize(
"text, language, expected",
[
("Der Brief lag auf dem Tisch. " * 10, "deu", True),
("Am Abend saß er lange in der Küche und trank einen Tee. Die Gedanken "
"an den Brief ließen ihn nicht los.", "deu", True),
("The letter was lying on the table in the kitchen, and he read it "
"twice before he put it away.", "deu+eng", True),
# Cover mit Titel und Autor: zu wenig Wörter
("Reinhard Otto Kranz OPERATION LJUTSCH Band I Schlüssel zur Deutschen Einheit", "deu", False),
("fi@ |l1 ;; ¦¦ ~~ ‚, ., " * 20, "deu", False),
# echte Tesseract-Ausgabe einer Rauschseite
("©Se te\nansa |\naa\n.\nerNee =\nae\neea\nBa:Ben\nBena\needaaf\neeee\naar.\nEl eeBR\neen\n", "deu", False),
("Zu kurz.", "deu", False),
],
)
def test_looks_like_text(text, language, expected):
assert p.looks_like_text(text, language) is expected
def test_make_tasks_groups_and_chunks():
plan = [p.RUN_PLAIN] * 3 + [p.RUN_OCR] * 12 + [p.RUN_PLAIN]
tasks = p.make_tasks(plan)
assert [(run, pages[0], pages[-1]) for run, pages in tasks] == [
(p.RUN_PLAIN, 0, 2),
(p.RUN_OCR, 3, 12),
(p.RUN_OCR, 13, 14),
(p.RUN_PLAIN, 15, 15),
]