Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

module.exports = {
testURL: 'http://localhost',
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this fix?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, as I remember I had some problems with Jest and after googling this helped me.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without it I get

 FAIL  src/parse/__tests__/td3.js
  ● Test suite failed to run

    SecurityError: localStorage is not available for opaque origins
      
      at Window.get localStorage [as localStorage] (node_modules/jsdom/lib/jsdom/browser/Window.js:257:15)
          at Array.forEach (<anonymous>)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you try with testEnvironment: "node" instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it helps.
Thanks!

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mrz",
"version": "3.1.1",
"name": "@lukesolo/mrz",
"version": "3.1.3",
Comment on lines +2 to +3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"name": "@lukesolo/mrz",
"version": "3.1.3",
"name": "mrz",
"version": "3.1.1",

"description": "Parse MRZ (Machine Readable Zone) from identity documents",
"main": "./src/index.js",
"files": [
Expand Down
3 changes: 2 additions & 1 deletion src/formats.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const formats = {
TD2: 'TD2',
TD3: 'TD3',
SWISS_DRIVING_LICENSE: 'SWISS_DRIVING_LICENSE',
FRENCH_NATIONAL_ID: 'FRENCH_NATIONAL_ID'
FRENCH_NATIONAL_ID: 'FRENCH_NATIONAL_ID',
FRENCH_DRIVING_LICENSE: 'FRENCH_DRIVING_LICENSE'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we call it EU_DRIVING_LICENSE or the french one has non-standard parts?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no standard in EU driving license. As I know, French, Swiss and Netherlands driving licenses are all has unique format.

};
Object.freeze(formats);

Expand Down
22 changes: 22 additions & 0 deletions src/parse/__tests__/frenchDrivingLicense.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

const parse = require('../parse');

describe('parse French Driving License', () => {
it('valid MRZ', function () {
const MRZ = [ 'D1FRA13BB148959280920BETTOLO<7' ];
var result = parse(MRZ);
expect(result.format).toBe('FRENCH_DRIVING_LICENSE');
expect(result.details.filter((a) => !a.valid)).toHaveLength(0);
expect(result.fields).toEqual({
documentCode: 'D',
bapConfiguration: '1',
issuingState: 'FRA',
documentNumber: '13BB14895',
documentNumberCheckDigit: '9',
expirationDate: '280920',
lastName: 'BETTOLO',
compositeCheckDigit: '7'
});
});
});
28 changes: 27 additions & 1 deletion src/parse/__tests__/frenchNationalId.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('parse French National Id', () => {
administrativeCode: '0CHE02',
issueDate: '1710',
administrativeCode2: 'GVA',
documentNumber: '12345',
documentNumber: '1710GVA12345',
documentNumberCheckDigit: '1',
firstName: 'ROBERTA',
birthDate: '911231',
Expand All @@ -28,4 +28,30 @@ describe('parse French National Id', () => {
compositeCheckDigit: '2'
});
});

it('valid MRZ of version without administrativeCode', function () {
const MRZ = [
'IDFRABERTHIER<<<<<<<<<<<<<<<<<<<<<<<',
'9409923102854CORINNE<<<<<<<6512068F4'
];
var result = parse(MRZ);
expect(result.format).toBe('FRENCH_NATIONAL_ID');
// expect(result.valid).toEqual(true);
expect(result.details.filter((a) => !a.valid)).toHaveLength(0);
expect(result.fields).toEqual({
documentCode: 'ID',
issuingState: 'FRA',
lastName: 'BERTHIER',
administrativeCode: '',
issueDate: '9409',
administrativeCode2: '923',
documentNumber: '940992310285',
documentNumberCheckDigit: '4',
firstName: 'CORINNE',
birthDate: '651206',
birthDateCheckDigit: '8',
sex: 'female',
compositeCheckDigit: '4'
});
});
});
8 changes: 7 additions & 1 deletion src/parse/fieldTemplates.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ const issuingStateTemplate = {
parser: require('../parsers/parseState')
};

const bapConfigurationTemplate = {
label: 'BAP configuration',
field: 'bapConfiguration'
};

module.exports = {
documentNumberTemplate,
documentNumberCheckDigitTemplate,
Expand All @@ -97,5 +102,6 @@ module.exports = {
compositeCheckDigitTemplate,
firstNameTemplate,
lastNameTemplate,
issuingStateTemplate
issuingStateTemplate,
bapConfigurationTemplate
};
25 changes: 25 additions & 0 deletions src/parse/frenchDrivingLicense.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';

const checkLines = require('./checkLines');
const getResult = require('./getResult');
const { FRENCH_DRIVING_LICENSE } = require('../formats');
const frenchDrivingLicenseFields = require('./frenchDrivingLicenseFields');

module.exports = function parseFrenchDrivingLicense(lines) {
lines = checkLines(lines);
if (lines.length !== 1) {
throw new Error(
`invalid number of lines: ${
lines.length
}: Must be 1 for ${FRENCH_DRIVING_LICENSE}`
);
}
if (lines[0].length !== 30) {
throw new Error(
`invalid number of characters for line: ${
lines[0].length
}. Must be 30 for ${FRENCH_DRIVING_LICENSE}`
);
}
return getResult(FRENCH_DRIVING_LICENSE, lines, frenchDrivingLicenseFields);
};
81 changes: 81 additions & 0 deletions src/parse/frenchDrivingLicenseFields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
'use strict';

const parseDocumentCode = require('../parsers/euDrivingLicense/parseDocumentCode');
const parseBapConfiguration = require('../parsers/euDrivingLicense/parseBapConfiguration');
const parseState = require('../parsers/parseState');
const parseDocumentNumber = require('../parsers/euDrivingLicense/france/parseDocumentNumber');
const parseLastName = require('../parsers/euDrivingLicense/france/parseLastName');

const {
documentCodeTemplate,
bapConfigurationTemplate,
issuingStateTemplate,
documentNumberTemplate,
documentNumberCheckDigitTemplate,
expirationDateTemplate,
lastNameTemplate,
compositeCheckDigitTemplate
} = require('./fieldTemplates');
const createFieldParser = require('./createFieldParser');

module.exports = [
Object.assign({}, documentCodeTemplate, {
line: 0,
start: 0,
end: 1,
parser: parseDocumentCode
}),
Object.assign({}, bapConfigurationTemplate, {
line: 0,
start: 1,
end: 2,
parser: parseBapConfiguration
}),
Object.assign({}, issuingStateTemplate, {
line: 0,
start: 2,
end: 5,
parser: parseState
}),
Object.assign({}, documentNumberTemplate, {
line: 0,
start: 5,
end: 14,
parser: parseDocumentNumber
}),
Object.assign({}, documentNumberCheckDigitTemplate, {
line: 0,
start: 14,
end: 15,
related: [
{
line: 0,
start: 5,
end: 14
}
]
}),
Object.assign({}, expirationDateTemplate, {
line: 0,
start: 15,
end: 21
}),
Object.assign({}, lastNameTemplate, {
line: 0,
start: 21,
end: 29,
parser: parseLastName
}),
Object.assign({}, compositeCheckDigitTemplate, {
line: 0,
start: 29,
end: 30,
related: [
{
line: 0,
start: 0,
end: 29
}
]
})
].map(createFieldParser);
2 changes: 1 addition & 1 deletion src/parse/frenchNationalIdFields.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ module.exports = [
},
Object.assign({}, documentNumberTemplate, {
line: 1,
start: 7,
start: 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change? The 3 fields are separated in the description of wikipedia. They can always be reconcatenated later

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://www.consilium.europa.eu/prado/en/FRA-BO-02002/image-7890.html
First 12 symbols represent complete Document Number. I don't think there is a case to use just part of it somewhere. Without this change all library clients should check if it's France then concatenate 3 fields to get the doc number.

I can make a pull request without this change if you don't want to break the backward compatibility.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation. This LGTM then. No problem, we'll make a major release with all the changes.

end: 12
}),
Object.assign({}, documentNumberCheckDigitTemplate, {
Expand Down
11 changes: 9 additions & 2 deletions src/parse/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ const parsers = require('./parsers');
function parseMRZ(lines) {
lines = checkLines(lines);
switch (lines.length) {
case 1:{
if (lines[0].match(/^D[1PN<]FRA/)) {
return parsers.FRENCH_DRIVING_LICENSE(lines);
}
throw new Error(
'unrecognized document format. Input must match pattern /^D[1PN<]FRA/ (French Driving License)'
);
}
case 2:
case 3: {
switch (lines[0].length) {
case 30:
return parsers.TD1(lines);
case 36: {
const endLine1 = lines[0].substr(30, 36);
if (endLine1.match(/[0-9]/)) {
if (lines[0].match(/^I.FRA/)) {
return parsers.FRENCH_NATIONAL_ID(lines);
} else {
return parsers.TD2(lines);
Expand Down
4 changes: 3 additions & 1 deletion src/parse/parsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ const parseTD2 = require('./td2');
const parseTD3 = require('./td3');
const parseSwissDrivingLicense = require('./swissDrivingLicense');
const parseFrenchNationalId = require('./frenchNationalId');
const parseFrenchDrivingLicense = require('./frenchDrivingLicense');

module.exports = {
TD1: parseTD1,
TD2: parseTD2,
TD3: parseTD3,
SWISS_DRIVING_LICENSE: parseSwissDrivingLicense,
FRENCH_NATIONAL_ID: parseFrenchNationalId
FRENCH_NATIONAL_ID: parseFrenchNationalId,
FRENCH_DRIVING_LICENSE: parseFrenchDrivingLicense
};
11 changes: 11 additions & 0 deletions src/parsers/euDrivingLicense/france/parseDocumentNumber.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

module.exports = function parseDocumentCode(source) {
// french driving license number
if (!source.match(/^[0-9]{2}[A-Z]{2}[0-9]{5}$/)) {
throw new Error(
`invalid document number: ${source}.`
);
}
return source;
};
12 changes: 12 additions & 0 deletions src/parsers/euDrivingLicense/france/parseLastName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict';

var parseText = require('../../parseText');

module.exports = function parseLastName(source) {
const parsed = parseText(source, /^[A-Z<]+$/);
return {
value: parsed,
start: 0,
end: parsed.length
};
};
15 changes: 15 additions & 0 deletions src/parsers/euDrivingLicense/parseBapConfiguration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

module.exports = function parseBapConfiguration(bapConfig) {
switch (bapConfig) {
case '1':
case 'P':
case 'N':
case '<':
return bapConfig;
default:
throw new Error(
`invalid BAP configuration code: ${bapConfig}. Must be 1, P, N or <`
);
}
};
8 changes: 8 additions & 0 deletions src/parsers/euDrivingLicense/parseDocumentCode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict';

module.exports = function parseDocumentCode(source) {
if (source !== 'D') {
throw new Error(`invalid document code: ${source}. Must be D`);
}
return source;
};
4 changes: 2 additions & 2 deletions src/parsers/parseDocumentCodeId.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

module.exports = function parseDocumentCodeId(source) {
const first = source.charAt(0);
if (first !== 'A' && first !== 'C' && first !== 'I') {
if (first !== 'A' && first !== 'C' && first !== 'I' && first !== 'R') {
throw new Error(
`invalid document code: ${source}. First character must be A, C or I`
`invalid document code: ${source}. First character must be A, C, R or I`
);
}

Expand Down