survey_seahorse

Software Engineering Project - Fall 2018
Log | Files | Refs | README

qrinput.php (24803B)


      1 <?php
      2 /*
      3  * PHP QR Code encoder
      4  *
      5  * Input encoding class
      6  *
      7  * Based on libqrencode C library distributed under LGPL 2.1
      8  * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net>
      9  *
     10  * PHP QR Code is distributed under LGPL 3
     11  * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
     12  *
     13  * This library is free software; you can redistribute it and/or
     14  * modify it under the terms of the GNU Lesser General Public
     15  * License as published by the Free Software Foundation; either
     16  * version 3 of the License, or any later version.
     17  *
     18  * This library is distributed in the hope that it will be useful,
     19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
     21  * Lesser General Public License for more details.
     22  *
     23  * You should have received a copy of the GNU Lesser General Public
     24  * License along with this library; if not, write to the Free Software
     25  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
     26  */
     27  
     28     define('STRUCTURE_HEADER_BITS',  20);
     29     define('MAX_STRUCTURED_SYMBOLS', 16);
     30 
     31     class QRinputItem {
     32     
     33         public $mode;
     34         public $size;
     35         public $data;
     36         public $bstream;
     37 
     38         public function __construct($mode, $size, $data, $bstream = null) 
     39         {
     40             $setData = array_slice($data, 0, $size);
     41             
     42             if (count($setData) < $size) {
     43                 $setData = array_merge($setData, array_fill(0,$size-count($setData),0));
     44             }
     45         
     46             if(!QRinput::check($mode, $size, $setData)) {
     47                 throw new Exception('Error m:'.$mode.',s:'.$size.',d:'.join(',',$setData));
     48                 return null;
     49             }
     50             
     51             $this->mode = $mode;
     52             $this->size = $size;
     53             $this->data = $setData;
     54             $this->bstream = $bstream;
     55         }
     56         
     57         //----------------------------------------------------------------------
     58         public function encodeModeNum($version)
     59         {
     60             try {
     61             
     62                 $words = (int)($this->size / 3);
     63                 $bs = new QRbitstream();
     64                 
     65                 $val = 0x1;
     66                 $bs->appendNum(4, $val);
     67                 $bs->appendNum(QRspec::lengthIndicator(QR_MODE_NUM, $version), $this->size);
     68 
     69                 for($i=0; $i<$words; $i++) {
     70                     $val  = (ord($this->data[$i*3  ]) - ord('0')) * 100;
     71                     $val += (ord($this->data[$i*3+1]) - ord('0')) * 10;
     72                     $val += (ord($this->data[$i*3+2]) - ord('0'));
     73                     $bs->appendNum(10, $val);
     74                 }
     75 
     76                 if($this->size - $words * 3 == 1) {
     77                     $val = ord($this->data[$words*3]) - ord('0');
     78                     $bs->appendNum(4, $val);
     79                 } else if($this->size - $words * 3 == 2) {
     80                     $val  = (ord($this->data[$words*3  ]) - ord('0')) * 10;
     81                     $val += (ord($this->data[$words*3+1]) - ord('0'));
     82                     $bs->appendNum(7, $val);
     83                 }
     84 
     85                 $this->bstream = $bs;
     86                 return 0;
     87                 
     88             } catch (Exception $e) {
     89                 return -1;
     90             }
     91         }
     92         
     93         //----------------------------------------------------------------------
     94         public function encodeModeAn($version)
     95         {
     96             try {
     97                 $words = (int)($this->size / 2);
     98                 $bs = new QRbitstream();
     99                 
    100                 $bs->appendNum(4, 0x02);
    101                 $bs->appendNum(QRspec::lengthIndicator(QR_MODE_AN, $version), $this->size);
    102 
    103                 for($i=0; $i<$words; $i++) {
    104                     $val  = (int)QRinput::lookAnTable(ord($this->data[$i*2  ])) * 45;
    105                     $val += (int)QRinput::lookAnTable(ord($this->data[$i*2+1]));
    106 
    107                     $bs->appendNum(11, $val);
    108                 }
    109 
    110                 if($this->size & 1) {
    111                     $val = QRinput::lookAnTable(ord($this->data[$words * 2]));
    112                     $bs->appendNum(6, $val);
    113                 }
    114         
    115                 $this->bstream = $bs;
    116                 return 0;
    117             
    118             } catch (Exception $e) {
    119                 return -1;
    120             }
    121         }
    122         
    123         //----------------------------------------------------------------------
    124         public function encodeMode8($version)
    125         {
    126             try {
    127                 $bs = new QRbitstream();
    128 
    129                 $bs->appendNum(4, 0x4);
    130                 $bs->appendNum(QRspec::lengthIndicator(QR_MODE_8, $version), $this->size);
    131 
    132                 for($i=0; $i<$this->size; $i++) {
    133                     $bs->appendNum(8, ord($this->data[$i]));
    134                 }
    135 
    136                 $this->bstream = $bs;
    137                 return 0;
    138             
    139             } catch (Exception $e) {
    140                 return -1;
    141             }
    142         }
    143         
    144         //----------------------------------------------------------------------
    145         public function encodeModeKanji($version)
    146         {
    147             try {
    148 
    149                 $bs = new QRbitrtream();
    150                 
    151                 $bs->appendNum(4, 0x8);
    152                 $bs->appendNum(QRspec::lengthIndicator(QR_MODE_KANJI, $version), (int)($this->size / 2));
    153 
    154                 for($i=0; $i<$this->size; $i+=2) {
    155                     $val = (ord($this->data[$i]) << 8) | ord($this->data[$i+1]);
    156                     if($val <= 0x9ffc) {
    157                         $val -= 0x8140;
    158                     } else {
    159                         $val -= 0xc140;
    160                     }
    161                     
    162                     $h = ($val >> 8) * 0xc0;
    163                     $val = ($val & 0xff) + $h;
    164 
    165                     $bs->appendNum(13, $val);
    166                 }
    167 
    168                 $this->bstream = $bs;
    169                 return 0;
    170             
    171             } catch (Exception $e) {
    172                 return -1;
    173             }
    174         }
    175 
    176         //----------------------------------------------------------------------
    177         public function encodeModeStructure()
    178         {
    179             try {
    180                 $bs =  new QRbitstream();
    181                 
    182                 $bs->appendNum(4, 0x03);
    183                 $bs->appendNum(4, ord($this->data[1]) - 1);
    184                 $bs->appendNum(4, ord($this->data[0]) - 1);
    185                 $bs->appendNum(8, ord($this->data[2]));
    186 
    187                 $this->bstream = $bs;
    188                 return 0;
    189             
    190             } catch (Exception $e) {
    191                 return -1;
    192             }
    193         }
    194         
    195         //----------------------------------------------------------------------
    196         public function estimateBitStreamSizeOfEntry($version)
    197         {
    198             $bits = 0;
    199 
    200             if($version == 0) 
    201                 $version = 1;
    202 
    203             switch($this->mode) {
    204                 case QR_MODE_NUM:        $bits = QRinput::estimateBitsModeNum($this->size);    break;
    205                 case QR_MODE_AN:        $bits = QRinput::estimateBitsModeAn($this->size);    break;
    206                 case QR_MODE_8:            $bits = QRinput::estimateBitsMode8($this->size);    break;
    207                 case QR_MODE_KANJI:        $bits = QRinput::estimateBitsModeKanji($this->size);break;
    208                 case QR_MODE_STRUCTURE:    return STRUCTURE_HEADER_BITS;            
    209                 default:
    210                     return 0;
    211             }
    212 
    213             $l = QRspec::lengthIndicator($this->mode, $version);
    214             $m = 1 << $l;
    215             $num = (int)(($this->size + $m - 1) / $m);
    216 
    217             $bits += $num * (4 + $l);
    218 
    219             return $bits;
    220         }
    221         
    222         //----------------------------------------------------------------------
    223         public function encodeBitStream($version)
    224         {
    225             try {
    226             
    227                 unset($this->bstream);
    228                 $words = QRspec::maximumWords($this->mode, $version);
    229                 
    230                 if($this->size > $words) {
    231                 
    232                     $st1 = new QRinputItem($this->mode, $words, $this->data);
    233                     $st2 = new QRinputItem($this->mode, $this->size - $words, array_slice($this->data, $words));
    234 
    235                     $st1->encodeBitStream($version);
    236                     $st2->encodeBitStream($version);
    237                     
    238                     $this->bstream = new QRbitstream();
    239                     $this->bstream->append($st1->bstream);
    240                     $this->bstream->append($st2->bstream);
    241                     
    242                     unset($st1);
    243                     unset($st2);
    244                     
    245                 } else {
    246                     
    247                     $ret = 0;
    248                     
    249                     switch($this->mode) {
    250                         case QR_MODE_NUM:        $ret = $this->encodeModeNum($version);    break;
    251                         case QR_MODE_AN:        $ret = $this->encodeModeAn($version);    break;
    252                         case QR_MODE_8:            $ret = $this->encodeMode8($version);    break;
    253                         case QR_MODE_KANJI:        $ret = $this->encodeModeKanji($version);break;
    254                         case QR_MODE_STRUCTURE:    $ret = $this->encodeModeStructure();    break;
    255                         
    256                         default:
    257                             break;
    258                     }
    259                     
    260                     if($ret < 0)
    261                         return -1;
    262                 }
    263 
    264                 return $this->bstream->size();
    265             
    266             } catch (Exception $e) {
    267                 return -1;
    268             }
    269         }
    270     };
    271     
    272     //##########################################################################
    273 
    274     class QRinput {
    275 
    276         public $items;
    277         
    278         private $version;
    279         private $level;
    280         
    281         //----------------------------------------------------------------------
    282         public function __construct($version = 0, $level = QR_ECLEVEL_L)
    283         {
    284             if ($version < 0 || $version > QRSPEC_VERSION_MAX || $level > QR_ECLEVEL_H) {
    285                 throw new Exception('Invalid version no');
    286                 return NULL;
    287             }
    288             
    289             $this->version = $version;
    290             $this->level = $level;
    291         }
    292         
    293         //----------------------------------------------------------------------
    294         public function getVersion()
    295         {
    296             return $this->version;
    297         }
    298         
    299         //----------------------------------------------------------------------
    300         public function setVersion($version)
    301         {
    302             if($version < 0 || $version > QRSPEC_VERSION_MAX) {
    303                 throw new Exception('Invalid version no');
    304                 return -1;
    305             }
    306 
    307             $this->version = $version;
    308 
    309             return 0;
    310         }
    311         
    312         //----------------------------------------------------------------------
    313         public function getErrorCorrectionLevel()
    314         {
    315             return $this->level;
    316         }
    317 
    318         //----------------------------------------------------------------------
    319         public function setErrorCorrectionLevel($level)
    320         {
    321             if($level > QR_ECLEVEL_H) {
    322                 throw new Exception('Invalid ECLEVEL');
    323                 return -1;
    324             }
    325 
    326             $this->level = $level;
    327 
    328             return 0;
    329         }
    330         
    331         //----------------------------------------------------------------------
    332         public function appendEntry(QRinputItem $entry)
    333         {
    334             $this->items[] = $entry;
    335         }
    336         
    337         //----------------------------------------------------------------------
    338         public function append($mode, $size, $data)
    339         {
    340             try {
    341                 $entry = new QRinputItem($mode, $size, $data);
    342                 $this->items[] = $entry;
    343                 return 0;
    344             } catch (Exception $e) {
    345                 return -1;
    346             }
    347         }
    348         
    349         //----------------------------------------------------------------------
    350         
    351         public function insertStructuredAppendHeader($size, $index, $parity)
    352         {
    353             if( $size > MAX_STRUCTURED_SYMBOLS ) {
    354                 throw new Exception('insertStructuredAppendHeader wrong size');
    355             }
    356             
    357             if( $index <= 0 || $index > MAX_STRUCTURED_SYMBOLS ) {
    358                 throw new Exception('insertStructuredAppendHeader wrong index');
    359             }
    360 
    361             $buf = array($size, $index, $parity);
    362             
    363             try {
    364                 $entry = new QRinputItem(QR_MODE_STRUCTURE, 3, buf);
    365                 array_unshift($this->items, $entry);
    366                 return 0;
    367             } catch (Exception $e) {
    368                 return -1;
    369             }
    370         }
    371 
    372         //----------------------------------------------------------------------
    373         public function calcParity()
    374         {
    375             $parity = 0;
    376             
    377             foreach($this->items as $item) {
    378                 if($item->mode != QR_MODE_STRUCTURE) {
    379                     for($i=$item->size-1; $i>=0; $i--) {
    380                         $parity ^= $item->data[$i];
    381                     }
    382                 }
    383             }
    384 
    385             return $parity;
    386         }
    387         
    388         //----------------------------------------------------------------------
    389         public static function checkModeNum($size, $data)
    390         {
    391             for($i=0; $i<$size; $i++) {
    392                 if((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))){
    393                     return false;
    394                 }
    395             }
    396 
    397             return true;
    398         }
    399 
    400         //----------------------------------------------------------------------
    401         public static function estimateBitsModeNum($size)
    402         {
    403             $w = (int)$size / 3;
    404             $bits = $w * 10;
    405             
    406             switch($size - $w * 3) {
    407                 case 1:
    408                     $bits += 4;
    409                     break;
    410                 case 2:
    411                     $bits += 7;
    412                     break;
    413                 default:
    414                     break;
    415             }
    416 
    417             return $bits;
    418         }
    419         
    420         //----------------------------------------------------------------------
    421         public static $anTable = array(
    422             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    423             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    424             36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,
    425              0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 44, -1, -1, -1, -1, -1,
    426             -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
    427             25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
    428             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    429             -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
    430         );
    431         
    432         //----------------------------------------------------------------------
    433         public static function lookAnTable($c)
    434         {
    435             return (($c > 127)?-1:self::$anTable[$c]);
    436         }
    437         
    438         //----------------------------------------------------------------------
    439         public static function checkModeAn($size, $data)
    440         {
    441             for($i=0; $i<$size; $i++) {
    442                 if (self::lookAnTable(ord($data[$i])) == -1) {
    443                     return false;
    444                 }
    445             }
    446 
    447             return true;
    448         }
    449         
    450         //----------------------------------------------------------------------
    451         public static function estimateBitsModeAn($size)
    452         {
    453             $w = (int)($size / 2);
    454             $bits = $w * 11;
    455             
    456             if($size & 1) {
    457                 $bits += 6;
    458             }
    459 
    460             return $bits;
    461         }
    462     
    463         //----------------------------------------------------------------------
    464         public static function estimateBitsMode8($size)
    465         {
    466             return $size * 8;
    467         }
    468         
    469         //----------------------------------------------------------------------
    470         public function estimateBitsModeKanji($size)
    471         {
    472             return (int)(($size / 2) * 13);
    473         }
    474         
    475         //----------------------------------------------------------------------
    476         public static function checkModeKanji($size, $data)
    477         {
    478             if($size & 1)
    479                 return false;
    480 
    481             for($i=0; $i<$size; $i+=2) {
    482                 $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
    483                 if( $val < 0x8140 
    484                 || ($val > 0x9ffc && $val < 0xe040) 
    485                 || $val > 0xebbf) {
    486                     return false;
    487                 }
    488             }
    489 
    490             return true;
    491         }
    492 
    493         /***********************************************************************
    494          * Validation
    495          **********************************************************************/
    496 
    497         public static function check($mode, $size, $data)
    498         {
    499             if($size <= 0) 
    500                 return false;
    501 
    502             switch($mode) {
    503                 case QR_MODE_NUM:       return self::checkModeNum($size, $data);   break;
    504                 case QR_MODE_AN:        return self::checkModeAn($size, $data);    break;
    505                 case QR_MODE_KANJI:     return self::checkModeKanji($size, $data); break;
    506                 case QR_MODE_8:         return true; break;
    507                 case QR_MODE_STRUCTURE: return true; break;
    508                 
    509                 default:
    510                     break;
    511             }
    512 
    513             return false;
    514         }
    515         
    516         
    517         //----------------------------------------------------------------------
    518         public function estimateBitStreamSize($version)
    519         {
    520             $bits = 0;
    521 
    522             foreach($this->items as $item) {
    523                 $bits += $item->estimateBitStreamSizeOfEntry($version);
    524             }
    525 
    526             return $bits;
    527         }
    528         
    529         //----------------------------------------------------------------------
    530         public function estimateVersion()
    531         {
    532             $version = 0;
    533             $prev = 0;
    534             do {
    535                 $prev = $version;
    536                 $bits = $this->estimateBitStreamSize($prev);
    537                 $version = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
    538                 if ($version < 0) {
    539                     return -1;
    540                 }
    541             } while ($version > $prev);
    542 
    543             return $version;
    544         }
    545         
    546         //----------------------------------------------------------------------
    547         public static function lengthOfCode($mode, $version, $bits)
    548         {
    549             $payload = $bits - 4 - QRspec::lengthIndicator($mode, $version);
    550             switch($mode) {
    551                 case QR_MODE_NUM:
    552                     $chunks = (int)($payload / 10);
    553                     $remain = $payload - $chunks * 10;
    554                     $size = $chunks * 3;
    555                     if($remain >= 7) {
    556                         $size += 2;
    557                     } else if($remain >= 4) {
    558                         $size += 1;
    559                     }
    560                     break;
    561                 case QR_MODE_AN:
    562                     $chunks = (int)($payload / 11);
    563                     $remain = $payload - $chunks * 11;
    564                     $size = $chunks * 2;
    565                     if($remain >= 6) 
    566                         $size++;
    567                     break;
    568                 case QR_MODE_8:
    569                     $size = (int)($payload / 8);
    570                     break;
    571                 case QR_MODE_KANJI:
    572                     $size = (int)(($payload / 13) * 2);
    573                     break;
    574                 case QR_MODE_STRUCTURE:
    575                     $size = (int)($payload / 8);
    576                     break;
    577                 default:
    578                     $size = 0;
    579                     break;
    580             }
    581             
    582             $maxsize = QRspec::maximumWords($mode, $version);
    583             if($size < 0) $size = 0;
    584             if($size > $maxsize) $size = $maxsize;
    585 
    586             return $size;
    587         }
    588         
    589         //----------------------------------------------------------------------
    590         public function createBitStream()
    591         {
    592             $total = 0;
    593 
    594             foreach($this->items as $item) {
    595                 $bits = $item->encodeBitStream($this->version);
    596                 
    597                 if($bits < 0) 
    598                     return -1;
    599                     
    600                 $total += $bits;
    601             }
    602 
    603             return $total;
    604         }
    605         
    606         //----------------------------------------------------------------------
    607         public function convertData()
    608         {
    609             $ver = $this->estimateVersion();
    610             if($ver > $this->getVersion()) {
    611                 $this->setVersion($ver);
    612             }
    613 
    614             for(;;) {
    615                 $bits = $this->createBitStream();
    616                 
    617                 if($bits < 0) 
    618                     return -1;
    619                     
    620                 $ver = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
    621                 if($ver < 0) {
    622                     throw new Exception('WRONG VERSION');
    623                     return -1;
    624                 } else if($ver > $this->getVersion()) {
    625                     $this->setVersion($ver);
    626                 } else {
    627                     break;
    628                 }
    629             }
    630 
    631             return 0;
    632         }
    633         
    634         //----------------------------------------------------------------------
    635         public function appendPaddingBit(&$bstream)
    636         {
    637             $bits = $bstream->size();
    638             $maxwords = QRspec::getDataLength($this->version, $this->level);
    639             $maxbits = $maxwords * 8;
    640 
    641             if ($maxbits == $bits) {
    642                 return 0;
    643             }
    644 
    645             if ($maxbits - $bits < 5) {
    646                 return $bstream->appendNum($maxbits - $bits, 0);
    647             }
    648 
    649             $bits += 4;
    650             $words = (int)(($bits + 7) / 8);
    651 
    652             $padding = new QRbitstream();
    653             $ret = $padding->appendNum($words * 8 - $bits + 4, 0);
    654             
    655             if($ret < 0) 
    656                 return $ret;
    657 
    658             $padlen = $maxwords - $words;
    659             
    660             if($padlen > 0) {
    661                 
    662                 $padbuf = array();
    663                 for($i=0; $i<$padlen; $i++) {
    664                     $padbuf[$i] = ($i&1)?0x11:0xec;
    665                 }
    666                 
    667                 $ret = $padding->appendBytes($padlen, $padbuf);
    668                 
    669                 if($ret < 0)
    670                     return $ret;
    671                 
    672             }
    673 
    674             $ret = $bstream->append($padding);
    675             
    676             return $ret;
    677         }
    678 
    679         //----------------------------------------------------------------------
    680         public function mergeBitStream()
    681         {
    682             if($this->convertData() < 0) {
    683                 return null;
    684             }
    685 
    686             $bstream = new QRbitstream();
    687             
    688             foreach($this->items as $item) {
    689                 $ret = $bstream->append($item->bstream);
    690                 if($ret < 0) {
    691                     return null;
    692                 }
    693             }
    694 
    695             return $bstream;
    696         }
    697 
    698         //----------------------------------------------------------------------
    699         public function getBitStream()
    700         {
    701 
    702             $bstream = $this->mergeBitStream();
    703             
    704             if($bstream == null) {
    705                 return null;
    706             }
    707             
    708             $ret = $this->appendPaddingBit($bstream);
    709             if($ret < 0) {
    710                 return null;
    711             }
    712 
    713             return $bstream;
    714         }
    715         
    716         //----------------------------------------------------------------------
    717         public function getByteStream()
    718         {
    719             $bstream = $this->getBitStream();
    720             if($bstream == null) {
    721                 return null;
    722             }
    723             
    724             return $bstream->toByte();
    725         }
    726     }
    727         
    728         
    729