3 * BarCode Coder Library (BCC Library)
10 * Author : DEMONTE Jean-Baptiste <jbdemonte@gmail.com>
13 * Web site: https://barcode-coder.com/
14 * dual licence : https://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
15 * https://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);
142 for ($i=0; $i<$len; $i++
) {
149 private static function digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit)
151 $lines = count($digit);
152 $columns = count($digit[0]);
153 $angle = deg2rad(-$angle);
157 self
::_rotate($columns * $mw / 2, $lines * $mh / 2, $cos, $sin, $x, $y);
160 for ($y=0; $y<$lines; $y++
) {
162 while ($x <$columns) {
164 if ($digit[$y][$x] == '1') {
166 while (($z +
1 <$columns) && ($digit[$y][$z +
1] == '1')) {
172 $x2 = ($z +
1) * $mw;
173 $y2 = ($y +
1) * $mh;
174 self
::_rotate($x1, $y1, $cos, $sin, $xA, $yA);
175 self
::_rotate($x2, $y1, $cos, $sin, $xB, $yB);
176 self
::_rotate($x2, $y2, $cos, $sin, $xC, $yC);
177 self
::_rotate($x1, $y2, $cos, $sin, $xD, $yD);
179 $xA +
$xi, $yA +
$yi,
180 $xB +
$xi, $yB +
$yi,
181 $xC +
$xi, $yC +
$yi,
189 return self
::result($xi, $yi, $columns, $lines, $mw, $mh, $cos, $sin);
192 // GD barcode renderer
193 private static function digitToGDRenderer($gd, $color, $xi, $yi, $angle, $mw, $mh, $digit)
195 $fn = function ($points) use ($gd, $color) {
196 imagefilledpolygon($gd, $points, 4, $color);
198 return self
::digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit);
200 // FPDF barcode renderer
201 private static function digitToFPDFRenderer($pdf, $color, $xi, $yi, $angle, $mw, $mh, $digit)
203 if (!is_array($color)) {
204 if (preg_match('`([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})`i', $color, $m)) {
205 $color = array(hexdec($m[1]),hexdec($m[2]),hexdec($m[3]));
207 $color = array(0,0,0);
211 $color = array_values($color);
212 $pdf->SetDrawColor($color[0], $color[1], $color[2]);
213 $pdf->SetFillColor($color[0], $color[1], $color[2]);
215 $fn = function ($points) use ($pdf) {
220 for ($i=0; $i < 8; $i+
=2) {
221 $points_string .= sprintf('%.2F %.2F', $points[$i]*$k, ($h-$points[$i+
1])*$k);
222 $points_string .= $i ?
' l ' : ' m ';
225 $pdf->_out($points_string . $op);
227 return self
::digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit);
230 private static function result($xi, $yi, $columns, $lines, $mw, $mh, $cos, $sin)
232 self
::_rotate(0, 0, $cos, $sin, $x1, $y1);
233 self
::_rotate($columns * $mw, 0, $cos, $sin, $x2, $y2);
234 self
::_rotate($columns * $mw, $lines * $mh, $cos, $sin, $x3, $y3);
235 self
::_rotate(0, $lines * $mh, $cos, $sin, $x4, $y4);
238 'width' => $columns * $mw,
239 'height'=> $lines * $mh,
259 private static function _rotate($x1, $y1, $cos, $sin, &$x, &$y)
261 $x = $x1 * $cos - $y1 * $sin;
262 $y = $x1 * $sin +
$y1 * $cos;
265 public static function rotate($x1, $y1, $angle, &$x, &$y)
267 $angle = deg2rad(-$angle);
270 $x = $x1 * $cos - $y1 * $sin;
271 $y = $x1 * $sin +
$y1 * $cos;
277 static private $encoding = array('NNWWN', 'WNNNW', 'NWNNW', 'WWNNN', 'NNWNW', 'WNWNN', 'NWWNN', 'NNNWW', 'WNNWN','NWNWN');
279 public static function compute($code, $crc, $type)
282 if (strlen($code) %
2) {
286 if (($type == 'int25') && (strlen($code) %
2 == 0)) {
292 for ($i=strlen($code)-1; $i>-1; $i--) {
293 $v = intval($code[$i]);
294 $sum +
= $odd ?
3 * $v : $v;
298 $code .= (string) ((10 - $sum %
10) %
10);
304 public static function getDigit($code, $crc, $type)
306 $code = self
::compute($code, $crc, $type);
313 if ($type == 'int25') { // Interleaved 2 of 5
318 $end = strlen($code) / 2;
319 for ($i=0; $i<$end; $i++
) {
322 for ($j=0; $j<5; $j++
) {
324 if (self
::$encoding[$c1][$j] == 'W') {
329 if (self
::$encoding[$c2][$j] == 'W') {
337 } else if ($type == 'std25') {
338 // Standard 2 of 5 is a numeric-only barcode that has been in use a long time.
339 // 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.
340 // The code is self-checking and does not include a checksum.
343 $result .= '11011010';
346 $end = strlen($code);
347 for ($i=0; $i<$end; $i++
) {
349 for ($j=0; $j<5; $j++
) {
351 if (self
::$encoding[$c][$j] == 'W') {
360 $result .= '11010110';
370 static private $encoding = array(
371 array('0001101', '0100111', '1110010'),
372 array('0011001', '0110011', '1100110'),
373 array('0010011', '0011011', '1101100'),
374 array('0111101', '0100001', '1000010'),
375 array('0100011', '0011101', '1011100'),
376 array('0110001', '0111001', '1001110'),
377 array('0101111', '0000101', '1010000'),
378 array('0111011', '0010001', '1000100'),
379 array('0110111', '0001001', '1001000'),
380 array('0001011', '0010111', '1110100')
383 static private $first = array('000000','001011','001101','001110','010011','011001','011100','010101','010110','011010');
385 public static function getDigit($code, $type)
387 // Check len (12 for ean13, 7 for ean8)
388 $len = $type == 'ean8' ?
7 : 12;
389 $code = substr($code, 0, $len);
390 if (!preg_match('`[0-9]{'.$len.'}`', $code)) {
395 $code = self
::compute($code, $type);
398 $result = '101'; // start
400 if ($type == 'ean8') {
402 for ($i=0; $i<4; $i++
) {
403 $result .= self
::$encoding[intval($code[$i])][0];
409 // process right part
410 for ($i=4; $i<8; $i++
) {
411 $result .= self
::$encoding[intval($code[$i])][2];
414 // extract first digit and get sequence
415 $seq = self
::$first[ intval($code[0]) ];
418 for ($i=1; $i<7; $i++
) {
419 $result .= self
::$encoding[intval($code[$i])][ intval($seq[$i-1]) ];
425 // process right part
426 for ($i=7; $i<13; $i++
) {
427 $result .= self
::$encoding[intval($code[$i])][ 2 ];
431 $result .= '101'; // stop
435 public static function compute($code, $type)
437 $len = $type == 'ean13' ?
12 : 7;
438 $code = substr($code, 0, $len);
439 if (!preg_match('`[0-9]{'.$len.'}`', $code)) {
445 for ($i=$len-1; $i>-1; $i--) {
446 $sum +
= ($odd ?
3 : 1) * intval($code[$i]);
450 return($code . ( (string) ((10 - $sum %
10) %
10)));
457 public static function getDigit($code)
459 if (strlen($code) < 12) {
463 return BarcodeEAN
::getDigit($code, 'ean13');
466 public static function compute($code)
468 if (strlen($code) < 12) {
472 return substr(BarcodeEAN
::compute($code, 'ean13'), 1);
478 static private $encoding = array(
479 '100100100100', '100100100110', '100100110100', '100100110110',
480 '100110100100', '100110100110', '100110110100', '100110110110',
481 '110100100100', '110100100110');
483 public static function compute($code, $crc)
485 if (is_array($crc)) {
486 if ($crc['crc1'] == 'mod10') {
487 $code = self
::computeMod10($code);
488 } else if ($crc['crc1'] == 'mod11') {
489 $code = self
::computeMod11($code);
492 if ($crc['crc2'] == 'mod10') {
493 $code = self
::computeMod10($code);
494 } else if ($crc['crc2'] == 'mod11') {
495 $code = self
::computeMod11($code);
498 $code = self
::computeMod10($code);
504 private static function computeMod10($code)
506 $len = strlen($code);
510 for ($i=0; $i<$len; $i++
) {
512 $n1 = 10 * $n1 +
intval($code[$i]);
514 $sum +
= intval($code[$i]);
517 $toPart1 = ! $toPart1;
520 $s1 = (string) (2 * $n1);
522 for ($i=0; $i<$len; $i++
) {
523 $sum +
= intval($s1[$i]);
526 return($code . ( (string) (10 - $sum %
10) %
10));
529 private static function computeMod11($code)
533 for ($i=strlen($code)-1; $i>-1; $i--) {
534 $sum +
= $weight * intval($code[$i]);
535 $weight = $weight == 7 ?
2 : $weight +
1;
538 return($code . ( (string) (11 - $sum %
11) %
11) );
541 public static function getDigit($code, $crc)
543 if (preg_match('`[^0-9]`', $code)) {
550 $code = self
::compute($code, false);
556 $len = strlen($code);
557 for ($i=0; $i<$len; $i++
) {
558 $result .= self
::$encoding[ intval($code[$i]) ];
570 static private $encoding = array(
571 '101011', '1101011', '1001011', '1100101',
572 '1011011', '1101101', '1001101', '1010011',
573 '1101001', '110101', '101101');
575 public static function getDigit($code)
577 if (preg_match('`[^0-9\-]`', $code)) {
582 $intercharacter = '0';
585 $result = '1011001' . $intercharacter;
588 $len = strlen($code);
589 for ($i=0; $i<$len; $i++
) {
590 $index = $code[$i] == '-' ?
10 : intval($code[$i]);
591 $result .= self
::$encoding[ $index ] . $intercharacter;
597 $weightK = 1; // start at 1 because the right-most character is 'C' checksum
599 for ($i=$len-1; $i>-1; $i--) {
600 $weightC = $weightC == 10 ?
1 : $weightC +
1;
601 $weightK = $weightK == 10 ?
1 : $weightK +
1;
603 $index = $code[$i] == '-' ?
10 : intval($code[$i]);
605 $weightSumC +
= $weightC * $index;
606 $weightSumK +
= $weightK * $index;
609 $c = $weightSumC %
11;
611 $k = $weightSumK %
11;
613 $result .= self
::$encoding[$c] . $intercharacter;
616 $result .= self
::$encoding[$k] . $intercharacter;
620 $result .= '1011001';
628 static private $encoding = array(
629 '101001101101', '110100101011', '101100101011', '110110010101',
630 '101001101011', '110100110101', '101100110101', '101001011011',
631 '110100101101', '101100101101', '110101001011', '101101001011',
632 '110110100101', '101011001011', '110101100101', '101101100101',
633 '101010011011', '110101001101', '101101001101', '101011001101',
634 '110101010011', '101101010011', '110110101001', '101011010011',
635 '110101101001', '101101101001', '101010110011', '110101011001',
636 '101101011001', '101011011001', '110010101011', '100110101011',
637 '110011010101', '100101101011', '110010110101', '100110110101',
638 '100101011011', '110010101101', '100110101101', '100100100101',
639 '100100101001', '100101001001', '101001001001', '100101101101');
640 public static function getDigit($code)
642 $table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*';
644 $intercharacter = '0';
646 if (strpos($code, '*') !== false) {
650 // Add Start and Stop charactere : *
651 $code = strtoupper('*' . $code . '*');
653 $len = strlen($code);
654 for ($i=0; $i<$len; $i++
) {
655 $index = strpos($table, $code[$i]);
656 if ($index === false) {
661 $result .= $intercharacter;
664 $result .= self
::$encoding[ $index ];
673 static private $encoding = array(
674 '100010100', '101001000', '101000100', '101000010',
675 '100101000', '100100100', '100100010', '101010000',
676 '100010010', '100001010', '110101000', '110100100',
677 '110100010', '110010100', '110010010', '110001010',
678 '101101000', '101100100', '101100010', '100110100',
679 '100011010', '101011000', '101001100', '101000110',
680 '100101100', '100010110', '110110100', '110110010',
681 '110101100', '110100110', '110010110', '110011010',
682 '101101100', '101100110', '100110110', '100111010',
683 '100101110', '111010100', '111010010', '111001010',
684 '101101110', '101110110', '110101110', '100100110',
685 '111011010', '111010110', '100110010', '101011110');
687 public static function getDigit($code, $crc)
689 $table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%____*'; // _ => ($), (%), (/) et (+)
692 if (strpos($code, '*') !== false) {
696 $code = strtoupper($code);
699 $result .= self
::$encoding[47];
702 $len = strlen($code);
703 for ($i=0; $i<$len; $i++
) {
705 $index = strpos($table, $c);
706 if (($c == '_') ||
($index === false)) {
710 $result .= self
::$encoding[ $index ];
717 $weightK = 1; // start at 1 because the right-most character is 'C' checksum
719 for ($i=$len-1; $i>-1; $i--) {
720 $weightC = $weightC == 20 ?
1 : $weightC +
1;
721 $weightK = $weightK == 15 ?
1 : $weightK +
1;
723 $index = strpos($table, $code[$i]);
725 $weightSumC +
= $weightC * $index;
726 $weightSumK +
= $weightK * $index;
729 $c = $weightSumC %
47;
731 $k = $weightSumK %
47;
733 $result .= self
::$encoding[$c];
734 $result .= self
::$encoding[$k];
738 $result .= self
::$encoding[47];
748 static private $encoding = array(
749 '11011001100', '11001101100', '11001100110', '10010011000',
750 '10010001100', '10001001100', '10011001000', '10011000100',
751 '10001100100', '11001001000', '11001000100', '11000100100',
752 '10110011100', '10011011100', '10011001110', '10111001100',
753 '10011101100', '10011100110', '11001110010', '11001011100',
754 '11001001110', '11011100100', '11001110100', '11101101110',
755 '11101001100', '11100101100', '11100100110', '11101100100',
756 '11100110100', '11100110010', '11011011000', '11011000110',
757 '11000110110', '10100011000', '10001011000', '10001000110',
758 '10110001000', '10001101000', '10001100010', '11010001000',
759 '11000101000', '11000100010', '10110111000', '10110001110',
760 '10001101110', '10111011000', '10111000110', '10001110110',
761 '11101110110', '11010001110', '11000101110', '11011101000',
762 '11011100010', '11011101110', '11101011000', '11101000110',
763 '11100010110', '11101101000', '11101100010', '11100011010',
764 '11101111010', '11001000010', '11110001010', '10100110000',
765 '10100001100', '10010110000', '10010000110', '10000101100',
766 '10000100110', '10110010000', '10110000100', '10011010000',
767 '10011000010', '10000110100', '10000110010', '11000010010',
768 '11001010000', '11110111010', '11000010100', '10001111010',
769 '10100111100', '10010111100', '10010011110', '10111100100',
770 '10011110100', '10011110010', '11110100100', '11110010100',
771 '11110010010', '11011011110', '11011110110', '11110110110',
772 '10101111000', '10100011110', '10001011110', '10111101000',
773 '10111100010', '11110101000', '11110100010', '10111011110',
774 '10111101110', '11101011110', '11110101110', '11010000100',
775 '11010010000', '11010011100', '11000111010');
776 public static function getDigit($code)
778 $tableB = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
786 // check each characters
787 $len = strlen($code);
788 for ($i=0; $i<$len; $i++
) {
789 if (strpos($tableB, $code[$i]) === false) {
794 // check firsts characters : start with C table only if enought numeric
795 $tableCActivated = $len> 1;
797 for ($i=0; $i<3 && $i<$len; $i++
) {
798 $tableCActivated &= preg_match('`[0-9]`', $code[$i]);
801 $sum = $tableCActivated ?
105 : 104;
803 // start : [105] : C table or [104] : B table
804 $result = self
::$encoding[ $sum ];
808 if (! $tableCActivated) {
810 // check next character to activate C table if interresting
811 while (($i +
$j < $len) && preg_match('`[0-9]`', $code[$i+
$j])) {
815 // 6 min everywhere or 4 mini at the end
816 $tableCActivated = ($j > 5) ||
(($i +
$j - 1 == $len) && ($j > 3));
818 if ($tableCActivated) {
819 $result .= self
::$encoding[ 99 ]; // C table
820 $sum +
= ++
$isum * 99;
823 // 2 min for table C so need table B
824 } 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
825 $tableCActivated = false;
826 $result .= self
::$encoding[ 100 ]; // B table
827 $sum +
= ++
$isum * 100;
830 if ($tableCActivated) {
831 $value = intval(substr($code, $i, 2)); // Add two characters (numeric)
834 $value = strpos($tableB, $code[$i]); // Add one character
838 $result .= self
::$encoding[ $value ];
839 $sum +
= ++
$isum * $value;
843 $result .= self
::$encoding[ $sum %
103 ];
846 $result .= self
::$encoding[ 106 ];
857 static private $encoding = array(
858 '101010011', '101011001', '101001011', '110010101',
859 '101101001', '110101001', '100101011', '100101101',
860 '100110101', '110100101', '101001101', '101100101',
861 '1101011011', '1101101011', '1101101101', '1011011011',
862 '1011001001', '1010010011', '1001001011', '1010011001');
864 public static function getDigit($code)
866 $table = '0123456789-$:/.+';
868 $intercharacter = '0';
870 // add start : A->D : arbitrary choose A
871 $result .= self
::$encoding[16] . $intercharacter;
873 $len = strlen($code);
874 for ($i=0; $i<$len; $i++
) {
875 $index = strpos($table, $code[$i]);
876 if ($index === false) {
880 $result .= self
::$encoding[ $index ] . $intercharacter;
883 // add stop : A->D : arbitrary choose A
884 $result .= self
::$encoding[16];
889 class BarcodeDatamatrix
891 static private $lengthRows = array(
892 10, 12, 14, 16, 18, 20, 22, 24, 26, // 24 squares et 6 rectangular
893 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144,
894 8, 8, 12, 12, 16, 16);
895 static private $lengthCols = array(
896 10, 12, 14, 16, 18, 20, 22, 24, 26, // Number of columns for the entire datamatrix
897 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144,
898 18, 32, 26, 36, 36, 48);
899 static private $dataCWCount = array(
900 3, 5, 8, 12, 18, 22, 30, 36, // Number of data codewords for the datamatrix
901 44, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050,
902 1304, 1558, 5, 10, 16, 22, 32, 49);
903 static private $solomonCWCount = array(
904 5, 7, 10, 12, 14, 18, 20, 24, 28, // Number of Reed-Solomon codewords for the datamatrix
905 36, 42, 48, 56, 68, 84, 112, 144, 192, 224, 272, 336, 408, 496, 620,
906 7, 11, 14, 18, 24, 28);
907 static private $dataRegionRows = array(
908 8, 10, 12, 14, 16, 18, 20, 22, // Number of rows per region
909 24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
910 6, 6, 10, 10, 14, 14);
911 static private $dataRegionCols = array(
912 8, 10, 12, 14, 16, 18, 20, 22, // Number of columns per region
913 24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
914 16, 14, 24, 16, 16, 22);
915 static private $regionRows = array(
916 1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per row
917 1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
919 static private $regionCols = array(
920 1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per column
921 1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
923 static private $interleavedBlocks = array(
924 1, 1, 1, 1, 1, 1, 1, 1, // Number of blocks
925 1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 6, 6, 8, 8,
927 static private $logTab = array(
928 -255, 255, 1, 240, 2, 225, 241, 53, 3, // Table of log for the Galois field
929 38, 226, 133, 242, 43, 54, 210, 4, 195, 39, 114, 227, 106, 134, 28,
930 243, 140, 44, 23, 55, 118, 211, 234, 5, 219, 196, 96, 40, 222, 115,
931 103, 228, 78, 107, 125, 135, 8, 29, 162, 244, 186, 141, 180, 45, 99,
932 24, 49, 56, 13, 119, 153, 212, 199, 235, 91, 6, 76, 220, 217, 197,
933 11, 97, 184, 41, 36, 223, 253, 116, 138, 104, 193, 229, 86, 79, 171,
934 108, 165, 126, 145, 136, 34, 9, 74, 30, 32, 163, 84, 245, 173, 187,
935 204, 142, 81, 181, 190, 46, 88, 100, 159, 25, 231, 50, 207, 57, 147,
936 14, 67, 120, 128, 154, 248, 213, 167, 200, 63, 236, 110, 92, 176, 7,
937 161, 77, 124, 221, 102, 218, 95, 198, 90, 12, 152, 98, 48, 185, 179,
938 42, 209, 37, 132, 224, 52, 254, 239, 117, 233, 139, 22, 105, 27, 194,
939 113, 230, 206, 87, 158, 80, 189, 172, 203, 109, 175, 166, 62, 127,
940 247, 146, 66, 137, 192, 35, 252, 10, 183, 75, 216, 31, 83, 33, 73,
941 164, 144, 85, 170, 246, 65, 174, 61, 188, 202, 205, 157, 143, 169, 82,
942 72, 182, 215, 191, 251, 47, 178, 89, 151, 101, 94, 160, 123, 26, 112,
943 232, 21, 51, 238, 208, 131, 58, 69, 148, 18, 15, 16, 68, 17, 121, 149,
944 129, 19, 155, 59, 249, 70, 214, 250, 168, 71, 201, 156, 64, 60, 237,
945 130, 111, 20, 93, 122, 177, 150);
946 static private $aLogTab = array(
947 1, 2, 4, 8, 16, 32, 64, 128, 45, 90, // Table of aLog for the Galois field
948 180, 69, 138, 57, 114, 228, 229, 231, 227, 235, 251, 219, 155, 27, 54,
949 108, 216, 157, 23, 46, 92, 184, 93, 186, 89, 178, 73, 146, 9, 18, 36,
950 72, 144, 13, 26, 52, 104, 208, 141, 55, 110, 220, 149, 7, 14, 28, 56,
951 112, 224, 237, 247, 195, 171, 123, 246, 193, 175, 115, 230, 225, 239,
952 243, 203, 187, 91, 182, 65, 130, 41, 82, 164, 101, 202, 185, 95, 190,
953 81, 162, 105, 210, 137, 63, 126, 252, 213, 135, 35, 70, 140, 53, 106,
954 212, 133, 39, 78, 156, 21, 42, 84, 168, 125, 250, 217, 159, 19, 38, 76,
955 152, 29, 58, 116, 232, 253, 215, 131, 43, 86, 172, 117, 234, 249, 223,
956 147, 11, 22, 44, 88, 176, 77, 154, 25, 50, 100, 200, 189, 87, 174, 113,
957 226, 233, 255, 211, 139, 59, 118, 236, 245, 199, 163, 107, 214, 129,
958 47, 94, 188, 85, 170, 121, 242, 201, 191, 83, 166, 97, 194, 169, 127,
959 254, 209, 143, 51, 102, 204, 181, 71, 142, 49, 98, 196, 165, 103, 206,
960 177, 79, 158, 17, 34, 68, 136, 61, 122, 244, 197, 167, 99, 198, 161,
961 111, 222, 145, 15, 30, 60, 120, 240, 205, 183, 67, 134, 33, 66, 132,
962 37, 74, 148, 5, 10, 20, 40, 80, 160, 109, 218, 153, 31, 62, 124, 248,
963 221, 151, 3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75,
965 private static function champGaloisMult($a, $b)
967 // MULTIPLICATION IN GALOIS FIELD GF(2^8)
972 return self
::$aLogTab[(self
::$logTab[$a] + self
::$logTab[$b]) %
255];
974 private static function champGaloisDoub($a, $b)
976 // THE OPERATION a * 2^b IN GALOIS FIELD GF(2^8)
985 return self
::$aLogTab[(self
::$logTab[$a] +
$b) %
255];
987 private static function champGaloisSum($a, $b)
989 // SUM IN GALOIS FIELD GF(2^8)
992 private static function selectIndex($dataCodeWordsCount, $rectangular)
994 // CHOOSE THE GOOD INDEX FOR TABLES
995 if (($dataCodeWordsCount<1 ||
$dataCodeWordsCount>1558) && !$rectangular) {
999 if (($dataCodeWordsCount<1 ||
$dataCodeWordsCount>49) && $rectangular) {
1003 $n = $rectangular ?
24 : 0;
1005 while (self
::$dataCWCount[$n] < $dataCodeWordsCount) {
1011 private static function encodeDataCodeWordsASCII($text)
1013 $dataCodeWords = array();
1015 $len = strlen($text);
1016 for ($i=0; $i<$len; $i++
) {
1017 $c = ord($text[$i]);
1019 $dataCodeWords[$n] = 235;
1022 } else if (($c>=48 && $c<=57) && ($i+
1<$len) && (preg_match('`[0-9]`', $text[$i+
1]))) {
1023 $c = (($c - 48) * 10) +
intval($text[$i+
1]);
1030 $dataCodeWords[$n] = $c;
1034 return $dataCodeWords;
1036 private static function addPadCW(&$tab, $from, $to)
1043 for ($i=$from+
1; $i<$to; $i++
) {
1044 $r = ((149 * ($i+
1)) %
253) +
1;
1045 $tab[$i] = (129 +
$r) %
254;
1048 private static function calculSolFactorTable($solomonCWCount)
1050 // CALCULATE THE REED SOLOMON FACTORS
1051 $g = array_fill(0, $solomonCWCount+
1, 1);
1052 for ($i = 1; $i <= $solomonCWCount; $i++
) {
1053 for ($j = $i - 1; $j >= 0; $j--) {
1054 $g[$j] = self
::champGaloisDoub($g[$j], $i);
1056 $g[$j] = self
::champGaloisSum($g[$j], $g[$j-1]);
1063 private static function addReedSolomonCW($nSolomonCW, $coeffTab, $nDataCW, &$dataTab, $blocks)
1065 // Add the Reed Solomon codewords
1066 $errorBlocks = $nSolomonCW / $blocks;
1067 $correctionCW = array();
1069 for ($k = 0; $k < $blocks; $k++
) {
1070 for ($i=0; $i < $errorBlocks; $i++
) {
1071 $correctionCW[$i] = 0;
1074 for ($i=$k; $i<$nDataCW; $i+
=$blocks) {
1075 $temp = self
::champGaloisSum($dataTab[$i], $correctionCW[$errorBlocks-1]);
1076 for ($j=$errorBlocks-1; $j>=0; $j--) {
1078 $correctionCW[$j] = 0;
1080 $correctionCW[$j] = self
::champGaloisMult($temp, $coeffTab[$j]);
1084 $correctionCW[$j] = self
::champGaloisSum($correctionCW[$j-1], $correctionCW[$j]);
1089 // Renversement des blocs calcules
1091 for ($i=$errorBlocks-1; $i>=0; $i--) {
1092 $dataTab[$j] = $correctionCW[$i];
1099 private static function getBits($entier)
1101 // Transform integer to tab of bits
1103 for ($i=0; $i<8; $i++
) {
1104 $bits[$i] = $entier & (128 >> $i) ?
1 : 0;
1109 private static function next($etape, $totalRows, $totalCols, $codeWordsBits, &$datamatrix, &$assigned)
1111 // Place codewords into the matrix
1112 $chr = 0; // Place of the 8st bit from the first character to [4][0]
1117 // Check for a special case of corner
1118 if (($row == $totalRows) && ($col == 0)) {
1119 self
::patternShapeSpecial1($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1121 } else if (($etape<3) && ($row == $totalRows-2) && ($col == 0) && ($totalCols%4
!= 0)) {
1122 self
::patternShapeSpecial2($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1124 } else if (($row == $totalRows-2) && ($col == 0) && ($totalCols%8
== 4)) {
1125 self
::patternShapeSpecial3($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1127 } else if (($row == $totalRows+
4) && ($col == 2) && ($totalCols%8
== 0)) {
1128 self
::patternShapeSpecial4($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1132 // Go up and right in the datamatrix
1134 if (($row < $totalRows) && ($col >= 0) && (!isset($assigned[$row][$col]) ||
$assigned[$row][$col]!=1)) {
1135 self
::patternShapeStandard($datamatrix, $assigned, $codeWordsBits[$chr], $row, $col, $totalRows, $totalCols);
1141 } while (($row >= 0) && ($col < $totalCols));
1145 // Go down and left in the datamatrix
1147 if (($row >= 0) && ($col < $totalCols) && (!isset($assigned[$row][$col]) ||
$assigned[$row][$col]!=1)) {
1148 self
::patternShapeStandard($datamatrix, $assigned, $codeWordsBits[$chr], $row, $col, $totalRows, $totalCols);
1154 } while (($row < $totalRows) && ($col >=0));
1157 } while (($row < $totalRows) ||
($col < $totalCols));
1159 private static function patternShapeStandard(&$datamatrix, &$assigned, $bits, $row, $col, $totalRows, $totalCols)
1161 // Place bits in the matrix (standard or special case)
1162 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $row-2, $col-2, $totalRows, $totalCols);
1163 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $row-2, $col-1, $totalRows, $totalCols);
1164 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $row-1, $col-2, $totalRows, $totalCols);
1165 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], $row-1, $col-1, $totalRows, $totalCols);
1166 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], $row-1, $col, $totalRows, $totalCols);
1167 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], $row, $col-2, $totalRows, $totalCols);
1168 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], $row, $col-1, $totalRows, $totalCols);
1169 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], $row, $col, $totalRows, $totalCols);
1171 private static function patternShapeSpecial1(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1173 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-1, 0, $totalRows, $totalCols);
1174 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-1, 1, $totalRows, $totalCols);
1175 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows-1, 2, $totalRows, $totalCols);
1176 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-2, $totalRows, $totalCols);
1177 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-1, $totalRows, $totalCols);
1178 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols-1, $totalRows, $totalCols);
1179 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 2, $totalCols-1, $totalRows, $totalCols);
1180 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 3, $totalCols-1, $totalRows, $totalCols);
1182 private static function patternShapeSpecial2(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1184 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-3, 0, $totalRows, $totalCols);
1185 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-2, 0, $totalRows, $totalCols);
1186 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows-1, 0, $totalRows, $totalCols);
1187 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-4, $totalRows, $totalCols);
1188 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-3, $totalRows, $totalCols);
1189 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 0, $totalCols-2, $totalRows, $totalCols);
1190 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 0, $totalCols-1, $totalRows, $totalCols);
1191 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 1, $totalCols-1, $totalRows, $totalCols);
1193 private static function patternShapeSpecial3(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1195 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-3, 0, $totalRows, $totalCols);
1196 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-2, 0, $totalRows, $totalCols);
1197 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows-1, 0, $totalRows, $totalCols);
1198 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-2, $totalRows, $totalCols);
1199 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-1, $totalRows, $totalCols);
1200 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols-1, $totalRows, $totalCols);
1201 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 2, $totalCols-1, $totalRows, $totalCols);
1202 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 3, $totalCols-1, $totalRows, $totalCols);
1204 private static function patternShapeSpecial4(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1206 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-1, 0, $totalRows, $totalCols);
1207 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-1, $totalCols-1, $totalRows, $totalCols);
1208 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], 0, $totalCols-3, $totalRows, $totalCols);
1209 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-2, $totalRows, $totalCols);
1210 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-1, $totalRows, $totalCols);
1211 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols-3, $totalRows, $totalCols);
1212 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 1, $totalCols-2, $totalRows, $totalCols);
1213 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 1, $totalCols-1, $totalRows, $totalCols);
1215 private static function placeBitInDatamatrix(&$datamatrix, &$assigned, $bit, $row, $col, $totalRows, $totalCols)
1217 // Put a bit into the matrix
1220 $col +
= 4 - (($totalRows+
4)%8
);
1225 $row +
= 4 - (($totalCols+
4)%8
);
1228 if (!isset($assigned[$row][$col]) ||
$assigned[$row][$col] != 1) {
1229 $datamatrix[$row][$col] = $bit;
1230 $assigned[$row][$col] = 1;
1233 private static function addFinderPattern($datamatrix, $rowsRegion, $colsRegion, $rowsRegionCW, $colsRegionCW)
1235 // Add the finder pattern
1236 $totalRowsCW = ($rowsRegionCW+
2) * $rowsRegion;
1237 $totalColsCW = ($colsRegionCW+
2) * $colsRegion;
1239 $datamatrixTemp = array();
1240 $datamatrixTemp[0] = array_fill(0, $totalColsCW+
2, 0);
1242 for ($i=0; $i<$totalRowsCW; $i++
) {
1243 $datamatrixTemp[$i+
1] = array();
1244 $datamatrixTemp[$i+
1][0] = 0;
1245 $datamatrixTemp[$i+
1][$totalColsCW+
1] = 0;
1246 for ($j=0; $j<$totalColsCW; $j++
) {
1247 if ($i%
($rowsRegionCW+
2) == 0) {
1249 $datamatrixTemp[$i+
1][$j+
1] = 1;
1251 $datamatrixTemp[$i+
1][$j+
1] = 0;
1253 } else if ($i%
($rowsRegionCW+
2) == $rowsRegionCW+
1) {
1254 $datamatrixTemp[$i+
1][$j+
1] = 1;
1255 } else if ($j%
($colsRegionCW+
2) == $colsRegionCW+
1) {
1257 $datamatrixTemp[$i+
1][$j+
1] = 0;
1259 $datamatrixTemp[$i+
1][$j+
1] = 1;
1261 } else if ($j%
($colsRegionCW+
2) == 0) {
1262 $datamatrixTemp[$i+
1][$j+
1] = 1;
1264 $datamatrixTemp[$i+
1][$j+
1] = 0;
1265 $datamatrixTemp[$i+
1][$j+
1] = $datamatrix[$i-1-(2*(floor($i/($rowsRegionCW+
2))))][$j-1-(2*(floor($j/($colsRegionCW+
2))))]; // todo : parseInt => ?
1270 $datamatrixTemp[$totalRowsCW+
1] = array();
1271 for ($j=0; $j<$totalColsCW+
2; $j++
) {
1272 $datamatrixTemp[$totalRowsCW+
1][$j] = 0;
1275 return $datamatrixTemp;
1277 public static function getDigit($text, $rectangular)
1279 $dataCodeWords = self
::encodeDataCodeWordsASCII($text); // Code the text in the ASCII mode
1280 $dataCWCount = count($dataCodeWords);
1281 $index = self
::selectIndex($dataCWCount, $rectangular); // Select the index for the data tables
1282 $totalDataCWCount = self
::$dataCWCount[$index]; // Number of data CW
1283 $solomonCWCount = self
::$solomonCWCount[$index]; // Number of Reed Solomon CW
1284 $totalCWCount = $totalDataCWCount +
$solomonCWCount; // Number of CW
1285 $rowsTotal = self
::$lengthRows[$index]; // Size of symbol
1286 $colsTotal = self
::$lengthCols[$index];
1287 $rowsRegion = self
::$regionRows[$index]; // Number of region
1288 $colsRegion = self
::$regionCols[$index];
1289 $rowsRegionCW = self
::$dataRegionRows[$index];
1290 $colsRegionCW = self
::$dataRegionCols[$index];
1291 $rowsLengthMatrice = $rowsTotal-2*$rowsRegion; // Size of matrice data
1292 $colsLengthMatrice = $colsTotal-2*$colsRegion;
1293 $blocks = self
::$interleavedBlocks[$index]; // Number of Reed Solomon blocks
1294 $errorBlocks = $solomonCWCount / $blocks;
1296 self
::addPadCW($dataCodeWords, $dataCWCount, $totalDataCWCount); // Add codewords pads
1298 $g = self
::calculSolFactorTable($errorBlocks); // Calculate correction coefficients
1300 self
::addReedSolomonCW($solomonCWCount, $g, $totalDataCWCount, $dataCodeWords, $blocks); // Add Reed Solomon codewords
1302 $codeWordsBits = array(); // Calculte bits from codewords
1303 for ($i=0; $i<$totalCWCount; $i++
) {
1304 $codeWordsBits[$i] = self
::getBits($dataCodeWords[$i]);
1307 $datamatrix = array_fill(0, $colsLengthMatrice, array());
1308 $assigned = array_fill(0, $colsLengthMatrice, array());
1310 // Add the bottom-right corner if needed
1311 if ((($rowsLengthMatrice * $colsLengthMatrice) %
8) == 4) {
1312 $datamatrix[$rowsLengthMatrice-2][$colsLengthMatrice-2] = 1;
1313 $datamatrix[$rowsLengthMatrice-1][$colsLengthMatrice-1] = 1;
1314 $datamatrix[$rowsLengthMatrice-1][$colsLengthMatrice-2] = 0;
1315 $datamatrix[$rowsLengthMatrice-2][$colsLengthMatrice-1] = 0;
1316 $assigned[$rowsLengthMatrice-2][$colsLengthMatrice-2] = 1;
1317 $assigned[$rowsLengthMatrice-1][$colsLengthMatrice-1] = 1;
1318 $assigned[$rowsLengthMatrice-1][$colsLengthMatrice-2] = 1;
1319 $assigned[$rowsLengthMatrice-2][$colsLengthMatrice-1] = 1;
1322 // Put the codewords into the matrix
1323 self
::next(0, $rowsLengthMatrice, $colsLengthMatrice, $codeWordsBits, $datamatrix, $assigned);
1325 // Add the finder pattern
1326 $datamatrix = self
::addFinderPattern($datamatrix, $rowsRegion, $colsRegion, $rowsRegionCW, $colsRegionCW);