<?php
/*
 *  BarCode Coder Library (BCC Library)
 *  BCCL Version 2.0
 *
 *  Porting : PHP
 *  Version : 2.0.3
 *
 *  Date    : 2013-01-06
 *  Author  : DEMONTE Jean-Baptiste <jbdemonte@gmail.com>
 *            HOUREZ Jonathan
 *
 *  Web site: http://barcode-coder.com/
 *  dual licence :  http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
 *                  http://www.gnu.org/licenses/gpl.html
 */

class Barcode {

    static public function gd($res, $color, $x, $y, $angle, $type, $datas, $width = null, $height = null){
        return self::_draw(__FUNCTION__, $res, $color, $x, $y, $angle, $type, $datas, $width, $height);
    }

    static public function fpdf($res, $color, $x, $y, $angle, $type, $datas, $width = null, $height = null){
        return self::_draw(__FUNCTION__, $res, $color, $x, $y, $angle, $type, $datas, $width, $height);
    }

    static private function _draw($call, $res, $color, $x, $y, $angle, $type, $datas, $width, $height){
        $digit = '';
        $hri   = '';
        $code  = '';
        $crc   = true;
        $rect  = false;
        $b2d   = false;

        if (is_array($datas)){
            foreach(array('code' => '', 'crc' => true, 'rect' => false) as $v => $def){
                $$v = isset($datas[$v]) ? $datas[$v] : $def;
            }
            $code = $code;
        } else {
            $code = $datas;
        }
        if ($code == '') return false;
        $code = (string) $code;

        $type = strtolower($type);

        switch($type){
            case 'std25':
            case 'int25':
                $digit = BarcodeI25::getDigit($code, $crc, $type);
                $hri = BarcodeI25::compute($code, $crc, $type);
                break;
            case 'ean8':
            case 'ean13':
                $digit = BarcodeEAN::getDigit($code, $type);
                $hri = BarcodeEAN::compute($code, $type);
                break;
            case 'upc':
                $digit = BarcodeUPC::getDigit($code);
                $hri = BarcodeUPC::compute($code);
                break;
            case 'code11':
                $digit = Barcode11::getDigit($code);
                $hri = $code;
                break;
            case 'code39':
                $digit = Barcode39::getDigit($code);
                $hri = $code;
                break;
            case 'code93':
                $digit = Barcode93::getDigit($code, $crc);
                $hri = $code;
                break;
            case 'code128':
                $digit = Barcode128::getDigit($code);
                $hri = $code;
                break;
            case 'codabar':
                $digit = BarcodeCodabar::getDigit($code);
                $hri = $code;
                break;
            case 'msi':
                $digit = BarcodeMSI::getDigit($code, $crc);
                $hri = BarcodeMSI::compute($code, $crc);
                break;
            case 'datamatrix':
                $digit = BarcodeDatamatrix::getDigit($code, $rect);
                $hri = $code;
                $b2d = true;
                break;
        }

        if ($digit == '') return false;

        if ( $b2d ){
            $width = is_null($width) ? 5 : $width;
            $height = $width;
        } else {
            $width = is_null($width) ? 1 : $width;
            $height = is_null($height) ? 50 : $height;
            $digit = self::bitStringTo2DArray($digit);
        }

        if ( $call == 'gd' ){
            $result = self::digitToGDRenderer($res, $color, $x, $y, $angle, $width, $height, $digit);
        } else if ( $call == 'fpdf' ){
            $result = self::digitToFPDFRenderer($res, $color, $x, $y, $angle, $width, $height, $digit);
        }

        $result['hri'] = $hri;
        return $result;
    }

    // convert a bit string to an array of array of bit char
    private static function bitStringTo2DArray( $digit ){
        $d = array();
        $len = strlen($digit);
        for($i=0; $i<$len; $i++) $d[$i] = $digit[$i];
        return(array($d));
    }

    private static function digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit){
        $lines = count($digit);
        $columns = count($digit[0]);
        $angle = deg2rad(-$angle);
        $cos = cos($angle);
        $sin = sin($angle);

        self::_rotate($columns * $mw / 2, $lines * $mh / 2, $cos, $sin , $x, $y);
        $xi -=$x;
        $yi -=$y;
        for($y=0; $y<$lines; $y++){
            $x = -1;
            while($x <$columns) {
                $x++;
                if ($digit[$y][$x] == '1') {
                    $z = $x;
                    while(($z + 1 <$columns) && ($digit[$y][$z + 1] == '1')) {
                        $z++;
                    }
                    $x1 = $x * $mw;
                    $y1 = $y * $mh;
                    $x2 = ($z + 1) * $mw;
                    $y2 = ($y + 1) * $mh;
                    self::_rotate($x1, $y1, $cos, $sin, $xA, $yA);
                    self::_rotate($x2, $y1, $cos, $sin, $xB, $yB);
                    self::_rotate($x2, $y2, $cos, $sin, $xC, $yC);
                    self::_rotate($x1, $y2, $cos, $sin, $xD, $yD);
                    $fn(array(
                        $xA + $xi, $yA + $yi,
                        $xB + $xi, $yB + $yi,
                        $xC + $xi, $yC + $yi,
                        $xD + $xi, $yD + $yi
                    ));
                    $x = $z + 1;
                }
            }
        }
        return self::result($xi, $yi, $columns, $lines, $mw, $mh, $cos, $sin);
    }

    // GD barcode renderer
    private static function digitToGDRenderer($gd, $color, $xi, $yi, $angle, $mw, $mh, $digit){
        $fn = function($points) use ($gd, $color) {
            imagefilledpolygon($gd, $points, 4, $color);
        };
        return self::digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit);
    }
    // FPDF barcode renderer
    private static function digitToFPDFRenderer($pdf, $color, $xi, $yi, $angle, $mw, $mh, $digit){
        if (!is_array($color)){
            if (preg_match('`([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})`i', $color, $m)){
                $color = array(hexdec($m[1]),hexdec($m[2]),hexdec($m[3]));
            } else {
                $color = array(0,0,0);
            }
        }
        $color = array_values($color);
        $pdf->SetDrawColor($color[0],$color[1],$color[2]);
        $pdf->SetFillColor($color[0],$color[1],$color[2]);

        $fn = function($points) use ($pdf) {
            $op = 'f';
            $h = $pdf->Geth();
            $k = $pdf->Getk();
            $points_string = '';
            for($i=0; $i < 8; $i+=2){
                $points_string .= sprintf('%.2F %.2F', $points[$i]*$k, ($h-$points[$i+1])*$k);
                $points_string .= $i ? ' l ' : ' m ';
            }
            $pdf->OutPoints($points_string . $op);
        };
        return self::digitToRenderer($fn, $xi, $yi, $angle, $mw, $mh, $digit);
    }

    static private function result($xi, $yi, $columns, $lines, $mw, $mh, $cos, $sin){
        self::_rotate(0, 0, $cos, $sin , $x1, $y1);
        self::_rotate($columns * $mw, 0, $cos, $sin , $x2, $y2);
        self::_rotate($columns * $mw, $lines * $mh, $cos, $sin , $x3, $y3);
        self::_rotate(0, $lines * $mh, $cos, $sin , $x4, $y4);

        return array(
            'width' => $columns * $mw,
            'height'=> $lines * $mh,
            'p1' => array(
                'x' => $xi + $x1,
                'y' => $yi + $y1
            ),
            'p2' => array(
                'x' => $xi + $x2,
                'y' => $yi + $y2
            ),
            'p3' => array(
                'x' => $xi + $x3,
                'y' => $yi + $y3
            ),
            'p4' => array(
                'x' => $xi + $x4,
                'y' => $yi + $y4
            )
        );
    }

    static private function _rotate($x1, $y1, $cos, $sin , &$x, &$y){
        $x = $x1 * $cos - $y1 * $sin;
        $y = $x1 * $sin + $y1 * $cos;
    }

    static public function rotate($x1, $y1, $angle , &$x, &$y){
        $angle = deg2rad(-$angle);
        $cos = cos($angle);
        $sin = sin($angle);
        $x = $x1 * $cos - $y1 * $sin;
        $y = $x1 * $sin + $y1 * $cos;
    }
}

class BarcodeI25 {
    static private $encoding = array('NNWWN', 'WNNNW', 'NWNNW', 'WWNNN', 'NNWNW', 'WNWNN', 'NWWNN', 'NNNWW', 'WNNWN','NWNWN');

    static public function compute($code, $crc, $type){
        if (! $crc) {
            if (strlen($code) % 2) $code = '0' . $code;
        } else {
            if ( ($type == 'int25') && (strlen($code) % 2 == 0) ) $code = '0' . $code;
            $odd = true;
            $sum = 0;
            for($i=strlen($code)-1; $i>-1; $i--){
                $v = intval($code[$i]);
                $sum += $odd ? 3 * $v : $v;
                $odd = ! $odd;
            }
            $code .= (string) ((10 - $sum % 10) % 10);
        }
        return($code);
    }

    static public function getDigit($code, $crc, $type){
        $code = self::compute($code, $crc, $type);
        if ($code == '') return($code);
        $result = '';

        if ($type == 'int25') { // Interleaved 2 of 5
            // start
            $result .= '1010';

            // digits + CRC
            $end = strlen($code) / 2;
            for($i=0; $i<$end; $i++){
                $c1 = $code[2*$i];
                $c2 = $code[2*$i+1];
                for($j=0; $j<5; $j++){
                    $result .= '1';
                    if (self::$encoding[$c1][$j] == 'W') $result .= '1';
                    $result .= '0';
                    if (self::$encoding[$c2][$j] == 'W') $result .= '0';
                }
            }
            // stop
            $result .= '1101';
        } else if ($type == 'std25') {
            // Standard 2 of 5 is a numeric-only barcode that has been in use a long time.
            // Unlike Interleaved 2 of 5, all of the information is encoded in the bars; the spaces are fixed width and are used only to separate the bars.
            // The code is self-checking and does not include a checksum.

            // start
            $result .= '11011010';

            // digits + CRC
            $end = strlen($code);
            for($i=0; $i<$end; $i++){
                $c = $code[$i];
                for($j=0; $j<5; $j++){
                    $result .= '1';
                    if (self::$encoding[$c][$j] == 'W') $result .= '11';
                    $result .= '0';
                }
            }
            // stop
            $result .= '11010110';
        }
        return($result);
    }
}


class BarcodeEAN {
    static private $encoding = array(
        array('0001101', '0100111', '1110010'),
        array('0011001', '0110011', '1100110'),
        array('0010011', '0011011', '1101100'),
        array('0111101', '0100001', '1000010'),
        array('0100011', '0011101', '1011100'),
        array('0110001', '0111001', '1001110'),
        array('0101111', '0000101', '1010000'),
        array('0111011', '0010001', '1000100'),
        array('0110111', '0001001', '1001000'),
        array('0001011', '0010111', '1110100')
    );

    static private $first = array('000000','001011','001101','001110','010011','011001','011100','010101','010110','011010');

    static public function getDigit($code, $type){
        // Check len (12 for ean13, 7 for ean8)
        $len = $type == 'ean8' ? 7 : 12;
        $code = substr($code, 0, $len);
        if (!preg_match('`[0-9]{'.$len.'}`', $code)) return('');

        // get checksum
        $code = self::compute($code, $type);

        // process analyse
        $result = '101'; // start

        if ($type == 'ean8'){
            // process left part
            for($i=0; $i<4; $i++){
                $result .= self::$encoding[intval($code[$i])][0];
            }

            // center guard bars
            $result .= '01010';

            // process right part
            for($i=4; $i<8; $i++){
                $result .= self::$encoding[intval($code[$i])][2];
            }

        } else { // ean13
            // extract first digit and get sequence
            $seq = self::$first[ intval($code[0]) ];

            // process left part
            for($i=1; $i<7; $i++){
                $result .= self::$encoding[intval($code[$i])][ intval($seq[$i-1]) ];
            }

            // center guard bars
            $result .= '01010';

            // process right part
            for($i=7; $i<13; $i++){
                $result .= self::$encoding[intval($code[$i])][ 2 ];
            }
        } // ean13

        $result .= '101'; // stop
        return($result);
    }

    static public function compute($code, $type){
        $len = $type == 'ean13' ? 12 : 7;
        $code = substr($code, 0, $len);
        if (!preg_match('`[0-9]{'.$len.'}`', $code)) return('');
        $sum = 0;
        $odd = true;
        for($i=$len-1; $i>-1; $i--){
            $sum += ($odd ? 3 : 1) * intval($code[$i]);
            $odd = ! $odd;
        }
        return($code . ( (string) ((10 - $sum % 10) % 10)));
    }
}

class BarcodeUPC {

    static public function getDigit($code){
        if (strlen($code) < 12) {
            $code = '0' . $code;
        }
        return BarcodeEAN::getDigit($code, 'ean13');
    }

    static public function compute($code){
        if (strlen($code) < 12) {
            $code = '0' . $code;
        }
        return substr(BarcodeEAN::compute($code, 'ean13'), 1);
    }
}

class BarcodeMSI {
    static private $encoding = array(
        '100100100100', '100100100110', '100100110100', '100100110110',
        '100110100100', '100110100110', '100110110100', '100110110110',
        '110100100100', '110100100110');

    static public function compute($code, $crc){
        if (is_array($crc)){
            if ($crc['crc1'] == 'mod10'){
                $code = self::computeMod10($code);
            } else if ($crc['crc1'] == 'mod11'){
                $code = self::computeMod11($code);
            }
            if ($crc['crc2'] == 'mod10'){
                $code = self::computeMod10($code);
            } else if ($crc['crc2'] == 'mod11'){
                $code = self::computeMod11($code);
            }
        } else if ($crc){
            $code = self::computeMod10($code);
        }
        return($code);
    }

    static private function computeMod10($code){
        $len = strlen($code);
        $toPart1 = $len % 2;
        $n1 = 0;
        $sum = 0;
        for($i=0; $i<$len; $i++){
            if ($toPart1) {
                $n1 = 10 * $n1 + intval($code[$i]);
            } else {
                $sum += intval($code[$i]);
            }
            $toPart1 = ! $toPart1;
        }
        $s1 = (string) (2 * $n1);
        $len = strlen($s1);
        for($i=0; $i<$len; $i++){
            $sum += intval($s1[$i]);
        }
        return($code . ( (string) (10 - $sum % 10) % 10));
    }

    static private function computeMod11($code){
        $sum = 0;
        $weight = 2;
        for($i=strlen($code)-1; $i>-1; $i--){
            $sum += $weight * intval($code[$i]);
            $weight = $weight == 7 ? 2 : $weight + 1;
        }
        return($code . ( (string) (11 - $sum % 11) % 11) );
    }

    static public function getDigit($code, $crc){
        if (preg_match('`[^0-9]`', $code)) return '';
        $index = 0;
        $result = '';

        $code = self::compute($code, false);

        // start
        $result = '110';

        // digits
        $len = strlen($code);
        for($i=0; $i<$len; $i++){
            $result .= self::$encoding[ intval($code[$i]) ];
        }

        // stop
        $result .= '1001';

        return($result);
    }
}

class Barcode11 {
    static private $encoding = array(
        '101011', '1101011', '1001011', '1100101',
        '1011011', '1101101', '1001101', '1010011',
        '1101001', '110101', '101101');

    static public function getDigit($code){
        if (preg_match('`[^0-9\-]`', $code)) return '';
        $result = '';
        $intercharacter = '0';

        // start
        $result = '1011001' . $intercharacter;

        // digits
        $len = strlen($code);
        for($i=0; $i<$len; $i++){
            $index = $code[$i] == '-' ? 10 : intval($code[$i]);
            $result .= self::$encoding[ $index ] . $intercharacter;
        }

        // checksum
        $weightC    = 0;
        $weightSumC = 0;
        $weightK    = 1; // start at 1 because the right-most character is 'C' checksum
        $weightSumK = 0;
        for($i=$len-1; $i>-1; $i--){
            $weightC = $weightC == 10 ? 1 : $weightC + 1;
            $weightK = $weightK == 10 ? 1 : $weightK + 1;

            $index = $code[$i] == '-' ? 10 : intval($code[$i]);

            $weightSumC += $weightC * $index;
            $weightSumK += $weightK * $index;
        }

        $c = $weightSumC % 11;
        $weightSumK += $c;
        $k = $weightSumK % 11;

        $result .= self::$encoding[$c] . $intercharacter;

        if ($len >= 10){
            $result .= self::$encoding[$k] . $intercharacter;
        }

        // stop
        $result  .= '1011001';

        return($result);
    }
}

class Barcode39 {
    static private $encoding = array(
        '101001101101', '110100101011', '101100101011', '110110010101',
        '101001101011', '110100110101', '101100110101', '101001011011',
        '110100101101', '101100101101', '110101001011', '101101001011',
        '110110100101', '101011001011', '110101100101', '101101100101',
        '101010011011', '110101001101', '101101001101', '101011001101',
        '110101010011', '101101010011', '110110101001', '101011010011',
        '110101101001', '101101101001', '101010110011', '110101011001',
        '101101011001', '101011011001', '110010101011', '100110101011',
        '110011010101', '100101101011', '110010110101', '100110110101',
        '100101011011', '110010101101', '100110101101', '100100100101',
        '100100101001', '100101001001', '101001001001', '100101101101');
    static public function getDigit($code){
        $table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*';
        $result = '';
        $intercharacter = '0';

        if (strpos($code, '*') !== false) return('');

        // Add Start and Stop charactere : *
        $code = strtoupper('*' . $code . '*');

        $len = strlen($code);
        for($i=0; $i<$len; $i++){
            $index = strpos($table, $code[$i]);
            if ($index === false) return('');
            if ($i > 0) $result .= $intercharacter;
            $result .= self::$encoding[ $index ];
        }
        return($result);
    }
}

class Barcode93{
    static private $encoding = array(
        '100010100', '101001000', '101000100', '101000010',
        '100101000', '100100100', '100100010', '101010000',
        '100010010', '100001010', '110101000', '110100100',
        '110100010', '110010100', '110010010', '110001010',
        '101101000', '101100100', '101100010', '100110100',
        '100011010', '101011000', '101001100', '101000110',
        '100101100', '100010110', '110110100', '110110010',
        '110101100', '110100110', '110010110', '110011010',
        '101101100', '101100110', '100110110', '100111010',
        '100101110', '111010100', '111010010', '111001010',
        '101101110', '101110110', '110101110', '100100110',
        '111011010', '111010110', '100110010', '101011110');

    static public function getDigit($code, $crc){
        $table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%____*'; // _ => ($), (%), (/) et (+)
        $result = '';

        if (strpos($code, '*') !== false) return('');

        $code = strtoupper($code);

        // start :  *
        $result  .= self::$encoding[47];

        // digits
        $len = strlen($code);
        for($i=0; $i<$len; $i++){
            $c = $code[$i];
            $index = strpos($table, $c);
            if ( ($c == '_') || ($index === false) ) return('');
            $result .= self::$encoding[ $index ];
        }

        // checksum
        if ($crc){
            $weightC    = 0;
            $weightSumC = 0;
            $weightK    = 1; // start at 1 because the right-most character is 'C' checksum
            $weightSumK = 0;
            for($i=$len-1; $i>-1; $i--){
                $weightC = $weightC == 20 ? 1 : $weightC + 1;
                $weightK = $weightK == 15 ? 1 : $weightK + 1;

                $index = strpos($table, $code[$i]);

                $weightSumC += $weightC * $index;
                $weightSumK += $weightK * $index;
            }

            $c = $weightSumC % 47;
            $weightSumK += $c;
            $k = $weightSumK % 47;

            $result .= self::$encoding[$c];
            $result .= self::$encoding[$k];
        }

        // stop : *
        $result  .= self::$encoding[47];

        // Terminaison bar
        $result  .= '1';
        return($result);
    }
}

class Barcode128 {
    static private $encoding = array(
        '11011001100', '11001101100', '11001100110', '10010011000',
        '10010001100', '10001001100', '10011001000', '10011000100',
        '10001100100', '11001001000', '11001000100', '11000100100',
        '10110011100', '10011011100', '10011001110', '10111001100',
        '10011101100', '10011100110', '11001110010', '11001011100',
        '11001001110', '11011100100', '11001110100', '11101101110',
        '11101001100', '11100101100', '11100100110', '11101100100',
        '11100110100', '11100110010', '11011011000', '11011000110',
        '11000110110', '10100011000', '10001011000', '10001000110',
        '10110001000', '10001101000', '10001100010', '11010001000',
        '11000101000', '11000100010', '10110111000', '10110001110',
        '10001101110', '10111011000', '10111000110', '10001110110',
        '11101110110', '11010001110', '11000101110', '11011101000',
        '11011100010', '11011101110', '11101011000', '11101000110',
        '11100010110', '11101101000', '11101100010', '11100011010',
        '11101111010', '11001000010', '11110001010', '10100110000',
        '10100001100', '10010110000', '10010000110', '10000101100',
        '10000100110', '10110010000', '10110000100', '10011010000',
        '10011000010', '10000110100', '10000110010', '11000010010',
        '11001010000', '11110111010', '11000010100', '10001111010',
        '10100111100', '10010111100', '10010011110', '10111100100',
        '10011110100', '10011110010', '11110100100', '11110010100',
        '11110010010', '11011011110', '11011110110', '11110110110',
        '10101111000', '10100011110', '10001011110', '10111101000',
        '10111100010', '11110101000', '11110100010', '10111011110',
        '10111101110', '11101011110', '11110101110', '11010000100',
        '11010010000', '11010011100', '11000111010');
    static public function getDigit($code){
        $tableB = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
        $result = "";
        $sum = 0;
        $isum = 0;
        $i = 0;
        $j = 0;
        $value = 0;

        // check each characters
        $len = strlen($code);
        for($i=0; $i<$len; $i++){
            if (strpos($tableB, $code[$i]) === false) return("");
        }

        // check firsts characters : start with C table only if enought numeric
        $tableCActivated = $len> 1;
        $c = '';
        for($i=0; $i<3 && $i<$len; $i++){
            $tableCActivated &= preg_match('`[0-9]`', $code[$i]);
        }

        $sum = $tableCActivated ? 105 : 104;

        // start : [105] : C table or [104] : B table
        $result = self::$encoding[ $sum ];

        $i = 0;
        while( $i < $len ){
            if (! $tableCActivated){
                $j = 0;
                // check next character to activate C table if interresting
                while ( ($i + $j < $len) && preg_match('`[0-9]`', $code[$i+$j]) ) $j++;

                // 6 min everywhere or 4 mini at the end
                $tableCActivated = ($j > 5) || (($i + $j - 1 == $len) && ($j > 3));

                if ( $tableCActivated ){
                    $result .= self::$encoding[ 99 ]; // C table
                    $sum += ++$isum * 99;
                }
                // 2 min for table C so need table B
            } 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
                $tableCActivated = false;
                $result .= self::$encoding[ 100 ]; // B table
                $sum += ++$isum * 100;
            }

            if ( $tableCActivated ) {
                $value = intval(substr($code, $i, 2)); // Add two characters (numeric)
                $i += 2;
            } else {
                $value = strpos($tableB, $code[$i]); // Add one character
                $i++;
            }
            $result  .= self::$encoding[ $value ];
            $sum += ++$isum * $value;
        }

        // Add CRC
        $result  .= self::$encoding[ $sum % 103 ];

        // Stop
        $result .= self::$encoding[ 106 ];

        // Termination bar
        $result .= '11';

        return($result);
    }
}

class BarcodeCodabar {
    static private $encoding = array(
        '101010011', '101011001', '101001011', '110010101',
        '101101001', '110101001', '100101011', '100101101',
        '100110101', '110100101', '101001101', '101100101',
        '1101011011', '1101101011', '1101101101', '1011011011',
        '1011001001', '1010010011', '1001001011', '1010011001');

    static public function getDigit($code){
        $table = '0123456789-$:/.+';
        $result = '';
        $intercharacter = '0';

        // add start : A->D : arbitrary choose A
        $result .= self::$encoding[16] . $intercharacter;

        $len = strlen($code);
        for($i=0; $i<$len; $i++){
            $index = strpos($table, $code[$i]);
            if ($index === false) return('');
            $result .= self::$encoding[ $index ] . $intercharacter;
        }

        // add stop : A->D : arbitrary choose A
        $result .= self::$encoding[16];
        return($result);
    }
}

class BarcodeDatamatrix {
    static private $lengthRows = array(
        10, 12, 14, 16, 18, 20, 22, 24, 26,  // 24 squares et 6 rectangular
        32, 36, 40, 44, 48, 52, 64, 72, 80,  88, 96, 104, 120, 132, 144,
        8, 8, 12, 12, 16, 16);
    static private $lengthCols = array(
        10, 12, 14, 16, 18, 20, 22, 24, 26,  // Number of columns for the entire datamatrix
        32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144,
        18, 32, 26, 36, 36, 48);
    static private $dataCWCount = array(
        3, 5, 8, 12,  18,  22,  30,  36,  // Number of data codewords for the datamatrix
        44, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050,
        1304, 1558, 5, 10, 16, 22, 32, 49);
    static private $solomonCWCount = array(
        5, 7, 10, 12, 14, 18, 20, 24, 28, // Number of Reed-Solomon codewords for the datamatrix
        36, 42, 48, 56, 68, 84, 112, 144, 192, 224, 272, 336, 408, 496, 620,
        7, 11, 14, 18, 24, 28);
    static private $dataRegionRows = array(
        8, 10, 12, 14, 16, 18, 20, 22, // Number of rows per region
        24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
        6,  6, 10, 10, 14, 14);
    static private $dataRegionCols = array(
        8, 10, 12, 14, 16, 18, 20, 22, // Number of columns per region
        24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
        16, 14, 24, 16, 16, 22);
    static private $regionRows = array(
        1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per row
        1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
        1, 1, 1, 1, 1, 1);
    static private $regionCols = array(
        1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per column
        1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
        1, 2, 1, 2, 2, 2);
    static private $interleavedBlocks = array(
        1, 1, 1, 1, 1, 1, 1, 1, // Number of blocks
        1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 6, 6, 8, 8,
        1, 1, 1, 1, 1, 1);
    static private $logTab = array(
        -255, 255, 1, 240, 2, 225, 241, 53, 3,  // Table of log for the Galois field
        38, 226, 133, 242, 43, 54, 210, 4, 195, 39, 114, 227, 106, 134, 28,
        243, 140, 44, 23, 55, 118, 211, 234, 5, 219, 196, 96, 40, 222, 115,
        103, 228, 78, 107, 125, 135, 8, 29, 162, 244, 186, 141, 180, 45, 99,
        24, 49, 56, 13, 119, 153, 212, 199, 235, 91, 6, 76, 220, 217, 197,
        11, 97, 184, 41, 36, 223, 253, 116, 138, 104, 193, 229, 86, 79, 171,
        108, 165, 126, 145, 136, 34, 9, 74, 30, 32, 163, 84, 245, 173, 187,
        204, 142, 81, 181, 190, 46, 88, 100, 159, 25, 231, 50, 207, 57, 147,
        14, 67, 120, 128, 154, 248, 213, 167, 200, 63, 236, 110, 92, 176, 7,
        161, 77, 124, 221, 102, 218, 95, 198, 90, 12, 152, 98, 48, 185, 179,
        42, 209, 37, 132, 224, 52, 254, 239, 117, 233, 139, 22, 105, 27, 194,
        113, 230, 206, 87, 158, 80, 189, 172, 203, 109, 175, 166, 62, 127,
        247, 146, 66, 137, 192, 35, 252, 10, 183, 75, 216, 31, 83, 33, 73,
        164, 144, 85, 170, 246, 65, 174, 61, 188, 202, 205, 157, 143, 169, 82,
        72, 182, 215, 191, 251, 47, 178, 89, 151, 101, 94, 160, 123, 26, 112,
        232, 21, 51, 238, 208, 131, 58, 69, 148, 18, 15, 16, 68, 17, 121, 149,
        129, 19, 155, 59, 249, 70, 214, 250, 168, 71, 201, 156, 64, 60, 237,
        130, 111, 20, 93, 122, 177, 150);
    static private $aLogTab = array(
        1, 2, 4, 8, 16, 32, 64, 128, 45, 90, // Table of aLog for the Galois field
        180, 69, 138, 57, 114, 228, 229, 231, 227, 235, 251, 219, 155, 27, 54,
        108, 216, 157, 23, 46, 92, 184, 93, 186, 89, 178, 73, 146, 9, 18, 36,
        72, 144, 13, 26, 52, 104, 208, 141, 55, 110, 220, 149, 7, 14, 28, 56,
        112, 224, 237, 247, 195, 171, 123, 246, 193, 175, 115, 230, 225, 239,
        243, 203, 187, 91, 182, 65, 130, 41, 82, 164, 101, 202, 185, 95, 190,
        81, 162, 105, 210, 137, 63, 126, 252, 213, 135, 35, 70, 140, 53, 106,
        212, 133, 39, 78, 156, 21, 42, 84, 168, 125, 250, 217, 159, 19, 38, 76,
        152, 29, 58, 116, 232, 253, 215, 131, 43, 86, 172, 117, 234, 249, 223,
        147, 11, 22, 44, 88, 176, 77, 154, 25, 50, 100, 200, 189, 87, 174, 113,
        226, 233, 255, 211, 139, 59, 118, 236, 245, 199, 163, 107, 214, 129,
        47, 94, 188, 85, 170, 121, 242, 201, 191, 83, 166, 97, 194, 169, 127,
        254, 209, 143, 51, 102, 204, 181, 71, 142, 49, 98, 196, 165, 103, 206,
        177, 79, 158, 17, 34, 68, 136, 61, 122, 244, 197, 167, 99, 198, 161,
        111, 222, 145, 15, 30, 60, 120, 240, 205, 183, 67, 134, 33, 66, 132,
        37, 74, 148, 5, 10, 20, 40, 80, 160, 109, 218, 153, 31, 62, 124, 248,
        221, 151, 3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75,
        150, 1);
    static private function champGaloisMult($a, $b){  // MULTIPLICATION IN GALOIS FIELD GF(2^8)
        if(!$a || !$b) return 0;
        return self::$aLogTab[(self::$logTab[$a] + self::$logTab[$b]) % 255];
    }
    static private function champGaloisDoub($a, $b){  // THE OPERATION a * 2^b IN GALOIS FIELD GF(2^8)
        if (!$a) return 0;
        if (!$b) return $a;
        return self::$aLogTab[(self::$logTab[$a] + $b) % 255];
    }
    static private function champGaloisSum($a, $b){ // SUM IN GALOIS FIELD GF(2^8)
        return $a ^ $b;
    }
    static private function selectIndex($dataCodeWordsCount, $rectangular){ // CHOOSE THE GOOD INDEX FOR TABLES
        if (($dataCodeWordsCount<1 || $dataCodeWordsCount>1558) && !$rectangular) return -1;
        if (($dataCodeWordsCount<1 || $dataCodeWordsCount>49) && $rectangular)  return -1;

        $n = $rectangular ? 24 : 0;

        while (self::$dataCWCount[$n] < $dataCodeWordsCount) $n++;
        return $n;
    }
    static private function encodeDataCodeWordsASCII($text) {
        $dataCodeWords = array();
        $n = 0;
        $len = strlen($text);
        for ($i=0; $i<$len; $i++){
            $c = ord($text[$i]);
            if ($c > 127) {
                $dataCodeWords[$n] = 235;
                $c -= 127;
                $n++;
            } else if (($c>=48 && $c<=57) && ($i+1<$len) && (preg_match('`[0-9]`', $text[$i+1]))) {
                $c = (($c - 48) * 10) + intval($text[$i+1]);
                $c += 130;
                $i++;
            } else $c++;
            $dataCodeWords[$n] = $c;
            $n++;
        }
        return $dataCodeWords;
    }
    static private function addPadCW(&$tab, $from, $to){
        if ($from >= $to) return ;
        $tab[$from] = 129;
        for ($i=$from+1; $i<$to; $i++){
            $r = ((149 * ($i+1)) % 253) + 1;
            $tab[$i] = (129 + $r) % 254;
        }
    }
    static private function calculSolFactorTable($solomonCWCount){ // CALCULATE THE REED SOLOMON FACTORS
        $g = array_fill(0, $solomonCWCount+1, 1);
        for($i = 1; $i <= $solomonCWCount; $i++) {
            for($j = $i - 1; $j >= 0; $j--) {
                $g[$j] = self::champGaloisDoub($g[$j], $i);
                if($j > 0) $g[$j] = self::champGaloisSum($g[$j], $g[$j-1]);
            }
        }
        return $g;
    }
    static private function addReedSolomonCW($nSolomonCW, $coeffTab, $nDataCW, &$dataTab, $blocks){ // Add the Reed Solomon codewords
        $errorBlocks = $nSolomonCW / $blocks;
        $correctionCW = array();

        for($k = 0; $k < $blocks; $k++) {
            for ($i=0; $i < $errorBlocks; $i++) $correctionCW[$i] = 0;

            for ($i=$k; $i<$nDataCW; $i+=$blocks){
                $temp = self::champGaloisSum($dataTab[$i], $correctionCW[$errorBlocks-1]);
                for ($j=$errorBlocks-1; $j>=0; $j--){
                    if ( !$temp ) {
                        $correctionCW[$j] = 0;
                    } else {
                        $correctionCW[$j] = self::champGaloisMult($temp, $coeffTab[$j]);
                    }
                    if ($j>0) $correctionCW[$j] = self::champGaloisSum($correctionCW[$j-1], $correctionCW[$j]);
                }
            }
            // Renversement des blocs calcules
            $j = $nDataCW + $k;
            for ($i=$errorBlocks-1; $i>=0; $i--){
                $dataTab[$j] = $correctionCW[$i];
                $j=$j+$blocks;
            }
        }
        return $dataTab;
    }
    static private function getBits($entier){ // Transform integer to tab of bits
        $bits = array();
        for ($i=0; $i<8; $i++){
            $bits[$i] = $entier & (128 >> $i) ? 1 : 0;
        }
        return $bits;
    }
    static private function next($etape, $totalRows, $totalCols, $codeWordsBits, &$datamatrix, &$assigned){ // Place codewords into the matrix
        $chr = 0; // Place of the 8st bit from the first character to [4][0]
        $row = 4;
        $col = 0;

        do {
            // Check for a special case of corner
            if(($row == $totalRows) && ($col == 0)){
                self::patternShapeSpecial1($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
                $chr++;
            } else if(($etape<3) && ($row == $totalRows-2) && ($col == 0) && ($totalCols%4 != 0)){
                self::patternShapeSpecial2($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
                $chr++;
            } else if(($row == $totalRows-2) && ($col == 0) && ($totalCols%8 == 4)){
                self::patternShapeSpecial3($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
                $chr++;
            }
            else if(($row == $totalRows+4) && ($col == 2) && ($totalCols%8 == 0)){
                self::patternShapeSpecial4($datamatrix, $assigned, $codeWordsBits[$chr], $totalRows, $totalCols);
                $chr++;
            }

            // Go up and right in the datamatrix
            do {
                if(($row < $totalRows) && ($col >= 0) && (!isset($assigned[$row][$col]) || $assigned[$row][$col]!=1)) {
                    self::patternShapeStandard($datamatrix, $assigned, $codeWordsBits[$chr], $row, $col, $totalRows, $totalCols);
                    $chr++;
                }
                $row -= 2;
                $col += 2;
            } while (($row >= 0) && ($col < $totalCols));
            $row += 1;
            $col += 3;

            // Go down and left in the datamatrix
            do {
                if(($row >= 0) && ($col < $totalCols) && (!isset($assigned[$row][$col]) || $assigned[$row][$col]!=1)){
                    self::patternShapeStandard($datamatrix, $assigned, $codeWordsBits[$chr], $row, $col, $totalRows, $totalCols);
                    $chr++;
                }
                $row += 2;
                $col -= 2;
            } while (($row < $totalRows) && ($col >=0));
            $row += 3;
            $col += 1;
        } while (($row < $totalRows) || ($col < $totalCols));
    }
    static private function patternShapeStandard(&$datamatrix, &$assigned, $bits, $row, $col, $totalRows, $totalCols){ // Place bits in the matrix (standard or special case)
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $row-2, $col-2, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $row-2, $col-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $row-1, $col-2, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], $row-1, $col-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], $row-1, $col, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], $row, $col-2, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], $row, $col-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], $row, $col, $totalRows, $totalCols);
    }
    static private function patternShapeSpecial1(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols ){
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-1,  0, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-1,  1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows-1,  2, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-2, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 2, $totalCols-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 3, $totalCols-1, $totalRows, $totalCols);
    }
    static private function patternShapeSpecial2(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols ){
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-3,  0, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-2,  0, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows-1,  0, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-4, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-3, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 0, $totalCols-2, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 0, $totalCols-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 1, $totalCols-1, $totalRows, $totalCols);
    }
    static private function patternShapeSpecial3(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols ){
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-3,  0, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-2,  0, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], $totalRows-1,  0, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-2, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 2, $totalCols-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 3, $totalCols-1, $totalRows, $totalCols);
    }
    static private function patternShapeSpecial4(&$datamatrix, &$assigned, $bits, $totalRows, $totalCols ){
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[0], $totalRows-1,  0, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[1], $totalRows-1, $totalCols-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[2], 0, $totalCols-3, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[3], 0, $totalCols-2, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[4], 0, $totalCols-1, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[5], 1, $totalCols-3, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[6], 1, $totalCols-2, $totalRows, $totalCols);
        self::placeBitInDatamatrix($datamatrix, $assigned, $bits[7], 1, $totalCols-1, $totalRows, $totalCols);
    }
    static private function placeBitInDatamatrix(&$datamatrix, &$assigned, $bit, $row, $col, $totalRows, $totalCols){ // Put a bit into the matrix
        if ($row < 0) {
            $row += $totalRows;
            $col += 4 - (($totalRows+4)%8);
        }
        if ($col < 0) {
            $col += $totalCols;
            $row += 4 - (($totalCols+4)%8);
        }
        if (!isset($assigned[$row][$col]) || $assigned[$row][$col] != 1) {
            $datamatrix[$row][$col] = $bit;
            $assigned[$row][$col] = 1;
        }
    }
    static private function addFinderPattern($datamatrix, $rowsRegion, $colsRegion, $rowsRegionCW, $colsRegionCW){ // Add the finder pattern
        $totalRowsCW = ($rowsRegionCW+2) * $rowsRegion;
        $totalColsCW = ($colsRegionCW+2) * $colsRegion;

        $datamatrixTemp = array();
        $datamatrixTemp[0] = array_fill(0, $totalColsCW+2, 0);

        for ($i=0; $i<$totalRowsCW; $i++){
            $datamatrixTemp[$i+1] = array();
            $datamatrixTemp[$i+1][0] = 0;
            $datamatrixTemp[$i+1][$totalColsCW+1] = 0;
            for ($j=0; $j<$totalColsCW; $j++){
                if ($i%($rowsRegionCW+2) == 0){
                    if ($j%2 == 0){
                        $datamatrixTemp[$i+1][$j+1] = 1;
                    } else {
                        $datamatrixTemp[$i+1][$j+1] = 0;
                    }
                } else if ($i%($rowsRegionCW+2) == $rowsRegionCW+1){
                    $datamatrixTemp[$i+1][$j+1] = 1;
                } else if ($j%($colsRegionCW+2) == $colsRegionCW+1){
                    if ($i%2 == 0){
                        $datamatrixTemp[$i+1][$j+1] = 0;
                    } else {
                        $datamatrixTemp[$i+1][$j+1] = 1;
                    }
                } else if ($j%($colsRegionCW+2) == 0){
                    $datamatrixTemp[$i+1][$j+1] = 1;
                } else{
                    $datamatrixTemp[$i+1][$j+1] = 0;
                    $datamatrixTemp[$i+1][$j+1] = $datamatrix[$i-1-(2*(floor($i/($rowsRegionCW+2))))][$j-1-(2*(floor($j/($colsRegionCW+2))))]; // todo : parseInt => ?
                }
            }
        }
        $datamatrixTemp[$totalRowsCW+1] = array();
        for ($j=0; $j<$totalColsCW+2; $j++){
            $datamatrixTemp[$totalRowsCW+1][$j] = 0;
        }
        return $datamatrixTemp;
    }
    static public function getDigit($text, $rectangular){
        $dataCodeWords = self::encodeDataCodeWordsASCII($text); // Code the text in the ASCII mode
        $dataCWCount = count($dataCodeWords);
        $index = self::selectIndex($dataCWCount, $rectangular); // Select the index for the data tables
        $totalDataCWCount = self::$dataCWCount[$index]; // Number of data CW
        $solomonCWCount = self::$solomonCWCount[$index]; // Number of Reed Solomon CW
        $totalCWCount = $totalDataCWCount + $solomonCWCount; // Number of CW
        $rowsTotal = self::$lengthRows[$index]; // Size of symbol
        $colsTotal = self::$lengthCols[$index];
        $rowsRegion = self::$regionRows[$index]; // Number of region
        $colsRegion = self::$regionCols[$index];
        $rowsRegionCW = self::$dataRegionRows[$index];
        $colsRegionCW = self::$dataRegionCols[$index];
        $rowsLengthMatrice = $rowsTotal-2*$rowsRegion; // Size of matrice data
        $colsLengthMatrice = $colsTotal-2*$colsRegion;
        $blocks = self::$interleavedBlocks[$index];  // Number of Reed Solomon blocks
        $errorBlocks = $solomonCWCount / $blocks;

        self::addPadCW($dataCodeWords, $dataCWCount, $totalDataCWCount); // Add codewords pads

        $g = self::calculSolFactorTable($errorBlocks); // Calculate correction coefficients

        self::addReedSolomonCW($solomonCWCount, $g, $totalDataCWCount, $dataCodeWords, $blocks); // Add Reed Solomon codewords

        $codeWordsBits = array(); // Calculte bits from codewords
        for ($i=0; $i<$totalCWCount; $i++){
            $codeWordsBits[$i] = self::getBits($dataCodeWords[$i]);
        }

        $datamatrix = array_fill(0, $colsLengthMatrice, array());
        $assigned = array_fill(0, $colsLengthMatrice, array());

        // Add the bottom-right corner if needed
        if ( (($rowsLengthMatrice * $colsLengthMatrice) % 8) == 4) {
            $datamatrix[$rowsLengthMatrice-2][$colsLengthMatrice-2] = 1;
            $datamatrix[$rowsLengthMatrice-1][$colsLengthMatrice-1] = 1;
            $datamatrix[$rowsLengthMatrice-1][$colsLengthMatrice-2] = 0;
            $datamatrix[$rowsLengthMatrice-2][$colsLengthMatrice-1] = 0;
            $assigned[$rowsLengthMatrice-2][$colsLengthMatrice-2] = 1;
            $assigned[$rowsLengthMatrice-1][$colsLengthMatrice-1] = 1;
            $assigned[$rowsLengthMatrice-1][$colsLengthMatrice-2] = 1;
            $assigned[$rowsLengthMatrice-2][$colsLengthMatrice-1] = 1;
        }

        // Put the codewords into the matrix
        self::next(0,$rowsLengthMatrice,$colsLengthMatrice, $codeWordsBits, $datamatrix, $assigned);

        // Add the finder pattern
        $datamatrix = self::addFinderPattern($datamatrix, $rowsRegion, $colsRegion, $rowsRegionCW, $colsRegionCW);

        return $datamatrix;
    }
}
?>