repairMrzLine2Strict function
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;
}