From 456789a024da31e270c1c916ad221569f2aee818 Mon Sep 17 00:00:00 2001 From: markjcrane Date: Thu, 30 Aug 2018 07:30:39 +0000 Subject: [PATCH] Add a BSD licensed QR code library --- resources/qr_code/.phpqrcode.php.swp | Bin 0 -> 12288 bytes resources/qr_code/QR8bitByte.php | 81 +++ resources/qr_code/QRBitBuffer.php | 107 ++++ resources/qr_code/QRByte.php | 41 ++ resources/qr_code/QRCode.php | 656 ++++++++++++++++++++++ resources/qr_code/QRCodeException.php | 14 + resources/qr_code/QRCodeImage.php | 187 ++++++ resources/qr_code/QRErrorCorrectLevel.php | 20 + resources/qr_code/QRMath.php | 145 +++++ resources/qr_code/QRMode.php | 20 + resources/qr_code/QRPolynominal.php | 145 +++++ resources/qr_code/QRRSBlock.php | 503 +++++++++++++++++ resources/qr_code/QRUtil.php | 464 +++++++++++++++ resources/qr_code/README.md | 30 + resources/qr_code/rsblock.dat | 1 + 15 files changed, 2414 insertions(+) create mode 100644 resources/qr_code/.phpqrcode.php.swp create mode 100644 resources/qr_code/QR8bitByte.php create mode 100644 resources/qr_code/QRBitBuffer.php create mode 100644 resources/qr_code/QRByte.php create mode 100644 resources/qr_code/QRCode.php create mode 100644 resources/qr_code/QRCodeException.php create mode 100644 resources/qr_code/QRCodeImage.php create mode 100644 resources/qr_code/QRErrorCorrectLevel.php create mode 100644 resources/qr_code/QRMath.php create mode 100644 resources/qr_code/QRMode.php create mode 100644 resources/qr_code/QRPolynominal.php create mode 100644 resources/qr_code/QRRSBlock.php create mode 100644 resources/qr_code/QRUtil.php create mode 100644 resources/qr_code/README.md create mode 100644 resources/qr_code/rsblock.dat diff --git a/resources/qr_code/.phpqrcode.php.swp b/resources/qr_code/.phpqrcode.php.swp new file mode 100644 index 0000000000000000000000000000000000000000..26179d0b0d3ca261442db17284134eaa0f7feeac GIT binary patch literal 12288 zcmeI%F;2rU6oBEEs$xS$aDfz-F2oJ3M6bZmFcmM18|UK`s3(CK0b5jc z4EZftezu-1zi+d=ore!=_ugAd+b%@TX6EtWkL5iSFe|F + * @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; + } +} \ No newline at end of file diff --git a/resources/qr_code/QRBitBuffer.php b/resources/qr_code/QRBitBuffer.php new file mode 100644 index 0000000000..e6b091f4a3 --- /dev/null +++ b/resources/qr_code/QRBitBuffer.php @@ -0,0 +1,107 @@ + + * @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++; + } +} \ No newline at end of file diff --git a/resources/qr_code/QRByte.php b/resources/qr_code/QRByte.php new file mode 100644 index 0000000000..dd63f575ee --- /dev/null +++ b/resources/qr_code/QRByte.php @@ -0,0 +1,41 @@ + + * @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); +} \ No newline at end of file diff --git a/resources/qr_code/QRCode.php b/resources/qr_code/QRCode.php new file mode 100644 index 0000000000..9f80398fc1 --- /dev/null +++ b/resources/qr_code/QRCode.php @@ -0,0 +1,656 @@ + + */ + +/** + * 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 + */ +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; + } +} \ No newline at end of file diff --git a/resources/qr_code/QRCodeException.php b/resources/qr_code/QRCodeException.php new file mode 100644 index 0000000000..074084df28 --- /dev/null +++ b/resources/qr_code/QRCodeException.php @@ -0,0 +1,14 @@ + + * @package phpQr + */ +class QRCodeException extends ErrorException {}; diff --git a/resources/qr_code/QRCodeImage.php b/resources/qr_code/QRCodeImage.php new file mode 100644 index 0000000000..a0fc10a5c4 --- /dev/null +++ b/resources/qr_code/QRCodeImage.php @@ -0,0 +1,187 @@ + + * @package phpQr + */ +class QRCodeImageException extends QRCodeException{}; + +/** + * This class provides all needed functionality to create an image out of an QRCode bitmap + * + * @author Maik Greubel + * @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; + } + } +} \ No newline at end of file diff --git a/resources/qr_code/QRErrorCorrectLevel.php b/resources/qr_code/QRErrorCorrectLevel.php new file mode 100644 index 0000000000..9e7bebef16 --- /dev/null +++ b/resources/qr_code/QRErrorCorrectLevel.php @@ -0,0 +1,20 @@ + + * @package phpQr + */ +abstract class QRErrorCorrectLevel +{ + const L = 1; + const M = 0; + const Q = 3; + const H = 2; +} diff --git a/resources/qr_code/QRMath.php b/resources/qr_code/QRMath.php new file mode 100644 index 0000000000..c1e1edf441 --- /dev/null +++ b/resources/qr_code/QRMath.php @@ -0,0 +1,145 @@ + + * @package phpQr + */ +class QRMathException extends QRCodeException +{ +} + +/** + * QR Code math helper class + * + * @author Maik Greubel + * @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)" ); + } +} \ No newline at end of file diff --git a/resources/qr_code/QRMode.php b/resources/qr_code/QRMode.php new file mode 100644 index 0000000000..46cd4a2c2c --- /dev/null +++ b/resources/qr_code/QRMode.php @@ -0,0 +1,20 @@ + + * @package phpQr + */ +abstract class QRMode +{ + const MODE_NUMBER = 1; + const MODE_ALPHA_NUM = 2; + const MODE_8BIT_BYTE = 4; + const MODE_KANJI = 8; +} diff --git a/resources/qr_code/QRPolynominal.php b/resources/qr_code/QRPolynominal.php new file mode 100644 index 0000000000..5186b6e959 --- /dev/null +++ b/resources/qr_code/QRPolynominal.php @@ -0,0 +1,145 @@ + + * @package phpQr + */ +class QRPolynominalException extends QRCodeException{}; + +/** + * The purpose of this class is to provide a polynominal implementation for the QRCode package + * + * @author Maik Greubel + * @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; + } +} \ No newline at end of file diff --git a/resources/qr_code/QRRSBlock.php b/resources/qr_code/QRRSBlock.php new file mode 100644 index 0000000000..9ecf7d0c84 --- /dev/null +++ b/resources/qr_code/QRRSBlock.php @@ -0,0 +1,503 @@ + + * @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 + * @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)); + } +} \ No newline at end of file diff --git a/resources/qr_code/QRUtil.php b/resources/qr_code/QRUtil.php new file mode 100644 index 0000000000..d370ed4cc8 --- /dev/null +++ b/resources/qr_code/QRUtil.php @@ -0,0 +1,464 @@ + + * @package phpQr + */ +class QRUtilException extends QRCodeException {}; + +/** + * Mask pattern enumeration + * + * @author Maik Greubel + * @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 + * @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); + } +} \ No newline at end of file diff --git a/resources/qr_code/README.md b/resources/qr_code/README.md new file mode 100644 index 0000000000..778205fb87 --- /dev/null +++ b/resources/qr_code/README.md @@ -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 + +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 + +Trademark information +- + +QR Code is registered trademark of +DENSO WAVE INCORPORATED + +License +- +All files in the package are published under the terms of the BSD 2 License. + \ No newline at end of file diff --git a/resources/qr_code/rsblock.dat b/resources/qr_code/rsblock.dat new file mode 100644 index 0000000000..2aaaa1796b --- /dev/null +++ b/resources/qr_code/rsblock.dat @@ -0,0 +1 @@ +a:160:{i:0;a:3:{i:0;i:1;i:1;i:26;i:2;i:19;}i:1;a:3:{i:0;i:1;i:1;i:26;i:2;i:16;}i:2;a:3:{i:0;i:1;i:1;i:26;i:2;i:13;}i:3;a:3:{i:0;i:1;i:1;i:26;i:2;i:9;}i:4;a:3:{i:0;i:1;i:1;i:44;i:2;i:34;}i:5;a:3:{i:0;i:1;i:1;i:44;i:2;i:28;}i:6;a:3:{i:0;i:1;i:1;i:44;i:2;i:22;}i:7;a:3:{i:0;i:1;i:1;i:44;i:2;i:16;}i:8;a:3:{i:0;i:1;i:1;i:70;i:2;i:55;}i:9;a:3:{i:0;i:1;i:1;i:70;i:2;i:44;}i:10;a:3:{i:0;i:2;i:1;i:35;i:2;i:17;}i:11;a:3:{i:0;i:2;i:1;i:35;i:2;i:13;}i:12;a:3:{i:0;i:1;i:1;i:100;i:2;i:80;}i:13;a:3:{i:0;i:2;i:1;i:50;i:2;i:32;}i:14;a:3:{i:0;i:2;i:1;i:50;i:2;i:24;}i:15;a:3:{i:0;i:4;i:1;i:25;i:2;i:9;}i:16;a:3:{i:0;i:1;i:1;i:134;i:2;i:108;}i:17;a:3:{i:0;i:2;i:1;i:67;i:2;i:43;}i:18;a:6:{i:0;i:2;i:1;i:33;i:2;i:15;i:3;i:2;i:4;i:34;i:5;i:16;}i:19;a:6:{i:0;i:2;i:1;i:33;i:2;i:11;i:3;i:2;i:4;i:34;i:5;i:12;}i:20;a:3:{i:0;i:2;i:1;i:86;i:2;i:68;}i:21;a:3:{i:0;i:4;i:1;i:43;i:2;i:27;}i:22;a:3:{i:0;i:4;i:1;i:43;i:2;i:19;}i:23;a:3:{i:0;i:4;i:1;i:43;i:2;i:15;}i:24;a:3:{i:0;i:2;i:1;i:98;i:2;i:78;}i:25;a:3:{i:0;i:4;i:1;i:49;i:2;i:31;}i:26;a:6:{i:0;i:2;i:1;i:32;i:2;i:14;i:3;i:4;i:4;i:33;i:5;i:15;}i:27;a:6:{i:0;i:4;i:1;i:39;i:2;i:13;i:3;i:1;i:4;i:40;i:5;i:14;}i:28;a:3:{i:0;i:2;i:1;i:121;i:2;i:97;}i:29;a:6:{i:0;i:2;i:1;i:60;i:2;i:38;i:3;i:2;i:4;i:61;i:5;i:39;}i:30;a:6:{i:0;i:4;i:1;i:40;i:2;i:18;i:3;i:2;i:4;i:41;i:5;i:19;}i:31;a:6:{i:0;i:4;i:1;i:40;i:2;i:14;i:3;i:2;i:4;i:41;i:5;i:15;}i:32;a:3:{i:0;i:2;i:1;i:146;i:2;i:116;}i:33;a:6:{i:0;i:3;i:1;i:58;i:2;i:36;i:3;i:2;i:4;i:59;i:5;i:37;}i:34;a:6:{i:0;i:4;i:1;i:36;i:2;i:16;i:3;i:4;i:4;i:37;i:5;i:17;}i:35;a:6:{i:0;i:4;i:1;i:36;i:2;i:12;i:3;i:4;i:4;i:37;i:5;i:13;}i:36;a:6:{i:0;i:2;i:1;i:86;i:2;i:68;i:3;i:2;i:4;i:87;i:5;i:69;}i:37;a:6:{i:0;i:4;i:1;i:69;i:2;i:43;i:3;i:1;i:4;i:70;i:5;i:44;}i:38;a:6:{i:0;i:6;i:1;i:43;i:2;i:19;i:3;i:2;i:4;i:44;i:5;i:20;}i:39;a:6:{i:0;i:6;i:1;i:43;i:2;i:15;i:3;i:2;i:4;i:44;i:5;i:16;}i:40;a:3:{i:0;i:4;i:1;i:101;i:2;i:81;}i:41;a:6:{i:0;i:1;i:1;i:80;i:2;i:50;i:3;i:4;i:4;i:81;i:5;i:51;}i:42;a:6:{i:0;i:4;i:1;i:50;i:2;i:22;i:3;i:4;i:4;i:51;i:5;i:23;}i:43;a:6:{i:0;i:3;i:1;i:36;i:2;i:12;i:3;i:8;i:4;i:37;i:5;i:13;}i:44;a:6:{i:0;i:2;i:1;i:116;i:2;i:92;i:3;i:2;i:4;i:117;i:5;i:93;}i:45;a:6:{i:0;i:6;i:1;i:58;i:2;i:36;i:3;i:2;i:4;i:59;i:5;i:37;}i:46;a:6:{i:0;i:4;i:1;i:46;i:2;i:20;i:3;i:6;i:4;i:47;i:5;i:21;}i:47;a:6:{i:0;i:7;i:1;i:42;i:2;i:14;i:3;i:4;i:4;i:43;i:5;i:15;}i:48;a:3:{i:0;i:4;i:1;i:133;i:2;i:107;}i:49;a:6:{i:0;i:8;i:1;i:59;i:2;i:37;i:3;i:1;i:4;i:60;i:5;i:38;}i:50;a:6:{i:0;i:8;i:1;i:44;i:2;i:20;i:3;i:4;i:4;i:45;i:5;i:21;}i:51;a:6:{i:0;i:12;i:1;i:33;i:2;i:11;i:3;i:4;i:4;i:34;i:5;i:12;}i:52;a:6:{i:0;i:3;i:1;i:145;i:2;i:115;i:3;i:1;i:4;i:146;i:5;i:116;}i:53;a:6:{i:0;i:4;i:1;i:64;i:2;i:40;i:3;i:5;i:4;i:65;i:5;i:41;}i:54;a:6:{i:0;i:11;i:1;i:36;i:2;i:16;i:3;i:5;i:4;i:37;i:5;i:17;}i:55;a:6:{i:0;i:11;i:1;i:36;i:2;i:12;i:3;i:5;i:4;i:37;i:5;i:13;}i:56;a:6:{i:0;i:5;i:1;i:109;i:2;i:87;i:3;i:1;i:4;i:110;i:5;i:88;}i:57;a:6:{i:0;i:5;i:1;i:65;i:2;i:41;i:3;i:5;i:4;i:66;i:5;i:42;}i:58;a:6:{i:0;i:5;i:1;i:54;i:2;i:24;i:3;i:7;i:4;i:55;i:5;i:25;}i:59;a:3:{i:0;i:11;i:1;i:36;i:2;i:12;}i:60;a:6:{i:0;i:5;i:1;i:122;i:2;i:98;i:3;i:1;i:4;i:123;i:5;i:99;}i:61;a:6:{i:0;i:7;i:1;i:73;i:2;i:45;i:3;i:3;i:4;i:74;i:5;i:46;}i:62;a:6:{i:0;i:15;i:1;i:43;i:2;i:19;i:3;i:2;i:4;i:44;i:5;i:20;}i:63;a:6:{i:0;i:3;i:1;i:45;i:2;i:15;i:3;i:13;i:4;i:46;i:5;i:16;}i:64;a:6:{i:0;i:1;i:1;i:135;i:2;i:107;i:3;i:5;i:4;i:136;i:5;i:108;}i:65;a:6:{i:0;i:10;i:1;i:74;i:2;i:46;i:3;i:1;i:4;i:75;i:5;i:47;}i:66;a:6:{i:0;i:1;i:1;i:50;i:2;i:22;i:3;i:15;i:4;i:51;i:5;i:23;}i:67;a:6:{i:0;i:2;i:1;i:42;i:2;i:14;i:3;i:17;i:4;i:43;i:5;i:15;}i:68;a:6:{i:0;i:5;i:1;i:150;i:2;i:120;i:3;i:1;i:4;i:151;i:5;i:121;}i:69;a:6:{i:0;i:9;i:1;i:69;i:2;i:43;i:3;i:4;i:4;i:70;i:5;i:44;}i:70;a:6:{i:0;i:17;i:1;i:50;i:2;i:22;i:3;i:1;i:4;i:51;i:5;i:23;}i:71;a:6:{i:0;i:2;i:1;i:42;i:2;i:14;i:3;i:19;i:4;i:43;i:5;i:15;}i:72;a:6:{i:0;i:3;i:1;i:141;i:2;i:113;i:3;i:4;i:4;i:142;i:5;i:114;}i:73;a:6:{i:0;i:3;i:1;i:70;i:2;i:44;i:3;i:11;i:4;i:71;i:5;i:45;}i:74;a:6:{i:0;i:17;i:1;i:47;i:2;i:21;i:3;i:4;i:4;i:48;i:5;i:22;}i:75;a:6:{i:0;i:9;i:1;i:39;i:2;i:13;i:3;i:16;i:4;i:40;i:5;i:14;}i:76;a:6:{i:0;i:3;i:1;i:135;i:2;i:107;i:3;i:5;i:4;i:136;i:5;i:108;}i:77;a:6:{i:0;i:3;i:1;i:67;i:2;i:41;i:3;i:13;i:4;i:68;i:5;i:42;}i:78;a:6:{i:0;i:15;i:1;i:54;i:2;i:24;i:3;i:5;i:4;i:55;i:5;i:25;}i:79;a:6:{i:0;i:15;i:1;i:43;i:2;i:15;i:3;i:10;i:4;i:44;i:5;i:16;}i:80;a:6:{i:0;i:4;i:1;i:144;i:2;i:116;i:3;i:4;i:4;i:145;i:5;i:117;}i:81;a:3:{i:0;i:17;i:1;i:68;i:2;i:42;}i:82;a:6:{i:0;i:17;i:1;i:50;i:2;i:22;i:3;i:6;i:4;i:51;i:5;i:23;}i:83;a:6:{i:0;i:19;i:1;i:46;i:2;i:16;i:3;i:6;i:4;i:47;i:5;i:17;}i:84;a:6:{i:0;i:2;i:1;i:139;i:2;i:111;i:3;i:7;i:4;i:140;i:5;i:112;}i:85;a:3:{i:0;i:17;i:1;i:74;i:2;i:46;}i:86;a:6:{i:0;i:7;i:1;i:54;i:2;i:24;i:3;i:16;i:4;i:55;i:5;i:25;}i:87;a:3:{i:0;i:34;i:1;i:37;i:2;i:13;}i:88;a:6:{i:0;i:4;i:1;i:151;i:2;i:121;i:3;i:5;i:4;i:152;i:5;i:122;}i:89;a:6:{i:0;i:4;i:1;i:75;i:2;i:47;i:3;i:14;i:4;i:76;i:5;i:48;}i:90;a:6:{i:0;i:11;i:1;i:54;i:2;i:24;i:3;i:14;i:4;i:55;i:5;i:25;}i:91;a:6:{i:0;i:16;i:1;i:45;i:2;i:15;i:3;i:14;i:4;i:46;i:5;i:16;}i:92;a:6:{i:0;i:6;i:1;i:147;i:2;i:117;i:3;i:4;i:4;i:148;i:5;i:118;}i:93;a:6:{i:0;i:6;i:1;i:73;i:2;i:45;i:3;i:14;i:4;i:74;i:5;i:46;}i:94;a:6:{i:0;i:11;i:1;i:54;i:2;i:24;i:3;i:16;i:4;i:55;i:5;i:25;}i:95;a:6:{i:0;i:30;i:1;i:46;i:2;i:16;i:3;i:2;i:4;i:47;i:5;i:17;}i:96;a:6:{i:0;i:8;i:1;i:132;i:2;i:106;i:3;i:4;i:4;i:133;i:5;i:107;}i:97;a:6:{i:0;i:8;i:1;i:75;i:2;i:47;i:3;i:13;i:4;i:76;i:5;i:48;}i:98;a:6:{i:0;i:7;i:1;i:54;i:2;i:24;i:3;i:22;i:4;i:55;i:5;i:25;}i:99;a:6:{i:0;i:22;i:1;i:45;i:2;i:15;i:3;i:13;i:4;i:46;i:5;i:16;}i:100;a:6:{i:0;i:10;i:1;i:142;i:2;i:114;i:3;i:2;i:4;i:143;i:5;i:115;}i:101;a:6:{i:0;i:19;i:1;i:74;i:2;i:46;i:3;i:4;i:4;i:75;i:5;i:47;}i:102;a:6:{i:0;i:28;i:1;i:50;i:2;i:22;i:3;i:6;i:4;i:51;i:5;i:23;}i:103;a:6:{i:0;i:33;i:1;i:46;i:2;i:16;i:3;i:4;i:4;i:47;i:5;i:17;}i:104;a:6:{i:0;i:8;i:1;i:152;i:2;i:122;i:3;i:4;i:4;i:153;i:5;i:123;}i:105;a:6:{i:0;i:22;i:1;i:73;i:2;i:45;i:3;i:3;i:4;i:74;i:5;i:46;}i:106;a:6:{i:0;i:8;i:1;i:53;i:2;i:23;i:3;i:26;i:4;i:54;i:5;i:24;}i:107;a:6:{i:0;i:12;i:1;i:45;i:2;i:15;i:3;i:28;i:4;i:46;i:5;i:16;}i:108;a:6:{i:0;i:3;i:1;i:147;i:2;i:117;i:3;i:10;i:4;i:148;i:5;i:118;}i:109;a:6:{i:0;i:3;i:1;i:73;i:2;i:45;i:3;i:23;i:4;i:74;i:5;i:46;}i:110;a:6:{i:0;i:4;i:1;i:54;i:2;i:24;i:3;i:31;i:4;i:55;i:5;i:25;}i:111;a:6:{i:0;i:11;i:1;i:45;i:2;i:15;i:3;i:31;i:4;i:46;i:5;i:16;}i:112;a:6:{i:0;i:7;i:1;i:146;i:2;i:116;i:3;i:7;i:4;i:147;i:5;i:117;}i:113;a:6:{i:0;i:21;i:1;i:73;i:2;i:45;i:3;i:7;i:4;i:74;i:5;i:46;}i:114;a:6:{i:0;i:1;i:1;i:53;i:2;i:23;i:3;i:37;i:4;i:54;i:5;i:24;}i:115;a:6:{i:0;i:19;i:1;i:45;i:2;i:15;i:3;i:26;i:4;i:46;i:5;i:16;}i:116;a:6:{i:0;i:5;i:1;i:145;i:2;i:115;i:3;i:10;i:4;i:146;i:5;i:116;}i:117;a:6:{i:0;i:19;i:1;i:75;i:2;i:47;i:3;i:10;i:4;i:76;i:5;i:48;}i:118;a:6:{i:0;i:15;i:1;i:54;i:2;i:24;i:3;i:25;i:4;i:55;i:5;i:25;}i:119;a:6:{i:0;i:23;i:1;i:45;i:2;i:15;i:3;i:25;i:4;i:46;i:5;i:16;}i:120;a:6:{i:0;i:13;i:1;i:145;i:2;i:115;i:3;i:3;i:4;i:146;i:5;i:116;}i:121;a:6:{i:0;i:2;i:1;i:74;i:2;i:46;i:3;i:29;i:4;i:75;i:5;i:47;}i:122;a:6:{i:0;i:42;i:1;i:54;i:2;i:24;i:3;i:1;i:4;i:55;i:5;i:25;}i:123;a:6:{i:0;i:23;i:1;i:45;i:2;i:15;i:3;i:28;i:4;i:46;i:5;i:16;}i:124;a:3:{i:0;i:17;i:1;i:145;i:2;i:115;}i:125;a:6:{i:0;i:10;i:1;i:74;i:2;i:46;i:3;i:23;i:4;i:75;i:5;i:47;}i:126;a:6:{i:0;i:42;i:1;i:54;i:2;i:24;i:3;i:1;i:4;i:55;i:5;i:25;}i:127;a:6:{i:0;i:23;i:1;i:45;i:2;i:15;i:3;i:28;i:4;i:46;i:5;i:16;}i:128;a:6:{i:0;i:17;i:1;i:145;i:2;i:115;i:3;i:1;i:4;i:146;i:5;i:116;}i:129;a:6:{i:0;i:14;i:1;i:74;i:2;i:46;i:3;i:21;i:4;i:75;i:5;i:47;}i:130;a:6:{i:0;i:29;i:1;i:54;i:2;i:24;i:3;i:19;i:4;i:55;i:5;i:25;}i:131;a:6:{i:0;i:11;i:1;i:45;i:2;i:15;i:3;i:46;i:4;i:46;i:5;i:16;}i:132;a:6:{i:0;i:13;i:1;i:145;i:2;i:115;i:3;i:6;i:4;i:146;i:5;i:116;}i:133;a:6:{i:0;i:14;i:1;i:74;i:2;i:46;i:3;i:21;i:4;i:75;i:5;i:47;}i:134;a:6:{i:0;i:44;i:1;i:54;i:2;i:24;i:3;i:7;i:4;i:55;i:5;i:25;}i:135;a:6:{i:0;i:59;i:1;i:46;i:2;i:16;i:3;i:1;i:4;i:47;i:5;i:17;}i:136;a:6:{i:0;i:12;i:1;i:151;i:2;i:121;i:3;i:7;i:4;i:152;i:5;i:122;}i:137;a:6:{i:0;i:12;i:1;i:75;i:2;i:47;i:3;i:26;i:4;i:76;i:5;i:48;}i:138;a:6:{i:0;i:39;i:1;i:54;i:2;i:24;i:3;i:14;i:4;i:55;i:5;i:25;}i:139;a:6:{i:0;i:22;i:1;i:45;i:2;i:15;i:3;i:41;i:4;i:46;i:5;i:16;}i:140;a:6:{i:0;i:6;i:1;i:151;i:2;i:121;i:3;i:14;i:4;i:152;i:5;i:122;}i:141;a:6:{i:0;i:6;i:1;i:75;i:2;i:47;i:3;i:34;i:4;i:76;i:5;i:48;}i:142;a:6:{i:0;i:46;i:1;i:54;i:2;i:24;i:3;i:10;i:4;i:55;i:5;i:25;}i:143;a:6:{i:0;i:2;i:1;i:45;i:2;i:15;i:3;i:64;i:4;i:46;i:5;i:16;}i:144;a:6:{i:0;i:17;i:1;i:152;i:2;i:122;i:3;i:4;i:4;i:153;i:5;i:123;}i:145;a:6:{i:0;i:29;i:1;i:74;i:2;i:46;i:3;i:14;i:4;i:75;i:5;i:47;}i:146;a:6:{i:0;i:49;i:1;i:54;i:2;i:24;i:3;i:10;i:4;i:55;i:5;i:25;}i:147;a:6:{i:0;i:24;i:1;i:45;i:2;i:15;i:3;i:46;i:4;i:46;i:5;i:16;}i:148;a:6:{i:0;i:4;i:1;i:152;i:2;i:122;i:3;i:18;i:4;i:153;i:5;i:123;}i:149;a:6:{i:0;i:13;i:1;i:74;i:2;i:46;i:3;i:32;i:4;i:75;i:5;i:47;}i:150;a:6:{i:0;i:48;i:1;i:54;i:2;i:24;i:3;i:14;i:4;i:55;i:5;i:25;}i:151;a:6:{i:0;i:42;i:1;i:45;i:2;i:15;i:3;i:32;i:4;i:46;i:5;i:16;}i:152;a:6:{i:0;i:20;i:1;i:147;i:2;i:117;i:3;i:4;i:4;i:148;i:5;i:118;}i:153;a:6:{i:0;i:40;i:1;i:75;i:2;i:47;i:3;i:7;i:4;i:76;i:5;i:48;}i:154;a:6:{i:0;i:43;i:1;i:54;i:2;i:24;i:3;i:22;i:4;i:55;i:5;i:25;}i:155;a:6:{i:0;i:10;i:1;i:45;i:2;i:15;i:3;i:67;i:4;i:46;i:5;i:16;}i:156;a:6:{i:0;i:19;i:1;i:148;i:2;i:118;i:3;i:6;i:4;i:149;i:5;i:119;}i:157;a:6:{i:0;i:18;i:1;i:75;i:2;i:47;i:3;i:31;i:4;i:76;i:5;i:48;}i:158;a:6:{i:0;i:34;i:1;i:54;i:2;i:24;i:3;i:34;i:4;i:55;i:5;i:25;}i:159;a:6:{i:0;i:20;i:1;i:45;i:2;i:15;i:3;i:61;i:4;i:46;i:5;i:16;}} \ No newline at end of file