aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/chillerlan/php-qrcode/src/Data
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/chillerlan/php-qrcode/src/Data')
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/AlphaNum.php116
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/Byte.php67
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/ECI.php165
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/Hanzi.php206
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/Kanji.php171
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/MaskPatternTester.php203
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/Number.php135
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/QRCodeDataException.php9
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/QRData.php263
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/QRDataAbstract.php311
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/QRDataInterface.php200
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/QRDataModeAbstract.php61
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/QRDataModeInterface.php63
-rwxr-xr-xvendor/chillerlan/php-qrcode/src/Data/QRMatrix.php811
-rw-r--r--vendor/chillerlan/php-qrcode/src/Data/ReedSolomonEncoder.php127
15 files changed, 1739 insertions, 1169 deletions
diff --git a/vendor/chillerlan/php-qrcode/src/Data/AlphaNum.php b/vendor/chillerlan/php-qrcode/src/Data/AlphaNum.php
index 28d9d7563..93ef9fc59 100644
--- a/vendor/chillerlan/php-qrcode/src/Data/AlphaNum.php
+++ b/vendor/chillerlan/php-qrcode/src/Data/AlphaNum.php
@@ -2,9 +2,7 @@
/**
* Class AlphaNum
*
- * @filesource AlphaNum.php
* @created 25.11.2015
- * @package chillerlan\QRCode\Data
* @author Smiley <smiley@chillerlan.net>
* @copyright 2015 Smiley
* @license MIT
@@ -12,9 +10,8 @@
namespace chillerlan\QRCode\Data;
-use chillerlan\QRCode\QRCode;
-
-use function ord, sprintf;
+use chillerlan\QRCode\Common\{BitBuffer, Mode};
+use function ceil, intdiv, preg_match, strpos;
/**
* Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / :
@@ -22,39 +19,118 @@ use function ord, sprintf;
* ISO/IEC 18004:2000 Section 8.3.3
* ISO/IEC 18004:2000 Section 8.4.3
*/
-final class AlphaNum extends QRDataAbstract{
+final class AlphaNum extends QRDataModeAbstract{
- protected int $datamode = QRCode::DATA_ALPHANUM;
+ /**
+ * ISO/IEC 18004:2000 Table 5
+ *
+ * @var string
+ */
+ private const CHAR_MAP = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
- protected array $lengthBits = [9, 11, 13];
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::ALPHANUM;
/**
- * @inheritdoc
+ * @inheritDoc
*/
- protected function write(string $data):void{
+ public function getLengthInBits():int{
+ return (int)ceil($this->getCharCount() * (11 / 2));
+ }
- for($i = 0; $i + 1 < $this->strlen; $i += 2){
- $this->bitBuffer->put($this->getCharCode($data[$i]) * 45 + $this->getCharCode($data[$i + 1]), 11);
+ /**
+ * @inheritDoc
+ */
+ public static function validateString(string $string):bool{
+ return (bool)preg_match('/^[A-Z\d %$*+-.:\/]+$/', $string);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+ $len = $this->getCharCount();
+
+ $bitBuffer
+ ->put(self::DATAMODE, 4)
+ ->put($len, $this::getLengthBits($versionNumber))
+ ;
+
+ // encode 2 characters in 11 bits
+ for($i = 0; ($i + 1) < $len; $i += 2){
+ $bitBuffer->put(
+ ($this->ord($this->data[$i]) * 45 + $this->ord($this->data[($i + 1)])),
+ 11,
+ );
}
- if($i < $this->strlen){
- $this->bitBuffer->put($this->getCharCode($data[$i]), 6);
+ // encode a remaining character in 6 bits
+ if($i < $len){
+ $bitBuffer->put($this->ord($this->data[$i]), 6);
}
+ return $this;
}
/**
- * get the code for the given character
+ * @inheritDoc
*
- * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+ $length = $bitBuffer->read(self::getLengthBits($versionNumber));
+ $result = '';
+ // Read two characters at a time
+ while($length > 1){
+
+ if($bitBuffer->available() < 11){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $nextTwoCharsBits = $bitBuffer->read(11);
+ $result .= self::chr(intdiv($nextTwoCharsBits, 45));
+ $result .= self::chr($nextTwoCharsBits % 45);
+ $length -= 2;
+ }
+
+ if($length === 1){
+ // special case: one character left
+ if($bitBuffer->available() < 6){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $result .= self::chr($bitBuffer->read(6));
+ }
+
+ return $result;
+ }
+
+ /**
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ private function ord(string $chr):int{
+ /** @phan-suppress-next-line PhanParamSuspiciousOrder */
+ $ord = strpos(self::CHAR_MAP, $chr);
+
+ if($ord === false){
+ throw new QRCodeDataException('invalid character'); // @codeCoverageIgnore
+ }
+
+ return $ord;
+ }
+
+ /**
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
- protected function getCharCode(string $chr):int{
+ private static function chr(int $ord):string{
- if(!isset($this::CHAR_MAP_ALPHANUM[$chr])){
- throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, ord($chr)));
+ if($ord < 0 || $ord > 44){
+ throw new QRCodeDataException('invalid character code'); // @codeCoverageIgnore
}
- return $this::CHAR_MAP_ALPHANUM[$chr];
+ return self::CHAR_MAP[$ord];
}
}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/Byte.php b/vendor/chillerlan/php-qrcode/src/Data/Byte.php
index 02e76a639..10ab85262 100644
--- a/vendor/chillerlan/php-qrcode/src/Data/Byte.php
+++ b/vendor/chillerlan/php-qrcode/src/Data/Byte.php
@@ -2,9 +2,7 @@
/**
* Class Byte
*
- * @filesource Byte.php
* @created 25.11.2015
- * @package chillerlan\QRCode\Data
* @author Smiley <smiley@chillerlan.net>
* @copyright 2015 Smiley
* @license MIT
@@ -12,33 +10,76 @@
namespace chillerlan\QRCode\Data;
-use chillerlan\QRCode\QRCode;
-
-use function ord;
+use chillerlan\QRCode\Common\{BitBuffer, Mode};
+use function chr, ord;
/**
- * Byte mode, ISO-8859-1 or UTF-8
+ * 8-bit Byte mode, ISO-8859-1 or UTF-8
*
* ISO/IEC 18004:2000 Section 8.3.4
* ISO/IEC 18004:2000 Section 8.4.4
*/
-final class Byte extends QRDataAbstract{
+final class Byte extends QRDataModeAbstract{
- protected int $datamode = QRCode::DATA_BYTE;
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::BYTE;
- protected array $lengthBits = [8, 16, 16];
+ /**
+ * @inheritDoc
+ */
+ public function getLengthInBits():int{
+ return ($this->getCharCount() * 8);
+ }
/**
- * @inheritdoc
+ * @inheritDoc
*/
- protected function write(string $data):void{
+ public static function validateString(string $string):bool{
+ return $string !== '';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+ $len = $this->getCharCount();
+
+ $bitBuffer
+ ->put(self::DATAMODE, 4)
+ ->put($len, $this::getLengthBits($versionNumber))
+ ;
+
$i = 0;
- while($i < $this->strlen){
- $this->bitBuffer->put(ord($data[$i]), 8);
+ while($i < $len){
+ $bitBuffer->put(ord($this->data[$i]), 8);
$i++;
}
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+ $length = $bitBuffer->read(self::getLengthBits($versionNumber));
+
+ if($bitBuffer->available() < (8 * $length)){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $readBytes = '';
+
+ for($i = 0; $i < $length; $i++){
+ $readBytes .= chr($bitBuffer->read(8));
+ }
+
+ return $readBytes;
}
}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/ECI.php b/vendor/chillerlan/php-qrcode/src/Data/ECI.php
new file mode 100644
index 000000000..1aacd0b71
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/ECI.php
@@ -0,0 +1,165 @@
+<?php
+/**
+ * Class ECI
+ *
+ * @created 20.11.2020
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, ECICharset, Mode};
+use function mb_convert_encoding, mb_detect_encoding, mb_internal_encoding, sprintf;
+
+/**
+ * Adds an ECI Designator
+ *
+ * ISO/IEC 18004:2000 8.4.1.1
+ *
+ * Please note that you have to take care for the correct data encoding when adding with QRCode::add*Segment()
+ */
+final class ECI extends QRDataModeAbstract{
+
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::ECI;
+
+ /**
+ * The current ECI encoding id
+ */
+ private int $encoding;
+
+ /**
+ * @inheritDoc
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ * @noinspection PhpMissingParentConstructorInspection
+ */
+ public function __construct(int $encoding){
+
+ if($encoding < 0 || $encoding > 999999){
+ throw new QRCodeDataException(sprintf('invalid encoding id: "%s"', $encoding));
+ }
+
+ $this->encoding = $encoding;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLengthInBits():int{
+
+ if($this->encoding < 128){
+ return 8;
+ }
+
+ if($this->encoding < 16384){
+ return 16;
+ }
+
+ return 24;
+ }
+
+ /**
+ * Writes an ECI designator to the bitbuffer
+ *
+ * @inheritDoc
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+ $bitBuffer->put(self::DATAMODE, 4);
+
+ if($this->encoding < 128){
+ $bitBuffer->put($this->encoding, 8);
+ }
+ elseif($this->encoding < 16384){
+ $bitBuffer->put(($this->encoding | 0x8000), 16);
+ }
+ elseif($this->encoding < 1000000){
+ $bitBuffer->put(($this->encoding | 0xC00000), 24);
+ }
+ else{
+ throw new QRCodeDataException('invalid ECI ID');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Reads and parses the value of an ECI designator
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function parseValue(BitBuffer $bitBuffer):ECICharset{
+ $firstByte = $bitBuffer->read(8);
+
+ // just one byte
+ if(($firstByte & 0b10000000) === 0){
+ $id = ($firstByte & 0b01111111);
+ }
+ // two bytes
+ elseif(($firstByte & 0b11000000) === 0b10000000){
+ $id = ((($firstByte & 0b00111111) << 8) | $bitBuffer->read(8));
+ }
+ // three bytes
+ elseif(($firstByte & 0b11100000) === 0b11000000){
+ $id = ((($firstByte & 0b00011111) << 16) | $bitBuffer->read(16));
+ }
+ else{
+ throw new QRCodeDataException(sprintf('error decoding ECI value first byte: %08b', $firstByte));// @codeCoverageIgnore
+ }
+
+ return new ECICharset($id);
+ }
+
+ /**
+ * @codeCoverageIgnore Unused, but required as per interface
+ */
+ public static function validateString(string $string):bool{
+ return true;
+ }
+
+ /**
+ * Reads and decodes the ECI designator including the following byte sequence
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+ $eciCharset = self::parseValue($bitBuffer);
+ $nextMode = $bitBuffer->read(4);
+ $data = self::decodeModeSegment($nextMode, $bitBuffer, $versionNumber);
+ $encoding = $eciCharset->getName();
+
+ if($encoding === null){
+ // The spec isn't clear on this mode; see
+ // section 6.4.5: it does not say which encoding to assuming
+ // upon decoding. I have seen ISO-8859-1 used as well as
+ // Shift_JIS -- without anything like an ECI designator to
+ // give a hint.
+ $encoding = mb_detect_encoding($data, ['ISO-8859-1', 'Windows-1252', 'SJIS', 'UTF-8'], true);
+
+ if($encoding === false){
+ throw new QRCodeDataException('could not determine encoding in ECI mode'); // @codeCoverageIgnore
+ }
+ }
+
+ return mb_convert_encoding($data, mb_internal_encoding(), $encoding);
+ }
+
+ /**
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ private static function decodeModeSegment(int $mode, BitBuffer $bitBuffer, int $versionNumber):string{
+
+ switch(true){
+ case $mode === Mode::NUMBER: return Number::decodeSegment($bitBuffer, $versionNumber);
+ case $mode === Mode::ALPHANUM: return AlphaNum::decodeSegment($bitBuffer, $versionNumber);
+ case $mode === Mode::BYTE: return Byte::decodeSegment($bitBuffer, $versionNumber);
+ }
+
+ throw new QRCodeDataException(sprintf('ECI designator followed by invalid mode: "%04b"', $mode));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/Hanzi.php b/vendor/chillerlan/php-qrcode/src/Data/Hanzi.php
new file mode 100644
index 000000000..2df8ac1c6
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/Hanzi.php
@@ -0,0 +1,206 @@
+<?php
+/**
+ * Class Hanzi
+ *
+ * @created 19.11.2020
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, Mode};
+use Throwable;
+use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding,
+ mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
+
+/**
+ * Hanzi (simplified Chinese) mode, GBT18284-2000: 13-bit double-byte characters from the GB2312/GB18030 character set
+ *
+ * Please note that this is not part of the QR Code specification and may not be supported by all readers (ZXing-based ones do).
+ *
+ * @see https://en.wikipedia.org/wiki/GB_2312
+ * @see http://www.herongyang.com/GB2312/Introduction-of-GB2312.html
+ * @see https://en.wikipedia.org/wiki/GBK_(character_encoding)#Encoding
+ * @see https://gist.github.com/codemasher/91da33c44bfb48a81a6c1426bb8e4338
+ * @see https://github.com/zxing/zxing/blob/dfb06fa33b17a9e68321be151c22846c7b78048f/core/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java#L172-L209
+ * @see https://www.chinesestandard.net/PDF/English.aspx/GBT18284-2000
+ */
+final class Hanzi extends QRDataModeAbstract{
+
+ /**
+ * possible values: GB2312, GB18030
+ *
+ * @var string
+ */
+ public const ENCODING = 'GB18030';
+
+ /**
+ * @todo: other subsets???
+ *
+ * @var int
+ */
+ public const GB2312_SUBSET = 0b0001;
+
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::HANZI;
+
+ /**
+ * @inheritDoc
+ */
+ protected function getCharCount():int{
+ return mb_strlen($this->data, self::ENCODING);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLengthInBits():int{
+ return ($this->getCharCount() * 13);
+ }
+
+ /**
+ * @inheritDoc
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function convertEncoding(string $string):string{
+ mb_detect_order([mb_internal_encoding(), 'UTF-8', 'GB2312', 'GB18030', 'CP936', 'EUC-CN', 'HZ']);
+
+ $detected = mb_detect_encoding($string, null, true);
+
+ if($detected === false){
+ throw new QRCodeDataException('mb_detect_encoding error');
+ }
+
+ if($detected === self::ENCODING){
+ return $string;
+ }
+
+ $string = mb_convert_encoding($string, self::ENCODING, $detected);
+
+ if(!is_string($string)){
+ throw new QRCodeDataException('mb_convert_encoding error');
+ }
+
+ return $string;
+ }
+
+ /**
+ * checks if a string qualifies as Hanzi/GB2312
+ */
+ public static function validateString(string $string):bool{
+
+ try{
+ $string = self::convertEncoding($string);
+ }
+ catch(Throwable $e){
+ return false;
+ }
+
+ $len = strlen($string);
+
+ if($len < 2 || ($len % 2) !== 0){
+ return false;
+ }
+
+ for($i = 0; $i < $len; $i += 2){
+ $byte1 = ord($string[$i]);
+ $byte2 = ord($string[($i + 1)]);
+
+ // byte 1 unused ranges
+ if($byte1 < 0xa1 || ($byte1 > 0xa9 && $byte1 < 0xb0) || $byte1 > 0xf7){
+ return false;
+ }
+
+ // byte 2 unused ranges
+ if($byte2 < 0xa1 || $byte2 > 0xfe){
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+
+ $bitBuffer
+ ->put(self::DATAMODE, 4)
+ ->put($this::GB2312_SUBSET, 4)
+ ->put($this->getCharCount(), $this::getLengthBits($versionNumber))
+ ;
+
+ $len = strlen($this->data);
+
+ for($i = 0; ($i + 1) < $len; $i += 2){
+ $c = (((0xff & ord($this->data[$i])) << 8) | (0xff & ord($this->data[($i + 1)])));
+
+ if($c >= 0xa1a1 && $c <= 0xaafe){
+ $c -= 0x0a1a1;
+ }
+ elseif($c >= 0xb0a1 && $c <= 0xfafe){
+ $c -= 0x0a6a1;
+ }
+ else{
+ throw new QRCodeDataException(sprintf('illegal char at %d [%d]', ($i + 1), $c));
+ }
+
+ $bitBuffer->put((((($c >> 8) & 0xff) * 0x060) + ($c & 0xff)), 13);
+ }
+
+ if($i < $len){
+ throw new QRCodeDataException(sprintf('illegal char at %d', ($i + 1)));
+ }
+
+ return $this;
+ }
+
+ /**
+ * See specification GBT 18284-2000
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+
+ // Hanzi mode contains a subset indicator right after mode indicator
+ if($bitBuffer->read(4) !== self::GB2312_SUBSET){
+ throw new QRCodeDataException('ecpected subset indicator for Hanzi mode');
+ }
+
+ $length = $bitBuffer->read(self::getLengthBits($versionNumber));
+
+ if($bitBuffer->available() < ($length * 13)){
+ throw new QRCodeDataException('not enough bits available');
+ }
+
+ // Each character will require 2 bytes. Read the characters as 2-byte pairs and decode as GB2312 afterwards
+ $buffer = [];
+ $offset = 0;
+
+ while($length > 0){
+ // Each 13 bits encodes a 2-byte character
+ $twoBytes = $bitBuffer->read(13);
+ $assembledTwoBytes = ((intdiv($twoBytes, 0x060) << 8) | ($twoBytes % 0x060));
+
+ $assembledTwoBytes += ($assembledTwoBytes < 0x00a00) // 0x003BF
+ ? 0x0a1a1 // In the 0xA1A1 to 0xAAFE range
+ : 0x0a6a1; // In the 0xB0A1 to 0xFAFE range
+
+ $buffer[$offset] = chr(0xff & ($assembledTwoBytes >> 8));
+ $buffer[($offset + 1)] = chr(0xff & $assembledTwoBytes);
+ $offset += 2;
+ $length--;
+ }
+
+ return mb_convert_encoding(implode('', $buffer), mb_internal_encoding(), self::ENCODING);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/Kanji.php b/vendor/chillerlan/php-qrcode/src/Data/Kanji.php
index e106c50f1..ccde82642 100644
--- a/vendor/chillerlan/php-qrcode/src/Data/Kanji.php
+++ b/vendor/chillerlan/php-qrcode/src/Data/Kanji.php
@@ -2,9 +2,7 @@
/**
* Class Kanji
*
- * @filesource Kanji.php
* @created 25.11.2015
- * @package chillerlan\QRCode\Data
* @author Smiley <smiley@chillerlan.net>
* @copyright 2015 Smiley
* @license MIT
@@ -12,58 +10,183 @@
namespace chillerlan\QRCode\Data;
-use chillerlan\QRCode\QRCode;
-
-use function mb_strlen, ord, sprintf, strlen;
+use chillerlan\QRCode\Common\{BitBuffer, Mode};
+use Throwable;
+use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding,
+ mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
/**
- * Kanji mode: double-byte characters from the Shift JIS character set
+ * Kanji mode: 13-bit double-byte characters from the Shift-JIS character set
*
* ISO/IEC 18004:2000 Section 8.3.5
* ISO/IEC 18004:2000 Section 8.4.5
+ *
+ * @see https://en.wikipedia.org/wiki/Shift_JIS#As_defined_in_JIS_X_0208:1997
+ * @see http://www.rikai.com/library/kanjitables/kanji_codes.sjis.shtml
+ * @see https://gist.github.com/codemasher/d07d3e6e9346c08e7a41b8b978784952
*/
-final class Kanji extends QRDataAbstract{
+final class Kanji extends QRDataModeAbstract{
- protected int $datamode = QRCode::DATA_KANJI;
+ /**
+ * possible values: SJIS, SJIS-2004
+ *
+ * SJIS-2004 may produce errors in PHP < 8
+ *
+ * @var string
+ */
+ public const ENCODING = 'SJIS';
- protected array $lengthBits = [8, 10, 12];
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::KANJI;
+
+ /**
+ * @inheritDoc
+ */
+ protected function getCharCount():int{
+ return mb_strlen($this->data, self::ENCODING);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLengthInBits():int{
+ return ($this->getCharCount() * 13);
+ }
/**
- * @inheritdoc
+ * @inheritDoc
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
- protected function getLength(string $data):int{
- return mb_strlen($data, 'SJIS');
+ public static function convertEncoding(string $string):string{
+ mb_detect_order([mb_internal_encoding(), 'UTF-8', 'SJIS', 'SJIS-2004']);
+
+ $detected = mb_detect_encoding($string, null, true);
+
+ if($detected === false){
+ throw new QRCodeDataException('mb_detect_encoding error');
+ }
+
+ if($detected === self::ENCODING){
+ return $string;
+ }
+
+ $string = mb_convert_encoding($string, self::ENCODING, $detected);
+
+ if(!is_string($string)){
+ throw new QRCodeDataException(sprintf('invalid encoding: %s', $detected));
+ }
+
+ return $string;
}
/**
- * @inheritdoc
+ * checks if a string qualifies as SJIS Kanji
+ */
+ public static function validateString(string $string):bool{
+
+ try{
+ $string = self::convertEncoding($string);
+ }
+ catch(Throwable $e){
+ return false;
+ }
+
+ $len = strlen($string);
+
+ if($len < 2 || ($len % 2) !== 0){
+ return false;
+ }
+
+ for($i = 0; $i < $len; $i += 2){
+ $byte1 = ord($string[$i]);
+ $byte2 = ord($string[($i + 1)]);
+
+ // byte 1 unused and vendor ranges
+ if($byte1 < 0x81 || ($byte1 > 0x84 && $byte1 < 0x88) || ($byte1 > 0x9f && $byte1 < 0xe0) || $byte1 > 0xea){
+ return false;
+ }
+
+ // byte 2 unused ranges
+ if($byte2 < 0x40 || $byte2 === 0x7f || $byte2 > 0xfc){
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+ /**
+ * @inheritDoc
*
* @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
*/
- protected function write(string $data):void{
- $len = strlen($data);
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+
+ $bitBuffer
+ ->put(self::DATAMODE, 4)
+ ->put($this->getCharCount(), $this::getLengthBits($versionNumber))
+ ;
+
+ $len = strlen($this->data);
- for($i = 0; $i + 1 < $len; $i += 2){
- $c = ((0xff & ord($data[$i])) << 8) | (0xff & ord($data[$i + 1]));
+ for($i = 0; ($i + 1) < $len; $i += 2){
+ $c = (((0xff & ord($this->data[$i])) << 8) | (0xff & ord($this->data[($i + 1)])));
- if($c >= 0x8140 && $c <= 0x9FFC){
+ if($c >= 0x8140 && $c <= 0x9ffc){
$c -= 0x8140;
}
- elseif($c >= 0xE040 && $c <= 0xEBBF){
- $c -= 0xC140;
+ elseif($c >= 0xe040 && $c <= 0xebbf){
+ $c -= 0xc140;
}
else{
- throw new QRCodeDataException(sprintf('illegal char at %d [%d]', $i + 1, $c));
+ throw new QRCodeDataException(sprintf('illegal char at %d [%d]', ($i + 1), $c));
}
- $this->bitBuffer->put(((($c >> 8) & 0xff) * 0xC0) + ($c & 0xff), 13);
-
+ $bitBuffer->put((((($c >> 8) & 0xff) * 0xc0) + ($c & 0xff)), 13);
}
if($i < $len){
- throw new QRCodeDataException(sprintf('illegal char at %d', $i + 1));
+ throw new QRCodeDataException(sprintf('illegal char at %d', ($i + 1)));
+ }
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+ $length = $bitBuffer->read(self::getLengthBits($versionNumber));
+
+ if($bitBuffer->available() < ($length * 13)){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ // Each character will require 2 bytes. Read the characters as 2-byte pairs and decode as SJIS afterwards
+ $buffer = [];
+ $offset = 0;
+
+ while($length > 0){
+ // Each 13 bits encodes a 2-byte character
+ $twoBytes = $bitBuffer->read(13);
+ $assembledTwoBytes = ((intdiv($twoBytes, 0x0c0) << 8) | ($twoBytes % 0x0c0));
+
+ $assembledTwoBytes += ($assembledTwoBytes < 0x01f00)
+ ? 0x08140 // In the 0x8140 to 0x9FFC range
+ : 0x0c140; // In the 0xE040 to 0xEBBF range
+
+ $buffer[$offset] = chr(0xff & ($assembledTwoBytes >> 8));
+ $buffer[($offset + 1)] = chr(0xff & $assembledTwoBytes);
+ $offset += 2;
+ $length--;
}
+ return mb_convert_encoding(implode('', $buffer), mb_internal_encoding(), self::ENCODING);
}
}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/MaskPatternTester.php b/vendor/chillerlan/php-qrcode/src/Data/MaskPatternTester.php
deleted file mode 100644
index 7874cb53d..000000000
--- a/vendor/chillerlan/php-qrcode/src/Data/MaskPatternTester.php
+++ /dev/null
@@ -1,203 +0,0 @@
-<?php
-/**
- * Class MaskPatternTester
- *
- * @filesource MaskPatternTester.php
- * @created 22.11.2017
- * @package chillerlan\QRCode\Data
- * @author Smiley <smiley@chillerlan.net>
- * @copyright 2017 Smiley
- * @license MIT
- *
- * @noinspection PhpUnused
- */
-
-namespace chillerlan\QRCode\Data;
-
-use function abs, array_search, call_user_func_array, min;
-
-/**
- * Receives a QRDataInterface object and runs the mask pattern tests on it.
- *
- * ISO/IEC 18004:2000 Section 8.8.2 - Evaluation of masking results
- *
- * @see http://www.thonky.com/qr-code-tutorial/data-masking
- */
-final class MaskPatternTester{
-
- /**
- * The data interface that contains the data matrix to test
- */
- protected QRDataInterface $dataInterface;
-
- /**
- * Receives the QRDataInterface
- *
- * @see \chillerlan\QRCode\QROptions::$maskPattern
- * @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern
- */
- public function __construct(QRDataInterface $dataInterface){
- $this->dataInterface = $dataInterface;
- }
-
- /**
- * shoves a QRMatrix through the MaskPatternTester to find the lowest penalty mask pattern
- *
- * @see \chillerlan\QRCode\Data\MaskPatternTester
- */
- public function getBestMaskPattern():int{
- $penalties = [];
-
- for($pattern = 0; $pattern < 8; $pattern++){
- $penalties[$pattern] = $this->testPattern($pattern);
- }
-
- return array_search(min($penalties), $penalties, true);
- }
-
- /**
- * Returns the penalty for the given mask pattern
- *
- * @see \chillerlan\QRCode\QROptions::$maskPattern
- * @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern
- */
- public function testPattern(int $pattern):int{
- $matrix = $this->dataInterface->initMatrix($pattern, true);
- $penalty = 0;
-
- for($level = 1; $level <= 4; $level++){
- $penalty += call_user_func_array([$this, 'testLevel'.$level], [$matrix->matrix(true), $matrix->size()]);
- }
-
- return (int)$penalty;
- }
-
- /**
- * Checks for each group of five or more same-colored modules in a row (or column)
- */
- protected function testLevel1(array $m, int $size):int{
- $penalty = 0;
-
- foreach($m as $y => $row){
- foreach($row as $x => $val){
- $count = 0;
-
- for($ry = -1; $ry <= 1; $ry++){
-
- if($y + $ry < 0 || $size <= $y + $ry){
- continue;
- }
-
- for($rx = -1; $rx <= 1; $rx++){
-
- if(($ry === 0 && $rx === 0) || (($x + $rx) < 0 || $size <= ($x + $rx))){
- continue;
- }
-
- if($m[$y + $ry][$x + $rx] === $val){
- $count++;
- }
-
- }
- }
-
- if($count > 5){
- $penalty += (3 + $count - 5);
- }
-
- }
- }
-
- return $penalty;
- }
-
- /**
- * Checks for each 2x2 area of same-colored modules in the matrix
- */
- protected function testLevel2(array $m, int $size):int{
- $penalty = 0;
-
- foreach($m as $y => $row){
-
- if($y > $size - 2){
- break;
- }
-
- foreach($row as $x => $val){
-
- if($x > $size - 2){
- break;
- }
-
- if(
- $val === $m[$y][$x + 1]
- && $val === $m[$y + 1][$x]
- && $val === $m[$y + 1][$x + 1]
- ){
- $penalty++;
- }
- }
- }
-
- return 3 * $penalty;
- }
-
- /**
- * Checks if there are patterns that look similar to the finder patterns (1:1:3:1:1 ratio)
- */
- protected function testLevel3(array $m, int $size):int{
- $penalties = 0;
-
- foreach($m as $y => $row){
- foreach($row as $x => $val){
-
- if(
- $x + 6 < $size
- && $val
- && !$m[$y][$x + 1]
- && $m[$y][$x + 2]
- && $m[$y][$x + 3]
- && $m[$y][$x + 4]
- && !$m[$y][$x + 5]
- && $m[$y][$x + 6]
- ){
- $penalties++;
- }
-
- if(
- $y + 6 < $size
- && $val
- && !$m[$y + 1][$x]
- && $m[$y + 2][$x]
- && $m[$y + 3][$x]
- && $m[$y + 4][$x]
- && !$m[$y + 5][$x]
- && $m[$y + 6][$x]
- ){
- $penalties++;
- }
-
- }
- }
-
- return $penalties * 40;
- }
-
- /**
- * Checks if more than half of the modules are dark or light, with a larger penalty for a larger difference
- */
- protected function testLevel4(array $m, int $size):float{
- $count = 0;
-
- foreach($m as $y => $row){
- foreach($row as $x => $val){
- if($val){
- $count++;
- }
- }
- }
-
- return (abs(100 * $count / $size / $size - 50) / 5) * 10;
- }
-
-}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/Number.php b/vendor/chillerlan/php-qrcode/src/Data/Number.php
index 0a905b13e..3e4238fdc 100644
--- a/vendor/chillerlan/php-qrcode/src/Data/Number.php
+++ b/vendor/chillerlan/php-qrcode/src/Data/Number.php
@@ -2,9 +2,7 @@
/**
* Class Number
*
- * @filesource Number.php
* @created 26.11.2015
- * @package chillerlan\QRCode\Data
* @author Smiley <smiley@chillerlan.net>
* @copyright 2015 Smiley
* @license MIT
@@ -12,9 +10,8 @@
namespace chillerlan\QRCode\Data;
-use chillerlan\QRCode\QRCode;
-
-use function ord, sprintf, str_split, substr;
+use chillerlan\QRCode\Common\{BitBuffer, Mode};
+use function ceil, intdiv, substr, unpack;
/**
* Numeric mode: decimal digits 0 to 9
@@ -22,56 +19,142 @@ use function ord, sprintf, str_split, substr;
* ISO/IEC 18004:2000 Section 8.3.2
* ISO/IEC 18004:2000 Section 8.4.2
*/
-final class Number extends QRDataAbstract{
+final class Number extends QRDataModeAbstract{
- protected int $datamode = QRCode::DATA_NUMBER;
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::NUMBER;
+
+ /**
+ * @inheritDoc
+ */
+ public function getLengthInBits():int{
+ return (int)ceil($this->getCharCount() * (10 / 3));
+ }
- protected array $lengthBits = [10, 12, 14];
+ /**
+ * @inheritDoc
+ */
+ public static function validateString(string $string):bool{
+ return (bool)preg_match('/^\d+$/', $string);
+ }
/**
- * @inheritdoc
+ * @inheritDoc
*/
- protected function write(string $data):void{
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+ $len = $this->getCharCount();
+
+ $bitBuffer
+ ->put(self::DATAMODE, 4)
+ ->put($len, $this::getLengthBits($versionNumber))
+ ;
+
$i = 0;
- while($i + 2 < $this->strlen){
- $this->bitBuffer->put($this->parseInt(substr($data, $i, 3)), 10);
+ // encode numeric triplets in 10 bits
+ while(($i + 2) < $len){
+ $bitBuffer->put($this->parseInt(substr($this->data, $i, 3)), 10);
$i += 3;
}
- if($i < $this->strlen){
+ if($i < $len){
- if($this->strlen - $i === 1){
- $this->bitBuffer->put($this->parseInt(substr($data, $i, $i + 1)), 4);
+ // encode 2 remaining numbers in 7 bits
+ if(($len - $i) === 2){
+ $bitBuffer->put($this->parseInt(substr($this->data, $i, 2)), 7);
}
- elseif($this->strlen - $i === 2){
- $this->bitBuffer->put($this->parseInt(substr($data, $i, $i + 2)), 7);
+ // encode one remaining number in 4 bits
+ elseif(($len - $i) === 1){
+ $bitBuffer->put($this->parseInt(substr($this->data, $i, 1)), 4);
}
}
+ return $this;
}
/**
* get the code for the given numeric string
*
- * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
- protected function parseInt(string $string):int{
+ private function parseInt(string $string):int{
$num = 0;
- foreach(str_split($string) as $chr){
- $c = ord($chr);
+ $ords = unpack('C*', $string);
- if(!isset($this::CHAR_MAP_NUMBER[$chr])){
- throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, $c));
- }
+ if($ords === false){
+ throw new QRCodeDataException('unpack() error');
+ }
- $c = $c - 48; // ord('0')
- $num = $num * 10 + $c;
+ foreach($ords as $ord){
+ $num = ($num * 10 + $ord - 48);
}
return $num;
}
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+ $length = $bitBuffer->read(self::getLengthBits($versionNumber));
+ $result = '';
+ // Read three digits at a time
+ while($length >= 3){
+ // Each 10 bits encodes three digits
+ if($bitBuffer->available() < 10){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $threeDigitsBits = $bitBuffer->read(10);
+
+ if($threeDigitsBits >= 1000){
+ throw new QRCodeDataException('error decoding numeric value');
+ }
+
+ $result .= intdiv($threeDigitsBits, 100);
+ $result .= (intdiv($threeDigitsBits, 10) % 10);
+ $result .= ($threeDigitsBits % 10);
+
+ $length -= 3;
+ }
+
+ if($length === 2){
+ // Two digits left over to read, encoded in 7 bits
+ if($bitBuffer->available() < 7){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $twoDigitsBits = $bitBuffer->read(7);
+
+ if($twoDigitsBits >= 100){
+ throw new QRCodeDataException('error decoding numeric value');
+ }
+
+ $result .= intdiv($twoDigitsBits, 10);
+ $result .= ($twoDigitsBits % 10);
+ }
+ elseif($length === 1){
+ // One digit left over to read
+ if($bitBuffer->available() < 4){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $digitBits = $bitBuffer->read(4);
+
+ if($digitBits >= 10){
+ throw new QRCodeDataException('error decoding numeric value');
+ }
+
+ $result .= $digitBits;
+ }
+
+ return $result;
+ }
+
}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRCodeDataException.php b/vendor/chillerlan/php-qrcode/src/Data/QRCodeDataException.php
index 862f57ba0..04ffbd7ed 100644
--- a/vendor/chillerlan/php-qrcode/src/Data/QRCodeDataException.php
+++ b/vendor/chillerlan/php-qrcode/src/Data/QRCodeDataException.php
@@ -2,9 +2,7 @@
/**
* Class QRCodeDataException
*
- * @filesource QRCodeDataException.php
* @created 09.12.2015
- * @package chillerlan\QRCode\Data
* @author Smiley <smiley@chillerlan.net>
* @copyright 2015 Smiley
* @license MIT
@@ -14,4 +12,9 @@ namespace chillerlan\QRCode\Data;
use chillerlan\QRCode\QRCodeException;
-class QRCodeDataException extends QRCodeException{}
+/**
+ * An exception container
+ */
+final class QRCodeDataException extends QRCodeException{
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRData.php b/vendor/chillerlan/php-qrcode/src/Data/QRData.php
new file mode 100644
index 000000000..9d610b357
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/QRData.php
@@ -0,0 +1,263 @@
+<?php
+/**
+ * Class QRData
+ *
+ * @created 25.11.2015
+ * @author Smiley <smiley@chillerlan.net>
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, EccLevel, Mode, Version};
+use chillerlan\Settings\SettingsContainerInterface;
+use function count, sprintf;
+
+/**
+ * Processes the binary data and maps it on a QRMatrix which is then being returned
+ */
+final class QRData{
+
+ /**
+ * the options instance
+ *
+ * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions
+ */
+ private SettingsContainerInterface $options;
+
+ /**
+ * a BitBuffer instance
+ */
+ private BitBuffer $bitBuffer;
+
+ /**
+ * an EccLevel instance
+ */
+ private EccLevel $eccLevel;
+
+ /**
+ * current QR Code version
+ */
+ private Version $version;
+
+ /**
+ * @var \chillerlan\QRCode\Data\QRDataModeInterface[]
+ */
+ private array $dataSegments = [];
+
+ /**
+ * Max bits for the current ECC mode
+ *
+ * @var int[]
+ */
+ private array $maxBitsForEcc;
+
+ /**
+ * QRData constructor.
+ */
+ public function __construct(SettingsContainerInterface $options, array $dataSegments = []){
+ $this->options = $options;
+ $this->bitBuffer = new BitBuffer;
+ $this->eccLevel = new EccLevel($this->options->eccLevel);
+ $this->maxBitsForEcc = $this->eccLevel->getMaxBits();
+
+ $this->setData($dataSegments);
+ }
+
+ /**
+ * Sets the data string (internally called by the constructor)
+ *
+ * Subsequent calls will overwrite the current state - use the QRCode::add*Segement() method instead
+ *
+ * @param \chillerlan\QRCode\Data\QRDataModeInterface[] $dataSegments
+ */
+ public function setData(array $dataSegments):self{
+ $this->dataSegments = $dataSegments;
+ $this->version = $this->getMinimumVersion();
+
+ $this->bitBuffer->clear();
+ $this->writeBitBuffer();
+
+ return $this;
+ }
+
+ /**
+ * Returns the current BitBuffer instance
+ *
+ * @codeCoverageIgnore
+ */
+ public function getBitBuffer():BitBuffer{
+ return $this->bitBuffer;
+ }
+
+ /**
+ * Sets a BitBuffer object
+ *
+ * This can be used instead of setData(), however, the version auto-detection is not available in this case.
+ * The version needs to match the length bits range for the data mode the data has been encoded with,
+ * additionally the bit array needs to contain enough pad bits.
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function setBitBuffer(BitBuffer $bitBuffer):self{
+
+ if($this->options->version === Version::AUTO){
+ throw new QRCodeDataException('version auto detection is not available');
+ }
+
+ if($bitBuffer->getLength() === 0){
+ throw new QRCodeDataException('the given BitBuffer is empty');
+ }
+
+ $this->dataSegments = [];
+ $this->bitBuffer = $bitBuffer;
+ $this->version = new Version($this->options->version);
+
+ return $this;
+ }
+
+ /**
+ * returns a fresh matrix object with the data written and masked with the given $maskPattern
+ */
+ public function writeMatrix():QRMatrix{
+ return (new QRMatrix($this->version, $this->eccLevel))
+ ->initFunctionalPatterns()
+ ->writeCodewords($this->bitBuffer)
+ ;
+ }
+
+ /**
+ * estimates the total length of the several mode segments in order to guess the minimum version
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function estimateTotalBitLength():int{
+ $length = 0;
+
+ foreach($this->dataSegments as $segment){
+ // data length of the current segment
+ $length += $segment->getLengthInBits();
+ // +4 bits for the mode descriptor
+ $length += 4;
+ // Hanzi mode sets an additional 4 bit long subset identifier
+ if($segment instanceof Hanzi){
+ $length += 4;
+ }
+ }
+
+ $provisionalVersion = null;
+
+ foreach($this->maxBitsForEcc as $version => $maxBits){
+
+ if($length <= $maxBits){
+ $provisionalVersion = $version;
+ }
+
+ }
+
+ if($provisionalVersion !== null){
+
+ // add character count indicator bits for the provisional version
+ foreach($this->dataSegments as $segment){
+ $length += Mode::getLengthBitsForVersion($segment::DATAMODE, $provisionalVersion);
+ }
+
+ // it seems that in some cases the estimated total length is not 100% accurate,
+ // so we substract 4 bits from the total when not in mixed mode
+ if(count($this->dataSegments) <= 1){
+ $length -= 4;
+ }
+
+ // we've got a match!
+ // or let's see if there's a higher version number available
+ if($length <= $this->maxBitsForEcc[$provisionalVersion] || isset($this->maxBitsForEcc[($provisionalVersion + 1)])){
+ return $length;
+ }
+
+ }
+
+ throw new QRCodeDataException(sprintf('estimated data exceeds %d bits', $length));
+ }
+
+ /**
+ * returns the minimum version number for the given string
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function getMinimumVersion():Version{
+
+ if($this->options->version !== Version::AUTO){
+ return new Version($this->options->version);
+ }
+
+ $total = $this->estimateTotalBitLength();
+
+ // guess the version number within the given range
+ for($version = $this->options->versionMin; $version <= $this->options->versionMax; $version++){
+ if($total <= ($this->maxBitsForEcc[$version] - 4)){
+ return new Version($version);
+ }
+ }
+
+ // it's almost impossible to run into this one as $this::estimateTotalBitLength() would throw first
+ throw new QRCodeDataException('failed to guess minimum version'); // @codeCoverageIgnore
+ }
+
+ /**
+ * creates a BitBuffer and writes the string data to it
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException on data overflow
+ */
+ private function writeBitBuffer():void{
+ $MAX_BITS = $this->eccLevel->getMaxBitsForVersion($this->version);
+
+ foreach($this->dataSegments as $segment){
+ $segment->write($this->bitBuffer, $this->version->getVersionNumber());
+ }
+
+ // overflow, likely caused due to invalid version setting
+ if($this->bitBuffer->getLength() > $MAX_BITS){
+ throw new QRCodeDataException(
+ sprintf('code length overflow. (%d > %d bit)', $this->bitBuffer->getLength(), $MAX_BITS)
+ );
+ }
+
+ // add terminator (ISO/IEC 18004:2000 Table 2)
+ if(($this->bitBuffer->getLength() + 4) <= $MAX_BITS){
+ $this->bitBuffer->put(Mode::TERMINATOR, 4);
+ }
+
+ // Padding: ISO/IEC 18004:2000 8.4.9 Bit stream to codeword conversion
+
+ // if the final codeword is not exactly 8 bits in length, it shall be made 8 bits long
+ // by the addition of padding bits with binary value 0
+ while(($this->bitBuffer->getLength() % 8) !== 0){
+
+ if($this->bitBuffer->getLength() === $MAX_BITS){
+ break;
+ }
+
+ $this->bitBuffer->putBit(false);
+ }
+
+ // The message bit stream shall then be extended to fill the data capacity of the symbol
+ // corresponding to the Version and Error Correction Level, by the addition of the Pad
+ // Codewords 11101100 and 00010001 alternately.
+ $alternate = false;
+
+ while(($this->bitBuffer->getLength() + 8) <= $MAX_BITS){
+ $this->bitBuffer->put(($alternate) ? 0b00010001 : 0b11101100, 8);
+
+ $alternate = !$alternate;
+ }
+
+ // In certain versions of symbol, it may be necessary to add 3, 4 or 7 Remainder Bits (all zeros)
+ // to the end of the message in order exactly to fill the symbol capacity
+ while($this->bitBuffer->getLength() <= $MAX_BITS){
+ $this->bitBuffer->putBit(false);
+ }
+
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRDataAbstract.php b/vendor/chillerlan/php-qrcode/src/Data/QRDataAbstract.php
deleted file mode 100644
index 72b67b7b9..000000000
--- a/vendor/chillerlan/php-qrcode/src/Data/QRDataAbstract.php
+++ /dev/null
@@ -1,311 +0,0 @@
-<?php
-/**
- * Class QRDataAbstract
- *
- * @filesource QRDataAbstract.php
- * @created 25.11.2015
- * @package chillerlan\QRCode\Data
- * @author Smiley <smiley@chillerlan.net>
- * @copyright 2015 Smiley
- * @license MIT
- */
-
-namespace chillerlan\QRCode\Data;
-
-use chillerlan\QRCode\QRCode;
-use chillerlan\QRCode\Helpers\{BitBuffer, Polynomial};
-use chillerlan\Settings\SettingsContainerInterface;
-
-use function array_fill, array_merge, count, max, mb_convert_encoding, mb_detect_encoding, range, sprintf, strlen;
-
-/**
- * Processes the binary data and maps it on a matrix which is then being returned
- */
-abstract class QRDataAbstract implements QRDataInterface{
-
- /**
- * the string byte count
- */
- protected ?int $strlen = null;
-
- /**
- * the current data mode: Num, Alphanum, Kanji, Byte
- */
- protected int $datamode;
-
- /**
- * mode length bits for the version breakpoints 1-9, 10-26 and 27-40
- *
- * ISO/IEC 18004:2000 Table 3 - Number of bits in Character Count Indicator
- */
- protected array $lengthBits = [0, 0, 0];
-
- /**
- * current QR Code version
- */
- protected int $version;
-
- /**
- * ECC temp data
- */
- protected array $ecdata;
-
- /**
- * ECC temp data
- */
- protected array $dcdata;
-
- /**
- * the options instance
- *
- * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions
- */
- protected SettingsContainerInterface $options;
-
- /**
- * a BitBuffer instance
- */
- protected BitBuffer $bitBuffer;
-
- /**
- * QRDataInterface constructor.
- */
- public function __construct(SettingsContainerInterface $options, string $data = null){
- $this->options = $options;
-
- if($data !== null){
- $this->setData($data);
- }
- }
-
- /**
- * @inheritDoc
- */
- public function setData(string $data):QRDataInterface{
-
- if($this->datamode === QRCode::DATA_KANJI){
- $data = mb_convert_encoding($data, 'SJIS', mb_detect_encoding($data));
- }
-
- $this->strlen = $this->getLength($data);
- $this->version = $this->options->version === QRCode::VERSION_AUTO
- ? $this->getMinimumVersion()
- : $this->options->version;
-
- $this->writeBitBuffer($data);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function initMatrix(int $maskPattern, bool $test = null):QRMatrix{
- return (new QRMatrix($this->version, $this->options->eccLevel))
- ->init($maskPattern, $test)
- ->mapData($this->maskECC(), $maskPattern)
- ;
- }
-
- /**
- * returns the length bits for the version breakpoints 1-9, 10-26 and 27-40
- *
- * @throws \chillerlan\QRCode\Data\QRCodeDataException
- * @codeCoverageIgnore
- */
- protected function getLengthBits():int{
-
- foreach([9, 26, 40] as $key => $breakpoint){
- if($this->version <= $breakpoint){
- return $this->lengthBits[$key];
- }
- }
-
- throw new QRCodeDataException(sprintf('invalid version number: %d', $this->version));
- }
-
- /**
- * returns the byte count of the $data string
- */
- protected function getLength(string $data):int{
- return strlen($data);
- }
-
- /**
- * returns the minimum version number for the given string
- *
- * @throws \chillerlan\QRCode\Data\QRCodeDataException
- */
- protected function getMinimumVersion():int{
- $maxlength = 0;
-
- // guess the version number within the given range
- $dataMode = QRCode::DATA_MODES[$this->datamode];
- $eccMode = QRCode::ECC_MODES[$this->options->eccLevel];
-
- foreach(range($this->options->versionMin, $this->options->versionMax) as $version){
- $maxlength = $this::MAX_LENGTH[$version][$dataMode][$eccMode];
-
- if($this->strlen <= $maxlength){
- return $version;
- }
- }
-
- throw new QRCodeDataException(sprintf('data exceeds %d characters', $maxlength));
- }
-
- /**
- * writes the actual data string to the BitBuffer
- *
- * @see \chillerlan\QRCode\Data\QRDataAbstract::writeBitBuffer()
- */
- abstract protected function write(string $data):void;
-
- /**
- * creates a BitBuffer and writes the string data to it
- *
- * @throws \chillerlan\QRCode\QRCodeException on data overflow
- */
- protected function writeBitBuffer(string $data):void{
- $this->bitBuffer = new BitBuffer;
-
- $MAX_BITS = $this::MAX_BITS[$this->version][QRCode::ECC_MODES[$this->options->eccLevel]];
-
- $this->bitBuffer
- ->put($this->datamode, 4)
- ->put($this->strlen, $this->getLengthBits())
- ;
-
- $this->write($data);
-
- // overflow, likely caused due to invalid version setting
- if($this->bitBuffer->getLength() > $MAX_BITS){
- throw new QRCodeDataException(sprintf('code length overflow. (%d > %d bit)', $this->bitBuffer->getLength(), $MAX_BITS));
- }
-
- // add terminator (ISO/IEC 18004:2000 Table 2)
- if($this->bitBuffer->getLength() + 4 <= $MAX_BITS){
- $this->bitBuffer->put(0, 4);
- }
-
- // padding
- while($this->bitBuffer->getLength() % 8 !== 0){
- $this->bitBuffer->putBit(false);
- }
-
- // padding
- while(true){
-
- if($this->bitBuffer->getLength() >= $MAX_BITS){
- break;
- }
-
- $this->bitBuffer->put(0xEC, 8);
-
- if($this->bitBuffer->getLength() >= $MAX_BITS){
- break;
- }
-
- $this->bitBuffer->put(0x11, 8);
- }
-
- }
-
- /**
- * ECC masking
- *
- * ISO/IEC 18004:2000 Section 8.5 ff
- *
- * @see http://www.thonky.com/qr-code-tutorial/error-correction-coding
- */
- protected function maskECC():array{
- [$l1, $l2, $b1, $b2] = $this::RSBLOCKS[$this->version][QRCode::ECC_MODES[$this->options->eccLevel]];
-
- $rsBlocks = array_fill(0, $l1, [$b1, $b2]);
- $rsCount = $l1 + $l2;
- $this->ecdata = array_fill(0, $rsCount, []);
- $this->dcdata = $this->ecdata;
-
- if($l2 > 0){
- $rsBlocks = array_merge($rsBlocks, array_fill(0, $l2, [$b1 + 1, $b2 + 1]));
- }
-
- $totalCodeCount = 0;
- $maxDcCount = 0;
- $maxEcCount = 0;
- $offset = 0;
-
- $bitBuffer = $this->bitBuffer->getBuffer();
-
- foreach($rsBlocks as $key => $block){
- [$rsBlockTotal, $dcCount] = $block;
-
- $ecCount = $rsBlockTotal - $dcCount;
- $maxDcCount = max($maxDcCount, $dcCount);
- $maxEcCount = max($maxEcCount, $ecCount);
- $this->dcdata[$key] = array_fill(0, $dcCount, null);
-
- foreach($this->dcdata[$key] as $a => $_z){
- $this->dcdata[$key][$a] = 0xff & $bitBuffer[$a + $offset];
- }
-
- [$num, $add] = $this->poly($key, $ecCount);
-
- foreach($this->ecdata[$key] as $c => $_){
- $modIndex = $c + $add;
- $this->ecdata[$key][$c] = $modIndex >= 0 ? $num[$modIndex] : 0;
- }
-
- $offset += $dcCount;
- $totalCodeCount += $rsBlockTotal;
- }
-
- $data = array_fill(0, $totalCodeCount, null);
- $index = 0;
-
- $mask = function(array $arr, int $count) use (&$data, &$index, $rsCount):void{
- for($x = 0; $x < $count; $x++){
- for($y = 0; $y < $rsCount; $y++){
- if($x < count($arr[$y])){
- $data[$index] = $arr[$y][$x];
- $index++;
- }
- }
- }
- };
-
- $mask($this->dcdata, $maxDcCount);
- $mask($this->ecdata, $maxEcCount);
-
- return $data;
- }
-
- /**
- * helper method for the polynomial operations
- */
- protected function poly(int $key, int $count):array{
- $rsPoly = new Polynomial;
- $modPoly = new Polynomial;
-
- for($i = 0; $i < $count; $i++){
- $modPoly->setNum([1, $modPoly->gexp($i)]);
- $rsPoly->multiply($modPoly->getNum());
- }
-
- $rsPolyCount = count($rsPoly->getNum());
-
- $modPoly
- ->setNum($this->dcdata[$key], $rsPolyCount - 1)
- ->mod($rsPoly->getNum())
- ;
-
- $this->ecdata[$key] = array_fill(0, $rsPolyCount - 1, null);
- $num = $modPoly->getNum();
-
- return [
- $num,
- count($num) - count($this->ecdata[$key]),
- ];
- }
-
-}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRDataInterface.php b/vendor/chillerlan/php-qrcode/src/Data/QRDataInterface.php
deleted file mode 100644
index 93ad6221d..000000000
--- a/vendor/chillerlan/php-qrcode/src/Data/QRDataInterface.php
+++ /dev/null
@@ -1,200 +0,0 @@
-<?php
-/**
- * Interface QRDataInterface
- *
- * @filesource QRDataInterface.php
- * @created 01.12.2015
- * @package chillerlan\QRCode\Data
- * @author Smiley <smiley@chillerlan.net>
- * @copyright 2015 Smiley
- * @license MIT
- */
-
-namespace chillerlan\QRCode\Data;
-
-/**
- * Specifies the methods reqired for the data modules (Number, Alphanum, Byte and Kanji)
- * and holds version information in several constants
- */
-interface QRDataInterface{
-
- /**
- * @var int[]
- */
- const CHAR_MAP_NUMBER = [
- '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
- ];
-
- /**
- * ISO/IEC 18004:2000 Table 5
- *
- * @var int[]
- */
- const CHAR_MAP_ALPHANUM = [
- '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7,
- '8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15,
- 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23,
- 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31,
- 'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35, ' ' => 36, '$' => 37, '%' => 38, '*' => 39,
- '+' => 40, '-' => 41, '.' => 42, '/' => 43, ':' => 44,
- ];
-
- /**
- * ISO/IEC 18004:2000 Tables 7-11 - Number of symbol characters and input data capacity for versions 1 to 40
- *
- * @see http://www.qrcode.com/en/about/version.html
- *
- * @var int [][][]
- */
- const MAX_LENGTH =[
- // v => [NUMERIC => [L, M, Q, H ], ALPHANUM => [L, M, Q, H], BINARY => [L, M, Q, H ], KANJI => [L, M, Q, H ]] // modules
- 1 => [[ 41, 34, 27, 17], [ 25, 20, 16, 10], [ 17, 14, 11, 7], [ 10, 8, 7, 4]], // 21
- 2 => [[ 77, 63, 48, 34], [ 47, 38, 29, 20], [ 32, 26, 20, 14], [ 20, 16, 12, 8]], // 25
- 3 => [[ 127, 101, 77, 58], [ 77, 61, 47, 35], [ 53, 42, 32, 24], [ 32, 26, 20, 15]], // 29
- 4 => [[ 187, 149, 111, 82], [ 114, 90, 67, 50], [ 78, 62, 46, 34], [ 48, 38, 28, 21]], // 33
- 5 => [[ 255, 202, 144, 106], [ 154, 122, 87, 64], [ 106, 84, 60, 44], [ 65, 52, 37, 27]], // 37
- 6 => [[ 322, 255, 178, 139], [ 195, 154, 108, 84], [ 134, 106, 74, 58], [ 82, 65, 45, 36]], // 41
- 7 => [[ 370, 293, 207, 154], [ 224, 178, 125, 93], [ 154, 122, 86, 64], [ 95, 75, 53, 39]], // 45
- 8 => [[ 461, 365, 259, 202], [ 279, 221, 157, 122], [ 192, 152, 108, 84], [ 118, 93, 66, 52]], // 49
- 9 => [[ 552, 432, 312, 235], [ 335, 262, 189, 143], [ 230, 180, 130, 98], [ 141, 111, 80, 60]], // 53
- 10 => [[ 652, 513, 364, 288], [ 395, 311, 221, 174], [ 271, 213, 151, 119], [ 167, 131, 93, 74]], // 57
- 11 => [[ 772, 604, 427, 331], [ 468, 366, 259, 200], [ 321, 251, 177, 137], [ 198, 155, 109, 85]], // 61
- 12 => [[ 883, 691, 489, 374], [ 535, 419, 296, 227], [ 367, 287, 203, 155], [ 226, 177, 125, 96]], // 65
- 13 => [[1022, 796, 580, 427], [ 619, 483, 352, 259], [ 425, 331, 241, 177], [ 262, 204, 149, 109]], // 69 NICE!
- 14 => [[1101, 871, 621, 468], [ 667, 528, 376, 283], [ 458, 362, 258, 194], [ 282, 223, 159, 120]], // 73
- 15 => [[1250, 991, 703, 530], [ 758, 600, 426, 321], [ 520, 412, 292, 220], [ 320, 254, 180, 136]], // 77
- 16 => [[1408, 1082, 775, 602], [ 854, 656, 470, 365], [ 586, 450, 322, 250], [ 361, 277, 198, 154]], // 81
- 17 => [[1548, 1212, 876, 674], [ 938, 734, 531, 408], [ 644, 504, 364, 280], [ 397, 310, 224, 173]], // 85
- 18 => [[1725, 1346, 948, 746], [1046, 816, 574, 452], [ 718, 560, 394, 310], [ 442, 345, 243, 191]], // 89
- 19 => [[1903, 1500, 1063, 813], [1153, 909, 644, 493], [ 792, 624, 442, 338], [ 488, 384, 272, 208]], // 93
- 20 => [[2061, 1600, 1159, 919], [1249, 970, 702, 557], [ 858, 666, 482, 382], [ 528, 410, 297, 235]], // 97
- 21 => [[2232, 1708, 1224, 969], [1352, 1035, 742, 587], [ 929, 711, 509, 403], [ 572, 438, 314, 248]], // 101
- 22 => [[2409, 1872, 1358, 1056], [1460, 1134, 823, 640], [1003, 779, 565, 439], [ 618, 480, 348, 270]], // 105
- 23 => [[2620, 2059, 1468, 1108], [1588, 1248, 890, 672], [1091, 857, 611, 461], [ 672, 528, 376, 284]], // 109
- 24 => [[2812, 2188, 1588, 1228], [1704, 1326, 963, 744], [1171, 911, 661, 511], [ 721, 561, 407, 315]], // 113
- 25 => [[3057, 2395, 1718, 1286], [1853, 1451, 1041, 779], [1273, 997, 715, 535], [ 784, 614, 440, 330]], // 117
- 26 => [[3283, 2544, 1804, 1425], [1990, 1542, 1094, 864], [1367, 1059, 751, 593], [ 842, 652, 462, 365]], // 121
- 27 => [[3517, 2701, 1933, 1501], [2132, 1637, 1172, 910], [1465, 1125, 805, 625], [ 902, 692, 496, 385]], // 125
- 28 => [[3669, 2857, 2085, 1581], [2223, 1732, 1263, 958], [1528, 1190, 868, 658], [ 940, 732, 534, 405]], // 129
- 29 => [[3909, 3035, 2181, 1677], [2369, 1839, 1322, 1016], [1628, 1264, 908, 698], [1002, 778, 559, 430]], // 133
- 30 => [[4158, 3289, 2358, 1782], [2520, 1994, 1429, 1080], [1732, 1370, 982, 742], [1066, 843, 604, 457]], // 137
- 31 => [[4417, 3486, 2473, 1897], [2677, 2113, 1499, 1150], [1840, 1452, 1030, 790], [1132, 894, 634, 486]], // 141
- 32 => [[4686, 3693, 2670, 2022], [2840, 2238, 1618, 1226], [1952, 1538, 1112, 842], [1201, 947, 684, 518]], // 145
- 33 => [[4965, 3909, 2805, 2157], [3009, 2369, 1700, 1307], [2068, 1628, 1168, 898], [1273, 1002, 719, 553]], // 149
- 34 => [[5253, 4134, 2949, 2301], [3183, 2506, 1787, 1394], [2188, 1722, 1228, 958], [1347, 1060, 756, 590]], // 153
- 35 => [[5529, 4343, 3081, 2361], [3351, 2632, 1867, 1431], [2303, 1809, 1283, 983], [1417, 1113, 790, 605]], // 157
- 36 => [[5836, 4588, 3244, 2524], [3537, 2780, 1966, 1530], [2431, 1911, 1351, 1051], [1496, 1176, 832, 647]], // 161
- 37 => [[6153, 4775, 3417, 2625], [3729, 2894, 2071, 1591], [2563, 1989, 1423, 1093], [1577, 1224, 876, 673]], // 165
- 38 => [[6479, 5039, 3599, 2735], [3927, 3054, 2181, 1658], [2699, 2099, 1499, 1139], [1661, 1292, 923, 701]], // 169
- 39 => [[6743, 5313, 3791, 2927], [4087, 3220, 2298, 1774], [2809, 2213, 1579, 1219], [1729, 1362, 972, 750]], // 173
- 40 => [[7089, 5596, 3993, 3057], [4296, 3391, 2420, 1852], [2953, 2331, 1663, 1273], [1817, 1435, 1024, 784]], // 177
- ];
-
- /**
- * ISO/IEC 18004:2000 Tables 7-11 - Number of symbol characters and input data capacity for versions 1 to 40
- *
- * @var int [][]
- */
- const MAX_BITS = [
- // version => [L, M, Q, H ]
- 1 => [ 152, 128, 104, 72],
- 2 => [ 272, 224, 176, 128],
- 3 => [ 440, 352, 272, 208],
- 4 => [ 640, 512, 384, 288],
- 5 => [ 864, 688, 496, 368],
- 6 => [ 1088, 864, 608, 480],
- 7 => [ 1248, 992, 704, 528],
- 8 => [ 1552, 1232, 880, 688],
- 9 => [ 1856, 1456, 1056, 800],
- 10 => [ 2192, 1728, 1232, 976],
- 11 => [ 2592, 2032, 1440, 1120],
- 12 => [ 2960, 2320, 1648, 1264],
- 13 => [ 3424, 2672, 1952, 1440],
- 14 => [ 3688, 2920, 2088, 1576],
- 15 => [ 4184, 3320, 2360, 1784],
- 16 => [ 4712, 3624, 2600, 2024],
- 17 => [ 5176, 4056, 2936, 2264],
- 18 => [ 5768, 4504, 3176, 2504],
- 19 => [ 6360, 5016, 3560, 2728],
- 20 => [ 6888, 5352, 3880, 3080],
- 21 => [ 7456, 5712, 4096, 3248],
- 22 => [ 8048, 6256, 4544, 3536],
- 23 => [ 8752, 6880, 4912, 3712],
- 24 => [ 9392, 7312, 5312, 4112],
- 25 => [10208, 8000, 5744, 4304],
- 26 => [10960, 8496, 6032, 4768],
- 27 => [11744, 9024, 6464, 5024],
- 28 => [12248, 9544, 6968, 5288],
- 29 => [13048, 10136, 7288, 5608],
- 30 => [13880, 10984, 7880, 5960],
- 31 => [14744, 11640, 8264, 6344],
- 32 => [15640, 12328, 8920, 6760],
- 33 => [16568, 13048, 9368, 7208],
- 34 => [17528, 13800, 9848, 7688],
- 35 => [18448, 14496, 10288, 7888],
- 36 => [19472, 15312, 10832, 8432],
- 37 => [20528, 15936, 11408, 8768],
- 38 => [21616, 16816, 12016, 9136],
- 39 => [22496, 17728, 12656, 9776],
- 40 => [23648, 18672, 13328, 10208],
- ];
-
- /**
- * @see http://www.thonky.com/qr-code-tutorial/error-correction-table
- *
- * @var int [][][]
- */
- const RSBLOCKS = [
- 1 => [[ 1, 0, 26, 19], [ 1, 0, 26, 16], [ 1, 0, 26, 13], [ 1, 0, 26, 9]],
- 2 => [[ 1, 0, 44, 34], [ 1, 0, 44, 28], [ 1, 0, 44, 22], [ 1, 0, 44, 16]],
- 3 => [[ 1, 0, 70, 55], [ 1, 0, 70, 44], [ 2, 0, 35, 17], [ 2, 0, 35, 13]],
- 4 => [[ 1, 0, 100, 80], [ 2, 0, 50, 32], [ 2, 0, 50, 24], [ 4, 0, 25, 9]],
- 5 => [[ 1, 0, 134, 108], [ 2, 0, 67, 43], [ 2, 2, 33, 15], [ 2, 2, 33, 11]],
- 6 => [[ 2, 0, 86, 68], [ 4, 0, 43, 27], [ 4, 0, 43, 19], [ 4, 0, 43, 15]],
- 7 => [[ 2, 0, 98, 78], [ 4, 0, 49, 31], [ 2, 4, 32, 14], [ 4, 1, 39, 13]],
- 8 => [[ 2, 0, 121, 97], [ 2, 2, 60, 38], [ 4, 2, 40, 18], [ 4, 2, 40, 14]],
- 9 => [[ 2, 0, 146, 116], [ 3, 2, 58, 36], [ 4, 4, 36, 16], [ 4, 4, 36, 12]],
- 10 => [[ 2, 2, 86, 68], [ 4, 1, 69, 43], [ 6, 2, 43, 19], [ 6, 2, 43, 15]],
- 11 => [[ 4, 0, 101, 81], [ 1, 4, 80, 50], [ 4, 4, 50, 22], [ 3, 8, 36, 12]],
- 12 => [[ 2, 2, 116, 92], [ 6, 2, 58, 36], [ 4, 6, 46, 20], [ 7, 4, 42, 14]],
- 13 => [[ 4, 0, 133, 107], [ 8, 1, 59, 37], [ 8, 4, 44, 20], [12, 4, 33, 11]],
- 14 => [[ 3, 1, 145, 115], [ 4, 5, 64, 40], [11, 5, 36, 16], [11, 5, 36, 12]],
- 15 => [[ 5, 1, 109, 87], [ 5, 5, 65, 41], [ 5, 7, 54, 24], [11, 7, 36, 12]],
- 16 => [[ 5, 1, 122, 98], [ 7, 3, 73, 45], [15, 2, 43, 19], [ 3, 13, 45, 15]],
- 17 => [[ 1, 5, 135, 107], [10, 1, 74, 46], [ 1, 15, 50, 22], [ 2, 17, 42, 14]],
- 18 => [[ 5, 1, 150, 120], [ 9, 4, 69, 43], [17, 1, 50, 22], [ 2, 19, 42, 14]],
- 19 => [[ 3, 4, 141, 113], [ 3, 11, 70, 44], [17, 4, 47, 21], [ 9, 16, 39, 13]],
- 20 => [[ 3, 5, 135, 107], [ 3, 13, 67, 41], [15, 5, 54, 24], [15, 10, 43, 15]],
- 21 => [[ 4, 4, 144, 116], [17, 0, 68, 42], [17, 6, 50, 22], [19, 6, 46, 16]],
- 22 => [[ 2, 7, 139, 111], [17, 0, 74, 46], [ 7, 16, 54, 24], [34, 0, 37, 13]],
- 23 => [[ 4, 5, 151, 121], [ 4, 14, 75, 47], [11, 14, 54, 24], [16, 14, 45, 15]],
- 24 => [[ 6, 4, 147, 117], [ 6, 14, 73, 45], [11, 16, 54, 24], [30, 2, 46, 16]],
- 25 => [[ 8, 4, 132, 106], [ 8, 13, 75, 47], [ 7, 22, 54, 24], [22, 13, 45, 15]],
- 26 => [[10, 2, 142, 114], [19, 4, 74, 46], [28, 6, 50, 22], [33, 4, 46, 16]],
- 27 => [[ 8, 4, 152, 122], [22, 3, 73, 45], [ 8, 26, 53, 23], [12, 28, 45, 15]],
- 28 => [[ 3, 10, 147, 117], [ 3, 23, 73, 45], [ 4, 31, 54, 24], [11, 31, 45, 15]],
- 29 => [[ 7, 7, 146, 116], [21, 7, 73, 45], [ 1, 37, 53, 23], [19, 26, 45, 15]],
- 30 => [[ 5, 10, 145, 115], [19, 10, 75, 47], [15, 25, 54, 24], [23, 25, 45, 15]],
- 31 => [[13, 3, 145, 115], [ 2, 29, 74, 46], [42, 1, 54, 24], [23, 28, 45, 15]],
- 32 => [[17, 0, 145, 115], [10, 23, 74, 46], [10, 35, 54, 24], [19, 35, 45, 15]],
- 33 => [[17, 1, 145, 115], [14, 21, 74, 46], [29, 19, 54, 24], [11, 46, 45, 15]],
- 34 => [[13, 6, 145, 115], [14, 23, 74, 46], [44, 7, 54, 24], [59, 1, 46, 16]],
- 35 => [[12, 7, 151, 121], [12, 26, 75, 47], [39, 14, 54, 24], [22, 41, 45, 15]],
- 36 => [[ 6, 14, 151, 121], [ 6, 34, 75, 47], [46, 10, 54, 24], [ 2, 64, 45, 15]],
- 37 => [[17, 4, 152, 122], [29, 14, 74, 46], [49, 10, 54, 24], [24, 46, 45, 15]],
- 38 => [[ 4, 18, 152, 122], [13, 32, 74, 46], [48, 14, 54, 24], [42, 32, 45, 15]],
- 39 => [[20, 4, 147, 117], [40, 7, 75, 47], [43, 22, 54, 24], [10, 67, 45, 15]],
- 40 => [[19, 6, 148, 118], [18, 31, 75, 47], [34, 34, 54, 24], [20, 61, 45, 15]],
- ];
-
- /**
- * Sets the data string (internally called by the constructor)
- */
- public function setData(string $data):QRDataInterface;
-
- /**
- * returns a fresh matrix object with the data written for the given $maskPattern
- */
- public function initMatrix(int $maskPattern, bool $test = null):QRMatrix;
-
-}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRDataModeAbstract.php b/vendor/chillerlan/php-qrcode/src/Data/QRDataModeAbstract.php
new file mode 100644
index 000000000..94b93ac0e
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/QRDataModeAbstract.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Class QRDataModeAbstract
+ *
+ * @created 19.11.2020
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\Mode;
+
+/**
+ * abstract methods for the several data modes
+ */
+abstract class QRDataModeAbstract implements QRDataModeInterface{
+
+ /**
+ * The data to write
+ */
+ protected string $data;
+
+ /**
+ * QRDataModeAbstract constructor.
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function __construct(string $data){
+ $data = $this::convertEncoding($data);
+
+ if(!$this::validateString($data)){
+ throw new QRCodeDataException('invalid data');
+ }
+
+ $this->data = $data;
+ }
+
+ /**
+ * returns the character count of the $data string
+ */
+ protected function getCharCount():int{
+ return strlen($this->data);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function convertEncoding(string $string):string{
+ return $string;
+ }
+
+ /**
+ * shortcut
+ */
+ protected static function getLengthBits(int $versionNumber):int{
+ return Mode::getLengthBitsForVersion(static::DATAMODE, $versionNumber);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRDataModeInterface.php b/vendor/chillerlan/php-qrcode/src/Data/QRDataModeInterface.php
new file mode 100644
index 000000000..321cf60b6
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/QRDataModeInterface.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Interface QRDataModeInterface
+ *
+ * @created 01.12.2015
+ * @author Smiley <smiley@chillerlan.net>
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\BitBuffer;
+
+/**
+ * Specifies the methods reqired for the data modules (Number, Alphanum, Byte and Kanji)
+ */
+interface QRDataModeInterface{
+
+ /**
+ * the current data mode: Number, Alphanum, Kanji, Hanzi, Byte, ECI
+ *
+ * tbh I hate this constant here, but it's part of the interface, so I can't just declare it in the abstract class.
+ * (phan will complain about a PhanAccessOverridesFinalConstant)
+ *
+ * @see https://wiki.php.net/rfc/final_class_const
+ *
+ * @var int
+ * @see \chillerlan\QRCode\Common\Mode
+ * @internal do not call this constant from the interface, but rather from one of the child classes
+ */
+ public const DATAMODE = -1;
+
+ /**
+ * retruns the length in bits of the data string
+ */
+ public function getLengthInBits():int;
+
+ /**
+ * encoding conversion helper
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function convertEncoding(string $string):string;
+
+ /**
+ * checks if the given string qualifies for the encoder module
+ */
+ public static function validateString(string $string):bool;
+
+ /**
+ * writes the actual data string to the BitBuffer, uses the given version to determine the length bits
+ *
+ * @see \chillerlan\QRCode\Data\QRData::writeBitBuffer()
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface;
+
+ /**
+ * reads a segment from the BitBuffer and decodes in the current data mode
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string;
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRMatrix.php b/vendor/chillerlan/php-qrcode/src/Data/QRMatrix.php
index 05c8b9069..e32633a99 100755
--- a/vendor/chillerlan/php-qrcode/src/Data/QRMatrix.php
+++ b/vendor/chillerlan/php-qrcode/src/Data/QRMatrix.php
@@ -2,9 +2,7 @@
/**
* Class QRMatrix
*
- * @filesource QRMatrix.php
* @created 15.11.2017
- * @package chillerlan\QRCode\Data
* @author Smiley <smiley@chillerlan.net>
* @copyright 2017 Smiley
* @license MIT
@@ -12,207 +10,124 @@
namespace chillerlan\QRCode\Data;
-use chillerlan\QRCode\QRCode;
-use Closure;
-
-use function array_fill, array_key_exists, array_push, array_unshift, count, floor, in_array, max, min, range;
+use chillerlan\QRCode\Common\{BitBuffer, EccLevel, MaskPattern, Version};
+use function array_fill, array_map, array_reverse, count, intdiv;
/**
- * Holds a numerical representation of the final QR Code;
+ * Holds an array representation of the final QR Code that contains numerical values for later output modifications;
* maps the ECC coded binary data and applies the mask pattern
*
* @see http://www.thonky.com/qr-code-tutorial/format-version-information
*/
-final class QRMatrix{
+class QRMatrix{
+
+ /*
+ * special values
+ */
/** @var int */
- public const M_NULL = 0x00;
+ public const IS_DARK = 0b100000000000;
+ /** @var int */
+ public const M_NULL = 0b000000000000;
+ /** @var int */
+ public const M_LOGO = 0b001000000000;
+ /** @var int */
+ public const M_LOGO_DARK = 0b101000000000;
+
+ /*
+ * light values
+ */
+
+ /** @var int */
+ public const M_DATA = 0b000000000010;
+ /** @var int */
+ public const M_FINDER = 0b000000000100;
+ /** @var int */
+ public const M_SEPARATOR = 0b000000001000;
+ /** @var int */
+ public const M_ALIGNMENT = 0b000000010000;
/** @var int */
- public const M_DARKMODULE = 0x02;
+ public const M_TIMING = 0b000000100000;
/** @var int */
- public const M_DATA = 0x04;
+ public const M_FORMAT = 0b000001000000;
/** @var int */
- public const M_FINDER = 0x06;
+ public const M_VERSION = 0b000010000000;
/** @var int */
- public const M_SEPARATOR = 0x08;
+ public const M_QUIETZONE = 0b000100000000;
+
+ /*
+ * dark values
+ */
+
/** @var int */
- public const M_ALIGNMENT = 0x0a;
+ public const M_DARKMODULE = 0b100000000001;
/** @var int */
- public const M_TIMING = 0x0c;
+ public const M_DATA_DARK = 0b100000000010;
/** @var int */
- public const M_FORMAT = 0x0e;
+ public const M_FINDER_DARK = 0b100000000100;
/** @var int */
- public const M_VERSION = 0x10;
+ public const M_ALIGNMENT_DARK = 0b100000010000;
/** @var int */
- public const M_QUIETZONE = 0x12;
+ public const M_TIMING_DARK = 0b100000100000;
/** @var int */
- public const M_LOGO = 0x14;
+ public const M_FORMAT_DARK = 0b100001000000;
/** @var int */
- public const M_FINDER_DOT = 0x16;
+ public const M_VERSION_DARK = 0b100010000000;
/** @var int */
- public const M_TEST = 0xff;
+ public const M_FINDER_DOT = 0b110000000000;
- /**
- * ISO/IEC 18004:2000 Annex E, Table E.1 - Row/column coordinates of center module of Alignment Patterns
- *
- * version -> pattern
- *
- * @var int[][]
+ /*
+ * values used for reversed reflectance
*/
- protected const alignmentPattern = [
- 1 => [],
- 2 => [6, 18],
- 3 => [6, 22],
- 4 => [6, 26],
- 5 => [6, 30],
- 6 => [6, 34],
- 7 => [6, 22, 38],
- 8 => [6, 24, 42],
- 9 => [6, 26, 46],
- 10 => [6, 28, 50],
- 11 => [6, 30, 54],
- 12 => [6, 32, 58],
- 13 => [6, 34, 62],
- 14 => [6, 26, 46, 66],
- 15 => [6, 26, 48, 70],
- 16 => [6, 26, 50, 74],
- 17 => [6, 30, 54, 78],
- 18 => [6, 30, 56, 82],
- 19 => [6, 30, 58, 86],
- 20 => [6, 34, 62, 90],
- 21 => [6, 28, 50, 72, 94],
- 22 => [6, 26, 50, 74, 98],
- 23 => [6, 30, 54, 78, 102],
- 24 => [6, 28, 54, 80, 106],
- 25 => [6, 32, 58, 84, 110],
- 26 => [6, 30, 58, 86, 114],
- 27 => [6, 34, 62, 90, 118],
- 28 => [6, 26, 50, 74, 98, 122],
- 29 => [6, 30, 54, 78, 102, 126],
- 30 => [6, 26, 52, 78, 104, 130],
- 31 => [6, 30, 56, 82, 108, 134],
- 32 => [6, 34, 60, 86, 112, 138],
- 33 => [6, 30, 58, 86, 114, 142],
- 34 => [6, 34, 62, 90, 118, 146],
- 35 => [6, 30, 54, 78, 102, 126, 150],
- 36 => [6, 24, 50, 76, 102, 128, 154],
- 37 => [6, 28, 54, 80, 106, 132, 158],
- 38 => [6, 32, 58, 84, 110, 136, 162],
- 39 => [6, 26, 54, 82, 110, 138, 166],
- 40 => [6, 30, 58, 86, 114, 142, 170],
- ];
- /**
- * ISO/IEC 18004:2000 Annex D, Table D.1 - Version information bit stream for each version
- *
- * no version pattern for QR Codes < 7
- *
- * @var int[]
- */
- protected const versionPattern = [
- 7 => 0b000111110010010100,
- 8 => 0b001000010110111100,
- 9 => 0b001001101010011001,
- 10 => 0b001010010011010011,
- 11 => 0b001011101111110110,
- 12 => 0b001100011101100010,
- 13 => 0b001101100001000111,
- 14 => 0b001110011000001101,
- 15 => 0b001111100100101000,
- 16 => 0b010000101101111000,
- 17 => 0b010001010001011101,
- 18 => 0b010010101000010111,
- 19 => 0b010011010100110010,
- 20 => 0b010100100110100110,
- 21 => 0b010101011010000011,
- 22 => 0b010110100011001001,
- 23 => 0b010111011111101100,
- 24 => 0b011000111011000100,
- 25 => 0b011001000111100001,
- 26 => 0b011010111110101011,
- 27 => 0b011011000010001110,
- 28 => 0b011100110000011010,
- 29 => 0b011101001100111111,
- 30 => 0b011110110101110101,
- 31 => 0b011111001001010000,
- 32 => 0b100000100111010101,
- 33 => 0b100001011011110000,
- 34 => 0b100010100010111010,
- 35 => 0b100011011110011111,
- 36 => 0b100100101100001011,
- 37 => 0b100101010000101110,
- 38 => 0b100110101001100100,
- 39 => 0b100111010101000001,
- 40 => 0b101000110001101001,
- ];
+ /** @var int */
+ public const M_DARKMODULE_LIGHT = 0b000000000001;
+ /** @var int */
+ public const M_FINDER_DOT_LIGHT = 0b010000000000;
+ /** @var int */
+ public const M_SEPARATOR_DARK = 0b100000001000;
+ /** @var int */
+ public const M_QUIETZONE_DARK = 0b100100000000;
/**
- * ISO/IEC 18004:2000 Section 8.9 - Format Information
+ * Map of flag => coord
*
- * ECC level -> mask pattern
+ * @see \chillerlan\QRCode\Data\QRMatrix::checkNeighbours()
*
- * @var int[][]
+ * @var array
*/
- protected const formatPattern = [
- [ // L
- 0b111011111000100,
- 0b111001011110011,
- 0b111110110101010,
- 0b111100010011101,
- 0b110011000101111,
- 0b110001100011000,
- 0b110110001000001,
- 0b110100101110110,
- ],
- [ // M
- 0b101010000010010,
- 0b101000100100101,
- 0b101111001111100,
- 0b101101101001011,
- 0b100010111111001,
- 0b100000011001110,
- 0b100111110010111,
- 0b100101010100000,
- ],
- [ // Q
- 0b011010101011111,
- 0b011000001101000,
- 0b011111100110001,
- 0b011101000000110,
- 0b010010010110100,
- 0b010000110000011,
- 0b010111011011010,
- 0b010101111101101,
- ],
- [ // H
- 0b001011010001001,
- 0b001001110111110,
- 0b001110011100111,
- 0b001100111010000,
- 0b000011101100010,
- 0b000001001010101,
- 0b000110100001100,
- 0b000100000111011,
- ],
+ protected const neighbours = [
+ 0b00000001 => [-1, -1],
+ 0b00000010 => [ 0, -1],
+ 0b00000100 => [ 1, -1],
+ 0b00001000 => [ 1, 0],
+ 0b00010000 => [ 1, 1],
+ 0b00100000 => [ 0, 1],
+ 0b01000000 => [-1, 1],
+ 0b10000000 => [-1, 0],
];
/**
- * the current QR Code version number
+ * the matrix version - always set in QRMatrix, may be null in BitMatrix
*/
- protected int $version;
+ protected ?Version $version = null;
/**
- * the current ECC level
+ * the current ECC level - always set in QRMatrix, may be null in BitMatrix
*/
- protected int $eclevel;
+ protected ?EccLevel $eccLevel = null;
/**
- * the used mask pattern, set via QRMatrix::mapData()
+ * the mask pattern that was used in the most recent operation, set via:
+ *
+ * - QRMatrix::setFormatInfo()
+ * - QRMatrix::mask()
+ * - BitMatrix::readFormatInformation()
*/
- protected int $maskPattern = QRCode::MASK_PATTERN_AUTO;
+ protected ?MaskPattern $maskPattern = null;
/**
- * the size (side length) of the matrix
+ * the size (side length) of the matrix, including quiet zone (if created)
*/
protected int $moduleCount;
@@ -225,37 +140,33 @@ final class QRMatrix{
/**
* QRMatrix constructor.
- *
- * @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
- public function __construct(int $version, int $eclevel){
-
- if(!in_array($version, range(1, 40), true)){
- throw new QRCodeDataException('invalid QR Code version');
- }
-
- if(!array_key_exists($eclevel, QRCode::ECC_MODES)){
- throw new QRCodeDataException('invalid ecc level');
- }
-
+ public function __construct(Version $version, EccLevel $eccLevel){
$this->version = $version;
- $this->eclevel = $eclevel;
- $this->moduleCount = $this->version * 4 + 17;
- $this->matrix = array_fill(0, $this->moduleCount, array_fill(0, $this->moduleCount, $this::M_NULL));
+ $this->eccLevel = $eccLevel;
+ $this->moduleCount = $this->version->getDimension();
+ $this->matrix = $this->createMatrix($this->moduleCount, $this::M_NULL);
}
/**
- * shortcut to initialize the matrix
+ * Creates a 2-dimensional array (square) of the given $size
*/
- public function init(int $maskPattern, bool $test = null):QRMatrix{
+ protected function createMatrix(int $size, int $value):array{
+ return array_fill(0, $size, array_fill(0, $size, $value));
+ }
+
+ /**
+ * shortcut to initialize the functional patterns
+ */
+ public function initFunctionalPatterns():self{
return $this
->setFinderPattern()
->setSeparators()
->setAlignmentPattern()
->setTimingPattern()
- ->setVersionNumber($test)
- ->setFormatInfo($maskPattern, $test)
->setDarkModule()
+ ->setVersionNumber()
+ ->setFormatInfo()
;
}
@@ -264,93 +175,245 @@ final class QRMatrix{
*
* @return int[][]|bool[][]
*/
- public function matrix(bool $boolean = false):array{
+ public function getMatrix(?bool $boolean = null):array{
- if(!$boolean){
+ if($boolean !== true){
return $this->matrix;
}
- $matrix = [];
+ $matrix = $this->matrix;
- foreach($this->matrix as $y => $row){
- $matrix[$y] = [];
-
- foreach($row as $x => $val){
- $matrix[$y][$x] = ($val >> 8) > 0;
- }
+ foreach($matrix as &$row){
+ $row = array_map([$this, 'isDark'], $row);
}
return $matrix;
}
/**
+ * @deprecated 5.0.0 use QRMatrix::getMatrix() instead
+ * @see \chillerlan\QRCode\Data\QRMatrix::getMatrix()
+ * @codeCoverageIgnore
+ */
+ public function matrix(?bool $boolean = null):array{
+ return $this->getMatrix($boolean);
+ }
+
+ /**
* Returns the current version number
*/
- public function version():int{
+ public function getVersion():?Version{
return $this->version;
}
/**
+ * @deprecated 5.0.0 use QRMatrix::getVersion() instead
+ * @see \chillerlan\QRCode\Data\QRMatrix::getVersion()
+ * @codeCoverageIgnore
+ */
+ public function version():?Version{
+ return $this->getVersion();
+ }
+
+ /**
* Returns the current ECC level
*/
- public function eccLevel():int{
- return $this->eclevel;
+ public function getEccLevel():?EccLevel{
+ return $this->eccLevel;
+ }
+
+ /**
+ * @deprecated 5.0.0 use QRMatrix::getEccLevel() instead
+ * @see \chillerlan\QRCode\Data\QRMatrix::getEccLevel()
+ * @codeCoverageIgnore
+ */
+ public function eccLevel():?EccLevel{
+ return $this->getEccLevel();
}
/**
* Returns the current mask pattern
*/
- public function maskPattern():int{
+ public function getMaskPattern():?MaskPattern{
return $this->maskPattern;
}
/**
+ * @deprecated 5.0.0 use QRMatrix::getMaskPattern() instead
+ * @see \chillerlan\QRCode\Data\QRMatrix::getMaskPattern()
+ * @codeCoverageIgnore
+ */
+ public function maskPattern():?MaskPattern{
+ return $this->getMaskPattern();
+ }
+
+ /**
* Returns the absoulute size of the matrix, including quiet zone (after setting it).
*
* size = version * 4 + 17 [ + 2 * quietzone size]
*/
- public function size():int{
+ public function getSize():int{
return $this->moduleCount;
}
/**
- * Returns the value of the module at position [$x, $y]
+ * @deprecated 5.0.0 use QRMatrix::getSize() instead
+ * @see \chillerlan\QRCode\Data\QRMatrix::getSize()
+ * @codeCoverageIgnore
+ */
+ public function size():int{
+ return $this->getSize();
+ }
+
+ /**
+ * Returns the value of the module at position [$x, $y] or -1 if the coordinate is outside the matrix
*/
public function get(int $x, int $y):int{
+
+ if(!isset($this->matrix[$y][$x])){
+ return -1;
+ }
+
return $this->matrix[$y][$x];
}
/**
* Sets the $M_TYPE value for the module at position [$x, $y]
*
- * true => $M_TYPE << 8
+ * true => $M_TYPE | 0x800
* false => $M_TYPE
*/
- public function set(int $x, int $y, bool $value, int $M_TYPE):QRMatrix{
- $this->matrix[$y][$x] = $M_TYPE << ($value ? 8 : 0);
+ public function set(int $x, int $y, bool $value, int $M_TYPE):self{
+
+ if(isset($this->matrix[$y][$x])){
+ // we don't know whether the input is dark, so we remove the dark bit
+ $M_TYPE &= ~$this::IS_DARK;
+
+ if($value === true){
+ $M_TYPE |= $this::IS_DARK;
+ }
+
+ $this->matrix[$y][$x] = $M_TYPE;
+ }
return $this;
}
/**
- * Checks whether a module is true (dark) or false (light)
+ * Fills an area of $width * $height, from the given starting point [$startX, $startY] (top left) with $value for $M_TYPE.
+ */
+ public function setArea(int $startX, int $startY, int $width, int $height, bool $value, int $M_TYPE):self{
+
+ for($y = $startY; $y < ($startY + $height); $y++){
+ for($x = $startX; $x < ($startX + $width); $x++){
+ $this->set($x, $y, $value, $M_TYPE);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Flips the value of the module at ($x, $y)
+ */
+ public function flip(int $x, int $y):self{
+
+ if(isset($this->matrix[$y][$x])){
+ $this->matrix[$y][$x] ^= $this::IS_DARK;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Checks whether the module at ($x, $y) is of the given $M_TYPE
*
- * true => $value >> 8 === $M_TYPE
- * $value >> 8 > 0
+ * true => $value & $M_TYPE === $M_TYPE
*
- * false => $value === $M_TYPE
- * $value >> 8 === 0
+ * Also, returns false if the given coordinates are out of range.
+ */
+ public function checkType(int $x, int $y, int $M_TYPE):bool{
+
+ if(isset($this->matrix[$y][$x])){
+ return ($this->matrix[$y][$x] & $M_TYPE) === $M_TYPE;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the module at ($x, $y) is in the given array of $M_TYPES,
+ * returns true if a match is found, otherwise false.
+ */
+ public function checkTypeIn(int $x, int $y, array $M_TYPES):bool{
+
+ foreach($M_TYPES as $type){
+ if($this->checkType($x, $y, $type)){
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the module at ($x, $y) is true (dark) or false (light)
+ *
+ * Also, returns false if the given coordinates are out of range.
*/
public function check(int $x, int $y):bool{
- return ($this->matrix[$y][$x] >> 8) > 0;
+
+ if(isset($this->matrix[$y][$x])){
+ return $this->isDark($this->matrix[$y][$x]);
+ }
+
+ return false;
}
+ /**
+ * Checks whether the given $M_TYPE is a dark value
+ */
+ public function isDark(int $M_TYPE):bool{
+ return ($M_TYPE & $this::IS_DARK) === $this::IS_DARK;
+ }
+
+ /**
+ * Checks the status of the neighbouring modules for the module at ($x, $y) and returns a bitmask with the results.
+ *
+ * The 8 flags of the bitmask represent the status of each of the neighbouring fields,
+ * starting with the lowest bit for top left, going clockwise:
+ *
+ * 0 1 2
+ * 7 # 3
+ * 6 5 4
+ */
+ public function checkNeighbours(int $x, int $y, ?int $M_TYPE = null):int{
+ $bits = 0;
+
+ foreach($this::neighbours as $bit => [$ix, $iy]){
+ $ix += $x;
+ $iy += $y;
+
+ // $M_TYPE is given, skip if the field is not the same type
+ if($M_TYPE !== null && !$this->checkType($ix, $iy, $M_TYPE)){
+ continue;
+ }
+
+ if($this->checkType($ix, $iy, $this::IS_DARK)){
+ $bits |= $bit;
+ }
+ }
+
+ return $bits;
+ }
/**
* Sets the "dark module", that is always on the same position 1x1px away from the bottom left finder
+ *
+ * 4 * version + 9 or moduleCount - 8
*/
- public function setDarkModule():QRMatrix{
- $this->set(8, 4 * $this->version + 9, true, $this::M_DARKMODULE);
+ public function setDarkModule():self{
+ $this->set(8, ($this->moduleCount - 8), true, $this::M_DARKMODULE);
return $this;
}
@@ -360,31 +423,20 @@ final class QRMatrix{
*
* ISO/IEC 18004:2000 Section 7.3.2
*/
- public function setFinderPattern():QRMatrix{
+ public function setFinderPattern():self{
$pos = [
[0, 0], // top left
- [$this->moduleCount - 7, 0], // bottom left
- [0, $this->moduleCount - 7], // top right
+ [($this->moduleCount - 7), 0], // top right
+ [0, ($this->moduleCount - 7)], // bottom left
];
foreach($pos as $c){
- for($y = 0; $y < 7; $y++){
- for($x = 0; $x < 7; $x++){
- // outer (dark) 7*7 square
- if($x === 0 || $x === 6 || $y === 0 || $y === 6){
- $this->set($c[0] + $y, $c[1] + $x, true, $this::M_FINDER);
- }
- // inner (light) 5*5 square
- elseif($x === 1 || $x === 5 || $y === 1 || $y === 5){
- $this->set($c[0] + $y, $c[1] + $x, false, $this::M_FINDER);
- }
- // 3*3 dot
- else{
- $this->set($c[0] + $y, $c[1] + $x, true, $this::M_FINDER_DOT);
- }
- }
- }
+ $this
+ ->setArea( $c[0] , $c[1] , 7, 7, true, $this::M_FINDER)
+ ->setArea(($c[0] + 1), ($c[1] + 1), 5, 5, false, $this::M_FINDER)
+ ->setArea(($c[0] + 2), ($c[1] + 2), 3, 3, true, $this::M_FINDER_DOT)
+ ;
}
return $this;
@@ -395,24 +447,25 @@ final class QRMatrix{
*
* ISO/IEC 18004:2000 Section 7.3.3
*/
- public function setSeparators():QRMatrix{
+ public function setSeparators():self{
$h = [
[7, 0],
- [$this->moduleCount - 8, 0],
- [7, $this->moduleCount - 8],
+ [($this->moduleCount - 8), 0],
+ [7, ($this->moduleCount - 8)],
];
$v = [
[7, 7],
- [$this->moduleCount - 1, 7],
- [7, $this->moduleCount - 8],
+ [($this->moduleCount - 1), 7],
+ [7, ($this->moduleCount - 8)],
];
for($c = 0; $c < 3; $c++){
for($i = 0; $i < 8; $i++){
- $this->set($h[$c][0] , $h[$c][1] + $i, false, $this::M_SEPARATOR);
- $this->set($v[$c][0] - $i, $v[$c][1] , false, $this::M_SEPARATOR);
+ // phpcs:ignore
+ $this->set( $h[$c][0] , ($h[$c][1] + $i), false, $this::M_SEPARATOR);
+ $this->set(($v[$c][0] - $i), $v[$c][1] , false, $this::M_SEPARATOR);
}
}
@@ -425,23 +478,22 @@ final class QRMatrix{
*
* ISO/IEC 18004:2000 Section 7.3.5
*/
- public function setAlignmentPattern():QRMatrix{
+ public function setAlignmentPattern():self{
+ $alignmentPattern = $this->version->getAlignmentPattern();
- foreach($this::alignmentPattern[$this->version] as $y){
- foreach($this::alignmentPattern[$this->version] as $x){
+ foreach($alignmentPattern as $y){
+ foreach($alignmentPattern as $x){
// skip existing patterns
if($this->matrix[$y][$x] !== $this::M_NULL){
continue;
}
- for($ry = -2; $ry <= 2; $ry++){
- for($rx = -2; $rx <= 2; $rx++){
- $v = ($ry === 0 && $rx === 0) || $ry === 2 || $ry === -2 || $rx === 2 || $rx === -2;
-
- $this->set($x + $rx, $y + $ry, $v, $this::M_ALIGNMENT);
- }
- }
+ $this
+ ->setArea(($x - 2), ($y - 2), 5, 5, true, $this::M_ALIGNMENT)
+ ->setArea(($x - 1), ($y - 1), 3, 3, false, $this::M_ALIGNMENT)
+ ->set($x, $y, true, $this::M_ALIGNMENT)
+ ;
}
}
@@ -455,15 +507,15 @@ final class QRMatrix{
*
* ISO/IEC 18004:2000 Section 7.3.4
*/
- public function setTimingPattern():QRMatrix{
+ public function setTimingPattern():self{
- foreach(range(8, $this->moduleCount - 8 - 1) as $i){
+ for($i = 8; $i < ($this->moduleCount - 8); $i++){
if($this->matrix[6][$i] !== $this::M_NULL || $this->matrix[$i][6] !== $this::M_NULL){
continue;
}
- $v = $i % 2 === 0;
+ $v = ($i % 2) === 0;
$this->set($i, 6, $v, $this::M_TIMING); // h
$this->set(6, $i, $v, $this::M_TIMING); // v
@@ -477,15 +529,15 @@ final class QRMatrix{
*
* ISO/IEC 18004:2000 Section 8.10
*/
- public function setVersionNumber(bool $test = null):QRMatrix{
- $bits = $this::versionPattern[$this->version] ?? false;
+ public function setVersionNumber():self{
+ $bits = $this->version->getVersionPattern();
- if($bits !== false){
+ if($bits !== null){
for($i = 0; $i < 18; $i++){
- $a = (int)floor($i / 3);
- $b = $i % 3 + $this->moduleCount - 8 - 3;
- $v = !$test && (($bits >> $i) & 1) === 1;
+ $a = intdiv($i, 3);
+ $b = (($i % 3) + ($this->moduleCount - 8 - 3));
+ $v = (($bits >> $i) & 1) === 1;
$this->set($b, $a, $v, $this::M_VERSION); // ne
$this->set($a, $b, $v, $this::M_VERSION); // sw
@@ -497,40 +549,43 @@ final class QRMatrix{
}
/**
- * Draws the format info along the finder patterns
+ * Draws the format info along the finder patterns. If no $maskPattern, all format info modules will be set to false.
*
* ISO/IEC 18004:2000 Section 8.9
*/
- public function setFormatInfo(int $maskPattern, bool $test = null):QRMatrix{
- $bits = $this::formatPattern[QRCode::ECC_MODES[$this->eclevel]][$maskPattern] ?? 0;
+ public function setFormatInfo(?MaskPattern $maskPattern = null):self{
+ $this->maskPattern = $maskPattern;
+ $bits = 0; // sets all format fields to false (test mode)
+
+ if($this->maskPattern instanceof MaskPattern){
+ $bits = $this->eccLevel->getformatPattern($this->maskPattern);
+ }
for($i = 0; $i < 15; $i++){
- $v = !$test && (($bits >> $i) & 1) === 1;
+ $v = (($bits >> $i) & 1) === 1;
if($i < 6){
$this->set(8, $i, $v, $this::M_FORMAT);
}
elseif($i < 8){
- $this->set(8, $i + 1, $v, $this::M_FORMAT);
+ $this->set(8, ($i + 1), $v, $this::M_FORMAT);
}
else{
- $this->set(8, $this->moduleCount - 15 + $i, $v, $this::M_FORMAT);
+ $this->set(8, ($this->moduleCount - 15 + $i), $v, $this::M_FORMAT);
}
if($i < 8){
- $this->set($this->moduleCount - $i - 1, 8, $v, $this::M_FORMAT);
+ $this->set(($this->moduleCount - $i - 1), 8, $v, $this::M_FORMAT);
}
elseif($i < 9){
- $this->set(15 - $i, 8, $v, $this::M_FORMAT);
+ $this->set(((15 - $i)), 8, $v, $this::M_FORMAT);
}
else{
- $this->set(15 - $i - 1, 8, $v, $this::M_FORMAT);
+ $this->set((15 - $i - 1), 8, $v, $this::M_FORMAT);
}
}
- $this->set(8, $this->moduleCount - 8, !$test, $this::M_FORMAT);
-
return $this;
}
@@ -541,30 +596,62 @@ final class QRMatrix{
*
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
- public function setQuietZone(int $size = null):QRMatrix{
+ public function setQuietZone(int $quietZoneSize):self{
+
+ // early exit if there's nothing to add
+ if($quietZoneSize < 1){
+ return $this;
+ }
- if($this->matrix[$this->moduleCount - 1][$this->moduleCount - 1] === $this::M_NULL){
+ if($this->matrix[($this->moduleCount - 1)][($this->moduleCount - 1)] === $this::M_NULL){
throw new QRCodeDataException('use only after writing data');
}
- $size = $size !== null
- ? max(0, min($size, floor($this->moduleCount / 2)))
- : 4;
+ // create a matrix with the new size
+ $newSize = ($this->moduleCount + ($quietZoneSize * 2));
+ $newMatrix = $this->createMatrix($newSize, $this::M_QUIETZONE);
- for($y = 0; $y < $this->moduleCount; $y++){
- for($i = 0; $i < $size; $i++){
- array_unshift($this->matrix[$y], $this::M_QUIETZONE);
- array_push($this->matrix[$y], $this::M_QUIETZONE);
+ // copy over the current matrix
+ foreach($this->matrix as $y => $row){
+ foreach($row as $x => $val){
+ $newMatrix[($y + $quietZoneSize)][($x + $quietZoneSize)] = $val;
}
}
- $this->moduleCount += ($size * 2);
+ // set the new values
+ $this->moduleCount = $newSize;
+ $this->matrix = $newMatrix;
+
+ return $this;
+ }
- $r = array_fill(0, $this->moduleCount, $this::M_QUIETZONE);
+ /**
+ * Rotates the matrix by 90 degrees clock wise
+ */
+ public function rotate90():self{
+ /** @phan-suppress-next-line PhanParamTooFewInternalUnpack */
+ $this->matrix = array_map((fn(int ...$a):array => array_reverse($a)), ...$this->matrix);
- for($i = 0; $i < $size; $i++){
- array_unshift($this->matrix, $r);
- array_push($this->matrix, $r);
+ return $this;
+ }
+
+ /**
+ * Inverts the values of the whole matrix
+ *
+ * ISO/IEC 18004:2015 Section 6.2 - Reflectance reversal
+ */
+ public function invert():self{
+
+ foreach($this->matrix as $y => $row){
+ foreach($row as $x => $val){
+
+ // skip null fields
+ if($val === $this::M_NULL){
+ continue;
+ }
+
+ $this->flip($x, $y);
+ }
}
return $this;
@@ -572,11 +659,15 @@ final class QRMatrix{
/**
* Clears a space of $width * $height in order to add a logo or text.
+ * If no $height is given, the space will be assumed a square of $width.
*
- * Additionally, the logo space can be positioned within the QR Code - respecting the main functional patterns -
- * using $startX and $startY. If either of these are null, the logo space will be centered in that direction.
+ * Additionally, the logo space can be positioned within the QR Code using $startX and $startY.
+ * If either of these are null, the logo space will be centered in that direction.
* ECC level "H" (30%) is required.
*
+ * The coordinates of $startX and $startY do not include the quiet zone:
+ * [0, 0] is always the top left module of the top left finder pattern, negative values go into the quiet zone top and left.
+ *
* Please note that adding a logo space minimizes the error correction capacity of the QR Code and
* created images may become unreadable, especially when printed with a chance to receive damage.
* Please test thoroughly before using this feature in production.
@@ -588,13 +679,27 @@ final class QRMatrix{
*
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
- public function setLogoSpace(int $width, int $height, int $startX = null, int $startY = null):QRMatrix{
+ public function setLogoSpace(int $width, ?int $height = null, ?int $startX = null, ?int $startY = null):self{
+ $height ??= $width;
- // for logos we operate in ECC H (30%) only
- if($this->eclevel !== QRCode::ECC_H){
+ // if width and height happen to be negative or 0 (default value), just return - nothing to do
+ if($width <= 0 || $height <= 0){
+ return $this; // @codeCoverageIgnore
+ }
+
+ // for logos, we operate in ECC H (30%) only
+ if($this->eccLevel->getLevel() !== EccLevel::H){
throw new QRCodeDataException('ECC level "H" required to add logo space');
}
+ // $this->moduleCount includes the quiet zone (if created), we need the QR size here
+ $dimension = $this->version->getDimension();
+
+ // throw if the size exceeds the qrcode size
+ if($width > $dimension || $height > $dimension){
+ throw new QRCodeDataException('logo dimensions exceed matrix size');
+ }
+
// we need uneven sizes to center the logo space, adjust if needed
if($startX === null && ($width % 2) === 0){
$width++;
@@ -604,36 +709,29 @@ final class QRMatrix{
$height++;
}
- // $this->moduleCount includes the quiet zone (if created), we need the QR size here
- $length = $this->version * 4 + 17;
-
// throw if the logo space exceeds the maximum error correction capacity
- if($width * $height > floor($length * $length * 0.2)){
+ if(($width * $height) > (int)($dimension * $dimension * 0.25)){
throw new QRCodeDataException('logo space exceeds the maximum error correction capacity');
}
- // quiet zone size
- $qz = ($this->moduleCount - $length) / 2;
- // skip quiet zone and the first 9 rows/columns (finder-, mode-, version- and timing patterns)
- $start = $qz + 9;
- // skip quiet zone
- $end = $this->moduleCount - $qz;
+ $quietzone = (($this->moduleCount - $dimension) / 2);
+ $end = ($this->moduleCount - $quietzone);
// determine start coordinates
- $startX = ($startX !== null ? $startX : ($length - $width) / 2) + $qz;
- $startY = ($startY !== null ? $startY : ($length - $height) / 2) + $qz;
+ $startX ??= (($dimension - $width) / 2);
+ $startY ??= (($dimension - $height) / 2);
+ $endX = ($quietzone + $startX + $width);
+ $endY = ($quietzone + $startY + $height);
// clear the space
- foreach($this->matrix as $y => $row){
- foreach($row as $x => $val){
+ for($y = ($quietzone + $startY); $y < $endY; $y++){
+ for($x = ($quietzone + $startX); $x < $endX; $x++){
// out of bounds, skip
- if($x < $start || $y < $start ||$x >= $end || $y >= $end){
+ if($x < $quietzone || $y < $quietzone ||$x >= $end || $y >= $end){
continue;
}
- // a match
- if($x >= $startX && $x < ($startX + $width) && $y >= $startY && $y < ($startY + $height)){
- $this->set($x, $y, false, $this::M_LOGO);
- }
+
+ $this->set($x, $y, false, $this::M_LOGO);
}
}
@@ -641,100 +739,75 @@ final class QRMatrix{
}
/**
- * Maps the binary $data array from QRDataInterface::maskECC() on the matrix,
- * masking the data using $maskPattern (ISO/IEC 18004:2000 Section 8.8)
- *
- * @see \chillerlan\QRCode\Data\QRDataAbstract::maskECC()
- *
- * @param int[] $data
- * @param int $maskPattern
- *
- * @return \chillerlan\QRCode\Data\QRMatrix
+ * Maps the interleaved binary $data on the matrix
*/
- public function mapData(array $data, int $maskPattern):QRMatrix{
- $this->maskPattern = $maskPattern;
- $byteCount = count($data);
- $y = $this->moduleCount - 1;
- $inc = -1;
- $byteIndex = 0;
- $bitIndex = 7;
- $mask = $this->getMask($this->maskPattern);
+ public function writeCodewords(BitBuffer $bitBuffer):self{
+ $data = (new ReedSolomonEncoder($this->version, $this->eccLevel))->interleaveEcBytes($bitBuffer);
+ $byteCount = count($data);
+ $iByte = 0;
+ $iBit = 7;
+ $direction = true;
- for($i = $y; $i > 0; $i -= 2){
+ for($i = ($this->moduleCount - 1); $i > 0; $i -= 2){
+ // skip vertical alignment pattern
if($i === 6){
$i--;
}
- while(true){
- for($c = 0; $c < 2; $c++){
- $x = $i - $c;
-
- if($this->matrix[$y][$x] === $this::M_NULL){
- $v = false;
-
- if($byteIndex < $byteCount){
- $v = (($data[$byteIndex] >> $bitIndex) & 1) === 1;
- }
-
- if($mask($x, $y) === 0){
- $v = !$v;
- }
+ for($count = 0; $count < $this->moduleCount; $count++){
+ $y = $count;
- $this->matrix[$y][$x] = $this::M_DATA << ($v ? 8 : 0);
- $bitIndex--;
+ if($direction){
+ $y = ($this->moduleCount - 1 - $count);
+ }
- if($bitIndex === -1){
- $byteIndex++;
- $bitIndex = 7;
- }
+ for($col = 0; $col < 2; $col++){
+ $x = ($i - $col);
+ // skip functional patterns
+ if($this->matrix[$y][$x] !== $this::M_NULL){
+ continue;
}
- }
- $y += $inc;
+ $this->matrix[$y][$x] = $this::M_DATA;
- if($y < 0 || $this->moduleCount <= $y){
- $y -= $inc;
- $inc = -$inc;
+ if($iByte < $byteCount && (($data[$iByte] >> $iBit--) & 1) === 1){
+ $this->matrix[$y][$x] |= $this::IS_DARK;
+ }
- break;
+ if($iBit === -1){
+ $iByte++;
+ $iBit = 7;
+ }
}
-
}
+
+ $direction = !$direction; // switch directions
}
return $this;
}
/**
- * ISO/IEC 18004:2000 Section 8.8.1
- *
- * Note that some versions of the QR code standard have had errors in the section about mask patterns.
- * The information below has been corrected. (https://www.thonky.com/qr-code-tutorial/mask-patterns)
- *
- * @see \chillerlan\QRCode\QRMatrix::mapData()
+ * Applies/reverses the mask pattern
*
- * @internal
- *
- * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ * ISO/IEC 18004:2000 Section 8.8.1
*/
- protected function getMask(int $maskPattern):Closure{
+ public function mask(MaskPattern $maskPattern):self{
+ $this->maskPattern = $maskPattern;
+ $mask = $this->maskPattern->getMask();
- if((0b111 & $maskPattern) !== $maskPattern){
- throw new QRCodeDataException('invalid mask pattern'); // @codeCoverageIgnore
+ foreach($this->matrix as $y => $row){
+ foreach($row as $x => $val){
+ // skip non-data modules
+ if(($val & $this::M_DATA) === $this::M_DATA && $mask($x, $y)){
+ $this->flip($x, $y);
+ }
+ }
}
- return [
- 0b000 => fn($x, $y):int => ($x + $y) % 2,
- 0b001 => fn($x, $y):int => $y % 2,
- 0b010 => fn($x, $y):int => $x % 3,
- 0b011 => fn($x, $y):int => ($x + $y) % 3,
- 0b100 => fn($x, $y):int => ((int)($y / 2) + (int)($x / 3)) % 2,
- 0b101 => fn($x, $y):int => (($x * $y) % 2) + (($x * $y) % 3),
- 0b110 => fn($x, $y):int => ((($x * $y) % 2) + (($x * $y) % 3)) % 2,
- 0b111 => fn($x, $y):int => ((($x * $y) % 3) + (($x + $y) % 2)) % 2,
- ][$maskPattern];
+ return $this;
}
}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/ReedSolomonEncoder.php b/vendor/chillerlan/php-qrcode/src/Data/ReedSolomonEncoder.php
new file mode 100644
index 000000000..60444378c
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/ReedSolomonEncoder.php
@@ -0,0 +1,127 @@
+<?php
+/**
+ * Class ReedSolomonEncoder
+ *
+ * @created 07.01.2021
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2021 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, EccLevel, GenericGFPoly, GF256, Version};
+use function array_fill, array_merge, count, max;
+
+/**
+ * Reed-Solomon encoding - ISO/IEC 18004:2000 Section 8.5 ff
+ *
+ * @see http://www.thonky.com/qr-code-tutorial/error-correction-coding
+ */
+final class ReedSolomonEncoder{
+
+ private Version $version;
+ private EccLevel $eccLevel;
+
+ private array $interleavedData;
+ private int $interleavedDataIndex;
+
+ /**
+ * ReedSolomonDecoder constructor
+ */
+ public function __construct(Version $version, EccLevel $eccLevel){
+ $this->version = $version;
+ $this->eccLevel = $eccLevel;
+ }
+
+ /**
+ * ECC encoding and interleaving
+ *
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function interleaveEcBytes(BitBuffer $bitBuffer):array{
+ [$numEccCodewords, [[$l1, $b1], [$l2, $b2]]] = $this->version->getRSBlocks($this->eccLevel);
+
+ $rsBlocks = array_fill(0, $l1, [($numEccCodewords + $b1), $b1]);
+
+ if($l2 > 0){
+ $rsBlocks = array_merge($rsBlocks, array_fill(0, $l2, [($numEccCodewords + $b2), $b2]));
+ }
+
+ $bitBufferData = $bitBuffer->getBuffer();
+ $dataBytes = [];
+ $ecBytes = [];
+ $maxDataBytes = 0;
+ $maxEcBytes = 0;
+ $dataByteOffset = 0;
+
+ foreach($rsBlocks as $key => [$rsBlockTotal, $dataByteCount]){
+ $dataBytes[$key] = [];
+
+ for($i = 0; $i < $dataByteCount; $i++){
+ $dataBytes[$key][$i] = ($bitBufferData[($i + $dataByteOffset)] & 0xff);
+ }
+
+ $ecByteCount = ($rsBlockTotal - $dataByteCount);
+ $ecBytes[$key] = $this->encode($dataBytes[$key], $ecByteCount);
+ $maxDataBytes = max($maxDataBytes, $dataByteCount);
+ $maxEcBytes = max($maxEcBytes, $ecByteCount);
+ $dataByteOffset += $dataByteCount;
+ }
+
+ $this->interleavedData = array_fill(0, $this->version->getTotalCodewords(), 0);
+ $this->interleavedDataIndex = 0;
+ $numRsBlocks = ($l1 + $l2);
+
+ $this->interleave($dataBytes, $maxDataBytes, $numRsBlocks);
+ $this->interleave($ecBytes, $maxEcBytes, $numRsBlocks);
+
+ return $this->interleavedData;
+ }
+
+ /**
+ *
+ */
+ private function encode(array $dataBytes, int $ecByteCount):array{
+ $rsPoly = new GenericGFPoly([1]);
+
+ for($i = 0; $i < $ecByteCount; $i++){
+ $rsPoly = $rsPoly->multiply(new GenericGFPoly([1, GF256::exp($i)]));
+ }
+
+ $rsPolyDegree = $rsPoly->getDegree();
+
+ $modCoefficients = (new GenericGFPoly($dataBytes, $rsPolyDegree))
+ ->mod($rsPoly)
+ ->getCoefficients()
+ ;
+
+ $ecBytes = array_fill(0, $rsPolyDegree, 0);
+ $count = (count($modCoefficients) - $rsPolyDegree);
+
+ foreach($ecBytes as $i => &$val){
+ $modIndex = ($i + $count);
+ $val = 0;
+
+ if($modIndex >= 0){
+ $val = $modCoefficients[$modIndex];
+ }
+ }
+
+ return $ecBytes;
+ }
+
+ /**
+ *
+ */
+ private function interleave(array $byteArray, int $maxBytes, int $numRsBlocks):void{
+ for($x = 0; $x < $maxBytes; $x++){
+ for($y = 0; $y < $numRsBlocks; $y++){
+ if($x < count($byteArray[$y])){
+ $this->interleavedData[$this->interleavedDataIndex++] = $byteArray[$y][$x];
+ }
+ }
+ }
+ }
+
+}