Skip to content

Instantly share code, notes, and snippets.

@kategray
Created October 13, 2020 11:05
Show Gist options
  • Save kategray/70940fe275295513875a7cbbfe3d245c to your computer and use it in GitHub Desktop.
Save kategray/70940fe275295513875a7cbbfe3d245c to your computer and use it in GitHub Desktop.
Create a MRZ for ID cards
#!/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