diff options
Diffstat (limited to 'vendor/chillerlan/php-qrcode/src/QRCode.php')
-rwxr-xr-x | vendor/chillerlan/php-qrcode/src/QRCode.php | 313 |
1 files changed, 313 insertions, 0 deletions
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 !== ''; + } + +} |