This commit is contained in:
smiley
2015-12-04 04:34:00 +01:00
parent 02b289e366
commit 8671a6936a
14 changed files with 408 additions and 434 deletions
+2 -4
View File
@@ -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;
}
+1 -1
View File
@@ -31,5 +31,5 @@ echo '<style>
</style>';
// 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();
+1 -1
View File
@@ -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');
+11 -2
View File
@@ -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;
}
}
+18 -23
View File
@@ -4,7 +4,7 @@
*
* @filesource AlphaNum.php
* @created 25.11.2015
* @package codemasher\QRCode
* @package codemasher\QRCode\Data
* @author Smiley <smiley@chillerlan.net>
* @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);
}
}
}
+1 -4
View File
@@ -4,7 +4,7 @@
*
* @filesource Byte.php
* @created 25.11.2015
* @package codemasher\QRCode
* @package codemasher\QRCode\Data
* @author Smiley <smiley@chillerlan.net>
* @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;
/**
*
+1 -3
View File
@@ -4,7 +4,7 @@
*
* @filesource Kanji.php
* @created 25.11.2015
* @package codemasher\QRCode
* @package codemasher\QRCode\Data
* @author Smiley <smiley@chillerlan.net>
* @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;
+1 -3
View File
@@ -4,7 +4,7 @@
*
* @filesource Number.php
* @created 26.11.2015
* @package codemasher\QRCode
* @package QRCode
* @author Smiley <smiley@chillerlan.net>
* @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;
+17 -14
View File
@@ -4,7 +4,7 @@
*
* @filesource QRDataBase.php
* @created 25.11.2015
* @package codemasher\QRCode
* @package codemasher\QRCode\Data
* @author Smiley <smiley@chillerlan.net>
* @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');
}
}
+1
View File
@@ -4,6 +4,7 @@
*
* @filesource QRDataInterface.php
* @created 01.12.2015
* @package codemasher\QRCode\Data
* @author Smiley <smiley@chillerlan.net>
* @copyright 2015 Smiley
* @license MIT
+14 -39
View File
@@ -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];
+292 -293
View File
@@ -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 .= '<tr>';
for($col = 0; $col < $this->moduleCount; $col++){
$html .= '<td class="'.($this->isDark($row, $col) ? 'dark' : 'light').'"></td>';
$html .= '<td class="'.($this->modules[$row][$col] ? 'dark' : 'light').'"></td>';
}
$html .= '</tr>';
+13 -9
View File
@@ -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;
}
}
+35 -38
View File
@@ -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,
];
}