Created
October 13, 2020 11:05
-
-
Save kategray/70940fe275295513875a7cbbfe3d245c to your computer and use it in GitHub Desktop.
Create a MRZ for ID cards
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env php | |
<?php | |
// Line 1 | |
$document_number = rand (0, 1000000000); // 9 Digits | |
$country = 'XXX'; // Unspecified | |
$document_type = 'I'; // ID Card | |
$document_subtype = '<'; // Generally 'D', or '<' for ID | |
$line1_optional = 'COMPANY'; // Optional, 15 chars | |
// Line 2 | |
$dob = '999999'; // YYMMDD | |
$sex = '<'; // M, F, or < | |
$expiration = '222222'; // YYMMDD | |
$nationality = 'XXX'; // Country Code | |
$line2_optional = ''; // Optional, 11 chars | |
// Line 3 | |
$family_name = 'Smith'; // Comes first | |
$given_names = array ('John', 'Alfred'); // Truncated down to a total of 30 characters | |
printf ("Document Number: %s\n", $document_number); | |
$line1 = sprintf ('%s%s%s%09s%d', $document_type, $document_subtype, $country, $document_number, checkDigit($document_number)); | |
if (strlen ($line1) != 15) { | |
sprintf ("Invalid input data.\n"); | |
} | |
// Pad line out to 30 chars | |
$line1 = sprintf ('%15s%-15s', $line1, $line1_optional); | |
$line1 = str_replace (' ', '<', $line1); | |
if (strlen ($line1) != 30) { | |
sprintf ("Invalid input data.\n"); | |
} | |
// Encode line 2 | |
$line2 = sprintf ('%6d%1d%1s%6d%1d%3s', | |
$dob, checkDigit ($dob), | |
$sex, $expiration, checkDigit ($expiration), | |
$nationality); | |
$line2 = sprintf ('%15s%-11s', $line2, $line2_optional); | |
$line2 = str_replace (' ', '<', $line2); | |
// Generate the composite checksum | |
$composite_data = substr ($line1, 5, 25) . substr ($line2, 0, 7) . substr ($line2, 8, 7). substr ($line2, 18, 11); | |
$line2 .= checkDigit ($composite_data); | |
// Build line 3 | |
$line3 = sprintf ('%s %-30s', $family_name, implode (' ', $given_names)); | |
$line3 = strtoupper (str_replace (' ', '<', $line3)); | |
$line3 = substr ($line3, 0, 30); | |
printf ("%s\n", $line1); | |
printf ("%s\n", $line2); | |
printf ("%s\n", $line3); | |
function checkDigit ($input) { | |
// MRZ checksums are weighted | |
$weights = array (7, 3, 1); | |
$sum = 0; | |
// Split the string into it's respective characters | |
$chars = str_split ($input); | |
for ($i = 0; $i < strlen ($input); $i++) { | |
$result = (getValue ($chars[$i]) * $weights[$i % 3]); | |
// echo sprintf ("%s (%d) * %d = %d\n", $chars[$i], getValue ($chars[$i]), $weights[$i % 3], $result); | |
$sum += $result; | |
} | |
return $sum % 10; | |
} | |
function getValue ($input) { | |
$values = [ | |
'<' => 00, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, | |
'E' => 14, 'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, | |
'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23, | |
'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, | |
'T' => 29, 'U' => 30, 'V' => 31, 'W' => 32, 'X' => 33, | |
'Y' => 34, 'Z' => 35 | |
]; | |
if (strlen ($input) != 1) { | |
system ("Invalid character.\n"); | |
exit (1); | |
} | |
if (is_numeric ($input)) { | |
return (int)$input; | |
} else { | |
if (isset ($values[$input])) { | |
return $values[$input]; | |
} else { | |
printf ("Invalid character '%s' detected.\n", $input); | |
exit (1); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment