diff --git a/src/utils/mrz.ts b/src/utils/mrz.ts index 2a041933..ffd4e24a 100644 --- a/src/utils/mrz.ts +++ b/src/utils/mrz.ts @@ -1,55 +1,76 @@ +import moment from 'moment'; + type MRZ = { type: 'TD1' | 'TD2' | 'TD3'; zone: string[]; }; +const FIELD_LIST = { + name: { + field: 'full_name', + format: (value: string) => value.replace(/0/, 'O'), + }, + country: { + field: 'country', + format: (value: string) => value.replace(/0/, 'O'), + }, + birthDate: { + field: 'birth_date', + format: (value: string) => moment(value, 'YYMMDD').format('YYYY-MM-DD'), + }, + expireDate: { + field: 'expireDate', + format: (value: string) => moment(value, 'YYMMDD').format('YYYY-MM-DD'), + }, +}; + const MRZ_TD_1 = [ new RegExp( [ - '(?[0-9A-Z<]{1})', - '(?[A-Z<]{1})', - '(?[0-9A-Z<]{3})', - '(?[0-9A-Z<]{9})', - '(?[0-9A-Z<]{1})', - '(?[0-9A-Z<]{15})', + `(?[0-9A-Z<]{1})`, + `(?[A-Z<]{1})`, + `(?<${FIELD_LIST.country.field}>[0-9A-Z<]{3})`, + `(?[0-9A-Z<]{9})`, + `(?[0-9A-Z<]{1})`, + `(?[0-9A-Z<]{15})`, ].join(''), ), new RegExp( [ - '(?[0-9A-Z<]{6})', - '(?[0-9A-Z<]{1})', - '(?[mfMF<]{1})', - '(?[0-9A-Z<]{6})', - '(?[0-9A-Z<]{1})', - '(?[0-9A-Z<]{3})', - '(?[A-Z0-9<]{11})', - '(?[0-9A-Z<]{1})', + `(?<${FIELD_LIST.birthDate.field}>[0-9A-Z<]{6})`, + `(?[0-9A-Z<]{1})`, + `(?[mfMF<]{1})`, + `(?<${FIELD_LIST.expireDate.field}>[0-9A-Z<]{6})`, + `(?[0-9A-Z<]{1})`, + `(?[0-9A-Z<]{3})`, + `(?[A-Z0-9<]{11})`, + `(?[0-9A-Z<]{1})`, ].join(''), ), - new RegExp(['(?[A-Z<]{30})'].join('')), + new RegExp([`(?<${FIELD_LIST.name.field}>[A-Z<]{30})`].join('')), ]; const MRZ_TD_2 = [ new RegExp( [ - '(?[0-9A-Z<]{1})', - '(?[A-Z<]{1})', - '(?[0-9A-Z<]{3})', - '(?[A-Z<]{31})', + `(?[0-9A-Z<]{1})`, + `(?[A-Z<]{1})`, + `(?<${FIELD_LIST.country.field}>[0-9A-Z<]{3})`, + `(?<${FIELD_LIST.name.field}>[A-Z<]{31})`, ].join(''), ), new RegExp( [ - '(?[0-9A-Z<]{9})', - '(?[0-9A-Z<]{1})', - '(?[0-9A-Z<]{3})', - '(?[0-9A-Z<]{6})', - '(?[0-9A-Z<]{1})', - '(?[mfMF]{1})', - '(?[0-9A-Z<]{6})', - '(?[0-9A-Z<]{1})', - '(?[A-Z0-9<]{7})', - '(?[0-9A-Z<]{1})', + `(?[0-9A-Z<]{9})`, + `(?[0-9A-Z<]{1})`, + `(?[0-9A-Z<]{3})`, + `(?[0-9A-Z<]{6})`, + `(?[0-9A-Z<]{1})`, + `(?[mfMF]{1})`, + `(?[0-9A-Z<]{6})`, + `(?[0-9A-Z<]{1})`, + `(?[A-Z0-9<]{7})`, + `(?[0-9A-Z<]{1})`, ].join(''), ), ]; @@ -57,26 +78,26 @@ const MRZ_TD_2 = [ const MRZ_TD_3 = [ new RegExp( [ - '(?[A-Z0-9<]{1})', - '(?[A-Z0-9<]{1})', - '(?[A-Z0-9]{3})', - '(?[A-Z0-9<]{39})', + `(?[A-Z0-9<]{1})`, + `(?[A-Z0-9<]{1})`, + `(?<${FIELD_LIST.country.field}>[0-9A-Z<]{3})`, + `(?<${FIELD_LIST.name.field}>[A-Z0-9<]{39})`, ].join(''), ), new RegExp( [ - '(?[0-9A-Z<]{9})', - '(?[0-9A-Z<]{1})', - '(?[0-9A-Z<]{3})', - '(?[0-9A-Z<]{6})', - '(?[0-9A-Z<]{1})', - '(?[mfMF<]{1})', - '(?[0-9A-Z<]{6})', - '(?[0-9A-Z<]{1})', - '(?[A-Z0-9<]{14})', - '(?[0-9A-Z<]{1})', - '(?[0-9A-Z<]{1})', + `(?[0-9A-Z<]{9})`, + `(?[0-9A-Z<]{1})`, + `(?[0-9A-Z<]{3})`, + `(?<${FIELD_LIST.birthDate.field}>[0-9A-Z<]{6})`, + `(?[0-9A-Z<]{1})`, + `(?[mfMF<]{1})`, + `(?<${FIELD_LIST.expireDate.field}>[0-9A-Z<]{6})`, + `(?[0-9A-Z<]{1})`, + `(?[A-Z0-9<]{14})`, + `(?[0-9A-Z<]{1})`, + `(?[0-9A-Z<]{1})`, ].join(''), ), ]; @@ -88,9 +109,27 @@ function mrzCleanResult(obj: Record) { .replace(/\s{2,}/, ' ') .trim(); }); + + for (const value of Object.values(FIELD_LIST)) { + if (obj[value.field]) obj[value.field] = value.format(obj[value.field]); + } + return obj; } +export function checkData(data: string, num: number) { + const sum = data.split('').reduce((a, v, i) => { + const num = Number(v); + const weight = [7, 3, 1][i % 3]; + + if (Number.isNaN(num)) { + return a + (v.charCodeAt(0) - 55) * weight; + } + return a + num * weight; + }, 0); + return sum % 10 === num; +} + export function parseType1(mrz: MRZ) { const result: Record = {};