@trustchex/react-native-sdk 1.512.1 → 1.513.1

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 (58) hide show
  1. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +9 -2
  2. package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +54 -0
  3. package/ios/Camera/TrustchexCameraView.swift +6 -2
  4. package/ios/OpenCV/OpenCVModule.mm +59 -0
  5. package/lib/module/Screens/Dynamic/AddressCaptureScreen.js +427 -112
  6. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +9 -4
  7. package/lib/module/Screens/Static/ResultScreen.js +46 -0
  8. package/lib/module/Shared/Animations/address-scan.json +1 -0
  9. package/lib/module/Shared/Components/DebugNavigationPanel.js +10 -0
  10. package/lib/module/Shared/Components/EIDScanner.js +16 -4
  11. package/lib/module/Shared/Components/IdentityDocumentCamera.js +100 -19
  12. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +47 -0
  13. package/lib/module/Shared/Libs/addressProofHeuristic.js +130 -0
  14. package/lib/module/Shared/Libs/demo.utils.js +4 -0
  15. package/lib/module/Shared/Libs/sendDiagnosticReport.js +47 -1
  16. package/lib/module/Shared/Libs/vizName.js +138 -16
  17. package/lib/module/Translation/Resources/en.js +16 -3
  18. package/lib/module/Translation/Resources/tr.js +16 -3
  19. package/lib/module/version.js +1 -1
  20. package/lib/typescript/src/Screens/Dynamic/AddressCaptureScreen.d.ts.map +1 -1
  21. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  22. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  23. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  24. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts +8 -3
  25. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  26. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  27. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +25 -0
  28. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
  29. package/lib/typescript/src/Shared/Libs/addressProofHeuristic.d.ts +34 -0
  30. package/lib/typescript/src/Shared/Libs/addressProofHeuristic.d.ts.map +1 -0
  31. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  32. package/lib/typescript/src/Shared/Libs/sendDiagnosticReport.d.ts.map +1 -1
  33. package/lib/typescript/src/Shared/Libs/vizName.d.ts +31 -6
  34. package/lib/typescript/src/Shared/Libs/vizName.d.ts.map +1 -1
  35. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +0 -5
  36. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  37. package/lib/typescript/src/Translation/Resources/en.d.ts +13 -0
  38. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  39. package/lib/typescript/src/Translation/Resources/tr.d.ts +13 -0
  40. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  41. package/lib/typescript/src/version.d.ts +1 -1
  42. package/package.json +1 -1
  43. package/src/Screens/Dynamic/AddressCaptureScreen.tsx +434 -75
  44. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +14 -4
  45. package/src/Screens/Static/ResultScreen.tsx +59 -0
  46. package/src/Shared/Animations/address-scan.json +1 -0
  47. package/src/Shared/Components/DebugNavigationPanel.tsx +13 -0
  48. package/src/Shared/Components/EIDScanner.tsx +38 -12
  49. package/src/Shared/Components/IdentityDocumentCamera.tsx +109 -17
  50. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +53 -0
  51. package/src/Shared/Libs/addressProofHeuristic.ts +197 -0
  52. package/src/Shared/Libs/demo.utils.ts +5 -0
  53. package/src/Shared/Libs/sendDiagnosticReport.ts +65 -7
  54. package/src/Shared/Libs/vizName.ts +158 -20
  55. package/src/Shared/Types/identificationInfo.ts +0 -5
  56. package/src/Translation/Resources/en.ts +28 -3
  57. package/src/Translation/Resources/tr.ts +28 -3
  58. package/src/version.ts +1 -1
@@ -1147,6 +1147,39 @@ const ResultScreen = () => {
1147
1147
  </ScrollView>
1148
1148
  </>
1149
1149
  )}
1150
+
1151
+ {/* MASAK 4/C(1)(d): captured address + proof-document image.
1152
+ Independent of the scanned document, so it renders even when
1153
+ the flow jumps straight to ADDRESS_CAPTURE. */}
1154
+ {appContext.identificationInfo.address &&
1155
+ (appContext.identificationInfo.address.addressText ||
1156
+ appContext.identificationInfo.address.proofImage) && (
1157
+ <View style={styles.addressSection}>
1158
+ <Text style={styles.mrzTextLabel}>
1159
+ {t('addressCaptureScreen.addressLabel')}
1160
+ </Text>
1161
+ {appContext.identificationInfo.address.addressText ? (
1162
+ <Text style={styles.addressText}>
1163
+ {appContext.identificationInfo.address.addressText}
1164
+ </Text>
1165
+ ) : null}
1166
+ {appContext.identificationInfo.address.proofImage ? (
1167
+ <>
1168
+ <Text style={styles.addressProofLabel}>
1169
+ {t('addressCaptureScreen.proofLabel')}
1170
+ </Text>
1171
+ <Image
1172
+ style={styles.addressProofImage}
1173
+ resizeMode="contain"
1174
+ source={{
1175
+ uri: `data:image/jpeg;base64,${appContext.identificationInfo.address.proofImage}`,
1176
+ }}
1177
+ />
1178
+ </>
1179
+ ) : null}
1180
+ </View>
1181
+ )}
1182
+
1150
1183
  {appContext.identificationInfo.livenessDetection?.videoPath && (
1151
1184
  <>
1152
1185
  <Text style={styles.sectionHeader}>
@@ -1472,6 +1505,32 @@ const styles = StyleSheet.create({
1472
1505
  lineHeight: 14,
1473
1506
  color: '#1F2937',
1474
1507
  },
1508
+ addressSection: {
1509
+ marginTop: 16,
1510
+ padding: 12,
1511
+ backgroundColor: '#F3F4F6',
1512
+ borderRadius: 8,
1513
+ borderWidth: 1,
1514
+ borderColor: '#D1D5DB',
1515
+ },
1516
+ addressText: {
1517
+ fontSize: 14,
1518
+ lineHeight: 20,
1519
+ color: '#1F2937',
1520
+ },
1521
+ addressProofLabel: {
1522
+ fontSize: 12,
1523
+ fontWeight: '600',
1524
+ color: '#6B7280',
1525
+ marginTop: 12,
1526
+ marginBottom: 8,
1527
+ },
1528
+ addressProofImage: {
1529
+ width: '100%',
1530
+ // Portrait frame so the whole document shows upright, letterboxed not cropped.
1531
+ aspectRatio: 3 / 4,
1532
+ borderRadius: 8,
1533
+ },
1475
1534
  trustchexLogo: {
1476
1535
  width: 150,
1477
1536
  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":[]}
@@ -210,6 +210,19 @@ const DebugNavigationPanel = () => {
210
210
  }
211
211
  },
212
212
  },
213
+ {
214
+ screen: 'AddressCaptureScreen',
215
+ label: 'Address',
216
+ setupDemo: () => {
217
+ setupDemoSession(true);
218
+ const step = appContext.workflowSteps?.find(
219
+ (s) => s.type === 'ADDRESS_CAPTURE'
220
+ );
221
+ if (step) {
222
+ appContext.currentWorkflowStep = step;
223
+ }
224
+ },
225
+ },
213
226
  {
214
227
  screen: 'LivenessDetectionScreen',
215
228
  label: 'Liveness',
@@ -19,7 +19,11 @@ import { eidReader } from '../EIDReader/eidReader';
19
19
  import NativeProgressBar from './NativeProgressBar';
20
20
  import type { MRZFields } from '../Types/mrzFields';
21
21
  import { buildDocumentName } from '../Libs/mrzTransliteration';
22
- import { recoverVizName, recognizeVizBlocks } from '../Libs/vizName';
22
+ import {
23
+ recoverVizName,
24
+ recognizeVizBlocks,
25
+ voteVizName,
26
+ } from '../Libs/vizName';
23
27
  import type { DocumentName } from '../Types/documentReadResult';
24
28
  import {
25
29
  normalizeFromMRZInfo,
@@ -59,11 +63,16 @@ interface eIDScannerProps {
59
63
  */
60
64
  vizMrzFields?: MRZFields;
61
65
  /**
62
- * Full data-page image from the EID step's camera scan. A one-shot OCR pass
63
- * recovers the diacritic printed name when the chip's DG11 is absent or
66
+ * Full data-page image(s) from the EID step's camera scan. Each is OCR'd to
67
+ * recover the diacritic printed name when the chip's DG11 is absent or
64
68
  * one-sided — DG11 (the authenticated chip's own name) still takes precedence.
69
+ * When more than one image is available (e.g. passport page + ID front/back),
70
+ * the recovered names are fused by majority vote so a single noisy OCR pass
71
+ * can't corrupt the result.
65
72
  */
66
73
  vizImage?: string;
74
+ /** Additional VIZ images to OCR and vote over, alongside `vizImage`. */
75
+ vizImages?: (string | undefined)[];
67
76
  onScanSuccess?: (
68
77
  mrzInfo: MRZFields,
69
78
  faceImage: string,
@@ -79,6 +88,7 @@ const EIDScanner = ({
79
88
  documentType,
80
89
  vizMrzFields,
81
90
  vizImage,
91
+ vizImages,
82
92
  onScanSuccess,
83
93
  isNFCSupported,
84
94
  }: eIDScannerProps) => {
@@ -354,20 +364,36 @@ const EIDScanner = ({
354
364
  const dg11HasBothNames =
355
365
  !!passportData.dg11FirstName && !!passportData.dg11LastName;
356
366
  // Only OCR the VIZ when DG11 didn't already provide a clean name.
357
- const vizBlocks = !dg11HasBothNames
358
- ? await recognizeVizBlocks(vizImage)
367
+ // OCR every available image (data page + ID front/back) and fuse the
368
+ // recovered names by majority vote, so one noisy OCR pass can't corrupt
369
+ // the diacritic name. De-dup images so the same capture isn't voted
370
+ // twice (which would let a single pass outvote the others).
371
+ const vizSources = !dg11HasBothNames
372
+ ? Array.from(
373
+ new Set([vizImage, ...(vizImages ?? [])].filter(Boolean))
374
+ )
359
375
  : [];
360
- const viz = vizBlocks.length
361
- ? recoverVizName(
362
- vizBlocks,
376
+ const perImage = await Promise.all(
377
+ vizSources.map(async (img) => {
378
+ const blocks = await recognizeVizBlocks(img);
379
+ if (!blocks.length) return null;
380
+ return recoverVizName(
381
+ blocks,
363
382
  documentData.firstName,
364
383
  documentData.lastName,
365
- Math.max(...vizBlocks.map((b) => b.top))
366
- )
367
- : null;
384
+ Math.max(...blocks.map((b) => b.top)),
385
+ documentData.issuingCountry
386
+ );
387
+ })
388
+ );
389
+ const candidates = perImage.filter(
390
+ (c): c is NonNullable<typeof c> => c !== null
391
+ );
392
+ const viz = candidates.length ? voteVizName(candidates) : null;
368
393
  debugLog('VIZName', 'eid recovery', {
369
394
  dg11HasBothNames,
370
- vizBlockCount: vizBlocks.length,
395
+ sources: vizSources.length,
396
+ candidates: candidates.length,
371
397
  matchedFirst: !!viz?.vizFirst,
372
398
  matchedLast: !!viz?.vizLast,
373
399
  mismatch: viz?.mismatch ?? false,
@@ -50,6 +50,7 @@ import {
50
50
  hasRequiredMRZFields,
51
51
  validateFacePosition,
52
52
  isPassportDocumentCode,
53
+ StableFlag,
53
54
  } from './IdentityDocumentCamera.utils';
54
55
  import {
55
56
  handlePassportFlow,
@@ -175,6 +176,31 @@ const IdentityDocumentCamera = ({
175
176
  const consecutiveQualityFailures = useRef(0);
176
177
  const mrzDetectionCurrentRetryCount = useRef(0);
177
178
 
179
+ // Hysteresis for the on-screen quality guidance. Brightness/blur/glare are
180
+ // measured every frame; feeding the raw per-frame booleans straight into
181
+ // state made the guidance strobe (a single blurry hand-shake frame flipped
182
+ // "hold steady" on and off). These smooth each flag: it appears after a few
183
+ // confirming frames and lingers briefly before clearing, so the message is
184
+ // stable and only reflects a sustained condition. Raise fast (react to real
185
+ // problems), clear slow (don't flicker off on one good frame).
186
+ const brightnessLowFlag = useRef(new StableFlag(2, 5));
187
+ const blurryFlag = useRef(new StableFlag(3, 6));
188
+ const glareFlag = useRef(new StableFlag(2, 5));
189
+ // Glare is asserted by accept-time helpers that run inside the (separately
190
+ // scoped) handleFaceAndText callback, so it can't flow through a handler
191
+ // local. This ref is cleared at the top of each frame and set true if any
192
+ // glare check in that frame found glare; the handler reads it to feed
193
+ // `glareFlag`. `hadGlareText` distinguishes "card present but glaring" from
194
+ // an empty view so the warning only shows over an actual document.
195
+ const rawGlareRef = useRef(false);
196
+ // Whether a document was visible in the PREVIOUS frame (recognized text,
197
+ // barcode, or face). The quality guidance is stabilized BEFORE the quality
198
+ // gate (so dark/blurry frames — which the gate early-returns on — can still
199
+ // raise their warnings), but document-presence is only known LATER in the
200
+ // frame, so blur/glare are gated on the previous frame's value. One-frame lag
201
+ // at ~8fps is imperceptible and the hysteresis smooths it.
202
+ const documentPresentRef = useRef(false);
203
+
178
204
  // MRZ stability tracking - require consistent valid reads
179
205
  const lastValidMRZText = useRef<string | null>(null);
180
206
  const lastValidMRZFields = useRef<any>(null);
@@ -618,8 +644,13 @@ const IdentityDocumentCamera = ({
618
644
  100, // Full face height
619
645
  MAX_BRIGHTNESS_THRESHOLD
620
646
  );
621
- setIsGlareDetected(hasGlare);
622
- if (hasGlare) diagStats.current.hadGlare = true;
647
+ // OR-accumulate into the per-frame glare signal (reset at frame top); the
648
+ // frame handler feeds it through the stabilized UI flag. Also record the
649
+ // diagnostic.
650
+ if (hasGlare) {
651
+ rawGlareRef.current = true;
652
+ diagStats.current.hadGlare = true;
653
+ }
623
654
  return hasGlare;
624
655
  } catch (error) {
625
656
  return false; // Assume no glare on error
@@ -658,8 +689,13 @@ const IdentityDocumentCamera = ({
658
689
  Math.round(height * 0.8),
659
690
  MAX_BRIGHTNESS_THRESHOLD
660
691
  );
661
- setIsGlareDetected(hasGlare);
662
- if (hasGlare) diagStats.current.hadGlare = true;
692
+ // OR-accumulate into the per-frame glare signal (reset at frame top); the
693
+ // frame handler feeds it through the stabilized UI flag. Also record the
694
+ // diagnostic.
695
+ if (hasGlare) {
696
+ rawGlareRef.current = true;
697
+ diagStats.current.hadGlare = true;
698
+ }
663
699
  return hasGlare;
664
700
  } catch (error) {
665
701
  return false;
@@ -740,6 +776,16 @@ const IdentityDocumentCamera = ({
740
776
  }
741
777
  validMRZConsecutiveCount.current = 0;
742
778
  cachedBarcode.current = null; // Clear cached barcode on step change
779
+ // Start each step with clean, non-flickering guidance (no stale warning
780
+ // carried over from the previous side).
781
+ brightnessLowFlag.current.reset(false);
782
+ blurryFlag.current.reset(false);
783
+ glareFlag.current.reset(false);
784
+ rawGlareRef.current = false;
785
+ documentPresentRef.current = false;
786
+ setIsBrightnessLow(false);
787
+ setIsFrameBlurry(false);
788
+ setIsGlareDetected(false);
743
789
 
744
790
  if (nextStepType !== 'COMPLETED') {
745
791
  setTimeout(() => {
@@ -2082,17 +2128,25 @@ const IdentityDocumentCamera = ({
2082
2128
  isRegionBright = isOverallBright;
2083
2129
  }
2084
2130
 
2085
- setIsBrightnessLow(!isRegionBright);
2086
- if (!isRegionBright) diagStats.current.hadLowBrightness = true;
2087
-
2088
- // Reset glare to false at the start of every frame. The glare helpers
2089
- // (checkDocumentGlare/checkFaceGlare) only run at accept-time and set it
2090
- // true when they find glare; without this per-frame clear the flag would
2091
- // latch a "Glare detected" warning would stick over a now-clean view
2092
- // (and, once a face is locked, the face-glare site never runs again so it
2093
- // could never clear at all). The accept-time checks run later in this same
2094
- // frame's handlers and re-assert true if glare is still present.
2095
- setIsGlareDetected(false);
2131
+ // Raw per-frame quality readings. The UI-facing warnings are NOT set from
2132
+ // these directly — they are stabilized (hysteresis) and gated on document
2133
+ // presence at the end of the handler so the guidance doesn't strobe or nag
2134
+ // over an empty view. The raw values still feed the frame-processing
2135
+ // quality gate below unchanged.
2136
+ const rawBrightnessLow = !isRegionBright;
2137
+ if (rawBrightnessLow) diagStats.current.hadLowBrightness = true;
2138
+
2139
+ // Per-frame glare is asserted by the accept-time helpers (checkDocumentGlare
2140
+ // /checkFaceGlare), but those run inside handleFaceAndText which is fired
2141
+ // (async, un-awaited) LATER in this handler — so this frame's glare result
2142
+ // isn't known yet at the stabilization block below. We therefore feed the
2143
+ // stabilizer the PREVIOUS frame's glare signal (captured here before the
2144
+ // reset). At ~8fps the one-frame lag is imperceptible and the hysteresis
2145
+ // smooths it anyway; without this the read would always see false and the
2146
+ // glare warning could never fire. Reset after capturing so a now-clean
2147
+ // view clears (no latch).
2148
+ const prevFrameGlare = rawGlareRef.current;
2149
+ rawGlareRef.current = false;
2096
2150
 
2097
2151
  // Check blur only in center region (area of interest) to avoid false positives
2098
2152
  // from iOS depth-of-field background blur
@@ -2110,10 +2164,9 @@ const IdentityDocumentCamera = ({
2110
2164
  50 // threshold
2111
2165
  );
2112
2166
  isNotBlurry = !isBlurry;
2113
- setIsFrameBlurry(isBlurry);
2114
2167
  if (isBlurry) diagStats.current.hadBlurryFrames = true;
2115
2168
  } catch (error) {
2116
- setIsFrameBlurry(false);
2169
+ isBlurry = false;
2117
2170
  }
2118
2171
 
2119
2172
  // Flush accumulated camera stats into the diagnostics collector.
@@ -2140,6 +2193,35 @@ const IdentityDocumentCamera = ({
2140
2193
  // Only proceed if image quality is acceptable
2141
2194
  const hasAcceptableQuality = isRegionBright && isNotBlurry;
2142
2195
 
2196
+ // --- Stabilized, presence-gated quality guidance -------------------
2197
+ // MUST run BEFORE the quality-gate early-return below: a dark or blurry
2198
+ // frame is exactly what makes hasAcceptableQuality false, so if this ran
2199
+ // after the gate the "too dark" / "hold steady" warnings could never
2200
+ // raise (their triggering frames would have already returned). Blur/glare
2201
+ // are gated on document presence; brightness shows regardless, since a
2202
+ // dark room is worth flagging before the card is even framed.
2203
+ //
2204
+ // Presence combines the previous frame's confirmed value (documentPresent-
2205
+ // Ref, set post-detection) with a CHEAP raw-frame proxy read directly off
2206
+ // the native ML Kit results (faces/text/barcodes are on `frame` before any
2207
+ // processing). Without the raw proxy, a document that is blurry/dark from
2208
+ // its very first framed moment would keep failing the quality gate and
2209
+ // early-returning before documentPresentRef is ever set true — suppressing
2210
+ // its blur/glare hints for ~30 frames. The proxy lets the hint fire on the
2211
+ // first framed frame.
2212
+ const rawDocumentPresent =
2213
+ (frame.faces?.length ?? 0) > 0 ||
2214
+ (frame.textBlocks?.length ?? 0) > 0 ||
2215
+ (frame.barcodes?.length ?? 0) > 0;
2216
+ const documentWasPresent =
2217
+ documentPresentRef.current || rawDocumentPresent;
2218
+ setIsBrightnessLow(brightnessLowFlag.current.push(rawBrightnessLow));
2219
+ setIsFrameBlurry(blurryFlag.current.push(documentWasPresent && isBlurry));
2220
+ setIsGlareDetected(
2221
+ glareFlag.current.push(documentWasPresent && prevFrameGlare)
2222
+ );
2223
+ // -------------------------------------------------------------------
2224
+
2143
2225
  // Store quality metrics in ref for access in handleFaceAndText callback
2144
2226
  lastFrameQuality.current = {
2145
2227
  hasAcceptableQuality,
@@ -2442,6 +2524,16 @@ const IdentityDocumentCamera = ({
2442
2524
  setAllElementsDetected(false);
2443
2525
  }
2444
2526
 
2527
+ // Record whether a document is present THIS frame (recognized text, a
2528
+ // barcode, or a face) so next frame's blur/glare gating can read it.
2529
+ // The stabilized guidance itself is set earlier, before the quality
2530
+ // gate — see the block above `hasAcceptableQuality`.
2531
+ documentPresentRef.current =
2532
+ textBlocks.length > 0 ||
2533
+ barcodes.length > 0 ||
2534
+ detectedFaces.length > 0 ||
2535
+ cachedBarcode.current !== null;
2536
+
2445
2537
  // Check if detected elements are inside the scan area
2446
2538
  const { scanLeft, scanTop, scanRight, scanBottom, isInsideScan } =
2447
2539
  getScanAreaBounds(frame.width, frame.height);
@@ -13,6 +13,59 @@ import {
13
13
  } from './IdentityDocumentCamera.constants';
14
14
  import { debugLog, isDebugEnabled } from '../Libs/debug.utils';
15
15
 
16
+ /**
17
+ * Debounced boolean with hysteresis, for stabilizing per-frame quality flags.
18
+ *
19
+ * The camera measures brightness/blur/glare on EVERY frame, and a single noisy
20
+ * frame (a momentary blur while the hand moves, one dark frame) would otherwise
21
+ * flip the on-screen guidance on and off many times a second — a distracting
22
+ * flicker. This smooths a raw per-frame boolean so it only turns ON after
23
+ * `raiseFrames` consecutive true readings and only turns OFF after `clearFrames`
24
+ * consecutive false readings; isolated opposite frames are absorbed. Asymmetric
25
+ * by design: warnings should appear promptly once a problem is real (small
26
+ * `raiseFrames`) but linger briefly so they don't strobe (larger `clearFrames`).
27
+ */
28
+ export class StableFlag {
29
+ private value: boolean;
30
+ private streak = 0;
31
+ private lastRaw: boolean;
32
+
33
+ constructor(
34
+ private readonly raiseFrames: number,
35
+ private readonly clearFrames: number,
36
+ initial = false
37
+ ) {
38
+ this.value = initial;
39
+ this.lastRaw = initial;
40
+ }
41
+
42
+ /** Feed one raw measurement; returns the current stabilized value. */
43
+ push(raw: boolean): boolean {
44
+ if (raw === this.lastRaw) {
45
+ this.streak++;
46
+ } else {
47
+ this.streak = 1;
48
+ this.lastRaw = raw;
49
+ }
50
+ if (raw !== this.value) {
51
+ const needed = raw ? this.raiseFrames : this.clearFrames;
52
+ if (this.streak >= needed) this.value = raw;
53
+ }
54
+ return this.value;
55
+ }
56
+
57
+ /** Force the stabilized value (e.g. on step change) without waiting. */
58
+ reset(value: boolean): void {
59
+ this.value = value;
60
+ this.lastRaw = value;
61
+ this.streak = 0;
62
+ }
63
+
64
+ get current(): boolean {
65
+ return this.value;
66
+ }
67
+ }
68
+
16
69
  /**
17
70
  * Checks if a document code represents an ID card (TD1/TD2).
18
71
  * Per ICAO 9303, valid ID card codes start with 'I', 'A', or 'C'.