You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1329 lines
48 KiB

<?php
/*
* BarCode Coder Library (BCC Library)
* BCCL Version 2.0
*
* Porting : PHP
* Version : 2.0.3
*
* Date : 2013-01-06
* Author : DEMONTE Jean-Baptiste <jbdemonte@gmail.com>
* HOUREZ Jonathan
*
* Web site: https://barcode-coder.com/
* dual licence : https://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
* https://www.gnu.org/licenses/gpl.html
* BarCode Coder Library
* @package BCC Library
* @author DEMONTE Jean-Baptiste <jbdemonte@gmail.com>
* @author HOUREZ Jonathan
* @copyright 2013
*
* Added to Openemr by Terry Hill terry@lillysystems.com
* this is from the barcode-coder website
*
*/
class Barcode
{
public static function gd($res, $color, $x, $y, $angle, $type, $datas, $width = null, $height = null)
{
return self::_draw(__FUNCTION__, $res, $color, $x, $y, $angle, $type, $datas, $width, $height);
}
public static function fpdf($res, $color, $x, $y, $angle, $type, $datas, $width = null, $height = null)
{
return self::_draw(__FUNCTION__, $res, $color, $x, $y, $angle, $type, $datas, $width, $height);
}
private static function _draw($call, $res, $color, $x, $y, $angle, $type, $datas, $width, $height)
{
$digit = '';
$hri = '';
$code = '';
$crc = true;
$rect = false;
$b2d = false;
if (is_array($datas)) {
foreach (array('code' => '', 'crc' => true, 'rect' => false) as $v => $def) {
$$v = isset($datas[$v]) ? $datas[$v] : $def;
}
$code = $code;
} else {
$code = $datas;
}
if ($code == '') {
return false;
}
$code = (string) $code;
$type = strtolower($type);
switch ($type) {
case 'std25':
case 'int25':
$digit = BarcodeI25::getDigit($code, $crc, $type);
$hri = BarcodeI25::compute($code, $crc, $type);
break;
case 'ean8':
case 'ean13':
$digit = BarcodeEAN::getDigit($code, $type);
$hri = BarcodeEAN::compute($code, $type);
break;
case 'upc':
$digit = BarcodeUPC::getDigit($code);
$hri = BarcodeUPC::compute($code);
break;
case 'code11':
$digit = Barcode11::getDigit($code);
$hri = $code;
break;
case 'code39':
$digit = Barcode39::getDigit($code);
$hri = $code;
break;
case 'code93':
$digit = Barcode93::getDigit($code, $crc);
$hri = $code;
break;
case 'code128':
$digit = Barcode128::getDigit($code);
$hri = $code;
break;
case 'codabar':
$digit = BarcodeCodabar::getDigit($code);
$hri = $code;
break;
case 'msi':
$digit = BarcodeMSI::getDigit($code, $crc);
$hri = BarcodeMSI::compute($code, $crc);
break;
case 'datamatrix':
$digit = BarcodeDatamatrix::getDigit($code, $rect);
$hri = $code;
$b2d = true;
break;
}
if ($digit == '') {
return false;
}
if ($b2d) {
$width = is_null($width) ? 5 : $width;
$height = $width;
} else {
$width = is_null($width) ? 1 : $width;
$height = is_null($height) ? 50 : $height;
$digit = self::bitStringTo2DArray($digit);
}
if ($call == 'gd') {
$result = self::digitToGDRenderer($res, $color, $x, $y, $angle, $width, $height, $digit);
} elseif ($call == 'fpdf') {
$result = self::digitToFPDFRenderer($res, $color, $x, $y, $angle, $width, $height, $digit);
}
$result['hri'] = $hri;
return $result;
}
// convert a bit string to an array of array of bit char
private static function bitStringTo2DArray($digit)
{
$d = array();
$len = strlen($digit);
for ($i = 0; $i < $len; $i++) {
$d[$i] = $digit[$i];
}
return(array($d));
}
private static function digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit)
{
$lines = count($digit);
$columns = count($digit[0]);
$angle = deg2rad(-$angle);
$cos = cos($angle);
$sin = sin($angle);
self::_rotate($columns * $mw / 2, $lines * $mh / 2, $cos, $sin, $x, $y);
$xi -= $x;
$yi -= $y;
for ($y = 0; $y < $lines; $y++) {
$x = -1;
while ($x < $columns) {
$x++;
if ($digit[$y][$x] == '1') {
$z = $x;
while (($z + 1 < $columns) && ($digit[$y][$z + 1] == '1')) {
$z++;
}
$x1 = $x * $mw;
$y1 = $y * $mh;
$x2 = ($z + 1) * $mw;
$y2 = ($y + 1) * $mh;
self::_rotate($x1, $y1, $cos, $sin, $xA, $yA);
self::_rotate($x2, $y1, $cos, $sin, $xB, $yB);
self::_rotate($x2, $y2, $cos, $sin, $xC, $yC);
self::_rotate($x1, $y2, $cos, $sin, $xD, $yD);
$fn(array(
$xA + $xi, $yA + $yi,
$xB + $xi, $yB + $yi,
$xC + $xi, $yC + $yi,
$xD + $xi, $yD + $yi
));
$x = $z + 1;
}
}
}
return self::result($xi, $yi, $columns, $lines, $mw, $mh, $cos, $sin);
}
// GD barcode renderer
private static function digitToGDRenderer($gd, $color, $xi, $yi, $angle, $mw, $mh, $digit)
{
$fn = function ($points) use ($gd, $color) {
imagefilledpolygon($gd, $points, 4, $color);
};
return self::digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit);
}
// FPDF barcode renderer
private static function digitToFPDFRenderer($pdf, $color, $xi, $yi, $angle, $mw, $mh, $digit)
{
if (!is_array($color)) {
if (preg_match('`([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})`i', $color, $m)) {
$color = array(hexdec($m[1]),hexdec($m[2]),hexdec($m[3]));
} else {
$color = array(0,0,0);
}
}
$color = array_values($color);
$pdf->SetDrawColor($color[0], $color[1], $color[2]);
$pdf->SetFillColor($color[0], $color[1], $color[2]);
$fn = function ($points) use ($pdf) {
$op = 'f';
$h = $pdf->h;
$k = $pdf->k;
$points_string = '';
for ($i = 0; $i < 8; $i += 2) {
$points_string .= sprintf('%.2F %.2F', $points[$i] * $k, ($h - $points[$i + 1]) * $k);
$points_string .= $i ? ' l ' : ' m ';
}
$pdf->_out($points_string . $op);
};
return self::digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit);
}
private static function result($xi, $yi, $columns, $lines, $mw, $mh, $cos, $sin)
{
self::_rotate(0, 0, $cos, $sin, $x1, $y1);
self::_rotate($columns * $mw, 0, $cos, $sin, $x2, $y2);
self::_rotate($columns * $mw, $lines * $mh, $cos, $sin, $x3, $y3);
self::_rotate(0, $lines * $mh, $cos, $sin, $x4, $y4);
return array(
'width' => $columns * $mw,
'height' => $lines * $mh,
'p1' => array(
'x' => $xi + $x1,
'y' => $yi + $y1
),
'p2' => array(
'x' => $xi + $x2,
'y' => $yi + $y2
),
'p3' => array(
'x' => $xi + $x3,
'y' => $yi + $y3
),
'p4' => array(
'x' => $xi + $x4,
'y' => $yi + $y4
)
);
}
private static function _rotate($x1, $y1, $cos, $sin, &$x, &$y)
{
$x = $x1 * $cos - $y1 * $sin;
$y = $x1 * $sin + $y1 * $cos;
}
public static function rotate($x1, $y1, $angle, &$x, &$y)
{
$angle = deg2rad(-$angle);
$cos = cos($angle);
$sin = sin($angle);
$x = $x1 * $cos - $y1 * $sin;
$y = $x1 * $sin + $y1 * $cos;
}
}
class BarcodeI25
{
static private $encoding = array('NNWWN', 'WNNNW', 'NWNNW', 'WWNNN', 'NNWNW', 'WNWNN', 'NWWNN', 'NNNWW', 'WNNWN','NWNWN');
public static function compute($code, $crc, $type)
{
if (! $crc) {
if (strlen($code) % 2) {
$code = '0' . $code;
}
} else {
if (($type == 'int25') && (strlen($code) % 2 == 0)) {
$code = '0' . $code;
}
$odd = true;
$sum = 0;
for ($i = strlen($code) - 1; $i > -1; $i--) {
$v = intval($code[$i]);
$sum += $odd ? 3 * $v : $v;
$odd = ! $odd;
}
$code .= (string) ((10 - $sum % 10) % 10);
}
return($code);
}
public static function getDigit($code, $crc, $type)
{
$code = self::compute($code, $crc, $type);
if ($code == '') {
return($code);
}
$result = '';
if ($type == 'int25') { // Interleaved 2 of 5
// start
$result .= '1010';
// digits + CRC
$end = strlen($code) / 2;
for ($i = 0; $i < $end; $i++) {
$c1 = $code[2 * $i];
$c2 = $code[2 * $i + 1];
for ($j = 0; $j < 5; $j++) {
$result .= '1';
if (self::$encoding[$c1][$j] == 'W') {
$result .= '1';
}
$result .= '0';
if (self::$encoding[$c2][$j] == 'W') {
$result .= '0';
}
}
}
// stop
$result .= '1101';
} elseif ($type == 'std25') {
// Standard 2 of 5 is a numeric-only barcode that has been in use a long time.
// Unlike Interleaved 2 of 5, all of the information is encoded in the bars; the spaces are fixed width and are used only to separate the bars.
// The code is self-checking and does not include a checksum.
// start
$result .= '11011010';
// digits + CRC
$end = strlen($code);
for ($i = 0; $i < $end; $i++) {
$c = $code[$i];
for ($j = 0; $j < 5; $j++) {
$result .= '1';
if (self::$encoding[$c][$j] == 'W') {
$result .= '11';
}
$result .= '0';
}
}
// stop
$result .= '11010110';
}
return($result);
}
}
class BarcodeEAN
{
static private $encoding = array(
array('0001101', '0100111', '1110010'),
array('0011001', '0110011', '1100110'),
array('0010011', '0011011', '1101100'),
array('0111101', '0100001', '1000010'),
array('0100011', '0011101', '1011100'),
array('0110001', '0111001', '1001110'),
array('0101111', '0000101', '1010000'),
array('0111011', '0010001', '1000100'),
array('0110111', '0001001', '1001000'),
array('0001011', '0010111', '1110100')
);
static private $first = array('000000','001011','001101','001110','010011','011001','011100','010101','010110','011010');
public static function getDigit($code, $type)
{
// Check len (12 for ean13, 7 for ean8)
$len = $type == 'ean8' ? 7 : 12;
$code = substr($code, 0, $len);
if (!preg_match('`[0-9]{' . $len . '}`', $code)) {
return('');
}
// get checksum
$code = self::compute($code, $type);
// process analyse
$result = '101'; // start
if ($type == 'ean8') {
// process left part
for ($i = 0; $i < 4; $i++) {
$result .= self::$encoding[intval($code[$i])][0];
}
// center guard bars
$result .= '01010';
// process right part
for ($i = 4; $i < 8; $i++) {
$result .= self::$encoding[intval($code[$i])][2];
}
} else { // ean13
// extract first digit and get sequence
$seq = self::$first[ intval($code[0]) ];
// process left part
for ($i = 1; $i < 7; $i++) {
$result .= self::$encoding[intval($code[$i])][ intval($seq[$i - 1]) ];
}
// center guard bars
$result .= '01010';
// process right part
for ($i = 7; $i < 13; $i++) {
$result .= self::$encoding[intval($code[$i])][ 2 ];
}
} // ean13
$result .= '101'; // stop
return($result);
}
public static function compute($code, $type)
{
$len = $type == 'ean13' ? 12 : 7;
$code = substr($code, 0, $len);
if (!preg_match('`[0-9]{' . $len . '}`', $code)) {
return('');
}
$sum = 0;
$odd = true;
for ($i = $len - 1; $i > -1; $i--) {
$sum += ($odd ? 3 : 1) * intval($code[$i]);
$odd = ! $odd;
}
return($code . ( (string) ((10 - $sum % 10) % 10)));
}
}
class BarcodeUPC
{
public static function getDigit($code)
{
if (strlen($code) < 12) {
$code = '0' . $code;
}
return BarcodeEAN::getDigit($code, 'ean13');
}
public static function compute($code)
{
if (strlen($code) < 12) {
$code = '0' . $code;
}
return substr(BarcodeEAN::compute($code, 'ean13'), 1);
}
}
class BarcodeMSI
{
static private $encoding = array(
'100100100100', '100100100110', '100100110100', '100100110110',
'100110100100', '100110100110', '100110110100', '100110110110',
'110100100100', '110100100110');
public static function compute($code, $crc)
{
if (is_array($crc)) {
if ($crc['crc1'] == 'mod10') {
$code = self::computeMod10($code);
} elseif ($crc['crc1'] == 'mod11') {
$code = self::computeMod11($code);
}
if ($crc['crc2'] == 'mod10') {
$code = self::computeMod10($code);
} elseif ($crc['crc2'] == 'mod11') {
$code = self::computeMod11($code);
}
} elseif ($crc) {
$code = self::computeMod10($code);
}
return($code);
}
private static function computeMod10($code)
{
$len = strlen($code);
$toPart1 = $len % 2;
$n1 = 0;
$sum = 0;
for ($i = 0; $i < $len; $i++) {
if ($toPart1) {
$n1 = 10 * $n1 + intval($code[$i]);
} else {
$sum += intval($code[$i]);
}
$toPart1 = ! $toPart1;
}
$s1 = (string) (2 * $n1);
$len = strlen($s1);
for ($i = 0; $i < $len; $i++) {
$sum += intval($s1[$i]);
}
return($code . ( (string) (10 - $sum % 10) % 10));
}
private static function computeMod11($code)
{
$sum = 0;
$weight = 2;
for ($i = strlen($code) - 1; $i > -1; $i--) {
$sum += $weight * intval($code[$i]);
$weight = $weight == 7 ? 2 : $weight + 1;
}
return($code . ( (string) (11 - $sum % 11) % 11) );
}
public static function getDigit($code, $crc)
{
if (preg_match('`[^0-9]`', $code)) {
return '';
}
$index = 0;
$result = '';
$code = self::compute($code, false);
// start
$result = '110';
// digits
$len = strlen($code);
for ($i = 0; $i < $len; $i++) {
$result .= self::$encoding[ intval($code[$i]) ];
}
// stop
$result .= '1001';
return($result);
}
}
class Barcode11
{
static private $encoding = array(
'101011', '1101011', '1001011', '1100101',
'1011011', '1101101', '1001101', '1010011',
'1101001', '110101', '101101');
public static function getDigit($code)
{
if (preg_match('`[^0-9\-]`', $code)) {
return '';
}
$result = '';
$intercharacter = '0';
// start
$result = '1011001' . $intercharacter;
// digits
$len = strlen($code);
for ($i = 0; $i < $len; $i++) {
$index = $code[$i] == '-' ? 10 : intval($code[$i]);
$result .= self::$encoding[ $index ] . $intercharacter;
}
// checksum
$weightC = 0;
$weightSumC = 0;
$weightK = 1; // start at 1 because the right-most character is 'C' checksum
$weightSumK = 0;
for ($i = $len - 1; $i > -1; $i--) {
$weightC = $weightC == 10 ? 1 : $weightC + 1;
$weightK = $weightK == 10 ? 1 : $weightK + 1;
$index = $code[$i] == '-' ? 10 : intval($code[$i]);
$weightSumC += $weightC * $index;
$weightSumK += $weightK * $index;
}
$c = $weightSumC % 11;
$weightSumK += $c;
$k = $weightSumK % 11;
$result .= self::$encoding[$c] . $intercharacter;
if ($len >= 10) {
$result .= self::$encoding[$k] . $intercharacter;
}
// stop
$result .= '1011001';
return($result);
}
}
class Barcode39
{
static private $encoding = array(
'101001101101', '110100101011', '101100101011', '110110010101',
'101001101011', '110100110101', '101100110101', '101001011011',
'110100101101', '101100101101', '110101001011', '101101001011',
'110110100101', '101011001011', '110101100101', '101101100101',
'101010011011', '110101001101', '101101001101', '101011001101',
'110101010011', '101101010011', '110110101001', '101011010011',
'110101101001', '101101101001', '101010110011', '110101011001',
'101101011001', '101011011001', '110010101011', '100110101011',
'110011010101', '100101101011', '110010110101', '100110110101',
'100101011011', '110010101101', '100110101101', '100100100101',
'100100101001', '100101001001', '101001001001', '100101101101');
public static function getDigit($code)
{
$table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*';
$result = '';
$intercharacter = '0';
if (strpos($code, '*') !== false) {
return('');
}
// Add Start and Stop charactere : *
$code = strtoupper('*' . $code . '*');
$len = strlen($code);
for ($i = 0; $i < $len; $i++) {
$index = strpos($table, $code[$i]);
if ($index === false) {
return('');
}
if ($i > 0) {
$result .= $intercharacter;
}
$result .= self::$encoding[ $index ];
}
return($result);
}
}
class Barcode93
{
static private $encoding = array(
'100010100', '101001000', '101000100', '101000010',
'100101000', '100100100', '100100010', '101010000',
'100010010', '100001010', '110101000', '110100100',
'110100010', '110010100', '110010010', '110001010',
'101101000', '101100100', '101100010', '100110100',
'100011010', '101011000', '101001100', '101000110',
'100101100', '100010110', '110110100', '110110010',
'110101100', '110100110', '110010110', '110011010',
'101101100', '101100110', '100110110', '100111010',
'100101110', '111010100', '111010010', '111001010',
'101101110', '101110110', '110101110', '100100110',
'111011010', '111010110', '100110010', '101011110');
public static function getDigit($code, $crc)
{
$table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%____*'; // _ => ($), (%), (/) et (+)
$result = '';
if (strpos($code, '*') !== false) {
return('');
}
$code = strtoupper($code);
// start : *
$result .= self::$encoding[47];
// digits
$len = strlen($code);
for ($i = 0; $i < $len; $i++) {
$c = $code[$i];
$index = strpos($table, $c);
if (($c == '_') || ($index === false)) {
return('');
}
$result .= self::$encoding[ $index ];
}
// checksum
if ($crc) {
$weightC = 0;
$weightSumC = 0;
$weightK = 1; // start at 1 because the right-most character is 'C' checksum
$weightSumK = 0;
for ($i = $len - 1; $i > -1; $i--) {
$weightC = $weightC == 20 ? 1 : $weightC + 1;
$weightK = $weightK == 15 ? 1 : $weightK + 1;
$index = strpos($table, $code[$i]);
$weightSumC += $weightC * $index;
$weightSumK += $weightK * $index;
}
$c = $weightSumC % 47;
$weightSumK += $c;
$k = $weightSumK % 47;
$result .= self::$encoding[$c];
$result .= self::$encoding[$k];
}
// stop : *
$result .= self::$encoding[47];
// Terminaison bar
$result .= '1';
return($result);
}
}
class Barcode128
{
static private $encoding = array(
'11011001100', '11001101100', '11001100110', '10010011000',
'10010001100', '10001001100', '10011001000', '10011000100',
'10001100100', '11001001000', '11001000100', '11000100100',
'10110011100', '10011011100', '10011001110', '10111001100',
'10011101100', '10011100110', '11001110010', '11001011100',
'11001001110', '11011100100', '11001110100', '11101101110',
'11101001100', '11100101100', '11100100110', '11101100100',
'11100110100', '11100110010', '11011011000', '11011000110',
'11000110110', '10100011000', '10001011000', '10001000110',
'10110001000', '10001101000', '10001100010', '11010001000',
'11000101000', '11000100010', '10110111000', '10110001110',
'10001101110', '10111011000', '10111000110', '10001110110',
'11101110110', '11010001110', '11000101110', '11011101000',
'11011100010', '11011101110', '11101011000', '11101000110',
'11100010110', '11101101000', '11101100010', '11100011010',
'11101111010', '11001000010', '11110001010', '10100110000',
'10100001100', '10010110000', '10010000110', '10000101100',
'10000100110', '10110010000', '10110000100', '10011010000',
'10011000010', '10000110100', '10000110010', '11000010010',
'11001010000', '11110111010', '11000010100', '10001111010',
'10100111100', '10010111100', '10010011110', '10111100100',
'10011110100', '10011110010', '11110100100', '11110010100',
'11110010010', '11011011110', '11011110110', '11110110110',
'10101111000', '10100011110', '10001011110', '10111101000',
'10111100010', '11110101000', '11110100010', '10111011110',
'10111101110', '11101011110', '11110101110', '11010000100',
'11010010000', '11010011100', '11000111010');
public static function getDigit($code)
{
$tableB = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
$result = "";
$sum = 0;
$isum = 0;
$i = 0;
$j = 0;
$value = 0;
// check each characters
$len = strlen($code);
for ($i = 0; $i < $len; $i++) {
if (strpos($tableB, $code[$i]) === false) {
return("");
}
}
// check firsts characters : start with C table only if enought numeric
$tableCActivated = $len > 1;
$c = '';
for ($i = 0; $i < 3 && $i < $len; $i++) {
$tableCActivated &= preg_match('`[0-9]`', $code[$i]);
}
$sum = $tableCActivated ? 105 : 104;
// start : [105] : C table or [104] : B table
$result = self::$encoding[ $sum ];
$i = 0;
while ($i < $len) {
if (! $tableCActivated) {
$j = 0;
// check next character to activate C table if interresting
while (($i + $j < $len) && preg_match('`[0-9]`', $code[$i + $j])) {
$j++;
}
// 6 min everywhere or 4 mini at the end
$tableCActivated = ($j > 5) || (($i + $j - 1 == $len) && ($j > 3));
if ($tableCActivated) {
$result .= self::$encoding[ 99 ]; // C table
$sum += ++$isum * 99;
}
// 2 min for table C so need table B
} elseif (($i == $len - 1) || (preg_match('`[^0-9]`', $code[$i])) || (preg_match('`[^0-9]`', $code[$i + 1]))) { //todo : verifier le JS : len - 1!!! XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$tableCActivated = false;
$result .= self::$encoding[ 100 ]; // B table
$sum += ++$isum * 100;
}
if ($tableCActivated) {
$value = intval(substr($code, $i, 2)); // Add two characters (numeric)
$i += 2;
} else {
$value = strpos($tableB, $code[$i]); // Add one character
$i++;
}
$result .= self::$encoding[ $value ];
$sum += ++$isum * $value;
}
// Add CRC
$result .= self::$encoding[ $sum % 103 ];
// Stop
$result .= self::$encoding[ 106 ];
// Termination bar
$result .= '11';
return($result);
}
}
class BarcodeCodabar
{
static private $encoding = array(
'101010011', '101011001', '101001011', '110010101',
'101101001', '110101001', '100101011', '100101101',
'100110101', '110100101', '101001101', '101100101',
'1101011011', '1101101011', '1101101101', '1011011011',
'1011001001', '1010010011', '1001001011', '1010011001');
public static function getDigit($code)
{
$table = '0123456789-$:/.+';
$result = '';
$intercharacter = '0';
// add start : A->D : arbitrary choose A
$result .= self::$encoding[16] . $intercharacter;
$len = strlen($code);
for ($i = 0; $i < $len; $i++) {
$index = strpos($table, $code[$i]);
if ($index === false) {
return('');
}
$result .= self::$encoding[ $index ] . $intercharacter;
}
// add stop : A->D : arbitrary choose A
$result .= self::$encoding[16];
return($result);
}
}
class BarcodeDatamatrix
{
static private $lengthRows = array(
10, 12, 14, 16, 18, 20, 22, 24, 26, // 24 squares et 6 rectangular
32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144,
8, 8, 12, 12, 16, 16);
static private $lengthCols = array(
10, 12, 14, 16, 18, 20, 22, 24, 26, // Number of columns for the entire datamatrix
32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144,
18, 32, 26, 36, 36, 48);
static private $dataCWCount = array(
3, 5, 8, 12, 18, 22, 30, 36, // Number of data codewords for the datamatrix
44, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050,
1304, 1558, 5, 10, 16, 22, 32, 49);
static private $solomonCWCount = array(
5, 7, 10, 12, 14, 18, 20, 24, 28, // Number of Reed-Solomon codewords for the datamatrix
36, 42, 48, 56, 68, 84, 112, 144, 192, 224, 272, 336, 408, 496, 620,
7, 11, 14, 18, 24, 28);
static private $dataRegionRows = array(
8, 10, 12, 14, 16, 18, 20, 22, // Number of rows per region
24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
6, 6, 10, 10, 14, 14);
static private $dataRegionCols = array(
8, 10, 12, 14, 16, 18, 20, 22, // Number of columns per region
24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
16, 14, 24, 16, 16, 22);
static private $regionRows = array(
1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per row
1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
1, 1, 1, 1, 1, 1);
static private $regionCols = array(
1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per column
1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
1, 2, 1, 2, 2, 2);
static private $interleavedBlocks = array(
1, 1, 1, 1, 1, 1, 1, 1, // Number of blocks
1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 6, 6, 8, 8,
1, 1, 1, 1, 1, 1);
static private $logTab = array(
-255, 255, 1, 240, 2, 225, 241, 53, 3, // Table of log for the Galois field
38, 226, 133, 242, 43, 54, 210, 4, 195, 39, 114, 227, 106, 134, 28,
243, 140, 44, 23, 55, 118, 211, 234, 5, 219, 196, 96, 40, 222, 115,
103, 228, 78, 107, 125, 135, 8, 29, 162, 244, 186, 141, 180, 45, 99,
24, 49, 56, 13, 119, 153, 212, 199, 235, 91, 6, 76, 220, 217, 197,
11, 97, 184, 41, 36, 223, 253, 116, 138, 104, 193, 229, 86, 79, 171,
108, 165, 126, 145, 136, 34, 9, 74, 30, 32, 163, 84, 245, 173, 187,
204, 142, 81, 181, 190, 46, 88, 100, 159, 25, 231, 50, 207, 57, 147,
14, 67, 120, 128, 154, 248, 213, 167, 200, 63, 236, 110, 92, 176, 7,
161, 77, 124, 221, 102, 218, 95, 198, 90, 12, 152, 98, 48, 185, 179,
42, 209, 37, 132, 224, 52, 254, 239, 117, 233, 139, 22, 105, 27, 194,
113, 230, 206, 87, 158, 80, 189, 172, 203, 109, 175, 166, 62, 127,
247, 146, 66, 137, 192, 35, 252, 10, 183, 75, 216, 31, 83, 33, 73,
164, 144, 85, 170, 246, 65, 174, 61, 188, 202, 205, 157, 143, 169, 82,
72, 182, 215, 191, 251, 47, 178, 89, 151, 101, 94, 160, 123, 26, 112,
232, 21, 51, 238, 208, 131, 58, 69, 148, 18, 15, 16, 68, 17, 121, 149,
129, 19, 155, 59, 249, 70, 214, 250, 168, 71, 201, 156, 64, 60, 237,
130, 111, 20, 93, 122, 177, 150);
static private $aLogTab = array(
1, 2, 4, 8, 16, 32, 64, 128, 45, 90, // Table of aLog for the Galois field
180, 69, 138, 57, 114, 228, 229, 231, 227, 235, 251, 219, 155, 27, 54,
108, 216, 157, 23, 46, 92, 184, 93, 186, 89, 178, 73, 146, 9, 18, 36,
72, 144, 13, 26, 52, 104, 208, 141, 55, 110, 220, 149, 7, 14, 28, 56,
112, 224, 237, 247, 195, 171, 123, 246, 193, 175, 115, 230, 225, 239,
243, 203, 187, 91, 182, 65, 130, 41, 82, 164, 101, 202, 185, 95, 190,
81, 162, 105, 210, 137, 63, 126, 252, 213, 135, 35, 70, 140, 53, 106,
212, 133, 39, 78, 156, 21, 42, 84, 168, 125, 250, 217, 159, 19, 38, 76,
152, 29, 58, 116, 232, 253, 215, 131, 43, 86, 172, 117, 234, 249, 223,
147, 11, 22, 44, 88, 176, 77, 154, 25, 50, 100, 200, 189, 87, 174, 113,
226, 233, 255, 211, 139, 59, 118, 236, 245, 199, 163, 107, 214, 129,
47, 94, 188, 85, 170, 121, 242, 201, 191, 83, 166, 97, 194, 169, 127,
254, 209, 143, 51, 102, 204, 181, 71, 142, 49, 98, 196, 165, 103, 206,
177, 79, 158, 17, 34, 68, 136, 61, 122, 244, 197, 167, 99, 198, 161,
111, 222, 145, 15, 30, 60, 120, 240, 205, 183, 67, 134, 33, 66, 132,
37, 74, 148, 5, 10, 20, 40, 80, 160, 109, 218, 153, 31, 62, 124, 248,
221, 151, 3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75,
150, 1);
private static function champGaloisMult($a, $b)
{
// MULTIPLICATION IN GALOIS FIELD GF(2^8)
if (!$a || !$b) {
return 0;
}
return self::$aLogTab[(self::$logTab[$a] + self::$logTab[$b]) % 255];
}
private static function champGaloisDoub($a, $b)
{
// THE OPERATION a * 2^b IN GALOIS FIELD GF(2^8)
if (!$a) {
return 0;
}
if (!$b) {
return $a;
}
return self::$aLogTab[(self::$logTab[$a] + $b) % 255];
}
private static function champGaloisSum($a, $b)
{
// SUM IN GALOIS FIELD GF(2^8)
return $a ^ $b;
}
private static function selectIndex($dataCodeWordsCount, $rectangular)
{
// CHOOSE THE GOOD INDEX FOR TABLES
if (($dataCodeWordsCount < 1 || $dataCodeWordsCount > 1558) && !$rectangular) {
return -1;
}
if (($dataCodeWordsCount < 1 || $dataCodeWordsCount > 49) && $rectangular) {
return -1;
}
$n = $rectangular ? 24 : 0;
while (self::$dataCWCount[$n] < $dataCodeWordsCount) {
$n++;
}
return $n;
}
private static function encodeDataCodeWordsASCII($text)
{
$dataCodeWords = array();
$n = 0;
$len = strlen($text);
for ($i = 0; $i < $len; $i++) {
$c = ord($text[$i]);
if ($c > 127) {
$dataCodeWords[$n] = 235;
$c -= 127;
$n++;
} elseif (($c >= 48 && $c <= 57) && ($i + 1 < $len) && (preg_match('`[0-9]`', $text[$i + 1]))) {
$c = (($c - 48) * 10) + intval($text[$i + 1]);
$c += 130;
$i++;
} else {
$c++;
}
$dataCodeWords[$n] = $c;
$n++;
}
return $dataCodeWords;
}
private static function addPadCW(&$tab, $from, $to)
{
if ($from >= $to) {
return ;
}
$tab[$from] = 129;
for ($i = $from + 1; $i < $to; $i++) {
$r = ((149 * ($i + 1)) % 253) + 1;
$tab[$i] = (129 + $r) % 254;
}
}
private static function calculSolFactorTable($solomonCWCount)
{
// CALCULATE THE REED SOLOMON FACTORS
$g = array_fill(0, $solomonCWCount + 1, 1);
for ($i = 1; $i <= $solomonCWCount; $i++) {
for ($j = $i - 1; $j >= 0; $j--) {
$g[$j] = self::champGaloisDoub($g[$j], $i);
if ($j > 0) {
$g[$j] = self::champGaloisSum($g[$j], $g[$j - 1]);
}
}
}
return $g;
}
private static function addReedSolomonCW($nSolomonCW, $coeffTab, $nDataCW, &$dataTab, $blocks)
{
// Add the Reed Solomon codewords
$errorBlocks = $nSolomonCW / $blocks;
$correctionCW = array();
for ($k = 0; $k < $blocks; $k++) {
for ($i = 0; $i < $errorBlocks; $i++) {
$correctionCW[$i] = 0;
}
for ($i = $k; $i < $nDataCW; $i += $blocks) {
$temp = self::champGaloisSum($dataTab[$i], $correctionCW[$errorBlocks - 1]);
for ($j = $errorBlocks - 1; $j >= 0; $j--) {
if (!$temp) {
$correctionCW[$j] = 0;
} else {
$correctionCW[$j] = self::champGaloisMult($temp, $coeffTab[$j]);
}
if ($j > 0) {
$correctionCW[$j] = self::champGaloisSum($correctionCW[$j - 1], $correctionCW[$j]);
}
}
}
// Renversement des blocs calcules
$j = $nDataCW + $k;
for ($i = $errorBlocks - 1; $i >= 0; $i--) {
$dataTab[$j] = $correctionCW[$i];
$j = $j + $blocks;
}
}
return $dataTab;
}
private static function getBits($entier)
{
// Transform integer to tab of bits
$bits = array();
for ($i = 0; $i < 8; $i++) {
$bits[$i] = $entier & (128 >> $i) ? 1 : 0;
}
return $bits;
}
private static function next($etape, $totalRows, $totalCols, $codeWordsBits, &$datamatrix, &$assigned)
{
// Place codewords into the matrix
$chr = 0; // Place of the 8st bit from the first character to [4][0]
$row = 4;
$col = 0;
do {
// Check for a special case of corner
if (($row == $totalRows) && ($col == 0)) {
self::patternShapeSpecial1($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
$chr++;
} elseif (($etape < 3) && ($row == $totalRows - 2) && ($col == 0) && ($totalCols % 4 != 0)) {
self::patternShapeSpecial2($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
$chr++;
} elseif (($row == $totalRows - 2) && ($col == 0) && ($totalCols % 8 == 4)) {
self::patternShapeSpecial3($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
$chr++;
} elseif (($row == $totalRows + 4) && ($col == 2) && ($totalCols % 8 == 0)) {
self::patternShapeSpecial4($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
$chr++;
}
// Go up and right in the datamatrix
do {
if (($row < $totalRows) && ($col >= 0) && (!isset($assigned[$row][$col]) || $assigned[$row][$col] != 1)) {
self::patternShapeStandard($datamatrix, $assigned, $codeWordsBits[$chr], $row, $col, $totalRows, $totalCols);
$chr++;
}
$row -= 2;
$col += 2;
} while (($row >= 0) && ($col < $totalCols));
$row += 1;
$col += 3;
// Go down and left in the datamatrix
do {
if (($row >= 0) && ($col < $totalCols) && (!isset($assigned[$row][$col]) || $assigned[$row][$col] != 1)) {
self::patternShapeStandard($datamatrix, $assigned, $codeWordsBits[$chr], $row, $col, $totalRows, $totalCols);
$chr++;
}
$row += 2;
$col -= 2;
} while (($row < $totalRows) && ($col >= 0));
$row += 3;
$col += 1;
} while (($row < $totalRows) || ($col < $totalCols));
}
private static function patternShapeStandard(&$datamatrix, &$assigned, $bits, $row, $col, $totalRows, $totalCols)
{
// Place bits in the matrix (standard or special case)
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $row - 2, $col - 2, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $row - 2, $col - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $row - 1, $col - 2, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], $row - 1, $col - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], $row - 1, $col, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], $row, $col - 2, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], $row, $col - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], $row, $col, $totalRows, $totalCols);
}
private static function patternShapeSpecial1(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
{
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows - 1, 0, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows - 1, 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows - 1, 2, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols - 2, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 2, $totalCols - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 3, $totalCols - 1, $totalRows, $totalCols);
}
private static function patternShapeSpecial2(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
{
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows - 3, 0, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows - 2, 0, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows - 1, 0, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols - 4, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols - 3, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 0, $totalCols - 2, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 0, $totalCols - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 1, $totalCols - 1, $totalRows, $totalCols);
}
private static function patternShapeSpecial3(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
{
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows - 3, 0, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows - 2, 0, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows - 1, 0, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols - 2, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 2, $totalCols - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 3, $totalCols - 1, $totalRows, $totalCols);
}
private static function patternShapeSpecial4(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
{
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows - 1, 0, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows - 1, $totalCols - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], 0, $totalCols - 3, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols - 2, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols - 1, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols - 3, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 1, $totalCols - 2, $totalRows, $totalCols);
self::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 1, $totalCols - 1, $totalRows, $totalCols);
}
private static function placeBitInDatamatrix(&$datamatrix, &$assigned, $bit, $row, $col, $totalRows, $totalCols)
{
// Put a bit into the matrix
if ($row < 0) {
$row += $totalRows;
$col += 4 - (($totalRows + 4) % 8);
}
if ($col < 0) {
$col += $totalCols;
$row += 4 - (($totalCols + 4) % 8);
}
if (!isset($assigned[$row][$col]) || $assigned[$row][$col] != 1) {
$datamatrix[$row][$col] = $bit;
$assigned[$row][$col] = 1;
}
}
private static function addFinderPattern($datamatrix, $rowsRegion, $colsRegion, $rowsRegionCW, $colsRegionCW)
{
// Add the finder pattern
$totalRowsCW = ($rowsRegionCW + 2) * $rowsRegion;
$totalColsCW = ($colsRegionCW + 2) * $colsRegion;
$datamatrixTemp = array();
$datamatrixTemp[0] = array_fill(0, $totalColsCW + 2, 0);
for ($i = 0; $i < $totalRowsCW; $i++) {
$datamatrixTemp[$i + 1] = array();
$datamatrixTemp[$i + 1][0] = 0;
$datamatrixTemp[$i + 1][$totalColsCW + 1] = 0;
for ($j = 0; $j < $totalColsCW; $j++) {
if ($i % ($rowsRegionCW + 2) == 0) {
if ($j % 2 == 0) {
$datamatrixTemp[$i + 1][$j + 1] = 1;
} else {
$datamatrixTemp[$i + 1][$j + 1] = 0;
}
} elseif ($i % ($rowsRegionCW + 2) == $rowsRegionCW + 1) {
$datamatrixTemp[$i + 1][$j + 1] = 1;
} elseif ($j % ($colsRegionCW + 2) == $colsRegionCW + 1) {
if ($i % 2 == 0) {
$datamatrixTemp[$i + 1][$j + 1] = 0;
} else {
$datamatrixTemp[$i + 1][$j + 1] = 1;
}
} elseif ($j % ($colsRegionCW + 2) == 0) {
$datamatrixTemp[$i + 1][$j + 1] = 1;
} else {
$datamatrixTemp[$i + 1][$j + 1] = 0;
$datamatrixTemp[$i + 1][$j + 1] = $datamatrix[$i - 1 - (2 * (floor($i / ($rowsRegionCW + 2))))][$j - 1 - (2 * (floor($j / ($colsRegionCW + 2))))]; // todo : parseInt => ?
}
}
}
$datamatrixTemp[$totalRowsCW + 1] = array();
for ($j = 0; $j < $totalColsCW + 2; $j++) {
$datamatrixTemp[$totalRowsCW + 1][$j] = 0;
}
return $datamatrixTemp;
}
public static function getDigit($text, $rectangular)
{
$dataCodeWords = self::encodeDataCodeWordsASCII($text); // Code the text in the ASCII mode
$dataCWCount = count($dataCodeWords);
$index = self::selectIndex($dataCWCount, $rectangular); // Select the index for the data tables
$totalDataCWCount = self::$dataCWCount[$index]; // Number of data CW
$solomonCWCount = self::$solomonCWCount[$index]; // Number of Reed Solomon CW
$totalCWCount = $totalDataCWCount + $solomonCWCount; // Number of CW
$rowsTotal = self::$lengthRows[$index]; // Size of symbol
$colsTotal = self::$lengthCols[$index];
$rowsRegion = self::$regionRows[$index]; // Number of region
$colsRegion = self::$regionCols[$index];
$rowsRegionCW = self::$dataRegionRows[$index];
$colsRegionCW = self::$dataRegionCols[$index];
$rowsLengthMatrice = $rowsTotal - 2 * $rowsRegion; // Size of matrice data
$colsLengthMatrice = $colsTotal - 2 * $colsRegion;
$blocks = self::$interleavedBlocks[$index]; // Number of Reed Solomon blocks
$errorBlocks = $solomonCWCount / $blocks;
self::addPadCW($dataCodeWords, $dataCWCount, $totalDataCWCount); // Add codewords pads
$g = self::calculSolFactorTable($errorBlocks); // Calculate correction coefficients
self::addReedSolomonCW($solomonCWCount, $g, $totalDataCWCount, $dataCodeWords, $blocks); // Add Reed Solomon codewords
$codeWordsBits = array(); // Calculte bits from codewords
for ($i = 0; $i < $totalCWCount; $i++) {
$codeWordsBits[$i] = self::getBits($dataCodeWords[$i]);
}
$datamatrix = array_fill(0, $colsLengthMatrice, array());
$assigned = array_fill(0, $colsLengthMatrice, array());
// Add the bottom-right corner if needed
if ((($rowsLengthMatrice * $colsLengthMatrice) % 8) == 4) {
$datamatrix[$rowsLengthMatrice - 2][$colsLengthMatrice - 2] = 1;
$datamatrix[$rowsLengthMatrice - 1][$colsLengthMatrice - 1] = 1;
$datamatrix[$rowsLengthMatrice - 1][$colsLengthMatrice - 2] = 0;
$datamatrix[$rowsLengthMatrice - 2][$colsLengthMatrice - 1] = 0;
$assigned[$rowsLengthMatrice - 2][$colsLengthMatrice - 2] = 1;
$assigned[$rowsLengthMatrice - 1][$colsLengthMatrice - 1] = 1;
$assigned[$rowsLengthMatrice - 1][$colsLengthMatrice - 2] = 1;
$assigned[$rowsLengthMatrice - 2][$colsLengthMatrice - 1] = 1;
}
// Put the codewords into the matrix
self::next(0, $rowsLengthMatrice, $colsLengthMatrice, $codeWordsBits, $datamatrix, $assigned);
// Add the finder pattern
$datamatrix = self::addFinderPattern($datamatrix, $rowsRegion, $colsRegion, $rowsRegionCW, $colsRegionCW);
return $datamatrix;
}
}