feat(scan): support iX1600 duplex scanning on Linux

This commit is contained in:
2026-09-17 18:22:43 +02:00
parent cfef07b96d
commit dd37622bb6
24 changed files with 1411 additions and 215 deletions

View File

@@ -0,0 +1,74 @@
#!/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()