4 * BarCode Coder Library (BCC Library)
11 * Author : DEMONTE Jean-Baptiste <jbdemonte@gmail.com>
14 * Web site: https://barcode-coder.com/
15 * dual licence : https://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
16 * https://www.gnu.org/licenses/gpl.html
17 * BarCode Coder Library
18 * @package BCC Library
19 * @author DEMONTE Jean-Baptiste <jbdemonte@gmail.com>
20 * @author HOUREZ Jonathan
23 * Added to Openemr by Terry Hill terry@lillysystems.com
24 * 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 } elseif ($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++
) {
321 $c2 = $code[2 * $i +
1];
322 for ($j = 0; $j < 5; $j++
) {
324 if (self
::$encoding[$c1][$j] == 'W') {
329 if (self
::$encoding[$c2][$j] == 'W') {
337 } elseif ($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)));
456 public static function getDigit($code)
458 if (strlen($code) < 12) {
462 return BarcodeEAN
::getDigit($code, 'ean13');
465 public static function compute($code)
467 if (strlen($code) < 12) {
471 return substr(BarcodeEAN
::compute($code, 'ean13'), 1);
477 static private $encoding = array(
478 '100100100100', '100100100110', '100100110100', '100100110110',
479 '100110100100', '100110100110', '100110110100', '100110110110',
480 '110100100100', '110100100110');
482 public static function compute($code, $crc)
484 if (is_array($crc)) {
485 if ($crc['crc1'] == 'mod10') {
486 $code = self
::computeMod10($code);
487 } elseif ($crc['crc1'] == 'mod11') {
488 $code = self
::computeMod11($code);
491 if ($crc['crc2'] == 'mod10') {
492 $code = self
::computeMod10($code);
493 } elseif ($crc['crc2'] == 'mod11') {
494 $code = self
::computeMod11($code);
497 $code = self
::computeMod10($code);
503 private static function computeMod10($code)
505 $len = strlen($code);
509 for ($i = 0; $i < $len; $i++
) {
511 $n1 = 10 * $n1 +
intval($code[$i]);
513 $sum +
= intval($code[$i]);
516 $toPart1 = ! $toPart1;
519 $s1 = (string) (2 * $n1);
521 for ($i = 0; $i < $len; $i++
) {
522 $sum +
= intval($s1[$i]);
525 return($code . ( (string) (10 - $sum %
10) %
10));
528 private static function computeMod11($code)
532 for ($i = strlen($code) - 1; $i > -1; $i--) {
533 $sum +
= $weight * intval($code[$i]);
534 $weight = $weight == 7 ?
2 : $weight +
1;
537 return($code . ( (string) (11 - $sum %
11) %
11) );
540 public static function getDigit($code, $crc)
542 if (preg_match('`[^0-9]`', $code)) {
549 $code = self
::compute($code, false);
555 $len = strlen($code);
556 for ($i = 0; $i < $len; $i++
) {
557 $result .= self
::$encoding[ intval($code[$i]) ];
569 static private $encoding = array(
570 '101011', '1101011', '1001011', '1100101',
571 '1011011', '1101101', '1001101', '1010011',
572 '1101001', '110101', '101101');
574 public static function getDigit($code)
576 if (preg_match('`[^0-9\-]`', $code)) {
581 $intercharacter = '0';
584 $result = '1011001' . $intercharacter;
587 $len = strlen($code);
588 for ($i = 0; $i < $len; $i++
) {
589 $index = $code[$i] == '-' ?
10 : intval($code[$i]);
590 $result .= self
::$encoding[ $index ] . $intercharacter;
596 $weightK = 1; // start at 1 because the right-most character is 'C' checksum
598 for ($i = $len - 1; $i > -1; $i--) {
599 $weightC = $weightC == 10 ?
1 : $weightC +
1;
600 $weightK = $weightK == 10 ?
1 : $weightK +
1;
602 $index = $code[$i] == '-' ?
10 : intval($code[$i]);
604 $weightSumC +
= $weightC * $index;
605 $weightSumK +
= $weightK * $index;
608 $c = $weightSumC %
11;
610 $k = $weightSumK %
11;
612 $result .= self
::$encoding[$c] . $intercharacter;
615 $result .= self
::$encoding[$k] . $intercharacter;
619 $result .= '1011001';
627 static private $encoding = array(
628 '101001101101', '110100101011', '101100101011', '110110010101',
629 '101001101011', '110100110101', '101100110101', '101001011011',
630 '110100101101', '101100101101', '110101001011', '101101001011',
631 '110110100101', '101011001011', '110101100101', '101101100101',
632 '101010011011', '110101001101', '101101001101', '101011001101',
633 '110101010011', '101101010011', '110110101001', '101011010011',
634 '110101101001', '101101101001', '101010110011', '110101011001',
635 '101101011001', '101011011001', '110010101011', '100110101011',
636 '110011010101', '100101101011', '110010110101', '100110110101',
637 '100101011011', '110010101101', '100110101101', '100100100101',
638 '100100101001', '100101001001', '101001001001', '100101101101');
639 public static function getDigit($code)
641 $table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*';
643 $intercharacter = '0';
645 if (strpos($code, '*') !== false) {
649 // Add Start and Stop charactere : *
650 $code = strtoupper('*' . $code . '*');
652 $len = strlen($code);
653 for ($i = 0; $i < $len; $i++
) {
654 $index = strpos($table, $code[$i]);
655 if ($index === false) {
660 $result .= $intercharacter;
663 $result .= self
::$encoding[ $index ];
672 static private $encoding = array(
673 '100010100', '101001000', '101000100', '101000010',
674 '100101000', '100100100', '100100010', '101010000',
675 '100010010', '100001010', '110101000', '110100100',
676 '110100010', '110010100', '110010010', '110001010',
677 '101101000', '101100100', '101100010', '100110100',
678 '100011010', '101011000', '101001100', '101000110',
679 '100101100', '100010110', '110110100', '110110010',
680 '110101100', '110100110', '110010110', '110011010',
681 '101101100', '101100110', '100110110', '100111010',
682 '100101110', '111010100', '111010010', '111001010',
683 '101101110', '101110110', '110101110', '100100110',
684 '111011010', '111010110', '100110010', '101011110');
686 public static function getDigit($code, $crc)
688 $table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%____*'; // _ => ($), (%), (/) et (+)
691 if (strpos($code, '*') !== false) {
695 $code = strtoupper($code);
698 $result .= self
::$encoding[47];
701 $len = strlen($code);
702 for ($i = 0; $i < $len; $i++
) {
704 $index = strpos($table, $c);
705 if (($c == '_') ||
($index === false)) {
709 $result .= self
::$encoding[ $index ];
716 $weightK = 1; // start at 1 because the right-most character is 'C' checksum
718 for ($i = $len - 1; $i > -1; $i--) {
719 $weightC = $weightC == 20 ?
1 : $weightC +
1;
720 $weightK = $weightK == 15 ?
1 : $weightK +
1;
722 $index = strpos($table, $code[$i]);
724 $weightSumC +
= $weightC * $index;
725 $weightSumK +
= $weightK * $index;
728 $c = $weightSumC %
47;
730 $k = $weightSumK %
47;
732 $result .= self
::$encoding[$c];
733 $result .= self
::$encoding[$k];
737 $result .= self
::$encoding[47];
747 static private $encoding = array(
748 '11011001100', '11001101100', '11001100110', '10010011000',
749 '10010001100', '10001001100', '10011001000', '10011000100',
750 '10001100100', '11001001000', '11001000100', '11000100100',
751 '10110011100', '10011011100', '10011001110', '10111001100',
752 '10011101100', '10011100110', '11001110010', '11001011100',
753 '11001001110', '11011100100', '11001110100', '11101101110',
754 '11101001100', '11100101100', '11100100110', '11101100100',
755 '11100110100', '11100110010', '11011011000', '11011000110',
756 '11000110110', '10100011000', '10001011000', '10001000110',
757 '10110001000', '10001101000', '10001100010', '11010001000',
758 '11000101000', '11000100010', '10110111000', '10110001110',
759 '10001101110', '10111011000', '10111000110', '10001110110',
760 '11101110110', '11010001110', '11000101110', '11011101000',
761 '11011100010', '11011101110', '11101011000', '11101000110',
762 '11100010110', '11101101000', '11101100010', '11100011010',
763 '11101111010', '11001000010', '11110001010', '10100110000',
764 '10100001100', '10010110000', '10010000110', '10000101100',
765 '10000100110', '10110010000', '10110000100', '10011010000',
766 '10011000010', '10000110100', '10000110010', '11000010010',
767 '11001010000', '11110111010', '11000010100', '10001111010',
768 '10100111100', '10010111100', '10010011110', '10111100100',
769 '10011110100', '10011110010', '11110100100', '11110010100',
770 '11110010010', '11011011110', '11011110110', '11110110110',
771 '10101111000', '10100011110', '10001011110', '10111101000',
772 '10111100010', '11110101000', '11110100010', '10111011110',
773 '10111101110', '11101011110', '11110101110', '11010000100',
774 '11010010000', '11010011100', '11000111010');
775 public static function getDigit($code)
777 $tableB = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
785 // check each characters
786 $len = strlen($code);
787 for ($i = 0; $i < $len; $i++
) {
788 if (strpos($tableB, $code[$i]) === false) {
793 // check firsts characters : start with C table only if enought numeric
794 $tableCActivated = $len > 1;
796 for ($i = 0; $i < 3 && $i < $len; $i++
) {
797 $tableCActivated &= preg_match('`[0-9]`', $code[$i]);
800 $sum = $tableCActivated ?
105 : 104;
802 // start : [105] : C table or [104] : B table
803 $result = self
::$encoding[ $sum ];
807 if (! $tableCActivated) {
809 // check next character to activate C table if interresting
810 while (($i +
$j < $len) && preg_match('`[0-9]`', $code[$i +
$j])) {
814 // 6 min everywhere or 4 mini at the end
815 $tableCActivated = ($j > 5) ||
(($i +
$j - 1 == $len) && ($j > 3));
817 if ($tableCActivated) {
818 $result .= self
::$encoding[ 99 ]; // C table
819 $sum +
= ++
$isum * 99;
822 // 2 min for table C so need table B
823 } elseif (($i == $len - 1) ||
(preg_match('`[^0-9]`', $code[$i])) ||
(preg_match('`[^0-9]`', $code[$i +
1]))) { //todo : verifier le JS : len - 1!!! XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
824 $tableCActivated = false;
825 $result .= self
::$encoding[ 100 ]; // B table
826 $sum +
= ++
$isum * 100;
829 if ($tableCActivated) {
830 $value = intval(substr($code, $i, 2)); // Add two characters (numeric)
833 $value = strpos($tableB, $code[$i]); // Add one character
837 $result .= self
::$encoding[ $value ];
838 $sum +
= ++
$isum * $value;
842 $result .= self
::$encoding[ $sum %
103 ];
845 $result .= self
::$encoding[ 106 ];
856 static private $encoding = array(
857 '101010011', '101011001', '101001011', '110010101',
858 '101101001', '110101001', '100101011', '100101101',
859 '100110101', '110100101', '101001101', '101100101',
860 '1101011011', '1101101011', '1101101101', '1011011011',
861 '1011001001', '1010010011', '1001001011', '1010011001');
863 public static function getDigit($code)
865 $table = '0123456789-$:/.+';
867 $intercharacter = '0';
869 // add start : A->D : arbitrary choose A
870 $result .= self
::$encoding[16] . $intercharacter;
872 $len = strlen($code);
873 for ($i = 0; $i < $len; $i++
) {
874 $index = strpos($table, $code[$i]);
875 if ($index === false) {
879 $result .= self
::$encoding[ $index ] . $intercharacter;
882 // add stop : A->D : arbitrary choose A
883 $result .= self
::$encoding[16];
888 class BarcodeDatamatrix
890 static private $lengthRows = array(
891 10, 12, 14, 16, 18, 20, 22, 24, 26, // 24 squares et 6 rectangular
892 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144,
893 8, 8, 12, 12, 16, 16);
894 static private $lengthCols = array(
895 10, 12, 14, 16, 18, 20, 22, 24, 26, // Number of columns for the entire datamatrix
896 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144,
897 18, 32, 26, 36, 36, 48);
898 static private $dataCWCount = array(
899 3, 5, 8, 12, 18, 22, 30, 36, // Number of data codewords for the datamatrix
900 44, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050,
901 1304, 1558, 5, 10, 16, 22, 32, 49);
902 static private $solomonCWCount = array(
903 5, 7, 10, 12, 14, 18, 20, 24, 28, // Number of Reed-Solomon codewords for the datamatrix
904 36, 42, 48, 56, 68, 84, 112, 144, 192, 224, 272, 336, 408, 496, 620,
905 7, 11, 14, 18, 24, 28);
906 static private $dataRegionRows = array(
907 8, 10, 12, 14, 16, 18, 20, 22, // Number of rows per region
908 24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
909 6, 6, 10, 10, 14, 14);
910 static private $dataRegionCols = array(
911 8, 10, 12, 14, 16, 18, 20, 22, // Number of columns per region
912 24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
913 16, 14, 24, 16, 16, 22);
914 static private $regionRows = array(
915 1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per row
916 1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
918 static private $regionCols = array(
919 1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per column
920 1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
922 static private $interleavedBlocks = array(
923 1, 1, 1, 1, 1, 1, 1, 1, // Number of blocks
924 1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 6, 6, 8, 8,
926 static private $logTab = array(
927 -255, 255, 1, 240, 2, 225, 241, 53, 3, // Table of log for the Galois field
928 38, 226, 133, 242, 43, 54, 210, 4, 195, 39, 114, 227, 106, 134, 28,
929 243, 140, 44, 23, 55, 118, 211, 234, 5, 219, 196, 96, 40, 222, 115,
930 103, 228, 78, 107, 125, 135, 8, 29, 162, 244, 186, 141, 180, 45, 99,
931 24, 49, 56, 13, 119, 153, 212, 199, 235, 91, 6, 76, 220, 217, 197,
932 11, 97, 184, 41, 36, 223, 253, 116, 138, 104, 193, 229, 86, 79, 171,
933 108, 165, 126, 145, 136, 34, 9, 74, 30, 32, 163, 84, 245, 173, 187,
934 204, 142, 81, 181, 190, 46, 88, 100, 159, 25, 231, 50, 207, 57, 147,
935 14, 67, 120, 128, 154, 248, 213, 167, 200, 63, 236, 110, 92, 176, 7,
936 161, 77, 124, 221, 102, 218, 95, 198, 90, 12, 152, 98, 48, 185, 179,
937 42, 209, 37, 132, 224, 52, 254, 239, 117, 233, 139, 22, 105, 27, 194,
938 113, 230, 206, 87, 158, 80, 189, 172, 203, 109, 175, 166, 62, 127,
939 247, 146, 66, 137, 192, 35, 252, 10, 183, 75, 216, 31, 83, 33, 73,
940 164, 144, 85, 170, 246, 65, 174, 61, 188, 202, 205, 157, 143, 169, 82,
941 72, 182, 215, 191, 251, 47, 178, 89, 151, 101, 94, 160, 123, 26, 112,
942 232, 21, 51, 238, 208, 131, 58, 69, 148, 18, 15, 16, 68, 17, 121, 149,
943 129, 19, 155, 59, 249, 70, 214, 250, 168, 71, 201, 156, 64, 60, 237,
944 130, 111, 20, 93, 122, 177, 150);
945 static private $aLogTab = array(
946 1, 2, 4, 8, 16, 32, 64, 128, 45, 90, // Table of aLog for the Galois field
947 180, 69, 138, 57, 114, 228, 229, 231, 227, 235, 251, 219, 155, 27, 54,
948 108, 216, 157, 23, 46, 92, 184, 93, 186, 89, 178, 73, 146, 9, 18, 36,
949 72, 144, 13, 26, 52, 104, 208, 141, 55, 110, 220, 149, 7, 14, 28, 56,
950 112, 224, 237, 247, 195, 171, 123, 246, 193, 175, 115, 230, 225, 239,
951 243, 203, 187, 91, 182, 65, 130, 41, 82, 164, 101, 202, 185, 95, 190,
952 81, 162, 105, 210, 137, 63, 126, 252, 213, 135, 35, 70, 140, 53, 106,
953 212, 133, 39, 78, 156, 21, 42, 84, 168, 125, 250, 217, 159, 19, 38, 76,
954 152, 29, 58, 116, 232, 253, 215, 131, 43, 86, 172, 117, 234, 249, 223,
955 147, 11, 22, 44, 88, 176, 77, 154, 25, 50, 100, 200, 189, 87, 174, 113,
956 226, 233, 255, 211, 139, 59, 118, 236, 245, 199, 163, 107, 214, 129,
957 47, 94, 188, 85, 170, 121, 242, 201, 191, 83, 166, 97, 194, 169, 127,
958 254, 209, 143, 51, 102, 204, 181, 71, 142, 49, 98, 196, 165, 103, 206,
959 177, 79, 158, 17, 34, 68, 136, 61, 122, 244, 197, 167, 99, 198, 161,
960 111, 222, 145, 15, 30, 60, 120, 240, 205, 183, 67, 134, 33, 66, 132,
961 37, 74, 148, 5, 10, 20, 40, 80, 160, 109, 218, 153, 31, 62, 124, 248,
962 221, 151, 3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75,
964 private static function champGaloisMult($a, $b)
966 // MULTIPLICATION IN GALOIS FIELD GF(2^8)
971 return self
::$aLogTab[(self
::$logTab[$a] + self
::$logTab[$b]) %
255];
973 private static function champGaloisDoub($a, $b)
975 // THE OPERATION a * 2^b IN GALOIS FIELD GF(2^8)
984 return self
::$aLogTab[(self
::$logTab[$a] +
$b) %
255];
986 private static function champGaloisSum($a, $b)
988 // SUM IN GALOIS FIELD GF(2^8)
991 private static function selectIndex($dataCodeWordsCount, $rectangular)
993 // CHOOSE THE GOOD INDEX FOR TABLES
994 if (($dataCodeWordsCount < 1 ||
$dataCodeWordsCount > 1558) && !$rectangular) {
998 if (($dataCodeWordsCount < 1 ||
$dataCodeWordsCount > 49) && $rectangular) {
1002 $n = $rectangular ?
24 : 0;
1004 while (self
::$dataCWCount[$n] < $dataCodeWordsCount) {
1010 private static function encodeDataCodeWordsASCII($text)
1012 $dataCodeWords = array();
1014 $len = strlen($text);
1015 for ($i = 0; $i < $len; $i++
) {
1016 $c = ord($text[$i]);
1018 $dataCodeWords[$n] = 235;
1021 } elseif (($c >= 48 && $c <= 57) && ($i +
1 < $len) && (preg_match('`[0-9]`', $text[$i +
1]))) {
1022 $c = (($c - 48) * 10) +
intval($text[$i +
1]);
1029 $dataCodeWords[$n] = $c;
1033 return $dataCodeWords;
1035 private static function addPadCW(&$tab, $from, $to)
1042 for ($i = $from +
1; $i < $to; $i++
) {
1043 $r = ((149 * ($i +
1)) %
253) +
1;
1044 $tab[$i] = (129 +
$r) %
254;
1047 private static function calculSolFactorTable($solomonCWCount)
1049 // CALCULATE THE REED SOLOMON FACTORS
1050 $g = array_fill(0, $solomonCWCount +
1, 1);
1051 for ($i = 1; $i <= $solomonCWCount; $i++
) {
1052 for ($j = $i - 1; $j >= 0; $j--) {
1053 $g[$j] = self
::champGaloisDoub($g[$j], $i);
1055 $g[$j] = self
::champGaloisSum($g[$j], $g[$j - 1]);
1062 private static function addReedSolomonCW($nSolomonCW, $coeffTab, $nDataCW, &$dataTab, $blocks)
1064 // Add the Reed Solomon codewords
1065 $errorBlocks = $nSolomonCW / $blocks;
1066 $correctionCW = array();
1068 for ($k = 0; $k < $blocks; $k++
) {
1069 for ($i = 0; $i < $errorBlocks; $i++
) {
1070 $correctionCW[$i] = 0;
1073 for ($i = $k; $i < $nDataCW; $i +
= $blocks) {
1074 $temp = self
::champGaloisSum($dataTab[$i], $correctionCW[$errorBlocks - 1]);
1075 for ($j = $errorBlocks - 1; $j >= 0; $j--) {
1077 $correctionCW[$j] = 0;
1079 $correctionCW[$j] = self
::champGaloisMult($temp, $coeffTab[$j]);
1083 $correctionCW[$j] = self
::champGaloisSum($correctionCW[$j - 1], $correctionCW[$j]);
1088 // Renversement des blocs calcules
1090 for ($i = $errorBlocks - 1; $i >= 0; $i--) {
1091 $dataTab[$j] = $correctionCW[$i];
1098 private static function getBits($entier)
1100 // Transform integer to tab of bits
1102 for ($i = 0; $i < 8; $i++
) {
1103 $bits[$i] = $entier & (128 >> $i) ?
1 : 0;
1108 private static function next($etape, $totalRows, $totalCols, $codeWordsBits, &$datamatrix, &$assigned)
1110 // Place codewords into the matrix
1111 $chr = 0; // Place of the 8st bit from the first character to [4][0]
1116 // Check for a special case of corner
1117 if (($row == $totalRows) && ($col == 0)) {
1118 self
::patternShapeSpecial1($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1120 } elseif (($etape < 3) && ($row == $totalRows - 2) && ($col == 0) && ($totalCols %
4 != 0)) {
1121 self
::patternShapeSpecial2($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1123 } elseif (($row == $totalRows - 2) && ($col == 0) && ($totalCols %
8 == 4)) {
1124 self
::patternShapeSpecial3($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1126 } elseif (($row == $totalRows +
4) && ($col == 2) && ($totalCols %
8 == 0)) {
1127 self
::patternShapeSpecial4($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
1131 // Go up and right in the datamatrix
1133 if (($row < $totalRows) && ($col >= 0) && (!isset($assigned[$row][$col]) ||
$assigned[$row][$col] != 1)) {
1134 self
::patternShapeStandard($datamatrix, $assigned, $codeWordsBits[$chr], $row, $col, $totalRows, $totalCols);
1140 } while (($row >= 0) && ($col < $totalCols));
1144 // Go down and left in the datamatrix
1146 if (($row >= 0) && ($col < $totalCols) && (!isset($assigned[$row][$col]) ||
$assigned[$row][$col] != 1)) {
1147 self
::patternShapeStandard($datamatrix, $assigned, $codeWordsBits[$chr], $row, $col, $totalRows, $totalCols);
1153 } while (($row < $totalRows) && ($col >= 0));
1156 } while (($row < $totalRows) ||
($col < $totalCols));
1158 private static function patternShapeStandard(&$datamatrix, &$assigned, $bits, $row, $col, $totalRows, $totalCols)
1160 // Place bits in the matrix (standard or special case)
1161 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $row - 2, $col - 2, $totalRows, $totalCols);
1162 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $row - 2, $col - 1, $totalRows, $totalCols);
1163 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $row - 1, $col - 2, $totalRows, $totalCols);
1164 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], $row - 1, $col - 1, $totalRows, $totalCols);
1165 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], $row - 1, $col, $totalRows, $totalCols);
1166 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], $row, $col - 2, $totalRows, $totalCols);
1167 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], $row, $col - 1, $totalRows, $totalCols);
1168 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], $row, $col, $totalRows, $totalCols);
1170 private static function patternShapeSpecial1(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1172 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows - 1, 0, $totalRows, $totalCols);
1173 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows - 1, 1, $totalRows, $totalCols);
1174 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows - 1, 2, $totalRows, $totalCols);
1175 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols - 2, $totalRows, $totalCols);
1176 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols - 1, $totalRows, $totalCols);
1177 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols - 1, $totalRows, $totalCols);
1178 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 2, $totalCols - 1, $totalRows, $totalCols);
1179 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 3, $totalCols - 1, $totalRows, $totalCols);
1181 private static function patternShapeSpecial2(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1183 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows - 3, 0, $totalRows, $totalCols);
1184 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows - 2, 0, $totalRows, $totalCols);
1185 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows - 1, 0, $totalRows, $totalCols);
1186 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols - 4, $totalRows, $totalCols);
1187 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols - 3, $totalRows, $totalCols);
1188 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 0, $totalCols - 2, $totalRows, $totalCols);
1189 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 0, $totalCols - 1, $totalRows, $totalCols);
1190 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 1, $totalCols - 1, $totalRows, $totalCols);
1192 private static function patternShapeSpecial3(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1194 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows - 3, 0, $totalRows, $totalCols);
1195 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows - 2, 0, $totalRows, $totalCols);
1196 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows - 1, 0, $totalRows, $totalCols);
1197 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols - 2, $totalRows, $totalCols);
1198 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols - 1, $totalRows, $totalCols);
1199 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols - 1, $totalRows, $totalCols);
1200 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 2, $totalCols - 1, $totalRows, $totalCols);
1201 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 3, $totalCols - 1, $totalRows, $totalCols);
1203 private static function patternShapeSpecial4(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols)
1205 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows - 1, 0, $totalRows, $totalCols);
1206 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows - 1, $totalCols - 1, $totalRows, $totalCols);
1207 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], 0, $totalCols - 3, $totalRows, $totalCols);
1208 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols - 2, $totalRows, $totalCols);
1209 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols - 1, $totalRows, $totalCols);
1210 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols - 3, $totalRows, $totalCols);
1211 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 1, $totalCols - 2, $totalRows, $totalCols);
1212 self
::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 1, $totalCols - 1, $totalRows, $totalCols);
1214 private static function placeBitInDatamatrix(&$datamatrix, &$assigned, $bit, $row, $col, $totalRows, $totalCols)
1216 // Put a bit into the matrix
1219 $col +
= 4 - (($totalRows +
4) %
8);
1224 $row +
= 4 - (($totalCols +
4) %
8);
1227 if (!isset($assigned[$row][$col]) ||
$assigned[$row][$col] != 1) {
1228 $datamatrix[$row][$col] = $bit;
1229 $assigned[$row][$col] = 1;
1232 private static function addFinderPattern($datamatrix, $rowsRegion, $colsRegion, $rowsRegionCW, $colsRegionCW)
1234 // Add the finder pattern
1235 $totalRowsCW = ($rowsRegionCW +
2) * $rowsRegion;
1236 $totalColsCW = ($colsRegionCW +
2) * $colsRegion;
1238 $datamatrixTemp = array();
1239 $datamatrixTemp[0] = array_fill(0, $totalColsCW +
2, 0);
1241 for ($i = 0; $i < $totalRowsCW; $i++
) {
1242 $datamatrixTemp[$i +
1] = array();
1243 $datamatrixTemp[$i +
1][0] = 0;
1244 $datamatrixTemp[$i +
1][$totalColsCW +
1] = 0;
1245 for ($j = 0; $j < $totalColsCW; $j++
) {
1246 if ($i %
($rowsRegionCW +
2) == 0) {
1248 $datamatrixTemp[$i +
1][$j +
1] = 1;
1250 $datamatrixTemp[$i +
1][$j +
1] = 0;
1252 } elseif ($i %
($rowsRegionCW +
2) == $rowsRegionCW +
1) {
1253 $datamatrixTemp[$i +
1][$j +
1] = 1;
1254 } elseif ($j %
($colsRegionCW +
2) == $colsRegionCW +
1) {
1256 $datamatrixTemp[$i +
1][$j +
1] = 0;
1258 $datamatrixTemp[$i +
1][$j +
1] = 1;
1260 } elseif ($j %
($colsRegionCW +
2) == 0) {
1261 $datamatrixTemp[$i +
1][$j +
1] = 1;
1263 $datamatrixTemp[$i +
1][$j +
1] = 0;
1264 $datamatrixTemp[$i +
1][$j +
1] = $datamatrix[$i - 1 - (2 * (floor($i / ($rowsRegionCW +
2))))][$j - 1 - (2 * (floor($j / ($colsRegionCW +
2))))]; // todo : parseInt => ?
1269 $datamatrixTemp[$totalRowsCW +
1] = array();
1270 for ($j = 0; $j < $totalColsCW +
2; $j++
) {
1271 $datamatrixTemp[$totalRowsCW +
1][$j] = 0;
1274 return $datamatrixTemp;
1276 public static function getDigit($text, $rectangular)
1278 $dataCodeWords = self
::encodeDataCodeWordsASCII($text); // Code the text in the ASCII mode
1279 $dataCWCount = count($dataCodeWords);
1280 $index = self
::selectIndex($dataCWCount, $rectangular); // Select the index for the data tables
1281 $totalDataCWCount = self
::$dataCWCount[$index]; // Number of data CW
1282 $solomonCWCount = self
::$solomonCWCount[$index]; // Number of Reed Solomon CW
1283 $totalCWCount = $totalDataCWCount +
$solomonCWCount; // Number of CW
1284 $rowsTotal = self
::$lengthRows[$index]; // Size of symbol
1285 $colsTotal = self
::$lengthCols[$index];
1286 $rowsRegion = self
::$regionRows[$index]; // Number of region
1287 $colsRegion = self
::$regionCols[$index];
1288 $rowsRegionCW = self
::$dataRegionRows[$index];
1289 $colsRegionCW = self
::$dataRegionCols[$index];
1290 $rowsLengthMatrice = $rowsTotal - 2 * $rowsRegion; // Size of matrice data
1291 $colsLengthMatrice = $colsTotal - 2 * $colsRegion;
1292 $blocks = self
::$interleavedBlocks[$index]; // Number of Reed Solomon blocks
1293 $errorBlocks = $solomonCWCount / $blocks;
1295 self
::addPadCW($dataCodeWords, $dataCWCount, $totalDataCWCount); // Add codewords pads
1297 $g = self
::calculSolFactorTable($errorBlocks); // Calculate correction coefficients
1299 self
::addReedSolomonCW($solomonCWCount, $g, $totalDataCWCount, $dataCodeWords, $blocks); // Add Reed Solomon codewords
1301 $codeWordsBits = array(); // Calculte bits from codewords
1302 for ($i = 0; $i < $totalCWCount; $i++
) {
1303 $codeWordsBits[$i] = self
::getBits($dataCodeWords[$i]);
1306 $datamatrix = array_fill(0, $colsLengthMatrice, array());
1307 $assigned = array_fill(0, $colsLengthMatrice, array());
1309 // Add the bottom-right corner if needed
1310 if ((($rowsLengthMatrice * $colsLengthMatrice) %
8) == 4) {
1311 $datamatrix[$rowsLengthMatrice - 2][$colsLengthMatrice - 2] = 1;
1312 $datamatrix[$rowsLengthMatrice - 1][$colsLengthMatrice - 1] = 1;
1313 $datamatrix[$rowsLengthMatrice - 1][$colsLengthMatrice - 2] = 0;
1314 $datamatrix[$rowsLengthMatrice - 2][$colsLengthMatrice - 1] = 0;
1315 $assigned[$rowsLengthMatrice - 2][$colsLengthMatrice - 2] = 1;
1316 $assigned[$rowsLengthMatrice - 1][$colsLengthMatrice - 1] = 1;
1317 $assigned[$rowsLengthMatrice - 1][$colsLengthMatrice - 2] = 1;
1318 $assigned[$rowsLengthMatrice - 2][$colsLengthMatrice - 1] = 1;
1321 // Put the codewords into the matrix
1322 self
::next(0, $rowsLengthMatrice, $colsLengthMatrice, $codeWordsBits, $datamatrix, $assigned);
1324 // Add the finder pattern
1325 $datamatrix = self
::addFinderPattern($datamatrix, $rowsRegion, $colsRegion, $rowsRegionCW, $colsRegionCW);