diff --git a/examples/custom.php b/examples/custom.php
index 4f552b5c1..3732219d8 100644
--- a/examples/custom.php
+++ b/examples/custom.php
@@ -10,13 +10,11 @@ $starttime = microtime(true);
// google authenticator
// https://chart.googleapis.com/chart?chs=200x200&chld=M%7C0&cht=qr&chl=otpauth%3A%2F%2Ftotp%2Ftest%3Fsecret%3DB3JX4VCVJDVNXNZ5%26issuer%3Dchillerlan.net
-$qrcode = (new QRCode(5, QRConst::ERROR_CORRECT_LEVEL_M))
- ->addData('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net')
- ->make();
+$qrcode = new QRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_M, 5);
for($row = 0; $row < $qrcode->moduleCount; $row++){
for($col = 0; $col < $qrcode->moduleCount; $col++){
- echo $qrcode->isDark($row, $col) ? 'X' : ' ';
+ echo (bool)$qrcode->modules[$row][$col] ? '#' : ' ';
}
echo PHP_EOL;
}
diff --git a/examples/html.php b/examples/html.php
index 196c87d1f..2a6dfa631 100644
--- a/examples/html.php
+++ b/examples/html.php
@@ -31,5 +31,5 @@ echo '';
// google authenticator
-$qr = (new QRCode)->getMinimumQRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_M);
+$qr = new QRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_M);
echo $qr->printHTML();
diff --git a/examples/image.php b/examples/image.php
index 91a814c8b..97184fa8f 100644
--- a/examples/image.php
+++ b/examples/image.php
@@ -6,7 +6,7 @@ use codemasher\QRCode\QRCode;
use codemasher\QRCode\QRConst;
// google authenticator
-$qr = (new QRCode)->getMinimumQRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_L);
+$qr = new QRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_L);
header('Content-type: image/png');
diff --git a/src/BitBuffer.php b/src/BitBuffer.php
index ffc559509..a62d3a421 100644
--- a/src/BitBuffer.php
+++ b/src/BitBuffer.php
@@ -29,7 +29,7 @@ class BitBuffer{
/**
* @return string
*/
- public function __toString(){
+/* public function __toString(){
$buffer = '';
for($i = 0; $i < $this->length; $i++){
@@ -38,19 +38,26 @@ class BitBuffer{
return $buffer;
}
-
+*/
/**
* @param $num
* @param $length
+ *
+ * @return $this
*/
public function put($num, $length){
+
for($i = 0; $i < $length; $i++){
$this->putBit((($num >> ($length - $i - 1))&1) === 1);
}
+
+ return $this;
}
/**
* @param $bit
+ *
+ * @return $this
*/
public function putBit($bit){
$bufIndex = floor($this->length / 8);
@@ -64,6 +71,8 @@ class BitBuffer{
}
$this->length++;
+
+ return $this;
}
}
diff --git a/src/Data/AlphaNum.php b/src/Data/AlphaNum.php
index 657119c7d..39677c5b5 100644
--- a/src/Data/AlphaNum.php
+++ b/src/Data/AlphaNum.php
@@ -4,7 +4,7 @@
*
* @filesource AlphaNum.php
* @created 25.11.2015
- * @package codemasher\QRCode
+ * @package codemasher\QRCode\Data
* @author Smiley
* @copyright 2015 Smiley
* @license MIT
@@ -13,8 +13,6 @@
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;
@@ -58,26 +56,23 @@ class AlphaNum extends QRDataBase implements QRDataInterface{
*/
protected function getCode($c){
- if(ord('0') <= $c && $c <= ord('9')){
- return $c - ord('0');
- }
- else if(ord('A') <= $c && $c <= ord('Z')){
- return $c - ord('A') + 10;
- }
- else{
- switch($c){
- case ord(' '): return 36;
- case ord('$'): return 37;
- case ord('%'): return 38;
- case ord('*'): return 39;
- case ord('+'): return 40;
- case ord('-'): return 41;
- case ord('.'): return 42;
- case ord('/'): return 43;
- case ord(':'): return 44;
- default :
- throw new QRCodeException('illegal char: '.$c);
- }
+ switch(true){
+ case ord('0') <= $c && $c <= ord('9'): return $c - ord('0');
+ case ord('A') <= $c && $c <= ord('Z'): return $c - ord('A') + 10;
+ default:
+ switch($c){
+ case ord(' '): return 36;
+ case ord('$'): return 37;
+ case ord('%'): return 38;
+ case ord('*'): return 39;
+ case ord('+'): return 40;
+ case ord('-'): return 41;
+ case ord('.'): return 42;
+ case ord('/'): return 43;
+ case ord(':'): return 44;
+ default:
+ throw new QRCodeException('illegal char: '.$c);
+ }
}
}
diff --git a/src/Data/Byte.php b/src/Data/Byte.php
index a8fab7d98..7bec67979 100644
--- a/src/Data/Byte.php
+++ b/src/Data/Byte.php
@@ -4,7 +4,7 @@
*
* @filesource Byte.php
* @created 25.11.2015
- * @package codemasher\QRCode
+ * @package codemasher\QRCode\Data
* @author Smiley
* @copyright 2015 Smiley
* @license MIT
@@ -14,9 +14,6 @@ namespace codemasher\QRCode\Data;
use codemasher\QRCode\BitBuffer;
use codemasher\QRCode\QRConst;
-use codemasher\QRCode\Data\QRDataBase;
-use codemasher\QRCode\Data\QRDataInterface;
-
/**
*
diff --git a/src/Data/Kanji.php b/src/Data/Kanji.php
index d9a1d335c..d921271e5 100644
--- a/src/Data/Kanji.php
+++ b/src/Data/Kanji.php
@@ -4,7 +4,7 @@
*
* @filesource Kanji.php
* @created 25.11.2015
- * @package codemasher\QRCode
+ * @package codemasher\QRCode\Data
* @author Smiley
* @copyright 2015 Smiley
* @license MIT
@@ -13,8 +13,6 @@
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;
diff --git a/src/Data/Number.php b/src/Data/Number.php
index c528985d7..a547e74e1 100644
--- a/src/Data/Number.php
+++ b/src/Data/Number.php
@@ -4,7 +4,7 @@
*
* @filesource Number.php
* @created 26.11.2015
- * @package codemasher\QRCode
+ * @package QRCode
* @author Smiley
* @copyright 2015 Smiley
* @license MIT
@@ -13,8 +13,6 @@
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;
diff --git a/src/Data/QRDataBase.php b/src/Data/QRDataBase.php
index 03dfb311c..453a30fca 100644
--- a/src/Data/QRDataBase.php
+++ b/src/Data/QRDataBase.php
@@ -4,7 +4,7 @@
*
* @filesource QRDataBase.php
* @created 25.11.2015
- * @package codemasher\QRCode
+ * @package codemasher\QRCode\Data
* @author Smiley
* @copyright 2015 Smiley
* @license MIT
@@ -13,14 +13,12 @@
namespace codemasher\QRCode\Data;
use codemasher\QRCode\BitBuffer;
-use codemasher\QRCode\QRConst;
use codemasher\QRCode\QRCodeException;
-use codemasher\QRCode\Util;
/**
*
*/
-class QRDataBase{
+class QRDataBase implements QRDataInterface{
/**
* @var
@@ -42,11 +40,6 @@ class QRDataBase{
*/
protected $lengthBits = [0, 0, 0];
- /**
- * @var \codemasher\QRCode\Util
- */
- protected $util;
-
/**
* QRDataBase constructor.
*
@@ -55,7 +48,6 @@ class QRDataBase{
public function __construct($data){
$this->data = $data;
$this->dataLength = strlen($data);
- $this->util = new Util;
}
/**
@@ -67,13 +59,24 @@ class QRDataBase{
public function getLengthInBits($type){
switch(true){
- case 1 <= $type && $type < 10: return $this->lengthBits[0]; break; // 1 - 9
- case $type < 27: return $this->lengthBits[1]; break; // 10 - 26
- case $type < 41: return $this->lengthBits[2]; break; // 27 - 40
+ case $type >= 1 && $type <= 9: return $this->lengthBits[0]; // 1 - 9
+ case $type <= 26 : return $this->lengthBits[1]; // 10 - 26
+ case $type <= 40 : return $this->lengthBits[2]; // 27 - 40
default:
- throw new QRCodeException('mode: '.$this->mode);
+ throw new QRCodeException('$type: '.$type);
}
}
+ /**
+ * @todo Implement write() method.
+ *
+ * @param \codemasher\QRCode\BitBuffer $buffer
+ *
+ * @throws \codemasher\QRCode\QRCodeException
+ */
+ public function write(BitBuffer &$buffer){
+ throw new QRCodeException('Method should be implemented in child classes');
+ }
+
}
diff --git a/src/Data/QRDataInterface.php b/src/Data/QRDataInterface.php
index 61847f595..66f13514c 100644
--- a/src/Data/QRDataInterface.php
+++ b/src/Data/QRDataInterface.php
@@ -4,6 +4,7 @@
*
* @filesource QRDataInterface.php
* @created 01.12.2015
+ * @package codemasher\QRCode\Data
* @author Smiley
* @copyright 2015 Smiley
* @license MIT
diff --git a/src/Polynomial.php b/src/Polynomial.php
index c32440488..01cd40698 100644
--- a/src/Polynomial.php
+++ b/src/Polynomial.php
@@ -38,8 +38,7 @@ class Polynomial{
* @param int $shift
*/
public function __construct(array $num = [1], $shift = 0){
- $this->setNum($num, $shift)
- ->setTables();
+ $this->setNum($num, $shift)->setTables();
}
/**
@@ -56,10 +55,7 @@ class Polynomial{
$offset++;
}
- $this->num = [];
- for($i = 0; $i < $numCount - $offset + $shift; $i++){
- $this->num[] = 0;
- }
+ $this->num = array_fill(0, $numCount - $offset + $shift, 0);
for($i = 0; $i < $numCount - $offset; $i++){
$this->num[$i] = $num[$i + $offset];
@@ -72,12 +68,8 @@ class Polynomial{
* @return $this
*/
protected function setTables(){
- $numArr = [];
- for($i = 0; $i < 256; $i++){
- $numArr[] = 0;
- }
- $this->EXP_TABLE = $this->LOG_TABLE = $numArr;
+ $this->EXP_TABLE = $this->LOG_TABLE = array_fill(0, 256, 0);
for($i = 0; $i < 8; $i++){
$this->EXP_TABLE[$i] = 1 << $i;
@@ -97,7 +89,7 @@ class Polynomial{
/**
* @return string
*/
- public function __toString(){
+/* public function __toString(){
$buffer = '';
foreach($this->num as $i => $value){
@@ -109,11 +101,11 @@ class Polynomial{
return $buffer;
}
-
+*/
/**
* @return string
*/
- public function toLogString(){
+/* public function toLogString(){
$buffer = '';
foreach($this->num as $i => $value){
@@ -125,7 +117,7 @@ class Polynomial{
return $buffer;
}
-
+*/
/**
* @param array $e
*
@@ -133,12 +125,7 @@ class Polynomial{
*/
public function multiply(array $e){
- $num = [];
- $len = count($this->num) + count($e) - 1;
- for($i = 0; $i < $len; $i++){
- $num[] = 0;
- }
-
+ $num = array_fill(0, count($this->num) + count($e) - 1, 0);
foreach($this->num as $i => $vi){
foreach($e as $j => $vj){
$num[$i + $j] ^= $this->gexp($this->glog($vi) + $this->glog($vj));
@@ -156,28 +143,19 @@ class Polynomial{
* @return $this
*/
public function mod($e){
-# $starttime = microtime(true);
if(count($this->num) - count($e) < 0){
return $this;
}
$ratio = $this->glog($this->num[0]) - $this->glog($e[0]);
-
- $num = [];
- foreach($this->num as $i => $value){
- $num[$i] = $value;
- }
-
foreach($e as $i => $value){
- $num[$i] ^= $this->gexp($this->glog($e[$i]) + $ratio);
+ $this->num[$i] ^= $this->gexp($this->glog($e[$i]) + $ratio);
}
- $this->setNum($num)->mod($e);
+ $this->setNum($this->num)->mod($e);
-# echo 'Polynomial::mod '.round((microtime(true)-$starttime), 5).PHP_EOL;
-
- return $num;
+ return $this->num;
}
/**
@@ -202,12 +180,9 @@ class Polynomial{
*/
public function gexp($n){
- while($n < 0){
- $n += 255;
- }
-
- while($n >= 256){
- $n -= 255;
+ switch(true){
+ case $n < 0 : $n += 255; break;
+ case $n >= 256: $n -= 255; break;
}
return $this->EXP_TABLE[$n];
diff --git a/src/QRCode.php b/src/QRCode.php
index 2013f0df7..55feba592 100644
--- a/src/QRCode.php
+++ b/src/QRCode.php
@@ -12,13 +12,7 @@
namespace codemasher\QRCode;
-use codemasher\QRCode\Polynomial;
-use codemasher\QRCode\QRConst;
-use codemasher\QRCode\Data\AlphaNum;
-use codemasher\QRCode\Data\Byte;
-use codemasher\QRCode\Data\Kanji;
-use codemasher\QRCode\Data\Number;
-use codemasher\QRCode\Data\QRDataInterface;
+use codemasher\QRCode\Data\QRDataBase;
/**
* @link https://github.com/kazuhikoarase/qrcode-generator/tree/master/php
@@ -28,28 +22,28 @@ class QRCode{
const QR_PAD0 = 0xEC;
const QR_PAD1 = 0x11;
- /**
- * @var int
- */
- public $typeNumber;
-
- /**
- * @var array
- */
- protected $modules;
-
/**
* @var int
*/
public $moduleCount;
/**
- * @var int
+ * @var array
*/
- public $errorCorrectLevel;
+ public $modules;
/**
- * @var array
+ * @var int
+ */
+ protected $typeNumber;
+
+ /**
+ * @var int
+ */
+ protected $errorCorrectLevel;
+
+ /**
+ * @var array -> \codemasher\QRCode\Data\QRDataInterface
*/
protected $qrDataList = [];
@@ -63,6 +57,31 @@ class QRCode{
*/
protected $data;
+ /**
+ * @var int
+ */
+ protected $mode;
+
+ /**
+ * @var array
+ */
+ protected $ERROR_CORRECT_LEVEL = [
+ QRConst::ERROR_CORRECT_LEVEL_L,
+ QRConst::ERROR_CORRECT_LEVEL_M,
+ QRConst::ERROR_CORRECT_LEVEL_Q,
+ QRConst::ERROR_CORRECT_LEVEL_H,
+ ];
+
+ /**
+ * @var array
+ */
+ protected $qrDataInterface = [
+ QRConst::MODE_ALPHANUM => '\\codemasher\\QRCode\\Data\\AlphaNum',
+ QRConst::MODE_BYTE => '\\codemasher\\QRCode\\Data\\Byte',
+ QRConst::MODE_KANJI => '\\codemasher\\QRCode\\Data\\Kanji',
+ QRConst::MODE_NUMBER => '\\codemasher\\QRCode\\Data\\Number',
+ ];
+
/**
* @var \codemasher\QRCode\Util
*/
@@ -79,79 +98,40 @@ class QRCode{
protected $bitBuffer;
/**
+ * @todo WIP
+ *
* QRCode constructor.
*
- * @param int $typeNumber
- * @param int $errorCorrectLevel
+ * @param string $data
+ * @param int $errorCorrectLevel
+ * @param int $typeNumber
+ *
+ * @throws \codemasher\QRCode\QRCodeException
*/
- public function __construct($typeNumber = 1, $errorCorrectLevel = QRConst::ERROR_CORRECT_LEVEL_H){
+ public function __construct($data = '', $errorCorrectLevel = QRConst::ERROR_CORRECT_LEVEL_M, $typeNumber = null){
$this->util = new Util;
$this->rsBlock = new RSBlock;
$this->bitBuffer = new BitBuffer;
- $this->typeNumber = $typeNumber;
- $this->errorCorrectLevel = $errorCorrectLevel;
+ $this->setErrorCorrectLevel($errorCorrectLevel)->setTypeNumber($typeNumber);
+
+ if(!empty($data)){
+ $this->getMinimumQRCode($data);
+ }
+
}
/**
- * @param string $data
- * @param int $mode
+ * @todo WIP
*
- * @return $this
- * @throws \codemasher\QRCode\QRCodeException
- */
- public function addData($data, $mode = null){
- if($mode === null){
- $mode = $this->util->getMode($data);
- }
-
- switch($mode){
- case QRConst::MODE_NUMBER : $this->qrDataList[] = new Number($data); break;
- case QRConst::MODE_ALPHANUM: $this->qrDataList[] = new AlphaNum($data); break;
- case QRConst::MODE_BYTE : $this->qrDataList[] = new Byte($data); break;
- case QRConst::MODE_KANJI : $this->qrDataList[] = new Kanji($data); break;
- default:
- throw new QRCodeException('mode: '.$mode);
- }
-
- return $this;
- }
-
- /**
- * @param int $row
- * @param int $col
- *
- * @return bool
- */
- public function isDark($row, $col){
- if($this->modules[$row][$col] !== null){
- return (bool)$this->modules[$row][$col];
- }
- else{
- return false;
- }
- }
-
- /**
- *
- */
- public function make(){
- $this->makeImpl(false, $this->getBestMaskPattern());
-
- return $this;
- }
-
- /**
* @param $data
- * @param $errorCorrectLevel
*
* @return \codemasher\QRCode\QRCode
* @throws \codemasher\QRCode\QRCodeException
*/
- public function getMinimumQRCode($data, $errorCorrectLevel = QRConst::ERROR_CORRECT_LEVEL_H){
+ public function getMinimumQRCode($data){
$mode = $this->util->getMode($data);
$this->addData($data, $mode);
- $this->errorCorrectLevel = $errorCorrectLevel;
/** @var \codemasher\QRCode\Data\QRDataBase $qrData */
$qrData = $this->qrDataList[0];
@@ -164,18 +144,194 @@ class QRCode{
}
}
- $this->makeImpl(false, $this->getBestMaskPattern());
+ $this->make();
+
+ return $this;
+ }
+
+ /**
+ * @todo WIP
+ *
+ * @param string $data
+ *
+ * @param null $mode
+ *
+ * @return $this
+ * @throws \codemasher\QRCode\QRCodeException
+ */
+ public function addData($data, $mode = null){
+ if($mode === null){
+ $mode = $this->util->getMode($data);
+ }
+
+ if(!isset($this->qrDataInterface[$mode])){
+ throw new QRCodeException('mode: '.$mode);
+ }
+
+ $this->qrDataList[] = new $this->qrDataInterface[$mode]($data);
+
+ return $this;
+ }
+
+
+ /**
+ * @param $errorCorrectLevel
+ *
+ * @return $this
+ * @throws \codemasher\QRCode\QRCodeException
+ */
+ public function setErrorCorrectLevel($errorCorrectLevel){
+
+ if(!in_array($errorCorrectLevel, $this->ERROR_CORRECT_LEVEL)){
+ throw new QRCodeException('Invalid error correct level: '.$errorCorrectLevel);
+ }
+
+ $this->errorCorrectLevel = $errorCorrectLevel;
+
+ return $this;
+ }
+
+ /**
+ * @param int $typeNumber
+ *
+ * @return $this
+ * @throws \codemasher\QRCode\QRCodeException
+ */
+ public function setTypeNumber($typeNumber){
+ $typeNumber = intval($typeNumber);
+
+ if($typeNumber < 1 || $typeNumber > 10){
+ throw new QRCodeException('Invalid type number: '.$typeNumber);
+ }
+
+ $this->typeNumber = $typeNumber;
+
+ return $this;
+ }
+
+ /**
+ *
+ */
+ public function make(){
+ $minLostPoint = 0;
+ $pattern = 0;
+
+ for($i = 0; $i < 8; $i++){
+ $this->makeImpl(true, $i);
+ $lostPoint = 0;
+
+ // LEVEL1
+ for($row = 0; $row < $this->moduleCount; $row++){
+ for($col = 0; $col < $this->moduleCount; $col++){
+ $sameCount = 0;
+ $dark = $this->modules[$row][$col];
+
+ for($r = -1; $r <= 1; $r++){
+ if($row + $r < 0 || $this->moduleCount <= $row + $r){
+ continue;
+ }
+
+ for($c = -1; $c <= 1; $c++){
+
+ if(($r === 0 && $c === 0) || ($col + $c < 0 || $this->moduleCount <= $col + $c)){
+ continue;
+ }
+
+ if($this->modules[$row + $r][$col + $c] === $dark){
+ $sameCount++;
+ }
+
+ }
+ }
+
+ if($sameCount > 5){
+ $lostPoint += (3 + $sameCount - 5);
+ }
+ }
+ }
+
+ // LEVEL2
+ for($row = 0; $row < $this->moduleCount - 1; $row++){
+ for($col = 0; $col < $this->moduleCount - 1; $col++){
+ $count = 0;
+
+ if($this->modules[$row][$col] || $this->modules[$row][$col + 1]
+ || $this->modules[$row + 1][$col] || $this->modules[$row + 1][$col + 1]
+ ){
+ $count++;
+ }
+
+ if($count === 0 || $count === 4){
+ $lostPoint += 3;
+ }
+
+ }
+ }
+
+ // LEVEL3
+ for($row = 0; $row < $this->moduleCount; $row++){
+ for($col = 0; $col < $this->moduleCount - 6; $col++){
+ if($this->modules[$row][$col]
+ && !$this->modules[$row][$col + 1]
+ && $this->modules[$row][$col + 2]
+ && $this->modules[$row][$col + 3]
+ && $this->modules[$row][$col + 4]
+ && !$this->modules[$row][$col + 5]
+ && $this->modules[$row][$col + 6]
+ ){
+ $lostPoint += 40;
+ }
+ }
+ }
+
+ for($col = 0; $col < $this->moduleCount; $col++){
+ for($row = 0; $row < $this->moduleCount - 6; $row++){
+ if($this->modules[$row][$col]
+ && !$this->modules[$row + 1][$col]
+ && $this->modules[$row + 2][$col]
+ && $this->modules[$row + 3][$col]
+ && $this->modules[$row + 4][$col]
+ && !$this->modules[$row + 5][$col]
+ && $this->modules[$row + 6][$col]
+ ){
+ $lostPoint += 40;
+ }
+ }
+ }
+
+ // LEVEL4
+ $darkCount = 0;
+ for($col = 0; $col < $this->moduleCount; $col++){
+ for($row = 0; $row < $this->moduleCount; $row++){
+ if($this->modules[$row][$col]){
+ $darkCount++;
+ }
+ }
+ }
+
+ $ratio = abs(100 * $darkCount / $this->moduleCount / $this->moduleCount - 50) / 5;
+ $lostPoint += $ratio * 10;
+
+ if($i === 0 || $minLostPoint > $lostPoint){
+ $minLostPoint = $lostPoint;
+ $pattern = $i;
+ }
+
+ }
+
+ $this->makeImpl(false, $pattern);
return $this;
}
/**
* @param bool $test
- * @param int $maskPattern
+ * @param int $pattern
*
* @return $this
+ * @throws \codemasher\QRCode\QRCodeException
*/
- protected function makeImpl($test, $maskPattern){
+ protected function makeImpl($test, $pattern){
$this->moduleCount = $this->typeNumber * 4 + 17;
$this->modules = [];
@@ -186,15 +342,41 @@ class QRCode{
$this->setupPositionProbePattern(0, 0)
->setupPositionProbePattern($this->moduleCount - 7, 0)
- ->setupPositionProbePattern(0, $this->moduleCount - 7)
- ->setupPositionAdjustPattern()
- ->setupTimingPattern();
+ ->setupPositionProbePattern(0, $this->moduleCount - 7);
- $data = ($this->errorCorrectLevel << 3) | $maskPattern;
+ // setupPositionAdjustPattern
+ $pos = $this->util->PATTERN_POSITION[$this->typeNumber - 1];
+ foreach($pos as $i => $posI){
+ foreach($pos as $j => $posJ){
+ if($this->modules[$posI][$posJ] !== null){
+ continue;
+ }
+
+ for($row = -2; $row <= 2; $row++){
+ for($col = -2; $col <= 2; $col++){
+ $this->modules[$posI + $row][$posJ + $col] =
+ (bool)$row === -2 || $row === 2 || $col === -2 || $col === 2 || ($row === 0 && $col === 0);
+ }
+ }
+
+ }
+ }
+
+ // setupTimingPattern
+ for($i = 8; $i < $this->moduleCount - 8; $i++){
+ if($this->modules[$i][6] !== null){
+ continue;
+ }
+
+ $this->modules[$i][6] = $this->modules[6][$i] = (bool)$i % 2 === 0;
+ }
+
+
+ $data = ($this->errorCorrectLevel << 3) | $pattern;
$bits = $this->util->getBCHTypeInfo($data);
for($i = 0; $i < 15; $i++){
- $mod = !$test && (($bits >> $i) & 1) === 1;
+ $mod = (bool)!$test && (($bits >> $i) & 1) === 1;
switch(true){
case $i < 6: $this->modules[$i][8] = $mod; break;
@@ -221,23 +403,12 @@ class QRCode{
$a = (int)floor($i / 3);
$b = $i % 3 + $this->moduleCount - 8 - 3;
- $this->modules[$a][$b] = $this->modules[$b][$a] = !$test && (($bits >> $i) & 1) === 1;
+ $this->modules[$a][$b] = $this->modules[$b][$a] = (bool)!$test && (($bits >> $i) & 1) === 1;
}
}
$this->data = $this->createData($this->typeNumber, $this->errorCorrectLevel);
- $this->mapData($maskPattern);
- return $this;
- }
-
- /**
- * @param $maskPattern
- *
- * @return $this
- * @throws \codemasher\QRCode\QRCodeException
- */
- protected function mapData($maskPattern){
$inc = -1;
$row = $this->moduleCount - 1;
$bitIndex = 7;
@@ -261,24 +432,27 @@ class QRCode{
}
$_col = $col - $c;
- switch($maskPattern){
- case QRConst::MASK_PATTERN000: $mask = ($row + $_col) % 2 === 0; break;
- case QRConst::MASK_PATTERN001: $mask = $row % 2 === 0; break;
- case QRConst::MASK_PATTERN010: $mask = $_col % 3 === 0; break;
- case QRConst::MASK_PATTERN011: $mask = ($row + $_col) % 3 === 0; break;
- case QRConst::MASK_PATTERN100: $mask = (floor($row / 2) + floor($_col / 3)) % 2 === 0; break;
- case QRConst::MASK_PATTERN101: $mask = ($row * $_col) % 2 + ($row * $_col) % 3 === 0; break;
- case QRConst::MASK_PATTERN110: $mask = (($row * $_col) % 2 + ($row * $_col) % 3) % 2 === 0; break;
- case QRConst::MASK_PATTERN111: $mask = (($row * $_col) % 3 + ($row + $_col) % 2) % 2 === 0; break;
- default :
- throw new QRCodeException('mask: '.$maskPattern);
+
+ $MASK_PATTERN = [
+ QRConst::MASK_PATTERN000 => ($row + $_col) % 2 === 0,
+ QRConst::MASK_PATTERN001 => $row % 2 === 0,
+ QRConst::MASK_PATTERN010 => $_col % 3 === 0,
+ QRConst::MASK_PATTERN011 => ($row + $_col) % 3 === 0,
+ QRConst::MASK_PATTERN100 => (floor($row / 2) + floor($_col / 3)) % 2 === 0,
+ QRConst::MASK_PATTERN101 => ($row * $_col) % 2 + ($row * $_col) % 3 === 0,
+ QRConst::MASK_PATTERN110 => (($row * $_col) % 2 + ($row * $_col) % 3) % 2 === 0,
+ QRConst::MASK_PATTERN111 => (($row * $_col) % 3 + ($row + $_col) % 2) % 2 === 0,
+ ];
+
+ if(!isset($MASK_PATTERN[$pattern])){
+ throw new QRCodeException('mask: '.$pattern);
}
- if($mask){
+ if($MASK_PATTERN[$pattern]){
$dark = !$dark;
}
- $this->modules[$row][$col - $c] = $dark;
+ $this->modules[$row][$col - $c] = (bool)$dark;
$bitIndex--;
if($bitIndex === -1){
@@ -328,198 +502,29 @@ class QRCode{
return $this;
}
- /**
- * @return $this
- */
- protected function setupPositionAdjustPattern(){
- $pos = $this->util->PATTERN_POSITION[$this->typeNumber - 1];
-
- foreach($pos as $i => $posI){
- foreach($pos as $j => $posJ){
- if($this->modules[$posI][$posJ] !== null){
- continue;
- }
-
- for($row = -2; $row <= 2; $row++){
- for($col = -2; $col <= 2; $col++){
- $this->modules[$posI + $row][$posJ + $col] =
- (bool)$row === -2 || $row === 2 || $col === -2 || $col === 2 || ($row === 0 && $col === 0);
- }
- }
-
- }
- }
-
- return $this;
- }
-
- /**
- * @return $this
- */
- protected function setupTimingPattern(){
-
- for($i = 8; $i < $this->moduleCount - 8; $i++){
- if($this->modules[$i][6] !== null){
- continue;
- }
-
- $this->modules[$i][6] = $this->modules[6][$i] = $i % 2 === 0;
- }
-
- return $this;
- }
-
- /**
- * @return int
- */
- protected function getBestMaskPattern(){
- $minLostPoint = 0;
- $pattern = 0;
-
- for($i = 0; $i < 8; $i++){
- $this->makeImpl(true, $i);
- $lostPoint = 0;
-
- // LEVEL1
-
- for($row = 0; $row < $this->moduleCount; $row++){
- for($col = 0; $col < $this->moduleCount; $col++){
- $sameCount = 0;
- $dark = $this->isDark($row, $col);
-
- for($r = -1; $r <= 1; $r++){
- if($row + $r < 0 || $this->moduleCount <= $row + $r){
- continue;
- }
-
- for($c = -1; $c <= 1; $c++){
-
- if($col + $c < 0 || $this->moduleCount <= $col + $c){
- continue;
- }
-
- if($r == 0 && $c == 0){
- continue;
- }
-
- if($dark === $this->isDark($row + $r, $col + $c)){
- $sameCount++;
- }
- }
- }
-
- if($sameCount > 5){
- $lostPoint += (3 + $sameCount - 5);
- }
- }
- }
-
- // LEVEL2
-
- for($row = 0; $row < $this->moduleCount - 1; $row++){
- for($col = 0; $col < $this->moduleCount - 1; $col++){
- $count = 0;
-
- if($this->isDark($row, $col)){
- $count++;
- }
-
- if($this->isDark($row + 1, $col)){
- $count++;
- }
-
- if($this->isDark($row, $col + 1)){
- $count++;
- }
-
- if($this->isDark($row + 1, $col + 1)){
- $count++;
- }
-
- if($count === 0 || $count === 4){
- $lostPoint += 3;
- }
- }
- }
-
- // LEVEL3
-
- for($row = 0; $row < $this->moduleCount; $row++){
- for($col = 0; $col < $this->moduleCount - 6; $col++){
- if($this->isDark($row, $col)
- && !$this->isDark($row, $col + 1)
- && $this->isDark($row, $col + 2)
- && $this->isDark($row, $col + 3)
- && $this->isDark($row, $col + 4)
- && !$this->isDark($row, $col + 5)
- && $this->isDark($row, $col + 6)
- ){
- $lostPoint += 40;
- }
- }
- }
-
- for($col = 0; $col < $this->moduleCount; $col++){
- for($row = 0; $row < $this->moduleCount - 6; $row++){
- if($this->isDark($row, $col)
- && !$this->isDark($row + 1, $col)
- && $this->isDark($row + 2, $col)
- && $this->isDark($row + 3, $col)
- && $this->isDark($row + 4, $col)
- && !$this->isDark($row + 5, $col)
- && $this->isDark($row + 6, $col)
- ){
- $lostPoint += 40;
- }
- }
- }
-
- // LEVEL4
-
- $darkCount = 0;
- for($col = 0; $col < $this->moduleCount; $col++){
- for($row = 0; $row < $this->moduleCount; $row++){
- if($this->isDark($row, $col)){
- $darkCount++;
- }
- }
- }
-
- $ratio = abs(100 * $darkCount / $this->moduleCount / $this->moduleCount - 50) / 5;
- $lostPoint += $ratio * 10;
-
- if($i === 0 || $minLostPoint > $lostPoint){
- $minLostPoint = $lostPoint;
- $pattern = $i;
- }
- }
-
- return $pattern;
- }
-
/**
* @param $typeNumber
* @param $errorCorrectLevel
*
* @return array
* @throws \codemasher\QRCode\QRCodeException
- * @todo: slooooow in PHP5
- *
*/
protected function createData($typeNumber, $errorCorrectLevel){
$this->bitBuffer->buffer = [];
$this->bitBuffer->length = 0;
-
$this->rsBlockList = $this->rsBlock->getRSBlocks($typeNumber, $errorCorrectLevel);
+ $rsBlockCount = count($this->rsBlockList);
- $totalDataCount = $totalCodeCount = $offset = $maxDcCount = $maxEcCount = 0;
+ $totalDataCount = $totalCodeCount = $offset = $maxDcCount = $maxEcCount = $index = 0;
+ $dcdata = $ecdata = array_fill(0, $rsBlockCount, null);
- /** @var \codemasher\QRCode\Data\QRDataInterface $data */
+
+ /** @var \codemasher\QRCode\Data\QRDataBase $data */
foreach($this->qrDataList as &$data){
- $len = $data->mode === QRConst::MODE_KANJI ? floor($data->dataLength / 2) : $data->dataLength;
+ $this->bitBuffer
+ ->put($data->mode, 4)
+ ->put($data->mode === QRConst::MODE_KANJI ? floor($data->dataLength / 2) : $data->dataLength, $data->getLengthInBits($typeNumber));
- $this->bitBuffer->put($data->mode, 4);
- $this->bitBuffer->put($len, $data->getLengthInBits($typeNumber));
$data->write($this->bitBuffer);
}
@@ -560,9 +565,6 @@ class QRCode{
$this->bitBuffer->put(self::QR_PAD1, 8);
}
- $rsBlockCount = count($this->rsBlockList);
- $dcdata = $ecdata = array_fill(0, $rsBlockCount, null);
-
foreach($this->rsBlockList as $r => &$_rsData){
$this->rsBlock->totalCount = $_rsData[0];
$this->rsBlock->dataCount = $_rsData[1];
@@ -582,13 +584,11 @@ class QRCode{
$offset += $this->rsBlock->dataCount;
- // PHP5: 0.09s
for($i = 0; $i < $this->rsBlock->totalCount - $this->rsBlock->dataCount; $i++){
$modPoly->setNum([1, $modPoly->gexp($i)]);
$rsPoly->multiply($modPoly->num);
}
- // PHP5: 0.11s
$rsPolyCount = count($rsPoly->num);
$modPoly->setNum($dcdata[$r], $rsPolyCount - 1)->mod($rsPoly->num);
$ecdata[$r] = array_fill(0, $rsPolyCount - 1, null);
@@ -603,7 +603,6 @@ class QRCode{
}
$data = array_fill(0, $totalCodeCount, null);
- $index = 0;
for($i = 0; $i < $maxDcCount; $i++){
for($r = 0; $r < $rsBlockCount; $r++){
@@ -681,7 +680,7 @@ class QRCode{
for($r = 0; $r < $this->moduleCount; $r++){
for($c = 0; $c < $this->moduleCount; $c++){
- if($this->isDark($r, $c)){
+ if($this->modules[$r][$c]){
// update $black to $fgc
imagefilledrectangle($image,
@@ -707,7 +706,7 @@ class QRCode{
$html .= '';
for($col = 0; $col < $this->moduleCount; $col++){
- $html .= ' | ';
+ $html .= ' | ';
}
$html .= '
';
diff --git a/src/RSBlock.php b/src/RSBlock.php
index 295cbce60..797c98f3b 100644
--- a/src/RSBlock.php
+++ b/src/RSBlock.php
@@ -101,6 +101,16 @@ class RSBlock{
];
+ /**
+ * @var array
+ */
+ protected $RSBLOCK = [
+ QRConst::ERROR_CORRECT_LEVEL_L => 0,
+ QRConst::ERROR_CORRECT_LEVEL_M => 1,
+ QRConst::ERROR_CORRECT_LEVEL_Q => 2,
+ QRConst::ERROR_CORRECT_LEVEL_H => 3,
+ ];
+
/**
* @param $typeNumber
* @param $errorCorrectLevel
@@ -110,16 +120,11 @@ class RSBlock{
*/
public function getRSBlocks($typeNumber, $errorCorrectLevel){
- switch($errorCorrectLevel){
- case QRConst::ERROR_CORRECT_LEVEL_L: $rsBlock = 0; break;
- case QRConst::ERROR_CORRECT_LEVEL_M: $rsBlock = 1; break;
- case QRConst::ERROR_CORRECT_LEVEL_Q: $rsBlock = 2; break;
- case QRConst::ERROR_CORRECT_LEVEL_H: $rsBlock = 3; break;
- default:
- throw new QRCodeException('$typeNumber:'.$typeNumber.'/$errorCorrectLevel:'.$errorCorrectLevel);
+ if(!isset($this->RSBLOCK[$errorCorrectLevel])){
+ throw new QRCodeException('$typeNumber: '.$typeNumber.' / $errorCorrectLevel: '.$errorCorrectLevel.PHP_EOL.print_r($this->RSBLOCK, true));
}
- $rsBlock = $this->BLOCK_TABLE[($typeNumber - 1) * 4 + $rsBlock];
+ $rsBlock = $this->BLOCK_TABLE[($typeNumber - 1) * 4 + $this->RSBLOCK[$errorCorrectLevel]];
$list = [];
$length = count($rsBlock) / 3;
@@ -136,5 +141,4 @@ class RSBlock{
return $list;
}
-
}
diff --git a/src/Util.php b/src/Util.php
index 7445fabe7..e919813dc 100644
--- a/src/Util.php
+++ b/src/Util.php
@@ -11,11 +11,6 @@
namespace codemasher\QRCode;
-use codemasher\QRCode\Polynomial;
-use codemasher\QRCode\QRCode;
-use codemasher\QRCode\QRConst;
-use codemasher\QRCode\QRCodeException;
-
/**
* Class Util
*/
@@ -87,39 +82,47 @@ class Util{
[6, 30, 58, 86, 114, 142, 170],
];
+ /**
+ * @var array
+ */
+ protected $ERROR_CORRECT_LEVEL = [
+ QRConst::ERROR_CORRECT_LEVEL_L => 0,
+ QRConst::ERROR_CORRECT_LEVEL_M => 1,
+ QRConst::ERROR_CORRECT_LEVEL_Q => 2,
+ QRConst::ERROR_CORRECT_LEVEL_H => 3,
+ ];
+
+ /**
+ * @var array
+ */
+ protected $MODE = [
+ QRConst::MODE_NUMBER => 0,
+ QRConst::MODE_ALPHANUM => 1,
+ QRConst::MODE_BYTE => 2,
+ QRConst::MODE_KANJI => 3,
+ ];
+
/**
* @param int $typeNumber
* @param int $mode
- * @param int $errorCorrectLevel
+ * @param int $ecLevel
*
* @return mixed
* @throws \codemasher\QRCode\QRCodeException
*/
- public function getMaxLength($typeNumber, $mode, $errorCorrectLevel){
- $_type = $typeNumber - 1;
+ public function getMaxLength($typeNumber, $mode, $ecLevel){
- 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);
+ if(!isset($this->ERROR_CORRECT_LEVEL[$ecLevel])){
+ throw new QRCodeException('$_err: '.$ecLevel);
}
- switch($mode){
- case QRConst::MODE_NUMBER : $_mode = 0; break;
- case QRConst::MODE_ALPHANUM: $_mode = 1; break;
- case QRConst::MODE_BYTE: $_mode = 2; break;
- case QRConst::MODE_KANJI : $_mode = 3; break;
- default :
- throw new QRCodeException('$_mode: '.$mode);
+ if(!isset($this->MODE[$mode])){
+ throw new QRCodeException('$_mode: '.$mode);
}
- return $this->MAX_LENGTH[$_type][$_err][$_mode];
+ return $this->MAX_LENGTH[$typeNumber - 1][$this->ERROR_CORRECT_LEVEL[$ecLevel]][$this->MODE[$mode]];
}
-
/**
* @param $s
*
@@ -127,17 +130,11 @@ class Util{
*/
public function getMode($s){
- if($this->isAlphaNum($s)){
- if($this->isNumber($s)){
- return QRConst::MODE_NUMBER;
- }
- return QRConst::MODE_ALPHANUM;
- }
- else if($this->isKanji($s)){
- return QRConst::MODE_KANJI;
- }
- else{
- return QRConst::MODE_BYTE;
+ switch(true){
+ case $this->isAlphaNum($s): return $this->isNumber($s) ? QRConst::MODE_NUMBER : QRConst::MODE_ALPHANUM;
+ case $this->isKanji($s) : return QRConst::MODE_KANJI;
+ default:
+ return QRConst::MODE_BYTE;
}
}
@@ -262,9 +259,9 @@ class Util{
*/
public function hex2rgb($hex = 0x0){
return [
- 'r' => floor($hex / 65536),
- 'g' => floor($hex / 256) % 256,
- 'b' => $hex % 256,
+ 'r' => floor($hex / 65536),
+ 'g' => floor($hex / 256) % 256,
+ 'b' => $hex % 256,
];
}