87 lines
2.7 KiB
Dart
87 lines
2.7 KiB
Dart
/// Receipt parsing entry point: spatial OCR pipeline with text fallback.
|
|
library;
|
|
|
|
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
|
|
|
|
import '../models/line_item.dart';
|
|
import '../models/ocr_element.dart';
|
|
import 'receipt_ocr.dart';
|
|
import 'receipt_parse_config.dart';
|
|
export 'receipt_parse_config.dart' show loadReceiptParseConfig;
|
|
export 'receipt_text_parser.dart' show ReceiptParseResult;
|
|
import 'receipt_spatial_parser.dart';
|
|
import 'receipt_text_parser.dart';
|
|
|
|
class ReceiptParser {
|
|
ReceiptParser._();
|
|
|
|
static Future<void> loadConfig() => loadReceiptParseConfig();
|
|
|
|
static List<LineItem> parse(String rawText) => parseReceipt(rawText).items;
|
|
|
|
/// Fallback when only flattened OCR text is available (tests, re-parse).
|
|
static ReceiptParseResult parseReceipt(String rawText) =>
|
|
ReceiptTextParser.parseReceipt(rawText);
|
|
|
|
/// Primary path: positioned ML Kit elements → row clustering → lookahead.
|
|
static ReceiptParseResult parseReceiptFromOcr(OcrDocument document) =>
|
|
_parseDocument(document);
|
|
|
|
static ReceiptParseResult parseRecognizedText(
|
|
RecognizedText recognized, {
|
|
required double imageWidth,
|
|
required double imageHeight,
|
|
}) {
|
|
final document = ReceiptOcr.fromRecognizedText(
|
|
recognized,
|
|
imageWidth: imageWidth,
|
|
imageHeight: imageHeight,
|
|
);
|
|
return _parseDocument(document);
|
|
}
|
|
|
|
static ReceiptParseResult _parseDocument(OcrDocument document) {
|
|
if (ReceiptTextParser.looksLikeColumnOcr(document.flattenedText)) {
|
|
return _withImageDimensions(
|
|
ReceiptTextParser.parseReceipt(document.flattenedText),
|
|
document,
|
|
);
|
|
}
|
|
|
|
final spatial = ReceiptSpatialParser.parse(document);
|
|
if (_shouldFallbackToText(spatial)) {
|
|
return _withImageDimensions(
|
|
ReceiptTextParser.parseReceipt(document.flattenedText),
|
|
document,
|
|
);
|
|
}
|
|
return spatial;
|
|
}
|
|
|
|
static bool _shouldFallbackToText(ReceiptParseResult spatial) {
|
|
if (spatial.items.isEmpty) return true;
|
|
final flagged =
|
|
spatial.items.where((item) => item.needsManualReview).length;
|
|
if (flagged > 0) return true;
|
|
if (spatial.items.any((item) => item.price <= 0)) return true;
|
|
if (spatial.receiptTotal != null && !spatial.validationPassed) return true;
|
|
return false;
|
|
}
|
|
|
|
static ReceiptParseResult _withImageDimensions(
|
|
ReceiptParseResult result,
|
|
OcrDocument document,
|
|
) {
|
|
return ReceiptParseResult(
|
|
items: result.items,
|
|
store: result.store,
|
|
receiptTotal: result.receiptTotal,
|
|
totalDiscount: result.totalDiscount,
|
|
validationPassed: result.validationPassed,
|
|
expectedItemCount: result.expectedItemCount,
|
|
ocrImageWidth: document.imageWidth,
|
|
ocrImageHeight: document.imageHeight,
|
|
);
|
|
}
|
|
}
|