From 2b32ee54c48fd8cb8573f2db2b189aa4389a5a6c Mon Sep 17 00:00:00 2001 From: seshanthS Date: Fri, 11 Jul 2025 21:29:49 +0530 Subject: [PATCH 1/3] feat: update td1 regex --- .../com/passportreader/utils/OcrUtils.kt | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt b/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt index c4a40b673..70afd2c72 100644 --- a/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt +++ b/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt @@ -21,6 +21,9 @@ object OcrUtils { private val REGEX_TD1_LINE2 = "(?[0-9]{6})(?[0-9]{1})(?[FM<]{1})(?[0-9]{6})(?[0-9]{1})(?[A-Z<]{3})(?[A-Z0-9<]{7})" private val REGEX_TD1_LINE3 ="(?[A-Z<]{30})" + // TD1 (ID Card) + private val documentCodeRegex = "(?[IP]{1}[DM<]{1})" + fun processOcr( results: Text, timeRequired: Long, @@ -47,11 +50,65 @@ object OcrUtils { val patternTD1Line2 = Pattern.compile(REGEX_TD1_LINE2) val patternTD1Line3 = Pattern.compile(REGEX_TD1_LINE3) + val patternDocumentCode = Pattern.compile(documentCodeRegex) + val matcherTD1Line1 = patternTD1Line1.matcher(fullRead) val matcherTD1Line2 = patternTD1Line2.matcher(fullRead) val matcherTD1Line3 = patternTD1Line3.matcher(fullRead) + val matcherDocumentCode = patternDocumentCode.matcher(fullRead) + + if (matcherDocumentCode.find() && matcherDocumentCode.group("documentCode") == "ID") { + Log.d(TAG, "ID card found") + val documentNumberRegex = "(ID)(?[A-Z]{3})(?[A-Z0-9<]{9})(?[0-9]{1})" + val dateOfBirthRegex = "(?[0-9]{6})(?[0-9]{1})(?[FM<]{1})" + + val patternDocumentNumber = Pattern.compile(documentNumberRegex) + val patternDateOfBirth = Pattern.compile(dateOfBirthRegex) + + val matcherDocumentNumber = patternDocumentNumber.matcher(fullRead) + val matcherDateOfBirth = patternDateOfBirth.matcher(fullRead) + + val hasDocumentNumber = matcherDocumentNumber.find() + val hasDateOfBirth = matcherDateOfBirth.find() + + val documentNumber = if (hasDocumentNumber) matcherDocumentNumber.group("documentNumber") else null + val checkDigitDocumentNumber = if (hasDocumentNumber) matcherDocumentNumber.group("checkDigitDocumentNumber")?.toIntOrNull() else null + val countryCode = if (hasDocumentNumber) matcherDocumentNumber.group("country") else null + val dateOfBirth = if (hasDateOfBirth) matcherDateOfBirth.group("dateOfBirth") else null + val checkDigitDateOfBirth = if (hasDateOfBirth) matcherDateOfBirth.group("checkDigitDateOfBirth")?.toIntOrNull() else null + val gender = if (hasDateOfBirth) matcherDateOfBirth.group("gender") else null + + val expirationDateRegex = "(?[0-9]{6})(?[0-9]{1})$countryCode" + val patternExpirationDate = Pattern.compile(expirationDateRegex) + val matcherExpirationDate = patternExpirationDate.matcher(fullRead) + + + val hasExpirationDate = matcherExpirationDate.find() + val expirationDate = if (hasExpirationDate) matcherExpirationDate.group("expirationDate") else null + val checkDigitExpiration = if (hasExpirationDate) matcherExpirationDate.group("checkDigitExpiration")?.toIntOrNull() else null + + // Only proceed if all required fields are present and non-empty + if (!countryCode.isNullOrEmpty() && !documentNumber.isNullOrEmpty() && !dateOfBirth.isNullOrEmpty() && !expirationDate.isNullOrEmpty() && checkDigitDocumentNumber != null) { + val cleanDocumentNumber = cleanDocumentNumber(documentNumber, checkDigitDocumentNumber) + Log.d(TAG, "cleanDocumentNumber") + if (cleanDocumentNumber != null) { + val mrzInfo = createDummyMrz("ID", countryCode, documentNumber, dateOfBirth, expirationDate) + // Log.d(TAG, "MRZ-TD1: $mrzInfo") + callback.onMRZRead(mrzInfo, timeRequired) + return + } + } else { + if (countryCode.isNullOrEmpty()) Log.d(TAG, "Missing or invalid countryCode") + if (documentNumber.isNullOrEmpty()) Log.d(TAG, "Missing or invalid documentNumber") + if (dateOfBirth.isNullOrEmpty()) Log.d(TAG, "Missing or invalid dateOfBirth") + if (expirationDate.isNullOrEmpty()) Log.d(TAG, "Missing or invalid expirationDate") + if (checkDigitDocumentNumber == null) Log.d(TAG, "Missing or invalid checkDigitDocumentNumber") + } + } + if (matcherTD1Line1.find() && matcherTD1Line2.find()) { + Log.d(TAG, "TD1Line1 and TD1Line2 found") val documentNumber = matcherTD1Line1.group("documentNumber") val checkDigitDocumentNumber = matcherTD1Line1.group("checkDigitDocumentNumber").toInt() val dateOfBirth = matcherTD1Line2.group("dateOfBirth") @@ -62,7 +119,7 @@ object OcrUtils { val cleanDocumentNumber = cleanDocumentNumber(documentNumber, checkDigitDocumentNumber) if (cleanDocumentNumber != null) { val mrzInfo = createDummyMrz(documentType, issuingState, documentNumber, dateOfBirth, expirationDate) - Log.d(TAG, "MRZ-TD1: $mrzInfo") + Log.d(TAG, "cleanDocumentNumber") callback.onMRZRead(mrzInfo, timeRequired) return } From 910185232b9ab1a24dfcc3be40cbc0234c5b18e4 Mon Sep 17 00:00:00 2001 From: seshanthS Date: Fri, 11 Jul 2025 22:00:15 +0530 Subject: [PATCH 2/3] update review comments --- .../com/passportreader/utils/OcrUtils.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt b/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt index 70afd2c72..376965ba7 100644 --- a/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt +++ b/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt @@ -22,7 +22,14 @@ object OcrUtils { private val REGEX_TD1_LINE3 ="(?[A-Z<]{30})" // TD1 (ID Card) - private val documentCodeRegex = "(?[IP]{1}[DM<]{1})" + private val REGEX_ID_DOCUMENT_CODE = "(?[IP]{1}[DM<]{1})" + private val REGEX_ID_DOCUMENT_NUMBER = "(ID)(?[A-Z<]{3})(?[A-Z0-9<]{9})(?[0-9]{1})" + private val REGEX_ID_DATE_OF_BIRTH = "(?[0-9]{6})(?[0-9]{1})(?[FM<]{1})" + + private val patternDocumentNumber = Pattern.compile(REGEX_ID_DOCUMENT_NUMBER) + private val patternDateOfBirth = Pattern.compile(REGEX_ID_DATE_OF_BIRTH) + private val patternDocumentCode = Pattern.compile(REGEX_ID_DOCUMENT_CODE) + fun processOcr( results: Text, @@ -50,7 +57,6 @@ object OcrUtils { val patternTD1Line2 = Pattern.compile(REGEX_TD1_LINE2) val patternTD1Line3 = Pattern.compile(REGEX_TD1_LINE3) - val patternDocumentCode = Pattern.compile(documentCodeRegex) val matcherTD1Line1 = patternTD1Line1.matcher(fullRead) val matcherTD1Line2 = patternTD1Line2.matcher(fullRead) @@ -60,11 +66,6 @@ object OcrUtils { if (matcherDocumentCode.find() && matcherDocumentCode.group("documentCode") == "ID") { Log.d(TAG, "ID card found") - val documentNumberRegex = "(ID)(?[A-Z]{3})(?[A-Z0-9<]{9})(?[0-9]{1})" - val dateOfBirthRegex = "(?[0-9]{6})(?[0-9]{1})(?[FM<]{1})" - - val patternDocumentNumber = Pattern.compile(documentNumberRegex) - val patternDateOfBirth = Pattern.compile(dateOfBirthRegex) val matcherDocumentNumber = patternDocumentNumber.matcher(fullRead) val matcherDateOfBirth = patternDateOfBirth.matcher(fullRead) @@ -79,7 +80,7 @@ object OcrUtils { val checkDigitDateOfBirth = if (hasDateOfBirth) matcherDateOfBirth.group("checkDigitDateOfBirth")?.toIntOrNull() else null val gender = if (hasDateOfBirth) matcherDateOfBirth.group("gender") else null - val expirationDateRegex = "(?[0-9]{6})(?[0-9]{1})$countryCode" + val expirationDateRegex = "(?[0-9]{6})(?[0-9]{1})${Pattern.quote(countryCode)}" val patternExpirationDate = Pattern.compile(expirationDateRegex) val matcherExpirationDate = patternExpirationDate.matcher(fullRead) From e56f0a1189bd2a9151b3f39f0daf24ac4c85960c Mon Sep 17 00:00:00 2001 From: seshanthS Date: Fri, 11 Jul 2025 23:19:25 +0530 Subject: [PATCH 3/3] fix: NPE on expirationDate regex --- .../jllarraz/com/passportreader/utils/OcrUtils.kt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt b/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt index 376965ba7..7f1d63193 100644 --- a/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt +++ b/app/android/android-passport-reader/app/src/main/java/example/jllarraz/com/passportreader/utils/OcrUtils.kt @@ -80,14 +80,12 @@ object OcrUtils { val checkDigitDateOfBirth = if (hasDateOfBirth) matcherDateOfBirth.group("checkDigitDateOfBirth")?.toIntOrNull() else null val gender = if (hasDateOfBirth) matcherDateOfBirth.group("gender") else null - val expirationDateRegex = "(?[0-9]{6})(?[0-9]{1})${Pattern.quote(countryCode)}" - val patternExpirationDate = Pattern.compile(expirationDateRegex) - val matcherExpirationDate = patternExpirationDate.matcher(fullRead) - - - val hasExpirationDate = matcherExpirationDate.find() - val expirationDate = if (hasExpirationDate) matcherExpirationDate.group("expirationDate") else null - val checkDigitExpiration = if (hasExpirationDate) matcherExpirationDate.group("checkDigitExpiration")?.toIntOrNull() else null + val expirationDate: String? = if (!countryCode.isNullOrEmpty()) { + val expirationDateRegex = "(?[0-9]{6})(?[0-9]{1})" + Pattern.quote(countryCode) + val patternExpirationDate = Pattern.compile(expirationDateRegex) + val matcherExpirationDate = patternExpirationDate.matcher(fullRead) + if (matcherExpirationDate.find()) matcherExpirationDate.group("expirationDate") else null + } else null // Only proceed if all required fields are present and non-empty if (!countryCode.isNullOrEmpty() && !documentNumber.isNullOrEmpty() && !dateOfBirth.isNullOrEmpty() && !expirationDate.isNullOrEmpty() && checkDigitDocumentNumber != null) {