Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

mrz

Package Overview
Dependencies
Maintainers
3
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mrz - npm Package Compare versions

Comparing version 2.0.0 to 3.0.0

src/formats.js

29

package.json
{
"name": "mrz",
"version": "2.0.0",
"description": "Create and parse MRZ (Machine Readable Zone) in TD1 and TD3 format",
"version": "3.0.0",
"description": "Parse MRZ (Machine Readable Zone) from identity documents",
"main": "./src/index.js",
"files": [
"runkit.js",
"src"
],
"scripts": {
"eslint": "eslint src __tests__",
"eslint": "eslint builder src",
"eslint-fix": "npm run eslint -- --fix",

@@ -17,5 +16,5 @@ "test": "npm run test-only && npm run eslint",

"test-only": "jest",
"build": "npm run buildCountries && cheminfo build",
"prepublish": "npm run buildCountries",
"buildCountries": "node builder/createCountriesJs.js"
"build": "npm run buildStates && cheminfo build",
"prepublish": "npm run buildStates",
"buildStates": "node builder/createStatesJs.js"
},

@@ -27,7 +26,10 @@ "repository": {

"keywords": [
"mrz",
"machine",
"learning",
"data",
"mining",
"datamining"
"readable",
"zone",
"identity",
"documents",
"card",
"passport"
],

@@ -42,8 +44,7 @@ "author": "Luc Patiny <luc@patiny.com>",

"cheminfo-tools": "^1.20.2",
"eslint": "^4.15.0",
"eslint": "^4.16.0",
"eslint-config-cheminfo": "^1.14.0",
"eslint-plugin-jest": "^21.7.0",
"jest": "^22.1.3",
"should": "^11.1.0"
"jest": "^22.1.4"
}
}

@@ -9,7 +9,7 @@ # mrz

Parse MRZ (Machine Readable Zone) in TD1, TD2, TD3 or CH driving licence format
Parse MRZ (Machine Readable Zone) from identity documents.
## Installation
`$ npm install mrz`
`$ npm install --save mrz`

@@ -19,7 +19,9 @@ ## Example

```js
const parse = require("mrz").parse;
const parse = require('mrz').parse;
let mrz = `I<UTOD23145890<1233<<<<<<<<<<<
7408122F1204159UTO<<<<<<<<<<<6
ERIKSSON<<ANNA<MARIA<<<<<<<<<<`;
let mrz = [
'I<UTOD23145890<1233<<<<<<<<<<<',
'7408122F1204159UTO<<<<<<<<<<<6',
'ERIKSSON<<ANNA<MARIA<<<<<<<<<<'
];

@@ -30,11 +32,60 @@ var result = parse(mrz);

Or test it in [Runkit](https://runkit.com/npm/mrz)
## API
### parse(mrz)
Parses the provided MRZ. The argument can be an array of lines or a single string
including line breaks. This function throws an error if the input is in an
unsupported format. It will never throw an error when there are invalid fields
in the MRZ. Instead, the `result.valid` value will be `false` and
details about the invalid fields can be found in `result.details`.
#### result.format
String identifying the format of the parsed MRZ. Supported formats are:
* TD1 (identity card with three MRZ lines)
* TD2 (identity card with two MRZ lines)
* TD3 (passport)
* SWISS_DRIVING_LICENSE
#### result.valid
`true` if all fields are valid. `false` otherwise.
#### result.fields
Object mapping field names to their respective value. The value is set to `null`
if it is invalid. The value may be different than the raw value. For example
`result.fields.sex` will be "male" when the raw value was "M".
#### result.details
Array of objects describing all parsed fields. Its structure is:
* label {string} - Full english term for the field.
* field {string} - Name of the field in `result.fields`.
* value {string} - Value of the field or `null`.
* valid {boolean}
* ranges {Array} - Array of ranges that are necessary to compute this field.
Ranges are objects with `line`, `start`, `end` and `raw`.
* line {number} - Index of the line where the field is located.
* start {number} - Index of the start of the field in `line`.
* end {number} - Index of the end of the field in `line`.
### formats
Static mapping of supported formats.
### states
Static mapping of state code to state name.
## Specifications
## TD1, TD2 and TD3
### TD1, TD2 and TD3
https://www.icao.int/publications/pages/publication.aspx?docnum=9303
## Swiss driving license
### Swiss driving license

@@ -41,0 +92,0 @@ http://www.astra2.admin.ch/media/pdfpub/2003-10-15_2262_f.pdf

'use strict';
const COUNTRIES = require('./generated/countries');
const states = require('./generated/states');
const formats = require('./formats');
const parse = require('./parse/parse');
module.exports = {
COUNTRIES,
states,
formats,
parse
};
'use strict';
module.exports = function (fieldOptions) {
const result = Object.assign({}, fieldOptions, { parser: undefined });
const parser = fieldOptions.parser;
if (
!fieldOptions.line === undefined ||
!fieldOptions.start === undefined ||
!fieldOptions.end === undefined ||
!fieldOptions.parser
) {
throw new Error('field must have a line, start, stop and parser');
checkType(fieldOptions, 'label', 'string');
if (fieldOptions.field !== null) {
checkType(fieldOptions, 'field', 'string');
}
return function (lines) {
checkType(fieldOptions, 'line', 'number');
checkType(fieldOptions, 'start', 'number');
checkType(fieldOptions, 'end', 'number');
checkType(fieldOptions, 'parser', 'function');
const ranges = [
{
line: fieldOptions.line,
start: fieldOptions.start,
end: fieldOptions.end
}
];
if (Array.isArray(fieldOptions.related)) {
for (const related of fieldOptions.related) {
checkType(related, 'line', 'number');
checkType(related, 'start', 'number');
checkType(related, 'end', 'number');
ranges.push(related);
}
}
return function parseField(lines) {
const source = getText(lines, fieldOptions);
let related = fieldOptions.related || [];
related = related.map((r) => getText(lines, r));
const result = {
label: fieldOptions.label,
field: fieldOptions.field,
value: null,
valid: false,
ranges: ranges.map((range) => ({
line: range.line,
start: range.start,
end: range.end,
raw: getText(lines, range)
}))
};
try {
let parsed = parser(source, ...related);
result.parsed = parsed;
let parsed = fieldOptions.parser(source, ...related);
result.value = typeof parsed === 'object' ? parsed.value : parsed;
result.valid = true;
const range = result.ranges[0];
result.line = range.line;
result.start = range.start;
result.end = range.end;
if (typeof parsed === 'object') {
result.start = range.start + parsed.start;
result.end = range.start + parsed.end;
}
} catch (e) {
result.parsed = null;
result.valid = false;
result.message = e.message;
result.error = e.message;
}

@@ -36,1 +68,7 @@ return result;

}
function checkType(options, name, type) {
if (typeof options[name] !== type) {
throw new TypeError(`${name} must be a ${type}`);
}
}
'use strict';
var parseTD1 = require('./td1');
var parseTD2 = require('./td2');
var parseTD3 = require('./td3');
var parsePCC = require('./pcc');
const checkLines = require('./checkLines');
const formats = require('../formats');
const parsers = require('./parsers');
module.exports = function parse(lines) {
let result = {};
if (typeof lines === 'string') {
lines = lines.split(/[\r\n]+/);
}
function parseMRZ(lines) {
lines = checkLines(lines);
switch (lines.length) {
case 2:
if (lines[0].length < 41) {
result = parseTD2(lines);
} else {
result = parseTD3(lines);
case 3: {
switch (lines[0].length) {
case 30:
return parsers.TD1(lines);
case 36:
return parsers.TD2(lines);
case 44:
return parsers.TD3(lines);
case 9:
return parsers.SWISS_DRIVING_LICENSE(lines);
default:
throw new Error(
'unrecognized document format. First line of input must have 30 (TD1), 36 (TD2), 44 (TD3) or 9 (Swiss Driving License) characters'
);
}
break;
case 3:
if (lines[0].length < 15) {
// in fact it should be 9
result = parsePCC(lines);
} else {
result = parseTD1(lines);
}
break;
}
default:
throw new Error('input must be an array of 2 or 3 elements');
throw new Error(
`unrecognized document format. Input must have two or three lines, found${
lines.length
}`
);
}
}
return result;
};
for (const format in formats) {
parseMRZ[format] = parsers[format];
}
module.exports = parseMRZ;
'use strict';
const { getAnnotations, completeResult } = require('./fieldHelper');
const { TD1: TD1Fields } = require('./fields');
const checkLines = require('./checkLines');
const getResult = require('./getResult');
const { TD1 } = require('../formats');
const TD1Fields = require('./td1Fields');
module.exports = function parseTD1(lines) {
lines.forEach((line) => {
lines = checkLines(lines);
lines.forEach((line, index) => {
if (line.length !== 30) {
throw new Error('each line should have a length of 30 in TD1');
throw new Error(
`invalid number of characters for line ${index + 1}: ${
line.length
}. Must be 30 for TD1`
);
}
});
const result = {
format: 'TD1',
annotations: getAnnotations(lines, TD1Fields)
};
completeResult(result);
return result;
return getResult(TD1, lines, TD1Fields);
};
'use strict';
const { getAnnotations, completeResult } = require('./fieldHelper');
const { TD2: TD2Fields } = require('./fields');
const checkLines = require('./checkLines');
const getResult = require('./getResult');
const { TD2 } = require('../formats');
const TD2Fields = require('./td2Fields');
module.exports = function parseTD1(lines) {
lines.forEach((line) => {
module.exports = function parseTD2(lines) {
lines = checkLines(lines);
lines.forEach((line, index) => {
if (line.length !== 36) {
throw new Error('each line should have a length of 36 in TD2');
throw new Error(
`invalid number of characters for line ${index + 1}: ${
line.length
}. Must be 36 for TD2`
);
}
});
const result = {
format: 'TD2',
annotations: getAnnotations(lines, TD2Fields)
};
completeResult(result);
return result;
return getResult(TD2, lines, TD2Fields);
};
'use strict';
const { getAnnotations, completeResult } = require('./fieldHelper');
const { TD3: TD3Fields } = require('./fields');
const checkLines = require('./checkLines');
const getResult = require('./getResult');
const { TD3 } = require('../formats');
const TD3Fields = require('./td3Fields');
module.exports = function parseTD1(lines) {
lines.forEach((line) => {
module.exports = function parseTD3(lines) {
lines = checkLines(lines);
lines.forEach((line, index) => {
if (line.length !== 44) {
throw new Error('each line should have a length of 30 in TD1');
throw new Error(
`invalid number of characters for line ${index + 1}: ${
line.length
}. Must be 44 for TD3`
);
}
});
const result = {
format: 'TD3',
annotations: getAnnotations(lines, TD3Fields)
};
completeResult(result);
return result;
return getResult(TD3, lines, TD3Fields);
};

@@ -6,5 +6,5 @@ 'use strict';

test('test check digits', () => {
expect(check('592166117<231', 8)).toBe(true);
expect(check('592166111<773', 5)).toBe(true);
expect(() => check('592166117<231', 8)).not.toThrow();
expect(() => check('592166111<773', 5)).not.toThrow();
expect(() => check('592166111<773', 4)).toThrow(/invalid check digit/);
});

@@ -14,3 +14,6 @@ 'use strict';

}
return code % 10 === Number(value);
code %= 10;
if (code !== Number(value)) {
throw new Error(`invalid check digit: ${value}. Must be ${code}`);
}
};
'use strict';
module.exports = function parseDate(value) {
const year = value.substring(0, 2);
if (!value.match(/^[0-9<]{6}$/)) {
throw new Error(`invalid date: ${value}`);
}
const month = value.substring(2, 4);
const day = value.substring(4, 6);
if (month < 1 || month > 12) {
throw new Error(`Month "${month}" not valid`);
if (month !== '<<' && (month < 1 || month > 12)) {
throw new Error(`invalid date month: ${month}`);
}
if (day < 1 || day > 31) {
throw new Error(`Day "${day}" not valid`);
if (day !== '<<' && (day < 1 || day > 31)) {
throw new Error(`invalid date day: ${day}`);
}
return `${day}.${month}.${year}`;
return value;
};

@@ -5,7 +5,7 @@ 'use strict';

module.exports = function (checkDigit, value) {
if (checkDigit !== false && !check(value, checkDigit)) {
throw new Error(`Check digit "${checkDigit}" not valid`);
module.exports = function parseCheckDigit(checkDigit, value) {
if (checkDigit !== false) {
check(value, checkDigit);
}
return checkDigit;
};

@@ -6,7 +6,16 @@ 'use strict';

module.exports = function parseDocumentNumber(source, checkDigit, optional) {
let end, value;
if (checkDigit === '<' && optional) {
optional = cleanText(optional);
source += optional.substring(0, optional.length - 1);
value = source + optional.substring(0, optional.length - 1);
end = value.length + 1;
} else {
value = cleanText(source);
end = value.length;
}
return source.replace(/</g);
return {
value,
start: 0,
end
};
};

@@ -6,3 +6,7 @@ 'use strict';

module.exports = function (checkDigit, source, optional) {
module.exports = function parseDocumentNumberCheckDigit(
checkDigit,
source,
optional
) {
if (checkDigit === '<' && optional) {

@@ -14,6 +18,4 @@ optional = cleanText(optional);

if (!check(source, checkDigit)) {
throw new Error(`document number check digit "${checkDigit}" not valid`);
}
check(source, checkDigit);
return checkDigit;
};

@@ -5,3 +5,3 @@ 'use strict';

if (!source.match(/^[0-9]+$/)) {
throw new Error('It may only be composed of numbers');
throw new Error(`invalid number: ${source}`);
}

@@ -8,0 +8,0 @@

@@ -5,4 +5,9 @@ 'use strict';

module.exports = function (value) {
return parseText(value, /^[A-Z0-9<]+<*$/);
module.exports = function parsePersonalNumber(source) {
const value = parseText(source, /^[A-Z0-9<]+<*$/);
return {
value,
start: 0,
end: value.length
};
};

@@ -5,4 +5,2 @@ 'use strict';

switch (source) {
case '<':
return 'unknown';
case 'M':

@@ -12,7 +10,7 @@ return 'male';

return 'female';
case '<':
return 'nonspecified';
default:
throw new Error(
`The sex "${source}" is incorrect. Allowed values: M, F or <.`
);
throw new Error(`invalid sex: ${source}. Must be M, F or <.`);
}
};

@@ -7,5 +7,7 @@ 'use strict';

if (!source.match(regexp)) {
throw new Error(`it must match the following regexp: ${regexp}`);
throw new Error(
`invalid text: ${source}. Must match the following regular expression: ${regexp}`
);
}
return cleanText(source);
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc