Browse Source

:octocat:

smiley 10 years ago
commit
b8606b24da

+ 22 - 0
.gitignore

@@ -0,0 +1,22 @@
+#################
+## PHPStorm
+#################
+
+.idea/
+
+#################
+## Eclipse
+#################
+
+.settings/
+.buildpath
+.project
+
+#################
+## Misc
+#################
+
+pdoc.bat
+composer.lock
+docs
+vendor

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Smiley <smiley@chillerlan.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 7 - 0
README.md

@@ -0,0 +1,7 @@
+# codemasher/php-qrcode
+
+[![Packagist](https://img.shields.io/packagist/v/codemasher/php-qrcode.svg?style=flat-square)](https://packagist.org/packages/codemasher/php-qrcode)
+[![License](https://img.shields.io/packagist/l/codemasher/php-qrcode.svg?style=flat-square)](LICENSE)
+
+## Requirements
+- PHP 5.6+, PHP 7

+ 24 - 0
composer.json

@@ -0,0 +1,24 @@
+{
+	"name": "codemasher/php-qrcode",
+	"description": "A QR code generator. PHP 5.6+, PHP 7",
+	"homepage": "https://github.com/codemasher/php-qrcode",
+	"license": "MIT",
+	"keywords": [
+		"QR code",
+		"library"
+	],
+	"authors": [
+		{
+			"name": "Smiley",
+			"email": "smiley@chillerlan.net"
+		}
+	],
+	"require": {
+		"php": ">=5.6.0"
+	},
+	"autoload": {
+		"psr-4": {
+			"codemasher\\QRCode\\": "src/"
+		}
+	}
+}

+ 22 - 0
examples/sample_custom.php

@@ -0,0 +1,22 @@
+<?php
+
+require_once '../vendor/autoload.php';
+
+use codemasher\QRCode\QRCode;
+use codemasher\QRCode\QRConst;
+
+$qrcode = new QRCode;
+$qr = $qrcode->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;
+}
+

+ 42 - 0
examples/sample_html.php

@@ -0,0 +1,42 @@
+<?php
+
+require_once '../vendor/autoload.php';
+
+use codemasher\QRCode\QRCode;
+use codemasher\QRCode\QRConst;
+//---------------------------------------------------------
+
+$qrcode = new QRCode;
+
+
+print('<h4>明示的に型番を指定</h4>');
+
+// エラー訂正レベルを設定
+// 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出力
+$qrcode->printHTML();
+
+//---------------------------------------------------------
+print('<h4>型番自動</h4>');
+
+// 型番が最小となるQRコードを作成
+$qr = $qrcode->getMinimumQRCode('QRコード', QRConst::ERROR_CORRECT_LEVEL_L);
+
+// HTML出力
+$qr->printHTML();

+ 18 - 0
examples/sample_image.php

@@ -0,0 +1,18 @@
+<?php
+
+require_once '../vendor/autoload.php';
+
+use codemasher\QRCode\QRCode;
+use codemasher\QRCode\QRConst;
+
+$qrcode = new QRCode;
+$qr = $qrcode->getMinimumQRCode('QRコード', QRConst::ERROR_CORRECT_LEVEL_L);
+
+// イメージ作成(引数:サイズ,マージン)
+$im = $qr->createImage(2, 4);
+
+header('Content-type: image/png');
+imagepng($im);
+
+imagedestroy($im);
+

+ 92 - 0
src/BitBuffer.php

@@ -0,0 +1,92 @@
+<?php
+/**
+ *
+ * @filesource   BitBuffer.php
+ * @created      25.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode;
+
+/**
+ * Class BitBuffer
+ */
+class BitBuffer{
+
+	/**
+	 * @var array
+	 */
+	protected $buffer = [];
+
+	/**
+	 * @var int
+	 */
+	protected $length = 0;
+
+	/**
+	 * @return array
+	 */
+	public function getBuffer(){
+		return $this->buffer;
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getLengthInBits(){
+		return $this->length;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function __toString(){
+		$buffer = '';
+
+		for($i = 0; $i < $this->getLengthInBits(); $i++){
+			$buffer .= $this->get($i) ? '1' : '0';
+		}
+
+		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
+	 */
+	public function put($num, $length){
+		for($i = 0; $i < $length; $i++){
+			$this->putBit((($num >> ($length - $i - 1))&1) === 1);
+		}
+	}
+
+	/**
+	 * @param $bit
+	 */
+	public function putBit($bit){
+		$bufIndex = floor($this->length / 8);
+
+		if(count($this->buffer) <= $bufIndex){
+			$this->buffer[] = 0;
+		}
+
+		if($bit){
+			$this->buffer[(int)$bufIndex] |= (0x80 >> ($this->length % 8));
+		}
+
+		$this->length++;
+	}
+
+}

+ 106 - 0
src/Data/AlphaNum.php

@@ -0,0 +1,106 @@
+<?php
+/**
+ *
+ * @filesource   AlphaNum.php
+ * @created      25.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode\Data;
+
+use codemasher\QRCode\BitBuffer;
+use codemasher\QRCode\Data;
+use codemasher\QRCode\Data\QRDataBase;
+use codemasher\QRCode\Data\QRDataInterface;
+use codemasher\QRCode\QRCodeException;
+use codemasher\QRCode\QRConst;
+
+/**
+ * Class AlphaNum
+ */
+class AlphaNum extends QRDataBase implements QRDataInterface{
+
+
+	/**
+	 * AlphaNum constructor.
+	 *
+	 * @param $data
+	 */
+	public function __construct($data){
+		parent::__construct($data, QRConst::MODE_ALPHA_NUM);
+	}
+
+	/**
+	 * @param $buffer
+	 */
+	public function write(BitBuffer &$buffer){
+
+		$i = 0;
+		$c = $this->getData();
+
+		while($i + 1 < strlen($c)){
+			$buffer->put($this->getCode(ord($c[$i])) * 45
+				+ $this->getCode(ord($c[$i + 1])), 11);
+			$i += 2;
+		}
+
+		if($i < strlen($c)){
+			$buffer->put($this->getCode(ord($c[$i])), 6);
+		}
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getLength(){
+		return strlen($this->getData());
+	}
+
+	/**
+	 * @param $c
+	 *
+	 * @return int
+	 * @throws \codemasher\QRCode\QRCodeException
+	 */
+	protected function getCode($c){
+
+		if($this->util->toCharCode('0') <= $c
+			&& $c <= $this->util->toCharCode('9')
+		){
+			return $c - $this->util->toCharCode('0');
+		}
+		else if($this->util->toCharCode('A') <= $c
+			&& $c <= $this->util->toCharCode('Z')
+		){
+			return $c - $this->util->toCharCode('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;
+				default :
+					throw new QRCodeException('illegal char: '.$c);
+			}
+		}
+
+	}
+}

+ 51 - 0
src/Data/EightBitByte.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ *
+ * @filesource   EightBitByte.php
+ * @created      25.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode\Data;
+
+use codemasher\QRCode\BitBuffer;
+use codemasher\QRCode\QRConst;
+use codemasher\QRCode\Data\QRDataBase;
+use codemasher\QRCode\Data\QRDataInterface;
+
+
+/**
+ * Class EightBitByte
+ */
+class EightBitByte extends QRDataBase implements QRDataInterface{
+
+	/**
+	 * EightBitByte constructor.
+	 *
+	 * @param $data
+	 */
+	public function __construct($data){
+		parent::__construct($data, QRConst::MODE_8BIT_BYTE);
+	}
+
+	/**
+	 * @param $buffer
+	 */
+	public function write(BitBuffer &$buffer){
+		$data = $this->getData();
+
+		for($i = 0; $i < strlen($data); $i++){
+			$buffer->put(ord($data[$i]), 8);
+		}
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getLength(){
+		return strlen($this->getData());
+	}
+}

+ 72 - 0
src/Data/Kanji.php

@@ -0,0 +1,72 @@
+<?php
+/**
+ *
+ * @filesource   Kanji.php
+ * @created      25.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode\Data;
+
+use codemasher\QRCode\BitBuffer;
+use codemasher\QRCode\Data\QRDataBase;
+use codemasher\QRCode\Data\QRDataInterface;
+use codemasher\QRCode\QRCodeException;
+use codemasher\QRCode\QRConst;
+
+/**
+ * Class Kanji
+ */
+class Kanji extends QRDataBase implements QRDataInterface{
+
+	/**
+	 * Kanji constructor.
+	 *
+	 * @param $data
+	 */
+	public function __construct($data){
+		parent::__construct($data, QRConst::MODE_KANJI);
+	}
+
+	/**
+	 * @param $buffer
+	 *
+	 * @throws \codemasher\QRCode\QRCodeException
+	 */
+	public function write(BitBuffer &$buffer){
+		$data = $this->getData();
+
+		$i = 0;
+		while($i + 1 < strlen($data)){
+			$c = ((0xff&ord($data[$i])) << 8)|(0xff&ord($data[$i + 1]));
+
+			if(0x8140 <= $c && $c <= 0x9FFC){
+				$c -= 0x8140;
+			}
+			else if(0xE040 <= $c && $c <= 0xEBBF){
+				$c -= 0xC140;
+			}
+			else{
+				throw new QRCodeException('illegal char at '.($i + 1).' ('.$c.')');
+			}
+
+			$c = (($c >> 8)&0xff) * 0xC0 + ($c&0xff);
+			$buffer->put($c, 13);
+			$i += 2;
+		}
+
+		if($i < strlen($data)){
+			throw new QRCodeException('illegal char at '.($i + 1));
+		}
+	}
+
+	/**
+	 * @return float
+	 */
+	public function getLength(){
+		return floor(strlen($this->getData()) / 2);
+	}
+}

+ 101 - 0
src/Data/Number.php

@@ -0,0 +1,101 @@
+<?php
+/**
+ *
+ * @filesource   Number.php
+ * @created      26.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode\Data;
+
+use codemasher\QRCode\BitBuffer;
+use codemasher\QRCode\Data\QRDataBase;
+use codemasher\QRCode\Data\QRDataInterface;
+use codemasher\QRCode\QRCodeException;
+use codemasher\QRCode\QRConst;
+
+/**
+ * Class Number
+ */
+class Number extends QRDataBase implements QRDataInterface{
+
+	/**
+	 * @var \codemasher\QRCode\Util
+	 */
+	protected $util;
+
+	/**
+	 * Number constructor.
+	 *
+	 * @param $data
+	 */
+	public function __construct($data){
+		parent::__construct($data, QRConst::MODE_NUMBER);
+	}
+
+	/**
+	 * @param $buffer
+	 */
+	public function write(BitBuffer &$buffer){
+		$data = $this->getData();
+
+		$i = 0;
+		while($i + 2 < strlen($data)){
+			$num = $this->parseInt(substr($data, $i, 3));
+			$buffer->put($num, 10);
+			$i += 3;
+		}
+
+		if($i < strlen($data)){
+
+			if(strlen($data) - $i == 1){
+				$num = $this->parseInt(substr($data, $i, $i + 1));
+				$buffer->put($num, 4);
+			}
+			else if(strlen($data) - $i == 2){
+				$num = $this->parseInt(substr($data, $i, $i + 2));
+				$buffer->put($num, 7);
+			}
+		}
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getLength(){
+		return strlen($this->getData());
+	}
+
+	/**
+	 * @param $s
+	 *
+	 * @return int
+	 */
+	protected function parseInt($s){
+		$num = 0;
+
+		for($i = 0; $i < strlen($s); $i++){
+			$num = $num * 10 + $this->parseIntAt(ord($s[$i]));
+		}
+
+		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);
+	}
+
+}

+ 132 - 0
src/Data/QRDataBase.php

@@ -0,0 +1,132 @@
+<?php
+/**
+ *
+ * @filesource   QRDataBase.php
+ * @created      25.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode\Data;
+
+use codemasher\QRCode\BitBuffer;
+use codemasher\QRCode\QRConst;
+use codemasher\QRCode\QRCodeException;
+use codemasher\QRCode\Util;
+
+/**
+ * Class QRDataBase
+ */
+class QRDataBase{
+
+	/**
+	 * @var
+	 */
+	protected $mode;
+
+	/**
+	 * @var
+	 */
+	protected $data;
+
+	/**
+	 * @var \codemasher\QRCode\Util
+	 */
+	protected $util;
+
+	/**
+	 * QRDataBase constructor.
+	 *
+	 * @param $data
+	 * @param $mode
+	 */
+	public function __construct($data, $mode){
+		$this->data = $data;
+		$this->mode = $mode;
+		$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.');
+	}
+
+	/**
+	 * @param $type
+	 *
+	 * @return int
+	 * @throws \codemasher\QRCode\QRCodeException
+	 */
+	public function getLengthInBits($type){
+		if(1 <= $type && $type < 10){
+
+			// 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;
+				default :
+					throw new QRCodeException('mode: '.$this->mode);
+			}
+
+		}
+		else if($type < 27){
+
+			// 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;
+				default :
+					throw new QRCodeException('mode: '.$this->mode);
+			}
+
+		}
+		else if($type < 41){
+
+			// 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;
+				default :
+					throw new QRCodeException('mode: '.$this->mode);
+			}
+
+		}
+		else{
+			throw new QRCodeException('mode: '.$this->mode);
+		}
+	}
+
+}

+ 47 - 0
src/Data/QRDataInterface.php

@@ -0,0 +1,47 @@
+<?php
+/**
+ *
+ * @filesource   QRDataInterface.php
+ * @created      01.12.2015
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode\Data;
+
+use codemasher\QRCode\BitBuffer;
+
+/**
+ * Interface QRDataInterface
+ */
+interface QRDataInterface{
+
+	/**
+	 * @param \codemasher\QRCode\BitBuffer $buffer
+	 */
+	public function write(BitBuffer &$buffer);
+
+	/**
+	 * @return int
+	 */
+	public function getLength();
+
+	/**
+	 * @return mixed
+	 */
+	public function getData();
+
+	/**
+	 * @return mixed
+	 */
+	public function getMode();
+
+	/**
+	 * @param $type
+	 *
+	 * @return int
+	 * @throws \codemasher\QRCode\QRCodeException
+	 */
+	public function getLengthInBits($type);
+}

+ 104 - 0
src/Math.php

@@ -0,0 +1,104 @@
+<?php
+/**
+ *
+ * @filesource   Math.php
+ * @created      25.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @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];
+	}
+
+}

+ 150 - 0
src/Polynomial.php

@@ -0,0 +1,150 @@
+<?php
+/**
+ *
+ * @filesource   Polynomial.php
+ * @created      25.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode;
+
+use codemasher\QRCode\Math;
+
+/**
+ * Class Polynomial
+ */
+class Polynomial{
+
+	/**
+	 * @var
+	 */
+	protected $num;
+
+	/**
+	 * @var \codemasher\QRCode\Math
+	 */
+	protected $math;
+
+	/**
+	 * Polynomial constructor.
+	 *
+	 * @param array $num
+	 * @param int   $shift
+	 */
+	public function __construct(array $num, $shift = 0){
+		$this->math = new Math;
+
+		$offset = 0;
+		while($offset < count($num) && $num[$offset] === 0){
+			$offset++;
+		}
+
+		$this->num = $this->math->createNumArray(count($num) - $offset + $shift);
+		for($i = 0; $i < count($num) - $offset; $i++){
+			$this->num[$i] = $num[$i + $offset];
+		}
+	}
+
+	/**
+	 * @param $index
+	 * @todo rename!
+	 *
+	 * @return mixed
+	 */
+	public function get($index){
+		return $this->num[$index];
+	}
+
+	/**
+	 * @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++){
+			if($i > 0){
+				$buffer .= ',';
+			}
+			$buffer .= $this->get($i);
+		}
+
+		return $buffer;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function toLogString(){
+		$buffer = '';
+
+		for($i = 0; $i < $this->getLength(); $i++){
+			if($i > 0){
+				$buffer .= ',';
+			}
+			$buffer .= $this->math->glog($this->get($i));
+		}
+
+		return $buffer;
+	}
+
+	/**
+	 * @param \codemasher\QRCode\Polynomial $e
+	 *
+	 * @return \codemasher\QRCode\Polynomial
+	 */
+	public function multiply($e){
+		$num = $this->math->createNumArray($this->getLength() + $e->getLength() - 1);
+
+		for($i = 0; $i < $this->getLength(); $i++){
+			for($j = 0; $j < $e->getLength(); $j++){
+				$num[$i + $j] ^= $this->math->gexp($this->math->glog($this->get($i)) + $this->math->glog($e->get($j)));
+			}
+		}
+
+		return new Polynomial($num);
+	}
+
+	/**
+	 * @param \codemasher\QRCode\Polynomial $e
+	 *
+	 * @return $this|\codemasher\QRCode\Polynomial
+	 */
+	public function mod($e){
+
+		if($this->getLength() - $e->getLength() < 0){
+			return $this;
+		}
+
+		$ratio = $this->math->glog($this->get(0)) - $this->math->glog($e->get(0));
+
+		$num = $this->math->createNumArray($this->getLength());
+		for($i = 0; $i < $this->getLength(); $i++){
+			$num[$i] = $this->get($i);
+		}
+
+		for($i = 0; $i < $e->getLength(); $i++){
+			$num[$i] ^= $this->math->gexp($this->math->glog($e->get($i)) + $ratio);
+		}
+
+		return (new Polynomial($num))->mod($e);
+	}
+
+}

+ 597 - 0
src/QRCode.php

@@ -0,0 +1,597 @@
+<?php
+/**
+ *
+ * @filesource   QRCode.php
+ * @created      26.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode;
+use codemasher\QRCode\Data\AlphaNum;
+use codemasher\QRCode\Data\EightBitByte;
+use codemasher\QRCode\Data\Kanji;
+use codemasher\QRCode\Data\Number;
+use codemasher\QRCode\QRConst;
+
+/**
+ * Class QRCode
+ */
+class QRCode{
+
+	const QR_PAD0 = 0xEC;
+	const QR_PAD1 = 0x11;
+
+	protected $typeNumber;
+
+	protected $modules;
+
+	protected $moduleCount;
+
+	protected $errorCorrectLevel;
+
+	protected $qrDataList;
+
+	protected $util;
+
+	protected $rs_block;
+
+	public function __construct(){
+		$this->typeNumber = 1;
+		$this->errorCorrectLevel = QRConst::ERROR_CORRECT_LEVEL_H;
+		$this->qrDataList = [];
+		$this->util = new Util;
+		$this->rs_block = new RSBlock;
+	}
+
+	public function getTypeNumber(){
+		return $this->typeNumber;
+	}
+
+	public function setTypeNumber($typeNumber){
+		$this->typeNumber = $typeNumber;
+	}
+
+	public function getErrorCorrectLevel(){
+		return $this->errorCorrectLevel;
+	}
+
+	public function setErrorCorrectLevel($errorCorrectLevel){
+		$this->errorCorrectLevel = $errorCorrectLevel;
+	}
+
+	public function addData($data, $mode = 0){
+
+		if($mode == 0){
+			$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 :
+				trigger_error("mode:$mode", E_USER_ERROR);
+		}
+	}
+
+	public function clearData(){
+		$this->qrDataList = [];
+	}
+
+	public function addDataImpl(&$qrData){
+		$this->qrDataList[] = $qrData;
+	}
+
+	public function getDataCount(){
+		return count($this->qrDataList);
+	}
+
+	public function getData($index){
+		return $this->qrDataList[$index];
+	}
+
+	public function isDark($row, $col){
+		if($this->modules[$row][$col] !== null){
+			return $this->modules[$row][$col];
+		}
+		else{
+			return false;
+		}
+	}
+
+	public function getModuleCount(){
+		return $this->moduleCount;
+	}
+
+	// used for converting fg/bg colors (e.g. #0000ff = 0x0000FF)
+	// added 2015.07.27 ~ DoktorJ
+	public function hex2rgb($hex = 0x0){
+		return [
+			'r' => floor($hex / 65536),
+			'g' => floor($hex / 256) % 256,
+			'b' => $hex % 256,
+		];
+	}
+
+	public function make(){
+		$this->makeImpl(false, $this->getBestMaskPattern());
+	}
+
+	public function getBestMaskPattern(){
+
+		$minLostPoint = 0;
+		$pattern = 0;
+
+		for($i = 0; $i < 8; $i++){
+
+			$this->makeImpl(true, $i);
+
+			$lostPoint = $this->util->getLostPoint($this);
+
+			if($i == 0 || $minLostPoint > $lostPoint){
+				$minLostPoint = $lostPoint;
+				$pattern = $i;
+			}
+		}
+
+		return $pattern;
+	}
+
+	public function createNullArray($length){
+		$nullArray = [];
+		for($i = 0; $i < $length; $i++){
+			$nullArray[] = null;
+		}
+		return $nullArray;
+	}
+
+	public function makeImpl($test, $maskPattern){
+
+		$this->moduleCount = $this->typeNumber * 4 + 17;
+
+		$this->modules = [];
+		for($i = 0; $i < $this->moduleCount; $i++){
+			$this->modules[] = $this->createNullArray($this->moduleCount);
+		}
+
+		$this->setupPositionProbePattern(0, 0);
+		$this->setupPositionProbePattern($this->moduleCount - 7, 0);
+		$this->setupPositionProbePattern(0, $this->moduleCount - 7);
+
+		$this->setupPositionAdjustPattern();
+		$this->setupTimingPattern();
+
+		$this->setupTypeInfo($test, $maskPattern);
+
+		if($this->typeNumber >= 7){
+			$this->setupTypeNumber($test);
+		}
+
+		$dataArray = $this->qrDataList;
+
+		$data = $this->createData($this->typeNumber, $this->errorCorrectLevel, $dataArray);
+
+		$this->mapData($data, $maskPattern);
+	}
+
+	public function mapData(&$data, $maskPattern){
+
+		$inc = -1;
+		$row = $this->moduleCount - 1;
+		$bitIndex = 7;
+		$byteIndex = 0;
+
+		for($col = $this->moduleCount - 1; $col > 0; $col -= 2){
+
+			if($col == 6){
+				$col--;
+			}
+
+			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);
+						}
+
+						$mask = $this->util->getMask($maskPattern, $row, $col - $c);
+
+						if($mask){
+							$dark = !$dark;
+						}
+
+						$this->modules[$row][$col - $c] = $dark;
+						$bitIndex--;
+
+						if($bitIndex == -1){
+							$byteIndex++;
+							$bitIndex = 7;
+						}
+					}
+				}
+
+				$row += $inc;
+
+				if($row < 0 || $this->moduleCount <= $row){
+					$row -= $inc;
+					$inc = -$inc;
+					break;
+				}
+			}
+		}
+	}
+
+	public function setupPositionAdjustPattern(){
+
+		$pos = $this->util->getPatternPosition($this->typeNumber);
+
+		for($i = 0; $i < count($pos); $i++){
+
+			for($j = 0; $j < count($pos); $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;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	public 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
+				){
+					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;
+				}
+			}
+		}
+	}
+
+	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);
+		}
+	}
+
+	public function setupTypeNumber($test){
+
+		$bits = $this->util->getBCHTypeNumber($this->typeNumber);
+
+		for($i = 0; $i < 18; $i++){
+			$mod = (!$test && (($bits >> $i)&1) == 1);
+			$this->modules[(int)floor($i / 3)][$i % 3 + $this->moduleCount - 8 - 3] = $mod;
+		}
+
+		for($i = 0; $i < 18; $i++){
+			$mod = (!$test && (($bits >> $i)&1) == 1);
+			$this->modules[$i % 3 + $this->moduleCount - 8 - 3][(int)floor($i / 3)] = $mod;
+		}
+	}
+
+	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;
+	}
+
+	public function createData($typeNumber, $errorCorrectLevel, $dataArray){
+
+		$rsBlocks = $this->rs_block->getRSBlocks($typeNumber, $errorCorrectLevel);
+
+		$buffer = new BitBuffer;
+
+		for($i = 0; $i < count($dataArray); $i++){
+			$data = $dataArray[$i];
+			$buffer->put($data->getMode(), 4);
+			$buffer->put($data->getLength(), $data->getLengthInBits($typeNumber));
+			$data->write($buffer);
+		}
+
+		$totalDataCount = 0;
+		for($i = 0; $i < count($rsBlocks); $i++){
+			$totalDataCount += $rsBlocks[$i]->getDataCount();
+		}
+
+		if($buffer->getLengthInBits() > $totalDataCount * 8){
+			trigger_error("code length overflow. ("
+				.$buffer->getLengthInBits()
+				.">"
+				.$totalDataCount * 8
+				.")", E_USER_ERROR);
+		}
+
+		// end code.
+		if($buffer->getLengthInBits() + 4 <= $totalDataCount * 8){
+			$buffer->put(0, 4);
+		}
+
+		// padding
+		while($buffer->getLengthInBits() % 8 != 0){
+			$buffer->putBit(false);
+		}
+
+		// padding
+		while(true){
+
+			if($buffer->getLengthInBits() >= $totalDataCount * 8){
+				break;
+			}
+			$buffer->put(self::QR_PAD0, 8);
+
+			if($buffer->getLengthInBits() >= $totalDataCount * 8){
+				break;
+			}
+			$buffer->put(self::QR_PAD1, 8);
+		}
+
+		return $this->createBytes($buffer, $rsBlocks);
+	}
+
+	public function createBytes(&$buffer, &$rsBlocks){
+
+		$offset = 0;
+
+		$maxDcCount = 0;
+		$maxEcCount = 0;
+
+		$dcdata = $this->createNullArray(count($rsBlocks));
+		$ecdata = $this->createNullArray(count($rsBlocks));
+
+		for($r = 0; $r < count($rsBlocks); $r++){
+
+			$dcCount = $rsBlocks[$r]->getDataCount();
+			$ecCount = $rsBlocks[$r]->getTotalCount() - $dcCount;
+
+			$maxDcCount = max($maxDcCount, $dcCount);
+			$maxEcCount = max($maxEcCount, $ecCount);
+
+			$dcdata[$r] = $this->createNullArray($dcCount);
+			for($i = 0; $i < count($dcdata[$r]); $i++){
+				$bdata = $buffer->getBuffer();
+				$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);
+			for($i = 0; $i < count($ecdata[$r]); $i++){
+				$modIndex = $i + $modPoly->getLength() - count($ecdata[$r]);
+				$ecdata[$r][$i] = ($modIndex >= 0) ? $modPoly->get($modIndex) : 0;
+			}
+		}
+
+		$totalCodeCount = 0;
+		for($i = 0; $i < count($rsBlocks); $i++){
+			$totalCodeCount += $rsBlocks[$i]->getTotalCount();
+		}
+
+		$data = $this->createNullArray($totalCodeCount);
+
+		$index = 0;
+
+		for($i = 0; $i < $maxDcCount; $i++){
+			for($r = 0; $r < count($rsBlocks); $r++){
+				if($i < count($dcdata[$r])){
+					$data[$index++] = $dcdata[$r][$i];
+				}
+			}
+		}
+
+		for($i = 0; $i < $maxEcCount; $i++){
+			for($r = 0; $r < count($rsBlocks); $r++){
+				if($i < count($ecdata[$r])){
+					$data[$index++] = $ecdata[$r][$i];
+				}
+			}
+		}
+
+		return $data;
+	}
+
+	public function getMinimumQRCode($data, $errorCorrectLevel){
+
+		$mode = $this->util->getMode($data);
+
+		$qr = new QRCode();
+		$qr->setErrorCorrectLevel($errorCorrectLevel);
+		$qr->addData($data, $mode);
+
+		$qrData = $qr->getData(0);
+		$length = $qrData->getLength();
+
+		for($typeNumber = 1; $typeNumber <= 10; $typeNumber++){
+			if($length <= $this->util->getMaxLength($typeNumber, $mode, $errorCorrectLevel)){
+				$qr->setTypeNumber($typeNumber);
+				break;
+			}
+		}
+
+		$qr->make();
+
+		return $qr;
+	}
+
+	// added $fg (foreground), $bg (background), and $bgtrans (use transparent bg) parameters
+	// also added some simple error checking on parameters
+	// updated 2015.07.27 ~ DoktorJ
+	public function createImage($size = 2, $margin = 2, $fg = 0x000000, $bg = 0xFFFFFF, $bgtrans = false){
+
+		// size/margin EC
+		if(!is_numeric($size)){
+			$size = 2;
+		}
+		if(!is_numeric($margin)){
+			$margin = 2;
+		}
+		if($size < 1){
+			$size = 1;
+		}
+		if($margin < 0){
+			$margin = 0;
+		}
+
+		$image_size = $this->getModuleCount() * $size + $margin * 2;
+
+		$image = imagecreatetruecolor($image_size, $image_size);
+
+		// fg/bg EC
+		if($fg < 0 || $fg > 0xFFFFFF){
+			$fg = 0x0;
+		}
+		if($bg < 0 || $bg > 0xFFFFFF){
+			$bg = 0xFFFFFF;
+		}
+
+		// convert hexadecimal RGB to arrays for imagecolorallocate
+		$fgrgb = $this->hex2rgb($fg);
+		$bgrgb = $this->hex2rgb($bg);
+
+		// replace $black and $white with $fgc and $bgc
+		$fgc = imagecolorallocate($image, $fgrgb['r'], $fgrgb['g'], $fgrgb['b']);
+		$bgc = imagecolorallocate($image, $bgrgb['r'], $bgrgb['g'], $bgrgb['b']);
+		if($bgtrans){
+			imagecolortransparent($image, $bgc);
+		}
+
+		// update $white to $bgc
+		imagefilledrectangle($image, 0, 0, $image_size, $image_size, $bgc);
+
+		for($r = 0; $r < $this->getModuleCount(); $r++){
+			for($c = 0; $c < $this->getModuleCount(); $c++){
+				if($this->isDark($r, $c)){
+
+					// update $black to $fgc
+					imagefilledrectangle($image,
+						$margin + $c * $size,
+						$margin + $r * $size,
+						$margin + ($c + 1) * $size - 1,
+						$margin + ($r + 1) * $size - 1,
+						$fgc);
+				}
+			}
+		}
+
+		return $image;
+	}
+
+	public function printHTML($size = "2px"){
+
+		$style = "border-style:none;border-collapse:collapse;margin:0px;padding:0px;";
+
+		print("<table style='$style'>");
+
+		for($r = 0; $r < $this->getModuleCount(); $r++){
+
+			print("<tr style='$style'>");
+
+			for($c = 0; $c < $this->getModuleCount(); $c++){
+				$color = $this->isDark($r, $c) ? "#000000" : "#ffffff";
+				print("<td style='$style;width:$size;height:$size;background-color:$color'></td>");
+			}
+
+			print("</tr>");
+		}
+
+		print("</table>");
+	}
+}

+ 22 - 0
src/QRCodeException.php

@@ -0,0 +1,22 @@
+<?php
+/**
+ * Class QRCodeException
+ *
+ * @filesource   QRCodeException.php
+ * @created      27.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode;
+
+use Exception;
+
+/**
+ * Placeholder
+ */
+class QRCodeException extends Exception{
+
+}

+ 38 - 0
src/QRConst.php

@@ -0,0 +1,38 @@
+<?php
+/**
+ *
+ * @filesource   QRConst.php
+ * @created      26.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode;
+
+/**
+ * Class QRConst
+ */
+class QRConst{
+
+	const MODE_NUMBER = 1 << 0;
+	const MODE_ALPHA_NUM = 1 << 1;
+	const MODE_8BIT_BYTE = 1 << 2;
+	const MODE_KANJI = 1 << 3;
+
+	const MASK_PATTERN000 = 0;
+	const MASK_PATTERN001 = 1;
+	const MASK_PATTERN010 = 2;
+	const MASK_PATTERN011 = 3;
+	const MASK_PATTERN100 = 4;
+	const MASK_PATTERN101 = 5;
+	const MASK_PATTERN110 = 6;
+	const MASK_PATTERN111 = 7;
+
+	const ERROR_CORRECT_LEVEL_L = 1; // 7%.
+	const ERROR_CORRECT_LEVEL_M = 0; // 15%.
+	const ERROR_CORRECT_LEVEL_Q = 3; // 25%.
+	const ERROR_CORRECT_LEVEL_H = 2; // 30%.
+
+}

+ 180 - 0
src/RSBlock.php

@@ -0,0 +1,180 @@
+<?php
+/**
+ *
+ * @filesource   RSBlock.php
+ * @created      26.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode;
+
+use codemasher\QRCode\QRConst;
+use codemasher\QRCode\QRCodeException;
+
+/**
+ * Class RSBlock
+ */
+class RSBlock{
+
+	/**
+	 * @var int
+	 */
+	protected $totalCount;
+
+	/**
+	 * @var int
+	 */
+	protected $dataCount;
+
+	/**
+	 * @var array
+	 */
+	protected $QR_RS_BLOCK_TABLE = [
+
+		// L
+		// M
+		// Q
+		// H
+
+		// 1
+		[1, 26, 19],
+		[1, 26, 16],
+		[1, 26, 13],
+		[1, 26, 9],
+
+		// 2
+		[1, 44, 34],
+		[1, 44, 28],
+		[1, 44, 22],
+		[1, 44, 16],
+
+		// 3
+		[1, 70, 55],
+		[1, 70, 44],
+		[2, 35, 17],
+		[2, 35, 13],
+
+		// 4
+		[1, 100, 80],
+		[2, 50, 32],
+		[2, 50, 24],
+		[4, 25, 9],
+
+		// 5
+		[1, 134, 108],
+		[2, 67, 43],
+		[2, 33, 15, 2, 34, 16],
+		[2, 33, 11, 2, 34, 12],
+
+		// 6
+		[2, 86, 68],
+		[4, 43, 27],
+		[4, 43, 19],
+		[4, 43, 15],
+
+		// 7
+		[2, 98, 78],
+		[4, 49, 31],
+		[2, 32, 14, 4, 33, 15],
+		[4, 39, 13, 1, 40, 14],
+
+		// 8
+		[2, 121, 97],
+		[2, 60, 38, 2, 61, 39],
+		[4, 40, 18, 2, 41, 19],
+		[4, 40, 14, 2, 41, 15],
+
+		// 9
+		[2, 146, 116],
+		[3, 58, 36, 2, 59, 37],
+		[4, 36, 16, 4, 37, 17],
+		[4, 36, 12, 4, 37, 13],
+
+		// 10
+		[2, 86, 68, 2, 87, 69],
+		[4, 69, 43, 1, 70, 44],
+		[6, 43, 19, 2, 44, 20],
+		[6, 43, 15, 2, 44, 16],
+
+	];
+
+
+	public function __construct($totalCount = null, $dataCount = null){
+		if(isset($totalCount, $dataCount)){
+			$this->setCount($totalCount, $dataCount);
+		}
+	}
+
+	/**
+	 * RSBlock constructor.
+	 *
+	 * @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
+	 */
+	public function getRSBlocks($typeNumber, $errorCorrectLevel){
+		$rsBlock = $this->getRsBlockTable($typeNumber, $errorCorrectLevel);
+		$length = count($rsBlock) / 3;
+		$list = [];
+
+		for($i = 0; $i < $length; $i++){
+			$count = $rsBlock[$i * 3 + 0];
+			$totalCount = $rsBlock[$i * 3 + 1];
+			$dataCount = $rsBlock[$i * 3 + 2];
+
+			for($j = 0; $j < $count; $j++){
+				$list[] = new RSBlock($totalCount, $dataCount);
+			}
+		}
+
+		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);
+		}
+
+	}
+
+}

+ 466 - 0
src/Util.php

@@ -0,0 +1,466 @@
+<?php
+/**
+ *
+ * @filesource   Util.php
+ * @created      25.11.2015
+ * @package      codemasher\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2015 Smiley
+ * @license      MIT
+ */
+
+namespace codemasher\QRCode;
+
+use codemasher\QRCode\Math;
+use codemasher\QRCode\Polynomial;
+use codemasher\QRCode\QRCode;
+use codemasher\QRCode\QRConst;
+use codemasher\QRCode\QRCodeException;
+
+/**
+ * Class Util
+ */
+class Util{
+
+	const QR_G15 = (1 << 10)|(1 << 8)|(1 << 5)|(1 << 4)|(1 << 2)|(1 << 1)|(1 << 0);
+	const QR_G18 = (1 << 12)|(1 << 11)|(1 << 10)|(1 << 9)|(1 << 8)|(1 << 5)|(1 << 2)|(1 << 0);
+	const QR_G15_MASK = (1 << 14)|(1 << 12)|(1 << 10)|(1 << 4)|(1 << 1);
+
+	/**
+	 * @var array
+	 */
+	protected $QR_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]],
+		[[187, 114, 78, 48], [149, 90, 62, 38], [111, 67, 46, 28], [82, 50, 34, 21]],
+		[[255, 154, 106, 65], [202, 122, 84, 52], [144, 87, 60, 37], [106, 64, 44, 27]],
+		[[322, 195, 134, 82], [255, 154, 106, 65], [178, 108, 74, 45], [139, 84, 58, 36]],
+		[[370, 224, 154, 95], [293, 178, 122, 75], [207, 125, 86, 53], [154, 93, 64, 39]],
+		[[461, 279, 192, 118], [365, 221, 152, 93], [259, 157, 108, 66], [202, 122, 84, 52]],
+		[[552, 335, 230, 141], [432, 262, 180, 111], [312, 189, 130, 80], [235, 143, 98, 60]],
+		[[652, 395, 271, 167], [513, 311, 213, 131], [364, 221, 151, 93], [288, 174, 119, 74]],
+	];
+
+	/**
+	 * @var array
+	 */
+	protected $QR_PATTERN_POSITION_TABLE = [
+		[],
+		[6, 18],
+		[6, 22],
+		[6, 26],
+		[6, 30],
+		[6, 34],
+		[6, 22, 38],
+		[6, 24, 42],
+		[6, 26, 46],
+		[6, 28, 50],
+		[6, 30, 54],
+		[6, 32, 58],
+		[6, 34, 62],
+		[6, 26, 46, 66],
+		[6, 26, 48, 70],
+		[6, 26, 50, 74],
+		[6, 30, 54, 78],
+		[6, 30, 56, 82],
+		[6, 30, 58, 86],
+		[6, 34, 62, 90],
+		[6, 28, 50, 72, 94],
+		[6, 26, 50, 74, 98],
+		[6, 30, 54, 78, 102],
+		[6, 28, 54, 80, 106],
+		[6, 32, 58, 84, 110],
+		[6, 30, 58, 86, 114],
+		[6, 34, 62, 90, 118],
+		[6, 26, 50, 74, 98, 122],
+		[6, 30, 54, 78, 102, 126],
+		[6, 26, 52, 78, 104, 130],
+		[6, 30, 56, 82, 108, 134],
+		[6, 34, 60, 86, 112, 138],
+		[6, 30, 58, 86, 114, 142],
+		[6, 34, 62, 90, 118, 146],
+		[6, 30, 54, 78, 102, 126, 150],
+		[6, 24, 50, 76, 102, 128, 154],
+		[6, 28, 54, 80, 106, 132, 158],
+		[6, 32, 58, 84, 110, 136, 162],
+		[6, 26, 54, 82, 110, 138, 166],
+		[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 int
+	 */
+	public function getPatternPosition($typeNumber){
+		return $this->QR_PATTERN_POSITION_TABLE[$typeNumber - 1];
+	}
+
+	/**
+	 * @param int $typeNumber
+	 * @param int $mode
+	 * @param int $errorCorrectLevel
+	 *
+	 * @return mixed
+	 * @throws \codemasher\QRCode\QRCodeException
+	 */
+	public function getMaxLength($typeNumber, $mode, $errorCorrectLevel){
+		$_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;
+			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;
+			default :
+				throw new QRCodeException('$_mode: '.$mode);
+		}
+
+		return $this->QR_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
+	 *
+	 * @return int
+	 */
+	public function getMode($s){
+		if($this->isAlphaNum($s)){
+			if($this->isNumber($s)){
+				return QRConst::MODE_NUMBER;
+			}
+			return QRConst::MODE_ALPHA_NUM;
+		}
+		else if($this->isKanji($s)){
+			return QRConst::MODE_KANJI;
+		}
+		else{
+			return QRConst::MODE_8BIT_BYTE;
+		}
+	}
+
+	/**
+	 * @param $s
+	 *
+	 * @return bool
+	 */
+	public function isNumber($s){
+		for($i = 0; $i < strlen($s); $i++){
+			$c = ord($s[$i]);
+
+			if(!($this->toCharCode('0') <= $c && $c <= $this->toCharCode('9'))){
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * @param $s
+	 *
+	 * @return bool
+	 */
+	public function isAlphaNum($s){
+		for($i = 0; $i < strlen($s); $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
+			){
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * @param $s
+	 *
+	 * @return bool
+	 */
+	public function isKanji($s){
+		$data = $s;
+		$i = 0;
+
+		while($i + 1 < strlen($data)){
+			$c = ((0xff&ord($data[$i])) << 8)|(0xff&ord($data[$i + 1]));
+
+			if(!(0x8140 <= $c && $c <= 0x9FFC) && !(0xE040 <= $c && $c <= 0xEBBF)){
+				return false;
+			}
+
+			$i += 2;
+		}
+
+		if($i < strlen($data)){
+			return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * @param $s
+	 *
+	 * @return int
+	 */
+	public function toCharCode($s){
+		return ord($s[0]);
+	}
+
+	/**
+	 * @param $data
+	 *
+	 * @return int
+	 */
+	public function getBCHTypeInfo($data){
+		$d = $data << 10;
+
+		while($this->getBCHDigit($d) - $this->getBCHDigit(self::QR_G15) >= 0){
+			$d ^= (self::QR_G15 << ($this->getBCHDigit($d) - $this->getBCHDigit(self::QR_G15)));
+		}
+
+		return (($data << 10)|$d)^self::QR_G15_MASK;
+	}
+
+	/**
+	 * @param $data
+	 *
+	 * @return int
+	 */
+	public function getBCHTypeNumber($data){
+		$d = $data << 12;
+
+		while($this->getBCHDigit($d) - $this->getBCHDigit(self::QR_G18) >= 0){
+			$d ^= (self::QR_G18 << ($this->getBCHDigit($d) - $this->getBCHDigit(self::QR_G18)));
+		}
+
+		return ($data << 12)|$d;
+	}
+
+	/**
+	 * @param $data
+	 *
+	 * @return int
+	 */
+	public function getBCHDigit($data){
+		$digit = 0;
+
+		while($data != 0){
+			$digit++;
+			$data >>= 1;
+		}
+
+		return $digit;
+	}
+
+}