diff options
Diffstat (limited to 'vendor/chillerlan/php-qrcode/src/Common')
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]; + } + +} |