From 52cba9dc6d269002c234880094ad9e2b58432b65 Mon Sep 17 00:00:00 2001 From: Alexey Salangin Date: Sat, 25 Sep 2021 21:04:44 +0200 Subject: [PATCH 1/3] Fix typo --- Sources/MRZParser/Public/MRZParser.swift | 4 ++-- Sources/MRZParser/Public/MRZResult.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/MRZParser/Public/MRZParser.swift b/Sources/MRZParser/Public/MRZParser.swift index 0bfc61a..95c0e2d 100644 --- a/Sources/MRZParser/Public/MRZParser.swift +++ b/Sources/MRZParser/Public/MRZParser.swift @@ -59,9 +59,9 @@ public struct MRZParser { private func mrzFormat(from mrzLines: [String]) -> MRZFormat? { switch mrzLines.count { case MRZFormat.td2.linesCount, MRZFormat.td3.linesCount: - return [.td2, .td3].first(where: { $0.lineLenth == uniformedLineLength(for: mrzLines) }) + return [.td2, .td3].first(where: { $0.lineLength == uniformedLineLength(for: mrzLines) }) case MRZFormat.td1.linesCount: - return (uniformedLineLength(for: mrzLines) == MRZFormat.td1.lineLenth) ? .td1 : nil + return (uniformedLineLength(for: mrzLines) == MRZFormat.td1.lineLength) ? .td1 : nil default: return nil } diff --git a/Sources/MRZParser/Public/MRZResult.swift b/Sources/MRZParser/Public/MRZResult.swift index 87d04c2..6b530b3 100644 --- a/Sources/MRZParser/Public/MRZResult.swift +++ b/Sources/MRZParser/Public/MRZResult.swift @@ -10,7 +10,7 @@ import Foundation public enum MRZFormat: CaseIterable { case td1, td2, td3 - public var lineLenth: Int { + public var lineLength: Int { switch self { case .td1: return 30 From 8d0856d878897b6f20ca6d6129cfdc94bf2a560c Mon Sep 17 00:00:00 2001 From: Alexey Salangin Date: Sat, 25 Sep 2021 21:07:54 +0200 Subject: [PATCH 2/3] Some refactoring --- .../Private/MRZField/MRZFieldFormatter.swift | 53 ++++++++++++------- Sources/MRZParser/Private/OCRCorrector.swift | 3 +- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/Sources/MRZParser/Private/MRZField/MRZFieldFormatter.swift b/Sources/MRZParser/Private/MRZField/MRZFieldFormatter.swift index 0d01864..60ae3bc 100644 --- a/Sources/MRZParser/Private/MRZField/MRZFieldFormatter.swift +++ b/Sources/MRZParser/Private/MRZField/MRZFieldFormatter.swift @@ -8,6 +8,9 @@ import Foundation struct MRZFieldFormatter { + private static let currentCentennial = Calendar.current.component(.year, from: Date()) / 100 + private static let previousCentennial = Self.currentCentennial - 1 + private let isOCRCorrectionEnabled: Bool private let ocrCorrector = OCRCorrector() private let dateFormatter: DateFormatter = { @@ -141,15 +144,17 @@ struct MRZFieldFormatter { private func birthdate(from string: String) -> Date? { guard CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: string)), let parsedYear = Int(string.substring(0, to: 1)) else { return nil } - let currentYear = Calendar.current.component(.year, from: Date()) - 2000 - let centennial = (parsedYear > currentYear) ? "19" : "20" + let currentYear = Calendar.current.component(.year, from: Date()) - Self.currentCentennial * 100 + let centennial = (parsedYear > currentYear) ? String(Self.previousCentennial) : String(Self.currentCentennial) return dateFormatter.date(from: centennial + string) } private func expiryDate(from string: String) -> Date? { guard CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: string)), let parsedYear = Int(string.substring(0, to: 1)) else { return nil } - let centennial = (parsedYear >= 70) ? "19" : "20" + let currentYear = Calendar.current.component(.year, from: Date()) - Self.currentCentennial * 100 + let boundaryYear = currentYear + 50 + let centennial = (parsedYear >= boundaryYear) ? String(Self.previousCentennial) : String(Self.currentCentennial) return dateFormatter.date(from: centennial + string) } @@ -162,25 +167,33 @@ struct MRZFieldFormatter { return checkDigit == "<" ? rawValue.trimmingFillers.isEmpty : false } - var total = 0 - - for (index, character) in rawValue.enumerated() { - guard let unicodeScalar = character.unicodeScalars.first else { return false } - let charValue: Int - - if CharacterSet.uppercaseLetters.contains(unicodeScalar) { - charValue = Int(10 + unicodeScalar.value) - 65 - } else if CharacterSet.decimalDigits.contains(unicodeScalar), let digit = Int(String(character)) { - charValue = digit - } else if character == "<" { - charValue = 0 - } else { - return false - } + return Self.checkDigit(for: rawValue) == numericCheckDigit + } - total += charValue * [7, 3, 1][index % 3] + static func checkDigit(for value: String) -> Int? { + var sum: Int = 0 + for (index, character) in value.enumerated() { + guard let number = Self.number(for: character) else { return nil } + let weights = [7, 3, 1] + sum += number * weights[index % 3] } + return sum % 10 + } - return total % 10 == numericCheckDigit + // < A B C D E F G H I J K L M N O P Q R S T U V W X Y Z + // 0 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 + private static func number(for character: Character) -> Int? { + guard let unicodeScalar = character.unicodeScalars.first else { return nil } + let number: Int + if CharacterSet.uppercaseLetters.contains(unicodeScalar) { + number = Int(10 + unicodeScalar.value) - 65 + } else if CharacterSet.decimalDigits.contains(unicodeScalar), let digit = character.wholeNumberValue { + number = digit + } else if character == "<" { + number = 0 + } else { + return nil + } + return number } } diff --git a/Sources/MRZParser/Private/OCRCorrector.swift b/Sources/MRZParser/Private/OCRCorrector.swift index 6b9a195..e6a80f1 100644 --- a/Sources/MRZParser/Private/OCRCorrector.swift +++ b/Sources/MRZParser/Private/OCRCorrector.swift @@ -7,6 +7,7 @@ struct OCRCorrector { func correct(_ string: String, fieldType: MRZFieldType) -> String { + let string = string.uppercased() switch fieldType { // TODO: Check correction of dates (month & day) case .birthdate, .expiryDate, .hash: @@ -17,7 +18,7 @@ struct OCRCorrector { // TODO: Improve correction (take into account "M" & "<" too) case .sex: return string.replace("P", with: "F") - default: + case .documentNumber, .personalNumber, .optionalData: return string } } From 35ccd2cf593e9763e4c0f936e74ea5c9899349c4 Mon Sep 17 00:00:00 2001 From: Alexey Salangin <22199708+magauran@users.noreply.github.com> Date: Sat, 25 Sep 2021 21:19:46 +0200 Subject: [PATCH 3/3] Fixes for code review Co-authored-by: Roman Mazeev --- Sources/MRZParser/Private/MRZField/MRZFieldFormatter.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/MRZParser/Private/MRZField/MRZFieldFormatter.swift b/Sources/MRZParser/Private/MRZField/MRZFieldFormatter.swift index 60ae3bc..1d438a2 100644 --- a/Sources/MRZParser/Private/MRZField/MRZFieldFormatter.swift +++ b/Sources/MRZParser/Private/MRZField/MRZFieldFormatter.swift @@ -154,7 +154,7 @@ struct MRZFieldFormatter { let parsedYear = Int(string.substring(0, to: 1)) else { return nil } let currentYear = Calendar.current.component(.year, from: Date()) - Self.currentCentennial * 100 let boundaryYear = currentYear + 50 - let centennial = (parsedYear >= boundaryYear) ? String(Self.previousCentennial) : String(Self.currentCentennial) + let centennial = parsedYear >= boundaryYear ? String(Self.previousCentennial) : String(Self.currentCentennial) return dateFormatter.date(from: centennial + string) } @@ -173,7 +173,7 @@ struct MRZFieldFormatter { static func checkDigit(for value: String) -> Int? { var sum: Int = 0 for (index, character) in value.enumerated() { - guard let number = Self.number(for: character) else { return nil } + guard let number = number(for: character) else { return nil } let weights = [7, 3, 1] sum += number * weights[index % 3] }