+
+
\ No newline at end of file
diff --git a/vendor/chillerlan/php-qrcode/.idea/inspectionProfiles/Project_Default.xml b/vendor/chillerlan/php-qrcode/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..14c92c1
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vendor/chillerlan/php-qrcode/LICENSE-ASL-2.0 b/vendor/chillerlan/php-qrcode/LICENSE-ASL-2.0
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/LICENSE-ASL-2.0
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/chillerlan/php-qrcode/LICENSE-MIT b/vendor/chillerlan/php-qrcode/LICENSE-MIT
new file mode 100644
index 0000000..dac8528
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/LICENSE-MIT
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Smiley
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/chillerlan/php-qrcode/NOTICE b/vendor/chillerlan/php-qrcode/NOTICE
new file mode 100644
index 0000000..596130b
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/NOTICE
@@ -0,0 +1,40 @@
+Parts of this code are ported to php from the ZXing project
+and licensed under the Apache License, Version 2.0.
+
+Copyright 2007 ZXing authors (https://github.com/zxing/zxing),
+Copyright (c) Ashot Khanamiryan (https://github.com/khanamiryan/php-qrcode-detector-decoder)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+List of affected files:
+
+src/Common/ECICharset.php
+src/Common/GenericGFPoly.php
+src/Common/GF256.php
+src/Common/LuminanceSourceAbstract.php
+src/Common/MaskPattern.php
+src/Decoder/Binarizer.php
+src/Decoder/BitMatrix.php
+src/Decoder/Decoder.php
+src/Decoder/DecoderResult.php
+src/Decoder/ReedSolomonDecoder.php
+src/Detector/AlignmentPattern.php
+src/Detector/AlignmentPatternFinder.php
+src/Detector/Detector.php
+src/Detector/FinderPattern.php
+src/Detector/FinderPatternFinder.php
+src/Detector/GridSampler.php
+src/Detector/PerspectiveTransform.php
+src/Detector/ResultPoint.php
+tests/Common/MaskPatternTest.php
diff --git a/vendor/chillerlan/php-qrcode/README.md b/vendor/chillerlan/php-qrcode/README.md
new file mode 100644
index 0000000..d2b69db
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/README.md
@@ -0,0 +1,168 @@
+# chillerlan/php-qrcode
+
+A PHP QR Code generator based on the [implementation by Kazuhiko Arase](https://github.com/kazuhikoarase/qrcode-generator), namespaced, cleaned up, improved and other stuff.
+It also features a QR Code reader based on a [PHP port](https://github.com/khanamiryan/php-qrcode-detector-decoder) of the [ZXing library](https://github.com/zxing/zxing).
+
+**Attention:** there is now also a javascript port: [chillerlan/js-qrcode](https://github.com/chillerlan/js-qrcode).
+
+[![PHP Version Support][php-badge]][php]
+[![Packagist version][packagist-badge]][packagist]
+[![Continuous Integration][gh-action-badge]][gh-action]
+[![CodeCov][coverage-badge]][coverage]
+[![Codacy][codacy-badge]][codacy]
+[![Packagist downloads][downloads-badge]][downloads]
+[![Documentation][readthedocs-badge]][readthedocs]
+
+[php-badge]: https://img.shields.io/packagist/php-v/chillerlan/php-qrcode?logo=php&color=8892BF
+[php]: https://www.php.net/supported-versions.php
+[packagist-badge]: https://img.shields.io/packagist/v/chillerlan/php-qrcode.svg?logo=packagist
+[packagist]: https://packagist.org/packages/chillerlan/php-qrcode
+[gh-action-badge]: https://img.shields.io/github/actions/workflow/status/chillerlan/php-qrcode/ci.yml?branch=v5.0.x&logo=github
+[gh-action]: https://github.com/chillerlan/php-qrcode/actions/workflows/ci.yml?query=branch%3Amain
+[coverage-badge]: https://img.shields.io/codecov/c/github/chillerlan/php-qrcode/v5.0.x?logo=codecov
+[coverage]: https://app.codecov.io/gh/chillerlan/php-qrcode/tree/v5.0.x
+[codacy-badge]: https://img.shields.io/codacy/grade/edccfc4fe5a34b74b1c53ee03f097b8d/v5.0.x?logo=codacy
+[codacy]: https://app.codacy.com/gh/chillerlan/php-qrcode/dashboard?branch=v5.0.x
+[downloads-badge]: https://img.shields.io/packagist/dt/chillerlan/php-qrcode?logo=packagist
+[downloads]: https://packagist.org/packages/chillerlan/php-qrcode/stats
+[readthedocs-badge]: https://img.shields.io/readthedocs/php-qrcode/v5.0.x?logo=readthedocs
+[readthedocs]: https://php-qrcode.readthedocs.io/en/v5.0.x/
+
+## Overview
+
+### Features
+
+- Creation of [Model 2 QR Codes](https://www.qrcode.com/en/codes/model12.html), [Version 1 to 40](https://www.qrcode.com/en/about/version.html)
+- [ECC Levels](https://www.qrcode.com/en/about/error_correction.html) L/M/Q/H supported
+- Mixed mode support (encoding modes can be combined within a QR symbol). Supported modes:
+ - numeric
+ - alphanumeric
+ - 8-bit binary
+ - [ECI support](https://en.wikipedia.org/wiki/Extended_Channel_Interpretation)
+ - 13-bit double-byte:
+ - kanji (Japanese, Shift-JIS)
+ - hanzi (simplified Chinese, GB2312/GB18030) as [defined in GBT18284-2000](https://www.chinesestandard.net/PDF/English.aspx/GBT18284-2000)
+- Flexible, easily extensible output modules, built-in support for the following output formats:
+ - [GdImage](https://www.php.net/manual/book.image) (raster graphics: bmp, gif, jpeg, png, webp)
+ - [ImageMagick](https://www.php.net/manual/book.imagick) ([multiple supported image formats](https://imagemagick.org/script/formats.php))
+ - Markup types: SVG, HTML, etc.
+ - String types: JSON, plain text, etc.
+ - Encapsulated Postscript (EPS)
+ - PDF via [FPDF](https://github.com/setasign/fpdf)
+- QR Code reader (via GD and ImageMagick)
+
+
+### Requirements
+
+- PHP 7.4+
+ - [`ext-mbstring`](https://www.php.net/manual/book.mbstring.php)
+ - optional:
+ - [`ext-gd`](https://www.php.net/manual/book.image)
+ - [`ext-imagick`](https://github.com/Imagick/imagick) with [ImageMagick](https://imagemagick.org) installed
+ - [`ext-fileinfo`](https://www.php.net/manual/book.fileinfo.php) (required by `QRImagick` output)
+ - [`setasign/fpdf`](https://github.com/setasign/fpdf) for the PDF output module
+
+For the QRCode reader, either `ext-gd` or `ext-imagick` is required!
+
+
+## Documentation
+
+- The user manual is at https://php-qrcode.readthedocs.io/ ([sources](https://github.com/chillerlan/php-qrcode/tree/v5.0.x/docs))
+- An API documentation created with [phpDocumentor](https://www.phpdoc.org/) can be found at https://chillerlan.github.io/php-qrcode/
+- The documentation for the `QROptions` container can be found here: [chillerlan/php-settings-container](https://github.com/chillerlan/php-settings-container#readme)
+
+
+## Installation with [composer](https://getcomposer.org)
+
+See [the installation guide](https://php-qrcode.readthedocs.io/en/v5.0.x/Usage-Installation.html) for more info!
+
+
+### Terminal
+
+```
+composer require chillerlan/php-qrcode
+```
+
+
+### composer.json
+
+```json
+{
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "chillerlan/php-qrcode": "v5.0.x-dev#"
+ }
+}
+```
+
+Note: replace `v5.0.x-dev` with a [version constraint](https://getcomposer.org/doc/articles/versions.md#writing-version-constraints), e.g. `^4.3` - see [releases](https://github.com/chillerlan/php-qrcode/releases) for valid versions.
+
+
+## Quickstart
+
+We want to encode this URI for a mobile authenticator into a QRcode image:
+
+```php
+$data = 'otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net';
+
+// quick and simple:
+echo '';
+```
+
+Wait, what was that? Please again, slower! See [Advanced usage](https://php-qrcode.readthedocs.io/en/v5.0.x/Usage/Advanced-usage.html) in the manual.
+Also, have a look [in the examples folder](https://github.com/chillerlan/php-qrcode/tree/v5.0.x/examples) for some more usage examples.
+
+
+
+
+
+
+### Reading QR Codes
+
+Using the built-in QR Code reader is pretty straight-forward:
+
+```php
+// it's generally a good idea to wrap the reader in a try/catch block because it WILL throw eventually
+try{
+ $result = (new QRCode)->readFromFile('path/to/file.png'); // -> DecoderResult
+
+ // you can now use the result instance...
+ $content = $result->data;
+ $matrix = $result->getMatrix(); // -> QRMatrix
+
+ // ...or simply cast it to string to get the content:
+ $content = (string)$result;
+}
+catch(Throwable $e){
+ // oopsies!
+}
+```
+
+
+## Shameless advertising
+
+Hi, please check out some of my other projects that are way cooler than qrcodes!
+
+- [js-qrcode](https://github.com/chillerlan/js-qrcode) - a javascript port of this library
+- [php-authenticator](https://github.com/chillerlan/php-authenticator) - a Google Authenticator implementation (see [authenticator example](https://github.com/chillerlan/php-qrcode/blob/v5.0.x/examples/authenticator.php))
+- [php-httpinterface](https://github.com/chillerlan/php-httpinterface) - a PSR-7/15/17/18 implemetation
+- [php-oauth-core](https://github.com/chillerlan/php-oauth-core) - an OAuth 1/2 client library along with a bunch of [providers](https://github.com/chillerlan/php-oauth-providers)
+- [php-database](https://github.com/chillerlan/php-database) - a database client & querybuilder for MySQL, Postgres, SQLite, MSSQL, Firebird
+- [php-tootbot](https://github.com/php-tootbot/tootbot-template) - a Mastodon bot library (see [@dwil](https://github.com/php-tootbot/dwil))
+
+
+## Disclaimer!
+
+I don't take responsibility for molten CPUs, misled applications, failed log-ins etc.. Use at your own risk!
+
+
+### License notice
+
+- Parts of this code are [ported to PHP](https://github.com/codemasher/php-qrcode-decoder) from the [ZXing project](https://github.com/zxing/zxing) and licensed under the [Apache License, Version 2.0](./NOTICE).
+- [The documentation](https://github.com/chillerlan/php-qrcode/tree/v5.0.x/docs) is licensed under the [Creative Commons Attribution 4.0 International (CC BY 4.0) License](https://creativecommons.org/licenses/by/4.0/).
+
+
+### Trademark Notice
+
+The word "QR Code" is a registered trademark of *DENSO WAVE INCORPORATED*
+https://www.qrcode.com/en/faq.html#patentH2Title
diff --git a/vendor/chillerlan/php-qrcode/composer.json b/vendor/chillerlan/php-qrcode/composer.json
new file mode 100644
index 0000000..7e74be5
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/composer.json
@@ -0,0 +1,79 @@
+{
+ "name": "chillerlan/php-qrcode",
+ "description": "A QR code generator and reader with a user friendly API. PHP 7.4+",
+ "homepage": "https://github.com/chillerlan/php-qrcode",
+ "license": [
+ "MIT", "Apache-2.0"
+ ],
+ "type": "library",
+ "keywords": [
+ "QR code", "qrcode", "qr", "qrcode-generator", "phpqrcode", "qrcode-reader", "qr-reader"
+ ],
+ "authors": [
+ {
+ "name": "Kazuhiko Arase",
+ "homepage": "https://github.com/kazuhikoarase/qrcode-generator"
+ },
+ {
+ "name":"ZXing Authors",
+ "homepage": "https://github.com/zxing/zxing"
+ },
+ {
+ "name": "Ashot Khanamiryan",
+ "homepage": "https://github.com/khanamiryan/php-qrcode-detector-decoder"
+ },
+ {
+ "name": "Smiley",
+ "email": "smiley@chillerlan.net",
+ "homepage": "https://github.com/codemasher"
+ },
+ {
+ "name": "Contributors",
+ "homepage":"https://github.com/chillerlan/php-qrcode/graphs/contributors"
+ }
+ ],
+ "support": {
+ "docs": "https://php-qrcode.readthedocs.io",
+ "issues": "https://github.com/chillerlan/php-qrcode/issues",
+ "source": "https://github.com/chillerlan/php-qrcode"
+ },
+ "minimum-stability": "stable",
+ "prefer-stable": true,
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "ext-mbstring": "*",
+ "chillerlan/php-settings-container": "^2.1.4 || ^3.1"
+ },
+ "require-dev": {
+ "chillerlan/php-authenticator": "^4.1 || ^5.1",
+ "phan/phan": "^5.4",
+ "phpunit/phpunit": "^9.6",
+ "phpmd/phpmd": "^2.15",
+ "setasign/fpdf": "^1.8.2",
+ "squizlabs/php_codesniffer": "^3.8"
+ },
+ "suggest": {
+ "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.",
+ "setasign/fpdf": "Required to use the QR FPDF output.",
+ "simple-icons/simple-icons": "SVG icons that you can use to embed as logos in the QR Code"
+ },
+ "autoload": {
+ "psr-4": {
+ "chillerlan\\QRCode\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "chillerlan\\QRCodeTest\\": "tests/"
+ }
+ },
+ "scripts": {
+ "phpunit": "@php vendor/bin/phpunit",
+ "phan": "@php vendor/bin/phan"
+ },
+ "config": {
+ "lock": false,
+ "sort-packages": true,
+ "platform-check": true
+ }
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/BitBuffer.php b/vendor/chillerlan/php-qrcode/src/Common/BitBuffer.php
new file mode 100644
index 0000000..4a59f2b
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/BitBuffer.php
@@ -0,0 +1,180 @@
+
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+use function count, floor, min;
+
+/**
+ * Holds the raw binary data
+ */
+final class BitBuffer{
+
+ /**
+ * The buffer content
+ *
+ * @var int[]
+ */
+ private array $buffer;
+
+ /**
+ * Length of the content (bits)
+ */
+ private int $length;
+
+ /**
+ * Read count (bytes)
+ */
+ private int $bytesRead = 0;
+
+ /**
+ * Read count (bits)
+ */
+ private int $bitsRead = 0;
+
+ /**
+ * BitBuffer constructor.
+ *
+ * @param int[] $bytes
+ */
+ public function __construct(array $bytes = []){
+ $this->buffer = $bytes;
+ $this->length = count($this->buffer);
+ }
+
+ /**
+ * appends a sequence of bits
+ */
+ public function put(int $bits, int $length):self{
+
+ for($i = 0; $i < $length; $i++){
+ $this->putBit((($bits >> ($length - $i - 1)) & 1) === 1);
+ }
+
+ return $this;
+ }
+
+ /**
+ * appends a single bit
+ */
+ public function putBit(bool $bit):self{
+ $bufIndex = (int)floor($this->length / 8);
+
+ if(count($this->buffer) <= $bufIndex){
+ $this->buffer[] = 0;
+ }
+
+ if($bit === true){
+ $this->buffer[$bufIndex] |= (0x80 >> ($this->length % 8));
+ }
+
+ $this->length++;
+
+ return $this;
+ }
+
+ /**
+ * returns the current buffer length
+ */
+ public function getLength():int{
+ return $this->length;
+ }
+
+ /**
+ * returns the buffer content
+ *
+ * to debug: array_map(fn($v) => sprintf('%08b', $v), $bitBuffer->getBuffer())
+ */
+ public function getBuffer():array{
+ return $this->buffer;
+ }
+
+ /**
+ * @return int number of bits that can be read successfully
+ */
+ public function available():int{
+ return ((8 * ($this->length - $this->bytesRead)) - $this->bitsRead);
+ }
+
+ /**
+ * @author Sean Owen, ZXing
+ *
+ * @param int $numBits number of bits to read
+ *
+ * @return int representing the bits read. The bits will appear as the least-significant bits of the int
+ * @throws \chillerlan\QRCode\QRCodeException if numBits isn't in [1,32] or more than is available
+ */
+ public function read(int $numBits):int{
+
+ if($numBits < 1 || $numBits > $this->available()){
+ throw new QRCodeException('invalid $numBits: '.$numBits);
+ }
+
+ $result = 0;
+
+ // First, read remainder from current byte
+ if($this->bitsRead > 0){
+ $bitsLeft = (8 - $this->bitsRead);
+ $toRead = min($numBits, $bitsLeft);
+ $bitsToNotRead = ($bitsLeft - $toRead);
+ $mask = ((0xff >> (8 - $toRead)) << $bitsToNotRead);
+ $result = (($this->buffer[$this->bytesRead] & $mask) >> $bitsToNotRead);
+ $numBits -= $toRead;
+ $this->bitsRead += $toRead;
+
+ if($this->bitsRead === 8){
+ $this->bitsRead = 0;
+ $this->bytesRead++;
+ }
+ }
+
+ // Next read whole bytes
+ if($numBits > 0){
+
+ while($numBits >= 8){
+ $result = (($result << 8) | ($this->buffer[$this->bytesRead] & 0xff));
+ $this->bytesRead++;
+ $numBits -= 8;
+ }
+
+ // Finally read a partial byte
+ if($numBits > 0){
+ $bitsToNotRead = (8 - $numBits);
+ $mask = ((0xff >> $bitsToNotRead) << $bitsToNotRead);
+ $result = (($result << $numBits) | (($this->buffer[$this->bytesRead] & $mask) >> $bitsToNotRead));
+ $this->bitsRead += $numBits;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Clears the buffer and resets the stats
+ */
+ public function clear():self{
+ $this->buffer = [];
+ $this->length = 0;
+
+ return $this->rewind();
+ }
+
+ /**
+ * Resets the read-counters
+ */
+ public function rewind():self{
+ $this->bytesRead = 0;
+ $this->bitsRead = 0;
+
+ return $this;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/ECICharset.php b/vendor/chillerlan/php-qrcode/src/Common/ECICharset.php
new file mode 100644
index 0000000..0c98e36
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/ECICharset.php
@@ -0,0 +1,125 @@
+
+ * @copyright 2021 smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+use function sprintf;
+
+/**
+ * ISO/IEC 18004:2000 - 8.4.1 Extended Channel Interpretation (ECI) Mode
+ */
+final class ECICharset{
+
+ public const CP437 = 0; // Code page 437, DOS Latin US
+ public const ISO_IEC_8859_1_GLI = 1; // GLI encoding with characters 0 to 127 identical to ISO/IEC 646 and characters 128 to 255 identical to ISO 8859-1
+ public const CP437_WO_GLI = 2; // An equivalent code table to CP437, without the return-to-GLI 0 logic
+ public const ISO_IEC_8859_1 = 3; // Latin-1 (Default)
+ public const ISO_IEC_8859_2 = 4; // Latin-2
+ public const ISO_IEC_8859_3 = 5; // Latin-3
+ public const ISO_IEC_8859_4 = 6; // Latin-4
+ public const ISO_IEC_8859_5 = 7; // Latin/Cyrillic
+ public const ISO_IEC_8859_6 = 8; // Latin/Arabic
+ public const ISO_IEC_8859_7 = 9; // Latin/Greek
+ public const ISO_IEC_8859_8 = 10; // Latin/Hebrew
+ public const ISO_IEC_8859_9 = 11; // Latin-5
+ public const ISO_IEC_8859_10 = 12; // Latin-6
+ public const ISO_IEC_8859_11 = 13; // Latin/Thai
+ // 14 reserved
+ public const ISO_IEC_8859_13 = 15; // Latin-7 (Baltic Rim)
+ public const ISO_IEC_8859_14 = 16; // Latin-8 (Celtic)
+ public const ISO_IEC_8859_15 = 17; // Latin-9
+ public const ISO_IEC_8859_16 = 18; // Latin-10
+ // 19 reserved
+ public const SHIFT_JIS = 20; // JIS X 0208 Annex 1 + JIS X 0201
+ public const WINDOWS_1250_LATIN_2 = 21; // Superset of Latin-2, Central Europe
+ public const WINDOWS_1251_CYRILLIC = 22; // Latin/Cyrillic
+ public const WINDOWS_1252_LATIN_1 = 23; // Superset of Latin-1
+ public const WINDOWS_1256_ARABIC = 24;
+ public const ISO_IEC_10646_UCS_2 = 25; // High order byte first (UTF-16BE)
+ public const ISO_IEC_10646_UTF_8 = 26; // UTF-8
+ public const ISO_IEC_646_1991 = 27; // International Reference Version of ISO 7-bit coded character set (US-ASCII)
+ public const BIG5 = 28; // Big 5 (Taiwan) Chinese Character Set
+ public const GB18030 = 29; // GB (PRC) Chinese Character Set
+ public const EUC_KR = 30; // Korean Character Set
+
+ /**
+ * map of charset id -> name
+ *
+ * @see \mb_list_encodings()
+ */
+ public const MB_ENCODINGS = [
+ self::CP437 => null,
+ self::ISO_IEC_8859_1_GLI => null,
+ self::CP437_WO_GLI => null,
+ self::ISO_IEC_8859_1 => 'ISO-8859-1',
+ self::ISO_IEC_8859_2 => 'ISO-8859-2',
+ self::ISO_IEC_8859_3 => 'ISO-8859-3',
+ self::ISO_IEC_8859_4 => 'ISO-8859-4',
+ self::ISO_IEC_8859_5 => 'ISO-8859-5',
+ self::ISO_IEC_8859_6 => 'ISO-8859-6',
+ self::ISO_IEC_8859_7 => 'ISO-8859-7',
+ self::ISO_IEC_8859_8 => 'ISO-8859-8',
+ self::ISO_IEC_8859_9 => 'ISO-8859-9',
+ self::ISO_IEC_8859_10 => 'ISO-8859-10',
+ self::ISO_IEC_8859_11 => null,
+ self::ISO_IEC_8859_13 => 'ISO-8859-13',
+ self::ISO_IEC_8859_14 => 'ISO-8859-14',
+ self::ISO_IEC_8859_15 => 'ISO-8859-15',
+ self::ISO_IEC_8859_16 => 'ISO-8859-16',
+ self::SHIFT_JIS => 'SJIS',
+ self::WINDOWS_1250_LATIN_2 => null, // @see https://www.php.net/manual/en/function.mb-convert-encoding.php#112547
+ self::WINDOWS_1251_CYRILLIC => 'Windows-1251',
+ self::WINDOWS_1252_LATIN_1 => 'Windows-1252',
+ self::WINDOWS_1256_ARABIC => null, // @see https://stackoverflow.com/a/8592995
+ self::ISO_IEC_10646_UCS_2 => 'UTF-16BE',
+ self::ISO_IEC_10646_UTF_8 => 'UTF-8',
+ self::ISO_IEC_646_1991 => 'ASCII',
+ self::BIG5 => 'BIG-5',
+ self::GB18030 => 'GB18030',
+ self::EUC_KR => 'EUC-KR',
+ ];
+
+ /**
+ * The current ECI character set ID
+ */
+ private int $charsetID;
+
+ /**
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function __construct(int $charsetID){
+
+ if($charsetID < 0 || $charsetID > 999999){
+ throw new QRCodeException(sprintf('invalid charset id: "%s"', $charsetID));
+ }
+
+ $this->charsetID = $charsetID;
+ }
+
+ /**
+ * Returns the current character set ID
+ */
+ public function getID():int{
+ return $this->charsetID;
+ }
+
+ /**
+ * Returns the name of the current character set or null if no name is available
+ *
+ * @see \mb_convert_encoding()
+ * @see \iconv()
+ */
+ public function getName():?string{
+ return (self::MB_ENCODINGS[$this->charsetID] ?? null);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/EccLevel.php b/vendor/chillerlan/php-qrcode/src/Common/EccLevel.php
new file mode 100644
index 0000000..789d7f7
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/EccLevel.php
@@ -0,0 +1,223 @@
+
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+use function array_column;
+
+/**
+ * This class encapsulates the four error correction levels defined by the QR code standard.
+ */
+final class EccLevel{
+
+ // ISO/IEC 18004:2000 Tables 12, 25
+
+ /** @var int */
+ public const L = 0b01; // 7%.
+ /** @var int */
+ public const M = 0b00; // 15%.
+ /** @var int */
+ public const Q = 0b11; // 25%.
+ /** @var int */
+ public const H = 0b10; // 30%.
+
+ /**
+ * ISO/IEC 18004:2000 Tables 7-11 - Number of symbol characters and input data capacity for versions 1 to 40
+ *
+ * @var int[][]
+ */
+ private const MAX_BITS = [
+ // [ L, M, Q, H] // v => modules
+ [ 0, 0, 0, 0], // 0 => will be ignored, index starts at 1
+ [ 152, 128, 104, 72], // 1 => 21
+ [ 272, 224, 176, 128], // 2 => 25
+ [ 440, 352, 272, 208], // 3 => 29
+ [ 640, 512, 384, 288], // 4 => 33
+ [ 864, 688, 496, 368], // 5 => 37
+ [ 1088, 864, 608, 480], // 6 => 41
+ [ 1248, 992, 704, 528], // 7 => 45
+ [ 1552, 1232, 880, 688], // 8 => 49
+ [ 1856, 1456, 1056, 800], // 9 => 53
+ [ 2192, 1728, 1232, 976], // 10 => 57
+ [ 2592, 2032, 1440, 1120], // 11 => 61
+ [ 2960, 2320, 1648, 1264], // 12 => 65
+ [ 3424, 2672, 1952, 1440], // 13 => 69 NICE!
+ [ 3688, 2920, 2088, 1576], // 14 => 73
+ [ 4184, 3320, 2360, 1784], // 15 => 77
+ [ 4712, 3624, 2600, 2024], // 16 => 81
+ [ 5176, 4056, 2936, 2264], // 17 => 85
+ [ 5768, 4504, 3176, 2504], // 18 => 89
+ [ 6360, 5016, 3560, 2728], // 19 => 93
+ [ 6888, 5352, 3880, 3080], // 20 => 97
+ [ 7456, 5712, 4096, 3248], // 21 => 101
+ [ 8048, 6256, 4544, 3536], // 22 => 105
+ [ 8752, 6880, 4912, 3712], // 23 => 109
+ [ 9392, 7312, 5312, 4112], // 24 => 113
+ [10208, 8000, 5744, 4304], // 25 => 117
+ [10960, 8496, 6032, 4768], // 26 => 121
+ [11744, 9024, 6464, 5024], // 27 => 125
+ [12248, 9544, 6968, 5288], // 28 => 129
+ [13048, 10136, 7288, 5608], // 29 => 133
+ [13880, 10984, 7880, 5960], // 30 => 137
+ [14744, 11640, 8264, 6344], // 31 => 141
+ [15640, 12328, 8920, 6760], // 32 => 145
+ [16568, 13048, 9368, 7208], // 33 => 149
+ [17528, 13800, 9848, 7688], // 34 => 153
+ [18448, 14496, 10288, 7888], // 35 => 157
+ [19472, 15312, 10832, 8432], // 36 => 161
+ [20528, 15936, 11408, 8768], // 37 => 165
+ [21616, 16816, 12016, 9136], // 38 => 169
+ [22496, 17728, 12656, 9776], // 39 => 173
+ [23648, 18672, 13328, 10208], // 40 => 177
+ ];
+
+ /**
+ * ISO/IEC 18004:2000 Section 8.9 - Format Information
+ *
+ * ECC level -> mask pattern
+ *
+ * @var int[][]
+ */
+ private const FORMAT_PATTERN = [
+ [ // L
+ 0b111011111000100,
+ 0b111001011110011,
+ 0b111110110101010,
+ 0b111100010011101,
+ 0b110011000101111,
+ 0b110001100011000,
+ 0b110110001000001,
+ 0b110100101110110,
+ ],
+ [ // M
+ 0b101010000010010,
+ 0b101000100100101,
+ 0b101111001111100,
+ 0b101101101001011,
+ 0b100010111111001,
+ 0b100000011001110,
+ 0b100111110010111,
+ 0b100101010100000,
+ ],
+ [ // Q
+ 0b011010101011111,
+ 0b011000001101000,
+ 0b011111100110001,
+ 0b011101000000110,
+ 0b010010010110100,
+ 0b010000110000011,
+ 0b010111011011010,
+ 0b010101111101101,
+ ],
+ [ // H
+ 0b001011010001001,
+ 0b001001110111110,
+ 0b001110011100111,
+ 0b001100111010000,
+ 0b000011101100010,
+ 0b000001001010101,
+ 0b000110100001100,
+ 0b000100000111011,
+ ],
+ ];
+
+ /**
+ * The current ECC level value
+ *
+ * L: 0b01
+ * M: 0b00
+ * Q: 0b11
+ * H: 0b10
+ */
+ private int $eccLevel;
+
+ /**
+ * @param int $eccLevel containing the two bits encoding a QR Code's error correction level
+ *
+ * @todo: accept string values (PHP8+)
+ * @see https://github.com/chillerlan/php-qrcode/discussions/160
+ *
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function __construct(int $eccLevel){
+
+ if((0b11 & $eccLevel) !== $eccLevel){
+ throw new QRCodeException('invalid ECC level');
+ }
+
+ $this->eccLevel = $eccLevel;
+ }
+
+ /**
+ * returns the string representation of the current ECC level
+ */
+ public function __toString():string{
+ return [
+ self::L => 'L',
+ self::M => 'M',
+ self::Q => 'Q',
+ self::H => 'H',
+ ][$this->eccLevel];
+ }
+
+ /**
+ * returns the current ECC level
+ */
+ public function getLevel():int{
+ return $this->eccLevel;
+ }
+
+ /**
+ * returns the ordinal value of the current ECC level
+ *
+ * references to the keys of the following tables:
+ *
+ * @see \chillerlan\QRCode\Common\EccLevel::MAX_BITS
+ * @see \chillerlan\QRCode\Common\EccLevel::FORMAT_PATTERN
+ * @see \chillerlan\QRCode\Common\Version::RSBLOCKS
+ */
+ public function getOrdinal():int{
+ return [
+ self::L => 0,
+ self::M => 1,
+ self::Q => 2,
+ self::H => 3,
+ ][$this->eccLevel];
+ }
+
+ /**
+ * returns the format pattern for the given $eccLevel and $maskPattern
+ */
+ public function getformatPattern(MaskPattern $maskPattern):int{
+ return self::FORMAT_PATTERN[$this->getOrdinal()][$maskPattern->getPattern()];
+ }
+
+ /**
+ * returns an array with the max bit lengths for version 1-40 and the current ECC level
+ *
+ * @return int[]
+ */
+ public function getMaxBits():array{
+ $col = array_column(self::MAX_BITS, $this->getOrdinal());
+
+ unset($col[0]); // remove the inavlid index 0
+
+ return $col;
+ }
+
+ /**
+ * Returns the maximum bit length for the given version and current ECC level
+ */
+ public function getMaxBitsForVersion(Version $version):int{
+ return self::MAX_BITS[$version->getVersionNumber()][$this->getOrdinal()];
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/GDLuminanceSource.php b/vendor/chillerlan/php-qrcode/src/Common/GDLuminanceSource.php
new file mode 100644
index 0000000..027466f
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/GDLuminanceSource.php
@@ -0,0 +1,97 @@
+
+ * @copyright 2021 Smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\Decoder\QRCodeDecoderException;
+use chillerlan\Settings\SettingsContainerInterface;
+use function file_get_contents, get_resource_type, imagecolorat, imagecolorsforindex,
+ imagecreatefromstring, imagefilter, imagesx, imagesy, is_resource;
+use const IMG_FILTER_BRIGHTNESS, IMG_FILTER_CONTRAST, IMG_FILTER_GRAYSCALE, IMG_FILTER_NEGATE, PHP_MAJOR_VERSION;
+
+/**
+ * This class is used to help decode images from files which arrive as GD Resource
+ * It does not support rotation.
+ */
+class GDLuminanceSource extends LuminanceSourceAbstract{
+
+ /**
+ * @var resource|\GdImage
+ */
+ protected $gdImage;
+
+ /**
+ * GDLuminanceSource constructor.
+ *
+ * @param resource|\GdImage $gdImage
+ * @param \chillerlan\Settings\SettingsContainerInterface|null $options
+ *
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ public function __construct($gdImage, SettingsContainerInterface $options = null){
+
+ /** @noinspection PhpFullyQualifiedNameUsageInspection */
+ if(
+ (PHP_MAJOR_VERSION >= 8 && !$gdImage instanceof \GdImage) // @todo: remove version check in v6
+ || (PHP_MAJOR_VERSION < 8 && (!is_resource($gdImage) || get_resource_type($gdImage) !== 'gd'))
+ ){
+ throw new QRCodeDecoderException('Invalid GD image source.'); // @codeCoverageIgnore
+ }
+
+ parent::__construct(imagesx($gdImage), imagesy($gdImage), $options);
+
+ $this->gdImage = $gdImage;
+
+ if($this->options->readerGrayscale){
+ imagefilter($this->gdImage, IMG_FILTER_GRAYSCALE);
+ }
+
+ if($this->options->readerInvertColors){
+ imagefilter($this->gdImage, IMG_FILTER_NEGATE);
+ }
+
+ if($this->options->readerIncreaseContrast){
+ imagefilter($this->gdImage, IMG_FILTER_BRIGHTNESS, -100);
+ imagefilter($this->gdImage, IMG_FILTER_CONTRAST, -100);
+ }
+
+ $this->setLuminancePixels();
+ }
+
+ /**
+ *
+ */
+ protected function setLuminancePixels():void{
+
+ for($j = 0; $j < $this->height; $j++){
+ for($i = 0; $i < $this->width; $i++){
+ $argb = imagecolorat($this->gdImage, $i, $j);
+ $pixel = imagecolorsforindex($this->gdImage, $argb);
+
+ $this->setLuminancePixel($pixel['red'], $pixel['green'], $pixel['blue']);
+ }
+ }
+
+ }
+
+ /** @inheritDoc */
+ public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
+ return new self(imagecreatefromstring(file_get_contents(self::checkFile($path))), $options);
+ }
+
+ /** @inheritDoc */
+ public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
+ return new self(imagecreatefromstring($blob), $options);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/GF256.php b/vendor/chillerlan/php-qrcode/src/Common/GF256.php
new file mode 100644
index 0000000..d8ba095
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/GF256.php
@@ -0,0 +1,154 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+
+use function array_fill;
+
+/**
+ * This class contains utility methods for performing mathematical operations over
+ * the Galois Fields. Operations use a given primitive polynomial in calculations.
+ *
+ * Throughout this package, elements of the GF are represented as an int
+ * for convenience and speed (but at the cost of memory).
+ *
+ *
+ * @author Sean Owen
+ * @author David Olivier
+ */
+final class GF256{
+
+ /**
+ * irreducible polynomial whose coefficients are represented by the bits of an int,
+ * where the least-significant bit represents the constant coefficient
+ */
+# private int $primitive = 0x011D;
+
+ private const logTable = [
+ 0, // the first value is never returned, index starts at 1
+ 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75,
+ 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113,
+ 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69,
+ 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166,
+ 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136,
+ 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64,
+ 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61,
+ 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87,
+ 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24,
+ 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46,
+ 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97,
+ 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162,
+ 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246,
+ 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90,
+ 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215,
+ 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175,
+ ];
+
+ private const expTable = [
+ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38,
+ 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192,
+ 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35,
+ 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161,
+ 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240,
+ 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226,
+ 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206,
+ 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204,
+ 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84,
+ 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115,
+ 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255,
+ 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65,
+ 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166,
+ 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9,
+ 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22,
+ 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1,
+ ];
+
+ /**
+ * Implements both addition and subtraction -- they are the same in GF(size).
+ *
+ * @return int sum/difference of a and b
+ */
+ public static function addOrSubtract(int $a, int $b):int{
+ return ($a ^ $b);
+ }
+
+ /**
+ * @return GenericGFPoly the monomial representing coefficient * x^degree
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public static function buildMonomial(int $degree, int $coefficient):GenericGFPoly{
+
+ if($degree < 0){
+ throw new QRCodeException('degree < 0');
+ }
+
+ $coefficients = array_fill(0, ($degree + 1), 0);
+ $coefficients[0] = $coefficient;
+
+ return new GenericGFPoly($coefficients);
+ }
+
+ /**
+ * @return int 2 to the power of $a in GF(size)
+ */
+ public static function exp(int $a):int{
+
+ if($a < 0){
+ $a += 255;
+ }
+ elseif($a >= 256){
+ $a -= 255;
+ }
+
+ return self::expTable[$a];
+ }
+
+ /**
+ * @return int base 2 log of $a in GF(size)
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public static function log(int $a):int{
+
+ if($a < 1){
+ throw new QRCodeException('$a < 1');
+ }
+
+ return self::logTable[$a];
+ }
+
+ /**
+ * @return int multiplicative inverse of a
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public static function inverse(int $a):int{
+
+ if($a === 0){
+ throw new QRCodeException('$a === 0');
+ }
+
+ return self::expTable[(256 - self::logTable[$a] - 1)];
+ }
+
+ /**
+ * @return int product of a and b in GF(size)
+ */
+ public static function multiply(int $a, int $b):int{
+
+ if($a === 0 || $b === 0){
+ return 0;
+ }
+
+ return self::expTable[((self::logTable[$a] + self::logTable[$b]) % 255)];
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/GenericGFPoly.php b/vendor/chillerlan/php-qrcode/src/Common/GenericGFPoly.php
new file mode 100644
index 0000000..ae361b9
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/GenericGFPoly.php
@@ -0,0 +1,263 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+use function array_fill, array_slice, array_splice, count;
+
+/**
+ * Represents a polynomial whose coefficients are elements of a GF.
+ * Instances of this class are immutable.
+ *
+ * Much credit is due to William Rucklidge since portions of this code are an indirect
+ * port of his C++ Reed-Solomon implementation.
+ *
+ * @author Sean Owen
+ */
+final class GenericGFPoly{
+
+ private array $coefficients;
+
+ /**
+ * @param array $coefficients array coefficients as ints representing elements of GF(size), arranged
+ * from most significant (highest-power term) coefficient to the least significant
+ * @param int|null $degree
+ *
+ * @throws \chillerlan\QRCode\QRCodeException if argument is null or empty, or if leading coefficient is 0 and this
+ * is not a constant polynomial (that is, it is not the monomial "0")
+ */
+ public function __construct(array $coefficients, int $degree = null){
+ $degree ??= 0;
+
+ if(empty($coefficients)){
+ throw new QRCodeException('arg $coefficients is empty');
+ }
+
+ if($degree < 0){
+ throw new QRCodeException('negative degree');
+ }
+
+ $coefficientsLength = count($coefficients);
+
+ // Leading term must be non-zero for anything except the constant polynomial "0"
+ $firstNonZero = 0;
+
+ while($firstNonZero < $coefficientsLength && $coefficients[$firstNonZero] === 0){
+ $firstNonZero++;
+ }
+
+ $this->coefficients = [0];
+
+ if($firstNonZero !== $coefficientsLength){
+ $this->coefficients = array_fill(0, ($coefficientsLength - $firstNonZero + $degree), 0);
+
+ for($i = 0; $i < ($coefficientsLength - $firstNonZero); $i++){
+ $this->coefficients[$i] = $coefficients[($i + $firstNonZero)];
+ }
+ }
+
+ }
+
+ /**
+ * @return int $coefficient of x^degree term in this polynomial
+ */
+ public function getCoefficient(int $degree):int{
+ return $this->coefficients[(count($this->coefficients) - 1 - $degree)];
+ }
+
+ /**
+ * @return int[]
+ */
+ public function getCoefficients():array{
+ return $this->coefficients;
+ }
+
+ /**
+ * @return int $degree of this polynomial
+ */
+ public function getDegree():int{
+ return (count($this->coefficients) - 1);
+ }
+
+ /**
+ * @return bool true if this polynomial is the monomial "0"
+ */
+ public function isZero():bool{
+ return $this->coefficients[0] === 0;
+ }
+
+ /**
+ * @return int evaluation of this polynomial at a given point
+ */
+ public function evaluateAt(int $a):int{
+
+ if($a === 0){
+ // Just return the x^0 coefficient
+ return $this->getCoefficient(0);
+ }
+
+ $result = 0;
+
+ foreach($this->coefficients as $c){
+ // if $a === 1 just the sum of the coefficients
+ $result = GF256::addOrSubtract((($a === 1) ? $result : GF256::multiply($a, $result)), $c);
+ }
+
+ return $result;
+ }
+
+ /**
+ *
+ */
+ public function multiply(GenericGFPoly $other):self{
+
+ if($this->isZero() || $other->isZero()){
+ return new self([0]);
+ }
+
+ $product = array_fill(0, (count($this->coefficients) + count($other->coefficients) - 1), 0);
+
+ foreach($this->coefficients as $i => $aCoeff){
+ foreach($other->coefficients as $j => $bCoeff){
+ $product[($i + $j)] ^= GF256::multiply($aCoeff, $bCoeff);
+ }
+ }
+
+ return new self($product);
+ }
+
+ /**
+ * @return \chillerlan\QRCode\Common\GenericGFPoly[] [quotient, remainder]
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function divide(GenericGFPoly $other):array{
+
+ if($other->isZero()){
+ throw new QRCodeException('Division by 0');
+ }
+
+ $quotient = new self([0]);
+ $remainder = clone $this;
+
+ $denominatorLeadingTerm = $other->getCoefficient($other->getDegree());
+ $inverseDenominatorLeadingTerm = GF256::inverse($denominatorLeadingTerm);
+
+ while($remainder->getDegree() >= $other->getDegree() && !$remainder->isZero()){
+ $scale = GF256::multiply($remainder->getCoefficient($remainder->getDegree()), $inverseDenominatorLeadingTerm);
+ $diff = ($remainder->getDegree() - $other->getDegree());
+ $quotient = $quotient->addOrSubtract(GF256::buildMonomial($diff, $scale));
+ $remainder = $remainder->addOrSubtract($other->multiplyByMonomial($diff, $scale));
+ }
+
+ return [$quotient, $remainder];
+
+ }
+
+ /**
+ *
+ */
+ public function multiplyInt(int $scalar):self{
+
+ if($scalar === 0){
+ return new self([0]);
+ }
+
+ if($scalar === 1){
+ return $this;
+ }
+
+ $product = array_fill(0, count($this->coefficients), 0);
+
+ foreach($this->coefficients as $i => $c){
+ $product[$i] = GF256::multiply($c, $scalar);
+ }
+
+ return new self($product);
+ }
+
+ /**
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function multiplyByMonomial(int $degree, int $coefficient):self{
+
+ if($degree < 0){
+ throw new QRCodeException('degree < 0');
+ }
+
+ if($coefficient === 0){
+ return new self([0]);
+ }
+
+ $product = array_fill(0, (count($this->coefficients) + $degree), 0);
+
+ foreach($this->coefficients as $i => $c){
+ $product[$i] = GF256::multiply($c, $coefficient);
+ }
+
+ return new self($product);
+ }
+
+ /**
+ *
+ */
+ public function mod(GenericGFPoly $other):self{
+
+ if((count($this->coefficients) - count($other->coefficients)) < 0){
+ return $this;
+ }
+
+ $ratio = (GF256::log($this->coefficients[0]) - GF256::log($other->coefficients[0]));
+
+ foreach($other->coefficients as $i => $c){
+ $this->coefficients[$i] ^= GF256::exp(GF256::log($c) + $ratio);
+ }
+
+ return (new self($this->coefficients))->mod($other);
+ }
+
+ /**
+ *
+ */
+ public function addOrSubtract(GenericGFPoly $other):self{
+
+ if($this->isZero()){
+ return $other;
+ }
+
+ if($other->isZero()){
+ return $this;
+ }
+
+ $smallerCoefficients = $this->coefficients;
+ $largerCoefficients = $other->coefficients;
+
+ if(count($smallerCoefficients) > count($largerCoefficients)){
+ $temp = $smallerCoefficients;
+ $smallerCoefficients = $largerCoefficients;
+ $largerCoefficients = $temp;
+ }
+
+ $sumDiff = array_fill(0, count($largerCoefficients), 0);
+ $lengthDiff = (count($largerCoefficients) - count($smallerCoefficients));
+ // Copy high-order terms only found in higher-degree polynomial's coefficients
+ array_splice($sumDiff, 0, $lengthDiff, array_slice($largerCoefficients, 0, $lengthDiff));
+
+ $countLargerCoefficients = count($largerCoefficients);
+
+ for($i = $lengthDiff; $i < $countLargerCoefficients; $i++){
+ $sumDiff[$i] = GF256::addOrSubtract($smallerCoefficients[($i - $lengthDiff)], $largerCoefficients[$i]);
+ }
+
+ return new self($sumDiff);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/IMagickLuminanceSource.php b/vendor/chillerlan/php-qrcode/src/Common/IMagickLuminanceSource.php
new file mode 100644
index 0000000..d4f66c5
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/IMagickLuminanceSource.php
@@ -0,0 +1,78 @@
+
+ * @copyright 2021 Smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\Settings\SettingsContainerInterface;
+use Imagick;
+use function count;
+
+/**
+ * This class is used to help decode images from files which arrive as Imagick Resource
+ * It does not support rotation.
+ */
+class IMagickLuminanceSource extends LuminanceSourceAbstract{
+
+ protected Imagick $imagick;
+
+ /**
+ * IMagickLuminanceSource constructor.
+ */
+ public function __construct(Imagick $imagick, SettingsContainerInterface $options = null){
+ parent::__construct($imagick->getImageWidth(), $imagick->getImageHeight(), $options);
+
+ $this->imagick = $imagick;
+
+ if($this->options->readerGrayscale){
+ $this->imagick->setImageColorspace(Imagick::COLORSPACE_GRAY);
+ }
+
+ if($this->options->readerInvertColors){
+ $this->imagick->negateImage($this->options->readerGrayscale);
+ }
+
+ if($this->options->readerIncreaseContrast){
+ for($i = 0; $i < 10; $i++){
+ $this->imagick->contrastImage(false); // misleading docs
+ }
+ }
+
+ $this->setLuminancePixels();
+ }
+
+ /**
+ *
+ */
+ protected function setLuminancePixels():void{
+ $pixels = $this->imagick->exportImagePixels(1, 1, $this->width, $this->height, 'RGB', Imagick::PIXEL_CHAR);
+ $count = count($pixels);
+
+ for($i = 0; $i < $count; $i += 3){
+ $this->setLuminancePixel(($pixels[$i] & 0xff), ($pixels[($i + 1)] & 0xff), ($pixels[($i + 2)] & 0xff));
+ }
+ }
+
+ /** @inheritDoc */
+ public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
+ return new self(new Imagick(self::checkFile($path)), $options);
+ }
+
+ /** @inheritDoc */
+ public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
+ $im = new Imagick;
+ $im->readImageBlob($blob);
+
+ return new self($im, $options);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceAbstract.php b/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceAbstract.php
new file mode 100644
index 0000000..432a6e0
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceAbstract.php
@@ -0,0 +1,104 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\Decoder\QRCodeDecoderException;
+use chillerlan\QRCode\QROptions;
+use chillerlan\Settings\SettingsContainerInterface;
+use function array_slice, array_splice, file_exists, is_file, is_readable, realpath;
+
+/**
+ * The purpose of this class hierarchy is to abstract different bitmap implementations across
+ * platforms into a standard interface for requesting greyscale luminance values.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
+
+ /** @var \chillerlan\QRCode\QROptions|\chillerlan\Settings\SettingsContainerInterface */
+ protected SettingsContainerInterface $options;
+ protected array $luminances;
+ protected int $width;
+ protected int $height;
+
+ /**
+ *
+ */
+ public function __construct(int $width, int $height, SettingsContainerInterface $options = null){
+ $this->width = $width;
+ $this->height = $height;
+ $this->options = ($options ?? new QROptions);
+
+ $this->luminances = [];
+ }
+
+ /** @inheritDoc */
+ public function getLuminances():array{
+ return $this->luminances;
+ }
+
+ /** @inheritDoc */
+ public function getWidth():int{
+ return $this->width;
+ }
+
+ /** @inheritDoc */
+ public function getHeight():int{
+ return $this->height;
+ }
+
+ /** @inheritDoc */
+ public function getRow(int $y):array{
+
+ if($y < 0 || $y >= $this->getHeight()){
+ throw new QRCodeDecoderException('Requested row is outside the image: '.$y);
+ }
+
+ $arr = [];
+
+ array_splice($arr, 0, $this->width, array_slice($this->luminances, ($y * $this->width), $this->width));
+
+ return $arr;
+ }
+
+ /**
+ *
+ */
+ protected function setLuminancePixel(int $r, int $g, int $b):void{
+ $this->luminances[] = ($r === $g && $g === $b)
+ // Image is already greyscale, so pick any channel.
+ ? $r // (($r + 128) % 256) - 128;
+ // Calculate luminance cheaply, favoring green.
+ : (($r + 2 * $g + $b) / 4); // (((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
+ }
+
+ /**
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ protected static function checkFile(string $path):string{
+ $path = trim($path);
+
+ if(!file_exists($path) || !is_file($path) || !is_readable($path)){
+ throw new QRCodeDecoderException('invalid file: '.$path);
+ }
+
+ $realpath = realpath($path);
+
+ if($realpath === false){
+ throw new QRCodeDecoderException('unable to resolve path: '.$path);
+ }
+
+ return $realpath;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceInterface.php b/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceInterface.php
new file mode 100644
index 0000000..64409e3
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/LuminanceSourceInterface.php
@@ -0,0 +1,61 @@
+
+ * @copyright 2021 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Common;
+
+/**
+ */
+interface LuminanceSourceInterface{
+
+ /**
+ * Fetches luminance data for the underlying bitmap. Values should be fetched using:
+ * `int luminance = array[y * width + x] & 0xff`
+ *
+ * @return array A row-major 2D array of luminance values. Do not use result $length as it may be
+ * larger than $width * $height bytes on some platforms. Do not modify the contents
+ * of the result.
+ */
+ public function getLuminances():array;
+
+ /**
+ * @return int The width of the bitmap.
+ */
+ public function getWidth():int;
+
+ /**
+ * @return int The height of the bitmap.
+ */
+ public function getHeight():int;
+
+ /**
+ * Fetches one row of luminance data from the underlying platform's bitmap. Values range from
+ * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
+ * to bitwise and with 0xff for each value. It is preferable for implementations of this method
+ * to only fetch this row rather than the whole image, since no 2D Readers may be installed and
+ * getLuminances() may never be called.
+ *
+ * @param int $y The row to fetch, which must be in [0,getHeight())
+ *
+ * @return array An array containing the luminance data.
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ public function getRow(int $y):array;
+
+ /**
+ * Creates a LuminanceSource instance from the given file
+ */
+ public static function fromFile(string $path):self;
+
+ /**
+ * Creates a LuminanceSource instance from the given data blob
+ */
+ public static function fromBlob(string $blob):self;
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/MaskPattern.php b/vendor/chillerlan/php-qrcode/src/Common/MaskPattern.php
new file mode 100644
index 0000000..8441a7f
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/MaskPattern.php
@@ -0,0 +1,329 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+use chillerlan\QRCode\Data\QRMatrix;
+use Closure;
+use function abs, array_column, array_search, intdiv, min;
+
+/**
+ * ISO/IEC 18004:2000 Section 8.8.1
+ * ISO/IEC 18004:2000 Section 8.8.2 - Evaluation of masking results
+ *
+ * @see http://www.thonky.com/qr-code-tutorial/data-masking
+ * @see https://github.com/zxing/zxing/blob/e9e2bd280bcaeabd59d0f955798384fe6c018a6c/core/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java
+ */
+final class MaskPattern{
+
+ /**
+ * @see \chillerlan\QRCode\QROptionsTrait::$maskPattern
+ *
+ * @var int
+ */
+ public const AUTO = -1;
+
+ public const PATTERN_000 = 0b000;
+ public const PATTERN_001 = 0b001;
+ public const PATTERN_010 = 0b010;
+ public const PATTERN_011 = 0b011;
+ public const PATTERN_100 = 0b100;
+ public const PATTERN_101 = 0b101;
+ public const PATTERN_110 = 0b110;
+ public const PATTERN_111 = 0b111;
+
+ /**
+ * @var int[]
+ */
+ public const PATTERNS = [
+ self::PATTERN_000,
+ self::PATTERN_001,
+ self::PATTERN_010,
+ self::PATTERN_011,
+ self::PATTERN_100,
+ self::PATTERN_101,
+ self::PATTERN_110,
+ self::PATTERN_111,
+ ];
+
+ /*
+ * Penalty scores
+ *
+ * ISO/IEC 18004:2000 Section 8.8.1 - Table 24
+ */
+ private const PENALTY_N1 = 3;
+ private const PENALTY_N2 = 3;
+ private const PENALTY_N3 = 40;
+ private const PENALTY_N4 = 10;
+
+ /**
+ * The current mask pattern value (0-7)
+ */
+ private int $maskPattern;
+
+ /**
+ * MaskPattern constructor.
+ *
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function __construct(int $maskPattern){
+
+ if((0b111 & $maskPattern) !== $maskPattern){
+ throw new QRCodeException('invalid mask pattern');
+ }
+
+ $this->maskPattern = $maskPattern;
+ }
+
+ /**
+ * Returns the current mask pattern
+ */
+ public function getPattern():int{
+ return $this->maskPattern;
+ }
+
+ /**
+ * Returns a closure that applies the mask for the chosen mask pattern.
+ *
+ * Note that the diagram in section 6.8.1 is misleading since it indicates that $i is column position
+ * and $j is row position. In fact, as the text says, $i is row position and $j is column position.
+ *
+ * @see https://www.thonky.com/qr-code-tutorial/mask-patterns
+ * @see https://github.com/zxing/zxing/blob/e9e2bd280bcaeabd59d0f955798384fe6c018a6c/core/src/main/java/com/google/zxing/qrcode/decoder/DataMask.java#L32-L117
+ */
+ public function getMask():Closure{
+ // $x = column (width), $y = row (height)
+ return [
+ self::PATTERN_000 => fn(int $x, int $y):bool => (($x + $y) % 2) === 0,
+ self::PATTERN_001 => fn(int $x, int $y):bool => ($y % 2) === 0,
+ self::PATTERN_010 => fn(int $x, int $y):bool => ($x % 3) === 0,
+ self::PATTERN_011 => fn(int $x, int $y):bool => (($x + $y) % 3) === 0,
+ self::PATTERN_100 => fn(int $x, int $y):bool => ((intdiv($y, 2) + intdiv($x, 3)) % 2) === 0,
+ self::PATTERN_101 => fn(int $x, int $y):bool => (($x * $y) % 6) === 0,
+ self::PATTERN_110 => fn(int $x, int $y):bool => (($x * $y) % 6) < 3,
+ self::PATTERN_111 => fn(int $x, int $y):bool => (($x + $y + (($x * $y) % 3)) % 2) === 0,
+ ][$this->maskPattern];
+ }
+
+ /**
+ * Evaluates the matrix of the given data interface and returns a new mask pattern instance for the best result
+ */
+ public static function getBestPattern(QRMatrix $QRMatrix):self{
+ $penalties = [];
+ $size = $QRMatrix->getSize();
+
+ foreach(self::PATTERNS as $pattern){
+ $mp = new self($pattern);
+ $matrix = (clone $QRMatrix)->setFormatInfo($mp)->mask($mp)->getMatrix(true);
+ $penalty = 0;
+
+ for($level = 1; $level <= 4; $level++){
+ $penalty += self::{'testRule'.$level}($matrix, $size, $size);
+ }
+
+ $penalties[$pattern] = (int)$penalty;
+ }
+
+ return new self(array_search(min($penalties), $penalties, true));
+ }
+
+ /**
+ * Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and
+ * give penalty to them. Example: 00000 or 11111.
+ */
+ public static function testRule1(array $matrix, int $height, int $width):int{
+ $penalty = 0;
+
+ // horizontal
+ foreach($matrix as $row){
+ $penalty += self::applyRule1($row);
+ }
+
+ // vertical
+ for($x = 0; $x < $width; $x++){
+ $penalty += self::applyRule1(array_column($matrix, $x));
+ }
+
+ return $penalty;
+ }
+
+ /**
+ *
+ */
+ private static function applyRule1(array $rc):int{
+ $penalty = 0;
+ $numSameBitCells = 0;
+ $prevBit = null;
+
+ foreach($rc as $val){
+
+ if($val === $prevBit){
+ $numSameBitCells++;
+ }
+ else{
+
+ if($numSameBitCells >= 5){
+ $penalty += (self::PENALTY_N1 + $numSameBitCells - 5);
+ }
+
+ $numSameBitCells = 1; // Include the cell itself.
+ $prevBit = $val;
+ }
+ }
+
+ if($numSameBitCells >= 5){
+ $penalty += (self::PENALTY_N1 + $numSameBitCells - 5);
+ }
+
+ return $penalty;
+ }
+
+ /**
+ * Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
+ * penalty to them. This is actually equivalent to the spec's rule, which is to find MxN blocks and give a
+ * penalty proportional to (M-1)x(N-1), because this is the number of 2x2 blocks inside such a block.
+ */
+ public static function testRule2(array $matrix, int $height, int $width):int{
+ $penalty = 0;
+
+ foreach($matrix as $y => $row){
+
+ if($y > ($height - 2)){
+ break;
+ }
+
+ foreach($row as $x => $val){
+
+ if($x > ($width - 2)){
+ break;
+ }
+
+ if(
+ $val === $row[($x + 1)]
+ && $val === $matrix[($y + 1)][$x]
+ && $val === $matrix[($y + 1)][($x + 1)]
+ ){
+ $penalty++;
+ }
+ }
+ }
+
+ return (self::PENALTY_N2 * $penalty);
+ }
+
+ /**
+ * Apply mask penalty rule 3 and return the penalty. Find consecutive runs of 1:1:3:1:1:4
+ * starting with black, or 4:1:1:3:1:1 starting with white, and give penalty to them. If we
+ * find patterns like 000010111010000, we give penalty once.
+ */
+ public static function testRule3(array $matrix, int $height, int $width):int{
+ $penalties = 0;
+
+ foreach($matrix as $y => $row){
+ foreach($row as $x => $val){
+
+ if(
+ ($x + 6) < $width
+ && $val
+ && !$row[($x + 1)]
+ && $row[($x + 2)]
+ && $row[($x + 3)]
+ && $row[($x + 4)]
+ && !$row[($x + 5)]
+ && $row[($x + 6)]
+ && (
+ self::isWhiteHorizontal($row, $width, ($x - 4), $x)
+ || self::isWhiteHorizontal($row, $width, ($x + 7), ($x + 11))
+ )
+ ){
+ $penalties++;
+ }
+
+ if(
+ ($y + 6) < $height
+ && $val
+ && !$matrix[($y + 1)][$x]
+ && $matrix[($y + 2)][$x]
+ && $matrix[($y + 3)][$x]
+ && $matrix[($y + 4)][$x]
+ && !$matrix[($y + 5)][$x]
+ && $matrix[($y + 6)][$x]
+ && (
+ self::isWhiteVertical($matrix, $height, $x, ($y - 4), $y)
+ || self::isWhiteVertical($matrix, $height, $x, ($y + 7), ($y + 11))
+ )
+ ){
+ $penalties++;
+ }
+
+ }
+ }
+
+ return ($penalties * self::PENALTY_N3);
+ }
+
+ /**
+ *
+ */
+ private static function isWhiteHorizontal(array $row, int $width, int $from, int $to):bool{
+
+ if($from < 0 || $width < $to){
+ return false;
+ }
+
+ for($x = $from; $x < $to; $x++){
+ if($row[$x]){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ *
+ */
+ private static function isWhiteVertical(array $matrix, int $height, int $x, int $from, int $to):bool{
+
+ if($from < 0 || $height < $to){
+ return false;
+ }
+
+ for($y = $from; $y < $to; $y++){
+ if($matrix[$y][$x] === true){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
+ * penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance.
+ */
+ public static function testRule4(array $matrix, int $height, int $width):int{
+ $darkCells = 0;
+ $totalCells = ($height * $width);
+
+ foreach($matrix as $row){
+ foreach($row as $val){
+ if($val === true){
+ $darkCells++;
+ }
+ }
+ }
+
+ return (intdiv((abs($darkCells * 2 - $totalCells) * 10), $totalCells) * self::PENALTY_N4);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/Mode.php b/vendor/chillerlan/php-qrcode/src/Common/Mode.php
new file mode 100644
index 0000000..523d379
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/Mode.php
@@ -0,0 +1,96 @@
+
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\Data\{AlphaNum, Byte, Hanzi, Kanji, Number};
+use chillerlan\QRCode\QRCodeException;
+
+/**
+ * Data mode information - ISO 18004:2006, 6.4.1, Tables 2 and 3
+ */
+final class Mode{
+
+ // ISO/IEC 18004:2000 Table 2
+
+ /** @var int */
+ public const TERMINATOR = 0b0000;
+ /** @var int */
+ public const NUMBER = 0b0001;
+ /** @var int */
+ public const ALPHANUM = 0b0010;
+ /** @var int */
+ public const BYTE = 0b0100;
+ /** @var int */
+ public const KANJI = 0b1000;
+ /** @var int */
+ public const HANZI = 0b1101;
+ /** @var int */
+ public const STRCTURED_APPEND = 0b0011;
+ /** @var int */
+ public const FNC1_FIRST = 0b0101;
+ /** @var int */
+ public const FNC1_SECOND = 0b1001;
+ /** @var int */
+ public const ECI = 0b0111;
+
+ /**
+ * mode length bits for the version breakpoints 1-9, 10-26 and 27-40
+ *
+ * ISO/IEC 18004:2000 Table 3 - Number of bits in Character Count Indicator
+ */
+ public const LENGTH_BITS = [
+ self::NUMBER => [10, 12, 14],
+ self::ALPHANUM => [ 9, 11, 13],
+ self::BYTE => [ 8, 16, 16],
+ self::KANJI => [ 8, 10, 12],
+ self::HANZI => [ 8, 10, 12],
+ self::ECI => [ 0, 0, 0],
+ ];
+
+ /**
+ * Map of data mode => interface (detection order)
+ *
+ * @var string[]
+ */
+ public const INTERFACES = [
+ self::NUMBER => Number::class,
+ self::ALPHANUM => AlphaNum::class,
+ self::KANJI => Kanji::class,
+ self::HANZI => Hanzi::class,
+ self::BYTE => Byte::class,
+ ];
+
+ /**
+ * returns the length bits for the version breakpoints 1-9, 10-26 and 27-40
+ *
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public static function getLengthBitsForVersion(int $mode, int $version):int{
+
+ if(!isset(self::LENGTH_BITS[$mode])){
+ throw new QRCodeException('invalid mode given');
+ }
+
+ $minVersion = 0;
+
+ foreach([9, 26, 40] as $key => $breakpoint){
+
+ if($version > $minVersion && $version <= $breakpoint){
+ return self::LENGTH_BITS[$mode][$key];
+ }
+
+ $minVersion = $breakpoint;
+ }
+
+ throw new QRCodeException(sprintf('invalid version number: %d', $version));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Common/Version.php b/vendor/chillerlan/php-qrcode/src/Common/Version.php
new file mode 100644
index 0000000..fe7240f
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Common/Version.php
@@ -0,0 +1,287 @@
+
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Common;
+
+use chillerlan\QRCode\QRCodeException;
+
+/**
+ * Version related tables and methods
+ */
+final class Version{
+
+ /**
+ * Enable version auto detection
+ *
+ * @see \chillerlan\QRCode\QROptionsTrait::$version
+ *
+ * @var int
+ */
+ public const AUTO = -1;
+
+ /**
+ * ISO/IEC 18004:2000 Annex E, Table E.1 - Row/column coordinates of center module of Alignment Patterns
+ *
+ * version -> pattern
+ *
+ * @var int[][]
+ */
+ private const ALIGNMENT_PATTERN = [
+ 1 => [],
+ 2 => [6, 18],
+ 3 => [6, 22],
+ 4 => [6, 26],
+ 5 => [6, 30],
+ 6 => [6, 34],
+ 7 => [6, 22, 38],
+ 8 => [6, 24, 42],
+ 9 => [6, 26, 46],
+ 10 => [6, 28, 50],
+ 11 => [6, 30, 54],
+ 12 => [6, 32, 58],
+ 13 => [6, 34, 62],
+ 14 => [6, 26, 46, 66],
+ 15 => [6, 26, 48, 70],
+ 16 => [6, 26, 50, 74],
+ 17 => [6, 30, 54, 78],
+ 18 => [6, 30, 56, 82],
+ 19 => [6, 30, 58, 86],
+ 20 => [6, 34, 62, 90],
+ 21 => [6, 28, 50, 72, 94],
+ 22 => [6, 26, 50, 74, 98],
+ 23 => [6, 30, 54, 78, 102],
+ 24 => [6, 28, 54, 80, 106],
+ 25 => [6, 32, 58, 84, 110],
+ 26 => [6, 30, 58, 86, 114],
+ 27 => [6, 34, 62, 90, 118],
+ 28 => [6, 26, 50, 74, 98, 122],
+ 29 => [6, 30, 54, 78, 102, 126],
+ 30 => [6, 26, 52, 78, 104, 130],
+ 31 => [6, 30, 56, 82, 108, 134],
+ 32 => [6, 34, 60, 86, 112, 138],
+ 33 => [6, 30, 58, 86, 114, 142],
+ 34 => [6, 34, 62, 90, 118, 146],
+ 35 => [6, 30, 54, 78, 102, 126, 150],
+ 36 => [6, 24, 50, 76, 102, 128, 154],
+ 37 => [6, 28, 54, 80, 106, 132, 158],
+ 38 => [6, 32, 58, 84, 110, 136, 162],
+ 39 => [6, 26, 54, 82, 110, 138, 166],
+ 40 => [6, 30, 58, 86, 114, 142, 170],
+ ];
+
+ /**
+ * ISO/IEC 18004:2000 Annex D, Table D.1 - Version information bit stream for each version
+ *
+ * no version pattern for QR Codes < 7
+ *
+ * @var int[]
+ */
+ private const VERSION_PATTERN = [
+ 7 => 0b000111110010010100,
+ 8 => 0b001000010110111100,
+ 9 => 0b001001101010011001,
+ 10 => 0b001010010011010011,
+ 11 => 0b001011101111110110,
+ 12 => 0b001100011101100010,
+ 13 => 0b001101100001000111,
+ 14 => 0b001110011000001101,
+ 15 => 0b001111100100101000,
+ 16 => 0b010000101101111000,
+ 17 => 0b010001010001011101,
+ 18 => 0b010010101000010111,
+ 19 => 0b010011010100110010,
+ 20 => 0b010100100110100110,
+ 21 => 0b010101011010000011,
+ 22 => 0b010110100011001001,
+ 23 => 0b010111011111101100,
+ 24 => 0b011000111011000100,
+ 25 => 0b011001000111100001,
+ 26 => 0b011010111110101011,
+ 27 => 0b011011000010001110,
+ 28 => 0b011100110000011010,
+ 29 => 0b011101001100111111,
+ 30 => 0b011110110101110101,
+ 31 => 0b011111001001010000,
+ 32 => 0b100000100111010101,
+ 33 => 0b100001011011110000,
+ 34 => 0b100010100010111010,
+ 35 => 0b100011011110011111,
+ 36 => 0b100100101100001011,
+ 37 => 0b100101010000101110,
+ 38 => 0b100110101001100100,
+ 39 => 0b100111010101000001,
+ 40 => 0b101000110001101001,
+ ];
+
+ /**
+ * ISO/IEC 18004:2000 Tables 13-22 - Error correction characteristics
+ *
+ * @see http://www.thonky.com/qr-code-tutorial/error-correction-table
+ */
+ private const RSBLOCKS = [
+ 1 => [[ 7, [[ 1, 19], [ 0, 0]]], [10, [[ 1, 16], [ 0, 0]]], [13, [[ 1, 13], [ 0, 0]]], [17, [[ 1, 9], [ 0, 0]]]],
+ 2 => [[10, [[ 1, 34], [ 0, 0]]], [16, [[ 1, 28], [ 0, 0]]], [22, [[ 1, 22], [ 0, 0]]], [28, [[ 1, 16], [ 0, 0]]]],
+ 3 => [[15, [[ 1, 55], [ 0, 0]]], [26, [[ 1, 44], [ 0, 0]]], [18, [[ 2, 17], [ 0, 0]]], [22, [[ 2, 13], [ 0, 0]]]],
+ 4 => [[20, [[ 1, 80], [ 0, 0]]], [18, [[ 2, 32], [ 0, 0]]], [26, [[ 2, 24], [ 0, 0]]], [16, [[ 4, 9], [ 0, 0]]]],
+ 5 => [[26, [[ 1, 108], [ 0, 0]]], [24, [[ 2, 43], [ 0, 0]]], [18, [[ 2, 15], [ 2, 16]]], [22, [[ 2, 11], [ 2, 12]]]],
+ 6 => [[18, [[ 2, 68], [ 0, 0]]], [16, [[ 4, 27], [ 0, 0]]], [24, [[ 4, 19], [ 0, 0]]], [28, [[ 4, 15], [ 0, 0]]]],
+ 7 => [[20, [[ 2, 78], [ 0, 0]]], [18, [[ 4, 31], [ 0, 0]]], [18, [[ 2, 14], [ 4, 15]]], [26, [[ 4, 13], [ 1, 14]]]],
+ 8 => [[24, [[ 2, 97], [ 0, 0]]], [22, [[ 2, 38], [ 2, 39]]], [22, [[ 4, 18], [ 2, 19]]], [26, [[ 4, 14], [ 2, 15]]]],
+ 9 => [[30, [[ 2, 116], [ 0, 0]]], [22, [[ 3, 36], [ 2, 37]]], [20, [[ 4, 16], [ 4, 17]]], [24, [[ 4, 12], [ 4, 13]]]],
+ 10 => [[18, [[ 2, 68], [ 2, 69]]], [26, [[ 4, 43], [ 1, 44]]], [24, [[ 6, 19], [ 2, 20]]], [28, [[ 6, 15], [ 2, 16]]]],
+ 11 => [[20, [[ 4, 81], [ 0, 0]]], [30, [[ 1, 50], [ 4, 51]]], [28, [[ 4, 22], [ 4, 23]]], [24, [[ 3, 12], [ 8, 13]]]],
+ 12 => [[24, [[ 2, 92], [ 2, 93]]], [22, [[ 6, 36], [ 2, 37]]], [26, [[ 4, 20], [ 6, 21]]], [28, [[ 7, 14], [ 4, 15]]]],
+ 13 => [[26, [[ 4, 107], [ 0, 0]]], [22, [[ 8, 37], [ 1, 38]]], [24, [[ 8, 20], [ 4, 21]]], [22, [[12, 11], [ 4, 12]]]],
+ 14 => [[30, [[ 3, 115], [ 1, 116]]], [24, [[ 4, 40], [ 5, 41]]], [20, [[11, 16], [ 5, 17]]], [24, [[11, 12], [ 5, 13]]]],
+ 15 => [[22, [[ 5, 87], [ 1, 88]]], [24, [[ 5, 41], [ 5, 42]]], [30, [[ 5, 24], [ 7, 25]]], [24, [[11, 12], [ 7, 13]]]],
+ 16 => [[24, [[ 5, 98], [ 1, 99]]], [28, [[ 7, 45], [ 3, 46]]], [24, [[15, 19], [ 2, 20]]], [30, [[ 3, 15], [13, 16]]]],
+ 17 => [[28, [[ 1, 107], [ 5, 108]]], [28, [[10, 46], [ 1, 47]]], [28, [[ 1, 22], [15, 23]]], [28, [[ 2, 14], [17, 15]]]],
+ 18 => [[30, [[ 5, 120], [ 1, 121]]], [26, [[ 9, 43], [ 4, 44]]], [28, [[17, 22], [ 1, 23]]], [28, [[ 2, 14], [19, 15]]]],
+ 19 => [[28, [[ 3, 113], [ 4, 114]]], [26, [[ 3, 44], [11, 45]]], [26, [[17, 21], [ 4, 22]]], [26, [[ 9, 13], [16, 14]]]],
+ 20 => [[28, [[ 3, 107], [ 5, 108]]], [26, [[ 3, 41], [13, 42]]], [30, [[15, 24], [ 5, 25]]], [28, [[15, 15], [10, 16]]]],
+ 21 => [[28, [[ 4, 116], [ 4, 117]]], [26, [[17, 42], [ 0, 0]]], [28, [[17, 22], [ 6, 23]]], [30, [[19, 16], [ 6, 17]]]],
+ 22 => [[28, [[ 2, 111], [ 7, 112]]], [28, [[17, 46], [ 0, 0]]], [30, [[ 7, 24], [16, 25]]], [24, [[34, 13], [ 0, 0]]]],
+ 23 => [[30, [[ 4, 121], [ 5, 122]]], [28, [[ 4, 47], [14, 48]]], [30, [[11, 24], [14, 25]]], [30, [[16, 15], [14, 16]]]],
+ 24 => [[30, [[ 6, 117], [ 4, 118]]], [28, [[ 6, 45], [14, 46]]], [30, [[11, 24], [16, 25]]], [30, [[30, 16], [ 2, 17]]]],
+ 25 => [[26, [[ 8, 106], [ 4, 107]]], [28, [[ 8, 47], [13, 48]]], [30, [[ 7, 24], [22, 25]]], [30, [[22, 15], [13, 16]]]],
+ 26 => [[28, [[10, 114], [ 2, 115]]], [28, [[19, 46], [ 4, 47]]], [28, [[28, 22], [ 6, 23]]], [30, [[33, 16], [ 4, 17]]]],
+ 27 => [[30, [[ 8, 122], [ 4, 123]]], [28, [[22, 45], [ 3, 46]]], [30, [[ 8, 23], [26, 24]]], [30, [[12, 15], [28, 16]]]],
+ 28 => [[30, [[ 3, 117], [10, 118]]], [28, [[ 3, 45], [23, 46]]], [30, [[ 4, 24], [31, 25]]], [30, [[11, 15], [31, 16]]]],
+ 29 => [[30, [[ 7, 116], [ 7, 117]]], [28, [[21, 45], [ 7, 46]]], [30, [[ 1, 23], [37, 24]]], [30, [[19, 15], [26, 16]]]],
+ 30 => [[30, [[ 5, 115], [10, 116]]], [28, [[19, 47], [10, 48]]], [30, [[15, 24], [25, 25]]], [30, [[23, 15], [25, 16]]]],
+ 31 => [[30, [[13, 115], [ 3, 116]]], [28, [[ 2, 46], [29, 47]]], [30, [[42, 24], [ 1, 25]]], [30, [[23, 15], [28, 16]]]],
+ 32 => [[30, [[17, 115], [ 0, 0]]], [28, [[10, 46], [23, 47]]], [30, [[10, 24], [35, 25]]], [30, [[19, 15], [35, 16]]]],
+ 33 => [[30, [[17, 115], [ 1, 116]]], [28, [[14, 46], [21, 47]]], [30, [[29, 24], [19, 25]]], [30, [[11, 15], [46, 16]]]],
+ 34 => [[30, [[13, 115], [ 6, 116]]], [28, [[14, 46], [23, 47]]], [30, [[44, 24], [ 7, 25]]], [30, [[59, 16], [ 1, 17]]]],
+ 35 => [[30, [[12, 121], [ 7, 122]]], [28, [[12, 47], [26, 48]]], [30, [[39, 24], [14, 25]]], [30, [[22, 15], [41, 16]]]],
+ 36 => [[30, [[ 6, 121], [14, 122]]], [28, [[ 6, 47], [34, 48]]], [30, [[46, 24], [10, 25]]], [30, [[ 2, 15], [64, 16]]]],
+ 37 => [[30, [[17, 122], [ 4, 123]]], [28, [[29, 46], [14, 47]]], [30, [[49, 24], [10, 25]]], [30, [[24, 15], [46, 16]]]],
+ 38 => [[30, [[ 4, 122], [18, 123]]], [28, [[13, 46], [32, 47]]], [30, [[48, 24], [14, 25]]], [30, [[42, 15], [32, 16]]]],
+ 39 => [[30, [[20, 117], [ 4, 118]]], [28, [[40, 47], [ 7, 48]]], [30, [[43, 24], [22, 25]]], [30, [[10, 15], [67, 16]]]],
+ 40 => [[30, [[19, 118], [ 6, 119]]], [28, [[18, 47], [31, 48]]], [30, [[34, 24], [34, 25]]], [30, [[20, 15], [61, 16]]]],
+ ];
+
+ /**
+ * ISO/IEC 18004:2000 Table 1 - Data capacity of all versions of QR Code
+ */
+ private const TOTAL_CODEWORDS = [
+ 1 => 26,
+ 2 => 44,
+ 3 => 70,
+ 4 => 100,
+ 5 => 134,
+ 6 => 172,
+ 7 => 196,
+ 8 => 242,
+ 9 => 292,
+ 10 => 346,
+ 11 => 404,
+ 12 => 466,
+ 13 => 532,
+ 14 => 581,
+ 15 => 655,
+ 16 => 733,
+ 17 => 815,
+ 18 => 901,
+ 19 => 991,
+ 20 => 1085,
+ 21 => 1156,
+ 22 => 1258,
+ 23 => 1364,
+ 24 => 1474,
+ 25 => 1588,
+ 26 => 1706,
+ 27 => 1828,
+ 28 => 1921,
+ 29 => 2051,
+ 30 => 2185,
+ 31 => 2323,
+ 32 => 2465,
+ 33 => 2611,
+ 34 => 2761,
+ 35 => 2876,
+ 36 => 3034,
+ 37 => 3196,
+ 38 => 3362,
+ 39 => 3532,
+ 40 => 3706,
+ ];
+
+ /**
+ * QR Code version number
+ */
+ private int $version;
+
+ /**
+ * Version constructor.
+ *
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function __construct(int $version){
+
+ if($version < 1 || $version > 40){
+ throw new QRCodeException('invalid version given');
+ }
+
+ $this->version = $version;
+ }
+
+ /**
+ * returns the current version number as string
+ */
+ public function __toString():string{
+ return (string)$this->version;
+ }
+
+ /**
+ * returns the current version number
+ */
+ public function getVersionNumber():int{
+ return $this->version;
+ }
+
+ /**
+ * the matrix size for the given version
+ */
+ public function getDimension():int{
+ return (($this->version * 4) + 17);
+ }
+
+ /**
+ * the version pattern for the given version
+ */
+ public function getVersionPattern():?int{
+ return (self::VERSION_PATTERN[$this->version] ?? null);
+ }
+
+ /**
+ * the alignment patterns for the current version
+ *
+ * @return int[]
+ */
+ public function getAlignmentPattern():array{
+ return self::ALIGNMENT_PATTERN[$this->version];
+ }
+
+ /**
+ * returns ECC block information for the given $version and $eccLevel
+ */
+ public function getRSBlocks(EccLevel $eccLevel):array{
+ return self::RSBLOCKS[$this->version][$eccLevel->getOrdinal()];
+ }
+
+ /**
+ * returns the maximum codewords for the current version
+ */
+ public function getTotalCodewords():int{
+ return self::TOTAL_CODEWORDS[$this->version];
+ }
+
+}
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 0000000..77242d7
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/AlphaNum.php
@@ -0,0 +1,137 @@
+
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, Mode};
+use function array_flip, ceil, intdiv, str_split;
+
+/**
+ * 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 QRDataModeAbstract{
+
+ /**
+ * ISO/IEC 18004:2000 Table 5
+ *
+ * @var int[]
+ */
+ private const CHAR_TO_ORD = [
+ '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,
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::ALPHANUM;
+
+ /**
+ * @inheritDoc
+ */
+ public function getLengthInBits():int{
+ return (int)ceil($this->getCharCount() * (11 / 2));
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function validateString(string $string):bool{
+
+ if($string === ''){
+ return false;
+ }
+
+ foreach(str_split($string) as $chr){
+ if(!isset(self::CHAR_TO_ORD[$chr])){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+ $len = $this->getCharCount();
+
+ $bitBuffer
+ ->put(self::DATAMODE, 4)
+ ->put($len, $this::getLengthBits($versionNumber))
+ ;
+
+ // encode 2 characters in 11 bits
+ for($i = 0; ($i + 1) < $len; $i += 2){
+ $bitBuffer->put((self::CHAR_TO_ORD[$this->data[$i]] * 45 + self::CHAR_TO_ORD[$this->data[($i + 1)]]), 11);
+ }
+
+ // encode a remaining character in 6 bits
+ if($i < $len){
+ $bitBuffer->put(self::CHAR_TO_ORD[$this->data[$i]], 6);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+ $length = $bitBuffer->read(self::getLengthBits($versionNumber));
+ $charmap = array_flip(self::CHAR_TO_ORD);
+
+ // @todo
+ $toAlphaNumericChar = function(int $ord) use ($charmap):string{
+
+ if(isset($charmap[$ord])){
+ return $charmap[$ord];
+ }
+
+ throw new QRCodeDataException('invalid character value: '.$ord);
+ };
+
+ $result = '';
+ // Read two characters at a time
+ while($length > 1){
+
+ if($bitBuffer->available() < 11){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $nextTwoCharsBits = $bitBuffer->read(11);
+ $result .= $toAlphaNumericChar(intdiv($nextTwoCharsBits, 45));
+ $result .= $toAlphaNumericChar($nextTwoCharsBits % 45);
+ $length -= 2;
+ }
+
+ if($length === 1){
+ // special case: one character left
+ if($bitBuffer->available() < 6){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $result .= $toAlphaNumericChar($bitBuffer->read(6));
+ }
+
+ return $result;
+ }
+
+}
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 0000000..10ab852
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/Byte.php
@@ -0,0 +1,85 @@
+
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, Mode};
+use function chr, ord;
+
+/**
+ * 8-bit Byte mode, ISO-8859-1 or UTF-8
+ *
+ * ISO/IEC 18004:2000 Section 8.3.4
+ * ISO/IEC 18004:2000 Section 8.4.4
+ */
+final class Byte extends QRDataModeAbstract{
+
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::BYTE;
+
+ /**
+ * @inheritDoc
+ */
+ public function getLengthInBits():int{
+ return ($this->getCharCount() * 8);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function validateString(string $string):bool{
+ return $string !== '';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+ $len = $this->getCharCount();
+
+ $bitBuffer
+ ->put(self::DATAMODE, 4)
+ ->put($len, $this::getLengthBits($versionNumber))
+ ;
+
+ $i = 0;
+
+ while($i < $len){
+ $bitBuffer->put(ord($this->data[$i]), 8);
+ $i++;
+ }
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+ $length = $bitBuffer->read(self::getLengthBits($versionNumber));
+
+ if($bitBuffer->available() < (8 * $length)){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $readBytes = '';
+
+ for($i = 0; $i < $length; $i++){
+ $readBytes .= chr($bitBuffer->read(8));
+ }
+
+ return $readBytes;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/ECI.php b/vendor/chillerlan/php-qrcode/src/Data/ECI.php
new file mode 100644
index 0000000..3029e83
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/ECI.php
@@ -0,0 +1,155 @@
+
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, ECICharset, Mode};
+use function mb_convert_encoding, mb_detect_encoding, mb_internal_encoding, sprintf;
+
+/**
+ * Adds an ECI Designator
+ *
+ * ISO/IEC 18004:2000 8.4.1.1
+ *
+ * Please note that you have to take care for the correct data encoding when adding with QRCode::add*Segment()
+ */
+final class ECI extends QRDataModeAbstract{
+
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::ECI;
+
+ /**
+ * The current ECI encoding id
+ */
+ private int $encoding;
+
+ /**
+ * @inheritDoc
+ * @noinspection PhpMissingParentConstructorInspection
+ */
+ public function __construct(int $encoding){
+
+ if($encoding < 0 || $encoding > 999999){
+ throw new QRCodeDataException(sprintf('invalid encoding id: "%s"', $encoding));
+ }
+
+ $this->encoding = $encoding;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLengthInBits():int{
+
+ if($this->encoding < 128){
+ return 8;
+ }
+
+ if($this->encoding < 16384){
+ return 16;
+ }
+
+ return 24;
+ }
+
+ /**
+ * Writes an ECI designator to the bitbuffer
+ *
+ * @inheritDoc
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+ $bitBuffer->put(self::DATAMODE, 4);
+
+ if($this->encoding < 128){
+ $bitBuffer->put($this->encoding, 8);
+ }
+ elseif($this->encoding < 16384){
+ $bitBuffer->put(($this->encoding | 0x8000), 16);
+ }
+ elseif($this->encoding < 1000000){
+ $bitBuffer->put(($this->encoding | 0xC00000), 24);
+ }
+ else{
+ throw new QRCodeDataException('invalid ECI ID');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Reads and parses the value of an ECI designator
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function parseValue(BitBuffer $bitBuffer):ECICharset{
+ $firstByte = $bitBuffer->read(8);
+
+ // just one byte
+ if(($firstByte & 0b10000000) === 0){
+ $id = ($firstByte & 0b01111111);
+ }
+ // two bytes
+ elseif(($firstByte & 0b11000000) === 0b10000000){
+ $id = ((($firstByte & 0b00111111) << 8) | $bitBuffer->read(8));
+ }
+ // three bytes
+ elseif(($firstByte & 0b11100000) === 0b11000000){
+ $id = ((($firstByte & 0b00011111) << 16) | $bitBuffer->read(16));
+ }
+ else{
+ throw new QRCodeDataException(sprintf('error decoding ECI value first byte: %08b', $firstByte)); // @codeCoverageIgnore
+ }
+
+ return new ECICharset($id);
+ }
+
+ /**
+ * @codeCoverageIgnore Unused, but required as per interface
+ */
+ public static function validateString(string $string):bool{
+ return true;
+ }
+
+ /**
+ * Reads and decodes the ECI designator including the following byte sequence
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+ $eciCharset = self::parseValue($bitBuffer);
+ $nextMode = $bitBuffer->read(4);
+
+ if($nextMode !== Mode::BYTE){
+ throw new QRCodeDataException(sprintf('ECI designator followed by invalid mode: "%04b"', $nextMode));
+ }
+
+ $data = Byte::decodeSegment($bitBuffer, $versionNumber);
+ $encoding = $eciCharset->getName();
+
+ if($encoding === null){
+ // The spec isn't clear on this mode; see
+ // section 6.4.5: t does not say which encoding to assuming
+ // upon decoding. I have seen ISO-8859-1 used as well as
+ // Shift_JIS -- without anything like an ECI designator to
+ // give a hint.
+ $encoding = mb_detect_encoding($data, ['ISO-8859-1', 'Windows-1252', 'SJIS', 'UTF-8'], true);
+
+ if($encoding === false){
+ throw new QRCodeDataException('could not determine encoding in ECI mode'); // @codeCoverageIgnore
+ }
+ }
+
+ return mb_convert_encoding($data, mb_internal_encoding(), $encoding);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/Hanzi.php b/vendor/chillerlan/php-qrcode/src/Data/Hanzi.php
new file mode 100644
index 0000000..4476ad9
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/Hanzi.php
@@ -0,0 +1,205 @@
+
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, Mode};
+use Throwable;
+use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding,
+ mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
+
+/**
+ * Hanzi (simplified Chinese) mode, GBT18284-2000: 13-bit double-byte characters from the GB2312/GB18030 character set
+ *
+ * Please note that this is not part of the QR Code specification and may not be supported by all readers (ZXing-based ones do).
+ *
+ * @see https://en.wikipedia.org/wiki/GB_2312
+ * @see http://www.herongyang.com/GB2312/Introduction-of-GB2312.html
+ * @see https://en.wikipedia.org/wiki/GBK_(character_encoding)#Encoding
+ * @see https://gist.github.com/codemasher/91da33c44bfb48a81a6c1426bb8e4338
+ * @see https://github.com/zxing/zxing/blob/dfb06fa33b17a9e68321be151c22846c7b78048f/core/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java#L172-L209
+ * @see https://www.chinesestandard.net/PDF/English.aspx/GBT18284-2000
+ */
+final class Hanzi extends QRDataModeAbstract{
+
+ /**
+ * possible values: GB2312, GB18030
+ *
+ * @var string
+ */
+ public const ENCODING = 'GB18030';
+
+ /**
+ * @todo: other subsets???
+ *
+ * @var int
+ */
+ public const GB2312_SUBSET = 0b0001;
+
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::HANZI;
+
+ /**
+ * @inheritDoc
+ */
+ protected function getCharCount():int{
+ return mb_strlen($this->data, self::ENCODING);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLengthInBits():int{
+ return ($this->getCharCount() * 13);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function convertEncoding(string $string):string{
+ mb_detect_order([mb_internal_encoding(), 'UTF-8', 'GB2312', 'GB18030', 'CP936', 'EUC-CN', 'HZ']);
+
+ $detected = mb_detect_encoding($string, null, true);
+
+ if($detected === false){
+ throw new QRCodeDataException('mb_detect_encoding error');
+ }
+
+ if($detected === self::ENCODING){
+ return $string;
+ }
+
+ $string = mb_convert_encoding($string, self::ENCODING, $detected);
+
+ if(!is_string($string)){
+ throw new QRCodeDataException('mb_convert_encoding error');
+ }
+
+ return $string;
+ }
+
+ /**
+ * checks if a string qualifies as Hanzi/GB2312
+ */
+ public static function validateString(string $string):bool{
+
+ try{
+ $string = self::convertEncoding($string);
+ }
+ catch(Throwable $e){
+ return false;
+ }
+
+ $len = strlen($string);
+
+ if($len < 2 || ($len % 2) !== 0){
+ return false;
+ }
+
+ for($i = 0; $i < $len; $i += 2){
+ $byte1 = ord($string[$i]);
+ $byte2 = ord($string[($i + 1)]);
+
+ // byte 1 unused ranges
+ if($byte1 < 0xa1 || ($byte1 > 0xa9 && $byte1 < 0xb0) || $byte1 > 0xf7){
+ return false;
+ }
+
+ // byte 2 unused ranges
+ if($byte2 < 0xa1 || $byte2 > 0xfe){
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+
+ $bitBuffer
+ ->put(self::DATAMODE, 4)
+ ->put($this::GB2312_SUBSET, 4)
+ ->put($this->getCharCount(), $this::getLengthBits($versionNumber))
+ ;
+
+ $len = strlen($this->data);
+
+ for($i = 0; ($i + 1) < $len; $i += 2){
+ $c = (((0xff & ord($this->data[$i])) << 8) | (0xff & ord($this->data[($i + 1)])));
+
+ if($c >= 0xa1a1 && $c <= 0xaafe){
+ $c -= 0x0a1a1;
+ }
+ elseif($c >= 0xb0a1 && $c <= 0xfafe){
+ $c -= 0x0a6a1;
+ }
+ else{
+ throw new QRCodeDataException(sprintf('illegal char at %d [%d]', ($i + 1), $c));
+ }
+
+ $bitBuffer->put((((($c >> 8) & 0xff) * 0x060) + ($c & 0xff)), 13);
+ }
+
+ if($i < $len){
+ throw new QRCodeDataException(sprintf('illegal char at %d', ($i + 1)));
+ }
+
+ return $this;
+ }
+
+ /**
+ * See specification GBT 18284-2000
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+
+ // Hanzi mode contains a subset indicator right after mode indicator
+ if($bitBuffer->read(4) !== self::GB2312_SUBSET){
+ throw new QRCodeDataException('ecpected subset indicator for Hanzi mode');
+ }
+
+ $length = $bitBuffer->read(self::getLengthBits($versionNumber));
+
+ if($bitBuffer->available() < ($length * 13)){
+ throw new QRCodeDataException('not enough bits available');
+ }
+
+ // Each character will require 2 bytes. Read the characters as 2-byte pairs and decode as GB2312 afterwards
+ $buffer = [];
+ $offset = 0;
+
+ while($length > 0){
+ // Each 13 bits encodes a 2-byte character
+ $twoBytes = $bitBuffer->read(13);
+ $assembledTwoBytes = ((intdiv($twoBytes, 0x060) << 8) | ($twoBytes % 0x060));
+
+ $assembledTwoBytes += ($assembledTwoBytes < 0x00a00) // 0x003BF
+ ? 0x0a1a1 // In the 0xA1A1 to 0xAAFE range
+ : 0x0a6a1; // In the 0xB0A1 to 0xFAFE range
+
+ $buffer[$offset] = chr(0xff & ($assembledTwoBytes >> 8));
+ $buffer[($offset + 1)] = chr(0xff & $assembledTwoBytes);
+ $offset += 2;
+ $length--;
+ }
+
+ return mb_convert_encoding(implode($buffer), mb_internal_encoding(), self::ENCODING);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/Kanji.php b/vendor/chillerlan/php-qrcode/src/Data/Kanji.php
new file mode 100644
index 0000000..e42f96d
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/Kanji.php
@@ -0,0 +1,191 @@
+
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, Mode};
+use Throwable;
+use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding,
+ mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
+
+/**
+ * Kanji mode: 13-bit double-byte characters from the Shift-JIS character set
+ *
+ * ISO/IEC 18004:2000 Section 8.3.5
+ * ISO/IEC 18004:2000 Section 8.4.5
+ *
+ * @see https://en.wikipedia.org/wiki/Shift_JIS#As_defined_in_JIS_X_0208:1997
+ * @see http://www.rikai.com/library/kanjitables/kanji_codes.sjis.shtml
+ * @see https://gist.github.com/codemasher/d07d3e6e9346c08e7a41b8b978784952
+ */
+final class Kanji extends QRDataModeAbstract{
+
+ /**
+ * possible values: SJIS, SJIS-2004
+ *
+ * SJIS-2004 may produce errors in PHP < 8
+ *
+ * @var string
+ */
+ public const ENCODING = 'SJIS';
+
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::KANJI;
+
+ /**
+ * @inheritDoc
+ */
+ protected function getCharCount():int{
+ return mb_strlen($this->data, self::ENCODING);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLengthInBits():int{
+ return ($this->getCharCount() * 13);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function convertEncoding(string $string):string{
+ mb_detect_order([mb_internal_encoding(), 'UTF-8', 'SJIS', 'SJIS-2004']);
+
+ $detected = mb_detect_encoding($string, null, true);
+
+ if($detected === false){
+ throw new QRCodeDataException('mb_detect_encoding error');
+ }
+
+ if($detected === self::ENCODING){
+ return $string;
+ }
+
+ $string = mb_convert_encoding($string, self::ENCODING, $detected);
+
+ if(!is_string($string)){
+ throw new QRCodeDataException(sprintf('invalid encoding: %s', $detected));
+ }
+
+ return $string;
+ }
+
+ /**
+ * checks if a string qualifies as SJIS Kanji
+ */
+ public static function validateString(string $string):bool{
+
+ try{
+ $string = self::convertEncoding($string);
+ }
+ catch(Throwable $e){
+ return false;
+ }
+
+ $len = strlen($string);
+
+ if($len < 2 || ($len % 2) !== 0){
+ return false;
+ }
+
+ for($i = 0; $i < $len; $i += 2){
+ $byte1 = ord($string[$i]);
+ $byte2 = ord($string[($i + 1)]);
+
+ // byte 1 unused and vendor ranges
+ if($byte1 < 0x81 || ($byte1 > 0x84 && $byte1 < 0x88) || ($byte1 > 0x9f && $byte1 < 0xe0) || $byte1 > 0xea){
+ return false;
+ }
+
+ // byte 2 unused ranges
+ if($byte2 < 0x40 || $byte2 === 0x7f || $byte2 > 0xfc){
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+
+ $bitBuffer
+ ->put(self::DATAMODE, 4)
+ ->put($this->getCharCount(), $this::getLengthBits($versionNumber))
+ ;
+
+ $len = strlen($this->data);
+
+ for($i = 0; ($i + 1) < $len; $i += 2){
+ $c = (((0xff & ord($this->data[$i])) << 8) | (0xff & ord($this->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));
+ }
+
+ $bitBuffer->put((((($c >> 8) & 0xff) * 0xc0) + ($c & 0xff)), 13);
+ }
+
+ if($i < $len){
+ throw new QRCodeDataException(sprintf('illegal char at %d', ($i + 1)));
+ }
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+ $length = $bitBuffer->read(self::getLengthBits($versionNumber));
+
+ if($bitBuffer->available() < ($length * 13)){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ // Each character will require 2 bytes. Read the characters as 2-byte pairs and decode as SJIS afterwards
+ $buffer = [];
+ $offset = 0;
+
+ while($length > 0){
+ // Each 13 bits encodes a 2-byte character
+ $twoBytes = $bitBuffer->read(13);
+ $assembledTwoBytes = ((intdiv($twoBytes, 0x0c0) << 8) | ($twoBytes % 0x0c0));
+
+ $assembledTwoBytes += ($assembledTwoBytes < 0x01f00)
+ ? 0x08140 // In the 0x8140 to 0x9FFC range
+ : 0x0c140; // In the 0xE040 to 0xEBBF range
+
+ $buffer[$offset] = chr(0xff & ($assembledTwoBytes >> 8));
+ $buffer[($offset + 1)] = chr(0xff & $assembledTwoBytes);
+ $offset += 2;
+ $length--;
+ }
+
+ return mb_convert_encoding(implode($buffer), mb_internal_encoding(), self::ENCODING);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/Number.php b/vendor/chillerlan/php-qrcode/src/Data/Number.php
new file mode 100644
index 0000000..285be37
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/Number.php
@@ -0,0 +1,182 @@
+
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, Mode};
+use function array_flip, ceil, intdiv, str_split, substr, unpack;
+
+/**
+ * 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 QRDataModeAbstract{
+
+ /**
+ * @var int[]
+ */
+ private const NUMBER_TO_ORD = [
+ '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ public const DATAMODE = Mode::NUMBER;
+
+ /**
+ * @inheritDoc
+ */
+ public function getLengthInBits():int{
+ return (int)ceil($this->getCharCount() * (10 / 3));
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function validateString(string $string):bool{
+
+ if($string === ''){
+ return false;
+ }
+
+ foreach(str_split($string) as $chr){
+ if(!isset(self::NUMBER_TO_ORD[$chr])){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface{
+ $len = $this->getCharCount();
+
+ $bitBuffer
+ ->put(self::DATAMODE, 4)
+ ->put($len, $this::getLengthBits($versionNumber))
+ ;
+
+ $i = 0;
+
+ // encode numeric triplets in 10 bits
+ while(($i + 2) < $len){
+ $bitBuffer->put($this->parseInt(substr($this->data, $i, 3)), 10);
+ $i += 3;
+ }
+
+ if($i < $len){
+
+ // encode 2 remaining numbers in 7 bits
+ if(($len - $i) === 2){
+ $bitBuffer->put($this->parseInt(substr($this->data, $i, 2)), 7);
+ }
+ // encode one remaining number in 4 bits
+ elseif(($len - $i) === 1){
+ $bitBuffer->put($this->parseInt(substr($this->data, $i, 1)), 4);
+ }
+
+ }
+
+ return $this;
+ }
+
+ /**
+ * get the code for the given numeric string
+ */
+ private function parseInt(string $string):int{
+ $num = 0;
+
+ foreach(unpack('C*', $string) as $chr){
+ $num = ($num * 10 + $chr - 48);
+ }
+
+ return $num;
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+ $length = $bitBuffer->read(self::getLengthBits($versionNumber));
+ $charmap = array_flip(self::NUMBER_TO_ORD);
+
+ // @todo
+ $toNumericChar = function(int $ord) use ($charmap):string{
+
+ if(isset($charmap[$ord])){
+ return $charmap[$ord];
+ }
+
+ throw new QRCodeDataException('invalid character value: '.$ord);
+ };
+
+ $result = '';
+ // Read three digits at a time
+ while($length >= 3){
+ // Each 10 bits encodes three digits
+ if($bitBuffer->available() < 10){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $threeDigitsBits = $bitBuffer->read(10);
+
+ if($threeDigitsBits >= 1000){
+ throw new QRCodeDataException('error decoding numeric value');
+ }
+
+ $result .= $toNumericChar(intdiv($threeDigitsBits, 100));
+ $result .= $toNumericChar(intdiv($threeDigitsBits, 10) % 10);
+ $result .= $toNumericChar($threeDigitsBits % 10);
+
+ $length -= 3;
+ }
+
+ if($length === 2){
+ // Two digits left over to read, encoded in 7 bits
+ if($bitBuffer->available() < 7){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $twoDigitsBits = $bitBuffer->read(7);
+
+ if($twoDigitsBits >= 100){
+ throw new QRCodeDataException('error decoding numeric value');
+ }
+
+ $result .= $toNumericChar(intdiv($twoDigitsBits, 10));
+ $result .= $toNumericChar($twoDigitsBits % 10);
+ }
+ elseif($length === 1){
+ // One digit left over to read
+ if($bitBuffer->available() < 4){
+ throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
+ }
+
+ $digitBits = $bitBuffer->read(4);
+
+ if($digitBits >= 10){
+ throw new QRCodeDataException('error decoding numeric value');
+ }
+
+ $result .= $toNumericChar($digitBits);
+ }
+
+ return $result;
+ }
+
+}
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 0000000..04ffbd7
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/QRCodeDataException.php
@@ -0,0 +1,20 @@
+
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\QRCodeException;
+
+/**
+ * An exception container
+ */
+final class QRCodeDataException extends QRCodeException{
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRData.php b/vendor/chillerlan/php-qrcode/src/Data/QRData.php
new file mode 100644
index 0000000..7056b63
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/QRData.php
@@ -0,0 +1,263 @@
+
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, EccLevel, Mode, Version};
+use chillerlan\Settings\SettingsContainerInterface;
+use function count, sprintf;
+
+/**
+ * Processes the binary data and maps it on a QRMatrix which is then being returned
+ */
+final class QRData{
+
+ /**
+ * the options instance
+ *
+ * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions
+ */
+ private SettingsContainerInterface $options;
+
+ /**
+ * a BitBuffer instance
+ */
+ private BitBuffer $bitBuffer;
+
+ /**
+ * an EccLevel instance
+ */
+ private EccLevel $eccLevel;
+
+ /**
+ * current QR Code version
+ */
+ private Version $version;
+
+ /**
+ * @var \chillerlan\QRCode\Data\QRDataModeInterface[]
+ */
+ private array $dataSegments = [];
+
+ /**
+ * Max bits for the current ECC mode
+ *
+ * @var int[]
+ */
+ private array $maxBitsForEcc;
+
+ /**
+ * QRData constructor.
+ */
+ public function __construct(SettingsContainerInterface $options, array $dataSegments = []){
+ $this->options = $options;
+ $this->bitBuffer = new BitBuffer;
+ $this->eccLevel = new EccLevel($this->options->eccLevel);
+ $this->maxBitsForEcc = $this->eccLevel->getMaxBits();
+
+ $this->setData($dataSegments);
+ }
+
+ /**
+ * Sets the data string (internally called by the constructor)
+ *
+ * Subsequent calls will overwrite the current state - use the QRCode::add*Segement() method instead
+ *
+ * @param \chillerlan\QRCode\Data\QRDataModeInterface[] $dataSegments
+ */
+ public function setData(array $dataSegments):self{
+ $this->dataSegments = $dataSegments;
+ $this->version = $this->getMinimumVersion();
+
+ $this->bitBuffer->clear();
+ $this->writeBitBuffer();
+
+ return $this;
+ }
+
+ /**
+ * Returns the current BitBuffer instance
+ *
+ * @codeCoverageIgnore
+ */
+ public function getBitBuffer():BitBuffer{
+ return $this->bitBuffer;
+ }
+
+ /**
+ * Sets a BitBuffer object
+ *
+ * This can be used instead of setData(), however, the version auto-detection is not available in this case.
+ * The version needs to match the length bits range for the data mode the data has been encoded with,
+ * additionally the bit array needs to contain enough pad bits.
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function setBitBuffer(BitBuffer $bitBuffer):self{
+
+ if($this->options->version === Version::AUTO){
+ throw new QRCodeDataException('version auto detection is not available');
+ }
+
+ if($bitBuffer->getLength() === 0){
+ throw new QRCodeDataException('the given BitBuffer is empty');
+ }
+
+ $this->dataSegments = [];
+ $this->bitBuffer = $bitBuffer;
+ $this->version = new Version($this->options->version);
+
+ return $this;
+ }
+
+ /**
+ * returns a fresh matrix object with the data written and masked with the given $maskPattern
+ */
+ public function writeMatrix():QRMatrix{
+ return (new QRMatrix($this->version, $this->eccLevel))
+ ->initFunctionalPatterns()
+ ->writeCodewords($this->bitBuffer)
+ ;
+ }
+
+ /**
+ * estimates the total length of the several mode segments in order to guess the minimum version
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function estimateTotalBitLength():int{
+ $length = 0;
+
+ foreach($this->dataSegments as $segment){
+ // data length of the current segment
+ $length += $segment->getLengthInBits();
+ // +4 bits for the mode descriptor
+ $length += 4;
+ // Hanzi mode sets an additional 4 bit long subset identifier
+ if($segment instanceof Hanzi){
+ $length += 4;
+ }
+ }
+
+ $provisionalVersion = null;
+
+ foreach($this->maxBitsForEcc as $version => $maxBits){
+
+ if($length <= $maxBits){
+ $provisionalVersion = $version;
+ }
+
+ }
+
+ if($provisionalVersion !== null){
+
+ // add character count indicator bits for the provisional version
+ foreach($this->dataSegments as $segment){
+ $length += Mode::getLengthBitsForVersion($segment::DATAMODE, $provisionalVersion);
+ }
+
+ // it seems that in some cases the estimated total length is not 100% accurate,
+ // so we substract 4 bits from the total when not in mixed mode
+ if(count($this->dataSegments) <= 1){
+ $length -= 4;
+ }
+
+ // we've got a match!
+ // or let's see if there's a higher version number available
+ if($length <= $this->maxBitsForEcc[$provisionalVersion] || isset($this->maxBitsForEcc[($provisionalVersion + 1)])){
+ return $length;
+ }
+
+ }
+
+ throw new QRCodeDataException(sprintf('estimated data exceeds %d bits', $length));
+ }
+
+ /**
+ * returns the minimum version number for the given string
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function getMinimumVersion():Version{
+
+ if($this->options->version !== Version::AUTO){
+ return new Version($this->options->version);
+ }
+
+ $total = $this->estimateTotalBitLength();
+
+ // guess the version number within the given range
+ for($version = $this->options->versionMin; $version <= $this->options->versionMax; $version++){
+ if($total <= $this->maxBitsForEcc[$version]){
+ return new Version($version);
+ }
+ }
+
+ // it's almost impossible to run into this one as $this::estimateTotalBitLength() would throw first
+ throw new QRCodeDataException('failed to guess minimum version'); // @codeCoverageIgnore
+ }
+
+ /**
+ * creates a BitBuffer and writes the string data to it
+ *
+ * @throws \chillerlan\QRCode\QRCodeException on data overflow
+ */
+ private function writeBitBuffer():void{
+ $MAX_BITS = $this->eccLevel->getMaxBitsForVersion($this->version);
+
+ foreach($this->dataSegments as $segment){
+ $segment->write($this->bitBuffer, $this->version->getVersionNumber());
+ }
+
+ // overflow, likely caused due to invalid version setting
+ if($this->bitBuffer->getLength() > $MAX_BITS){
+ throw new QRCodeDataException(
+ sprintf('code length overflow. (%d > %d bit)', $this->bitBuffer->getLength(), $MAX_BITS)
+ );
+ }
+
+ // add terminator (ISO/IEC 18004:2000 Table 2)
+ if(($this->bitBuffer->getLength() + 4) <= $MAX_BITS){
+ $this->bitBuffer->put(Mode::TERMINATOR, 4);
+ }
+
+ // Padding: ISO/IEC 18004:2000 8.4.9 Bit stream to codeword conversion
+
+ // if the final codeword is not exactly 8 bits in length, it shall be made 8 bits long
+ // by the addition of padding bits with binary value 0
+ while(($this->bitBuffer->getLength() % 8) !== 0){
+
+ if($this->bitBuffer->getLength() === $MAX_BITS){
+ break;
+ }
+
+ $this->bitBuffer->putBit(false);
+ }
+
+ // The message bit stream shall then be extended to fill the data capacity of the symbol
+ // corresponding to the Version and Error Correction Level, by the addition of the Pad
+ // Codewords 11101100 and 00010001 alternately.
+ $alternate = false;
+
+ while(($this->bitBuffer->getLength() + 8) <= $MAX_BITS){
+ $this->bitBuffer->put(($alternate) ? 0b00010001 : 0b11101100, 8);
+
+ $alternate = !$alternate;
+ }
+
+ // In certain versions of symbol, it may be necessary to add 3, 4 or 7 Remainder Bits (all zeros)
+ // to the end of the message in order exactly to fill the symbol capacity
+ while($this->bitBuffer->getLength() <= $MAX_BITS){
+ $this->bitBuffer->putBit(false);
+ }
+
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRDataModeAbstract.php b/vendor/chillerlan/php-qrcode/src/Data/QRDataModeAbstract.php
new file mode 100644
index 0000000..94b93ac
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/QRDataModeAbstract.php
@@ -0,0 +1,61 @@
+
+ * @copyright 2020 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\Mode;
+
+/**
+ * abstract methods for the several data modes
+ */
+abstract class QRDataModeAbstract implements QRDataModeInterface{
+
+ /**
+ * The data to write
+ */
+ protected string $data;
+
+ /**
+ * QRDataModeAbstract constructor.
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function __construct(string $data){
+ $data = $this::convertEncoding($data);
+
+ if(!$this::validateString($data)){
+ throw new QRCodeDataException('invalid data');
+ }
+
+ $this->data = $data;
+ }
+
+ /**
+ * returns the character count of the $data string
+ */
+ protected function getCharCount():int{
+ return strlen($this->data);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function convertEncoding(string $string):string{
+ return $string;
+ }
+
+ /**
+ * shortcut
+ */
+ protected static function getLengthBits(int $versionNumber):int{
+ return Mode::getLengthBitsForVersion(static::DATAMODE, $versionNumber);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRDataModeInterface.php b/vendor/chillerlan/php-qrcode/src/Data/QRDataModeInterface.php
new file mode 100644
index 0000000..321cf60
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/QRDataModeInterface.php
@@ -0,0 +1,63 @@
+
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\BitBuffer;
+
+/**
+ * Specifies the methods reqired for the data modules (Number, Alphanum, Byte and Kanji)
+ */
+interface QRDataModeInterface{
+
+ /**
+ * the current data mode: Number, Alphanum, Kanji, Hanzi, Byte, ECI
+ *
+ * tbh I hate this constant here, but it's part of the interface, so I can't just declare it in the abstract class.
+ * (phan will complain about a PhanAccessOverridesFinalConstant)
+ *
+ * @see https://wiki.php.net/rfc/final_class_const
+ *
+ * @var int
+ * @see \chillerlan\QRCode\Common\Mode
+ * @internal do not call this constant from the interface, but rather from one of the child classes
+ */
+ public const DATAMODE = -1;
+
+ /**
+ * retruns the length in bits of the data string
+ */
+ public function getLengthInBits():int;
+
+ /**
+ * encoding conversion helper
+ *
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public static function convertEncoding(string $string):string;
+
+ /**
+ * checks if the given string qualifies for the encoder module
+ */
+ public static function validateString(string $string):bool;
+
+ /**
+ * writes the actual data string to the BitBuffer, uses the given version to determine the length bits
+ *
+ * @see \chillerlan\QRCode\Data\QRData::writeBitBuffer()
+ */
+ public function write(BitBuffer $bitBuffer, int $versionNumber):QRDataModeInterface;
+
+ /**
+ * reads a segment from the BitBuffer and decodes in the current data mode
+ */
+ public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string;
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/QRMatrix.php b/vendor/chillerlan/php-qrcode/src/Data/QRMatrix.php
new file mode 100644
index 0000000..d92c1a7
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/QRMatrix.php
@@ -0,0 +1,812 @@
+
+ * @copyright 2017 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, EccLevel, MaskPattern, Version};
+use function array_fill, array_map, array_reverse, count, intdiv;
+
+/**
+ * Holds an array representation of the final QR Code that contains numerical values for later output modifications;
+ * maps the ECC coded binary data and applies the mask pattern
+ *
+ * @see http://www.thonky.com/qr-code-tutorial/format-version-information
+ */
+class QRMatrix{
+
+ /*
+ * special values
+ */
+
+ /** @var int */
+ public const IS_DARK = 0b100000000000;
+ /** @var int */
+ public const M_NULL = 0b000000000000;
+ /** @var int */
+ public const M_LOGO = 0b001000000000;
+ /** @var int */
+ public const M_LOGO_DARK = 0b101000000000;
+
+ /*
+ * light values
+ */
+
+ /** @var int */
+ public const M_DATA = 0b000000000010;
+ /** @var int */
+ public const M_FINDER = 0b000000000100;
+ /** @var int */
+ public const M_SEPARATOR = 0b000000001000;
+ /** @var int */
+ public const M_ALIGNMENT = 0b000000010000;
+ /** @var int */
+ public const M_TIMING = 0b000000100000;
+ /** @var int */
+ public const M_FORMAT = 0b000001000000;
+ /** @var int */
+ public const M_VERSION = 0b000010000000;
+ /** @var int */
+ public const M_QUIETZONE = 0b000100000000;
+
+ /*
+ * dark values
+ */
+
+ /** @var int */
+ public const M_DARKMODULE = 0b100000000001;
+ /** @var int */
+ public const M_DATA_DARK = 0b100000000010;
+ /** @var int */
+ public const M_FINDER_DARK = 0b100000000100;
+ /** @var int */
+ public const M_ALIGNMENT_DARK = 0b100000010000;
+ /** @var int */
+ public const M_TIMING_DARK = 0b100000100000;
+ /** @var int */
+ public const M_FORMAT_DARK = 0b100001000000;
+ /** @var int */
+ public const M_VERSION_DARK = 0b100010000000;
+ /** @var int */
+ public const M_FINDER_DOT = 0b110000000000;
+
+ /*
+ * values used for reversed reflectance
+ */
+
+ /** @var int */
+ public const M_DARKMODULE_LIGHT = 0b000000000001;
+ /** @var int */
+ public const M_FINDER_DOT_LIGHT = 0b010000000000;
+ /** @var int */
+ public const M_SEPARATOR_DARK = 0b100000001000;
+ /** @var int */
+ public const M_QUIETZONE_DARK = 0b100100000000;
+
+ /**
+ * Map of flag => coord
+ *
+ * @see \chillerlan\QRCode\Data\QRMatrix::checkNeighbours()
+ *
+ * @var array
+ */
+ protected const neighbours = [
+ 0b00000001 => [-1, -1],
+ 0b00000010 => [ 0, -1],
+ 0b00000100 => [ 1, -1],
+ 0b00001000 => [ 1, 0],
+ 0b00010000 => [ 1, 1],
+ 0b00100000 => [ 0, 1],
+ 0b01000000 => [-1, 1],
+ 0b10000000 => [-1, 0],
+ ];
+
+ /**
+ * the matrix version - always set in QRMatrix, may be null in BitMatrix
+ */
+ protected ?Version $version = null;
+
+ /**
+ * the current ECC level - always set in QRMatrix, may be null in BitMatrix
+ */
+ protected ?EccLevel $eccLevel = null;
+
+ /**
+ * the mask pattern that was used in the most recent operation, set via:
+ *
+ * - QRMatrix::setFormatInfo()
+ * - QRMatrix::mask()
+ * - BitMatrix::readFormatInformation()
+ */
+ protected ?MaskPattern $maskPattern = null;
+
+ /**
+ * the size (side length) of the matrix, including quiet zone (if created)
+ */
+ protected int $moduleCount;
+
+ /**
+ * the actual matrix data array
+ *
+ * @var int[][]
+ */
+ protected array $matrix;
+
+ /**
+ * QRMatrix constructor.
+ */
+ public function __construct(Version $version, EccLevel $eccLevel){
+ $this->version = $version;
+ $this->eccLevel = $eccLevel;
+ $this->moduleCount = $this->version->getDimension();
+ $this->matrix = $this->createMatrix($this->moduleCount, $this::M_NULL);
+ }
+
+ /**
+ * Creates a 2-dimensional array (square) of the given $size
+ */
+ protected function createMatrix(int $size, int $value):array{
+ return array_fill(0, $size, array_fill(0, $size, $value));
+ }
+
+ /**
+ * shortcut to initialize the functional patterns
+ */
+ public function initFunctionalPatterns():self{
+ return $this
+ ->setFinderPattern()
+ ->setSeparators()
+ ->setAlignmentPattern()
+ ->setTimingPattern()
+ ->setDarkModule()
+ ->setVersionNumber()
+ ->setFormatInfo()
+ ;
+ }
+
+ /**
+ * Returns the data matrix, returns a pure boolean representation if $boolean is set to true
+ *
+ * @return int[][]|bool[][]
+ */
+ public function getMatrix(bool $boolean = null):array{
+
+ if($boolean !== true){
+ return $this->matrix;
+ }
+
+ $matrix = $this->matrix;
+
+ foreach($matrix as &$row){
+ $row = array_map([$this, 'isDark'], $row);
+ }
+
+ return $matrix;
+ }
+
+ /**
+ * @deprecated 5.0.0 use QRMatrix::getMatrix() instead
+ * @see \chillerlan\QRCode\Data\QRMatrix::getMatrix()
+ * @codeCoverageIgnore
+ */
+ public function matrix(bool $boolean = null):array{
+ return $this->getMatrix($boolean);
+ }
+
+ /**
+ * Returns the current version number
+ */
+ public function getVersion():?Version{
+ return $this->version;
+ }
+
+ /**
+ * @deprecated 5.0.0 use QRMatrix::getVersion() instead
+ * @see \chillerlan\QRCode\Data\QRMatrix::getVersion()
+ * @codeCoverageIgnore
+ */
+ public function version():?Version{
+ return $this->getVersion();
+ }
+
+ /**
+ * Returns the current ECC level
+ */
+ public function getEccLevel():?EccLevel{
+ return $this->eccLevel;
+ }
+
+ /**
+ * @deprecated 5.0.0 use QRMatrix::getEccLevel() instead
+ * @see \chillerlan\QRCode\Data\QRMatrix::getEccLevel()
+ * @codeCoverageIgnore
+ */
+ public function eccLevel():?EccLevel{
+ return $this->getEccLevel();
+ }
+
+ /**
+ * Returns the current mask pattern
+ */
+ public function getMaskPattern():?MaskPattern{
+ return $this->maskPattern;
+ }
+
+ /**
+ * @deprecated 5.0.0 use QRMatrix::getMaskPattern() instead
+ * @see \chillerlan\QRCode\Data\QRMatrix::getMaskPattern()
+ * @codeCoverageIgnore
+ */
+ public function maskPattern():?MaskPattern{
+ return $this->getMaskPattern();
+ }
+
+ /**
+ * Returns the absoulute size of the matrix, including quiet zone (after setting it).
+ *
+ * size = version * 4 + 17 [ + 2 * quietzone size]
+ */
+ public function getSize():int{
+ return $this->moduleCount;
+ }
+
+ /**
+ * @deprecated 5.0.0 use QRMatrix::getSize() instead
+ * @see \chillerlan\QRCode\Data\QRMatrix::getSize()
+ * @codeCoverageIgnore
+ */
+ public function size():int{
+ return $this->getSize();
+ }
+
+ /**
+ * Returns the value of the module at position [$x, $y] or -1 if the coordinate is outside the matrix
+ */
+ public function get(int $x, int $y):int{
+
+ if(!isset($this->matrix[$y][$x])){
+ return -1;
+ }
+
+ return $this->matrix[$y][$x];
+ }
+
+ /**
+ * Sets the $M_TYPE value for the module at position [$x, $y]
+ *
+ * true => $M_TYPE | 0x800
+ * false => $M_TYPE
+ */
+ public function set(int $x, int $y, bool $value, int $M_TYPE):self{
+
+ if(isset($this->matrix[$y][$x])){
+ // we don't know whether the input is dark, so we remove the dark bit
+ $M_TYPE &= ~$this::IS_DARK;
+
+ if($value === true){
+ $M_TYPE |= $this::IS_DARK;
+ }
+
+ $this->matrix[$y][$x] = $M_TYPE;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Fills an area of $width * $height, from the given starting point [$startX, $startY] (top left) with $value for $M_TYPE.
+ */
+ public function setArea(int $startX, int $startY, int $width, int $height, bool $value, int $M_TYPE):self{
+
+ for($y = $startY; $y < ($startY + $height); $y++){
+ for($x = $startX; $x < ($startX + $width); $x++){
+ $this->set($x, $y, $value, $M_TYPE);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Flips the value of the module at ($x, $y)
+ */
+ public function flip(int $x, int $y):self{
+
+ if(isset($this->matrix[$y][$x])){
+ $this->matrix[$y][$x] ^= $this::IS_DARK;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Checks whether the module at ($x, $y) is of the given $M_TYPE
+ *
+ * true => $value & $M_TYPE === $M_TYPE
+ *
+ * Also, returns false if the given coordinates are out of range.
+ */
+ public function checkType(int $x, int $y, int $M_TYPE):bool{
+
+ if(isset($this->matrix[$y][$x])){
+ return ($this->matrix[$y][$x] & $M_TYPE) === $M_TYPE;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the module at ($x, $y) is in the given array of $M_TYPES,
+ * returns true if a match is found, otherwise false.
+ */
+ public function checkTypeIn(int $x, int $y, array $M_TYPES):bool{
+
+ foreach($M_TYPES as $type){
+ if($this->checkType($x, $y, $type)){
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the module at ($x, $y) is true (dark) or false (light)
+ *
+ * Also, returns false if the given coordinates are out of range.
+ */
+ public function check(int $x, int $y):bool{
+
+ if(isset($this->matrix[$y][$x])){
+ return $this->isDark($this->matrix[$y][$x]);
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given $M_TYPE is a dark value
+ */
+ public function isDark(int $M_TYPE):bool{
+ return ($M_TYPE & $this::IS_DARK) === $this::IS_DARK;
+ }
+
+ /**
+ * Checks the status of the neighbouring modules for the module at ($x, $y) and returns a bitmask with the results.
+ *
+ * The 8 flags of the bitmask represent the status of each of the neighbouring fields,
+ * starting with the lowest bit for top left, going clockwise:
+ *
+ * 0 1 2
+ * 7 # 3
+ * 6 5 4
+ */
+ public function checkNeighbours(int $x, int $y, int $M_TYPE = null):int{
+ $bits = 0;
+
+ foreach($this::neighbours as $bit => [$ix, $iy]){
+ $ix += $x;
+ $iy += $y;
+
+ // $M_TYPE is given, skip if the field is not the same type
+ if($M_TYPE !== null && !$this->checkType($ix, $iy, $M_TYPE)){
+ continue;
+ }
+
+ if($this->checkType($ix, $iy, $this::IS_DARK)){
+ $bits |= $bit;
+ }
+ }
+
+ return $bits;
+ }
+
+ /**
+ * Sets the "dark module", that is always on the same position 1x1px away from the bottom left finder
+ *
+ * 4 * version + 9 or moduleCount - 8
+ */
+ public function setDarkModule():self{
+ $this->set(8, ($this->moduleCount - 8), 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():self{
+
+ $pos = [
+ [0, 0], // top left
+ [($this->moduleCount - 7), 0], // top right
+ [0, ($this->moduleCount - 7)], // bottom left
+ ];
+
+ foreach($pos as $c){
+ $this
+ ->setArea( $c[0] , $c[1] , 7, 7, true, $this::M_FINDER)
+ ->setArea(($c[0] + 1), ($c[1] + 1), 5, 5, false, $this::M_FINDER)
+ ->setArea(($c[0] + 2), ($c[1] + 2), 3, 3, true, $this::M_FINDER_DOT)
+ ;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Draws the separator lines around the finder patterns
+ *
+ * ISO/IEC 18004:2000 Section 7.3.3
+ */
+ public function setSeparators():self{
+
+ $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():self{
+ $alignmentPattern = $this->version->getAlignmentPattern();
+
+ foreach($alignmentPattern as $y){
+ foreach($alignmentPattern as $x){
+
+ // skip existing patterns
+ if($this->matrix[$y][$x] !== $this::M_NULL){
+ continue;
+ }
+
+ $this
+ ->setArea(($x - 2), ($y - 2), 5, 5, true, $this::M_ALIGNMENT)
+ ->setArea(($x - 1), ($y - 1), 3, 3, false, $this::M_ALIGNMENT)
+ ->set($x, $y, true, $this::M_ALIGNMENT)
+ ;
+
+ }
+ }
+
+ 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():self{
+
+ for($i = 8; $i < ($this->moduleCount - 8); $i++){
+
+ if($this->matrix[6][$i] !== $this::M_NULL || $this->matrix[$i][6] !== $this::M_NULL){
+ continue;
+ }
+
+ $v = ($i % 2) === 0;
+
+ $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():self{
+ $bits = $this->version->getVersionPattern();
+
+ if($bits !== null){
+
+ for($i = 0; $i < 18; $i++){
+ $a = intdiv($i, 3);
+ $b = (($i % 3) + ($this->moduleCount - 8 - 3));
+ $v = (($bits >> $i) & 1) === 1;
+
+ $this->set($b, $a, $v, $this::M_VERSION); // ne
+ $this->set($a, $b, $v, $this::M_VERSION); // sw
+ }
+
+ }
+
+ return $this;
+ }
+
+ /**
+ * Draws the format info along the finder patterns. If no $maskPattern, all format info modules will be set to false.
+ *
+ * ISO/IEC 18004:2000 Section 8.9
+ */
+ public function setFormatInfo(MaskPattern $maskPattern = null):self{
+ $this->maskPattern = $maskPattern;
+ $bits = 0; // sets all format fields to false (test mode)
+
+ if($this->maskPattern instanceof MaskPattern){
+ $bits = $this->eccLevel->getformatPattern($this->maskPattern);
+ }
+
+ for($i = 0; $i < 15; $i++){
+ $v = (($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);
+ }
+
+ }
+
+ 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 $quietZoneSize):self{
+
+ // early exit if there's nothing to add
+ if($quietZoneSize < 1){
+ return $this;
+ }
+
+ if($this->matrix[($this->moduleCount - 1)][($this->moduleCount - 1)] === $this::M_NULL){
+ throw new QRCodeDataException('use only after writing data');
+ }
+
+ // create a matrix with the new size
+ $newSize = ($this->moduleCount + ($quietZoneSize * 2));
+ $newMatrix = $this->createMatrix($newSize, $this::M_QUIETZONE);
+
+ // copy over the current matrix
+ foreach($this->matrix as $y => $row){
+ foreach($row as $x => $val){
+ $newMatrix[($y + $quietZoneSize)][($x + $quietZoneSize)] = $val;
+ }
+ }
+
+ // set the new values
+ $this->moduleCount = $newSize;
+ $this->matrix = $newMatrix;
+
+ return $this;
+ }
+
+ /**
+ * Rotates the matrix by 90 degrees clock wise
+ */
+ public function rotate90():self{
+ /** @phan-suppress-next-line PhanParamTooFewInternalUnpack */
+ $this->matrix = array_map((fn(int ...$a):array => array_reverse($a)), ...$this->matrix);
+
+ return $this;
+ }
+
+ /**
+ * Inverts the values of the whole matrix
+ *
+ * ISO/IEC 18004:2015 Section 6.2 - Reflectance reversal
+ */
+ public function invert():self{
+
+ foreach($this->matrix as $y => $row){
+ foreach($row as $x => $val){
+
+ // skip null fields
+ if($val === $this::M_NULL){
+ continue;
+ }
+
+ $this->flip($x, $y);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Clears a space of $width * $height in order to add a logo or text.
+ * If no $height is given, the space will be assumed a square of $width.
+ *
+ * Additionally, the logo space can be positioned within the QR Code using $startX and $startY.
+ * If either of these are null, the logo space will be centered in that direction.
+ * ECC level "H" (30%) is required.
+ *
+ * The coordinates of $startX and $startY do not include the quiet zone:
+ * [0, 0] is always the top left module of the top left finder pattern, negative values go into the quiet zone top and left.
+ *
+ * Please note that adding a logo space minimizes the error correction capacity of the QR Code and
+ * created images may become unreadable, especially when printed with a chance to receive damage.
+ * Please test thoroughly before using this feature in production.
+ *
+ * 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 = null, int $startX = null, int $startY = null):self{
+ $height ??= $width;
+
+ // if width and height happen to be negative or 0 (default value), just return - nothing to do
+ if($width <= 0 || $height <= 0){
+ return $this; // @codeCoverageIgnore
+ }
+
+ // for logos, we operate in ECC H (30%) only
+ if($this->eccLevel->getLevel() !== EccLevel::H){
+ throw new QRCodeDataException('ECC level "H" required to add logo space');
+ }
+
+ // $this->moduleCount includes the quiet zone (if created), we need the QR size here
+ $dimension = $this->version->getDimension();
+
+ // throw if the size exceeds the qrcode size
+ if($width > $dimension || $height > $dimension){
+ throw new QRCodeDataException('logo dimensions exceed matrix size');
+ }
+
+ // we need uneven sizes to center the logo space, adjust if needed
+ if($startX === null && ($width % 2) === 0){
+ $width++;
+ }
+
+ if($startY === null && ($height % 2) === 0){
+ $height++;
+ }
+
+ // throw if the logo space exceeds the maximum error correction capacity
+ if(($width * $height) > (int)($dimension * $dimension * 0.25)){
+ throw new QRCodeDataException('logo space exceeds the maximum error correction capacity');
+ }
+
+ $quietzone = (($this->moduleCount - $dimension) / 2);
+ $end = ($this->moduleCount - $quietzone);
+
+ // determine start coordinates
+ $startX ??= (($dimension - $width) / 2);
+ $startY ??= (($dimension - $height) / 2);
+ $endX = ($quietzone + $startX + $width);
+ $endY = ($quietzone + $startY + $height);
+
+ // clear the space
+ for($y = ($quietzone + $startY); $y < $endY; $y++){
+ for($x = ($quietzone + $startX); $x < $endX; $x++){
+ // out of bounds, skip
+ if($x < $quietzone || $y < $quietzone ||$x >= $end || $y >= $end){
+ continue;
+ }
+
+ $this->set($x, $y, false, $this::M_LOGO);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Maps the interleaved binary $data on the matrix
+ */
+ public function writeCodewords(BitBuffer $bitBuffer):self{
+ $data = (new ReedSolomonEncoder($this->version, $this->eccLevel))->interleaveEcBytes($bitBuffer);
+ $byteCount = count($data);
+ $iByte = 0;
+ $iBit = 7;
+ $direction = true;
+
+ for($i = ($this->moduleCount - 1); $i > 0; $i -= 2){
+
+ // skip vertical alignment pattern
+ if($i === 6){
+ $i--;
+ }
+
+ for($count = 0; $count < $this->moduleCount; $count++){
+ $y = $count;
+
+ if($direction){
+ $y = ($this->moduleCount - 1 - $count);
+ }
+
+ for($col = 0; $col < 2; $col++){
+ $x = ($i - $col);
+
+ // skip functional patterns
+ if($this->matrix[$y][$x] !== $this::M_NULL){
+ continue;
+ }
+
+ $this->matrix[$y][$x] = $this::M_DATA;
+
+ if($iByte < $byteCount && (($data[$iByte] >> $iBit--) & 1) === 1){
+ $this->matrix[$y][$x] |= $this::IS_DARK;
+ }
+
+ if($iBit === -1){
+ $iByte++;
+ $iBit = 7;
+ }
+ }
+ }
+
+ $direction = !$direction; // switch directions
+ }
+
+ return $this;
+ }
+
+ /**
+ * Applies/reverses the mask pattern
+ *
+ * ISO/IEC 18004:2000 Section 8.8.1
+ */
+ public function mask(MaskPattern $maskPattern):self{
+ $this->maskPattern = $maskPattern;
+ $mask = $this->maskPattern->getMask();
+
+ foreach($this->matrix as $y => $row){
+ foreach($row as $x => $val){
+ // skip non-data modules
+ if(($val & $this::M_DATA) === $this::M_DATA && $mask($x, $y)){
+ $this->flip($x, $y);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Data/ReedSolomonEncoder.php b/vendor/chillerlan/php-qrcode/src/Data/ReedSolomonEncoder.php
new file mode 100644
index 0000000..6044437
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Data/ReedSolomonEncoder.php
@@ -0,0 +1,127 @@
+
+ * @copyright 2021 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Data;
+
+use chillerlan\QRCode\Common\{BitBuffer, EccLevel, GenericGFPoly, GF256, Version};
+use function array_fill, array_merge, count, max;
+
+/**
+ * Reed-Solomon encoding - ISO/IEC 18004:2000 Section 8.5 ff
+ *
+ * @see http://www.thonky.com/qr-code-tutorial/error-correction-coding
+ */
+final class ReedSolomonEncoder{
+
+ private Version $version;
+ private EccLevel $eccLevel;
+
+ private array $interleavedData;
+ private int $interleavedDataIndex;
+
+ /**
+ * ReedSolomonDecoder constructor
+ */
+ public function __construct(Version $version, EccLevel $eccLevel){
+ $this->version = $version;
+ $this->eccLevel = $eccLevel;
+ }
+
+ /**
+ * ECC encoding and interleaving
+ *
+ * @throws \chillerlan\QRCode\QRCodeException
+ */
+ public function interleaveEcBytes(BitBuffer $bitBuffer):array{
+ [$numEccCodewords, [[$l1, $b1], [$l2, $b2]]] = $this->version->getRSBlocks($this->eccLevel);
+
+ $rsBlocks = array_fill(0, $l1, [($numEccCodewords + $b1), $b1]);
+
+ if($l2 > 0){
+ $rsBlocks = array_merge($rsBlocks, array_fill(0, $l2, [($numEccCodewords + $b2), $b2]));
+ }
+
+ $bitBufferData = $bitBuffer->getBuffer();
+ $dataBytes = [];
+ $ecBytes = [];
+ $maxDataBytes = 0;
+ $maxEcBytes = 0;
+ $dataByteOffset = 0;
+
+ foreach($rsBlocks as $key => [$rsBlockTotal, $dataByteCount]){
+ $dataBytes[$key] = [];
+
+ for($i = 0; $i < $dataByteCount; $i++){
+ $dataBytes[$key][$i] = ($bitBufferData[($i + $dataByteOffset)] & 0xff);
+ }
+
+ $ecByteCount = ($rsBlockTotal - $dataByteCount);
+ $ecBytes[$key] = $this->encode($dataBytes[$key], $ecByteCount);
+ $maxDataBytes = max($maxDataBytes, $dataByteCount);
+ $maxEcBytes = max($maxEcBytes, $ecByteCount);
+ $dataByteOffset += $dataByteCount;
+ }
+
+ $this->interleavedData = array_fill(0, $this->version->getTotalCodewords(), 0);
+ $this->interleavedDataIndex = 0;
+ $numRsBlocks = ($l1 + $l2);
+
+ $this->interleave($dataBytes, $maxDataBytes, $numRsBlocks);
+ $this->interleave($ecBytes, $maxEcBytes, $numRsBlocks);
+
+ return $this->interleavedData;
+ }
+
+ /**
+ *
+ */
+ private function encode(array $dataBytes, int $ecByteCount):array{
+ $rsPoly = new GenericGFPoly([1]);
+
+ for($i = 0; $i < $ecByteCount; $i++){
+ $rsPoly = $rsPoly->multiply(new GenericGFPoly([1, GF256::exp($i)]));
+ }
+
+ $rsPolyDegree = $rsPoly->getDegree();
+
+ $modCoefficients = (new GenericGFPoly($dataBytes, $rsPolyDegree))
+ ->mod($rsPoly)
+ ->getCoefficients()
+ ;
+
+ $ecBytes = array_fill(0, $rsPolyDegree, 0);
+ $count = (count($modCoefficients) - $rsPolyDegree);
+
+ foreach($ecBytes as $i => &$val){
+ $modIndex = ($i + $count);
+ $val = 0;
+
+ if($modIndex >= 0){
+ $val = $modCoefficients[$modIndex];
+ }
+ }
+
+ return $ecBytes;
+ }
+
+ /**
+ *
+ */
+ private function interleave(array $byteArray, int $maxBytes, int $numRsBlocks):void{
+ for($x = 0; $x < $maxBytes; $x++){
+ for($y = 0; $y < $numRsBlocks; $y++){
+ if($x < count($byteArray[$y])){
+ $this->interleavedData[$this->interleavedDataIndex++] = $byteArray[$y][$x];
+ }
+ }
+ }
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Decoder/Binarizer.php b/vendor/chillerlan/php-qrcode/src/Decoder/Binarizer.php
new file mode 100644
index 0000000..7b7b49f
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Decoder/Binarizer.php
@@ -0,0 +1,361 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Decoder;
+
+use chillerlan\QRCode\Common\LuminanceSourceInterface;
+use chillerlan\QRCode\Data\QRMatrix;
+use function array_fill, count, intdiv, max;
+
+/**
+ * This class implements a local thresholding algorithm, which while slower than the
+ * GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
+ * high frequency images of barcodes with black data on white backgrounds. For this application,
+ * it does a much better job than a global blackpoint with severe shadows and gradients.
+ * However, it tends to produce artifacts on lower frequency images and is therefore not
+ * a good general purpose binarizer for uses outside ZXing.
+ *
+ * This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,
+ * and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already
+ * inherently local, and only fails for horizontal gradients. We can revisit that problem later,
+ * but for now it was not a win to use local blocks for 1D.
+ *
+ * This Binarizer is the default for the unit tests and the recommended class for library users.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+final class Binarizer{
+
+ // This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
+ // So this is the smallest dimension in each axis we can accept.
+ private const BLOCK_SIZE_POWER = 3;
+ private const BLOCK_SIZE = 8; // ...0100...00
+ private const BLOCK_SIZE_MASK = 7; // ...0011...11
+ private const MINIMUM_DIMENSION = 40;
+ private const MIN_DYNAMIC_RANGE = 24;
+
+# private const LUMINANCE_BITS = 5;
+ private const LUMINANCE_SHIFT = 3;
+ private const LUMINANCE_BUCKETS = 32;
+
+ private LuminanceSourceInterface $source;
+ private array $luminances;
+
+ /**
+ *
+ */
+ public function __construct(LuminanceSourceInterface $source){
+ $this->source = $source;
+ $this->luminances = $this->source->getLuminances();
+ }
+
+ /**
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ private function estimateBlackPoint(array $buckets):int{
+ // Find the tallest peak in the histogram.
+ $numBuckets = count($buckets);
+ $maxBucketCount = 0;
+ $firstPeak = 0;
+ $firstPeakSize = 0;
+
+ for($x = 0; $x < $numBuckets; $x++){
+
+ if($buckets[$x] > $firstPeakSize){
+ $firstPeak = $x;
+ $firstPeakSize = $buckets[$x];
+ }
+
+ if($buckets[$x] > $maxBucketCount){
+ $maxBucketCount = $buckets[$x];
+ }
+ }
+
+ // Find the second-tallest peak which is somewhat far from the tallest peak.
+ $secondPeak = 0;
+ $secondPeakScore = 0;
+
+ for($x = 0; $x < $numBuckets; $x++){
+ $distanceToBiggest = ($x - $firstPeak);
+ // Encourage more distant second peaks by multiplying by square of distance.
+ $score = ($buckets[$x] * $distanceToBiggest * $distanceToBiggest);
+
+ if($score > $secondPeakScore){
+ $secondPeak = $x;
+ $secondPeakScore = $score;
+ }
+ }
+
+ // Make sure firstPeak corresponds to the black peak.
+ if($firstPeak > $secondPeak){
+ $temp = $firstPeak;
+ $firstPeak = $secondPeak;
+ $secondPeak = $temp;
+ }
+
+ // If there is too little contrast in the image to pick a meaningful black point, throw rather
+ // than waste time trying to decode the image, and risk false positives.
+ if(($secondPeak - $firstPeak) <= ($numBuckets / 16)){
+ throw new QRCodeDecoderException('no meaningful dark point found'); // @codeCoverageIgnore
+ }
+
+ // Find a valley between them that is low and closer to the white peak.
+ $bestValley = ($secondPeak - 1);
+ $bestValleyScore = -1;
+
+ for($x = ($secondPeak - 1); $x > $firstPeak; $x--){
+ $fromFirst = ($x - $firstPeak);
+ $score = ($fromFirst * $fromFirst * ($secondPeak - $x) * ($maxBucketCount - $buckets[$x]));
+
+ if($score > $bestValleyScore){
+ $bestValley = $x;
+ $bestValleyScore = $score;
+ }
+ }
+
+ return ($bestValley << self::LUMINANCE_SHIFT);
+ }
+
+ /**
+ * Calculates the final BitMatrix once for all requests. This could be called once from the
+ * constructor instead, but there are some advantages to doing it lazily, such as making
+ * profiling easier, and not doing heavy lifting when callers don't expect it.
+ *
+ * Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
+ * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
+ * may not apply sharpening. Therefore, a row from this matrix may not be identical to one
+ * fetched using getBlackRow(), so don't mix and match between them.
+ *
+ * @return \chillerlan\QRCode\Decoder\BitMatrix The 2D array of bits for the image (true means black).
+ */
+ public function getBlackMatrix():BitMatrix{
+ $width = $this->source->getWidth();
+ $height = $this->source->getHeight();
+
+ if($width >= self::MINIMUM_DIMENSION && $height >= self::MINIMUM_DIMENSION){
+ $subWidth = ($width >> self::BLOCK_SIZE_POWER);
+
+ if(($width & self::BLOCK_SIZE_MASK) !== 0){
+ $subWidth++;
+ }
+
+ $subHeight = ($height >> self::BLOCK_SIZE_POWER);
+
+ if(($height & self::BLOCK_SIZE_MASK) !== 0){
+ $subHeight++;
+ }
+
+ return $this->calculateThresholdForBlock($subWidth, $subHeight, $width, $height);
+ }
+
+ // If the image is too small, fall back to the global histogram approach.
+ return $this->getHistogramBlackMatrix($width, $height);
+ }
+
+ /**
+ *
+ */
+ private function getHistogramBlackMatrix(int $width, int $height):BitMatrix{
+
+ // Quickly calculates the histogram by sampling four rows from the image. This proved to be
+ // more robust on the blackbox tests than sampling a diagonal as we used to do.
+ $buckets = array_fill(0, self::LUMINANCE_BUCKETS, 0);
+ $right = intdiv(($width * 4), 5);
+ $x = intdiv($width, 5);
+
+ for($y = 1; $y < 5; $y++){
+ $row = intdiv(($height * $y), 5);
+ $localLuminances = $this->source->getRow($row);
+
+ for(; $x < $right; $x++){
+ $pixel = ($localLuminances[$x] & 0xff);
+ $buckets[($pixel >> self::LUMINANCE_SHIFT)]++;
+ }
+ }
+
+ $blackPoint = $this->estimateBlackPoint($buckets);
+
+ // We delay reading the entire image luminance until the black point estimation succeeds.
+ // Although we end up reading four rows twice, it is consistent with our motto of
+ // "fail quickly" which is necessary for continuous scanning.
+ $matrix = new BitMatrix(max($width, $height));
+
+ for($y = 0; $y < $height; $y++){
+ $offset = ($y * $width);
+
+ for($x = 0; $x < $width; $x++){
+ $matrix->set($x, $y, (($this->luminances[($offset + $x)] & 0xff) < $blackPoint), QRMatrix::M_DATA);
+ }
+ }
+
+ return $matrix;
+ }
+
+ /**
+ * Calculates a single black point for each block of pixels and saves it away.
+ * See the following thread for a discussion of this algorithm:
+ *
+ * @see http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
+ */
+ private function calculateBlackPoints(int $subWidth, int $subHeight, int $width, int $height):array{
+ $blackPoints = array_fill(0, $subHeight, array_fill(0, $subWidth, 0));
+
+ for($y = 0; $y < $subHeight; $y++){
+ $yoffset = ($y << self::BLOCK_SIZE_POWER);
+ $maxYOffset = ($height - self::BLOCK_SIZE);
+
+ if($yoffset > $maxYOffset){
+ $yoffset = $maxYOffset;
+ }
+
+ for($x = 0; $x < $subWidth; $x++){
+ $xoffset = ($x << self::BLOCK_SIZE_POWER);
+ $maxXOffset = ($width - self::BLOCK_SIZE);
+
+ if($xoffset > $maxXOffset){
+ $xoffset = $maxXOffset;
+ }
+
+ $sum = 0;
+ $min = 255;
+ $max = 0;
+
+ for($yy = 0, $offset = ($yoffset * $width + $xoffset); $yy < self::BLOCK_SIZE; $yy++, $offset += $width){
+
+ for($xx = 0; $xx < self::BLOCK_SIZE; $xx++){
+ $pixel = ((int)($this->luminances[(int)($offset + $xx)]) & 0xff);
+ $sum += $pixel;
+ // still looking for good contrast
+ if($pixel < $min){
+ $min = $pixel;
+ }
+
+ if($pixel > $max){
+ $max = $pixel;
+ }
+ }
+
+ // short-circuit min/max tests once dynamic range is met
+ if(($max - $min) > self::MIN_DYNAMIC_RANGE){
+ // finish the rest of the rows quickly
+ for($yy++, $offset += $width; $yy < self::BLOCK_SIZE; $yy++, $offset += $width){
+ for($xx = 0; $xx < self::BLOCK_SIZE; $xx++){
+ $sum += ((int)($this->luminances[(int)($offset + $xx)]) & 0xff);
+ }
+ }
+ }
+ }
+
+ // The default estimate is the average of the values in the block.
+ $average = ($sum >> (self::BLOCK_SIZE_POWER * 2));
+
+ if(($max - $min) <= self::MIN_DYNAMIC_RANGE){
+ // If variation within the block is low, assume this is a block with only light or only
+ // dark pixels. In that case we do not want to use the average, as it would divide this
+ // low contrast area into black and white pixels, essentially creating data out of noise.
+ //
+ // The default assumption is that the block is light/background. Since no estimate for
+ // the level of dark pixels exists locally, use half the min for the block.
+ $average = ($min / 2);
+
+ if($y > 0 && $x > 0){
+ // Correct the "white background" assumption for blocks that have neighbors by comparing
+ // the pixels in this block to the previously calculated black points. This is based on
+ // the fact that dark barcode symbology is always surrounded by some amount of light
+ // background for which reasonable black point estimates were made. The bp estimated at
+ // the boundaries is used for the interior.
+
+ // The (min < bp) is arbitrary but works better than other heuristics that were tried.
+ $averageNeighborBlackPoint = (
+ ($blackPoints[($y - 1)][$x] + (2 * $blackPoints[$y][($x - 1)]) + $blackPoints[($y - 1)][($x - 1)]) / 4
+ );
+
+ if($min < $averageNeighborBlackPoint){
+ $average = $averageNeighborBlackPoint;
+ }
+ }
+ }
+
+ $blackPoints[$y][$x] = $average;
+ }
+ }
+
+ return $blackPoints;
+ }
+
+ /**
+ * For each block in the image, calculate the average black point using a 5x5 grid
+ * of the surrounding blocks. Also handles the corner cases (fractional blocks are computed based
+ * on the last pixels in the row/column which are also used in the previous block).
+ */
+ private function calculateThresholdForBlock(int $subWidth, int $subHeight, int $width, int $height):BitMatrix{
+ $matrix = new BitMatrix(max($width, $height));
+ $blackPoints = $this->calculateBlackPoints($subWidth, $subHeight, $width, $height);
+
+ for($y = 0; $y < $subHeight; $y++){
+ $yoffset = ($y << self::BLOCK_SIZE_POWER);
+ $maxYOffset = ($height - self::BLOCK_SIZE);
+
+ if($yoffset > $maxYOffset){
+ $yoffset = $maxYOffset;
+ }
+
+ for($x = 0; $x < $subWidth; $x++){
+ $xoffset = ($x << self::BLOCK_SIZE_POWER);
+ $maxXOffset = ($width - self::BLOCK_SIZE);
+
+ if($xoffset > $maxXOffset){
+ $xoffset = $maxXOffset;
+ }
+
+ $left = $this->cap($x, 2, ($subWidth - 3));
+ $top = $this->cap($y, 2, ($subHeight - 3));
+ $sum = 0;
+
+ for($z = -2; $z <= 2; $z++){
+ $br = $blackPoints[($top + $z)];
+ $sum += ($br[($left - 2)] + $br[($left - 1)] + $br[$left] + $br[($left + 1)] + $br[($left + 2)]);
+ }
+
+ $average = (int)($sum / 25);
+
+ // Applies a single threshold to a block of pixels.
+ for($j = 0, $o = ($yoffset * $width + $xoffset); $j < self::BLOCK_SIZE; $j++, $o += $width){
+ for($i = 0; $i < self::BLOCK_SIZE; $i++){
+ // Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
+ $v = (((int)($this->luminances[($o + $i)]) & 0xff) <= $average);
+
+ $matrix->set(($xoffset + $i), ($yoffset + $j), $v, QRMatrix::M_DATA);
+ }
+ }
+ }
+ }
+
+ return $matrix;
+ }
+
+ /**
+ * @noinspection PhpSameParameterValueInspection
+ */
+ private function cap(int $value, int $min, int $max):int{
+
+ if($value < $min){
+ return $min;
+ }
+
+ if($value > $max){
+ return $max;
+ }
+
+ return $value;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Decoder/BitMatrix.php b/vendor/chillerlan/php-qrcode/src/Decoder/BitMatrix.php
new file mode 100644
index 0000000..ea2e321
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Decoder/BitMatrix.php
@@ -0,0 +1,430 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Decoder;
+
+use chillerlan\QRCode\Common\{EccLevel, MaskPattern, Version};
+use chillerlan\QRCode\Data\{QRCodeDataException, QRMatrix};
+use function array_fill, array_reverse, count;
+use const PHP_INT_MAX, PHP_INT_SIZE;
+
+/**
+ * Extended QRMatrix to map read data from the Binarizer
+ */
+final class BitMatrix extends QRMatrix{
+
+ /**
+ * See ISO 18004:2006, Annex C, Table C.1
+ *
+ * [data bits, sequence after masking]
+ */
+ private const DECODE_LOOKUP = [
+ 0x5412, // 0101010000010010
+ 0x5125, // 0101000100100101
+ 0x5E7C, // 0101111001111100
+ 0x5B4B, // 0101101101001011
+ 0x45F9, // 0100010111111001
+ 0x40CE, // 0100000011001110
+ 0x4F97, // 0100111110010111
+ 0x4AA0, // 0100101010100000
+ 0x77C4, // 0111011111000100
+ 0x72F3, // 0111001011110011
+ 0x7DAA, // 0111110110101010
+ 0x789D, // 0111100010011101
+ 0x662F, // 0110011000101111
+ 0x6318, // 0110001100011000
+ 0x6C41, // 0110110001000001
+ 0x6976, // 0110100101110110
+ 0x1689, // 0001011010001001
+ 0x13BE, // 0001001110111110
+ 0x1CE7, // 0001110011100111
+ 0x19D0, // 0001100111010000
+ 0x0762, // 0000011101100010
+ 0x0255, // 0000001001010101
+ 0x0D0C, // 0000110100001100
+ 0x083B, // 0000100000111011
+ 0x355F, // 0011010101011111
+ 0x3068, // 0011000001101000
+ 0x3F31, // 0011111100110001
+ 0x3A06, // 0011101000000110
+ 0x24B4, // 0010010010110100
+ 0x2183, // 0010000110000011
+ 0x2EDA, // 0010111011011010
+ 0x2BED, // 0010101111101101
+ ];
+
+ private const FORMAT_INFO_MASK_QR = 0x5412; // 0101010000010010
+
+ /**
+ * This flag has effect only on the copyVersionBit() method.
+ * Before proceeding with readCodewords() the resetInfo() method should be called.
+ */
+ private bool $mirror = false;
+
+ /**
+ * @noinspection PhpMissingParentConstructorInspection
+ */
+ public function __construct(int $dimension){
+ $this->moduleCount = $dimension;
+ $this->matrix = array_fill(0, $this->moduleCount, array_fill(0, $this->moduleCount, $this::M_NULL));
+ }
+
+ /**
+ * Resets the current version info in order to attempt another reading
+ */
+ public function resetVersionInfo():self{
+ $this->version = null;
+ $this->eccLevel = null;
+ $this->maskPattern = null;
+
+ return $this;
+ }
+
+ /**
+ * Mirror the bit matrix diagonally in order to attempt a second reading.
+ */
+ public function mirrorDiagonal():self{
+ $this->mirror = !$this->mirror;
+
+ // mirror vertically
+ $this->matrix = array_reverse($this->matrix);
+ // rotate by 90 degrees clockwise
+ /** @phan-suppress-next-line PhanTypeMismatchReturnSuperType */
+ return $this->rotate90();
+ }
+
+ /**
+ * Reads the bits in the BitMatrix representing the finder pattern in the
+ * correct order in order to reconstruct the codewords bytes contained within the
+ * QR Code. Throws if the exact number of bytes expected is not read.
+ *
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ public function readCodewords():array{
+
+ $this
+ ->readFormatInformation()
+ ->readVersion()
+ ->mask($this->maskPattern) // reverse the mask pattern
+ ;
+
+ // invoke a fresh matrix with only the function & format patterns to compare against
+ $matrix = (new QRMatrix($this->version, $this->eccLevel))
+ ->initFunctionalPatterns()
+ ->setFormatInfo($this->maskPattern)
+ ;
+
+ $result = [];
+ $byte = 0;
+ $bitsRead = 0;
+ $direction = true;
+
+ // Read columns in pairs, from right to left
+ for($i = ($this->moduleCount - 1); $i > 0; $i -= 2){
+
+ // Skip whole column with vertical alignment pattern;
+ // saves time and makes the other code proceed more cleanly
+ if($i === 6){
+ $i--;
+ }
+ // Read alternatingly from bottom to top then top to bottom
+ for($count = 0; $count < $this->moduleCount; $count++){
+ $y = ($direction) ? ($this->moduleCount - 1 - $count) : $count;
+
+ for($col = 0; $col < 2; $col++){
+ $x = ($i - $col);
+
+ // Ignore bits covered by the function pattern
+ if($matrix->get($x, $y) !== $this::M_NULL){
+ continue;
+ }
+
+ $bitsRead++;
+ $byte <<= 1;
+
+ if($this->check($x, $y)){
+ $byte |= 1;
+ }
+ // If we've made a whole byte, save it off
+ if($bitsRead === 8){
+ $result[] = $byte;
+ $bitsRead = 0;
+ $byte = 0;
+ }
+ }
+ }
+
+ $direction = !$direction; // switch directions
+ }
+
+ if(count($result) !== $this->version->getTotalCodewords()){
+ throw new QRCodeDecoderException('result count differs from total codewords for version');
+ }
+
+ // bytes encoded within the QR Code
+ return $result;
+ }
+
+ /**
+ * Reads format information from one of its two locations within the QR Code.
+ * Throws if both format information locations cannot be parsed as the valid encoding of format information.
+ *
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ private function readFormatInformation():self{
+
+ if($this->eccLevel !== null && $this->maskPattern !== null){
+ return $this;
+ }
+
+ // Read top-left format info bits
+ $formatInfoBits1 = 0;
+
+ for($i = 0; $i < 6; $i++){
+ $formatInfoBits1 = $this->copyVersionBit($i, 8, $formatInfoBits1);
+ }
+
+ // ... and skip a bit in the timing pattern ...
+ $formatInfoBits1 = $this->copyVersionBit(7, 8, $formatInfoBits1);
+ $formatInfoBits1 = $this->copyVersionBit(8, 8, $formatInfoBits1);
+ $formatInfoBits1 = $this->copyVersionBit(8, 7, $formatInfoBits1);
+ // ... and skip a bit in the timing pattern ...
+ for($j = 5; $j >= 0; $j--){
+ $formatInfoBits1 = $this->copyVersionBit(8, $j, $formatInfoBits1);
+ }
+
+ // Read the top-right/bottom-left pattern too
+ $formatInfoBits2 = 0;
+ $jMin = ($this->moduleCount - 7);
+
+ for($j = ($this->moduleCount - 1); $j >= $jMin; $j--){
+ $formatInfoBits2 = $this->copyVersionBit(8, $j, $formatInfoBits2);
+ }
+
+ for($i = ($this->moduleCount - 8); $i < $this->moduleCount; $i++){
+ $formatInfoBits2 = $this->copyVersionBit($i, 8, $formatInfoBits2);
+ }
+
+ $formatInfo = $this->doDecodeFormatInformation($formatInfoBits1, $formatInfoBits2);
+
+ if($formatInfo === null){
+
+ // Should return null, but, some QR codes apparently do not mask this info.
+ // Try again by actually masking the pattern first.
+ $formatInfo = $this->doDecodeFormatInformation(
+ ($formatInfoBits1 ^ $this::FORMAT_INFO_MASK_QR),
+ ($formatInfoBits2 ^ $this::FORMAT_INFO_MASK_QR)
+ );
+
+ // still nothing???
+ if($formatInfo === null){
+ throw new QRCodeDecoderException('failed to read format info'); // @codeCoverageIgnore
+ }
+
+ }
+
+ $this->eccLevel = new EccLevel(($formatInfo >> 3) & 0x03); // Bits 3,4
+ $this->maskPattern = new MaskPattern($formatInfo & 0x07); // Bottom 3 bits
+
+ return $this;
+ }
+
+ /**
+ *
+ */
+ private function copyVersionBit(int $i, int $j, int $versionBits):int{
+
+ $bit = $this->mirror
+ ? $this->check($j, $i)
+ : $this->check($i, $j);
+
+ return ($bit) ? (($versionBits << 1) | 0x1) : ($versionBits << 1);
+ }
+
+ /**
+ * Returns information about the format it specifies, or null if it doesn't seem to match any known pattern
+ */
+ private function doDecodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2):?int{
+ $bestDifference = PHP_INT_MAX;
+ $bestFormatInfo = 0;
+
+ // Find the int in FORMAT_INFO_DECODE_LOOKUP with the fewest bits differing
+ foreach($this::DECODE_LOOKUP as $maskedBits => $dataBits){
+
+ if($maskedFormatInfo1 === $dataBits || $maskedFormatInfo2 === $dataBits){
+ // Found an exact match
+ return $maskedBits;
+ }
+
+ $bitsDifference = $this->numBitsDiffering($maskedFormatInfo1, $dataBits);
+
+ if($bitsDifference < $bestDifference){
+ $bestFormatInfo = $maskedBits;
+ $bestDifference = $bitsDifference;
+ }
+
+ if($maskedFormatInfo1 !== $maskedFormatInfo2){
+ // also try the other option
+ $bitsDifference = $this->numBitsDiffering($maskedFormatInfo2, $dataBits);
+
+ if($bitsDifference < $bestDifference){
+ $bestFormatInfo = $maskedBits;
+ $bestDifference = $bitsDifference;
+ }
+ }
+ }
+ // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match
+ if($bestDifference <= 3){
+ return $bestFormatInfo;
+ }
+
+ return null;
+ }
+
+ /**
+ * Reads version information from one of its two locations within the QR Code.
+ * Throws if both version information locations cannot be parsed as the valid encoding of version information.
+ *
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ * @noinspection DuplicatedCode
+ */
+ private function readVersion():self{
+
+ if($this->version !== null){
+ return $this;
+ }
+
+ $provisionalVersion = (($this->moduleCount - 17) / 4);
+
+ // no version info if v < 7
+ if($provisionalVersion < 7){
+ $this->version = new Version($provisionalVersion);
+
+ return $this;
+ }
+
+ // Read top-right version info: 3 wide by 6 tall
+ $versionBits = 0;
+ $ijMin = ($this->moduleCount - 11);
+
+ for($y = 5; $y >= 0; $y--){
+ for($x = ($this->moduleCount - 9); $x >= $ijMin; $x--){
+ $versionBits = $this->copyVersionBit($x, $y, $versionBits);
+ }
+ }
+
+ $this->version = $this->decodeVersionInformation($versionBits);
+
+ if($this->version !== null && $this->version->getDimension() === $this->moduleCount){
+ return $this;
+ }
+
+ // Hmm, failed. Try bottom left: 6 wide by 3 tall
+ $versionBits = 0;
+
+ for($x = 5; $x >= 0; $x--){
+ for($y = ($this->moduleCount - 9); $y >= $ijMin; $y--){
+ $versionBits = $this->copyVersionBit($x, $y, $versionBits);
+ }
+ }
+
+ $this->version = $this->decodeVersionInformation($versionBits);
+
+ if($this->version !== null && $this->version->getDimension() === $this->moduleCount){
+ return $this;
+ }
+
+ throw new QRCodeDecoderException('failed to read version');
+ }
+
+ /**
+ * Decodes the version information from the given bit sequence, returns null if no valid match is found.
+ */
+ private function decodeVersionInformation(int $versionBits):?Version{
+ $bestDifference = PHP_INT_MAX;
+ $bestVersion = 0;
+
+ for($i = 7; $i <= 40; $i++){
+ $targetVersion = new Version($i);
+ $targetVersionPattern = $targetVersion->getVersionPattern();
+
+ // Do the version info bits match exactly? done.
+ if($targetVersionPattern === $versionBits){
+ return $targetVersion;
+ }
+
+ // Otherwise see if this is the closest to a real version info bit string
+ // we have seen so far
+ /** @phan-suppress-next-line PhanTypeMismatchArgumentNullable ($targetVersionPattern is never null here) */
+ $bitsDifference = $this->numBitsDiffering($versionBits, $targetVersionPattern);
+
+ if($bitsDifference < $bestDifference){
+ $bestVersion = $i;
+ $bestDifference = $bitsDifference;
+ }
+ }
+ // We can tolerate up to 3 bits of error since no two version info codewords will
+ // differ in less than 8 bits.
+ if($bestDifference <= 3){
+ return new Version($bestVersion);
+ }
+
+ // If we didn't find a close enough match, fail
+ return null;
+ }
+
+ /**
+ *
+ */
+ private function uRShift(int $a, int $b):int{
+
+ if($b === 0){
+ return $a;
+ }
+
+ return (($a >> $b) & ~((1 << (8 * PHP_INT_SIZE - 1)) >> ($b - 1)));
+ }
+
+ /**
+ *
+ */
+ private function numBitsDiffering(int $a, int $b):int{
+ // a now has a 1 bit exactly where its bit differs with b's
+ $a ^= $b;
+ // Offset $i holds the number of 1-bits in the binary representation of $i
+ $BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
+ // Count bits set quickly with a series of lookups:
+ $count = 0;
+
+ for($i = 0; $i < 32; $i += 4){
+ $count += $BITS_SET_IN_HALF_BYTE[($this->uRShift($a, $i) & 0x0F)];
+ }
+
+ return $count;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function setQuietZone(int $quietZoneSize = null):self{
+ throw new QRCodeDataException('not supported');
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
+ */
+ public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):self{
+ throw new QRCodeDataException('not supported');
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Decoder/Decoder.php b/vendor/chillerlan/php-qrcode/src/Decoder/Decoder.php
new file mode 100644
index 0000000..6f369a6
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Decoder/Decoder.php
@@ -0,0 +1,173 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Decoder;
+
+use chillerlan\QRCode\Common\{BitBuffer, EccLevel, LuminanceSourceInterface, MaskPattern, Mode, Version};
+use chillerlan\QRCode\Data\{AlphaNum, Byte, ECI, Hanzi, Kanji, Number};
+use chillerlan\QRCode\Detector\Detector;
+use Throwable;
+use function chr, str_replace;
+
+/**
+ * The main class which implements QR Code decoding -- as opposed to locating and extracting
+ * the QR Code from an image.
+ *
+ * @author Sean Owen
+ */
+final class Decoder{
+
+ private ?Version $version = null;
+ private ?EccLevel $eccLevel = null;
+ private ?MaskPattern $maskPattern = null;
+ private BitBuffer $bitBuffer;
+
+ /**
+ * Decodes a QR Code represented as a BitMatrix.
+ * A 1 or "true" is taken to mean a black module.
+ *
+ * @throws \Throwable|\chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ public function decode(LuminanceSourceInterface $source):DecoderResult{
+ $matrix = (new Detector($source))->detect();
+
+ try{
+ // clone the BitMatrix to avoid errors in case we run into mirroring
+ return $this->decodeMatrix(clone $matrix);
+ }
+ catch(Throwable $e){
+
+ try{
+ /*
+ * Prepare for a mirrored reading.
+ *
+ * Since we're here, this means we have successfully detected some kind
+ * of version and format information when mirrored. This is a good sign,
+ * that the QR code may be mirrored, and we should try once more with a
+ * mirrored content.
+ */
+ return $this->decodeMatrix($matrix->resetVersionInfo()->mirrorDiagonal());
+ }
+ catch(Throwable $f){
+ // Throw the exception from the original reading
+ throw $e;
+ }
+
+ }
+
+ }
+
+ /**
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ private function decodeMatrix(BitMatrix $matrix):DecoderResult{
+ // Read raw codewords
+ $rawCodewords = $matrix->readCodewords();
+ $this->version = $matrix->getVersion();
+ $this->eccLevel = $matrix->getEccLevel();
+ $this->maskPattern = $matrix->getMaskPattern();
+
+ if($this->version === null || $this->eccLevel === null || $this->maskPattern === null){
+ throw new QRCodeDecoderException('unable to read version or format info'); // @codeCoverageIgnore
+ }
+
+ $resultBytes = (new ReedSolomonDecoder($this->version, $this->eccLevel))->decode($rawCodewords);
+
+ return $this->decodeBitStream($resultBytes);
+ }
+
+ /**
+ * Decode the contents of that stream of bytes
+ *
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ private function decodeBitStream(BitBuffer $bitBuffer):DecoderResult{
+ $this->bitBuffer = $bitBuffer;
+ $versionNumber = $this->version->getVersionNumber();
+ $symbolSequence = -1;
+ $parityData = -1;
+ $fc1InEffect = false;
+ $result = '';
+
+ // While still another segment to read...
+ while($this->bitBuffer->available() >= 4){
+ $datamode = $this->bitBuffer->read(4); // mode is encoded by 4 bits
+
+ // OK, assume we're done
+ if($datamode === Mode::TERMINATOR){
+ break;
+ }
+ elseif($datamode === Mode::NUMBER){
+ $result .= Number::decodeSegment($this->bitBuffer, $versionNumber);
+ }
+ elseif($datamode === Mode::ALPHANUM){
+ $result .= $this->decodeAlphanumSegment($versionNumber, $fc1InEffect);
+ }
+ elseif($datamode === Mode::BYTE){
+ $result .= Byte::decodeSegment($this->bitBuffer, $versionNumber);
+ }
+ elseif($datamode === Mode::KANJI){
+ $result .= Kanji::decodeSegment($this->bitBuffer, $versionNumber);
+ }
+ elseif($datamode === Mode::STRCTURED_APPEND){
+
+ if($this->bitBuffer->available() < 16){
+ throw new QRCodeDecoderException('structured append: not enough bits left');
+ }
+ // sequence number and parity is added later to the result metadata
+ // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
+ $symbolSequence = $this->bitBuffer->read(8);
+ $parityData = $this->bitBuffer->read(8);
+ }
+ elseif($datamode === Mode::FNC1_FIRST || $datamode === Mode::FNC1_SECOND){
+ // We do little with FNC1 except alter the parsed result a bit according to the spec
+ $fc1InEffect = true;
+ }
+ elseif($datamode === Mode::ECI){
+ $result .= ECI::decodeSegment($this->bitBuffer, $versionNumber);
+ }
+ elseif($datamode === Mode::HANZI){
+ $result .= Hanzi::decodeSegment($this->bitBuffer, $versionNumber);
+ }
+ else{
+ throw new QRCodeDecoderException('invalid data mode');
+ }
+
+ }
+
+ return new DecoderResult([
+ 'rawBytes' => $this->bitBuffer,
+ 'data' => $result,
+ 'version' => $this->version,
+ 'eccLevel' => $this->eccLevel,
+ 'maskPattern' => $this->maskPattern,
+ 'structuredAppendParity' => $parityData,
+ 'structuredAppendSequence' => $symbolSequence,
+ ]);
+ }
+
+ /**
+ *
+ */
+ private function decodeAlphanumSegment(int $versionNumber, bool $fc1InEffect):string{
+ $str = AlphaNum::decodeSegment($this->bitBuffer, $versionNumber);
+
+ // See section 6.4.8.1, 6.4.8.2
+ if($fc1InEffect){ // ???
+ // We need to massage the result a bit if in an FNC1 mode:
+ $str = str_replace(chr(0x1d), '%', $str);
+ $str = str_replace('%%', '%', $str);
+ }
+
+ return $str;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Decoder/DecoderResult.php b/vendor/chillerlan/php-qrcode/src/Decoder/DecoderResult.php
new file mode 100644
index 0000000..02b4d79
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Decoder/DecoderResult.php
@@ -0,0 +1,99 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Decoder;
+
+use chillerlan\QRCode\Common\{BitBuffer, EccLevel, MaskPattern, Version};
+use chillerlan\QRCode\Data\QRMatrix;
+use function property_exists;
+
+/**
+ * Encapsulates the result of decoding a matrix of bits. This typically
+ * applies to 2D barcode formats. For now, it contains the raw bytes obtained
+ * as well as a String interpretation of those bytes, if applicable.
+ *
+ * @property \chillerlan\QRCode\Common\BitBuffer $rawBytes
+ * @property string $data
+ * @property \chillerlan\QRCode\Common\Version $version
+ * @property \chillerlan\QRCode\Common\EccLevel $eccLevel
+ * @property \chillerlan\QRCode\Common\MaskPattern $maskPattern
+ * @property int $structuredAppendParity
+ * @property int $structuredAppendSequence
+ */
+final class DecoderResult{
+
+ private BitBuffer $rawBytes;
+ private Version $version;
+ private EccLevel $eccLevel;
+ private MaskPattern $maskPattern;
+ private string $data = '';
+ private int $structuredAppendParity = -1;
+ private int $structuredAppendSequence = -1;
+
+ /**
+ * DecoderResult constructor.
+ */
+ public function __construct(iterable $properties = null){
+
+ if(!empty($properties)){
+
+ foreach($properties as $property => $value){
+
+ if(!property_exists($this, $property)){
+ continue;
+ }
+
+ $this->{$property} = $value;
+ }
+
+ }
+
+ }
+
+ /**
+ * @return mixed|null
+ */
+ public function __get(string $property){
+
+ if(property_exists($this, $property)){
+ return $this->{$property};
+ }
+
+ return null;
+ }
+
+ /**
+ *
+ */
+ public function __toString():string{
+ return $this->data;
+ }
+
+ /**
+ *
+ */
+ public function hasStructuredAppend():bool{
+ return $this->structuredAppendParity >= 0 && $this->structuredAppendSequence >= 0;
+ }
+
+ /**
+ * Returns a QRMatrix instance with the settings and data of the reader result
+ */
+ public function getQRMatrix():QRMatrix{
+ return (new QRMatrix($this->version, $this->eccLevel))
+ ->initFunctionalPatterns()
+ ->writeCodewords($this->rawBytes)
+ ->setFormatInfo($this->maskPattern)
+ ->mask($this->maskPattern)
+ ;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Decoder/QRCodeDecoderException.php b/vendor/chillerlan/php-qrcode/src/Decoder/QRCodeDecoderException.php
new file mode 100644
index 0000000..11157af
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Decoder/QRCodeDecoderException.php
@@ -0,0 +1,20 @@
+
+ * @copyright 2021 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Decoder;
+
+use chillerlan\QRCode\QRCodeException;
+
+/**
+ * An exception container
+ */
+final class QRCodeDecoderException extends QRCodeException{
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Decoder/ReedSolomonDecoder.php b/vendor/chillerlan/php-qrcode/src/Decoder/ReedSolomonDecoder.php
new file mode 100644
index 0000000..2bd539a
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Decoder/ReedSolomonDecoder.php
@@ -0,0 +1,313 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Decoder;
+
+use chillerlan\QRCode\Common\{BitBuffer, EccLevel, GenericGFPoly, GF256, Version};
+use function array_fill, array_reverse, count;
+
+/**
+ * Implements Reed-Solomon decoding
+ *
+ * The algorithm will not be explained here, but the following references were helpful
+ * in creating this implementation:
+ *
+ * - Bruce Maggs "Decoding Reed-Solomon Codes" (see discussion of Forney's Formula)
+ * http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps
+ * - J.I. Hall. "Chapter 5. Generalized Reed-Solomon Codes" (see discussion of Euclidean algorithm)
+ * https://users.math.msu.edu/users/halljo/classes/codenotes/GRS.pdf
+ *
+ * Much credit is due to William Rucklidge since portions of this code are an indirect
+ * port of his C++ Reed-Solomon implementation.
+ *
+ * @author Sean Owen
+ * @author William Rucklidge
+ * @author sanfordsquires
+ */
+final class ReedSolomonDecoder{
+
+ private Version $version;
+ private EccLevel $eccLevel;
+
+ /**
+ * ReedSolomonDecoder constructor
+ */
+ public function __construct(Version $version, EccLevel $eccLevel){
+ $this->version = $version;
+ $this->eccLevel = $eccLevel;
+ }
+
+ /**
+ * Error-correct and copy data blocks together into a stream of bytes
+ */
+ public function decode(array $rawCodewords):BitBuffer{
+ $dataBlocks = $this->deinterleaveRawBytes($rawCodewords);
+ $dataBytes = [];
+
+ foreach($dataBlocks as [$numDataCodewords, $codewordBytes]){
+ $corrected = $this->correctErrors($codewordBytes, $numDataCodewords);
+
+ for($i = 0; $i < $numDataCodewords; $i++){
+ $dataBytes[] = $corrected[$i];
+ }
+ }
+
+ return new BitBuffer($dataBytes);
+ }
+
+ /**
+ * When QR Codes use multiple data blocks, they are actually interleaved.
+ * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
+ * method will separate the data into original blocks.
+ *
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ private function deinterleaveRawBytes(array $rawCodewords):array{
+ // Figure out the number and size of data blocks used by this version and
+ // error correction level
+ [$numEccCodewords, $eccBlocks] = $this->version->getRSBlocks($this->eccLevel);
+
+ // Now establish DataBlocks of the appropriate size and number of data codewords
+ $result = [];//new DataBlock[$totalBlocks];
+ $numResultBlocks = 0;
+
+ foreach($eccBlocks as [$numEccBlocks, $eccPerBlock]){
+ for($i = 0; $i < $numEccBlocks; $i++, $numResultBlocks++){
+ $result[$numResultBlocks] = [$eccPerBlock, array_fill(0, ($numEccCodewords + $eccPerBlock), 0)];
+ }
+ }
+
+ // All blocks have the same amount of data, except that the last n
+ // (where n may be 0) have 1 more byte. Figure out where these start.
+ /** @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset */
+ $shorterBlocksTotalCodewords = count($result[0][1]);
+ $longerBlocksStartAt = (count($result) - 1);
+
+ while($longerBlocksStartAt >= 0){
+ $numCodewords = count($result[$longerBlocksStartAt][1]);
+
+ if($numCodewords == $shorterBlocksTotalCodewords){
+ break;
+ }
+
+ $longerBlocksStartAt--;
+ }
+
+ $longerBlocksStartAt++;
+
+ $shorterBlocksNumDataCodewords = ($shorterBlocksTotalCodewords - $numEccCodewords);
+ // The last elements of result may be 1 element longer;
+ // first fill out as many elements as all of them have
+ $rawCodewordsOffset = 0;
+
+ for($i = 0; $i < $shorterBlocksNumDataCodewords; $i++){
+ for($j = 0; $j < $numResultBlocks; $j++){
+ $result[$j][1][$i] = $rawCodewords[$rawCodewordsOffset++];
+ }
+ }
+
+ // Fill out the last data block in the longer ones
+ for($j = $longerBlocksStartAt; $j < $numResultBlocks; $j++){
+ $result[$j][1][$shorterBlocksNumDataCodewords] = $rawCodewords[$rawCodewordsOffset++];
+ }
+
+ // Now add in error correction blocks
+ /** @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset */
+ $max = count($result[0][1]);
+
+ for($i = $shorterBlocksNumDataCodewords; $i < $max; $i++){
+ for($j = 0; $j < $numResultBlocks; $j++){
+ $iOffset = ($j < $longerBlocksStartAt) ? $i : ($i + 1);
+ $result[$j][1][$iOffset] = $rawCodewords[$rawCodewordsOffset++];
+ }
+ }
+
+ // DataBlocks containing original bytes, "de-interleaved" from representation in the QR Code
+ return $result;
+ }
+
+ /**
+ * Given data and error-correction codewords received, possibly corrupted by errors, attempts to
+ * correct the errors in-place using Reed-Solomon error correction.
+ */
+ private function correctErrors(array $codewordBytes, int $numDataCodewords):array{
+ // First read into an array of ints
+ $codewordsInts = [];
+
+ foreach($codewordBytes as $codewordByte){
+ $codewordsInts[] = ($codewordByte & 0xFF);
+ }
+
+ $decoded = $this->decodeWords($codewordsInts, (count($codewordBytes) - $numDataCodewords));
+
+ // Copy back into array of bytes -- only need to worry about the bytes that were data
+ // We don't care about errors in the error-correction codewords
+ for($i = 0; $i < $numDataCodewords; $i++){
+ $codewordBytes[$i] = $decoded[$i];
+ }
+
+ return $codewordBytes;
+ }
+
+ /**
+ * Decodes given set of received codewords, which include both data and error-correction
+ * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
+ * in the input.
+ *
+ * @param array $received data and error-correction codewords
+ * @param int $numEccCodewords number of error-correction codewords available
+ *
+ * @return int[]
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException if decoding fails for any reason
+ */
+ private function decodeWords(array $received, int $numEccCodewords):array{
+ $poly = new GenericGFPoly($received);
+ $syndromeCoefficients = [];
+ $error = false;
+
+ for($i = 0; $i < $numEccCodewords; $i++){
+ $syndromeCoefficients[$i] = $poly->evaluateAt(GF256::exp($i));
+
+ if($syndromeCoefficients[$i] !== 0){
+ $error = true;
+ }
+ }
+
+ if(!$error){
+ return $received;
+ }
+
+ [$sigma, $omega] = $this->runEuclideanAlgorithm(
+ GF256::buildMonomial($numEccCodewords, 1),
+ new GenericGFPoly(array_reverse($syndromeCoefficients)),
+ $numEccCodewords
+ );
+
+ $errorLocations = $this->findErrorLocations($sigma);
+ $errorMagnitudes = $this->findErrorMagnitudes($omega, $errorLocations);
+ $errorLocationsCount = count($errorLocations);
+ $receivedCount = count($received);
+
+ for($i = 0; $i < $errorLocationsCount; $i++){
+ $position = ($receivedCount - 1 - GF256::log($errorLocations[$i]));
+
+ if($position < 0){
+ throw new QRCodeDecoderException('Bad error location');
+ }
+
+ $received[$position] ^= $errorMagnitudes[$i];
+ }
+
+ return $received;
+ }
+
+ /**
+ * @return \chillerlan\QRCode\Common\GenericGFPoly[] [sigma, omega]
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ private function runEuclideanAlgorithm(GenericGFPoly $a, GenericGFPoly $b, int $z):array{
+ // Assume a's degree is >= b's
+ if($a->getDegree() < $b->getDegree()){
+ $temp = $a;
+ $a = $b;
+ $b = $temp;
+ }
+
+ $rLast = $a;
+ $r = $b;
+ $tLast = new GenericGFPoly([0]);
+ $t = new GenericGFPoly([1]);
+
+ // Run Euclidean algorithm until r's degree is less than z/2
+ while((2 * $r->getDegree()) >= $z){
+ $rLastLast = $rLast;
+ $tLastLast = $tLast;
+ $rLast = $r;
+ $tLast = $t;
+
+ // Divide rLastLast by rLast, with quotient in q and remainder in r
+ [$q, $r] = $rLastLast->divide($rLast);
+
+ $t = $q->multiply($tLast)->addOrSubtract($tLastLast);
+
+ if($r->getDegree() >= $rLast->getDegree()){
+ throw new QRCodeDecoderException('Division algorithm failed to reduce polynomial?');
+ }
+ }
+
+ $sigmaTildeAtZero = $t->getCoefficient(0);
+
+ if($sigmaTildeAtZero === 0){
+ throw new QRCodeDecoderException('sigmaTilde(0) was zero');
+ }
+
+ $inverse = GF256::inverse($sigmaTildeAtZero);
+
+ return [$t->multiplyInt($inverse), $r->multiplyInt($inverse)];
+ }
+
+ /**
+ * @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
+ */
+ private function findErrorLocations(GenericGFPoly $errorLocator):array{
+ // This is a direct application of Chien's search
+ $numErrors = $errorLocator->getDegree();
+
+ if($numErrors === 1){ // shortcut
+ return [$errorLocator->getCoefficient(1)];
+ }
+
+ $result = array_fill(0, $numErrors, 0);
+ $e = 0;
+
+ for($i = 1; $i < 256 && $e < $numErrors; $i++){
+ if($errorLocator->evaluateAt($i) === 0){
+ $result[$e] = GF256::inverse($i);
+ $e++;
+ }
+ }
+
+ if($e !== $numErrors){
+ throw new QRCodeDecoderException('Error locator degree does not match number of roots');
+ }
+
+ return $result;
+ }
+
+ /**
+ *
+ */
+ private function findErrorMagnitudes(GenericGFPoly $errorEvaluator, array $errorLocations):array{
+ // This is directly applying Forney's Formula
+ $s = count($errorLocations);
+ $result = [];
+
+ for($i = 0; $i < $s; $i++){
+ $xiInverse = GF256::inverse($errorLocations[$i]);
+ $denominator = 1;
+
+ for($j = 0; $j < $s; $j++){
+ if($i !== $j){
+# $denominator = GF256::multiply($denominator, GF256::addOrSubtract(1, GF256::multiply($errorLocations[$j], $xiInverse)));
+ // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
+ // Below is a funny-looking workaround from Steven Parkes
+ $term = GF256::multiply($errorLocations[$j], $xiInverse);
+ $denominator = GF256::multiply($denominator, ((($term & 0x1) === 0) ? ($term | 1) : ($term & ~1)));
+ }
+ }
+
+ $result[$i] = GF256::multiply($errorEvaluator->evaluateAt($xiInverse), GF256::inverse($denominator));
+ }
+
+ return $result;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Detector/AlignmentPattern.php b/vendor/chillerlan/php-qrcode/src/Detector/AlignmentPattern.php
new file mode 100644
index 0000000..72feafd
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Detector/AlignmentPattern.php
@@ -0,0 +1,34 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Detector;
+
+/**
+ * Encapsulates an alignment pattern, which are the smaller square patterns found in
+ * all but the simplest QR Codes.
+ *
+ * @author Sean Owen
+ */
+final class AlignmentPattern extends ResultPoint{
+
+ /**
+ * Combines this object's current estimate of a finder pattern position and module size
+ * with a new estimate. It returns a new FinderPattern containing an average of the two.
+ */
+ public function combineEstimate(float $i, float $j, float $newModuleSize):self{
+ return new self(
+ (($this->x + $j) / 2.0),
+ (($this->y + $i) / 2.0),
+ (($this->estimatedModuleSize + $newModuleSize) / 2.0)
+ );
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Detector/AlignmentPatternFinder.php b/vendor/chillerlan/php-qrcode/src/Detector/AlignmentPatternFinder.php
new file mode 100644
index 0000000..ca62c6f
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Detector/AlignmentPatternFinder.php
@@ -0,0 +1,283 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Detector;
+
+use chillerlan\QRCode\Decoder\BitMatrix;
+use function abs, count;
+
+/**
+ * This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder
+ * patterns but are smaller and appear at regular intervals throughout the image.
+ *
+ * At the moment this only looks for the bottom-right alignment pattern.
+ *
+ * This is mostly a simplified copy of FinderPatternFinder. It is copied,
+ * pasted and stripped down here for maximum performance but does unfortunately duplicate
+ * some code.
+ *
+ * This class is thread-safe but not reentrant. Each thread must allocate its own object.
+ *
+ * @author Sean Owen
+ */
+final class AlignmentPatternFinder{
+
+ private BitMatrix $matrix;
+ private float $moduleSize;
+ /** @var \chillerlan\QRCode\Detector\AlignmentPattern[] */
+ private array $possibleCenters;
+
+ /**
+ * Creates a finder that will look in a portion of the whole image.
+ *
+ * @param \chillerlan\QRCode\Decoder\BitMatrix $matrix image to search
+ * @param float $moduleSize estimated module size so far
+ */
+ public function __construct(BitMatrix $matrix, float $moduleSize){
+ $this->matrix = $matrix;
+ $this->moduleSize = $moduleSize;
+ $this->possibleCenters = [];
+ }
+
+ /**
+ * This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
+ * it's pretty performance-critical and so is written to be fast foremost.
+ *
+ * @param int $startX left column from which to start searching
+ * @param int $startY top row from which to start searching
+ * @param int $width width of region to search
+ * @param int $height height of region to search
+ *
+ * @return \chillerlan\QRCode\Detector\AlignmentPattern|null
+ */
+ public function find(int $startX, int $startY, int $width, int $height):?AlignmentPattern{
+ $maxJ = ($startX + $width);
+ $middleI = ($startY + ($height / 2));
+ $stateCount = [];
+
+ // We are looking for black/white/black modules in 1:1:1 ratio;
+ // this tracks the number of black/white/black modules seen so far
+ for($iGen = 0; $iGen < $height; $iGen++){
+ // Search from middle outwards
+ $i = (int)($middleI + ((($iGen & 0x01) === 0) ? ($iGen + 1) / 2 : -(($iGen + 1) / 2)));
+ $stateCount[0] = 0;
+ $stateCount[1] = 0;
+ $stateCount[2] = 0;
+ $j = $startX;
+ // Burn off leading white pixels before anything else; if we start in the middle of
+ // a white run, it doesn't make sense to count its length, since we don't know if the
+ // white run continued to the left of the start point
+ while($j < $maxJ && !$this->matrix->check($j, $i)){
+ $j++;
+ }
+
+ $currentState = 0;
+
+ while($j < $maxJ){
+
+ if($this->matrix->check($j, $i)){
+ // Black pixel
+ if($currentState === 1){ // Counting black pixels
+ $stateCount[$currentState]++;
+ }
+ // Counting white pixels
+ else{
+ // A winner?
+ if($currentState === 2){
+ // Yes
+ if($this->foundPatternCross($stateCount)){
+ $confirmed = $this->handlePossibleCenter($stateCount, $i, $j);
+
+ if($confirmed !== null){
+ return $confirmed;
+ }
+ }
+
+ $stateCount[0] = $stateCount[2];
+ $stateCount[1] = 1;
+ $stateCount[2] = 0;
+ $currentState = 1;
+ }
+ else{
+ $stateCount[++$currentState]++;
+ }
+ }
+ }
+ // White pixel
+ else{
+ // Counting black pixels
+ if($currentState === 1){
+ $currentState++;
+ }
+
+ $stateCount[$currentState]++;
+ }
+
+ $j++;
+ }
+
+ if($this->foundPatternCross($stateCount)){
+ $confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ);
+
+ if($confirmed !== null){
+ return $confirmed;
+ }
+ }
+
+ }
+
+ // Hmm, nothing we saw was observed and confirmed twice. If we had
+ // any guess at all, return it.
+ if(count($this->possibleCenters)){
+ return $this->possibleCenters[0];
+ }
+
+ return null;
+ }
+
+ /**
+ * @param int[] $stateCount count of black/white/black pixels just read
+ *
+ * @return bool true if the proportions of the counts is close enough to the 1/1/1 ratios
+ * used by alignment patterns to be considered a match
+ */
+ private function foundPatternCross(array $stateCount):bool{
+ $maxVariance = ($this->moduleSize / 2.0);
+
+ for($i = 0; $i < 3; $i++){
+ if(abs($this->moduleSize - $stateCount[$i]) >= $maxVariance){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * This is called when a horizontal scan finds a possible alignment pattern. It will
+ * cross-check with a vertical scan, and if successful, will see if this pattern had been
+ * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
+ * found the alignment pattern.
+ *
+ * @param int[] $stateCount reading state module counts from horizontal scan
+ * @param int $i row where alignment pattern may be found
+ * @param int $j end of possible alignment pattern in row
+ *
+ * @return \chillerlan\QRCode\Detector\AlignmentPattern|null if we have found the same pattern twice, or null if not
+ */
+ private function handlePossibleCenter(array $stateCount, int $i, int $j):?AlignmentPattern{
+ $stateCountTotal = ($stateCount[0] + $stateCount[1] + $stateCount[2]);
+ $centerJ = $this->centerFromEnd($stateCount, $j);
+ $centerI = $this->crossCheckVertical($i, (int)$centerJ, (2 * $stateCount[1]), $stateCountTotal);
+
+ if($centerI !== null){
+ $estimatedModuleSize = (($stateCount[0] + $stateCount[1] + $stateCount[2]) / 3.0);
+
+ foreach($this->possibleCenters as $center){
+ // Look for about the same center and module size:
+ if($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)){
+ return $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize);
+ }
+ }
+
+ // Hadn't found this before; save it
+ $point = new AlignmentPattern($centerJ, $centerI, $estimatedModuleSize);
+ $this->possibleCenters[] = $point;
+ }
+
+ return null;
+ }
+
+ /**
+ * Given a count of black/white/black pixels just seen and an end position,
+ * figures the location of the center of this black/white/black run.
+ *
+ * @param int[] $stateCount
+ * @param int $end
+ *
+ * @return float
+ */
+ private function centerFromEnd(array $stateCount, int $end):float{
+ return (float)(($end - $stateCount[2]) - $stateCount[1] / 2);
+ }
+
+ /**
+ * After a horizontal scan finds a potential alignment pattern, this method
+ * "cross-checks" by scanning down vertically through the center of the possible
+ * alignment pattern to see if the same proportion is detected.
+ *
+ * @param int $startI row where an alignment pattern was detected
+ * @param int $centerJ center of the section that appears to cross an alignment pattern
+ * @param int $maxCount maximum reasonable number of modules that should be
+ * observed in any reading state, based on the results of the horizontal scan
+ * @param int $originalStateCountTotal
+ *
+ * @return float|null vertical center of alignment pattern, or null if not found
+ */
+ private function crossCheckVertical(int $startI, int $centerJ, int $maxCount, int $originalStateCountTotal):?float{
+ $maxI = $this->matrix->getSize();
+ $stateCount = [];
+ $stateCount[0] = 0;
+ $stateCount[1] = 0;
+ $stateCount[2] = 0;
+
+ // Start counting up from center
+ $i = $startI;
+ while($i >= 0 && $this->matrix->check($centerJ, $i) && $stateCount[1] <= $maxCount){
+ $stateCount[1]++;
+ $i--;
+ }
+ // If already too many modules in this state or ran off the edge:
+ if($i < 0 || $stateCount[1] > $maxCount){
+ return null;
+ }
+
+ while($i >= 0 && !$this->matrix->check($centerJ, $i) && $stateCount[0] <= $maxCount){
+ $stateCount[0]++;
+ $i--;
+ }
+
+ if($stateCount[0] > $maxCount){
+ return null;
+ }
+
+ // Now also count down from center
+ $i = ($startI + 1);
+ while($i < $maxI && $this->matrix->check($centerJ, $i) && $stateCount[1] <= $maxCount){
+ $stateCount[1]++;
+ $i++;
+ }
+
+ if($i == $maxI || $stateCount[1] > $maxCount){
+ return null;
+ }
+
+ while($i < $maxI && !$this->matrix->check($centerJ, $i) && $stateCount[2] <= $maxCount){
+ $stateCount[2]++;
+ $i++;
+ }
+
+ if($stateCount[2] > $maxCount){
+ return null;
+ }
+
+ if((5 * abs(($stateCount[0] + $stateCount[1] + $stateCount[2]) - $originalStateCountTotal)) >= (2 * $originalStateCountTotal)){
+ return null;
+ }
+
+ if(!$this->foundPatternCross($stateCount)){
+ return null;
+ }
+
+ return $this->centerFromEnd($stateCount, $i);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Detector/Detector.php b/vendor/chillerlan/php-qrcode/src/Detector/Detector.php
new file mode 100644
index 0000000..e43798b
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Detector/Detector.php
@@ -0,0 +1,350 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Detector;
+
+use chillerlan\QRCode\Common\{LuminanceSourceInterface, Version};
+use chillerlan\QRCode\Decoder\{Binarizer, BitMatrix};
+use function abs, intdiv, is_nan, max, min, round;
+use const NAN;
+
+/**
+ * Encapsulates logic that can detect a QR Code in an image, even if the QR Code
+ * is rotated or skewed, or partially obscured.
+ *
+ * @author Sean Owen
+ */
+final class Detector{
+
+ private BitMatrix $matrix;
+
+ /**
+ * Detector constructor.
+ */
+ public function __construct(LuminanceSourceInterface $source){
+ $this->matrix = (new Binarizer($source))->getBlackMatrix();
+ }
+
+ /**
+ * Detects a QR Code in an image.
+ */
+ public function detect():BitMatrix{
+ [$bottomLeft, $topLeft, $topRight] = (new FinderPatternFinder($this->matrix))->find();
+
+ $moduleSize = $this->calculateModuleSize($topLeft, $topRight, $bottomLeft);
+ $dimension = $this->computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize);
+ $provisionalVersion = new Version(intdiv(($dimension - 17), 4));
+ $alignmentPattern = null;
+
+ // Anything above version 1 has an alignment pattern
+ if(!empty($provisionalVersion->getAlignmentPattern())){
+ // Guess where a "bottom right" finder pattern would have been
+ $bottomRightX = ($topRight->getX() - $topLeft->getX() + $bottomLeft->getX());
+ $bottomRightY = ($topRight->getY() - $topLeft->getY() + $bottomLeft->getY());
+
+ // Estimate that alignment pattern is closer by 3 modules
+ // from "bottom right" to known top left location
+ $correctionToTopLeft = (1.0 - 3.0 / (float)($provisionalVersion->getDimension() - 7));
+ $estAlignmentX = (int)($topLeft->getX() + $correctionToTopLeft * ($bottomRightX - $topLeft->getX()));
+ $estAlignmentY = (int)($topLeft->getY() + $correctionToTopLeft * ($bottomRightY - $topLeft->getY()));
+
+ // Kind of arbitrary -- expand search radius before giving up
+ for($i = 4; $i <= 16; $i <<= 1){//??????????
+ $alignmentPattern = $this->findAlignmentInRegion($moduleSize, $estAlignmentX, $estAlignmentY, (float)$i);
+
+ if($alignmentPattern !== null){
+ break;
+ }
+ }
+ // If we didn't find alignment pattern... well try anyway without it
+ }
+
+ $transform = $this->createTransform($topLeft, $topRight, $bottomLeft, $dimension, $alignmentPattern);
+
+ return (new GridSampler)->sampleGrid($this->matrix, $dimension, $transform);
+ }
+
+ /**
+ * Computes an average estimated module size based on estimated derived from the positions
+ * of the three finder patterns.
+ *
+ * @throws \chillerlan\QRCode\Detector\QRCodeDetectorException
+ */
+ private function calculateModuleSize(FinderPattern $topLeft, FinderPattern $topRight, FinderPattern $bottomLeft):float{
+ // Take the average
+ $moduleSize = ((
+ $this->calculateModuleSizeOneWay($topLeft, $topRight) +
+ $this->calculateModuleSizeOneWay($topLeft, $bottomLeft)
+ ) / 2.0);
+
+ if($moduleSize < 1.0){
+ throw new QRCodeDetectorException('module size < 1.0');
+ }
+
+ return $moduleSize;
+ }
+
+ /**
+ * Estimates module size based on two finder patterns -- it uses
+ * #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int) to figure the
+ * width of each, measuring along the axis between their centers.
+ */
+ private function calculateModuleSizeOneWay(FinderPattern $a, FinderPattern $b):float{
+
+ $moduleSizeEst1 = $this->sizeOfBlackWhiteBlackRunBothWays($a->getX(), $a->getY(), $b->getX(), $b->getY());
+ $moduleSizeEst2 = $this->sizeOfBlackWhiteBlackRunBothWays($b->getX(), $b->getY(), $a->getX(), $a->getY());
+
+ if(is_nan($moduleSizeEst1)){
+ return ($moduleSizeEst2 / 7.0);
+ }
+
+ if(is_nan($moduleSizeEst2)){
+ return ($moduleSizeEst1 / 7.0);
+ }
+ // Average them, and divide by 7 since we've counted the width of 3 black modules,
+ // and 1 white and 1 black module on either side. Ergo, divide sum by 14.
+ return (($moduleSizeEst1 + $moduleSizeEst2) / 14.0);
+ }
+
+ /**
+ * See #sizeOfBlackWhiteBlackRun(int, int, int, int); computes the total width of
+ * a finder pattern by looking for a black-white-black run from the center in the direction
+ * of another po$(another finder pattern center), and in the opposite direction too.
+ *
+ * @noinspection DuplicatedCode
+ */
+ private function sizeOfBlackWhiteBlackRunBothWays(float $fromX, float $fromY, float $toX, float $toY):float{
+ $result = $this->sizeOfBlackWhiteBlackRun((int)$fromX, (int)$fromY, (int)$toX, (int)$toY);
+ $dimension = $this->matrix->getSize();
+ // Now count other way -- don't run off image though of course
+ $scale = 1.0;
+ $otherToX = ($fromX - ($toX - $fromX));
+
+ if($otherToX < 0){
+ $scale = ($fromX / ($fromX - $otherToX));
+ $otherToX = 0;
+ }
+ elseif($otherToX >= $dimension){
+ $scale = (($dimension - 1 - $fromX) / ($otherToX - $fromX));
+ $otherToX = ($dimension - 1);
+ }
+
+ $otherToY = (int)($fromY - ($toY - $fromY) * $scale);
+ $scale = 1.0;
+
+ if($otherToY < 0){
+ $scale = ($fromY / ($fromY - $otherToY));
+ $otherToY = 0;
+ }
+ elseif($otherToY >= $dimension){
+ $scale = (($dimension - 1 - $fromY) / ($otherToY - $fromY));
+ $otherToY = ($dimension - 1);
+ }
+
+ $otherToX = (int)($fromX + ($otherToX - $fromX) * $scale);
+ $result += $this->sizeOfBlackWhiteBlackRun((int)$fromX, (int)$fromY, $otherToX, $otherToY);
+
+ // Middle pixel is double-counted this way; subtract 1
+ return ($result - 1.0);
+ }
+
+ /**
+ * This method traces a line from a po$in the image, in the direction towards another point.
+ * It begins in a black region, and keeps going until it finds white, then black, then white again.
+ * It reports the distance from the start to this point.
+ *
+ * This is used when figuring out how wide a finder pattern is, when the finder pattern
+ * may be skewed or rotated.
+ */
+ private function sizeOfBlackWhiteBlackRun(int $fromX, int $fromY, int $toX, int $toY):float{
+ // Mild variant of Bresenham's algorithm;
+ // @see https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
+ $steep = abs($toY - $fromY) > abs($toX - $fromX);
+
+ if($steep){
+ $temp = $fromX;
+ $fromX = $fromY;
+ $fromY = $temp;
+ $temp = $toX;
+ $toX = $toY;
+ $toY = $temp;
+ }
+
+ $dx = abs($toX - $fromX);
+ $dy = abs($toY - $fromY);
+ $error = (-$dx / 2);
+ $xstep = (($fromX < $toX) ? 1 : -1);
+ $ystep = (($fromY < $toY) ? 1 : -1);
+
+ // In black pixels, looking for white, first or second time.
+ $state = 0;
+ // Loop up until x == toX, but not beyond
+ $xLimit = ($toX + $xstep);
+
+ for($x = $fromX, $y = $fromY; $x !== $xLimit; $x += $xstep){
+ $realX = ($steep) ? $y : $x;
+ $realY = ($steep) ? $x : $y;
+
+ // Does current pixel mean we have moved white to black or vice versa?
+ // Scanning black in state 0,2 and white in state 1, so if we find the wrong
+ // color, advance to next state or end if we are in state 2 already
+ if(($state === 1) === $this->matrix->check($realX, $realY)){
+
+ if($state === 2){
+ return FinderPattern::distance($x, $y, $fromX, $fromY);
+ }
+
+ $state++;
+ }
+
+ $error += $dy;
+
+ if($error > 0){
+
+ if($y === $toY){
+ break;
+ }
+
+ $y += $ystep;
+ $error -= $dx;
+ }
+ }
+
+ // Found black-white-black; give the benefit of the doubt that the next pixel outside the image
+ // is "white" so this last po$at (toX+xStep,toY) is the right ending. This is really a
+ // small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this.
+ if($state === 2){
+ return FinderPattern::distance(($toX + $xstep), $toY, $fromX, $fromY);
+ }
+
+ // else we didn't find even black-white-black; no estimate is really possible
+ return NAN;
+ }
+
+ /**
+ * Computes the dimension (number of modules on a size) of the QR Code based on the position
+ * of the finder patterns and estimated module size.
+ *
+ * @throws \chillerlan\QRCode\Detector\QRCodeDetectorException
+ */
+ private function computeDimension(FinderPattern $nw, FinderPattern $ne, FinderPattern $sw, float $size):int{
+ $tltrCentersDimension = (int)round($nw->getDistance($ne) / $size);
+ $tlblCentersDimension = (int)round($nw->getDistance($sw) / $size);
+ $dimension = (int)((($tltrCentersDimension + $tlblCentersDimension) / 2) + 7);
+
+ switch($dimension % 4){
+ case 0:
+ $dimension++;
+ break;
+ // 1? do nothing
+ case 2:
+ $dimension--;
+ break;
+ case 3:
+ throw new QRCodeDetectorException('estimated dimension: '.$dimension);
+ }
+
+ if(($dimension % 4) !== 1){
+ throw new QRCodeDetectorException('dimension mod 4 is not 1');
+ }
+
+ return $dimension;
+ }
+
+ /**
+ * Attempts to locate an alignment pattern in a limited region of the image, which is
+ * guessed to contain it.
+ *
+ * @param float $overallEstModuleSize estimated module size so far
+ * @param int $estAlignmentX x coordinate of center of area probably containing alignment pattern
+ * @param int $estAlignmentY y coordinate of above
+ * @param float $allowanceFactor number of pixels in all directions to search from the center
+ *
+ * @return \chillerlan\QRCode\Detector\AlignmentPattern|null if found, or null otherwise
+ */
+ private function findAlignmentInRegion(
+ float $overallEstModuleSize,
+ int $estAlignmentX,
+ int $estAlignmentY,
+ float $allowanceFactor
+ ):?AlignmentPattern{
+ // Look for an alignment pattern (3 modules in size) around where it should be
+ $dimension = $this->matrix->getSize();
+ $allowance = (int)($allowanceFactor * $overallEstModuleSize);
+ $alignmentAreaLeftX = max(0, ($estAlignmentX - $allowance));
+ $alignmentAreaRightX = min(($dimension - 1), ($estAlignmentX + $allowance));
+
+ if(($alignmentAreaRightX - $alignmentAreaLeftX) < ($overallEstModuleSize * 3)){
+ return null;
+ }
+
+ $alignmentAreaTopY = max(0, ($estAlignmentY - $allowance));
+ $alignmentAreaBottomY = min(($dimension - 1), ($estAlignmentY + $allowance));
+
+ if(($alignmentAreaBottomY - $alignmentAreaTopY) < ($overallEstModuleSize * 3)){
+ return null;
+ }
+
+ return (new AlignmentPatternFinder($this->matrix, $overallEstModuleSize))->find(
+ $alignmentAreaLeftX,
+ $alignmentAreaTopY,
+ ($alignmentAreaRightX - $alignmentAreaLeftX),
+ ($alignmentAreaBottomY - $alignmentAreaTopY),
+ );
+ }
+
+ /**
+ *
+ */
+ private function createTransform(
+ FinderPattern $nw,
+ FinderPattern $ne,
+ FinderPattern $sw,
+ int $size,
+ AlignmentPattern $ap = null
+ ):PerspectiveTransform{
+ $dimMinusThree = ($size - 3.5);
+
+ if($ap instanceof AlignmentPattern){
+ $bottomRightX = $ap->getX();
+ $bottomRightY = $ap->getY();
+ $sourceBottomRightX = ($dimMinusThree - 3.0);
+ $sourceBottomRightY = $sourceBottomRightX;
+ }
+ else{
+ // Don't have an alignment pattern, just make up the bottom-right point
+ $bottomRightX = ($ne->getX() - $nw->getX() + $sw->getX());
+ $bottomRightY = ($ne->getY() - $nw->getY() + $sw->getY());
+ $sourceBottomRightX = $dimMinusThree;
+ $sourceBottomRightY = $dimMinusThree;
+ }
+
+ return (new PerspectiveTransform)->quadrilateralToQuadrilateral(
+ 3.5,
+ 3.5,
+ $dimMinusThree,
+ 3.5,
+ $sourceBottomRightX,
+ $sourceBottomRightY,
+ 3.5,
+ $dimMinusThree,
+ $nw->getX(),
+ $nw->getY(),
+ $ne->getX(),
+ $ne->getY(),
+ $bottomRightX,
+ $bottomRightY,
+ $sw->getX(),
+ $sw->getY()
+ );
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Detector/FinderPattern.php b/vendor/chillerlan/php-qrcode/src/Detector/FinderPattern.php
new file mode 100644
index 0000000..1f799e8
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Detector/FinderPattern.php
@@ -0,0 +1,92 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Detector;
+
+use function sqrt;
+
+/**
+ * Encapsulates a finder pattern, which are the three square patterns found in
+ * the corners of QR Codes. It also encapsulates a count of similar finder patterns,
+ * as a convenience to the finder's bookkeeping.
+ *
+ * @author Sean Owen
+ */
+final class FinderPattern extends ResultPoint{
+
+ private int $count;
+
+ /**
+ *
+ */
+ public function __construct(float $posX, float $posY, float $estimatedModuleSize, int $count = null){
+ parent::__construct($posX, $posY, $estimatedModuleSize);
+
+ $this->count = ($count ?? 1);
+ }
+
+ /**
+ *
+ */
+ public function getCount():int{
+ return $this->count;
+ }
+
+ /**
+ * @param \chillerlan\QRCode\Detector\FinderPattern $b second pattern
+ *
+ * @return float distance between two points
+ */
+ public function getDistance(FinderPattern $b):float{
+ return self::distance($this->x, $this->y, $b->x, $b->y);
+ }
+
+ /**
+ * Get square of distance between a and b.
+ */
+ public function getSquaredDistance(FinderPattern $b):float{
+ return self::squaredDistance($this->x, $this->y, $b->x, $b->y);
+ }
+
+ /**
+ * Combines this object's current estimate of a finder pattern position and module size
+ * with a new estimate. It returns a new FinderPattern containing a weighted average
+ * based on count.
+ */
+ public function combineEstimate(float $i, float $j, float $newModuleSize):self{
+ $combinedCount = ($this->count + 1);
+
+ return new self(
+ ($this->count * $this->x + $j) / $combinedCount,
+ ($this->count * $this->y + $i) / $combinedCount,
+ ($this->count * $this->estimatedModuleSize + $newModuleSize) / $combinedCount,
+ $combinedCount
+ );
+ }
+
+ /**
+ *
+ */
+ private static function squaredDistance(float $aX, float $aY, float $bX, float $bY):float{
+ $xDiff = ($aX - $bX);
+ $yDiff = ($aY - $bY);
+
+ return ($xDiff * $xDiff + $yDiff * $yDiff);
+ }
+
+ /**
+ *
+ */
+ public static function distance(float $aX, float $aY, float $bX, float $bY):float{
+ return sqrt(self::squaredDistance($aX, $aY, $bX, $bY));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Detector/FinderPatternFinder.php b/vendor/chillerlan/php-qrcode/src/Detector/FinderPatternFinder.php
new file mode 100644
index 0000000..755d08c
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Detector/FinderPatternFinder.php
@@ -0,0 +1,770 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ *
+ * @phan-file-suppress PhanTypePossiblyInvalidDimOffset
+ */
+
+namespace chillerlan\QRCode\Detector;
+
+use chillerlan\QRCode\Decoder\BitMatrix;
+use function abs, count, intdiv, usort;
+use const PHP_FLOAT_MAX;
+
+/**
+ * This class attempts to find finder patterns in a QR Code. Finder patterns are the square
+ * markers at three corners of a QR Code.
+ *
+ * This class is thread-safe but not reentrant. Each thread must allocate its own object.
+ *
+ * @author Sean Owen
+ */
+final class FinderPatternFinder{
+
+ private const MIN_SKIP = 2;
+ private const MAX_MODULES = 177; // 1 pixel/module times 3 modules/center
+ private const CENTER_QUORUM = 2; // support up to version 10 for mobile clients
+ private BitMatrix $matrix;
+ /** @var \chillerlan\QRCode\Detector\FinderPattern[] */
+ private array $possibleCenters;
+ private bool $hasSkipped = false;
+
+ /**
+ * Creates a finder that will search the image for three finder patterns.
+ *
+ * @param BitMatrix $matrix image to search
+ */
+ public function __construct(BitMatrix $matrix){
+ $this->matrix = $matrix;
+ $this->possibleCenters = [];
+ }
+
+ /**
+ * @return \chillerlan\QRCode\Detector\FinderPattern[]
+ */
+ public function find():array{
+ $dimension = $this->matrix->getSize();
+
+ // We are looking for black/white/black/white/black modules in
+ // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
+ // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
+ // image, and then account for the center being 3 modules in size. This gives the smallest
+ // number of pixels the center could be, so skip this often.
+ $iSkip = intdiv((3 * $dimension), (4 * self::MAX_MODULES));
+
+ if($iSkip < self::MIN_SKIP){
+ $iSkip = self::MIN_SKIP;
+ }
+
+ $done = false;
+
+ for($i = ($iSkip - 1); ($i < $dimension) && !$done; $i += $iSkip){
+ // Get a row of black/white values
+ $stateCount = $this->getCrossCheckStateCount();
+ $currentState = 0;
+
+ for($j = 0; $j < $dimension; $j++){
+
+ // Black pixel
+ if($this->matrix->check($j, $i)){
+ // Counting white pixels
+ if(($currentState & 1) === 1){
+ $currentState++;
+ }
+
+ $stateCount[$currentState]++;
+ }
+ // White pixel
+ else{
+ // Counting black pixels
+ if(($currentState & 1) === 0){
+ // A winner?
+ if($currentState === 4){
+ // Yes
+ if($this->foundPatternCross($stateCount)){
+ $confirmed = $this->handlePossibleCenter($stateCount, $i, $j);
+
+ if($confirmed){
+ // Start examining every other line. Checking each line turned out to be too
+ // expensive and didn't improve performance.
+ $iSkip = 3;
+
+ if($this->hasSkipped){
+ $done = $this->haveMultiplyConfirmedCenters();
+ }
+ else{
+ $rowSkip = $this->findRowSkip();
+
+ if($rowSkip > $stateCount[2]){
+ // Skip rows between row of lower confirmed center
+ // and top of presumed third confirmed center
+ // but back up a bit to get a full chance of detecting
+ // it, entire width of center of finder pattern
+
+ // Skip by rowSkip, but back off by $stateCount[2] (size of last center
+ // of pattern we saw) to be conservative, and also back off by iSkip which
+ // is about to be re-added
+ $i += ($rowSkip - $stateCount[2] - $iSkip);
+ $j = ($dimension - 1);
+ }
+ }
+ }
+ else{
+ $stateCount = $this->doShiftCounts2($stateCount);
+ $currentState = 3;
+
+ continue;
+ }
+ // Clear state to start looking again
+ $currentState = 0;
+ $stateCount = $this->getCrossCheckStateCount();
+ }
+ // No, shift counts back by two
+ else{
+ $stateCount = $this->doShiftCounts2($stateCount);
+ $currentState = 3;
+ }
+ }
+ else{
+ $stateCount[++$currentState]++;
+ }
+ }
+ // Counting white pixels
+ else{
+ $stateCount[$currentState]++;
+ }
+ }
+ }
+
+ if($this->foundPatternCross($stateCount)){
+ $confirmed = $this->handlePossibleCenter($stateCount, $i, $dimension);
+
+ if($confirmed){
+ $iSkip = $stateCount[0];
+
+ if($this->hasSkipped){
+ // Found a third one
+ $done = $this->haveMultiplyConfirmedCenters();
+ }
+ }
+ }
+ }
+
+ return $this->orderBestPatterns($this->selectBestPatterns());
+ }
+
+ /**
+ * @return int[]
+ */
+ private function getCrossCheckStateCount():array{
+ return [0, 0, 0, 0, 0];
+ }
+
+ /**
+ * @param int[] $stateCount
+ *
+ * @return int[]
+ */
+ private function doShiftCounts2(array $stateCount):array{
+ $stateCount[0] = $stateCount[2];
+ $stateCount[1] = $stateCount[3];
+ $stateCount[2] = $stateCount[4];
+ $stateCount[3] = 1;
+ $stateCount[4] = 0;
+
+ return $stateCount;
+ }
+
+ /**
+ * Given a count of black/white/black/white/black pixels just seen and an end position,
+ * figures the location of the center of this run.
+ *
+ * @param int[] $stateCount
+ */
+ private function centerFromEnd(array $stateCount, int $end):float{
+ return (float)(($end - $stateCount[4] - $stateCount[3]) - $stateCount[2] / 2);
+ }
+
+ /**
+ * @param int[] $stateCount
+ */
+ private function foundPatternCross(array $stateCount):bool{
+ // Allow less than 50% variance from 1-1-3-1-1 proportions
+ return $this->foundPatternVariance($stateCount, 2.0);
+ }
+
+ /**
+ * @param int[] $stateCount
+ */
+ private function foundPatternDiagonal(array $stateCount):bool{
+ // Allow less than 75% variance from 1-1-3-1-1 proportions
+ return $this->foundPatternVariance($stateCount, 1.333);
+ }
+
+ /**
+ * @param int[] $stateCount count of black/white/black/white/black pixels just read
+ *
+ * @return bool true if the proportions of the counts is close enough to the 1/1/3/1/1 ratios
+ * used by finder patterns to be considered a match
+ */
+ private function foundPatternVariance(array $stateCount, float $variance):bool{
+ $totalModuleSize = 0;
+
+ for($i = 0; $i < 5; $i++){
+ $count = $stateCount[$i];
+
+ if($count === 0){
+ return false;
+ }
+
+ $totalModuleSize += $count;
+ }
+
+ if($totalModuleSize < 7){
+ return false;
+ }
+
+ $moduleSize = ($totalModuleSize / 7.0);
+ $maxVariance = ($moduleSize / $variance);
+
+ return
+ abs($moduleSize - $stateCount[0]) < $maxVariance
+ && abs($moduleSize - $stateCount[1]) < $maxVariance
+ && abs(3.0 * $moduleSize - $stateCount[2]) < (3 * $maxVariance)
+ && abs($moduleSize - $stateCount[3]) < $maxVariance
+ && abs($moduleSize - $stateCount[4]) < $maxVariance;
+ }
+
+ /**
+ * After a vertical and horizontal scan finds a potential finder pattern, this method
+ * "cross-cross-cross-checks" by scanning down diagonally through the center of the possible
+ * finder pattern to see if the same proportion is detected.
+ *
+ * @param int $centerI row where a finder pattern was detected
+ * @param int $centerJ center of the section that appears to cross a finder pattern
+ *
+ * @return bool true if proportions are withing expected limits
+ */
+ private function crossCheckDiagonal(int $centerI, int $centerJ):bool{
+ $stateCount = $this->getCrossCheckStateCount();
+
+ // Start counting up, left from center finding black center mass
+ $i = 0;
+
+ while($centerI >= $i && $centerJ >= $i && $this->matrix->check(($centerJ - $i), ($centerI - $i))){
+ $stateCount[2]++;
+ $i++;
+ }
+
+ if($stateCount[2] === 0){
+ return false;
+ }
+
+ // Continue up, left finding white space
+ while($centerI >= $i && $centerJ >= $i && !$this->matrix->check(($centerJ - $i), ($centerI - $i))){
+ $stateCount[1]++;
+ $i++;
+ }
+
+ if($stateCount[1] === 0){
+ return false;
+ }
+
+ // Continue up, left finding black border
+ while($centerI >= $i && $centerJ >= $i && $this->matrix->check(($centerJ - $i), ($centerI - $i))){
+ $stateCount[0]++;
+ $i++;
+ }
+
+ if($stateCount[0] === 0){
+ return false;
+ }
+
+ $dimension = $this->matrix->getSize();
+
+ // Now also count down, right from center
+ $i = 1;
+ while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && $this->matrix->check(($centerJ + $i), ($centerI + $i))){
+ $stateCount[2]++;
+ $i++;
+ }
+
+ while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && !$this->matrix->check(($centerJ + $i), ($centerI + $i))){
+ $stateCount[3]++;
+ $i++;
+ }
+
+ if($stateCount[3] === 0){
+ return false;
+ }
+
+ while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && $this->matrix->check(($centerJ + $i), ($centerI + $i))){
+ $stateCount[4]++;
+ $i++;
+ }
+
+ if($stateCount[4] === 0){
+ return false;
+ }
+
+ return $this->foundPatternDiagonal($stateCount);
+ }
+
+ /**
+ * After a horizontal scan finds a potential finder pattern, this method
+ * "cross-checks" by scanning down vertically through the center of the possible
+ * finder pattern to see if the same proportion is detected.
+ *
+ * @param int $startI row where a finder pattern was detected
+ * @param int $centerJ center of the section that appears to cross a finder pattern
+ * @param int $maxCount maximum reasonable number of modules that should be
+ * observed in any reading state, based on the results of the horizontal scan
+ * @param int $originalStateCountTotal
+ *
+ * @return float|null vertical center of finder pattern, or null if not found
+ * @noinspection DuplicatedCode
+ */
+ private function crossCheckVertical(int $startI, int $centerJ, int $maxCount, int $originalStateCountTotal):?float{
+ $maxI = $this->matrix->getSize();
+ $stateCount = $this->getCrossCheckStateCount();
+
+ // Start counting up from center
+ $i = $startI;
+ while($i >= 0 && $this->matrix->check($centerJ, $i)){
+ $stateCount[2]++;
+ $i--;
+ }
+
+ if($i < 0){
+ return null;
+ }
+
+ while($i >= 0 && !$this->matrix->check($centerJ, $i) && $stateCount[1] <= $maxCount){
+ $stateCount[1]++;
+ $i--;
+ }
+
+ // If already too many modules in this state or ran off the edge:
+ if($i < 0 || $stateCount[1] > $maxCount){
+ return null;
+ }
+
+ while($i >= 0 && $this->matrix->check($centerJ, $i) && $stateCount[0] <= $maxCount){
+ $stateCount[0]++;
+ $i--;
+ }
+
+ if($stateCount[0] > $maxCount){
+ return null;
+ }
+
+ // Now also count down from center
+ $i = ($startI + 1);
+ while($i < $maxI && $this->matrix->check($centerJ, $i)){
+ $stateCount[2]++;
+ $i++;
+ }
+
+ if($i === $maxI){
+ return null;
+ }
+
+ while($i < $maxI && !$this->matrix->check($centerJ, $i) && $stateCount[3] < $maxCount){
+ $stateCount[3]++;
+ $i++;
+ }
+
+ if($i === $maxI || $stateCount[3] >= $maxCount){
+ return null;
+ }
+
+ while($i < $maxI && $this->matrix->check($centerJ, $i) && $stateCount[4] < $maxCount){
+ $stateCount[4]++;
+ $i++;
+ }
+
+ if($stateCount[4] >= $maxCount){
+ return null;
+ }
+
+ // If we found a finder-pattern-like section, but its size is more than 40% different from
+ // the original, assume it's a false positive
+ $stateCountTotal = ($stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4]);
+
+ if((5 * abs($stateCountTotal - $originalStateCountTotal)) >= (2 * $originalStateCountTotal)){
+ return null;
+ }
+
+ if(!$this->foundPatternCross($stateCount)){
+ return null;
+ }
+
+ return $this->centerFromEnd($stateCount, $i);
+ }
+
+ /**
+ * Like #crossCheckVertical(int, int, int, int), and in fact is basically identical,
+ * except it reads horizontally instead of vertically. This is used to cross-cross
+ * check a vertical cross-check and locate the real center of the alignment pattern.
+ * @noinspection DuplicatedCode
+ */
+ private function crossCheckHorizontal(int $startJ, int $centerI, int $maxCount, int $originalStateCountTotal):?float{
+ $maxJ = $this->matrix->getSize();
+ $stateCount = $this->getCrossCheckStateCount();
+
+ $j = $startJ;
+ while($j >= 0 && $this->matrix->check($j, $centerI)){
+ $stateCount[2]++;
+ $j--;
+ }
+
+ if($j < 0){
+ return null;
+ }
+
+ while($j >= 0 && !$this->matrix->check($j, $centerI) && $stateCount[1] <= $maxCount){
+ $stateCount[1]++;
+ $j--;
+ }
+
+ if($j < 0 || $stateCount[1] > $maxCount){
+ return null;
+ }
+
+ while($j >= 0 && $this->matrix->check($j, $centerI) && $stateCount[0] <= $maxCount){
+ $stateCount[0]++;
+ $j--;
+ }
+
+ if($stateCount[0] > $maxCount){
+ return null;
+ }
+
+ $j = ($startJ + 1);
+ while($j < $maxJ && $this->matrix->check($j, $centerI)){
+ $stateCount[2]++;
+ $j++;
+ }
+
+ if($j === $maxJ){
+ return null;
+ }
+
+ while($j < $maxJ && !$this->matrix->check($j, $centerI) && $stateCount[3] < $maxCount){
+ $stateCount[3]++;
+ $j++;
+ }
+
+ if($j === $maxJ || $stateCount[3] >= $maxCount){
+ return null;
+ }
+
+ while($j < $maxJ && $this->matrix->check($j, $centerI) && $stateCount[4] < $maxCount){
+ $stateCount[4]++;
+ $j++;
+ }
+
+ if($stateCount[4] >= $maxCount){
+ return null;
+ }
+
+ // If we found a finder-pattern-like section, but its size is significantly different from
+ // the original, assume it's a false positive
+ $stateCountTotal = ($stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4]);
+
+ if((5 * abs($stateCountTotal - $originalStateCountTotal)) >= $originalStateCountTotal){
+ return null;
+ }
+
+ if(!$this->foundPatternCross($stateCount)){
+ return null;
+ }
+
+ return $this->centerFromEnd($stateCount, $j);
+ }
+
+ /**
+ * This is called when a horizontal scan finds a possible alignment pattern. It will
+ * cross-check with a vertical scan, and if successful, will, ah, cross-cross-check
+ * with another horizontal scan. This is needed primarily to locate the real horizontal
+ * center of the pattern in cases of extreme skew.
+ * And then we cross-cross-cross check with another diagonal scan.
+ *
+ * If that succeeds the finder pattern location is added to a list that tracks
+ * the number of times each location has been nearly-matched as a finder pattern.
+ * Each additional find is more evidence that the location is in fact a finder
+ * pattern center
+ *
+ * @param int[] $stateCount reading state module counts from horizontal scan
+ * @param int $i row where finder pattern may be found
+ * @param int $j end of possible finder pattern in row
+ *
+ * @return bool if a finder pattern candidate was found this time
+ */
+ private function handlePossibleCenter(array $stateCount, int $i, int $j):bool{
+ $stateCountTotal = ($stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4]);
+ $centerJ = $this->centerFromEnd($stateCount, $j);
+ $centerI = $this->crossCheckVertical($i, (int)$centerJ, $stateCount[2], $stateCountTotal);
+
+ if($centerI !== null){
+ // Re-cross check
+ $centerJ = $this->crossCheckHorizontal((int)$centerJ, (int)$centerI, $stateCount[2], $stateCountTotal);
+ if($centerJ !== null && ($this->crossCheckDiagonal((int)$centerI, (int)$centerJ))){
+ $estimatedModuleSize = ($stateCountTotal / 7.0);
+ $found = false;
+
+ // cautious (was in for fool in which $this->possibleCenters is updated)
+ $count = count($this->possibleCenters);
+
+ for($index = 0; $index < $count; $index++){
+ $center = $this->possibleCenters[$index];
+ // Look for about the same center and module size:
+ if($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)){
+ $this->possibleCenters[$index] = $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize);
+ $found = true;
+ break;
+ }
+ }
+
+ if(!$found){
+ $point = new FinderPattern($centerJ, $centerI, $estimatedModuleSize);
+ $this->possibleCenters[] = $point;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @return int number of rows we could safely skip during scanning, based on the first
+ * two finder patterns that have been located. In some cases their position will
+ * allow us to infer that the third pattern must lie below a certain point farther
+ * down in the image.
+ */
+ private function findRowSkip():int{
+ $max = count($this->possibleCenters);
+
+ if($max <= 1){
+ return 0;
+ }
+
+ $firstConfirmedCenter = null;
+
+ foreach($this->possibleCenters as $center){
+
+ if($center->getCount() >= self::CENTER_QUORUM){
+
+ if($firstConfirmedCenter === null){
+ $firstConfirmedCenter = $center;
+ }
+ else{
+ // We have two confirmed centers
+ // How far down can we skip before resuming looking for the next
+ // pattern? In the worst case, only the difference between the
+ // difference in the x / y coordinates of the two centers.
+ // This is the case where you find top left last.
+ $this->hasSkipped = true;
+
+ return (int)((abs($firstConfirmedCenter->getX() - $center->getX()) -
+ abs($firstConfirmedCenter->getY() - $center->getY())) / 2);
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * @return bool true if we have found at least 3 finder patterns that have been detected
+ * at least #CENTER_QUORUM times each, and, the estimated module size of the
+ * candidates is "pretty similar"
+ */
+ private function haveMultiplyConfirmedCenters():bool{
+ $confirmedCount = 0;
+ $totalModuleSize = 0.0;
+ $max = count($this->possibleCenters);
+
+ foreach($this->possibleCenters as $pattern){
+ if($pattern->getCount() >= self::CENTER_QUORUM){
+ $confirmedCount++;
+ $totalModuleSize += $pattern->getEstimatedModuleSize();
+ }
+ }
+
+ if($confirmedCount < 3){
+ return false;
+ }
+ // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
+ // and that we need to keep looking. We detect this by asking if the estimated module sizes
+ // vary too much. We arbitrarily say that when the total deviation from average exceeds
+ // 5% of the total module size estimates, it's too much.
+ $average = ($totalModuleSize / (float)$max);
+ $totalDeviation = 0.0;
+
+ foreach($this->possibleCenters as $pattern){
+ $totalDeviation += abs($pattern->getEstimatedModuleSize() - $average);
+ }
+
+ return $totalDeviation <= (0.05 * $totalModuleSize);
+ }
+
+ /**
+ * @return \chillerlan\QRCode\Detector\FinderPattern[] the 3 best FinderPatterns from our list of candidates. The "best" are
+ * those that have been detected at least #CENTER_QUORUM times, and whose module
+ * size differs from the average among those patterns the least
+ * @throws \chillerlan\QRCode\Detector\QRCodeDetectorException if 3 such finder patterns do not exist
+ */
+ private function selectBestPatterns():array{
+ $startSize = count($this->possibleCenters);
+
+ if($startSize < 3){
+ throw new QRCodeDetectorException('could not find enough finder patterns');
+ }
+
+ usort(
+ $this->possibleCenters,
+ fn(FinderPattern $a, FinderPattern $b) => ($a->getEstimatedModuleSize() <=> $b->getEstimatedModuleSize())
+ );
+
+ $distortion = PHP_FLOAT_MAX;
+ $bestPatterns = [];
+
+ for($i = 0; $i < ($startSize - 2); $i++){
+ $fpi = $this->possibleCenters[$i];
+ $minModuleSize = $fpi->getEstimatedModuleSize();
+
+ for($j = ($i + 1); $j < ($startSize - 1); $j++){
+ $fpj = $this->possibleCenters[$j];
+ $squares0 = $fpi->getSquaredDistance($fpj);
+
+ for($k = ($j + 1); $k < $startSize; $k++){
+ $fpk = $this->possibleCenters[$k];
+ $maxModuleSize = $fpk->getEstimatedModuleSize();
+
+ // module size is not similar
+ if($maxModuleSize > ($minModuleSize * 1.4)){
+ continue;
+ }
+
+ $a = $squares0;
+ $b = $fpj->getSquaredDistance($fpk);
+ $c = $fpi->getSquaredDistance($fpk);
+
+ // sorts ascending - inlined
+ if($a < $b){
+ if($b > $c){
+ if($a < $c){
+ $temp = $b;
+ $b = $c;
+ $c = $temp;
+ }
+ else{
+ $temp = $a;
+ $a = $c;
+ $c = $b;
+ $b = $temp;
+ }
+ }
+ }
+ else{
+ if($b < $c){
+ if($a < $c){
+ $temp = $a;
+ $a = $b;
+ $b = $temp;
+ }
+ else{
+ $temp = $a;
+ $a = $b;
+ $b = $c;
+ $c = $temp;
+ }
+ }
+ else{
+ $temp = $a;
+ $a = $c;
+ $c = $temp;
+ }
+ }
+
+ // a^2 + b^2 = c^2 (Pythagorean theorem), and a = b (isosceles triangle).
+ // Since any right triangle satisfies the formula c^2 - b^2 - a^2 = 0,
+ // we need to check both two equal sides separately.
+ // The value of |c^2 - 2 * b^2| + |c^2 - 2 * a^2| increases as dissimilarity
+ // from isosceles right triangle.
+ $d = (abs($c - 2 * $b) + abs($c - 2 * $a));
+
+ if($d < $distortion){
+ $distortion = $d;
+ $bestPatterns = [$fpi, $fpj, $fpk];
+ }
+ }
+ }
+ }
+
+ if($distortion === PHP_FLOAT_MAX){
+ throw new QRCodeDetectorException('finder patterns may be too distorted');
+ }
+
+ return $bestPatterns;
+ }
+
+ /**
+ * Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC
+ * and BC is less than AC, and the angle between BC and BA is less than 180 degrees.
+ *
+ * @param \chillerlan\QRCode\Detector\FinderPattern[] $patterns array of three FinderPattern to order
+ *
+ * @return \chillerlan\QRCode\Detector\FinderPattern[]
+ */
+ private function orderBestPatterns(array $patterns):array{
+
+ // Find distances between pattern centers
+ $zeroOneDistance = $patterns[0]->getDistance($patterns[1]);
+ $oneTwoDistance = $patterns[1]->getDistance($patterns[2]);
+ $zeroTwoDistance = $patterns[0]->getDistance($patterns[2]);
+
+ // Assume one closest to other two is B; A and C will just be guesses at first
+ if($oneTwoDistance >= $zeroOneDistance && $oneTwoDistance >= $zeroTwoDistance){
+ [$pointB, $pointA, $pointC] = $patterns;
+ }
+ elseif($zeroTwoDistance >= $oneTwoDistance && $zeroTwoDistance >= $zeroOneDistance){
+ [$pointA, $pointB, $pointC] = $patterns;
+ }
+ else{
+ [$pointA, $pointC, $pointB] = $patterns;
+ }
+
+ // Use cross product to figure out whether A and C are correct or flipped.
+ // This asks whether BC x BA has a positive z component, which is the arrangement
+ // we want for A, B, C. If it's negative, then we've got it flipped around and
+ // should swap A and C.
+ if($this->crossProductZ($pointA, $pointB, $pointC) < 0.0){
+ $temp = $pointA;
+ $pointA = $pointC;
+ $pointC = $temp;
+ }
+
+ return [$pointA, $pointB, $pointC];
+ }
+
+ /**
+ * Returns the z component of the cross product between vectors BC and BA.
+ */
+ private function crossProductZ(FinderPattern $pointA, FinderPattern $pointB, FinderPattern $pointC):float{
+ $bX = $pointB->getX();
+ $bY = $pointB->getY();
+
+ return ((($pointC->getX() - $bX) * ($pointA->getY() - $bY)) - (($pointC->getY() - $bY) * ($pointA->getX() - $bX)));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Detector/GridSampler.php b/vendor/chillerlan/php-qrcode/src/Detector/GridSampler.php
new file mode 100644
index 0000000..0d915e3
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Detector/GridSampler.php
@@ -0,0 +1,181 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Detector;
+
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Decoder\BitMatrix;
+use function array_fill, count, intdiv, sprintf;
+
+/**
+ * Implementations of this class can, given locations of finder patterns for a QR code in an
+ * image, sample the right points in the image to reconstruct the QR code, accounting for
+ * perspective distortion. It is abstracted since it is relatively expensive and should be allowed
+ * to take advantage of platform-specific optimized implementations, like Sun's Java Advanced
+ * Imaging library, but which may not be available in other environments such as J2ME, and vice
+ * versa.
+ *
+ * The implementation used can be controlled by calling #setGridSampler(GridSampler)
+ * with an instance of a class which implements this interface.
+ *
+ * @author Sean Owen
+ */
+final class GridSampler{
+
+ private array $points;
+
+ /**
+ * Checks a set of points that have been transformed to sample points on an image against
+ * the image's dimensions to see if the point are even within the image.
+ *
+ * This method will actually "nudge" the endpoints back onto the image if they are found to be
+ * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
+ * patterns in an image where the QR Code runs all the way to the image border.
+ *
+ * For efficiency, the method will check points from either end of the line until one is found
+ * to be within the image. Because the set of points are assumed to be linear, this is valid.
+ *
+ * @param int $dimension matrix width/height
+ *
+ * @throws \chillerlan\QRCode\Detector\QRCodeDetectorException if an endpoint is lies outside the image boundaries
+ */
+ private function checkAndNudgePoints(int $dimension):void{
+ $nudged = true;
+ $max = count($this->points);
+
+ // Check and nudge points from start until we see some that are OK:
+ for($offset = 0; $offset < $max && $nudged; $offset += 2){
+ $x = (int)$this->points[$offset];
+ $y = (int)$this->points[($offset + 1)];
+
+ if($x < -1 || $x > $dimension || $y < -1 || $y > $dimension){
+ throw new QRCodeDetectorException(sprintf('checkAndNudgePoints 1, x: %s, y: %s, d: %s', $x, $y, $dimension));
+ }
+
+ $nudged = false;
+
+ if($x === -1){
+ $this->points[$offset] = 0.0;
+ $nudged = true;
+ }
+ elseif($x === $dimension){
+ $this->points[$offset] = ($dimension - 1);
+ $nudged = true;
+ }
+
+ if($y === -1){
+ $this->points[($offset + 1)] = 0.0;
+ $nudged = true;
+ }
+ elseif($y === $dimension){
+ $this->points[($offset + 1)] = ($dimension - 1);
+ $nudged = true;
+ }
+
+ }
+
+ // Check and nudge points from end:
+ $nudged = true;
+
+ for($offset = ($max - 2); $offset >= 0 && $nudged; $offset -= 2){
+ $x = (int)$this->points[$offset];
+ $y = (int)$this->points[($offset + 1)];
+
+ if($x < -1 || $x > $dimension || $y < -1 || $y > $dimension){
+ throw new QRCodeDetectorException(sprintf('checkAndNudgePoints 2, x: %s, y: %s, d: %s', $x, $y, $dimension));
+ }
+
+ $nudged = false;
+
+ if($x === -1){
+ $this->points[$offset] = 0.0;
+ $nudged = true;
+ }
+ elseif($x === $dimension){
+ $this->points[$offset] = ($dimension - 1);
+ $nudged = true;
+ }
+
+ if($y === -1){
+ $this->points[($offset + 1)] = 0.0;
+ $nudged = true;
+ }
+ elseif($y === $dimension){
+ $this->points[($offset + 1)] = ($dimension - 1);
+ $nudged = true;
+ }
+
+ }
+
+ }
+
+ /**
+ * Samples an image for a rectangular matrix of bits of the given dimension. The sampling
+ * transformation is determined by the coordinates of 4 points, in the original and transformed
+ * image space.
+ *
+ * @return \chillerlan\QRCode\Decoder\BitMatrix representing a grid of points sampled from the image within a region
+ * defined by the "from" parameters
+ * @throws \chillerlan\QRCode\Detector\QRCodeDetectorException if image can't be sampled, for example, if the transformation defined
+ * by the given points is invalid or results in sampling outside the image boundaries
+ */
+ public function sampleGrid(BitMatrix $matrix, int $dimension, PerspectiveTransform $transform):BitMatrix{
+
+ if($dimension <= 0){
+ throw new QRCodeDetectorException('invalid matrix size');
+ }
+
+ $bits = new BitMatrix($dimension);
+ $this->points = array_fill(0, (2 * $dimension), 0.0);
+
+ for($y = 0; $y < $dimension; $y++){
+ $max = count($this->points);
+ $iValue = ($y + 0.5);
+
+ for($x = 0; $x < $max; $x += 2){
+ $this->points[$x] = (($x / 2) + 0.5);
+ $this->points[($x + 1)] = $iValue;
+ }
+ // phpcs:ignore
+ [$this->points, ] = $transform->transformPoints($this->points);
+ // Quick check to see if points transformed to something inside the image;
+ // sufficient to check the endpoints
+ $this->checkAndNudgePoints($matrix->getSize());
+
+ // no need to try/catch as QRMatrix::set() will silently discard out of bounds values
+# try{
+ for($x = 0; $x < $max; $x += 2){
+ // Black(-ish) pixel
+ $bits->set(
+ intdiv($x, 2),
+ $y,
+ $matrix->check((int)$this->points[$x], (int)$this->points[($x + 1)]),
+ QRMatrix::M_DATA
+ );
+ }
+# }
+# catch(\Throwable $aioobe){//ArrayIndexOutOfBoundsException
+ // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
+ // transform gets "twisted" such that it maps a straight line of points to a set of points
+ // whose endpoints are in bounds, but others are not. There is probably some mathematical
+ // way to detect this about the transformation that I don't know yet.
+ // This results in an ugly runtime exception despite our clever checks above -- can't have
+ // that. We could check each point's coordinates but that feels duplicative. We settle for
+ // catching and wrapping ArrayIndexOutOfBoundsException.
+# throw new QRCodeDetectorException('ArrayIndexOutOfBoundsException');
+# }
+
+ }
+
+ return $bits;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Detector/PerspectiveTransform.php b/vendor/chillerlan/php-qrcode/src/Detector/PerspectiveTransform.php
new file mode 100644
index 0000000..4f1f654
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Detector/PerspectiveTransform.php
@@ -0,0 +1,182 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Detector;
+
+use function count;
+
+/**
+ * This class implements a perspective transform in two dimensions. Given four source and four
+ * destination points, it will compute the transformation implied between them. The code is based
+ * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.
+ *
+ * @author Sean Owen
+ */
+final class PerspectiveTransform{
+
+ private float $a11;
+ private float $a12;
+ private float $a13;
+ private float $a21;
+ private float $a22;
+ private float $a23;
+ private float $a31;
+ private float $a32;
+ private float $a33;
+
+ /**
+ *
+ */
+ private function set(
+ float $a11, float $a21, float $a31,
+ float $a12, float $a22, float $a32,
+ float $a13, float $a23, float $a33
+ ):self{
+ $this->a11 = $a11;
+ $this->a12 = $a12;
+ $this->a13 = $a13;
+ $this->a21 = $a21;
+ $this->a22 = $a22;
+ $this->a23 = $a23;
+ $this->a31 = $a31;
+ $this->a32 = $a32;
+ $this->a33 = $a33;
+
+ return $this;
+ }
+
+ /**
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+ public function quadrilateralToQuadrilateral(
+ float $x0, float $y0, float $x1, float $y1, float $x2, float $y2, float $x3, float $y3,
+ float $x0p, float $y0p, float $x1p, float $y1p, float $x2p, float $y2p, float $x3p, float $y3p
+ ):self{
+ return (new self)
+ ->squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p)
+ ->times($this->quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3));
+ }
+
+ /**
+ *
+ */
+ private function quadrilateralToSquare(
+ float $x0, float $y0, float $x1, float $y1,
+ float $x2, float $y2, float $x3, float $y3
+ ):self{
+ // Here, the adjoint serves as the inverse:
+ return $this
+ ->squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
+ ->buildAdjoint();
+ }
+
+ /**
+ *
+ */
+ private function buildAdjoint():self{
+ // Adjoint is the transpose of the cofactor matrix:
+ return $this->set(
+ ($this->a22 * $this->a33 - $this->a23 * $this->a32),
+ ($this->a23 * $this->a31 - $this->a21 * $this->a33),
+ ($this->a21 * $this->a32 - $this->a22 * $this->a31),
+ ($this->a13 * $this->a32 - $this->a12 * $this->a33),
+ ($this->a11 * $this->a33 - $this->a13 * $this->a31),
+ ($this->a12 * $this->a31 - $this->a11 * $this->a32),
+ ($this->a12 * $this->a23 - $this->a13 * $this->a22),
+ ($this->a13 * $this->a21 - $this->a11 * $this->a23),
+ ($this->a11 * $this->a22 - $this->a12 * $this->a21)
+ );
+ }
+
+ /**
+ *
+ */
+ private function squareToQuadrilateral(
+ float $x0, float $y0, float $x1, float $y1,
+ float $x2, float $y2, float $x3, float $y3
+ ):self{
+ $dx3 = ($x0 - $x1 + $x2 - $x3);
+ $dy3 = ($y0 - $y1 + $y2 - $y3);
+
+ if($dx3 === 0.0 && $dy3 === 0.0){
+ // Affine
+ return $this->set(($x1 - $x0), ($x2 - $x1), $x0, ($y1 - $y0), ($y2 - $y1), $y0, 0.0, 0.0, 1.0);
+ }
+
+ $dx1 = ($x1 - $x2);
+ $dx2 = ($x3 - $x2);
+ $dy1 = ($y1 - $y2);
+ $dy2 = ($y3 - $y2);
+ $denominator = ($dx1 * $dy2 - $dx2 * $dy1);
+ $a13 = (($dx3 * $dy2 - $dx2 * $dy3) / $denominator);
+ $a23 = (($dx1 * $dy3 - $dx3 * $dy1) / $denominator);
+
+ return $this->set(
+ ($x1 - $x0 + $a13 * $x1),
+ ($x3 - $x0 + $a23 * $x3),
+ $x0,
+ ($y1 - $y0 + $a13 * $y1),
+ ($y3 - $y0 + $a23 * $y3),
+ $y0,
+ $a13,
+ $a23,
+ 1.0
+ );
+ }
+
+ /**
+ *
+ */
+ private function times(PerspectiveTransform $other):self{
+ return $this->set(
+ ($this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13),
+ ($this->a11 * $other->a21 + $this->a21 * $other->a22 + $this->a31 * $other->a23),
+ ($this->a11 * $other->a31 + $this->a21 * $other->a32 + $this->a31 * $other->a33),
+ ($this->a12 * $other->a11 + $this->a22 * $other->a12 + $this->a32 * $other->a13),
+ ($this->a12 * $other->a21 + $this->a22 * $other->a22 + $this->a32 * $other->a23),
+ ($this->a12 * $other->a31 + $this->a22 * $other->a32 + $this->a32 * $other->a33),
+ ($this->a13 * $other->a11 + $this->a23 * $other->a12 + $this->a33 * $other->a13),
+ ($this->a13 * $other->a21 + $this->a23 * $other->a22 + $this->a33 * $other->a23),
+ ($this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33)
+ );
+ }
+
+ /**
+ * @return array[] [$xValues, $yValues]
+ */
+ public function transformPoints(array $xValues, array $yValues = null):array{
+ $max = count($xValues);
+
+ if($yValues !== null){ // unused
+
+ for($i = 0; $i < $max; $i++){
+ $x = $xValues[$i];
+ $y = $yValues[$i];
+ $denominator = ($this->a13 * $x + $this->a23 * $y + $this->a33);
+ $xValues[$i] = (($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator);
+ $yValues[$i] = (($this->a12 * $x + $this->a22 * $y + $this->a32) / $denominator);
+ }
+
+ return [$xValues, $yValues];
+ }
+
+ for($i = 0; $i < $max; $i += 2){
+ $x = $xValues[$i];
+ $y = $xValues[($i + 1)];
+ $denominator = ($this->a13 * $x + $this->a23 * $y + $this->a33);
+ $xValues[$i] = (($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator);
+ $xValues[($i + 1)] = (($this->a12 * $x + $this->a22 * $y + $this->a32) / $denominator);
+ }
+
+ return [$xValues, []];
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Detector/QRCodeDetectorException.php b/vendor/chillerlan/php-qrcode/src/Detector/QRCodeDetectorException.php
new file mode 100644
index 0000000..2444e19
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Detector/QRCodeDetectorException.php
@@ -0,0 +1,20 @@
+
+ * @copyright 2021 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Detector;
+
+use chillerlan\QRCode\QRCodeException;
+
+/**
+ * An exception container
+ */
+final class QRCodeDetectorException extends QRCodeException{
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Detector/ResultPoint.php b/vendor/chillerlan/php-qrcode/src/Detector/ResultPoint.php
new file mode 100644
index 0000000..92997a7
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Detector/ResultPoint.php
@@ -0,0 +1,73 @@
+
+ * @copyright 2021 Smiley
+ * @license Apache-2.0
+ */
+
+namespace chillerlan\QRCode\Detector;
+
+use function abs;
+
+/**
+ * Encapsulates a point of interest in an image containing a barcode. Typically, this
+ * would be the location of a finder pattern or the corner of the barcode, for example.
+ *
+ * @author Sean Owen
+ */
+abstract class ResultPoint{
+
+ protected float $x;
+ protected float $y;
+ protected float $estimatedModuleSize;
+
+ /**
+ *
+ */
+ public function __construct(float $x, float $y, float $estimatedModuleSize){
+ $this->x = $x;
+ $this->y = $y;
+ $this->estimatedModuleSize = $estimatedModuleSize;
+ }
+
+ /**
+ *
+ */
+ public function getX():float{
+ return $this->x;
+ }
+
+ /**
+ *
+ */
+ public function getY():float{
+ return $this->y;
+ }
+
+ /**
+ *
+ */
+ public function getEstimatedModuleSize():float{
+ return $this->estimatedModuleSize;
+ }
+
+ /**
+ * Determines if this finder pattern "about equals" a finder pattern at the stated
+ * position and size -- meaning, it is at nearly the same center with nearly the same size.
+ */
+ public function aboutEquals(float $moduleSize, float $i, float $j):bool{
+
+ if(abs($i - $this->y) <= $moduleSize && abs($j - $this->x) <= $moduleSize){
+ $moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize);
+
+ return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize;
+ }
+
+ return false;
+ }
+
+}
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 0000000..bf30f1b
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php
@@ -0,0 +1,20 @@
+
+ * @copyright 2015 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use chillerlan\QRCode\QRCodeException;
+
+/**
+ * An exception container
+ */
+final class QRCodeOutputException extends QRCodeException{
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QREps.php b/vendor/chillerlan/php-qrcode/src/Output/QREps.php
new file mode 100644
index 0000000..a04f20d
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QREps.php
@@ -0,0 +1,173 @@
+
+ * @copyright 2022 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function array_values, count, date, implode, is_array, is_numeric, max, min, round, sprintf;
+
+/**
+ * Encapsulated Postscript (EPS) output
+ *
+ * @see https://github.com/t0k4rt/phpqrcode/blob/bb29e6eb77e0a2a85bb0eb62725e0adc11ff5a90/qrvect.php#L52-L137
+ * @see https://web.archive.org/web/20170818010030/http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/postscript/pdfs/5002.EPSF_Spec.pdf
+ * @see https://web.archive.org/web/20210419003859/https://www.adobe.com/content/dam/acom/en/devnet/actionscript/articles/PLRM.pdf
+ * @see https://github.com/chillerlan/php-qrcode/discussions/148
+ */
+class QREps extends QROutputAbstract{
+
+ public const MIME_TYPE = 'application/postscript';
+
+ /**
+ * @inheritDoc
+ */
+ public static function moduleValueIsValid($value):bool{
+
+ if(!is_array($value) || count($value) < 3){
+ return false;
+ }
+
+ // check the first values of the array
+ foreach(array_values($value) as $i => $val){
+
+ if($i > 3){
+ break;
+ }
+
+ if(!is_numeric($val)){
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $value
+ *
+ * @inheritDoc
+ */
+ protected function prepareModuleValue($value):string{
+ $values = [];
+
+ foreach(array_values($value) as $i => $val){
+
+ if($i > 3){
+ break;
+ }
+
+ // clamp value and convert from int 0-255 to float 0-1 RGB/CMYK range
+ $values[] = round((max(0, min(255, intval($val))) / 255), 6);
+ }
+
+ return $this->formatColor($values);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getDefaultModuleValue(bool $isDark):string{
+ return $this->formatColor(($isDark) ? [0.0, 0.0, 0.0] : [1.0, 1.0, 1.0]);
+ }
+
+ /**
+ * Set the color format string
+ *
+ * 4 values in the color array will be interpreted as CMYK, 3 as RGB
+ *
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ */
+ protected function formatColor(array $values):string{
+ $count = count($values);
+
+ if($count < 3){
+ throw new QRCodeOutputException('invalid color value');
+ }
+
+ $format = ($count === 4)
+ // CMYK
+ ? '%f %f %f %f C'
+ // RGB
+ :'%f %f %f R';
+
+ return sprintf($format, ...$values);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function dump(string $file = null):string{
+ [$width, $height] = $this->getOutputDimensions();
+
+ $eps = [
+ // main header
+ '%!PS-Adobe-3.0 EPSF-3.0',
+ '%%Creator: php-qrcode (https://github.com/chillerlan/php-qrcode)',
+ '%%Title: QR Code',
+ sprintf('%%%%CreationDate: %1$s', date('c')),
+ '%%DocumentData: Clean7Bit',
+ '%%LanguageLevel: 3',
+ sprintf('%%%%BoundingBox: 0 0 %s %s', $width, $height),
+ '%%EndComments',
+ // function definitions
+ '%%BeginProlog',
+ '/F { rectfill } def',
+ '/R { setrgbcolor } def',
+ '/C { setcmykcolor } def',
+ '%%EndProlog',
+ ];
+
+ if($this::moduleValueIsValid($this->options->bgColor)){
+ $eps[] = $this->prepareModuleValue($this->options->bgColor);
+ $eps[] = sprintf('0 0 %s %s F', $width, $height);
+ }
+
+ // create the path elements
+ $paths = $this->collectModules(fn(int $x, int $y, int $M_TYPE):string => $this->module($x, $y, $M_TYPE));
+
+ foreach($paths as $M_TYPE => $path){
+
+ if(empty($path)){
+ continue;
+ }
+
+ $eps[] = $this->getModuleValue($M_TYPE);
+ $eps[] = implode("\n", $path);
+ }
+
+ // end file
+ $eps[] = '%%EOF';
+
+ $data = implode("\n", $eps);
+
+ $this->saveToFile($data, $file);
+
+ return $data;
+ }
+
+ /**
+ * Returns a path segment for a single module
+ */
+ protected function module(int $x, int $y, int $M_TYPE):string{
+
+ if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
+ return '';
+ }
+
+ $outputX = ($x * $this->scale);
+ // Actual size - one block = Topmost y pos.
+ $top = ($this->length - $this->scale);
+ // Apparently y-axis is inverted (y0 is at bottom and not top) in EPS, so we have to switch the y-axis here
+ $outputY = ($top - ($y * $this->scale));
+
+ return sprintf('%d %d %d %d F', $outputX, $outputY, $this->scale, $this->scale);
+ }
+
+}
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 0000000..a802954
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php
@@ -0,0 +1,177 @@
+ $val){
+
+ if($i > 2){
+ break;
+ }
+
+ if(!is_numeric($val)){
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $value
+ *
+ * @inheritDoc
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ */
+ protected function prepareModuleValue($value):array{
+ $values = [];
+
+ foreach(array_values($value) as $i => $val){
+
+ if($i > 2){
+ break;
+ }
+
+ $values[] = max(0, min(255, intval($val)));
+ }
+
+ if(count($values) !== 3){
+ throw new QRCodeOutputException('invalid color value');
+ }
+
+ return $values;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getDefaultModuleValue(bool $isDark):array{
+ return ($isDark) ? [0, 0, 0] : [255, 255, 255];
+ }
+
+ /**
+ * Initializes an FPDF instance
+ */
+ protected function initFPDF():FPDF{
+ return new FPDF('P', $this->options->fpdfMeasureUnit, $this->getOutputDimensions());
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @return string|\FPDF
+ */
+ public function dump(string $file = null){
+ $this->fpdf = $this->initFPDF();
+ $this->fpdf->AddPage();
+
+ if($this::moduleValueIsValid($this->options->bgColor)){
+ $bgColor = $this->prepareModuleValue($this->options->bgColor);
+ [$width, $height] = $this->getOutputDimensions();
+
+ /** @phan-suppress-next-line PhanParamTooFewUnpack */
+ $this->fpdf->SetFillColor(...$bgColor);
+ $this->fpdf->Rect(0, 0, $width, $height, 'F');
+ }
+
+ $this->prevColor = null;
+
+ foreach($this->matrix->getMatrix() as $y => $row){
+ foreach($row as $x => $M_TYPE){
+ $this->module($x, $y, $M_TYPE);
+ }
+ }
+
+ if($this->options->returnResource){
+ return $this->fpdf;
+ }
+
+ $pdfData = $this->fpdf->Output('S');
+
+ $this->saveToFile($pdfData, $file);
+
+ if($this->options->outputBase64){
+ $pdfData = $this->toBase64DataURI($pdfData);
+ }
+
+ return $pdfData;
+ }
+
+ /**
+ * Renders a single module
+ */
+ protected function module(int $x, int $y, int $M_TYPE):void{
+
+ if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
+ return;
+ }
+
+ $color = $this->getModuleValue($M_TYPE);
+
+ if($color !== null && $color !== $this->prevColor){
+ /** @phan-suppress-next-line PhanParamTooFewUnpack */
+ $this->fpdf->SetFillColor(...$color);
+ $this->prevColor = $color;
+ }
+
+ $this->fpdf->Rect(($x * $this->scale), ($y * $this->scale), $this->scale, $this->scale, 'F');
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImage.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImage.php
new file mode 100644
index 0000000..f3ed6ea
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImage.php
@@ -0,0 +1,400 @@
+
+ * @copyright 2015 Smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\Settings\SettingsContainerInterface;
+use ErrorException;
+use Throwable;
+use function array_values, count, extension_loaded, imagebmp, imagecolorallocate, imagecolortransparent,
+ imagecreatetruecolor, imagedestroy, imagefilledellipse, imagefilledrectangle, imagegif, imagejpeg, imagepng,
+ imagescale, imagetypes, imagewebp, intdiv, intval, is_array, is_numeric, max, min, ob_end_clean, ob_get_contents, ob_start,
+ restore_error_handler, set_error_handler, sprintf;
+use const IMG_BMP, IMG_GIF, IMG_JPG, IMG_PNG, IMG_WEBP;
+
+/**
+ * Converts the matrix into GD images, raw or base64 output (requires ext-gd)
+ *
+ * @see https://php.net/manual/book.image.php
+ *
+ * @deprecated 5.0.0 this class will be made abstract in future versions,
+ * calling it directly is deprecated - use one of the child classes instead
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+class QRGdImage extends QROutputAbstract{
+
+ /**
+ * The GD image resource
+ *
+ * @see imagecreatetruecolor()
+ * @var resource|\GdImage
+ *
+ * @todo: add \GdImage type in v6
+ */
+ protected $image;
+
+ /**
+ * The allocated background color
+ *
+ * @see \imagecolorallocate()
+ */
+ protected int $background;
+
+ /**
+ * Whether we're running in upscale mode (scale < 20)
+ *
+ * @see \chillerlan\QRCode\QROptions::$drawCircularModules
+ */
+ protected bool $upscaled = false;
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ * @noinspection PhpMissingParentConstructorInspection
+ */
+ public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
+ $this->options = $options;
+ $this->matrix = $matrix;
+
+ $this->checkGD();
+
+ if($this->options->invertMatrix){
+ $this->matrix->invert();
+ }
+
+ $this->copyVars();
+ $this->setMatrixDimensions();
+ }
+
+ /**
+ * Checks whether GD is installed and if the given mode is supported
+ *
+ * @return void
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ * @codeCoverageIgnore
+ */
+ protected function checkGD():void{
+
+ if(!extension_loaded('gd')){
+ throw new QRCodeOutputException('ext-gd not loaded');
+ }
+
+ $modes = [
+ self::GDIMAGE_BMP => IMG_BMP,
+ self::GDIMAGE_GIF => IMG_GIF,
+ self::GDIMAGE_JPG => IMG_JPG,
+ self::GDIMAGE_PNG => IMG_PNG,
+ self::GDIMAGE_WEBP => IMG_WEBP,
+ ];
+
+ // likely using default or custom output
+ if(!isset($modes[$this->options->outputType])){
+ return;
+ }
+
+ $mode = $modes[$this->options->outputType];
+
+ if((imagetypes() & $mode) !== $mode){
+ throw new QRCodeOutputException(sprintf('output mode "%s" not supported', $this->options->outputType));
+ }
+
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function moduleValueIsValid($value):bool{
+
+ if(!is_array($value) || count($value) < 3){
+ return false;
+ }
+
+ // check the first 3 values of the array
+ foreach(array_values($value) as $i => $val){
+
+ if($i > 2){
+ break;
+ }
+
+ if(!is_numeric($val)){
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $value
+ *
+ * @inheritDoc
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ */
+ protected function prepareModuleValue($value):int{
+ $values = [];
+
+ foreach(array_values($value) as $i => $val){
+
+ if($i > 2){
+ break;
+ }
+
+ $values[] = max(0, min(255, intval($val)));
+ }
+
+ /** @phan-suppress-next-line PhanParamTooFewInternalUnpack */
+ $color = imagecolorallocate($this->image, ...$values);
+
+ if($color === false){
+ throw new QRCodeOutputException('could not set color: imagecolorallocate() error');
+ }
+
+ return $color;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getDefaultModuleValue(bool $isDark):int{
+ return $this->prepareModuleValue(($isDark) ? [0, 0, 0] : [255, 255, 255]);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @return string|resource|\GdImage
+ *
+ * @phan-suppress PhanUndeclaredTypeReturnType, PhanTypeMismatchReturn
+ * @throws \ErrorException
+ */
+ public function dump(string $file = null){
+
+ set_error_handler(function(int $errno, string $errstr):bool{
+ throw new ErrorException($errstr, $errno);
+ });
+
+ $this->image = $this->createImage();
+ // set module values after image creation because we need the GdImage instance
+ $this->setModuleValues();
+ $this->setBgColor();
+
+ imagefilledrectangle($this->image, 0, 0, $this->length, $this->length, $this->background);
+
+ $this->drawImage();
+
+ if($this->upscaled){
+ // scale down to the expected size
+ $this->image = imagescale($this->image, ($this->length / 10), ($this->length / 10));
+ $this->upscaled = false;
+ }
+
+ // set transparency after scaling, otherwise it would be undone
+ // @see https://www.php.net/manual/en/function.imagecolortransparent.php#77099
+ $this->setTransparencyColor();
+
+ if($this->options->returnResource){
+ restore_error_handler();
+
+ return $this->image;
+ }
+
+ $imageData = $this->dumpImage();
+
+ $this->saveToFile($imageData, $file);
+
+ if($this->options->outputBase64){
+ // @todo: remove mime parameter in v6
+ $imageData = $this->toBase64DataURI($imageData, 'image/'.$this->options->outputType);
+ }
+
+ restore_error_handler();
+
+ return $imageData;
+ }
+
+ /**
+ * Creates a new GdImage resource and scales it if necessary
+ *
+ * we're scaling the image up in order to draw crisp round circles, otherwise they appear square-y on small scales
+ *
+ * @see https://github.com/chillerlan/php-qrcode/issues/23
+ *
+ * @return \GdImage|resource
+ */
+ protected function createImage(){
+
+ if($this->drawCircularModules && $this->options->gdImageUseUpscale && $this->options->scale < 20){
+ // increase the initial image size by 10
+ $this->length *= 10;
+ $this->scale *= 10;
+ $this->upscaled = true;
+ }
+
+ return imagecreatetruecolor($this->length, $this->length);
+ }
+
+ /**
+ * Sets the background color
+ */
+ protected function setBgColor():void{
+
+ if(isset($this->background)){
+ return;
+ }
+
+ if($this::moduleValueIsValid($this->options->bgColor)){
+ $this->background = $this->prepareModuleValue($this->options->bgColor);
+
+ return;
+ }
+
+ $this->background = $this->prepareModuleValue([255, 255, 255]);
+ }
+
+ /**
+ * Sets the transparency color
+ */
+ protected function setTransparencyColor():void{
+
+ // @todo: the jpg skip can be removed in v6
+ if($this->options->outputType === QROutputInterface::GDIMAGE_JPG || !$this->options->imageTransparent){
+ return;
+ }
+
+ $transparencyColor = $this->background;
+
+ if($this::moduleValueIsValid($this->options->transparencyColor)){
+ $transparencyColor = $this->prepareModuleValue($this->options->transparencyColor);
+ }
+
+ imagecolortransparent($this->image, $transparencyColor);
+ }
+
+ /**
+ * Draws the QR image
+ */
+ protected function drawImage():void{
+ foreach($this->matrix->getMatrix() as $y => $row){
+ foreach($row as $x => $M_TYPE){
+ $this->module($x, $y, $M_TYPE);
+ }
+ }
+ }
+
+ /**
+ * Creates a single QR pixel with the given settings
+ */
+ protected function module(int $x, int $y, int $M_TYPE):void{
+
+ if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
+ return;
+ }
+
+ $color = $this->getModuleValue($M_TYPE);
+
+ if($this->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->keepAsSquare)){
+ imagefilledellipse(
+ $this->image,
+ (($x * $this->scale) + intdiv($this->scale, 2)),
+ (($y * $this->scale) + intdiv($this->scale, 2)),
+ (int)($this->circleDiameter * $this->scale),
+ (int)($this->circleDiameter * $this->scale),
+ $color
+ );
+
+ return;
+ }
+
+ imagefilledrectangle(
+ $this->image,
+ ($x * $this->scale),
+ ($y * $this->scale),
+ (($x + 1) * $this->scale),
+ (($y + 1) * $this->scale),
+ $color
+ );
+ }
+
+ /**
+ * Renders the image with the gdimage function for the desired output
+ *
+ * @see \imagebmp()
+ * @see \imagegif()
+ * @see \imagejpeg()
+ * @see \imagepng()
+ * @see \imagewebp()
+ *
+ * @todo: v6.0: make abstract and call from child classes
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ * @codeCoverageIgnore
+ */
+ protected function renderImage():void{
+
+ switch($this->options->outputType){
+ case QROutputInterface::GDIMAGE_BMP:
+ imagebmp($this->image, null, ($this->options->quality > 0));
+ break;
+ case QROutputInterface::GDIMAGE_GIF:
+ imagegif($this->image);
+ break;
+ case QROutputInterface::GDIMAGE_JPG:
+ imagejpeg($this->image, null, max(-1, min(100, $this->options->quality)));
+ break;
+ case QROutputInterface::GDIMAGE_WEBP:
+ imagewebp($this->image, null, max(-1, min(100, $this->options->quality)));
+ break;
+ // silently default to png output
+ case QROutputInterface::GDIMAGE_PNG:
+ default:
+ imagepng($this->image, null, max(-1, min(9, $this->options->quality)));
+ }
+
+ }
+
+ /**
+ * Creates the final image by calling the desired GD output function
+ *
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ */
+ protected function dumpImage():string{
+ $exception = null;
+ $imageData = null;
+
+ ob_start();
+
+ try{
+ $this->renderImage();
+
+ $imageData = ob_get_contents();
+ imagedestroy($this->image);
+ }
+ // not going to cover edge cases
+ // @codeCoverageIgnoreStart
+ catch(Throwable $e){
+ $exception = $e;
+ }
+ // @codeCoverageIgnoreEnd
+
+ ob_end_clean();
+
+ // throw here in case an exception happened within the output buffer
+ if($exception instanceof Throwable){
+ throw new QRCodeOutputException($exception->getMessage());
+ }
+
+ return $imageData;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImageBMP.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageBMP.php
new file mode 100644
index 0000000..268ebe7
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageBMP.php
@@ -0,0 +1,33 @@
+
+ * @copyright 2023 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function imagebmp;
+
+/**
+ * GdImage bmp output
+ *
+ * @see \imagebmp()
+ */
+class QRGdImageBMP extends QRGdImage{
+
+ public const MIME_TYPE = 'image/bmp';
+
+ /**
+ * @inheritDoc
+ */
+ protected function renderImage():void{
+ imagebmp($this->image, null, ($this->options->quality > 0));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImageGIF.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageGIF.php
new file mode 100644
index 0000000..a021309
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageGIF.php
@@ -0,0 +1,33 @@
+
+ * @copyright 2023 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function imagegif;
+
+/**
+ * GdImage gif output
+ *
+ * @see \imagegif()
+ */
+class QRGdImageGIF extends QRGdImage{
+
+ public const MIME_TYPE = 'image/gif';
+
+ /**
+ * @inheritDoc
+ */
+ protected function renderImage():void{
+ imagegif($this->image);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImageJPEG.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageJPEG.php
new file mode 100644
index 0000000..6be36e2
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageJPEG.php
@@ -0,0 +1,40 @@
+
+ * @copyright 2023 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function imagejpeg, max, min;
+
+/**
+ * GdImage jpeg output
+ *
+ * @see \imagejpeg()
+ */
+class QRGdImageJPEG extends QRGdImage{
+
+ public const MIME_TYPE = 'image/jpg';
+
+ /**
+ * @inheritDoc
+ */
+ protected function setTransparencyColor():void{
+ // noop - transparency is not supported
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function renderImage():void{
+ imagejpeg($this->image, null, max(-1, min(100, $this->options->quality)));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImagePNG.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImagePNG.php
new file mode 100644
index 0000000..2db3fd5
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImagePNG.php
@@ -0,0 +1,33 @@
+
+ * @copyright 2023 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function imagepng, max, min;
+
+/**
+ * GdImage png output
+ *
+ * @see \imagepng()
+ */
+class QRGdImagePNG extends QRGdImage{
+
+ public const MIME_TYPE = 'image/png';
+
+ /**
+ * @inheritDoc
+ */
+ protected function renderImage():void{
+ imagepng($this->image, null, max(-1, min(9, $this->options->quality)));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImageWEBP.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageWEBP.php
new file mode 100644
index 0000000..cf8dfa9
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageWEBP.php
@@ -0,0 +1,33 @@
+
+ * @copyright 2023 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function imagewebp, max, min;
+
+/**
+ * GdImage webp output
+ *
+ * @see \imagewebp()
+ */
+class QRGdImageWEBP extends QRGdImage{
+
+ public const MIME_TYPE = 'image/webp';
+
+ /**
+ * @inheritDoc
+ */
+ protected function renderImage():void{
+ imagewebp($this->image, null, max(-1, min(100, $this->options->quality)));
+ }
+
+}
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 0000000..cda496d
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRImage.php
@@ -0,0 +1,19 @@
+
+ * @copyright 2021 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Output;
+
+/**
+ * @deprecated 5.0.0 backward compatibility, use QRGdImage instead
+ * @see \chillerlan\QRCode\Output\QRGdImage
+ */
+class QRImage extends QRGdImage{
+
+}
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 0000000..cfd3a58
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRImagick.php
@@ -0,0 +1,235 @@
+
+ * @copyright 2018 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\Settings\SettingsContainerInterface;
+use finfo, Imagick, ImagickDraw, ImagickPixel;
+use function extension_loaded, in_array, is_string, max, min, preg_match, strlen;
+use const FILEINFO_MIME_TYPE;
+
+/**
+ * ImageMagick output module (requires ext-imagick)
+ *
+ * @see https://php.net/manual/book.imagick.php
+ * @see https://phpimagick.com
+ */
+class QRImagick extends QROutputAbstract{
+
+ /**
+ * The main image instance
+ */
+ protected Imagick $imagick;
+
+ /**
+ * The main draw instance
+ */
+ protected ImagickDraw $imagickDraw;
+
+ /**
+ * The allocated background color
+ */
+ protected ImagickPixel $backgroundColor;
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ */
+ public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
+
+ foreach(['fileinfo', 'imagick'] as $ext){
+ if(!extension_loaded($ext)){
+ throw new QRCodeOutputException(sprintf('ext-%s not loaded', $ext)); // @codeCoverageIgnore
+ }
+ }
+
+ parent::__construct($options, $matrix);
+ }
+
+ /**
+ * note: we're not necessarily validating the several values, just checking the general syntax
+ *
+ * @see https://www.php.net/manual/imagickpixel.construct.php
+ * @inheritDoc
+ */
+ public static function moduleValueIsValid($value):bool{
+
+ if(!is_string($value)){
+ return false;
+ }
+
+ $value = trim($value);
+
+ // hex notation
+ // #rgb(a)
+ // #rrggbb(aa)
+ // #rrrrggggbbbb(aaaa)
+ // ...
+ if(preg_match('/^#[a-f\d]+$/i', $value) && in_array((strlen($value) - 1), [3, 4, 6, 8, 9, 12, 16, 24, 32], true)){
+ return true;
+ }
+
+ // css (-like) func(...values)
+ if(preg_match('#^(graya?|hs(b|la?)|rgba?)\([\d .,%]+\)$#i', $value)){
+ return true;
+ }
+
+ // predefined css color
+ if(preg_match('/^[a-z]+$/i', $value)){
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function prepareModuleValue($value):ImagickPixel{
+ return new ImagickPixel($value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getDefaultModuleValue(bool $isDark):ImagickPixel{
+ return $this->prepareModuleValue(($isDark) ? '#000' : '#fff');
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @return string|\Imagick
+ */
+ public function dump(string $file = null){
+ $this->setBgColor();
+
+ $this->imagick = $this->createImage();
+
+ $this->drawImage();
+ // set transparency color after all operations
+ $this->setTransparencyColor();
+
+ if($this->options->returnResource){
+ return $this->imagick;
+ }
+
+ $imageData = $this->imagick->getImageBlob();
+
+ $this->imagick->destroy();
+
+ $this->saveToFile($imageData, $file);
+
+ if($this->options->outputBase64){
+ $imageData = $this->toBase64DataURI($imageData, (new finfo(FILEINFO_MIME_TYPE))->buffer($imageData));
+ }
+
+ return $imageData;
+ }
+
+ /**
+ * Sets the background color
+ */
+ protected function setBgColor():void{
+
+ if($this::moduleValueIsValid($this->options->bgColor)){
+ $this->backgroundColor = $this->prepareModuleValue($this->options->bgColor);
+
+ return;
+ }
+
+ $this->backgroundColor = $this->prepareModuleValue('white');
+ }
+
+ /**
+ * Creates a new Imagick instance
+ */
+ protected function createImage():Imagick{
+ $imagick = new Imagick;
+ [$width, $height] = $this->getOutputDimensions();
+
+ $imagick->newImage($width, $height, $this->backgroundColor, $this->options->imagickFormat);
+
+ if($this->options->quality > -1){
+ $imagick->setImageCompressionQuality(max(0, min(100, $this->options->quality)));
+ }
+
+ return $imagick;
+ }
+
+ /**
+ * Sets the transparency color
+ */
+ protected function setTransparencyColor():void{
+
+ if(!$this->options->imageTransparent){
+ return;
+ }
+
+ $transparencyColor = $this->backgroundColor;
+
+ if($this::moduleValueIsValid($this->options->transparencyColor)){
+ $transparencyColor = $this->prepareModuleValue($this->options->transparencyColor);
+ }
+
+ $this->imagick->transparentPaintImage($transparencyColor, 0.0, 10, false);
+ }
+
+ /**
+ * Creates the QR image via ImagickDraw
+ */
+ protected function drawImage():void{
+ $this->imagickDraw = new ImagickDraw;
+ $this->imagickDraw->setStrokeWidth(0);
+
+ foreach($this->matrix->getMatrix() as $y => $row){
+ foreach($row as $x => $M_TYPE){
+ $this->module($x, $y, $M_TYPE);
+ }
+ }
+
+ $this->imagick->drawImage($this->imagickDraw);
+ }
+
+ /**
+ * draws a single pixel at the given position
+ */
+ protected function module(int $x, int $y, int $M_TYPE):void{
+
+ if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
+ return;
+ }
+
+ $this->imagickDraw->setFillColor($this->getModuleValue($M_TYPE));
+
+ if($this->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->keepAsSquare)){
+ $this->imagickDraw->circle(
+ (($x + 0.5) * $this->scale),
+ (($y + 0.5) * $this->scale),
+ (($x + 0.5 + $this->circleRadius) * $this->scale),
+ (($y + 0.5) * $this->scale)
+ );
+
+ return;
+ }
+
+ $this->imagickDraw->rectangle(
+ ($x * $this->scale),
+ ($y * $this->scale),
+ ((($x + 1) * $this->scale) - 1),
+ ((($y + 1) * $this->scale) - 1)
+ );
+ }
+
+}
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 0000000..22551e3
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php
@@ -0,0 +1,94 @@
+
+ * @copyright 2016 Smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function is_string, preg_match, strip_tags, trim;
+
+/**
+ * Abstract for markup types: HTML, SVG, ... XML anyone?
+ */
+abstract class QRMarkup extends QROutputAbstract{
+
+ /**
+ * note: we're not necessarily validating the several values, just checking the general syntax
+ * note: css4 colors are not included
+ *
+ * @todo: XSS proof
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
+ * @inheritDoc
+ */
+ public static function moduleValueIsValid($value):bool{
+
+ if(!is_string($value)){
+ return false;
+ }
+
+ $value = trim(strip_tags($value), " '\"\r\n\t");
+
+ // hex notation
+ // #rgb(a)
+ // #rrggbb(aa)
+ if(preg_match('/^#([\da-f]{3}){1,2}$|^#([\da-f]{4}){1,2}$/i', $value)){
+ return true;
+ }
+
+ // css: hsla/rgba(...values)
+ if(preg_match('#^(hsla?|rgba?)\([\d .,%/]+\)$#i', $value)){
+ return true;
+ }
+
+ // predefined css color
+ if(preg_match('/^[a-z]+$/i', $value)){
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function prepareModuleValue($value):string{
+ return trim(strip_tags($value), " '\"\r\n\t");
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getDefaultModuleValue(bool $isDark):string{
+ return ($isDark) ? '#000' : '#fff';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function dump(string $file = null):string{
+ $data = $this->createMarkup($file !== null);
+
+ $this->saveToFile($data, $file);
+
+ return $data;
+ }
+
+ /**
+ * returns a string with all css classes for the current element
+ */
+ protected function getCssClass(int $M_TYPE = 0):string{
+ return $this->options->cssClass;
+ }
+
+ /**
+ * returns the fully parsed and rendered markup string for the given input
+ */
+ abstract protected function createMarkup(bool $saveToFile):string;
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRMarkupHTML.php b/vendor/chillerlan/php-qrcode/src/Output/QRMarkupHTML.php
new file mode 100644
index 0000000..65dc49a
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRMarkupHTML.php
@@ -0,0 +1,51 @@
+
+ * @copyright 2022 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function implode, sprintf;
+
+/**
+ * HTML output (a cheap markup substitute when SVG is not available or not an option)
+ */
+class QRMarkupHTML extends QRMarkup{
+
+ public const MIME_TYPE = 'text/html';
+
+ /**
+ * @inheritDoc
+ */
+ protected function createMarkup(bool $saveToFile):string{
+ $rows = [];
+ $cssClass = $this->getCssClass();
+
+ foreach($this->matrix->getMatrix() as $row){
+ $element = '';
+ $modules = array_map(fn(int $M_TYPE):string => sprintf($element, $this->getModuleValue($M_TYPE)), $row);
+
+ $rows[] = sprintf('