Implementation
static OcrMrzResult? handle(OcrData ocr, void Function(OcrMrzLog log)? mrzLogger) {
// secondLineFixed must be the full TD3 line 2 (length 44), already normalized (< padded).
final _dateSexRe = RegExp(r'(\d{6})(\d)([MFX])(\d{6})(\d)', caseSensitive: false);
final _td23Lead = RegExp(r'^[A-Z0-9<]{9}\d[A-Z<]{3}$'); // used on a 13-char slice from start
String _normLine(String s) => s.toUpperCase().replaceAll(' ', '<').replaceAll(RegExp(r'[^A-Z0-9<\r\n]'), '<').trim();
String _normalizeNat(String s) => s
.toUpperCase()
.replaceAll('1', 'I') // common OCR confusables
.replaceAll('0', 'O');
OcrMrzValidation validation = OcrMrzValidation();
DocumentStandardType? type;
String? format;
String? docNumber;
String? firstName;
String? lastName;
DateTime? birthDate;
DateTime? expiryDate;
DateTime? issueDate;
String? countryCode;
String? nationality;
String? issuing;
String? docCode;
String? sex;
String? optional;
MrzName? name;
String finalCheckValue = "";
List<String> rawLines = ocr.lines.map((a) => a.text).toList();
List<String> rawMrzLines = [];
List<String> otherLines = [];
List<String> fixedMrzLines = [ ];
String firstLineFixed = "";
String secondLineFixed = "";
String? thirdLineFixed;
final lines = _normLine(ocr.text).split(RegExp(r'[\r\n]+')).map((e) => e.trim()).where((e) => e.isNotEmpty).toList();
if (lines.isNotEmpty) {
// 1) Find the line that contains the date+sex block (this is line2)
int idx2 = -1;
RegExpMatch? m;
for (var i = 0; i < lines.length; i++) {
final mm = _dateSexRe.firstMatch(lines[i]);
if (mm != null) {
idx2 = i;
m = mm;
break;
}
}
if (idx2 != -1 && m != null) {
// mrzLogger?.call(OcrMrzLog(rawText: ocr.text, rawMrzLines: rawMrzLines, fixedMrzLines: [], validation: validation, extractedData: {}));
final line2 = lines[idx2];
// 2) Decide TD1 vs TD2/TD3 by looking at start-of-line signature
// If the first 13 chars look like doc(9)+chk(1)+nat(3) → TD2/TD3.
final head13 = line2.length >= 13 ? line2.substring(0, 13) : line2;
final looksTd23 = head13.length == 13 && _td23Lead.hasMatch(head13);
// 4) Collect line1 (previous non-empty) and line3 (next non-empty) if present
String line1 = '';
String? line3;
// previous non-empty line
for (int i = idx2 - 1; i >= 0; i--) {
if (lines[i].isNotEmpty) {
line1 = lines[i];
break;
}
}
// next non-empty line
for (int i = idx2 + 1; i < lines.length; i++) {
if (lines[i].isNotEmpty) {
line3 = lines[i];
break;
}
}
if (line3 == null) {
} else {
rawMrzLines = [line1, line2, line3];
}
// If we didn’t find line1 (sometimes input has only one line), keep empty.
// For TD1 we expect a third line; for TD2/TD3 names are usually on line1.
// log("\n${rawMrzLines.join("\n")}");
// Also use length as a hint to pick TD2 vs TD3 later
if (looksTd23) {
// Differentiate TD2 vs TD3 by typical lengths (36 vs 44)
if (line2.length >= 40) {
type = DocumentStandardType.td3;
format = "td3";
rawMrzLines = [line1, line2];
fixedMrzLines = rawMrzLines.map((a) => normalize(a, len: 44)).toList();
} else if (line2.length >= 36) {
type = DocumentStandardType.td2;
format = 'td2';
rawMrzLines = [line1, line2];
fixedMrzLines = rawMrzLines.map((a) => normalize(a, len: 36)).toList();
} else {
format = "td2";
rawMrzLines = [line1, line2];
fixedMrzLines = rawMrzLines.map((a) => normalize(a, len: 30)).toList();
// fallback: date block position near 13 strongly suggests TD2/TD3
type = DocumentStandardType.td2;
}
} else {
format = "td1";
rawMrzLines = [line1, line2, line3 ?? ''];
fixedMrzLines = rawMrzLines.map((a) => normalize(a, len: 30)).toList();
type = DocumentStandardType.td1;
}
int newIdx2 = -1;
RegExpMatch? newM;
for (var i = 0; i < fixedMrzLines.length; i++) {
final mm = _dateSexRe.firstMatch(fixedMrzLines[i]);
if (mm != null) {
newIdx2 = i;
newM = mm;
break;
}
}
if (newIdx2 != -1 && newM != null) {
// mrzLogger?.call(OcrMrzLog(rawText: ocr.text, rawMrzLines: rawMrzLines, fixedMrzLines: fixedMrzLines, validation: validation, extractedData: {}));
final dsStart = newM.start; // start index of YYMMDD+chk+sex+YYMMDD+chk in line2
final birthStr = newM.group(1)!;
final birthChkStr = newM.group(2)!;
final sexStr = newM.group(3)!;
final expiryStr = newM.group(4)!;
final expiryChkStr = newM.group(5)!;
// 3) Extract nationality relative to the date+sex block
String nationalityRaw;
if (type == DocumentStandardType.td1) {
// TD1: nationality is AFTER the 15-char date+sex block (pos 15..17)
final natStart = dsStart + 15;
final natEnd = natStart + 3;
nationalityRaw = (natEnd <= line2.length) ? line2.substring(natStart, natEnd) : '';
} else {
// TD2/TD3: nationality is the 3 chars BEFORE the date+sex block
final natStart = dsStart - 3;
final natEnd = dsStart;
nationalityRaw = (natStart >= 0) ? line2.substring(natStart, natEnd) : '';
}
nationality = _normalizeNat(nationalityRaw);
if (isValidMrzCountry(nationality)) {
// mrzLogger?.call(OcrMrzLog(rawText: ocr.text, rawMrzLines: rawMrzLines, fixedMrzLines: [], validation: validation, extractedData: {}));
log("nationality is valid");
otherLines = [...rawLines].where((a) => !rawMrzLines.contains(a)).toList();
if (fixedMrzLines.length >= 2) {
validation.linesLengthValid = true;
firstLineFixed = fixedMrzLines.first;
docCode = firstLineFixed.substring(0, 2);
countryCode = firstLineFixed.substring(2, 5);
issuing = firstLineFixed.substring(2, 5);
validation.docCodeValid = DocumentCodeHelper.isValid(docCode);
validation.countryValid = isValidMrzCountry(countryCode);
if (!validation.countryValid) {
log("country ${countryCode} is no valid");
}
secondLineFixed = fixedMrzLines[1];
final mrzDatesSex = RegExp(r'(\d{6})(\d)([MFX<])(\d{6})(\d)', caseSensitive: false);
final dateSexMatch = mrzDatesSex.firstMatch(secondLineFixed);
if (dateSexMatch != null) {
final birthDateStr = dateSexMatch.group(1);
final birthCheck = dateSexMatch.group(2);
final sexStr = dateSexMatch.group(3);
final expiryDateStr = dateSexMatch.group(4);
final expiryCheck = dateSexMatch.group(5);
validation.birthDateValid = _computeMrzCheckDigit(birthDateStr!) == birthCheck;
validation.expiryDateValid = _computeMrzCheckDigit(expiryDateStr!) == expiryCheck;
birthDate = _parseMrzDate(birthDateStr);
expiryDate = _parseMrzDate(expiryDateStr);
sex = sexStr;
}
if (type == DocumentStandardType.td3) {
final td3DocNumber = RegExp(r'^([A-Z0-9<]{9})(\d)', caseSensitive: false);
final td3DocNumberMatch = td3DocNumber.firstMatch(secondLineFixed);
if (td3DocNumberMatch != null) {
final docNumberStr = td3DocNumberMatch.group(1);
final docNumberCheckStr = td3DocNumberMatch.group(2);
docNumber = docNumberStr;
validation.docNumberValid = _computeMrzCheckDigit(docNumberStr ?? '') == docNumberCheckStr;
finalCheckValue += docNumberStr ?? '';
finalCheckValue += docNumberCheckStr ?? '';
}
final td3Nationality = RegExp(r'^[A-Z0-9<]{10}([A-Z0-9<]{3})', caseSensitive: false);
final td3NationalityMatch = td3Nationality.firstMatch(secondLineFixed);
if (td3NationalityMatch != null) {
final nationalityStr = fixAlphaOnlyField(td3NationalityMatch.group(1)!);
// log("nationalityStr ${nationalityStr}");
nationality = nationalityStr;
validation.nationalityValid = isValidMrzCountry(nationalityStr ?? '');
} else {
// log("td3NationalityMatch no match");
}
if (!validation.docNumberValid) {
final oldFixes = parseOldNumNat(secondLineFixed);
if (oldFixes.length == 2) {
docNumber = oldFixes[0];
nationality = oldFixes[1];
validation.docNumberValid = true;
validation.nationalityValid = true;
}
}
if (dateSexMatch != null) {
finalCheckValue += stripSexFromDateSex(dateSexMatch.group(0)!);
}
final td3OptionalFinal = RegExp(r'^.{28}([A-Z0-9<]{15})(\d)$', caseSensitive: false);
final td3OptionalFinalMatch = td3OptionalFinal.firstMatch(secondLineFixed);
if (td3OptionalFinalMatch != null) {
final optionalStr = td3OptionalFinalMatch.group(1);
final finalCheckStr = td3OptionalFinalMatch.group(2);
optional = optionalStr;
finalCheckValue += (optionalStr ?? '');
validation.personalNumberValid = true;
validation.hasFinalCheck = true;
validation.finalCheckValid = _computeMrzCheckDigit(finalCheckValue) == finalCheckStr;
}
name = parseNamesTd3OrTd2(firstLineFixed);
} else if (type == DocumentStandardType.td2) {
// Doc number + check (pos 0..8, 9)
final td2DocNumber = RegExp(r'^([A-Z0-9<]{9})(\d)', caseSensitive: false);
final td2DocNumberMatch = td2DocNumber.firstMatch(secondLineFixed);
if (td2DocNumberMatch != null) {
final docNumberStr = td2DocNumberMatch.group(1);
final docNumberCheckStr = td2DocNumberMatch.group(2);
docNumber = docNumberStr;
validation.docNumberValid = _computeMrzCheckDigit(docNumberStr ?? '') == docNumberCheckStr;
finalCheckValue += (docNumberStr ?? '');
finalCheckValue += (docNumberCheckStr ?? '');
}
// Nationality (pos 10..12)
final td2Nationality = RegExp(r'^[A-Z0-9<]{10}([A-Z<]{3})', caseSensitive: false);
final td2NationalityMatch = td2Nationality.firstMatch(secondLineFixed);
if (td2NationalityMatch != null) {
final nationalityStr = fixAlphaOnlyField(td2NationalityMatch.group(1)!);
nationality = nationalityStr;
validation.nationalityValid = isValidMrzCountry(nationalityStr ?? '');
finalCheckValue += nationalityStr!;
}
// Dates + sex (pos 13..27)
if (dateSexMatch != null) {
finalCheckValue += dateSexMatch.group(0)!;
}
// Optional (7) + final check (1) (pos 28..34, 35)
final td2OptionalFinal = RegExp(r'^.{28}([A-Z0-9<]{7})(\d)$', caseSensitive: false);
final td2OptionalFinalMatch = td2OptionalFinal.firstMatch(secondLineFixed);
if (td2OptionalFinalMatch != null) {
final optionalStr = td2OptionalFinalMatch.group(1);
final finalCheckStr = td2OptionalFinalMatch.group(2);
if (docCode.startsWith("V")) {
validation.hasFinalCheck = true;
validation.personalNumberValid = true;
validation.finalCheckValid = true;
} else {
optional = optionalStr;
finalCheckValue += (optionalStr ?? '');
validation.personalNumberValid = true; // treat optional as personal no.
validation.hasFinalCheck = true;
validation.finalCheckValid = _computeMrzCheckDigit(finalCheckValue) == finalCheckStr;
}
}
name = parseNamesTd3OrTd2(firstLineFixed);
} else if (type == DocumentStandardType.td1) {
docNumber = firstLineFixed.substring(5, 14);
final docNumberCheck = firstLineFixed[14];
validation.docNumberValid = _computeMrzCheckDigit(docNumber) == docNumberCheck;
finalCheckValue += firstLineFixed.substring(5, 30);
// Dates + sex first (pos 0..14)
if (dateSexMatch != null) {
finalCheckValue += stripSexFromDateSex(dateSexMatch.group(0)!);
}
// Nationality (pos 15..17)
final td1Nationality = RegExp(r'^.{15}([A-Z<]{3})', caseSensitive: false);
final td1NationalityMatch = td1Nationality.firstMatch(secondLineFixed);
if (td1NationalityMatch != null) {
final nationalityStr = fixAlphaOnlyField(td1NationalityMatch.group(1)!);
nationality = nationalityStr;
validation.nationalityValid = isValidMrzCountry(nationalityStr ?? '');
}
// Optional (11) + final check (1) (pos 18..28, 29)
final td1OptionalFinal = RegExp(r'^.{18}([A-Z0-9<]{11})(\d)$', caseSensitive: false);
final td1OptionalFinalMatch = td1OptionalFinal.firstMatch(secondLineFixed);
if (td1OptionalFinalMatch != null) {
final optionalStr = td1OptionalFinalMatch.group(1);
final finalCheckStr = td1OptionalFinalMatch.group(2);
optional = optionalStr;
final natMatch = RegExp(r'^.{15}([A-Z<]{3})', caseSensitive: false).firstMatch(secondLineFixed);
final nationalityForFinal = natMatch?.group(1) ?? '';
finalCheckValue += (optionalStr ?? ''); // positions 18..28
validation.personalNumberValid = true;
validation.hasFinalCheck = true;
validation.finalCheckValid = _computeMrzCheckDigit(finalCheckValue) == finalCheckStr;
}
String nameLine = fixedMrzLines.last;
name = parseNamesTd1(nameLine);
}
if (name != null) {
firstName = name.givenNames.join(" ");
lastName = name.surname;
var (a,_,__) = name.validateNames(otherLines,OcrMrzSetting(nameValidationMode: NameValidationMode.exact),[]);
validation.nameValid = a;
}
log(validation.toString());
log("-" * 100);
}
}
}
// return null; // no MRZ date+sex found
}
}
OcrMrzResult result = OcrMrzResult(
line1: firstLineFixed,
line2: secondLineFixed,
format: format??'unknown',
documentCode: docCode??'',
documentType: type?.name.toUpperCase()??'',
mrzFormat: format,
countryCode: fixExceptionalCountry(countryCode??''),
issuingState: fixExceptionalCountry(issuing??''),
lastName: lastName ?? '',
firstName: firstName ?? '',
documentNumber: docNumber?.replaceAll("<", "") ?? '',
nationality: fixExceptionalCountry(nationality ?? ''),
birthDate: birthDate,
expiryDate: expiryDate,
sex: sex ?? '',
personalNumber: optional ?? '',
optionalData: optional ?? '',
valid: validation,
checkDigits: CheckDigits(document: true, birth: true, expiry: true, optional: true),
ocrData: ocr,
);
if(rawMrzLines.isNotEmpty){
log("\n${rawMrzLines.join("\n")}");
mrzLogger?.call(OcrMrzLog(rawText: ocr.text, rawMrzLines: rawMrzLines, fixedMrzLines: fixedMrzLines, validation: validation, extractedData: result.toJson()));
}else{
}
return result;
// log("\n${fixedMrzLines.join("\n")}");
// return null;
// /////////////////////////////////////////////////////////////////////////////
// if (rawLines.any((a) => a.contains("<<"))) {
// // log("Has Ocr");
// String firstLine = rawLines.firstWhere((a) => a.contains("<<"));
// int firstLineIndex = rawLines.indexOf(firstLine);
// rawMrzLines.add(firstLine);
// if (firstLineIndex < rawLines.length - 1) {
// String secondLine = rawLines[firstLineIndex + 1];
// rawMrzLines.add(secondLine);
// }
// if (firstLineIndex < rawLines.length - 2) {
// String thirdLine = rawLines[firstLineIndex + 2];
// if (thirdLine.contains("<")) {
// rawMrzLines.add(thirdLine);
// }
// }
// // log("\n${rawMrzLines.map((a)=>'$a\n${normalize44(a)}').join("\n")}");
// // log("-"*100);
// List<String> fixedMrzLines;
// if (rawMrzLines.length < 3) {
// if (rawMrzLines.first.length > 40) {
// type = DocumentStandardType.td3;
// format = MrzFormat.TD3;
//
// fixedMrzLines = rawMrzLines.map((a) => normalize(a, len: 44)).toList();
// } else {
// type = DocumentStandardType.td2;
// format = MrzFormat.TD2;
// fixedMrzLines = rawMrzLines.map((a) => normalize(a, len: 36)).toList();
// }
// } else {
// format = MrzFormat.TD1;
// type = DocumentStandardType.td1;
// fixedMrzLines = rawMrzLines.map((a) => normalize(a, len: 30)).toList();
// }
// log("\n${fixedMrzLines.join("\n")}");
//
// otherLines = [...rawLines].where((a) => !rawMrzLines.contains(a)).toList();
// if (fixedMrzLines.length < 2) return null;
// validation.linesLengthValid = true;
//
// final firstLineFixed = fixedMrzLines.first;
// docCode = firstLineFixed.substring(0, 2);
// countryCode = firstLineFixed.substring(2, 5);
// issuing = firstLineFixed.substring(2, 5);
//
// validation.docCodeValid = DocumentCodeHelper.isValid(docCode);
// validation.countryValid = isValidMrzCountry(countryCode);
//
// if (!validation.countryValid) {
// log("country ${countryCode} is no valid");
// }
//
// final secondLineFixed = fixedMrzLines[1];
// final mrzDatesSex = RegExp(r'(\d{6})(\d)([MFX<])(\d{6})(\d)', caseSensitive: false);
// final dateSexMatch = mrzDatesSex.firstMatch(secondLineFixed);
// if (dateSexMatch != null) {
// final birthDateStr = dateSexMatch.group(1);
// final birthCheck = dateSexMatch.group(2);
// final sexStr = dateSexMatch.group(3);
// final expiryDateStr = dateSexMatch.group(4);
// final expiryCheck = dateSexMatch.group(5);
//
// validation.birthDateValid = _computeMrzCheckDigit(birthDateStr!) == birthCheck;
// validation.expiryDateValid = _computeMrzCheckDigit(expiryDateStr!) == expiryCheck;
// birthDate = _parseMrzDate(birthDateStr);
// expiryDate = _parseMrzDate(expiryDateStr);
// sex = sexStr;
// }
//
// if (type == DocumentStandardType.td3) {
// final td3DocNumber = RegExp(r'^([A-Z0-9<]{9})(\d)', caseSensitive: false);
// final td3DocNumberMatch = td3DocNumber.firstMatch(secondLineFixed);
// if (td3DocNumberMatch != null) {
// final docNumberStr = td3DocNumberMatch.group(1);
// final docNumberCheckStr = td3DocNumberMatch.group(2);
// docNumber = docNumberStr;
// validation.docNumberValid = _computeMrzCheckDigit(docNumberStr ?? '') == docNumberCheckStr;
//
// finalCheckValue += docNumberStr ?? '';
// finalCheckValue += docNumberCheckStr ?? '';
// }
//
// final td3Nationality = RegExp(r'^[A-Z0-9<]{10}([A-Z0-9<]{3})', caseSensitive: false);
// final td3NationalityMatch = td3Nationality.firstMatch(secondLineFixed);
// if (td3NationalityMatch != null) {
// final nationalityStr = fixAlphaOnlyField(td3NationalityMatch.group(1)!);
// // log("nationalityStr ${nationalityStr}");
// nationality = nationalityStr;
// validation.nationalityValid = isValidMrzCountry(nationalityStr ?? '');
// } else {
// // log("td3NationalityMatch no match");
// }
//
// if (!validation.docNumberValid) {
// final oldFixes = parseOldNumNat(secondLineFixed);
// if (oldFixes.length == 2) {
// docNumber = oldFixes[0];
// nationality = oldFixes[1];
// validation.docNumberValid = true;
// validation.nationalityValid = true;
// }
// }
//
// if (dateSexMatch != null) {
// finalCheckValue += stripSexFromDateSex(dateSexMatch.group(0)!);
// }
//
// final td3OptionalFinal = RegExp(r'^.{28}([A-Z0-9<]{15})(\d)$', caseSensitive: false);
// final td3OptionalFinalMatch = td3OptionalFinal.firstMatch(secondLineFixed);
//
// if (td3OptionalFinalMatch != null) {
// final optionalStr = td3OptionalFinalMatch.group(1);
// final finalCheckStr = td3OptionalFinalMatch.group(2);
//
// optional = optionalStr;
// finalCheckValue += (optionalStr ?? '');
//
// validation.personalNumberValid = true;
// validation.hasFinalCheck = true;
// validation.finalCheckValid = _computeMrzCheckDigit(finalCheckValue) == finalCheckStr;
// }
//
// name = parseNamesTd3OrTd2(firstLineFixed);
// } else if (type == DocumentStandardType.td2) {
// // Doc number + check (pos 0..8, 9)
// final td2DocNumber = RegExp(r'^([A-Z0-9<]{9})(\d)', caseSensitive: false);
// final td2DocNumberMatch = td2DocNumber.firstMatch(secondLineFixed);
// if (td2DocNumberMatch != null) {
// final docNumberStr = td2DocNumberMatch.group(1);
// final docNumberCheckStr = td2DocNumberMatch.group(2);
// docNumber = docNumberStr;
// validation.docNumberValid = _computeMrzCheckDigit(docNumberStr ?? '') == docNumberCheckStr;
//
// finalCheckValue += (docNumberStr ?? '');
// finalCheckValue += (docNumberCheckStr ?? '');
// }
//
// // Nationality (pos 10..12)
// final td2Nationality = RegExp(r'^[A-Z0-9<]{10}([A-Z<]{3})', caseSensitive: false);
// final td2NationalityMatch = td2Nationality.firstMatch(secondLineFixed);
// if (td2NationalityMatch != null) {
// final nationalityStr = fixAlphaOnlyField(td2NationalityMatch.group(1)!);
// nationality = nationalityStr;
// validation.nationalityValid = isValidMrzCountry(nationalityStr ?? '');
// finalCheckValue += nationalityStr!;
// }
//
// // Dates + sex (pos 13..27)
// if (dateSexMatch != null) {
// finalCheckValue += dateSexMatch.group(0)!;
// }
//
// // Optional (7) + final check (1) (pos 28..34, 35)
// final td2OptionalFinal = RegExp(r'^.{28}([A-Z0-9<]{7})(\d)$', caseSensitive: false);
// final td2OptionalFinalMatch = td2OptionalFinal.firstMatch(secondLineFixed);
//
// if (td2OptionalFinalMatch != null) {
// final optionalStr = td2OptionalFinalMatch.group(1);
// final finalCheckStr = td2OptionalFinalMatch.group(2);
//
// if (docCode.startsWith("V")) {
// validation.hasFinalCheck = true;
// validation.personalNumberValid = true;
// validation.finalCheckValid = true;
// } else {
// optional = optionalStr;
// finalCheckValue += (optionalStr ?? '');
// validation.personalNumberValid = true; // treat optional as personal no.
// validation.hasFinalCheck = true;
// validation.finalCheckValid = _computeMrzCheckDigit(finalCheckValue) == finalCheckStr;
// }
// }
//
// name = parseNamesTd3OrTd2(firstLineFixed);
// } else if (type == DocumentStandardType.td1) {
// docNumber = firstLineFixed.substring(5, 14);
// final docNumberCheck = firstLineFixed[14];
// validation.docNumberValid = _computeMrzCheckDigit(docNumber) == docNumberCheck;
//
// finalCheckValue += firstLineFixed.substring(5, 30);
//
// // Dates + sex first (pos 0..14)
// if (dateSexMatch != null) {
// finalCheckValue += stripSexFromDateSex(dateSexMatch.group(0)!);
// }
//
// // Nationality (pos 15..17)
// final td1Nationality = RegExp(r'^.{15}([A-Z<]{3})', caseSensitive: false);
// final td1NationalityMatch = td1Nationality.firstMatch(secondLineFixed);
// if (td1NationalityMatch != null) {
// final nationalityStr = fixAlphaOnlyField(td1NationalityMatch.group(1)!);
// nationality = nationalityStr;
// validation.nationalityValid = isValidMrzCountry(nationalityStr ?? '');
// }
//
// // Optional (11) + final check (1) (pos 18..28, 29)
// final td1OptionalFinal = RegExp(r'^.{18}([A-Z0-9<]{11})(\d)$', caseSensitive: false);
// final td1OptionalFinalMatch = td1OptionalFinal.firstMatch(secondLineFixed);
//
// if (td1OptionalFinalMatch != null) {
// final optionalStr = td1OptionalFinalMatch.group(1);
// final finalCheckStr = td1OptionalFinalMatch.group(2);
//
// optional = optionalStr;
//
// final natMatch = RegExp(r'^.{15}([A-Z<]{3})', caseSensitive: false).firstMatch(secondLineFixed);
// final nationalityForFinal = natMatch?.group(1) ?? '';
//
// finalCheckValue += (optionalStr ?? ''); // positions 18..28
// validation.personalNumberValid = true;
// validation.hasFinalCheck = true;
// validation.finalCheckValid = _computeMrzCheckDigit(finalCheckValue) == finalCheckStr;
// }
// String nameLine = fixedMrzLines.last;
// name = parseNamesTd1(nameLine);
// }
//
// if (name != null) {
// firstName = name.givenNames.join(" ");
// lastName = name.surname;
// validation.nameValid = name.validateNames(otherLines,OcrMrzSetting(nameValidationMode: NameValidationMode.exact),[]);
// }
//
// log(validation.toString());
// log("-" * 100);
//
// OcrMrzResult result = OcrMrzResult(
// line1: firstLineFixed,
// line2: secondLineFixed,
// format: format,
// documentCode: docCode,
// documentType: type.name.toUpperCase(),
// mrzFormat: format,
// countryCode: fixExceptionalCountry(countryCode),
// issuingState: fixExceptionalCountry(issuing),
// lastName: lastName ?? '',
// firstName: firstName ?? '',
// documentNumber: docNumber ?? '',
// nationality: fixExceptionalCountry(nationality ?? ''),
// birthDate: birthDate,
// expiryDate: expiryDate,
// sex: sex ?? '',
// personalNumber: optional ?? '',
// optionalData: optional ?? '',
// valid: validation,
// checkDigits: CheckDigits(document: true, birth: true, expiry: true, optional: true),
// ocrData: ocr,
// );
// mrzLogger?.call(OcrMrzLog(rawText: ocr.text, rawMrzLines: rawMrzLines, fixedMrzLines: fixedMrzLines, validation: validation, extractedData: result.toJson()));
// return result;
// } else {
// // log("No Ocr");
// }
// return null;
}