feat: implement region-based QR code scanning for inbox documents
Build and Push Multi-Platform Images / build-and-push (push) Successful in 33s

This commit is contained in:
2026-05-04 21:39:32 +02:00
parent 60ac522435
commit 9a1095ad6e
7 changed files with 226 additions and 3 deletions
@@ -2,6 +2,7 @@ import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as fs from 'fs/promises';
import sharp = require('sharp');
import { PdfService } from '../preprocessing/pdf.service';
import { QrCodeService } from '../preprocessing/qr-code.service';
import {
@@ -76,6 +77,7 @@ export class BarcodeScannerService implements OnApplicationBootstrap {
doc.QrCodes = qrCodes;
doc.PageCount = pageCount;
doc.IsScanned = true;
try {
await this.documentRepo.save(doc);
} catch (err: any) {
@@ -174,6 +176,61 @@ export class BarcodeScannerService implements OnApplicationBootstrap {
}
}
/**
* Rendert eine einzelne Seite bei hoher DPI, beschneidet den angegebenen
* Bereich (normalisierte Koordinaten 0..1) und scannt ihn nach QR-Codes.
* Neu gefundene QR-Codes werden in der DB persistiert.
*/
async scanRegion(
doc: InboxDocument,
pdfPath: string,
page: number,
x: number,
y: number,
w: number,
h: number,
): Promise<{ found: string[] }> {
let imagePath: string | null = null;
try {
imagePath = await this.pdfService.pdfPageToImage(pdfPath, page, 400);
const image = sharp(imagePath);
const { width: imgW, height: imgH } = await image.metadata();
if (!imgW || !imgH) return { found: [] };
const left = Math.round(Math.max(0, x * imgW));
const top = Math.round(Math.max(0, y * imgH));
const width = Math.round(Math.min(imgW - left, w * imgW));
const height = Math.round(Math.min(imgH - top, h * imgH));
if (width <= 0 || height <= 0) return { found: [] };
const cropped = await image.extract({ left, top, width, height }).png().toBuffer();
const qrResults = await this.qrCodeService.extractFromImage(cropped);
if (qrResults.length === 0) return { found: [] };
const existingKeys = new Set((doc.QrCodes ?? []).map((qr) => `${qr.page}:${qr.value}`));
const found: string[] = [];
let changed = false;
for (const qr of qrResults) {
found.push(qr.data);
const key = `${page}:${qr.data}`;
if (!existingKeys.has(key)) {
doc.QrCodes = [...(doc.QrCodes ?? []), { page, value: qr.data }];
changed = true;
}
}
if (changed) {
await this.documentRepo.save(doc);
}
return { found };
} finally {
if (imagePath) await this.pdfService.cleanup([imagePath]);
}
}
/**
* Rescannt alle Inbox-Dokumente — wird nach Änderungen an Eingangsdokumentarten aufgerufen.
* Läuft sequenziell, um PDF-Rendering nicht zu überlasten. Fire-and-forget vom Caller.