diff options
author | Mario <mario@mariovavti.com> | 2023-03-19 13:55:18 +0000 |
---|---|---|
committer | Mario <mario@mariovavti.com> | 2023-03-19 13:55:18 +0000 |
commit | 89285f1408d21091bb80d45b391ddcbe06ba8d0f (patch) | |
tree | b2eb07d9f3d91d77f89a4565a58e6e5231b20c1c /vendor/chillerlan/php-qrcode/src | |
parent | 0a679e503ef367eda3085c44af103ee53869a94f (diff) | |
parent | 17c0bb2069dcfe35d3febc5bfdb3a7295f15d49c (diff) | |
download | volse-hubzilla-89285f1408d21091bb80d45b391ddcbe06ba8d0f.tar.gz volse-hubzilla-89285f1408d21091bb80d45b391ddcbe06ba8d0f.tar.bz2 volse-hubzilla-89285f1408d21091bb80d45b391ddcbe06ba8d0f.zip |
Merge branch '8.2RC'8.2
Diffstat (limited to 'vendor/chillerlan/php-qrcode/src')
23 files changed, 3601 insertions, 0 deletions
diff --git a/vendor/chillerlan/php-qrcode/src/Data/AlphaNum.php b/vendor/chillerlan/php-qrcode/src/Data/AlphaNum.php new file mode 100644 index 000000000..28d9d7563 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Data/AlphaNum.php @@ -0,0 +1,60 @@ +<?php +/** + * Class AlphaNum + * + * @filesource AlphaNum.php + * @created 25.11.2015 + * @package chillerlan\QRCode\Data + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Data; + +use chillerlan\QRCode\QRCode; + +use function ord, sprintf; + +/** + * Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / : + * + * ISO/IEC 18004:2000 Section 8.3.3 + * ISO/IEC 18004:2000 Section 8.4.3 + */ +final class AlphaNum extends QRDataAbstract{ + + protected int $datamode = QRCode::DATA_ALPHANUM; + + protected array $lengthBits = [9, 11, 13]; + + /** + * @inheritdoc + */ + protected function write(string $data):void{ + + for($i = 0; $i + 1 < $this->strlen; $i += 2){ + $this->bitBuffer->put($this->getCharCode($data[$i]) * 45 + $this->getCharCode($data[$i + 1]), 11); + } + + if($i < $this->strlen){ + $this->bitBuffer->put($this->getCharCode($data[$i]), 6); + } + + } + + /** + * get the code for the given character + * + * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence + */ + protected function getCharCode(string $chr):int{ + + if(!isset($this::CHAR_MAP_ALPHANUM[$chr])){ + throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, ord($chr))); + } + + return $this::CHAR_MAP_ALPHANUM[$chr]; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Data/Byte.php b/vendor/chillerlan/php-qrcode/src/Data/Byte.php new file mode 100644 index 000000000..02e76a639 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Data/Byte.php @@ -0,0 +1,44 @@ +<?php +/** + * Class Byte + * + * @filesource Byte.php + * @created 25.11.2015 + * @package chillerlan\QRCode\Data + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Data; + +use chillerlan\QRCode\QRCode; + +use function ord; + +/** + * Byte mode, ISO-8859-1 or UTF-8 + * + * ISO/IEC 18004:2000 Section 8.3.4 + * ISO/IEC 18004:2000 Section 8.4.4 + */ +final class Byte extends QRDataAbstract{ + + protected int $datamode = QRCode::DATA_BYTE; + + protected array $lengthBits = [8, 16, 16]; + + /** + * @inheritdoc + */ + protected function write(string $data):void{ + $i = 0; + + while($i < $this->strlen){ + $this->bitBuffer->put(ord($data[$i]), 8); + $i++; + } + + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Data/Kanji.php b/vendor/chillerlan/php-qrcode/src/Data/Kanji.php new file mode 100644 index 000000000..e106c50f1 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Data/Kanji.php @@ -0,0 +1,69 @@ +<?php +/** + * Class Kanji + * + * @filesource Kanji.php + * @created 25.11.2015 + * @package chillerlan\QRCode\Data + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Data; + +use chillerlan\QRCode\QRCode; + +use function mb_strlen, ord, sprintf, strlen; + +/** + * Kanji mode: double-byte characters from the Shift JIS character set + * + * ISO/IEC 18004:2000 Section 8.3.5 + * ISO/IEC 18004:2000 Section 8.4.5 + */ +final class Kanji extends QRDataAbstract{ + + protected int $datamode = QRCode::DATA_KANJI; + + protected array $lengthBits = [8, 10, 12]; + + /** + * @inheritdoc + */ + protected function getLength(string $data):int{ + return mb_strlen($data, 'SJIS'); + } + + /** + * @inheritdoc + * + * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence + */ + protected function write(string $data):void{ + $len = strlen($data); + + for($i = 0; $i + 1 < $len; $i += 2){ + $c = ((0xff & ord($data[$i])) << 8) | (0xff & ord($data[$i + 1])); + + if($c >= 0x8140 && $c <= 0x9FFC){ + $c -= 0x8140; + } + elseif($c >= 0xE040 && $c <= 0xEBBF){ + $c -= 0xC140; + } + else{ + throw new QRCodeDataException(sprintf('illegal char at %d [%d]', $i + 1, $c)); + } + + $this->bitBuffer->put(((($c >> 8) & 0xff) * 0xC0) + ($c & 0xff), 13); + + } + + if($i < $len){ + throw new QRCodeDataException(sprintf('illegal char at %d', $i + 1)); + } + + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Data/MaskPatternTester.php b/vendor/chillerlan/php-qrcode/src/Data/MaskPatternTester.php new file mode 100644 index 000000000..7874cb53d --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Data/MaskPatternTester.php @@ -0,0 +1,203 @@ +<?php +/** + * Class MaskPatternTester + * + * @filesource MaskPatternTester.php + * @created 22.11.2017 + * @package chillerlan\QRCode\Data + * @author Smiley <smiley@chillerlan.net> + * @copyright 2017 Smiley + * @license MIT + * + * @noinspection PhpUnused + */ + +namespace chillerlan\QRCode\Data; + +use function abs, array_search, call_user_func_array, min; + +/** + * Receives a QRDataInterface object and runs the mask pattern tests on it. + * + * ISO/IEC 18004:2000 Section 8.8.2 - Evaluation of masking results + * + * @see http://www.thonky.com/qr-code-tutorial/data-masking + */ +final class MaskPatternTester{ + + /** + * The data interface that contains the data matrix to test + */ + protected QRDataInterface $dataInterface; + + /** + * Receives the QRDataInterface + * + * @see \chillerlan\QRCode\QROptions::$maskPattern + * @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern + */ + public function __construct(QRDataInterface $dataInterface){ + $this->dataInterface = $dataInterface; + } + + /** + * shoves a QRMatrix through the MaskPatternTester to find the lowest penalty mask pattern + * + * @see \chillerlan\QRCode\Data\MaskPatternTester + */ + public function getBestMaskPattern():int{ + $penalties = []; + + for($pattern = 0; $pattern < 8; $pattern++){ + $penalties[$pattern] = $this->testPattern($pattern); + } + + return array_search(min($penalties), $penalties, true); + } + + /** + * Returns the penalty for the given mask pattern + * + * @see \chillerlan\QRCode\QROptions::$maskPattern + * @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern + */ + public function testPattern(int $pattern):int{ + $matrix = $this->dataInterface->initMatrix($pattern, true); + $penalty = 0; + + for($level = 1; $level <= 4; $level++){ + $penalty += call_user_func_array([$this, 'testLevel'.$level], [$matrix->matrix(true), $matrix->size()]); + } + + return (int)$penalty; + } + + /** + * Checks for each group of five or more same-colored modules in a row (or column) + */ + protected function testLevel1(array $m, int $size):int{ + $penalty = 0; + + foreach($m as $y => $row){ + foreach($row as $x => $val){ + $count = 0; + + for($ry = -1; $ry <= 1; $ry++){ + + if($y + $ry < 0 || $size <= $y + $ry){ + continue; + } + + for($rx = -1; $rx <= 1; $rx++){ + + if(($ry === 0 && $rx === 0) || (($x + $rx) < 0 || $size <= ($x + $rx))){ + continue; + } + + if($m[$y + $ry][$x + $rx] === $val){ + $count++; + } + + } + } + + if($count > 5){ + $penalty += (3 + $count - 5); + } + + } + } + + return $penalty; + } + + /** + * Checks for each 2x2 area of same-colored modules in the matrix + */ + protected function testLevel2(array $m, int $size):int{ + $penalty = 0; + + foreach($m as $y => $row){ + + if($y > $size - 2){ + break; + } + + foreach($row as $x => $val){ + + if($x > $size - 2){ + break; + } + + if( + $val === $m[$y][$x + 1] + && $val === $m[$y + 1][$x] + && $val === $m[$y + 1][$x + 1] + ){ + $penalty++; + } + } + } + + return 3 * $penalty; + } + + /** + * Checks if there are patterns that look similar to the finder patterns (1:1:3:1:1 ratio) + */ + protected function testLevel3(array $m, int $size):int{ + $penalties = 0; + + foreach($m as $y => $row){ + foreach($row as $x => $val){ + + if( + $x + 6 < $size + && $val + && !$m[$y][$x + 1] + && $m[$y][$x + 2] + && $m[$y][$x + 3] + && $m[$y][$x + 4] + && !$m[$y][$x + 5] + && $m[$y][$x + 6] + ){ + $penalties++; + } + + if( + $y + 6 < $size + && $val + && !$m[$y + 1][$x] + && $m[$y + 2][$x] + && $m[$y + 3][$x] + && $m[$y + 4][$x] + && !$m[$y + 5][$x] + && $m[$y + 6][$x] + ){ + $penalties++; + } + + } + } + + return $penalties * 40; + } + + /** + * Checks if more than half of the modules are dark or light, with a larger penalty for a larger difference + */ + protected function testLevel4(array $m, int $size):float{ + $count = 0; + + foreach($m as $y => $row){ + foreach($row as $x => $val){ + if($val){ + $count++; + } + } + } + + return (abs(100 * $count / $size / $size - 50) / 5) * 10; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Data/Number.php b/vendor/chillerlan/php-qrcode/src/Data/Number.php new file mode 100644 index 000000000..0a905b13e --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Data/Number.php @@ -0,0 +1,77 @@ +<?php +/** + * Class Number + * + * @filesource Number.php + * @created 26.11.2015 + * @package chillerlan\QRCode\Data + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Data; + +use chillerlan\QRCode\QRCode; + +use function ord, sprintf, str_split, substr; + +/** + * Numeric mode: decimal digits 0 to 9 + * + * ISO/IEC 18004:2000 Section 8.3.2 + * ISO/IEC 18004:2000 Section 8.4.2 + */ +final class Number extends QRDataAbstract{ + + protected int $datamode = QRCode::DATA_NUMBER; + + protected array $lengthBits = [10, 12, 14]; + + /** + * @inheritdoc + */ + protected function write(string $data):void{ + $i = 0; + + while($i + 2 < $this->strlen){ + $this->bitBuffer->put($this->parseInt(substr($data, $i, 3)), 10); + $i += 3; + } + + if($i < $this->strlen){ + + if($this->strlen - $i === 1){ + $this->bitBuffer->put($this->parseInt(substr($data, $i, $i + 1)), 4); + } + elseif($this->strlen - $i === 2){ + $this->bitBuffer->put($this->parseInt(substr($data, $i, $i + 2)), 7); + } + + } + + } + + /** + * get the code for the given numeric string + * + * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence + */ + protected function parseInt(string $string):int{ + $num = 0; + + foreach(str_split($string) as $chr){ + $c = ord($chr); + + if(!isset($this::CHAR_MAP_NUMBER[$chr])){ + throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, $c)); + } + + $c = $c - 48; // ord('0') + $num = $num * 10 + $c; + } + + return $num; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRCodeDataException.php b/vendor/chillerlan/php-qrcode/src/Data/QRCodeDataException.php new file mode 100644 index 000000000..862f57ba0 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Data/QRCodeDataException.php @@ -0,0 +1,17 @@ +<?php +/** + * Class QRCodeDataException + * + * @filesource QRCodeDataException.php + * @created 09.12.2015 + * @package chillerlan\QRCode\Data + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Data; + +use chillerlan\QRCode\QRCodeException; + +class QRCodeDataException extends QRCodeException{} diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRDataAbstract.php b/vendor/chillerlan/php-qrcode/src/Data/QRDataAbstract.php new file mode 100644 index 000000000..72b67b7b9 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Data/QRDataAbstract.php @@ -0,0 +1,311 @@ +<?php +/** + * Class QRDataAbstract + * + * @filesource QRDataAbstract.php + * @created 25.11.2015 + * @package chillerlan\QRCode\Data + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Data; + +use chillerlan\QRCode\QRCode; +use chillerlan\QRCode\Helpers\{BitBuffer, Polynomial}; +use chillerlan\Settings\SettingsContainerInterface; + +use function array_fill, array_merge, count, max, mb_convert_encoding, mb_detect_encoding, range, sprintf, strlen; + +/** + * Processes the binary data and maps it on a matrix which is then being returned + */ +abstract class QRDataAbstract implements QRDataInterface{ + + /** + * the string byte count + */ + protected ?int $strlen = null; + + /** + * the current data mode: Num, Alphanum, Kanji, Byte + */ + protected int $datamode; + + /** + * mode length bits for the version breakpoints 1-9, 10-26 and 27-40 + * + * ISO/IEC 18004:2000 Table 3 - Number of bits in Character Count Indicator + */ + protected array $lengthBits = [0, 0, 0]; + + /** + * current QR Code version + */ + protected int $version; + + /** + * ECC temp data + */ + protected array $ecdata; + + /** + * ECC temp data + */ + protected array $dcdata; + + /** + * the options instance + * + * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions + */ + protected SettingsContainerInterface $options; + + /** + * a BitBuffer instance + */ + protected BitBuffer $bitBuffer; + + /** + * QRDataInterface constructor. + */ + public function __construct(SettingsContainerInterface $options, string $data = null){ + $this->options = $options; + + if($data !== null){ + $this->setData($data); + } + } + + /** + * @inheritDoc + */ + public function setData(string $data):QRDataInterface{ + + if($this->datamode === QRCode::DATA_KANJI){ + $data = mb_convert_encoding($data, 'SJIS', mb_detect_encoding($data)); + } + + $this->strlen = $this->getLength($data); + $this->version = $this->options->version === QRCode::VERSION_AUTO + ? $this->getMinimumVersion() + : $this->options->version; + + $this->writeBitBuffer($data); + + return $this; + } + + /** + * @inheritDoc + */ + public function initMatrix(int $maskPattern, bool $test = null):QRMatrix{ + return (new QRMatrix($this->version, $this->options->eccLevel)) + ->init($maskPattern, $test) + ->mapData($this->maskECC(), $maskPattern) + ; + } + + /** + * returns the length bits for the version breakpoints 1-9, 10-26 and 27-40 + * + * @throws \chillerlan\QRCode\Data\QRCodeDataException + * @codeCoverageIgnore + */ + protected function getLengthBits():int{ + + foreach([9, 26, 40] as $key => $breakpoint){ + if($this->version <= $breakpoint){ + return $this->lengthBits[$key]; + } + } + + throw new QRCodeDataException(sprintf('invalid version number: %d', $this->version)); + } + + /** + * returns the byte count of the $data string + */ + protected function getLength(string $data):int{ + return strlen($data); + } + + /** + * returns the minimum version number for the given string + * + * @throws \chillerlan\QRCode\Data\QRCodeDataException + */ + protected function getMinimumVersion():int{ + $maxlength = 0; + + // guess the version number within the given range + $dataMode = QRCode::DATA_MODES[$this->datamode]; + $eccMode = QRCode::ECC_MODES[$this->options->eccLevel]; + + foreach(range($this->options->versionMin, $this->options->versionMax) as $version){ + $maxlength = $this::MAX_LENGTH[$version][$dataMode][$eccMode]; + + if($this->strlen <= $maxlength){ + return $version; + } + } + + throw new QRCodeDataException(sprintf('data exceeds %d characters', $maxlength)); + } + + /** + * writes the actual data string to the BitBuffer + * + * @see \chillerlan\QRCode\Data\QRDataAbstract::writeBitBuffer() + */ + abstract protected function write(string $data):void; + + /** + * creates a BitBuffer and writes the string data to it + * + * @throws \chillerlan\QRCode\QRCodeException on data overflow + */ + protected function writeBitBuffer(string $data):void{ + $this->bitBuffer = new BitBuffer; + + $MAX_BITS = $this::MAX_BITS[$this->version][QRCode::ECC_MODES[$this->options->eccLevel]]; + + $this->bitBuffer + ->put($this->datamode, 4) + ->put($this->strlen, $this->getLengthBits()) + ; + + $this->write($data); + + // overflow, likely caused due to invalid version setting + if($this->bitBuffer->getLength() > $MAX_BITS){ + throw new QRCodeDataException(sprintf('code length overflow. (%d > %d bit)', $this->bitBuffer->getLength(), $MAX_BITS)); + } + + // add terminator (ISO/IEC 18004:2000 Table 2) + if($this->bitBuffer->getLength() + 4 <= $MAX_BITS){ + $this->bitBuffer->put(0, 4); + } + + // padding + while($this->bitBuffer->getLength() % 8 !== 0){ + $this->bitBuffer->putBit(false); + } + + // padding + while(true){ + + if($this->bitBuffer->getLength() >= $MAX_BITS){ + break; + } + + $this->bitBuffer->put(0xEC, 8); + + if($this->bitBuffer->getLength() >= $MAX_BITS){ + break; + } + + $this->bitBuffer->put(0x11, 8); + } + + } + + /** + * ECC masking + * + * ISO/IEC 18004:2000 Section 8.5 ff + * + * @see http://www.thonky.com/qr-code-tutorial/error-correction-coding + */ + protected function maskECC():array{ + [$l1, $l2, $b1, $b2] = $this::RSBLOCKS[$this->version][QRCode::ECC_MODES[$this->options->eccLevel]]; + + $rsBlocks = array_fill(0, $l1, [$b1, $b2]); + $rsCount = $l1 + $l2; + $this->ecdata = array_fill(0, $rsCount, []); + $this->dcdata = $this->ecdata; + + if($l2 > 0){ + $rsBlocks = array_merge($rsBlocks, array_fill(0, $l2, [$b1 + 1, $b2 + 1])); + } + + $totalCodeCount = 0; + $maxDcCount = 0; + $maxEcCount = 0; + $offset = 0; + + $bitBuffer = $this->bitBuffer->getBuffer(); + + foreach($rsBlocks as $key => $block){ + [$rsBlockTotal, $dcCount] = $block; + + $ecCount = $rsBlockTotal - $dcCount; + $maxDcCount = max($maxDcCount, $dcCount); + $maxEcCount = max($maxEcCount, $ecCount); + $this->dcdata[$key] = array_fill(0, $dcCount, null); + + foreach($this->dcdata[$key] as $a => $_z){ + $this->dcdata[$key][$a] = 0xff & $bitBuffer[$a + $offset]; + } + + [$num, $add] = $this->poly($key, $ecCount); + + foreach($this->ecdata[$key] as $c => $_){ + $modIndex = $c + $add; + $this->ecdata[$key][$c] = $modIndex >= 0 ? $num[$modIndex] : 0; + } + + $offset += $dcCount; + $totalCodeCount += $rsBlockTotal; + } + + $data = array_fill(0, $totalCodeCount, null); + $index = 0; + + $mask = function(array $arr, int $count) use (&$data, &$index, $rsCount):void{ + for($x = 0; $x < $count; $x++){ + for($y = 0; $y < $rsCount; $y++){ + if($x < count($arr[$y])){ + $data[$index] = $arr[$y][$x]; + $index++; + } + } + } + }; + + $mask($this->dcdata, $maxDcCount); + $mask($this->ecdata, $maxEcCount); + + return $data; + } + + /** + * helper method for the polynomial operations + */ + protected function poly(int $key, int $count):array{ + $rsPoly = new Polynomial; + $modPoly = new Polynomial; + + for($i = 0; $i < $count; $i++){ + $modPoly->setNum([1, $modPoly->gexp($i)]); + $rsPoly->multiply($modPoly->getNum()); + } + + $rsPolyCount = count($rsPoly->getNum()); + + $modPoly + ->setNum($this->dcdata[$key], $rsPolyCount - 1) + ->mod($rsPoly->getNum()) + ; + + $this->ecdata[$key] = array_fill(0, $rsPolyCount - 1, null); + $num = $modPoly->getNum(); + + return [ + $num, + count($num) - count($this->ecdata[$key]), + ]; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRDataInterface.php b/vendor/chillerlan/php-qrcode/src/Data/QRDataInterface.php new file mode 100644 index 000000000..93ad6221d --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Data/QRDataInterface.php @@ -0,0 +1,200 @@ +<?php +/** + * Interface QRDataInterface + * + * @filesource QRDataInterface.php + * @created 01.12.2015 + * @package chillerlan\QRCode\Data + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Data; + +/** + * Specifies the methods reqired for the data modules (Number, Alphanum, Byte and Kanji) + * and holds version information in several constants + */ +interface QRDataInterface{ + + /** + * @var int[] + */ + const CHAR_MAP_NUMBER = [ + '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, + ]; + + /** + * ISO/IEC 18004:2000 Table 5 + * + * @var int[] + */ + const CHAR_MAP_ALPHANUM = [ + '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, + '8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15, + 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23, + 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31, + 'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35, ' ' => 36, '$' => 37, '%' => 38, '*' => 39, + '+' => 40, '-' => 41, '.' => 42, '/' => 43, ':' => 44, + ]; + + /** + * ISO/IEC 18004:2000 Tables 7-11 - Number of symbol characters and input data capacity for versions 1 to 40 + * + * @see http://www.qrcode.com/en/about/version.html + * + * @var int [][][] + */ + const MAX_LENGTH =[ + // v => [NUMERIC => [L, M, Q, H ], ALPHANUM => [L, M, Q, H], BINARY => [L, M, Q, H ], KANJI => [L, M, Q, H ]] // modules + 1 => [[ 41, 34, 27, 17], [ 25, 20, 16, 10], [ 17, 14, 11, 7], [ 10, 8, 7, 4]], // 21 + 2 => [[ 77, 63, 48, 34], [ 47, 38, 29, 20], [ 32, 26, 20, 14], [ 20, 16, 12, 8]], // 25 + 3 => [[ 127, 101, 77, 58], [ 77, 61, 47, 35], [ 53, 42, 32, 24], [ 32, 26, 20, 15]], // 29 + 4 => [[ 187, 149, 111, 82], [ 114, 90, 67, 50], [ 78, 62, 46, 34], [ 48, 38, 28, 21]], // 33 + 5 => [[ 255, 202, 144, 106], [ 154, 122, 87, 64], [ 106, 84, 60, 44], [ 65, 52, 37, 27]], // 37 + 6 => [[ 322, 255, 178, 139], [ 195, 154, 108, 84], [ 134, 106, 74, 58], [ 82, 65, 45, 36]], // 41 + 7 => [[ 370, 293, 207, 154], [ 224, 178, 125, 93], [ 154, 122, 86, 64], [ 95, 75, 53, 39]], // 45 + 8 => [[ 461, 365, 259, 202], [ 279, 221, 157, 122], [ 192, 152, 108, 84], [ 118, 93, 66, 52]], // 49 + 9 => [[ 552, 432, 312, 235], [ 335, 262, 189, 143], [ 230, 180, 130, 98], [ 141, 111, 80, 60]], // 53 + 10 => [[ 652, 513, 364, 288], [ 395, 311, 221, 174], [ 271, 213, 151, 119], [ 167, 131, 93, 74]], // 57 + 11 => [[ 772, 604, 427, 331], [ 468, 366, 259, 200], [ 321, 251, 177, 137], [ 198, 155, 109, 85]], // 61 + 12 => [[ 883, 691, 489, 374], [ 535, 419, 296, 227], [ 367, 287, 203, 155], [ 226, 177, 125, 96]], // 65 + 13 => [[1022, 796, 580, 427], [ 619, 483, 352, 259], [ 425, 331, 241, 177], [ 262, 204, 149, 109]], // 69 NICE! + 14 => [[1101, 871, 621, 468], [ 667, 528, 376, 283], [ 458, 362, 258, 194], [ 282, 223, 159, 120]], // 73 + 15 => [[1250, 991, 703, 530], [ 758, 600, 426, 321], [ 520, 412, 292, 220], [ 320, 254, 180, 136]], // 77 + 16 => [[1408, 1082, 775, 602], [ 854, 656, 470, 365], [ 586, 450, 322, 250], [ 361, 277, 198, 154]], // 81 + 17 => [[1548, 1212, 876, 674], [ 938, 734, 531, 408], [ 644, 504, 364, 280], [ 397, 310, 224, 173]], // 85 + 18 => [[1725, 1346, 948, 746], [1046, 816, 574, 452], [ 718, 560, 394, 310], [ 442, 345, 243, 191]], // 89 + 19 => [[1903, 1500, 1063, 813], [1153, 909, 644, 493], [ 792, 624, 442, 338], [ 488, 384, 272, 208]], // 93 + 20 => [[2061, 1600, 1159, 919], [1249, 970, 702, 557], [ 858, 666, 482, 382], [ 528, 410, 297, 235]], // 97 + 21 => [[2232, 1708, 1224, 969], [1352, 1035, 742, 587], [ 929, 711, 509, 403], [ 572, 438, 314, 248]], // 101 + 22 => [[2409, 1872, 1358, 1056], [1460, 1134, 823, 640], [1003, 779, 565, 439], [ 618, 480, 348, 270]], // 105 + 23 => [[2620, 2059, 1468, 1108], [1588, 1248, 890, 672], [1091, 857, 611, 461], [ 672, 528, 376, 284]], // 109 + 24 => [[2812, 2188, 1588, 1228], [1704, 1326, 963, 744], [1171, 911, 661, 511], [ 721, 561, 407, 315]], // 113 + 25 => [[3057, 2395, 1718, 1286], [1853, 1451, 1041, 779], [1273, 997, 715, 535], [ 784, 614, 440, 330]], // 117 + 26 => [[3283, 2544, 1804, 1425], [1990, 1542, 1094, 864], [1367, 1059, 751, 593], [ 842, 652, 462, 365]], // 121 + 27 => [[3517, 2701, 1933, 1501], [2132, 1637, 1172, 910], [1465, 1125, 805, 625], [ 902, 692, 496, 385]], // 125 + 28 => [[3669, 2857, 2085, 1581], [2223, 1732, 1263, 958], [1528, 1190, 868, 658], [ 940, 732, 534, 405]], // 129 + 29 => [[3909, 3035, 2181, 1677], [2369, 1839, 1322, 1016], [1628, 1264, 908, 698], [1002, 778, 559, 430]], // 133 + 30 => [[4158, 3289, 2358, 1782], [2520, 1994, 1429, 1080], [1732, 1370, 982, 742], [1066, 843, 604, 457]], // 137 + 31 => [[4417, 3486, 2473, 1897], [2677, 2113, 1499, 1150], [1840, 1452, 1030, 790], [1132, 894, 634, 486]], // 141 + 32 => [[4686, 3693, 2670, 2022], [2840, 2238, 1618, 1226], [1952, 1538, 1112, 842], [1201, 947, 684, 518]], // 145 + 33 => [[4965, 3909, 2805, 2157], [3009, 2369, 1700, 1307], [2068, 1628, 1168, 898], [1273, 1002, 719, 553]], // 149 + 34 => [[5253, 4134, 2949, 2301], [3183, 2506, 1787, 1394], [2188, 1722, 1228, 958], [1347, 1060, 756, 590]], // 153 + 35 => [[5529, 4343, 3081, 2361], [3351, 2632, 1867, 1431], [2303, 1809, 1283, 983], [1417, 1113, 790, 605]], // 157 + 36 => [[5836, 4588, 3244, 2524], [3537, 2780, 1966, 1530], [2431, 1911, 1351, 1051], [1496, 1176, 832, 647]], // 161 + 37 => [[6153, 4775, 3417, 2625], [3729, 2894, 2071, 1591], [2563, 1989, 1423, 1093], [1577, 1224, 876, 673]], // 165 + 38 => [[6479, 5039, 3599, 2735], [3927, 3054, 2181, 1658], [2699, 2099, 1499, 1139], [1661, 1292, 923, 701]], // 169 + 39 => [[6743, 5313, 3791, 2927], [4087, 3220, 2298, 1774], [2809, 2213, 1579, 1219], [1729, 1362, 972, 750]], // 173 + 40 => [[7089, 5596, 3993, 3057], [4296, 3391, 2420, 1852], [2953, 2331, 1663, 1273], [1817, 1435, 1024, 784]], // 177 + ]; + + /** + * ISO/IEC 18004:2000 Tables 7-11 - Number of symbol characters and input data capacity for versions 1 to 40 + * + * @var int [][] + */ + const MAX_BITS = [ + // version => [L, M, Q, H ] + 1 => [ 152, 128, 104, 72], + 2 => [ 272, 224, 176, 128], + 3 => [ 440, 352, 272, 208], + 4 => [ 640, 512, 384, 288], + 5 => [ 864, 688, 496, 368], + 6 => [ 1088, 864, 608, 480], + 7 => [ 1248, 992, 704, 528], + 8 => [ 1552, 1232, 880, 688], + 9 => [ 1856, 1456, 1056, 800], + 10 => [ 2192, 1728, 1232, 976], + 11 => [ 2592, 2032, 1440, 1120], + 12 => [ 2960, 2320, 1648, 1264], + 13 => [ 3424, 2672, 1952, 1440], + 14 => [ 3688, 2920, 2088, 1576], + 15 => [ 4184, 3320, 2360, 1784], + 16 => [ 4712, 3624, 2600, 2024], + 17 => [ 5176, 4056, 2936, 2264], + 18 => [ 5768, 4504, 3176, 2504], + 19 => [ 6360, 5016, 3560, 2728], + 20 => [ 6888, 5352, 3880, 3080], + 21 => [ 7456, 5712, 4096, 3248], + 22 => [ 8048, 6256, 4544, 3536], + 23 => [ 8752, 6880, 4912, 3712], + 24 => [ 9392, 7312, 5312, 4112], + 25 => [10208, 8000, 5744, 4304], + 26 => [10960, 8496, 6032, 4768], + 27 => [11744, 9024, 6464, 5024], + 28 => [12248, 9544, 6968, 5288], + 29 => [13048, 10136, 7288, 5608], + 30 => [13880, 10984, 7880, 5960], + 31 => [14744, 11640, 8264, 6344], + 32 => [15640, 12328, 8920, 6760], + 33 => [16568, 13048, 9368, 7208], + 34 => [17528, 13800, 9848, 7688], + 35 => [18448, 14496, 10288, 7888], + 36 => [19472, 15312, 10832, 8432], + 37 => [20528, 15936, 11408, 8768], + 38 => [21616, 16816, 12016, 9136], + 39 => [22496, 17728, 12656, 9776], + 40 => [23648, 18672, 13328, 10208], + ]; + + /** + * @see http://www.thonky.com/qr-code-tutorial/error-correction-table + * + * @var int [][][] + */ + const RSBLOCKS = [ + 1 => [[ 1, 0, 26, 19], [ 1, 0, 26, 16], [ 1, 0, 26, 13], [ 1, 0, 26, 9]], + 2 => [[ 1, 0, 44, 34], [ 1, 0, 44, 28], [ 1, 0, 44, 22], [ 1, 0, 44, 16]], + 3 => [[ 1, 0, 70, 55], [ 1, 0, 70, 44], [ 2, 0, 35, 17], [ 2, 0, 35, 13]], + 4 => [[ 1, 0, 100, 80], [ 2, 0, 50, 32], [ 2, 0, 50, 24], [ 4, 0, 25, 9]], + 5 => [[ 1, 0, 134, 108], [ 2, 0, 67, 43], [ 2, 2, 33, 15], [ 2, 2, 33, 11]], + 6 => [[ 2, 0, 86, 68], [ 4, 0, 43, 27], [ 4, 0, 43, 19], [ 4, 0, 43, 15]], + 7 => [[ 2, 0, 98, 78], [ 4, 0, 49, 31], [ 2, 4, 32, 14], [ 4, 1, 39, 13]], + 8 => [[ 2, 0, 121, 97], [ 2, 2, 60, 38], [ 4, 2, 40, 18], [ 4, 2, 40, 14]], + 9 => [[ 2, 0, 146, 116], [ 3, 2, 58, 36], [ 4, 4, 36, 16], [ 4, 4, 36, 12]], + 10 => [[ 2, 2, 86, 68], [ 4, 1, 69, 43], [ 6, 2, 43, 19], [ 6, 2, 43, 15]], + 11 => [[ 4, 0, 101, 81], [ 1, 4, 80, 50], [ 4, 4, 50, 22], [ 3, 8, 36, 12]], + 12 => [[ 2, 2, 116, 92], [ 6, 2, 58, 36], [ 4, 6, 46, 20], [ 7, 4, 42, 14]], + 13 => [[ 4, 0, 133, 107], [ 8, 1, 59, 37], [ 8, 4, 44, 20], [12, 4, 33, 11]], + 14 => [[ 3, 1, 145, 115], [ 4, 5, 64, 40], [11, 5, 36, 16], [11, 5, 36, 12]], + 15 => [[ 5, 1, 109, 87], [ 5, 5, 65, 41], [ 5, 7, 54, 24], [11, 7, 36, 12]], + 16 => [[ 5, 1, 122, 98], [ 7, 3, 73, 45], [15, 2, 43, 19], [ 3, 13, 45, 15]], + 17 => [[ 1, 5, 135, 107], [10, 1, 74, 46], [ 1, 15, 50, 22], [ 2, 17, 42, 14]], + 18 => [[ 5, 1, 150, 120], [ 9, 4, 69, 43], [17, 1, 50, 22], [ 2, 19, 42, 14]], + 19 => [[ 3, 4, 141, 113], [ 3, 11, 70, 44], [17, 4, 47, 21], [ 9, 16, 39, 13]], + 20 => [[ 3, 5, 135, 107], [ 3, 13, 67, 41], [15, 5, 54, 24], [15, 10, 43, 15]], + 21 => [[ 4, 4, 144, 116], [17, 0, 68, 42], [17, 6, 50, 22], [19, 6, 46, 16]], + 22 => [[ 2, 7, 139, 111], [17, 0, 74, 46], [ 7, 16, 54, 24], [34, 0, 37, 13]], + 23 => [[ 4, 5, 151, 121], [ 4, 14, 75, 47], [11, 14, 54, 24], [16, 14, 45, 15]], + 24 => [[ 6, 4, 147, 117], [ 6, 14, 73, 45], [11, 16, 54, 24], [30, 2, 46, 16]], + 25 => [[ 8, 4, 132, 106], [ 8, 13, 75, 47], [ 7, 22, 54, 24], [22, 13, 45, 15]], + 26 => [[10, 2, 142, 114], [19, 4, 74, 46], [28, 6, 50, 22], [33, 4, 46, 16]], + 27 => [[ 8, 4, 152, 122], [22, 3, 73, 45], [ 8, 26, 53, 23], [12, 28, 45, 15]], + 28 => [[ 3, 10, 147, 117], [ 3, 23, 73, 45], [ 4, 31, 54, 24], [11, 31, 45, 15]], + 29 => [[ 7, 7, 146, 116], [21, 7, 73, 45], [ 1, 37, 53, 23], [19, 26, 45, 15]], + 30 => [[ 5, 10, 145, 115], [19, 10, 75, 47], [15, 25, 54, 24], [23, 25, 45, 15]], + 31 => [[13, 3, 145, 115], [ 2, 29, 74, 46], [42, 1, 54, 24], [23, 28, 45, 15]], + 32 => [[17, 0, 145, 115], [10, 23, 74, 46], [10, 35, 54, 24], [19, 35, 45, 15]], + 33 => [[17, 1, 145, 115], [14, 21, 74, 46], [29, 19, 54, 24], [11, 46, 45, 15]], + 34 => [[13, 6, 145, 115], [14, 23, 74, 46], [44, 7, 54, 24], [59, 1, 46, 16]], + 35 => [[12, 7, 151, 121], [12, 26, 75, 47], [39, 14, 54, 24], [22, 41, 45, 15]], + 36 => [[ 6, 14, 151, 121], [ 6, 34, 75, 47], [46, 10, 54, 24], [ 2, 64, 45, 15]], + 37 => [[17, 4, 152, 122], [29, 14, 74, 46], [49, 10, 54, 24], [24, 46, 45, 15]], + 38 => [[ 4, 18, 152, 122], [13, 32, 74, 46], [48, 14, 54, 24], [42, 32, 45, 15]], + 39 => [[20, 4, 147, 117], [40, 7, 75, 47], [43, 22, 54, 24], [10, 67, 45, 15]], + 40 => [[19, 6, 148, 118], [18, 31, 75, 47], [34, 34, 54, 24], [20, 61, 45, 15]], + ]; + + /** + * Sets the data string (internally called by the constructor) + */ + public function setData(string $data):QRDataInterface; + + /** + * returns a fresh matrix object with the data written for the given $maskPattern + */ + public function initMatrix(int $maskPattern, bool $test = null):QRMatrix; + +} diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRMatrix.php b/vendor/chillerlan/php-qrcode/src/Data/QRMatrix.php new file mode 100755 index 000000000..05c8b9069 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Data/QRMatrix.php @@ -0,0 +1,740 @@ +<?php +/** + * Class QRMatrix + * + * @filesource QRMatrix.php + * @created 15.11.2017 + * @package chillerlan\QRCode\Data + * @author Smiley <smiley@chillerlan.net> + * @copyright 2017 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Data; + +use chillerlan\QRCode\QRCode; +use Closure; + +use function array_fill, array_key_exists, array_push, array_unshift, count, floor, in_array, max, min, range; + +/** + * Holds a numerical representation of the final QR Code; + * maps the ECC coded binary data and applies the mask pattern + * + * @see http://www.thonky.com/qr-code-tutorial/format-version-information + */ +final class QRMatrix{ + + /** @var int */ + public const M_NULL = 0x00; + /** @var int */ + public const M_DARKMODULE = 0x02; + /** @var int */ + public const M_DATA = 0x04; + /** @var int */ + public const M_FINDER = 0x06; + /** @var int */ + public const M_SEPARATOR = 0x08; + /** @var int */ + public const M_ALIGNMENT = 0x0a; + /** @var int */ + public const M_TIMING = 0x0c; + /** @var int */ + public const M_FORMAT = 0x0e; + /** @var int */ + public const M_VERSION = 0x10; + /** @var int */ + public const M_QUIETZONE = 0x12; + /** @var int */ + public const M_LOGO = 0x14; + /** @var int */ + public const M_FINDER_DOT = 0x16; + /** @var int */ + public const M_TEST = 0xff; + + /** + * ISO/IEC 18004:2000 Annex E, Table E.1 - Row/column coordinates of center module of Alignment Patterns + * + * version -> pattern + * + * @var int[][] + */ + protected const alignmentPattern = [ + 1 => [], + 2 => [6, 18], + 3 => [6, 22], + 4 => [6, 26], + 5 => [6, 30], + 6 => [6, 34], + 7 => [6, 22, 38], + 8 => [6, 24, 42], + 9 => [6, 26, 46], + 10 => [6, 28, 50], + 11 => [6, 30, 54], + 12 => [6, 32, 58], + 13 => [6, 34, 62], + 14 => [6, 26, 46, 66], + 15 => [6, 26, 48, 70], + 16 => [6, 26, 50, 74], + 17 => [6, 30, 54, 78], + 18 => [6, 30, 56, 82], + 19 => [6, 30, 58, 86], + 20 => [6, 34, 62, 90], + 21 => [6, 28, 50, 72, 94], + 22 => [6, 26, 50, 74, 98], + 23 => [6, 30, 54, 78, 102], + 24 => [6, 28, 54, 80, 106], + 25 => [6, 32, 58, 84, 110], + 26 => [6, 30, 58, 86, 114], + 27 => [6, 34, 62, 90, 118], + 28 => [6, 26, 50, 74, 98, 122], + 29 => [6, 30, 54, 78, 102, 126], + 30 => [6, 26, 52, 78, 104, 130], + 31 => [6, 30, 56, 82, 108, 134], + 32 => [6, 34, 60, 86, 112, 138], + 33 => [6, 30, 58, 86, 114, 142], + 34 => [6, 34, 62, 90, 118, 146], + 35 => [6, 30, 54, 78, 102, 126, 150], + 36 => [6, 24, 50, 76, 102, 128, 154], + 37 => [6, 28, 54, 80, 106, 132, 158], + 38 => [6, 32, 58, 84, 110, 136, 162], + 39 => [6, 26, 54, 82, 110, 138, 166], + 40 => [6, 30, 58, 86, 114, 142, 170], + ]; + + /** + * ISO/IEC 18004:2000 Annex D, Table D.1 - Version information bit stream for each version + * + * no version pattern for QR Codes < 7 + * + * @var int[] + */ + protected const versionPattern = [ + 7 => 0b000111110010010100, + 8 => 0b001000010110111100, + 9 => 0b001001101010011001, + 10 => 0b001010010011010011, + 11 => 0b001011101111110110, + 12 => 0b001100011101100010, + 13 => 0b001101100001000111, + 14 => 0b001110011000001101, + 15 => 0b001111100100101000, + 16 => 0b010000101101111000, + 17 => 0b010001010001011101, + 18 => 0b010010101000010111, + 19 => 0b010011010100110010, + 20 => 0b010100100110100110, + 21 => 0b010101011010000011, + 22 => 0b010110100011001001, + 23 => 0b010111011111101100, + 24 => 0b011000111011000100, + 25 => 0b011001000111100001, + 26 => 0b011010111110101011, + 27 => 0b011011000010001110, + 28 => 0b011100110000011010, + 29 => 0b011101001100111111, + 30 => 0b011110110101110101, + 31 => 0b011111001001010000, + 32 => 0b100000100111010101, + 33 => 0b100001011011110000, + 34 => 0b100010100010111010, + 35 => 0b100011011110011111, + 36 => 0b100100101100001011, + 37 => 0b100101010000101110, + 38 => 0b100110101001100100, + 39 => 0b100111010101000001, + 40 => 0b101000110001101001, + ]; + + /** + * ISO/IEC 18004:2000 Section 8.9 - Format Information + * + * ECC level -> mask pattern + * + * @var int[][] + */ + protected const formatPattern = [ + [ // L + 0b111011111000100, + 0b111001011110011, + 0b111110110101010, + 0b111100010011101, + 0b110011000101111, + 0b110001100011000, + 0b110110001000001, + 0b110100101110110, + ], + [ // M + 0b101010000010010, + 0b101000100100101, + 0b101111001111100, + 0b101101101001011, + 0b100010111111001, + 0b100000011001110, + 0b100111110010111, + 0b100101010100000, + ], + [ // Q + 0b011010101011111, + 0b011000001101000, + 0b011111100110001, + 0b011101000000110, + 0b010010010110100, + 0b010000110000011, + 0b010111011011010, + 0b010101111101101, + ], + [ // H + 0b001011010001001, + 0b001001110111110, + 0b001110011100111, + 0b001100111010000, + 0b000011101100010, + 0b000001001010101, + 0b000110100001100, + 0b000100000111011, + ], + ]; + + /** + * the current QR Code version number + */ + protected int $version; + + /** + * the current ECC level + */ + protected int $eclevel; + + /** + * the used mask pattern, set via QRMatrix::mapData() + */ + protected int $maskPattern = QRCode::MASK_PATTERN_AUTO; + + /** + * the size (side length) of the matrix + */ + protected int $moduleCount; + + /** + * the actual matrix data array + * + * @var int[][] + */ + protected array $matrix; + + /** + * QRMatrix constructor. + * + * @throws \chillerlan\QRCode\Data\QRCodeDataException + */ + public function __construct(int $version, int $eclevel){ + + if(!in_array($version, range(1, 40), true)){ + throw new QRCodeDataException('invalid QR Code version'); + } + + if(!array_key_exists($eclevel, QRCode::ECC_MODES)){ + throw new QRCodeDataException('invalid ecc level'); + } + + $this->version = $version; + $this->eclevel = $eclevel; + $this->moduleCount = $this->version * 4 + 17; + $this->matrix = array_fill(0, $this->moduleCount, array_fill(0, $this->moduleCount, $this::M_NULL)); + } + + /** + * shortcut to initialize the matrix + */ + public function init(int $maskPattern, bool $test = null):QRMatrix{ + return $this + ->setFinderPattern() + ->setSeparators() + ->setAlignmentPattern() + ->setTimingPattern() + ->setVersionNumber($test) + ->setFormatInfo($maskPattern, $test) + ->setDarkModule() + ; + } + + /** + * Returns the data matrix, returns a pure boolean representation if $boolean is set to true + * + * @return int[][]|bool[][] + */ + public function matrix(bool $boolean = false):array{ + + if(!$boolean){ + return $this->matrix; + } + + $matrix = []; + + foreach($this->matrix as $y => $row){ + $matrix[$y] = []; + + foreach($row as $x => $val){ + $matrix[$y][$x] = ($val >> 8) > 0; + } + } + + return $matrix; + } + + /** + * Returns the current version number + */ + public function version():int{ + return $this->version; + } + + /** + * Returns the current ECC level + */ + public function eccLevel():int{ + return $this->eclevel; + } + + /** + * Returns the current mask pattern + */ + public function maskPattern():int{ + return $this->maskPattern; + } + + /** + * Returns the absoulute size of the matrix, including quiet zone (after setting it). + * + * size = version * 4 + 17 [ + 2 * quietzone size] + */ + public function size():int{ + return $this->moduleCount; + } + + /** + * Returns the value of the module at position [$x, $y] + */ + public function get(int $x, int $y):int{ + return $this->matrix[$y][$x]; + } + + /** + * Sets the $M_TYPE value for the module at position [$x, $y] + * + * true => $M_TYPE << 8 + * false => $M_TYPE + */ + public function set(int $x, int $y, bool $value, int $M_TYPE):QRMatrix{ + $this->matrix[$y][$x] = $M_TYPE << ($value ? 8 : 0); + + return $this; + } + + /** + * Checks whether a module is true (dark) or false (light) + * + * true => $value >> 8 === $M_TYPE + * $value >> 8 > 0 + * + * false => $value === $M_TYPE + * $value >> 8 === 0 + */ + public function check(int $x, int $y):bool{ + return ($this->matrix[$y][$x] >> 8) > 0; + } + + + /** + * Sets the "dark module", that is always on the same position 1x1px away from the bottom left finder + */ + public function setDarkModule():QRMatrix{ + $this->set(8, 4 * $this->version + 9, true, $this::M_DARKMODULE); + + return $this; + } + + /** + * Draws the 7x7 finder patterns in the corners top left/right and bottom left + * + * ISO/IEC 18004:2000 Section 7.3.2 + */ + public function setFinderPattern():QRMatrix{ + + $pos = [ + [0, 0], // top left + [$this->moduleCount - 7, 0], // bottom left + [0, $this->moduleCount - 7], // top right + ]; + + foreach($pos as $c){ + for($y = 0; $y < 7; $y++){ + for($x = 0; $x < 7; $x++){ + // outer (dark) 7*7 square + if($x === 0 || $x === 6 || $y === 0 || $y === 6){ + $this->set($c[0] + $y, $c[1] + $x, true, $this::M_FINDER); + } + // inner (light) 5*5 square + elseif($x === 1 || $x === 5 || $y === 1 || $y === 5){ + $this->set($c[0] + $y, $c[1] + $x, false, $this::M_FINDER); + } + // 3*3 dot + else{ + $this->set($c[0] + $y, $c[1] + $x, true, $this::M_FINDER_DOT); + } + } + } + } + + return $this; + } + + /** + * Draws the separator lines around the finder patterns + * + * ISO/IEC 18004:2000 Section 7.3.3 + */ + public function setSeparators():QRMatrix{ + + $h = [ + [7, 0], + [$this->moduleCount - 8, 0], + [7, $this->moduleCount - 8], + ]; + + $v = [ + [7, 7], + [$this->moduleCount - 1, 7], + [7, $this->moduleCount - 8], + ]; + + for($c = 0; $c < 3; $c++){ + for($i = 0; $i < 8; $i++){ + $this->set($h[$c][0] , $h[$c][1] + $i, false, $this::M_SEPARATOR); + $this->set($v[$c][0] - $i, $v[$c][1] , false, $this::M_SEPARATOR); + } + } + + return $this; + } + + + /** + * Draws the 5x5 alignment patterns + * + * ISO/IEC 18004:2000 Section 7.3.5 + */ + public function setAlignmentPattern():QRMatrix{ + + foreach($this::alignmentPattern[$this->version] as $y){ + foreach($this::alignmentPattern[$this->version] as $x){ + + // skip existing patterns + if($this->matrix[$y][$x] !== $this::M_NULL){ + continue; + } + + for($ry = -2; $ry <= 2; $ry++){ + for($rx = -2; $rx <= 2; $rx++){ + $v = ($ry === 0 && $rx === 0) || $ry === 2 || $ry === -2 || $rx === 2 || $rx === -2; + + $this->set($x + $rx, $y + $ry, $v, $this::M_ALIGNMENT); + } + } + + } + } + + return $this; + } + + + /** + * Draws the timing pattern (h/v checkered line between the finder patterns) + * + * ISO/IEC 18004:2000 Section 7.3.4 + */ + public function setTimingPattern():QRMatrix{ + + foreach(range(8, $this->moduleCount - 8 - 1) as $i){ + + if($this->matrix[6][$i] !== $this::M_NULL || $this->matrix[$i][6] !== $this::M_NULL){ + continue; + } + + $v = $i % 2 === 0; + + $this->set($i, 6, $v, $this::M_TIMING); // h + $this->set(6, $i, $v, $this::M_TIMING); // v + } + + return $this; + } + + /** + * Draws the version information, 2x 3x6 pixel + * + * ISO/IEC 18004:2000 Section 8.10 + */ + public function setVersionNumber(bool $test = null):QRMatrix{ + $bits = $this::versionPattern[$this->version] ?? false; + + if($bits !== false){ + + for($i = 0; $i < 18; $i++){ + $a = (int)floor($i / 3); + $b = $i % 3 + $this->moduleCount - 8 - 3; + $v = !$test && (($bits >> $i) & 1) === 1; + + $this->set($b, $a, $v, $this::M_VERSION); // ne + $this->set($a, $b, $v, $this::M_VERSION); // sw + } + + } + + return $this; + } + + /** + * Draws the format info along the finder patterns + * + * ISO/IEC 18004:2000 Section 8.9 + */ + public function setFormatInfo(int $maskPattern, bool $test = null):QRMatrix{ + $bits = $this::formatPattern[QRCode::ECC_MODES[$this->eclevel]][$maskPattern] ?? 0; + + for($i = 0; $i < 15; $i++){ + $v = !$test && (($bits >> $i) & 1) === 1; + + if($i < 6){ + $this->set(8, $i, $v, $this::M_FORMAT); + } + elseif($i < 8){ + $this->set(8, $i + 1, $v, $this::M_FORMAT); + } + else{ + $this->set(8, $this->moduleCount - 15 + $i, $v, $this::M_FORMAT); + } + + if($i < 8){ + $this->set($this->moduleCount - $i - 1, 8, $v, $this::M_FORMAT); + } + elseif($i < 9){ + $this->set(15 - $i, 8, $v, $this::M_FORMAT); + } + else{ + $this->set(15 - $i - 1, 8, $v, $this::M_FORMAT); + } + + } + + $this->set(8, $this->moduleCount - 8, !$test, $this::M_FORMAT); + + return $this; + } + + /** + * Draws the "quiet zone" of $size around the matrix + * + * ISO/IEC 18004:2000 Section 7.3.7 + * + * @throws \chillerlan\QRCode\Data\QRCodeDataException + */ + public function setQuietZone(int $size = null):QRMatrix{ + + if($this->matrix[$this->moduleCount - 1][$this->moduleCount - 1] === $this::M_NULL){ + throw new QRCodeDataException('use only after writing data'); + } + + $size = $size !== null + ? max(0, min($size, floor($this->moduleCount / 2))) + : 4; + + for($y = 0; $y < $this->moduleCount; $y++){ + for($i = 0; $i < $size; $i++){ + array_unshift($this->matrix[$y], $this::M_QUIETZONE); + array_push($this->matrix[$y], $this::M_QUIETZONE); + } + } + + $this->moduleCount += ($size * 2); + + $r = array_fill(0, $this->moduleCount, $this::M_QUIETZONE); + + for($i = 0; $i < $size; $i++){ + array_unshift($this->matrix, $r); + array_push($this->matrix, $r); + } + + return $this; + } + + /** + * Clears a space of $width * $height in order to add a logo or text. + * + * Additionally, the logo space can be positioned within the QR Code - respecting the main functional patterns - + * using $startX and $startY. If either of these are null, the logo space will be centered in that direction. + * ECC level "H" (30%) is required. + * + * Please note that adding a logo space minimizes the error correction capacity of the QR Code and + * created images may become unreadable, especially when printed with a chance to receive damage. + * Please test thoroughly before using this feature in production. + * + * This method should be called from within an output module (after the matrix has been filled with data). + * Note that there is no restiction on how many times this method could be called on the same matrix instance. + * + * @link https://github.com/chillerlan/php-qrcode/issues/52 + * + * @throws \chillerlan\QRCode\Data\QRCodeDataException + */ + public function setLogoSpace(int $width, int $height, int $startX = null, int $startY = null):QRMatrix{ + + // for logos we operate in ECC H (30%) only + if($this->eclevel !== QRCode::ECC_H){ + throw new QRCodeDataException('ECC level "H" required to add logo space'); + } + + // we need uneven sizes to center the logo space, adjust if needed + if($startX === null && ($width % 2) === 0){ + $width++; + } + + if($startY === null && ($height % 2) === 0){ + $height++; + } + + // $this->moduleCount includes the quiet zone (if created), we need the QR size here + $length = $this->version * 4 + 17; + + // throw if the logo space exceeds the maximum error correction capacity + if($width * $height > floor($length * $length * 0.2)){ + throw new QRCodeDataException('logo space exceeds the maximum error correction capacity'); + } + + // quiet zone size + $qz = ($this->moduleCount - $length) / 2; + // skip quiet zone and the first 9 rows/columns (finder-, mode-, version- and timing patterns) + $start = $qz + 9; + // skip quiet zone + $end = $this->moduleCount - $qz; + + // determine start coordinates + $startX = ($startX !== null ? $startX : ($length - $width) / 2) + $qz; + $startY = ($startY !== null ? $startY : ($length - $height) / 2) + $qz; + + // clear the space + foreach($this->matrix as $y => $row){ + foreach($row as $x => $val){ + // out of bounds, skip + if($x < $start || $y < $start ||$x >= $end || $y >= $end){ + continue; + } + // a match + if($x >= $startX && $x < ($startX + $width) && $y >= $startY && $y < ($startY + $height)){ + $this->set($x, $y, false, $this::M_LOGO); + } + } + } + + return $this; + } + + /** + * Maps the binary $data array from QRDataInterface::maskECC() on the matrix, + * masking the data using $maskPattern (ISO/IEC 18004:2000 Section 8.8) + * + * @see \chillerlan\QRCode\Data\QRDataAbstract::maskECC() + * + * @param int[] $data + * @param int $maskPattern + * + * @return \chillerlan\QRCode\Data\QRMatrix + */ + public function mapData(array $data, int $maskPattern):QRMatrix{ + $this->maskPattern = $maskPattern; + $byteCount = count($data); + $y = $this->moduleCount - 1; + $inc = -1; + $byteIndex = 0; + $bitIndex = 7; + $mask = $this->getMask($this->maskPattern); + + for($i = $y; $i > 0; $i -= 2){ + + if($i === 6){ + $i--; + } + + while(true){ + for($c = 0; $c < 2; $c++){ + $x = $i - $c; + + if($this->matrix[$y][$x] === $this::M_NULL){ + $v = false; + + if($byteIndex < $byteCount){ + $v = (($data[$byteIndex] >> $bitIndex) & 1) === 1; + } + + if($mask($x, $y) === 0){ + $v = !$v; + } + + $this->matrix[$y][$x] = $this::M_DATA << ($v ? 8 : 0); + $bitIndex--; + + if($bitIndex === -1){ + $byteIndex++; + $bitIndex = 7; + } + + } + } + + $y += $inc; + + if($y < 0 || $this->moduleCount <= $y){ + $y -= $inc; + $inc = -$inc; + + break; + } + + } + } + + return $this; + } + + /** + * ISO/IEC 18004:2000 Section 8.8.1 + * + * Note that some versions of the QR code standard have had errors in the section about mask patterns. + * The information below has been corrected. (https://www.thonky.com/qr-code-tutorial/mask-patterns) + * + * @see \chillerlan\QRCode\QRMatrix::mapData() + * + * @internal + * + * @throws \chillerlan\QRCode\Data\QRCodeDataException + */ + protected function getMask(int $maskPattern):Closure{ + + if((0b111 & $maskPattern) !== $maskPattern){ + throw new QRCodeDataException('invalid mask pattern'); // @codeCoverageIgnore + } + + return [ + 0b000 => fn($x, $y):int => ($x + $y) % 2, + 0b001 => fn($x, $y):int => $y % 2, + 0b010 => fn($x, $y):int => $x % 3, + 0b011 => fn($x, $y):int => ($x + $y) % 3, + 0b100 => fn($x, $y):int => ((int)($y / 2) + (int)($x / 3)) % 2, + 0b101 => fn($x, $y):int => (($x * $y) % 2) + (($x * $y) % 3), + 0b110 => fn($x, $y):int => ((($x * $y) % 2) + (($x * $y) % 3)) % 2, + 0b111 => fn($x, $y):int => ((($x * $y) % 3) + (($x + $y) % 2)) % 2, + ][$maskPattern]; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Helpers/BitBuffer.php b/vendor/chillerlan/php-qrcode/src/Helpers/BitBuffer.php new file mode 100644 index 000000000..de47f20f4 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Helpers/BitBuffer.php @@ -0,0 +1,89 @@ +<?php +/** + * Class BitBuffer + * + * @filesource BitBuffer.php + * @created 25.11.2015 + * @package chillerlan\QRCode\Helpers + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Helpers; + +use function count, floor; + +/** + * Holds the raw binary data + */ +final class BitBuffer{ + + /** + * The buffer content + * + * @var int[] + */ + protected array $buffer = []; + + /** + * Length of the content (bits) + */ + protected int $length = 0; + + /** + * clears the buffer + */ + public function clear():BitBuffer{ + $this->buffer = []; + $this->length = 0; + + return $this; + } + + /** + * appends a sequence of bits + */ + public function put(int $num, int $length):BitBuffer{ + + for($i = 0; $i < $length; $i++){ + $this->putBit((($num >> ($length - $i - 1)) & 1) === 1); + } + + return $this; + } + + /** + * appends a single bit + */ + public function putBit(bool $bit):BitBuffer{ + $bufIndex = floor($this->length / 8); + + if(count($this->buffer) <= $bufIndex){ + $this->buffer[] = 0; + } + + if($bit === true){ + $this->buffer[(int)$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 + */ + public function getBuffer():array{ + return $this->buffer; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Helpers/Polynomial.php b/vendor/chillerlan/php-qrcode/src/Helpers/Polynomial.php new file mode 100644 index 000000000..c42e0831c --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Helpers/Polynomial.php @@ -0,0 +1,178 @@ +<?php +/** + * Class Polynomial + * + * @filesource Polynomial.php + * @created 25.11.2015 + * @package chillerlan\QRCode\Helpers + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Helpers; + +use chillerlan\QRCode\QRCodeException; + +use function array_fill, count, sprintf; + +/** + * Polynomial long division helpers + * + * @see http://www.thonky.com/qr-code-tutorial/error-correction-coding + */ +final class Polynomial{ + + /** + * @see http://www.thonky.com/qr-code-tutorial/log-antilog-table + */ + protected const table = [ + [ 1, 0], [ 2, 0], [ 4, 1], [ 8, 25], [ 16, 2], [ 32, 50], [ 64, 26], [128, 198], + [ 29, 3], [ 58, 223], [116, 51], [232, 238], [205, 27], [135, 104], [ 19, 199], [ 38, 75], + [ 76, 4], [152, 100], [ 45, 224], [ 90, 14], [180, 52], [117, 141], [234, 239], [201, 129], + [143, 28], [ 3, 193], [ 6, 105], [ 12, 248], [ 24, 200], [ 48, 8], [ 96, 76], [192, 113], + [157, 5], [ 39, 138], [ 78, 101], [156, 47], [ 37, 225], [ 74, 36], [148, 15], [ 53, 33], + [106, 53], [212, 147], [181, 142], [119, 218], [238, 240], [193, 18], [159, 130], [ 35, 69], + [ 70, 29], [140, 181], [ 5, 194], [ 10, 125], [ 20, 106], [ 40, 39], [ 80, 249], [160, 185], + [ 93, 201], [186, 154], [105, 9], [210, 120], [185, 77], [111, 228], [222, 114], [161, 166], + [ 95, 6], [190, 191], [ 97, 139], [194, 98], [153, 102], [ 47, 221], [ 94, 48], [188, 253], + [101, 226], [202, 152], [137, 37], [ 15, 179], [ 30, 16], [ 60, 145], [120, 34], [240, 136], + [253, 54], [231, 208], [211, 148], [187, 206], [107, 143], [214, 150], [177, 219], [127, 189], + [254, 241], [225, 210], [223, 19], [163, 92], [ 91, 131], [182, 56], [113, 70], [226, 64], + [217, 30], [175, 66], [ 67, 182], [134, 163], [ 17, 195], [ 34, 72], [ 68, 126], [136, 110], + [ 13, 107], [ 26, 58], [ 52, 40], [104, 84], [208, 250], [189, 133], [103, 186], [206, 61], + [129, 202], [ 31, 94], [ 62, 155], [124, 159], [248, 10], [237, 21], [199, 121], [147, 43], + [ 59, 78], [118, 212], [236, 229], [197, 172], [151, 115], [ 51, 243], [102, 167], [204, 87], + [133, 7], [ 23, 112], [ 46, 192], [ 92, 247], [184, 140], [109, 128], [218, 99], [169, 13], + [ 79, 103], [158, 74], [ 33, 222], [ 66, 237], [132, 49], [ 21, 197], [ 42, 254], [ 84, 24], + [168, 227], [ 77, 165], [154, 153], [ 41, 119], [ 82, 38], [164, 184], [ 85, 180], [170, 124], + [ 73, 17], [146, 68], [ 57, 146], [114, 217], [228, 35], [213, 32], [183, 137], [115, 46], + [230, 55], [209, 63], [191, 209], [ 99, 91], [198, 149], [145, 188], [ 63, 207], [126, 205], + [252, 144], [229, 135], [215, 151], [179, 178], [123, 220], [246, 252], [241, 190], [255, 97], + [227, 242], [219, 86], [171, 211], [ 75, 171], [150, 20], [ 49, 42], [ 98, 93], [196, 158], + [149, 132], [ 55, 60], [110, 57], [220, 83], [165, 71], [ 87, 109], [174, 65], [ 65, 162], + [130, 31], [ 25, 45], [ 50, 67], [100, 216], [200, 183], [141, 123], [ 7, 164], [ 14, 118], + [ 28, 196], [ 56, 23], [112, 73], [224, 236], [221, 127], [167, 12], [ 83, 111], [166, 246], + [ 81, 108], [162, 161], [ 89, 59], [178, 82], [121, 41], [242, 157], [249, 85], [239, 170], + [195, 251], [155, 96], [ 43, 134], [ 86, 177], [172, 187], [ 69, 204], [138, 62], [ 9, 90], + [ 18, 203], [ 36, 89], [ 72, 95], [144, 176], [ 61, 156], [122, 169], [244, 160], [245, 81], + [247, 11], [243, 245], [251, 22], [235, 235], [203, 122], [139, 117], [ 11, 44], [ 22, 215], + [ 44, 79], [ 88, 174], [176, 213], [125, 233], [250, 230], [233, 231], [207, 173], [131, 232], + [ 27, 116], [ 54, 214], [108, 244], [216, 234], [173, 168], [ 71, 80], [142, 88], [ 1, 175], + ]; + + /** + * @var int[] + */ + protected array $num = []; + + /** + * Polynomial constructor. + */ + public function __construct(array $num = null, int $shift = null){ + $this->setNum($num ?? [1], $shift); + } + + /** + * + */ + public function getNum():array{ + return $this->num; + } + + /** + * @param int[] $num + * @param int|null $shift + * + * @return \chillerlan\QRCode\Helpers\Polynomial + */ + public function setNum(array $num, int $shift = null):Polynomial{ + $offset = 0; + $numCount = count($num); + + while($offset < $numCount && $num[$offset] === 0){ + $offset++; + } + + $this->num = array_fill(0, $numCount - $offset + ($shift ?? 0), 0); + + for($i = 0; $i < $numCount - $offset; $i++){ + $this->num[$i] = $num[$i + $offset]; + } + + return $this; + } + + /** + * @param int[] $e + * + * @return \chillerlan\QRCode\Helpers\Polynomial + */ + public function multiply(array $e):Polynomial{ + $n = array_fill(0, count($this->num) + count($e) - 1, 0); + + foreach($this->num as $i => $vi){ + $vi = $this->glog($vi); + + foreach($e as $j => $vj){ + $n[$i + $j] ^= $this->gexp($vi + $this->glog($vj)); + } + + } + + $this->setNum($n); + + return $this; + } + + /** + * @param int[] $e + * + * @return \chillerlan\QRCode\Helpers\Polynomial + */ + public function mod(array $e):Polynomial{ + $n = $this->num; + + if(count($n) - count($e) < 0){ + return $this; + } + + $ratio = $this->glog($n[0]) - $this->glog($e[0]); + + foreach($e as $i => $v){ + $n[$i] ^= $this->gexp($this->glog($v) + $ratio); + } + + $this->setNum($n)->mod($e); + + return $this; + } + + /** + * @throws \chillerlan\QRCode\QRCodeException + */ + public function glog(int $n):int{ + + if($n < 1){ + throw new QRCodeException(sprintf('log(%s)', $n)); + } + + return Polynomial::table[$n][1]; + } + + /** + * + */ + public function gexp(int $n):int{ + + if($n < 0){ + $n += 255; + } + elseif($n >= 256){ + $n -= 255; + } + + return Polynomial::table[$n][0]; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php b/vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php new file mode 100644 index 000000000..639bdd111 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php @@ -0,0 +1,17 @@ +<?php +/** + * Class QRCodeOutputException + * + * @filesource QRCodeOutputException.php + * @created 09.12.2015 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\QRCodeException; + +class QRCodeOutputException extends QRCodeException{} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php b/vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php new file mode 100644 index 000000000..a15ae9ff3 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php @@ -0,0 +1,113 @@ +<?php +/** + * Class QRFpdf + * + * https://github.com/chillerlan/php-qrcode/pull/49 + * + * @filesource QRFpdf.php + * @created 03.06.2020 + * @package chillerlan\QRCode\Output + * @author Maximilian Kresse + * + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\Data\QRMatrix; +use chillerlan\QRCode\QRCodeException; +use chillerlan\Settings\SettingsContainerInterface; +use FPDF; + +use function array_values, class_exists, count, is_array; + +/** + * QRFpdf output module (requires fpdf) + * + * @see https://github.com/Setasign/FPDF + * @see http://www.fpdf.org/ + */ +class QRFpdf extends QROutputAbstract{ + + public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){ + + if(!class_exists(FPDF::class)){ + // @codeCoverageIgnoreStart + throw new QRCodeException( + 'The QRFpdf output requires FPDF as dependency but the class "\FPDF" couldn\'t be found.' + ); + // @codeCoverageIgnoreEnd + } + + parent::__construct($options, $matrix); + } + + /** + * @inheritDoc + */ + protected function setModuleValues():void{ + + foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){ + $v = $this->options->moduleValues[$M_TYPE] ?? null; + + if(!is_array($v) || count($v) < 3){ + $this->moduleValues[$M_TYPE] = $defaultValue + ? [0, 0, 0] + : [255, 255, 255]; + } + else{ + $this->moduleValues[$M_TYPE] = array_values($v); + } + + } + + } + + /** + * @inheritDoc + * + * @return string|\FPDF + */ + public function dump(string $file = null){ + $file ??= $this->options->cachefile; + + $fpdf = new FPDF('P', $this->options->fpdfMeasureUnit, [$this->length, $this->length]); + $fpdf->AddPage(); + + $prevColor = null; + + foreach($this->matrix->matrix() as $y => $row){ + + foreach($row as $x => $M_TYPE){ + /** @var int $M_TYPE */ + $color = $this->moduleValues[$M_TYPE]; + + if($prevColor === null || $prevColor !== $color){ + /** @phan-suppress-next-line PhanParamTooFewUnpack */ + $fpdf->SetFillColor(...$color); + $prevColor = $color; + } + + $fpdf->Rect($x * $this->scale, $y * $this->scale, 1 * $this->scale, 1 * $this->scale, 'F'); + } + + } + + if($this->options->returnResource){ + return $fpdf; + } + + $pdfData = $fpdf->Output('S'); + + if($file !== null){ + $this->saveToFile($pdfData, $file); + } + + if($this->options->imageBase64){ + $pdfData = sprintf('data:application/pdf;base64,%s', base64_encode($pdfData)); + } + + return $pdfData; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRImage.php b/vendor/chillerlan/php-qrcode/src/Output/QRImage.php new file mode 100644 index 000000000..8f533d341 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRImage.php @@ -0,0 +1,217 @@ +<?php +/** + * Class QRImage + * + * @filesource QRImage.php + * @created 05.12.2015 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + * + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\Data\QRMatrix; +use chillerlan\QRCode\{QRCode, QRCodeException}; +use chillerlan\Settings\SettingsContainerInterface; +use Exception; + +use function array_values, base64_encode, call_user_func, count, extension_loaded, imagecolorallocate, imagecolortransparent, + imagecreatetruecolor, imagedestroy, imagefilledrectangle, imagegif, imagejpeg, imagepng, in_array, + is_array, ob_end_clean, ob_get_contents, ob_start, range, sprintf; + +/** + * Converts the matrix into GD images, raw or base64 output (requires ext-gd) + * + * @see http://php.net/manual/book.image.php + */ +class QRImage extends QROutputAbstract{ + + /** + * GD image types that support transparency + * + * @var string[] + */ + protected const TRANSPARENCY_TYPES = [ + QRCode::OUTPUT_IMAGE_PNG, + QRCode::OUTPUT_IMAGE_GIF, + ]; + + protected string $defaultMode = QRCode::OUTPUT_IMAGE_PNG; + + /** + * The GD image resource + * + * @see imagecreatetruecolor() + * @var resource|\GdImage + * + * @phan-suppress PhanUndeclaredTypeProperty + */ + protected $image; + + /** + * @inheritDoc + * + * @throws \chillerlan\QRCode\QRCodeException + */ + public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){ + + if(!extension_loaded('gd')){ + throw new QRCodeException('ext-gd not loaded'); // @codeCoverageIgnore + } + + parent::__construct($options, $matrix); + } + + /** + * @inheritDoc + */ + protected function setModuleValues():void{ + + foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){ + $v = $this->options->moduleValues[$M_TYPE] ?? null; + + if(!is_array($v) || count($v) < 3){ + $this->moduleValues[$M_TYPE] = $defaultValue + ? [0, 0, 0] + : [255, 255, 255]; + } + else{ + $this->moduleValues[$M_TYPE] = array_values($v); + } + + } + + } + + /** + * @inheritDoc + * + * @return string|resource|\GdImage + * + * @phan-suppress PhanUndeclaredTypeReturnType, PhanTypeMismatchReturn + */ + public function dump(string $file = null){ + $file ??= $this->options->cachefile; + + $this->image = imagecreatetruecolor($this->length, $this->length); + + // avoid: Indirect modification of overloaded property $imageTransparencyBG has no effect + // https://stackoverflow.com/a/10455217 + $tbg = $this->options->imageTransparencyBG; + /** @phan-suppress-next-line PhanParamTooFewInternalUnpack */ + $background = imagecolorallocate($this->image, ...$tbg); + + if((bool)$this->options->imageTransparent && in_array($this->options->outputType, $this::TRANSPARENCY_TYPES, true)){ + imagecolortransparent($this->image, $background); + } + + imagefilledrectangle($this->image, 0, 0, $this->length, $this->length, $background); + + foreach($this->matrix->matrix() as $y => $row){ + foreach($row as $x => $M_TYPE){ + $this->setPixel($x, $y, $this->moduleValues[$M_TYPE]); + } + } + + if($this->options->returnResource){ + return $this->image; + } + + $imageData = $this->dumpImage(); + + if($file !== null){ + $this->saveToFile($imageData, $file); + } + + if($this->options->imageBase64){ + $imageData = sprintf('data:image/%s;base64,%s', $this->options->outputType, base64_encode($imageData)); + } + + return $imageData; + } + + /** + * Creates a single QR pixel with the given settings + */ + protected function setPixel(int $x, int $y, array $rgb):void{ + imagefilledrectangle( + $this->image, + $x * $this->scale, + $y * $this->scale, + ($x + 1) * $this->scale, + ($y + 1) * $this->scale, + /** @phan-suppress-next-line PhanParamTooFewInternalUnpack */ + imagecolorallocate($this->image, ...$rgb) + ); + } + + /** + * Creates the final image by calling the desired GD output function + * + * @throws \chillerlan\QRCode\Output\QRCodeOutputException + */ + protected function dumpImage():string{ + ob_start(); + + try{ + call_user_func([$this, $this->outputMode ?? $this->defaultMode]); + } + // not going to cover edge cases + // @codeCoverageIgnoreStart + catch(Exception $e){ + throw new QRCodeOutputException($e->getMessage()); + } + // @codeCoverageIgnoreEnd + + $imageData = ob_get_contents(); + imagedestroy($this->image); + + ob_end_clean(); + + return $imageData; + } + + /** + * PNG output + * + * @return void + */ + protected function png():void{ + imagepng( + $this->image, + null, + in_array($this->options->pngCompression, range(-1, 9), true) + ? $this->options->pngCompression + : -1 + ); + } + + /** + * Jiff - like... JitHub! + * + * @return void + */ + protected function gif():void{ + imagegif($this->image); + } + + /** + * JPG output + * + * @return void + */ + protected function jpg():void{ + imagejpeg( + $this->image, + null, + in_array($this->options->jpegQuality, range(0, 100), true) + ? $this->options->jpegQuality + : 85 + ); + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRImagick.php b/vendor/chillerlan/php-qrcode/src/Output/QRImagick.php new file mode 100644 index 000000000..49516d30e --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRImagick.php @@ -0,0 +1,119 @@ +<?php +/** + * Class QRImagick + * + * @filesource QRImagick.php + * @created 04.07.2018 + * @package chillerlan\QRCode\Output + * @author smiley <smiley@chillerlan.net> + * @copyright 2018 smiley + * @license MIT + * + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\Data\QRMatrix; +use chillerlan\QRCode\QRCodeException; +use chillerlan\Settings\SettingsContainerInterface; +use Imagick, ImagickDraw, ImagickPixel; + +use function extension_loaded, is_string; + +/** + * ImageMagick output module (requires ext-imagick) + * + * @see http://php.net/manual/book.imagick.php + * @see http://phpimagick.com + */ +class QRImagick extends QROutputAbstract{ + + protected Imagick $imagick; + + /** + * @inheritDoc + */ + public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){ + + if(!extension_loaded('imagick')){ + throw new QRCodeException('ext-imagick not loaded'); // @codeCoverageIgnore + } + + parent::__construct($options, $matrix); + } + + /** + * @inheritDoc + */ + protected function setModuleValues():void{ + + foreach($this::DEFAULT_MODULE_VALUES as $type => $defaultValue){ + $v = $this->options->moduleValues[$type] ?? null; + + if(!is_string($v)){ + $this->moduleValues[$type] = $defaultValue + ? new ImagickPixel($this->options->markupDark) + : new ImagickPixel($this->options->markupLight); + } + else{ + $this->moduleValues[$type] = new ImagickPixel($v); + } + } + } + + /** + * @inheritDoc + * + * @return string|\Imagick + */ + public function dump(string $file = null){ + $file ??= $this->options->cachefile; + $this->imagick = new Imagick; + + $this->imagick->newImage( + $this->length, + $this->length, + new ImagickPixel($this->options->imagickBG ?? 'transparent'), + $this->options->imagickFormat + ); + + $this->drawImage(); + + if($this->options->returnResource){ + return $this->imagick; + } + + $imageData = $this->imagick->getImageBlob(); + + if($file !== null){ + $this->saveToFile($imageData, $file); + } + + return $imageData; + } + + /** + * Creates the QR image via ImagickDraw + */ + protected function drawImage():void{ + $draw = new ImagickDraw; + + foreach($this->matrix->matrix() as $y => $row){ + foreach($row as $x => $M_TYPE){ + $draw->setStrokeColor($this->moduleValues[$M_TYPE]); + $draw->setFillColor($this->moduleValues[$M_TYPE]); + $draw->rectangle( + $x * $this->scale, + $y * $this->scale, + ($x + 1) * $this->scale, + ($y + 1) * $this->scale + ); + + } + } + + $this->imagick->drawImage($draw); + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php b/vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php new file mode 100644 index 000000000..06d6e88cb --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php @@ -0,0 +1,160 @@ +<?php +/** + * Class QRMarkup + * + * @filesource QRMarkup.php + * @created 17.12.2016 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2016 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\QRCode; + +use function is_string, sprintf, strip_tags, trim; + +/** + * Converts the matrix into markup types: HTML, SVG, ... + */ +class QRMarkup extends QROutputAbstract{ + + protected string $defaultMode = QRCode::OUTPUT_MARKUP_SVG; + + /** + * @see \sprintf() + */ + protected string $svgHeader = '<svg xmlns="http://www.w3.org/2000/svg" class="qr-svg %1$s" '. + 'style="width: 100%%; height: auto;" viewBox="0 0 %2$d %2$d">'; + + /** + * @inheritDoc + */ + protected function setModuleValues():void{ + + foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){ + $v = $this->options->moduleValues[$M_TYPE] ?? null; + + if(!is_string($v)){ + $this->moduleValues[$M_TYPE] = $defaultValue + ? $this->options->markupDark + : $this->options->markupLight; + } + else{ + $this->moduleValues[$M_TYPE] = trim(strip_tags($v), '\'"'); + } + + } + + } + + /** + * HTML output + */ + protected function html(string $file = null):string{ + + $html = empty($this->options->cssClass) + ? '<div>' + : '<div class="'.$this->options->cssClass.'">'; + + $html .= $this->options->eol; + + foreach($this->matrix->matrix() as $row){ + $html .= '<div>'; + + foreach($row as $M_TYPE){ + $html .= '<span style="background: '.$this->moduleValues[$M_TYPE].';"></span>'; + } + + $html .= '</div>'.$this->options->eol; + } + + $html .= '</div>'.$this->options->eol; + + if($file !== null){ + return '<!DOCTYPE html>'. + '<head><meta charset="UTF-8"><title>QR Code</title></head>'. + '<body>'.$this->options->eol.$html.'</body>'; + } + + return $html; + } + + /** + * SVG output + * + * @see https://github.com/codemasher/php-qrcode/pull/5 + */ + protected function svg(string $file = null):string{ + $matrix = $this->matrix->matrix(); + + $svg = sprintf($this->svgHeader, $this->options->cssClass, $this->options->svgViewBoxSize ?? $this->moduleCount) + .$this->options->eol + .'<defs>'.$this->options->svgDefs.'</defs>' + .$this->options->eol; + + foreach($this->moduleValues as $M_TYPE => $value){ + $path = ''; + + foreach($matrix as $y => $row){ + //we'll combine active blocks within a single row as a lightweight compression technique + $start = null; + $count = 0; + + foreach($row as $x => $module){ + + if($module === $M_TYPE){ + $count++; + + if($start === null){ + $start = $x; + } + + if(isset($row[$x + 1])){ + continue; + } + } + + if($count > 0){ + $len = $count; + $start ??= 0; // avoid type coercion in sprintf() - phan happy + + $path .= sprintf('M%s %s h%s v1 h-%sZ ', $start, $y, $len, $len); + + // reset count + $count = 0; + $start = null; + } + + } + + } + + if(!empty($path)){ + $svg .= sprintf( + '<path class="qr-%s %s" stroke="transparent" fill="%s" fill-opacity="%s" d="%s" />', + $M_TYPE, $this->options->cssClass, $value, $this->options->svgOpacity, $path + ); + } + + } + + // close svg + $svg .= '</svg>'.$this->options->eol; + + // if saving to file, append the correct headers + if($file !== null){ + return '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'. + $this->options->eol.$svg; + } + + if($this->options->imageBase64){ + $svg = sprintf('data:image/svg+xml;base64,%s', base64_encode($svg)); + } + + return $svg; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php b/vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php new file mode 100644 index 000000000..d4ed3d0c9 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php @@ -0,0 +1,129 @@ +<?php +/** + * Class QROutputAbstract + * + * @filesource QROutputAbstract.php + * @created 09.12.2015 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\{Data\QRMatrix, QRCode}; +use chillerlan\Settings\SettingsContainerInterface; + +use function call_user_func_array, dirname, file_put_contents, get_called_class, in_array, is_writable, sprintf; + +/** + * common output abstract + */ +abstract class QROutputAbstract implements QROutputInterface{ + + /** + * the current size of the QR matrix + * + * @see \chillerlan\QRCode\Data\QRMatrix::size() + */ + protected int $moduleCount; + + /** + * the current output mode + * + * @see \chillerlan\QRCode\QROptions::$outputType + */ + protected string $outputMode; + + /** + * the default output mode of the current output module + */ + protected string $defaultMode; + + /** + * the current scaling for a QR pixel + * + * @see \chillerlan\QRCode\QROptions::$scale + */ + protected int $scale; + + /** + * the side length of the QR image (modules * scale) + */ + protected int $length; + + /** + * an (optional) array of color values for the several QR matrix parts + */ + protected array $moduleValues; + + /** + * the (filled) data matrix object + */ + protected QRMatrix $matrix; + + /** + * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions + */ + protected SettingsContainerInterface $options; + + /** + * QROutputAbstract constructor. + */ + public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){ + $this->options = $options; + $this->matrix = $matrix; + $this->moduleCount = $this->matrix->size(); + $this->scale = $this->options->scale; + $this->length = $this->moduleCount * $this->scale; + + $class = get_called_class(); + + if(isset(QRCode::OUTPUT_MODES[$class]) && in_array($this->options->outputType, QRCode::OUTPUT_MODES[$class])){ + $this->outputMode = $this->options->outputType; + } + + $this->setModuleValues(); + } + + /** + * Sets the initial module values (clean-up & defaults) + */ + abstract protected function setModuleValues():void; + + /** + * saves the qr data to a file + * + * @see file_put_contents() + * @see \chillerlan\QRCode\QROptions::cachefile + * + * @throws \chillerlan\QRCode\Output\QRCodeOutputException + */ + protected function saveToFile(string $data, string $file):bool{ + + if(!is_writable(dirname($file))){ + throw new QRCodeOutputException(sprintf('Could not write data to cache file: %s', $file)); + } + + return (bool)file_put_contents($file, $data); + } + + /** + * @inheritDoc + */ + public function dump(string $file = null){ + $file ??= $this->options->cachefile; + + // call the built-in output method with the optional file path as parameter + // to make the called method aware if a cache file was given + $data = call_user_func_array([$this, $this->outputMode ?? $this->defaultMode], [$file]); + + if($file !== null){ + $this->saveToFile($data, $file); + } + + return $data; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QROutputInterface.php b/vendor/chillerlan/php-qrcode/src/Output/QROutputInterface.php new file mode 100644 index 000000000..b07b8e7a5 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QROutputInterface.php @@ -0,0 +1,54 @@ +<?php +/** + * Interface QROutputInterface, + * + * @filesource QROutputInterface.php + * @created 02.12.2015 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\Data\QRMatrix; + +/** + * Converts the data matrix into readable output + */ +interface QROutputInterface{ + + const DEFAULT_MODULE_VALUES = [ + // light + QRMatrix::M_NULL => false, // 0 + QRMatrix::M_DATA => false, // 4 + QRMatrix::M_FINDER => false, // 6 + QRMatrix::M_SEPARATOR => false, // 8 + QRMatrix::M_ALIGNMENT => false, // 10 + QRMatrix::M_TIMING => false, // 12 + QRMatrix::M_FORMAT => false, // 14 + QRMatrix::M_VERSION => false, // 16 + QRMatrix::M_QUIETZONE => false, // 18 + QRMatrix::M_LOGO => false, // 20 + QRMatrix::M_TEST => false, // 255 + // dark + QRMatrix::M_DARKMODULE << 8 => true, // 512 + QRMatrix::M_DATA << 8 => true, // 1024 + QRMatrix::M_FINDER << 8 => true, // 1536 + QRMatrix::M_ALIGNMENT << 8 => true, // 2560 + QRMatrix::M_TIMING << 8 => true, // 3072 + QRMatrix::M_FORMAT << 8 => true, // 3584 + QRMatrix::M_VERSION << 8 => true, // 4096 + QRMatrix::M_FINDER_DOT << 8 => true, // 5632 + QRMatrix::M_TEST << 8 => true, // 65280 + ]; + + /** + * generates the output, optionally dumps it to a file, and returns it + * + * @return mixed + */ + public function dump(string $file = null); + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRString.php b/vendor/chillerlan/php-qrcode/src/Output/QRString.php new file mode 100644 index 000000000..3ed5153e1 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRString.php @@ -0,0 +1,76 @@ +<?php +/** + * Class QRString + * + * @filesource QRString.php + * @created 05.12.2015 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + * + * @noinspection PhpUnusedParameterInspection + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\QRCode; + +use function implode, is_string, json_encode; + +/** + * Converts the matrix data into string types + */ +class QRString extends QROutputAbstract{ + + protected string $defaultMode = QRCode::OUTPUT_STRING_TEXT; + + /** + * @inheritDoc + */ + protected function setModuleValues():void{ + + foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){ + $v = $this->options->moduleValues[$M_TYPE] ?? null; + + if(!is_string($v)){ + $this->moduleValues[$M_TYPE] = $defaultValue + ? $this->options->textDark + : $this->options->textLight; + } + else{ + $this->moduleValues[$M_TYPE] = $v; + } + + } + + } + + /** + * string output + */ + protected function text(string $file = null):string{ + $str = []; + + foreach($this->matrix->matrix() as $row){ + $r = []; + + foreach($row as $M_TYPE){ + $r[] = $this->moduleValues[$M_TYPE]; + } + + $str[] = implode('', $r); + } + + return implode($this->options->eol, $str); + } + + /** + * JSON output + */ + protected function json(string $file = null):string{ + return json_encode($this->matrix->matrix()); + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/QRCode.php b/vendor/chillerlan/php-qrcode/src/QRCode.php new file mode 100755 index 000000000..908030feb --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/QRCode.php @@ -0,0 +1,313 @@ +<?php +/** + * Class QRCode + * + * @filesource QRCode.php + * @created 26.11.2015 + * @package chillerlan\QRCode + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode; + +use chillerlan\QRCode\Data\{ + AlphaNum, Byte, Kanji, MaskPatternTester, Number, QRCodeDataException, QRDataInterface, QRMatrix +}; +use chillerlan\QRCode\Output\{ + QRCodeOutputException, QRFpdf, QRImage, QRImagick, QRMarkup, QROutputInterface, QRString +}; +use chillerlan\Settings\SettingsContainerInterface; + +use function call_user_func_array, class_exists, in_array, ord, strlen, strtolower, str_split; + +/** + * Turns a text string into a Model 2 QR Code + * + * @see https://github.com/kazuhikoarase/qrcode-generator/tree/master/php + * @see http://www.qrcode.com/en/codes/model12.html + * @see https://www.swisseduc.ch/informatik/theoretische_informatik/qr_codes/docs/qr_standard.pdf + * @see https://en.wikipedia.org/wiki/QR_code + * @see http://www.thonky.com/qr-code-tutorial/ + */ +class QRCode{ + + /** @var int */ + public const VERSION_AUTO = -1; + /** @var int */ + public const MASK_PATTERN_AUTO = -1; + + // ISO/IEC 18004:2000 Table 2 + + /** @var int */ + public const DATA_NUMBER = 0b0001; + /** @var int */ + public const DATA_ALPHANUM = 0b0010; + /** @var int */ + public const DATA_BYTE = 0b0100; + /** @var int */ + public const DATA_KANJI = 0b1000; + + /** + * References to the keys of the following tables: + * + * @see \chillerlan\QRCode\Data\QRDataInterface::MAX_LENGTH + * + * @var int[] + */ + public const DATA_MODES = [ + self::DATA_NUMBER => 0, + self::DATA_ALPHANUM => 1, + self::DATA_BYTE => 2, + self::DATA_KANJI => 3, + ]; + + // ISO/IEC 18004:2000 Tables 12, 25 + + /** @var int */ + public const ECC_L = 0b01; // 7%. + /** @var int */ + public const ECC_M = 0b00; // 15%. + /** @var int */ + public const ECC_Q = 0b11; // 25%. + /** @var int */ + public const ECC_H = 0b10; // 30%. + + /** + * References to the keys of the following tables: + * + * @see \chillerlan\QRCode\Data\QRDataInterface::MAX_BITS + * @see \chillerlan\QRCode\Data\QRDataInterface::RSBLOCKS + * @see \chillerlan\QRCode\Data\QRMatrix::formatPattern + * + * @var int[] + */ + public const ECC_MODES = [ + self::ECC_L => 0, + self::ECC_M => 1, + self::ECC_Q => 2, + self::ECC_H => 3, + ]; + + /** @var string */ + public const OUTPUT_MARKUP_HTML = 'html'; + /** @var string */ + public const OUTPUT_MARKUP_SVG = 'svg'; + /** @var string */ + public const OUTPUT_IMAGE_PNG = 'png'; + /** @var string */ + public const OUTPUT_IMAGE_JPG = 'jpg'; + /** @var string */ + public const OUTPUT_IMAGE_GIF = 'gif'; + /** @var string */ + public const OUTPUT_STRING_JSON = 'json'; + /** @var string */ + public const OUTPUT_STRING_TEXT = 'text'; + /** @var string */ + public const OUTPUT_IMAGICK = 'imagick'; + /** @var string */ + public const OUTPUT_FPDF = 'fpdf'; + /** @var string */ + public const OUTPUT_CUSTOM = 'custom'; + + /** + * Map of built-in output modules => capabilities + * + * @var string[][] + */ + public const OUTPUT_MODES = [ + QRMarkup::class => [ + self::OUTPUT_MARKUP_SVG, + self::OUTPUT_MARKUP_HTML, + ], + QRImage::class => [ + self::OUTPUT_IMAGE_PNG, + self::OUTPUT_IMAGE_GIF, + self::OUTPUT_IMAGE_JPG, + ], + QRString::class => [ + self::OUTPUT_STRING_JSON, + self::OUTPUT_STRING_TEXT, + ], + QRImagick::class => [ + self::OUTPUT_IMAGICK, + ], + QRFpdf::class => [ + self::OUTPUT_FPDF + ] + ]; + + /** + * Map of data mode => interface + * + * @var string[] + */ + protected const DATA_INTERFACES = [ + 'number' => Number::class, + 'alphanum' => AlphaNum::class, + 'kanji' => Kanji::class, + 'byte' => Byte::class, + ]; + + /** + * The settings container + * + * @var \chillerlan\QRCode\QROptions|\chillerlan\Settings\SettingsContainerInterface + */ + protected SettingsContainerInterface $options; + + /** + * The selected data interface (Number, AlphaNum, Kanji, Byte) + */ + protected QRDataInterface $dataInterface; + + /** + * QRCode constructor. + * + * Sets the options instance, determines the current mb-encoding and sets it to UTF-8 + */ + public function __construct(SettingsContainerInterface $options = null){ + $this->options = $options ?? new QROptions; + } + + /** + * Renders a QR Code for the given $data and QROptions + * + * @return mixed + */ + public function render(string $data, string $file = null){ + return $this->initOutputInterface($data)->dump($file); + } + + /** + * Returns a QRMatrix object for the given $data and current QROptions + * + * @throws \chillerlan\QRCode\Data\QRCodeDataException + */ + public function getMatrix(string $data):QRMatrix{ + + if(empty($data)){ + throw new QRCodeDataException('QRCode::getMatrix() No data given.'); + } + + $this->dataInterface = $this->initDataInterface($data); + + $maskPattern = $this->options->maskPattern === $this::MASK_PATTERN_AUTO + ? (new MaskPatternTester($this->dataInterface))->getBestMaskPattern() + : $this->options->maskPattern; + + $matrix = $this->dataInterface->initMatrix($maskPattern); + + if((bool)$this->options->addQuietzone){ + $matrix->setQuietZone($this->options->quietzoneSize); + } + + return $matrix; + } + + /** + * returns a fresh QRDataInterface for the given $data + * + * @throws \chillerlan\QRCode\Data\QRCodeDataException + */ + public function initDataInterface(string $data):QRDataInterface{ + + // allow forcing the data mode + // see https://github.com/chillerlan/php-qrcode/issues/39 + $interface = $this::DATA_INTERFACES[strtolower($this->options->dataModeOverride)] ?? null; + + if($interface !== null){ + return new $interface($this->options, $data); + } + + foreach($this::DATA_INTERFACES as $mode => $dataInterface){ + + if(call_user_func_array([$this, 'is'.$mode], [$data])){ + return new $dataInterface($this->options, $data); + } + + } + + throw new QRCodeDataException('invalid data type'); // @codeCoverageIgnore + } + + /** + * returns a fresh (built-in) QROutputInterface + * + * @throws \chillerlan\QRCode\Output\QRCodeOutputException + */ + protected function initOutputInterface(string $data):QROutputInterface{ + + if($this->options->outputType === $this::OUTPUT_CUSTOM && class_exists($this->options->outputInterface)){ + /** @phan-suppress-next-line PhanTypeExpectedObjectOrClassName */ + return new $this->options->outputInterface($this->options, $this->getMatrix($data)); + } + + foreach($this::OUTPUT_MODES as $outputInterface => $modes){ + + if(in_array($this->options->outputType, $modes, true) && class_exists($outputInterface)){ + return new $outputInterface($this->options, $this->getMatrix($data)); + } + + } + + throw new QRCodeOutputException('invalid output type'); + } + + /** + * checks if a string qualifies as numeric + */ + public function isNumber(string $string):bool{ + return $this->checkString($string, QRDataInterface::CHAR_MAP_NUMBER); + } + + /** + * checks if a string qualifies as alphanumeric + */ + public function isAlphaNum(string $string):bool{ + return $this->checkString($string, QRDataInterface::CHAR_MAP_ALPHANUM); + } + + /** + * checks is a given $string matches the characters of a given $charmap, returns false on the first invalid occurence. + */ + protected function checkString(string $string, array $charmap):bool{ + + foreach(str_split($string) as $chr){ + if(!isset($charmap[$chr])){ + return false; + } + } + + return true; + } + + /** + * checks if a string qualifies as Kanji + */ + public function isKanji(string $string):bool{ + $i = 0; + $len = strlen($string); + + while($i + 1 < $len){ + $c = ((0xff & ord($string[$i])) << 8) | (0xff & ord($string[$i + 1])); + + if(!($c >= 0x8140 && $c <= 0x9FFC) && !($c >= 0xE040 && $c <= 0xEBBF)){ + return false; + } + + $i += 2; + } + + return $i >= $len; + } + + /** + * a dummy + */ + public function isByte(string $data):bool{ + return $data !== ''; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/QRCodeException.php b/vendor/chillerlan/php-qrcode/src/QRCodeException.php new file mode 100644 index 000000000..737a0803e --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/QRCodeException.php @@ -0,0 +1,20 @@ +<?php +/** + * Class QRCodeException + * + * @filesource QRCodeException.php + * @created 27.11.2015 + * @package chillerlan\QRCode + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode; + +use Exception; + +/** + * An exception container + */ +class QRCodeException extends Exception{} diff --git a/vendor/chillerlan/php-qrcode/src/QROptions.php b/vendor/chillerlan/php-qrcode/src/QROptions.php new file mode 100644 index 000000000..e36f6701a --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/QROptions.php @@ -0,0 +1,54 @@ +<?php +/** + * Class QROptions + * + * @filesource QROptions.php + * @created 08.12.2015 + * @package chillerlan\QRCode + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode; + +use chillerlan\Settings\SettingsContainerAbstract; + +/** + * The QRCode settings container + * + * @property int $version + * @property int $versionMin + * @property int $versionMax + * @property int $eccLevel + * @property int $maskPattern + * @property bool $addQuietzone + * @property int $quietzoneSize + * @property string|null $dataModeOverride + * @property string $outputType + * @property string|null $outputInterface + * @property string|null $cachefile + * @property string $eol + * @property int $scale + * @property string $cssClass + * @property float $svgOpacity + * @property string $svgDefs + * @property int $svgViewBoxSize + * @property string $textDark + * @property string $textLight + * @property string $markupDark + * @property string $markupLight + * @property bool $returnResource + * @property bool $imageBase64 + * @property bool $imageTransparent + * @property array $imageTransparencyBG + * @property int $pngCompression + * @property int $jpegQuality + * @property string $imagickFormat + * @property string|null $imagickBG + * @property string $fpdfMeasureUnit + * @property array|null $moduleValues + */ +class QROptions extends SettingsContainerAbstract{ + use QROptionsTrait; +} diff --git a/vendor/chillerlan/php-qrcode/src/QROptionsTrait.php b/vendor/chillerlan/php-qrcode/src/QROptionsTrait.php new file mode 100644 index 000000000..74c384b13 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/QROptionsTrait.php @@ -0,0 +1,341 @@ +<?php +/** + * Trait QROptionsTrait + * + * @filesource QROptionsTrait.php + * @created 10.03.2018 + * @package chillerlan\QRCode + * @author smiley <smiley@chillerlan.net> + * @copyright 2018 smiley + * @license MIT + * + * @noinspection PhpUnused + */ + +namespace chillerlan\QRCode; + +use function array_values, count, in_array, is_numeric, max, min, sprintf, strtolower; + +/** + * The QRCode plug-in settings & setter functionality + */ +trait QROptionsTrait{ + + /** + * QR Code version number + * + * [1 ... 40] or QRCode::VERSION_AUTO + */ + protected int $version = QRCode::VERSION_AUTO; + + /** + * Minimum QR version + * + * if $version = QRCode::VERSION_AUTO + */ + protected int $versionMin = 1; + + /** + * Maximum QR version + */ + protected int $versionMax = 40; + + /** + * Error correct level + * + * QRCode::ECC_X where X is: + * + * - L => 7% + * - M => 15% + * - Q => 25% + * - H => 30% + */ + protected int $eccLevel = QRCode::ECC_L; + + /** + * Mask Pattern to use + * + * [0...7] or QRCode::MASK_PATTERN_AUTO + */ + protected int $maskPattern = QRCode::MASK_PATTERN_AUTO; + + /** + * Add a "quiet zone" (margin) according to the QR code spec + */ + protected bool $addQuietzone = true; + + /** + * Size of the quiet zone + * + * internally clamped to [0 ... $moduleCount / 2], defaults to 4 modules + */ + protected int $quietzoneSize = 4; + + /** + * Use this to circumvent the data mode detection and force the usage of the given mode. + * + * valid modes are: Number, AlphaNum, Kanji, Byte (case insensitive) + * + * @see https://github.com/chillerlan/php-qrcode/issues/39 + * @see https://github.com/chillerlan/php-qrcode/issues/97 (changed default value to '') + */ + protected string $dataModeOverride = ''; + + /** + * The output type + * + * - QRCode::OUTPUT_MARKUP_XXXX where XXXX = HTML, SVG + * - QRCode::OUTPUT_IMAGE_XXX where XXX = PNG, GIF, JPG + * - QRCode::OUTPUT_STRING_XXXX where XXXX = TEXT, JSON + * - QRCode::OUTPUT_CUSTOM + */ + protected string $outputType = QRCode::OUTPUT_IMAGE_PNG; + + /** + * the FQCN of the custom QROutputInterface if $outputType is set to QRCode::OUTPUT_CUSTOM + */ + protected ?string $outputInterface = null; + + /** + * /path/to/cache.file + */ + protected ?string $cachefile = null; + + /** + * newline string [HTML, SVG, TEXT] + */ + protected string $eol = PHP_EOL; + + /** + * size of a QR code pixel [SVG, IMAGE_*], HTML via CSS + */ + protected int $scale = 5; + + /** + * a common css class + */ + protected string $cssClass = ''; + + /** + * SVG opacity + */ + protected float $svgOpacity = 1.0; + + /** + * anything between <defs> + * + * @see https://developer.mozilla.org/docs/Web/SVG/Element/defs + */ + protected string $svgDefs = '<style>rect{shape-rendering:crispEdges}</style>'; + + /** + * SVG viewBox size. a single integer number which defines width/height of the viewBox attribute. + * + * viewBox="0 0 x x" + * + * @see https://css-tricks.com/scale-svg/#article-header-id-3 + */ + protected ?int $svgViewBoxSize = null; + + /** + * string substitute for dark + */ + protected string $textDark = '🔴'; + + /** + * string substitute for light + */ + protected string $textLight = 'â•'; + + /** + * markup substitute for dark (CSS value) + */ + protected string $markupDark = '#000'; + + /** + * markup substitute for light (CSS value) + */ + protected string $markupLight = '#fff'; + + /** + * Return the image resource instead of a render if applicable. + * This option overrides other output options, such as $cachefile and $imageBase64. + * + * Supported by the following modules: + * + * - QRImage: resource (PHP < 8), GdImage + * - QRImagick: Imagick + * - QRFpdf: FPDF + * + * @see \chillerlan\QRCode\Output\QROutputInterface::dump() + * + * @var bool + */ + protected bool $returnResource = false; + + /** + * toggle base64 or raw image data + */ + protected bool $imageBase64 = true; + + /** + * toggle transparency, not supported by jpg + */ + protected bool $imageTransparent = true; + + /** + * @see imagecolortransparent() + * + * [R, G, B] + */ + protected array $imageTransparencyBG = [255, 255, 255]; + + /** + * @see imagepng() + */ + protected int $pngCompression = -1; + + /** + * @see imagejpeg() + */ + protected int $jpegQuality = 85; + + /** + * Imagick output format + * + * @see \Imagick::setType() + */ + protected string $imagickFormat = 'png'; + + /** + * Imagick background color (defaults to "transparent") + * + * @see \ImagickPixel::__construct() + */ + protected ?string $imagickBG = null; + + /** + * Measurement unit for FPDF output: pt, mm, cm, in (defaults to "pt") + * + * @see \FPDF::__construct() + */ + protected string $fpdfMeasureUnit = 'pt'; + + /** + * Module values map + * + * - HTML, IMAGICK: #ABCDEF, cssname, rgb(), rgba()... + * - IMAGE: [63, 127, 255] // R, G, B + */ + protected ?array $moduleValues = null; + + /** + * clamp min/max version number + */ + protected function setMinMaxVersion(int $versionMin, int $versionMax):void{ + $min = max(1, min(40, $versionMin)); + $max = max(1, min(40, $versionMax)); + + $this->versionMin = min($min, $max); + $this->versionMax = max($min, $max); + } + + /** + * sets the minimum version number + */ + protected function set_versionMin(int $version):void{ + $this->setMinMaxVersion($version, $this->versionMax); + } + + /** + * sets the maximum version number + */ + protected function set_versionMax(int $version):void{ + $this->setMinMaxVersion($this->versionMin, $version); + } + + /** + * sets the error correction level + * + * @throws \chillerlan\QRCode\QRCodeException + */ + protected function set_eccLevel(int $eccLevel):void{ + + if(!isset(QRCode::ECC_MODES[$eccLevel])){ + throw new QRCodeException(sprintf('Invalid error correct level: %s', $eccLevel)); + } + + $this->eccLevel = $eccLevel; + } + + /** + * sets/clamps the mask pattern + */ + protected function set_maskPattern(int $maskPattern):void{ + + if($maskPattern !== QRCode::MASK_PATTERN_AUTO){ + $this->maskPattern = max(0, min(7, $maskPattern)); + } + + } + + /** + * sets the transparency background color + * + * @throws \chillerlan\QRCode\QRCodeException + */ + protected function set_imageTransparencyBG(array $imageTransparencyBG):void{ + + // invalid value - set to white as default + if(count($imageTransparencyBG) < 3){ + $this->imageTransparencyBG = [255, 255, 255]; + + return; + } + + foreach($imageTransparencyBG as $k => $v){ + + // cut off exceeding items + if($k > 2){ + break; + } + + if(!is_numeric($v)){ + throw new QRCodeException('Invalid RGB value.'); + } + + // clamp the values + $this->imageTransparencyBG[$k] = max(0, min(255, (int)$v)); + } + + // use the array values to not run into errors with the spread operator (...$arr) + $this->imageTransparencyBG = array_values($this->imageTransparencyBG); + } + + /** + * sets/clamps the version number + */ + protected function set_version(int $version):void{ + + if($version !== QRCode::VERSION_AUTO){ + $this->version = max(1, min(40, $version)); + } + + } + + /** + * sets the FPDF measurement unit + * + * @codeCoverageIgnore + */ + protected function set_fpdfMeasureUnit(string $unit):void{ + $unit = strtolower($unit); + + if(in_array($unit, ['cm', 'in', 'mm', 'pt'], true)){ + $this->fpdfMeasureUnit = $unit; + } + + // @todo throw or ignore silently? + } + +} |