diff --git a/classes/admin.php b/classes/admin.php index 7448a322..dc069787 100644 --- a/classes/admin.php +++ b/classes/admin.php @@ -381,10 +381,15 @@ class Admin $twofa_admin_enabled = $this->grav['config']->get('plugins.admin.twofa_enabled', false); + + if ($twofa_admin_enabled && isset($user->twofa_enabled) && $user->twofa_enabled == true) { $twofa = $this->get2FA(); + + $twofa->createSecret(); + $secret = isset($user->twofa_secret) ? $user->twofa_secret : null; - if (!(isset($data['2fa_code']) && $data['2fa_code'] == $twofa->getCode($secret))) { + if (!(isset($data['2fa_code']) && $twofa->verifyCode($secret, $data['2fa_code']))) { return false; } } @@ -1724,6 +1729,8 @@ class Admin public function get2FA() { - return new TwoFactorAuth('Grav'); + $provider = new BaconQRProvider(); + $twofa = new TwoFactorAuth('Grav', 6, 30, 'sha1', $provider); + return $twofa; } } diff --git a/classes/baconqrprovider.php b/classes/baconqrprovider.php new file mode 100644 index 00000000..5dd5da74 --- /dev/null +++ b/classes/baconqrprovider.php @@ -0,0 +1,25 @@ +setHeight($size); + $renderer->setWidth($size); + $writer = new BaconWriter($renderer); + $result = $writer->writeString($qrtext); + + return $result; + } +} diff --git a/composer.json b/composer.json index 4e22c156..441ac56a 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,8 @@ "require": { "composer/semver": "^1.4", "fguillot/picofeed": "@stable", - "robthree/twofactorauth": "^1.6" + "robthree/twofactorauth": "^1.6", + "bacon/bacon-qr-code": "^1.0" }, "require-dev": { "codeception/codeception": "^2.1", diff --git a/composer.lock b/composer.lock index 162f8478..7c8e124e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,50 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "fda627919c5cab76d0e1f4c2aed4e487", + "content-hash": "20422b73ece969b58e32511cbcc54346", "packages": [ + { + "name": "bacon/bacon-qr-code", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "031a2ce68c5794064b49d11775b2daf45c96e21c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/031a2ce68c5794064b49d11775b2daf45c96e21c", + "reference": "031a2ce68c5794064b49d11775b2daf45c96e21c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-gd": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-0": { + "BaconQrCode": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "http://www.dasprids.de", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "time": "2016-01-09T22:55:35+00:00" + }, { "name": "composer/semver", "version": "1.4.2", diff --git a/vendor/bacon/bacon-qr-code/.gitignore b/vendor/bacon/bacon-qr-code/.gitignore new file mode 100644 index 00000000..35c1bcdf --- /dev/null +++ b/vendor/bacon/bacon-qr-code/.gitignore @@ -0,0 +1,7 @@ +nbproject +.idea +.buildpath +.project +.DS_Store +.*.sw* +.*.un~ diff --git a/vendor/bacon/bacon-qr-code/.travis.yml b/vendor/bacon/bacon-qr-code/.travis.yml new file mode 100644 index 00000000..4e554be6 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/.travis.yml @@ -0,0 +1,9 @@ +language: php +php: + - 5.3 + - 5.4 + - 5.5 + - 7.0 + - hhvm + +script: phpunit --bootstrap tests/bootstrap.php --configuration tests/phpunit.xml tests \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/LICENSE b/vendor/bacon/bacon-qr-code/LICENSE new file mode 100644 index 00000000..a72461a0 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013, Ben 'DASPRiD' Scholzen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/bacon/bacon-qr-code/Module.php b/vendor/bacon/bacon-qr-code/Module.php new file mode 100644 index 00000000..ad7798c0 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/Module.php @@ -0,0 +1,37 @@ + array( + __DIR__ . '/autoload_classmap.php', + ), + 'Zend\Loader\StandardAutoloader' => array( + 'namespaces' => array( + __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__, + ), + ), + ); + } +} diff --git a/vendor/bacon/bacon-qr-code/README.md b/vendor/bacon/bacon-qr-code/README.md new file mode 100644 index 00000000..a836bd61 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/README.md @@ -0,0 +1,24 @@ +QR Code generator +================= + +Master: [](http://travis-ci.org/Bacon/BaconQrCode) + +Introduction +------------ +BaconQrCode is a port of QR code portion of the ZXing library. It currently +only features the encoder part, but could later receive the decoder part as +well. + +As the Reed Solomon codec implementation of the ZXing library performs quite +slow in PHP, it was exchanged with the implementation by Phil Karn. + + +Example usage +------------- +```php +$renderer = new \BaconQrCode\Renderer\Image\Png(); +$renderer->setHeight(256); +$renderer->setWidth(256); +$writer = new \BaconQrCode\Writer($renderer); +$writer->writeFile('Hello World!', 'qrcode.png'); +``` diff --git a/vendor/bacon/bacon-qr-code/autoload_classmap.php b/vendor/bacon/bacon-qr-code/autoload_classmap.php new file mode 100644 index 00000000..9fbeb35b --- /dev/null +++ b/vendor/bacon/bacon-qr-code/autoload_classmap.php @@ -0,0 +1,43 @@ + __DIR__ . '/src/BaconQrCode/Common/AbstractEnum.php', + 'BaconQrCode\Common\BitArray' => __DIR__ . '/src/BaconQrCode/Common/BitArray.php', + 'BaconQrCode\Common\BitMatrix' => __DIR__ . '/src/BaconQrCode/Common/BitMatrix.php', + 'BaconQrCode\Common\BitUtils' => __DIR__ . '/src/BaconQrCode/Common/BitUtils.php', + 'BaconQrCode\Common\CharacterSetEci' => __DIR__ . '/src/BaconQrCode/Common/CharacterSetEci.php', + 'BaconQrCode\Common\EcBlock' => __DIR__ . '/src/BaconQrCode/Common/EcBlock.php', + 'BaconQrCode\Common\EcBlocks' => __DIR__ . '/src/BaconQrCode/Common/EcBlocks.php', + 'BaconQrCode\Common\ErrorCorrectionLevel' => __DIR__ . '/src/BaconQrCode/Common/ErrorCorrectionLevel.php', + 'BaconQrCode\Common\FormatInformation' => __DIR__ . '/src/BaconQrCode/Common/FormatInformation.php', + 'BaconQrCode\Common\Mode' => __DIR__ . '/src/BaconQrCode/Common/Mode.php', + 'BaconQrCode\Common\ReedSolomonCodec' => __DIR__ . '/src/BaconQrCode/Common/ReedSolomonCodec.php', + 'BaconQrCode\Common\Version' => __DIR__ . '/src/BaconQrCode/Common/Version.php', + 'BaconQrCode\Encoder\BlockPair' => __DIR__ . '/src/BaconQrCode/Encoder/BlockPair.php', + 'BaconQrCode\Encoder\ByteMatrix' => __DIR__ . '/src/BaconQrCode/Encoder/ByteMatrix.php', + 'BaconQrCode\Encoder\Encoder' => __DIR__ . '/src/BaconQrCode/Encoder/Encoder.php', + 'BaconQrCode\Encoder\MaskUtil' => __DIR__ . '/src/BaconQrCode/Encoder/MaskUtil.php', + 'BaconQrCode\Encoder\MatrixUtil' => __DIR__ . '/src/BaconQrCode/Encoder/MatrixUtil.php', + 'BaconQrCode\Encoder\QrCode' => __DIR__ . '/src/BaconQrCode/Encoder/QrCode.php', + 'BaconQrCode\Exception\ExceptionInterface' => __DIR__ . '/src/BaconQrCode/Exception/ExceptionInterface.php', + 'BaconQrCode\Exception\InvalidArgumentException' => __DIR__ . '/src/BaconQrCode/Exception/InvalidArgumentException.php', + 'BaconQrCode\Exception\OutOfBoundsException' => __DIR__ . '/src/BaconQrCode/Exception/OutOfBoundsException.php', + 'BaconQrCode\Exception\RuntimeException' => __DIR__ . '/src/BaconQrCode/Exception/RuntimeException.php', + 'BaconQrCode\Exception\UnexpectedValueException' => __DIR__ . '/src/BaconQrCode/Exception/UnexpectedValueException.php', + 'BaconQrCode\Exception\WriterException' => __DIR__ . '/src/BaconQrCode/Exception/WriterException.php', + 'BaconQrCode\Renderer\Color\Cmyk' => __DIR__ . '/src/BaconQrCode/Renderer/Color/Cmyk.php', + 'BaconQrCode\Renderer\Color\ColorInterface' => __DIR__ . '/src/BaconQrCode/Renderer/Color/ColorInterface.php', + 'BaconQrCode\Renderer\Color\Gray' => __DIR__ . '/src/BaconQrCode/Renderer/Color/Gray.php', + 'BaconQrCode\Renderer\Color\Rgb' => __DIR__ . '/src/BaconQrCode/Renderer/Color/Rgb.php', + 'BaconQrCode\Renderer\Image\AbstractRenderer' => __DIR__ . '/src/BaconQrCode/Renderer/Image/AbstractRenderer.php', + 'BaconQrCode\Renderer\Image\Decorator\DecoratorInterface' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Decorator/DecoratorInterface.php', + 'BaconQrCode\Renderer\Image\Decorator\FinderPattern' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Decorator/FinderPattern.php', + 'BaconQrCode\Renderer\Image\Eps' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Eps.php', + 'BaconQrCode\Renderer\Image\Png' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Png.php', + 'BaconQrCode\Renderer\Image\RendererInterface' => __DIR__ . '/src/BaconQrCode/Renderer/Image/RendererInterface.php', + 'BaconQrCode\Renderer\Image\Svg' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Svg.php', + 'BaconQrCode\Renderer\RendererInterface' => __DIR__ . '/src/BaconQrCode/Renderer/RendererInterface.php', + 'BaconQrCode\Renderer\Text\Plain' => __DIR__ . '/src/BaconQrCode/Renderer/Text/Plain.php', + 'BaconQrCode\Renderer\Text\Html' => __DIR__ . '/src/BaconQrCode/Renderer/Text/Html.php', + 'BaconQrCode\Writer' => __DIR__ . '/src/BaconQrCode/Writer.php', +); \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/autoload_function.php b/vendor/bacon/bacon-qr-code/autoload_function.php new file mode 100644 index 00000000..9148da38 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/autoload_function.php @@ -0,0 +1,12 @@ +=5.3.3" + }, + "suggest": { + "ext-gd": "to generate QR code images" + }, + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "http://www.dasprids.de", + "role": "Developer" + } + ], + "autoload": { + "psr-0": { + "BaconQrCode": "src/" + } + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/AbstractEnum.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/AbstractEnum.php new file mode 100644 index 00000000..9544338f --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/AbstractEnum.php @@ -0,0 +1,115 @@ +strict = $strict; + $this->change($initialValue); + } + + /** + * Changes the value of the enum. + * + * @param mixed $value + * @return void + */ + public function change($value) + { + if (!in_array($value, $this->getConstList(), $this->strict)) { + throw new Exception\UnexpectedValueException('Value not a const in enum ' . get_class($this)); + } + + $this->value = $value; + } + + /** + * Gets current value. + * + * @return mixed + */ + public function get() + { + return $this->value; + } + + /** + * Gets all constants (possible values) as an array. + * + * @param boolean $includeDefault + * @return array + */ + public function getConstList($includeDefault = true) + { + if ($this->constants === null) { + $reflection = new ReflectionClass($this); + $this->constants = $reflection->getConstants(); + } + + if ($includeDefault) { + return $this->constants; + } + + $constants = $this->constants; + unset($constants['__default']); + + return $constants; + } + + /** + * Gets the name of the enum. + * + * @return string + */ + public function __toString() + { + return array_search($this->value, $this->getConstList()); + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/BitArray.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/BitArray.php new file mode 100644 index 00000000..0a99d9a9 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/BitArray.php @@ -0,0 +1,435 @@ +size = $size; + $this->bits = new SplFixedArray(($this->size + 31) >> 3); + } + + /** + * Gets the size in bits. + * + * @return integer + */ + public function getSize() + { + return $this->size; + } + + /** + * Gets the size in bytes. + * + * @return integer + */ + public function getSizeInBytes() + { + return ($this->size + 7) >> 3; + } + + /** + * Ensures that the array has a minimum capacity. + * + * @param integer $size + * @return void + */ + public function ensureCapacity($size) + { + if ($size > count($this->bits) << 5) { + $this->bits->setSize(($size + 31) >> 5); + } + } + + /** + * Gets a specific bit. + * + * @param integer $i + * @return boolean + */ + public function get($i) + { + return ($this->bits[$i >> 5] & (1 << ($i & 0x1f))) !== 0; + } + + /** + * Sets a specific bit. + * + * @param integer $i + * @return void + */ + public function set($i) + { + $this->bits[$i >> 5] = $this->bits[$i >> 5] | 1 << ($i & 0x1f); + } + + /** + * Flips a specific bit. + * + * @param integer $i + * @return void + */ + public function flip($i) + { + $this->bits[$i >> 5] ^= 1 << ($i & 0x1f); + } + + /** + * Gets the next set bit position from a given position. + * + * @param integer $from + * @return integer + */ + public function getNextSet($from) + { + if ($from >= $this->size) { + return $this->size; + } + + $bitsOffset = $from >> 5; + $currentBits = $this->bits[$bitsOffset]; + $bitsLength = count($this->bits); + + $currentBits &= ~((1 << ($from & 0x1f)) - 1); + + while ($currentBits === 0) { + if (++$bitsOffset === $bitsLength) { + return $this->size; + } + + $currentBits = $this->bits[$bitsOffset]; + } + + $result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits); + + return $result > $this->size ? $this->size : $result; + } + + /** + * Gets the next unset bit position from a given position. + * + * @param integer $from + * @return integer + */ + public function getNextUnset($from) + { + if ($from >= $this->size) { + return $this->size; + } + + $bitsOffset = $from >> 5; + $currentBits = ~$this->bits[$bitsOffset]; + $bitsLength = count($this->bits); + + $currentBits &= ~((1 << ($from & 0x1f)) - 1); + + while ($currentBits === 0) { + if (++$bitsOffset === $bitsLength) { + return $this->size; + } + + $currentBits = ~$this->bits[$bitsOffset]; + } + + $result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits); + + return $result > $this->size ? $this->size : $result; + } + + /** + * Sets a bulk of bits. + * + * @param integer $i + * @param integer $newBits + * @return void + */ + public function setBulk($i, $newBits) + { + $this->bits[$i >> 5] = $newBits; + } + + /** + * Sets a range of bits. + * + * @param integer $start + * @param integer $end + * @return void + * @throws Exception\InvalidArgumentException + */ + public function setRange($start, $end) + { + if ($end < $start) { + throw new Exception\InvalidArgumentException('End must be greater or equal to start'); + } + + if ($end === $start) { + return; + } + + $end--; + + $firstInt = $start >> 5; + $lastInt = $end >> 5; + + for ($i = $firstInt; $i <= $lastInt; $i++) { + $firstBit = $i > $firstInt ? 0 : $start & 0x1f; + $lastBit = $i < $lastInt ? 31 : $end & 0x1f; + + if ($firstBit === 0 && $lastBit === 31) { + $mask = 0x7fffffff; + } else { + $mask = 0; + + for ($j = $firstBit; $j < $lastBit; $j++) { + $mask |= 1 << $j; + } + } + + $this->bits[$i] = $this->bits[$i] | $mask; + } + } + + /** + * Clears the bit array, unsetting every bit. + * + * @return void + */ + public function clear() + { + $bitsLength = count($this->bits); + + for ($i = 0; $i < $bitsLength; $i++) { + $this->bits[$i] = 0; + } + } + + /** + * Checks if a range of bits is set or not set. + * + * @param integer $start + * @param integer $end + * @param integer $value + * @return boolean + * @throws Exception\InvalidArgumentException + */ + public function isRange($start, $end, $value) + { + if ($end < $start) { + throw new Exception\InvalidArgumentException('End must be greater or equal to start'); + } + + if ($end === $start) { + return; + } + + $end--; + + $firstInt = $start >> 5; + $lastInt = $end >> 5; + + for ($i = $firstInt; $i <= $lastInt; $i++) { + $firstBit = $i > $firstInt ? 0 : $start & 0x1f; + $lastBit = $i < $lastInt ? 31 : $end & 0x1f; + + if ($firstBit === 0 && $lastBit === 31) { + $mask = 0x7fffffff; + } else { + $mask = 0; + + for ($j = $firstBit; $j <= $lastBit; $j++) { + $mask |= 1 << $j; + } + } + + if (($this->bits[$i] & $mask) !== ($value ? $mask : 0)) { + return false; + } + } + + return true; + } + + /** + * Appends a bit to the array. + * + * @param boolean $bit + * @return void + */ + public function appendBit($bit) + { + $this->ensureCapacity($this->size + 1); + + if ($bit) { + $this->bits[$this->size >> 5] = $this->bits[$this->size >> 5] | (1 << ($this->size & 0x1f)); + } + + $this->size++; + } + + /** + * Appends a number of bits (up to 32) to the array. + * + * @param integer $value + * @param integer $numBits + * @return void + * @throws Exception\InvalidArgumentException + */ + public function appendBits($value, $numBits) + { + if ($numBits < 0 || $numBits > 32) { + throw new Exception\InvalidArgumentException('Num bits must be between 0 and 32'); + } + + $this->ensureCapacity($this->size + $numBits); + + for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) { + $this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) === 1); + } + } + + /** + * Appends another bit array to this array. + * + * @param BitArray $other + * @return void + */ + public function appendBitArray(self $other) + { + $otherSize = $other->getSize(); + $this->ensureCapacity($this->size + $other->getSize()); + + for ($i = 0; $i < $otherSize; $i++) { + $this->appendBit($other->get($i)); + } + } + + /** + * Makes an exclusive-or comparision on the current bit array. + * + * @param BitArray $other + * @return void + * @throws Exception\InvalidArgumentException + */ + public function xorBits(self $other) + { + $bitsLength = count($this->bits); + $otherBits = $other->getBitArray(); + + if ($bitsLength !== count($otherBits)) { + throw new Exception\InvalidArgumentException('Sizes don\'t match'); + } + + for ($i = 0; $i < $bitsLength; $i++) { + $this->bits[$i] = $this->bits[$i] ^ $otherBits[$i]; + } + } + + /** + * Converts the bit array to a byte array. + * + * @param integer $bitOffset + * @param integer $numBytes + * @return SplFixedArray + */ + public function toBytes($bitOffset, $numBytes) + { + $bytes = new SplFixedArray($numBytes); + + for ($i = 0; $i < $numBytes; $i++) { + $byte = 0; + + for ($j = 0; $j < 8; $j++) { + if ($this->get($bitOffset)) { + $byte |= 1 << (7 - $j); + } + + $bitOffset++; + } + + $bytes[$i] = $byte; + } + + return $bytes; + } + + /** + * Gets the internal bit array. + * + * @return SplFixedArray + */ + public function getBitArray() + { + return $this->bits; + } + + /** + * Reverses the array. + * + * @return void + */ + public function reverse() + { + $newBits = new SplFixedArray(count($this->bits)); + + for ($i = 0; $i < $this->size; $i++) { + if ($this->get($this->size - $i - 1)) { + $newBits[$i >> 5] = $newBits[$i >> 5] | (1 << ($i & 0x1f)); + } + } + + $this->bits = newBits; + } + + /** + * Returns a string representation of the bit array. + * + * @return string + */ + public function __toString() + { + $result = ''; + + for ($i = 0; $i < $this->size; $i++) { + if (($i & 0x07) === 0) { + $result .= ' '; + } + + $result .= $this->get($i) ? 'X' : '.'; + } + + return $result; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/BitMatrix.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/BitMatrix.php new file mode 100644 index 00000000..b930f88b --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/BitMatrix.php @@ -0,0 +1,350 @@ +width = $width; + $this->height = $height; + $this->rowSize = ($width + 31) >> 5; + $this->bits = new SplFixedArray($this->rowSize * $height); + } + + /** + * Gets the requested bit, where true means black. + * + * @param integer $x + * @param integer $y + * @return boolean + */ + public function get($x, $y) + { + $offset = $y * $this->rowSize + ($x >> 5); + return (BitUtils::unsignedRightShift($this->bits[$offset], ($x & 0x1f)) & 1) !== 0; + } + + /** + * Sets the given bit to true. + * + * @param integer $x + * @param integer $y + * @return void + */ + public function set($x, $y) + { + $offset = $y * $this->rowSize + ($x >> 5); + $this->bits[$offset] = $this->bits[$offset] | (1 << ($x & 0x1f)); + } + + /** + * Flips the given bit. + * + * @param integer $x + * @param integer $y + * @return void + */ + public function flip($x, $y) + { + $offset = $y * $this->rowSize + ($x >> 5); + $this->bits[$offset] = $this->bits[$offset] ^ (1 << ($x & 0x1f)); + } + + /** + * Clears all bits (set to false). + * + * @return void + */ + public function clear() + { + $max = count($this->bits); + + for ($i = 0; $i < $max; $i++) { + $this->bits[$i] = 0; + } + } + + /** + * Sets a square region of the bit matrix to true. + * + * @param integer $left + * @param integer $top + * @param integer $width + * @param integer $height + * @return void + */ + public function setRegion($left, $top, $width, $height) + { + if ($top < 0 || $left < 0) { + throw new Exception\InvalidArgumentException('Left and top must be non-negative'); + } + + if ($height < 1 || $width < 1) { + throw new Exception\InvalidArgumentException('Width and height must be at least 1'); + } + + $right = $left + $width; + $bottom = $top + $height; + + if ($bottom > $this->height || $right > $this->width) { + throw new Exception\InvalidArgumentException('The region must fit inside the matrix'); + } + + for ($y = $top; $y < $bottom; $y++) { + $offset = $y * $this->rowSize; + + for ($x = $left; $x < $right; $x++) { + $index = $offset + ($x >> 5); + $this->bits[$index] = $this->bits[$index] | (1 << ($x & 0x1f)); + } + } + } + + /** + * A fast method to retrieve one row of data from the matrix as a BitArray. + * + * @param integer $y + * @param BitArray $row + * @return BitArray + */ + public function getRow($y, BitArray $row = null) + { + if ($row === null || $row->getSize() < $this->width) { + $row = new BitArray($this->width); + } + + $offset = $y * $this->rowSize; + + for ($x = 0; $x < $this->rowSize; $x++) { + $row->setBulk($x << 5, $this->bits[$offset + $x]); + } + + return $row; + } + + /** + * Sets a row of data from a BitArray. + * + * @param integer $y + * @param BitArray $row + * @return void + */ + public function setRow($y, BitArray $row) + { + $bits = $row->getBitArray(); + + for ($i = 0; $i < $this->rowSize; $i++) { + $this->bits[$y * $this->rowSize + $i] = $bits[$i]; + } + } + + /** + * This is useful in detecting the enclosing rectangle of a 'pure' barcode. + * + * @return SplFixedArray + */ + public function getEnclosingRectangle() + { + $left = $this->width; + $top = $this->height; + $right = -1; + $bottom = -1; + + for ($y = 0; $y < $this->height; $y++) { + for ($x32 = 0; $x32 < $this->rowSize; $x32++) { + $bits = $this->bits[$y * $this->rowSize + $x32]; + + if ($bits !== 0) { + if ($y < $top) { + $top = $y; + } + + if ($y > $bottom) { + $bottom = $y; + } + + if ($x32 * 32 < $left) { + $bit = 0; + + while (($bits << (31 - $bit)) === 0) { + $bit++; + } + + if (($x32 * 32 + $bit) < $left) { + $left = $x32 * 32 + $bit; + } + } + } + + if ($x32 * 32 + 31 > $right) { + $bit = 31; + + while (BitUtils::unsignedRightShift($bits, $bit) === 0) { + $bit--; + } + + if (($x32 * 32 + $bit) > $right) { + $right = $x32 * 32 + $bit; + } + } + } + } + + $width = $right - $left; + $height = $bottom - $top; + + if ($width < 0 || $height < 0) { + return null; + } + + return SplFixedArray::fromArray(array($left, $top, $width, $height), false); + } + + /** + * Gets the most top left set bit. + * + * This is useful in detecting a corner of a 'pure' barcode. + * + * @return SplFixedArray + */ + public function getTopLeftOnBit() + { + $bitsOffset = 0; + + while ($bitsOffset < count($this->bits) && $this->bits[$bitsOffset] === 0) { + $bitsOffset++; + } + + if ($bitsOffset === count($this->bits)) { + return null; + } + + $x = intval($bitsOffset / $this->rowSize); + $y = ($bitsOffset % $this->rowSize) << 5; + + $bits = $this->bits[$bitsOffset]; + $bit = 0; + + while (($bits << (31 - $bit)) === 0) { + $bit++; + } + + $x += $bit; + + return SplFixedArray::fromArray(array($x, $y), false); + } + + /** + * Gets the most bottom right set bit. + * + * This is useful in detecting a corner of a 'pure' barcode. + * + * @return SplFixedArray + */ + public function getBottomRightOnBit() + { + $bitsOffset = count($this->bits) - 1; + + while ($bitsOffset >= 0 && $this->bits[$bitsOffset] === 0) { + $bitsOffset--; + } + + if ($bitsOffset < 0) { + return null; + } + + $x = intval($bitsOffset / $this->rowSize); + $y = ($bitsOffset % $this->rowSize) << 5; + + $bits = $this->bits[$bitsOffset]; + $bit = 0; + + while (BitUtils::unsignedRightShift($bits, $bit) === 0) { + $bit--; + } + + $x += $bit; + + return SplFixedArray::fromArray(array($x, $y), false); + } + + /** + * Gets the width of the matrix, + * + * @return integer + */ + public function getWidth() + { + return $this->width; + } + + /** + * Gets the height of the matrix. + * + * @return integer + */ + public function getHeight() + { + return $this->height; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/BitUtils.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/BitUtils.php new file mode 100644 index 00000000..a6412440 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/BitUtils.php @@ -0,0 +1,51 @@ +>>" in other + * languages. + * + * @param integer $a + * @param integer $b + * @return integer + */ + public static function unsignedRightShift($a, $b) + { + return ( + $a >= 0 + ? $a >> $b + : (($a & 0x7fffffff) >> $b) | (0x40000000 >> ($b - 1)) + ); + } + + /** + * Gets the number of trailing zeros. + * + * @param integer $i + * @return integer + */ + public static function numberOfTrailingZeros($i) + { + $lastPos = strrpos(str_pad(decbin($i), 32, '0', STR_PAD_LEFT), '1'); + + return $lastPos === false ? 32 : 31 - $lastPos; + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/CharacterSetEci.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/CharacterSetEci.php new file mode 100644 index 00000000..77662360 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/CharacterSetEci.php @@ -0,0 +1,134 @@ + self::ISO8859_1, + 'ISO-8859-2' => self::ISO8859_2, + 'ISO-8859-3' => self::ISO8859_3, + 'ISO-8859-4' => self::ISO8859_4, + 'ISO-8859-5' => self::ISO8859_5, + 'ISO-8859-6' => self::ISO8859_6, + 'ISO-8859-7' => self::ISO8859_7, + 'ISO-8859-8' => self::ISO8859_8, + 'ISO-8859-9' => self::ISO8859_9, + 'ISO-8859-10' => self::ISO8859_10, + 'ISO-8859-11' => self::ISO8859_11, + 'ISO-8859-12' => self::ISO8859_12, + 'ISO-8859-13' => self::ISO8859_13, + 'ISO-8859-14' => self::ISO8859_14, + 'ISO-8859-15' => self::ISO8859_15, + 'ISO-8859-16' => self::ISO8859_16, + 'SHIFT-JIS' => self::SJIS, + 'WINDOWS-1250' => self::CP1250, + 'WINDOWS-1251' => self::CP1251, + 'WINDOWS-1252' => self::CP1252, + 'WINDOWS-1256' => self::CP1256, + 'UTF-16BE' => self::UNICODE_BIG_UNMARKED, + 'UTF-8' => self::UTF8, + 'ASCII' => self::ASCII, + 'GBK' => self::GB18030, + 'EUC-KR' => self::EUC_KR, + ); + + /** + * Additional possible values for character sets. + * + * @var array + */ + protected $additionalValues = array( + self::CP437 => 2, + self::ASCII => 170, + ); + + /** + * Gets character set ECI by value. + * + * @param string $name + * @return CharacterSetEci|null + */ + public static function getCharacterSetECIByValue($value) + { + if ($value < 0 || $value >= 900) { + throw new Exception\InvalidArgumentException('Value must be between 0 and 900'); + } + + if (false !== ($key = array_search($value, self::$additionalValues))) { + $value = $key; + } + + try { + return new self($value); + } catch (Exception\UnexpectedValueException $e) { + return null; + } + } + + /** + * Gets character set ECI by name. + * + * @param string $name + * @return CharacterSetEci|null + */ + public static function getCharacterSetECIByName($name) + { + $name = strtoupper($name); + + if (isset(self::$nameToEci[$name])) { + return new self(self::$nameToEci[$name]); + } + + return null; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/EcBlock.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/EcBlock.php new file mode 100644 index 00000000..cbcc2ba0 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/EcBlock.php @@ -0,0 +1,65 @@ +count = $count; + $this->dataCodewords = $dataCodewords; + } + + /** + * Returns how many times the block is used. + * + * @return integer + */ + public function getCount() + { + return $this->count; + } + + /** + * Returns the number of data codewords. + * + * @return integer + */ + public function getDataCodewords() + { + return $this->dataCodewords; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/EcBlocks.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/EcBlocks.php new file mode 100644 index 00000000..87cef5d0 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/EcBlocks.php @@ -0,0 +1,101 @@ +ecCodewordsPerBlock = $ecCodewordsPerBlock; + + $this->ecBlocks = new SplFixedArray($ecb2 === null ? 1 : 2); + $this->ecBlocks[0] = $ecb1; + + if ($ecb2 !== null) { + $this->ecBlocks[1] = $ecb2; + } + } + + /** + * Gets the number of EC codewords per block. + * + * @return integer + */ + public function getEcCodewordsPerBlock() + { + return $this->ecCodewordsPerBlock; + } + + /** + * Gets the total number of EC block appearances. + * + * @return integer + */ + public function getNumBlocks() + { + $total = 0; + + foreach ($this->ecBlocks as $ecBlock) { + $total += $ecBlock->getCount(); + } + + return $total; + } + + /** + * Gets the total count of EC codewords. + * + * @return integer + */ + public function getTotalEcCodewords() + { + return $this->ecCodewordsPerBlock * $this->getNumBlocks(); + } + + /** + * Gets the EC blocks included in this collection. + * + * @return SplFixedArray + */ + public function getEcBlocks() + { + return $this->ecBlocks; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/ErrorCorrectionLevel.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/ErrorCorrectionLevel.php new file mode 100644 index 00000000..bd0a60a3 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/ErrorCorrectionLevel.php @@ -0,0 +1,62 @@ +value) { + case self::L: + return 0; + break; + + case self::M: + return 1; + break; + + case self::Q: + return 2; + break; + + case self::H: + return 3; + break; + } + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/FormatInformation.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/FormatInformation.php new file mode 100644 index 00000000..5ec9ffd4 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/FormatInformation.php @@ -0,0 +1,236 @@ +ecLevel = new ErrorCorrectionLevel(($formatInfo >> 3) & 0x3); + $this->dataMask = $formatInfo & 0x7; + } + + /** + * Checks how many bits are different between two integers. + * + * @param integer $a + * @param integer $b + * @return integer + */ + public static function numBitsDiffering($a, $b) + { + $a ^= $b; + + return ( + self::$bitsSetInHalfByte[$a & 0xf] + + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 4) & 0xf)] + + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 8) & 0xf)] + + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 12) & 0xf)] + + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 16) & 0xf)] + + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 20) & 0xf)] + + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 24) & 0xf)] + + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 28) & 0xf)] + ); + } + + /** + * Decodes format information. + * + * @param integer $maskedFormatInfo1 + * @param integer $maskedFormatInfo2 + * @return FormatInformation|null + */ + public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) + { + $formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2); + + if ($formatInfo !== null) { + return $formatInfo; + } + + // Should return null, but, some QR codes apparently do not mask this + // info. Try again by actually masking the pattern first. + return self::doDecodeFormatInformation( + $maskedFormatInfo1 ^ self::FORMAT_INFO_MASK_QR, + $maskedFormatInfo2 ^ self::FORMAT_INFO_MASK_QR + ); + } + + /** + * Internal method for decoding format information. + * + * @param integer $maskedFormatInfo1 + * @param integer $maskedFormatInfo2 + * @return FormatInformation|null + */ + protected static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) + { + $bestDifference = PHP_INT_MAX; + $bestFormatInfo = 0; + + foreach (self::$formatInfoDecodeLookup as $decodeInfo) { + $targetInfo = $decodeInfo[0]; + + if ($targetInfo === $maskedFormatInfo1 || $targetInfo === $maskedFormatInfo2) { + // Found an exact match + return new self($decodeInfo[1]); + } + + $bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo); + + if ($bitsDifference < $bestDifference) { + $bestFormatInfo = $decodeInfo[1]; + $bestDifference = $bitsDifference; + } + + if ($maskedFormatInfo1 !== $maskedFormatInfo2) { + // Also try the other option + $bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo); + + if ($bitsDifference < $bestDifference) { + $bestFormatInfo = $decodeInfo[1]; + $bestDifference = $bitsDifference; + } + } + } + + // Hamming distance of the 32 masked codes is 7, by construction, so + // <= 3 bits differing means we found a match. + if ($bestDifference <= 3) { + return new self($bestFormatInfo); + } + + return null; + } + + /** + * Gets the error correction level. + * + * @return ErrorCorrectionLevel + */ + public function getErrorCorrectionLevel() + { + return $this->ecLevel; + } + + /** + * Gets the data mask. + * + * @return integer + */ + public function getDataMask() + { + return $this->dataMask; + } + + /** + * Hashes the code of the EC level. + * + * @return integer + */ + public function hashCode() + { + return ($this->ecLevel->get() << 3) | $this->dataMask; + } + + /** + * Verifies if this instance equals another one. + * + * @param mixed $other + * @return boolean + */ + public function equals($other) { + if (!$other instanceof self) { + return false; + } + + return ( + $this->ecLevel->get() === $other->getErrorCorrectionLevel()->get() + && $this->dataMask === $other->getDataMask() + ); + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/Mode.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/Mode.php new file mode 100644 index 00000000..8faf344e --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/Mode.php @@ -0,0 +1,70 @@ + array(0, 0, 0), + self::NUMERIC => array(10, 12, 14), + self::ALPHANUMERIC => array(9, 11, 13), + self::STRUCTURED_APPEND => array(0, 0, 0), + self::BYTE => array(8, 16, 16), + self::ECI => array(0, 0, 0), + self::KANJI => array(8, 10, 12), + self::FNC1_FIRST_POSITION => array(0, 0, 0), + self::FNC1_SECOND_POSITION => array(0, 0, 0), + self::HANZI => array(8, 10, 12), + ); + + /** + * Gets the number of bits used in a specific QR code version. + * + * @param Version $version + * @return integer + */ + public function getCharacterCountBits(Version $version) + { + $number = $version->getVersionNumber(); + + if ($number <= 9) { + $offset = 0; + } elseif ($number <= 26) { + $offset = 1; + } else { + $offset = 2; + } + + return self::$characterCountBitsForVersions[$this->value][$offset]; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/ReedSolomonCodec.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/ReedSolomonCodec.php new file mode 100644 index 00000000..e8d45b94 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/ReedSolomonCodec.php @@ -0,0 +1,476 @@ + 8) { + throw new Exception\InvalidArgumentException('Symbol size must be between 0 and 8'); + } + + if ($firstRoot < 0 || $firstRoot >= (1 << $symbolSize)) { + throw new Exception\InvalidArgumentException('First root must be between 0 and ' . (1 << $symbolSize)); + } + + if ($numRoots < 0 || $numRoots >= (1 << $symbolSize)) { + throw new Exception\InvalidArgumentException('Num roots must be between 0 and ' . (1 << $symbolSize)); + } + + if ($padding < 0 || $padding >= ((1 << $symbolSize) - 1 - $numRoots)) { + throw new Exception\InvalidArgumentException('Padding must be between 0 and ' . ((1 << $symbolSize) - 1 - $numRoots)); + } + + $this->symbolSize = $symbolSize; + $this->blockSize = (1 << $symbolSize) - 1; + $this->padding = $padding; + $this->alphaTo = SplFixedArray::fromArray(array_fill(0, $this->blockSize + 1, 0), false); + $this->indexOf = SplFixedArray::fromArray(array_fill(0, $this->blockSize + 1, 0), false); + + // Generate galous field lookup table + $this->indexOf[0] = $this->blockSize; + $this->alphaTo[$this->blockSize] = 0; + + $sr = 1; + + for ($i = 0; $i < $this->blockSize; $i++) { + $this->indexOf[$sr] = $i; + $this->alphaTo[$i] = $sr; + + $sr <<= 1; + + if ($sr & (1 << $symbolSize)) { + $sr ^= $gfPoly; + } + + $sr &= $this->blockSize; + } + + if ($sr !== 1) { + throw new Exception\RuntimeException('Field generator polynomial is not primitive'); + } + + // Form RS code generator polynomial from its roots + $this->generatorPoly = SplFixedArray::fromArray(array_fill(0, $numRoots + 1, 0), false); + $this->firstRoot = $firstRoot; + $this->primitive = $primitive; + $this->numRoots = $numRoots; + + // Find prim-th root of 1, used in decoding + for ($iPrimitive = 1; ($iPrimitive % $primitive) !== 0; $iPrimitive += $this->blockSize); + $this->iPrimitive = intval($iPrimitive / $primitive); + + $this->generatorPoly[0] = 1; + + for ($i = 0, $root = $firstRoot * $primitive; $i < $numRoots; $i++, $root += $primitive) { + $this->generatorPoly[$i + 1] = 1; + + for ($j = $i; $j > 0; $j--) { + if ($this->generatorPoly[$j] !== 0) { + $this->generatorPoly[$j] = $this->generatorPoly[$j - 1] ^ $this->alphaTo[$this->modNn($this->indexOf[$this->generatorPoly[$j]] + $root)]; + } else { + $this->generatorPoly[$j] = $this->generatorPoly[$j - 1]; + } + } + + $this->generatorPoly[$j] = $this->alphaTo[$this->modNn($this->indexOf[$this->generatorPoly[0]] + $root)]; + } + + // Convert generator poly to index form for quicker encoding + for ($i = 0; $i <= $numRoots; $i++) { + $this->generatorPoly[$i] = $this->indexOf[$this->generatorPoly[$i]]; + } + } + + /** + * Encodes data and writes result back into parity array. + * + * @param SplFixedArray $data + * @param SplFixedArray $parity + * @return void + */ + public function encode(SplFixedArray $data, SplFixedArray $parity) + { + for ($i = 0; $i < $this->numRoots; $i++) { + $parity[$i] = 0; + } + + $iterations = $this->blockSize - $this->numRoots - $this->padding; + + for ($i = 0; $i < $iterations; $i++) { + $feedback = $this->indexOf[$data[$i] ^ $parity[0]]; + + if ($feedback !== $this->blockSize) { + // Feedback term is non-zero + $feedback = $this->modNn($this->blockSize - $this->generatorPoly[$this->numRoots] + $feedback); + + for ($j = 1; $j < $this->numRoots; $j++) { + $parity[$j] = $parity[$j] ^ $this->alphaTo[$this->modNn($feedback + $this->generatorPoly[$this->numRoots - $j])]; + } + } + + for ($j = 0; $j < $this->numRoots - 1; $j++) { + $parity[$j] = $parity[$j + 1]; + } + + if ($feedback !== $this->blockSize) { + $parity[$this->numRoots - 1] = $this->alphaTo[$this->modNn($feedback + $this->generatorPoly[0])]; + } else { + $parity[$this->numRoots - 1] = 0; + } + } + } + + /** + * Decodes received data. + * + * @param SplFixedArray $data + * @param SplFixedArray|null $erasures + * @return null|integer + */ + public function decode(SplFixedArray $data, SplFixedArray $erasures = null) + { + // This speeds up the initialization a bit. + $numRootsPlusOne = SplFixedArray::fromArray(array_fill(0, $this->numRoots + 1, 0), false); + $numRoots = SplFixedArray::fromArray(array_fill(0, $this->numRoots, 0), false); + + $lambda = clone $numRootsPlusOne; + $b = clone $numRootsPlusOne; + $t = clone $numRootsPlusOne; + $omega = clone $numRootsPlusOne; + $root = clone $numRoots; + $loc = clone $numRoots; + + $numErasures = ($erasures !== null ? count($erasures) : 0); + + // Form the Syndromes; i.e., evaluate data(x) at roots of g(x) + $syndromes = SplFixedArray::fromArray(array_fill(0, $this->numRoots, $data[0]), false); + + for ($i = 1; $i < $this->blockSize - $this->padding; $i++) { + for ($j = 0; $j < $this->numRoots; $j++) { + if ($syndromes[$j] === 0) { + $syndromes[$j] = $data[$i]; + } else { + $syndromes[$j] = $data[$i] ^ $this->alphaTo[ + $this->modNn($this->indexOf[$syndromes[$j]] + ($this->firstRoot + $j) * $this->primitive) + ]; + } + } + } + + // Convert syndromes to index form, checking for nonzero conditions + $syndromeError = 0; + + for ($i = 0; $i < $this->numRoots; $i++) { + $syndromeError |= $syndromes[$i]; + $syndromes[$i] = $this->indexOf[$syndromes[$i]]; + } + + if (!$syndromeError) { + // If syndrome is zero, data[] is a codeword and there are no errors + // to correct, so return data[] unmodified. + return 0; + } + + $lambda[0] = 1; + + if ($numErasures > 0) { + // Init lambda to be the erasure locator polynomial + $lambda[1] = $this->alphaTo[$this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[0]))]; + + for ($i = 1; $i < $numErasures; $i++) { + $u = $this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[$i])); + + for ($j = $i + 1; $j > 0; $j--) { + $tmp = $this->indexOf[$lambda[$j - 1]]; + + if ($tmp !== $this->blockSize) { + $lambda[$j] = $lambda[$j] ^ $this->alphaTo[$this->modNn($u + $tmp)]; + } + } + } + } + + for ($i = 0; $i <= $this->numRoots; $i++) { + $b[$i] = $this->indexOf[$lambda[$i]]; + } + + // Begin Berlekamp-Massey algorithm to determine error+erasure locator + // polynomial + $r = $numErasures; + $el = $numErasures; + + while (++$r <= $this->numRoots) { + // Compute discrepancy at the r-th step in poly form + $discrepancyR = 0; + + for ($i = 0; $i < $r; $i++) { + if ($lambda[$i] !== 0 && $syndromes[$r - $i - 1] !== $this->blockSize) { + $discrepancyR ^= $this->alphaTo[$this->modNn($this->indexOf[$lambda[$i]] + $syndromes[$r - $i - 1])]; + } + } + + $discrepancyR = $this->indexOf[$discrepancyR]; + + if ($discrepancyR === $this->blockSize) { + $tmp = $b->toArray(); + array_unshift($tmp, $this->blockSize); + array_pop($tmp); + $b = SplFixedArray::fromArray($tmp, false); + } else { + $t[0] = $lambda[0]; + + for ($i = 0; $i < $this->numRoots; $i++) { + if ($b[$i] !== $this->blockSize) { + $t[$i + 1] = $lambda[$i + 1] ^ $this->alphaTo[$this->modNn($discrepancyR + $b[$i])]; + } else { + $t[$i + 1] = $lambda[$i + 1]; + } + } + + if (2 * $el <= $r + $numErasures - 1) { + $el = $r + $numErasures - $el; + + for ($i = 0; $i <= $this->numRoots; $i++) { + $b[$i] = ( + $lambda[$i] === 0 + ? $this->blockSize + : $this->modNn($this->indexOf[$lambda[$i]] - $discrepancyR + $this->blockSize) + ); + } + } else { + $tmp = $b->toArray(); + array_unshift($tmp, $this->blockSize); + array_pop($tmp); + $b = SplFixedArray::fromArray($tmp, false); + } + + $lambda = clone $t; + } + } + + // Convert lambda to index form and compute deg(lambda(x)) + $degLambda = 0; + + for ($i = 0; $i <= $this->numRoots; $i++) { + $lambda[$i] = $this->indexOf[$lambda[$i]]; + + if ($lambda[$i] !== $this->blockSize) { + $degLambda = $i; + } + } + + // Find roots of the error+erasure locator polynomial by Chien search. + $reg = clone $lambda; + $reg[0] = 0; + $count = 0; + + for ($i = 1, $k = $this->iPrimitive - 1; $i <= $this->blockSize; $i++, $k = $this->modNn($k + $this->iPrimitive)) { + $q = 1; + + for ($j = $degLambda; $j > 0; $j--) { + if ($reg[$j] !== $this->blockSize) { + $reg[$j] = $this->modNn($reg[$j] + $j); + $q ^= $this->alphaTo[$reg[$j]]; + } + } + + if ($q !== 0) { + // Not a root + continue; + } + + // Store root (index-form) and error location number + $root[$count] = $i; + $loc[$count] = $k; + + if (++$count === $degLambda) { + break; + } + } + + if ($degLambda !== $count) { + // deg(lambda) unequal to number of roots: uncorreactable error + // detected + return null; + } + + // Compute err+eras evaluate poly omega(x) = s(x)*lambda(x) (modulo + // x**numRoots). In index form. Also find deg(omega). + $degOmega = $degLambda - 1; + + for ($i = 0; $i <= $degOmega; $i++) { + $tmp = 0; + + for ($j = $i; $j >= 0; $j--) { + if ($syndromes[$i - $j] !== $this->blockSize && $lambda[$j] !== $this->blockSize) { + $tmp ^= $this->alphaTo[$this->modNn($syndromes[$i - $j] + $lambda[$j])]; + } + } + + $omega[$i] = $this->indexOf[$tmp]; + } + + // Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = + // inv(X(l))**(firstRoot-1) and den = lambda_pr(inv(X(l))) all in poly + // form. + for ($j = $count - 1; $j >= 0; $j--) { + $num1 = 0; + + for ($i = $degOmega; $i >= 0; $i--) { + if ($omega[$i] !== $this->blockSize) { + $num1 ^= $this->alphaTo[$this->modNn($omega[$i] + $i * $root[$j])]; + } + } + + $num2 = $this->alphaTo[$this->modNn($root[$j] * ($this->firstRoot - 1) + $this->blockSize)]; + $den = 0; + + // lambda[i+1] for i even is the formal derivativelambda_pr of + // lambda[i] + for ($i = min($degLambda, $this->numRoots - 1) & ~1; $i >= 0; $i -= 2) { + if ($lambda[$i + 1] !== $this->blockSize) { + $den ^= $this->alphaTo[$this->modNn($lambda[$i + 1] + $i * $root[$j])]; + } + } + + // Apply error to data + if ($num1 !== 0 && $loc[$j] >= $this->padding) { + $data[$loc[$j] - $this->padding] = $data[$loc[$j] - $this->padding] ^ ( + $this->alphaTo[ + $this->modNn( + $this->indexOf[$num1] + $this->indexOf[$num2] + $this->blockSize - $this->indexOf[$den] + ) + ] + ); + } + } + + if ($erasures !== null) { + if (count($erasures) < $count) { + $erasures->setSize($count); + } + + for ($i = 0; $i < $count; $i++) { + $erasures[$i] = $loc[$i]; + } + } + + return $count; + } + + /** + * Computes $x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, without a slow + * divide. + * + * @param itneger $x + * @return integer + */ + protected function modNn($x) + { + while ($x >= $this->blockSize) { + $x -= $this->blockSize; + $x = ($x >> $this->symbolSize) + ($x & $this->blockSize); + } + + return $x; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/Version.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/Version.php new file mode 100644 index 00000000..a4df355f --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Common/Version.php @@ -0,0 +1,687 @@ +versionNumber = $versionNumber; + $this->alignmentPatternCenters = $alignmentPatternCenters; + $this->errorCorrectionBlocks = $ecBlocks; + + $totalCodewords = 0; + $ecCodewords = $ecBlocks[0]->getEcCodewordsPerBlock(); + + foreach ($ecBlocks[0]->getEcBlocks() as $ecBlock) { + $totalCodewords += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords); + } + + $this->totalCodewords = $totalCodewords; + } + + /** + * Gets the version number. + * + * @return integer + */ + public function getVersionNumber() + { + return $this->versionNumber; + } + + /** + * Gets the alignment pattern centers. + * + * @return SplFixedArray + */ + public function getAlignmentPatternCenters() + { + return $this->alignmentPatternCenters; + } + + /** + * Gets the total number of codewords. + * + * @return integer + */ + public function getTotalCodewords() + { + return $this->totalCodewords; + } + + /** + * Gets the dimension for the current version. + * + * @return integer + */ + public function getDimensionForVersion() + { + return 17 + 4 * $this->versionNumber; + } + + /** + * Gets the number of EC blocks for a specific EC level. + * + * @param ErrorCorrectionLevel $ecLevel + * @return integer + */ + public function getEcBlocksForLevel(ErrorCorrectionLevel $ecLevel) + { + return $this->errorCorrectionBlocks[$ecLevel->getOrdinal()]; + } + + /** + * Gets a provisional version number for a specific dimension. + * + * @param integer $dimension + * @return Version + * @throws Exception\InvalidArgumentException + */ + public static function getProvisionalVersionForDimension($dimension) + { + if ($dimension % 4 !== 1) { + throw new Exception\InvalidArgumentException('Dimension is not 1 mod 4'); + } + + return self::getVersionForNumber(($dimension - 17) >> 2); + } + + /** + * Gets a version instance for a specific version number. + * + * @param integer $versionNumber + * @return Version + * @throws Exception\InvalidArgumentException + */ + public static function getVersionForNumber($versionNumber) + { + if ($versionNumber < 1 || $versionNumber > 40) { + throw new Exception\InvalidArgumentException('Version number must be between 1 and 40'); + } + + if (!isset(self::$versions[$versionNumber])) { + self::buildVersion($versionNumber); + } + + return self::$versions[$versionNumber - 1]; + } + + /** + * Decodes version information from an integer and returns the version. + * + * @param integer $versionBits + * @return Version|null + */ + public static function decodeVersionInformation($versionBits) + { + $bestDifference = PHP_INT_MAX; + $bestVersion = 0; + + foreach (self::$versionDecodeInfo as $i => $targetVersion) { + if ($targetVersion === $versionBits) { + return self::getVersionForNumber($i + 7); + } + + $bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion); + + if ($bitsDifference < $bestDifference) { + $bestVersion = $i + 7; + $bestDifference = $bitsDifference; + } + } + + if ($bestDifference <= 3) { + return self::getVersionForNumber($bestVersion); + } + + return null; + } + + /** + * Builds the function pattern for the current version. + * + * @return BitMatrix + */ + public function buildFunctionPattern() + { + $dimension = $this->getDimensionForVersion(); + $bitMatrix = new BitMatrix($dimension); + + // Top left finder pattern + separator + format + $bitMatrix->setRegion(0, 0, 9, 9); + // Top right finder pattern + separator + format + $bitMatrix->setRegion($dimension - 8, 0, 8, 9); + // Bottom left finder pattern + separator + format + $bitMatrix->setRegion(0, $dimension - 8, 9, 8); + + // Alignment patterns + $max = count($this->alignmentPatternCenters); + + for ($x = 0; $x < $max; $x++) { + $i = $this->alignmentPatternCenters[$x] - 2; + + for ($y = 0; $y < $max; $y++) { + if (($x === 0 && ($y === 0 || $y === $max - 1)) || ($x === $max - 1 && $y === 0)) { + // No alignment patterns near the three finder paterns + continue; + } + + $bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5); + } + } + + // Vertical timing pattern + $bitMatrix->setRegion(6, 9, 1, $dimension - 17); + // Horizontal timing pattern + $bitMatrix->setRegion(9, 6, $dimension - 17, 1); + + if ($this->versionNumber > 6) { + // Version info, top right + $bitMatrix->setRegion($dimension - 11, 0, 3, 6); + // Version info, bottom left + $bitMatrix->setRegion(0, $dimension - 11, 6, 3); + } + + return $bitMatrix; + } + + /** + * Returns a string representation for the version. + * + * @return string + */ + public function __toString() + { + return (string) $this->versionNumber; + } + + /** + * Build and cache a specific version. + * + * See ISO 18004:2006 6.5.1 Table 9. + * + * @param integer $versionNumber + * @return void + */ + protected static function buildVersion($versionNumber) + { + switch ($versionNumber) { + case 1: + $patterns = array(); + $ecBlocks = array( + new EcBlocks(7, new EcBlock(1, 19)), + new EcBlocks(10, new EcBlock(1, 16)), + new EcBlocks(13, new EcBlock(1, 13)), + new EcBlocks(17, new EcBlock(1, 9)), + ); + break; + + case 2: + $patterns = array(6, 18); + $ecBlocks = array( + new EcBlocks(10, new EcBlock(1, 34)), + new EcBlocks(16, new EcBlock(1, 28)), + new EcBlocks(22, new EcBlock(1, 22)), + new EcBlocks(28, new EcBlock(1, 16)), + ); + break; + + case 3: + $patterns = array(6, 22); + $ecBlocks = array( + new EcBlocks(15, new EcBlock(1, 55)), + new EcBlocks(26, new EcBlock(1, 44)), + new EcBlocks(18, new EcBlock(2, 17)), + new EcBlocks(22, new EcBlock(2, 13)), + ); + break; + + case 4: + $patterns = array(6, 26); + $ecBlocks = array( + new EcBlocks(20, new EcBlock(1, 80)), + new EcBlocks(18, new EcBlock(2, 32)), + new EcBlocks(26, new EcBlock(3, 24)), + new EcBlocks(16, new EcBlock(4, 9)), + ); + break; + + case 5: + $patterns = array(6, 30); + $ecBlocks = array( + new EcBlocks(26, new EcBlock(1, 108)), + new EcBlocks(24, new EcBlock(2, 43)), + new EcBlocks(18, new EcBlock(2, 15), new EcBlock(2, 16)), + new EcBlocks(22, new EcBlock(2, 11), new EcBlock(2, 12)), + ); + break; + + case 6: + $patterns = array(6, 34); + $ecBlocks = array( + new EcBlocks(18, new EcBlock(2, 68)), + new EcBlocks(16, new EcBlock(4, 27)), + new EcBlocks(24, new EcBlock(4, 19)), + new EcBlocks(28, new EcBlock(4, 15)), + ); + break; + + case 7: + $patterns = array(6, 22, 38); + $ecBlocks = array( + new EcBlocks(20, new EcBlock(2, 78)), + new EcBlocks(18, new EcBlock(4, 31)), + new EcBlocks(18, new EcBlock(2, 14), new EcBlock(4, 15)), + new EcBlocks(26, new EcBlock(4, 13)), + ); + break; + + case 8: + $patterns = array(6, 24, 42); + $ecBlocks = array( + new EcBlocks(24, new EcBlock(2, 97)), + new EcBlocks(22, new EcBlock(2, 38), new EcBlock(2, 39)), + new EcBlocks(22, new EcBlock(4, 18), new EcBlock(2, 19)), + new EcBlocks(26, new EcBlock(4, 14), new EcBlock(2, 15)), + ); + break; + + case 9: + $patterns = array(6, 26, 46); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(2, 116)), + new EcBlocks(22, new EcBlock(3, 36), new EcBlock(2, 37)), + new EcBlocks(20, new EcBlock(4, 16), new EcBlock(4, 17)), + new EcBlocks(24, new EcBlock(4, 12), new EcBlock(4, 13)), + ); + break; + + case 10: + $patterns = array(6, 28, 50); + $ecBlocks = array( + new EcBlocks(18, new EcBlock(2, 68), new EcBlock(2, 69)), + new EcBlocks(26, new EcBlock(4, 43), new EcBlock(1, 44)), + new EcBlocks(24, new EcBlock(6, 19), new EcBlock(2, 20)), + new EcBlocks(28, new EcBlock(6, 15), new EcBlock(2, 16)), + ); + break; + + case 11: + $patterns = array(6, 30, 54); + $ecBlocks = array( + new EcBlocks(20, new EcBlock(4, 81)), + new EcBlocks(30, new EcBlock(1, 50), new EcBlock(4, 51)), + new EcBlocks(28, new EcBlock(4, 22), new EcBlock(4, 23)), + new EcBlocks(24, new EcBlock(3, 12), new EcBlock(8, 13)), + ); + break; + + case 12: + $patterns = array(6, 32, 58); + $ecBlocks = array( + new EcBlocks(24, new EcBlock(2, 92), new EcBlock(2, 93)), + new EcBlocks(22, new EcBlock(6, 36), new EcBlock(2, 37)), + new EcBlocks(26, new EcBlock(4, 20), new EcBlock(6, 21)), + new EcBlocks(28, new EcBlock(7, 14), new EcBlock(4, 15)), + ); + break; + + case 13: + $patterns = array(6, 34, 62); + $ecBlocks = array( + new EcBlocks(26, new EcBlock(4, 107)), + new EcBlocks(22, new EcBlock(8, 37), new EcBlock(1, 38)), + new EcBlocks(24, new EcBlock(8, 20), new EcBlock(4, 21)), + new EcBlocks(22, new EcBlock(12, 11), new EcBlock(4, 12)), + ); + break; + + case 14: + $patterns = array(6, 26, 46, 66); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(3, 115), new EcBlock(1, 116)), + new EcBlocks(24, new EcBlock(4, 40), new EcBlock(5, 41)), + new EcBlocks(20, new EcBlock(11, 16), new EcBlock(5, 17)), + new EcBlocks(24, new EcBlock(11, 12), new EcBlock(5, 13)), + ); + break; + + case 15: + $patterns = array(6, 26, 48, 70); + $ecBlocks = array( + new EcBlocks(22, new EcBlock(5, 87), new EcBlock(1, 88)), + new EcBlocks(24, new EcBlock(5, 41), new EcBlock(5, 42)), + new EcBlocks(30, new EcBlock(5, 24), new EcBlock(7, 25)), + new EcBlocks(24, new EcBlock(11, 12), new EcBlock(7, 13)), + ); + break; + + case 16: + $patterns = array(6, 26, 50, 74); + $ecBlocks = array( + new EcBlocks(24, new EcBlock(5, 98), new EcBlock(1, 99)), + new EcBlocks(28, new EcBlock(7, 45), new EcBlock(3, 46)), + new EcBlocks(24, new EcBlock(15, 19), new EcBlock(2, 20)), + new EcBlocks(30, new EcBlock(3, 15), new EcBlock(13, 16)), + ); + break; + + case 17: + $patterns = array(6, 30, 54, 78); + $ecBlocks = array( + new EcBlocks(28, new EcBlock(1, 107), new EcBlock(5, 108)), + new EcBlocks(28, new EcBlock(10, 46), new EcBlock(1, 47)), + new EcBlocks(28, new EcBlock(1, 22), new EcBlock(15, 23)), + new EcBlocks(28, new EcBlock(2, 14), new EcBlock(17, 15)), + ); + break; + + case 18: + $patterns = array(6, 30, 56, 82); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(5, 120), new EcBlock(1, 121)), + new EcBlocks(26, new EcBlock(9, 43), new EcBlock(4, 44)), + new EcBlocks(28, new EcBlock(17, 22), new EcBlock(1, 23)), + new EcBlocks(28, new EcBlock(2, 14), new EcBlock(19, 15)), + ); + break; + + case 19: + $patterns = array(6, 30, 58, 86); + $ecBlocks = array( + new EcBlocks(28, new EcBlock(3, 113), new EcBlock(4, 114)), + new EcBlocks(26, new EcBlock(3, 44), new EcBlock(11, 45)), + new EcBlocks(26, new EcBlock(17, 21), new EcBlock(4, 22)), + new EcBlocks(26, new EcBlock(9, 13), new EcBlock(16, 14)), + ); + break; + + case 20: + $patterns = array(6, 34, 62, 90); + $ecBlocks = array( + new EcBlocks(28, new EcBlock(3, 107), new EcBlock(5, 108)), + new EcBlocks(26, new EcBlock(3, 41), new EcBlock(13, 42)), + new EcBlocks(30, new EcBlock(15, 24), new EcBlock(5, 25)), + new EcBlocks(28, new EcBlock(15, 15), new EcBlock(10, 16)), + ); + break; + + case 21: + $patterns = array(6, 28, 50, 72, 94); + $ecBlocks = array( + new EcBlocks(28, new EcBlock(4, 116), new EcBlock(4, 117)), + new EcBlocks(26, new EcBlock(17, 42)), + new EcBlocks(28, new EcBlock(17, 22), new EcBlock(6, 23)), + new EcBlocks(30, new EcBlock(19, 16), new EcBlock(6, 17)), + ); + break; + + case 22: + $patterns = array(6, 26, 50, 74, 98); + $ecBlocks = array( + new EcBlocks(28, new EcBlock(2, 111), new EcBlock(7, 112)), + new EcBlocks(28, new EcBlock(17, 46)), + new EcBlocks(30, new EcBlock(7, 24), new EcBlock(16, 25)), + new EcBlocks(24, new EcBlock(34, 13)), + ); + break; + + case 23: + $patterns = array(6, 30, 54, 78, 102); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(4, 121), new EcBlock(5, 122)), + new EcBlocks(28, new EcBlock(4, 47), new EcBlock(14, 48)), + new EcBlocks(30, new EcBlock(11, 24), new EcBlock(14, 25)), + new EcBlocks(30, new EcBlock(16, 15), new EcBlock(14, 16)), + ); + break; + + case 24: + $patterns = array(6, 28, 54, 80, 106); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(6, 117), new EcBlock(4, 118)), + new EcBlocks(28, new EcBlock(6, 45), new EcBlock(14, 46)), + new EcBlocks(30, new EcBlock(11, 24), new EcBlock(16, 25)), + new EcBlocks(30, new EcBlock(30, 16), new EcBlock(2, 17)), + ); + break; + + case 25: + $patterns = array(6, 32, 58, 84, 110); + $ecBlocks = array( + new EcBlocks(26, new EcBlock(8, 106), new EcBlock(4, 107)), + new EcBlocks(28, new EcBlock(8, 47), new EcBlock(13, 48)), + new EcBlocks(30, new EcBlock(7, 24), new EcBlock(22, 25)), + new EcBlocks(30, new EcBlock(22, 15), new EcBlock(13, 16)), + ); + break; + + case 26: + $patterns = array(6, 30, 58, 86, 114); + $ecBlocks = array( + new EcBlocks(28, new EcBlock(10, 114), new EcBlock(2, 115)), + new EcBlocks(28, new EcBlock(19, 46), new EcBlock(4, 47)), + new EcBlocks(28, new EcBlock(28, 22), new EcBlock(6, 23)), + new EcBlocks(30, new EcBlock(33, 16), new EcBlock(4, 17)), + ); + break; + + case 27: + $patterns = array(6, 34, 62, 90, 118); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(8, 122), new EcBlock(4, 123)), + new EcBlocks(28, new EcBlock(22, 45), new EcBlock(3, 46)), + new EcBlocks(30, new EcBlock(8, 23), new EcBlock(26, 24)), + new EcBlocks(30, new EcBlock(12, 15), new EcBlock(28, 16)), + ); + break; + + case 28: + $patterns = array(6, 26, 50, 74, 98, 122); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(3, 117), new EcBlock(10, 118)), + new EcBlocks(28, new EcBlock(3, 45), new EcBlock(23, 46)), + new EcBlocks(30, new EcBlock(4, 24), new EcBlock(31, 25)), + new EcBlocks(30, new EcBlock(11, 15), new EcBlock(31, 16)), + ); + break; + + case 29: + $patterns = array(6, 30, 54, 78, 102, 126); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(7, 116), new EcBlock(7, 117)), + new EcBlocks(28, new EcBlock(21, 45), new EcBlock(7, 46)), + new EcBlocks(30, new EcBlock(1, 23), new EcBlock(37, 24)), + new EcBlocks(30, new EcBlock(19, 15), new EcBlock(26, 16)), + ); + break; + + case 30: + $patterns = array(6, 26, 52, 78, 104, 130); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(5, 115), new EcBlock(10, 116)), + new EcBlocks(28, new EcBlock(19, 47), new EcBlock(10, 48)), + new EcBlocks(30, new EcBlock(15, 24), new EcBlock(25, 25)), + new EcBlocks(30, new EcBlock(23, 15), new EcBlock(25, 16)), + ); + break; + + case 31: + $patterns = array(6, 30, 56, 82, 108, 134); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(13, 115), new EcBlock(3, 116)), + new EcBlocks(28, new EcBlock(2, 46), new EcBlock(29, 47)), + new EcBlocks(30, new EcBlock(42, 24), new EcBlock(1, 25)), + new EcBlocks(30, new EcBlock(23, 15), new EcBlock(28, 16)), + ); + break; + + case 32: + $patterns = array(6, 34, 60, 86, 112, 138); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(17, 115)), + new EcBlocks(28, new EcBlock(10, 46), new EcBlock(23, 47)), + new EcBlocks(30, new EcBlock(10, 24), new EcBlock(35, 25)), + new EcBlocks(30, new EcBlock(19, 15), new EcBlock(35, 16)), + ); + break; + + case 33: + $patterns = array(6, 30, 58, 86, 114, 142); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(17, 115), new EcBlock(1, 116)), + new EcBlocks(28, new EcBlock(14, 46), new EcBlock(21, 47)), + new EcBlocks(30, new EcBlock(29, 24), new EcBlock(19, 25)), + new EcBlocks(30, new EcBlock(11, 15), new EcBlock(46, 16)), + ); + break; + + case 34: + $patterns = array(6, 34, 62, 90, 118, 146); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(13, 115), new EcBlock(6, 116)), + new EcBlocks(28, new EcBlock(14, 46), new EcBlock(23, 47)), + new EcBlocks(30, new EcBlock(44, 24), new EcBlock(7, 25)), + new EcBlocks(30, new EcBlock(59, 16), new EcBlock(1, 17)), + ); + break; + + case 35: + $patterns = array(6, 30, 54, 78, 102, 126, 150); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(12, 121), new EcBlock(7, 122)), + new EcBlocks(28, new EcBlock(12, 47), new EcBlock(26, 48)), + new EcBlocks(30, new EcBlock(39, 24), new EcBlock(14, 25)), + new EcBlocks(30, new EcBlock(22, 15), new EcBlock(41, 16)), + ); + break; + + case 36: + $patterns = array(6, 24, 50, 76, 102, 128, 154); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(6, 121), new EcBlock(14, 122)), + new EcBlocks(28, new EcBlock(6, 47), new EcBlock(34, 48)), + new EcBlocks(30, new EcBlock(46, 24), new EcBlock(10, 25)), + new EcBlocks(30, new EcBlock(2, 15), new EcBlock(64, 16)), + ); + break; + + case 37: + $patterns = array(6, 28, 54, 80, 106, 132, 158); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(17, 122), new EcBlock(4, 123)), + new EcBlocks(28, new EcBlock(29, 46), new EcBlock(14, 47)), + new EcBlocks(30, new EcBlock(49, 24), new EcBlock(10, 25)), + new EcBlocks(30, new EcBlock(24, 15), new EcBlock(46, 16)), + ); + break; + + case 38: + $patterns = array(6, 32, 58, 84, 110, 136, 162); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(4, 122), new EcBlock(18, 123)), + new EcBlocks(28, new EcBlock(13, 46), new EcBlock(32, 47)), + new EcBlocks(30, new EcBlock(48, 24), new EcBlock(14, 25)), + new EcBlocks(30, new EcBlock(42, 15), new EcBlock(32, 16)), + ); + break; + + case 39: + $patterns = array(6, 26, 54, 82, 110, 138, 166); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(20, 117), new EcBlock(4, 118)), + new EcBlocks(28, new EcBlock(40, 47), new EcBlock(7, 48)), + new EcBlocks(30, new EcBlock(43, 24), new EcBlock(22, 25)), + new EcBlocks(30, new EcBlock(10, 15), new EcBlock(67, 16)), + ); + break; + + case 40: + $patterns = array(6, 30, 58, 86, 114, 142, 170); + $ecBlocks = array( + new EcBlocks(30, new EcBlock(19, 118), new EcBlock(6, 119)), + new EcBlocks(28, new EcBlock(18, 47), new EcBlock(31, 48)), + new EcBlocks(30, new EcBlock(34, 24), new EcBlock(34, 25)), + new EcBlocks(30, new EcBlock(20, 15), new EcBlock(61, 16)), + ); + break; + } + + self::$versions[$versionNumber - 1] = new self( + $versionNumber, + SplFixedArray::fromArray($patterns, false), + SplFixedArray::fromArray($ecBlocks, false) + ); + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/BlockPair.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/BlockPair.php new file mode 100644 index 00000000..090db297 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/BlockPair.php @@ -0,0 +1,64 @@ +dataBytes = $data; + $this->errorCorrectionBytes = $errorCorrection; + } + + /** + * Gets the data bytes. + * + * @return SplFixedArray + */ + public function getDataBytes() + { + return $this->dataBytes; + } + + /** + * Gets the error correction bytes. + * + * @return SplFixedArray + */ + public function getErrorCorrectionBytes() + { + return $this->errorCorrectionBytes; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/ByteMatrix.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/ByteMatrix.php new file mode 100644 index 00000000..a378f083 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/ByteMatrix.php @@ -0,0 +1,158 @@ +height = $height; + $this->width = $width; + $this->bytes = new SplFixedArray($height); + + for ($y = 0; $y < $height; $y++) { + $this->bytes[$y] = new SplFixedArray($width); + } + } + + /** + * Gets the width of the matrix. + * + * @return integer + */ + public function getWidth() + { + return $this->width; + } + + /** + * Gets the height of the matrix. + * + * @return integer + */ + public function getHeight() + { + return $this->height; + } + + /** + * Gets the internal representation of the matrix. + * + * @return SplFixedArray + */ + public function getArray() + { + return $this->bytes; + } + + /** + * Gets the byte for a specific position. + * + * @param integer $x + * @param integer $y + * @return integer + */ + public function get($x, $y) + { + return $this->bytes[$y][$x]; + } + + /** + * Sets the byte for a specific position. + * + * @param integer $x + * @param integer $y + * @param integer $value + * @return void + */ + public function set($x, $y, $value) + { + $this->bytes[$y][$x] = (int) $value; + } + + /** + * Clears the matrix with a specific value. + * + * @param integer $value + * @return void + */ + public function clear($value) + { + for ($y = 0; $y < $this->height; $y++) { + for ($x = 0; $x < $this->width; $x++) { + $this->bytes[$y][$x] = $value; + } + } + } + + /** + * Returns a string representation of the matrix. + * + * @return string + */ + public function __toString() + { + $result = ''; + + for ($y = 0; $y < $this->height; $y++) { + for ($x = 0; $x < $this->width; $x++) { + switch ($this->bytes[$y][$x]) { + case 0: + $result .= ' 0'; + break; + + case 1: + $result .= ' 1'; + break; + + default: + $result .= ' '; + break; + } + } + + $result .= "\n"; + } + + return $result; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/Encoder.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/Encoder.php new file mode 100644 index 00000000..c8efc35d --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/Encoder.php @@ -0,0 +1,687 @@ +get() === Mode::BYTE && $encoding !== self::DEFAULT_BYTE_MODE_ECODING) { + $eci = CharacterSetEci::getCharacterSetEciByName($encoding); + + if ($eci !== null) { + self::appendEci($eci, $headerBits); + } + } + + // (With ECI in place,) Write the mode marker + self::appendModeInfo($mode, $headerBits); + + // Collect data within the main segment, separately, to count its size + // if needed. Don't add it to main payload yet. + $dataBits = new BitArray(); + self::appendBytes($content, $mode, $dataBits, $encoding); + + // Hard part: need to know version to know how many bits length takes. + // But need to know how many bits it takes to know version. First we + // take a guess at version by assuming version will be the minimum, 1: + $provisionalBitsNeeded = $headerBits->getSize() + + $mode->getCharacterCountBits(Version::getVersionForNumber(1)) + + $dataBits->getSize(); + $provisionalVersion = self::chooseVersion($provisionalBitsNeeded, $ecLevel); + + // Use that guess to calculate the right version. I am still not sure + // this works in 100% of cases. + $bitsNeeded = $headerBits->getSize() + + $mode->getCharacterCountBits($provisionalVersion) + + $dataBits->getSize(); + $version = self::chooseVersion($bitsNeeded, $ecLevel); + + $headerAndDataBits = new BitArray(); + $headerAndDataBits->appendBitArray($headerBits); + + // Find "length" of main segment and write it. + $numLetters = ($mode->get() === Mode::BYTE ? $dataBits->getSizeInBytes() : strlen($content)); + self::appendLengthInfo($numLetters, $version, $mode, $headerAndDataBits); + + // Put data together into the overall payload. + $headerAndDataBits->appendBitArray($dataBits); + $ecBlocks = $version->getEcBlocksForLevel($ecLevel); + $numDataBytes = $version->getTotalCodewords() - $ecBlocks->getTotalEcCodewords(); + + // Terminate the bits properly. + self::terminateBits($numDataBytes, $headerAndDataBits); + + // Interleave data bits with error correction code. + $finalBits = self::interleaveWithEcBytes( + $headerAndDataBits, + $version->getTotalCodewords(), + $numDataBytes, + $ecBlocks->getNumBlocks() + ); + + $qrCode = new QrCode(); + $qrCode->setErrorCorrectionLevel($ecLevel); + $qrCode->setMode($mode); + $qrCode->setVersion($version); + + // Choose the mask pattern and set to "qrCode". + $dimension = $version->getDimensionForVersion(); + $matrix = new ByteMatrix($dimension, $dimension); + $maskPattern = self::chooseMaskPattern($finalBits, $ecLevel, $version, $matrix); + $qrCode->setMaskPattern($maskPattern); + + // Build the matrix and set it to "qrCode". + MatrixUtil::buildMatrix($finalBits, $ecLevel, $version, $maskPattern, $matrix); + $qrCode->setMatrix($matrix); + + return $qrCode; + } + + /** + * Gets the alphanumeric code for a byte. + * + * @param string|integer $code + * @return integer + */ + protected static function getAlphanumericCode($code) + { + $code = (is_string($code) ? ord($code) : $code); + + if (isset(self::$alphanumericTable[$code])) { + return self::$alphanumericTable[$code]; + } + + return -1; + } + + /** + * Chooses the best mode for a given content. + * + * @param string $content + * @param string $encoding + * @return Mode + */ + protected static function chooseMode($content, $encoding = null) + { + if (strcasecmp($encoding, 'SHIFT-JIS') === 0) { + return self::isOnlyDoubleByteKanji($content) ? new Mode(Mode::KANJI) : new Mode(Mode::BYTE); + } + + $hasNumeric = false; + $hasAlphanumeric = false; + $contentLength = strlen($content); + + for ($i = 0; $i < $contentLength; $i++) { + $char = $content[$i]; + + if (ctype_digit($char)) { + $hasNumeric = true; + } elseif (self::getAlphanumericCode($char) !== -1) { + $hasAlphanumeric = true; + } else { + return new Mode(Mode::BYTE); + } + } + + if ($hasAlphanumeric) { + return new Mode(Mode::ALPHANUMERIC); + } elseif ($hasNumeric) { + return new Mode(Mode::NUMERIC); + } + + return new Mode(Mode::BYTE); + } + + /** + * Calculates the mask penalty for a matrix. + * + * @param ByteMatrix $matrix + * @return integer + */ + protected static function calculateMaskPenalty(ByteMatrix $matrix) + { + return ( + MaskUtil::applyMaskPenaltyRule1($matrix) + + MaskUtil::applyMaskPenaltyRule2($matrix) + + MaskUtil::applyMaskPenaltyRule3($matrix) + + MaskUtil::applyMaskPenaltyRule4($matrix) + ); + } + + /** + * Chooses the best mask pattern for a matrix. + * + * @param BitArray $bits + * @param ErrorCorrectionLevel $ecLevel + * @param Version $version + * @param ByteMatrix $matrix + * @return integer + */ + protected static function chooseMaskPattern( + BitArray $bits, + ErrorCorrectionLevel $ecLevel, + Version $version, + ByteMatrix $matrix + ) { + $minPenality = PHP_INT_MAX; + $bestMaskPattern = -1; + + for ($maskPattern = 0; $maskPattern < QrCode::NUM_MASK_PATTERNS; $maskPattern++) { + MatrixUtil::buildMatrix($bits, $ecLevel, $version, $maskPattern, $matrix); + $penalty = self::calculateMaskPenalty($matrix); + + if ($penalty < $minPenality) { + $minPenality = $penalty; + $bestMaskPattern = $maskPattern; + } + } + + return $bestMaskPattern; + } + + /** + * Chooses the best version for the input. + * + * @param integer $numInputBits + * @param ErrorCorrectionLevel $ecLevel + * @return Version + * @throws Exception\WriterException + */ + protected static function chooseVersion($numInputBits, ErrorCorrectionLevel $ecLevel) + { + for ($versionNum = 1; $versionNum <= 40; $versionNum++) { + $version = Version::getVersionForNumber($versionNum); + $numBytes = $version->getTotalCodewords(); + + $ecBlocks = $version->getEcBlocksForLevel($ecLevel); + $numEcBytes = $ecBlocks->getTotalEcCodewords(); + + $numDataBytes = $numBytes - $numEcBytes; + $totalInputBytes = intval(($numInputBits + 8) / 8); + + if ($numDataBytes >= $totalInputBytes) { + return $version; + } + } + + throw new Exception\WriterException('Data too big'); + } + + /** + * Terminates the bits in a bit array. + * + * @param integer $numDataBytes + * @param BitArray $bits + * @throws Exception\WriterException + */ + protected static function terminateBits($numDataBytes, BitArray $bits) + { + $capacity = $numDataBytes << 3; + + if ($bits->getSize() > $capacity) { + throw new Exception\WriterException('Data bits cannot fit in the QR code'); + } + + for ($i = 0; $i < 4 && $bits->getSize() < $capacity; $i++) { + $bits->appendBit(false); + } + + $numBitsInLastByte = $bits->getSize() & 0x7; + + if ($numBitsInLastByte > 0) { + for ($i = $numBitsInLastByte; $i < 8; $i++) { + $bits->appendBit(false); + } + } + + $numPaddingBytes = $numDataBytes - $bits->getSizeInBytes(); + + for ($i = 0; $i < $numPaddingBytes; $i++) { + $bits->appendBits(($i & 0x1) === 0 ? 0xec : 0x11, 8); + } + + if ($bits->getSize() !== $capacity) { + throw new Exception\WriterException('Bits size does not equal capacity'); + } + } + + /** + * Gets number of data- and EC bytes for a block ID. + * + * @param integer $numTotalBytes + * @param integer $numDataBytes + * @param integer $numRsBlocks + * @param integer $blockId + * @return array + * @throws Exception\WriterException + */ + protected static function getNumDataBytesAndNumEcBytesForBlockId( + $numTotalBytes, + $numDataBytes, + $numRsBlocks, + $blockId + ) { + if ($blockId >= $numRsBlocks) { + throw new Exception\WriterException('Block ID too large'); + } + + $numRsBlocksInGroup2 = $numTotalBytes % $numRsBlocks; + $numRsBlocksInGroup1 = $numRsBlocks - $numRsBlocksInGroup2; + $numTotalBytesInGroup1 = intval($numTotalBytes / $numRsBlocks); + $numTotalBytesInGroup2 = $numTotalBytesInGroup1 + 1; + $numDataBytesInGroup1 = intval($numDataBytes / $numRsBlocks); + $numDataBytesInGroup2 = $numDataBytesInGroup1 + 1; + $numEcBytesInGroup1 = $numTotalBytesInGroup1 - $numDataBytesInGroup1; + $numEcBytesInGroup2 = $numTotalBytesInGroup2 - $numDataBytesInGroup2; + + if ($numEcBytesInGroup1 !== $numEcBytesInGroup2) { + throw new Exception\WriterException('EC bytes mismatch'); + } + + if ($numRsBlocks !== $numRsBlocksInGroup1 + $numRsBlocksInGroup2) { + throw new Exception\WriterException('RS blocks mismatch'); + } + + if ($numTotalBytes !== + (($numDataBytesInGroup1 + $numEcBytesInGroup1) * $numRsBlocksInGroup1) + + (($numDataBytesInGroup2 + $numEcBytesInGroup2) * $numRsBlocksInGroup2) + ) { + throw new Exception\WriterException('Total bytes mismatch'); + } + + if ($blockId < $numRsBlocksInGroup1) { + return array($numDataBytesInGroup1, $numEcBytesInGroup1); + } else { + return array($numDataBytesInGroup2, $numEcBytesInGroup2); + } + } + + /** + * Interleaves data with EC bytes. + * + * @param BitArray $bits + * @param integer $numTotalBytes + * @param integer $numDataBytes + * @param integer $numRsBlocks + * @return BitArray + * @throws Exception\WriterException + */ + protected static function interleaveWithEcBytes(BitArray $bits, $numTotalBytes, $numDataBytes, $numRsBlocks) + { + if ($bits->getSizeInBytes() !== $numDataBytes) { + throw new Exception\WriterException('Number of bits and data bytes does not match'); + } + + $dataBytesOffset = 0; + $maxNumDataBytes = 0; + $maxNumEcBytes = 0; + + $blocks = new SplFixedArray($numRsBlocks); + + for ($i = 0; $i < $numRsBlocks; $i++) { + list($numDataBytesInBlock, $numEcBytesInBlock) = self::getNumDataBytesAndNumEcBytesForBlockId( + $numTotalBytes, + $numDataBytes, + $numRsBlocks, + $i + ); + + $size = $numDataBytesInBlock; + $dataBytes = $bits->toBytes(8 * $dataBytesOffset, $size); + $ecBytes = self::generateEcBytes($dataBytes, $numEcBytesInBlock); + $blocks[$i] = new BlockPair($dataBytes, $ecBytes); + + $maxNumDataBytes = max($maxNumDataBytes, $size); + $maxNumEcBytes = max($maxNumEcBytes, count($ecBytes)); + $dataBytesOffset += $numDataBytesInBlock; + } + + if ($numDataBytes !== $dataBytesOffset) { + throw new Exception\WriterException('Data bytes does not match offset'); + } + + $result = new BitArray(); + + for ($i = 0; $i < $maxNumDataBytes; $i++) { + foreach ($blocks as $block) { + $dataBytes = $block->getDataBytes(); + + if ($i < count($dataBytes)) { + $result->appendBits($dataBytes[$i], 8); + } + } + } + + for ($i = 0; $i < $maxNumEcBytes; $i++) { + foreach ($blocks as $block) { + $ecBytes = $block->getErrorCorrectionBytes(); + + if ($i < count($ecBytes)) { + $result->appendBits($ecBytes[$i], 8); + } + } + } + + if ($numTotalBytes !== $result->getSizeInBytes()) { + throw new Exception\WriterException('Interleaving error: ' . $numTotalBytes . ' and ' . $result->getSizeInBytes() . ' differ'); + } + + return $result; + } + + /** + * Generates EC bytes for given data. + * + * @param SplFixedArray $dataBytes + * @param integer $numEcBytesInBlock + * @return SplFixedArray + */ + protected static function generateEcBytes(SplFixedArray $dataBytes, $numEcBytesInBlock) + { + $numDataBytes = count($dataBytes); + $toEncode = new SplFixedArray($numDataBytes + $numEcBytesInBlock); + + for ($i = 0; $i < $numDataBytes; $i++) { + $toEncode[$i] = $dataBytes[$i] & 0xff; + } + + $ecBytes = new SplFixedArray($numEcBytesInBlock); + $codec = self::getCodec($numDataBytes, $numEcBytesInBlock); + $codec->encode($toEncode, $ecBytes); + + return $ecBytes; + } + + /** + * Gets an RS codec and caches it. + * + * @param integer $numDataBytes + * @param integer $numEcBytesInBlock + * @return ReedSolomonCodec + */ + protected static function getCodec($numDataBytes, $numEcBytesInBlock) + { + $cacheId = $numDataBytes . '-' . $numEcBytesInBlock; + + if (!isset(self::$codecs[$cacheId])) { + self::$codecs[$cacheId] = new ReedSolomonCodec( + 8, + 0x11d, + 0, + 1, + $numEcBytesInBlock, + 255 - $numDataBytes - $numEcBytesInBlock + ); + } + + return self::$codecs[$cacheId]; + } + + /** + * Appends mode information to a bit array. + * + * @param Mode $mode + * @param BitArray $bits + * @return void + */ + protected static function appendModeInfo(Mode $mode, BitArray $bits) + { + $bits->appendBits($mode->get(), 4); + } + + /** + * Appends length information to a bit array. + * + * @param integer $numLetters + * @param Version $version + * @param Mode $mode + * @param BitArray $bits + * @return void + * @throws Exception\WriterException + */ + protected static function appendLengthInfo($numLetters, Version $version, Mode $mode, BitArray $bits) + { + $numBits = $mode->getCharacterCountBits($version); + + if ($numLetters >= (1 << $numBits)) { + throw new Exception\WriterException($numLetters . ' is bigger than ' . ((1 << $numBits) - 1)); + } + + $bits->appendBits($numLetters, $numBits); + } + + /** + * Appends bytes to a bit array in a specific mode. + * + * @param stirng $content + * @param Mode $mode + * @param BitArray $bits + * @param string $encoding + * @return void + * @throws Exception\WriterException + */ + protected static function appendBytes($content, Mode $mode, BitArray $bits, $encoding) + { + switch ($mode->get()) { + case Mode::NUMERIC: + self::appendNumericBytes($content, $bits); + break; + + case Mode::ALPHANUMERIC: + self::appendAlphanumericBytes($content, $bits); + break; + + case Mode::BYTE: + self::append8BitBytes($content, $bits, $encoding); + break; + + case Mode::KANJI: + self::appendKanjiBytes($content, $bits); + break; + + default: + throw new Exception\WriterException('Invalid mode: ' . $mode->get()); + } + } + + /** + * Appends numeric bytes to a bit array. + * + * @param string $content + * @param BitArray $bits + * @return void + */ + protected static function appendNumericBytes($content, BitArray $bits) + { + $length = strlen($content); + $i = 0; + + while ($i < $length) { + $num1 = (int) $content[$i]; + + if ($i + 2 < $length) { + // Encode three numeric letters in ten bits. + $num2 = (int) $content[$i + 1]; + $num3 = (int) $content[$i + 2]; + $bits->appendBits($num1 * 100 + $num2 * 10 + $num3, 10); + $i += 3; + } elseif ($i + 1 < $length) { + // Encode two numeric letters in seven bits. + $num2 = (int) $content[$i + 1]; + $bits->appendBits($num1 * 10 + $num2, 7); + $i += 2; + } else { + // Encode one numeric letter in four bits. + $bits->appendBits($num1, 4); + $i++; + } + } + } + + /** + * Appends alpha-numeric bytes to a bit array. + * + * @param string $content + * @param BitArray $bits + * @return void + */ + protected static function appendAlphanumericBytes($content, BitArray $bits) + { + $length = strlen($content); + $i = 0; + + while ($i < $length) { + if (-1 === ($code1 = self::getAlphanumericCode($content[$i]))) { + throw new Exception\WriterException('Invalid alphanumeric code'); + } + + if ($i + 1 < $length) { + if (-1 === ($code2 = self::getAlphanumericCode($content[$i + 1]))) { + throw new Exception\WriterException('Invalid alphanumeric code'); + } + + // Encode two alphanumeric letters in 11 bits. + $bits->appendBits($code1 * 45 + $code2, 11); + $i += 2; + } else { + // Encode one alphanumeric letter in six bits. + $bits->appendBits($code1, 6); + $i++; + } + } + } + + /** + * Appends regular 8-bit bytes to a bit array. + * + * @param string $content + * @param BitArray $bits + * @return void + */ + protected static function append8BitBytes($content, BitArray $bits, $encoding) + { + if (false === ($bytes = @iconv('utf-8', $encoding, $content))) { + throw new Exception\WriterException('Could not encode content to ' . $encoding); + } + + $length = strlen($bytes); + + for ($i = 0; $i < $length; $i++) { + $bits->appendBits(ord($bytes[$i]), 8); + } + } + + /** + * Appends KANJI bytes to a bit array. + * + * @param string $content + * @param BitArray $bits + * @return void + */ + protected static function appendKanjiBytes($content, BitArray $bits) + { + if (strlen($content) % 2 > 0) { + // We just do a simple length check here. The for loop will check + // individual characters. + throw new Exception\WriterException('Content does not seem to be encoded in SHIFT-JIS'); + } + + $length = strlen($content); + + for ($i = 0; $i < $length; $i += 2) { + $byte1 = ord($content[$i]) & 0xff; + $byte2 = ord($content[$i + 1]) & 0xff; + $code = ($byte1 << 8) | $byte2; + + if ($code >= 0x8140 && $code <= 0x9ffc) { + $subtracted = $code - 0x8140; + } elseif ($code >= 0xe040 && $code <= 0xebbf) { + $subtracted = $code - 0xc140; + } else { + throw new Exception\WriterException('Invalid byte sequence'); + } + + $encoded = (($subtracted >> 8) * 0xc0) + ($subtracted & 0xff); + + $bits->appendBits($encoded, 13); + } + } + + /** + * Appends ECI information to a bit array. + * + * @param CharacterSetEci $eci + * @param BitArray $bits + * @return void + */ + protected static function appendEci(CharacterSetEci $eci, BitArray $bits) + { + $mode = new Mode(Mode::ECI); + $bits->appendBits($mode->get(), 4); + $bits->appendBits($eci->get(), 8); + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/MaskUtil.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/MaskUtil.php new file mode 100644 index 00000000..c294d557 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/MaskUtil.php @@ -0,0 +1,291 @@ +getArray(); + $width = $matrix->getWidth(); + $height = $matrix->getHeight(); + + for ($y = 0; $y < $height - 1; $y++) { + for ($x = 0; $x < $width - 1; $x++) { + $value = $array[$y][$x]; + + if ($value === $array[$y][$x + 1] && $value === $array[$y + 1][$x] && $value === $array[$y + 1][$x + 1]) { + $penalty++; + } + } + } + + return self::N2 * $penalty; + } + + /** + * Applies mask penalty rule 3 and returns the penalty. + * + * Finds consecutive cells of 00001011101 or 10111010000, and gives penalty + * to them. If we find patterns like 000010111010000, we give penalties + * twice (i.e. 40 * 2). + * + * @param ByteMatrix $matrix + * @return integer + */ + public static function applyMaskPenaltyRule3(ByteMatrix $matrix) + { + $penalty = 0; + $array = $matrix->getArray(); + $width = $matrix->getWidth(); + $height = $matrix->getHeight(); + + for ($y = 0; $y < $height; $y++) { + for ($x = 0; $x < $width; $x++) { + if ( + $x + 6 < $width + && $array[$y][$x] === 1 + && $array[$y][$x + 1] === 0 + && $array[$y][$x + 2] === 1 + && $array[$y][$x + 3] === 1 + && $array[$y][$x + 4] === 1 + && $array[$y][$x + 5] === 0 + && $array[$y][$x + 6] === 1 + && ( + ( + $x + 10 < $width + && $array[$y][$x + 7] === 0 + && $array[$y][$x + 8] === 0 + && $array[$y][$x + 9] === 0 + && $array[$y][$x + 10] === 0 + ) + || ( + $x - 4 >= 0 + && $array[$y][$x - 1] === 0 + && $array[$y][$x - 2] === 0 + && $array[$y][$x - 3] === 0 + && $array[$y][$x - 4] === 0 + ) + ) + ) { + $penalty += self::N3; + } + + if ( + $y + 6 < $height + && $array[$y][$x] === 1 + && $array[$y + 1][$x] === 0 + && $array[$y + 2][$x] === 1 + && $array[$y + 3][$x] === 1 + && $array[$y + 4][$x] === 1 + && $array[$y + 5][$x] === 0 + && $array[$y + 6][$x] === 1 + && ( + ( + $y + 10 < $height + && $array[$y + 7][$x] === 0 + && $array[$y + 8][$x] === 0 + && $array[$y + 9][$x] === 0 + && $array[$y + 10][$x] === 0 + ) + || ( + $y - 4 >= 0 + && $array[$y - 1][$x] === 0 + && $array[$y - 2][$x] === 0 + && $array[$y - 3][$x] === 0 + && $array[$y - 4][$x] === 0 + ) + ) + ) { + $penalty += self::N3; + } + } + } + + return $penalty; + } + + /** + * Applies mask penalty rule 4 and returns the penalty. + * + * Calculates the ratio of dark cells and gives penalty if the ratio is far + * from 50%. It gives 10 penalty for 5% distance. + * + * @param ByteMatrix $matrix + * @return integer + */ + public static function applyMaskPenaltyRule4(ByteMatrix $matrix) + { + $numDarkCells = 0; + + $array = $matrix->getArray(); + $width = $matrix->getWidth(); + $height = $matrix->getHeight(); + + for ($y = 0; $y < $height; $y++) { + $arrayY = $array[$y]; + + for ($x = 0; $x < $width; $x++) { + if ($arrayY[$x] === 1) { + $numDarkCells++; + } + } + } + + $numTotalCells = $height * $width; + $darkRatio = $numDarkCells / $numTotalCells; + $fixedPercentVariances = (int) (abs($darkRatio - 0.5) * 20); + + return $fixedPercentVariances * self::N4; + } + + /** + * Returns the mask bit for "getMaskPattern" at "x" and "y". + * + * See 8.8 of JISX0510:2004 for mask pattern conditions. + * + * @param integer $maskPattern + * @param integer $x + * @param integer $y + * @return integer + * @throws Exception\InvalidArgumentException + */ + public static function getDataMaskBit($maskPattern, $x, $y) + { + switch ($maskPattern) { + case 0: + $intermediate = ($y + $x) & 0x1; + break; + + case 1: + $intermediate = $y & 0x1; + break; + + case 2: + $intermediate = $x % 3; + break; + + case 3: + $intermediate = ($y + $x) % 3; + break; + + case 4: + $intermediate = (BitUtils::unsignedRightShift($y, 1) + ($x / 3)) & 0x1; + break; + + case 5: + $temp = $y * $x; + $intermediate = ($temp & 0x1) + ($temp % 3); + break; + + case 6: + $temp = $y * $x; + $intermediate = (($temp & 0x1) + ($temp % 3)) & 0x1; + break; + + case 7: + $temp = $y * $x; + $intermediate = (($temp % 3) + (($y + $x) & 0x1)) & 0x1; + break; + + default: + throw new Exception\InvalidArgumentException('Invalid mask pattern: ' . $maskPattern); + } + + return $intermediate === 0; + } + + /** + * Helper function for applyMaskPenaltyRule1. + * + * We need this for doing this calculation in both vertical and horizontal + * orders respectively. + * + * @param ByteMatrix $matrix + * @param boolean $isHorizontal + * @return integer + */ + protected static function applyMaskPenaltyRule1Internal(ByteMatrix $matrix, $isHorizontal) + { + $penalty = 0; + $iLimit = $isHorizontal ? $matrix->getHeight() : $matrix->getWidth(); + $jLimit = $isHorizontal ? $matrix->getWidth() : $matrix->getHeight(); + $array = $matrix->getArray(); + + for ($i = 0; $i < $iLimit; $i++) { + $numSameBitCells = 0; + $prevBit = -1; + + for ($j = 0; $j < $jLimit; $j++) { + $bit = $isHorizontal ? $array[$i][$j] : $array[$j][$i]; + + if ($bit === $prevBit) { + $numSameBitCells++; + } else { + if ($numSameBitCells >= 5) { + $penalty += self::N1 + ($numSameBitCells - 5); + } + + $numSameBitCells = 1; + $prevBit = $bit; + } + } + + if ($numSameBitCells >= 5) { + $penalty += self::N1 + ($numSameBitCells - 5); + } + } + + return $penalty; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/MatrixUtil.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/MatrixUtil.php new file mode 100644 index 00000000..83273818 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/MatrixUtil.php @@ -0,0 +1,580 @@ +clear(-1); + } + + /** + * Builds a complete matrix. + * + * @param BitArray $dataBits + * @param ErrorCorrectionLevel $level + * @param Version $version + * @param integer $maskPattern + * @param ByteMatrix $matrix + * @return void + */ + public static function buildMatrix( + BitArray $dataBits, + ErrorCorrectionLevel $level, + Version $version, + $maskPattern, + ByteMatrix $matrix + ) { + self::clearMatrix($matrix); + self::embedBasicPatterns($version, $matrix); + self::embedTypeInfo($level, $maskPattern, $matrix); + self::maybeEmbedVersionInfo($version, $matrix); + self::embedDataBits($dataBits, $maskPattern, $matrix); + } + + /** + * Embeds type information into a matrix. + * + * @param ErrorCorrectionLevel $level + * @param integer $maskPattern + * @param ByteMatrix $matrix + * @return void + */ + protected static function embedTypeInfo(ErrorCorrectionLevel $level, $maskPattern, ByteMatrix $matrix) + { + $typeInfoBits = new BitArray(); + self::makeTypeInfoBits($level, $maskPattern, $typeInfoBits); + + $typeInfoBitsSize = $typeInfoBits->getSize(); + + for ($i = 0; $i < $typeInfoBitsSize; $i++) { + $bit = $typeInfoBits->get($typeInfoBitsSize - 1 - $i); + + $x1 = self::$typeInfoCoordinates[$i][0]; + $y1 = self::$typeInfoCoordinates[$i][1]; + + $matrix->set($x1, $y1, $bit); + + if ($i < 8) { + $x2 = $matrix->getWidth() - $i - 1; + $y2 = 8; + } else { + $x2 = 8; + $y2 = $matrix->getHeight() - 7 + ($i - 8); + } + + $matrix->set($x2, $y2, $bit); + } + } + + /** + * Generates type information bits and appends them to a bit array. + * + * @param ErrorCorrectionLevel $level + * @param integer $maskPattern + * @param BitArray $bits + * @return void + * @throws Exception\RuntimeException + */ + protected static function makeTypeInfoBits(ErrorCorrectionLevel $level, $maskPattern, BitArray $bits) + { + $typeInfo = ($level->get() << 3) | $maskPattern; + $bits->appendBits($typeInfo, 5); + + $bchCode = self::calculateBchCode($typeInfo, self::$typeInfoPoly); + $bits->appendBits($bchCode, 10); + + $maskBits = new BitArray(); + $maskBits->appendBits(self::$typeInfoMaskPattern, 15); + $bits->xorBits($maskBits); + + if ($bits->getSize() !== 15) { + throw new Exception\RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize()); + } + } + + /** + * Embeds version information if required. + * + * @param Version $version + * @param ByteMatrix $matrix + * @return void + */ + protected static function maybeEmbedVersionInfo(Version $version, ByteMatrix $matrix) + { + if ($version->getVersionNumber() < 7) { + return; + } + + $versionInfoBits = new BitArray(); + self::makeVersionInfoBits($version, $versionInfoBits); + + $bitIndex = 6 * 3 - 1; + + for ($i = 0; $i < 6; $i++) { + for ($j = 0; $j < 3; $j++) { + $bit = $versionInfoBits->get($bitIndex); + $bitIndex--; + + $matrix->set($i, $matrix->getHeight() - 11 + $j, $bit); + $matrix->set($matrix->getHeight() - 11 + $j, $i, $bit); + } + } + } + + /** + * Generates version information bits and appends them to a bit array. + * + * @param Version $version + * @param BitArray $bits + * @return void + * @throws Exception\RuntimeException + */ + protected static function makeVersionInfoBits(Version $version, BitArray $bits) + { + $bits->appendBits($version->getVersionNumber(), 6); + + $bchCode = self::calculateBchCode($version->getVersionNumber(), self::$versionInfoPoly); + $bits->appendBits($bchCode, 12); + + if ($bits->getSize() !== 18) { + throw new Exception\RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize()); + } + } + + /** + * Calculates the BHC code for a value and a polynomial. + * + * @param integer $value + * @param integer $poly + * @return integer + */ + protected static function calculateBchCode($value, $poly) + { + $msbSetInPoly = self::findMsbSet($poly); + $value <<= $msbSetInPoly - 1; + + while (self::findMsbSet($value) >= $msbSetInPoly) { + $value ^= $poly << (self::findMsbSet($value) - $msbSetInPoly); + } + + return $value; + } + + /** + * Finds and MSB set. + * + * @param integer $value + * @return integer + */ + protected static function findMsbSet($value) + { + $numDigits = 0; + + while ($value !== 0) { + $value >>= 1; + $numDigits++; + } + + return $numDigits; + } + + /** + * Embeds basic patterns into a matrix. + * + * @param Version $version + * @param ByteMatrix $matrix + * @return void + */ + protected static function embedBasicPatterns(Version $version, ByteMatrix $matrix) + { + self::embedPositionDetectionPatternsAndSeparators($matrix); + self::embedDarkDotAtLeftBottomCorner($matrix); + self::maybeEmbedPositionAdjustmentPatterns($version, $matrix); + self::embedTimingPatterns($matrix); + } + + /** + * Embeds position detection patterns and separators into a byte matrix. + * + * @param ByteMatrix $matrix + * @return void + */ + protected static function embedPositionDetectionPatternsAndSeparators(ByteMatrix $matrix) + { + $pdpWidth = count(self::$positionDetectionPattern[0]); + + self::embedPositionDetectionPattern(0, 0, $matrix); + self::embedPositionDetectionPattern($matrix->getWidth() - $pdpWidth, 0, $matrix); + self::embedPositionDetectionPattern(0, $matrix->getWidth() - $pdpWidth, $matrix); + + $hspWidth = 8; + + self::embedHorizontalSeparationPattern(0, $hspWidth - 1, $matrix); + self::embedHorizontalSeparationPattern($matrix->getWidth() - $hspWidth, $hspWidth - 1, $matrix); + self::embedHorizontalSeparationPattern(0, $matrix->getWidth() - $hspWidth, $matrix); + + $vspSize = 7; + + self::embedVerticalSeparationPattern($vspSize, 0, $matrix); + self::embedVerticalSeparationPattern($matrix->getHeight() - $vspSize - 1, 0, $matrix); + self::embedVerticalSeparationPattern($vspSize, $matrix->getHeight() - $vspSize, $matrix); + } + + /** + * Embeds a single position detection pattern into a byte matrix. + * + * @param integer $xStart + * @param integer $yStart + * @param ByteMatrix $matrix + * @return void + */ + protected static function embedPositionDetectionPattern($xStart, $yStart, ByteMatrix $matrix) + { + for ($y = 0; $y < 7; $y++) { + for ($x = 0; $x < 7; $x++) { + $matrix->set($xStart + $x, $yStart + $y, self::$positionDetectionPattern[$y][$x]); + } + } + } + + /** + * Embeds a single horizontal separation pattern. + * + * @param integer $xStart + * @param integer $yStart + * @param ByteMatrix $matrix + * @return void + * @throws Exception\RuntimeException + */ + protected static function embedHorizontalSeparationPattern($xStart, $yStart, ByteMatrix $matrix) + { + for ($x = 0; $x < 8; $x++) { + if ($matrix->get($xStart + $x, $yStart) !== -1) { + throw new Exception\RuntimeException('Byte already set'); + } + + $matrix->set($xStart + $x, $yStart, 0); + } + } + + /** + * Embeds a single vertical separation pattern. + * + * @param integer $xStart + * @param integer $yStart + * @param ByteMatrix $matrix + * @return void + * @throws Exception\RuntimeException + */ + protected static function embedVerticalSeparationPattern($xStart, $yStart, ByteMatrix $matrix) + { + for ($y = 0; $y < 7; $y++) { + if ($matrix->get($xStart, $yStart + $y) !== -1) { + throw new Exception\RuntimeException('Byte already set'); + } + + $matrix->set($xStart, $yStart + $y, 0); + } + } + + /** + * Embeds a dot at the left bottom corner. + * + * @param ByteMatrix $matrix + * @return void + * @throws Exception\RuntimeException + */ + protected static function embedDarkDotAtLeftBottomCorner(ByteMatrix $matrix) + { + if ($matrix->get(8, $matrix->getHeight() - 8) === 0) { + throw new Exception\RuntimeException('Byte already set to 0'); + } + + $matrix->set(8, $matrix->getHeight() - 8, 1); + } + + /** + * Embeds position adjustment patterns if required. + * + * @param Version $version + * @param ByteMatrix $matrix + * @return void + */ + protected static function maybeEmbedPositionAdjustmentPatterns(Version $version, ByteMatrix $matrix) + { + if ($version->getVersionNumber() < 2) { + return; + } + + $index = $version->getVersionNumber() - 1; + + $coordinates = self::$positionAdjustmentPatternCoordinateTable[$index]; + $numCoordinates = count($coordinates); + + for ($i = 0; $i < $numCoordinates; $i++) { + for ($j = 0; $j < $numCoordinates; $j++) { + $y = $coordinates[$i]; + $x = $coordinates[$j]; + + if ($x === null || $y === null) { + continue; + } + + if ($matrix->get($x, $y) === -1) { + self::embedPositionAdjustmentPattern($x - 2, $y - 2, $matrix); + } + } + } + } + + /** + * Embeds a single position adjustment pattern. + * + * @param integer $xStart + * @param intger $yStart + * @param ByteMatrix $matrix + * @return void + */ + protected static function embedPositionAdjustmentPattern($xStart, $yStart, ByteMatrix $matrix) + { + for ($y = 0; $y < 5; $y++) { + for ($x = 0; $x < 5; $x++) { + $matrix->set($xStart + $x, $yStart + $y, self::$positionAdjustmentPattern[$y][$x]); + } + } + } + + /** + * Embeds timing patterns into a matrix. + * + * @param ByteMatrix $matrix + * @return void + */ + protected static function embedTimingPatterns(ByteMatrix $matrix) + { + $matrixWidth = $matrix->getWidth(); + + for ($i = 8; $i < $matrixWidth - 8; $i++) { + $bit = ($i + 1) % 2; + + if ($matrix->get($i, 6) === -1) { + $matrix->set($i, 6, $bit); + } + + if ($matrix->get(6, $i) === -1) { + $matrix->set(6, $i, $bit); + } + } + } + + /** + * Embeds "dataBits" using "getMaskPattern". + * + * For debugging purposes, it skips masking process if "getMaskPattern" is + * -1. See 8.7 of JISX0510:2004 (p.38) for how to embed data bits. + * + * @param BitArray $dataBits + * @param integer $maskPattern + * @param ByteMatrix $matrix + * @return void + * @throws Exception\WriterException + */ + protected static function embedDataBits(BitArray $dataBits, $maskPattern, ByteMatrix $matrix) + { + $bitIndex = 0; + $direction = -1; + + // Start from the right bottom cell. + $x = $matrix->getWidth() - 1; + $y = $matrix->getHeight() - 1; + + while ($x > 0) { + // Skip vertical timing pattern. + if ($x === 6) { + $x--; + } + + while ($y >= 0 && $y < $matrix->getHeight()) { + for ($i = 0; $i < 2; $i++) { + $xx = $x - $i; + + // Skip the cell if it's not empty. + if ($matrix->get($xx, $y) !== -1) { + continue; + } + + if ($bitIndex < $dataBits->getSize()) { + $bit = $dataBits->get($bitIndex); + $bitIndex++; + } else { + // Padding bit. If there is no bit left, we'll fill the + // left cells with 0, as described in 8.4.9 of + // JISX0510:2004 (p. 24). + $bit = false; + } + + // Skip masking if maskPattern is -1. + if ($maskPattern !== -1 && MaskUtil::getDataMaskBit($maskPattern, $xx, $y)) { + $bit = !$bit; + } + + $matrix->set($xx, $y, $bit); + } + + $y += $direction; + } + + $direction = -$direction; + $y += $direction; + $x -= 2; + } + + // All bits should be consumed + if ($bitIndex !== $dataBits->getSize()) { + throw new Exception\WriterException('Not all bits consumed (' . $bitIndex . ' out of ' . $dataBits->getSize() .')'); + } + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/QrCode.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/QrCode.php new file mode 100644 index 00000000..07e1c385 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Encoder/QrCode.php @@ -0,0 +1,201 @@ +mode; + } + + /** + * Sets the mode. + * + * @param Mode $mode + * @return void + */ + public function setMode(Mode $mode) + { + $this->mode = $mode; + } + + /** + * Gets the EC level. + * + * @return ErrorCorrectionLevel + */ + public function getErrorCorrectionLevel() + { + return $this->errorCorrectionLevel; + } + + /** + * Sets the EC level. + * + * @param ErrorCorrectionLevel $errorCorrectionLevel + * @return void + */ + public function setErrorCorrectionLevel(ErrorCorrectionLevel $errorCorrectionLevel) + { + $this->errorCorrectionLevel = $errorCorrectionLevel; + } + + /** + * Gets the version. + * + * @return Version + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the version. + * + * @param Version $version + * @return void + */ + public function setVersion(Version $version) + { + $this->version = $version; + } + + /** + * Gets the mask pattern. + * + * @return integer + */ + public function getMaskPattern() + { + return $this->maskPattern; + } + + /** + * Sets the mask pattern. + * + * @param integer $maskPattern + * @return void + */ + public function setMaskPattern($maskPattern) + { + $this->maskPattern = $maskPattern; + } + + /** + * Gets the matrix. + * + * @return ByteMatrix + */ + public function getMatrix() + { + return $this->matrix; + } + + /** + * Sets the matrix. + * + * @param ByteMatrix $matrix + * @return void + */ + public function setMatrix(ByteMatrix $matrix) + { + $this->matrix = $matrix; + } + + /** + * Validates whether a mask pattern is valid. + * + * @param integer $maskPattern + * @return boolean + */ + public static function isValidMaskPattern($maskPattern) + { + return $maskPattern > 0 && $maskPattern < self::NUM_MASK_PATTERNS; + } + + /** + * Returns a string representation of the QR code. + * + * @return string + */ + public function __toString() + { + $result = "<<\n" + . " mode: " . $this->mode . "\n" + . " ecLevel: " . $this->errorCorrectionLevel . "\n" + . " version: " . $this->version . "\n" + . " maskPattern: " . $this->maskPattern . "\n"; + + if ($this->matrix === null) { + $result .= " matrix: null\n"; + } else { + $result .= " matrix:\n"; + $result .= $this->matrix; + } + + $result .= ">>\n"; + + return $result; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Exception/ExceptionInterface.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Exception/ExceptionInterface.php new file mode 100644 index 00000000..5c58fc51 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Exception/ExceptionInterface.php @@ -0,0 +1,14 @@ + 100) { + throw new Exception\InvalidArgumentException('Cyan must be between 0 and 100'); + } + + if ($magenta < 0 || $magenta > 100) { + throw new Exception\InvalidArgumentException('Magenta must be between 0 and 100'); + } + + if ($yellow < 0 || $yellow > 100) { + throw new Exception\InvalidArgumentException('Yellow must be between 0 and 100'); + } + + if ($black < 0 || $black > 100) { + throw new Exception\InvalidArgumentException('Black must be between 0 and 100'); + } + + $this->cyan = (int) $cyan; + $this->magenta = (int) $magenta; + $this->yellow = (int) $yellow; + $this->black = (int) $black; + } + + /** + * Returns the cyan value. + * + * @return integer + */ + public function getCyan() + { + return $this->cyan; + } + + /** + * Returns the magenta value. + * + * @return integer + */ + public function getMagenta() + { + return $this->magenta; + } + + /** + * Returns the yellow value. + * + * @return integer + */ + public function getYellow() + { + return $this->yellow; + } + + /** + * Returns the black value. + * + * @return integer + */ + public function getBlack() + { + return $this->black; + } + + /** + * toRgb(): defined by ColorInterface. + * + * @see ColorInterface::toRgb() + * @return Rgb + */ + public function toRgb() + { + $k = $this->black / 100; + $c = (-$k * $this->cyan + $k * 100 + $this->cyan) / 100; + $m = (-$k * $this->magenta + $k * 100 + $this->magenta) / 100; + $y = (-$k * $this->yellow + $k * 100 + $this->yellow) / 100; + + return new Rgb( + -$c * 255 + 255, + -$m * 255 + 255, + -$y * 255 + 255 + ); + } + + /** + * toCmyk(): defined by ColorInterface. + * + * @see ColorInterface::toCmyk() + * @return Cmyk + */ + public function toCmyk() + { + return $this; + } + + /** + * toGray(): defined by ColorInterface. + * + * @see ColorInterface::toGray() + * @return Gray + */ + public function toGray() + { + return $this->toRgb()->toGray(); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Color/ColorInterface.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Color/ColorInterface.php new file mode 100644 index 00000000..747accc1 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Color/ColorInterface.php @@ -0,0 +1,37 @@ + 100) { + throw new Exception\InvalidArgumentException('Gray must be between 0 and 100'); + } + + $this->gray = (int) $gray; + } + + /** + * Returns the gray value. + * + * @return integer + */ + public function getGray() + { + return $this->gray; + } + + /** + * toRgb(): defined by ColorInterface. + * + * @see ColorInterface::toRgb() + * @return Rgb + */ + public function toRgb() + { + return new Rgb($this->gray * 2.55, $this->gray * 2.55, $this->gray * 2.55); + } + + /** + * toCmyk(): defined by ColorInterface. + * + * @see ColorInterface::toCmyk() + * @return Cmyk + */ + public function toCmyk() + { + return new Cmyk(0, 0, 0, 100 - $this->gray); + } + + /** + * toGray(): defined by ColorInterface. + * + * @see ColorInterface::toGray() + * @return Gray + */ + public function toGray() + { + return $this; + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Color/Rgb.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Color/Rgb.php new file mode 100644 index 00000000..44e4060c --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Color/Rgb.php @@ -0,0 +1,148 @@ + 255) { + throw new Exception\InvalidArgumentException('Red must be between 0 and 255'); + } + + if ($green < 0 || $green > 255) { + throw new Exception\InvalidArgumentException('Green must be between 0 and 255'); + } + + if ($blue < 0 || $blue > 255) { + throw new Exception\InvalidArgumentException('Blue must be between 0 and 255'); + } + + $this->red = (int) $red; + $this->green = (int) $green; + $this->blue = (int) $blue; + } + + /** + * Returns the red value. + * + * @return integer + */ + public function getRed() + { + return $this->red; + } + + /** + * Returns the green value. + * + * @return integer + */ + public function getGreen() + { + return $this->green; + } + + /** + * Returns the blue value. + * + * @return integer + */ + public function getBlue() + { + return $this->blue; + } + + /** + * Returns a hexadecimal string representation of the RGB value. + * + * @return string + */ + public function __toString() + { + return sprintf('%02x%02x%02x', $this->red, $this->green, $this->blue); + } + + /** + * toRgb(): defined by ColorInterface. + * + * @see ColorInterface::toRgb() + * @return Rgb + */ + public function toRgb() + { + return $this; + } + + /** + * toCmyk(): defined by ColorInterface. + * + * @see ColorInterface::toCmyk() + * @return Cmyk + */ + public function toCmyk() + { + $c = 1 - ($this->red / 255); + $m = 1 - ($this->green / 255); + $y = 1 - ($this->blue / 255); + $k = min($c, $m, $y); + + return new Cmyk( + 100 * ($c - $k) / (1 - $k), + 100 * ($m - $k) / (1 - $k), + 100 * ($y - $k) / (1 - $k), + 100 * $k + ); + } + + /** + * toGray(): defined by ColorInterface. + * + * @see ColorInterface::toGray() + * @return Gray + */ + public function toGray() + { + return new Gray(($this->red * 0.21 + $this->green * 0.71 + $this->blue * 0.07) / 2.55); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/AbstractRenderer.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/AbstractRenderer.php new file mode 100644 index 00000000..b0bb02ac --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/AbstractRenderer.php @@ -0,0 +1,338 @@ +margin = (int) $margin; + return $this; + } + + /** + * Gets the margin around the QR code. + * + * @return integer + */ + public function getMargin() + { + return $this->margin; + } + + /** + * Sets the height around the renderd image. + * + * If the width is smaller than the matrix width plus padding, the renderer + * will automatically use that as the width instead of the specified one. + * + * @param integer $width + * @return AbstractRenderer + */ + public function setWidth($width) + { + $this->width = (int) $width; + return $this; + } + + /** + * Gets the width of the rendered image. + * + * @return integer + */ + public function getWidth() + { + return $this->width; + } + + /** + * Sets the height around the renderd image. + * + * If the height is smaller than the matrix height plus padding, the + * renderer will automatically use that as the height instead of the + * specified one. + * + * @param integer $height + * @return AbstractRenderer + */ + public function setHeight($height) + { + $this->height = (int) $height; + return $this; + } + + /** + * Gets the height around the rendered image. + * + * @return integer + */ + public function getHeight() + { + return $this->height; + } + + /** + * Sets whether dimensions should be rounded down. + * + * @param boolean $flag + * @return AbstractRenderer + */ + public function setRoundDimensions($flag) + { + $this->floorToClosestDimension = $flag; + return $this; + } + + /** + * Gets whether dimensions should be rounded down. + * + * @return boolean + */ + public function shouldRoundDimensions() + { + return $this->floorToClosestDimension; + } + + /** + * Sets background color. + * + * @param Color\ColorInterface $color + * @return AbstractRenderer + */ + public function setBackgroundColor(Color\ColorInterface $color) + { + $this->backgroundColor = $color; + return $this; + } + + /** + * Gets background color. + * + * @return Color\ColorInterface + */ + public function getBackgroundColor() + { + if ($this->backgroundColor === null) { + $this->backgroundColor = new Color\Gray(100); + } + + return $this->backgroundColor; + } + + /** + * Sets foreground color. + * + * @param Color\ColorInterface $color + * @return AbstractRenderer + */ + public function setForegroundColor(Color\ColorInterface $color) + { + $this->foregroundColor = $color; + return $this; + } + + /** + * Gets foreground color. + * + * @return Color\ColorInterface + */ + public function getForegroundColor() + { + if ($this->foregroundColor === null) { + $this->foregroundColor = new Color\Gray(0); + } + + return $this->foregroundColor; + } + + /** + * Adds a decorator to the renderer. + * + * @param DecoratorInterface $decorator + * @return AbstractRenderer + */ + public function addDecorator(DecoratorInterface $decorator) + { + $this->decorators[] = $decorator; + return $this; + } + + /** + * render(): defined by RendererInterface. + * + * @see RendererInterface::render() + * @param QrCode $qrCode + * @return string + */ + public function render(QrCode $qrCode) + { + $input = $qrCode->getMatrix(); + $inputWidth = $input->getWidth(); + $inputHeight = $input->getHeight(); + $qrWidth = $inputWidth + ($this->getMargin() << 1); + $qrHeight = $inputHeight + ($this->getMargin() << 1); + $outputWidth = max($this->getWidth(), $qrWidth); + $outputHeight = max($this->getHeight(), $qrHeight); + $multiple = (int) min($outputWidth / $qrWidth, $outputHeight / $qrHeight); + + if ($this->shouldRoundDimensions()) { + $outputWidth -= $outputWidth % $multiple; + $outputHeight -= $outputHeight % $multiple; + } + + // Padding includes both the quiet zone and the extra white pixels to + // accommodate the requested dimensions. For example, if input is 25x25 + // the QR will be 33x33 including the quiet zone. If the requested size + // is 200x160, the multiple will be 4, for a QR of 132x132. These will + // handle all the padding from 100x100 (the actual QR) up to 200x160. + $leftPadding = (int) (($outputWidth - ($inputWidth * $multiple)) / 2); + $topPadding = (int) (($outputHeight - ($inputHeight * $multiple)) / 2); + + // Store calculated parameters + $this->finalWidth = $outputWidth; + $this->finalHeight = $outputHeight; + $this->blockSize = $multiple; + + $this->init(); + $this->addColor('background', $this->getBackgroundColor()); + $this->addColor('foreground', $this->getForegroundColor()); + $this->drawBackground('background'); + + foreach ($this->decorators as $decorator) { + $decorator->preProcess( + $qrCode, + $this, + $outputWidth, + $outputHeight, + $leftPadding, + $topPadding, + $multiple + ); + } + + for ($inputY = 0, $outputY = $topPadding; $inputY < $inputHeight; $inputY++, $outputY += $multiple) { + for ($inputX = 0, $outputX = $leftPadding; $inputX < $inputWidth; $inputX++, $outputX += $multiple) { + if ($input->get($inputX, $inputY) === 1) { + $this->drawBlock($outputX, $outputY, 'foreground'); + } + } + } + + foreach ($this->decorators as $decorator) { + $decorator->postProcess( + $qrCode, + $this, + $outputWidth, + $outputHeight, + $leftPadding, + $topPadding, + $multiple + ); + } + + return $this->getByteStream(); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/Decorator/DecoratorInterface.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/Decorator/DecoratorInterface.php new file mode 100644 index 00000000..e67268b5 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/Decorator/DecoratorInterface.php @@ -0,0 +1,63 @@ +outerColor = $color; + return $this; + } + + /** + * Gets outer color. + * + * @return Color\ColorInterface + */ + public function getOuterColor() + { + if ($this->outerColor === null) { + $this->outerColor = new Color\Gray(100); + } + + return $this->outerColor; + } + + /** + * Sets inner color. + * + * @param Color\ColorInterface $color + * @return FinderPattern + */ + public function setInnerColor(Color\ColorInterface $color) + { + $this->innerColor = $color; + return $this; + } + + /** + * Gets inner color. + * + * @return Color\ColorInterface + */ + public function getInnerColor() + { + if ($this->innerColor === null) { + $this->innerColor = new Color\Gray(0); + } + + return $this->innerColor; + } + + /** + * preProcess(): defined by DecoratorInterface. + * + * @see DecoratorInterface::preProcess() + * @param QrCode $qrCode + * @param RendererInterface $renderer + * @param integer $outputWidth + * @param integer $outputHeight + * @param integer $leftPadding + * @param integer $topPadding + * @param integer $multiple + * @return void + */ + public function preProcess( + QrCode $qrCode, + RendererInterface $renderer, + $outputWidth, + $outputHeight, + $leftPadding, + $topPadding, + $multiple + ) { + $matrix = $qrCode->getMatrix(); + $positions = array( + array(0, 0), + array($matrix->getWidth() - 7, 0), + array(0, $matrix->getHeight() - 7), + ); + + foreach (self::$outerPositionDetectionPattern as $y => $row) { + foreach ($row as $x => $isSet) { + foreach ($positions as $position) { + $matrix->set($x + $position[0], $y + $position[1], 0); + } + } + } + } + + /** + * postProcess(): defined by DecoratorInterface. + * + * @see DecoratorInterface::postProcess() + * + * @param QrCode $qrCode + * @param RendererInterface $renderer + * @param integer $outputWidth + * @param integer $outputHeight + * @param integer $leftPadding + * @param integer $topPadding + * @param integer $multiple + * @return void + */ + public function postProcess( + QrCode $qrCode, + RendererInterface $renderer, + $outputWidth, + $outputHeight, + $leftPadding, + $topPadding, + $multiple + ) { + $matrix = $qrCode->getMatrix(); + $positions = array( + array(0, 0), + array($matrix->getWidth() - 7, 0), + array(0, $matrix->getHeight() - 7), + ); + + $renderer->addColor('finder-outer', $this->getOuterColor()); + $renderer->addColor('finder-inner', $this->getInnerColor()); + + foreach (self::$outerPositionDetectionPattern as $y => $row) { + foreach ($row as $x => $isOuterSet) { + $isInnerSet = self::$innerPositionDetectionPattern[$y][$x]; + + if ($isOuterSet) { + foreach ($positions as $position) { + $renderer->drawBlock( + $leftPadding + $x * $multiple + $position[0] * $multiple, + $topPadding + $y * $multiple + $position[1] * $multiple, + 'finder-outer' + ); + } + } + + if ($isInnerSet) { + foreach ($positions as $position) { + $renderer->drawBlock( + $leftPadding + $x * $multiple + $position[0] * $multiple, + $topPadding + $y * $multiple + $position[1] * $multiple, + 'finder-inner' + ); + } + } + } + } + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/Eps.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/Eps.php new file mode 100644 index 00000000..1b930d87 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/Eps.php @@ -0,0 +1,152 @@ +eps = "%!PS-Adobe-3.0 EPSF-3.0\n" + . "%%BoundingBox: 0 0 " . $this->finalWidth . " " . $this->finalHeight . "\n" + . "/F { rectfill } def\n"; + } + + /** + * addColor(): defined by RendererInterface. + * + * @see ImageRendererInterface::addColor() + * @param string $id + * @param ColorInterface $color + * @return void + */ + public function addColor($id, ColorInterface $color) + { + if ( + !$color instanceof Rgb + && !$color instanceof Cmyk + && !$color instanceof Gray + ) { + $color = $color->toCmyk(); + } + + $this->colors[$id] = $color; + } + + /** + * drawBackground(): defined by RendererInterface. + * + * @see ImageRendererInterface::drawBackground() + * @param string $colorId + * @return void + */ + public function drawBackground($colorId) + { + $this->setColor($colorId); + $this->eps .= "0 0 " . $this->finalWidth . " " . $this->finalHeight . " F\n"; + } + + /** + * drawBlock(): defined by RendererInterface. + * + * @see ImageRendererInterface::drawBlock() + * @param integer $x + * @param integer $y + * @param string $colorId + * @return void + */ + public function drawBlock($x, $y, $colorId) + { + $this->setColor($colorId); + $this->eps .= $x . " " . ($this->finalHeight - $y) . " " . $this->blockSize . " " . $this->blockSize . " F\n"; + } + + /** + * getByteStream(): defined by RendererInterface. + * + * @see ImageRendererInterface::getByteStream() + * @return string + */ + public function getByteStream() + { + return $this->eps; + } + + /** + * Sets color to use. + * + * @param string $colorId + * @return void + */ + protected function setColor($colorId) + { + if ($colorId !== $this->currentColor) { + $color = $this->colors[$colorId]; + + if ($color instanceof Rgb) { + $this->eps .= sprintf( + "%F %F %F setrgbcolor\n", + $color->getRed() / 100, + $color->getGreen() / 100, + $color->getBlue() / 100 + ); + } elseif ($color instanceof Cmyk) { + $this->eps .= sprintf( + "%F %F %F %F setcmykcolor\n", + $color->getCyan() / 100, + $color->getMagenta() / 100, + $color->getYellow() / 100, + $color->getBlack() / 100 + ); + } elseif ($color instanceof Gray) { + $this->eps .= sprintf( + "%F setgray\n", + $color->getGray() / 100 + ); + } + + $this->currentColor = $colorId; + } + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/Png.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/Png.php new file mode 100644 index 00000000..dd593a8b --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/Png.php @@ -0,0 +1,115 @@ +image = imagecreatetruecolor($this->finalWidth, $this->finalHeight); + } + + /** + * addColor(): defined by RendererInterface. + * + * @see ImageRendererInterface::addColor() + * @param string $id + * @param ColorInterface $color + * @return void + * @throws Exception\RuntimeException + */ + public function addColor($id, ColorInterface $color) + { + if ($this->image === null) { + throw new Exception\RuntimeException('Colors can only be added after init'); + } + + $color = $color->toRgb(); + + $this->colors[$id] = imagecolorallocate( + $this->image, + $color->getRed(), + $color->getGreen(), + $color->getBlue() + ); + } + + /** + * drawBackground(): defined by RendererInterface. + * + * @see ImageRendererInterface::drawBackground() + * @param string $colorId + * @return void + */ + public function drawBackground($colorId) + { + imagefill($this->image, 0, 0, $this->colors[$colorId]); + } + + /** + * drawBlock(): defined by RendererInterface. + * + * @see ImageRendererInterface::drawBlock() + * @param integer $x + * @param integer $y + * @param string $colorId + * @return void + */ + public function drawBlock($x, $y, $colorId) + { + imagefilledrectangle( + $this->image, + $x, + $y, + $x + $this->blockSize - 1, + $y + $this->blockSize - 1, + $this->colors[$colorId] + ); + } + + /** + * getByteStream(): defined by RendererInterface. + * + * @see ImageRendererInterface::getByteStream() + * @return string + */ + public function getByteStream() + { + ob_start(); + imagepng($this->image); + return ob_get_clean(); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/RendererInterface.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/RendererInterface.php new file mode 100644 index 00000000..52101a65 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Image/RendererInterface.php @@ -0,0 +1,61 @@ +svg = new SimpleXMLElement( + '' + . '' + ); + $this->svg->addAttribute('version', '1.1'); + $this->svg->addAttribute('width', $this->finalWidth . 'px'); + $this->svg->addAttribute('height', $this->finalHeight . 'px'); + $this->svg->addAttribute('viewBox', '0 0 ' . $this->finalWidth . ' ' . $this->finalHeight); + $this->svg->addChild('defs'); + } + + /** + * addColor(): defined by RendererInterface. + * + * @see ImageRendererInterface::addColor() + * @param string $id + * @param ColorInterface $color + * @return void + * @throws Exception\InvalidArgumentException + */ + public function addColor($id, ColorInterface $color) + { + $this->colors[$id] = (string) $color->toRgb(); + } + + /** + * drawBackground(): defined by RendererInterface. + * + * @see ImageRendererInterface::drawBackground() + * @param string $colorId + * @return void + */ + public function drawBackground($colorId) + { + $rect = $this->svg->addChild('rect'); + $rect->addAttribute('x', 0); + $rect->addAttribute('y', 0); + $rect->addAttribute('width', $this->finalWidth); + $rect->addAttribute('height', $this->finalHeight); + $rect->addAttribute('fill', '#' . $this->colors[$colorId]); + } + + /** + * drawBlock(): defined by RendererInterface. + * + * @see ImageRendererInterface::drawBlock() + * @param integer $x + * @param integer $y + * @param string $colorId + * @return void + */ + public function drawBlock($x, $y, $colorId) + { + $use = $this->svg->addChild('use'); + $use->addAttribute('x', $x); + $use->addAttribute('y', $y); + $use->addAttribute( + 'xlink:href', + $this->getRectPrototypeId($colorId), + 'http://www.w3.org/1999/xlink' + ); + } + + /** + * getByteStream(): defined by RendererInterface. + * + * @see ImageRendererInterface::getByteStream() + * @return string + */ + public function getByteStream() + { + return $this->svg->asXML(); + } + + /** + * Get the prototype ID for a color. + * + * @param integer $colorId + * @return string + */ + protected function getRectPrototypeId($colorId) + { + if (!isset($this->prototypeIds[$colorId])) { + $id = 'r' . dechex(count($this->prototypeIds)); + + $rect = $this->svg->defs->addChild('rect'); + $rect->addAttribute('id', $id); + $rect->addAttribute('width', $this->blockSize); + $rect->addAttribute('height', $this->blockSize); + $rect->addAttribute('fill', '#' . $this->colors[$colorId]); + + $this->prototypeIds[$colorId] = '#' . $id; + } + + return $this->prototypeIds[$colorId]; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/RendererInterface.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/RendererInterface.php new file mode 100644 index 00000000..554e1d88 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/RendererInterface.php @@ -0,0 +1,26 @@ +class = $class; + } + + /** + * Get CSS class name. + * + * @return string + */ + public function getClass() + { + return $this->class; + } + + /** + * Set CSS style value. + * + * @param string $style + */ + public function setStyle($style) + { + $this->style = $style; + } + + /** + * Get CSS style value. + * + * @return string + */ + public function getStyle() + { + return $this->style; + } + + /** + * render(): defined by RendererInterface. + * + * @see RendererInterface::render() + * @param QrCode $qrCode + * @return string + */ + public function render(QrCode $qrCode) + { + $textCode = parent::render($qrCode); + + $result = '
' . $textCode . ''; + + return $result; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Text/Plain.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Text/Plain.php new file mode 100644 index 00000000..a7e4cfb7 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Renderer/Text/Plain.php @@ -0,0 +1,150 @@ +fullBlock = $fullBlock; + } + + /** + * Get char used as full block (occupied space, "black"). + * + * @return string + */ + public function getFullBlock() + { + return $this->fullBlock; + } + + /** + * Set char used as empty block (empty space, "white"). + * + * @param string $emptyBlock + */ + public function setEmptyBlock($emptyBlock) + { + $this->emptyBlock = $emptyBlock; + } + + /** + * Get char used as empty block (empty space, "white"). + * + * @return string + */ + public function getEmptyBlock() + { + return $this->emptyBlock; + } + + /** + * Sets the margin around the QR code. + * + * @param integer $margin + * @return AbstractRenderer + * @throws Exception\InvalidArgumentException + */ + public function setMargin($margin) + { + if ($margin < 0) { + throw new Exception\InvalidArgumentException('Margin must be equal to greater than 0'); + } + + $this->margin = (int) $margin; + + return $this; + } + + /** + * Gets the margin around the QR code. + * + * @return integer + */ + public function getMargin() + { + return $this->margin; + } + + /** + * render(): defined by RendererInterface. + * + * @see RendererInterface::render() + * @param QrCode $qrCode + * @return string + */ + public function render(QrCode $qrCode) + { + $result = ''; + $matrix = $qrCode->getMatrix(); + $width = $matrix->getWidth(); + + // Top margin + for ($x = 0; $x < $this->margin; $x++) { + $result .= str_repeat($this->emptyBlock, $width + 2 * $this->margin)."\n"; + } + + // Body + $array = $matrix->getArray(); + + foreach ($array as $row) { + $result .= str_repeat($this->emptyBlock, $this->margin); // left margin + foreach ($row as $byte) { + $result .= $byte ? $this->fullBlock : $this->emptyBlock; + } + $result .= str_repeat($this->emptyBlock, $this->margin); // right margin + $result .= "\n"; + } + + // Bottom margin + for ($x = 0; $x < $this->margin; $x++) { + $result .= str_repeat($this->emptyBlock, $width + 2 * $this->margin)."\n"; + } + + return $result; + } +} diff --git a/vendor/bacon/bacon-qr-code/src/BaconQrCode/Writer.php b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Writer.php new file mode 100644 index 00000000..0f803138 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/src/BaconQrCode/Writer.php @@ -0,0 +1,105 @@ +renderer = $renderer; + } + + /** + * Sets the renderer used to create a byte stream. + * + * @param RendererInterface $renderer + * @return Writer + */ + public function setRenderer(RendererInterface $renderer) + { + $this->renderer = $renderer; + return $this; + } + + /** + * Gets the renderer used to create a byte stream. + * + * @return RendererInterface + */ + public function getRenderer() + { + return $this->renderer; + } + + /** + * Writes QR code and returns it as string. + * + * Content is a string which *should* be encoded in UTF-8, in case there are + * non ASCII-characters present. + * + * @param string $content + * @param string $encoding + * @param integer $ecLevel + * @return string + * @throws Exception\InvalidArgumentException + */ + public function writeString( + $content, + $encoding = Encoder::DEFAULT_BYTE_MODE_ECODING, + $ecLevel = ErrorCorrectionLevel::L + ) { + if (strlen($content) === 0) { + throw new Exception\InvalidArgumentException('Found empty contents'); + } + + $qrCode = Encoder::encode($content, new ErrorCorrectionLevel($ecLevel), $encoding); + + return $this->getRenderer()->render($qrCode); + } + + /** + * Writes QR code to a file. + * + * @see Writer::writeString() + * @param string $content + * @param string $filename + * @param string $encoding + * @param integer $ecLevel + * @return void + */ + public function writeFile( + $content, + $filename, + $encoding = Encoder::DEFAULT_BYTE_MODE_ECODING, + $ecLevel = ErrorCorrectionLevel::L + ) { + file_put_contents($filename, $this->writeString($content, $encoding, $ecLevel)); + } +} diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/BitArrayTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/BitArrayTest.php new file mode 100644 index 00000000..06aa4e16 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/BitArrayTest.php @@ -0,0 +1,197 @@ +assertFalse($array->get($i)); + $array->set($i); + $this->assertTrue($array->get($i)); + } + } + + public function testGetNextSet1() + { + $array = new BitArray(32); + + for ($i = 0; $i < $array->getSize(); $i++) { + $this->assertEquals($i, 32, '', $array->getNextSet($i)); + } + + $array = new BitArray(33); + + for ($i = 0; $i < $array->getSize(); $i++) { + $this->assertEquals($i, 33, '', $array->getNextSet($i)); + } + } + + public function testGetNextSet2() + { + $array = new BitArray(33); + + for ($i = 0; $i < $array->getSize(); $i++) { + $this->assertEquals($i, $i <= 31 ? 31 : 33, '', $array->getNextSet($i)); + } + + $array = new BitArray(33); + + for ($i = 0; $i < $array->getSize(); $i++) { + $this->assertEquals($i, 32, '', $array->getNextSet($i)); + } + } + + public function testGetNextSet3() + { + $array = new BitArray(63); + $array->set(31); + $array->set(32); + + for ($i = 0; $i < $array->getSize(); $i++) { + if ($i <= 31) { + $expected = 31; + } elseif ($i <= 32) { + $expected = 32; + } else { + $expected = 63; + } + + $this->assertEquals($i, $expected, '', $array->getNextSet($i)); + } + } + + public function testGetNextSet4() + { + $array = new BitArray(63); + $array->set(33); + $array->set(40); + + for ($i = 0; $i < $array->getSize(); $i++) { + if ($i <= 33) { + $expected = 33; + } elseif ($i <= 40) { + $expected = 40; + } else { + $expected = 63; + } + + $this->assertEquals($i, $expected, '', $array->getNextSet($i)); + } + } + + public function testGetNextSet5() + { + mt_srand(hexdec('deadbeef')); + + for ($i = 0; $i < 10; $i++) { + $array = new BitArray(mt_rand(1, 100)); + $numSet = mt_rand(0, 19); + + for ($j = 0; $j < $numSet; $j++) { + $array->set(mt_rand(0, $array->getSize() - 1)); + } + + $numQueries = mt_rand(0, 19); + + for ($j = 0; $j < $numQueries; $j++) { + $query = mt_rand(0, $array->getSize() - 1); + $expected = $query; + + while ($expected < $array->getSize() && !$array->get($expected)) { + $expected++; + } + + $actual = $array->getNextSet($query); + + if ($actual !== $expected) { + $array->getNextSet($query); + } + + $this->assertEquals($expected, $actual); + } + } + } + + public function testSetBulk() + { + $array = new BitArray(64); + $array->setBulk(32, 0xFFFF0000); + + for ($i = 0; $i < 48; $i++) { + $this->assertFalse($array->get($i)); + } + + for ($i = 48; $i < 64; $i++) { + $this->assertTrue($array->get($i)); + } + } + + public function testClear() + { + $array = new BitArray(32); + + for ($i = 0; $i < 32; $i++) { + $array->set($i); + } + + $array->clear(); + + for ($i = 0; $i < 32; $i++) { + $this->assertFalse($array->get($i)); + } + } + + public function testGetArray() + { + $array = new BitArray(64); + $array->set(0); + $array->set(63); + + $ints = $array->getBitArray(); + + $this->assertEquals(1, $ints[0]); + $this->assertEquals(0x80000000, $ints[1]); + } + + public function testIsRange() + { + $array = new BitArray(64); + $this->assertTrue($array->isRange(0, 64, false)); + $this->assertFalse($array->isRange(0, 64, true)); + + $array->set(32); + $this->assertTrue($array->isRange(32, 33, true)); + + $array->set(31); + $this->assertTrue($array->isRange(31, 33, true)); + + $array->set(34); + $this->assertFalse($array->isRange(31, 35, true)); + + for ($i = 0; $i < 31; $i++) { + $array->set($i); + } + + $this->assertTrue($array->isRange(0, 33, true)); + + for ($i = 33; $i < 64; $i++) { + $array->set($i); + } + + $this->assertTrue($array->isRange(0, 64, true)); + $this->assertFalse($array->isRange(0, 64, false)); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/BitMatrixTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/BitMatrixTest.php new file mode 100644 index 00000000..89a58812 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/BitMatrixTest.php @@ -0,0 +1,119 @@ +assertEquals(33, $matrix->getHeight()); + + for ($y = 0; $y < 33; $y++) { + for ($x = 0; $x < 33; $x++) { + if ($y * $x % 3 === 0) { + $matrix->set($x, $y); + } + } + } + + for ($y = 0; $y < 33; $y++) { + for ($x = 0; $x < 33; $x++) { + $this->assertEquals($x * $y % 3 === 0, $matrix->get($x, $y)); + } + } + } + + public function testSetRegion() + { + $matrix = new BitMatrix(5); + $matrix->setRegion(1, 1, 3, 3); + + for ($y = 0; $y < 5; $y++) { + for ($x = 0; $x < 5; $x++) { + $this->assertEquals($y >= 1 && $y <= 3 && $x >= 1 && $x <= 3, $matrix->get($x, $y)); + } + } + } + + public function testRectangularMatrix() + { + $matrix = new BitMatrix(75, 20); + $this->assertEquals(75, $matrix->getWidth()); + $this->assertEquals(20, $matrix->getHeight()); + + $matrix->set(10, 0); + $matrix->set(11, 1); + $matrix->set(50, 2); + $matrix->set(51, 3); + $matrix->flip(74, 4); + $matrix->flip(0, 5); + + $this->assertTrue($matrix->get(10, 0)); + $this->assertTrue($matrix->get(11, 1)); + $this->assertTrue($matrix->get(50, 2)); + $this->assertTrue($matrix->get(51, 3)); + $this->assertTrue($matrix->get(74, 4)); + $this->assertTrue($matrix->get(0, 5)); + + $matrix->flip(50, 2); + $matrix->flip(51, 3); + + $this->assertFalse($matrix->get(50, 2)); + $this->assertFalse($matrix->get(51, 3)); + } + + public function testRectangularSetRegion() + { + $matrix = new BitMatrix(320, 240); + $this->assertEquals(320, $matrix->getWidth()); + $this->assertEquals(240, $matrix->getHeight()); + + $matrix->setRegion(105, 22, 80, 12); + + for ($y = 0; $y < 240; $y++) { + for ($x = 0; $x < 320; $x++) { + $this->assertEquals($y >= 22 && $y < 34 && $x >= 105 && $x < 185, $matrix->get($x, $y)); + } + } + } + + public function testGetRow() + { + $matrix = new BitMatrix(102, 5); + + for ($x = 0; $x < 102; $x++) { + if ($x & 3 === 0) { + $matrix->set($x, 2); + } + } + + $array1 = $matrix->getRow(2, null); + $this->assertEquals(102, $array1->getSize()); + + $array2 = new BitArray(60); + $array2 = $matrix->getRow(2, $array2); + $this->assertEquals(102, $array2->getSize()); + + $array3 = new BitArray(200); + $array3 = $matrix->getRow(2, $array3); + $this->assertEquals(200, $array3->getSize()); + + for ($x = 0; $x < 102; $x++) { + $on = ($x & 3 === 0); + + $this->assertEquals($on, $array1->get($x)); + $this->assertEquals($on, $array2->get($x)); + $this->assertEquals($on, $array3->get($x)); + } + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/BitUtilsTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/BitUtilsTest.php new file mode 100644 index 00000000..b80ff7df --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/BitUtilsTest.php @@ -0,0 +1,30 @@ +assertEquals(1, BitUtils::unsignedRightShift(1, 0)); + $this->assertEquals(1, BitUtils::unsignedRightShift(10, 3)); + $this->assertEquals(536870910, BitUtils::unsignedRightShift(-10, 3)); + } + + public function testNumberOfTrailingZeros() + { + $this->assertEquals(32, BitUtils::numberOfTrailingZeros(0)); + $this->assertEquals(1, BitUtils::numberOfTrailingZeros(10)); + $this->assertEquals(0, BitUtils::numberOfTrailingZeros(15)); + $this->assertEquals(2, BitUtils::numberOfTrailingZeros(20)); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/ErrorCorrectionLevelTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/ErrorCorrectionLevelTest.php new file mode 100644 index 00000000..736e995d --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/ErrorCorrectionLevelTest.php @@ -0,0 +1,40 @@ +assertEquals(0x0, ErrorCorrectionLevel::M); + $this->assertEquals(0x1, ErrorCorrectionLevel::L); + $this->assertEquals(0x2, ErrorCorrectionLevel::H); + $this->assertEquals(0x3, ErrorCorrectionLevel::Q); + } + + public function testInvalidErrorCorrectionLevelThrowsException() + { + $this->setExpectedException( + 'BaconQrCode\Exception\UnexpectedValueException', + 'Value not a const in enum BaconQrCode\Common\ErrorCorrectionLevel' + ); + new ErrorCorrectionLevel(4); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/FormatInformationTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/FormatInformationTest.php new file mode 100644 index 00000000..5f6ee58c --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/FormatInformationTest.php @@ -0,0 +1,104 @@ +unmaskedTestFormatInfo = $this->maskedTestFormatInfo ^ 0x5412; + } + + + public function testBitsDiffering() + { + $this->assertEquals(0, FormatInformation::numBitsDiffering(1, 1)); + $this->assertEquals(1, FormatInformation::numBitsDiffering(0, 2)); + $this->assertEquals(2, FormatInformation::numBitsDiffering(1, 2)); + $this->assertEquals(32, FormatInformation::numBitsDiffering(-1, 0)); + } + + public function testDecode() + { + $expected = FormatInformation::decodeFormatInformation( + $this->maskedTestFormatInfo, + $this->maskedTestFormatInfo + ); + + $this->assertNotNull($expected); + $this->assertEquals(7, $expected->getDataMask()); + $this->assertEquals(ErrorCorrectionLevel::Q, $expected->getErrorCorrectionLevel()->get()); + + $this->assertEquals( + $expected, + FormatInformation::decodeFormatInformation( + $this->unmaskedTestFormatInfo, + $this->maskedTestFormatInfo + ) + ); + } + + public function testDecodeWithBitDifference() + { + $expected = FormatInformation::decodeFormatInformation( + $this->maskedTestFormatInfo, + $this->maskedTestFormatInfo + ); + + $this->assertEquals( + $expected, + FormatInformation::decodeFormatInformation( + $this->maskedTestFormatInfo ^ 0x1, + $this->maskedTestFormatInfo ^ 0x1 + ) + ); + $this->assertEquals( + $expected, + FormatInformation::decodeFormatInformation( + $this->maskedTestFormatInfo ^ 0x3, + $this->maskedTestFormatInfo ^ 0x3 + ) + ); + $this->assertEquals( + $expected, + FormatInformation::decodeFormatInformation( + $this->maskedTestFormatInfo ^ 0x7, + $this->maskedTestFormatInfo ^ 0x7 + ) + ); + $this->assertNull( + FormatInformation::decodeFormatInformation( + $this->maskedTestFormatInfo ^ 0xf, + $this->maskedTestFormatInfo ^ 0xf + ) + ); + } + + public function testDecodeWithMisRead() + { + $expected = FormatInformation::decodeFormatInformation( + $this->maskedTestFormatInfo, + $this->maskedTestFormatInfo + ); + + $this->assertEquals( + $expected, + FormatInformation::decodeFormatInformation( + $this->maskedTestFormatInfo ^ 0x3, + $this->maskedTestFormatInfo ^ 0xf + ) + ); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/ModeTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/ModeTest.php new file mode 100644 index 00000000..4daab7c3 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/ModeTest.php @@ -0,0 +1,42 @@ +assertEquals(0x0, Mode::TERMINATOR); + $this->assertEquals(0x1, Mode::NUMERIC); + $this->assertEquals(0x2, Mode::ALPHANUMERIC); + $this->assertEquals(0x4, Mode::BYTE); + $this->assertEquals(0x8, Mode::KANJI); + } + + public function testInvalidModeThrowsException() + { + $this->setExpectedException( + 'BaconQrCode\Exception\UnexpectedValueException', + 'Value not a const in enum BaconQrCode\Common\Mode' + ); + new Mode(10); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/ReedSolomonCodecTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/ReedSolomonCodecTest.php new file mode 100644 index 00000000..99a6c725 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/ReedSolomonCodecTest.php @@ -0,0 +1,107 @@ +encode($block, $parity); + + // Copy parity into test blocks + for ($i = 0; $i < $numRoots; $i++) { + $block[$i + $dataSize] = $parity[$i]; + $tBlock[$i + $dataSize] = $parity[$i]; + } + + // Seed with errors + for ($i = 0; $i < $errors; $i++) { + $errorValue = mt_rand(1, $blockSize); + + do { + $errorLocation = mt_rand(0, $blockSize); + } while ($errorLocations[$errorLocation] !== 0); + + $errorLocations[$errorLocation] = 1; + + if (mt_rand(0, 1)) { + $erasures[] = $errorLocation; + } + + $tBlock[$errorLocation] ^= $errorValue; + } + + $erasures = SplFixedArray::fromArray($erasures, false); + + // Decode the errored block + $foundErrors = $codec->decode($tBlock, $erasures); + + if ($errors > 0 && $foundErrors === null) { + $this->assertEquals($block, $tBlock, 'Decoder failed to correct errors'); + } + + $this->assertEquals($errors, $foundErrors, 'Found errors do not equal expected errors'); + + for ($i = 0; $i < $foundErrors; $i++) { + if ($errorLocations[$erasures[$i]] === 0) { + $this->fail(sprintf('Decoder indicates error in location %d without error', $erasures[$i])); + } + } + + $this->assertEquals($block, $tBlock, 'Decoder did not correct errors'); + } + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/VersionTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/VersionTest.php new file mode 100644 index 00000000..8b3fc01f --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Common/VersionTest.php @@ -0,0 +1,88 @@ +assertNotNull($version); + $this->assertEquals($versionNumber, $version->getVersionNumber()); + $this->assertNotNull($version->getAlignmentPatternCenters()); + + if ($versionNumber > 1) { + $this->assertTrue(count($version->getAlignmentPatternCenters()) > 0); + } + + $this->assertEquals($dimension, $version->getDimensionForVersion()); + $this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::H))); + $this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::L))); + $this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::M))); + $this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::Q))); + $this->assertNotNull($version->buildFunctionPattern()); + } + + /** + * @dataProvider versionProvider + * @param integer $versionNumber + * @param integer $dimension + */ + public function testGetProvisionalVersionForDimension($versionNumber, $dimension) + { + $this->assertEquals( + $versionNumber, + Version::getProvisionalVersionForDimension($dimension)->getVersionNumber() + ); + } + + /** + * @dataProvider decodeInformationProvider + * @param integer $expectedVersion + * @param integer $mask + */ + public function testDecodeVersionInformation($expectedVersion, $mask) + { + $version = Version::decodeVersionInformation($mask); + $this->assertNotNull($version); + $this->assertEquals($expectedVersion, $version->getVersionNumber()); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Encoder/EncoderTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Encoder/EncoderTest.php new file mode 100644 index 00000000..31cdaa4e --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Encoder/EncoderTest.php @@ -0,0 +1,468 @@ +getMethods(ReflectionMethod::IS_STATIC) as $method) { + $method->setAccessible(true); + $this->methods[$method->getName()] = $method; + } + } + + public function testGetAlphanumericCode() + { + // The first ten code points are numbers. + for ($i = 0; $i < 10; $i++) { + $this->assertEquals($i, $this->methods['getAlphanumericCode']->invoke(null, ord('0') + $i)); + } + + // The next 26 code points are capital alphabet letters. + for ($i = 10; $i < 36; $i++) { + // The first ten code points are numbers + $this->assertEquals($i, $this->methods['getAlphanumericCode']->invoke(null, ord('A') + $i - 10)); + } + + // Others are symbol letters. + $this->assertEquals(36, $this->methods['getAlphanumericCode']->invoke(null, ' ')); + $this->assertEquals(37, $this->methods['getAlphanumericCode']->invoke(null, '$')); + $this->assertEquals(38, $this->methods['getAlphanumericCode']->invoke(null, '%')); + $this->assertEquals(39, $this->methods['getAlphanumericCode']->invoke(null, '*')); + $this->assertEquals(40, $this->methods['getAlphanumericCode']->invoke(null, '+')); + $this->assertEquals(41, $this->methods['getAlphanumericCode']->invoke(null, '-')); + $this->assertEquals(42, $this->methods['getAlphanumericCode']->invoke(null, '.')); + $this->assertEquals(43, $this->methods['getAlphanumericCode']->invoke(null, '/')); + $this->assertEquals(44, $this->methods['getAlphanumericCode']->invoke(null, ':')); + + // Should return -1 for other letters. + $this->assertEquals(-1, $this->methods['getAlphanumericCode']->invoke(null, 'a')); + $this->assertEquals(-1, $this->methods['getAlphanumericCode']->invoke(null, '#')); + $this->assertEquals(-1, $this->methods['getAlphanumericCode']->invoke(null, "\0")); + } + + public function testChooseMode() + { + // Numeric mode + $this->assertSame(Mode::NUMERIC, $this->methods['chooseMode']->invoke(null, '0')->get()); + $this->assertSame(Mode::NUMERIC, $this->methods['chooseMode']->invoke(null, '0123456789')->get()); + + // Alphanumeric mode + $this->assertSame(Mode::ALPHANUMERIC, $this->methods['chooseMode']->invoke(null, 'A')->get()); + $this->assertSame(Mode::ALPHANUMERIC, $this->methods['chooseMode']->invoke(null, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:')->get()); + + // 8-bit byte mode + $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, 'a')->get()); + $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, '#')->get()); + $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, '')->get()); + + // AIUE in Hiragana in SHIFT-JIS + $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, "\x8\xa\x8\xa\x8\xa\x8\xa6")->get()); + + // Nihon in Kanji in SHIFT-JIS + $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, "\x9\xf\x9\x7b")->get()); + + // Sou-Utso-Byou in Kanji in SHIFT-JIS + $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, "\xe\x4\x9\x5\x9\x61")->get()); + } + + public function testEncode() + { + $qrCode = Encoder::encode('ABCDEF', new ErrorCorrectionLevel(ErrorCorrectionLevel::H)); + $expected = "<<\n" + . " mode: ALPHANUMERIC\n" + . " ecLevel: H\n" + . " version: 1\n" + . " maskPattern: 0\n" + . " matrix:\n" + . " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0\n" + . " 0 0 1 0 1 1 1 0 1 1 0 0 1 1 0 0 0 1 0 0 1\n" + . " 1 0 1 1 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0\n" + . " 0 0 1 1 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 1 0\n" + . " 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0\n" + . " 0 0 1 1 0 1 1 1 1 0 0 0 1 0 1 0 1 1 1 1 0\n" + . " 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 1 0 1 0 0 0\n" + . " 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 1 0 0 0 0 1\n" + . " 1 0 0 0 0 0 1 0 1 1 1 1 0 1 0 1 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 1 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 1 0 1 1 1 1 0 1 0 1 0\n" + . " 1 0 1 1 1 0 1 0 1 0 0 0 1 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 1 1 0 1 1 0 1 0 0 0 1 1\n" + . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1\n" + . ">>\n"; + + $this->assertEquals($expected, $qrCode->__toString()); + } + + public function testSimpleUtf8Eci() + { + $qrCode = Encoder::encode('hello', new ErrorCorrectionLevel(ErrorCorrectionLevel::H), 'utf-8'); + $expected = "<<\n" + . " mode: BYTE\n" + . " ecLevel: H\n" + . " version: 1\n" + . " maskPattern: 3\n" + . " matrix:\n" + . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 1 0 1 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 1 1 0 0 1 1 1 1 0 0 0 1 1 0 1 0 0 0 0\n" + . " 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 1 0 1 1 1 0\n" + . " 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 1 1 1\n" + . " 1 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 1 0 1 1 0\n" + . " 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 0 1 0 0\n" + . " 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1 1 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 0 0 1 0 0\n" + . " 1 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0\n" + . " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 0 0 1 1 0 0 0\n" + . " 1 0 1 1 1 0 1 0 1 1 0 0 0 1 0 0 1 0 0 0 0\n" + . " 1 0 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 1 1 0\n" + . " 1 1 1 1 1 1 1 0 0 1 0 1 1 1 0 1 1 0 0 0 0\n" + . ">>\n"; + + $this->assertEquals($expected, $qrCode->__toString()); + } + + public function testAppendModeInfo() + { + $bits = new BitArray(); + $this->methods['appendModeInfo']->invoke(null, new Mode(Mode::NUMERIC), $bits); + $this->assertEquals(' ...X', $bits->__toString()); + } + + public function testAppendLengthInfo() + { + // 1 letter (1/1), 10 bits. + $bits = new BitArray(); + $this->methods['appendLengthInfo']->invoke( + null, + 1, + Version::getVersionForNumber(1), + new Mode(Mode::NUMERIC), + $bits + ); + $this->assertEquals(' ........ .X', $bits->__toString()); + + // 2 letters (2/1), 11 bits. + $bits = new BitArray(); + $this->methods['appendLengthInfo']->invoke( + null, + 2, + Version::getVersionForNumber(10), + new Mode(Mode::ALPHANUMERIC), + $bits + ); + $this->assertEquals(' ........ .X.', $bits->__toString()); + + // 255 letters (255/1), 16 bits. + $bits = new BitArray(); + $this->methods['appendLengthInfo']->invoke( + null, + 255, + Version::getVersionForNumber(27), + new Mode(Mode::BYTE), + $bits + ); + $this->assertEquals(' ........ XXXXXXXX', $bits->__toString()); + + // 512 letters (1024/2), 12 bits. + $bits = new BitArray(); + $this->methods['appendLengthInfo']->invoke( + null, + 512, + Version::getVersionForNumber(40), + new Mode(Mode::KANJI), + $bits + ); + $this->assertEquals(' ..X..... ....', $bits->__toString()); + } + + public function testAppendBytes() + { + // Should use appendNumericBytes. + // 1 = 01 = 0001 in 4 bits. + $bits = new BitArray(); + $this->methods['appendBytes']->invoke( + null, + '1', + new Mode(Mode::NUMERIC), + $bits, + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->assertEquals(' ...X', $bits->__toString()); + + // Should use appendAlphaNumericBytes. + // A = 10 = 0xa = 001010 in 6 bits. + $bits = new BitArray(); + $this->methods['appendBytes']->invoke( + null, + 'A', + new Mode(Mode::ALPHANUMERIC), + $bits, + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->assertEquals(' ..X.X.', $bits->__toString()); + + // Should use append8BitBytes. + // 0x61, 0x62, 0x63 + $bits = new BitArray(); + $this->methods['appendBytes']->invoke( + null, + 'abc', + new Mode(Mode::BYTE), + $bits, + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->assertEquals(' .XX....X .XX...X. .XX...XX', $bits->__toString()); + + // Should use appendKanjiBytes. + // 0x93, 0x5f + $bits = new BitArray(); + $this->methods['appendBytes']->invoke( + null, + "\x93\x5f", + new Mode(Mode::KANJI), + $bits, + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->assertEquals(' .XX.XX.. XXXXX', $bits->__toString()); + + // Lower letters such as 'a' cannot be encoded in alphanumeric mode. + $this->setExpectedException( + 'BaconQrCode\Exception\WriterException', + 'Invalid alphanumeric code' + ); + $this->methods['appendBytes']->invoke( + null, + "a", + new Mode(Mode::ALPHANUMERIC), + $bits, + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + } + + public function testTerminateBits() + { + $bits = new BitArray(); + $this->methods['terminateBits']->invoke(null, 0, $bits); + $this->assertEquals('', $bits->__toString()); + + $bits = new BitArray(); + $this->methods['terminateBits']->invoke(null, 1, $bits); + $this->assertEquals(' ........', $bits->__toString()); + + $bits = new BitArray(); + $bits->appendBits(0, 3); + $this->methods['terminateBits']->invoke(null, 1, $bits); + $this->assertEquals(' ........', $bits->__toString()); + + $bits = new BitArray(); + $bits->appendBits(0, 5); + $this->methods['terminateBits']->invoke(null, 1, $bits); + $this->assertEquals(' ........', $bits->__toString()); + + $bits = new BitArray(); + $bits->appendBits(0, 8); + $this->methods['terminateBits']->invoke(null, 1, $bits); + $this->assertEquals(' ........', $bits->__toString()); + + $bits = new BitArray(); + $this->methods['terminateBits']->invoke(null, 2, $bits); + $this->assertEquals(' ........ XXX.XX..', $bits->__toString()); + + $bits = new BitArray(); + $bits->appendBits(0, 1); + $this->methods['terminateBits']->invoke(null, 3, $bits); + $this->assertEquals(' ........ XXX.XX.. ...X...X', $bits->__toString()); + } + + public function testGetNumDataBytesAndNumEcBytesForBlockId() + { + // Version 1-H. + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 26, 9, 1, 0); + $this->assertEquals(9, $numDataBytes); + $this->assertEquals(17, $numEcBytes); + + // Version 3-H. 2 blocks. + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 70, 26, 2, 0); + $this->assertEquals(13, $numDataBytes); + $this->assertEquals(22, $numEcBytes); + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 70, 26, 2, 1); + $this->assertEquals(13, $numDataBytes); + $this->assertEquals(22, $numEcBytes); + + // Version 7-H. (4 + 1) blocks. + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 196, 66, 5, 0); + $this->assertEquals(13, $numDataBytes); + $this->assertEquals(26, $numEcBytes); + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 196, 66, 5, 4); + $this->assertEquals(14, $numDataBytes); + $this->assertEquals(26, $numEcBytes); + + // Version 40-H. (20 + 61) blocks. + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 3706, 1276, 81, 0); + $this->assertEquals(15, $numDataBytes); + $this->assertEquals(30, $numEcBytes); + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 3706, 1276, 81, 20); + $this->assertEquals(16, $numDataBytes); + $this->assertEquals(30, $numEcBytes); + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 3706, 1276, 81, 80); + $this->assertEquals(16, $numDataBytes); + $this->assertEquals(30, $numEcBytes); + } + + public function testInterleaveWithEcBytes() + { + $dataBytes = SplFixedArray::fromArray(array(32, 65, 205, 69, 41, 220, 46, 128, 236), false); + $in = new BitArray(); + + foreach ($dataBytes as $dataByte) { + $in->appendBits($dataByte, 8); + } + + $outBits = $this->methods['interleaveWithEcBytes']->invoke(null, $in, 26, 9, 1); + $expected = SplFixedArray::fromArray(array( + // Data bytes. + 32, 65, 205, 69, 41, 220, 46, 128, 236, + // Error correction bytes. + 42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61, + ), false); + + $out = $outBits->toBytes(0, count($expected)); + + $this->assertEquals($expected, $out); + } + + public function testAppendNumericBytes() + { + // 1 = 01 = 0001 in 4 bits. + $bits = new BitArray(); + $this->methods['appendNumericBytes']->invoke(null, '1', $bits); + $this->assertEquals(' ...X', $bits->__toString()); + + // 12 = 0xc = 0001100 in 7 bits. + $bits = new BitArray(); + $this->methods['appendNumericBytes']->invoke(null, '12', $bits); + $this->assertEquals(' ...XX..', $bits->__toString()); + + // 123 = 0x7b = 0001111011 in 10 bits. + $bits = new BitArray(); + $this->methods['appendNumericBytes']->invoke(null, '123', $bits); + $this->assertEquals(' ...XXXX. XX', $bits->__toString()); + + // 1234 = "123" + "4" = 0001111011 + 0100 in 14 bits. + $bits = new BitArray(); + $this->methods['appendNumericBytes']->invoke(null, '1234', $bits); + $this->assertEquals(' ...XXXX. XX.X..', $bits->__toString()); + + // Empty + $bits = new BitArray(); + $this->methods['appendNumericBytes']->invoke(null, '', $bits); + $this->assertEquals('', $bits->__toString()); + } + + public function testAppendAlphanumericBytes() + { + $bits = new BitArray(); + $this->methods['appendAlphanumericBytes']->invoke(null, 'A', $bits); + $this->assertEquals(' ..X.X.', $bits->__toString()); + + $bits = new BitArray(); + $this->methods['appendAlphanumericBytes']->invoke(null, 'AB', $bits); + $this->assertEquals(' ..XXX..X X.X', $bits->__toString()); + + $bits = new BitArray(); + $this->methods['appendAlphanumericBytes']->invoke(null, 'ABC', $bits); + $this->assertEquals(' ..XXX..X X.X..XX. .', $bits->__toString()); + + // Empty + $bits = new BitArray(); + $this->methods['appendAlphanumericBytes']->invoke(null, '', $bits); + $this->assertEquals('', $bits->__toString()); + + // Invalid data + $this->setExpectedException('BaconQrCode\Exception\WriterException', 'Invalid alphanumeric code'); + $bits = new BitArray(); + $this->methods['appendAlphanumericBytes']->invoke(null, 'abc', $bits); + } + + public function testAppend8BitBytes() + { + // 0x61, 0x62, 0x63 + $bits = new BitArray(); + $this->methods['append8BitBytes']->invoke(null, 'abc', $bits, Encoder::DEFAULT_BYTE_MODE_ECODING); + $this->assertEquals(' .XX....X .XX...X. .XX...XX', $bits->__toString()); + + // Empty + $bits = new BitArray(); + $this->methods['append8BitBytes']->invoke(null, '', $bits, Encoder::DEFAULT_BYTE_MODE_ECODING); + $this->assertEquals('', $bits->__toString()); + } + + public function testAppendKanjiBytes() + { + // Numbers are from page 21 of JISX0510:2004 + $bits = new BitArray(); + $this->methods['appendKanjiBytes']->invoke(null, "\x93\x5f", $bits); + $this->assertEquals(' .XX.XX.. XXXXX', $bits->__toString()); + + $this->methods['appendKanjiBytes']->invoke(null, "\xe4\xaa", $bits); + $this->assertEquals(' .XX.XX.. XXXXXXX. X.X.X.X. X.', $bits->__toString()); + } + + public function testGenerateEcBytes() + { + // Numbers are from http://www.swetake.com/qr/qr3.html and + // http://www.swetake.com/qr/qr9.html + $dataBytes = SplFixedArray::fromArray(array(32, 65, 205, 69, 41, 220, 46, 128, 236), false); + $ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 17); + $expected = SplFixedArray::fromArray(array(42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61), false); + $this->assertEquals($expected, $ecBytes); + + $dataBytes = SplFixedArray::fromArray(array(67, 70, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214), false); + $ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 18); + $expected = SplFixedArray::fromArray(array(175, 80, 155, 64, 178, 45, 214, 233, 65, 209, 12, 155, 117, 31, 140, 214, 27, 187), false); + $this->assertEquals($expected, $ecBytes); + + // High-order zero coefficient case. + $dataBytes = SplFixedArray::fromArray(array(32, 49, 205, 69, 42, 20, 0, 236, 17), false); + $ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 17); + $expected = SplFixedArray::fromArray(array(0, 3, 130, 179, 194, 0, 55, 211, 110, 79, 98, 72, 170, 96, 211, 137, 213), false); + $this->assertEquals($expected, $ecBytes); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Encoder/MaskUtilTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Encoder/MaskUtilTest.php new file mode 100644 index 00000000..a5c38656 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Encoder/MaskUtilTest.php @@ -0,0 +1,281 @@ +fail('Data mask bit did not match'); + } + } + } + } + + public function testApplyMaskPenaltyRule1() + { + $matrix = new ByteMatrix(4, 1); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(2, 0, 0); + $matrix->set(3, 0, 0); + + $this->assertEquals(0, MaskUtil::applyMaskPenaltyRule1($matrix)); + + // Horizontal + $matrix = new ByteMatrix(6, 1); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(2, 0, 0); + $matrix->set(3, 0, 0); + $matrix->set(4, 0, 0); + $matrix->set(5, 0, 1); + $this->assertEquals(3, MaskUtil::applyMaskPenaltyRule1($matrix)); + $matrix->set(5, 0, 0); + $this->assertEquals(4, MaskUtil::applyMaskPenaltyRule1($matrix)); + + // Vertical + $matrix = new ByteMatrix(1, 6); + $matrix->set(0, 0, 0); + $matrix->set(0, 1, 0); + $matrix->set(0, 2, 0); + $matrix->set(0, 3, 0); + $matrix->set(0, 4, 0); + $matrix->set(0, 5, 1); + $this->assertEquals(3, MaskUtil::applyMaskPenaltyRule1($matrix)); + $matrix->set(0, 5, 0); + $this->assertEquals(4, MaskUtil::applyMaskPenaltyRule1($matrix)); + } + + public function testApplyMaskPenaltyRule2() + { + $matrix = new ByteMatrix(1, 1); + $matrix->set(0, 0, 0); + $this->assertEquals(0, MaskUtil::applyMaskPenaltyRule2($matrix)); + + $matrix = new ByteMatrix(2, 2); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(0, 1, 0); + $matrix->set(1, 1, 1); + $this->assertEquals(0, MaskUtil::applyMaskPenaltyRule2($matrix)); + + $matrix = new ByteMatrix(2, 2); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(0, 1, 0); + $matrix->set(1, 1, 0); + $this->assertEquals(3, MaskUtil::applyMaskPenaltyRule2($matrix)); + + $matrix = new ByteMatrix(3, 3); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(2, 0, 0); + $matrix->set(0, 1, 0); + $matrix->set(1, 1, 0); + $matrix->set(2, 1, 0); + $matrix->set(0, 2, 0); + $matrix->set(1, 2, 0); + $matrix->set(2, 2, 0); + $this->assertEquals(3 * 4, MaskUtil::applyMaskPenaltyRule2($matrix)); + } + + public function testApplyMaskPenalty3() + { + // Horizontal 00001011101 + $matrix = new ByteMatrix(11, 1); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(2, 0, 0); + $matrix->set(3, 0, 0); + $matrix->set(4, 0, 1); + $matrix->set(5, 0, 0); + $matrix->set(6, 0, 1); + $matrix->set(7, 0, 1); + $matrix->set(8, 0, 1); + $matrix->set(9, 0, 0); + $matrix->set(10, 0, 1); + $this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix)); + + // Horizontal 10111010000 + $matrix = new ByteMatrix(11, 1); + $matrix->set(0, 0, 1); + $matrix->set(1, 0, 0); + $matrix->set(2, 0, 1); + $matrix->set(3, 0, 1); + $matrix->set(4, 0, 1); + $matrix->set(5, 0, 0); + $matrix->set(6, 0, 1); + $matrix->set(7, 0, 0); + $matrix->set(8, 0, 0); + $matrix->set(9, 0, 0); + $matrix->set(10, 0, 0); + $this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix)); + + // Vertical 00001011101 + $matrix = new ByteMatrix(1, 11); + $matrix->set(0, 0, 0); + $matrix->set(0, 1, 0); + $matrix->set(0, 2, 0); + $matrix->set(0, 3, 0); + $matrix->set(0, 4, 1); + $matrix->set(0, 5, 0); + $matrix->set(0, 6, 1); + $matrix->set(0, 7, 1); + $matrix->set(0, 8, 1); + $matrix->set(0, 9, 0); + $matrix->set(0, 10, 1); + $this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix)); + + // Vertical 10111010000 + $matrix = new ByteMatrix(1, 11); + $matrix->set(0, 0, 1); + $matrix->set(0, 1, 0); + $matrix->set(0, 2, 1); + $matrix->set(0, 3, 1); + $matrix->set(0, 4, 1); + $matrix->set(0, 5, 0); + $matrix->set(0, 6, 1); + $matrix->set(0, 7, 0); + $matrix->set(0, 8, 0); + $matrix->set(0, 9, 0); + $matrix->set(0, 10, 0); + $this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix)); + } + + public function testApplyMaskPenaltyRule4() + { + // Dark cell ratio = 0% + $matrix = new ByteMatrix(1, 1); + $matrix->set(0, 0, 0); + $this->assertEquals(100, MaskUtil::applyMaskPenaltyRule4($matrix)); + + // Dark cell ratio = 5% + $matrix = new ByteMatrix(2, 1); + $matrix->set(0, 0, 0); + $matrix->set(0, 0, 1); + $this->assertEquals(0, MaskUtil::applyMaskPenaltyRule4($matrix)); + + // Dark cell ratio = 66.67% + $matrix = new ByteMatrix(6, 1); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 1); + $matrix->set(2, 0, 1); + $matrix->set(3, 0, 1); + $matrix->set(4, 0, 1); + $matrix->set(5, 0, 0); + $this->assertEquals(30, MaskUtil::applyMaskPenaltyRule4($matrix)); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Encoder/MatrixUtilTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Encoder/MatrixUtilTest.php new file mode 100644 index 00000000..bf3544f0 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Encoder/MatrixUtilTest.php @@ -0,0 +1,336 @@ +getMethods(ReflectionMethod::IS_STATIC) as $method) { + $method->setAccessible(true); + $this->methods[$method->getName()] = $method; + } + } + + public function testToString() + { + $matrix= new ByteMatrix(3, 3); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 1); + $matrix->set(2, 0, 0); + $matrix->set(0, 1, 1); + $matrix->set(1, 1, 0); + $matrix->set(2, 1, 1); + $matrix->set(0, 2, -1); + $matrix->set(1, 2, -1); + $matrix->set(2, 2, -1); + + $expected = " 0 1 0\n 1 0 1\n \n"; + $this->assertEquals($expected, $matrix->__toString()); + } + + public function testClearMatrix() + { + $matrix = new ByteMatrix(2, 2); + MatrixUtil::clearMatrix($matrix); + + $this->assertEquals(-1, $matrix->get(0, 0)); + $this->assertEquals(-1, $matrix->get(1, 0)); + $this->assertEquals(-1, $matrix->get(0, 1)); + $this->assertEquals(-1, $matrix->get(1, 1)); + } + + public function testEmbedBasicPatterns1() + { + $matrix = new ByteMatrix(21, 21); + MatrixUtil::clearMatrix($matrix); + $this->methods['embedBasicPatterns']->invoke( + null, + Version::getVersionForNumber(1), + $matrix + ); + $expected = " 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 \n" + . " 0 \n" + . " 1 \n" + . " 0 \n" + . " 1 \n" + . " 0 0 0 0 0 0 0 0 1 \n" + . " 1 1 1 1 1 1 1 0 \n" + . " 1 0 0 0 0 0 1 0 \n" + . " 1 0 1 1 1 0 1 0 \n" + . " 1 0 1 1 1 0 1 0 \n" + . " 1 0 1 1 1 0 1 0 \n" + . " 1 0 0 0 0 0 1 0 \n" + . " 1 1 1 1 1 1 1 0 \n"; + + $this->assertEquals($expected, $matrix->__toString()); + } + + public function testEmbedBasicPatterns2() + { + $matrix = new ByteMatrix(25, 25); + MatrixUtil::clearMatrix($matrix); + $this->methods['embedBasicPatterns']->invoke( + null, + Version::getVersionForNumber(2), + $matrix + ); + $expected = " 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 \n" + . " 0 \n" + . " 1 \n" + . " 0 \n" + . " 1 \n" + . " 0 \n" + . " 1 \n" + . " 0 \n" + . " 1 1 1 1 1 1 \n" + . " 0 0 0 0 0 0 0 0 1 1 0 0 0 1 \n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 \n" + . " 1 0 0 0 0 0 1 0 1 0 0 0 1 \n" + . " 1 0 1 1 1 0 1 0 1 1 1 1 1 \n" + . " 1 0 1 1 1 0 1 0 \n" + . " 1 0 1 1 1 0 1 0 \n" + . " 1 0 0 0 0 0 1 0 \n" + . " 1 1 1 1 1 1 1 0 \n"; + + $this->assertEquals($expected, $matrix->__toString()); + } + + public function testEmbedTypeInfo() + { + $matrix = new ByteMatrix(21, 21); + MatrixUtil::clearMatrix($matrix); + $this->methods['embedTypeInfo']->invoke( + null, + new ErrorCorrectionLevel(ErrorCorrectionLevel::M), + 5, + $matrix + ); + $expected = " 0 \n" + . " 1 \n" + . " 1 \n" + . " 1 \n" + . " 0 \n" + . " 0 \n" + . " \n" + . " 1 \n" + . " 1 0 0 0 0 0 0 1 1 1 0 0 1 1 1 0\n" + . " \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " 0 \n" + . " 0 \n" + . " 0 \n" + . " 0 \n" + . " 0 \n" + . " 0 \n" + . " 1 \n"; + + $this->assertEquals($expected, $matrix->__toString()); + } + + public function testEmbedVersionInfo() + { + $matrix = new ByteMatrix(21, 21); + MatrixUtil::clearMatrix($matrix); + $this->methods['maybeEmbedVersionInfo']->invoke( + null, + Version::getVersionForNumber(7), + $matrix + ); + $expected = " 0 0 1 \n" + . " 0 1 0 \n" + . " 0 1 0 \n" + . " 0 1 1 \n" + . " 1 1 1 \n" + . " 0 0 0 \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " 0 0 0 0 1 0 \n" + . " 0 1 1 1 1 0 \n" + . " 1 0 0 1 1 0 \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " \n"; + + $this->assertEquals($expected, $matrix->__toString()); + } + + public function testEmbedDataBits() + { + $matrix = new ByteMatrix(21, 21); + MatrixUtil::clearMatrix($matrix); + $this->methods['embedBasicPatterns']->invoke( + null, + Version::getVersionForNumber(1), + $matrix + ); + + $bits = new BitArray(); + $this->methods['embedDataBits']->invoke( + null, + $bits, + -1, + $matrix + ); + + $expected = " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"; + + $this->assertEquals($expected, $matrix->__toString()); + } + + public function testBuildMatrix() + { + $bytes = array( + 32, 65, 205, 69, 41, 220, 46, 128, 236, 42, 159, 74, 221, 244, 169, + 239, 150, 138, 70, 237, 85, 224, 96, 74, 219 , 61 + ); + $bits = new BitArray(); + + foreach ($bytes as $byte) { + $bits->appendBits($byte, 8); + } + + $matrix = new ByteMatrix(21, 21); + MatrixUtil::buildMatrix( + $bits, + new ErrorCorrectionLevel(ErrorCorrectionLevel::H), + Version::getVersionForNumber(1), + 3, + $matrix + ); + + $expected = " 1 1 1 1 1 1 1 0 0 1 1 0 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 0 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 1 0 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 1 1 0 0 1 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 0 1 1 1 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0\n" + . " 0 0 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1 0 0 0 0\n" + . " 1 0 1 0 1 0 0 0 0 0 1 1 1 0 0 1 0 1 1 1 0\n" + . " 1 1 1 1 0 1 1 0 1 0 1 1 1 0 0 1 1 1 0 1 0\n" + . " 1 0 1 0 1 1 0 1 1 1 0 0 1 1 1 0 0 1 0 1 0\n" + . " 0 0 1 0 0 1 1 1 0 0 0 0 0 0 1 0 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 1 0 1 1\n" + . " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 0 0 1 0 1 1 0\n" + . " 1 0 0 0 0 0 1 0 0 0 0 1 0 1 1 1 0 0 0 0 0\n" + . " 1 0 1 1 1 0 1 0 0 1 0 0 1 1 0 0 1 0 0 1 1\n" + . " 1 0 1 1 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 1 0\n" + . " 1 0 1 1 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 0 0\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0\n" + . " 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 0 1 0 0 1 0\n"; + + $this->assertEquals($expected, $matrix->__toString()); + } + + public function testFindMsbSet() + { + $this->assertEquals(0, $this->methods['findMsbSet']->invoke(null, 0)); + $this->assertEquals(1, $this->methods['findMsbSet']->invoke(null, 1)); + $this->assertEquals(8, $this->methods['findMsbSet']->invoke(null, 0x80)); + $this->assertEquals(32, $this->methods['findMsbSet']->invoke(null, 0x80000000)); + } + + public function testCalculateBchCode() + { + // Encoding of type information. + // From Appendix C in JISX0510:2004 (p 65) + $this->assertEquals(0xdc, $this->methods['calculateBchCode']->invoke(null, 5, 0x537)); + // From http://www.swetake.com/qr/qr6.html + $this->assertEquals(0x1c2, $this->methods['calculateBchCode']->invoke(null, 0x13, 0x537)); + // From http://www.swetake.com/qr/qr11.html + $this->assertEquals(0x214, $this->methods['calculateBchCode']->invoke(null, 0x1b, 0x537)); + + // Encoding of version information. + // From Appendix D in JISX0510:2004 (p 68) + $this->assertEquals(0xc94, $this->methods['calculateBchCode']->invoke(null, 7, 0x1f25)); + $this->assertEquals(0x5bc, $this->methods['calculateBchCode']->invoke(null, 8, 0x1f25)); + $this->assertEquals(0xa99, $this->methods['calculateBchCode']->invoke(null, 9, 0x1f25)); + $this->assertEquals(0x4d3, $this->methods['calculateBchCode']->invoke(null, 10, 0x1f25)); + $this->assertEquals(0x9a6, $this->methods['calculateBchCode']->invoke(null, 20, 0x1f25)); + $this->assertEquals(0xd75, $this->methods['calculateBchCode']->invoke(null, 30, 0x1f25)); + $this->assertEquals(0xc69, $this->methods['calculateBchCode']->invoke(null, 40, 0x1f25)); + } + + public function testMakeVersionInfoBits() + { + // From Appendix D in JISX0510:2004 (p 68) + $bits = new BitArray(); + $this->methods['makeVersionInfoBits']->invoke(null, Version::getVersionForNumber(7), $bits); + $this->assertEquals(' ...XXXXX ..X..X.X ..', $bits->__toString()); + } + + public function testMakeTypeInfoBits() + { + // From Appendix D in JISX0510:2004 (p 68) + $bits = new BitArray(); + $this->methods['makeTypeInfoBits']->invoke(null, new ErrorCorrectionLevel(ErrorCorrectionLevel::M), 5, $bits); + $this->assertEquals(' X......X X..XXX.', $bits->__toString()); + } +} \ No newline at end of file diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Renderer/Text/HtmlTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Renderer/Text/HtmlTest.php new file mode 100644 index 00000000..0c69dd2e --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Renderer/Text/HtmlTest.php @@ -0,0 +1,99 @@ +renderer = new Html(); + $this->writer = new Writer($this->renderer); + } + + public function testBasicRender() + { + $content = 'foobar'; + $expected = + '
' . + " \n" . + " ███████ █████ ███████ \n" . + " █ █ █ █ █ █ \n" . + " █ ███ █ ██ █ ███ █ \n" . + " █ ███ █ ███ █ ███ █ \n" . + " █ ███ █ █ █ █ ███ █ \n" . + " █ █ ██ █ █ \n" . + " ███████ █ █ █ ███████ \n" . + " █████ \n" . + " ██ ██ █ ██ █ █ █ \n" . + " ██ ██ █ █ ██ \n" . + " ████████ █ ██ █ ██ \n" . + " ██ █ █ \n" . + " ██ ███ █ █ █ █ \n" . + " █ ███ █ █ \n" . + " ███████ ██ ██████ \n" . + " █ █ ████ ██ \n" . + " █ ███ █ ██ ██ ██ █ ██ \n" . + " █ ███ █ ██ ██ █ ██ \n" . + " █ ███ █ █ █ ██ ██ \n" . + " █ █ ███ ███ ████ \n" . + " ███████ ████ ██ \n" . + " \n" . + '' + ; + + $qrCode = Encoder::encode( + $content, + new ErrorCorrectionLevel(ErrorCorrectionLevel::L), + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->assertEquals($expected, $this->renderer->render($qrCode)); + } + + public function testSetStyle() + { + $content = 'foobar'; + $qrCode = Encoder::encode( + $content, + new ErrorCorrectionLevel(ErrorCorrectionLevel::L), + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->renderer->setStyle('bar'); + $this->assertEquals('bar', $this->renderer->getStyle()); + $this->assertStringMatchesFormat('%astyle="bar"%a', $this->renderer->render($qrCode)); + } + + public function testSetClass() + { + $content = 'foobar'; + $qrCode = Encoder::encode( + $content, + new ErrorCorrectionLevel(ErrorCorrectionLevel::L), + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->renderer->setClass('bar'); + $this->assertEquals('bar', $this->renderer->getClass()); + $this->assertStringMatchesFormat('%aclass="bar"%a', $this->renderer->render($qrCode)); + } +} diff --git a/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Renderer/Text/TextTest.php b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Renderer/Text/TextTest.php new file mode 100644 index 00000000..d94e8e5d --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Renderer/Text/TextTest.php @@ -0,0 +1,149 @@ +renderer = new Plain(); + $this->writer = new Writer($this->renderer); + } + + public function testBasicRender() + { + $content = 'foobar'; + $expected = + " \n" . + " ███████ █████ ███████ \n" . + " █ █ █ █ █ █ \n" . + " █ ███ █ ██ █ ███ █ \n" . + " █ ███ █ ███ █ ███ █ \n" . + " █ ███ █ █ █ █ ███ █ \n" . + " █ █ ██ █ █ \n" . + " ███████ █ █ █ ███████ \n" . + " █████ \n" . + " ██ ██ █ ██ █ █ █ \n" . + " ██ ██ █ █ ██ \n" . + " ████████ █ ██ █ ██ \n" . + " ██ █ █ \n" . + " ██ ███ █ █ █ █ \n" . + " █ ███ █ █ \n" . + " ███████ ██ ██████ \n" . + " █ █ ████ ██ \n" . + " █ ███ █ ██ ██ ██ █ ██ \n" . + " █ ███ █ ██ ██ █ ██ \n" . + " █ ███ █ █ █ ██ ██ \n" . + " █ █ ███ ███ ████ \n" . + " ███████ ████ ██ \n" . + " \n" + ; + + $qrCode = Encoder::encode( + $content, + new ErrorCorrectionLevel(ErrorCorrectionLevel::L), + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->assertEquals($expected, $this->renderer->render($qrCode)); + } + + public function testBasicRenderNoMargins() + { + $content = 'foobar'; + $expected = + "███████ █████ ███████\n" . + "█ █ █ █ █ █\n" . + "█ ███ █ ██ █ ███ █\n" . + "█ ███ █ ███ █ ███ █\n" . + "█ ███ █ █ █ █ ███ █\n" . + "█ █ ██ █ █\n" . + "███████ █ █ █ ███████\n" . + " █████ \n" . + "██ ██ █ ██ █ █ █\n" . + " ██ ██ █ █ ██ \n" . + " ████████ █ ██ █ ██\n" . + " ██ █ █\n" . + " ██ ███ █ █ █ █\n" . + " █ ███ █ █ \n" . + "███████ ██ ██████ \n" . + "█ █ ████ ██ \n" . + "█ ███ █ ██ ██ ██ █ ██\n" . + "█ ███ █ ██ ██ █ ██ \n" . + "█ ███ █ █ █ ██ ██\n" . + "█ █ ███ ███ ████\n" . + "███████ ████ ██ \n" + ; + + $qrCode = Encoder::encode( + $content, + new ErrorCorrectionLevel(ErrorCorrectionLevel::L), + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->renderer->setMargin(0); + $this->assertEquals(0, $this->renderer->getMargin()); + $this->assertEquals($expected, $this->renderer->render($qrCode)); + } + + public function testBasicRenderCustomChar() + { + $content = 'foobar'; + $expected = + "-----------------------\n" . + "-#######-#####-#######-\n" . + "-#-----#--#-#--#-----#-\n" . + "-#-###-#--##---#-###-#-\n" . + "-#-###-#--###--#-###-#-\n" . + "-#-###-#---#-#-#-###-#-\n" . + "-#-----#----##-#-----#-\n" . + "-#######-#-#-#-#######-\n" . + "---------#####---------\n" . + "-##-##-#--##-#-#-----#-\n" . + "----##----##-#-#-##----\n" . + "--########-#--##-#--##-\n" . + "-----------##------#-#-\n" . + "--##--###--#---#--#--#-\n" . + "---------#-###----#-#--\n" . + "-#######--##-######----\n" . + "-#-----#---####---##---\n" . + "-#-###-#-##-##-##-#-##-\n" . + "-#-###-#-##-##--#-##---\n" . + "-#-###-#---#---#-##-##-\n" . + "-#-----#-###--###-####-\n" . + "-#######-####---##-----\n" . + "-----------------------\n" + ; + + $qrCode = Encoder::encode( + $content, + new ErrorCorrectionLevel(ErrorCorrectionLevel::L), + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->renderer->setFullBlock('#'); + $this->renderer->setEmptyBlock('-'); + $this->assertEquals('#', $this->renderer->getFullBlock()); + $this->assertEquals('-', $this->renderer->getEmptyBlock()); + $this->assertEquals($expected, $this->renderer->render($qrCode)); + } +} diff --git a/vendor/bacon/bacon-qr-code/tests/bootstrap.php b/vendor/bacon/bacon-qr-code/tests/bootstrap.php new file mode 100644 index 00000000..05a49415 --- /dev/null +++ b/vendor/bacon/bacon-qr-code/tests/bootstrap.php @@ -0,0 +1,10 @@ + +