Skip to content

Commit 00eb5cd

Browse files
committed
feat: support for overflow of other countries
1 parent 3e66b97 commit 00eb5cd

File tree

1 file changed

+28
-24
lines changed

1 file changed

+28
-24
lines changed

app/ios/LiveMRZScannerView.swift

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ struct LiveMRZScannerView: View {
1010
@State private var lastMRZDetection: Date = Date()
1111
@State private var parsedMRZ: QKMRZResult? = nil
1212
@State private var scanComplete: Bool = false
13-
@State private var overrideDocumentNumber: String? = nil // for Belgian ID overflow format
13+
@State private var overrideDocumentNumber: String? = nil // for TD1 overflow format (ID cards)
1414
var onScanComplete: ((QKMRZResult) -> Void)? = nil
1515
var onScanResultAsDict: (([String: Any]) -> Void)? = nil
1616

@@ -61,8 +61,8 @@ struct LiveMRZScannerView: View {
6161
}
6262

6363
private func mapVisionResultToDictionary(_ result: QKMRZResult) -> [String: Any] {
64-
65-
// using manually validated document number for Belgian IDs (overflow format)
64+
65+
// using manually validated document number for TD1 documents with overflow format
6666
// this is necessary for NFC chip authentication which requires the full document number
6767
let documentNumber = overrideDocumentNumber ?? result.documentNumber
6868

@@ -111,12 +111,13 @@ struct LiveMRZScannerView: View {
111111
return sum % 10
112112
}
113113

114-
/// Extracts and validates the Belgian document number from MRZ line 1, handling both standard and overflow formats.
115-
/// Belgian TD1 format uses an overflow mechanism when document numbers exceed 9 digits.
114+
/// Extracts and validates the document number from TD1 MRZ line 1, handling both standard and overflow formats.
115+
/// TD1 format uses an overflow mechanism when document numbers exceed 9 digits.
116116
/// Example overflow format: IDBEL595392450<8039<<<<<<<<<< where positions 6-14 contain the principal part (595392450),
117117
/// position 15 contains the overflow indicator (<), positions 16-18 contain overflow digits (803), and position 19 contains the check digit (9).
118118
/// The full document number becomes: 595392450803.
119-
private func extractAndValidateBelgianDocumentNumber(line1: String) -> (documentNumber: String, isValid: Bool)? {
119+
/// This overflow format can occur for any country using TD1 MRZ (ID cards).
120+
private func extractAndValidateTD1DocumentNumber(line1: String) -> (documentNumber: String, isValid: Bool)? {
120121
guard line1.count == 30 else { return nil }
121122

122123
// extracting positions 6-14 (9 characters - principal part)
@@ -133,7 +134,7 @@ struct LiveMRZScannerView: View {
133134
let checkDigit = Int(String(pos15)) ?? -1
134135
let calculatedCheck = calculateMRZCheckDigit(principalPart)
135136
let isValid = (checkDigit == calculatedCheck)
136-
print("[extractAndValidateBelgianDocumentNumber] Standard format: \(principalPart), check=\(checkDigit), calculated=\(calculatedCheck), valid=\(isValid)")
137+
print("[extractAndValidateTD1DocumentNumber] Standard format: \(principalPart), check=\(checkDigit), calculated=\(calculatedCheck), valid=\(isValid)")
137138
return (principalPart, isValid)
138139
}
139140

@@ -153,7 +154,7 @@ struct LiveMRZScannerView: View {
153154
}
154155

155156
guard overflowDigits.count > 0 else {
156-
print("[extractAndValidateBelgianDocumentNumber] ERROR: No overflow digits found")
157+
print("[extractAndValidateTD1DocumentNumber] ERROR: No overflow digits found")
157158
return nil
158159
}
159160

@@ -167,14 +168,14 @@ struct LiveMRZScannerView: View {
167168
// validating check digit against full document number
168169
guard let checkDigitChar = checkDigitChar,
169170
let checkDigit = Int(String(checkDigitChar)) else {
170-
print("[extractAndValidateBelgianDocumentNumber] ERROR: Invalid check digit")
171+
print("[extractAndValidateTD1DocumentNumber] ERROR: Invalid check digit")
171172
return nil
172173
}
173174
let calculatedCheck = calculateMRZCheckDigit(fullDocumentNumber)
174175
let isValid = (checkDigit == calculatedCheck)
175176

176177
#if DEBUG
177-
print("[extractAndValidateBelgianDocumentNumber] Overflow format:")
178+
print("[extractAndValidateTD1DocumentNumber] Overflow format:")
178179
print(" Principal part (6-14): \(principalPart)")
179180
print(" Overflow with check: \(overflowDigits)")
180181
print(" Overflow without check: \(overflowWithoutCheck)")
@@ -198,11 +199,11 @@ struct LiveMRZScannerView: View {
198199
onScanResultAsDict?(mapVisionResultToDictionary(result))
199200
}
200201

201-
/// Processes Belgian ID documents by manually extracting and validating the document number using the overflow format handler,
202+
/// Processes TD1 documents (ID cards) by manually extracting and validating the document number using the overflow format handler,
202203
/// then parses the remaining MRZ fields (name, dates, etc.) using QKMRZParser. This bypasses QKMRZParser's validation for the
203-
/// document number field since it doesn't handle Belgian overflow format correctly.
204-
private func processBelgiumDocument(result: String, parser: QKMRZParser) -> QKMRZResult? {
205-
print("[LiveMRZScannerView] Processing Belgium document with manual validation")
204+
/// document number field since it doesn't handle TD1 overflow format correctly.
205+
private func processTD1DocumentWithOverflow(result: String, parser: QKMRZParser) -> QKMRZResult? {
206+
print("[LiveMRZScannerView] Processing TD1 document with manual overflow validation")
206207

207208
let lines = result.components(separatedBy: "\n")
208209
guard lines.count >= 3 else {
@@ -214,17 +215,17 @@ struct LiveMRZScannerView: View {
214215
print("[LiveMRZScannerView] Line 1: \(line1)")
215216

216217
// extracting and validating document number manually using overflow format handler
217-
guard let (documentNumber, isDocNumberValid) = extractAndValidateBelgianDocumentNumber(line1: line1) else {
218-
print("[LiveMRZScannerView] Failed to extract Belgian document number")
218+
guard let (documentNumber, isDocNumberValid) = extractAndValidateTD1DocumentNumber(line1: line1) else {
219+
print("[LiveMRZScannerView] Failed to extract TD1 document number")
219220
return nil
220221
}
221222

222223
if !isDocNumberValid {
223-
print("[LiveMRZScannerView] Belgian document number check digit is INVALID")
224+
print("[LiveMRZScannerView] TD1 document number check digit is INVALID")
224225
return nil
225226
}
226227

227-
print("[LiveMRZScannerView] Belgian document number validated: \(documentNumber)")
228+
print("[LiveMRZScannerView] TD1 document number validated: \(documentNumber)")
228229

229230
// parsing the original MRZ to get all other fields (name, birthdate, etc.)
230231
// using QKMRZParser for non-documentNumber fields
@@ -233,9 +234,9 @@ struct LiveMRZScannerView: View {
233234
return nil
234235
}
235236

236-
// validating that other fields are also correct (consistency with non-Belgium documents)
237+
// validating that other fields are also correct
237238
if !mrzResult.isBirthdateValid || !mrzResult.isExpiryDateValid {
238-
print("[LiveMRZScannerView] Belgium document has invalid birthdate or expiry date")
239+
print("[LiveMRZScannerView] TD1 document has invalid birthdate or expiry date")
239240
return nil
240241
}
241242

@@ -285,10 +286,13 @@ struct LiveMRZScannerView: View {
285286
return
286287
}
287288

288-
// Handle Belgium documents (only if not already valid)
289-
if doc.countryCode == "BEL" {
290-
if let belgiumResult = processBelgiumDocument(result: result, parser: parser) {
291-
handleValidMRZResult(belgiumResult)
289+
// handling TD1 documents with potential overflow format (only if not already valid)
290+
// TD1 format has 3 lines of 30 characters each
291+
let lines = result.components(separatedBy: "\n")
292+
if lines.count >= 3 && lines[0].count == 30 {
293+
// trying overflow validation
294+
if let td1Result = processTD1DocumentWithOverflow(result: result, parser: parser) {
295+
handleValidMRZResult(td1Result)
292296
}
293297
return
294298
}

0 commit comments

Comments
 (0)