@trustchex/react-native-sdk 1.355.1 → 1.357.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/README.md +2 -9
  2. package/TrustchexSDK.podspec +5 -4
  3. package/android/build.gradle +6 -4
  4. package/android/src/main/AndroidManifest.xml +1 -1
  5. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKPackage.kt +45 -25
  6. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraManager.kt +168 -0
  7. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +871 -0
  8. package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +245 -0
  9. package/android/src/main/java/com/trustchex/reactnativesdk/mrz/MRZValidationModule.kt +785 -0
  10. package/android/src/main/java/com/trustchex/reactnativesdk/mrz/MRZValidator.kt +419 -0
  11. package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +818 -0
  12. package/ios/Camera/TrustchexCameraManager.m +37 -0
  13. package/ios/Camera/TrustchexCameraManager.swift +125 -0
  14. package/ios/Camera/TrustchexCameraView.swift +1176 -0
  15. package/ios/MLKit/MLKitModule.m +23 -0
  16. package/ios/MLKit/MLKitModule.swift +250 -0
  17. package/ios/MRZValidation.m +39 -0
  18. package/ios/MRZValidation.swift +802 -0
  19. package/ios/MRZValidator.swift +466 -0
  20. package/ios/OpenCV/OpenCVModule.h +4 -0
  21. package/ios/OpenCV/OpenCVModule.mm +810 -0
  22. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +2 -3
  23. package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +1 -2
  24. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +418 -193
  25. package/lib/module/Screens/Static/OTPVerificationScreen.js +11 -11
  26. package/lib/module/Screens/Static/QrCodeScanningScreen.js +5 -1
  27. package/lib/module/Screens/Static/ResultScreen.js +25 -2
  28. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +25 -7
  29. package/lib/module/Shared/Components/DebugNavigationPanel.js +234 -24
  30. package/lib/module/Shared/Components/EIDScanner.js +99 -9
  31. package/lib/module/Shared/Components/FaceCamera.js +170 -179
  32. package/lib/module/Shared/Components/IdentityDocumentCamera.js +2151 -771
  33. package/lib/module/Shared/Components/QrCodeScannerCamera.js +109 -107
  34. package/lib/module/Shared/Components/TrustchexCamera.js +122 -0
  35. package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +91 -0
  36. package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +2 -124
  37. package/lib/module/Shared/EIDReader/tlv/tlvInputStream.js +4 -4
  38. package/lib/module/Shared/EIDReader/tlv/tlvOutputState.js +4 -4
  39. package/lib/module/Shared/EIDReader/tlv/tlvOutputStream.js +4 -4
  40. package/lib/module/Shared/Libs/analytics.utils.js +2 -2
  41. package/lib/module/Shared/Libs/debug.utils.js +132 -0
  42. package/lib/module/Shared/Libs/deeplink.utils.js +6 -5
  43. package/lib/module/Shared/Libs/demo.utils.js +13 -3
  44. package/lib/module/Shared/Libs/mrz.utils.js +1 -175
  45. package/lib/module/Shared/Libs/native-device-info.utils.js +12 -6
  46. package/lib/module/Shared/Libs/tts.utils.js +40 -6
  47. package/lib/module/Shared/Services/AnalyticsService.js +9 -8
  48. package/lib/module/Shared/Types/mrzFields.js +1 -0
  49. package/lib/module/Translation/Resources/en.js +87 -88
  50. package/lib/module/Translation/Resources/tr.js +84 -85
  51. package/lib/module/Trustchex.js +9 -2
  52. package/lib/module/index.js +1 -0
  53. package/lib/module/version.js +1 -1
  54. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  55. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
  56. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  57. package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -1
  58. package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
  59. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  60. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  61. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  62. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts +2 -2
  63. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  64. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts +18 -4
  65. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  66. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts +3 -4
  67. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  68. package/lib/typescript/src/Shared/Components/QrCodeScannerCamera.d.ts +2 -1
  69. package/lib/typescript/src/Shared/Components/QrCodeScannerCamera.d.ts.map +1 -1
  70. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts +124 -0
  71. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts.map +1 -0
  72. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.helpers.d.ts +11 -0
  73. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.helpers.d.ts.map +1 -0
  74. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts +2 -39
  75. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
  76. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  77. package/lib/typescript/src/Shared/Libs/debug.utils.d.ts +42 -0
  78. package/lib/typescript/src/Shared/Libs/debug.utils.d.ts.map +1 -0
  79. package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
  80. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  81. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  82. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts +0 -4
  83. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  84. package/lib/typescript/src/Shared/Libs/native-device-info.utils.d.ts.map +1 -1
  85. package/lib/typescript/src/Shared/Libs/tts.utils.d.ts +4 -3
  86. package/lib/typescript/src/Shared/Libs/tts.utils.d.ts.map +1 -1
  87. package/lib/typescript/src/Shared/Services/AnalyticsService.d.ts.map +1 -1
  88. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +2 -2
  89. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  90. package/lib/typescript/src/Shared/Types/mrzFields.d.ts +11 -0
  91. package/lib/typescript/src/Shared/Types/mrzFields.d.ts.map +1 -0
  92. package/lib/typescript/src/Translation/Resources/en.d.ts +4 -5
  93. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  94. package/lib/typescript/src/Translation/Resources/tr.d.ts +4 -5
  95. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  96. package/lib/typescript/src/Trustchex.d.ts +2 -0
  97. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  98. package/lib/typescript/src/index.d.ts +1 -0
  99. package/lib/typescript/src/index.d.ts.map +1 -1
  100. package/lib/typescript/src/version.d.ts +1 -1
  101. package/package.json +4 -35
  102. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +1 -1
  103. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +7 -5
  104. package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +2 -3
  105. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +498 -216
  106. package/src/Screens/Static/OTPVerificationScreen.tsx +37 -31
  107. package/src/Screens/Static/QrCodeScanningScreen.tsx +8 -1
  108. package/src/Screens/Static/ResultScreen.tsx +136 -88
  109. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +46 -13
  110. package/src/Shared/Components/DebugNavigationPanel.tsx +290 -34
  111. package/src/Shared/Components/EIDScanner.tsx +94 -16
  112. package/src/Shared/Components/FaceCamera.tsx +236 -203
  113. package/src/Shared/Components/IdentityDocumentCamera.tsx +3073 -1030
  114. package/src/Shared/Components/QrCodeScannerCamera.tsx +133 -127
  115. package/src/Shared/Components/TrustchexCamera.tsx +289 -0
  116. package/src/Shared/Config/camera-enhancement.config.ts +2 -2
  117. package/src/Shared/EIDReader/tlv/tlv.helpers.ts +96 -0
  118. package/src/Shared/EIDReader/tlv/tlv.utils.ts +2 -125
  119. package/src/Shared/EIDReader/tlv/tlvInputStream.ts +4 -4
  120. package/src/Shared/EIDReader/tlv/tlvOutputState.ts +4 -4
  121. package/src/Shared/EIDReader/tlv/tlvOutputStream.ts +4 -4
  122. package/src/Shared/Libs/analytics.utils.ts +48 -20
  123. package/src/Shared/Libs/debug.utils.ts +149 -0
  124. package/src/Shared/Libs/deeplink.utils.ts +7 -5
  125. package/src/Shared/Libs/demo.utils.ts +4 -0
  126. package/src/Shared/Libs/http-client.ts +12 -8
  127. package/src/Shared/Libs/mrz.utils.ts +1 -163
  128. package/src/Shared/Libs/native-device-info.utils.ts +12 -6
  129. package/src/Shared/Libs/tts.utils.ts +48 -6
  130. package/src/Shared/Services/AnalyticsService.ts +69 -24
  131. package/src/Shared/Types/identificationInfo.ts +2 -2
  132. package/src/Shared/Types/mrzFields.ts +29 -0
  133. package/src/Translation/Resources/en.ts +90 -100
  134. package/src/Translation/Resources/tr.ts +89 -97
  135. package/src/Translation/index.ts +1 -1
  136. package/src/Trustchex.tsx +21 -4
  137. package/src/index.tsx +14 -0
  138. package/src/version.ts +1 -1
  139. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/barcodescanner/BarcodeScannerFrameProcessorPlugin.kt +0 -301
  140. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/cropper/BitmapUtils.kt +0 -205
  141. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/cropper/CropperPlugin.kt +0 -72
  142. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/cropper/FrameMetadata.kt +0 -4
  143. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/facedetector/FaceDetectorFrameProcessorPlugin.kt +0 -303
  144. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/textrecognition/TextRecognitionFrameProcessorPlugin.kt +0 -115
  145. package/ios/VisionCameraPlugins/BarcodeScanner/BarcodeScannerFrameProcessorPlugin-Bridging-Header.h +0 -9
  146. package/ios/VisionCameraPlugins/BarcodeScanner/BarcodeScannerFrameProcessorPlugin.mm +0 -22
  147. package/ios/VisionCameraPlugins/BarcodeScanner/BarcodeScannerFrameProcessorPlugin.swift +0 -188
  148. package/ios/VisionCameraPlugins/Cropper/Cropper-Bridging-Header.h +0 -13
  149. package/ios/VisionCameraPlugins/Cropper/Cropper.h +0 -20
  150. package/ios/VisionCameraPlugins/Cropper/Cropper.mm +0 -22
  151. package/ios/VisionCameraPlugins/Cropper/Cropper.swift +0 -145
  152. package/ios/VisionCameraPlugins/Cropper/CropperUtils.swift +0 -49
  153. package/ios/VisionCameraPlugins/FaceDetector/FaceDetectorFrameProcessorPlugin-Bridging-Header.h +0 -4
  154. package/ios/VisionCameraPlugins/FaceDetector/FaceDetectorFrameProcessorPlugin.mm +0 -22
  155. package/ios/VisionCameraPlugins/FaceDetector/FaceDetectorFrameProcessorPlugin.swift +0 -320
  156. package/ios/VisionCameraPlugins/TextRecognition/TextRecognitionFrameProcessorPlugin-Bridging-Header.h +0 -4
  157. package/ios/VisionCameraPlugins/TextRecognition/TextRecognitionFrameProcessorPlugin.mm +0 -27
  158. package/ios/VisionCameraPlugins/TextRecognition/TextRecognitionFrameProcessorPlugin.swift +0 -144
  159. package/lib/module/Shared/Libs/camera.utils.js +0 -308
  160. package/lib/module/Shared/Libs/frame-enhancement.utils.js +0 -133
  161. package/lib/module/Shared/Libs/opencv.utils.js +0 -21
  162. package/lib/module/Shared/Libs/worklet.utils.js +0 -68
  163. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useBarcodeScanner.js +0 -46
  164. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useCameraPermissions.js +0 -35
  165. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/index.js +0 -19
  166. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/scanCodes.js +0 -26
  167. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/types.js +0 -3
  168. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/utils/convert.js +0 -197
  169. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/utils/geometry.js +0 -101
  170. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/utils/highlights.js +0 -60
  171. package/lib/module/Shared/VisionCameraPlugins/Cropper/index.js +0 -47
  172. package/lib/module/Shared/VisionCameraPlugins/FaceDetector/Camera.js +0 -42
  173. package/lib/module/Shared/VisionCameraPlugins/FaceDetector/detectFaces.js +0 -35
  174. package/lib/module/Shared/VisionCameraPlugins/FaceDetector/index.js +0 -4
  175. package/lib/module/Shared/VisionCameraPlugins/FaceDetector/types.js +0 -3
  176. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/Camera.js +0 -56
  177. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/PhotoRecognizer.js +0 -20
  178. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/RemoveLanguageModel.js +0 -9
  179. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/index.js +0 -6
  180. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/scanText.js +0 -20
  181. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/translateText.js +0 -19
  182. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/types.js +0 -3
  183. package/lib/typescript/src/Shared/Libs/camera.utils.d.ts +0 -87
  184. package/lib/typescript/src/Shared/Libs/camera.utils.d.ts.map +0 -1
  185. package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts +0 -25
  186. package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts.map +0 -1
  187. package/lib/typescript/src/Shared/Libs/opencv.utils.d.ts +0 -3
  188. package/lib/typescript/src/Shared/Libs/opencv.utils.d.ts.map +0 -1
  189. package/lib/typescript/src/Shared/Libs/worklet.utils.d.ts +0 -9
  190. package/lib/typescript/src/Shared/Libs/worklet.utils.d.ts.map +0 -1
  191. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useBarcodeScanner.d.ts +0 -13
  192. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useBarcodeScanner.d.ts.map +0 -1
  193. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useCameraPermissions.d.ts +0 -6
  194. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useCameraPermissions.d.ts.map +0 -1
  195. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/index.d.ts +0 -12
  196. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/index.d.ts.map +0 -1
  197. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/scanCodes.d.ts +0 -3
  198. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/scanCodes.d.ts.map +0 -1
  199. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/types.d.ts +0 -52
  200. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/types.d.ts.map +0 -1
  201. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/convert.d.ts +0 -62
  202. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/convert.d.ts.map +0 -1
  203. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/geometry.d.ts +0 -34
  204. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/geometry.d.ts.map +0 -1
  205. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/highlights.d.ts +0 -32
  206. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/highlights.d.ts.map +0 -1
  207. package/lib/typescript/src/Shared/VisionCameraPlugins/Cropper/index.d.ts +0 -23
  208. package/lib/typescript/src/Shared/VisionCameraPlugins/Cropper/index.d.ts.map +0 -1
  209. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/Camera.d.ts +0 -9
  210. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/Camera.d.ts.map +0 -1
  211. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/detectFaces.d.ts +0 -3
  212. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/detectFaces.d.ts.map +0 -1
  213. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/index.d.ts +0 -3
  214. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/index.d.ts.map +0 -1
  215. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/types.d.ts +0 -79
  216. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/types.d.ts.map +0 -1
  217. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/Camera.d.ts +0 -6
  218. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/Camera.d.ts.map +0 -1
  219. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/PhotoRecognizer.d.ts +0 -3
  220. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/PhotoRecognizer.d.ts.map +0 -1
  221. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/RemoveLanguageModel.d.ts +0 -3
  222. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/RemoveLanguageModel.d.ts.map +0 -1
  223. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/index.d.ts +0 -5
  224. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/index.d.ts.map +0 -1
  225. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/scanText.d.ts +0 -3
  226. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/scanText.d.ts.map +0 -1
  227. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/translateText.d.ts +0 -3
  228. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/translateText.d.ts.map +0 -1
  229. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/types.d.ts +0 -67
  230. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/types.d.ts.map +0 -1
  231. package/src/Shared/Libs/camera.utils.ts +0 -345
  232. package/src/Shared/Libs/frame-enhancement.utils.ts +0 -217
  233. package/src/Shared/Libs/opencv.utils.ts +0 -40
  234. package/src/Shared/Libs/worklet.utils.ts +0 -72
  235. package/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useBarcodeScanner.ts +0 -79
  236. package/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useCameraPermissions.ts +0 -46
  237. package/src/Shared/VisionCameraPlugins/BarcodeScanner/index.ts +0 -60
  238. package/src/Shared/VisionCameraPlugins/BarcodeScanner/scanCodes.ts +0 -32
  239. package/src/Shared/VisionCameraPlugins/BarcodeScanner/types.ts +0 -82
  240. package/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/convert.ts +0 -195
  241. package/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/geometry.ts +0 -135
  242. package/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/highlights.ts +0 -84
  243. package/src/Shared/VisionCameraPlugins/Cropper/index.ts +0 -78
  244. package/src/Shared/VisionCameraPlugins/FaceDetector/Camera.tsx +0 -63
  245. package/src/Shared/VisionCameraPlugins/FaceDetector/detectFaces.ts +0 -44
  246. package/src/Shared/VisionCameraPlugins/FaceDetector/index.ts +0 -3
  247. package/src/Shared/VisionCameraPlugins/FaceDetector/types.ts +0 -99
  248. package/src/Shared/VisionCameraPlugins/TextRecognition/Camera.tsx +0 -76
  249. package/src/Shared/VisionCameraPlugins/TextRecognition/PhotoRecognizer.ts +0 -18
  250. package/src/Shared/VisionCameraPlugins/TextRecognition/RemoveLanguageModel.ts +0 -7
  251. package/src/Shared/VisionCameraPlugins/TextRecognition/index.ts +0 -7
  252. package/src/Shared/VisionCameraPlugins/TextRecognition/scanText.ts +0 -27
  253. package/src/Shared/VisionCameraPlugins/TextRecognition/translateText.ts +0 -21
  254. package/src/Shared/VisionCameraPlugins/TextRecognition/types.ts +0 -141
@@ -0,0 +1,785 @@
1
+ package com.trustchex.reactnativesdk.mrz
2
+
3
+ import com.facebook.react.bridge.Promise
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
6
+ import com.facebook.react.bridge.ReactMethod
7
+ import com.facebook.react.bridge.WritableNativeArray
8
+ import com.facebook.react.bridge.WritableNativeMap
9
+
10
+ /**
11
+ * Native MRZ Validation Module
12
+ *
13
+ * Performs high-performance MRZ detection and validation with checksum verification
14
+ * according to ICAO 9303 standards. This native implementation is significantly faster
15
+ * than JavaScript parsing, especially for real-time camera frame processing.
16
+ *
17
+ * ICAO 9303 Compliance:
18
+ * - TD1: Identity cards (3 lines × 30 characters)
19
+ * - TD2: Identity cards (2 lines × 36 characters)
20
+ * - TD3: Passports (2 lines × 44 characters)
21
+ * - Check digit algorithm: 7-3-1 weighted modulo 10
22
+ * - Character values: 0-9 = 0-9, A-Z = 10-35, < = 0
23
+ * - Validates: document number, birth date, expiry date, optional/personal number, composite checksums
24
+ *
25
+ * Key features:
26
+ * - Native checksum calculation (7-3-1 algorithm)
27
+ * - MRZ format detection (TD1, TD2, TD3)
28
+ * - Field extraction and validation
29
+ * - OCR correction with position-aware character mapping
30
+ * - Composite checksum brute-force correction
31
+ *
32
+ * @see https://www2023.icao.int/publications/Documents/9303_p3_cons_en.pdf
33
+ */
34
+ class MRZValidationModule(reactContext: ReactApplicationContext) :
35
+ ReactContextBaseJavaModule(reactContext) {
36
+
37
+ override fun getName(): String {
38
+ return "MRZValidation"
39
+ }
40
+
41
+ /**
42
+ * Primary MRZ validation with OCR corrections and composite checksum brute-force
43
+ * This is the main entry point for fast native processing
44
+ *
45
+ * @param ocrText Raw OCR text from camera
46
+ * @param promise Promise resolving to validation result with field data
47
+ */
48
+ @ReactMethod
49
+ fun validateMRZWithCorrections(ocrText: String, promise: Promise) {
50
+ try {
51
+ // Fix and clean raw OCR text
52
+ val fixedText = fixMRZ(ocrText)
53
+ val lines = fixedText.trim().split("\n").map { it.trim() }
54
+
55
+ if (lines.isEmpty() || fixedText.length < 60) {
56
+ promise.resolve(createInvalidResult("MRZ text too short"))
57
+ return
58
+ }
59
+
60
+ val format = detectMRZFormat(lines)
61
+ if (format == "UNKNOWN") {
62
+ promise.resolve(createInvalidResult("Unknown MRZ format"))
63
+ return
64
+ }
65
+
66
+ // Phase 1: Try parsing with position-aware corrections
67
+ var result = parseMRZ(fixedText)
68
+ if (result.getBoolean("valid") == true) {
69
+ promise.resolve(result)
70
+ return
71
+ }
72
+
73
+ // Phase 2: Try composite checksum brute-force if only composite is invalid
74
+ val checksums = result.getMap("checksums")
75
+ val invalidFields = checksums?.getArray("invalidFields")
76
+
77
+ if (invalidFields != null && invalidFields.size() == 1) {
78
+ val field = invalidFields.getString(0)
79
+ if (field == "compositeCheckDigit") {
80
+ // Try all 10 possible composite checksum digits
81
+ val compositePos = when (format) {
82
+ "TD1" -> Pair(1, 29)
83
+ "TD2" -> Pair(1, 35)
84
+ "TD3" -> Pair(1, 43)
85
+ else -> null
86
+ }
87
+
88
+ if (compositePos != null) {
89
+ val mutableLines = lines.toMutableList()
90
+ val targetLine = mutableLines[compositePos.first]
91
+
92
+ for (digit in 0..9) {
93
+ val testLine = targetLine.substring(0, compositePos.second) +
94
+ digit +
95
+ targetLine.substring(compositePos.second + 1)
96
+ mutableLines[compositePos.first] = testLine
97
+
98
+ val testMRZ = mutableLines.joinToString("\n")
99
+ val testResult = parseMRZ(testMRZ)
100
+
101
+ if (testResult.getBoolean("valid") == true) {
102
+ android.util.Log.d("MRZValidation", "[Native] Valid MRZ found via composite brute-force")
103
+ promise.resolve(testResult)
104
+ return
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ // Phase 3: Try exhaustive O/0 permutations
112
+ android.util.Log.d("MRZValidation", "[Native] Position-aware corrections failed, trying O/0 permutations")
113
+ val o0Permutations = generateO0Permutations(fixedText)
114
+
115
+ for ((index, permutation) in o0Permutations.withIndex()) {
116
+ val testResult = parseMRZ(permutation)
117
+ if (testResult.getBoolean("valid") == true) {
118
+ android.util.Log.d("MRZValidation", "[Native] Valid MRZ found via O/0 permutation ${index + 1}/${o0Permutations.size}")
119
+ promise.resolve(testResult)
120
+ return
121
+ }
122
+ }
123
+
124
+ // Phase 4: Try I/1 permutations as last resort
125
+ android.util.Log.d("MRZValidation", "[Native] O/0 permutations failed, trying I/1 permutations")
126
+ val i1Permutations = generateI1Permutations(fixedText)
127
+
128
+ for ((index, permutation) in i1Permutations.withIndex()) {
129
+ val testResult = parseMRZ(permutation)
130
+ if (testResult.getBoolean("valid") == true) {
131
+ android.util.Log.d("MRZValidation", "[Native] Valid MRZ found via I/1 permutation ${index + 1}/${i1Permutations.size}")
132
+ promise.resolve(testResult)
133
+ return
134
+ }
135
+ }
136
+
137
+ // Return invalid result if all attempts failed
138
+ android.util.Log.d("MRZValidation", "[Native] All permutation attempts failed")
139
+ promise.resolve(result)
140
+ } catch (e: Exception) {
141
+ promise.reject("MRZ_VALIDATION_ERROR", "Failed to validate MRZ: ${e.message}", e)
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Validates MRZ text and returns parse result with checksum validation
147
+ *
148
+ * @param mrzText The MRZ text to validate (2 or 3 lines separated by \n)
149
+ * @param promise Promise resolving to validation result object
150
+ */
151
+ @ReactMethod
152
+ fun validateMRZ(mrzText: String, promise: Promise) {
153
+ try {
154
+ val result = parseMRZ(mrzText)
155
+ promise.resolve(result)
156
+ } catch (e: Exception) {
157
+ promise.reject("MRZ_VALIDATION_ERROR", "Failed to validate MRZ: ${e.message}", e)
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Create invalid result response
163
+ */
164
+ private fun createInvalidResult(error: String): WritableNativeMap {
165
+ val result = WritableNativeMap()
166
+ result.putBoolean("valid", false)
167
+ result.putString("error", error)
168
+ result.putString("format", "UNKNOWN")
169
+ return result
170
+ }
171
+
172
+ /**
173
+ * Calculates checksum for MRZ field using 7-3-1 algorithm
174
+ *
175
+ * @param data The field data to calculate checksum for
176
+ * @param promise Promise resolving to checksum character
177
+ */
178
+ @ReactMethod
179
+ fun calculateChecksum(data: String, promise: Promise) {
180
+ try {
181
+ val checksum = calculateMRZChecksum(data)
182
+ promise.resolve(checksum.toString())
183
+ } catch (e: Exception) {
184
+ promise.reject("CHECKSUM_ERROR", "Failed to calculate checksum: ${e.message}", e)
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Fixes common OCR errors in MRZ text
190
+ *
191
+ * @param mrzText Raw MRZ text from OCR
192
+ * @param promise Promise resolving to fixed MRZ text
193
+ */
194
+ @ReactMethod
195
+ fun fixMRZText(mrzText: String, promise: Promise) {
196
+ try {
197
+ val fixed = fixMRZ(mrzText)
198
+ promise.resolve(fixed)
199
+ } catch (e: Exception) {
200
+ promise.reject("MRZ_FIX_ERROR", "Failed to fix MRZ text: ${e.message}", e)
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Main MRZ parser - validates format and checksums
206
+ */
207
+ private fun parseMRZ(rawText: String): WritableNativeMap {
208
+ val result = WritableNativeMap()
209
+ val lines = rawText.trim().split("\n").map { it.trim() }
210
+
211
+ // Detect format
212
+ val format = detectMRZFormat(lines)
213
+ result.putString("format", format)
214
+
215
+ if (format == "UNKNOWN") {
216
+ result.putBoolean("valid", false)
217
+ result.putString("error", "Unknown MRZ format")
218
+ return result
219
+ }
220
+
221
+ // Parse based on format
222
+ val fields = when (format) {
223
+ "TD1" -> parseTD1(lines)
224
+ "TD2" -> parseTD2(lines)
225
+ "TD3" -> parseTD3(lines)
226
+ else -> {
227
+ result.putBoolean("valid", false)
228
+ result.putString("error", "Unsupported format")
229
+ return result
230
+ }
231
+ }
232
+
233
+ result.putMap("fields", fields)
234
+
235
+ // Validate all checksums
236
+ val checksumValidation = validateChecksums(format, lines)
237
+ result.putBoolean("valid", checksumValidation.getBoolean("allValid"))
238
+ result.putMap("checksums", checksumValidation)
239
+
240
+ return result
241
+ }
242
+
243
+ /**
244
+ * Detects MRZ format from line structure
245
+ */
246
+ private fun detectMRZFormat(lines: List<String>): String {
247
+ return when {
248
+ lines.size == 3 && lines.all { it.length == 30 } -> "TD1"
249
+ lines.size == 2 && lines.all { it.length == 36 } -> "TD2"
250
+ lines.size == 2 && lines.all { it.length == 44 } -> "TD3"
251
+ else -> "UNKNOWN"
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Parse TD1 format (ID card, 3 lines of 30 chars)
257
+ */
258
+ private fun parseTD1(lines: List<String>): WritableNativeMap {
259
+ val fields = WritableNativeMap()
260
+
261
+ // Line 1: Document code (2) + Issuing state (3) + Document number (9) + Check digit (1) + Optional (15)
262
+ fields.putString("documentCode", lines[0].substring(0, 2).replace('<', ' ').trim())
263
+ fields.putString("issuingState", lines[0].substring(2, 5).replace('<', ' ').trim())
264
+ fields.putString("documentNumber", lines[0].substring(5, 14).replace('<', ' ').trim())
265
+ fields.putString("documentNumberCheckDigit", lines[0].substring(14, 15))
266
+ fields.putString("optional1", lines[0].substring(15, 30).replace('<', ' ').trim())
267
+
268
+ // Line 2: Birth date (6) + Check digit (1) + Sex (1) + Expiry date (6) + Check digit (1) + Nationality (3) + Optional (11) + Composite check digit (1)
269
+ fields.putString("birthDate", lines[1].substring(0, 6))
270
+ fields.putString("birthDateCheckDigit", lines[1].substring(6, 7))
271
+ fields.putString("sex", lines[1].substring(7, 8))
272
+ fields.putString("expirationDate", lines[1].substring(8, 14))
273
+ fields.putString("expirationDateCheckDigit", lines[1].substring(14, 15))
274
+ fields.putString("nationality", lines[1].substring(15, 18).replace('<', ' ').trim())
275
+ fields.putString("optional2", lines[1].substring(18, 29).replace('<', ' ').trim())
276
+ fields.putString("compositeCheckDigit", lines[1].substring(29, 30))
277
+
278
+ // Line 3: Names
279
+ val names = lines[2].split("<<")
280
+ fields.putString("lastName", names[0].replace('<', ' ').trim())
281
+ fields.putString("firstName", if (names.size > 1) names[1].replace('<', ' ').trim() else "")
282
+
283
+ return fields
284
+ }
285
+
286
+ /**
287
+ * Parse TD2 format (ID card, 2 lines of 36 chars)
288
+ */
289
+ private fun parseTD2(lines: List<String>): WritableNativeMap {
290
+ val fields = WritableNativeMap()
291
+
292
+ // Line 1: Document code (2) + Issuing state (3) + Names (31)
293
+ fields.putString("documentCode", lines[0].substring(0, 2).replace('<', ' ').trim())
294
+ fields.putString("issuingState", lines[0].substring(2, 5).replace('<', ' ').trim())
295
+ val names = lines[0].substring(5, 36).split("<<")
296
+ fields.putString("lastName", names[0].replace('<', ' ').trim())
297
+ fields.putString("firstName", if (names.size > 1) names[1].replace('<', ' ').trim() else "")
298
+
299
+ // Line 2: Document number (9) + Check digit (1) + Nationality (3) + Birth date (6) + Check digit (1) + Sex (1) + Expiry date (6) + Check digit (1) + Optional (7) + Composite check digit (1)
300
+ fields.putString("documentNumber", lines[1].substring(0, 9).replace('<', ' ').trim())
301
+ fields.putString("documentNumberCheckDigit", lines[1].substring(9, 10))
302
+ fields.putString("nationality", lines[1].substring(10, 13).replace('<', ' ').trim())
303
+ fields.putString("birthDate", lines[1].substring(13, 19))
304
+ fields.putString("birthDateCheckDigit", lines[1].substring(19, 20))
305
+ fields.putString("sex", lines[1].substring(20, 21))
306
+ fields.putString("expirationDate", lines[1].substring(21, 27))
307
+ fields.putString("expirationDateCheckDigit", lines[1].substring(27, 28))
308
+ fields.putString("optional1", lines[1].substring(28, 35).replace('<', ' ').trim())
309
+ fields.putString("compositeCheckDigit", lines[1].substring(35, 36))
310
+
311
+ return fields
312
+ }
313
+
314
+ /**
315
+ * Parse TD3 format (Passport, 2 lines of 44 chars)
316
+ */
317
+ private fun parseTD3(lines: List<String>): WritableNativeMap {
318
+ val fields = WritableNativeMap()
319
+
320
+ // Line 1: Document code (2) + Issuing state (3) + Names (39)
321
+ fields.putString("documentCode", lines[0].substring(0, 2).replace('<', ' ').trim())
322
+ fields.putString("issuingState", lines[0].substring(2, 5).replace('<', ' ').trim())
323
+ val names = lines[0].substring(5, 44).split("<<")
324
+ fields.putString("lastName", names[0].replace('<', ' ').trim())
325
+ fields.putString("firstName", if (names.size > 1) names[1].replace('<', ' ').trim() else "")
326
+
327
+ // Line 2: Document number (9) + Check digit (1) + Nationality (3) + Birth date (6) + Check digit (1) + Sex (1) + Expiry date (6) + Check digit (1) + Personal number (14) + Check digit (1) + Composite check digit (1)
328
+ fields.putString("documentNumber", lines[1].substring(0, 9).replace('<', ' ').trim())
329
+ fields.putString("documentNumberCheckDigit", lines[1].substring(9, 10))
330
+ fields.putString("nationality", lines[1].substring(10, 13).replace('<', ' ').trim())
331
+ fields.putString("birthDate", lines[1].substring(13, 19))
332
+ fields.putString("birthDateCheckDigit", lines[1].substring(19, 20))
333
+ fields.putString("sex", lines[1].substring(20, 21))
334
+ fields.putString("expirationDate", lines[1].substring(21, 27))
335
+ fields.putString("expirationDateCheckDigit", lines[1].substring(27, 28))
336
+ fields.putString("optional1", lines[1].substring(28, 42).replace('<', ' ').trim())
337
+ fields.putString("optional1CheckDigit", lines[1].substring(42, 43))
338
+ fields.putString("compositeCheckDigit", lines[1].substring(43, 44))
339
+
340
+ return fields
341
+ }
342
+
343
+ /**
344
+ * Validates all checksums in MRZ according to ICAO 9303 Part 3
345
+ *
346
+ * ICAO 9303 Check Digit Calculation:
347
+ * - Weights: 7, 3, 1 (cycling)
348
+ * - Character values: 0-9 = 0-9, A-Z = 10-35, < (filler) = 0
349
+ * - Result: (sum of weighted values) mod 10
350
+ *
351
+ * @see https://www2023.icao.int/publications/Documents/9303_p3_cons_en.pdf Section 4.9
352
+ */
353
+ private fun validateChecksums(format: String, lines: List<String>): WritableNativeMap {
354
+ val result = WritableNativeMap()
355
+ val invalidFields = WritableNativeArray()
356
+ var allValid = true
357
+
358
+ when (format) {
359
+ "TD1" -> {
360
+ // TD1 Format (ID card, 3 lines × 30 characters)
361
+ // Line 1: Document code(2) + Issuing state(3) + Document number(9) + Check(1) + Optional(15)
362
+ // Line 2: Birth date(6) + Check(1) + Sex(1) + Expiry(6) + Check(1) + Nationality(3) + Optional(11) + Composite check(1)
363
+ // Line 3: Names
364
+
365
+ // Document number checksum - ICAO 9303 Part 3, Section 4.9
366
+ val docNum = lines[0].substring(5, 14) // Positions 5-13
367
+ val docNumCheck = lines[0][14] // Position 14
368
+ val docNumValid = calculateMRZChecksum(docNum) == docNumCheck
369
+ result.putBoolean("documentNumberCheckDigit", docNumValid)
370
+ if (!docNumValid) {
371
+ invalidFields.pushString("documentNumberCheckDigit")
372
+ allValid = false
373
+ }
374
+
375
+ // Birth date checksum
376
+ val birthDate = lines[1].substring(0, 6) // Positions 0-5
377
+ val birthDateCheck = lines[1][6] // Position 6
378
+ val birthDateValid = calculateMRZChecksum(birthDate) == birthDateCheck
379
+ result.putBoolean("birthDateCheckDigit", birthDateValid)
380
+ if (!birthDateValid) {
381
+ invalidFields.pushString("birthDateCheckDigit")
382
+ allValid = false
383
+ }
384
+
385
+ // Expiry date checksum
386
+ val expiryDate = lines[1].substring(8, 14) // Positions 8-13
387
+ val expiryDateCheck = lines[1][14] // Position 14
388
+ val expiryDateValid = calculateMRZChecksum(expiryDate) == expiryDateCheck
389
+ result.putBoolean("expirationDateCheckDigit", expiryDateValid)
390
+ if (!expiryDateValid) {
391
+ invalidFields.pushString("expirationDateCheckDigit")
392
+ allValid = false
393
+ }
394
+
395
+ // Composite checksum - ICAO 9303 Part 3, Section 4.9
396
+ // TD1 Composite = Line1(5-29) + Line2(0-6) + Line2(8-14) + Line2(18-28)
397
+ // This includes: doc number(9) + check(1) + optional(15) + birth(6) + check(1) + expiry(6) + check(1) + optional(11)
398
+ // Total: 50 characters
399
+ val composite = lines[0].substring(5, 30) + lines[1].substring(0, 7) + lines[1].substring(8, 15) + lines[1].substring(18, 29)
400
+ val compositeCheck = lines[1][29] // Position 29
401
+ val compositeValid = calculateMRZChecksum(composite) == compositeCheck
402
+ result.putBoolean("compositeCheckDigit", compositeValid)
403
+ if (!compositeValid) {
404
+ invalidFields.pushString("compositeCheckDigit")
405
+ allValid = false
406
+ }
407
+ }
408
+ "TD2", "TD3" -> {
409
+ // TD2 Format (ID card, 2 lines × 36 characters)
410
+ // Line 1: Document code(2) + Issuing state(3) + Names(31)
411
+ // Line 2: Doc number(9) + Check(1) + Nationality(3) + Birth(6) + Check(1) + Sex(1) + Expiry(6) + Check(1) + Optional(7) + Composite check(1)
412
+ //
413
+ // TD3 Format (Passport, 2 lines × 44 characters)
414
+ // Line 1: Document code(2) + Issuing state(3) + Names(39)
415
+ // Line 2: Doc number(9) + Check(1) + Nationality(3) + Birth(6) + Check(1) + Sex(1) + Expiry(6) + Check(1) + Personal number(14) + Check(1) + Composite check(1)
416
+
417
+ val line = lines[1]
418
+
419
+ // Document number checksum - ICAO 9303 Part 3, Section 4.9
420
+ val docNum = line.substring(0, 9) // Positions 0-8
421
+ val docNumCheck = line[9] // Position 9
422
+ val docNumValid = calculateMRZChecksum(docNum) == docNumCheck
423
+ result.putBoolean("documentNumberCheckDigit", docNumValid)
424
+ if (!docNumValid) {
425
+ invalidFields.pushString("documentNumberCheckDigit")
426
+ allValid = false
427
+ }
428
+
429
+ // Birth date checksum
430
+ val birthDate = line.substring(13, 19) // Positions 13-18
431
+ val birthDateCheck = line[19] // Position 19
432
+ val birthDateValid = calculateMRZChecksum(birthDate) == birthDateCheck
433
+ result.putBoolean("birthDateCheckDigit", birthDateValid)
434
+ if (!birthDateValid) {
435
+ invalidFields.pushString("birthDateCheckDigit")
436
+ allValid = false
437
+ }
438
+
439
+ // Expiry date checksum
440
+ val expiryDate = line.substring(21, 27) // Positions 21-26
441
+ val expiryDateCheck = line[27] // Position 27
442
+ val expiryDateValid = calculateMRZChecksum(expiryDate) == expiryDateCheck
443
+ result.putBoolean("expirationDateCheckDigit", expiryDateValid)
444
+ if (!expiryDateValid) {
445
+ invalidFields.pushString("expirationDateCheckDigit")
446
+ allValid = false
447
+ }
448
+
449
+ // TD3 has personal number checksum (optional field)
450
+ if (format == "TD3") {
451
+ val personalNum = line.substring(28, 42) // Positions 28-41
452
+ val personalNumCheck = line[42] // Position 42
453
+ val personalNumValid = calculateMRZChecksum(personalNum) == personalNumCheck
454
+ result.putBoolean("optional1CheckDigit", personalNumValid)
455
+ if (!personalNumValid) {
456
+ invalidFields.pushString("optional1CheckDigit")
457
+ allValid = false
458
+ }
459
+ }
460
+
461
+ // Composite checksum - ICAO 9303 Part 3, Section 4.9
462
+ // TD2: Doc number(9) + check(1) + Birth(6) + check(1) + Expiry(6) + check(1) + Optional(7) = 31 chars
463
+ // TD3: Doc number(9) + check(1) + Birth(6) + check(1) + Expiry(6) + check(1) + Personal(14) + check(1) = 39 chars
464
+ val compositeCheck = if (format == "TD2") line[35] else line[43]
465
+ val composite = if (format == "TD2") {
466
+ // TD2: positions 0-9 + 13-19 + 21-34 (excluding composite at 35)
467
+ line.substring(0, 10) + line.substring(13, 20) + line.substring(21, 35)
468
+ } else {
469
+ // TD3: positions 0-9 + 13-19 + 21-42 (excluding composite at 43)
470
+ line.substring(0, 10) + line.substring(13, 20) + line.substring(21, 43)
471
+ }
472
+ val compositeValid = calculateMRZChecksum(composite) == compositeCheck
473
+ result.putBoolean("compositeCheckDigit", compositeValid)
474
+ if (!compositeValid) {
475
+ invalidFields.pushString("compositeCheckDigit")
476
+ allValid = false
477
+ }
478
+ }
479
+ }
480
+
481
+ result.putBoolean("allValid", allValid)
482
+ result.putArray("invalidFields", invalidFields)
483
+
484
+ return result
485
+ }
486
+
487
+ /**
488
+ * Calculate MRZ checksum using 7-3-1 weighted algorithm per ICAO 9303
489
+ *
490
+ * ICAO 9303 Part 3, Section 4.9 - Check Digit Calculation:
491
+ *
492
+ * Algorithm:
493
+ * 1. Assign numerical values to characters:
494
+ * - Digits 0-9: values 0-9
495
+ * - Letters A-Z: values 10-35 (A=10, B=11, ..., Z=35)
496
+ * - Filler character '<': value 0
497
+ *
498
+ * 2. Apply weights [7, 3, 1] cyclically to each character position
499
+ *
500
+ * 3. Calculate: sum = Σ(character_value × weight) for all positions
501
+ *
502
+ * 4. Check digit = sum mod 10
503
+ *
504
+ * Example: "AB2134<"
505
+ * A(10)×7 + B(11)×3 + 2(2)×1 + 1(1)×7 + 3(3)×3 + 4(4)×1 + <(0)×7
506
+ * = 70 + 33 + 2 + 7 + 9 + 4 + 0 = 125
507
+ * Check digit = 125 mod 10 = 5
508
+ *
509
+ * @param data The string to calculate checksum for
510
+ * @return The check digit as a character ('0'-'9')
511
+ * @see https://www2023.icao.int/publications/Documents/9303_p3_cons_en.pdf
512
+ */
513
+ private fun calculateMRZChecksum(data: String): Char {
514
+ val weights = intArrayOf(7, 3, 1)
515
+ var sum = 0
516
+
517
+ for (i in data.indices) {
518
+ val char = data[i]
519
+ val value = when (char) {
520
+ in '0'..'9' -> char - '0'
521
+ in 'A'..'Z' -> char - 'A' + 10
522
+ '<' -> 0
523
+ else -> 0
524
+ }
525
+ sum += value * weights[i % 3]
526
+ }
527
+
528
+ return ('0' + (sum % 10))
529
+ }
530
+
531
+ /**
532
+ * Fix common OCR errors in MRZ text with mrz-fast inspired corrections
533
+ */
534
+ private fun fixMRZ(rawText: String): String {
535
+ var fixed = rawText
536
+ .replace(" ", "") // Remove spaces
537
+ .replace("«", "") // Remove « characters
538
+ .replace(Regex("<K+|r+K+|<r+K+"), "") // Remove invalid patterns
539
+
540
+ // Fix common O/0 confusion in Turkish ID document numbers
541
+ fixed = fixed.replace(Regex("""\bI<TUR([A-Z0-9]{3})0([A-Z0-9]{6})\b"""), "I<TUR$1O$2")
542
+
543
+ val lines = fixed.split("\n")
544
+ .map { it.trim() }
545
+ .filter { it.contains('<') }
546
+
547
+ // Detect format and pad lines
548
+ val targetLength = when {
549
+ lines.all { it.length <= 30 } -> 30 // TD1
550
+ lines.all { it.length <= 36 } -> 36 // TD2
551
+ lines.all { it.length <= 44 } -> 44 // TD3
552
+ else -> return rawText // Cannot detect format
553
+ }
554
+
555
+ val paddedLines = lines.map { line ->
556
+ if (line.length < targetLength) {
557
+ line.padEnd(targetLength, '<')
558
+ } else {
559
+ line
560
+ }
561
+ }
562
+
563
+ return applyOCRCorrections(paddedLines.joinToString("\n"))
564
+ }
565
+
566
+ /**
567
+ * Apply position-aware OCR corrections inspired by mrz-fast
568
+ * Character confusion mapping: O↔0, I↔1↔l, S↔5, B↔8, Z↔2, G↔6
569
+ */
570
+ private fun applyOCRCorrections(mrzText: String): String {
571
+ val lines = mrzText.split("\n").toMutableList()
572
+ if (lines.isEmpty()) return mrzText
573
+
574
+ val format = detectMRZFormat(lines)
575
+ if (format == "UNKNOWN") return mrzText
576
+
577
+ when (format) {
578
+ "TD1" -> {
579
+ // Line 2: positions 0-5 (birth date), 8-13 (expiry date), 6,14,29 (check digits)
580
+ lines[1] = correctDigitPositions(lines[1], listOf(
581
+ 0..5, 8..13 // Date fields need digits
582
+ ))
583
+ lines[1] = correctCheckDigitPositions(lines[1], listOf(6, 14, 29))
584
+ // Nationality (15-17) needs letters
585
+ lines[1] = correctLetterPositions(lines[1], listOf(15..17))
586
+ }
587
+ "TD2" -> {
588
+ // Line 2: positions 13-18 (birth), 21-26 (expiry), 9,19,27,35 (checks)
589
+ lines[1] = correctDigitPositions(lines[1], listOf(
590
+ 13..18, 21..26
591
+ ))
592
+ lines[1] = correctCheckDigitPositions(lines[1], listOf(9, 19, 27, 35))
593
+ // Nationality (10-12) needs letters
594
+ lines[1] = correctLetterPositions(lines[1], listOf(10..12))
595
+ }
596
+ "TD3" -> {
597
+ // Line 2: positions 13-18 (birth), 21-26 (expiry), 9,19,27,42 (checks)
598
+ lines[1] = correctDigitPositions(lines[1], listOf(
599
+ 13..18, 21..26
600
+ ))
601
+ lines[1] = correctCheckDigitPositions(lines[1], listOf(9, 19, 27, 42))
602
+ // Nationality (10-12) needs letters
603
+ lines[1] = correctLetterPositions(lines[1], listOf(10..12))
604
+ }
605
+ }
606
+
607
+ return lines.joinToString("\n")
608
+ }
609
+
610
+ /**
611
+ * Correct positions that should contain digits (0-9)
612
+ */
613
+ private fun correctDigitPositions(line: String, ranges: List<IntRange>): String {
614
+ var corrected = line
615
+ ranges.forEach { range ->
616
+ range.forEach { pos ->
617
+ if (pos < corrected.length) {
618
+ val char = corrected[pos]
619
+ val replacement = when (char) {
620
+ 'O', 'o' -> '0'
621
+ 'I', 'l' -> '1'
622
+ 'Z' -> '2'
623
+ 'S' -> '5'
624
+ 'G' -> '6'
625
+ 'B' -> '8'
626
+ 'D', 'Q' -> '0'
627
+ else -> char
628
+ }
629
+ if (replacement != char) {
630
+ corrected = corrected.substring(0, pos) + replacement + corrected.substring(pos + 1)
631
+ }
632
+ }
633
+ }
634
+ }
635
+ return corrected
636
+ }
637
+
638
+ /**
639
+ * Correct positions that should contain letters (A-Z) or <
640
+ */
641
+ private fun correctLetterPositions(line: String, ranges: List<IntRange>): String {
642
+ var corrected = line
643
+ ranges.forEach { range ->
644
+ range.forEach { pos ->
645
+ if (pos < corrected.length) {
646
+ val char = corrected[pos]
647
+ val replacement = when (char) {
648
+ '0' -> 'O'
649
+ '1' -> 'I'
650
+ '5' -> 'S'
651
+ '8' -> 'B'
652
+ '2' -> 'Z'
653
+ '6' -> 'G'
654
+ else -> char
655
+ }
656
+ if (replacement != char) {
657
+ corrected = corrected.substring(0, pos) + replacement + corrected.substring(pos + 1)
658
+ }
659
+ }
660
+ }
661
+ }
662
+ return corrected
663
+ }
664
+
665
+ /**
666
+ * Correct check digit positions (should be 0-9 only)
667
+ */
668
+ private fun correctCheckDigitPositions(line: String, positions: List<Int>): String {
669
+ var corrected = line
670
+ positions.forEach { pos ->
671
+ if (pos < corrected.length) {
672
+ val char = corrected[pos]
673
+ val replacement = when (char) {
674
+ 'O', 'o' -> '0'
675
+ 'I', 'l' -> '1'
676
+ 'Z' -> '2'
677
+ 'S' -> '5'
678
+ 'G' -> '6'
679
+ 'B' -> '8'
680
+ else -> char
681
+ }
682
+ if (replacement != char) {
683
+ corrected = corrected.substring(0, pos) + replacement + corrected.substring(pos + 1)
684
+ }
685
+ }
686
+ }
687
+ return corrected
688
+ }
689
+
690
+ /**
691
+ * Generate all possible O/0 permutations for MRZ text
692
+ * This handles the most common OCR confusion between O and 0
693
+ *
694
+ * @param mrzText Original MRZ text
695
+ * @param maxPermutations Maximum number of permutations to generate (default: 100)
696
+ * @return List of all possible O/0 permutations
697
+ */
698
+ private fun generateO0Permutations(mrzText: String, maxPermutations: Int = 100): List<String> {
699
+ val permutations = mutableSetOf(mrzText)
700
+
701
+ // Find all positions with O or 0
702
+ val ambiguousPositions = mrzText.indices.filter {
703
+ val char = mrzText[it]
704
+ char == 'O' || char == 'o' || char == '0'
705
+ }
706
+
707
+ // Limit positions to avoid combinatorial explosion
708
+ val positions = ambiguousPositions.take(minOf(7, ambiguousPositions.size))
709
+
710
+ // Generate all combinations (2^n where n = number of positions)
711
+ val numCombinations = 1 shl positions.size // 2^n
712
+ val limit = minOf(numCombinations, maxPermutations)
713
+
714
+ for (i in 0 until limit) {
715
+ var variant = mrzText
716
+
717
+ // For each bit in the combination number, decide O or 0
718
+ positions.forEachIndexed { j, pos ->
719
+ val useZero = (i and (1 shl j)) != 0
720
+ val currentChar = variant[pos]
721
+
722
+ // Replace with appropriate character
723
+ val replacement = if (useZero && currentChar != '0') {
724
+ '0'
725
+ } else if (!useZero && currentChar != 'O') {
726
+ 'O'
727
+ } else {
728
+ currentChar
729
+ }
730
+
731
+ if (replacement != currentChar) {
732
+ variant = variant.substring(0, pos) + replacement + variant.substring(pos + 1)
733
+ }
734
+ }
735
+
736
+ permutations.add(variant)
737
+ }
738
+
739
+ return permutations.toList()
740
+ }
741
+
742
+ /**
743
+ * Generate I/1 permutations for additional OCR confusion cases
744
+ */
745
+ private fun generateI1Permutations(mrzText: String, maxPermutations: Int = 50): List<String> {
746
+ val permutations = mutableSetOf(mrzText)
747
+
748
+ // Find all positions with I or 1
749
+ val ambiguousPositions = mrzText.indices.filter {
750
+ val char = mrzText[it]
751
+ char == 'I' || char == 'i' || char == 'l' || char == '1'
752
+ }
753
+
754
+ // Limit positions to avoid too many permutations
755
+ val positions = ambiguousPositions.take(minOf(6, ambiguousPositions.size))
756
+
757
+ val numCombinations = 1 shl positions.size
758
+ val limit = minOf(numCombinations, maxPermutations)
759
+
760
+ for (i in 0 until limit) {
761
+ var variant = mrzText
762
+
763
+ positions.forEachIndexed { j, pos ->
764
+ val useOne = (i and (1 shl j)) != 0
765
+ val currentChar = variant[pos]
766
+
767
+ val replacement = if (useOne && currentChar != '1') {
768
+ '1'
769
+ } else if (!useOne && (currentChar == '1' || currentChar == 'l' || currentChar == 'i')) {
770
+ 'I'
771
+ } else {
772
+ currentChar
773
+ }
774
+
775
+ if (replacement != currentChar) {
776
+ variant = variant.substring(0, pos) + replacement + variant.substring(pos + 1)
777
+ }
778
+ }
779
+
780
+ permutations.add(variant)
781
+ }
782
+
783
+ return permutations.toList()
784
+ }
785
+ }