parse static method

ScanResult parse(
  1. String rawText
)

Parses raw text input into a structured PAN card ScanResult.

Implementation

static ScanResult parse(String rawText) {
  final uppercaseText = rawText.toUpperCase();
  final foundPan = _extractPanCandidate(uppercaseText);

  if (foundPan != null) {
    final panNumber = foundPan;
    final statusChar = panNumber[3];
    final surnameInitial = panNumber[4];
    final holderStatus =
        _holderStatusMap[statusChar] ?? 'Taxpayer Category ($statusChar)';

    final Map<String, String> fields = {
      'Document Type': 'Income Tax PAN Card (India)',
      'PAN Number': panNumber,
      'Holder Category': holderStatus,
      'Taxpayer Status Code': 'Code $statusChar ($holderStatus)',
      'Surname Initial': surnameInitial,
      'Sequential Numbering': panNumber.substring(5, 9),
      'Checksum Control Character': panNumber[9],
      'PAN Syntax Status': 'Valid 10-Character Structure ✓',
    };

    final lines = rawText
        .split(_lineSplitRegex)
        .map((l) => l.trim())
        .toList();

    for (int i = 0; i < lines.length; i++) {
      final line = lines[i];
      if (_dobRegex.hasMatch(line)) {
        final dobMatch = _dobMatchRegex.firstMatch(line);
        if (dobMatch != null) fields['Date of Birth'] = dobMatch.group(0)!;
      } else if (line.contains('GOVT OF INDIA') ||
          line.contains('INCOME TAX') ||
          line.contains('PERMANENT ACCOUNT')) {
        continue;
      } else if (i < lines.length &&
          !line.contains(panNumber) &&
          line.length >= 3 &&
          _allCapsRegex.hasMatch(line)) {
        if (!fields.containsKey('Holder Name')) {
          fields['Holder Name'] = line;
        } else if (!fields.containsKey("Father's Name")) {
          fields["Father's Name"] = line;
        }
      }
    }

    final surnameMatch = fields.containsKey('Holder Name')
        ? fields['Holder Name']!.trim().split(' ').last.startsWith(surnameInitial)
        : true;

    final verifications = {
      'panSyntaxValid': true,
      'taxpayerCategoryDecoded': _holderStatusMap.containsKey(statusChar),
      'surnameInitialMatch': surnameMatch,
    };

    return ScanResult(
      mode: ScanMode.pan,
      rawValue: rawText,
      isValid: true,
      confidence: 0.99,
      fields: fields,
      verifications: verifications,
      metadata: {'panNumber': panNumber, 'statusChar': statusChar},
    );
  }

  return ScanResult(
    mode: ScanMode.pan,
    rawValue: rawText,
    isValid: false,
    confidence: 0.35,
    fields: {'Document Type': 'PAN Card (Unparsed)', 'Raw Text': rawText},
    verifications: {'panSyntaxValid': false},
  );
}