diff --git a/examples/custom.php b/examples/custom.php new file mode 100644 index 000000000..4f552b5c1 --- /dev/null +++ b/examples/custom.php @@ -0,0 +1,24 @@ +addData('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net') + ->make(); + +for($row = 0; $row < $qrcode->moduleCount; $row++){ + for($col = 0; $col < $qrcode->moduleCount; $col++){ + echo $qrcode->isDark($row, $col) ? 'X' : ' '; + } + echo PHP_EOL; +} + +echo 'QRCode '.round((microtime(true)-$starttime), 5).PHP_EOL; diff --git a/examples/html.php b/examples/html.php new file mode 100644 index 000000000..196c87d1f --- /dev/null +++ b/examples/html.php @@ -0,0 +1,35 @@ + +.qrcode{ + border-style:none; + border-collapse:collapse; + margin:0; + padding:0; +} + +.dark, .light{ + margin:0; + padding:0; + width: 1.25mm; + height: 1.25mm; +} + +.dark{ + background-color: #000; +} + +.light{ + background-color: #fff; +} +'; + +// google authenticator +$qr = (new QRCode)->getMinimumQRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_M); +echo $qr->printHTML(); diff --git a/examples/image.php b/examples/image.php new file mode 100644 index 000000000..91a814c8b --- /dev/null +++ b/examples/image.php @@ -0,0 +1,15 @@ +getMinimumQRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_L); + +header('Content-type: image/png'); + +$im = $qr->createImage(5, 5); +imagepng($im); +imagedestroy($im); diff --git a/examples/sample_custom.php b/examples/sample_custom.php deleted file mode 100644 index 21e68420a..000000000 --- a/examples/sample_custom.php +++ /dev/null @@ -1,22 +0,0 @@ -getMinimumQRCode('イメージ作成(引数:サイズ,マージン', QRConst::ERROR_CORRECT_LEVEL_L); - - -header('Content-type: text/plain'); - -$m = $qr->getModuleCount(); - -for($row = 0; $row < $m; $row++){ - for($col = 0; $col < $m; $col++){ - echo $qr->isDark($row, $col) ? '#' : ' '; - } - echo PHP_EOL; -} - diff --git a/examples/sample_html.php b/examples/sample_html.php deleted file mode 100644 index d31a4414a..000000000 --- a/examples/sample_html.php +++ /dev/null @@ -1,42 +0,0 @@ -明示的に型番を指定'); - -// エラー訂正レベルを設定 -// QR_ERROR_CORRECT_LEVEL_L : 7% -// QR_ERROR_CORRECT_LEVEL_M : 15% -// QR_ERROR_CORRECT_LEVEL_Q : 25% -// QR_ERROR_CORRECT_LEVEL_H : 30% -$qrcode->setErrorCorrectLevel(QRConst::ERROR_CORRECT_LEVEL_L); - -// 型番(大きさ)を設定 -// 1~10 -$qrcode->setTypeNumber(3); - -// データ(文字列※)を設定 -// ※日本語はSJIS -$qrcode->addData('QRコード'); - -// QRコードを作成 -$qrcode->make(); - -// HTML出力 -echo $qrcode->printHTML(); - -//--------------------------------------------------------- -print('

型番自動

'); - -// 型番が最小となるQRコードを作成 -$qr = $qrcode->getMinimumQRCode('QRコード', QRConst::ERROR_CORRECT_LEVEL_L); - -// HTML出力 -echo $qr->printHTML(); \ No newline at end of file diff --git a/examples/sample_image.php b/examples/sample_image.php deleted file mode 100644 index 6f57ff69e..000000000 --- a/examples/sample_image.php +++ /dev/null @@ -1,18 +0,0 @@ -getMinimumQRCode('QRコード', QRConst::ERROR_CORRECT_LEVEL_L); - -// イメージ作成(引数:サイズ,マージン) -$im = $qr->createImage(2, 4); - -header('Content-type: image/png'); -imagepng($im); - -imagedestroy($im); - diff --git a/src/BitBuffer.php b/src/BitBuffer.php index cb53e2d76..90ee961e7 100644 --- a/src/BitBuffer.php +++ b/src/BitBuffer.php @@ -19,12 +19,12 @@ class BitBuffer{ /** * @var array */ - protected $buffer = []; + public $buffer = []; /** * @var int */ - protected $length = 0; + public $length = 0; /** * @@ -34,43 +34,19 @@ class BitBuffer{ $this->length = 0; } - /** - * @return array - */ - public function getBuffer(){ - return $this->buffer; - } - - /** - * @return int - */ - public function getLengthInBits(){ - return $this->length; - } - /** * @return string */ public function __toString(){ $buffer = ''; - $count = $this->getLengthInBits(); - for($i = 0; $i < $count; $i++){ - $buffer .= $this->get($i) ? '1' : '0'; + for($i = 0; $i < $this->length; $i++){ + $buffer .= (string)(int)(($this->buffer[(int)floor($i / 8)] >> (7 - $i % 8))&1) === 1; } return $buffer; } - /** - * @param $index - * - * @return bool - */ - public function get($index){ - return (($this->buffer[(int)floor($index / 8)] >> (7 - $index % 8))&1) === 1; - } - /** * @param $num * @param $length diff --git a/src/Data/AlphaNum.php b/src/Data/AlphaNum.php index 113744270..57b54f2da 100644 --- a/src/Data/AlphaNum.php +++ b/src/Data/AlphaNum.php @@ -26,33 +26,25 @@ class AlphaNum extends QRDataBase implements QRDataInterface{ /** * @var */ - protected $mode = QRConst::MODE_ALPHA_NUM; + public $mode = QRConst::MODE_ALPHANUM; /** * @param $buffer */ public function write(BitBuffer &$buffer){ - $data = $this->getData(); $i = 0; - $len = strlen($data); + $len = strlen($this->data); while($i + 1 < $len){ - $buffer->put($this->getCode(ord($data[$i])) * 45 + $this->getCode(ord($data[$i + 1])), 11); + $buffer->put($this->getCode(ord($this->data[$i])) * 45 + $this->getCode(ord($this->data[$i + 1])), 11); $i += 2; } if($i < $len){ - $buffer->put($this->getCode(ord($data[$i])), 6); + $buffer->put($this->getCode(ord($this->data[$i])), 6); } } - /** - * @return int - */ - public function getLength(){ - return strlen($this->getData()); - } - /** * @param $c * @@ -61,23 +53,23 @@ class AlphaNum extends QRDataBase implements QRDataInterface{ */ protected function getCode($c){ - if($this->util->toCharCode('0') <= $c && $c <= $this->util->toCharCode('9')){ - return $c - $this->util->toCharCode('0'); + if(ord('0') <= $c && $c <= ord('9')){ + return $c - ord('0'); } - else if($this->util->toCharCode('A') <= $c && $c <= $this->util->toCharCode('Z')){ - return $c - $this->util->toCharCode('A') + 10; + else if(ord('A') <= $c && $c <= ord('Z')){ + return $c - ord('A') + 10; } else{ switch($c){ - case $this->util->toCharCode(' '): return 36; - case $this->util->toCharCode('$'): return 37; - case $this->util->toCharCode('%'): return 38; - case $this->util->toCharCode('*'): return 39; - case $this->util->toCharCode('+'): return 40; - case $this->util->toCharCode('-'): return 41; - case $this->util->toCharCode('.'): return 42; - case $this->util->toCharCode('/'): return 43; - case $this->util->toCharCode(':'): return 44; + case ord(' '): return 36; + case ord('$'): return 37; + case ord('%'): return 38; + case ord('*'): return 39; + case ord('+'): return 40; + case ord('-'): return 41; + case ord('.'): return 42; + case ord('/'): return 43; + case ord(':'): return 44; default : throw new QRCodeException('illegal char: '.$c); } diff --git a/src/Data/EightBitByte.php b/src/Data/Byte.php similarity index 59% rename from src/Data/EightBitByte.php rename to src/Data/Byte.php index d8173c747..9d134d11a 100644 --- a/src/Data/EightBitByte.php +++ b/src/Data/Byte.php @@ -1,7 +1,7 @@ @@ -18,32 +18,23 @@ use codemasher\QRCode\Data\QRDataInterface; /** - * Class EightBitByte + * Class Byte */ -class EightBitByte extends QRDataBase implements QRDataInterface{ +class Byte extends QRDataBase implements QRDataInterface{ /** * @var */ - protected $mode = QRConst::MODE_8BIT_BYTE; + public $mode = QRConst::MODE_BYTE; /** * @param $buffer */ public function write(BitBuffer &$buffer){ - $data = $this->getData(); - - $len = strlen($data); + $len = strlen($this->data); for($i = 0; $i < $len; $i++){ - $buffer->put(ord($data[$i]), 8); + $buffer->put(ord($this->data[$i]), 8); } } - /** - * @return int - */ - public function getLength(){ - return strlen($this->getData()); - } - } \ No newline at end of file diff --git a/src/Data/Kanji.php b/src/Data/Kanji.php index d81968ad7..f921a78b4 100644 --- a/src/Data/Kanji.php +++ b/src/Data/Kanji.php @@ -25,7 +25,7 @@ class Kanji extends QRDataBase implements QRDataInterface{ /** * @var */ - protected $mode = QRConst::MODE_KANJI; + public $mode = QRConst::MODE_KANJI; /** * @param $buffer @@ -33,12 +33,11 @@ class Kanji extends QRDataBase implements QRDataInterface{ * @throws \codemasher\QRCode\QRCodeException */ public function write(BitBuffer &$buffer){ - $data = $this->getData(); $i = 0; - $len = strlen($data); + $len = strlen($this->data); while($i + 1 < $len){ - $c = ((0xff&ord($data[$i])) << 8)|(0xff&ord($data[$i + 1])); + $c = ((0xff&ord($this->data[$i])) << 8)|(0xff&ord($this->data[$i + 1])); if(0x8140 <= $c && $c <= 0x9FFC){ $c -= 0x8140; @@ -55,7 +54,7 @@ class Kanji extends QRDataBase implements QRDataInterface{ $i += 2; } - if($i < strlen($data)){ + if($i < $len){ throw new QRCodeException('illegal char at '.($i + 1)); } } @@ -64,6 +63,7 @@ class Kanji extends QRDataBase implements QRDataInterface{ * @return float */ public function getLength(){ - return floor(strlen($this->getData()) / 2); + return floor(strlen($this->data) / 2); } + } diff --git a/src/Data/Number.php b/src/Data/Number.php index 68349a4e2..401a5e9c0 100644 --- a/src/Data/Number.php +++ b/src/Data/Number.php @@ -23,25 +23,21 @@ use codemasher\QRCode\QRConst; class Number extends QRDataBase implements QRDataInterface{ /** - * @var + * @var int */ - protected $mode = QRConst::MODE_NUMBER; - - /** - * @var \codemasher\QRCode\Util - */ - protected $util; + public $mode = QRConst::MODE_NUMBER; /** * @param $buffer + * + * @return $this */ public function write(BitBuffer &$buffer){ - $data = $this->getData(); $i = 0; - $len = strlen($data); + $len = strlen($this->data); while($i + 2 < $len){ - $num = $this->parseInt(substr($data, $i, 3)); + $num = $this->parseInt(substr($this->data, $i, 3)); $buffer->put($num, 10); $i += 3; } @@ -49,50 +45,44 @@ class Number extends QRDataBase implements QRDataInterface{ if($i < $len){ if($len - $i === 1){ - $num = $this->parseInt(substr($data, $i, $i + 1)); + $num = $this->parseInt(substr($this->data, $i, $i + 1)); $buffer->put($num, 4); } else if($len - $i === 2){ - $num = $this->parseInt(substr($data, $i, $i + 2)); + $num = $this->parseInt(substr($this->data, $i, $i + 2)); $buffer->put($num, 7); } + } + + return $this; } /** - * @return int - */ - public function getLength(){ - return strlen($this->getData()); - } - - /** - * @param $s + * @param string $string * * @return int + * @throws \codemasher\QRCode\QRCodeException */ - protected function parseInt($s){ + protected function parseInt($string){ $num = 0; - for($i = 0; $i < strlen($s); $i++){ - $num = $num * 10 + $this->parseIntAt(ord($s[$i])); + $len = strlen($string); + for($i = 0; $i < $len; $i++){ + $c = ord($string[$i]); + + $ord0 = ord('0'); + if(ord('0') <= $c && $c <= ord('9')){ + $c = $c - $ord0; + } + else{ + throw new QRCodeException('illegal char: '.$c); + } + + $num = $num * 10 + $c; } return $num; } - /** - * @param $c - * - * @return mixed - * @throws \codemasher\QRCode\QRCodeException - */ - protected function parseIntAt($c){ - if($this->util->toCharCode('0') <= $c && $c <= $this->util->toCharCode('9')){ - return $c - $this->util->toCharCode('0'); - } - - throw new QRCodeException('illegal char: '.$c); - } - } diff --git a/src/Data/QRDataBase.php b/src/Data/QRDataBase.php index e0026e0aa..59c97a75a 100644 --- a/src/Data/QRDataBase.php +++ b/src/Data/QRDataBase.php @@ -15,22 +15,21 @@ use codemasher\QRCode\BitBuffer; use codemasher\QRCode\QRConst; use codemasher\QRCode\QRCodeException; use codemasher\QRCode\Util; -use codemasher\QRCode\Data\QRDataInterface; /** * Class QRDataBase */ -class QRDataBase implements QRDataInterface{ +class QRDataBase{ /** * @var */ - protected $mode; + public $mode; /** * @var */ - protected $data; + public $data; /** * @var \codemasher\QRCode\Util @@ -47,34 +46,11 @@ class QRDataBase implements QRDataInterface{ $this->util = new Util; } - /** - * @return mixed - */ - public function getMode(){ - return $this->mode; - } - - /** - * @return mixed - */ - public function getData(){ - return $this->data; - } - /** * @throws \codemasher\QRCode\QRCodeException */ public function getLength(){ - throw new QRCodeException('not implemented.'); - } - - /** - * @param $buffer - * - * @throws \codemasher\QRCode\QRCodeException - */ - public function write(BitBuffer &$buffer){ - throw new QRCodeException('not implemented.'); + return strlen($this->data); } /** @@ -88,10 +64,10 @@ class QRDataBase implements QRDataInterface{ // 1 - 9 switch($this->mode){ - case QRConst::MODE_NUMBER : return 10; - case QRConst::MODE_ALPHA_NUM: return 9; - case QRConst::MODE_8BIT_BYTE: return 8; - case QRConst::MODE_KANJI : return 8; + case QRConst::MODE_NUMBER : return 10; + case QRConst::MODE_ALPHANUM: return 9; + case QRConst::MODE_BYTE : return 8; + case QRConst::MODE_KANJI : return 8; default : throw new QRCodeException('mode: '.$this->mode); } @@ -101,10 +77,10 @@ class QRDataBase implements QRDataInterface{ // 10 - 26 switch($this->mode){ - case QRConst::MODE_NUMBER : return 12; - case QRConst::MODE_ALPHA_NUM: return 11; - case QRConst::MODE_8BIT_BYTE: return 16; - case QRConst::MODE_KANJI : return 10; + case QRConst::MODE_NUMBER : return 12; + case QRConst::MODE_ALPHANUM: return 11; + case QRConst::MODE_BYTE : return 16; + case QRConst::MODE_KANJI : return 10; default : throw new QRCodeException('mode: '.$this->mode); } @@ -114,10 +90,10 @@ class QRDataBase implements QRDataInterface{ // 27 - 40 switch($this->mode){ - case QRConst::MODE_NUMBER : return 14; - case QRConst::MODE_ALPHA_NUM: return 13; - case QRConst::MODE_8BIT_BYTE: return 16; - case QRConst::MODE_KANJI : return 12; + case QRConst::MODE_NUMBER : return 14; + case QRConst::MODE_ALPHANUM: return 13; + case QRConst::MODE_BYTE : return 16; + case QRConst::MODE_KANJI : return 12; default : throw new QRCodeException('mode: '.$this->mode); } diff --git a/src/Data/QRDataInterface.php b/src/Data/QRDataInterface.php index fe740bb03..c473ff00b 100644 --- a/src/Data/QRDataInterface.php +++ b/src/Data/QRDataInterface.php @@ -27,16 +27,6 @@ interface QRDataInterface{ */ public function getLength(); - /** - * @return mixed - */ - public function getData(); - - /** - * @return mixed - */ - public function getMode(); - /** * @param $type * @@ -44,4 +34,5 @@ interface QRDataInterface{ * @throws \codemasher\QRCode\QRCodeException */ public function getLengthInBits($type); + } \ No newline at end of file diff --git a/src/Math.php b/src/Math.php deleted file mode 100644 index 0dac5160a..000000000 --- a/src/Math.php +++ /dev/null @@ -1,104 +0,0 @@ - - * @copyright 2015 Smiley - * @license MIT - */ - -namespace codemasher\QRCode; - -use codemasher\QRCode\QRCodeException; - -/** - * Class Math - */ -class Math{ - - /** - * @var array - */ - protected $QR_MATH_EXP_TABLE; - - /** - * @var array - */ - protected $QR_MATH_LOG_TABLE; - - /** - * Math constructor. - */ - public function __construct(){ - $this->QR_MATH_EXP_TABLE = $this->createNumArray(256); - - for($i = 0; $i < 8; $i++){ - $this->QR_MATH_EXP_TABLE[$i] = 1 << $i; - } - - for($i = 8; $i < 256; $i++){ - $this->QR_MATH_EXP_TABLE[$i] = - $this->QR_MATH_EXP_TABLE[$i - 4] - ^$this->QR_MATH_EXP_TABLE[$i - 5] - ^$this->QR_MATH_EXP_TABLE[$i - 6] - ^$this->QR_MATH_EXP_TABLE[$i - 8]; - } - - $this->QR_MATH_LOG_TABLE = $this->createNumArray(256); - - for($i = 0; $i < 255; $i++){ - $this->QR_MATH_LOG_TABLE[$this->QR_MATH_EXP_TABLE[$i]] = $i; - } - } - - /** - * @param int $length - * - * @return array - */ - public function createNumArray($length){ - $num_array = []; - - for($i = 0; $i < $length; $i++){ - $num_array[] = 0; - } - - return $num_array; - } - - /** - * @param int $n - * - * @return mixed - * @throws \codemasher\QRCode\QRCodeException - */ - public function glog($n){ - - if($n < 1){ - throw new QRCodeException('log('.$n.')'); - } - - return $this->QR_MATH_LOG_TABLE[$n]; - } - - /** - * @param int $n - * - * @return mixed - */ - public function gexp($n){ - - while($n < 0){ - $n += 255; - } - - while($n >= 256){ - $n -= 255; - } - - return $this->QR_MATH_EXP_TABLE[$n]; - } - -} diff --git a/src/Output/QROutputInterface.php b/src/Output/QROutputInterface.php new file mode 100644 index 000000000..6c7bd3b45 --- /dev/null +++ b/src/Output/QROutputInterface.php @@ -0,0 +1,20 @@ + + * @copyright 2015 Smiley + * @license MIT + */ + +namespace codemasher\QRCode\Output; + +/** + * + */ +interface QROutputInterface{ + +} diff --git a/src/Polynomial.php b/src/Polynomial.php index 8ba93a3e2..c32440488 100644 --- a/src/Polynomial.php +++ b/src/Polynomial.php @@ -11,22 +11,25 @@ namespace codemasher\QRCode; -use codemasher\QRCode\Math; - /** * Class Polynomial */ class Polynomial{ /** - * @var + * @var array */ - protected $num; + public $num = []; /** - * @var \codemasher\QRCode\Math + * @var array */ - protected $math; + protected $EXP_TABLE = []; + + /** + * @var array + */ + protected $LOG_TABLE = []; /** * Polynomial constructor. @@ -34,56 +37,74 @@ class Polynomial{ * @param array $num * @param int $shift */ - public function __construct(array $num, $shift = 0){ - $this->math = new Math; + public function __construct(array $num = [1], $shift = 0){ + $this->setNum($num, $shift) + ->setTables(); + } + /** + * @param array $num + * @param int $shift + * + * @return $this + */ + public function setNum(array $num, $shift = 0){ $offset = 0; - while($offset < count($num) && $num[$offset] === 0){ + $numCount = count($num); + + while($offset < $numCount && $num[$offset] === 0){ $offset++; } - $this->num = $this->math->createNumArray(count($num) - $offset + $shift); - for($i = 0; $i < count($num) - $offset; $i++){ + $this->num = []; + for($i = 0; $i < $numCount - $offset + $shift; $i++){ + $this->num[] = 0; + } + + for($i = 0; $i < $numCount - $offset; $i++){ $this->num[$i] = $num[$i + $offset]; } + + return $this; } /** - * @param $index - * @todo rename! - * - * @return mixed + * @return $this */ - public function getNum($index){ - return $this->num[$index]; + protected function setTables(){ + $numArr = []; + for($i = 0; $i < 256; $i++){ + $numArr[] = 0; + } + + $this->EXP_TABLE = $this->LOG_TABLE = $numArr; + + for($i = 0; $i < 8; $i++){ + $this->EXP_TABLE[$i] = 1 << $i; + } + + for($i = 8; $i < 256; $i++){ + $this->EXP_TABLE[$i] = $this->EXP_TABLE[$i - 4] ^ $this->EXP_TABLE[$i - 5] ^ $this->EXP_TABLE[$i - 6] ^ $this->EXP_TABLE[$i - 8]; + } + + for($i = 0; $i < 255; $i++){ + $this->LOG_TABLE[$this->EXP_TABLE[$i]] = $i; + } + + return $this; } /** - * @return int - */ - public function getLength(){ - return count($this->num); - } - - /** - * PHP5 * @return string */ public function __toString(){ - return $this->toString(); - } - - /** - * @return string - */ - public function toString(){ $buffer = ''; - for($i = 0; $i < $this->getLength(); $i++){ + foreach($this->num as $i => $value){ if($i > 0){ $buffer .= ','; } - $buffer .= $this->getNum($i); + $buffer .= $value; } return $buffer; @@ -95,56 +116,101 @@ class Polynomial{ public function toLogString(){ $buffer = ''; - for($i = 0; $i < $this->getLength(); $i++){ + foreach($this->num as $i => $value){ if($i > 0){ $buffer .= ','; } - $buffer .= $this->math->glog($this->getNum($i)); + $buffer .= $this->glog($value); } return $buffer; } /** - * @param \codemasher\QRCode\Polynomial $e + * @param array $e * - * @return \codemasher\QRCode\Polynomial + * @return $this */ - public function multiply($e){ - $num = $this->math->createNumArray($this->getLength() + $e->getLength() - 1); + public function multiply(array $e){ - for($i = 0; $i < $this->getLength(); $i++){ - for($j = 0; $j < $e->getLength(); $j++){ - $num[$i + $j] ^= $this->math->gexp($this->math->glog($this->getNum($i)) + $this->math->glog($e->getNum($j))); + $num = []; + $len = count($this->num) + count($e) - 1; + for($i = 0; $i < $len; $i++){ + $num[] = 0; + } + + foreach($this->num as $i => $vi){ + foreach($e as $j => $vj){ + $num[$i + $j] ^= $this->gexp($this->glog($vi) + $this->glog($vj)); } } - return new Polynomial($num); + $this->setNum($num); + + return $this; } /** - * @param \codemasher\QRCode\Polynomial $e + * @param array $e * - * @return $this|\codemasher\QRCode\Polynomial + * @return $this */ public function mod($e){ +# $starttime = microtime(true); - if($this->getLength() - $e->getLength() < 0){ + if(count($this->num) - count($e) < 0){ return $this; } - $ratio = $this->math->glog($this->getNum(0)) - $this->math->glog($e->getNum(0)); + $ratio = $this->glog($this->num[0]) - $this->glog($e[0]); - $num = $this->math->createNumArray($this->getLength()); - for($i = 0; $i < $this->getLength(); $i++){ - $num[$i] = $this->getNum($i); + $num = []; + foreach($this->num as $i => $value){ + $num[$i] = $value; } - for($i = 0; $i < $e->getLength(); $i++){ - $num[$i] ^= $this->math->gexp($this->math->glog($e->getNum($i)) + $ratio); + foreach($e as $i => $value){ + $num[$i] ^= $this->gexp($this->glog($e[$i]) + $ratio); } - return (new Polynomial($num))->mod($e); + $this->setNum($num)->mod($e); + +# echo 'Polynomial::mod '.round((microtime(true)-$starttime), 5).PHP_EOL; + + return $num; + } + + /** + * @param int $n + * + * @return mixed + * @throws \codemasher\QRCode\QRCodeException + */ + public function glog($n){ + + if($n < 1){ + throw new QRCodeException('log('.$n.')'); + } + + return $this->LOG_TABLE[$n]; + } + + /** + * @param int $n + * + * @return mixed + */ + public function gexp($n){ + + while($n < 0){ + $n += 255; + } + + while($n >= 256){ + $n -= 255; + } + + return $this->EXP_TABLE[$n]; } } diff --git a/src/QRCode.php b/src/QRCode.php index 2c0b142d0..b7b507b89 100644 --- a/src/QRCode.php +++ b/src/QRCode.php @@ -1,5 +1,6 @@ errorCorrectLevel = $errorCorrectLevel; } - /** - * @return int - */ - public function getTypeNumber(){ - return $this->typeNumber; - } - - /** - * @param int $typeNumber - */ - public function setTypeNumber($typeNumber){ - $this->typeNumber = $typeNumber; - } - - /** - * @return int - */ - public function getErrorCorrectLevel(){ - return $this->errorCorrectLevel; - } - - /** - * @param int $errorCorrectLevel - */ - public function setErrorCorrectLevel($errorCorrectLevel){ - $this->errorCorrectLevel = $errorCorrectLevel; - } - /** * @param string $data * @param int $mode * + * @return $this * @throws \codemasher\QRCode\QRCodeException */ - public function addData($data, $mode = 0){ - if($mode === 0){ + public function addData($data, $mode = null){ + if($mode === null){ $mode = $this->util->getMode($data); } switch($mode){ - case QRConst::MODE_NUMBER : - $this->addDataImpl(new Number($data)); - break; - case QRConst::MODE_ALPHA_NUM: - $this->addDataImpl(new AlphaNum($data)); - break; - case QRConst::MODE_8BIT_BYTE: - $this->addDataImpl(new EightBitByte($data)); - break; - case QRConst::MODE_KANJI : - $this->addDataImpl(new Kanji($data)); - break; - default : + case QRConst::MODE_NUMBER : $this->qrDataList[] = new Number($data); break; + case QRConst::MODE_ALPHANUM:$this->qrDataList[] = new AlphaNum($data); break; + case QRConst::MODE_BYTE:$this->qrDataList[] = new Byte($data); break; + case QRConst::MODE_KANJI : $this->qrDataList[] = new Kanji($data); break; + default: throw new QRCodeException('mode: '.$mode); } - } - - /** - * - */ - public function clearData(){ - $this->qrDataList = []; - } - - /** - * @param \codemasher\QRCode\Data\QRDataInterface $qrData - * - * @return $this - */ - public function addDataImpl(QRDataInterface &$qrData){ - $this->qrDataList[] = $qrData; return $this; } - /** - * @return int - */ - public function getDataCount(){ - return count($this->qrDataList); - } - - /** - * @param int $index - * - * @return \codemasher\QRCode\Data\QRDataInterface - */ - public function getData($index){ - return $this->qrDataList[$index]; - } - /** * @param int $row * @param int $col * - * @return mixed + * @return bool */ public function isDark($row, $col){ if($this->modules[$row][$col] !== null){ - return $this->modules[$row][$col]; + return (bool)$this->modules[$row][$col]; } else{ return false; } } - /** - * @return mixed - */ - public function getModuleCount(){ - return $this->moduleCount; - } - - /** - * used for converting fg/bg colors (e.g. #0000ff = 0x0000FF) - added 2015.07.27 ~ DoktorJ - * - * @param int $hex - * - * @return array - */ - public function hex2rgb($hex = 0x0){ - return [ - 'r' => floor($hex / 65536), - 'g' => floor($hex / 256) % 256, - 'b' => $hex % 256, - ]; - } - /** * */ @@ -226,15 +144,123 @@ class QRCode{ /** * @return int */ - public function getBestMaskPattern(){ + protected function getBestMaskPattern(){ $minLostPoint = 0; $pattern = 0; for($i = 0; $i < 8; $i++){ $this->makeImpl(true, $i); - $lostPoint = $this->util->getLostPoint($this); + $lostPoint = 0; - if($i == 0 || $minLostPoint > $lostPoint){ + // LEVEL1 + + for($row = 0; $row < $this->moduleCount; $row++){ + for($col = 0; $col < $this->moduleCount; $col++){ + $sameCount = 0; + $dark = $this->isDark($row, $col); + + for($r = -1; $r <= 1; $r++){ + if($row + $r < 0 || $this->moduleCount <= $row + $r){ + continue; + } + + for($c = -1; $c <= 1; $c++){ + + if($col + $c < 0 || $this->moduleCount <= $col + $c){ + continue; + } + + if($r == 0 && $c == 0){ + continue; + } + + if($dark === $this->isDark($row + $r, $col + $c)){ + $sameCount++; + } + } + } + + if($sameCount > 5){ + $lostPoint += (3 + $sameCount - 5); + } + } + } + + // LEVEL2 + + for($row = 0; $row < $this->moduleCount - 1; $row++){ + for($col = 0; $col < $this->moduleCount - 1; $col++){ + $count = 0; + + if($this->isDark($row, $col)){ + $count++; + } + + if($this->isDark($row + 1, $col)){ + $count++; + } + + if($this->isDark($row, $col + 1)){ + $count++; + } + + if($this->isDark($row + 1, $col + 1)){ + $count++; + } + + if($count === 0 || $count === 4){ + $lostPoint += 3; + } + } + } + + // LEVEL3 + + for($row = 0; $row < $this->moduleCount; $row++){ + for($col = 0; $col < $this->moduleCount - 6; $col++){ + if($this->isDark($row, $col) + && !$this->isDark($row, $col + 1) + && $this->isDark($row, $col + 2) + && $this->isDark($row, $col + 3) + && $this->isDark($row, $col + 4) + && !$this->isDark($row, $col + 5) + && $this->isDark($row, $col + 6) + ){ + $lostPoint += 40; + } + } + } + + for($col = 0; $col < $this->moduleCount; $col++){ + for($row = 0; $row < $this->moduleCount - 6; $row++){ + if($this->isDark($row, $col) + && !$this->isDark($row + 1, $col) + && $this->isDark($row + 2, $col) + && $this->isDark($row + 3, $col) + && $this->isDark($row + 4, $col) + && !$this->isDark($row + 5, $col) + && $this->isDark($row + 6, $col) + ){ + $lostPoint += 40; + } + } + } + + // LEVEL4 + + $darkCount = 0; + for($col = 0; $col < $this->moduleCount; $col++){ + for($row = 0; $row < $this->moduleCount; $row++){ + if($this->isDark($row, $col)){ + $darkCount++; + } + } + } + + $ratio = abs(100 * $darkCount / $this->moduleCount / $this->moduleCount - 50) / 5; + $lostPoint += $ratio * 10; + + if($i === 0 || $minLostPoint > $lostPoint){ $minLostPoint = $lostPoint; $pattern = $i; } @@ -244,63 +270,117 @@ class QRCode{ } /** - * @param int $length - * - * @return array - */ - public function createNullArray($length){ - $nullArray = []; - for($i = 0; $i < $length; $i++){ - $nullArray[] = null; - } - - return $nullArray; - } - - /** - * @param $test - * @param $maskPattern + * @param bool $test + * @param int $maskPattern * * @return $this */ - public function makeImpl($test, $maskPattern){ + protected function makeImpl($test, $maskPattern){ $this->moduleCount = $this->typeNumber * 4 + 17; $this->modules = []; + $nullArray = []; for($i = 0; $i < $this->moduleCount; $i++){ - $this->modules[] = $this->createNullArray($this->moduleCount); + $nullArray[] = null; + } + + for($i = 0; $i < $this->moduleCount; $i++){ + $this->modules[] = $nullArray; } $this->setupPositionProbePattern(0, 0) ->setupPositionProbePattern($this->moduleCount - 7, 0) - ->setupPositionProbePattern(0, $this->moduleCount - 7) - ->setupPositionAdjustPattern() - ->setupTimingPattern() - ->setupTypeInfo($test, $maskPattern); + ->setupPositionProbePattern(0, $this->moduleCount - 7); - if($this->typeNumber >= 7){ - $this->setupTypeNumber($test); + + $pos = $this->util->PATTERN_POSITION[$this->typeNumber - 1]; + $posCount = count($pos); + + for($i = 0; $i < $posCount; $i++){ + for($j = 0; $j < $posCount; $j++){ + if($this->modules[$pos[$i]][$pos[$j]] !== null){ + continue; + } + + for($r = -2; $r <= 2; $r++){ + for($c = -2; $c <= 2; $c++){ + $this->modules[$pos[$i] + $r][$pos[$j] + $c] = + $r === -2 || $r === 2 || $c === -2 || $c === 2 || ($r === 0 && $c === 0); + } + } + + } } - $data = $this->createData($this->typeNumber, $this->errorCorrectLevel); + for($r = 8; $r < $this->moduleCount - 8; $r++){ + if($this->modules[$r][6] !== null){ + continue; + } - $this->mapData($data, $maskPattern); + $this->modules[$r][6] = $this->modules[6][$r] = $r % 2 === 0; + } + + $data = ($this->errorCorrectLevel << 3) | $maskPattern; + $bits = $this->util->getBCHTypeInfo($data); + + for($i = 0; $i < 15; $i++){ + $mod = !$test && (($bits >> $i) & 1) === 1; + + if($i < 6){ + $this->modules[$i][8] = $mod; + } + else if($i < 8){ + $this->modules[$i + 1][8] = $mod; + } + else{ + $this->modules[$this->moduleCount - 15 + $i][8] = $mod; + } + + if($i < 8){ + $this->modules[8][$this->moduleCount - $i - 1] = $mod; + } + else if($i < 9){ + $this->modules[8][15 - $i - 1 + 1] = $mod; + } + else{ + $this->modules[8][15 - $i - 1] = $mod; + } + + } + + $this->modules[$this->moduleCount - 8][8] = !$test; + + if($this->typeNumber >= 7){ + $bits = $this->util->getBCHTypeNumber($this->typeNumber); + + for($i = 0; $i < 18; $i++){ + $a = (int)floor($i / 3); + $b = $i % 3 + $this->moduleCount - 8 - 3; + $mod = !$test && (($bits >> $i) & 1) === 1; + + $this->modules[$a][$b] = $this->modules[$b][$a] = $mod; + } + } + + $this->data = $this->createData($this->typeNumber, $this->errorCorrectLevel); + + $this->mapData($maskPattern); return $this; } /** - * @param $data * @param $maskPattern * * @return $this * @throws \codemasher\QRCode\QRCodeException */ - public function mapData(&$data, $maskPattern){ + protected function mapData($maskPattern){ $inc = -1; $row = $this->moduleCount - 1; $bitIndex = 7; $byteIndex = 0; + $dataCount = count($this->data); for($col = $this->moduleCount - 1; $col > 0; $col -= 2){ @@ -309,18 +389,28 @@ class QRCode{ } while(true){ - for($c = 0; $c < 2; $c++){ if($this->modules[$row][$col - $c] === null){ - $dark = false; - if($byteIndex < count($data)){ - $dark = ((($data[$byteIndex] >> $bitIndex) & 1) == 1); + if($byteIndex < $dataCount){ + $dark = (($this->data[$byteIndex] >> $bitIndex) & 1) === 1; } - $mask = $this->util->getMask($maskPattern, $row, $col - $c); + $_col = $col - $c; + switch($maskPattern){ + case QRConst::MASK_PATTERN000: $mask = ($row + $_col) % 2 === 0; break; + case QRConst::MASK_PATTERN001: $mask = $row % 2 === 0; break; + case QRConst::MASK_PATTERN010: $mask = $_col % 3 === 0; break; + case QRConst::MASK_PATTERN011: $mask = ($row + $_col) % 3 === 0; break; + case QRConst::MASK_PATTERN100: $mask = (floor($row / 2) + floor($_col / 3)) % 2 === 0; break; + case QRConst::MASK_PATTERN101: $mask = ($row * $_col) % 2 + ($row * $_col) % 3 === 0; break; + case QRConst::MASK_PATTERN110: $mask = (($row * $_col) % 2 + ($row * $_col) % 3) % 2 === 0; break; + case QRConst::MASK_PATTERN111: $mask = (($row * $_col) % 3 + ($row + $_col) % 2) % 2 === 0; break; + default : + throw new QRCodeException('mask: '.$maskPattern); + } if($mask){ $dark = !$dark; @@ -329,10 +419,11 @@ class QRCode{ $this->modules[$row][$col - $c] = $dark; $bitIndex--; - if($bitIndex == -1){ + if($bitIndex === -1){ $byteIndex++; $bitIndex = 7; } + } } @@ -349,171 +440,41 @@ class QRCode{ return $this; } - /** - * @return $this - */ - public function setupPositionAdjustPattern(){ - $pos = $this->util->getPatternPosition($this->typeNumber); - - $posCount = count($pos); - for($i = 0; $i < $posCount; $i++){ - for($j = 0; $j < $posCount; $j++){ - - $row = $pos[$i]; - $col = $pos[$j]; - - if($this->modules[$row][$col] !== null){ - continue; - } - - for($r = -2; $r <= 2; $r++){ - for($c = -2; $c <= 2; $c++){ - - if($r == -2 || $r == 2 || $c == -2 || $c == 2 - || ($r == 0 && $c == 0) - ){ - $this->modules[$row + $r][$col + $c] = true; - } - else{ - $this->modules[$row + $r][$col + $c] = false; - } - } - } - } - } - - return $this; - } - /** * @param $row * @param $col * * @return $this */ - public function setupPositionProbePattern($row, $col){ + protected function setupPositionProbePattern($row, $col){ for($r = -1; $r <= 7; $r++){ for($c = -1; $c <= 7; $c++){ - if($row + $r <= -1 || $this->moduleCount <= $row + $r - || $col + $c <= -1 || $this->moduleCount <= $col + $c - ){ + if($row + $r <= -1 || $this->moduleCount <= $row + $r || $col + $c <= -1 || $this->moduleCount <= $col + $c){ continue; } - if((0 <= $r && $r <= 6 && ($c == 0 || $c == 6)) - || (0 <= $c && $c <= 6 && ($r == 0 || $r == 6)) - || (2 <= $r && $r <= 4 && 2 <= $c && $c <= 4) - ){ - $this->modules[$row + $r][$col + $c] = true; - } - else{ - $this->modules[$row + $r][$col + $c] = false; - } + $this->modules[$row + $r][$col + $c] = + (0 <= $r && $r <= 6 && ($c === 0 || $c === 6)) + || (0 <= $c && $c <= 6 && ($r == 0 || $r === 6)) + || (2 <= $r && $r <= 4 && 2 <= $c && $c <= 4); } } return $this; } - /** - * @return $this - */ - public function setupTimingPattern(){ - - for($r = 8; $r < $this->moduleCount - 8; $r++){ - if($this->modules[$r][6] !== null){ - continue; - } - $this->modules[$r][6] = ($r % 2 == 0); - } - - for($c = 8; $c < $this->moduleCount - 8; $c++){ - if($this->modules[6][$c] !== null){ - continue; - } - $this->modules[6][$c] = ($c % 2 == 0); - } - - return $this; - } - - /** - * @param $test - * - * @return $this - */ - public function setupTypeNumber($test){ - $bits = $this->util->getBCHTypeNumber($this->typeNumber); - - for($i = 0; $i < 18; $i++){ - $this->modules[(int)floor($i / 3)][$i % 3 + $this->moduleCount - 8 - 3] = !$test && (($bits >> $i) & 1) === 1; - } - - for($i = 0; $i < 18; $i++){ - $this->modules[$i % 3 + $this->moduleCount - 8 - 3][(int)floor($i / 3)] = !$test && (($bits >> $i) & 1) === 1; - } - - return $this; - } - - /** - * @param $test - * @param $maskPattern - * - * @return $this - */ - public function setupTypeInfo($test, $maskPattern){ - - $data = ($this->errorCorrectLevel << 3) | $maskPattern; - $bits = $this->util->getBCHTypeInfo($data); - - for($i = 0; $i < 15; $i++){ - - $mod = !$test && (($bits >> $i) & 1) === 1; - - if($i < 6){ - $this->modules[$i][8] = $mod; - } - else if($i < 8){ - $this->modules[$i + 1][8] = $mod; - } - else{ - $this->modules[$this->moduleCount - 15 + $i][8] = $mod; - } - } - - for($i = 0; $i < 15; $i++){ - - $mod = !$test && (($bits >> $i) & 1) === 1; - - if($i < 8){ - $this->modules[8][$this->moduleCount - $i - 1] = $mod; - } - else if($i < 9){ - $this->modules[8][15 - $i - 1 + 1] = $mod; - } - else{ - $this->modules[8][15 - $i - 1] = $mod; - } - } - - $this->modules[$this->moduleCount - 8][8] = !$test; - - return $this; - } - /** * @param $typeNumber * @param $errorCorrectLevel * * @return array * @throws \codemasher\QRCode\QRCodeException - * @todo: slooooow + * @todo: slooooow in PHP5 * */ - public function createData($typeNumber, $errorCorrectLevel){ + protected function createData($typeNumber, $errorCorrectLevel){ $this->bitBuffer->reset(); $count = count($this->qrDataList); @@ -521,7 +482,7 @@ class QRCode{ /** @var \codemasher\QRCode\Data\QRDataInterface $data */ $data = $this->qrDataList[$i]; - $this->bitBuffer->put($data->getMode(), 4); + $this->bitBuffer->put($data->mode, 4); $this->bitBuffer->put($data->getLength(), $data->getLengthInBits($typeNumber)); $data->write($this->bitBuffer); } @@ -531,88 +492,122 @@ class QRCode{ $count = count($this->rsBlockList); for($i = 0; $i < $count; $i++){ - $this->rsBlock->setCount($this->rsBlockList[$i][0], $this->rsBlockList[$i][1]); + $this->rsBlock->totalCount = $this->rsBlockList[$i][0]; + $this->rsBlock->dataCount = $this->rsBlockList[$i][1]; - $totalDataCount += $this->rsBlock->getDataCount(); + $totalDataCount += $this->rsBlock->dataCount; } - if($this->bitBuffer->getLengthInBits() > $totalDataCount * 8){ - throw new QRCodeException('code length overflow. ('.$this->bitBuffer->getLengthInBits().'>'.($totalDataCount * 8).')'); + if($this->bitBuffer->length > $totalDataCount * 8){ + throw new QRCodeException('code length overflow. ('.$this->bitBuffer->length.'>'.($totalDataCount * 8).')'); } // end code. - if($this->bitBuffer->getLengthInBits() + 4 <= $totalDataCount * 8){ + if($this->bitBuffer->length + 4 <= $totalDataCount * 8){ $this->bitBuffer->put(0, 4); } // padding - while($this->bitBuffer->getLengthInBits() % 8 != 0){ + while($this->bitBuffer->length % 8 != 0){ $this->bitBuffer->putBit(false); } // padding while(true){ - if($this->bitBuffer->getLengthInBits() >= $totalDataCount * 8){ + if($this->bitBuffer->length >= $totalDataCount * 8){ break; } $this->bitBuffer->put(self::QR_PAD0, 8); - if($this->bitBuffer->getLengthInBits() >= $totalDataCount * 8){ + if($this->bitBuffer->length >= $totalDataCount * 8){ break; } $this->bitBuffer->put(self::QR_PAD1, 8); } - return $this->createBytes(); - } - /** - * @return array - */ - public function createBytes(){ $offset = $maxDcCount = $maxEcCount = 0; - $dcdata = $ecdata = $this->createNullArray(count($this->rsBlockList)); $rsBlockCount = count($this->rsBlockList); - for($r = 0; $r < $rsBlockCount; $r++){ - $this->rsBlock->setCount($this->rsBlockList[$r][0], $this->rsBlockList[$r][1]); + $nullArray = []; + for($i = 0; $i < $rsBlockCount; $i++){ + $nullArray[] = null; + } - $dcCount = $this->rsBlock->getDataCount(); - $ecCount = $this->rsBlock->getTotalCount() - $dcCount; + $dcdata = $ecdata = $nullArray; + $totalCodeCount = 0; + + for($r = 0; $r < $rsBlockCount; $r++){ + $this->rsBlock->totalCount = $this->rsBlockList[$r][0]; + $this->rsBlock->dataCount = $this->rsBlockList[$r][1]; + + + $dcCount = $this->rsBlock->dataCount; + $ecCount = $this->rsBlock->totalCount - $dcCount; $maxDcCount = max($maxDcCount, $dcCount); $maxEcCount = max($maxEcCount, $ecCount); - $dcdata[$r] = $this->createNullArray($dcCount); + $_nullArray = []; + for($i = 0; $i < $dcCount; $i++){ + $_nullArray[] = null; + } + + $dcdata[$r] = $_nullArray; $dcdataCount = count($dcdata[$r]); for($i = 0; $i < $dcdataCount; $i++){ - $bdata = $this->bitBuffer->getBuffer(); + $bdata = $this->bitBuffer->buffer; $dcdata[$r][$i] = 0xff & $bdata[$i + $offset]; } $offset += $dcCount; - $rsPoly = $this->util->getErrorCorrectPolynomial($ecCount); - $rawPoly = new Polynomial($dcdata[$r], $rsPoly->getLength() - 1); - $modPoly = $rawPoly->mod($rsPoly); - $ecdata[$r] = $this->createNullArray($rsPoly->getLength() - 1); - $ecdataCount = count($ecdata[$r]); - for($i = 0; $i < $ecdataCount; $i++){ - $modIndex = $i + $modPoly->getLength() - count($ecdata[$r]); - $ecdata[$r][$i] = ($modIndex >= 0) ? $modPoly->getNum($modIndex) : 0; + $rsPoly = new Polynomial; + $modPoly = new Polynomial; + +$starttime = microtime(true); + // 0.09s + for($i = 0; $i < $ecCount; $i++){ + $modPoly->setNum([1, $modPoly->gexp($i)]); + $rsPoly->multiply($modPoly->num); } + + $rsPolyCount = count($rsPoly->num); + + // 0.11s + $modPoly->setNum($dcdata[$r], $rsPolyCount - 1)->mod($rsPoly->num); + +echo 'QRCode::createData '.round((microtime(true)-$starttime), 5).PHP_EOL; + + $modPolyCount = count($modPoly->num); + + $_nullArray = []; + for($i = 0; $i < $rsPolyCount - 1; $i++){ + $_nullArray[] = null; + } + + $ecdata[$r] = $_nullArray; + $ecdataCount = count($ecdata[$r]); + + for($i = 0; $i < $ecdataCount; $i++){ + $modIndex = $i + $modPolyCount - $ecdataCount; + $ecdata[$r][$i] = ($modIndex >= 0) ? $modPoly->num[$modIndex] : 0; + } + + $this->rsBlock->totalCount = $this->rsBlockList[$r][0]; + $this->rsBlock->dataCount = $this->rsBlockList[$r][1]; + + $totalCodeCount += $this->rsBlock->totalCount; } - $totalCodeCount = 0; - for($i = 0; $i < $rsBlockCount; $i++){ - $this->rsBlock->setCount($this->rsBlockList[$i][0], $this->rsBlockList[$i][1]); - - $totalCodeCount += $this->rsBlock->getTotalCount(); + $nullArray = []; + for($i = 0; $i < $totalCodeCount; $i++){ + $nullArray[] = null; } - $data = $this->createNullArray($totalCodeCount); + $data = $nullArray; $index = 0; for($i = 0; $i < $maxDcCount; $i++){ for($r = 0; $r < $rsBlockCount; $r++){ @@ -640,27 +635,26 @@ class QRCode{ * @return \codemasher\QRCode\QRCode * @throws \codemasher\QRCode\QRCodeException */ - public function getMinimumQRCode($data, $errorCorrectLevel){ - + public function getMinimumQRCode($data, $errorCorrectLevel = QRConst::ERROR_CORRECT_LEVEL_H){ $mode = $this->util->getMode($data); + $this->addData($data, $mode); - $qr = new QRCode(); - $qr->setErrorCorrectLevel($errorCorrectLevel); - $qr->addData($data, $mode); + $this->errorCorrectLevel = $errorCorrectLevel; - $qrData = $qr->getData(0); + /** @var \codemasher\QRCode\Data\QRDataBase $qrData */ + $qrData = $this->qrDataList[0]; $length = $qrData->getLength(); for($typeNumber = 1; $typeNumber <= 10; $typeNumber++){ - if($length <= $this->util->getMaxLength($typeNumber, $mode, $errorCorrectLevel)){ - $qr->setTypeNumber($typeNumber); + if($length <= $this->util->getMaxLength($typeNumber, $mode, $this->errorCorrectLevel)){ + $this->typeNumber = $typeNumber; break; } } - $qr->make(); + $this->makeImpl(false, $this->getBestMaskPattern()); - return $qr; + return $this; } /** @@ -692,7 +686,7 @@ class QRCode{ $margin = 0; } - $image_size = $this->getModuleCount() * $size + $margin * 2; + $image_size = $this->moduleCount * $size + $margin * 2; $image = imagecreatetruecolor($image_size, $image_size); @@ -705,8 +699,8 @@ class QRCode{ } // convert hexadecimal RGB to arrays for imagecolorallocate - $fgrgb = $this->hex2rgb($fg); - $bgrgb = $this->hex2rgb($bg); + $fgrgb = $this->util->hex2rgb($fg); + $bgrgb = $this->util->hex2rgb($bg); // replace $black and $white with $fgc and $bgc $fgc = imagecolorallocate($image, $fgrgb['r'], $fgrgb['g'], $fgrgb['b']); @@ -718,9 +712,8 @@ class QRCode{ // update $white to $bgc imagefilledrectangle($image, 0, 0, $image_size, $image_size, $bgc); - $count = $this->getModuleCount(); - for($r = 0; $r < $count; $r++){ - for($c = 0; $c < $count; $c++){ + for($r = 0; $r < $this->moduleCount; $r++){ + for($c = 0; $c < $this->moduleCount; $c++){ if($this->isDark($r, $c)){ // update $black to $fgc @@ -742,7 +735,7 @@ class QRCode{ */ public function printHTML(){ $html = ''; - $count = $this->getModuleCount(); + $count = $this->moduleCount; for($r = 0; $r < $count; $r++){ $html .= ''; diff --git a/src/QRConst.php b/src/QRConst.php index 22f34b546..3aefb28a8 100644 --- a/src/QRConst.php +++ b/src/QRConst.php @@ -17,8 +17,8 @@ namespace codemasher\QRCode; class QRConst{ const MODE_NUMBER = 1 << 0; - const MODE_ALPHA_NUM = 1 << 1; - const MODE_8BIT_BYTE = 1 << 2; + const MODE_ALPHANUM = 1 << 1; + const MODE_BYTE = 1 << 2; const MODE_KANJI = 1 << 3; const MASK_PATTERN000 = 0; diff --git a/src/RSBlock.php b/src/RSBlock.php index ec11ddd69..0befe44f2 100644 --- a/src/RSBlock.php +++ b/src/RSBlock.php @@ -22,12 +22,12 @@ class RSBlock{ /** * @var int */ - protected $totalCount; + public $totalCount; /** * @var int */ - protected $dataCount; + public $dataCount; /** * @var array @@ -101,40 +101,28 @@ class RSBlock{ ]; - /** - * @param int $totalCount - * @param int $dataCount - */ - public function setCount($totalCount, $dataCount){ - $this->totalCount = $totalCount; - $this->dataCount = $dataCount; - } - - /** - * @return int - */ - public function getDataCount(){ - return $this->dataCount; - } - - /** - * @return int - */ - public function getTotalCount(){ - return $this->totalCount; - } - /** * @param $typeNumber * @param $errorCorrectLevel * * @return array + * @throws \codemasher\QRCode\QRCodeException */ public function getRSBlocks($typeNumber, $errorCorrectLevel){ - $rsBlock = $this->getRsBlockTable($typeNumber, $errorCorrectLevel); - $length = count($rsBlock) / 3; - $list = []; + switch($errorCorrectLevel){ + case QRConst::ERROR_CORRECT_LEVEL_L: $rsBlock = 0; break; + case QRConst::ERROR_CORRECT_LEVEL_M: $rsBlock = 1; break; + case QRConst::ERROR_CORRECT_LEVEL_Q: $rsBlock = 2; break; + case QRConst::ERROR_CORRECT_LEVEL_H: $rsBlock = 3; break; + default: + throw new QRCodeException('tn:'.$typeNumber.'/ecl:'.$errorCorrectLevel); + } + + $rsBlock = $this->QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + $rsBlock]; + + $list = []; + $length = count($rsBlock) / 3; for($i = 0; $i < $length; $i++){ $count = $rsBlock[$i * 3 + 0]; $totalCount = $rsBlock[$i * 3 + 1]; @@ -148,24 +136,5 @@ class RSBlock{ return $list; } - /** - * @param $typeNumber - * @param $errorCorrectLevel - * - * @return mixed - * @throws \codemasher\QRCode\QRCodeException - */ - public function getRsBlockTable($typeNumber, $errorCorrectLevel){ - - switch($errorCorrectLevel){ - case QRConst::ERROR_CORRECT_LEVEL_L: return $this->QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 0]; - case QRConst::ERROR_CORRECT_LEVEL_M: return $this->QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 1]; - case QRConst::ERROR_CORRECT_LEVEL_Q: return $this->QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 2]; - case QRConst::ERROR_CORRECT_LEVEL_H: return $this->QR_RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 3]; - default: - throw new QRCodeException('tn:'.$typeNumber.'/ecl:'.$errorCorrectLevel); - } - - } } diff --git a/src/Util.php b/src/Util.php index 66b482026..7445fabe7 100644 --- a/src/Util.php +++ b/src/Util.php @@ -11,7 +11,6 @@ namespace codemasher\QRCode; -use codemasher\QRCode\Math; use codemasher\QRCode\Polynomial; use codemasher\QRCode\QRCode; use codemasher\QRCode\QRConst; @@ -29,7 +28,7 @@ class Util{ /** * @var array */ - protected $QR_MAX_LENGTH = [ + public $MAX_LENGTH = [ [[41, 25, 17, 10], [34, 20, 14, 8], [27, 16, 11, 7], [17, 10, 7, 4]], [[77, 47, 32, 20], [63, 38, 26, 16], [48, 29, 20, 12], [34, 20, 14, 8]], [[127, 77, 53, 32], [101, 61, 42, 26], [77, 47, 32, 20], [58, 35, 24, 15]], @@ -45,7 +44,7 @@ class Util{ /** * @var array */ - protected $QR_PATTERN_POSITION_TABLE = [ + public $PATTERN_POSITION = [ [], [6, 18], [6, 22], @@ -88,27 +87,6 @@ class Util{ [6, 30, 58, 86, 114, 142, 170], ]; - /** - * @var \codemasher\QRCode\Math - */ - protected $math; - - /** - * Util constructor. - */ - public function __construct(){ - $this->math = new Math; - } - - /** - * @param int $typeNumber - * - * @return array - */ - public function getPatternPosition($typeNumber){ - return $this->QR_PATTERN_POSITION_TABLE[$typeNumber - 1]; - } - /** * @param int $typeNumber * @param int $mode @@ -121,209 +99,26 @@ class Util{ $_type = $typeNumber - 1; switch($errorCorrectLevel){ - case QRConst::ERROR_CORRECT_LEVEL_L: - $_err = 0; - break; - case QRConst::ERROR_CORRECT_LEVEL_M: - $_err = 1; - break; - case QRConst::ERROR_CORRECT_LEVEL_Q: - $_err = 2; - break; - case QRConst::ERROR_CORRECT_LEVEL_H: - $_err = 3; - break; + case QRConst::ERROR_CORRECT_LEVEL_L: $_err = 0; break; + case QRConst::ERROR_CORRECT_LEVEL_M: $_err = 1; break; + case QRConst::ERROR_CORRECT_LEVEL_Q: $_err = 2; break; + case QRConst::ERROR_CORRECT_LEVEL_H: $_err = 3; break; default: throw new QRCodeException('$_err: '.$errorCorrectLevel); } switch($mode){ - case QRConst::MODE_NUMBER: - $_mode = 0; - break; - case QRConst::MODE_ALPHA_NUM: - $_mode = 1; - break; - case QRConst::MODE_8BIT_BYTE: - $_mode = 2; - break; - case QRConst::MODE_KANJI: - $_mode = 3; - break; + case QRConst::MODE_NUMBER : $_mode = 0; break; + case QRConst::MODE_ALPHANUM: $_mode = 1; break; + case QRConst::MODE_BYTE: $_mode = 2; break; + case QRConst::MODE_KANJI : $_mode = 3; break; default : throw new QRCodeException('$_mode: '.$mode); } - return $this->QR_MAX_LENGTH[$_type][$_err][$_mode]; + return $this->MAX_LENGTH[$_type][$_err][$_mode]; } - /** - * @param $errorCorrectLength - * - * @return \codemasher\QRCode\Polynomial - */ - public function getErrorCorrectPolynomial($errorCorrectLength){ - $a = new Polynomial([1]); - - for($i = 0; $i < $errorCorrectLength; $i++){ - $a = $a->multiply(new Polynomial([1, $this->math->gexp($i)])); - } - - return $a; - } - - /** - * @param $maskPattern - * @param $i - * @param $j - * - * @return bool - * @throws \codemasher\QRCode\QRCodeException - */ - public function getMask($maskPattern, $i, $j){ - - switch($maskPattern){ - case QRConst::MASK_PATTERN000: - return ($i + $j) % 2 === 0; - case QRConst::MASK_PATTERN001: - return $i % 2 === 0; - case QRConst::MASK_PATTERN010: - return $j % 3 === 0; - case QRConst::MASK_PATTERN011: - return ($i + $j) % 3 === 0; - case QRConst::MASK_PATTERN100: - return (floor($i / 2) + floor($j / 3)) % 2 === 0; - case QRConst::MASK_PATTERN101: - return ($i * $j) % 2 + ($i * $j) % 3 === 0; - case QRConst::MASK_PATTERN110: - return (($i * $j) % 2 + ($i * $j) % 3) % 2 === 0; - case QRConst::MASK_PATTERN111: - return (($i * $j) % 3 + ($i + $j) % 2) % 2 === 0; - default : - throw new QRCodeException('mask: '.$maskPattern); - } - } - - /** - * @param \codemasher\QRCode\QRCode $qrCode - * - * @return float|int - */ - public function getLostPoint(QRCode $qrCode){ - $moduleCount = $qrCode->getModuleCount(); - - $lostPoint = 0; - - // LEVEL1 - - for($row = 0; $row < $moduleCount; $row++){ - for($col = 0; $col < $moduleCount; $col++){ - $sameCount = 0; - $dark = $qrCode->isDark($row, $col); - - for($r = -1; $r <= 1; $r++){ - if($row + $r < 0 || $moduleCount <= $row + $r){ - continue; - } - - for($c = -1; $c <= 1; $c++){ - - if($col + $c < 0 || $moduleCount <= $col + $c){ - continue; - } - - if($r == 0 && $c == 0){ - continue; - } - - if($dark == $qrCode->isDark($row + $r, $col + $c)){ - $sameCount++; - } - } - } - - if($sameCount > 5){ - $lostPoint += (3 + $sameCount - 5); - } - } - } - - // LEVEL2 - - for($row = 0; $row < $moduleCount - 1; $row++){ - for($col = 0; $col < $moduleCount - 1; $col++){ - $count = 0; - - if($qrCode->isDark($row, $col)){ - $count++; - } - - if($qrCode->isDark($row + 1, $col)){ - $count++; - } - - if($qrCode->isDark($row, $col + 1)){ - $count++; - } - - if($qrCode->isDark($row + 1, $col + 1)){ - $count++; - } - - if($count === 0 || $count === 4){ - $lostPoint += 3; - } - } - } - - // LEVEL3 - - for($row = 0; $row < $moduleCount; $row++){ - for($col = 0; $col < $moduleCount - 6; $col++){ - if($qrCode->isDark($row, $col) - && !$qrCode->isDark($row, $col + 1) - && $qrCode->isDark($row, $col + 2) - && $qrCode->isDark($row, $col + 3) - && $qrCode->isDark($row, $col + 4) - && !$qrCode->isDark($row, $col + 5) - && $qrCode->isDark($row, $col + 6) - ){ - $lostPoint += 40; - } - } - } - - for($col = 0; $col < $moduleCount; $col++){ - for($row = 0; $row < $moduleCount - 6; $row++){ - if($qrCode->isDark($row, $col) - && !$qrCode->isDark($row + 1, $col) - && $qrCode->isDark($row + 2, $col) - && $qrCode->isDark($row + 3, $col) - && $qrCode->isDark($row + 4, $col) - && !$qrCode->isDark($row + 5, $col) - && $qrCode->isDark($row + 6, $col) - ){ - $lostPoint += 40; - } - } - } - - // LEVEL4 - - $darkCount = 0; - for($col = 0; $col < $moduleCount; $col++){ - for($row = 0; $row < $moduleCount; $row++){ - if($qrCode->isDark($row, $col)){ - $darkCount++; - } - } - } - - $ratio = abs(100 * $darkCount / $moduleCount / $moduleCount - 50) / 5; - $lostPoint += $ratio * 10; - - return $lostPoint; - } /** * @param $s @@ -331,18 +126,20 @@ class Util{ * @return int */ public function getMode($s){ + if($this->isAlphaNum($s)){ if($this->isNumber($s)){ return QRConst::MODE_NUMBER; } - return QRConst::MODE_ALPHA_NUM; + return QRConst::MODE_ALPHANUM; } else if($this->isKanji($s)){ return QRConst::MODE_KANJI; } else{ - return QRConst::MODE_8BIT_BYTE; + return QRConst::MODE_BYTE; } + } /** @@ -350,11 +147,13 @@ class Util{ * * @return bool */ - public function isNumber($s){ - for($i = 0; $i < strlen($s); $i++){ + protected function isNumber($s){ + + $len = strlen($s); + for($i = 0; $i < $len; $i++){ $c = ord($s[$i]); - if(!($this->toCharCode('0') <= $c && $c <= $this->toCharCode('9'))){ + if(!(ord('0') <= $c && $c <= ord('9'))){ return false; } } @@ -367,14 +166,13 @@ class Util{ * * @return bool */ - public function isAlphaNum($s){ - for($i = 0; $i < strlen($s); $i++){ + protected function isAlphaNum($s){ + + $len = strlen($s); + for($i = 0; $i < $len; $i++){ $c = ord($s[$i]); - if(!($this->toCharCode('0') <= $c && $c <= $this->toCharCode('9')) - && !($this->toCharCode('A') <= $c && $c <= $this->toCharCode('Z')) - && strpos(' $%*+-./:', $s[$i]) === false - ){ + if(!(ord('0') <= $c && $c <= ord('9')) && !(ord('A') <= $c && $c <= ord('Z')) && strpos(' $%*+-./:', $s[$i]) === false){ return false; } } @@ -387,12 +185,12 @@ class Util{ * * @return bool */ - public function isKanji($s){ - $data = $s; + protected function isKanji($s){ + $i = 0; - - while($i + 1 < strlen($data)){ - $c = ((0xff&ord($data[$i])) << 8)|(0xff&ord($data[$i + 1])); + $len = strlen($s); + while($i + 1 < $len){ + $c = ((0xff&ord($s[$i])) << 8)|(0xff&ord($s[$i + 1])); if(!(0x8140 <= $c && $c <= 0x9FFC) && !(0xE040 <= $c && $c <= 0xEBBF)){ return false; @@ -401,22 +199,13 @@ class Util{ $i += 2; } - if($i < strlen($data)){ + if($i < $len){ return false; } return true; } - /** - * @param $s - * - * @return int - */ - public function toCharCode($s){ - return ord($s[0]); - } - /** * @param $data * @@ -455,7 +244,7 @@ class Util{ public function getBCHDigit($data){ $digit = 0; - while($data != 0){ + while($data !== 0){ $digit++; $data >>= 1; } @@ -463,4 +252,20 @@ class Util{ return $digit; } + + /** + * used for converting fg/bg colors (e.g. #0000ff = 0x0000FF) - added 2015.07.27 ~ DoktorJ + * + * @param int $hex + * + * @return array + */ + public function hex2rgb($hex = 0x0){ + return [ + 'r' => floor($hex / 65536), + 'g' => floor($hex / 256) % 256, + 'b' => $hex % 256, + ]; + } + }