| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729 | <?php/* * PHP QR Code encoder * * Input encoding class * * Based on libqrencode C library distributed under LGPL 2.1 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> * * PHP QR Code is distributed under LGPL 3 * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */     define('STRUCTURE_HEADER_BITS',  20);    define('MAX_STRUCTURED_SYMBOLS', 16);    class QRinputItem {            public $mode;        public $size;        public $data;        public $bstream;        public function __construct($mode, $size, $data, $bstream = null)         {            $setData = array_slice($data, 0, $size);                        if (count($setData) < $size) {                $setData = array_merge($setData, array_fill(0,$size-count($setData),0));            }                    if(!QRinput::check($mode, $size, $setData)) {                throw new Exception('Error m:'.$mode.',s:'.$size.',d:'.join(',',$setData));                return null;            }                        $this->mode = $mode;            $this->size = $size;            $this->data = $setData;            $this->bstream = $bstream;        }                //----------------------------------------------------------------------        public function encodeModeNum($version)        {            try {                            $words = (int)($this->size / 3);                $bs = new QRbitstream();                                $val = 0x1;                $bs->appendNum(4, $val);                $bs->appendNum(QRspec::lengthIndicator(QR_MODE_NUM, $version), $this->size);                for($i=0; $i<$words; $i++) {                    $val  = (ord($this->data[$i*3  ]) - ord('0')) * 100;                    $val += (ord($this->data[$i*3+1]) - ord('0')) * 10;                    $val += (ord($this->data[$i*3+2]) - ord('0'));                    $bs->appendNum(10, $val);                }                if($this->size - $words * 3 == 1) {                    $val = ord($this->data[$words*3]) - ord('0');                    $bs->appendNum(4, $val);                } else if($this->size - $words * 3 == 2) {                    $val  = (ord($this->data[$words*3  ]) - ord('0')) * 10;                    $val += (ord($this->data[$words*3+1]) - ord('0'));                    $bs->appendNum(7, $val);                }                $this->bstream = $bs;                return 0;                            } catch (Exception $e) {                return -1;            }        }                //----------------------------------------------------------------------        public function encodeModeAn($version)        {            try {                $words = (int)($this->size / 2);                $bs = new QRbitstream();                                $bs->appendNum(4, 0x02);                $bs->appendNum(QRspec::lengthIndicator(QR_MODE_AN, $version), $this->size);                for($i=0; $i<$words; $i++) {                    $val  = (int)QRinput::lookAnTable(ord($this->data[$i*2  ])) * 45;                    $val += (int)QRinput::lookAnTable(ord($this->data[$i*2+1]));                    $bs->appendNum(11, $val);                }                if($this->size & 1) {                    $val = QRinput::lookAnTable(ord($this->data[$words * 2]));                    $bs->appendNum(6, $val);                }                        $this->bstream = $bs;                return 0;                        } catch (Exception $e) {                return -1;            }        }                //----------------------------------------------------------------------        public function encodeMode8($version)        {            try {                $bs = new QRbitstream();                $bs->appendNum(4, 0x4);                $bs->appendNum(QRspec::lengthIndicator(QR_MODE_8, $version), $this->size);                for($i=0; $i<$this->size; $i++) {                    $bs->appendNum(8, ord($this->data[$i]));                }                $this->bstream = $bs;                return 0;                        } catch (Exception $e) {                return -1;            }        }                //----------------------------------------------------------------------        public function encodeModeKanji($version)        {            try {                $bs = new QRbitrtream();                                $bs->appendNum(4, 0x8);                $bs->appendNum(QRspec::lengthIndicator(QR_MODE_KANJI, $version), (int)($this->size / 2));                for($i=0; $i<$this->size; $i+=2) {                    $val = (ord($this->data[$i]) << 8) | ord($this->data[$i+1]);                    if($val <= 0x9ffc) {                        $val -= 0x8140;                    } else {                        $val -= 0xc140;                    }                                        $h = ($val >> 8) * 0xc0;                    $val = ($val & 0xff) + $h;                    $bs->appendNum(13, $val);                }                $this->bstream = $bs;                return 0;                        } catch (Exception $e) {                return -1;            }        }        //----------------------------------------------------------------------        public function encodeModeStructure()        {            try {                $bs =  new QRbitstream();                                $bs->appendNum(4, 0x03);                $bs->appendNum(4, ord($this->data[1]) - 1);                $bs->appendNum(4, ord($this->data[0]) - 1);                $bs->appendNum(8, ord($this->data[2]));                $this->bstream = $bs;                return 0;                        } catch (Exception $e) {                return -1;            }        }                //----------------------------------------------------------------------        public function estimateBitStreamSizeOfEntry($version)        {            $bits = 0;            if($version == 0)                 $version = 1;            switch($this->mode) {                case QR_MODE_NUM:        $bits = QRinput::estimateBitsModeNum($this->size);    break;                case QR_MODE_AN:        $bits = QRinput::estimateBitsModeAn($this->size);    break;                case QR_MODE_8:            $bits = QRinput::estimateBitsMode8($this->size);    break;                case QR_MODE_KANJI:        $bits = QRinput::estimateBitsModeKanji($this->size);break;                case QR_MODE_STRUCTURE:    return STRUCTURE_HEADER_BITS;                            default:                    return 0;            }            $l = QRspec::lengthIndicator($this->mode, $version);            $m = 1 << $l;            $num = (int)(($this->size + $m - 1) / $m);            $bits += $num * (4 + $l);            return $bits;        }                //----------------------------------------------------------------------        public function encodeBitStream($version)        {            try {                            unset($this->bstream);                $words = QRspec::maximumWords($this->mode, $version);                                if($this->size > $words) {                                    $st1 = new QRinputItem($this->mode, $words, $this->data);                    $st2 = new QRinputItem($this->mode, $this->size - $words, array_slice($this->data, $words));                    $st1->encodeBitStream($version);                    $st2->encodeBitStream($version);                                        $this->bstream = new QRbitstream();                    $this->bstream->append($st1->bstream);                    $this->bstream->append($st2->bstream);                                        unset($st1);                    unset($st2);                                    } else {                                        $ret = 0;                                        switch($this->mode) {                        case QR_MODE_NUM:        $ret = $this->encodeModeNum($version);    break;                        case QR_MODE_AN:        $ret = $this->encodeModeAn($version);    break;                        case QR_MODE_8:            $ret = $this->encodeMode8($version);    break;                        case QR_MODE_KANJI:        $ret = $this->encodeModeKanji($version);break;                        case QR_MODE_STRUCTURE:    $ret = $this->encodeModeStructure();    break;                                                default:                            break;                    }                                        if($ret < 0)                        return -1;                }                return $this->bstream->size();                        } catch (Exception $e) {                return -1;            }        }    };        //##########################################################################    class QRinput {        public $items;                private $version;        private $level;                //----------------------------------------------------------------------        public function __construct($version = 0, $level = QR_ECLEVEL_L)        {            if ($version < 0 || $version > QRSPEC_VERSION_MAX || $level > QR_ECLEVEL_H) {                throw new Exception('Invalid version no');                return NULL;            }                        $this->version = $version;            $this->level = $level;        }                //----------------------------------------------------------------------        public function getVersion()        {            return $this->version;        }                //----------------------------------------------------------------------        public function setVersion($version)        {            if($version < 0 || $version > QRSPEC_VERSION_MAX) {                throw new Exception('Invalid version no');                return -1;            }            $this->version = $version;            return 0;        }                //----------------------------------------------------------------------        public function getErrorCorrectionLevel()        {            return $this->level;        }        //----------------------------------------------------------------------        public function setErrorCorrectionLevel($level)        {            if($level > QR_ECLEVEL_H) {                throw new Exception('Invalid ECLEVEL');                return -1;            }            $this->level = $level;            return 0;        }                //----------------------------------------------------------------------        public function appendEntry(QRinputItem $entry)        {            $this->items[] = $entry;        }                //----------------------------------------------------------------------        public function append($mode, $size, $data)        {            try {                $entry = new QRinputItem($mode, $size, $data);                $this->items[] = $entry;                return 0;            } catch (Exception $e) {                return -1;            }        }                //----------------------------------------------------------------------                public function insertStructuredAppendHeader($size, $index, $parity)        {            if( $size > MAX_STRUCTURED_SYMBOLS ) {                throw new Exception('insertStructuredAppendHeader wrong size');            }                        if( $index <= 0 || $index > MAX_STRUCTURED_SYMBOLS ) {                throw new Exception('insertStructuredAppendHeader wrong index');            }            $buf = array($size, $index, $parity);                        try {                $entry = new QRinputItem(QR_MODE_STRUCTURE, 3, buf);                array_unshift($this->items, $entry);                return 0;            } catch (Exception $e) {                return -1;            }        }        //----------------------------------------------------------------------        public function calcParity()        {            $parity = 0;                        foreach($this->items as $item) {                if($item->mode != QR_MODE_STRUCTURE) {                    for($i=$item->size-1; $i>=0; $i--) {                        $parity ^= $item->data[$i];                    }                }            }            return $parity;        }                //----------------------------------------------------------------------        public static function checkModeNum($size, $data)        {            for($i=0; $i<$size; $i++) {                if((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))){                    return false;                }            }            return true;        }        //----------------------------------------------------------------------        public static function estimateBitsModeNum($size)        {            $w = (int)$size / 3;            $bits = $w * 10;                        switch($size - $w * 3) {                case 1:                    $bits += 4;                    break;                case 2:                    $bits += 7;                    break;                default:                    break;            }            return $bits;        }                //----------------------------------------------------------------------        public static $anTable = array(            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,            36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,             0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 44, -1, -1, -1, -1, -1,            -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,            25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1        );                //----------------------------------------------------------------------        public static function lookAnTable($c)        {            return (($c > 127)?-1:self::$anTable[$c]);        }                //----------------------------------------------------------------------        public static function checkModeAn($size, $data)        {            for($i=0; $i<$size; $i++) {                if (self::lookAnTable(ord($data[$i])) == -1) {                    return false;                }            }            return true;        }                //----------------------------------------------------------------------        public static function estimateBitsModeAn($size)        {            $w = (int)($size / 2);            $bits = $w * 11;                        if($size & 1) {                $bits += 6;            }            return $bits;        }            //----------------------------------------------------------------------        public static function estimateBitsMode8($size)        {            return $size * 8;        }                //----------------------------------------------------------------------        public function estimateBitsModeKanji($size)        {            return (int)(($size / 2) * 13);        }                //----------------------------------------------------------------------        public static function checkModeKanji($size, $data)        {            if($size & 1)                return false;            for($i=0; $i<$size; $i+=2) {                $val = (ord($data[$i]) << 8) | ord($data[$i+1]);                if( $val < 0x8140                 || ($val > 0x9ffc && $val < 0xe040)                 || $val > 0xebbf) {                    return false;                }            }            return true;        }        /***********************************************************************         * Validation         **********************************************************************/        public static function check($mode, $size, $data)        {            if($size <= 0)                 return false;            switch($mode) {                case QR_MODE_NUM:       return self::checkModeNum($size, $data);   break;                case QR_MODE_AN:        return self::checkModeAn($size, $data);    break;                case QR_MODE_KANJI:     return self::checkModeKanji($size, $data); break;                case QR_MODE_8:         return true; break;                case QR_MODE_STRUCTURE: return true; break;                                default:                    break;            }            return false;        }                        //----------------------------------------------------------------------        public function estimateBitStreamSize($version)        {            $bits = 0;            foreach($this->items as $item) {                $bits += $item->estimateBitStreamSizeOfEntry($version);            }            return $bits;        }                //----------------------------------------------------------------------        public function estimateVersion()        {            $version = 0;            $prev = 0;            do {                $prev = $version;                $bits = $this->estimateBitStreamSize($prev);                $version = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);                if ($version < 0) {                    return -1;                }            } while ($version > $prev);            return $version;        }                //----------------------------------------------------------------------        public static function lengthOfCode($mode, $version, $bits)        {            $payload = $bits - 4 - QRspec::lengthIndicator($mode, $version);            switch($mode) {                case QR_MODE_NUM:                    $chunks = (int)($payload / 10);                    $remain = $payload - $chunks * 10;                    $size = $chunks * 3;                    if($remain >= 7) {                        $size += 2;                    } else if($remain >= 4) {                        $size += 1;                    }                    break;                case QR_MODE_AN:                    $chunks = (int)($payload / 11);                    $remain = $payload - $chunks * 11;                    $size = $chunks * 2;                    if($remain >= 6)                         $size++;                    break;                case QR_MODE_8:                    $size = (int)($payload / 8);                    break;                case QR_MODE_KANJI:                    $size = (int)(($payload / 13) * 2);                    break;                case QR_MODE_STRUCTURE:                    $size = (int)($payload / 8);                    break;                default:                    $size = 0;                    break;            }                        $maxsize = QRspec::maximumWords($mode, $version);            if($size < 0) $size = 0;            if($size > $maxsize) $size = $maxsize;            return $size;        }                //----------------------------------------------------------------------        public function createBitStream()        {            $total = 0;            foreach($this->items as $item) {                $bits = $item->encodeBitStream($this->version);                                if($bits < 0)                     return -1;                                    $total += $bits;            }            return $total;        }                //----------------------------------------------------------------------        public function convertData()        {            $ver = $this->estimateVersion();            if($ver > $this->getVersion()) {                $this->setVersion($ver);            }            for(;;) {                $bits = $this->createBitStream();                                if($bits < 0)                     return -1;                                    $ver = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);                if($ver < 0) {                    throw new Exception('WRONG VERSION');                    return -1;                } else if($ver > $this->getVersion()) {                    $this->setVersion($ver);                } else {                    break;                }            }            return 0;        }                //----------------------------------------------------------------------        public function appendPaddingBit(&$bstream)        {            $bits = $bstream->size();            $maxwords = QRspec::getDataLength($this->version, $this->level);            $maxbits = $maxwords * 8;            if ($maxbits == $bits) {                return 0;            }            if ($maxbits - $bits < 5) {                return $bstream->appendNum($maxbits - $bits, 0);            }            $bits += 4;            $words = (int)(($bits + 7) / 8);            $padding = new QRbitstream();            $ret = $padding->appendNum($words * 8 - $bits + 4, 0);                        if($ret < 0)                 return $ret;            $padlen = $maxwords - $words;                        if($padlen > 0) {                                $padbuf = array();                for($i=0; $i<$padlen; $i++) {                    $padbuf[$i] = ($i&1)?0x11:0xec;                }                                $ret = $padding->appendBytes($padlen, $padbuf);                                if($ret < 0)                    return $ret;                            }            $ret = $bstream->append($padding);                        return $ret;        }        //----------------------------------------------------------------------        public function mergeBitStream()        {            if($this->convertData() < 0) {                return null;            }            $bstream = new QRbitstream();                        foreach($this->items as $item) {                $ret = $bstream->append($item->bstream);                if($ret < 0) {                    return null;                }            }            return $bstream;        }        //----------------------------------------------------------------------        public function getBitStream()        {            $bstream = $this->mergeBitStream();                        if($bstream == null) {                return null;            }                        $ret = $this->appendPaddingBit($bstream);            if($ret < 0) {                return null;            }            return $bstream;        }                //----------------------------------------------------------------------        public function getByteStream()        {            $bstream = $this->getBitStream();            if($bstream == null) {                return null;            }                        return $bstream->toByte();        }    }                    
 |