#!/usr/bin/env python3 """Assemble ordered scanner pages; OpenCV is optional, PDF creation is not.""" import argparse import json from pathlib import Path from PIL import Image def create_document(inputs, output, resolution, use_postprocess=False, profile="document", strict=False): if not inputs: raise ValueError("Keine Seiten vorhanden") output = Path(output) file_format = {".pdf": "PDF", ".png": "PNG", ".tiff": "TIFF"}.get(output.suffix.lower()) if not file_format: raise ValueError("Unbekanntes Ausgabeformat") if file_format != "PDF" and len(inputs) != 1: raise ValueError("Mehrseitige Scans benötigen PDF als Ausgabeformat") warnings = [] processor = None if use_postprocess: try: from opencv_postprocess import postprocess processor = postprocess except ImportError: if strict: raise warnings.append("OpenCV fehlt; alle Seiten wurden ohne Bildkorrektur übernommen.") partial = output.with_name(output.name + ".partial") try: for index, input_path in enumerate(inputs): input_path = Path(input_path) image_path = input_path corrected = input_path.with_name(input_path.stem + ".corrected.png") if processor: try: processor(input_path, corrected, profile) image_path = corrected except Exception: if strict: raise warnings.append(f"Seite {index + 1}: Bildkorrektur fehlgeschlagen; Rohscan übernommen.") # Only one decoded page is kept in memory, even for large duplex stacks. with Image.open(image_path) as image: with image.convert("RGB") as rgb: if file_format == "PDF": rgb.save(partial, "PDF", resolution=resolution, quality=90, append=index > 0) else: rgb.save(partial, file_format, dpi=(resolution, resolution)) corrected.unlink(missing_ok=True) partial.replace(output) finally: partial.unlink(missing_ok=True) return {"pageCount": len(inputs), "warnings": warnings} def main(): parser = argparse.ArgumentParser(description="Scan-Seiten zu einer Datei zusammenführen") parser.add_argument("--inputs", nargs="+", required=True) parser.add_argument("--output", required=True) parser.add_argument("--resolution", type=float, required=True) parser.add_argument("--profile", choices=["document", "receipt", "raw"], default="document") parser.add_argument("--postprocess", action="store_true") parser.add_argument("--strict", action="store_true") args = parser.parse_args() print(json.dumps(create_document(args.inputs, args.output, args.resolution, args.postprocess, args.profile, args.strict))) if __name__ == "__main__": main()