repairMrzLine2Strict function

String repairMrzLine2Strict(
  1. String rawLine
)

Implementation

String repairMrzLine2Strict(String rawLine) {
  final Map<String, String> replacements = {
    '«': '<', '|': '<', '\\': '<', '/': '<', '“': '<', '”': '<',
    '’': '<', '‘': '<', ' ': '<',
    'O': '0',
    // 'Q': '0', 'I': '1', 'L': '1', 'Z': '2', 'S': '5', 'B': '8', 'G': '6'
    // DO NOT add K/X as global replacement!
  };

  String cleaned = rawLine.toUpperCase().split('').map((c) => replacements[c] ?? c).where((c) => RegExp(r'[A-Z0-9<]').hasMatch(c)).join();
  if (cleaned.length < 44) cleaned = cleaned.padRight(44, '<');
  if (cleaned.length > 44) cleaned = cleaned.substring(0, 44);

  List<String> generateCandidates(String line) {
    final List<String> results = [line];

    for (int i = 0; i < line.length; i++) {
      if (line[i] == '<') {
        results.add(line.substring(0, i) + line.substring(i + 1));
      }
    }

    for (int i = 0; i < line.length; i++) {
      if (line[i] != '<') continue;
      for (int j = i + 1; j < line.length; j++) {
        if (line[j] != '<') continue;
        final removed = line.substring(0, i) + line.substring(i + 1, j) + line.substring(j + 1);
        results.add(removed);
      }
    }

    return results;
  }

  for (final candidate in generateCandidates(cleaned)) {
    String line = candidate;
    if (line.length < 44) line = line.padRight(44, '<');
    if (line.length > 44) line = line.substring(0, 44);




    final birth = line.substring(13, 19);
    final birthCheck = line[19];
    final expiry = line.substring(21, 27);
    final expiryCheck = line[27];

    final personalNum = line.substring(28, 42);
    final personalCheck = line[42];
    final finalCheck = line[43];

    final isBirthValid = RegExp(r'^\d{6}$').hasMatch(birth) && _computeMrzCheckDigit(birth) == birthCheck;
    final isExpiryValid = RegExp(r'^\d{6}$').hasMatch(expiry) && _computeMrzCheckDigit(expiry) == expiryCheck;

    // Optional validations:
    final isPersonalValid = personalNum.replaceAll('<', '').isEmpty
        ? (personalCheck == '0' || personalCheck == '<')
        : (_computeMrzCheckDigit(personalNum) == personalCheck);

    final finalCheckInput = line.substring(0, 10) + line.substring(13, 20) + line.substring(21, 43);
    final isFinalValid = _computeMrzCheckDigit(finalCheckInput) == finalCheck;

    final validSex = line[20] == 'M' || line[20] == 'F' || line[20] == '<';

    if (isBirthValid && isExpiryValid && isPersonalValid && isFinalValid && validSex) {
      return line;
    }
  }

  // fallback
  return cleaned;
}