Add a BSD licensed QR code library

This commit is contained in:
markjcrane 2018-08-30 07:30:39 +00:00
parent b42de823e8
commit 456789a024
15 changed files with 2414 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,81 @@
<?php
/**
* This file is part of the phpQr package
*
* See @see QRCode class for description of package and license.
*/
/**
* Import necessary dependencies
*/
require_once 'QRByte.php';
require_once 'QRMode.php';
/**
* This class provides the 8bit Byte implementaton of a QRByte
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
class QR8bitByte implements QRByte
{
/**
* The data
* @var array
*/
private $data;
/**
* The mode
* @var unknown
*/
private $mode;
/**
* Retrieve the mode
*
* @return int The mode
* @see QRByte::getMode()
*/
public function getMode()
{
return $this->mode;
}
/**
* Retrieve the length
*
* @return int The length
* @see QRByte::getLength()
*/
public function getLength()
{
return strlen($this->data);
}
/**
* Write data to byte
*
* @param QRBitBuffer $buffer The data to write into byte
*
* @see QRByte::write()
*/
public function write(QRBitBuffer $buffer)
{
for($i = 0; $i < strlen($this->data); $i++)
{
$buffer->put(ord($this->data[$i]), 8);
}
}
/**
* Create a new instance of a QR8bitByte
*
* @param array $data The data for the Byte
*/
public function __construct($data)
{
$this->data = $data;
$this->mode = QRMode::MODE_8BIT_BYTE;
}
}

View File

@ -0,0 +1,107 @@
<?php
/**
* This file is part of the phpQr package
*
* See @see QRCode class for description of package and license.
*/
/**
* QRBitBuffer class
*
* The purpose of this class is to act as data holder for QRCode.
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
class QRBitBuffer
{
/**
* Array based buffer access
*
* @var array
*/
private $buffer;
/**
* Length of array
*
* @var int
*/
private $length;
/**
* Create a new instance of QRBitBuffer
*/
public function __construct()
{
$this->buffer = array();
$this->length = 0;
}
/**
* Get particular bit given by index
*
* @param int $index The index of bit
* @return boolean
*/
public function get($index)
{
$bufIndex = floor($index / 8);
return ( ($this->buffer[$bufIndex] >> (7 - $index % 8) ) & 1) == 1;
}
/**
* Get the byte at particular index
*
* @param int $index The index of the byte
* @return string
*/
public function getAt($index)
{
return $this->buffer[$index];
}
/**
* Put amount of bits
* @param int $num The data to put
* @param int $length The length of data
*/
public function put($num, $length)
{
for($i = 0; $i < $length; $i++)
{
$this->putBit((($num >> ($length - $i - 1)) & 1) == 1);
}
}
/**
* Get current length in bits
*
* @return int The amount of bits
*/
public function getLengthInBits()
{
return $this->length;
}
/**
* Put particular bit
*
* @param int $bit The bit to put
*/
public function putBit($bit)
{
$bufIndex = floor($this->length / 8);
if(sizeof($this->buffer) <= $bufIndex)
{
array_push($this->buffer, 0);
}
if($bit)
{
$this->buffer[$bufIndex] |= (0x80 >> ($this->length % 8));
}
$this->length++;
}
}

View File

@ -0,0 +1,41 @@
<?php
/**
* This file is part of the phpQr package
*
* See @see QRCode class for description of package and license.
*/
/**
* Import necessary dependencies
*/
require_once 'QRBitBuffer.php';
/**
* This interface describes a QRByte implementation
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
interface QRByte
{
/**
* Retrieve the mode
*
* @return int The mode
*/
public function getMode();
/**
* Retrieve the length
*
* @return int The length
*/
public function getLength();
/**
* Write data to byte
*
* @param QRBitBuffer $buffer The data to write into byte
*/
public function write(QRBitBuffer $buffer);
}

View File

@ -0,0 +1,656 @@
<?php
/**
* This file provides the main QRCode class.
*
* The project is a rewrite from jQuery extension available at
* @link https://github.com/jeromeetienne/jquery-qrcode
*
* For a detailed description of QRCode and its features please check
* @link http://www.qrcode.com/
*
* QR Code is registered trademark of
* DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*
* All files in the package have the same license:
* http://opensource.org/licenses/BSD-2-Clause
*
* @copyright BSD2
* @author Maik Greubel <greubel@nkey.de>
*/
/**
* Import necessary dependencies
*/
require_once 'QR8bitByte.php';
require_once 'QRBitBuffer.php';
require_once 'QRRSBlock.php';
require_once 'QRUtil.php';
require_once 'QRCodeException.php';
/**
* This is the main class
*
* It provides the functionality to generate a QRCode bitmap
* out of appended data elements.
*
* @package phpQr
* @author Maik Greubel <greubel@nkey.de>
*/
class QRCode
{
/**
* Needed for padding
*
* @final
*
*/
const PAD0 = 0xec;
/**
* Needed for padding
*
* @final
*
*/
const PAD1 = 0x11;
/**
* The type number of qrcode
*
* @var int
*/
private $typeNumber;
/**
* Level of error correction
*
* @see QRErrorCorrectLevel
*
* @var int
*/
private $errorCorrectLevel;
/**
* Bitmap
*
* @var array
*/
private $modules;
/**
* Amount of modules in bitmap
*
* @var int
*/
private $moduleCount;
/**
* The data as array
*
* @var array
*/
private $dataCache;
/**
* All append data elements
*
* @var array
*/
private $dataList;
/**
* Create a new instance of QRCode
*
* @param int $typeNumber
* The type of QRCode
* @param int $errorCorrectLevel
* The error correction level
*/
public function __construct($typeNumber, $errorCorrectLevel)
{
$this->typeNumber = $typeNumber;
$this->errorCorrectLevel = $errorCorrectLevel;
$this->modules = null;
$this->moduleCount = 0;
$this->dataCache = null;
$this->dataList = array ();
}
/**
* This function is only needed for debugging purposes and returns the bitmap
* DONT USE THIS TO MANIPULATE THE BITMAP!
*
* @return array
*/
public function getModules()
{
return $this->modules;
}
/**
* Add a new data element to the QRCode
*
* @param string $data
*/
public function addData($data)
{
$newData = new QR8bitByte ( $data );
array_push ( $this->dataList, $newData );
$this->dataCache = null;
}
/**
* Returns whether a given bitmap entry is dark or not
*
* @param int $row
* The row in bitmap
* @param int $col
* The column in bitmap
*
* @throws QRCodeException
* @return true in case of its a dark bit, false otherwise
*/
public function isDark($row, $col)
{
if ($row < 0 || $this->moduleCount <= $row || $col < 0 || $this->moduleCount <= $col)
{
throw new QRCodeException ( "$row,$col" );
}
return $this->modules [$row] [$col];
}
/**
* Get the amount of modules in bitmap
*
* @return int
*/
public function getModuleCount()
{
return $this->moduleCount;
}
/**
* Generate the QRCode bitmap
*/
public function make()
{
if ($this->typeNumber < 1)
{
$typeNumber = 1;
for($typeNumber = 1; $typeNumber < 40; $typeNumber ++)
{
$rsBlocks = QRRSBlock::getInstance ()->getRSBlocks ( $typeNumber, $this->errorCorrectLevel );
$buffer = new QRBitBuffer ();
$totalDataCount = 0;
for($i = 0; $i < sizeof ( $rsBlocks ); $i ++)
{
$totalDataCount += $rsBlocks [$i]->getDataCount ();
}
for($i = 0; $i < sizeof ( $this->dataList ); $i ++)
{
$data = $this->dataList [$i];
assert ( $data instanceof QRByte );
$buffer->put ( $data->getMode (), 4 );
$buffer->put ( $data->getLength (), QRUtil::getInstance ()->getLengthInBits ( $data->getMode (), $typeNumber ) );
$data->write ( $buffer );
}
if ($buffer->getLengthInBits () <= $totalDataCount * 8)
break;
}
$this->typeNumber = $typeNumber;
}
$this->makeImpl ( false, $this->getBestMaskPattern () );
}
/**
* Generates the bitmap (really)
*
* @param boolean $test
* @param int $maskPattern
*/
private function makeImpl($test, $maskPattern)
{
$this->moduleCount = $this->typeNumber * 4 + 17;
$this->modules = QRUtil::getInstance ()->createEmptyArray ( $this->moduleCount );
for($row = 0; $row < $this->moduleCount; $row ++)
{
$this->modules [$row] = QRUtil::getInstance ()->createEmptyArray ( $this->moduleCount );
for($col = 0; $col < $this->moduleCount; $col ++)
{
$this->modules [$row] [$col] = null;
}
}
$this->setupPositionProbePattern ( 0, 0 );
$this->setupPositionProbePattern ( $this->moduleCount - 7, 0 );
$this->setupPositionProbePattern ( 0, $this->moduleCount - 7 );
$this->setupPositionAdjustPattern ();
$this->setupTimingPattern ();
$this->setupTypeInfo ( $test, $maskPattern );
if ($this->typeNumber >= 7)
{
$this->setTypeNumber ( $test );
}
if ($this->dataCache == null)
{
$this->dataCache = self::createData ( $this->typeNumber, $this->errorCorrectLevel, $this->dataList );
}
$this->mapData ( $this->dataCache, $maskPattern );
}
/**
* Add the position probes to the bitmap
*
* @param int $row
* @param int $col
*/
private function setupPositionProbePattern($row, $col)
{
for($r = - 1; $r <= 7; $r ++)
{
if ($row + $r <= - 1 || $this->moduleCount <= $row + $r)
continue;
for($c = - 1; $c <= 7; $c ++)
{
if ($col + $c <= - 1 || $this->moduleCount <= $col + $c)
continue;
if ((0 <= $r && $r <= 6 && ($c == 0 || $c == 6)) || (0 <= $c && $c <= 6 && ($r == 0 || $r == 6)) || (2 <= $r && $r <= 4 && 2 <= $c && $c <= 4))
{
$this->modules [$row + $r] [$col + $c] = true;
}
else
{
$this->modules [$row + $r] [$col + $c] = false;
}
}
}
}
/**
* Get the best mask pattern for this QRCode
*
* @return int
*/
private function getBestMaskPattern()
{
$minLostPoint = 0;
$pattern = 0;
for($i = 0; $i < 8; $i ++)
{
$this->makeImpl ( true, $i );
$lostPoint = QRUtil::getInstance ()->getLostPoint ( $this );
if ($i == 0 || $minLostPoint > $lostPoint)
{
$minLostPoint = $lostPoint;
$pattern = $i;
}
}
return $pattern;
}
/**
* Add the timing pattern to bitmap
*/
private function setupTimingPattern()
{
for($r = 8; $r < $this->moduleCount - 8; $r ++)
{
if ($this->modules [$r] [6] != null)
{
continue;
}
$this->modules [$r] [6] = ($r % 2 == 0);
}
for($c = 8; $c < $this->moduleCount - 8; $c ++)
{
if ($this->modules [6] [$c] != null)
{
continue;
}
$this->modules [6] [$c] = ($c % 2 == 0);
}
}
/**
* Add the position adjust pattern to bitmap
*/
private function setupPositionAdjustPattern()
{
$pos = QRUtil::getInstance ()->getPatternPosition ( $this->typeNumber );
for($i = 0; $i < sizeof ( $pos ); $i ++)
{
for($j = 0; $j < sizeof ( $pos ); $j ++)
{
$row = $pos [$i];
$col = $pos [$j];
if ($this->modules [$row] [$col] != null)
{
continue;
}
for($r = - 2; $r <= 2; $r ++)
{
for($c = - 2; $c <= 2; $c ++)
{
if ($r == - 2 || $r == 2 || $c == - 2 || $c == 2 || ($r == 0 && $c == 0))
{
$this->modules [$row + $r] [$col + $c] = true;
}
else
{
$this->modules [$row + $r] [$col + $c] = false;
}
}
}
}
}
}
/**
* Add the type number to bitmap
*
* @param boolean $test
*/
private function setTypeNumber($test)
{
$bits = QRUtil::getInstance ()->getBCHTypeNumber ( $this->typeNumber );
for($i = 0; $i < 18; $i ++)
{
$mod = (! $test && (($bits >> $i) & 1) == 1);
$this->modules [floor ( $i / 3 )] [$i % 3 + $this->moduleCount - 8 - 3] = $mod;
}
for($i = 0; $i < 18; $i ++)
{
$mod = (! $test && (($bits >> $i) & 1) == 1);
$this->modules [$i % 3 + $this->moduleCount - 8 - 3] [floor ( $i / 3 )] = $mod;
}
}
/**
* Add the type info to bitmap
*
* @param boolean $test
* @param int $maskPattern
*/
private function setupTypeInfo($test, $maskPattern)
{
$data = ($this->errorCorrectLevel << 3) | $maskPattern;
$bits = QRUtil::getInstance ()->getBCHTypeInfo ( $data );
// vertical
for($i = 0; $i < 15; $i ++)
{
$mod = (! $test && (($bits >> $i) & 1) == 1);
if ($i < 6)
{
$this->modules [$i] [8] = $mod;
}
else if ($i < 8)
{
$this->modules [$i + 1] [8] = $mod;
}
else
{
$this->modules [$this->moduleCount - 15 + $i] [8] = $mod;
}
}
// horizontal
for($i = 0; $i < 15; $i ++)
{
$mod = (! $test && (($bits >> $i) & 1) == 1);
if ($i < 8)
{
$this->modules [8] [$this->moduleCount - $i - 1] = $mod;
}
else if ($i < 9)
{
$this->modules [8] [15 - $i - 1 + 1] = $mod;
}
else
{
$this->modules [8] [15 - $i - 1] = $mod;
}
}
// fixed module
$this->modules [$this->moduleCount - 8] [8] = (! $test);
}
/**
* Add the data to bitmap
*
* @param array $data
* @param int $maskPattern
*/
private function mapData($data, $maskPattern)
{
$inc = - 1;
$row = $this->moduleCount - 1;
$bitIndex = 7;
$byteIndex = 0;
for($col = $this->moduleCount - 1; $col > 0; $col -= 2)
{
if ($col == 6)
$col --;
while ( true )
{
for($c = 0; $c < 2; $c ++)
{
if ($this->modules [$row] [$col - $c] === null)
{
$dark = false;
if ($byteIndex < sizeof ( $data ))
{
$dark = ((($data [$byteIndex] >> $bitIndex) & 1) == 1);
}
$mask = QRUtil::getInstance ()->getMask ( $maskPattern, $row, $col - $c );
if ($mask)
$dark = ! $dark;
$this->modules [$row] [$col - $c] = $dark;
$bitIndex --;
if ($bitIndex == - 1)
{
$byteIndex ++;
$bitIndex = 7;
}
}
}
$row += $inc;
if ($row < 0 || $this->moduleCount <= $row)
{
$row -= $inc;
$inc = - $inc;
break;
}
}
}
}
/**
* Create a bitmap out of all append data elements
*
* @param int $typeNumber
* @param int $errorCorrectLevel
* @param array $dataList
*
* @throws QRCodeException
*
* @return array
*/
private function createData($typeNumber, $errorCorrectLevel, $dataList)
{
$rsBlocks = QRRSBlock::getInstance ()->getRSBlocks ( $typeNumber, $errorCorrectLevel );
$buffer = new QRBitBuffer ();
for($i = 0; $i < sizeof ( $dataList ); $i ++)
{
$data = $dataList [$i];
assert ( $data instanceof QRByte );
$buffer->put ( $data->getMode (), 4 );
$buffer->put ( $data->getLength (), QRUtil::getInstance ()->getLengthInBits ( $data->getMode (), $typeNumber ) );
$data->write ( $buffer );
}
// calc num max data
$totalDataCount = 0;
for($i = 0; $i < sizeof ( $rsBlocks ); $i ++)
{
$totalDataCount += $rsBlocks [$i]->getDataCount ();
}
if ($buffer->getLengthInBits () > $totalDataCount * 8)
{
throw new QRCodeException ( "code length overflow (" . $buffer->getLengthInBits () . " > " . ($totalDataCount * 8) . ")" );
}
// end code
if ($buffer->getLengthInBits () + 4 <= $totalDataCount * 8)
{
$buffer->put ( 0, 4 );
}
// padding
while ( $buffer->getLengthInBits () % 8 != 0 )
{
$buffer->putBit ( false );
}
// padding
while ( true )
{
if ($buffer->getLengthInBits () >= $totalDataCount * 8)
{
break;
}
$buffer->put ( QRCode::PAD0, 8 );
if ($buffer->getLengthInBits () >= $totalDataCount * 8)
{
break;
}
$buffer->put ( QRCode::PAD1, 8 );
}
return $this->createBytes ( $buffer, $rsBlocks );
}
/**
* Create bitmap out of the bit buffer using reed solomon blocks
*
* @param QRBitBuffer $buffer
* @param array $rsBlocks
* @return array
*/
public function createBytes(QRBitBuffer $buffer, $rsBlocks)
{
$offset = 0;
$maxDcCount = 0;
$maxEcCount = 0;
$dcdata = QRUtil::getInstance ()->createEmptyArray ( sizeof ( $rsBlocks ) );
$ecdata = QRUtil::getInstance ()->createEmptyArray ( sizeof ( $rsBlocks ) );
for($r = 0; $r < sizeof ( $rsBlocks ); $r ++)
{
$dcCount = $rsBlocks [$r]->getDataCount ();
$ecCount = $rsBlocks [$r]->getTotalCount () - $dcCount;
$maxDcCount = max ( array (
$maxDcCount,
$dcCount
) );
$maxEcCount = max ( array (
$maxEcCount,
$ecCount
) );
$dcdata [$r] = QRUtil::getInstance ()->createEmptyArray ( $dcCount );
for($i = 0; $i < sizeof ( $dcdata [$r] ); $i ++)
{
$dcdata [$r] [$i] = 0xff & $buffer->getAt ( $i + $offset );
}
$offset += $dcCount;
$rsPoly = QRUtil::getInstance ()->getErrorCorrectPolynominal ( $ecCount );
$rawPoly = new QRPolynominal ( $dcdata [$r], $rsPoly->getLength () - 1 );
$modPoly = $rawPoly->mod ( $rsPoly );
$ecdata [$r] = QRUtil::getInstance ()->createEmptyArray ( $rsPoly->getLength () - 1 );
for($i = 0; $i < sizeof ( $ecdata [$r] ); $i ++)
{
$modIndex = $i + $modPoly->getLength () - sizeof ( $ecdata [$r] );
$ecdata [$r] [$i] = ($modIndex >= 0) ? $modPoly->get ( $modIndex ) : 0;
}
}
$totalCodeCount = 0;
for($i = 0; $i < sizeof ( $rsBlocks ); $i ++)
{
$totalCodeCount += $rsBlocks [$i]->getTotalCount ();
}
$data = QRUtil::getInstance ()->createEmptyArray ( $totalCodeCount );
$index = 0;
for($i = 0; $i < $maxDcCount; $i ++)
{
for($r = 0; $r < sizeof ( $rsBlocks ); $r ++)
{
if ($i < sizeof ( $dcdata [$r] ))
{
$data [$index ++] = $dcdata [$r] [$i];
}
}
}
for($i = 0; $i < $maxEcCount; $i ++)
{
for($r = 0; $r < sizeof ( $rsBlocks ); $r ++)
{
if ($i < sizeof ( $ecdata [$r] ))
{
$data [$index ++] = $ecdata [$r] [$i];
}
}
}
return $data;
}
}

View File

@ -0,0 +1,14 @@
<?php
/**
* This file is part of the phpQr package
*
* See @see QRCode class for description of package and license.
*/
/**
* A derived exception
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
class QRCodeException extends ErrorException {};

View File

@ -0,0 +1,187 @@
<?php
/**
* This file is part of the phpQr package
*
* See @see QRCode class for description of package and license.
*/
/**
* Import necessary dependencies
*/
require_once 'QRCodeException.php';
/**
* Derived exception class
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
class QRCodeImageException extends QRCodeException{};
/**
* This class provides all needed functionality to create an image out of an QRCode bitmap
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
class QRCodeImage
{
/**
* The previously created QRCode
*
* @var QRCode
*/
private $qrcode;
/**
* The desired width of the destination image
*
* @var int
*/
private $width;
/**
* The desired height of the destination image
*
* @var int
*/
private $height;
/**
* Quality of the destination image
*
* @var int
*/
private $quality;
/**
* The image buffer provided by GD function imagecreate()
*
* @var resource
*/
private $img;
/**
* Create a new QRCodeImage instance
*
* @param QRCode $qrcode The previously created QRCode
* @param int $width The desired width of the destination image
* @param int $height The desired height of the destination image
* @param int $quality The desired quality of the destination image
*/
public function __construct(QRCode $qrcode, $width, $height, $quality = 90)
{
$this->qrcode = $qrcode;
$this->width = $width;
$this->height = $height;
$this->quality = $quality;
$this->img = null;
}
/**
* Draw the image
*/
public function draw()
{
$moduleCount = $this->qrcode->getModuleCount();
$tileWidth = $this->width / $moduleCount;
$tileHeight = $this->height / $moduleCount;
$this->img = imagecreatetruecolor($this->width, $this->height);
if($this->img)
{
$fg = imagecolorallocate($this->img, 0, 0, 0);
if($fg === false)
{
$this->finish();
throw new QRCodeImageException('Could not allocate foreground color!');
}
$bg = imagecolorallocate($this->img, 255, 255, 255);
if($bg === false)
{
$this->finish();
throw new QRCodeImageException('Could not allocate background color!');
}
for($row = 0; $row < $moduleCount; $row++)
{
for($col = 0; $col < $moduleCount; $col++)
{
$fillStyle = $this->qrcode->isDark($row, $col) ? $fg : $bg;
$x = round($col * $tileWidth);
$y = round($row * $tileHeight);
$w = (ceil(($col + 1) * $tileWidth) - floor($col * $tileWidth));
if($x + $w > $this->width)
{
$w = $this->width - $x;
}
$h = (ceil(($row + 1) * $tileWidth) - floor($row * $tileWidth));
if($y + $h > $this->height)
{
$h = $this->height - $y;
}
if(!imagefilledrectangle($this->img, $x, $y, $x + $w, $y + $h, $fillStyle))
{
$this->finish();
throw new QRCodeImageException(sprintf('Could not fill the rectangle using desired coordinates (x = %d, y = %d, w = %d, h = %d, c = %d)',
$x, $y, $w, $h, $fillStyle));
}
}
}
}
else
{
throw new QRCodeImageException('Could not create true color image buffer!');
}
}
/**
* Store the image
*
* @param string $filename
*/
public function store($filename)
{
if($this->img)
{
if(!imagejpeg($this->img, $filename, $this->quality))
{
throw new QRCodeImageException(sprintf('Could not save image to file %s', $filename));
}
}
}
/**
* Return the image as string
*/
public function getImage()
{
if($this->img)
{
ob_start();
if(!imagejpeg($this->img, null, $this->quality))
{
ob_end_flush();
throw new QRCodeImageException('Could not create a jpeg out of the image buffer!');
}
$out = ob_get_clean();
return $out;
}
throw new QRCodeImageException('No image data available!');
}
/**
* Clean the image buffer
*/
public function finish()
{
if($this->img)
{
imagedestroy($this->img);
$this->img = null;
}
}
}

View File

@ -0,0 +1,20 @@
<?php
/**
* This file is part of the phpQr package
*
* See @see QRCode class for description of package and license.
*/
/**
* Error correct level enumeration
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
abstract class QRErrorCorrectLevel
{
const L = 1;
const M = 0;
const Q = 3;
const H = 2;
}

View File

@ -0,0 +1,145 @@
<?php
/**
* This file is part of the phpQr package
*
* See @see QRCode class for description of package and license.
*/
/**
* Import necessary dependencies
*/
require_once 'QRCodeException.php';
/**
* Derived exception
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
class QRMathException extends QRCodeException
{
}
/**
* QR Code math helper class
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
final class QRMath
{
/**
* Exponent table
*
* @var array
*/
private $EXP_TABLE = null;
/**
* Logarithm table
*
* @var array
*/
private $LOG_TABLE = null;
/**
* Singleton pattern
*
* @var QRMath
*/
private static $instance = null;
/**
* Singleton pattern
*
* @return QRMath Singleton
*/
public static function getInstance()
{
if (! self::$instance)
{
self::$instance = new self ();
}
return self::$instance;
}
/**
* Create a new instance of QRMath
*/
private function __construct()
{
$this->init ();
}
/**
* Initialize the tables
*/
private function init()
{
$this->EXP_TABLE = array ();
for($i = 0; $i < 8; $i ++)
{
$this->EXP_TABLE [$i] = 1 << $i;
}
for($i = 8; $i < 256; $i ++)
{
$this->EXP_TABLE [$i] = $this->EXP_TABLE [$i - 4] ^ $this->EXP_TABLE [$i - 5] ^ $this->EXP_TABLE [$i - 6] ^ $this->EXP_TABLE [$i - 8];
}
$this->LOG_TABLE = array ();
for($i = 0; $i < 255; $i ++)
{
$this->LOG_TABLE [$this->EXP_TABLE [$i]] = $i;
}
}
/**
* Get logarithm of n
*
* @param int $n
* @throws QRMathException
* @return int
*/
public function glog($n)
{
if ($n < 1)
{
throw new QRMathException ( "glog(" . $n . ")" );
}
foreach ( $this->LOG_TABLE as $key => $value )
{
if ($key == $n)
return $value;
}
throw new QRMathException ( "glog($n)" );
}
/**
* Get the exponent of n
*
* @param int $n
* @return int
*/
public function gexp($n)
{
while ( $n < 0 )
{
$n += 255;
}
while ( $n >= 256 )
{
$n -= 255;
}
foreach ( $this->EXP_TABLE as $key => $value )
{
if ($key == $n)
return $value;
}
throw new QRMathException ( "gexp($n)" );
}
}

View File

@ -0,0 +1,20 @@
<?php
/**
* This file is part of the phpQr package
*
* See @see QRCode class for description of package and license.
*/
/**
* QRCode mode enumeration
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
abstract class QRMode
{
const MODE_NUMBER = 1;
const MODE_ALPHA_NUM = 2;
const MODE_8BIT_BYTE = 4;
const MODE_KANJI = 8;
}

View File

@ -0,0 +1,145 @@
<?php
/**
* This file is part of the phpQr package
*
* See @see QRCode class for description of package and license.
*/
/**
* Import necessary dependencies
*/
require_once 'QRUtil.php';
require_once 'QRMath.php';
require_once 'QRCodeException.php';
/**
* Derived exception
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
class QRPolynominalException extends QRCodeException{};
/**
* The purpose of this class is to provide a polynominal implementation for the QRCode package
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
class QRPolynominal
{
/**
* Bitmap
*
* @var array
*/
private $num;
/**
* Create a new QRPolynominal instance
*
* @param array $num
* @param int $shift
*
* @throws QRPolynominalException
*/
public function __construct($num, $shift)
{
if(sizeof($num) == 0)
{
throw new QRPolynominalException("Invalid num size");
}
$offset = 0;
while($offset < sizeof($num) && $num[$offset] == 0)
{
$offset++;
}
$this->num = QRUtil::getInstance()->createEmptyArray(sizeof($num) - $offset + $shift);
for($i = 0; $i < sizeof($num) - $offset; $i++)
{
$this->num[$i] = $num[$i + $offset];
}
}
/**
* Get a particular bitmap index
*
* @param int $index
* @return multitype:
*/
public function get($index)
{
return $this->num[$index];
}
/**
* Get the length of bitmap
*/
public function getLength()
{
return sizeof($this->num);
}
/**
* Multiply another polynom against this
*
* @param QRPolynominal $e The other
* @return QRPolynominal The multiplied result
*/
public function multiply(QRPolynominal $e)
{
$num = QRUtil::getInstance()->createEmptyArray($this->getLength() + $e->getLength() - 1);
for($i = 0; $i < $this->getLength(); $i++)
{
for($j = 0; $j < $e->getLength(); $j++)
{
$a = QRMath::getInstance()->glog($this->get($i));
$b = QRMath::getInstance()->glog($e->get($j));
$base = 0;
if(isset($num[$i + $j]))
$base = $num[$i + $j];
$num[$i + $j] = $base ^ QRMath::getInstance()->gexp( $a + $b );
}
}
return new QRPolynominal($num, 0);
}
/**
* Perform modulus against another polynom
*
* @param QRPolynominal $e
*
* @return QRPolynominal
*/
public function mod(QRPolynominal $e)
{
if($this->getLength() - $e->getLength() < 0)
{
return $this;
}
$ratio = QRMath::getInstance()->glog($this->get(0)) - QRMath::getInstance()->glog($e->get(0));
$num = QRUtil::getInstance()->createEmptyArray($this->getLength());
for($i = 0; $i < $this->getLength(); $i++)
{
$num[$i] = $this->get($i);
}
for($i = 0; $i < $e->getLength(); $i++)
{
$num[$i] ^= QRMath::getInstance()->gexp(QRMath::getInstance()->glog($e->get($i)) + $ratio);
}
$result = new QRPolynominal($num, 0);
$result = $result->mod($e);
return $result;
}
}

View File

@ -0,0 +1,503 @@
<?php
/**
* This file is part of the phpQr package
*
* See @see QRCode class for description of package and license.
*/
/**
* Import necessary dependencies
*/
require_once 'QRCodeException.php';
require_once 'QRErrorCorrectLevel.php';
/**
* Derived exception class
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
class QRRSBlockException extends QRCodeException
{
}
/**
* This class is a Reed-Solomon implementation for the QRCode.
* The purpose is to provide error correction and block information.
*
* Inspired by qrcode.js from https://github.com/jeromeetienne/jquery-qrcode
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
* @link http://www.thonky.com/qr-code-tutorial/error-correction-table/
*/
class QRRSBlock
{
/**
* The total count of blocks
*
* @var int The total count of blocks
*/
private $totalCount;
/**
* The data count of blocks
*
* @var int The data count of blocks
*/
private $dataCount;
/**
* The block table
* @var array The block table
*/
private $RS_BLOCK_TABLE;
/**
* Singleton pattern
*
* @var QRRSBlock Singleton
*/
private static $instance;
/**
* The serialized block data for faster initialization
*
* @var string
*/
private $blockFileName = 'rsblock.dat';
/**
* Singleton pattern
*
* @return QRRSBlock
*/
public static function getInstance()
{
if(!self::$instance)
{
self::$instance = new self(0, 0);
}
return self::$instance;
}
/**
* Retrieve the data count
*
* @return int The data count
*/
public function getDataCount()
{
return $this->dataCount;
}
/**
* Retrieve the total count
*
* @return int The total count
*/
public function getTotalCount()
{
return $this->totalCount;
}
/**
* Create a new QR Reed-Solomon block instance
*
* @param int $totalCount The total count of blocks
* @param int $dataCount The data count of blocks
*/
private function __construct($totalCount, $dataCount)
{
$this->initRsBlock();
$this->totalCount = $totalCount;
$this->dataCount = $dataCount;
}
/**
* Get rs blocks of particular type and error correction level
*
* @param int $typeNumber
* @param int $errorCorrectLevel
* @throws QRRSBlockException
* @return QRRSBlock
*/
public function getRSBlocks($typeNumber, $errorCorrectLevel)
{
$rsBlock = $this->getRsBlockTable($typeNumber, $errorCorrectLevel);
if(!$rsBlock)
{
throw new QRRSBlockException("Bad RS Block at type number " . $typeNumber . " / error correct level " . $errorCorrectLevel);
}
$length = sizeof($rsBlock) / 3;
$list = array();
for($i = 0; $i < $length; $i++)
{
$count = $rsBlock[$i * 3 + 0];
$totalCount = $rsBlock[$i * 3 + 1];
$dataCount = $rsBlock[$i * 3 + 2];
for($j = 0; $j < $count; $j++)
{
array_push($list, new QRRSBlock($totalCount, $dataCount));
}
}
return $list;
}
/**
* Get the reed-solomon block table
*
* @param int $typeNumber
* @param int $errorCorrectLevel
* @return int|NULL
*/
public function getRsBlockTable($typeNumber, $errorCorrectLevel)
{
switch ($errorCorrectLevel)
{
case QRErrorCorrectLevel::L:
return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 0];
case QRErrorCorrectLevel::M:
return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 1];
case QRErrorCorrectLevel::Q:
return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 2];
case QRErrorCorrectLevel::H:
return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 3];
default:
return null;
}
}
/**
* This method initialize the RS block
*/
private function initRsBlock()
{
if($this->loadBlockFile())
{
return;
}
$this->RS_BLOCK_TABLE = array();
// L
// M
// Q
// H
// 1
$this->addRsBlock(array(1, 26, 19));
$this->addRsBlock(array(1, 26, 16));
$this->addRsBlock(array(1, 26, 13));
$this->addRsBlock(array(1, 26, 9));
// 2
$this->addRsBlock(array(1, 44, 34));
$this->addRsBlock(array(1, 44, 28));
$this->addRsBlock(array(1, 44, 22));
$this->addRsBlock(array(1, 44, 16));
// 3
$this->addRsBlock(array(1, 70, 55));
$this->addRsBlock(array(1, 70, 44));
$this->addRsBlock(array(2, 35, 17));
$this->addRsBlock(array(2, 35, 13));
// 4
$this->addRsBlock(array(1, 100, 80));
$this->addRsBlock(array(2, 50, 32));
$this->addRsBlock(array(2, 50, 24));
$this->addRsBlock(array(4, 25, 9));
// 5
$this->addRsBlock(array(1, 134, 108));
$this->addRsBlock(array(2, 67, 43));
$this->addRsBlock(array(2, 33, 15, 2, 34, 16));
$this->addRsBlock(array(2, 33, 11, 2, 34, 12));
// 6
$this->addRsBlock(array(2, 86, 68));
$this->addRsBlock(array(4, 43, 27));
$this->addRsBlock(array(4, 43, 19));
$this->addRsBlock(array(4, 43, 15));
// 7
$this->addRsBlock(array(2, 98, 78));
$this->addRsBlock(array(4, 49, 31));
$this->addRsBlock(array(2, 32, 14, 4, 33, 15));
$this->addRsBlock(array(4, 39, 13, 1, 40, 14));
// 8
$this->addRsBlock(array(2, 121, 97));
$this->addRsBlock(array(2, 60, 38, 2, 61, 39));
$this->addRsBlock(array(4, 40, 18, 2, 41, 19));
$this->addRsBlock(array(4, 40, 14, 2, 41, 15));
// 9
$this->addRsBlock(array(2, 146, 116));
$this->addRsBlock(array(3, 58, 36, 2, 59, 37));
$this->addRsBlock(array(4, 36, 16, 4, 37, 17));
$this->addRsBlock(array(4, 36, 12, 4, 37, 13));
// 10
$this->addRsBlock(array(2, 86, 68, 2, 87, 69));
$this->addRsBlock(array(4, 69, 43, 1, 70, 44));
$this->addRsBlock(array(6, 43, 19, 2, 44, 20));
$this->addRsBlock(array(6, 43, 15, 2, 44, 16));
// 11
$this->addRsBlock(array(4, 101, 81));
$this->addRsBlock(array(1, 80, 50, 4, 81, 51));
$this->addRsBlock(array(4, 50, 22, 4, 51, 23));
$this->addRsBlock(array(3, 36, 12, 8, 37, 13));
// 12
$this->addRsBlock(array(2, 116, 92, 2, 117, 93));
$this->addRsBlock(array(6, 58, 36, 2, 59, 37));
$this->addRsBlock(array(4, 46, 20, 6, 47, 21));
$this->addRsBlock(array(7, 42, 14, 4, 43, 15));
// 13
$this->addRsBlock(array(4, 133, 107));
$this->addRsBlock(array(8, 59, 37, 1, 60, 38));
$this->addRsBlock(array(8, 44, 20, 4, 45, 21));
$this->addRsBlock(array(12, 33, 11, 4, 34, 12));
// 14
$this->addRsBlock(array(3, 145, 115, 1, 146, 116));
$this->addRsBlock(array(4, 64, 40, 5, 65, 41));
$this->addRsBlock(array(11, 36, 16, 5, 37, 17));
$this->addRsBlock(array(11, 36, 12, 5, 37, 13));
// 15
$this->addRsBlock(array(5, 109, 87, 1, 110, 88));
$this->addRsBlock(array(5, 65, 41, 5, 66, 42));
$this->addRsBlock(array(5, 54, 24, 7, 55, 25));
$this->addRsBlock(array(11, 36, 12));
// 16
$this->addRsBlock(array(5, 122, 98, 1, 123, 99));
$this->addRsBlock(array(7, 73, 45, 3, 74, 46));
$this->addRsBlock(array(15, 43, 19, 2, 44, 20));
$this->addRsBlock(array(3, 45, 15, 13, 46, 16));
// 17
$this->addRsBlock(array(1, 135, 107, 5, 136, 108));
$this->addRsBlock(array(10, 74, 46, 1, 75, 47));
$this->addRsBlock(array(1, 50, 22, 15, 51, 23));
$this->addRsBlock(array(2, 42, 14, 17, 43, 15));
// 18
$this->addRsBlock(array(5, 150, 120, 1, 151, 121));
$this->addRsBlock(array(9, 69, 43, 4, 70, 44));
$this->addRsBlock(array(17, 50, 22, 1, 51, 23));
$this->addRsBlock(array(2, 42, 14, 19, 43, 15));
// 19
$this->addRsBlock(array(3, 141, 113, 4, 142, 114));
$this->addRsBlock(array(3, 70, 44, 11, 71, 45));
$this->addRsBlock(array(17, 47, 21, 4, 48, 22));
$this->addRsBlock(array(9, 39, 13, 16, 40, 14));
// 20
$this->addRsBlock(array(3, 135, 107, 5, 136, 108));
$this->addRsBlock(array(3, 67, 41, 13, 68, 42));
$this->addRsBlock(array(15, 54, 24, 5, 55, 25));
$this->addRsBlock(array(15, 43, 15, 10, 44, 16));
// 21
$this->addRsBlock(array(4, 144, 116, 4, 145, 117));
$this->addRsBlock(array(17, 68, 42));
$this->addRsBlock(array(17, 50, 22, 6, 51, 23));
$this->addRsBlock(array(19, 46, 16, 6, 47, 17));
// 22
$this->addRsBlock(array(2, 139, 111, 7, 140, 112));
$this->addRsBlock(array(17, 74, 46));
$this->addRsBlock(array(7, 54, 24, 16, 55, 25));
$this->addRsBlock(array(34, 37, 13));
// 23
$this->addRsBlock(array(4, 151, 121, 5, 152, 122));
$this->addRsBlock(array(4, 75, 47, 14, 76, 48));
$this->addRsBlock(array(11, 54, 24, 14, 55, 25));
$this->addRsBlock(array(16, 45, 15, 14, 46, 16));
// 24
$this->addRsBlock(array(6, 147, 117, 4, 148, 118));
$this->addRsBlock(array(6, 73, 45, 14, 74, 46));
$this->addRsBlock(array(11, 54, 24, 16, 55, 25));
$this->addRsBlock(array(30, 46, 16, 2, 47, 17));
// 25
$this->addRsBlock(array(8, 132, 106, 4, 133, 107));
$this->addRsBlock(array(8, 75, 47, 13, 76, 48));
$this->addRsBlock(array(7, 54, 24, 22, 55, 25));
$this->addRsBlock(array(22, 45, 15, 13, 46, 16));
// 26
$this->addRsBlock(array(10, 142, 114, 2, 143, 115));
$this->addRsBlock(array(19, 74, 46, 4, 75, 47));
$this->addRsBlock(array(28, 50, 22, 6, 51, 23));
$this->addRsBlock(array(33, 46, 16, 4, 47, 17));
// 27
$this->addRsBlock(array(8, 152, 122, 4, 153, 123));
$this->addRsBlock(array(22, 73, 45, 3, 74, 46));
$this->addRsBlock(array(8, 53, 23, 26, 54, 24));
$this->addRsBlock(array(12, 45, 15, 28, 46, 16));
// 28
$this->addRsBlock(array(3, 147, 117, 10, 148, 118));
$this->addRsBlock(array(3, 73, 45, 23, 74, 46));
$this->addRsBlock(array(4, 54, 24, 31, 55, 25));
$this->addRsBlock(array(11, 45, 15, 31, 46, 16));
// 29
$this->addRsBlock(array(7, 146, 116, 7, 147, 117));
$this->addRsBlock(array(21, 73, 45, 7, 74, 46));
$this->addRsBlock(array(1, 53, 23, 37, 54, 24));
$this->addRsBlock(array(19, 45, 15, 26, 46, 16));
// 30
$this->addRsBlock(array(5, 145, 115, 10, 146, 116));
$this->addRsBlock(array(19, 75, 47, 10, 76, 48));
$this->addRsBlock(array(15, 54, 24, 25, 55, 25));
$this->addRsBlock(array(23, 45, 15, 25, 46, 16));
// 31
$this->addRsBlock(array(13, 145, 115, 3, 146, 116));
$this->addRsBlock(array(2, 74, 46, 29, 75, 47));
$this->addRsBlock(array(42, 54, 24, 1, 55, 25));
$this->addRsBlock(array(23, 45, 15, 28, 46, 16));
// 32
$this->addRsBlock(array(17, 145, 115));
$this->addRsBlock(array(10, 74, 46, 23, 75, 47));
$this->addRsBlock(array(42, 54, 24, 1, 55, 25));
$this->addRsBlock(array(23, 45, 15, 28, 46, 16));
// 33
$this->addRsBlock(array(17, 145, 115, 1, 146, 116));
$this->addRsBlock(array(14, 74, 46, 21, 75, 47));
$this->addRsBlock(array(29, 54, 24, 19, 55, 25));
$this->addRsBlock(array(11, 45, 15, 46, 46, 16));
// 34
$this->addRsBlock(array(13, 145, 115, 6, 146, 116));
$this->addRsBlock(array(14, 74, 46, 21, 75, 47));
$this->addRsBlock(array(44, 54, 24, 7, 55, 25));
$this->addRsBlock(array(59, 46, 16, 1, 47, 17));
// 35
$this->addRsBlock(array(12, 151, 121, 7, 152, 122));
$this->addRsBlock(array(12, 75, 47, 26, 76, 48));
$this->addRsBlock(array(39, 54, 24, 14, 55, 25));
$this->addRsBlock(array(22, 45, 15, 41, 46, 16));
// 36
$this->addRsBlock(array(6, 151, 121, 14, 152, 122));
$this->addRsBlock(array(6, 75, 47, 34, 76, 48));
$this->addRsBlock(array(46, 54, 24, 10, 55, 25));
$this->addRsBlock(array(2, 45, 15, 64, 46, 16));
// 37
$this->addRsBlock(array(17, 152, 122, 4, 153, 123));
$this->addRsBlock(array(29, 74, 46, 14, 75, 47));
$this->addRsBlock(array(49, 54, 24, 10, 55, 25));
$this->addRsBlock(array(24, 45, 15, 46, 46, 16));
// 38
$this->addRsBlock(array(4, 152, 122, 18, 153, 123));
$this->addRsBlock(array(13, 74, 46, 32, 75, 47));
$this->addRsBlock(array(48, 54, 24, 14, 55, 25));
$this->addRsBlock(array(42, 45, 15, 32, 46, 16));
// 39
$this->addRsBlock(array(20, 147, 117, 4, 148, 118));
$this->addRsBlock(array(40, 75, 47, 7, 76, 48));
$this->addRsBlock(array(43, 54, 24, 22, 55, 25));
$this->addRsBlock(array(10, 45, 15, 67, 46, 16));
// 40
$this->addRsBlock(array(19, 148, 118, 6, 149, 119));
$this->addRsBlock(array(18, 75, 47, 31, 76, 48));
$this->addRsBlock(array(34, 54, 24, 34, 55, 25));
$this->addRsBlock(array(20, 45, 15, 61, 46, 16));
$this->saveBlockFile();
}
/**
* Add a new block information to the block
*
* @param array $block
*/
private function addRsBlock($block)
{
array_push($this->RS_BLOCK_TABLE, $block);
}
/**
* Return the absolute path to the block file
* @return string
*/
private function getBlockFileAbsolute()
{
return sprintf("%s%s%s", dirname(__FILE__), DIRECTORY_SEPARATOR, $this->blockFileName);
}
/**
* Try to load the block file
*
* @return boolean
*/
private function loadBlockFile()
{
$file = $this->getBlockFileAbsolute();
if(!file_exists($file))
{
return false;
}
$serialized = file_get_contents($file);
if(!$serialized)
{
return false;
}
$this->RS_BLOCK_TABLE = unserialize($serialized);
if(!$this->RS_BLOCK_TABLE)
{
return false;
}
return true;
}
/**
* Try to save the block file
*/
private function saveBlockFile()
{
$file = $this->getBlockFileAbsolute();
if(file_exists($file))
{
unlink($file);
}
file_put_contents($file, serialize($this->RS_BLOCK_TABLE));
}
}

View File

@ -0,0 +1,464 @@
<?php
/**
* This file is part of the phpQr package
*
* See @see QRCode class for description of package and license.
*/
/**
* Import necessary dependencies
*/
require_once 'QRCodeException.php';
require_once 'QRPolynominal.php';
/**
* Derived exception
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
class QRUtilException extends QRCodeException {};
/**
* Mask pattern enumeration
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
abstract class QRMaskPattern
{
const PATTERN000 = 0;
const PATTERN001 = 1;
const PATTERN010 = 2;
const PATTERN011 = 3;
const PATTERN100 = 4;
const PATTERN101 = 5;
const PATTERN110 = 6;
const PATTERN111 = 7;
}
/**
* The purpose of this class is to provide some common utility
* functionality for the QRCode class and its parts.
*
* @author Maik Greubel <greubel@nkey.de>
* @package phpQr
*/
class QRUtil
{
/**
* Pattern position table
*
* @var array
*/
private $PATTERN_POSITION_TABLE = null;
/**
*
* @var int G15 pattern
*/
private $G15;
/**
*
* @var int G18 pattern
*/
private $G18;
/**
*
* @var int G15 mask pattern
*/
private $G15_MASK;
/**
*
* @var QRUtil Singleton
*/
private static $instance;
/**
* Singleton pattern
*
* @return QRUtil
*/
public static function getInstance()
{
if(!self::$instance)
{
self::$instance = new self;
}
return self::$instance;
}
/**
* Create a new instance of QRUtil
*/
private function __construct()
{
$this->init();
$this->G15 = ((1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0));
$this->G18 = ((1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0));
$this->G15_MASK = ((1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1));
}
/**
* Retrieve the Bose-Chaudhuri-Hocquenghem code type info
*
* @param array $data
*
* @return int
*/
public function getBCHTypeInfo($data)
{
$d = $data << 10;
while($this->getBCHDigit($d) - $this->getBCHDigit($this->G15) >= 0)
{
$d ^= ($this->G15 << ($this->getBCHDigit($d) - $this->getBCHDigit($this->G15)));
}
return (($data << 10) | $d) ^ $this->G15_MASK;
}
/**
* Retrieve the Bose-Chaudhuri-Hocquenghem code type number
*
* @param array $data
*
* @return int
*/
public function getBCHTypeNumber($data)
{
$d = $data << 12;
while($this->getBCHDigit($d) - $this->getBCHDigit($this->G18) >= 0)
{
$d ^= ($this->G18 << ($this->getBCHDigit($d) - $this->getBCHDigit($this->G18)));
}
return ($data << 12) | $d;
}
/**
* Retrieve the Bose-Chaudhuri-Hocquenghem digit
*
* @param array $data
*
* @return int
*/
public function getBCHDigit($data)
{
$digit = 0;
while($data != 0)
{
$digit++;
$data = $data >> 1;
}
return $digit;
}
/**
* Return the pattern position
*
* @param int $typeNumber
* @return array
*/
public function getPatternPosition($typeNumber)
{
return $this->PATTERN_POSITION_TABLE[$typeNumber - 1];
}
/**
* Return whether to mask a bit
*
* @param int $maskPattern
* @param int $i
* @param int $j
* @throws QRUtilException
* @return boolean
*/
public function getMask($maskPattern, $i, $j)
{
switch($maskPattern)
{
case QRMaskPattern::PATTERN000: return ($i + $j) % 2 == 0;
case QRMaskPattern::PATTERN001: return ($i % 2) == 0;
case QRMaskPattern::PATTERN010: return ($j % 3) == 0;
case QRMaskPattern::PATTERN011: return ($i + $j) % 3 == 0;
case QRMaskPattern::PATTERN100: return (floor($i / 2) + floor($j / 3)) % 2 == 0;
case QRMaskPattern::PATTERN101: return ($i * $j) % 2 + ($i * $j) % 3 == 0;
case QRMaskPattern::PATTERN110: return (($i * $j) % 2 + ($i * $j) % 3) % 2 == 0;
case QRMaskPattern::PATTERN111: return (($i * $j) % 3 + ($i + $j) % 2) % 2 == 0;
default: throw new QRUtilException("Bad mask pattern " . $maskPattern);
}
}
/**
* Return error correction polynom
*
* @param int $errorCorrectLength
* @return QRPolynominal
*/
public function getErrorCorrectPolynominal($errorCorrectLength)
{
$a = new QRPolynominal(array(1), 0);
for($i = 0; $i < $errorCorrectLength; $i++)
{
$a = $a->multiply(new QRPolynominal(array(1, QRMath::getInstance()->gexp($i)), 0));
}
return $a;
}
/**
* Get the bitmap length in bits
*
* @param int $mode
* @param int $type
* @throws QRUtilException
* @return int
*/
public function getLengthInBits($mode, $type)
{
// 1 - 9
if(1 <= $type && $type < 10)
{
switch($mode)
{
case QRMode::MODE_NUMBER: return 10;
case QRMode::MODE_ALPHA_NUM: return 9;
case QRMode::MODE_8BIT_BYTE: return 8;
case QRMode::MODE_KANJI: return 8;
default: throw new QRUtilException("Bad mode " . $mode);
}
}
// 10 - 26
else if($type < 27)
{
switch($mode)
{
case QRMode::MODE_NUMBER: return 12;
case QRMode::MODE_ALPHA_NUM: return 11;
case QRMode::MODE_8BIT_BYTE: return 16;
case QRMode::MODE_KANJI: return 10;
default: throw new QRUtilException("Bad mode " . $mode);
}
}
// 27 - 40
else if($type < 41)
{
switch($mode)
{
case QRMode::MODE_NUMBER: return 14;
case QRMode::MODE_ALPHA_NUM: return 13;
case QRMode::MODE_8BIT_BYTE: return 16;
case QRMode::MODE_KANJI: return 12;
default: throw new QRUtilException("Bad mode " . $mode);
}
}
else
{
throw new QRUtilException("Bad type " . $type);
}
}
/**
* Calculate the lost point
*
* @param QRCode $qrCode
*
* @return number
*/
public function getLostPoint(QRCode $qrCode)
{
$moduleCount = $qrCode->getModuleCount();
$lostPoint = 0;
// Level 1
for($row = 0; $row < $moduleCount; $row++)
{
for($col = 0; $col < $moduleCount; $col++)
{
$sameCount = 0;
$dark = $qrCode->isDark($row, $col);
for($r = -1; $r <= 1; $r++)
{
if($row + $r < 0 || $moduleCount <= $row + $r)
{
continue;
}
for($c = -1; $c <= 1; $c++)
{
if($col + $c < 0 || $moduleCount <= $col + $c)
{
continue;
}
if($r == 0 && $c == 0)
{
continue;
}
if($dark == $qrCode->isDark($row + $r, $col + $c))
{
$sameCount++;
}
}
}
if($sameCount > 5)
{
$lostPoint += (3 + $sameCount - 5);
}
}
}
// Level 2
for($row = 0; $row < $moduleCount - 1; $row++)
{
for($col = 0; $col < $moduleCount - 1; $col++)
{
$count = 0;
if($qrCode->isDark($row, $col )) $count++;
if($qrCode->isDark($row + 1, $col )) $count++;
if($qrCode->isDark($row, $col + 1)) $count++;
if($qrCode->isDark($row + 1, $col + 1)) $count++;
if($count == 0 || $count == 4)
{
$lostPoint += 3;
}
}
}
// Level 3
for($row = 0; $row < $moduleCount; $row++)
{
for($col = 0; $col < $moduleCount - 6; $col++)
{
if($qrCode->isDark($row, $col)
&& !$qrCode->isDark($row, $col + 1)
&& $qrCode->isDark($row, $col + 2)
&& $qrCode->isDark($row, $col + 3)
&& $qrCode->isDark($row, $col + 4)
&& !$qrCode->isDark($row, $col + 5)
&& $qrCode->isDark($row, $col + 6))
{
$lostPoint += 40;
}
}
}
for($col = 0; $col < $moduleCount; $col++)
{
for($row = 0; $row < $moduleCount - 6; $row++)
{
if($qrCode->isDark($row, $col)
&& !$qrCode->isDark($row + 1, $col)
&& $qrCode->isDark($row + 2, $col)
&& $qrCode->isDark($row + 3, $col)
&& $qrCode->isDark($row + 4, $col)
&& !$qrCode->isDark($row + 5, $col)
&& $qrCode->isDark($row + 6, $col))
{
$lostPoint += 40;
}
}
}
// Level 4
$darkCount = 0;
for($col = 0; $col < $moduleCount; $col++)
{
for($row = 0; $row < $moduleCount; $row++)
{
if($qrCode->isDark($row, $col))
{
$darkCount++;
}
}
}
$ratio = abs(100 * $darkCount / $moduleCount / $moduleCount - 50) / 5;
$lostPoint += $ratio * 10;
return $lostPoint;
}
/**
* Initialize the pattern position table
*/
private function init()
{
$this->PATTERN_POSITION_TABLE = array();
$this->addPattern(array());
$this->addPattern(array(6, 18));
$this->addPattern(array(6, 22));
$this->addPattern(array(6, 26));
$this->addPattern(array(6, 30));
$this->addPattern(array(6, 34));
$this->addPattern(array(6, 22, 38));
$this->addPattern(array(6, 24, 42));
$this->addPattern(array(6, 26, 46));
$this->addPattern(array(6, 28, 50));
$this->addPattern(array(6, 30, 54));
$this->addPattern(array(6, 32, 58));
$this->addPattern(array(6, 34, 62));
$this->addPattern(array(6, 26, 46, 66));
$this->addPattern(array(6, 26, 48, 70));
$this->addPattern(array(6, 26, 50, 74));
$this->addPattern(array(6, 30, 54, 78));
$this->addPattern(array(6, 30, 56, 82));
$this->addPattern(array(6, 30, 58, 86));
$this->addPattern(array(6, 34, 62, 90));
$this->addPattern(array(6, 28, 50, 72, 94));
$this->addPattern(array(6, 26, 50, 74, 98));
$this->addPattern(array(6, 30, 54, 78, 102));
$this->addPattern(array(6, 28, 54, 80, 106));
$this->addPattern(array(6, 32, 58, 84, 110));
$this->addPattern(array(6, 30, 58, 86, 114));
$this->addPattern(array(6, 34, 62, 90, 118));
$this->addPattern(array(6, 26, 50, 74, 98, 122));
$this->addPattern(array(6, 30, 54, 78, 102, 126));
$this->addPattern(array(6, 26, 52, 78, 104, 130));
$this->addPattern(array(6, 30, 56, 82, 108, 134));
$this->addPattern(array(6, 34, 60, 86, 112, 138));
$this->addPattern(array(6, 30, 58, 86, 114, 142));
$this->addPattern(array(6, 34, 62, 90, 118, 146));
$this->addPattern(array(6, 30, 54, 78, 102, 126, 150));
$this->addPattern(array(6, 24, 50, 76, 102, 128, 154));
$this->addPattern(array(6, 28, 54, 80, 106, 132, 158));
$this->addPattern(array(6, 32, 58, 84, 110, 136, 162));
$this->addPattern(array(6, 26, 54, 82, 110, 138, 166));
$this->addPattern(array(6, 30, 58, 86, 114, 142, 170));
}
/**
* Add a pattern to the pattern position table
*
* @param array $d
*/
private function addPattern($d)
{
array_push($this->PATTERN_POSITION_TABLE, $d);
}
/**
* Create an empty array of n elements
*
* All elements are uninitialed.
*
* @param int $numElements
* @return array
*/
public function createEmptyArray($numElements)
{
return array_fill(0, $numElements, null);
}
}

View File

@ -0,0 +1,30 @@
phpQr - A QRCode encoder class
===
by Maik Greubel (greubel@nkey.de)
The intention to create a basic qrcode encoder for PHP was a request on phpclasses.org.
-
The author studied multiple sources to get an idea how qrcode implementation works under the hood.
The best source was the JavaScript library jquery which provides a HTML Table based implementation.
See source and documentation at <https://github.com/jeromeetienne/jquery-qrcode>
So the hard work was already done by Jerome Etienne. My part was only to port it to PHP code.
Currently this package does not have the capability to read QRCodes.
Further readings
-
For a detailed description of QRCode and its features please check <http://www.qrcode.com/>
Trademark information
-
QR Code is registered trademark of
DENSO WAVE INCORPORATED <http://www.denso-wave.com/qrcode/faqpatent-e.html>
License
-
All files in the package are published under the terms of the BSD 2 License. <http://opensource.org/licenses/BSD-2-Clause>

File diff suppressed because one or more lines are too long