aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/chillerlan/php-qrcode/src/Common
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/chillerlan/php-qrcode/src/Common')
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/BitBuffer.php180
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/ECICharset.php125
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/EccLevel.php223
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/GDLuminanceSource.php97
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/GF256.php154
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/GenericGFPoly.php263
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/IMagickLuminanceSource.php78
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceAbstract.php107
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceInterface.php61
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/MaskPattern.php329
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/Mode.php96
-rw-r--r--vendor/chillerlan/php-qrcode/src/Common/Version.php287
12 files changed, 2000 insertions, 0 deletions
diff --git a/vendor/chillerlan/php-qrcode/src/Common/BitBuffer.php b/vendor/chillerlan/php-qrcode/src/Common/BitBuffer.php
new file mode 100644
index 000000000..4a59f2b58
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/BitBuffer.php
@@ -0,0 +1,180 @@
+<?php
+/**
+ * Class BitBuffer
+ *
+ * @created 25.11.2015
+ * @author Smiley <smiley@chillerlan.net>
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+use function count, floor, min;
+
+/**
+ * Holds the raw binary data
+ */
+final class BitBuffer{
+
+ /**
+ * The buffer content
+ *
+ * @var int[]
+ */
+ private array $buffer;
+
+ /**
+ * Length of the content (bits)
+ */
+ private int $length;
+
+ /**
+ * Read count (bytes)
+ */
+ private int $bytesRead = 0;
+
+ /**
+ * Read count (bits)
+ */
+ private int $bitsRead = 0;
+
+ /**
+ * BitBuffer constructor.
+ *
+ * @param int[] $bytes
+ */
+ public function __construct(array $bytes = []){
+ $this->buffer = $bytes;
+ $this->length = count($this->buffer);
+ }
+
+ /**
+ * appends a sequence of bits
+ */
+ public function put(int $bits, int $length):self{
+
+ for($i = 0; $i < $length; $i++){
+ $this->putBit((($bits >> ($length - $i - 1)) & 1) === 1);
+ }
+
+ return $this;
+ }
+
+ /**
+ * appends a single bit
+ */
+ public function putBit(bool $bit):self{
+ $bufIndex = (int)floor($this->length / 8);
+
+ if(count($this->buffer) <= $bufIndex){
+ $this->buffer[] = 0;
+ }
+
+ if($bit === true){
+ $this->buffer[$bufIndex] |= (0x80 >> ($this->length % 8));
+ }
+
+ $this->length++;
+
+ return $this;
+ }
+
+ /**
+ * returns the current buffer length
+ */
+ public function getLength():int{
+ return $this->length;
+ }
+
+ /**
+ * returns the buffer content
+ *
+ * to debug: array_map(fn($v) => sprintf('%08b', $v), $bitBuffer->getBuffer())
+ */
+ public function getBuffer():array{
+ return $this->buffer;
+ }
+
+ /**
+ * @return int number of bits that can be read successfully
+ */
+ public function available():int{
+ return ((8 * ($this->length - $this->bytesRead)) - $this->bitsRead);
+ }
+
+ /**
+ * @author Sean Owen, ZXing
+ *
+ * @param int $numBits number of bits to read
+ *
+ * @return int representing the bits read. The bits will appear as the least-significant bits of the int
+ * @throws \chillerlan\QRCode\QRCodeException if numBits isn't in [1,32] or more than is available
+ */
+ public function read(int $numBits):int{
+
+ if($numBits < 1 || $numBits > $this->available()){
+ throw new QRCodeException('invalid $numBits: '.$numBits);
+ }
+
+ $result = 0;
+
+ // First, read remainder from current byte
+ if($this->bitsRead > 0){
+ $bitsLeft = (8 - $this->bitsRead);
+ $toRead = min($numBits, $bitsLeft);
+ $bitsToNotRead = ($bitsLeft - $toRead);
+ $mask = ((0xff >> (8 - $toRead)) << $bitsToNotRead);
+ $result = (($this->buffer[$this->bytesRead] & $mask) >> $bitsToNotRead);
+ $numBits -= $toRead;
+ $this->bitsRead += $toRead;
+
+ if($this->bitsRead === 8){
+ $this->bitsRead = 0;
+ $this->bytesRead++;
+ }
+ }
+
+ // Next read whole bytes
+ if($numBits > 0){
+
+ while($numBits >= 8){
+ $result = (($result << 8) | ($this->buffer[$this->bytesRead] & 0xff));
+ $this->bytesRead++;
+ $numBits -= 8;
+ }
+
+ // Finally read a partial byte
+ if($numBits > 0){
+ $bitsToNotRead = (8 - $numBits);
+ $mask = ((0xff >> $bitsToNotRead) << $bitsToNotRead);
+ $result = (($result << $numBits) | (($this->buffer[$this->bytesRead] & $mask) >> $bitsToNotRead));
+ $this->bitsRead += $numBits;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Clears the buffer and resets the stats
+ */
+ public function clear():self{
+ $this->buffer = [];
+ $this->length = 0;
+
+ return $this->rewind();
+ }
+
+ /**
+ * Resets the read-counters
+ */
+ public function rewind():self{
+ $this->bytesRead = 0;
+ $this->bitsRead = 0;
+
+ return $this;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/ECICharset.php b/vendor/chillerlan/php-qrcode/src/Common/ECICharset.php
new file mode 100644
index 000000000..0c98e36da
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/ECICharset.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * Class ECICharset
+ *
+ * @created 21.01.2021
+ * @author ZXing Authors
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2021 smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+use function sprintf;
+
+/**
+ * ISO/IEC 18004:2000 - 8.4.1 Extended Channel Interpretation (ECI) Mode
+ */
+final class ECICharset{
+
+ public const CP437 = 0; // Code page 437, DOS Latin US
+ public const ISO_IEC_8859_1_GLI = 1; // GLI encoding with characters 0 to 127 identical to ISO/IEC 646 and characters 128 to 255 identical to ISO 8859-1
+ public const CP437_WO_GLI = 2; // An equivalent code table to CP437, without the return-to-GLI 0 logic
+ public const ISO_IEC_8859_1 = 3; // Latin-1 (Default)
+ public const ISO_IEC_8859_2 = 4; // Latin-2
+ public const ISO_IEC_8859_3 = 5; // Latin-3
+ public const ISO_IEC_8859_4 = 6; // Latin-4
+ public const ISO_IEC_8859_5 = 7; // Latin/Cyrillic
+ public const ISO_IEC_8859_6 = 8; // Latin/Arabic
+ public const ISO_IEC_8859_7 = 9; // Latin/Greek
+ public const ISO_IEC_8859_8 = 10; // Latin/Hebrew
+ public const ISO_IEC_8859_9 = 11; // Latin-5
+ public const ISO_IEC_8859_10 = 12; // Latin-6
+ public const ISO_IEC_8859_11 = 13; // Latin/Thai
+ // 14 reserved
+ public const ISO_IEC_8859_13 = 15; // Latin-7 (Baltic Rim)
+ public const ISO_IEC_8859_14 = 16; // Latin-8 (Celtic)
+ public const ISO_IEC_8859_15 = 17; // Latin-9
+ public const ISO_IEC_8859_16 = 18; // Latin-10
+ // 19 reserved
+ public const SHIFT_JIS = 20; // JIS X 0208 Annex 1 + JIS X 0201
+ public const WINDOWS_1250_LATIN_2 = 21; // Superset of Latin-2, Central Europe
+ public const WINDOWS_1251_CYRILLIC = 22; // Latin/Cyrillic
+ public const WINDOWS_1252_LATIN_1 = 23; // Superset of Latin-1
+ public const WINDOWS_1256_ARABIC = 24;
+ public const ISO_IEC_10646_UCS_2 = 25; // High order byte first (UTF-16BE)
+ public const ISO_IEC_10646_UTF_8 = 26; // UTF-8
+ public const ISO_IEC_646_1991 = 27; // International Reference Version of ISO 7-bit coded character set (US-ASCII)
+ public const BIG5 = 28; // Big 5 (Taiwan) Chinese Character Set
+ public const GB18030 = 29; // GB (PRC) Chinese Character Set
+ public const EUC_KR = 30; // Korean Character Set
+
+ /**
+ * map of charset id -> name
+ *
+ * @see \mb_list_encodings()
+ */
+ public const MB_ENCODINGS = [
+ self::CP437 => null,
+ self::ISO_IEC_8859_1_GLI => null,
+ self::CP437_WO_GLI => null,
+ self::ISO_IEC_8859_1 => 'ISO-8859-1',
+ self::ISO_IEC_8859_2 => 'ISO-8859-2',
+ self::ISO_IEC_8859_3 => 'ISO-8859-3',
+ self::ISO_IEC_8859_4 => 'ISO-8859-4',
+ self::ISO_IEC_8859_5 => 'ISO-8859-5',
+ self::ISO_IEC_8859_6 => 'ISO-8859-6',
+ self::ISO_IEC_8859_7 => 'ISO-8859-7',
+ self::ISO_IEC_8859_8 => 'ISO-8859-8',
+ self::ISO_IEC_8859_9 => 'ISO-8859-9',
+ self::ISO_IEC_8859_10 => 'ISO-8859-10',
+ self::ISO_IEC_8859_11 => null,
+ self::ISO_IEC_8859_13 => 'ISO-8859-13',
+ self::ISO_IEC_8859_14 => 'ISO-8859-14',
+ self::ISO_IEC_8859_15 => 'ISO-8859-15',
+ self::ISO_IEC_8859_16 => 'ISO-8859-16',
+ self::SHIFT_JIS => 'SJIS',
+ self::WINDOWS_1250_LATIN_2 => null, // @see https://www.php.net/manual/en/function.mb-convert-encoding.php#112547
+ self::WINDOWS_1251_CYRILLIC => 'Windows-1251',
+ self::WINDOWS_1252_LATIN_1 => 'Windows-1252',
+ self::WINDOWS_1256_ARABIC => null, // @see https://stackoverflow.com/a/8592995
+ self::ISO_IEC_10646_UCS_2 => 'UTF-16BE',
+ self::ISO_IEC_10646_UTF_8 => 'UTF-8',
+ self::ISO_IEC_646_1991 => 'ASCII',
+ self::BIG5 => 'BIG-5',
+ self::GB18030 => 'GB18030',
+ self::EUC_KR => 'EUC-KR',
+ ];
+
+ /**
+ * The current ECI character set ID
+ */
+ private int $charsetID;
+
+ /**
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function __construct(int $charsetID){
+
+ if($charsetID < 0 || $charsetID > 999999){
+ throw new QRCodeException(sprintf('invalid charset id: "%s"', $charsetID));
+ }
+
+ $this->charsetID = $charsetID;
+ }
+
+ /**
+ * Returns the current character set ID
+ */
+ public function getID():int{
+ return $this->charsetID;
+ }
+
+ /**
+ * Returns the name of the current character set or null if no name is available
+ *
+ * @see \mb_convert_encoding()
+ * @see \iconv()
+ */
+ public function getName():?string{
+ return (self::MB_ENCODINGS[$this->charsetID] ?? null);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/EccLevel.php b/vendor/chillerlan/php-qrcode/src/Common/EccLevel.php
new file mode 100644
index 000000000..789d7f79d
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/EccLevel.php
@@ -0,0 +1,223 @@
+<?php
+/**
+ * Class EccLevel
+ *
+ * @created 19.11.2020
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+use function array_column;
+
+/**
+ * This class encapsulates the four error correction levels defined by the QR code standard.
+ */
+final class EccLevel{
+
+ // ISO/IEC 18004:2000 Tables 12, 25
+
+ /** @var int */
+ public const L = 0b01; // 7%.
+ /** @var int */
+ public const M = 0b00; // 15%.
+ /** @var int */
+ public const Q = 0b11; // 25%.
+ /** @var int */
+ public const H = 0b10; // 30%.
+
+ /**
+ * ISO/IEC 18004:2000 Tables 7-11 - Number of symbol characters and input data capacity for versions 1 to 40
+ *
+ * @var int[][]
+ */
+ private const MAX_BITS = [
+ // [ L, M, Q, H] // v => modules
+ [ 0, 0, 0, 0], // 0 => will be ignored, index starts at 1
+ [ 152, 128, 104, 72], // 1 => 21
+ [ 272, 224, 176, 128], // 2 => 25
+ [ 440, 352, 272, 208], // 3 => 29
+ [ 640, 512, 384, 288], // 4 => 33
+ [ 864, 688, 496, 368], // 5 => 37
+ [ 1088, 864, 608, 480], // 6 => 41
+ [ 1248, 992, 704, 528], // 7 => 45
+ [ 1552, 1232, 880, 688], // 8 => 49
+ [ 1856, 1456, 1056, 800], // 9 => 53
+ [ 2192, 1728, 1232, 976], // 10 => 57
+ [ 2592, 2032, 1440, 1120], // 11 => 61
+ [ 2960, 2320, 1648, 1264], // 12 => 65
+ [ 3424, 2672, 1952, 1440], // 13 => 69 NICE!
+ [ 3688, 2920, 2088, 1576], // 14 => 73
+ [ 4184, 3320, 2360, 1784], // 15 => 77
+ [ 4712, 3624, 2600, 2024], // 16 => 81
+ [ 5176, 4056, 2936, 2264], // 17 => 85
+ [ 5768, 4504, 3176, 2504], // 18 => 89
+ [ 6360, 5016, 3560, 2728], // 19 => 93
+ [ 6888, 5352, 3880, 3080], // 20 => 97
+ [ 7456, 5712, 4096, 3248], // 21 => 101
+ [ 8048, 6256, 4544, 3536], // 22 => 105
+ [ 8752, 6880, 4912, 3712], // 23 => 109
+ [ 9392, 7312, 5312, 4112], // 24 => 113
+ [10208, 8000, 5744, 4304], // 25 => 117
+ [10960, 8496, 6032, 4768], // 26 => 121
+ [11744, 9024, 6464, 5024], // 27 => 125
+ [12248, 9544, 6968, 5288], // 28 => 129
+ [13048, 10136, 7288, 5608], // 29 => 133
+ [13880, 10984, 7880, 5960], // 30 => 137
+ [14744, 11640, 8264, 6344], // 31 => 141
+ [15640, 12328, 8920, 6760], // 32 => 145
+ [16568, 13048, 9368, 7208], // 33 => 149
+ [17528, 13800, 9848, 7688], // 34 => 153
+ [18448, 14496, 10288, 7888], // 35 => 157
+ [19472, 15312, 10832, 8432], // 36 => 161
+ [20528, 15936, 11408, 8768], // 37 => 165
+ [21616, 16816, 12016, 9136], // 38 => 169
+ [22496, 17728, 12656, 9776], // 39 => 173
+ [23648, 18672, 13328, 10208], // 40 => 177
+ ];
+
+ /**
+ * ISO/IEC 18004:2000 Section 8.9 - Format Information
+ *
+ * ECC level -> mask pattern
+ *
+ * @var int[][]
+ */
+ private const FORMAT_PATTERN = [
+ [ // 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,
+ ],
+ ];
+
+ /**
+ * The current ECC level value
+ *
+ * L: 0b01
+ * M: 0b00
+ * Q: 0b11
+ * H: 0b10
+ */
+ private int $eccLevel;
+
+ /**
+ * @param int $eccLevel containing the two bits encoding a QR Code's error correction level
+ *
+ * @todo: accept string values (PHP8+)
+ * @see https://github.com/chillerlan/php-qrcode/discussions/160
+ *
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function __construct(int $eccLevel){
+
+ if((0b11 & $eccLevel) !== $eccLevel){
+ throw new QRCodeException('invalid ECC level');
+ }
+
+ $this->eccLevel = $eccLevel;
+ }
+
+ /**
+ * returns the string representation of the current ECC level
+ */
+ public function __toString():string{
+ return [
+ self::L => 'L',
+ self::M => 'M',
+ self::Q => 'Q',
+ self::H => 'H',
+ ][$this->eccLevel];
+ }
+
+ /**
+ * returns the current ECC level
+ */
+ public function getLevel():int{
+ return $this->eccLevel;
+ }
+
+ /**
+ * returns the ordinal value of the current ECC level
+ *
+ * references to the keys of the following tables:
+ *
+ * @see \chillerlan\QRCode\Common\EccLevel::MAX_BITS
+ * @see \chillerlan\QRCode\Common\EccLevel::FORMAT_PATTERN
+ * @see \chillerlan\QRCode\Common\Version::RSBLOCKS
+ */
+ public function getOrdinal():int{
+ return [
+ self::L => 0,
+ self::M => 1,
+ self::Q => 2,
+ self::H => 3,
+ ][$this->eccLevel];
+ }
+
+ /**
+ * returns the format pattern for the given $eccLevel and $maskPattern
+ */
+ public function getformatPattern(MaskPattern $maskPattern):int{
+ return self::FORMAT_PATTERN[$this->getOrdinal()][$maskPattern->getPattern()];
+ }
+
+ /**
+ * returns an array with the max bit lengths for version 1-40 and the current ECC level
+ *
+ * @return int[]
+ */
+ public function getMaxBits():array{
+ $col = array_column(self::MAX_BITS, $this->getOrdinal());
+
+ unset($col[0]); // remove the inavlid index 0
+
+ return $col;
+ }
+
+ /**
+ * Returns the maximum bit length for the given version and current ECC level
+ */
+ public function getMaxBitsForVersion(Version $version):int{
+ return self::MAX_BITS[$version->getVersionNumber()][$this->getOrdinal()];
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/GDLuminanceSource.php b/vendor/chillerlan/php-qrcode/src/Common/GDLuminanceSource.php
new file mode 100644
index 000000000..0702a67bb
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/GDLuminanceSource.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Class GDLuminanceSource
+ *
+ * @created 17.01.2021
+ * @author Ashot Khanamiryan
+ * @author Smiley <smiley@chillerlan.net>
+ * @copyright 2021 Smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\Decoder\QRCodeDecoderException;
+use chillerlan\Settings\SettingsContainerInterface;
+use function file_get_contents, get_resource_type, imagecolorat, imagecolorsforindex,
+ imagecreatefromstring, imagefilter, imagesx, imagesy, is_resource;
+use const IMG_FILTER_BRIGHTNESS, IMG_FILTER_CONTRAST, IMG_FILTER_GRAYSCALE, IMG_FILTER_NEGATE, PHP_MAJOR_VERSION;
+
+/**
+ * This class is used to help decode images from files which arrive as GD Resource
+ * It does not support rotation.
+ */
+class GDLuminanceSource extends LuminanceSourceAbstract{
+
+ /**
+ * @var resource|\GdImage
+ */
+ protected $gdImage;
+
+ /**
+ * GDLuminanceSource constructor.
+ *
+ * @param resource|\GdImage $gdImage
+ * @param \chillerlan\Settings\SettingsContainerInterface|null $options
+ *
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ public function __construct($gdImage, ?SettingsContainerInterface $options = null){
+
+ /** @noinspection PhpFullyQualifiedNameUsageInspection */
+ if(
+ (PHP_MAJOR_VERSION >= 8 && !$gdImage instanceof \GdImage) // @todo: remove version check in v6
+ || (PHP_MAJOR_VERSION < 8 && (!is_resource($gdImage) || get_resource_type($gdImage) !== 'gd'))
+ ){
+ throw new QRCodeDecoderException('Invalid GD image source.'); // @codeCoverageIgnore
+ }
+
+ parent::__construct(imagesx($gdImage), imagesy($gdImage), $options);
+
+ $this->gdImage = $gdImage;
+
+ if($this->options->readerGrayscale){
+ imagefilter($this->gdImage, IMG_FILTER_GRAYSCALE);
+ }
+
+ if($this->options->readerInvertColors){
+ imagefilter($this->gdImage, IMG_FILTER_NEGATE);
+ }
+
+ if($this->options->readerIncreaseContrast){
+ imagefilter($this->gdImage, IMG_FILTER_BRIGHTNESS, -100);
+ imagefilter($this->gdImage, IMG_FILTER_CONTRAST, -100);
+ }
+
+ $this->setLuminancePixels();
+ }
+
+ /**
+ *
+ */
+ protected function setLuminancePixels():void{
+
+ for($j = 0; $j < $this->height; $j++){
+ for($i = 0; $i < $this->width; $i++){
+ $argb = imagecolorat($this->gdImage, $i, $j);
+ $pixel = imagecolorsforindex($this->gdImage, $argb);
+
+ $this->setLuminancePixel($pixel['red'], $pixel['green'], $pixel['blue']);
+ }
+ }
+
+ }
+
+ /** @inheritDoc */
+ public static function fromFile(string $path, ?SettingsContainerInterface $options = null):self{
+ return new self(imagecreatefromstring(file_get_contents(self::checkFile($path))), $options);
+ }
+
+ /** @inheritDoc */
+ public static function fromBlob(string $blob, ?SettingsContainerInterface $options = null):self{
+ return new self(imagecreatefromstring($blob), $options);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/GF256.php b/vendor/chillerlan/php-qrcode/src/Common/GF256.php
new file mode 100644
index 000000000..d8ba0950b
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/GF256.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * Class GF256
+ *
+ * @created 16.01.2021
+ * @author ZXing Authors
+ * @author Smiley <smiley@chillerlan.net>
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+
+use function array_fill;
+
+/**
+ * This class contains utility methods for performing mathematical operations over
+ * the Galois Fields. Operations use a given primitive polynomial in calculations.
+ *
+ * Throughout this package, elements of the GF are represented as an int
+ * for convenience and speed (but at the cost of memory).
+ *
+ *
+ * @author Sean Owen
+ * @author David Olivier
+ */
+final class GF256{
+
+ /**
+ * irreducible polynomial whose coefficients are represented by the bits of an int,
+ * where the least-significant bit represents the constant coefficient
+ */
+# private int $primitive = 0x011D;
+
+ private const logTable = [
+ 0, // the first value is never returned, index starts at 1
+ 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75,
+ 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113,
+ 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69,
+ 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166,
+ 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136,
+ 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64,
+ 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61,
+ 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87,
+ 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24,
+ 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46,
+ 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97,
+ 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162,
+ 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246,
+ 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90,
+ 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215,
+ 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175,
+ ];
+
+ private const expTable = [
+ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38,
+ 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192,
+ 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35,
+ 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161,
+ 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240,
+ 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226,
+ 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206,
+ 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204,
+ 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84,
+ 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115,
+ 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255,
+ 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65,
+ 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166,
+ 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9,
+ 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22,
+ 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1,
+ ];
+
+ /**
+ * Implements both addition and subtraction -- they are the same in GF(size).
+ *
+ * @return int sum/difference of a and b
+ */
+ public static function addOrSubtract(int $a, int $b):int{
+ return ($a ^ $b);
+ }
+
+ /**
+ * @return GenericGFPoly the monomial representing coefficient * x^degree
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public static function buildMonomial(int $degree, int $coefficient):GenericGFPoly{
+
+ if($degree < 0){
+ throw new QRCodeException('degree < 0');
+ }
+
+ $coefficients = array_fill(0, ($degree + 1), 0);
+ $coefficients[0] = $coefficient;
+
+ return new GenericGFPoly($coefficients);
+ }
+
+ /**
+ * @return int 2 to the power of $a in GF(size)
+ */
+ public static function exp(int $a):int{
+
+ if($a < 0){
+ $a += 255;
+ }
+ elseif($a >= 256){
+ $a -= 255;
+ }
+
+ return self::expTable[$a];
+ }
+
+ /**
+ * @return int base 2 log of $a in GF(size)
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public static function log(int $a):int{
+
+ if($a < 1){
+ throw new QRCodeException('$a < 1');
+ }
+
+ return self::logTable[$a];
+ }
+
+ /**
+ * @return int multiplicative inverse of a
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public static function inverse(int $a):int{
+
+ if($a === 0){
+ throw new QRCodeException('$a === 0');
+ }
+
+ return self::expTable[(256 - self::logTable[$a] - 1)];
+ }
+
+ /**
+ * @return int product of a and b in GF(size)
+ */
+ public static function multiply(int $a, int $b):int{
+
+ if($a === 0 || $b === 0){
+ return 0;
+ }
+
+ return self::expTable[((self::logTable[$a] + self::logTable[$b]) % 255)];
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/GenericGFPoly.php b/vendor/chillerlan/php-qrcode/src/Common/GenericGFPoly.php
new file mode 100644
index 000000000..4023e74d2
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/GenericGFPoly.php
@@ -0,0 +1,263 @@
+<?php
+/**
+ * Class GenericGFPoly
+ *
+ * @created 16.01.2021
+ * @author ZXing Authors
+ * @author Smiley <smiley@chillerlan.net>
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+use function array_fill, array_slice, array_splice, count;
+
+/**
+ * Represents a polynomial whose coefficients are elements of a GF.
+ * Instances of this class are immutable.
+ *
+ * Much credit is due to William Rucklidge since portions of this code are an indirect
+ * port of his C++ Reed-Solomon implementation.
+ *
+ * @author Sean Owen
+ */
+final class GenericGFPoly{
+
+ private array $coefficients;
+
+ /**
+ * @param array $coefficients array coefficients as ints representing elements of GF(size), arranged
+ * from most significant (highest-power term) coefficient to the least significant
+ * @param int|null $degree
+ *
+ * @throws \chillerlan\QRCode\QRCodeException if argument is null or empty, or if leading coefficient is 0 and this
+ * is not a constant polynomial (that is, it is not the monomial "0")
+ */
+ public function __construct(array $coefficients, ?int $degree = null){
+ $degree ??= 0;
+
+ if(empty($coefficients)){
+ throw new QRCodeException('arg $coefficients is empty');
+ }
+
+ if($degree < 0){
+ throw new QRCodeException('negative degree');
+ }
+
+ $coefficientsLength = count($coefficients);
+
+ // Leading term must be non-zero for anything except the constant polynomial "0"
+ $firstNonZero = 0;
+
+ while($firstNonZero < $coefficientsLength && $coefficients[$firstNonZero] === 0){
+ $firstNonZero++;
+ }
+
+ $this->coefficients = [0];
+
+ if($firstNonZero !== $coefficientsLength){
+ $this->coefficients = array_fill(0, ($coefficientsLength - $firstNonZero + $degree), 0);
+
+ for($i = 0; $i < ($coefficientsLength - $firstNonZero); $i++){
+ $this->coefficients[$i] = $coefficients[($i + $firstNonZero)];
+ }
+ }
+
+ }
+
+ /**
+ * @return int $coefficient of x^degree term in this polynomial
+ */
+ public function getCoefficient(int $degree):int{
+ return $this->coefficients[(count($this->coefficients) - 1 - $degree)];
+ }
+
+ /**
+ * @return int[]
+ */
+ public function getCoefficients():array{
+ return $this->coefficients;
+ }
+
+ /**
+ * @return int $degree of this polynomial
+ */
+ public function getDegree():int{
+ return (count($this->coefficients) - 1);
+ }
+
+ /**
+ * @return bool true if this polynomial is the monomial "0"
+ */
+ public function isZero():bool{
+ return $this->coefficients[0] === 0;
+ }
+
+ /**
+ * @return int evaluation of this polynomial at a given point
+ */
+ public function evaluateAt(int $a):int{
+
+ if($a === 0){
+ // Just return the x^0 coefficient
+ return $this->getCoefficient(0);
+ }
+
+ $result = 0;
+
+ foreach($this->coefficients as $c){
+ // if $a === 1 just the sum of the coefficients
+ $result = GF256::addOrSubtract((($a === 1) ? $result : GF256::multiply($a, $result)), $c);
+ }
+
+ return $result;
+ }
+
+ /**
+ *
+ */
+ public function multiply(GenericGFPoly $other):self{
+
+ if($this->isZero() || $other->isZero()){
+ return new self([0]);
+ }
+
+ $product = array_fill(0, (count($this->coefficients) + count($other->coefficients) - 1), 0);
+
+ foreach($this->coefficients as $i => $aCoeff){
+ foreach($other->coefficients as $j => $bCoeff){
+ $product[($i + $j)] ^= GF256::multiply($aCoeff, $bCoeff);
+ }
+ }
+
+ return new self($product);
+ }
+
+ /**
+ * @return \chillerlan\QRCode\Common\GenericGFPoly[] [quotient, remainder]
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function divide(GenericGFPoly $other):array{
+
+ if($other->isZero()){
+ throw new QRCodeException('Division by 0');
+ }
+
+ $quotient = new self([0]);
+ $remainder = clone $this;
+
+ $denominatorLeadingTerm = $other->getCoefficient($other->getDegree());
+ $inverseDenominatorLeadingTerm = GF256::inverse($denominatorLeadingTerm);
+
+ while($remainder->getDegree() >= $other->getDegree() && !$remainder->isZero()){
+ $scale = GF256::multiply($remainder->getCoefficient($remainder->getDegree()), $inverseDenominatorLeadingTerm);
+ $diff = ($remainder->getDegree() - $other->getDegree());
+ $quotient = $quotient->addOrSubtract(GF256::buildMonomial($diff, $scale));
+ $remainder = $remainder->addOrSubtract($other->multiplyByMonomial($diff, $scale));
+ }
+
+ return [$quotient, $remainder];
+
+ }
+
+ /**
+ *
+ */
+ public function multiplyInt(int $scalar):self{
+
+ if($scalar === 0){
+ return new self([0]);
+ }
+
+ if($scalar === 1){
+ return $this;
+ }
+
+ $product = array_fill(0, count($this->coefficients), 0);
+
+ foreach($this->coefficients as $i => $c){
+ $product[$i] = GF256::multiply($c, $scalar);
+ }
+
+ return new self($product);
+ }
+
+ /**
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function multiplyByMonomial(int $degree, int $coefficient):self{
+
+ if($degree < 0){
+ throw new QRCodeException('degree < 0');
+ }
+
+ if($coefficient === 0){
+ return new self([0]);
+ }
+
+ $product = array_fill(0, (count($this->coefficients) + $degree), 0);
+
+ foreach($this->coefficients as $i => $c){
+ $product[$i] = GF256::multiply($c, $coefficient);
+ }
+
+ return new self($product);
+ }
+
+ /**
+ *
+ */
+ public function mod(GenericGFPoly $other):self{
+
+ if((count($this->coefficients) - count($other->coefficients)) < 0){
+ return $this;
+ }
+
+ $ratio = (GF256::log($this->coefficients[0]) - GF256::log($other->coefficients[0]));
+
+ foreach($other->coefficients as $i => $c){
+ $this->coefficients[$i] ^= GF256::exp(GF256::log($c) + $ratio);
+ }
+
+ return (new self($this->coefficients))->mod($other);
+ }
+
+ /**
+ *
+ */
+ public function addOrSubtract(GenericGFPoly $other):self{
+
+ if($this->isZero()){
+ return $other;
+ }
+
+ if($other->isZero()){
+ return $this;
+ }
+
+ $smallerCoefficients = $this->coefficients;
+ $largerCoefficients = $other->coefficients;
+
+ if(count($smallerCoefficients) > count($largerCoefficients)){
+ $temp = $smallerCoefficients;
+ $smallerCoefficients = $largerCoefficients;
+ $largerCoefficients = $temp;
+ }
+
+ $sumDiff = array_fill(0, count($largerCoefficients), 0);
+ $lengthDiff = (count($largerCoefficients) - count($smallerCoefficients));
+ // Copy high-order terms only found in higher-degree polynomial's coefficients
+ array_splice($sumDiff, 0, $lengthDiff, array_slice($largerCoefficients, 0, $lengthDiff));
+
+ $countLargerCoefficients = count($largerCoefficients);
+
+ for($i = $lengthDiff; $i < $countLargerCoefficients; $i++){
+ $sumDiff[$i] = GF256::addOrSubtract($smallerCoefficients[($i - $lengthDiff)], $largerCoefficients[$i]);
+ }
+
+ return new self($sumDiff);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/IMagickLuminanceSource.php b/vendor/chillerlan/php-qrcode/src/Common/IMagickLuminanceSource.php
new file mode 100644
index 000000000..ade994a78
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/IMagickLuminanceSource.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * Class IMagickLuminanceSource
+ *
+ * @created 17.01.2021
+ * @author Ashot Khanamiryan
+ * @author Smiley <smiley@chillerlan.net>
+ * @copyright 2021 Smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\Settings\SettingsContainerInterface;
+use Imagick;
+use function count;
+
+/**
+ * This class is used to help decode images from files which arrive as Imagick Resource
+ * It does not support rotation.
+ */
+class IMagickLuminanceSource extends LuminanceSourceAbstract{
+
+ protected Imagick $imagick;
+
+ /**
+ * IMagickLuminanceSource constructor.
+ */
+ public function __construct(Imagick $imagick, ?SettingsContainerInterface $options = null){
+ parent::__construct($imagick->getImageWidth(), $imagick->getImageHeight(), $options);
+
+ $this->imagick = $imagick;
+
+ if($this->options->readerGrayscale){
+ $this->imagick->setImageColorspace(Imagick::COLORSPACE_GRAY);
+ }
+
+ if($this->options->readerInvertColors){
+ $this->imagick->negateImage($this->options->readerGrayscale);
+ }
+
+ if($this->options->readerIncreaseContrast){
+ for($i = 0; $i < 10; $i++){
+ $this->imagick->contrastImage(false); // misleading docs
+ }
+ }
+
+ $this->setLuminancePixels();
+ }
+
+ /**
+ *
+ */
+ protected function setLuminancePixels():void{
+ $pixels = $this->imagick->exportImagePixels(1, 1, $this->width, $this->height, 'RGB', Imagick::PIXEL_CHAR);
+ $count = count($pixels);
+
+ for($i = 0; $i < $count; $i += 3){
+ $this->setLuminancePixel(($pixels[$i] & 0xff), ($pixels[($i + 1)] & 0xff), ($pixels[($i + 2)] & 0xff));
+ }
+ }
+
+ /** @inheritDoc */
+ public static function fromFile(string $path, ?SettingsContainerInterface $options = null):self{
+ return new self(new Imagick(self::checkFile($path)), $options);
+ }
+
+ /** @inheritDoc */
+ public static function fromBlob(string $blob, ?SettingsContainerInterface $options = null):self{
+ $im = new Imagick;
+ $im->readImageBlob($blob);
+
+ return new self($im, $options);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceAbstract.php b/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceAbstract.php
new file mode 100644
index 000000000..e4373b87d
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceAbstract.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Class LuminanceSourceAbstract
+ *
+ * @created 24.01.2021
+ * @author ZXing Authors
+ * @author Ashot Khanamiryan
+ * @author Smiley <smiley@chillerlan.net>
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\Decoder\QRCodeDecoderException;
+use chillerlan\QRCode\QROptions;
+use chillerlan\Settings\SettingsContainerInterface;
+use function array_slice, array_splice, file_exists, is_file, is_readable, realpath;
+
+/**
+ * The purpose of this class hierarchy is to abstract different bitmap implementations across
+ * platforms into a standard interface for requesting greyscale luminance values.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
+
+ /** @var \chillerlan\QRCode\QROptions|\chillerlan\Settings\SettingsContainerInterface */
+ protected SettingsContainerInterface $options;
+ protected array $luminances;
+ protected int $width;
+ protected int $height;
+
+ /**
+ *
+ */
+ public function __construct(int $width, int $height, ?SettingsContainerInterface $options = null){
+ $this->width = $width;
+ $this->height = $height;
+ $this->options = ($options ?? new QROptions);
+
+ $this->luminances = [];
+ }
+
+ /** @inheritDoc */
+ public function getLuminances():array{
+ return $this->luminances;
+ }
+
+ /** @inheritDoc */
+ public function getWidth():int{
+ return $this->width;
+ }
+
+ /** @inheritDoc */
+ public function getHeight():int{
+ return $this->height;
+ }
+
+ /**
+ * @inheritDoc
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ public function getRow(int $y):array{
+
+ if($y < 0 || $y >= $this->getHeight()){
+ throw new QRCodeDecoderException('Requested row is outside the image: '.$y);
+ }
+
+ $arr = [];
+
+ array_splice($arr, 0, $this->width, array_slice($this->luminances, ($y * $this->width), $this->width));
+
+ return $arr;
+ }
+
+ /**
+ *
+ */
+ protected function setLuminancePixel(int $r, int $g, int $b):void{
+ $this->luminances[] = ($r === $g && $g === $b)
+ // Image is already greyscale, so pick any channel.
+ ? $r // (($r + 128) % 256) - 128;
+ // Calculate luminance cheaply, favoring green.
+ : (($r + 2 * $g + $b) / 4); // (((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
+ }
+
+ /**
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ protected static function checkFile(string $path):string{
+ $path = trim($path);
+
+ if(!file_exists($path) || !is_file($path) || !is_readable($path)){
+ throw new QRCodeDecoderException('invalid file: '.$path);
+ }
+
+ $realpath = realpath($path);
+
+ if($realpath === false){
+ throw new QRCodeDecoderException('unable to resolve path: '.$path);
+ }
+
+ return $realpath;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceInterface.php b/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceInterface.php
new file mode 100644
index 000000000..64409e36a
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceInterface.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Interface LuminanceSourceInterface
+ *
+ * @created 18.11.2021
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2021 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Common;
+
+/**
+ */
+interface LuminanceSourceInterface{
+
+ /**
+ * Fetches luminance data for the underlying bitmap. Values should be fetched using:
+ * `int luminance = array[y * width + x] & 0xff`
+ *
+ * @return array A row-major 2D array of luminance values. Do not use result $length as it may be
+ * larger than $width * $height bytes on some platforms. Do not modify the contents
+ * of the result.
+ */
+ public function getLuminances():array;
+
+ /**
+ * @return int The width of the bitmap.
+ */
+ public function getWidth():int;
+
+ /**
+ * @return int The height of the bitmap.
+ */
+ public function getHeight():int;
+
+ /**
+ * Fetches one row of luminance data from the underlying platform's bitmap. Values range from
+ * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
+ * to bitwise and with 0xff for each value. It is preferable for implementations of this method
+ * to only fetch this row rather than the whole image, since no 2D Readers may be installed and
+ * getLuminances() may never be called.
+ *
+ * @param int $y The row to fetch, which must be in [0,getHeight())
+ *
+ * @return array An array containing the luminance data.
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ public function getRow(int $y):array;
+
+ /**
+ * Creates a LuminanceSource instance from the given file
+ */
+ public static function fromFile(string $path):self;
+
+ /**
+ * Creates a LuminanceSource instance from the given data blob
+ */
+ public static function fromBlob(string $blob):self;
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/MaskPattern.php b/vendor/chillerlan/php-qrcode/src/Common/MaskPattern.php
new file mode 100644
index 000000000..5c3ea93c1
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/MaskPattern.php
@@ -0,0 +1,329 @@
+<?php
+/**
+ * Class MaskPattern
+ *
+ * @created 19.01.2021
+ * @author ZXing Authors
+ * @author Smiley <smiley@chillerlan.net>
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+use chillerlan\QRCode\Data\QRMatrix;
+use Closure;
+use function abs, array_column, array_search, intdiv, min;
+
+/**
+ * ISO/IEC 18004:2000 Section 8.8.1
+ * ISO/IEC 18004:2000 Section 8.8.2 - Evaluation of masking results
+ *
+ * @see http://www.thonky.com/qr-code-tutorial/data-masking
+ * @see https://github.com/zxing/zxing/blob/e9e2bd280bcaeabd59d0f955798384fe6c018a6c/core/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java
+ */
+final class MaskPattern{
+
+ /**
+ * @see \chillerlan\QRCode\QROptionsTrait::$maskPattern
+ *
+ * @var int
+ */
+ public const AUTO = -1;
+
+ public const PATTERN_000 = 0b000;
+ public const PATTERN_001 = 0b001;
+ public const PATTERN_010 = 0b010;
+ public const PATTERN_011 = 0b011;
+ public const PATTERN_100 = 0b100;
+ public const PATTERN_101 = 0b101;
+ public const PATTERN_110 = 0b110;
+ public const PATTERN_111 = 0b111;
+
+ /**
+ * @var int[]
+ */
+ public const PATTERNS = [
+ self::PATTERN_000,
+ self::PATTERN_001,
+ self::PATTERN_010,
+ self::PATTERN_011,
+ self::PATTERN_100,
+ self::PATTERN_101,
+ self::PATTERN_110,
+ self::PATTERN_111,
+ ];
+
+ /*
+ * Penalty scores
+ *
+ * ISO/IEC 18004:2000 Section 8.8.1 - Table 24
+ */
+ private const PENALTY_N1 = 3;
+ private const PENALTY_N2 = 3;
+ private const PENALTY_N3 = 40;
+ private const PENALTY_N4 = 10;
+
+ /**
+ * The current mask pattern value (0-7)
+ */
+ private int $maskPattern;
+
+ /**
+ * MaskPattern constructor.
+ *
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function __construct(int $maskPattern){
+
+ if(($maskPattern & 0b111) !== $maskPattern){
+ throw new QRCodeException('invalid mask pattern');
+ }
+
+ $this->maskPattern = $maskPattern;
+ }
+
+ /**
+ * Returns the current mask pattern
+ */
+ public function getPattern():int{
+ return $this->maskPattern;
+ }
+
+ /**
+ * Returns a closure that applies the mask for the chosen mask pattern.
+ *
+ * Note that the diagram in section 6.8.1 is misleading since it indicates that $i is column position
+ * and $j is row position. In fact, as the text says, $i is row position and $j is column position.
+ *
+ * @see https://www.thonky.com/qr-code-tutorial/mask-patterns
+ * @see https://github.com/zxing/zxing/blob/e9e2bd280bcaeabd59d0f955798384fe6c018a6c/core/src/main/java/com/google/zxing/qrcode/decoder/DataMask.java#L32-L117
+ */
+ public function getMask():Closure{
+ // $x = column (width), $y = row (height)
+ return [
+ self::PATTERN_000 => fn(int $x, int $y):bool => (($x + $y) % 2) === 0,
+ self::PATTERN_001 => fn(int $x, int $y):bool => ($y % 2) === 0,
+ self::PATTERN_010 => fn(int $x, int $y):bool => ($x % 3) === 0,
+ self::PATTERN_011 => fn(int $x, int $y):bool => (($x + $y) % 3) === 0,
+ self::PATTERN_100 => fn(int $x, int $y):bool => ((intdiv($y, 2) + intdiv($x, 3)) % 2) === 0,
+ self::PATTERN_101 => fn(int $x, int $y):bool => (($x * $y) % 6) === 0,
+ self::PATTERN_110 => fn(int $x, int $y):bool => (($x * $y) % 6) < 3,
+ self::PATTERN_111 => fn(int $x, int $y):bool => (($x + $y + (($x * $y) % 3)) % 2) === 0,
+ ][$this->maskPattern];
+ }
+
+ /**
+ * Evaluates the matrix of the given data interface and returns a new mask pattern instance for the best result
+ */
+ public static function getBestPattern(QRMatrix $QRMatrix):self{
+ $penalties = [];
+ $size = $QRMatrix->getSize();
+
+ foreach(self::PATTERNS as $pattern){
+ $mp = new self($pattern);
+ $matrix = (clone $QRMatrix)->setFormatInfo($mp)->mask($mp)->getMatrix(true);
+ $penalty = 0;
+
+ for($level = 1; $level <= 4; $level++){
+ $penalty += self::{'testRule'.$level}($matrix, $size, $size);
+ }
+
+ $penalties[$pattern] = (int)$penalty;
+ }
+
+ return new self(array_search(min($penalties), $penalties, true));
+ }
+
+ /**
+ * Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and
+ * give penalty to them. Example: 00000 or 11111.
+ */
+ public static function testRule1(array $matrix, int $height, int $width):int{
+ $penalty = 0;
+
+ // horizontal
+ foreach($matrix as $row){
+ $penalty += self::applyRule1($row);
+ }
+
+ // vertical
+ for($x = 0; $x < $width; $x++){
+ $penalty += self::applyRule1(array_column($matrix, $x));
+ }
+
+ return $penalty;
+ }
+
+ /**
+ *
+ */
+ private static function applyRule1(array $rc):int{
+ $penalty = 0;
+ $numSameBitCells = 0;
+ $prevBit = null;
+
+ foreach($rc as $val){
+
+ if($val === $prevBit){
+ $numSameBitCells++;
+ }
+ else{
+
+ if($numSameBitCells >= 5){
+ $penalty += (self::PENALTY_N1 + $numSameBitCells - 5);
+ }
+
+ $numSameBitCells = 1; // Include the cell itself.
+ $prevBit = $val;
+ }
+ }
+
+ if($numSameBitCells >= 5){
+ $penalty += (self::PENALTY_N1 + $numSameBitCells - 5);
+ }
+
+ return $penalty;
+ }
+
+ /**
+ * Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
+ * penalty to them. This is actually equivalent to the spec's rule, which is to find MxN blocks and give a
+ * penalty proportional to (M-1)x(N-1), because this is the number of 2x2 blocks inside such a block.
+ */
+ public static function testRule2(array $matrix, int $height, int $width):int{
+ $penalty = 0;
+
+ foreach($matrix as $y => $row){
+
+ if($y > ($height - 2)){
+ break;
+ }
+
+ foreach($row as $x => $val){
+
+ if($x > ($width - 2)){
+ break;
+ }
+
+ if(
+ $val === $row[($x + 1)]
+ && $val === $matrix[($y + 1)][$x]
+ && $val === $matrix[($y + 1)][($x + 1)]
+ ){
+ $penalty++;
+ }
+ }
+ }
+
+ return (self::PENALTY_N2 * $penalty);
+ }
+
+ /**
+ * Apply mask penalty rule 3 and return the penalty. Find consecutive runs of 1:1:3:1:1:4
+ * starting with black, or 4:1:1:3:1:1 starting with white, and give penalty to them. If we
+ * find patterns like 000010111010000, we give penalty once.
+ */
+ public static function testRule3(array $matrix, int $height, int $width):int{
+ $penalties = 0;
+
+ foreach($matrix as $y => $row){
+ foreach($row as $x => $val){
+
+ if(
+ ($x + 6) < $width
+ && $val
+ && !$row[($x + 1)]
+ && $row[($x + 2)]
+ && $row[($x + 3)]
+ && $row[($x + 4)]
+ && !$row[($x + 5)]
+ && $row[($x + 6)]
+ && (
+ self::isWhiteHorizontal($row, $width, ($x - 4), $x)
+ || self::isWhiteHorizontal($row, $width, ($x + 7), ($x + 11))
+ )
+ ){
+ $penalties++;
+ }
+
+ if(
+ ($y + 6) < $height
+ && $val
+ && !$matrix[($y + 1)][$x]
+ && $matrix[($y + 2)][$x]
+ && $matrix[($y + 3)][$x]
+ && $matrix[($y + 4)][$x]
+ && !$matrix[($y + 5)][$x]
+ && $matrix[($y + 6)][$x]
+ && (
+ self::isWhiteVertical($matrix, $height, $x, ($y - 4), $y)
+ || self::isWhiteVertical($matrix, $height, $x, ($y + 7), ($y + 11))
+ )
+ ){
+ $penalties++;
+ }
+
+ }
+ }
+
+ return ($penalties * self::PENALTY_N3);
+ }
+
+ /**
+ *
+ */
+ private static function isWhiteHorizontal(array $row, int $width, int $from, int $to):bool{
+
+ if($from < 0 || $width < $to){
+ return false;
+ }
+
+ for($x = $from; $x < $to; $x++){
+ if($row[$x]){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ *
+ */
+ private static function isWhiteVertical(array $matrix, int $height, int $x, int $from, int $to):bool{
+
+ if($from < 0 || $height < $to){
+ return false;
+ }
+
+ for($y = $from; $y < $to; $y++){
+ if($matrix[$y][$x] === true){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
+ * penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance.
+ */
+ public static function testRule4(array $matrix, int $height, int $width):int{
+ $darkCells = 0;
+ $totalCells = ($height * $width);
+
+ foreach($matrix as $row){
+ foreach($row as $val){
+ if($val === true){
+ $darkCells++;
+ }
+ }
+ }
+
+ return (intdiv((abs($darkCells * 2 - $totalCells) * 10), $totalCells) * self::PENALTY_N4);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/Mode.php b/vendor/chillerlan/php-qrcode/src/Common/Mode.php
new file mode 100644
index 000000000..523d37919
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/Mode.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Class Mode
+ *
+ * @created 19.11.2020
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\Data\{AlphaNum, Byte, Hanzi, Kanji, Number};
+use chillerlan\QRCode\QRCodeException;
+
+/**
+ * Data mode information - ISO 18004:2006, 6.4.1, Tables 2 and 3
+ */
+final class Mode{
+
+ // ISO/IEC 18004:2000 Table 2
+
+ /** @var int */
+ public const TERMINATOR = 0b0000;
+ /** @var int */
+ public const NUMBER = 0b0001;
+ /** @var int */
+ public const ALPHANUM = 0b0010;
+ /** @var int */
+ public const BYTE = 0b0100;
+ /** @var int */
+ public const KANJI = 0b1000;
+ /** @var int */
+ public const HANZI = 0b1101;
+ /** @var int */
+ public const STRCTURED_APPEND = 0b0011;
+ /** @var int */
+ public const FNC1_FIRST = 0b0101;
+ /** @var int */
+ public const FNC1_SECOND = 0b1001;
+ /** @var int */
+ public const ECI = 0b0111;
+
+ /**
+ * 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
+ */
+ public const LENGTH_BITS = [
+ self::NUMBER => [10, 12, 14],
+ self::ALPHANUM => [ 9, 11, 13],
+ self::BYTE => [ 8, 16, 16],
+ self::KANJI => [ 8, 10, 12],
+ self::HANZI => [ 8, 10, 12],
+ self::ECI => [ 0, 0, 0],
+ ];
+
+ /**
+ * Map of data mode => interface (detection order)
+ *
+ * @var string[]
+ */
+ public const INTERFACES = [
+ self::NUMBER => Number::class,
+ self::ALPHANUM => AlphaNum::class,
+ self::KANJI => Kanji::class,
+ self::HANZI => Hanzi::class,
+ self::BYTE => Byte::class,
+ ];
+
+ /**
+ * returns the length bits for the version breakpoints 1-9, 10-26 and 27-40
+ *
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public static function getLengthBitsForVersion(int $mode, int $version):int{
+
+ if(!isset(self::LENGTH_BITS[$mode])){
+ throw new QRCodeException('invalid mode given');
+ }
+
+ $minVersion = 0;
+
+ foreach([9, 26, 40] as $key => $breakpoint){
+
+ if($version > $minVersion && $version <= $breakpoint){
+ return self::LENGTH_BITS[$mode][$key];
+ }
+
+ $minVersion = $breakpoint;
+ }
+
+ throw new QRCodeException(sprintf('invalid version number: %d', $version));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/Version.php b/vendor/chillerlan/php-qrcode/src/Common/Version.php
new file mode 100644
index 000000000..fe7240f8a
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/Version.php
@@ -0,0 +1,287 @@
+<?php
+/**
+ * Class Version
+ *
+ * @created 19.11.2020
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+
+/**
+ * Version related tables and methods
+ */
+final class Version{
+
+ /**
+ * Enable version auto detection
+ *
+ * @see \chillerlan\QRCode\QROptionsTrait::$version
+ *
+ * @var int
+ */
+ public const AUTO = -1;
+
+ /**
+ * ISO/IEC 18004:2000 Annex E, Table E.1 - Row/column coordinates of center module of Alignment Patterns
+ *
+ * version -> pattern
+ *
+ * @var int[][]
+ */
+ private const ALIGNMENT_PATTERN = [
+ 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[]
+ */
+ private const VERSION_PATTERN = [
+ 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,
+ ];
+
+ /**
+ * ISO/IEC 18004:2000 Tables 13-22 - Error correction characteristics
+ *
+ * @see http://www.thonky.com/qr-code-tutorial/error-correction-table
+ */
+ private const RSBLOCKS = [
+ 1 => [[ 7, [[ 1, 19], [ 0, 0]]], [10, [[ 1, 16], [ 0, 0]]], [13, [[ 1, 13], [ 0, 0]]], [17, [[ 1, 9], [ 0, 0]]]],
+ 2 => [[10, [[ 1, 34], [ 0, 0]]], [16, [[ 1, 28], [ 0, 0]]], [22, [[ 1, 22], [ 0, 0]]], [28, [[ 1, 16], [ 0, 0]]]],
+ 3 => [[15, [[ 1, 55], [ 0, 0]]], [26, [[ 1, 44], [ 0, 0]]], [18, [[ 2, 17], [ 0, 0]]], [22, [[ 2, 13], [ 0, 0]]]],
+ 4 => [[20, [[ 1, 80], [ 0, 0]]], [18, [[ 2, 32], [ 0, 0]]], [26, [[ 2, 24], [ 0, 0]]], [16, [[ 4, 9], [ 0, 0]]]],
+ 5 => [[26, [[ 1, 108], [ 0, 0]]], [24, [[ 2, 43], [ 0, 0]]], [18, [[ 2, 15], [ 2, 16]]], [22, [[ 2, 11], [ 2, 12]]]],
+ 6 => [[18, [[ 2, 68], [ 0, 0]]], [16, [[ 4, 27], [ 0, 0]]], [24, [[ 4, 19], [ 0, 0]]], [28, [[ 4, 15], [ 0, 0]]]],
+ 7 => [[20, [[ 2, 78], [ 0, 0]]], [18, [[ 4, 31], [ 0, 0]]], [18, [[ 2, 14], [ 4, 15]]], [26, [[ 4, 13], [ 1, 14]]]],
+ 8 => [[24, [[ 2, 97], [ 0, 0]]], [22, [[ 2, 38], [ 2, 39]]], [22, [[ 4, 18], [ 2, 19]]], [26, [[ 4, 14], [ 2, 15]]]],
+ 9 => [[30, [[ 2, 116], [ 0, 0]]], [22, [[ 3, 36], [ 2, 37]]], [20, [[ 4, 16], [ 4, 17]]], [24, [[ 4, 12], [ 4, 13]]]],
+ 10 => [[18, [[ 2, 68], [ 2, 69]]], [26, [[ 4, 43], [ 1, 44]]], [24, [[ 6, 19], [ 2, 20]]], [28, [[ 6, 15], [ 2, 16]]]],
+ 11 => [[20, [[ 4, 81], [ 0, 0]]], [30, [[ 1, 50], [ 4, 51]]], [28, [[ 4, 22], [ 4, 23]]], [24, [[ 3, 12], [ 8, 13]]]],
+ 12 => [[24, [[ 2, 92], [ 2, 93]]], [22, [[ 6, 36], [ 2, 37]]], [26, [[ 4, 20], [ 6, 21]]], [28, [[ 7, 14], [ 4, 15]]]],
+ 13 => [[26, [[ 4, 107], [ 0, 0]]], [22, [[ 8, 37], [ 1, 38]]], [24, [[ 8, 20], [ 4, 21]]], [22, [[12, 11], [ 4, 12]]]],
+ 14 => [[30, [[ 3, 115], [ 1, 116]]], [24, [[ 4, 40], [ 5, 41]]], [20, [[11, 16], [ 5, 17]]], [24, [[11, 12], [ 5, 13]]]],
+ 15 => [[22, [[ 5, 87], [ 1, 88]]], [24, [[ 5, 41], [ 5, 42]]], [30, [[ 5, 24], [ 7, 25]]], [24, [[11, 12], [ 7, 13]]]],
+ 16 => [[24, [[ 5, 98], [ 1, 99]]], [28, [[ 7, 45], [ 3, 46]]], [24, [[15, 19], [ 2, 20]]], [30, [[ 3, 15], [13, 16]]]],
+ 17 => [[28, [[ 1, 107], [ 5, 108]]], [28, [[10, 46], [ 1, 47]]], [28, [[ 1, 22], [15, 23]]], [28, [[ 2, 14], [17, 15]]]],
+ 18 => [[30, [[ 5, 120], [ 1, 121]]], [26, [[ 9, 43], [ 4, 44]]], [28, [[17, 22], [ 1, 23]]], [28, [[ 2, 14], [19, 15]]]],
+ 19 => [[28, [[ 3, 113], [ 4, 114]]], [26, [[ 3, 44], [11, 45]]], [26, [[17, 21], [ 4, 22]]], [26, [[ 9, 13], [16, 14]]]],
+ 20 => [[28, [[ 3, 107], [ 5, 108]]], [26, [[ 3, 41], [13, 42]]], [30, [[15, 24], [ 5, 25]]], [28, [[15, 15], [10, 16]]]],
+ 21 => [[28, [[ 4, 116], [ 4, 117]]], [26, [[17, 42], [ 0, 0]]], [28, [[17, 22], [ 6, 23]]], [30, [[19, 16], [ 6, 17]]]],
+ 22 => [[28, [[ 2, 111], [ 7, 112]]], [28, [[17, 46], [ 0, 0]]], [30, [[ 7, 24], [16, 25]]], [24, [[34, 13], [ 0, 0]]]],
+ 23 => [[30, [[ 4, 121], [ 5, 122]]], [28, [[ 4, 47], [14, 48]]], [30, [[11, 24], [14, 25]]], [30, [[16, 15], [14, 16]]]],
+ 24 => [[30, [[ 6, 117], [ 4, 118]]], [28, [[ 6, 45], [14, 46]]], [30, [[11, 24], [16, 25]]], [30, [[30, 16], [ 2, 17]]]],
+ 25 => [[26, [[ 8, 106], [ 4, 107]]], [28, [[ 8, 47], [13, 48]]], [30, [[ 7, 24], [22, 25]]], [30, [[22, 15], [13, 16]]]],
+ 26 => [[28, [[10, 114], [ 2, 115]]], [28, [[19, 46], [ 4, 47]]], [28, [[28, 22], [ 6, 23]]], [30, [[33, 16], [ 4, 17]]]],
+ 27 => [[30, [[ 8, 122], [ 4, 123]]], [28, [[22, 45], [ 3, 46]]], [30, [[ 8, 23], [26, 24]]], [30, [[12, 15], [28, 16]]]],
+ 28 => [[30, [[ 3, 117], [10, 118]]], [28, [[ 3, 45], [23, 46]]], [30, [[ 4, 24], [31, 25]]], [30, [[11, 15], [31, 16]]]],
+ 29 => [[30, [[ 7, 116], [ 7, 117]]], [28, [[21, 45], [ 7, 46]]], [30, [[ 1, 23], [37, 24]]], [30, [[19, 15], [26, 16]]]],
+ 30 => [[30, [[ 5, 115], [10, 116]]], [28, [[19, 47], [10, 48]]], [30, [[15, 24], [25, 25]]], [30, [[23, 15], [25, 16]]]],
+ 31 => [[30, [[13, 115], [ 3, 116]]], [28, [[ 2, 46], [29, 47]]], [30, [[42, 24], [ 1, 25]]], [30, [[23, 15], [28, 16]]]],
+ 32 => [[30, [[17, 115], [ 0, 0]]], [28, [[10, 46], [23, 47]]], [30, [[10, 24], [35, 25]]], [30, [[19, 15], [35, 16]]]],
+ 33 => [[30, [[17, 115], [ 1, 116]]], [28, [[14, 46], [21, 47]]], [30, [[29, 24], [19, 25]]], [30, [[11, 15], [46, 16]]]],
+ 34 => [[30, [[13, 115], [ 6, 116]]], [28, [[14, 46], [23, 47]]], [30, [[44, 24], [ 7, 25]]], [30, [[59, 16], [ 1, 17]]]],
+ 35 => [[30, [[12, 121], [ 7, 122]]], [28, [[12, 47], [26, 48]]], [30, [[39, 24], [14, 25]]], [30, [[22, 15], [41, 16]]]],
+ 36 => [[30, [[ 6, 121], [14, 122]]], [28, [[ 6, 47], [34, 48]]], [30, [[46, 24], [10, 25]]], [30, [[ 2, 15], [64, 16]]]],
+ 37 => [[30, [[17, 122], [ 4, 123]]], [28, [[29, 46], [14, 47]]], [30, [[49, 24], [10, 25]]], [30, [[24, 15], [46, 16]]]],
+ 38 => [[30, [[ 4, 122], [18, 123]]], [28, [[13, 46], [32, 47]]], [30, [[48, 24], [14, 25]]], [30, [[42, 15], [32, 16]]]],
+ 39 => [[30, [[20, 117], [ 4, 118]]], [28, [[40, 47], [ 7, 48]]], [30, [[43, 24], [22, 25]]], [30, [[10, 15], [67, 16]]]],
+ 40 => [[30, [[19, 118], [ 6, 119]]], [28, [[18, 47], [31, 48]]], [30, [[34, 24], [34, 25]]], [30, [[20, 15], [61, 16]]]],
+ ];
+
+ /**
+ * ISO/IEC 18004:2000 Table 1 - Data capacity of all versions of QR Code
+ */
+ private const TOTAL_CODEWORDS = [
+ 1 => 26,
+ 2 => 44,
+ 3 => 70,
+ 4 => 100,
+ 5 => 134,
+ 6 => 172,
+ 7 => 196,
+ 8 => 242,
+ 9 => 292,
+ 10 => 346,
+ 11 => 404,
+ 12 => 466,
+ 13 => 532,
+ 14 => 581,
+ 15 => 655,
+ 16 => 733,
+ 17 => 815,
+ 18 => 901,
+ 19 => 991,
+ 20 => 1085,
+ 21 => 1156,
+ 22 => 1258,
+ 23 => 1364,
+ 24 => 1474,
+ 25 => 1588,
+ 26 => 1706,
+ 27 => 1828,
+ 28 => 1921,
+ 29 => 2051,
+ 30 => 2185,
+ 31 => 2323,
+ 32 => 2465,
+ 33 => 2611,
+ 34 => 2761,
+ 35 => 2876,
+ 36 => 3034,
+ 37 => 3196,
+ 38 => 3362,
+ 39 => 3532,
+ 40 => 3706,
+ ];
+
+ /**
+ * QR Code version number
+ */
+ private int $version;
+
+ /**
+ * Version constructor.
+ *
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function __construct(int $version){
+
+ if($version < 1 || $version > 40){
+ throw new QRCodeException('invalid version given');
+ }
+
+ $this->version = $version;
+ }
+
+ /**
+ * returns the current version number as string
+ */
+ public function __toString():string{
+ return (string)$this->version;
+ }
+
+ /**
+ * returns the current version number
+ */
+ public function getVersionNumber():int{
+ return $this->version;
+ }
+
+ /**
+ * the matrix size for the given version
+ */
+ public function getDimension():int{
+ return (($this->version * 4) + 17);
+ }
+
+ /**
+ * the version pattern for the given version
+ */
+ public function getVersionPattern():?int{
+ return (self::VERSION_PATTERN[$this->version] ?? null);
+ }
+
+ /**
+ * the alignment patterns for the current version
+ *
+ * @return int[]
+ */
+ public function getAlignmentPattern():array{
+ return self::ALIGNMENT_PATTERN[$this->version];
+ }
+
+ /**
+ * returns ECC block information for the given $version and $eccLevel
+ */
+ public function getRSBlocks(EccLevel $eccLevel):array{
+ return self::RSBLOCKS[$this->version][$eccLevel->getOrdinal()];
+ }
+
+ /**
+ * returns the maximum codewords for the current version
+ */
+ public function getTotalCodewords():int{
+ return self::TOTAL_CODEWORDS[$this->version];
+ }
+
+}