@@ -26,9 +26,15 @@ object OcrUtils {
2626 private val REGEX_ID_DOCUMENT_NUMBER = " (ID)(?<country>[A-Z<]{3})(?<documentNumber>[A-Z0-9<]{9})(?<checkDigitDocumentNumber>[0-9]{1})"
2727 private val REGEX_ID_DATE_OF_BIRTH = " (?<dateOfBirth>[0-9]{6})(?<checkDigitDateOfBirth>[0-9]{1})(?<gender>[FM<]{1})"
2828
29+ // Belgium TD1 (ID Card) specific pattern
30+ private val REGEX_BELGIUM_ID_DOCUMENT_NUMBER = " IDBEL(?<doc9>[A-Z0-9]{9})<(?<doc3>[A-Z0-9]{3})(?<checkDigit>\\ d)"
31+ private val REGEX_BELGIUM_ID_DATE_OF_BIRTH = " (?<dateOfBirth>[0-9]{6})(?<checkDigitDateOfBirth>[0-9]{1})(?<gender>[FM<]{1})(?<expirationDate>[0-9]{6})(?<checkDigitExpiration>[0-9]{1})"
32+
2933 private val patternDocumentNumber = Pattern .compile(REGEX_ID_DOCUMENT_NUMBER )
3034 private val patternDateOfBirth = Pattern .compile(REGEX_ID_DATE_OF_BIRTH )
3135 private val patternDocumentCode = Pattern .compile(REGEX_ID_DOCUMENT_CODE )
36+ private val patternBelgiumDocumentNumber = Pattern .compile(REGEX_BELGIUM_ID_DOCUMENT_NUMBER )
37+ private val patternBelgiumDateOfBirth = Pattern .compile(REGEX_BELGIUM_ID_DATE_OF_BIRTH )
3238
3339
3440 fun processOcr (
@@ -50,7 +56,7 @@ object OcrUtils {
5056 fullRead + = " $temp -"
5157 }
5258 fullRead = fullRead.uppercase()
53- Log .d(TAG , " Read: $fullRead " )
59+ // Log.d(TAG, "Read: $fullRead")
5460
5561 // We try with TD1 format first (ID Card)
5662 val patternTD1Line1 = Pattern .compile(REGEX_TD1_LINE1 )
@@ -69,40 +75,63 @@ object OcrUtils {
6975
7076 val matcherDocumentNumber = patternDocumentNumber.matcher(fullRead)
7177 val matcherDateOfBirth = patternDateOfBirth.matcher(fullRead)
72-
7378 val hasDocumentNumber = matcherDocumentNumber.find()
7479 val hasDateOfBirth = matcherDateOfBirth.find()
7580
81+ // Belgium specific matchers
82+ val matcherBelgiumDocumentNumber = patternBelgiumDocumentNumber.matcher(fullRead)
83+ val hasBelgiumDocumentNumber = matcherBelgiumDocumentNumber.find()
84+
7685 val documentNumber = if (hasDocumentNumber) matcherDocumentNumber.group(" documentNumber" ) else null
7786 val checkDigitDocumentNumber = if (hasDocumentNumber) matcherDocumentNumber.group(" checkDigitDocumentNumber" )?.toIntOrNull() else null
7887 val countryCode = if (hasDocumentNumber) matcherDocumentNumber.group(" country" ) else null
7988 val dateOfBirth = if (hasDateOfBirth) matcherDateOfBirth.group(" dateOfBirth" ) else null
89+
90+ // Belgium specific values
91+ val belgiumCheckDigit = if (hasBelgiumDocumentNumber) matcherBelgiumDocumentNumber.group(" checkDigit" )?.toIntOrNull() else null
92+ val belgiumDateOfBirth = if (hasBelgiumDocumentNumber) {
93+ val dateOfBirthMatcher = patternBelgiumDateOfBirth.matcher(fullRead)
94+ if (dateOfBirthMatcher.find()) dateOfBirthMatcher.group(" dateOfBirth" ) else null
95+ } else null
96+
97+ // Final values
98+ val finalDocumentNumber = if (hasBelgiumDocumentNumber) {
99+ val doc9 = matcherBelgiumDocumentNumber.group(" doc9" )
100+ val doc3 = matcherBelgiumDocumentNumber.group(" doc3" )
101+ val checkDigit = matcherBelgiumDocumentNumber.group(" checkDigit" )
102+ cleanBelgiumDocumentNumber(doc9, doc3, checkDigit)
103+ } else documentNumber
104+ val finalDateOfBirth = if (hasBelgiumDocumentNumber) belgiumDateOfBirth else dateOfBirth
105+ val finalCountryCode = if (hasBelgiumDocumentNumber) " BEL" else countryCode
106+ val finalCheckDigit = if (hasBelgiumDocumentNumber) belgiumCheckDigit else checkDigitDocumentNumber
107+
80108 val checkDigitDateOfBirth = if (hasDateOfBirth) matcherDateOfBirth.group(" checkDigitDateOfBirth" )?.toIntOrNull() else null
81109 val gender = if (hasDateOfBirth) matcherDateOfBirth.group(" gender" ) else null
82110
83- val expirationDate: String? = if (! countryCode.isNullOrEmpty()) {
84- val expirationDateRegex = " (?<expirationDate>[0-9]{6})(?<checkDigitExpiration>[0-9]{1})" + Pattern .quote(countryCode)
111+ val expirationDate: String? = if (! finalCountryCode.isNullOrEmpty()) {
112+ val expirationDateRegex = " (?<expirationDate>[0-9]{6})(?<checkDigitExpiration>[0-9]{1})" + Pattern .quote(finalCountryCode)
113+ // val expirationDateRegex = "(?<expirationDate>[0-9]{6})(?<checkDigitExpiration>[0-9]{1})UTO"
85114 val patternExpirationDate = Pattern .compile(expirationDateRegex)
86115 val matcherExpirationDate = patternExpirationDate.matcher(fullRead)
87116 if (matcherExpirationDate.find()) matcherExpirationDate.group(" expirationDate" ) else null
88117 } else null
89118
90119 // Only proceed if all required fields are present and non-empty
91- if (! countryCode .isNullOrEmpty() && ! documentNumber .isNullOrEmpty() && ! dateOfBirth .isNullOrEmpty() && ! expirationDate.isNullOrEmpty() && checkDigitDocumentNumber != null ) {
92- val cleanDocumentNumber = cleanDocumentNumber(documentNumber, checkDigitDocumentNumber )
93- Log .d(TAG , " cleanDocumentNumber" )
120+ if (! finalCountryCode .isNullOrEmpty() && ! finalDocumentNumber .isNullOrEmpty() && ! finalDateOfBirth .isNullOrEmpty() && ! expirationDate.isNullOrEmpty() && finalCheckDigit != null ) {
121+ val cleanDocumentNumber = cleanDocumentNumber(finalDocumentNumber, finalCheckDigit )
122+ // Log.d(TAG, "cleanDocumentNumber")
94123 if (cleanDocumentNumber != null ) {
95- val mrzInfo = createDummyMrz(" ID" , countryCode , cleanDocumentNumber, dateOfBirth , expirationDate)
124+ val mrzInfo = createDummyMrz(" ID" , finalCountryCode , cleanDocumentNumber, finalDateOfBirth , expirationDate)
96125 // Log.d(TAG, "MRZ-TD1: $mrzInfo")
97126 callback.onMRZRead(mrzInfo, timeRequired)
98127 return
99128 }
100129 } else {
101- if (countryCode .isNullOrEmpty()) Log .d(TAG , " Missing or invalid countryCode " )
102- if (documentNumber .isNullOrEmpty()) Log .d(TAG , " Missing or invalid documentNumber " )
103- if (dateOfBirth .isNullOrEmpty()) Log .d(TAG , " Missing or invalid dateOfBirth" )
130+ if (finalCountryCode .isNullOrEmpty()) Log .d(TAG , " Missing or invalid finalCountryCode " )
131+ if (finalDocumentNumber .isNullOrEmpty()) Log .d(TAG , " Missing or invalid finalDocumentNumber " )
132+ if (finalDateOfBirth .isNullOrEmpty()) Log .d(TAG , " Missing or invalid dateOfBirth" )
104133 if (expirationDate.isNullOrEmpty()) Log .d(TAG , " Missing or invalid expirationDate" )
105- if (checkDigitDocumentNumber == null ) Log .d(TAG , " Missing or invalid checkDigitDocumentNumber " )
134+ if (finalCheckDigit == null ) Log .d(TAG , " Missing or invalid finalCheckDigit " )
106135 }
107136 }
108137
@@ -194,6 +223,27 @@ object OcrUtils {
194223 return null
195224 }
196225
226+ private fun cleanBelgiumDocumentNumber (doc9 : String , doc3 : String , checkDigit : String ): String? {
227+ // For Belgium TD1 format: IDBEL000001115<7027
228+ // doc9 = "000001115" (9 digits)
229+ // doc3 = "702" (3 digits after <)
230+ // checkDigit = "7" (single check digit)
231+
232+ var cleanDoc9 = doc9
233+ cleanDoc9 = cleanDoc9.substring(3 )
234+
235+ val fullDocumentNumber = cleanDoc9 + doc3
236+
237+ val checkDigitCalculated = MRZInfo .checkDigit(fullDocumentNumber).toString().toInt()
238+ val expectedCheckDigit = checkDigit.toInt()
239+
240+ if (checkDigitCalculated == expectedCheckDigit) {
241+ return fullDocumentNumber
242+ }
243+
244+ return null
245+ }
246+
197247 private fun createDummyMrz (
198248 documentType : String ,
199249 issuingState : String = "ESP ",
0 commit comments