Skip to main content

smart_document_capture 0.1.0 copy "smart_document_capture: ^0.1.0" to clipboard
smart_document_capture: ^0.1.0 copied to clipboard

Extract structured data from document, receipt and form images with per-field confidence, manual review and pluggable OCR engines.

example/lib/main.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:persian_calendar_kit/persian_calendar_kit.dart'
    show PersianDigits;
import 'package:smart_document_capture/smart_document_capture.dart';

void main() => runApp(const DemoApp());

const String _receipt = '''
فروشگاه زنجیره‌ای افق کوروش
شعبه ونک
تاریخ: ۱۴۰۴/۰۷/۲۷   ساعت: ۱۴:۳۵
شماره فاکتور: ۸۷۴۵۲
شیر پرچرب ۱ لیتری    ۴۵,۰۰۰
نان تست    ۳۸,۰۰۰
جمع اقلام: ۸۳,۰۰۰
تخفیف: ۵,۰۰۰
مبلغ قابل پرداخت: ۷۸,۰۰۰ ریال
شماره کارت: ۶۰۳۷-۹۹**-****-۷۸۹۳
شماره پیگیری: ۱۲۳۴۵۶''';

const String _form = '''
نام: علی
نام خانوادگی: محمدی
کد ملی: ۰۴۹۹۳۷۰۸۹۹
تاریخ تولد: ۱۳۷۰/۰۵/۱۲
موبایل: ۰۹۱۲۱۲۳۴۵۶۷
شبا: IR062960000000100324200001''';

/// The demo application.
class DemoApp extends StatelessWidget {
  /// Creates the app.
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Smart Document Capture',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
      builder: (BuildContext context, Widget? child) =>
          Directionality(textDirection: TextDirection.rtl, child: child!),
      home: const InputPage(),
    );
  }
}

/// Lets the user type or pick OCR text and a schema.
class InputPage extends StatefulWidget {
  /// Creates the page.
  const InputPage({super.key});

  @override
  State<InputPage> createState() => _InputPageState();
}

enum _Mode { photo, receiptText, form }

class _InputPageState extends State<InputPage> {
  final TextEditingController _text = TextEditingController(text: _receipt);
  _Mode _mode = _Mode.photo;
  double _ocrConfidence = 0.95;

  static const AssetImage _photo = AssetImage('assets/receipt.jpg');

  @override
  void dispose() {
    _text.dispose();
    super.dispose();
  }

  Future<void> _extract() async {
    // A real app would use an engine such as TesseractOcrEngine on a photo.
    // Here the photo mode replays a recorded OCR result with word boxes.
    final OcrEngine engine;
    if (_mode == _Mode.photo) {
      final String json = await DefaultAssetBundle.of(context)
          .loadString('assets/receipt_ocr.json');
      engine = FakeOcrEngine(
          OcrResult.fromJson(jsonDecode(json) as Map<String, dynamic>));
    } else {
      engine = FakeOcrEngine.text(_text.text, confidence: _ocrConfidence);
    }
    final DocumentCapture capture = DocumentCapture(engine: engine);
    final ExtractionResult result = await capture.process(
      const DocumentImage.file('receipt.jpg'),
      _mode == _Mode.form ? const KeyValueSchema() : ReceiptSchema.persian,
    );
    if (!mounted) return;
    await Navigator.of(context).push(MaterialPageRoute<void>(
      builder: (_) => ReviewPage(
        result: result,
        image: _mode == _Mode.photo ? _photo : null,
      ),
    ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('استخراج اطلاعات از سند')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: <Widget>[
          SegmentedButton<_Mode>(
            segments: const <ButtonSegment<_Mode>>[
              ButtonSegment<_Mode>(value: _Mode.photo, label: Text('عکس رسید')),
              ButtonSegment<_Mode>(
                  value: _Mode.receiptText, label: Text('متن رسید')),
              ButtonSegment<_Mode>(value: _Mode.form, label: Text('فرم')),
            ],
            selected: <_Mode>{_mode},
            onSelectionChanged: (Set<_Mode> s) => setState(() {
              _mode = s.first;
              if (_mode != _Mode.photo) {
                _text.text = _mode == _Mode.form ? _form : _receipt;
              }
            }),
          ),
          const SizedBox(height: 16),
          if (_mode == _Mode.photo) ...<Widget>[
            Text('عکس سند', style: Theme.of(context).textTheme.titleSmall),
            const SizedBox(height: 8),
            ClipRRect(
              borderRadius: BorderRadius.circular(12),
              child: ConstrainedBox(
                constraints: const BoxConstraints(maxHeight: 420),
                child: const Image(image: _photo, fit: BoxFit.contain),
              ),
            ),
          ] else ...<Widget>[
            Text('متن خوانده‌شده توسط OCR',
                style: Theme.of(context).textTheme.titleSmall),
            const SizedBox(height: 8),
            TextField(
              controller: _text,
              maxLines: 12,
              decoration: const InputDecoration(border: OutlineInputBorder()),
            ),
            const SizedBox(height: 16),
            Text('اطمینان شبیه‌سازی‌شده‌ی OCR: '
                '${PersianDigits.toPersian((_ocrConfidence * 100).round())}٪'),
            Slider(
              value: _ocrConfidence,
              min: 0.3,
              max: 1,
              divisions: 14,
              onChanged: (double v) => setState(() => _ocrConfidence = v),
            ),
          ],
          const SizedBox(height: 16),
          FilledButton.icon(
            onPressed: _extract,
            icon: const Icon(Icons.document_scanner_outlined),
            label: const Text('استخراج'),
          ),
        ],
      ),
    );
  }
}

/// Shows the extracted fields for review.
class ReviewPage extends StatefulWidget {
  /// Creates the page.
  const ReviewPage({super.key, required this.result, this.image});

  /// Result to review.
  final ExtractionResult result;

  /// The analyzed photo, when there is one.
  final ImageProvider? image;

  @override
  State<ReviewPage> createState() => _ReviewPageState();
}

class _ReviewPageState extends State<ReviewPage> {
  late final DocumentReviewController _controller =
      DocumentReviewController(widget.result);

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _showJson(ExtractionResult result) {
    final String json =
        const JsonEncoder.withIndent('  ').convert(result.toJson());
    showDialog<void>(
      context: context,
      builder: (BuildContext context) => AlertDialog(
        title: const Text('خروجی JSON'),
        content: SingleChildScrollView(
          child: Directionality(
            textDirection: TextDirection.ltr,
            child: SelectableText(json,
                style: const TextStyle(fontFamily: 'monospace', fontSize: 12)),
          ),
        ),
        actions: <Widget>[
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: const Text('بستن'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('بازبینی'),
        bottom: PreferredSize(
          preferredSize: const Size.fromHeight(28),
          child: ListenableBuilder(
            listenable: _controller,
            builder: (BuildContext context, _) {
              final ExtractionResult r = _controller.result;
              final int n = r.fieldsToReview().length;
              return Padding(
                padding: const EdgeInsets.only(bottom: 8),
                child: Text(n == 0
                    ? 'همه‌ی فیلدها قابل اطمینان‌اند'
                    : '${PersianDigits.toPersian(n)} فیلد نیاز به بررسی دارد'),
              );
            },
          ),
        ),
      ),
      body: DocumentReviewView(
        controller: _controller,
        image: widget.image,
        onSubmit: _showJson,
      ),
    );
  }
}
1
likes
160
points
46
downloads
screenshot

Documentation

API reference

Publisher

verified publishermohammadkhadem.ir

Weekly Downloads

Extract structured data from document, receipt and form images with per-field confidence, manual review and pluggable OCR engines.

Repository (GitHub)
View/report issues

Topics

#ocr #document #receipt #text-recognition #persian

License

MIT (license)

Dependencies

flutter, persian_calendar_kit

More

Packages that depend on smart_document_capture