@trustchex/react-native-sdk 1.475.0 → 1.475.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.
|
@@ -297,10 +297,43 @@ const reconstructMRZCandidates = rawText => {
|
|
|
297
297
|
const after = flat.indexOf(l1Source) + l1Source.length;
|
|
298
298
|
l2 = pad(flat.slice(after, after + 30), 30);
|
|
299
299
|
}
|
|
300
|
-
// Line 3 (name): the longest
|
|
301
|
-
// date line
|
|
302
|
-
|
|
303
|
-
|
|
300
|
+
// Line 3 (name): the longest mostly-letters+filler OCR line that is NOT the
|
|
301
|
+
// doc or date line. OCR mangles the name line too — a leading "O" reads as
|
|
302
|
+
// "0", and the trailing "<" fillers read as K/C — so we DON'T require a
|
|
303
|
+
// pure [A-Z<] line (that wrongly drops "0ZCAN<<ERDAL…" entirely). Accept a
|
|
304
|
+
// line that is predominantly letters/fillers, then normalise it.
|
|
305
|
+
const isNameLike = l => {
|
|
306
|
+
if (l === l1Source || l === l2Line || l.length < 5) return false;
|
|
307
|
+
if (!/</.test(l)) return false; // must have at least one filler
|
|
308
|
+
// Must look like "SURNAME<<NAMES…": starts with a letter (or its digit
|
|
309
|
+
// look-alike) and is dominated by letters/fillers, not a numeric record.
|
|
310
|
+
if (!/^[A-Z0-9]/.test(l)) return false;
|
|
311
|
+
const letters = (l.match(/[A-Z]/g) || []).length;
|
|
312
|
+
const digits = (l.match(/[0-9]/g) || []).length;
|
|
313
|
+
// A date/doc record is digit-heavy; a name is letter-heavy.
|
|
314
|
+
return letters >= 3 && letters >= digits;
|
|
315
|
+
};
|
|
316
|
+
// Normalise a name line: digit look-alikes back to letters (O/I/S/B/Z/G),
|
|
317
|
+
// and the filler-letter run misreads (K/C/E/G adjacent to fillers) to "<".
|
|
318
|
+
const normaliseName = l => {
|
|
319
|
+
const digitToLetter = {
|
|
320
|
+
'0': 'O',
|
|
321
|
+
'1': 'I',
|
|
322
|
+
'5': 'S',
|
|
323
|
+
'8': 'B',
|
|
324
|
+
'2': 'Z',
|
|
325
|
+
'6': 'G'
|
|
326
|
+
};
|
|
327
|
+
return l
|
|
328
|
+
// digit look-alikes inside the name → letters
|
|
329
|
+
.replace(/[015862]/g, d => digitToLetter[d] ?? d)
|
|
330
|
+
// a run of filler-letters (K/C/E/G) flanked by fillers or at the tail
|
|
331
|
+
// is the trailing/internal filler region
|
|
332
|
+
.replace(/(?<=<)[KCEG]+(?=<|$)/g, m => '<'.repeat(m.length)).replace(/[KCEG]+$/g, m => '<'.repeat(m.length));
|
|
333
|
+
};
|
|
334
|
+
const nameRuns = ocrLines.filter(isNameLike);
|
|
335
|
+
const bestName = nameRuns.sort((a, b) => b.length - a.length)[0] ?? '';
|
|
336
|
+
const l3 = pad(normaliseName(bestName), 30);
|
|
304
337
|
candidates.push([l1, l2, l3]);
|
|
305
338
|
}
|
|
306
339
|
}
|
|
@@ -798,6 +831,21 @@ const correctedMrzFromParse = result => {
|
|
|
798
831
|
return buffers.map(b => b.join('')).join('\n');
|
|
799
832
|
};
|
|
800
833
|
|
|
834
|
+
/**
|
|
835
|
+
* Heuristic: does a parsed result's name look like it still carries OCR noise
|
|
836
|
+
* (a digit where a name letter belongs, or a run of filler-confusable letters
|
|
837
|
+
* that should have been "<")? Names are letters only, so any digit is noise; a
|
|
838
|
+
* 3+ run of K/C/E/G is very likely misread fillers leaking into the given names.
|
|
839
|
+
*/
|
|
840
|
+
const nameLooksNoisy = r => {
|
|
841
|
+
const name = `${r.fields?.lastName ?? ''} ${r.fields?.firstName ?? ''}`;
|
|
842
|
+
// A DIGIT in a name is unambiguous OCR noise (MRZ names are letters only).
|
|
843
|
+
// A 5+ run of filler-confusable letters is almost certainly misread padding —
|
|
844
|
+
// real given names like "ECE"/"GECE" are short, so keep the threshold high to
|
|
845
|
+
// avoid false positives on legitimate names.
|
|
846
|
+
return /[0-9]/.test(name) || /[KCEG]{5,}/.test(name);
|
|
847
|
+
};
|
|
848
|
+
|
|
801
849
|
/**
|
|
802
850
|
* Validates MRZ text using the mrz npm package
|
|
803
851
|
* @param mrzText Raw or cleaned MRZ text
|
|
@@ -822,6 +870,27 @@ const validateMRZ = (mrzText, autocorrect = true) => {
|
|
|
822
870
|
result = null;
|
|
823
871
|
}
|
|
824
872
|
|
|
873
|
+
// The direct parse can validate (the name field has no check digit) while the
|
|
874
|
+
// name line still carries OCR noise — a leading "O" read as "0", or trailing
|
|
875
|
+
// "<" fillers read as K/C/E/G that leak into the given names. The
|
|
876
|
+
// reconstruction path normalises the name line; when the direct result's name
|
|
877
|
+
// looks noisy, prefer a reconstructed candidate that validates with a cleaner
|
|
878
|
+
// name. This only ever swaps in another fully check-digit-valid reading.
|
|
879
|
+
if (result?.valid && nameLooksNoisy(result)) {
|
|
880
|
+
for (const reLines of reconstructMRZCandidates(mrzText)) {
|
|
881
|
+
let cand;
|
|
882
|
+
try {
|
|
883
|
+
cand = parse(reLines);
|
|
884
|
+
} catch {
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
if (cand.valid && !nameLooksNoisy(cand)) {
|
|
888
|
+
result = cand;
|
|
889
|
+
break;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
825
894
|
// Primary recovery: exhaustive, CHECK-DIGIT-DRIVEN correction scoped to the
|
|
826
895
|
// fields whose check digit actually fails. It tries every OCR-ambiguous
|
|
827
896
|
// permutation within those fields — ordered by how likely the OCR error is
|
package/lib/module/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mrz.utils.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/mrz.utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAepD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;sBAOwB,MAAM,KAAG,MAAM;
|
|
1
|
+
{"version":3,"file":"mrz.utils.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/mrz.utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAepD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;sBAOwB,MAAM,KAAG,MAAM;2BAk5B7B,MAAM,gBACF,OAAO,KACnB,mBAAmB;0CA6JuB,MAAM,KAAG,mBAAmB;wCA52B9B,MAAM,KAAG,MAAM,EAAE,EAAE;wCAs3BnB,MAAM,KAAG,MAAM;gCAmDvB,MAAM;;;;;+BAvtBP,MAAM,KAAG,OAAO;iCA4Bd,MAAM,KAAG,MAAM;wCAysBR,MAAM,GAAG,IAAI,KAAG,MAAM,GAAG,IAAI;;AAgBxE,wBAUE"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "1.475.
|
|
1
|
+
export declare const SDK_VERSION = "1.475.1";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trustchex/react-native-sdk",
|
|
3
|
-
"version": "1.475.
|
|
3
|
+
"version": "1.475.1",
|
|
4
4
|
"description": "Trustchex mobile app react native SDK for android or ios devices",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -69,7 +69,8 @@
|
|
|
69
69
|
"uuid": "11.1.1",
|
|
70
70
|
"yaml": "2.8.3",
|
|
71
71
|
"fast-uri": "3.1.2",
|
|
72
|
-
"flatted": "3.4.2"
|
|
72
|
+
"flatted": "3.4.2",
|
|
73
|
+
"react-native-screens": "4.18.0"
|
|
73
74
|
},
|
|
74
75
|
"scripts": {
|
|
75
76
|
"example": "yarn workspace @trustchex/react-native-sdk-example",
|
|
@@ -347,17 +347,46 @@ const reconstructMRZCandidates = (rawText: string): string[][] => {
|
|
|
347
347
|
const after = flat.indexOf(l1Source) + l1Source.length;
|
|
348
348
|
l2 = pad(flat.slice(after, after + 30), 30);
|
|
349
349
|
}
|
|
350
|
-
// Line 3 (name): the longest
|
|
351
|
-
// date line
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
350
|
+
// Line 3 (name): the longest mostly-letters+filler OCR line that is NOT the
|
|
351
|
+
// doc or date line. OCR mangles the name line too — a leading "O" reads as
|
|
352
|
+
// "0", and the trailing "<" fillers read as K/C — so we DON'T require a
|
|
353
|
+
// pure [A-Z<] line (that wrongly drops "0ZCAN<<ERDAL…" entirely). Accept a
|
|
354
|
+
// line that is predominantly letters/fillers, then normalise it.
|
|
355
|
+
const isNameLike = (l: string): boolean => {
|
|
356
|
+
if (l === l1Source || l === l2Line || l.length < 5) return false;
|
|
357
|
+
if (!/</.test(l)) return false; // must have at least one filler
|
|
358
|
+
// Must look like "SURNAME<<NAMES…": starts with a letter (or its digit
|
|
359
|
+
// look-alike) and is dominated by letters/fillers, not a numeric record.
|
|
360
|
+
if (!/^[A-Z0-9]/.test(l)) return false;
|
|
361
|
+
const letters = (l.match(/[A-Z]/g) || []).length;
|
|
362
|
+
const digits = (l.match(/[0-9]/g) || []).length;
|
|
363
|
+
// A date/doc record is digit-heavy; a name is letter-heavy.
|
|
364
|
+
return letters >= 3 && letters >= digits;
|
|
365
|
+
};
|
|
366
|
+
// Normalise a name line: digit look-alikes back to letters (O/I/S/B/Z/G),
|
|
367
|
+
// and the filler-letter run misreads (K/C/E/G adjacent to fillers) to "<".
|
|
368
|
+
const normaliseName = (l: string): string => {
|
|
369
|
+
const digitToLetter: Record<string, string> = {
|
|
370
|
+
'0': 'O',
|
|
371
|
+
'1': 'I',
|
|
372
|
+
'5': 'S',
|
|
373
|
+
'8': 'B',
|
|
374
|
+
'2': 'Z',
|
|
375
|
+
'6': 'G',
|
|
376
|
+
};
|
|
377
|
+
return (
|
|
378
|
+
l
|
|
379
|
+
// digit look-alikes inside the name → letters
|
|
380
|
+
.replace(/[015862]/g, (d) => digitToLetter[d] ?? d)
|
|
381
|
+
// a run of filler-letters (K/C/E/G) flanked by fillers or at the tail
|
|
382
|
+
// is the trailing/internal filler region
|
|
383
|
+
.replace(/(?<=<)[KCEG]+(?=<|$)/g, (m) => '<'.repeat(m.length))
|
|
384
|
+
.replace(/[KCEG]+$/g, (m) => '<'.repeat(m.length))
|
|
385
|
+
);
|
|
386
|
+
};
|
|
387
|
+
const nameRuns = ocrLines.filter(isNameLike);
|
|
388
|
+
const bestName = nameRuns.sort((a, b) => b.length - a.length)[0] ?? '';
|
|
389
|
+
const l3 = pad(normaliseName(bestName), 30);
|
|
361
390
|
candidates.push([l1, l2, l3]);
|
|
362
391
|
}
|
|
363
392
|
}
|
|
@@ -904,6 +933,21 @@ const correctedMrzFromParse = (
|
|
|
904
933
|
return buffers.map((b) => b.join('')).join('\n');
|
|
905
934
|
};
|
|
906
935
|
|
|
936
|
+
/**
|
|
937
|
+
* Heuristic: does a parsed result's name look like it still carries OCR noise
|
|
938
|
+
* (a digit where a name letter belongs, or a run of filler-confusable letters
|
|
939
|
+
* that should have been "<")? Names are letters only, so any digit is noise; a
|
|
940
|
+
* 3+ run of K/C/E/G is very likely misread fillers leaking into the given names.
|
|
941
|
+
*/
|
|
942
|
+
const nameLooksNoisy = (r: ReturnType<typeof parse>): boolean => {
|
|
943
|
+
const name = `${r.fields?.lastName ?? ''} ${r.fields?.firstName ?? ''}`;
|
|
944
|
+
// A DIGIT in a name is unambiguous OCR noise (MRZ names are letters only).
|
|
945
|
+
// A 5+ run of filler-confusable letters is almost certainly misread padding —
|
|
946
|
+
// real given names like "ECE"/"GECE" are short, so keep the threshold high to
|
|
947
|
+
// avoid false positives on legitimate names.
|
|
948
|
+
return /[0-9]/.test(name) || /[KCEG]{5,}/.test(name);
|
|
949
|
+
};
|
|
950
|
+
|
|
907
951
|
/**
|
|
908
952
|
* Validates MRZ text using the mrz npm package
|
|
909
953
|
* @param mrzText Raw or cleaned MRZ text
|
|
@@ -929,6 +973,27 @@ const validateMRZ = (
|
|
|
929
973
|
result = null;
|
|
930
974
|
}
|
|
931
975
|
|
|
976
|
+
// The direct parse can validate (the name field has no check digit) while the
|
|
977
|
+
// name line still carries OCR noise — a leading "O" read as "0", or trailing
|
|
978
|
+
// "<" fillers read as K/C/E/G that leak into the given names. The
|
|
979
|
+
// reconstruction path normalises the name line; when the direct result's name
|
|
980
|
+
// looks noisy, prefer a reconstructed candidate that validates with a cleaner
|
|
981
|
+
// name. This only ever swaps in another fully check-digit-valid reading.
|
|
982
|
+
if (result?.valid && nameLooksNoisy(result)) {
|
|
983
|
+
for (const reLines of reconstructMRZCandidates(mrzText)) {
|
|
984
|
+
let cand: ReturnType<typeof parse>;
|
|
985
|
+
try {
|
|
986
|
+
cand = parse(reLines);
|
|
987
|
+
} catch {
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
if (cand.valid && !nameLooksNoisy(cand)) {
|
|
991
|
+
result = cand;
|
|
992
|
+
break;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
932
997
|
// Primary recovery: exhaustive, CHECK-DIGIT-DRIVEN correction scoped to the
|
|
933
998
|
// fields whose check digit actually fails. It tries every OCR-ambiguous
|
|
934
999
|
// permutation within those fields — ordered by how likely the OCR error is
|
package/src/version.ts
CHANGED