2024-10-02 15:02:12 +07:00
|
|
|
import moment from 'moment';
|
|
|
|
|
|
2024-10-02 13:55:52 +07:00
|
|
|
type MRZ = {
|
|
|
|
|
type: 'TD1' | 'TD2' | 'TD3';
|
|
|
|
|
zone: string[];
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-02 15:02:12 +07:00
|
|
|
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'),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-02 13:55:52 +07:00
|
|
|
const MRZ_TD_1 = [
|
|
|
|
|
new RegExp(
|
|
|
|
|
[
|
2024-10-02 15:02:12 +07:00
|
|
|
`(?<doc_type>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<doc_subtype>[A-Z<]{1})`,
|
|
|
|
|
`(?<${FIELD_LIST.country.field}>[0-9A-Z<]{3})`,
|
|
|
|
|
`(?<doc_number>[0-9A-Z<]{9})`,
|
|
|
|
|
`(?<doc_number_check>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<optional_data>[0-9A-Z<]{15})`,
|
2024-10-02 13:55:52 +07:00
|
|
|
].join(''),
|
|
|
|
|
),
|
|
|
|
|
new RegExp(
|
|
|
|
|
[
|
2024-10-02 15:02:12 +07:00
|
|
|
`(?<${FIELD_LIST.birthDate.field}>[0-9A-Z<]{6})`,
|
|
|
|
|
`(?<birth_date_check>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<sex>[mfMF<]{1})`,
|
|
|
|
|
`(?<${FIELD_LIST.expireDate.field}>[0-9A-Z<]{6})`,
|
|
|
|
|
`(?<expire_date_check>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<nationality>[0-9A-Z<]{3})`,
|
|
|
|
|
`(?<optional_data>[A-Z0-9<]{11})`,
|
|
|
|
|
`(?<line_check>[0-9A-Z<]{1})`,
|
2024-10-02 13:55:52 +07:00
|
|
|
].join(''),
|
|
|
|
|
),
|
2024-10-02 15:02:12 +07:00
|
|
|
new RegExp([`(?<${FIELD_LIST.name.field}>[A-Z<]{30})`].join('')),
|
2024-10-02 13:55:52 +07:00
|
|
|
];
|
|
|
|
|
const MRZ_TD_2 = [
|
|
|
|
|
new RegExp(
|
|
|
|
|
[
|
2024-10-02 15:02:12 +07:00
|
|
|
`(?<doc_type>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<doc_subtype>[A-Z<]{1})`,
|
|
|
|
|
`(?<${FIELD_LIST.country.field}>[0-9A-Z<]{3})`,
|
|
|
|
|
`(?<${FIELD_LIST.name.field}>[A-Z<]{31})`,
|
2024-10-02 13:55:52 +07:00
|
|
|
].join(''),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
new RegExp(
|
|
|
|
|
[
|
2024-10-02 15:02:12 +07:00
|
|
|
`(?<doc_number>[0-9A-Z<]{9})`,
|
|
|
|
|
`(?<doc_numbercheck>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<nationality>[0-9A-Z<]{3})`,
|
|
|
|
|
`(?<birth_date>[0-9A-Z<]{6})`,
|
|
|
|
|
`(?<birth_date_check>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<sex>[mfMF]{1})`,
|
|
|
|
|
`(?<expire_date>[0-9A-Z<]{6})`,
|
|
|
|
|
`(?<expire_date_check>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<optional_data>[A-Z0-9<]{7})`,
|
|
|
|
|
`(?<line_check>[0-9A-Z<]{1})`,
|
2024-10-02 13:55:52 +07:00
|
|
|
].join(''),
|
|
|
|
|
),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const MRZ_TD_3 = [
|
|
|
|
|
new RegExp(
|
|
|
|
|
[
|
2024-10-02 15:02:12 +07:00
|
|
|
`(?<doc_type>[A-Z0-9<]{1})`,
|
|
|
|
|
`(?<doc_subtype>[A-Z0-9<]{1})`,
|
|
|
|
|
`(?<${FIELD_LIST.country.field}>[0-9A-Z<]{3})`,
|
|
|
|
|
`(?<${FIELD_LIST.name.field}>[A-Z0-9<]{39})`,
|
2024-10-02 13:55:52 +07:00
|
|
|
].join(''),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
new RegExp(
|
|
|
|
|
[
|
2024-10-02 15:02:12 +07:00
|
|
|
`(?<doc_number>[0-9A-Z<]{9})`,
|
|
|
|
|
`(?<doc_number_check>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<nationality>[0-9A-Z<]{3})`,
|
|
|
|
|
`(?<${FIELD_LIST.birthDate.field}>[0-9A-Z<]{6})`,
|
|
|
|
|
`(?<birth_date_check>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<sex>[mfMF<]{1})`,
|
|
|
|
|
`(?<${FIELD_LIST.expireDate.field}>[0-9A-Z<]{6})`,
|
|
|
|
|
`(?<expire_date_check>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<optional_data>[A-Z0-9<]{14})`,
|
|
|
|
|
`(?<optional_data_check>[0-9A-Z<]{1})`,
|
|
|
|
|
`(?<line_check>[0-9A-Z<]{1})`,
|
2024-10-02 13:55:52 +07:00
|
|
|
].join(''),
|
|
|
|
|
),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
function mrzCleanResult(obj: Record<string, string>) {
|
|
|
|
|
Object.entries(obj).forEach(([k, v]) => {
|
|
|
|
|
obj[k] = v
|
|
|
|
|
.replace(/</g, ' ')
|
|
|
|
|
.replace(/\s{2,}/, ' ')
|
|
|
|
|
.trim();
|
|
|
|
|
});
|
2024-10-02 15:02:12 +07:00
|
|
|
|
|
|
|
|
for (const value of Object.values(FIELD_LIST)) {
|
|
|
|
|
if (obj[value.field]) obj[value.field] = value.format(obj[value.field]);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 13:55:52 +07:00
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 15:02:12 +07:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 13:55:52 +07:00
|
|
|
export function parseType1(mrz: MRZ) {
|
|
|
|
|
const result: Record<string, string> = {};
|
|
|
|
|
|
|
|
|
|
mrz.zone.forEach((line, i) => {
|
|
|
|
|
if (MRZ_TD_1[i].test(line)) {
|
|
|
|
|
Object.assign(result, MRZ_TD_1[i].exec(line)?.groups);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return { mrz, result: mrzCleanResult(result) };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function parseType2(mrz: MRZ) {
|
|
|
|
|
const result: Record<string, string> = {};
|
|
|
|
|
|
|
|
|
|
mrz.zone.forEach((line, i) => {
|
|
|
|
|
if (MRZ_TD_2[i].test(line)) {
|
|
|
|
|
Object.assign(result, MRZ_TD_2[i].exec(line)?.groups);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return { mrz, result: mrzCleanResult(result) };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function parseType3(mrz: MRZ) {
|
|
|
|
|
const result: Record<string, string> = {};
|
|
|
|
|
|
|
|
|
|
mrz.zone.forEach((line, i) => {
|
|
|
|
|
if (MRZ_TD_3[i].test(line)) {
|
|
|
|
|
Object.assign(result, MRZ_TD_3[i].exec(line)?.groups);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return { mrz, result: mrzCleanResult(result) };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function parseMRZ(mrz: MRZ) {
|
|
|
|
|
if (mrz.type === 'TD1') return parseType1(mrz);
|
|
|
|
|
if (mrz.type === 'TD2') return parseType2(mrz);
|
|
|
|
|
if (mrz.type === 'TD3') return parseType3(mrz);
|
|
|
|
|
return null;
|
|
|
|
|
}
|