QRsplit.php 9.9 KB
<?php
namespace tinymeng\code\Gateways\qrcode;

/*
 * PHP QR Code encoder
 *
 * Input splitting classes
 *
 * 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>
 *
 * The following data / specifications are taken from
 * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
 *  or
 * "Automatic identification and data capture techniques -- 
 *  QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
 *
 * 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
 */
class QRsplit {

    public $dataStr = '';
    public $input;
    public $modeHint;

    //----------------------------------------------------------------------
    public function __construct($dataStr, $input, $modeHint) 
    {
        $this->dataStr  = $dataStr;
        $this->input    = $input;
        $this->modeHint = $modeHint;
    }
    
    //----------------------------------------------------------------------
    public static function isdigitat($str, $pos)
    {    
        if ($pos >= strlen($str))
            return false;
        
        return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9')));
    }
    
    //----------------------------------------------------------------------
    public static function isalnumat($str, $pos)
    {
        if ($pos >= strlen($str))
            return false;
            
        return (QRinput::lookAnTable(ord($str[$pos])) >= 0);
    }

    //----------------------------------------------------------------------
    public function identifyMode($pos)
    {
        if ($pos >= strlen($this->dataStr)) 
            return QR_MODE_NUL;
            
        $c = $this->dataStr[$pos];
        
        if(self::isdigitat($this->dataStr, $pos)) {
            return QR_MODE_NUM;
        } else if(self::isalnumat($this->dataStr, $pos)) {
            return QR_MODE_AN;
        } else if($this->modeHint == QR_MODE_KANJI) {
        
            if ($pos+1 < strlen($this->dataStr)) 
            {
                $d = $this->dataStr[$pos+1];
                $word = (ord($c) << 8) | ord($d);
                if(($word >= 0x8140 && $word <= 0x9ffc) || ($word >= 0xe040 && $word <= 0xebbf)) {
                    return QR_MODE_KANJI;
                }
            }
        }

        return QR_MODE_8;
    } 
    
    //----------------------------------------------------------------------
    public function eatNum()
    {
        $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());

        $p = 0;
        while(self::isdigitat($this->dataStr, $p)) {
            $p++;
        }
        
        $run = $p;
        $mode = $this->identifyMode($p);
        
        if($mode == QR_MODE_8) {
            $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
                 + QRinput::estimateBitsMode8(1)         // + 4 + l8
                 - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
            if($dif > 0) {
                return $this->eat8();
            }
        }
        if($mode == QR_MODE_AN) {
            $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
                 + QRinput::estimateBitsModeAn(1)        // + 4 + la
                 - QRinput::estimateBitsModeAn($run + 1);// - 4 - la
            if($dif > 0) {
                return $this->eatAn();
            }
        }
        
        $ret = $this->input->append(QR_MODE_NUM, $run, str_split($this->dataStr));
        if($ret < 0)
            return -1;

        return $run;
    }
    
    //----------------------------------------------------------------------
    public function eatAn()
    {
        $la = QRspec::lengthIndicator(QR_MODE_AN,  $this->input->getVersion());
        $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());

        $p = 0;
        
        while(self::isalnumat($this->dataStr, $p)) {
            if(self::isdigitat($this->dataStr, $p)) {
                $q = $p;
                while(self::isdigitat($this->dataStr, $q)) {
                    $q++;
                }
                
                $dif = QRinput::estimateBitsModeAn($p) // + 4 + la
                     + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
                     - QRinput::estimateBitsModeAn($q); // - 4 - la
                     
                if($dif < 0) {
                    break;
                } else {
                    $p = $q;
                }
            } else {
                $p++;
            }
        }

        $run = $p;

        if(!self::isalnumat($this->dataStr, $p)) {
            $dif = QRinput::estimateBitsModeAn($run) + 4 + $la
                 + QRinput::estimateBitsMode8(1) // + 4 + l8
                  - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
            if($dif > 0) {
                return $this->eat8();
            }
        }

        $ret = $this->input->append(QR_MODE_AN, $run, str_split($this->dataStr));
        if($ret < 0)
            return -1;

        return $run;
    }
    
    //----------------------------------------------------------------------
    public function eatKanji()
    {
        $p = 0;
        
        while($this->identifyMode($p) == QR_MODE_KANJI) {
            $p += 2;
        }
        
        $ret = $this->input->append(QR_MODE_KANJI, $p, str_split($this->dataStr));
        if($ret < 0)
            return -1;

        return $run;
    }

    //----------------------------------------------------------------------
    public function eat8()
    {
        $la = QRspec::lengthIndicator(QR_MODE_AN, $this->input->getVersion());
        $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());

        $p = 1;
        $dataStrLen = strlen($this->dataStr);
        
        while($p < $dataStrLen) {
            
            $mode = $this->identifyMode($p);
            if($mode == QR_MODE_KANJI) {
                break;
            }
            if($mode == QR_MODE_NUM) {
                $q = $p;
                while(self::isdigitat($this->dataStr, $q)) {
                    $q++;
                }
                $dif = QRinput::estimateBitsMode8($p) // + 4 + l8
                     + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
                     - QRinput::estimateBitsMode8($q); // - 4 - l8
                if($dif < 0) {
                    break;
                } else {
                    $p = $q;
                }
            } else if($mode == QR_MODE_AN) {
                $q = $p;
                while(self::isalnumat($this->dataStr, $q)) {
                    $q++;
                }
                $dif = QRinput::estimateBitsMode8($p)  // + 4 + l8
                     + QRinput::estimateBitsModeAn($q - $p) + 4 + $la
                     - QRinput::estimateBitsMode8($q); // - 4 - l8
                if($dif < 0) {
                    break;
                } else {
                    $p = $q;
                }
            } else {
                $p++;
            }
        }

        $run = $p;
        $ret = $this->input->append(QR_MODE_8, $run, str_split($this->dataStr));
        
        if($ret < 0)
            return -1;

        return $run;
    }

    //----------------------------------------------------------------------
    public function splitString()
    {
        while (strlen($this->dataStr) > 0)
        {
            if($this->dataStr == '')
                return 0;

            $mode = $this->identifyMode(0);
            
            switch ($mode) {
                case QR_MODE_NUM: $length = $this->eatNum(); break;
                case QR_MODE_AN:  $length = $this->eatAn(); break;
                case QR_MODE_KANJI:
                    if ($hint == QR_MODE_KANJI)
                            $length = $this->eatKanji();
                    else    $length = $this->eat8();
                    break;
                default: $length = $this->eat8(); break;
            
            }

            if($length == 0) return 0;
            if($length < 0)  return -1;
            
            $this->dataStr = substr($this->dataStr, $length);
        }
    }

    //----------------------------------------------------------------------
    public function toUpper()
    {
        $stringLen = strlen($this->dataStr);
        $p = 0;
        
        while ($p<$stringLen) {
            $mode = self::identifyMode(substr($this->dataStr, $p), $this->modeHint);
            if($mode == QR_MODE_KANJI) {
                $p += 2;
            } else {
                if (ord($this->dataStr[$p]) >= ord('a') && ord($this->dataStr[$p]) <= ord('z')) {
                    $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
                }
                $p++;
            }
        }

        return $this->dataStr;
    }

    //----------------------------------------------------------------------
    public static function splitStringToQRinput($string, QRinput $input, $modeHint, $casesensitive = true)
    {
        if(is_null($string) || $string == '\0' || $string == '') {
            throw new Exception('empty string!!!');
        }

        $split = new QRsplit($string, $input, $modeHint);
        
        if(!$casesensitive)
            $split->toUpper();
            
        return $split->splitString();
    }
}