/// Extract positioned OCR elements from ML Kit structured output. library; import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart'; import '../models/ocr_element.dart'; class ReceiptOcr { ReceiptOcr._(); /// Reads blocks → lines → elements. Falls back to line-level boxes when a /// line has no element children. static OcrDocument fromRecognizedText( RecognizedText recognized, { required double imageWidth, required double imageHeight, }) { final elements = []; final lines = []; var maxRight = 0.0; var maxBottom = 0.0; for (final block in recognized.blocks) { for (final line in block.lines) { final lineElements = []; if (line.elements.isNotEmpty) { for (final element in line.elements) { final box = element.boundingBox; lineElements.add( OcrElement( text: element.text, left: box.left, top: box.top, right: box.right, bottom: box.bottom, ), ); if (box.right > maxRight) maxRight = box.right; if (box.bottom > maxBottom) maxBottom = box.bottom; } } else { final box = line.boundingBox; lineElements.add( OcrElement( text: line.text, left: box.left, top: box.top, right: box.right, bottom: box.bottom, ), ); if (box.right > maxRight) maxRight = box.right; if (box.bottom > maxBottom) maxBottom = box.bottom; } elements.addAll(lineElements); final box = line.boundingBox; lines.add( OcrLine( text: line.text, elements: lineElements, left: box.left, top: box.top, right: box.right, bottom: box.bottom, ), ); } } final width = imageWidth > 0 ? imageWidth : maxRight; final height = imageHeight > 0 ? imageHeight : maxBottom; return OcrDocument( elements: elements, lines: lines, imageWidth: width, imageHeight: height, flattenedText: recognized.text.trim(), ); } }