Просмотр исходного кода

:octocat: refactor QRCode::class

smiley 8 лет назад
Родитель
Сommit
6c00acfc84
6 измененных файлов с 1171 добавлено и 1049 удалено
  1. 10 660
      src/QRCode.php
  2. 989 0
      src/QRDataGenerator.php
  3. 0 260
      src/Util.php
  4. 0 54
      tests/QRCodeTest.php
  5. 172 0
      tests/QRDataGeneratorTest.php
  6. 0 75
      tests/UtilTest.php

+ 10 - 660
src/QRCode.php

@@ -12,16 +12,11 @@
 
 namespace chillerlan\QRCode;
 
-use chillerlan\QRCode\Data\{
-	AlphaNum, Byte, Kanji, Number, QRDataInterface
-};
 use chillerlan\QRCode\Output\QROutputInterface;
 
 /**
  * @link https://github.com/kazuhikoarase/qrcode-generator/tree/master/php
  * @link http://www.thonky.com/qr-code-tutorial/
- *
- * const ALL THE THINGS! ~John Carmack
  */
 class QRCode{
 
@@ -56,80 +51,6 @@ class QRCode{
 	const TYPE_09 =  9; // 1856 1456 1056  800
 	const TYPE_10 = 10; // 2192 1728 1232  976
 
-
-	const MAX_BITS = [
-		self::TYPE_01 => [ 128,  152,   72,  104],
-		self::TYPE_02 => [ 224,  272,  128,  176],
-		self::TYPE_03 => [ 352,  440,  208,  272],
-		self::TYPE_04 => [ 512,  640,  288,  384],
-		self::TYPE_05 => [ 688,  864,  368,  496],
-		self::TYPE_06 => [ 864, 1088,  480,  608],
-		self::TYPE_07 => [ 992, 1248,  528,  704],
-		self::TYPE_08 => [1232, 1552,  688,  880],
-		self::TYPE_09 => [1456, 1856,  800, 1056],
-		self::TYPE_10 => [1728, 2192,  976, 1232],
-	];
-
-	const RSBLOCK = [
-		self::ERROR_CORRECT_LEVEL_L => 0,
-		self::ERROR_CORRECT_LEVEL_M => 1,
-		self::ERROR_CORRECT_LEVEL_Q => 2,
-		self::ERROR_CORRECT_LEVEL_H => 3,
-	];
-
-	const PATTERN_POSITION = [
-		[],
-		[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 array
-	 */
-	protected $matrix = [];
-
-	/**
-	 * @var int
-	 */
-	protected $pixelCount = 0;
-
 	/**
 	 * @var int
 	 */
@@ -140,45 +61,15 @@ class QRCode{
 	 */
 	protected $errorCorrectLevel;
 
-	/**
-	 * @var int
-	 */
-	protected $lostPoint;
-
-	/**
-	 * @var int
-	 */
-	protected $darkCount;
-
-	/**
-	 * @var float
-	 */
-	protected $minLostPoint;
-
-	/**
-	 * @var int
-	 */
-	protected $maskPattern;
-
-	/**
-	 * @var \chillerlan\QRCode\BitBuffer
-	 */
-	protected $bitBuffer;
-
-	/**
-	 * @var \chillerlan\QRCode\Data\QRDataInterface
-	 */
-	protected $qrDataInterface;
-
 	/**
 	 * @var \chillerlan\QRCode\Output\QROutputInterface
 	 */
 	protected $qrOutputInterface;
 
 	/**
-	 * @var \chillerlan\QRCode\Util
+	 * @var string
 	 */
-	protected $util;
+	protected $data;
 
 	/**
 	 * QRCode constructor.
@@ -189,8 +80,6 @@ class QRCode{
 	 */
 	public function __construct($data, QROutputInterface $output, QROptions $options = null){
 		$this->qrOutputInterface = $output;
-		$this->bitBuffer = new BitBuffer;
-		$this->util = new Util;
 
 		$this->setData($data, $options);
 	}
@@ -203,73 +92,26 @@ class QRCode{
 	 * @throws \chillerlan\QRCode\QRCodeException
 	 */
 	public function setData(string $data, QROptions $options = null):QRCode {
-		$data = trim($data);
-
-		if(empty($data)){
-			throw new QRCodeException('No data given.');
-		}
 
 		if(!$options instanceof QROptions){
 			$options = new QROptions;
 		}
 
-		if(!in_array($options->errorCorrectLevel, self::RSBLOCK, true)){
-			throw new QRCodeException('Invalid error correct level: '.$options->errorCorrectLevel);
-		}
+		$this->data              = trim($data);
+		$this->errorCorrectLevel = (int)$options->errorCorrectLevel;
+		$this->typeNumber        = (int)$options->typeNumber;
 
-		$this->errorCorrectLevel = $options->errorCorrectLevel;
-
-		switch(true){
-			case $this->util->isAlphaNum($data):
-				$mode = $this->util->isNumber($data) ? QRDataInterface::MODE_NUMBER : QRDataInterface::MODE_ALPHANUM;
-				break;
-			case $this->util->isKanji($data):
-				$mode = QRDataInterface::MODE_KANJI;
-				break;
-			default:
-				$mode = QRDataInterface::MODE_BYTE;
-				break;
+		if(empty($this->data)){
+			throw new QRCodeException('No data given.');
 		}
 
-		$qrDataInterface = [
-			QRDataInterface::MODE_ALPHANUM => AlphaNum::class,
-			QRDataInterface::MODE_BYTE     => Byte::class,
-			QRDataInterface::MODE_KANJI    => Kanji::class,
-			QRDataInterface::MODE_NUMBER   => Number::class,
-		][$mode];
-
-		$this->qrDataInterface = new $qrDataInterface($data);
-		$this->typeNumber = $options->typeNumber;
-
-		if(!is_int($this->typeNumber) || $this->typeNumber < 1 || $this->typeNumber > 10){
-			$this->typeNumber = $this->getTypeNumber($mode);
+		if(!in_array($this->errorCorrectLevel, range(0, 3), true)){
+			throw new QRCodeException('Invalid error correct level: '.$this->errorCorrectLevel);
 		}
 
 		return $this;
 	}
 
-	/**
-	 * @param int $mode
-	 *
-	 * @return int
-	 * @throws \chillerlan\QRCode\QRCodeException
-	 */
-	protected function getTypeNumber(int $mode):int {
-		$length = $this->qrDataInterface->dataLength;
-
-		if($this->qrDataInterface->mode === QRDataInterface::MODE_KANJI){
-			$length = floor($length / 2);
-		}
-
-		foreach(range(1, 10) as $type){
-			if($length <= $this->util->getMaxLength($type, $mode, $this->errorCorrectLevel)){
-				return $type;
-			}
-		}
-
-		throw new QRCodeException('Unable to determine type number.'); // @codeCoverageIgnore
-	}
-
 	/**
 	 * @return mixed
 	 */
@@ -283,499 +125,7 @@ class QRCode{
 	 * @return array
 	 */
 	public function getRawData():array {
-		$this->minLostPoint = 0;
-		$this->maskPattern = 0;
-
-		for($pattern = 0; $pattern <= 7; $pattern++){
-			$this->testPattern($pattern);
-		}
-
-		$this->getMatrix(false, $this->maskPattern);
-
-		return $this->matrix;
-	}
-
-	/**
-	 * @param array $range
-	 *
-	 * @return void
-	 */
-	protected function testLevel1(array $range){
-
-		foreach($range as $row){
-			foreach($range as $col){
-				$sameCount = 0;
-
-				foreach([-1, 0, 1] as $rr){
-					if($row + $rr < 0 || $this->pixelCount <= $row + $rr){
-						continue;
-					}
-
-					foreach([-1, 0, 1] as $cr){
-
-						if(($rr === 0 && $cr === 0) || ($col + $cr < 0 || $this->pixelCount <= $col + $cr)){
-							continue;
-						}
-
-						if($this->matrix[$row + $rr][$col + $cr] === $this->matrix[$row][$col]){
-							$sameCount++;
-						}
-					}
-				}
-
-				if($sameCount > 5){
-					$this->lostPoint += (3 + $sameCount - 5);
-				}
-
-			}
-		}
-
-	}
-
-	/**
-	 * @param array $range
-	 *
-	 * @return void
-	 */
-	protected function testLevel2(array $range){
-
-		foreach($range as $row){
-			foreach($range as $col){
-				$count = 0;
-
-				if(
-					   $this->matrix[$row    ][$col    ]
-					|| $this->matrix[$row    ][$col + 1]
-					|| $this->matrix[$row + 1][$col    ]
-					|| $this->matrix[$row + 1][$col + 1]
-				){
-					$count++;
-				}
-
-				if($count === 0 || $count === 4){
-					$this->lostPoint += 3;
-				}
-
-			}
-		}
-
-	}
-
-	/**
-	 * @param array $range1
-	 * @param array $range2
-	 *
-	 * @return void
-	 */
-	protected function testLevel3(array $range1, array $range2){
-
-		foreach($range1 as $row){
-			foreach($range2 as $col){
-
-				if(
-					    $this->matrix[$row][$col    ]
-					&& !$this->matrix[$row][$col + 1]
-					&&  $this->matrix[$row][$col + 2]
-					&&  $this->matrix[$row][$col + 3]
-					&&  $this->matrix[$row][$col + 4]
-					&& !$this->matrix[$row][$col + 5]
-					&&  $this->matrix[$row][$col + 6]
-				){
-					$this->lostPoint += 40;
-				}
-
-			}
-		}
-
-		foreach($range1 as $col){
-			foreach($range2 as $row){
-
-				if(
-					    $this->matrix[$row    ][$col]
-					&& !$this->matrix[$row + 1][$col]
-					&&  $this->matrix[$row + 2][$col]
-					&&  $this->matrix[$row + 3][$col]
-					&&  $this->matrix[$row + 4][$col]
-					&& !$this->matrix[$row + 5][$col]
-					&&  $this->matrix[$row + 6][$col]
-				){
-					$this->lostPoint += 40;
-				}
-
-			}
-		}
-
-	}
-
-	/**
-	 * @param array $range
-	 *
-	 * @return void
-	 */
-	protected function testLevel4(array $range){
-
-		foreach($range as $col){
-			foreach($range as $row){
-				if($this->matrix[$row][$col]){
-					$this->darkCount++;
-				}
-			}
-		}
-
-	}
-
-	/**
-	 * @param int $pattern
-	 *
-	 * @return void
-	 */
-	protected function testPattern(int $pattern){
-		$this->getMatrix(true, $pattern);
-		$this->lostPoint = 0;
-		$this->darkCount = 0;
-
-		$range = range(0, $this->pixelCount-1);
-
-		$this->testLevel1($range);
-		$this->testLevel2(range(0, $this->pixelCount-2));
-		$this->testLevel3($range, range(0, $this->pixelCount-7));
-		$this->testLevel4($range);
-
-		$this->lostPoint += (abs(100 * $this->darkCount / $this->pixelCount / $this->pixelCount - 50) / 5) * 10;
-
-		if($pattern === 0 || $this->minLostPoint > $this->lostPoint){
-			$this->minLostPoint = $this->lostPoint;
-			$this->maskPattern = $pattern;
-		}
-
-	}
-
-	/**
-	 * @param bool $test
-	 *
-	 * @return void
-	 */
-	protected function setTypeNumber(bool $test){
-		$bits = $this->util->getBCHTypeNumber($this->typeNumber);
-
-		for($i = 0; $i < 18; $i++){
-			$a = (int)floor($i / 3);
-			$b = $i % 3 + $this->pixelCount - 8 - 3;
-
-			$this->matrix[$a][$b] = $this->matrix[$b][$a] = !$test && (($bits >> $i) & 1) === 1;
-		}
-
-	}
-
-	/**
-	 * @param bool $test
-	 * @param int  $pattern
-	 *
-	 * @return void
-	 */
-	protected function setTypeInfo(bool $test, int $pattern){
-		$this->setPattern();
-		$bits = $this->util->getBCHTypeInfo(($this->errorCorrectLevel << 3) | $pattern);
-
-		for($i = 0; $i < 15; $i++){
-			$mod = !$test && (($bits >> $i) & 1) === 1;
-
-			switch(true){
-				case $i < 6: $this->matrix[$i    ][8] = $mod; break;
-				case $i < 8: $this->matrix[$i + 1][8] = $mod; break;
-				default:
-					$this->matrix[$this->pixelCount - 15 + $i][8] = $mod;
-			}
-
-			switch(true){
-				case $i < 8: $this->matrix[8][$this->pixelCount - $i - 1] = $mod; break;
-				case $i < 9: $this->matrix[8][           15 + 1 - $i - 1] = $mod; break;
-				default:
-					$this->matrix[8][15 - $i - 1] = $mod;
-			}
-
-		}
-
-		$this->matrix[$this->pixelCount - 8][8] = !$test;
-	}
-
-	/**
-	 * @return void
-	 * @throws \chillerlan\QRCode\QRCodeException
-	 */
-	protected function createData(){
-		$this->bitBuffer->clear();
-		$this->bitBuffer->put($this->qrDataInterface->mode, 4);
-		$this->bitBuffer->put(
-			$this->qrDataInterface->mode === QRDataInterface::MODE_KANJI
-				? floor($this->qrDataInterface->dataLength / 2)
-				: $this->qrDataInterface->dataLength,
-			$this->qrDataInterface->getLengthInBits($this->typeNumber)
-		);
-
-		$this->qrDataInterface->write($this->bitBuffer);
-
-		$MAX_BITS = self::MAX_BITS[$this->typeNumber][$this->errorCorrectLevel];
-
-		if($this->bitBuffer->length > $MAX_BITS){
-			throw new QRCodeException('code length overflow. ('.$this->bitBuffer->length.' > '.$MAX_BITS.'bit)');
-		}
-
-		// end code.
-		if($this->bitBuffer->length + 4 <= $MAX_BITS){
-			$this->bitBuffer->put(0, 4);
-		}
-
-		// padding
-		while($this->bitBuffer->length % 8 !== 0){
-			$this->bitBuffer->putBit(false);
-		}
-
-		// padding
-		$PAD0 = 0xEC;
-		$PAD1 = 0x11;
-		while(true){
-
-			if($this->bitBuffer->length >= $MAX_BITS){
-				break;
-			}
-
-			$this->bitBuffer->put($PAD0, 8);
-
-			if($this->bitBuffer->length >= $MAX_BITS){
-				break;
-			}
-
-			$this->bitBuffer->put($PAD1, 8);
-		}
-
-	}
-
-	/**
-	 * @return array
-	 * @throws \chillerlan\QRCode\QRCodeException
-	 */
-	protected function createBytes():array {
-		$totalCodeCount = $maxDcCount = $maxEcCount = $offset = $index = 0;
-		$rsBlocks = $this->util->getRSBlocks($this->typeNumber, $this->errorCorrectLevel);
-		$rsBlockCount = count($rsBlocks);
-		$dcdata = $ecdata = array_fill(0, $rsBlockCount, null);
-
-		foreach($rsBlocks as $key => $value){
-			$rsBlockTotal = $value[0];
-			$rsBlockDataCount = $value[1];
-
-			$maxDcCount = max($maxDcCount, $rsBlockDataCount);
-			$maxEcCount = max($maxEcCount, $rsBlockTotal - $rsBlockDataCount);
-
-			$dcdata[$key] = array_fill(0, $rsBlockDataCount, null);
-
-			foreach($dcdata[$key] as $i => &$_dcdata){
-				$bdata = $this->bitBuffer->buffer;
-				$_dcdata = 0xff & $bdata[$i + $offset];
-			}
-
-			$offset += $rsBlockDataCount;
-
-			$rsPoly = new Polynomial;
-			$modPoly = new Polynomial;
-
-			foreach(range(0, $rsBlockTotal - $rsBlockDataCount - 1) as $i){
-				$modPoly->setNum([1, $modPoly->gexp($i)]);
-				$rsPoly->multiply($modPoly->num);
-			}
-
-			$rsPolyCount = count($rsPoly->num);
-			$modPoly->setNum($dcdata[$key], $rsPolyCount - 1)->mod($rsPoly->num);
-			$ecdata[$key] = array_fill(0, $rsPolyCount - 1, null);
-			$add = count($modPoly->num) - count($ecdata[$key]);
-
-			foreach($ecdata[$key] as $i => &$_ecdata){
-				$modIndex = $i + $add;
-				$_ecdata = $modIndex >= 0 ? $modPoly->num[$modIndex] : 0;
-			}
-
-			$totalCodeCount += $rsBlockTotal;
-		}
-
-		$data = array_fill(0, $totalCodeCount, null);
-		$rsrange = range(0, $rsBlockCount - 1);
-
-		foreach(range(0, $maxDcCount - 1) as $i){
-			foreach($rsrange as $key){
-				if($i < count($dcdata[$key])){
-					$data[$index++] = $dcdata[$key][$i];
-				}
-			}
-		}
-
-		foreach(range(0, $maxEcCount - 1) as $i){
-			foreach($rsrange as $key){
-				if($i < count($ecdata[$key])){
-					$data[$index++] = $ecdata[$key][$i];
-				}
-			}
-		}
-
-		return $data;
-	}
-
-	/**
-	 * @param int $pattern
-	 *
-	 * @return void
-	 * @throws \chillerlan\QRCode\QRCodeException
-	 */
-	protected function mapData(int $pattern){
-		$this->createData();
-		$data = $this->createBytes();
-		$inc = -1;
-		$row = $this->pixelCount - 1;
-		$bitIndex = 7;
-		$byteIndex = 0;
-		$dataCount = count($data);
-
-		for($col = $this->pixelCount - 1; $col > 0; $col -= 2){
-			if($col === 6){
-				$col--;
-			}
-
-			while(true){
-				foreach([0, 1] as $c){
-					$_col = $col - $c;
-
-					if($this->matrix[$row][$_col] === null){
-						$dark = false;
-
-						if($byteIndex < $dataCount){
-							$dark = (($data[$byteIndex] >> $bitIndex) & 1) === 1;
-						}
-
-						$a = $row + $_col;
-						$m = $row * $_col;
-						$MASK_PATTERN = [
-							0 => $a % 2,
-							1 => $row % 2,
-							2 => $_col % 3,
-							3 => $a % 3,
-							4 => (floor($row / 2) + floor($_col / 3)) % 2,
-							5 => $m % 2 + $m % 3,
-							6 => ($m % 2 + $m % 3) % 2,
-							7 => ($m % 3 + $a % 2) % 2,
-						][$pattern];
-
-						if($MASK_PATTERN === 0){
-							$dark = !$dark;
-						}
-
-						$this->matrix[$row][$_col] = $dark;
-
-						$bitIndex--;
-						if($bitIndex === -1){
-							$byteIndex++;
-							$bitIndex = 7;
-						}
-					}
-				}
-
-				$row += $inc;
-				if($row < 0 || $this->pixelCount <= $row){
-					$row -= $inc;
-					$inc = -$inc;
-					break;
-				}
-			}
-		}
-
-	}
-
-	/**
-	 * @return void
-	 */
-	protected function setupPositionProbePattern(){
-		$range = range(-1, 7);
-
-		foreach([[0, 0], [$this->pixelCount - 7, 0], [0, $this->pixelCount - 7]] as $grid){
-			$row = $grid[0];
-			$col = $grid[1];
-
-			foreach($range as $r){
-				foreach($range as $c){
-
-					if($row + $r <= -1 || $this->pixelCount <= $row + $r || $col + $c <= -1 || $this->pixelCount <= $col + $c){
-						continue;
-					}
-
-					$this->matrix[$row + $r][$col + $c] =
-						(0 <= $r && $r <= 6 && ($c === 0 || $c === 6))
-						|| (0 <= $c && $c <= 6 && ($r === 0 || $r === 6))
-						|| (2 <= $c && $c <= 4 &&  2 <= $r && $r <= 4);
-				}
-			}
-		}
-
-	}
-
-	/**
-	 * @return void
-	 */
-	protected function setupPositionAdjustPattern(){
-		$range = self::PATTERN_POSITION[$this->typeNumber - 1];
-
-		foreach($range as $i => $posI){
-			foreach($range as $j => $posJ){
-				if($this->matrix[$posI][$posJ] !== null){
-					continue;
-				}
-
-				for($row = -2; $row <= 2; $row++){
-					for($col = -2; $col <= 2; $col++){
-						$this->matrix[$posI + $row][$posJ + $col] =
-							    $row === -2 || $row === 2
-							||  $col === -2 || $col === 2
-							|| ($row ===  0 && $col === 0);
-					}
-				}
-			}
-		}
-
-	}
-
-	/**
-	 * @return void
-	 * @throws \chillerlan\QRCode\QRCodeException
-	 */
-	protected function setPattern(){
-		$this->setupPositionProbePattern();
-		$this->setupPositionAdjustPattern();
-
-		// setupTimingPattern
-		for($i = 8; $i < $this->pixelCount - 8; $i++){
-			if($this->matrix[$i][6] !== null){
-				continue; // @codeCoverageIgnore
-			}
-
-			$this->matrix[$i][6] = $this->matrix[6][$i] = $i % 2 === 0;
-		}
-
-	}
-
-	/**
-	 * @param bool $test
-	 * @param int  $maskPattern
-	 *
-	 * @throws \chillerlan\QRCode\QRCodeException
-	 */
-	protected function getMatrix(bool $test, int $maskPattern){
-		$this->pixelCount = $this->typeNumber * 4 + 17;
-		$this->matrix = array_fill(0, $this->pixelCount, array_fill(0, $this->pixelCount, null));
-		$this->setTypeInfo($test, $maskPattern);
-
-		if($this->typeNumber >= 7){
-			$this->setTypeNumber($test);
-		}
-
-		$this->mapData($maskPattern);
+		return (new QRDataGenerator($this->data, $this->typeNumber, $this->errorCorrectLevel))->getRawData();
 	}
 
 }

+ 989 - 0
src/QRDataGenerator.php

@@ -0,0 +1,989 @@
+<?php
+/**
+ * Class QRDataGenerator
+ *
+ * @filesource   QRDataGenerator.php
+ * @created      24.10.2017
+ * @package      chillerlan\QRCode
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2017 Smiley
+ * @license      MIT
+ */
+
+namespace chillerlan\QRCode;
+
+use chillerlan\QRCode\Data\{
+	AlphaNum, Byte, Kanji, Number, QRDataInterface
+};
+
+/**
+ * const ALL THE THINGS! ~John Carmack
+ */
+class QRDataGenerator{
+
+	const RSBLOCK = [
+		QRCode::ERROR_CORRECT_LEVEL_L => 0,
+		QRCode::ERROR_CORRECT_LEVEL_M => 1,
+		QRCode::ERROR_CORRECT_LEVEL_Q => 2,
+		QRCode::ERROR_CORRECT_LEVEL_H => 3,
+	];
+
+	const DATA_INTERFACES = [
+		QRDataInterface::MODE_ALPHANUM => AlphaNum::class,
+		QRDataInterface::MODE_BYTE     => Byte::class,
+		QRDataInterface::MODE_KANJI    => Kanji::class,
+		QRDataInterface::MODE_NUMBER   => Number::class,
+	];
+
+	const BLOCK_TABLE = [
+		// 1
+		[1, 26, 19], // L
+		[1, 26, 16], // M
+		[1, 26, 13], // Q
+		[1, 26,  9], // H
+		// 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],
+	];
+
+	const PATTERN_POSITION = [
+		[],
+		[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],
+	];
+
+	const MAX_BITS = [
+		QRCode::TYPE_01 => [128,  152,   72,  104],
+		QRCode::TYPE_02 => [224,  272,  128,  176],
+		QRCode::TYPE_03 => [352,  440,  208,  272],
+		QRCode::TYPE_04 => [512,  640,  288,  384],
+		QRCode::TYPE_05 => [688,  864,  368,  496],
+		QRCode::TYPE_06 => [864,  1088, 480,  608],
+		QRCode::TYPE_07 => [992,  1248, 528,  704],
+		QRCode::TYPE_08 => [1232, 1552, 688,  880],
+		QRCode::TYPE_09 => [1456, 1856, 800, 1056],
+		QRCode::TYPE_10 => [1728, 2192, 976, 1232],
+	];
+
+	const 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 int
+	 */
+	protected $typeNumber;
+
+	/**
+	 * @var int
+	 */
+	protected $errorCorrectLevel;
+
+	/**
+	 * @var int
+	 */
+	protected $lostPoint;
+
+	/**
+	 * @var int
+	 */
+	protected $darkCount;
+
+	/**
+	 * @var float
+	 */
+	protected $minLostPoint;
+
+	/**
+	 * @var int
+	 */
+	protected $maskPattern;
+
+	/**
+	 * @var array
+	 */
+	protected $matrix = [];
+
+	/**
+	 * @var int
+	 */
+	protected $pixelCount = 0;
+
+	/**
+	 * @var \chillerlan\QRCode\Data\QRDataInterface
+	 */
+	protected $qrDataInterface;
+
+	/**
+	 * @var \chillerlan\QRCode\BitBuffer
+	 */
+	protected $bitBuffer;
+
+	/**
+	 * QRDataGenerator constructor.
+	 *
+	 * @param string $data
+	 * @param int    $typeNumber
+	 * @param int    $errorCorrectLevel
+	 *
+	 * @throws \chillerlan\QRCode\QRCodeException
+	 */
+	public function __construct(string $data, int $typeNumber, int $errorCorrectLevel){
+		$this->typeNumber        = $typeNumber;
+		$this->errorCorrectLevel = $errorCorrectLevel;
+		$this->bitBuffer         = new BitBuffer;
+
+		switch(true){
+			case $this->isAlphaNum($data):
+				$mode = $this->isNumber($data)
+					? QRDataInterface::MODE_NUMBER
+					: QRDataInterface::MODE_ALPHANUM;
+				break;
+			case $this->isKanji($data):
+				$mode = QRDataInterface::MODE_KANJI;
+				break;
+			default:
+				$mode = QRDataInterface::MODE_BYTE;
+				break;
+		}
+
+		$qrDataInterface       = self::DATA_INTERFACES[$mode];
+		$this->qrDataInterface = new $qrDataInterface($data);
+
+		if($this->typeNumber < 1 || $this->typeNumber > 10){
+			$this->typeNumber = $this->getTypeNumber($this->qrDataInterface, $mode);
+		}
+
+	}
+
+	/**
+	 * @return array
+	 */
+	public function getRawData():array{
+		$this->minLostPoint = 0;
+		$this->maskPattern  = 0;
+
+		for($pattern = 0; $pattern <= 7; $pattern++){
+			$this->testPattern($pattern);
+		}
+
+		$this->getMatrix(false, $this->maskPattern);
+
+		return $this->matrix;
+	}
+
+	/**
+	 * @param string $string
+	 *
+	 * @return bool
+	 */
+	public function isNumber(string $string):bool {
+		$len = strlen($string);
+
+		for($i = 0; $i < $len; $i++){
+			$chr = ord($string[$i]);
+
+			if(!(ord('0') <= $chr && $chr <= ord('9'))){
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * @param string $string
+	 *
+	 * @return bool
+	 */
+	public function isAlphaNum(string $string):bool {
+		$len = strlen($string);
+
+		for($i = 0; $i < $len; $i++){
+			$chr = ord($string[$i]);
+
+			if(
+				   !(ord('0') <= $chr && $chr <= ord('9'))
+				&& !(ord('A') <= $chr && $chr <= ord('Z'))
+				&& strpos(' $%*+-./:', $string[$i]) === false
+			){
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * @param string $string
+	 *
+	 * @return bool
+	 */
+	public function isKanji(string $string):bool {
+
+		if(empty($string)){
+			return false;
+		}
+
+		$i      = 0;
+		$len = strlen($string);
+
+		while($i + 1 < $len){
+			$c = ((0xff&ord($string[$i])) << 8)|(0xff&ord($string[$i + 1]));
+
+			if(!($c >= 0x8140 && $c <= 0x9FFC) && !($c >= 0xE040 && $c <= 0xEBBF)){
+				return false;
+			}
+
+			$i += 2;
+		}
+
+		return !($i < $len);
+	}
+
+	/**
+	 * @param \chillerlan\QRCode\Data\QRDataInterface $qrDataInterface
+	 * @param int                                     $mode
+	 *
+	 * @return int
+	 * @throws \chillerlan\QRCode\QRCodeException
+	 */
+	protected function getTypeNumber(QRDataInterface $qrDataInterface, int $mode):int {
+		$length = $qrDataInterface->dataLength;
+
+		if($qrDataInterface->mode === QRDataInterface::MODE_KANJI){
+			$length = floor($length / 2);
+		}
+
+		$maxTypenumber = QRCode::TYPE_10;
+
+		$range = range(1, $maxTypenumber);
+		foreach($range as $type){
+
+			if($length <= $this->getMaxLength($type, $mode, $this->errorCorrectLevel)){
+				$maxTypenumber = $type;
+				break;
+			}
+
+		}
+
+		return $maxTypenumber;
+	}
+
+	/**
+	 * @param int $typeNumber
+	 * @param int $mode
+	 * @param int $errorCorrectLevel
+	 *
+	 * @return int
+	 * @throws \chillerlan\QRCode\QRCodeException
+	 */
+	protected function getMaxLength(int $typeNumber, int $mode, int $errorCorrectLevel):int {
+
+		if(!array_key_exists($errorCorrectLevel, self::RSBLOCK)){
+			throw new QRCodeException('Invalid error correct level: '.$errorCorrectLevel);
+		}
+
+		if(!array_key_exists($mode, QRDataInterface::MODE)){
+			throw new QRCodeException('Invalid mode: '.$mode);
+		}
+
+		return self::MAX_LENGTH[$typeNumber - 1][self::RSBLOCK[$errorCorrectLevel]][QRDataInterface::MODE[$mode]];
+	}
+
+	/**
+	 * @param int $data
+	 *
+	 * @return int
+	 */
+	protected function getBCHTypeInfo(int $data):int {
+		$G15_MASK = (1 << 14)|(1 << 12)|(1 << 10)|(1 << 4)|(1 << 1);
+		$G15      = (1 << 10)|(1 << 8)|(1 << 5)|(1 << 4)|(1 << 2)|(1 << 1)|(1 << 0);
+
+		return (($data << 10)|$this->getBCHT($data, 10, $G15))^$G15_MASK;
+	}
+
+	/**
+	 * @param int $data
+	 *
+	 * @return int
+	 */
+	protected function getBCHTypeNumber(int $data):int{
+		$G18 = (1 << 12)|(1 << 11)|(1 << 10)|(1 << 9)|(1 << 8)|(1 << 5)|(1 << 2)|(1 << 0);
+
+		return ($data << 12)|$this->getBCHT($data, 12, $G18);
+	}
+
+	/**
+	 * @param int $data
+	 * @param int $bits
+	 * @param int $mask
+	 *
+	 * @return int
+	 */
+	protected function getBCHT(int $data, int $bits, int $mask):int {
+		$digit = $data << $bits;
+
+		while($this->getBCHDigit($digit) - $this->getBCHDigit($mask) >= 0){
+			$digit ^= ($mask << ($this->getBCHDigit($digit) - $this->getBCHDigit($mask)));
+		}
+
+		return $digit;
+	}
+
+	/**
+	 * @param int $data
+	 *
+	 * @return int
+	 */
+	protected function getBCHDigit(int $data):int {
+		$digit = 0;
+
+		while($data !== 0){
+			$digit++;
+			$data >>= 1;
+		}
+
+		return $digit;
+	}
+
+	/**
+	 * @param int $typeNumber
+	 * @param int $errorCorrectLevel
+	 *
+	 * @return array
+	 * @throws \chillerlan\QRCode\QRCodeException
+	 */
+	protected function getRSBlocks(int $typeNumber, int $errorCorrectLevel):array {
+
+		if(!array_key_exists($errorCorrectLevel, self::RSBLOCK)){
+			throw new QRCodeException('$typeNumber: '.$typeNumber.' / $errorCorrectLevel: '.$errorCorrectLevel);
+		}
+
+		$rsBlock = self::BLOCK_TABLE[($typeNumber - 1) * 4 + self::RSBLOCK[$errorCorrectLevel]];
+		$list    = [];
+		$length  = count($rsBlock) / 3;
+
+		for($i = 0; $i < $length; $i++){
+			for($j = 0; $j < $rsBlock[$i * 3 + 0]; $j++){
+				$list[] = [$rsBlock[$i * 3 + 1], $rsBlock[$i * 3 + 2]];
+			}
+		}
+
+		return $list;
+	}
+
+	/**
+	 * @param array $range
+	 *
+	 * @return void
+	 */
+	protected function testLevel1(array $range){
+
+		foreach($range as $row){
+			foreach($range as $col){
+				$sameCount = 0;
+
+				foreach([-1, 0, 1] as $rowRange){
+
+					if($row + $rowRange < 0 || $this->pixelCount <= $row + $rowRange){
+						continue;
+					}
+
+					foreach([-1, 0, 1] as $colRange){
+
+						if(($rowRange === 0 && $colRange === 0) || ($col + $colRange < 0 || $this->pixelCount <= $col + $colRange)){
+							continue;
+						}
+
+						if($this->matrix[$row + $rowRange][$col + $colRange] === $this->matrix[$row][$col]){
+							$sameCount++;
+						}
+
+					}
+				}
+
+				if($sameCount > 5){
+					$this->lostPoint += (3 + $sameCount - 5);
+				}
+
+			}
+		}
+
+	}
+
+	/**
+	 * @param array $range
+	 *
+	 * @return void
+	 */
+	protected function testLevel2(array $range){
+
+		foreach($range as $row){
+			foreach($range as $col){
+				$count = 0;
+
+				if(
+					   $this->matrix[$row    ][$col    ]
+					|| $this->matrix[$row    ][$col + 1]
+					|| $this->matrix[$row + 1][$col    ]
+					|| $this->matrix[$row + 1][$col + 1]
+				){
+					$count++;
+				}
+
+				if($count === 0 || $count === 4){
+					$this->lostPoint += 3;
+				}
+
+			}
+		}
+
+	}
+
+	/**
+	 * @param array $range1
+	 * @param array $range2
+	 *
+	 * @return void
+	 */
+	protected function testLevel3(array $range1, array $range2){
+
+		foreach($range1 as $row){
+			foreach($range2 as $col){
+
+				if(
+					    $this->matrix[$row][$col    ]
+					&& !$this->matrix[$row][$col + 1]
+					&&  $this->matrix[$row][$col + 2]
+					&&  $this->matrix[$row][$col + 3]
+					&&  $this->matrix[$row][$col + 4]
+					&& !$this->matrix[$row][$col + 5]
+					&&  $this->matrix[$row][$col + 6]
+				){
+					$this->lostPoint += 40;
+				}
+
+			}
+		}
+
+		foreach($range1 as $col){
+			foreach($range2 as $row){
+
+				if(
+					    $this->matrix[$row    ][$col]
+					&& !$this->matrix[$row + 1][$col]
+					&&  $this->matrix[$row + 2][$col]
+					&&  $this->matrix[$row + 3][$col]
+					&&  $this->matrix[$row + 4][$col]
+					&& !$this->matrix[$row + 5][$col]
+					&&  $this->matrix[$row + 6][$col]
+				){
+					$this->lostPoint += 40;
+				}
+
+			}
+		}
+
+	}
+
+	/**
+	 * @param array $range
+	 *
+	 * @return void
+	 */
+	protected function testLevel4(array $range){
+
+		foreach($range as $col){
+			foreach($range as $row){
+				if($this->matrix[$row][$col]){
+					$this->darkCount++;
+				}
+			}
+		}
+
+	}
+
+	/**
+	 * @param int $pattern
+	 *
+	 * @return void
+	 */
+	protected function testPattern(int $pattern){
+		$this->getMatrix(true, $pattern);
+		$this->lostPoint = 0;
+		$this->darkCount = 0;
+
+		$range = range(0, $this->pixelCount - 1);
+
+		$this->testLevel1($range);
+		$this->testLevel2(range(0, $this->pixelCount - 2));
+		$this->testLevel3($range, range(0, $this->pixelCount - 7));
+		$this->testLevel4($range);
+
+		$this->lostPoint += (abs(100 * $this->darkCount / $this->pixelCount / $this->pixelCount - 50) / 5) * 10;
+
+		if($pattern === 0 || $this->minLostPoint > $this->lostPoint){
+			$this->minLostPoint = $this->lostPoint;
+			$this->maskPattern  = $pattern;
+		}
+
+	}
+
+	/**
+	 * @param bool $test
+	 *
+	 * @return void
+	 */
+	protected function setTypeNumber(bool $test){
+		$bits = $this->getBCHTypeNumber($this->typeNumber);
+
+		$range = range(0, 17);
+		foreach($range as $i){
+			$v = !$test && (($bits >> $i) & 1) === 1;
+
+			$a = (int)floor($i / 3);
+			$b = $i % 3 + $this->pixelCount - 8 - 3;
+
+			$this->matrix[$a][$b] = $v;
+			$this->matrix[$b][$a] = $v;
+		}
+
+	}
+
+	/**
+	 * @param bool $test
+	 * @param int  $pattern
+	 *
+	 * @return void
+	 */
+	protected function setTypeInfo(bool $test, int $pattern){
+		$this->setPattern();
+		$bits = $this->getBCHTypeInfo(($this->errorCorrectLevel << 3) | $pattern);
+
+		$range = range(0, 14);
+		foreach($range as $i){
+			$mod = !$test && (($bits >> $i) & 1) === 1;
+
+			switch(true){
+				case $i < 6:
+					$this->matrix[$i][8] = $mod;
+					break;
+				case $i < 8:
+					$this->matrix[$i + 1][8] = $mod;
+					break;
+				default:
+					$this->matrix[$this->pixelCount - 15 + $i][8] = $mod;
+			}
+
+			switch(true){
+				case $i < 8:
+					$this->matrix[8][$this->pixelCount - $i - 1] = $mod;
+					break;
+				case $i < 9:
+					$this->matrix[8][15 + 1 - $i - 1] = $mod;
+					break;
+				default:
+					$this->matrix[8][15 - $i - 1] = $mod;
+			}
+
+		}
+
+		$this->matrix[$this->pixelCount - 8][8] = !$test;
+	}
+
+	/**
+	 * @return void
+	 * @throws \chillerlan\QRCode\QRCodeException
+	 */
+	protected function createData(){
+		$MAX_BITS = self::MAX_BITS[$this->typeNumber][$this->errorCorrectLevel];
+		$PAD0     = 0xEC;
+		$PAD1     = 0x11;
+
+		$this->bitBuffer
+			->clear()
+			->put($this->qrDataInterface->mode, 4)
+			->put(
+			$this->qrDataInterface->mode === QRDataInterface::MODE_KANJI
+					? floor($this->qrDataInterface->dataLength / 2)
+					: $this->qrDataInterface->dataLength,
+				$this->qrDataInterface->getLengthInBits($this->typeNumber)
+			);
+
+		$this->qrDataInterface->write($this->bitBuffer);
+
+		if($this->bitBuffer->length > $MAX_BITS){
+			throw new QRCodeException('code length overflow. ('.$this->bitBuffer->length.' > '.$MAX_BITS.'bit)');
+		}
+
+		// end code.
+		if($this->bitBuffer->length + 4 <= $MAX_BITS){
+			$this->bitBuffer->put(0, 4);
+		}
+
+		// padding
+		while($this->bitBuffer->length % 8 !== 0){
+			$this->bitBuffer->putBit(false);
+		}
+
+		// padding
+		while(true){
+
+			if($this->bitBuffer->length >= $MAX_BITS){
+				break;
+			}
+
+			$this->bitBuffer->put($PAD0, 8);
+
+			if($this->bitBuffer->length >= $MAX_BITS){
+				break;
+			}
+
+			$this->bitBuffer->put($PAD1, 8);
+		}
+
+	}
+
+	/**
+	 * @return array
+	 * @throws \chillerlan\QRCode\QRCodeException
+	 */
+	protected function createBytes():array{
+		$rsBlocks       = $this->getRSBlocks($this->typeNumber, $this->errorCorrectLevel);
+		$rsBlockCount   = count($rsBlocks);
+		$ecdata         = array_fill(0, $rsBlockCount, null);
+		$dcdata         = $ecdata;
+		$totalCodeCount = 0;
+		$maxDcCount     = 0;
+		$maxEcCount     = 0;
+		$offset         = 0;
+		$index          = 0;
+
+		foreach($rsBlocks as $key => $block){
+			$rsBlockTotal     = $block[0];
+			$rsBlockDataCount = $block[1];
+			$maxDcCount       = max($maxDcCount, $rsBlockDataCount);
+			$maxEcCount       = max($maxEcCount, $rsBlockTotal - $rsBlockDataCount);
+			$dcdata[$key]     = array_fill(0, $rsBlockDataCount, null);
+
+			foreach($dcdata[$key] as $a => &$_dcdata){
+				$bdata   = $this->bitBuffer->buffer;
+				$_dcdata = 0xff & $bdata[$a + $offset];
+			}
+
+			$offset += $rsBlockDataCount;
+
+			$rsPoly  = new Polynomial;
+			$modPoly = new Polynomial;
+
+			$blockrange = range(0, $rsBlockTotal - $rsBlockDataCount - 1);
+
+			foreach($blockrange as $b){
+				$modPoly->setNum([1, $modPoly->gexp($b)]);
+				$rsPoly->multiply($modPoly->num);
+			}
+
+			$rsPolyCount = count($rsPoly->num);
+
+			$modPoly->setNum($dcdata[$key], $rsPolyCount - 1)->mod($rsPoly->num);
+
+			$ecdata[$key] = array_fill(0, $rsPolyCount - 1, null);
+			$add          = count($modPoly->num) - count($ecdata[$key]);
+
+			foreach($ecdata[$key] as $c => &$_ecdata){
+				$modIndex = $c + $add;
+				$_ecdata  = $modIndex >= 0 ? $modPoly->num[$modIndex] : 0;
+			}
+
+			$totalCodeCount += $rsBlockTotal;
+		}
+
+		$data    = array_fill(0, $totalCodeCount, null);
+		$rsrange = range(0, $rsBlockCount - 1);
+		$dcrange = range(0, $maxDcCount - 1);
+		$ecrange = range(0, $maxEcCount - 1);
+
+		foreach($dcrange as $x){
+			foreach($rsrange as $j){
+				if($x < count($dcdata[$j])){
+					$data[$index++] = $dcdata[$j][$x];
+				}
+			}
+		}
+
+		foreach($ecrange as $y){
+			foreach($rsrange as $k){
+				if($y < count($ecdata[$k])){
+					$data[$index++] = $ecdata[$k][$y];
+				}
+			}
+		}
+
+		return $data;
+	}
+
+	/**
+	 * @param int $pattern
+	 *
+	 * @return void
+	 * @throws \chillerlan\QRCode\QRCodeException
+	 */
+	protected function mapData(int $pattern){
+		$this->createData();
+
+		$data      = $this->createBytes();
+		$inc       = -1;
+		$row       = $this->pixelCount - 1;
+		$bitIndex  = 7;
+		$byteIndex = 0;
+		$dataCount = count($data);
+
+		for($col = $this->pixelCount - 1; $col > 0; $col -= 2){
+
+			if($col === 6){
+				$col--;
+			}
+
+			while(true){
+				foreach([0, 1] as $c){
+					$_col = $col - $c;
+
+					if($this->matrix[$row][$_col] === null){
+						$dark = false;
+
+						if($byteIndex < $dataCount){
+							$dark = (($data[$byteIndex] >> $bitIndex) & 1) === 1;
+						}
+
+						$a = $row + $_col;
+						$m = $row * $_col;
+
+						$MASK_PATTERN = [
+							0 => $a % 2,
+							1 => $row % 2,
+							2 => $_col % 3,
+							3 => $a % 3,
+							4 => (floor($row / 2) + floor($_col / 3)) % 2,
+							5 => $m % 2 + $m % 3,
+							6 => ($m % 2 + $m % 3) % 2,
+							7 => ($m % 3 + $a % 2) % 2,
+						][$pattern];
+
+						if($MASK_PATTERN === 0){
+							$dark = !$dark;
+						}
+
+						$this->matrix[$row][$_col] = $dark;
+
+						$bitIndex--;
+
+						if($bitIndex === -1){
+							$byteIndex++;
+							$bitIndex = 7;
+						}
+
+					}
+				}
+
+				$row += $inc;
+
+				if($row < 0 || $this->pixelCount <= $row){
+					$row -= $inc;
+					$inc = -$inc;
+
+					break;
+				}
+
+			}
+		}
+
+	}
+
+	/**
+	 * @return void
+	 */
+	protected function setupPositionProbePattern(){
+		$range = range(-1, 7);
+
+		foreach([[0, 0], [$this->pixelCount - 7, 0], [0, $this->pixelCount - 7]] as $grid){
+			$row = $grid[0];
+			$col = $grid[1];
+
+			foreach($range as $r){
+				foreach($range as $c){
+
+					if($row + $r <= -1 || $this->pixelCount <= $row + $r || $col + $c <= -1 || $this->pixelCount <= $col + $c){
+						continue;
+					}
+
+					$this->matrix[$row + $r][$col + $c] =
+						   (0 <= $r && $r <= 6 && ($c === 0 || $c === 6))
+						|| (0 <= $c && $c <= 6 && ($r === 0 || $r === 6))
+						|| (2 <= $c && $c <= 4 && 2 <= $r && $r <= 4);
+				}
+			}
+		}
+
+	}
+
+	/**
+	 * @return void
+	 */
+	protected function setupPositionAdjustPattern(){
+		$range = self::PATTERN_POSITION[$this->typeNumber - 1];
+
+		foreach($range as $i => $posI){
+			foreach($range as $j => $posJ){
+
+				if($this->matrix[$posI][$posJ] !== null){
+					continue;
+				}
+
+				for($row = -2; $row <= 2; $row++){
+					for($col = -2; $col <= 2; $col++){
+						$this->matrix[$posI + $row][$posJ + $col] =
+							    $row === -2 || $row === 2
+							||  $col === -2
+							||  $col === 2
+							|| ($row === 0 && $col === 0);
+					}
+				}
+
+			}
+		}
+
+	}
+
+	/**
+	 * @return void
+	 * @throws \chillerlan\QRCode\QRCodeException
+	 */
+	protected function setPattern(){
+		$this->setupPositionProbePattern();
+		$this->setupPositionAdjustPattern();
+
+		// setupTimingPattern
+		$range = range(8, $this->pixelCount - 8 - 1);
+		foreach($range as $i){
+
+			if($this->matrix[$i][6] !== null){
+				continue; // @codeCoverageIgnore
+			}
+
+			$v = $i % 2 === 0;
+
+			$this->matrix[$i][6] = $v;
+			$this->matrix[6][$i] = $v;
+		}
+
+	}
+
+	/**
+	 * @param bool $test
+	 * @param int  $maskPattern
+	 *
+	 * @throws \chillerlan\QRCode\QRCodeException
+	 */
+	protected function getMatrix(bool $test, int $maskPattern){
+		$this->pixelCount = $this->typeNumber * 4 + 17;
+		$this->matrix     = array_fill(0, $this->pixelCount, array_fill(0, $this->pixelCount, null));
+
+		$this->setTypeInfo($test, $maskPattern);
+
+		if($this->typeNumber >= 7){
+			$this->setTypeNumber($test);
+		}
+
+		$this->mapData($maskPattern);
+	}
+
+}

+ 0 - 260
src/Util.php

@@ -1,260 +0,0 @@
-<?php
-/**
- * Class Util
- *
- * @filesource   Util.php
- * @created      25.11.2015
- * @package      chillerlan\QRCode
- * @author       Smiley <smiley@chillerlan.net>
- * @copyright    2015 Smiley
- * @license      MIT
- */
-
-namespace chillerlan\QRCode;
-
-use chillerlan\QRCode\Data\QRDataInterface;
-
-/**
- *
- */
-class Util{
-
-	const 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]],
-	];
-
-	const BLOCK_TABLE = [
-		// 1
-		[1, 26, 19], // L
-		[1, 26, 16], // M
-		[1, 26, 13], // Q
-		[1, 26,  9], // H
-		// 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],
-	];
-
-	/**
-	 * @param string $string
-	 *
-	 * @return bool
-	 */
-	public function isNumber(string $string):bool {
-		$len = strlen($string);
-
-		for($i = 0; $i < $len; $i++){
-			$chr = ord($string[$i]);
-
-			if(!(ord('0') <= $chr && $chr <= ord('9'))){
-				return false;
-			}
-		}
-
-		return true;
-	}
-
-	/**
-	 * @param string $string
-	 *
-	 * @return bool
-	 */
-	public function isAlphaNum(string $string):bool {
-		$len = strlen($string);
-
-		for($i = 0; $i < $len; $i++){
-			$chr = ord($string[$i]);
-
-			if(
-				  !(ord('0') <= $chr && $chr <= ord('9'))
-			   && !(ord('A') <= $chr && $chr <= ord('Z'))
-			   && strpos(' $%*+-./:', $string[$i]) === false
-			){
-				return false;
-			}
-		}
-
-		return true;
-	}
-
-	/**
-	 * @param string $string
-	 *
-	 * @return bool
-	 */
-	public function isKanji(string $string):bool {
-
-		if(empty($string)){
-			return false;
-		}
-
-		$i = 0;
-		$length = strlen($string);
-
-		while($i + 1 < $length){
-			$c = ((0xff&ord($string[$i])) << 8)|(0xff&ord($string[$i + 1]));
-
-			if(!($c >= 0x8140 && $c <= 0x9FFC) && !($c >= 0xE040 && $c <= 0xEBBF)){
-				return false;
-			}
-
-			$i += 2;
-		}
-
-		return !($i < $length);
-	}
-
-	/**
-	 * @param int $data
-	 *
-	 * @return int
-	 */
-	public function getBCHTypeInfo(int $data):int {
-		$G15_MASK = (1 << 14)|(1 << 12)|(1 << 10)|(1 << 4)|(1 << 1);
-		$G15      = (1 << 10)|(1 << 8)|(1 << 5)|(1 << 4)|(1 << 2)|(1 << 1)|(1 << 0);
-
-		return (($data << 10)|$this->getBCHT($data, 10, $G15))^$G15_MASK;
-	}
-
-	/**
-	 * @param int $data
-	 *
-	 * @return int
-	 */
-	public function getBCHTypeNumber(int $data):int{
-		$G18 = (1 << 12)|(1 << 11)|(1 << 10)|(1 << 9)|(1 << 8)|(1 << 5)|(1 << 2)|(1 << 0);
-
-		return ($data << 12)|$this->getBCHT($data, 12, $G18);
-	}
-
-	/**
-	 * @param int $data
-	 * @param int $bits
-	 * @param int $mask
-	 *
-	 * @return int
-	 */
-	protected function getBCHT(int $data, int $bits, int $mask):int {
-		$digit = $data << $bits;
-
-		while($this->getBCHDigit($digit) - $this->getBCHDigit($mask) >= 0){
-			$digit ^= ($mask << ($this->getBCHDigit($digit) - $this->getBCHDigit($mask)));
-		}
-
-		return $digit;
-	}
-
-	/**
-	 * @param int $data
-	 *
-	 * @return int
-	 */
-	public function getBCHDigit(int $data):int {
-		$digit = 0;
-
-		while($data !== 0){
-			$digit++;
-			$data >>= 1;
-		}
-
-		return $digit;
-	}
-
-	/**
-	 * @param int $typeNumber
-	 * @param int $errorCorrectLevel
-	 *
-	 * @return array
-	 * @throws \chillerlan\QRCode\QRCodeException
-	 */
-	public function getRSBlocks(int $typeNumber, int $errorCorrectLevel):array {
-
-		if(!array_key_exists($errorCorrectLevel, QRCode::RSBLOCK)){
-			throw new QRCodeException('$typeNumber: '.$typeNumber.' / $errorCorrectLevel: '.$errorCorrectLevel);
-		}
-
-		$rsBlock = self::BLOCK_TABLE[($typeNumber - 1) * 4 + QRCode::RSBLOCK[$errorCorrectLevel]];
-		$list = [];
-		$length = count($rsBlock) / 3;
-
-		for($i = 0; $i < $length; $i++){
-			for($j = 0; $j < $rsBlock[$i * 3 + 0]; $j++){
-				$list[] = [$rsBlock[$i * 3 + 1], $rsBlock[$i * 3 + 2]];
-			}
-		}
-
-		return $list;
-	}
-
-	/**
-	 * @param int $typeNumber
-	 * @param int $mode
-	 * @param int $errorCorrectLevel
-	 *
-	 * @return int
-	 * @throws \chillerlan\QRCode\QRCodeException
-	 */
-	public function getMaxLength(int $typeNumber, int $mode, int $errorCorrectLevel):int {
-
-		if(!array_key_exists($errorCorrectLevel, QRCode::RSBLOCK)){
-			throw new QRCodeException('Invalid error correct level: '.$errorCorrectLevel);
-		}
-
-		if(!array_key_exists($mode, QRDataInterface::MODE)){
-			throw new QRCodeException('Invalid mode: '.$mode);
-		}
-
-		return self::MAX_LENGTH[$typeNumber - 1][QRCode::RSBLOCK[$errorCorrectLevel]][QRDataInterface::MODE[$mode]];
-	}
-
-}

+ 0 - 54
tests/QRCodeTest.php

@@ -77,60 +77,6 @@ class QRCodeTest extends TestCase{
 		}
 	}
 
-	/**
-	 * @dataProvider stringDataProvider
-	 */
-	public function testTypeAutoOverride($data, $type){
-
-		$property = $this->reflectionClass->getProperty('typeNumber');
-		$property->setAccessible(true);
-
-		$this->options->typeNumber = 'foo';
-		$this->assertSame($type, $property->getValue($this->reflectionClass->newInstanceArgs([$data, $this->output, $this->options])));
-
-		$this->options->typeNumber = 42;
-		$this->assertSame($type, $property->getValue($this->reflectionClass->newInstanceArgs([$data, $this->output, $this->options])));
-
-		$this->options->typeNumber = QRCode::TYPE_05;
-		$this->assertSame(QRCode::TYPE_05, $property->getValue($this->reflectionClass->newInstanceArgs([$data, $this->output, $this->options])));
-	}
-
-
-	public function getMatrixDataProvider(){
-		return [
-			[QRCode::TYPE_01, 'foobar', 21],
-			[QRCode::TYPE_05, 'foobar', 37],
-			[QRCode::TYPE_10, 'foobar', 57],
-			[QRCode::TYPE_05, '1234567890', 37],
-			[QRCode::TYPE_10, '1234567890', 57],
-			[QRCode::TYPE_03, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 29],
-			[QRCode::TYPE_05, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 37],
-			[QRCode::TYPE_10, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 57],
-			[QRCode::TYPE_05, '茗荷', 37],
-			[QRCode::TYPE_10, '茗荷', 57],
-		];
-	}
-
-	/**
-	 * @dataProvider getMatrixDataProvider
-	 */
-	public function testInternalGetMatrix($type, $data, $pixelCount){
-		$method = $this->reflectionClass->getMethod('getMatrix');
-		$method->setAccessible(true);
-
-		$property = $this->reflectionClass->getProperty('pixelCount');
-		$property->setAccessible(true);
-
-		$this->options->typeNumber = $type;
-
-		for($i = 0; $i <= 7; $i++){
-			$qrcode = $this->reflectionClass->newInstanceArgs([$data, $this->output, $this->options]);
-			$method->invokeArgs($qrcode, [false, $i]);
-
-			$this->assertSame($pixelCount, $property->getValue($qrcode));
-		}
-	}
-
 	/**
 	 * @expectedException \chillerlan\QRCode\QRCodeException
 	 * @expectedExceptionMessage No data given.

+ 172 - 0
tests/QRDataGeneratorTest.php

@@ -0,0 +1,172 @@
+<?php
+/**
+ * Class QRDataGeneratorTest
+ *
+ * @filesource   QRDataGeneratorTest.php
+ * @created      24.10.2017
+ * @package      chillerlan\QRCodeTest
+ * @author       Smiley <smiley@chillerlan.net>
+ * @copyright    2017 Smiley
+ * @license      MIT
+ */
+
+namespace chillerlan\QRCodeTest;
+
+use chillerlan\QRCode\Data\AlphaNum;
+use chillerlan\QRCode\Data\QRDataInterface;
+use chillerlan\QRCode\Output\QRString;
+use chillerlan\QRCode\QRCode;
+use chillerlan\QRCode\QRDataGenerator;
+use chillerlan\QRCode\QROptions;
+use PHPUnit\Framework\TestCase;
+use ReflectionClass;
+use ReflectionMethod;
+use ReflectionProperty;
+
+class QRDataGeneratorTest extends TestCase{
+
+	/**
+	 * @var \chillerlan\QRCode\QRDataGenerator
+	 */
+	protected $qrTest;
+
+	/**
+	 * @var \ReflectionClass
+	 */
+	protected $reflectionClass;
+
+
+	protected function setUp(){
+		$this->reflectionClass = new ReflectionClass(QRDataGenerator::class);
+
+		$this->qrTest = new QRDataGenerator('test', QRCode::TYPE_05, QRCode::ERROR_CORRECT_LEVEL_L);
+	}
+
+	private function getMethod(string $method):ReflectionMethod {
+		$method = $this->reflectionClass->getMethod($method);
+		$method->setAccessible(true);
+
+		return $method;
+	}
+
+	private function getProperty(string $property):ReflectionProperty{
+		$property = $this->reflectionClass->getProperty($property);
+		$property->setAccessible(true);
+
+		return $property;
+	}
+
+	public function stringDataProvider(){
+		return [
+			['1234567890', QRCode::TYPE_01],
+			['ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', QRCode::TYPE_03],
+			['#\\', QRCode::TYPE_01],
+			['茗荷', QRCode::TYPE_01],
+		];
+	}
+
+	/**
+	 * @dataProvider stringDataProvider
+	 */
+	public function testTypeAutoOverride($data, $type){
+		$qrcode = $this->reflectionClass->newInstanceArgs([$data, $type, QRCode::ERROR_CORRECT_LEVEL_L]);
+
+		$this->assertSame($type, $this->getProperty('typeNumber')->getValue($qrcode));
+	}
+
+	public function getMatrixDataProvider(){
+		return [
+			[QRCode::TYPE_01, 'foobar', 21],
+			[QRCode::TYPE_05, 'foobar', 37],
+			[QRCode::TYPE_10, 'foobar', 57],
+			[QRCode::TYPE_05, '1234567890', 37],
+			[QRCode::TYPE_10, '1234567890', 57],
+			[QRCode::TYPE_03, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 29],
+			[QRCode::TYPE_05, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 37],
+			[QRCode::TYPE_10, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 57],
+			[QRCode::TYPE_05, '茗荷', 37],
+			[QRCode::TYPE_10, '茗荷', 57],
+		];
+	}
+
+	/**
+	 * @dataProvider getMatrixDataProvider
+	 */
+	public function testInternalGetMatrix($type, $data, $pixelCount){
+		$method   = $this->getMethod('getMatrix');
+		$property = $this->getProperty('pixelCount');
+
+		for($i = 0; $i <= 7; $i++){
+			$qrcode = $this->reflectionClass->newInstanceArgs([$data, $type, QRCode::ERROR_CORRECT_LEVEL_L]);
+			$method->invokeArgs($qrcode, [false, $i]);
+
+			$this->assertSame($pixelCount, $property->getValue($qrcode));
+		}
+	}
+
+	public function testIsNumber(){
+		$this->assertTrue($this->qrTest->isNumber('1234567890'));
+		$this->assertFalse($this->qrTest->isNumber('abc'));
+	}
+
+	public function testIsAlphaNum(){
+		$this->assertTrue($this->qrTest->isAlphaNum('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:'));
+		$this->assertFalse($this->qrTest->isAlphaNum('#'));
+	}
+
+	// http://stackoverflow.com/a/24755772
+	public function testIsKanji(){
+		$this->assertTrue($this->qrTest->isKanji('茗荷'));
+		$this->assertFalse($this->qrTest->isKanji(''));
+		$this->assertFalse($this->qrTest->isKanji('ÃÃÃ')); // non-kanji
+		$this->assertFalse($this->qrTest->isKanji('荷')); // kanji forced into byte mode due to length
+	}
+
+	// coverage
+	public function testGetBCHTypeNumber(){
+		$this->assertSame(7973, $this->getMethod('getBCHTypeNumber')->invokeArgs($this->qrTest, [1]));
+	}
+
+	/**
+	 * @expectedException \chillerlan\QRCode\QRCodeException
+	 * @expectedExceptionMessage $typeNumber: 1 / $errorCorrectLevel: 42
+	 */
+	public function testGetRSBlocksException(){
+		$this->getMethod('getRSBlocks')->invokeArgs($this->qrTest, [1, 42]);
+	}
+
+	/**
+	 * @expectedException \chillerlan\QRCode\QRCodeException
+	 * @expectedExceptionMessage Invalid error correct level: 42
+	 */
+	public function testGetMaxLengthECLevelException(){
+		$this->getMethod('getMaxLength')->invokeArgs($this->qrTest, [QRCode::TYPE_01, QRDataInterface::MODE_BYTE, 42]);
+	}
+
+	/**
+	 * @expectedException \chillerlan\QRCode\QRCodeException
+	 * @expectedExceptionMessage Invalid mode: 1337
+	 */
+	public function testGetMaxLengthModeException(){
+		$this->getMethod('getMaxLength')->invokeArgs($this->qrTest, [QRCode::TYPE_01, 1337, QRCode::ERROR_CORRECT_LEVEL_H]);
+	}
+
+	public function getTypeNumberDataProvider(){
+		return [
+			[QRDataInterface::MODE_ALPHANUM, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 2],
+			[QRDataInterface::MODE_BYTE, '#\\', 1],
+			[QRDataInterface::MODE_KANJI, '茗荷', 1],
+			[QRDataInterface::MODE_NUMBER, '1234567890', 1],
+		];
+	}
+
+	/**
+	 * @dataProvider getTypeNumberDataProvider
+	 */
+	public function testGetTypeNumber($mode, $data, $expected){
+		$i = QRDataGenerator::DATA_INTERFACES[$mode];
+
+		$this->assertSame($expected, $this->getMethod('getTypeNumber')->invokeArgs($this->qrTest, [new $i($data), $mode]));
+	}
+
+}

+ 0 - 75
tests/UtilTest.php

@@ -1,75 +0,0 @@
-<?php
-/**
- * @filesource   UtilTest.php
- * @created      08.02.2016
- * @author       Smiley <smiley@chillerlan.net>
- * @copyright    2015 Smiley
- * @license      MIT
- */
-
-namespace chillerlan\QRCodeTest;
-
-use chillerlan\QRCode\QRCode;
-use chillerlan\QRCode\Data\QRDataInterface;
-use chillerlan\QRCode\Util;
-use PHPUnit\Framework\TestCase;
-
-class UtilTest extends TestCase{
-
-	/**
-	 * @var \chillerlan\QRCode\Util
-	 */
-	protected $util;
-
-	public function setUp(){
-		$this->util = new Util;
-	}
-
-	public function testIsNumber(){
-		$this->assertEquals(true, $this->util->isNumber('1234567890'));
-		$this->assertEquals(false, $this->util->isNumber('abc'));
-	}
-
-	public function testIsAlphaNum(){
-		$this->assertEquals(true, $this->util->isAlphaNum('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:'));
-		$this->assertEquals(false, $this->util->isAlphaNum('#'));
-	}
-
-	// http://stackoverflow.com/a/24755772
-	public function testIsKanji(){
-		$this->assertEquals(true,  $this->util->isKanji('茗荷'));
-		$this->assertEquals(false, $this->util->isKanji(''));
-		$this->assertEquals(false, $this->util->isKanji('ÃÃÃ')); // non-kanji
-		$this->assertEquals(false, $this->util->isKanji('荷')); // kanji forced into byte mode due to length
-	}
-
-	// coverage
-	public function testGetBCHTypeNumber(){
-		$this->assertEquals(7973, $this->util->getBCHTypeNumber(1));
-	}
-
-	/**
-	 * @expectedException \chillerlan\QRCode\QRCodeException
-	 * @expectedExceptionMessage $typeNumber: 1 / $errorCorrectLevel: 42
-	 */
-	public function testGetRSBlocksException(){
-		$this->util->getRSBlocks(1, 42);
-	}
-
-	/**
-	 * @expectedException \chillerlan\QRCode\QRCodeException
-	 * @expectedExceptionMessage Invalid error correct level: 42
-	 */
-	public function testGetMaxLengthECLevelException(){
-		$this->util->getMaxLength(QRCode::TYPE_01, QRDataInterface::MODE_BYTE, 42);
-	}
-
-	/**
-	 * @expectedException \chillerlan\QRCode\QRCodeException
-	 * @expectedExceptionMessage Invalid mode: 1337
-	 */
-	public function testGetMaxLengthModeException(){
-		$this->util->getMaxLength(QRCode::TYPE_01, 1337, QRCode::ERROR_CORRECT_LEVEL_H);
-	}
-
-}