3 * BarCode Coder Library (BCC Library)
10 * Author : DEMONTE Jean-Baptiste <jbdemonte@gmail.com>
13 * Web site: http://barcode-coder.com/
14 * dual licence : http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
15 * http://www.gnu.org/licenses/gpl.html
16 * BarCode Coder Library
17 * @package BCC Library
18 * @author DEMONTE Jean-Baptiste <jbdemonte@gmail.com>
19 * @author HOUREZ Jonathan
22 * Added to Openemr by Terry Hill terry@lillysystems.com
23 * this is from the barcode-coder website
31 public static function gd($res, $color, $x, $y, $angle, $type, $datas, $width = null, $height = null)
33 return self
::_draw(__FUNCTION__
, $res, $color, $x, $y, $angle, $type, $datas, $width, $height);
36 public static function fpdf($res, $color, $x, $y, $angle, $type, $datas, $width = null, $height = null)
38 return self
::_draw(__FUNCTION__
, $res, $color, $x, $y, $angle, $type, $datas, $width, $height);
41 private static function _draw($call, $res, $color, $x, $y, $angle, $type, $datas, $width, $height)
50 if (is_array($datas)) {
51 foreach (array('code' => '', 'crc' => true, 'rect' => false) as $v => $def) {
52 $
$v = isset($datas[$v]) ?
$datas[$v] : $def;
64 $code = (string) $code;
66 $type = strtolower($type);
71 $digit = BarcodeI25
::getDigit($code, $crc, $type);
72 $hri = BarcodeI25
::compute($code, $crc, $type);
76 $digit = BarcodeEAN
::getDigit($code, $type);
77 $hri = BarcodeEAN
::compute($code, $type);
80 $digit = BarcodeUPC
::getDigit($code);
81 $hri = BarcodeUPC
::compute($code);
84 $digit = Barcode11
::getDigit($code);
88 $digit = Barcode39
::getDigit($code);
92 $digit = Barcode93
::getDigit($code, $crc);
96 $digit = Barcode128
::getDigit($code);
100 $digit = BarcodeCodabar
::getDigit($code);
104 $digit = BarcodeMSI
::getDigit($code, $crc);
105 $hri = BarcodeMSI
::compute($code, $crc);
108 $digit = BarcodeDatamatrix
::getDigit($code, $rect);
119 $width = is_null($width) ?
5 : $width;
122 $width = is_null($width) ?
1 : $width;
123 $height = is_null($height) ?
50 : $height;
124 $digit = self
::bitStringTo2DArray($digit);
128 $result = self
::digitToGDRenderer($res, $color, $x, $y, $angle, $width, $height, $digit);
129 } else if ($call == 'fpdf') {
130 $result = self
::digitToFPDFRenderer($res, $color, $x, $y, $angle, $width, $height, $digit);
133 $result['hri'] = $hri;
137 // convert a bit string to an array of array of bit char
138 private static function bitStringTo2DArray($digit)
141 $len = strlen($digit);
150 private static function digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit)
152 $lines = count($digit);
153 $columns = count($digit[0]);
154 $angle = deg2rad(-$angle);
158 self
::_rotate($columns * $mw / 2, $lines * $mh / 2, $cos, $sin, $x, $y);
161 for ($y=0; $y<$lines; $y++
) {
163 while ($x <$columns) {
165 if ($digit[$y][$x] == '1') {
167 while (($z +
1 <$columns) && ($digit[$y][$z +
1] == '1')) {
173 $x2 = ($z +
1) * $mw;
174 $y2 = ($y +
1) * $mh;
175 self
::_rotate($x1, $y1, $cos, $sin, $xA, $yA);
176 self
::_rotate($x2, $y1, $cos, $sin, $xB, $yB);
177 self
::_rotate($x2, $y2, $cos, $sin, $xC, $yC);
178 self
::_rotate($x1, $y2, $cos, $sin, $xD, $yD);
180 $xA +
$xi, $yA +
$yi,
181 $xB +
$xi, $yB +
$yi,
182 $xC +
$xi, $yC +
$yi,
190 return self
::result($xi, $yi, $columns, $lines, $mw, $mh, $cos, $sin);
193 // GD barcode renderer
194 private static function digitToGDRenderer($gd, $color, $xi, $yi, $angle, $mw, $mh, $digit)
196 $fn = function ($points) use ($gd, $color) {
197 imagefilledpolygon($gd, $points, 4, $color);
199 return self
::digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit);
201 // FPDF barcode renderer
202 private static function digitToFPDFRenderer($pdf, $color, $xi, $yi, $angle, $mw, $mh, $digit)
204 if (!is_array($color)) {
205 if (preg_match('`([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})`i', $color, $m)) {
206 $color = array(hexdec($m[1]),hexdec($m[2]),hexdec($m[3]));
208 $color = array(0,0,0);
212 $color = array_values($color);
213 $pdf->SetDrawColor($color[0], $color[1], $color[2]);
214 $pdf->SetFillColor($color[0], $color[1], $color[2]);
216 $fn = function ($points) use ($pdf) {
221 for ($i=0; $i < 8; $i+
=2) {
222 $points_string .= sprintf('%.2F %.2F', $points[$i]*$k, ($h-$points[$i+
1])*$k);
223 $points_string .= $i ?
' l ' : ' m ';
226 $pdf->_out($points_string . $op);
228 return self
::digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit);
231 private static function result($xi, $yi, $columns, $lines, $mw, $mh, $cos, $sin)
233 self
::_rotate(0, 0, $cos, $sin, $x1, $y1);
234 self
::_rotate($columns * $mw, 0, $cos, $sin, $x2, $y2);
235 self
::_rotate($columns * $mw, $lines * $mh, $cos, $sin, $x3, $y3);
236 self
::_rotate(0, $lines * $mh, $cos, $sin, $x4, $y4);
239 'width' => $columns * $mw,
240 'height'=> $lines * $mh,
260 private static function _rotate($x1, $y1, $cos, $sin, &$x, &$y)
262 $x = $x1 * $cos - $y1 * $sin;
263 $y = $x1 * $sin +
$y1 * $cos;
266 public static function rotate($x1, $y1, $angle, &$x, &$y)
268 $angle = deg2rad(-$angle);
271 $x = $x1 * $cos - $y1 * $sin;
272 $y = $x1 * $sin +
$y1 * $cos;
278 static private $encoding = array('NNWWN', 'WNNNW', 'NWNNW', 'WWNNN', 'NNWNW', 'WNWNN', 'NWWNN', 'NNNWW', 'WNNWN','NWNWN');
280 public static function compute($code, $crc, $type)
283 if (strlen($code) %
2) {
287 if (($type == 'int25') && (strlen($code) %
2 == 0)) {
293 for ($i=strlen($code)-1; $i>-1; $i--) {
294 $v = intval($code[$i]);
295 $sum +
= $odd ?
3 * $v : $v;
299 $code .= (string) ((10 - $sum %
10) %
10);
305 public static function getDigit($code, $crc, $type)
307 $code = self
::compute($code, $crc, $type);
314 if ($type == 'int25') { // Interleaved 2 of 5
319 $end = strlen($code) / 2;
320 for ($i=0; $i<$end; $i++
) {
323 for ($j=0; $j<5; $j++
) {
325 if (self
::$encoding[$c1][$j] == 'W') {
330 if (self
::$encoding[$c2][$j] == 'W') {
338 } else if ($type == 'std25') {
339 // Standard 2 of 5 is a numeric-only barcode that has been in use a long time.
340 // 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.
341 // The code is self-checking and does not include a checksum.
344 $result .= '11011010';
347 $end = strlen($code);
348 for ($i=0; $i<$end; $i++
) {
350 for ($j=0; $j<5; $j++
) {
352 if (self
::$encoding[$c][$j] == 'W') {
361 $result .= '11010110';
371 static private $encoding = array(
372 array('0001101', '0100111', '1110010'),
373 array('0011001', '0110011', '1100110'),
374 array('0010011', '0011011', '1101100'),
375 array('0111101', '0100001', '1000010'),
376 array('0100011', '0011101', '1011100'),
377 array('0110001', '0111001', '1001110'),
378 array('0101111', '0000101', '1010000'),
379 array('0111011', '0010001', '1000100'),
380 array('0110111', '0001001', '1001000'),
381 array('0001011', '0010111', '1110100')
384 static private $first = array('000000','001011','001101','001110','010011','011001','011100','010101','010110','011010');
386 public static function getDigit($code, $type)
388 // Check len (12 for ean13, 7 for ean8)
389 $len = $type == 'ean8' ?
7 : 12;
390 $code = substr($code, 0, $len);
391 if (!preg_match('`[0-9]{'.$len.'}`', $code)) {
396 $code = self
::compute($code, $type);
399 $result = '101'; // start
401 if ($type == 'ean8') {
403 for ($i=0; $i<4; $i++
) {
404 $result .= self
::$encoding[intval($code[$i])][0];
410 // process right part
411 for ($i=4; $i<8; $i++
) {
412 $result .= self
::$encoding[intval($code[$i])][2];
415 // extract first digit and get sequence
416 $seq = self
::$first[ intval($code[0]) ];
419 for ($i=1; $i<7; $i++
) {
420 $result .= self
::$encoding[intval($code[$i])][ intval($seq[$i-1]) ];
426 // process right part
427 for ($i=7; $i<13; $i++
) {
428 $result .= self
::$encoding[intval($code[$i])][ 2 ];
432 $result .= '101'; // stop
436 public static function compute($code, $type)
438 $len = $type == 'ean13' ?
12 : 7;
439 $code = substr($code, 0, $len);
440 if (!preg_match('`[0-9]{'.$len.'}`', $code)) {
446 for ($i=$len-1; $i>-1; $i--) {
447 $sum +
= ($odd ?
3 : 1) * intval($code[$i]);
451 return($code . ( (string) ((10 - $sum %
10) %
10)));
458 public static function getDigit($code)
460 if (strlen($code) < 12) {
464 return BarcodeEAN
::getDigit($code, 'ean13');
467 public static function compute($code)
469 if (strlen($code) < 12) {
473 return substr(BarcodeEAN
::compute($code, 'ean13'), 1);
479 static private $encoding = array(
480 '100100100100', '100100100110', '100100110100', '100100110110',
481 '100110100100', '100110100110', '100110110100', '100110110110',
482 '110100100100', '110100100110');
484 public static function compute($code, $crc)
486 if (is_array($crc)) {
487 if ($crc['crc1'] == 'mod10') {
488 $code = self
::computeMod10($code);
489 } else if ($crc['crc1'] == 'mod11') {
490 $code = self
::computeMod11($code);
493 if ($crc['crc2'] == 'mod10') {
494 $code = self
::computeMod10($code);
495 } else if ($crc['crc2'] == 'mod11') {
496 $code = self
::computeMod11($code);
499 $code = self
::computeMod10($code);
505 private static function computeMod10($code)
507 $len = strlen($code);
511 for ($i=0; $i<$len; $i++
) {
513 $n1 = 10 * $n1 +
intval($code[$i]);
515 $sum +
= intval($code[$i]);
518 $toPart1 = ! $toPart1;
521 $s1 = (string) (2 * $n1);
523 for ($i=0; $i<$len; $i++
) {
524 $sum +
= intval($s1[$i]);
527 return($code . ( (string) (10 - $sum %
10) %
10));
530 private static function computeMod11($code)
534 for ($i=strlen($code)-1; $i>-1; $i--) {
535 $sum +
= $weight * intval($code[$i]);
536 $weight = $weight == 7 ?
2 : $weight +
1;
539 return($code . ( (string) (11 - $sum %
11) %
11) );
542 public static function getDigit($code, $crc)
544 if (preg_match('`[^0-9]`', $code)) {
551 $code = self
::compute($code, false);
557 $len = strlen($code);
558 for ($i=0; $i<$len; $i++
) {
559 $result .= self
::$encoding[ intval($code[$i]) ];
571 static private $encoding = array(
572 '101011', '1101011', '1001011', '1100101',
573 '1011011', '1101101', '1001101', '1010011',
574 '1101001', '110101', '101101');
576 public static function getDigit($code)
578 if (preg_match('`[^0-9\-]`', $code)) {
583 $intercharacter = '0';
586 $result = '1011001' . $intercharacter;
589 $len = strlen($code);
590 for ($i=0; $i<$len; $i++
) {
591 $index = $code[$i] == '-' ?
10 : intval($code[$i]);
592 $result .= self
::$encoding[ $index ] . $intercharacter;
598 $weightK = 1; // start at 1 because the right-most character is 'C' checksum
600 for ($i=$len-1; $i>-1; $i--) {
601 $weightC = $weightC == 10 ?
1 : $weightC +
1;
602 $weightK = $weightK == 10 ?
1 : $weightK +
1;
604 $index = $code[$i] == '-' ?
10 : intval($code[$i]);
606 $weightSumC +
= $weightC * $index;
607 $weightSumK +
= $weightK * $index;
610 $c = $weightSumC %
11;
612 $k = $weightSumK %
11;
614 $result .= self
::$encoding[$c] . $intercharacter;
617 $result .= self
::$encoding[$k] . $intercharacter;
621 $result .= '1011001';
629 static private $encoding = array(
630 '101001101101', '110100101011', '101100101011', '110110010101',
631 '101001101011', '110100110101', '101100110101', '101001011011',
632 '110100101101', '101100101101', '110101001011', '101101001011',
633 '110110100101', '101011001011', '110101100101', '101101100101',
634 '101010011011', '110101001101', '101101001101', '101011001101',
635 '110101010011', '101101010011', '110110101001', '101011010011',
636 '110101101001', '101101101001', '101010110011', '110101011001',
637 '101101011001', '101011011001', '110010101011', '100110101011',
638 '110011010101', '100101101011', '110010110101', '100110110101',
639 '100101011011', '110010101101', '100110101101', '100100100101',
640 '100100101001', '100101001001', '101001001001', '100101101101');
641 public static function getDigit($code)
643 $table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*';
645 $intercharacter = '0';
647 if (strpos($code, '*') !== false) {
651 // Add Start and Stop charactere : *
652 $code = strtoupper('*' . $code . '*');
654 $len = strlen($code);
655 for ($i=0; $i<$len; $i++
) {
656 $index = strpos($table, $code[$i]);
657 if ($index === false) {
662 $result .= $intercharacter;
665 $result .= self
::$encoding[ $index ];
674 static private $encoding = array(
675 '100010100', '101001000', '101000100', '101000010',
676 '100101000', '100100100', '100100010', '101010000',
677 '100010010', '100001010', '110101000', '110100100',
678 '110100010', '110010100', '110010010', '110001010',
679 '101101000', '101100100', '101100010', '100110100',
680 '100011010', '101011000', '101001100', '101000110',
681 '100101100', '100010110', '110110100', '110110010',
682 '110101100', '110100110', '110010110', '110011010',
683 '101101100', '101100110', '100110110', '100111010',
684 '100101110', '111010100', '111010010', '111001010',
685 '101101110', '101110110', '110101110', '100100110',
686 '111011010', '111010110', '100110010', '101011110');
688 public static function getDigit($code, $crc)
690 $table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%____*'; // _ => ($), (%), (/) et (+)
693 if (strpos($code, '*') !== false) {
697 $code = strtoupper($code);
700 $result .= self
::$encoding[47];
703 $len = strlen($code);
704 for ($i=0; $i<$len; $i++
) {
706 $index = strpos($table, $c);
707 if (($c == '_') ||
($index === false)) {
711 $result .= self
::$encoding[ $index ];
718 $weightK = 1; // start at 1 because the right-most character is 'C' checksum
720 for ($i=$len-1; $i>-1; $i--) {
721 $weightC = $weightC == 20 ?
1 : $weightC +
1;
722 $weightK = $weightK == 15 ?
1 : $weightK +
1;
724 $index = strpos($table, $code[$i]);
726 $weightSumC +
= $weightC * $index;
727 $weightSumK +
= $weightK * $index;
730 $c = $weightSumC %
47;
732 $k = $weightSumK %
47;
734 $result .= self
::$encoding[$c];
735 $result .= self
::$encoding[$k];
739 $result .= self
::$encoding[47];
749 static private $encoding = array(
750 '11011001100', '11001101100', '11001100110', '10010011000',
751 '10010001100', '10001001100', '10011001000', '10011000100',
752 '10001100100', '11001001000', '11001000100', '11000100100',
753 '10110011100', '10011011100', '10011001110', '10111001100',
754 '10011101100', '10011100110', '11001110010', '11001011100',
755 '11001001110', '11011100100', '11001110100', '11101101110',
756 '11101001100', '11100101100', '11100100110', '11101100100',
757 '11100110100', '11100110010', '11011011000', '11011000110',
758 '11000110110', '10100011000', '10001011000', '10001000110',
759 '10110001000', '10001101000', '10001100010', '11010001000',
760 '11000101000', '11000100010', '10110111000', '10110001110',
761 '10001101110', '10111011000', '10111000110', '10001110110',
762 '11101110110', '11010001110', '11000101110', '11011101000',
763 '11011100010', '11011101110', '11101011000', '11101000110',
764 '11100010110', '11101101000', '11101100010', '11100011010',
765 '11101111010', '11001000010', '11110001010', '10100110000',
766 '10100001100', '10010110000', '10010000110', '10000101100',
767 '10000100110', '10110010000', '10110000100', '10011010000',
768 '10011000010', '10000110100', '10000110010', '11000010010',
769 '11001010000', '11110111010', '11000010100', '10001111010',
770 '10100111100', '10010111100', '10010011110', '10111100100',
771 '10011110100', '10011110010', '11110100100', '11110010100',
772 '11110010010', '11011011110', '11011110110', '11110110110',
773 '10101111000', '10100011110', '10001011110', '10111101000',
774 '10111100010', '11110101000', '11110100010', '10111011110',
775 '10111101110', '11101011110', '11110101110', '11010000100',
776 '11010010000', '11010011100', '11000111010');
777 public static function getDigit($code)
779 $tableB = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
787 // check each characters
788 $len = strlen($code);
789 for ($i=0; $i<$len; $i++
) {
790 if (strpos($tableB, $code[$i]) === false) {
795 // check firsts characters : start with C table only if enought numeric
796 $tableCActivated = $len> 1;
798 for ($i=0; $i<3 && $i<$len; $i++
) {
799 $tableCActivated &= preg_match('`[0-9]`', $code[$i]);
802 $sum = $tableCActivated ?
105 : 104;
804 // start : [105] : C table or [104] : B table
805 $result = self
::$encoding[ $sum ];
809 if (! $tableCActivated) {
811 // check next character to activate C table if interresting
812 while (($i +
$j < $len) && preg_match('`[0-9]`', $code[$i+
$j])) {
816 // 6 min everywhere or 4 mini at the end
817 $tableCActivated = ($j > 5) ||
(($i +
$j - 1 == $len) && ($j > 3));
819 if ($tableCActivated) {
820 $result .= self
::$encoding[ 99 ]; // C table
821 $sum +
= ++
$isum * 99;
824 // 2 min for table C so need table B
825 } else if (($i == $len - 1) ||
(preg_match('`[^0-9]`', $code[$i])) ||
(preg_match('`[^0-9]`', $code[$i+
1]))) { //todo : verifier le JS : len - 1!!! XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
826 $tableCActivated = false;
827 $result .= self
::$encoding[ 100 ]; // B table
828 $sum +
= ++
$isum * 100;
831 if ($tableCActivated) {
832 $value = intval(substr($code, $i, 2)); // Add two characters (numeric)
835 $value = strpos($tableB, $code[$i]); // Add one character
839 $result .= self
::$encoding[ $value ];
840 $sum +
= ++
$isum * $value;
844 $result .= self
::$encoding[ $sum %
103 ];
847 $result .= self
::$encoding[ 106 ];
858 static private $encoding = array(
859 '101010011', '101011001', '101001011', '110010101',
860 '101101001', '110101001', '100101011', '100101101',
861 '100110101', '110100101', '101001101', '101100101',
862 '1101011011', '1101101011', '1101101101', '1011011011',
863 '1011001001', '1010010011', '1001001011', '1010011001');
865 public static function getDigit($code)
867 $table = '0123456789-$:/.+';
869 $intercharacter = '0';
871 // add start : A->D : arbitrary choose A
872 $result .= self
::$encoding[16] . $intercharacter;
874 $len = strlen($code);
875 for ($i=0; $i<$len; $i++
) {
876 $index = strpos($table, $code[$i]);
877 if ($index === false) {
881 $result .= self
::$encoding[ $index ] . $intercharacter;
884 // add stop : A->D : arbitrary choose A
885 $result .= self
::$encoding[16];
890 class BarcodeDatamatrix
892 static private $lengthRows = array(
893 10, 12, 14, 16, 18, 20, 22, 24, 26, // 24 squares et 6 rectangular
894 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144,
895 8, 8, 12, 12, 16, 16);
896 static private $lengthCols = array(
897 10, 12, 14, 16, 18, 20, 22, 24, 26, // Number of columns for the entire datamatrix
898 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144,
899 18, 32, 26, 36, 36, 48);
900 static private $dataCWCount = array(
901 3, 5, 8, 12, 18, 22, 30, 36, // Number of data codewords for the datamatrix
902 44, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050,
903 1304, 1558, 5, 10, 16, 22, 32, 49);
904 static private $solomonCWCount = array(
905 5, 7, 10, 12, 14, 18, 20, 24, 28, // Number of Reed-Solomon codewords for the datamatrix
906 36, 42, 48, 56, 68, 84, 112, 144, 192, 224, 272, 336, 408, 496, 620,
907 7, 11, 14, 18, 24, 28);
908 static private $dataRegionRows = array(
909 8, 10, 12, 14, 16, 18, 20, 22, // Number of rows per region
910 24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
911 6, 6, 10, 10, 14, 14);
912 static private $dataRegionCols = array(
913 8, 10, 12, 14, 16, 18, 20, 22, // Number of columns per region
914 24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
915 16, 14, 24, 16, 16, 22);
916 static private $regionRows = array(
917 1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per row
918 1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
920 static private $regionCols = array(
921 1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per column
922 1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
924 static private $interleavedBlocks = array(
925 1, 1, 1, 1, 1, 1, 1, 1, // Number of blocks
926 1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 6, 6, 8, 8,
928 static private $logTab = array(
929 -255, 255, 1, 240, 2, 225, 241, 53, 3, // Table of log for the Galois field
930 38, 226, 133, 242, 43, 54, 210, 4, 195, 39, 114, 227, 106, 134, 28,
931 243, 140, 44, 23, 55, 118, 211, 234, 5, 219, 196, 96, 40, 222, 115,
932 103, 228, 78, 107, 125, 135, 8, 29, 162, 244, 186, 141, 180, 45, 99,
933 24, 49, 56, 13, 119, 153, 212, 199, 235, 91, 6, 76, 220, 217, 197,
934 11, 97, 184, 41, 36, 223, 253, 116, 138, 104, 193, 229, 86, 79, 171,
935 108, 165, 126, 145, 136, 34, 9, 74, 30, 32, 163, 84, 245, 173, 187,
936 204, 142, 81, 181, 190, 46, 88, 100, 159, 25, 231, 50, 207, 57, 147,
937 14, 67, 120, 128, 154, 248, 213, 167, 200, 63, 236, 110, 92, 176, 7,
938 161, 77, 124, 221, 102, 218, 95, 198, 90, 12, 152, 98, 48, 185, 179,
939 42, 209, 37, 132, 224, 52, 254, 239, 117, 233, 139, 22, 105, 27, 194,
940 113, 230, 206, 87, 158, 80, 189, 172, 203, 109, 175, 166, 62, 127,
941 247, 146, 66, 137, 192, 35, 252, 10, 183, 75, 216, 31, 83, 33, 73,
942 164, 144, 85, 170, 246, 65, 174, 61, 188, 202, 205, 157, 143, 169, 82,
943 72, 182, 215, 191, 251, 47, 178, 89, 151, 101, 94, 160, 123, 26, 112,
944 232, 21, 51, 238, 208, 131, 58, 69, 148, 18, 15, 16, 68, 17, 121, 149,
945 129, 19, 155, 59, 249, 70, 214, 250, 168, 71, 201, 156, 64, 60, 237,
946 130, 111, 20, 93, 122, 177, 150);
947 static private $aLogTab = array(
948 1, 2, 4, 8, 16, 32, 64, 128, 45, 90, // Table of aLog for the Galois field
949 180, 69, 138, 57, 114, 228, 229, 231, 227, 235, 251, 219, 155, 27, 54,
950 108, 216, 157, 23, 46, 92, 184, 93, 186, 89, 178, 73, 146, 9, 18, 36,
951 72, 144, 13, 26, 52, 104, 208, 141, 55, 110, 220, 149, 7, 14, 28, 56,
952 112, 224, 237, 247, 195, 171, 123, 246, 193, 175, 115, 230, 225, 239,
953 243, 203, 187, 91, 182, 65, 130, 41, 82, 164, 101, 202, 185, 95, 190,
954 81, 162, 105, 210, 137, 63, 126, 252, 213, 135, 35, 70, 140, 53, 106,
955 212, 133, 39, 78, 156, 21, 42, 84, 168, 125, 250, 217, 159, 19, 38, 76,
956 152, 29, 58, 116, 232, 253, 215, 131, 43, 86, 172, 117, 234, 249, 223,
957 147, 11, 22, 44, 88, 176, 77, 154, 25, 50, 100, 200, 189, 87, 174, 113,
958 226, 233, 255, 211, 139, 59, 118, 236, 245, 199, 163, 107, 214, 129,
959 47, 94, 188, 85, 170, 121, 242, 201, 191, 83, 166, 97, 194, 169, 127,
960 254, 209, 143, 51, 102, 204, 181, 71, 142, 49, 98, 196, 165, 103, 206,
961 177, 79, 158, 17, 34, 68, 136, 61, 122, 244, 197, 167, 99, 198, 161,
962 111, 222, 145, 15, 30, 60, 120, 240, 205, 183, 67, 134, 33, 66, 132,
963 37, 74, 148, 5, 10, 20, 40, 80, 160, 109, 218, 153, 31, 62, 124, 248,
964 221, 151, 3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75,
966 private static function champGaloisMult($a, $b)
968 // MULTIPLICATION IN GALOIS FIELD GF(2^8)
973 return self
::$aLogTab[(self
::$logTab[$a] + self
::$logTab[$b]) %
255];
975 private static function champGaloisDoub($a, $b)
977 // THE OPERATION a * 2^b IN GALOIS FIELD GF(2^8)
986 return self
::$aLogTab[(self
::$logTab[$a] +
$b) %
255];
988 private static function champGaloisSum($a, $b)
990 // SUM IN GALOIS FIELD GF(2^8)
993 private static function selectIndex($dataCodeWordsCount, $rectangular)
995 // CHOOSE THE GOOD INDEX FOR TABLES
996 if (($dataCodeWordsCount<1 ||
$dataCodeWordsCount>1558) && !$rectangular) {
1000 if (($dataCodeWordsCount<1 ||
$dataCodeWordsCount>49) && $rectangular) {
1004 $n = $rectangular ?
24 : 0;
1006 while (self
::$dataCWCount[$n] < $dataCodeWordsCount) {
1012 private static function encodeDataCodeWordsASCII($text)
1014 $dataCodeWords = array();
1016 $len = strlen($text);
1017 for ($i=0; $i<$len; $i++
) {
1018 $c = ord($text[$i]);
1020 $dataCodeWords[$n] = 235;
1023 } else if (($c>=48 && $c<=57) && ($i+
1<$len) && (preg_match('`[0-9]`', $text[$i+
1]))) {
1024 $c = (($c - 48) * 10) +
intval($text[$i+
1]);
1031 $dataCodeWords[$n] = $c;
1035 return $dataCodeWords;
1037 private static function addPadCW(&$tab, $from, $to)
1044 for ($i=$from+
1; $i<$to; $i++
) {
1045 $r = ((149 * ($i+
1)) %
253) +
1;
1046 $tab[$i] = (129 +
$r) %
254;
1049 private static function calculSolFactorTable($solomonCWCount)
1051 // CALCULATE THE REED SOLOMON FACTORS
1052 $g = array_fill(0, $solomonCWCount+
1, 1);
1053 for ($i = 1; $i <= $solomonCWCount; $i++
) {
1054 for ($j = $i - 1; $j >= 0; $j--) {
1055 $g[$j] = self
::champGaloisDoub($g[$j], $i);
1057 $g[$j] = self
::champGaloisSum($g[$j], $g[$j-1]);
1064 private static function addReedSolomonCW($nSolomonCW, $coeffTab, $nDataCW, &$dataTab, $blocks)
1066 // Add the Reed Solomon codewords
1067 $errorBlocks = $nSolomonCW / $blocks;
1068 $correctionCW = array();
1070 for ($k = 0; $k < $blocks; $k++
) {
1071 for ($i=0; $i < $errorBlocks;
1073 $correctionCW[$i] = 0;
1076 for ($i=$k; $i<$nDataCW; $i+
=$blocks) {
1077 $temp = self
::champGaloisSum($dataTab[$i], $correctionCW[$errorBlocks-1]);
1078 for ($j=$errorBlocks-1; $j>=0; $j--) {
1080 $correctionCW[$j] = 0;
1082 $correctionCW[$j] = self
::champGaloisMult($temp, $coeffTab[$j]);
1086 $correctionCW[$j] = self
::champGaloisSum($correctionCW[$j-1], $correctionCW[$j]);
1091 // Renversement des blocs calcules
1093 for ($i=$errorBlocks-1; $i>=0; $i--) {
1094 $dataTab[$j] = $correctionCW[$i];
1101 private static function getBits($entier)
1103 // Transform integer to tab of bits
1105 for ($i=0; $i<8; $i++
) {
1106 $bits[$i] = $entier & (128 >> $i) ?
1 : 0;
1111 private static function next($etape, $totalRows, $totalCols, $codeWordsBits, &$datamatrix, &$assigned)
1113 // Place codewords into the matrix
1114 $chr = 0; // Place of the 8st bit from the first character to [4][0]
1119 // Check for a special case of corner
1120 if (($row == $totalRows) && ($col == 0)) {
1121 self
::patternShapeSpecial1($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1123 } else if (($etape<3) && ($row == $totalRows-2) && ($col == 0) && ($totalCols%4
!= 0)) {
1124 self
::patternShapeSpecial2($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1126 } else if (($row == $totalRows-2) && ($col == 0) && ($totalCols%8
== 4)) {
1127 self
::patternShapeSpecial3($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1129 } else if (($row == $totalRows+
4) && ($col == 2) && ($totalCols%8
== 0)) {
1130 self
::patternShapeSpecial4($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1134 // Go up and right in the datamatrix
1136 if (($row < $totalRows) && ($col >= 0) && (!isset($assigned[$row][$col]) ||
$assigned[$row][$col]!=1)) {
1137 self
::patternShapeStandard($datamatrix, $assigned, $codeWordsBits[$chr], $row, $col, $totalRows, $totalCols);
1143 } while (($row >= 0) && ($col < $totalCols));
1147 // Go down and left in the datamatrix
1149 if (($row >= 0) && ($col < $totalCols) && (!isset($assigned[$row][$col]) ||
$assigned[$row][$col]!=1)) {
1150 self
::patternShapeStandard($datamatrix, $assigned, $codeWordsBits[$chr], $row, $col, $totalRows, $totalCols);
1156 } while (($row < $totalRows) && ($col >=0));
1159 } while (($row < $totalRows) ||
($col < $totalCols));
1161 private static function patternShapeStandard(&$datamatrix, &$assigned, $bits, $row, $col, $totalRows, $totalCols)
1163 // Place bits in the matrix (standard or special case)
1164 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $row-2, $col-2, $totalRows, $totalCols);
1165 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $row-2, $col-1, $totalRows, $totalCols);
1166 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $row-1, $col-2, $totalRows, $totalCols);
1167 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], $row-1, $col-1, $totalRows, $totalCols);
1168 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], $row-1, $col, $totalRows, $totalCols);
1169 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], $row, $col-2, $totalRows, $totalCols);
1170 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], $row, $col-1, $totalRows, $totalCols);
1171 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], $row, $col, $totalRows, $totalCols);
1173 private static function patternShapeSpecial1(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1175 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-1, 0, $totalRows, $totalCols);
1176 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-1, 1, $totalRows, $totalCols);
1177 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows-1, 2, $totalRows, $totalCols);
1178 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-2, $totalRows, $totalCols);
1179 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-1, $totalRows, $totalCols);
1180 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols-1, $totalRows, $totalCols);
1181 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 2, $totalCols-1, $totalRows, $totalCols);
1182 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 3, $totalCols-1, $totalRows, $totalCols);
1184 private static function patternShapeSpecial2(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1186 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-3, 0, $totalRows, $totalCols);
1187 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-2, 0, $totalRows, $totalCols);
1188 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows-1, 0, $totalRows, $totalCols);
1189 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-4, $totalRows, $totalCols);
1190 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-3, $totalRows, $totalCols);
1191 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 0, $totalCols-2, $totalRows, $totalCols);
1192 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 0, $totalCols-1, $totalRows, $totalCols);
1193 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 1, $totalCols-1, $totalRows, $totalCols);
1195 private static function patternShapeSpecial3(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1197 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-3, 0, $totalRows, $totalCols);
1198 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-2, 0, $totalRows, $totalCols);
1199 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows-1, 0, $totalRows, $totalCols);
1200 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-2, $totalRows, $totalCols);
1201 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-1, $totalRows, $totalCols);
1202 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols-1, $totalRows, $totalCols);
1203 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 2, $totalCols-1, $totalRows, $totalCols);
1204 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 3, $totalCols-1, $totalRows, $totalCols);
1206 private static function patternShapeSpecial4(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1208 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-1, 0, $totalRows, $totalCols);
1209 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-1, $totalCols-1, $totalRows, $totalCols);
1210 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], 0, $totalCols-3, $totalRows, $totalCols);
1211 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-2, $totalRows, $totalCols);
1212 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-1, $totalRows, $totalCols);
1213 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols-3, $totalRows, $totalCols);
1214 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 1, $totalCols-2, $totalRows, $totalCols);
1215 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 1, $totalCols-1, $totalRows, $totalCols);
1217 private static function placeBitInDatamatrix(&$datamatrix, &$assigned, $bit, $row, $col, $totalRows, $totalCols)
1219 // Put a bit into the matrix
1222 $col +
= 4 - (($totalRows+
4)%8
);
1227 $row +
= 4 - (($totalCols+
4)%8
);
1230 if (!isset($assigned[$row][$col]) ||
$assigned[$row][$col] != 1) {
1231 $datamatrix[$row][$col] = $bit;
1232 $assigned[$row][$col] = 1;
1235 private static function addFinderPattern($datamatrix, $rowsRegion, $colsRegion, $rowsRegionCW, $colsRegionCW)
1237 // Add the finder pattern
1238 $totalRowsCW = ($rowsRegionCW+
2) * $rowsRegion;
1239 $totalColsCW = ($colsRegionCW+
2) * $colsRegion;
1241 $datamatrixTemp = array();
1242 $datamatrixTemp[0] = array_fill(0, $totalColsCW+
2, 0);
1244 for ($i=0; $i<$totalRowsCW; $i++
) {
1245 $datamatrixTemp[$i+
1] = array();
1246 $datamatrixTemp[$i+
1][0] = 0;
1247 $datamatrixTemp[$i+
1][$totalColsCW+
1] = 0;
1248 for ($j=0; $j<$totalColsCW; $j++
) {
1249 if ($i%
($rowsRegionCW+
2) == 0) {
1251 $datamatrixTemp[$i+
1][$j+
1] = 1;
1253 $datamatrixTemp[$i+
1][$j+
1] = 0;
1255 } else if ($i%
($rowsRegionCW+
2) == $rowsRegionCW+
1) {
1256 $datamatrixTemp[$i+
1][$j+
1] = 1;
1257 } else if ($j%
($colsRegionCW+
2) == $colsRegionCW+
1) {
1259 $datamatrixTemp[$i+
1][$j+
1] = 0;
1261 $datamatrixTemp[$i+
1][$j+
1] = 1;
1263 } else if ($j%
($colsRegionCW+
2) == 0) {
1264 $datamatrixTemp[$i+
1][$j+
1] = 1;
1266 $datamatrixTemp[$i+
1][$j+
1] = 0;
1267 $datamatrixTemp[$i+
1][$j+
1] = $datamatrix[$i-1-(2*(floor($i/($rowsRegionCW+
2))))][$j-1-(2*(floor($j/($colsRegionCW+
2))))]; // todo : parseInt => ?
1272 $datamatrixTemp[$totalRowsCW+
1] = array();
1273 for ($j=0; $j<$totalColsCW+
2; $j++
) {
1274 $datamatrixTemp[$totalRowsCW+
1][$j] = 0;
1277 return $datamatrixTemp;
1279 public static function getDigit($text, $rectangular)
1281 $dataCodeWords = self
::encodeDataCodeWordsASCII($text); // Code the text in the ASCII mode
1282 $dataCWCount = count($dataCodeWords);
1283 $index = self
::selectIndex($dataCWCount, $rectangular); // Select the index for the data tables
1284 $totalDataCWCount = self
::$dataCWCount[$index]; // Number of data CW
1285 $solomonCWCount = self
::$solomonCWCount[$index]; // Number of Reed Solomon CW
1286 $totalCWCount = $totalDataCWCount +
$solomonCWCount; // Number of CW
1287 $rowsTotal = self
::$lengthRows[$index]; // Size of symbol
1288 $colsTotal = self
::$lengthCols[$index];
1289 $rowsRegion = self
::$regionRows[$index]; // Number of region
1290 $colsRegion = self
::$regionCols[$index];
1291 $rowsRegionCW = self
::$dataRegionRows[$index];
1292 $colsRegionCW = self
::$dataRegionCols[$index];
1293 $rowsLengthMatrice = $rowsTotal-2*$rowsRegion; // Size of matrice data
1294 $colsLengthMatrice = $colsTotal-2*$colsRegion;
1295 $blocks = self
::$interleavedBlocks[$index]; // Number of Reed Solomon blocks
1296 $errorBlocks = $solomonCWCount / $blocks;
1298 self
::addPadCW($dataCodeWords, $dataCWCount, $totalDataCWCount); // Add codewords pads
1300 $g = self
::calculSolFactorTable($errorBlocks); // Calculate correction coefficients
1302 self
::addReedSolomonCW($solomonCWCount, $g, $totalDataCWCount, $dataCodeWords, $blocks); // Add Reed Solomon codewords
1304 $codeWordsBits = array(); // Calculte bits from codewords
1305 for ($i=0; $i<$totalCWCount; $i++
) {
1306 $codeWordsBits[$i] = self
::getBits($dataCodeWords[$i]);
1309 $datamatrix = array_fill(0, $colsLengthMatrice, array());
1310 $assigned = array_fill(0, $colsLengthMatrice, array());
1312 // Add the bottom-right corner if needed
1313 if ((($rowsLengthMatrice * $colsLengthMatrice) %
8) == 4) {
1314 $datamatrix[$rowsLengthMatrice-2][$colsLengthMatrice-2] = 1;
1315 $datamatrix[$rowsLengthMatrice-1][$colsLengthMatrice-1] = 1;
1316 $datamatrix[$rowsLengthMatrice-1][$colsLengthMatrice-2] = 0;
1317 $datamatrix[$rowsLengthMatrice-2][$colsLengthMatrice-1] = 0;
1318 $assigned[$rowsLengthMatrice-2][$colsLengthMatrice-2] = 1;
1319 $assigned[$rowsLengthMatrice-1][$colsLengthMatrice-1] = 1;
1320 $assigned[$rowsLengthMatrice-1][$colsLengthMatrice-2] = 1;
1321 $assigned[$rowsLengthMatrice-2][$colsLengthMatrice-1] = 1;
1324 // Put the codewords into the matrix
1325 self
::next(0, $rowsLengthMatrice, $colsLengthMatrice, $codeWordsBits, $datamatrix, $assigned);
1327 // Add the finder pattern
1328 $datamatrix = self
::addFinderPattern($datamatrix, $rowsRegion, $colsRegion, $rowsRegionCW, $colsRegionCW);