@trustchex/react-native-sdk 1.512.1 → 1.513.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.
- package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +9 -2
- package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +54 -0
- package/ios/Camera/TrustchexCameraView.swift +6 -2
- package/ios/OpenCV/OpenCVModule.mm +59 -0
- package/lib/module/Screens/Dynamic/AddressCaptureScreen.js +427 -112
- package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +9 -4
- package/lib/module/Screens/Static/ResultScreen.js +46 -0
- package/lib/module/Shared/Animations/address-scan.json +1 -0
- package/lib/module/Shared/Components/DebugNavigationPanel.js +10 -0
- package/lib/module/Shared/Components/EIDScanner.js +16 -4
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +100 -19
- package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +47 -0
- package/lib/module/Shared/Libs/addressProofHeuristic.js +130 -0
- package/lib/module/Shared/Libs/demo.utils.js +4 -0
- package/lib/module/Shared/Libs/sendDiagnosticReport.js +47 -1
- package/lib/module/Shared/Libs/vizName.js +138 -16
- package/lib/module/Translation/Resources/en.js +16 -3
- package/lib/module/Translation/Resources/tr.js +16 -3
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Dynamic/AddressCaptureScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/EIDScanner.d.ts +8 -3
- package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +25 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/addressProofHeuristic.d.ts +34 -0
- package/lib/typescript/src/Shared/Libs/addressProofHeuristic.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/sendDiagnosticReport.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/vizName.d.ts +31 -6
- package/lib/typescript/src/Shared/Libs/vizName.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +0 -5
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts +13 -0
- package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts +13 -0
- package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/Screens/Dynamic/AddressCaptureScreen.tsx +434 -75
- package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +14 -4
- package/src/Screens/Static/ResultScreen.tsx +59 -0
- package/src/Shared/Animations/address-scan.json +1 -0
- package/src/Shared/Components/DebugNavigationPanel.tsx +13 -0
- package/src/Shared/Components/EIDScanner.tsx +38 -12
- package/src/Shared/Components/IdentityDocumentCamera.tsx +109 -17
- package/src/Shared/Components/IdentityDocumentCamera.utils.ts +53 -0
- package/src/Shared/Libs/addressProofHeuristic.ts +197 -0
- package/src/Shared/Libs/demo.utils.ts +5 -0
- package/src/Shared/Libs/sendDiagnosticReport.ts +65 -7
- package/src/Shared/Libs/vizName.ts +158 -20
- package/src/Shared/Types/identificationInfo.ts +0 -5
- package/src/Translation/Resources/en.ts +28 -3
- package/src/Translation/Resources/tr.ts +28 -3
- package/src/version.ts +1 -1
|
@@ -807,6 +807,26 @@ const ResultScreen = () => {
|
|
|
807
807
|
})]
|
|
808
808
|
})
|
|
809
809
|
})]
|
|
810
|
+
}), appContext.identificationInfo.address && (appContext.identificationInfo.address.addressText || appContext.identificationInfo.address.proofImage) && /*#__PURE__*/_jsxs(View, {
|
|
811
|
+
style: styles.addressSection,
|
|
812
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
813
|
+
style: styles.mrzTextLabel,
|
|
814
|
+
children: t('addressCaptureScreen.addressLabel')
|
|
815
|
+
}), appContext.identificationInfo.address.addressText ? /*#__PURE__*/_jsx(Text, {
|
|
816
|
+
style: styles.addressText,
|
|
817
|
+
children: appContext.identificationInfo.address.addressText
|
|
818
|
+
}) : null, appContext.identificationInfo.address.proofImage ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
819
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
820
|
+
style: styles.addressProofLabel,
|
|
821
|
+
children: t('addressCaptureScreen.proofLabel')
|
|
822
|
+
}), /*#__PURE__*/_jsx(Image, {
|
|
823
|
+
style: styles.addressProofImage,
|
|
824
|
+
resizeMode: "contain",
|
|
825
|
+
source: {
|
|
826
|
+
uri: `data:image/jpeg;base64,${appContext.identificationInfo.address.proofImage}`
|
|
827
|
+
}
|
|
828
|
+
})]
|
|
829
|
+
}) : null]
|
|
810
830
|
}), appContext.identificationInfo.livenessDetection?.videoPath && /*#__PURE__*/_jsxs(_Fragment, {
|
|
811
831
|
children: [/*#__PURE__*/_jsx(Text, {
|
|
812
832
|
style: styles.sectionHeader,
|
|
@@ -1090,6 +1110,32 @@ const styles = StyleSheet.create({
|
|
|
1090
1110
|
lineHeight: 14,
|
|
1091
1111
|
color: '#1F2937'
|
|
1092
1112
|
},
|
|
1113
|
+
addressSection: {
|
|
1114
|
+
marginTop: 16,
|
|
1115
|
+
padding: 12,
|
|
1116
|
+
backgroundColor: '#F3F4F6',
|
|
1117
|
+
borderRadius: 8,
|
|
1118
|
+
borderWidth: 1,
|
|
1119
|
+
borderColor: '#D1D5DB'
|
|
1120
|
+
},
|
|
1121
|
+
addressText: {
|
|
1122
|
+
fontSize: 14,
|
|
1123
|
+
lineHeight: 20,
|
|
1124
|
+
color: '#1F2937'
|
|
1125
|
+
},
|
|
1126
|
+
addressProofLabel: {
|
|
1127
|
+
fontSize: 12,
|
|
1128
|
+
fontWeight: '600',
|
|
1129
|
+
color: '#6B7280',
|
|
1130
|
+
marginTop: 12,
|
|
1131
|
+
marginBottom: 8
|
|
1132
|
+
},
|
|
1133
|
+
addressProofImage: {
|
|
1134
|
+
width: '100%',
|
|
1135
|
+
// Portrait frame so the whole document shows upright, letterboxed not cropped.
|
|
1136
|
+
aspectRatio: 3 / 4,
|
|
1137
|
+
borderRadius: 8
|
|
1138
|
+
},
|
|
1093
1139
|
trustchexLogo: {
|
|
1094
1140
|
width: 150,
|
|
1095
1141
|
height: 75,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"nm":"Search","ddd":0,"h":500,"w":500,"meta":{"g":"@lottiefiles/toolkit-js 0.25.8"},"layers":[{"ty":4,"nm":"Check Middle","sr":1,"st":-60,"op":145,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.134,"y":1},"s":[100,100,100],"t":4},{"s":[69,69,100],"t":16}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[250,250,0],"t":2,"ti":[33,0,0],"to":[-33,0,0]},{"s":[52,250,0],"t":14}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":2},{"s":[0],"t":20}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.5,11.5],[-11,50.5],[60.5,-34.5]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"c":{"a":0,"k":[0.0627,0.7255,0.5059],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1},{"ty":4,"nm":"Check Right","sr":1,"st":-2,"op":144,"ip":58,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,250,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.5,11.5],[-11,50.5],[60.5,-34.5]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"c":{"a":0,"k":[0.0627,0.7255,0.5059],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tm","bm":0,"hd":false,"mn":"ADBE Vector Filter - Trim","nm":"Trim Paths 1","ix":2,"e":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":58},{"s":[100],"t":65}],"ix":2},"o":{"a":0,"k":0,"ix":3},"s":{"a":0,"k":0,"ix":1},"m":1}],"ind":2},{"ty":4,"nm":"Magnifying Glass","sr":1,"st":0,"op":144,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[86,86,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[-31.335,-31.31,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,-29.547],[29.547,0],[0,29.547],[-29.547,0]],"o":[[0,29.547],[-29.547,0],[0,-29.547],[29.547,0]],"v":[[53.5,0],[0,53.5],[-53.5,0],[0,-53.5]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":10,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":13,"ix":5},"c":{"a":0,"k":[0.0627,0.7255,0.5059],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[86,86],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3,"parent":4},{"ty":4,"nm":"Handle","sr":1,"st":0,"op":144,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-30.17,-31.17,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.394,"y":1},"s":[648.829,532.045,0],"t":18,"ti":[33.833,65.833,0],"to":[-71.333,-60.333,0]},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[220.829,170.045,0],"t":24,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.216,"y":0},"i":{"x":0,"y":1},"s":[220.829,170.045,0],"t":28,"ti":[-17.667,-10.167,0],"to":[13.667,-11.333,0]},{"o":{"x":0.851,"y":0.851},"i":{"x":0,"y":0},"s":[284.829,168.045,0],"t":32,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.375,"y":0},"i":{"x":0.057,"y":1},"s":[284.829,168.045,0],"t":34,"ti":[-2.631,-12.748,0],"to":[1.667,31.5,0]},{"o":{"x":0.981,"y":0},"i":{"x":0.826,"y":1},"s":[216.829,246.045,0],"t":37,"ti":[-9.667,-12,0],"to":[4.333,21,0]},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[280.829,246.045,0],"t":41,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.04,"y":1},"s":[280.829,246.045,0],"t":44,"ti":[-1,-13,0],"to":[-2.167,23.667,0]},{"o":{"x":0.871,"y":0},"i":{"x":0.667,"y":1},"s":[222.829,322.045,0],"t":48,"ti":[-10.667,-0.333,0],"to":[1,13,0]},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[286.829,324.045,0],"t":54,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.923,"y":0},"i":{"x":0.667,"y":1},"s":[286.829,324.045,0],"t":56,"ti":[-60.667,-35.333,0],"to":[60.667,35.333,0]},{"s":[650.829,536.045,0],"t":60}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.221,1.12],[0,0],[1.122,-1.22],[0,0],[-1.221,-1.121],[0,0],[-1.12,1.221],[0,0]],"o":[[0,0],[-1.221,-1.12],[0,0],[-1.121,1.221],[0,0],[1.22,1.121],[0,0],[1.121,-1.219]],"v":[[46.352,42.076],[-23.804,-22.35],[-28.044,-22.169],[-36.836,-12.594],[-36.656,-8.355],[33.501,56.07],[37.74,55.889],[46.533,46.314]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0627,0.7255,0.5059],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[38.207,23.72],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":4},{"ty":4,"nm":"Profile","sr":1,"st":4,"op":144,"ip":9,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[21.129,20.971,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0,100],"t":9},{"s":[93.913,93.913,100],"t":15}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[37.904,43.851,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Profile","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[3.642,0],[0,0],[0,3.642],[0,0],[-3.643,0],[0,0],[0,-3.642],[0,0]],"o":[[0,0],[-3.643,0],[0,0],[0,-3.642],[0,0],[3.642,0],[0,0],[0,3.642]],"v":[[14.126,20.72],[-14.126,20.72],[-20.721,14.126],[-20.721,-14.126],[-14.126,-20.72],[14.126,-20.72],[20.721,-14.126],[20.721,14.126]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.898,0.9059,0.9216],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[21.129,20.971],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":5,"parent":13},{"ty":4,"nm":"Line 1","sr":1,"st":5,"op":144,"ip":10,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[50.672,13.223,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,93.883,100],"t":10},{"s":[93.883,93.883,100],"t":16}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[65.64,36.578,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":10},{"s":[100],"t":16}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Line 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0]],"o":[[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[20.074,4.963],[-20.074,4.963],[-23.074,1.963],[-23.074,-1.963],[-20.074,-4.963],[20.074,-4.963],[23.074,-1.963],[23.074,1.963]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9529,0.9569,0.9647],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[73.746,13.223],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":6,"parent":13},{"ty":4,"nm":"Line 2","sr":1,"st":5,"op":144,"ip":10,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[50.421,32.288,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,93.883,100],"t":11},{"s":[93.883,93.883,100],"t":17}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[65.972,54.476,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":11},{"s":[100],"t":17}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Line 2","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.658,0],[0,0],[0,-1.658],[0,0]],"o":[[0,0],[-1.658,0],[0,0],[0,-1.658],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[20.576,4.962],[-20.576,4.962],[-23.576,1.962],[-23.576,-1.962],[-20.576,-4.962],[20.576,-4.962],[23.576,-1.962],[23.576,1.962]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.898,0.9059,0.9216],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[73.996,32.288],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":7,"parent":13},{"ty":4,"nm":"Line 3","sr":1,"st":5,"op":144,"ip":10,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0.501,60.383,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,93.883,100],"t":12},{"s":[93.883,93.883,100],"t":18}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[18.539,80.853,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":12},{"s":[100],"t":18}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Line 3","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0]],"o":[[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[45.159,4.963],[-45.159,4.963],[-48.159,1.963],[-48.159,-1.963],[-45.159,-4.963],[45.159,-4.963],[48.159,-1.963],[48.159,1.963]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.898,0.9059,0.9216],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[48.66,60.383],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":8,"parent":13},{"ty":4,"nm":"Line 4","sr":1,"st":5,"op":144,"ip":10,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0.752,83.963,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,93.883,100],"t":13},{"s":[93.883,93.883,100],"t":19}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[18.774,102.99,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":13},{"s":[100],"t":19}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Line 4","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0]],"o":[[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[45.034,4.962],[-45.034,4.962],[-48.034,1.962],[-48.034,-1.962],[-45.034,-4.962],[45.034,-4.962],[48.034,-1.962],[48.034,1.962]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.898,0.9059,0.9216],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[48.786,83.963],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":9,"parent":13},{"ty":4,"nm":"Line 5","sr":1,"st":5,"op":144,"ip":10,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0.25,107.543,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,93.883,100],"t":14},{"s":[93.883,93.883,100],"t":20}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[18.303,125.128,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":14},{"s":[100],"t":20}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Line 5","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0]],"o":[[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[45.661,4.962],[-45.661,4.962],[-48.661,1.962],[-48.661,-1.962],[-45.661,-4.962],[45.661,-4.962],[48.661,-1.962],[48.661,1.962]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.898,0.9059,0.9216],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[48.911,107.543],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":10,"parent":13},{"ty":4,"nm":"Line 6","sr":1,"st":5,"op":144,"ip":10,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0.25,131.876,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,93.883,100],"t":15},{"s":[93.883,93.883,100],"t":21}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[18.303,147.972,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":15},{"s":[100],"t":21}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Line 6","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.656],[0,0]],"o":[[0,0],[-1.657,0],[0,0],[0,-1.656],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[45.661,4.963],[-45.661,4.963],[-48.661,1.963],[-48.661,-1.963],[-45.661,-4.963],[45.661,-4.963],[48.661,-1.963],[48.661,1.963]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.898,0.9059,0.9216],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[48.911,131.876],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":11,"parent":13},{"ty":4,"nm":"Line 7","sr":1,"st":5,"op":145,"ip":10,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0.501,155.958,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,93.883,100],"t":16},{"s":[93.883,93.883,100],"t":22}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[18.539,170.581,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":16},{"s":[100],"t":22}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Line 7","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.656],[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0]],"o":[[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.656]],"v":[[45.159,4.963],[-45.159,4.963],[-48.159,1.963],[-48.159,-1.963],[-45.159,-4.963],[45.159,-4.963],[48.159,-1.963],[48.159,1.963]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.898,0.9059,0.9216],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[48.66,155.958],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":12,"parent":13},{"ty":4,"nm":"Right Page 1","sr":1,"st":-6,"op":145,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[64.304,101.087,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100,94.05,100],"t":4},{"s":[138,138,100],"t":16}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[435.776,247,0],"t":4,"ti":[30.833,-0.003,0],"to":[-30.833,0.003,0]},{"s":[250.776,247.017,0],"t":16}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[7.048,0],[0,0],[0,7.048],[0,0],[-7.048,0],[0,0],[0,-7.048],[0,0]],"o":[[0,0],[-7.048,0],[0,0],[0,-7.048],[0,0],[7.048,0],[0,0],[0,7.048]],"v":[[51.292,100.837],[-51.293,100.837],[-64.054,88.076],[-64.054,-88.076],[-51.293,-100.837],[51.292,-100.837],[64.054,-88.076],[64.054,88.076]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0.9176,0.9176,0.9176],"t":4},{"s":[1,1,1],"t":16}],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[101.747,96.634],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[64.304,101.087],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":13},{"ty":4,"nm":"Right Page 2","sr":1,"st":4,"op":148,"ip":4,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[64.304,101.087,0],"ix":1},"s":{"a":0,"k":[100,88.1,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[575.776,252.017,0],"t":8,"ti":[23.333,0.833,0],"to":[-23.333,-0.833,0]},{"s":[435.776,247.017,0],"t":20}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 9","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[7.048,0],[0,0],[0,7.048],[0,0],[-7.048,0],[0,0],[0,-7.047],[0,0]],"o":[[0,0],[-7.048,0],[0,0],[0,-7.047],[0,0],[7.048,0],[0,0],[0,7.048]],"v":[[51.292,100.837],[-51.293,100.837],[-64.054,88.076],[-64.054,-88.076],[-51.293,-100.837],[51.292,-100.837],[64.054,-88.076],[64.054,88.076]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.898,0.9059,0.9216],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[64.304,101.087],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":14},{"ty":4,"nm":"Left Page 1","sr":1,"st":0,"op":145,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[64.303,101.087,0],"ix":1},"s":{"a":0,"k":[100,93.058,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[64.898,247.073,0],"t":-2,"ti":[26.333,0,0],"to":[-26.333,0,0]},{"s":[-93.102,247.073,0],"t":10}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[7.048,0],[0,0],[0,7.048],[0,0],[-7.048,0],[0,0],[0,-7.048],[0,0]],"o":[[0,0],[-7.048,0],[0,0],[0,-7.048],[0,0],[7.048,0],[0,0],[0,7.048]],"v":[[51.293,100.837],[-51.292,100.837],[-64.053,88.075],[-64.053,-88.076],[-51.292,-100.837],[51.293,-100.837],[64.054,-88.076],[64.054,88.075]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9176,0.9176,0.9176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[64.303,101.087],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":15},{"ty":4,"nm":"Middle Text","sr":1,"st":0,"op":144,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[64.911,107,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100,100,100],"t":3},{"s":[75,75,100],"t":15}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[249.339,244.683,0],"t":2,"ti":[30.667,0,0],"to":[-30.667,0,0]},{"s":[65.339,244.683,0],"t":14}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":2},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":8},{"s":[0],"t":14}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[3.642,0],[0,0],[0,3.642],[0,0],[-3.642,0],[0,0],[0,-3.642],[0,0]],"o":[[0,0],[-3.642,0],[0,0],[0,-3.642],[0,0],[3.642,0],[0,0],[0,3.642]],"v":[[20.939,27.533],[-20.939,27.533],[-27.534,20.939],[-27.534,-20.939],[-20.939,-27.533],[20.939,-27.533],[27.534,-20.939],[27.534,20.939]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8471,0.8471,0.8471],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[27.993,27.784],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.656],[0,0],[-1.657,0],[0,0],[0,-1.658],[0,0]],"o":[[0,0],[-1.657,0],[0,0],[0,-1.658],[0,0],[1.657,0],[0,0],[0,1.656]],"v":[[61.661,6.594],[-61.661,6.594],[-64.661,3.594],[-64.661,-3.594],[-61.661,-6.594],[61.661,-6.594],[64.661,-3.594],[64.661,3.594]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8471,0.8471,0.8471],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[64.911,142.822],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0]],"o":[[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[60.827,6.594],[-60.827,6.594],[-63.827,3.594],[-63.827,-3.594],[-60.827,-6.594],[60.827,-6.594],[63.827,-3.594],[63.827,3.594]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8471,0.8471,0.8471],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[64.744,111.488],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 4","ix":4,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0]],"o":[[0,0],[-1.658,0],[0,0],[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[28.327,6.594],[-28.327,6.594],[-31.327,3.594],[-31.327,-3.594],[-28.327,-6.594],[28.327,-6.594],[31.327,-3.594],[31.327,3.594]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8471,0.8471,0.8471],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[98.244,42.822],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 5","ix":5,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0]],"o":[[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[27.661,6.594],[-27.661,6.594],[-30.661,3.594],[-30.661,-3.594],[-27.661,-6.594],[27.661,-6.594],[30.661,-3.594],[30.661,3.594]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8471,0.8471,0.8471],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[97.911,17.489],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 6","ix":6,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0]],"o":[[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[60.995,6.594],[-60.995,6.594],[-63.995,3.594],[-63.995,-3.594],[-60.995,-6.594],[60.995,-6.594],[63.995,-3.594],[63.995,3.594]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8471,0.8471,0.8471],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[64.577,80.155],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 7","ix":7,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0]],"o":[[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[61.661,6.594],[-61.661,6.594],[-64.661,3.594],[-64.661,-3.594],[-61.661,-6.594],[61.661,-6.594],[64.661,-3.594],[64.661,3.594]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8471,0.8471,0.8471],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[64.911,175.155],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 8","ix":8,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0]],"o":[[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657]],"v":[[60.995,6.594],[-60.995,6.594],[-63.995,3.594],[-63.995,-3.594],[-60.995,-6.594],[60.995,-6.594],[63.995,-3.594],[63.995,3.594]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8471,0.8471,0.8471],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[64.577,207.155],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":16},{"ty":4,"nm":"Middle Page","sr":1,"st":0,"op":144,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[91.789,134.239,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.392,"y":0},"i":{"x":0.598,"y":1},"s":[100,100,100],"t":3},{"s":[69,69,100],"t":15}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.377,"y":0},"i":{"x":0.667,"y":1},"s":[227.461,246.517,0],"t":2,"ti":[30.167,0,0],"to":[-30.167,0,0]},{"s":[46.461,246.517,0],"t":14}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[7.048,0],[0,0],[0,7.048],[0,0],[-7.047,0],[0,0],[0,-7.048],[0,0]],"o":[[0,0],[-7.047,0],[0,0],[0,-7.048],[0,0],[7.048,0],[0,0],[0,7.048]],"v":[[78.779,133.989],[-78.779,133.989],[-91.539,121.227],[-91.539,-121.228],[-78.779,-133.989],[78.779,-133.989],[91.539,-121.228],[91.539,121.227]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 2","c":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[1,1,1],"t":2},{"s":[0.9529,0.9569,0.9647],"t":14}],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[115.102,134.239],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":17},{"ty":4,"nm":"Circle","sr":1,"st":0,"op":144,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[168.508,168.508,0],"ix":1},"s":{"a":0,"k":[115.452,115.452,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[246.09,247.079,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,-92.927],[92.927,0],[0,92.926],[-92.927,0]],"o":[[0,92.926],[-92.927,0],[0,-92.927],[92.927,0]],"v":[[168.258,0],[-0.001,168.258],[-168.258,0],[-0.001,-168.258]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9647,0.9725,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[168.508,168.508],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":18}],"v":"5.4.4","fr":24,"op":72,"ip":0,"assets":[]}
|
|
@@ -149,6 +149,16 @@ const DebugNavigationPanel = () => {
|
|
|
149
149
|
appContext.currentWorkflowStep = step;
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
+
}, {
|
|
153
|
+
screen: 'AddressCaptureScreen',
|
|
154
|
+
label: 'Address',
|
|
155
|
+
setupDemo: () => {
|
|
156
|
+
setupDemoSession(true);
|
|
157
|
+
const step = appContext.workflowSteps?.find(s => s.type === 'ADDRESS_CAPTURE');
|
|
158
|
+
if (step) {
|
|
159
|
+
appContext.currentWorkflowStep = step;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
152
162
|
}, {
|
|
153
163
|
screen: 'LivenessDetectionScreen',
|
|
154
164
|
label: 'Liveness',
|
|
@@ -11,7 +11,7 @@ import { debugError, debugLog } from "../Libs/debug.utils.js";
|
|
|
11
11
|
import { eidReader } from "../EIDReader/eidReader.js";
|
|
12
12
|
import NativeProgressBar from "./NativeProgressBar.js";
|
|
13
13
|
import { buildDocumentName } from "../Libs/mrzTransliteration.js";
|
|
14
|
-
import { recoverVizName, recognizeVizBlocks } from "../Libs/vizName.js";
|
|
14
|
+
import { recoverVizName, recognizeVizBlocks, voteVizName } from "../Libs/vizName.js";
|
|
15
15
|
import { normalizeFromMRZInfo, normalizeFromMRZFields } from "../Libs/documentDataNormalizer.js";
|
|
16
16
|
import { compareChipToViz } from "../Libs/chipVizMatch.js";
|
|
17
17
|
import { useTranslation } from 'react-i18next';
|
|
@@ -38,6 +38,7 @@ const EIDScanner = ({
|
|
|
38
38
|
documentType,
|
|
39
39
|
vizMrzFields,
|
|
40
40
|
vizImage,
|
|
41
|
+
vizImages,
|
|
41
42
|
onScanSuccess,
|
|
42
43
|
isNFCSupported
|
|
43
44
|
}) => {
|
|
@@ -256,11 +257,22 @@ const EIDScanner = ({
|
|
|
256
257
|
// VIZ candidate when DG11 did NOT provide a clean two-part name.
|
|
257
258
|
const dg11HasBothNames = !!passportData.dg11FirstName && !!passportData.dg11LastName;
|
|
258
259
|
// Only OCR the VIZ when DG11 didn't already provide a clean name.
|
|
259
|
-
|
|
260
|
-
|
|
260
|
+
// OCR every available image (data page + ID front/back) and fuse the
|
|
261
|
+
// recovered names by majority vote, so one noisy OCR pass can't corrupt
|
|
262
|
+
// the diacritic name. De-dup images so the same capture isn't voted
|
|
263
|
+
// twice (which would let a single pass outvote the others).
|
|
264
|
+
const vizSources = !dg11HasBothNames ? Array.from(new Set([vizImage, ...(vizImages ?? [])].filter(Boolean))) : [];
|
|
265
|
+
const perImage = await Promise.all(vizSources.map(async img => {
|
|
266
|
+
const blocks = await recognizeVizBlocks(img);
|
|
267
|
+
if (!blocks.length) return null;
|
|
268
|
+
return recoverVizName(blocks, documentData.firstName, documentData.lastName, Math.max(...blocks.map(b => b.top)), documentData.issuingCountry);
|
|
269
|
+
}));
|
|
270
|
+
const candidates = perImage.filter(c => c !== null);
|
|
271
|
+
const viz = candidates.length ? voteVizName(candidates) : null;
|
|
261
272
|
debugLog('VIZName', 'eid recovery', {
|
|
262
273
|
dg11HasBothNames,
|
|
263
|
-
|
|
274
|
+
sources: vizSources.length,
|
|
275
|
+
candidates: candidates.length,
|
|
264
276
|
matchedFirst: !!viz?.vizFirst,
|
|
265
277
|
matchedLast: !!viz?.vizLast,
|
|
266
278
|
mismatch: viz?.mismatch ?? false
|
|
@@ -22,7 +22,7 @@ import { speak, resetLastMessage } from "../Libs/tts.utils.js";
|
|
|
22
22
|
import AppContext from "../Contexts/AppContext.js";
|
|
23
23
|
import { useTheme } from "../Contexts/ThemeContext.js";
|
|
24
24
|
import DebugOverlay, { TestModePanel } from "./DebugOverlay.js";
|
|
25
|
-
import { getStatusMessage, getFrameToScreenTransform, transformBoundsToScreen, getScanAreaBounds, angleBetweenPoints, detectDocumentType, determineDocumentTypeToSet, areMRZFieldsEqual, hasRequiredMRZFields, validateFacePosition, isPassportDocumentCode } from "./IdentityDocumentCamera.utils.js";
|
|
25
|
+
import { getStatusMessage, getFrameToScreenTransform, transformBoundsToScreen, getScanAreaBounds, angleBetweenPoints, detectDocumentType, determineDocumentTypeToSet, areMRZFieldsEqual, hasRequiredMRZFields, validateFacePosition, isPassportDocumentCode, StableFlag } from "./IdentityDocumentCamera.utils.js";
|
|
26
26
|
import { handlePassportFlow, handleIDFrontFlow, handleIDBackFlow, getNextStepAfterHologram } from "./IdentityDocumentCamera.flows.js";
|
|
27
27
|
import { HOLOGRAM_IMAGE_COUNT, HOLOGRAM_DETECTION_THRESHOLD, HOLOGRAM_DETECTION_RETRY_COUNT, HOLOGRAM_CAPTURE_INTERVAL, HOLOGRAM_MAX_FRAMES_WITHOUT_FACE, MIN_BRIGHTNESS_THRESHOLD, MAX_BRIGHTNESS_THRESHOLD, FACE_EDGE_MARGIN_PERCENT, MAX_CONSECUTIVE_QUALITY_FAILURES, REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS, HELP_PROMPT_DELAY_MS, SIGNATURE_TEXT_REGEX, MRZ_BLOCK_PATTERN, MIN_CARD_FACE_SIZE_PERCENT } from "./IdentityDocumentCamera.constants.js";
|
|
28
28
|
|
|
@@ -99,6 +99,31 @@ const IdentityDocumentCamera = ({
|
|
|
99
99
|
const consecutiveQualityFailures = useRef(0);
|
|
100
100
|
const mrzDetectionCurrentRetryCount = useRef(0);
|
|
101
101
|
|
|
102
|
+
// Hysteresis for the on-screen quality guidance. Brightness/blur/glare are
|
|
103
|
+
// measured every frame; feeding the raw per-frame booleans straight into
|
|
104
|
+
// state made the guidance strobe (a single blurry hand-shake frame flipped
|
|
105
|
+
// "hold steady" on and off). These smooth each flag: it appears after a few
|
|
106
|
+
// confirming frames and lingers briefly before clearing, so the message is
|
|
107
|
+
// stable and only reflects a sustained condition. Raise fast (react to real
|
|
108
|
+
// problems), clear slow (don't flicker off on one good frame).
|
|
109
|
+
const brightnessLowFlag = useRef(new StableFlag(2, 5));
|
|
110
|
+
const blurryFlag = useRef(new StableFlag(3, 6));
|
|
111
|
+
const glareFlag = useRef(new StableFlag(2, 5));
|
|
112
|
+
// Glare is asserted by accept-time helpers that run inside the (separately
|
|
113
|
+
// scoped) handleFaceAndText callback, so it can't flow through a handler
|
|
114
|
+
// local. This ref is cleared at the top of each frame and set true if any
|
|
115
|
+
// glare check in that frame found glare; the handler reads it to feed
|
|
116
|
+
// `glareFlag`. `hadGlareText` distinguishes "card present but glaring" from
|
|
117
|
+
// an empty view so the warning only shows over an actual document.
|
|
118
|
+
const rawGlareRef = useRef(false);
|
|
119
|
+
// Whether a document was visible in the PREVIOUS frame (recognized text,
|
|
120
|
+
// barcode, or face). The quality guidance is stabilized BEFORE the quality
|
|
121
|
+
// gate (so dark/blurry frames — which the gate early-returns on — can still
|
|
122
|
+
// raise their warnings), but document-presence is only known LATER in the
|
|
123
|
+
// frame, so blur/glare are gated on the previous frame's value. One-frame lag
|
|
124
|
+
// at ~8fps is imperceptible and the hysteresis smooths it.
|
|
125
|
+
const documentPresentRef = useRef(false);
|
|
126
|
+
|
|
102
127
|
// MRZ stability tracking - require consistent valid reads
|
|
103
128
|
const lastValidMRZText = useRef(null);
|
|
104
129
|
const lastValidMRZFields = useRef(null);
|
|
@@ -422,8 +447,13 @@ const IdentityDocumentCamera = ({
|
|
|
422
447
|
100,
|
|
423
448
|
// Full face height
|
|
424
449
|
MAX_BRIGHTNESS_THRESHOLD);
|
|
425
|
-
|
|
426
|
-
|
|
450
|
+
// OR-accumulate into the per-frame glare signal (reset at frame top); the
|
|
451
|
+
// frame handler feeds it through the stabilized UI flag. Also record the
|
|
452
|
+
// diagnostic.
|
|
453
|
+
if (hasGlare) {
|
|
454
|
+
rawGlareRef.current = true;
|
|
455
|
+
diagStats.current.hadGlare = true;
|
|
456
|
+
}
|
|
427
457
|
return hasGlare;
|
|
428
458
|
} catch (error) {
|
|
429
459
|
return false; // Assume no glare on error
|
|
@@ -442,8 +472,13 @@ const IdentityDocumentCamera = ({
|
|
|
442
472
|
try {
|
|
443
473
|
// Check center 80% region for glare (document area)
|
|
444
474
|
const hasGlare = await OpenCVModule.isRectangularRegionBright(image, Math.round(width * 0.1), Math.round(height * 0.1), Math.round(width * 0.8), Math.round(height * 0.8), MAX_BRIGHTNESS_THRESHOLD);
|
|
445
|
-
|
|
446
|
-
|
|
475
|
+
// OR-accumulate into the per-frame glare signal (reset at frame top); the
|
|
476
|
+
// frame handler feeds it through the stabilized UI flag. Also record the
|
|
477
|
+
// diagnostic.
|
|
478
|
+
if (hasGlare) {
|
|
479
|
+
rawGlareRef.current = true;
|
|
480
|
+
diagStats.current.hadGlare = true;
|
|
481
|
+
}
|
|
447
482
|
return hasGlare;
|
|
448
483
|
} catch (error) {
|
|
449
484
|
return false;
|
|
@@ -511,7 +546,16 @@ const IdentityDocumentCamera = ({
|
|
|
511
546
|
}
|
|
512
547
|
validMRZConsecutiveCount.current = 0;
|
|
513
548
|
cachedBarcode.current = null; // Clear cached barcode on step change
|
|
514
|
-
|
|
549
|
+
// Start each step with clean, non-flickering guidance (no stale warning
|
|
550
|
+
// carried over from the previous side).
|
|
551
|
+
brightnessLowFlag.current.reset(false);
|
|
552
|
+
blurryFlag.current.reset(false);
|
|
553
|
+
glareFlag.current.reset(false);
|
|
554
|
+
rawGlareRef.current = false;
|
|
555
|
+
documentPresentRef.current = false;
|
|
556
|
+
setIsBrightnessLow(false);
|
|
557
|
+
setIsFrameBlurry(false);
|
|
558
|
+
setIsGlareDetected(false);
|
|
515
559
|
if (nextStepType !== 'COMPLETED') {
|
|
516
560
|
setTimeout(() => {
|
|
517
561
|
setStatus('SEARCHING');
|
|
@@ -1407,17 +1451,26 @@ const IdentityDocumentCamera = ({
|
|
|
1407
1451
|
} catch (error) {
|
|
1408
1452
|
isRegionBright = isOverallBright;
|
|
1409
1453
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
//
|
|
1414
|
-
//
|
|
1415
|
-
//
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
// frame
|
|
1420
|
-
|
|
1454
|
+
|
|
1455
|
+
// Raw per-frame quality readings. The UI-facing warnings are NOT set from
|
|
1456
|
+
// these directly — they are stabilized (hysteresis) and gated on document
|
|
1457
|
+
// presence at the end of the handler so the guidance doesn't strobe or nag
|
|
1458
|
+
// over an empty view. The raw values still feed the frame-processing
|
|
1459
|
+
// quality gate below unchanged.
|
|
1460
|
+
const rawBrightnessLow = !isRegionBright;
|
|
1461
|
+
if (rawBrightnessLow) diagStats.current.hadLowBrightness = true;
|
|
1462
|
+
|
|
1463
|
+
// Per-frame glare is asserted by the accept-time helpers (checkDocumentGlare
|
|
1464
|
+
// /checkFaceGlare), but those run inside handleFaceAndText which is fired
|
|
1465
|
+
// (async, un-awaited) LATER in this handler — so this frame's glare result
|
|
1466
|
+
// isn't known yet at the stabilization block below. We therefore feed the
|
|
1467
|
+
// stabilizer the PREVIOUS frame's glare signal (captured here before the
|
|
1468
|
+
// reset). At ~8fps the one-frame lag is imperceptible and the hysteresis
|
|
1469
|
+
// smooths it anyway; without this the read would always see false and the
|
|
1470
|
+
// glare warning could never fire. Reset after capturing so a now-clean
|
|
1471
|
+
// view clears (no latch).
|
|
1472
|
+
const prevFrameGlare = rawGlareRef.current;
|
|
1473
|
+
rawGlareRef.current = false;
|
|
1421
1474
|
|
|
1422
1475
|
// Check blur only in center region (area of interest) to avoid false positives
|
|
1423
1476
|
// from iOS depth-of-field background blur
|
|
@@ -1437,10 +1490,9 @@ const IdentityDocumentCamera = ({
|
|
|
1437
1490
|
50 // threshold
|
|
1438
1491
|
);
|
|
1439
1492
|
isNotBlurry = !isBlurry;
|
|
1440
|
-
setIsFrameBlurry(isBlurry);
|
|
1441
1493
|
if (isBlurry) diagStats.current.hadBlurryFrames = true;
|
|
1442
1494
|
} catch (error) {
|
|
1443
|
-
|
|
1495
|
+
isBlurry = false;
|
|
1444
1496
|
}
|
|
1445
1497
|
|
|
1446
1498
|
// Flush accumulated camera stats into the diagnostics collector.
|
|
@@ -1465,6 +1517,29 @@ const IdentityDocumentCamera = ({
|
|
|
1465
1517
|
// Only proceed if image quality is acceptable
|
|
1466
1518
|
const hasAcceptableQuality = isRegionBright && isNotBlurry;
|
|
1467
1519
|
|
|
1520
|
+
// --- Stabilized, presence-gated quality guidance -------------------
|
|
1521
|
+
// MUST run BEFORE the quality-gate early-return below: a dark or blurry
|
|
1522
|
+
// frame is exactly what makes hasAcceptableQuality false, so if this ran
|
|
1523
|
+
// after the gate the "too dark" / "hold steady" warnings could never
|
|
1524
|
+
// raise (their triggering frames would have already returned). Blur/glare
|
|
1525
|
+
// are gated on document presence; brightness shows regardless, since a
|
|
1526
|
+
// dark room is worth flagging before the card is even framed.
|
|
1527
|
+
//
|
|
1528
|
+
// Presence combines the previous frame's confirmed value (documentPresent-
|
|
1529
|
+
// Ref, set post-detection) with a CHEAP raw-frame proxy read directly off
|
|
1530
|
+
// the native ML Kit results (faces/text/barcodes are on `frame` before any
|
|
1531
|
+
// processing). Without the raw proxy, a document that is blurry/dark from
|
|
1532
|
+
// its very first framed moment would keep failing the quality gate and
|
|
1533
|
+
// early-returning before documentPresentRef is ever set true — suppressing
|
|
1534
|
+
// its blur/glare hints for ~30 frames. The proxy lets the hint fire on the
|
|
1535
|
+
// first framed frame.
|
|
1536
|
+
const rawDocumentPresent = (frame.faces?.length ?? 0) > 0 || (frame.textBlocks?.length ?? 0) > 0 || (frame.barcodes?.length ?? 0) > 0;
|
|
1537
|
+
const documentWasPresent = documentPresentRef.current || rawDocumentPresent;
|
|
1538
|
+
setIsBrightnessLow(brightnessLowFlag.current.push(rawBrightnessLow));
|
|
1539
|
+
setIsFrameBlurry(blurryFlag.current.push(documentWasPresent && isBlurry));
|
|
1540
|
+
setIsGlareDetected(glareFlag.current.push(documentWasPresent && prevFrameGlare));
|
|
1541
|
+
// -------------------------------------------------------------------
|
|
1542
|
+
|
|
1468
1543
|
// Store quality metrics in ref for access in handleFaceAndText callback
|
|
1469
1544
|
lastFrameQuality.current = {
|
|
1470
1545
|
hasAcceptableQuality,
|
|
@@ -1698,6 +1773,12 @@ const IdentityDocumentCamera = ({
|
|
|
1698
1773
|
setAllElementsDetected(false);
|
|
1699
1774
|
}
|
|
1700
1775
|
|
|
1776
|
+
// Record whether a document is present THIS frame (recognized text, a
|
|
1777
|
+
// barcode, or a face) so next frame's blur/glare gating can read it.
|
|
1778
|
+
// The stabilized guidance itself is set earlier, before the quality
|
|
1779
|
+
// gate — see the block above `hasAcceptableQuality`.
|
|
1780
|
+
documentPresentRef.current = textBlocks.length > 0 || barcodes.length > 0 || detectedFaces.length > 0 || cachedBarcode.current !== null;
|
|
1781
|
+
|
|
1701
1782
|
// Check if detected elements are inside the scan area
|
|
1702
1783
|
const {
|
|
1703
1784
|
scanLeft,
|
|
@@ -4,6 +4,53 @@ import { Dimensions } from 'react-native';
|
|
|
4
4
|
import { SIGNATURE_REGEX, PASSPORT_MRZ_PATTERN, MRZ_DENSE_PATTERN } from "./IdentityDocumentCamera.constants.js";
|
|
5
5
|
import { debugLog, isDebugEnabled } from "../Libs/debug.utils.js";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Debounced boolean with hysteresis, for stabilizing per-frame quality flags.
|
|
9
|
+
*
|
|
10
|
+
* The camera measures brightness/blur/glare on EVERY frame, and a single noisy
|
|
11
|
+
* frame (a momentary blur while the hand moves, one dark frame) would otherwise
|
|
12
|
+
* flip the on-screen guidance on and off many times a second — a distracting
|
|
13
|
+
* flicker. This smooths a raw per-frame boolean so it only turns ON after
|
|
14
|
+
* `raiseFrames` consecutive true readings and only turns OFF after `clearFrames`
|
|
15
|
+
* consecutive false readings; isolated opposite frames are absorbed. Asymmetric
|
|
16
|
+
* by design: warnings should appear promptly once a problem is real (small
|
|
17
|
+
* `raiseFrames`) but linger briefly so they don't strobe (larger `clearFrames`).
|
|
18
|
+
*/
|
|
19
|
+
export class StableFlag {
|
|
20
|
+
streak = 0;
|
|
21
|
+
constructor(raiseFrames, clearFrames, initial = false) {
|
|
22
|
+
this.raiseFrames = raiseFrames;
|
|
23
|
+
this.clearFrames = clearFrames;
|
|
24
|
+
this.value = initial;
|
|
25
|
+
this.lastRaw = initial;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Feed one raw measurement; returns the current stabilized value. */
|
|
29
|
+
push(raw) {
|
|
30
|
+
if (raw === this.lastRaw) {
|
|
31
|
+
this.streak++;
|
|
32
|
+
} else {
|
|
33
|
+
this.streak = 1;
|
|
34
|
+
this.lastRaw = raw;
|
|
35
|
+
}
|
|
36
|
+
if (raw !== this.value) {
|
|
37
|
+
const needed = raw ? this.raiseFrames : this.clearFrames;
|
|
38
|
+
if (this.streak >= needed) this.value = raw;
|
|
39
|
+
}
|
|
40
|
+
return this.value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Force the stabilized value (e.g. on step change) without waiting. */
|
|
44
|
+
reset(value) {
|
|
45
|
+
this.value = value;
|
|
46
|
+
this.lastRaw = value;
|
|
47
|
+
this.streak = 0;
|
|
48
|
+
}
|
|
49
|
+
get current() {
|
|
50
|
+
return this.value;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
7
54
|
/**
|
|
8
55
|
* Checks if a document code represents an ID card (TD1/TD2).
|
|
9
56
|
* Per ICAO 9303, valid ID card codes start with 'I', 'A', or 'C'.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* On-device, best-effort check that a captured address-proof photo actually
|
|
5
|
+
* looks like a document containing the holder's NAME and ADDRESS information.
|
|
6
|
+
*
|
|
7
|
+
* MASAK Tebliğ No. 32 Art. 4/C(1)(d) requires an OPERATOR to manually review the
|
|
8
|
+
* address proof, so this is only a soft quality gate: it nudges the user to
|
|
9
|
+
* retake an obviously-wrong photo (a selfie, a blank wall, the wrong document)
|
|
10
|
+
* before it wastes an operator's time. A negative result NEVER blocks the user.
|
|
11
|
+
*
|
|
12
|
+
* Input is the OCR text recognised on the captured proof photo (full image,
|
|
13
|
+
* via the native still-image recognizer), plus the holder's name and any chip
|
|
14
|
+
* (DG11) address tokens — no extra ML models, no network, works offline.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { foldDiacritics } from "./vizName.js";
|
|
18
|
+
/**
|
|
19
|
+
* Address keywords across the languages this SDK targets (TR + common EU/EN).
|
|
20
|
+
* Matched as whole words after normalisation. A Set so membership is O(1) and
|
|
21
|
+
* duplicates collapse. Deliberately broad — recall over precision, since the
|
|
22
|
+
* operator makes the real decision — BUT we exclude very short tokens
|
|
23
|
+
* ('NO','IL','ST','SK','RD') that collide with everyday non-address text and
|
|
24
|
+
* would make the address signal fire on almost any document.
|
|
25
|
+
*/
|
|
26
|
+
const ADDRESS_KEYWORDS = new Set([
|
|
27
|
+
// Turkish
|
|
28
|
+
'MAH', 'MAHALLE', 'MAHALLESI', 'CAD', 'CADDE', 'CADDESI', 'SOK', 'SOKAK', 'APT', 'DAIRE', 'BINA', 'BLOK', 'KAPI', 'ILCE', 'SEHIR', 'POSTA', 'ADRES', 'BULVAR', 'BULVARI', 'SITE',
|
|
29
|
+
// English / generic
|
|
30
|
+
'STREET', 'AVENUE', 'AVE', 'ROAD', 'LANE', 'BLVD', 'BOULEVARD', 'APARTMENT', 'SUITE', 'FLOOR', 'BUILDING', 'CITY', 'TOWN', 'POSTAL', 'POSTCODE', 'ZIP', 'ADDRESS', 'DISTRICT', 'PROVINCE',
|
|
31
|
+
// German (passports/utility bills are common)
|
|
32
|
+
'STRASSE', 'PLATZ', 'GASSE', 'STADT', 'PLZ', 'ADRESSE']);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Strip diacritics + uppercase so "Şişli" ~ "SISLI" and OCR variants align.
|
|
36
|
+
* Shares the SDK's canonical diacritic fold ({@link foldDiacritics}) so the
|
|
37
|
+
* address path and the VIZ-name path stay in sync (ß→SS, ø→O, ł→L, …).
|
|
38
|
+
*/
|
|
39
|
+
function normalize(input) {
|
|
40
|
+
return foldDiacritics(input);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Split into alphabetic / numeric tokens, dropping punctuation. */
|
|
44
|
+
function tokenize(input) {
|
|
45
|
+
return normalize(input).split(/[^A-Z0-9]+/).filter(t => t.length > 0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A postal-code-like run, REQUIRED to be flanked by address-ish context to
|
|
50
|
+
* avoid matching invoice/account/order numbers. We accept a 4–6 digit run only
|
|
51
|
+
* when it is not part of a longer digit run (so account numbers like 1234567890
|
|
52
|
+
* don't count) — `\b\d{4,6}\b` already excludes longer runs.
|
|
53
|
+
*/
|
|
54
|
+
const POSTAL_RE = /\b\d{4,6}\b/;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Levenshtein distance ≤ 1 check (single insert/delete/substitute), used to
|
|
58
|
+
* tolerate one OCR slip in a name token (e.g. "SAYGI" read as "SAYGL").
|
|
59
|
+
*/
|
|
60
|
+
function withinEditDistance1(a, b) {
|
|
61
|
+
if (a === b) return true;
|
|
62
|
+
const la = a.length;
|
|
63
|
+
const lb = b.length;
|
|
64
|
+
if (Math.abs(la - lb) > 1) return false;
|
|
65
|
+
// Substitution (equal length): at most one differing char.
|
|
66
|
+
if (la === lb) {
|
|
67
|
+
let diffs = 0;
|
|
68
|
+
for (let i = 0; i < la; i++) {
|
|
69
|
+
if (a[i] !== b[i] && ++diffs > 1) return false;
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
// Insertion/deletion (length differs by 1): the shorter is a subsequence
|
|
74
|
+
// of the longer with exactly one extra char.
|
|
75
|
+
const [short, long] = la < lb ? [a, b] : [b, a];
|
|
76
|
+
let i = 0;
|
|
77
|
+
let j = 0;
|
|
78
|
+
let skips = 0;
|
|
79
|
+
while (i < short.length && j < long.length) {
|
|
80
|
+
if (short[i] === long[j]) {
|
|
81
|
+
i++;
|
|
82
|
+
j++;
|
|
83
|
+
} else if (++skips > 1) {
|
|
84
|
+
return false;
|
|
85
|
+
} else {
|
|
86
|
+
j++;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Assess whether the OCR text from a captured proof plausibly contains the
|
|
94
|
+
* holder's name and an address. Best-effort and tolerant of OCR noise.
|
|
95
|
+
*/
|
|
96
|
+
export function assessAddressProof(recognizedText, ctx) {
|
|
97
|
+
const text = (recognizedText ?? '').trim();
|
|
98
|
+
if (!text) {
|
|
99
|
+
// No text recognised at all (blank/sideways/too blurry) — fail soft.
|
|
100
|
+
return {
|
|
101
|
+
hasName: false,
|
|
102
|
+
hasAddress: false,
|
|
103
|
+
ok: false
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const normText = normalize(text);
|
|
107
|
+
const tokens = tokenize(text);
|
|
108
|
+
const textTokens = new Set(tokens);
|
|
109
|
+
|
|
110
|
+
// --- Name: a name part (>=3 chars) appears as a token in the OCR text,
|
|
111
|
+
// tolerating a single OCR slip (edit distance ≤1) on tokens of 4+ chars
|
|
112
|
+
// so the holder's own document isn't flagged over one misread letter. ---
|
|
113
|
+
const nameTokens = ctx.nameParts.flatMap(p => p ? tokenize(p) : []).filter(t => t.length >= 3 && !/^\d+$/.test(t));
|
|
114
|
+
const hasName = nameTokens.some(n => textTokens.has(n) || n.length >= 4 && tokens.some(t => withinEditDistance1(n, t)));
|
|
115
|
+
|
|
116
|
+
// --- Address: postal code OR an address keyword OR overlap with the chip
|
|
117
|
+
// address tokens (a strong, holder-specific signal). ---
|
|
118
|
+
const hasPostal = POSTAL_RE.test(normText);
|
|
119
|
+
const hasKeyword = tokens.some(t => ADDRESS_KEYWORDS.has(t));
|
|
120
|
+
const chipTokens = (ctx.chipAddress ? tokenize(ctx.chipAddress) : []).filter(t => t.length >= 3);
|
|
121
|
+
const chipOverlap = chipTokens.filter(t => textTokens.has(t)).length;
|
|
122
|
+
const hasChipMatch = chipOverlap >= 2; // ≥2 chip-address words present
|
|
123
|
+
|
|
124
|
+
const hasAddress = hasPostal || hasKeyword || hasChipMatch;
|
|
125
|
+
return {
|
|
126
|
+
hasName,
|
|
127
|
+
hasAddress,
|
|
128
|
+
ok: hasName && hasAddress
|
|
129
|
+
};
|
|
130
|
+
}
|