Update dependencies
This commit is contained in:
parent
4f030b05af
commit
c42ebb8062
|
|
@ -34,5 +34,11 @@
|
|||
"allow-plugins": {
|
||||
"ocramius/package-versions": true
|
||||
}
|
||||
},
|
||||
"archive": {
|
||||
"exclude": [
|
||||
"/test",
|
||||
"/phpunit.xml.dist"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true">
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="BaconQrCode Tests">
|
||||
<directory>./test</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
|
|
@ -89,6 +89,9 @@ final class CharacterSetEci extends AbstractEnum
|
|||
*/
|
||||
private static $nameToEci;
|
||||
|
||||
/**
|
||||
* @param int[] $values
|
||||
*/
|
||||
public function __construct(array $values, string ...$otherEncodingNames)
|
||||
{
|
||||
$this->values = $values;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class FormatInformation
|
|||
/**
|
||||
* Offset i holds the number of 1 bits in the binary representation of i.
|
||||
*
|
||||
* @var array
|
||||
* @var int[]
|
||||
*/
|
||||
private const BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ final class Mode extends AbstractEnum
|
|||
*/
|
||||
private $bits;
|
||||
|
||||
/**
|
||||
* @param int[] $characterCountBitsForVersions
|
||||
*/
|
||||
protected function __construct(array $characterCountBitsForVersions, int $bits)
|
||||
{
|
||||
$this->characterCountBitsForVersions = $characterCountBitsForVersions;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ final class Encoder
|
|||
/**
|
||||
* Codec cache.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string,ReedSolomonCodec>
|
||||
*/
|
||||
private static $codecs = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ final class SvgImageBackEnd implements ImageBackEndInterface
|
|||
$this->xmlWriter->writeAttribute('stop-color', $this->getColorString($startColor));
|
||||
|
||||
if ($startColor instanceof Alpha) {
|
||||
$this->xmlWriter->writeAttribute('stop-opacity', $startColor->getAlpha());
|
||||
$this->xmlWriter->writeAttribute('stop-opacity', (string) $startColor->getAlpha());
|
||||
}
|
||||
|
||||
$this->xmlWriter->endElement();
|
||||
|
|
@ -344,7 +344,7 @@ final class SvgImageBackEnd implements ImageBackEndInterface
|
|||
$this->xmlWriter->writeAttribute('stop-color', $this->getColorString($endColor));
|
||||
|
||||
if ($endColor instanceof Alpha) {
|
||||
$this->xmlWriter->writeAttribute('stop-opacity', $endColor->getAlpha());
|
||||
$this->xmlWriter->writeAttribute('stop-opacity', (string) $endColor->getAlpha());
|
||||
}
|
||||
|
||||
$this->xmlWriter->endElement();
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ final class EdgeIterator implements IteratorAggregate
|
|||
}
|
||||
|
||||
/**
|
||||
* @return Edge[]
|
||||
* @return Traversable<Edge>
|
||||
*/
|
||||
public function getIterator() : Traversable
|
||||
{
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ final class EllipticArc implements OperationInterface
|
|||
/**
|
||||
* @return Curve[]
|
||||
*/
|
||||
private function createCurves(float $fromX, $fromY) : array
|
||||
private function createCurves(float $fromX, float $fromY) : array
|
||||
{
|
||||
$xAngle = deg2rad($this->xAxisAngle);
|
||||
list($centerX, $centerY, $radiusX, $radiusY, $startAngle, $deltaAngle) =
|
||||
|
|
|
|||
|
|
@ -17,10 +17,16 @@ use BaconQrCode\Writer;
|
|||
use PHPUnit\Framework\TestCase;
|
||||
use Spatie\Snapshots\MatchesSnapshots;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
final class ImagickRenderingTest extends TestCase
|
||||
{
|
||||
use MatchesSnapshots;
|
||||
|
||||
/**
|
||||
* @requires extension imagick
|
||||
*/
|
||||
public function testGenericQrCode() : void
|
||||
{
|
||||
$renderer = new ImageRenderer(
|
||||
|
|
@ -35,6 +41,9 @@ final class ImagickRenderingTest extends TestCase
|
|||
unlink($tempName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires extension imagick
|
||||
*/
|
||||
public function testIssue79() : void
|
||||
{
|
||||
$eye = SquareEye::instance();
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ return array(
|
|||
'NumberFormatter' => $vendorDir . '/symfony/polyfill-intl-icu/Resources/stubs/NumberFormatter.php',
|
||||
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||
'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php',
|
||||
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||
'Stringable' => $vendorDir . '/myclabs/php-enum/stubs/Stringable.php',
|
||||
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ class ComposerStaticInit2d2f03c80bad397451a8de89740becf0
|
|||
'NumberFormatter' => __DIR__ . '/..' . '/symfony/polyfill-intl-icu/Resources/stubs/NumberFormatter.php',
|
||||
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||
'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php',
|
||||
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||
'Stringable' => __DIR__ . '/..' . '/myclabs/php-enum/stubs/Stringable.php',
|
||||
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,17 +2,17 @@
|
|||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
"version": "2.0.7",
|
||||
"version_normalized": "2.0.7.0",
|
||||
"version": "2.0.8",
|
||||
"version_normalized": "2.0.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Bacon/BaconQrCode.git",
|
||||
"reference": "d70c840f68657ce49094b8d91f9ee0cc07fbf66c"
|
||||
"reference": "8674e51bb65af933a5ffaf1c308a660387c35c22"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/d70c840f68657ce49094b8d91f9ee0cc07fbf66c",
|
||||
"reference": "d70c840f68657ce49094b8d91f9ee0cc07fbf66c",
|
||||
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22",
|
||||
"reference": "8674e51bb65af933a5ffaf1c308a660387c35c22",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
"suggest": {
|
||||
"ext-imagick": "to generate QR code images"
|
||||
},
|
||||
"time": "2022-03-14T02:02:36+00:00",
|
||||
"time": "2022-12-07T17:46:57+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
"homepage": "https://github.com/Bacon/BaconQrCode",
|
||||
"support": {
|
||||
"issues": "https://github.com/Bacon/BaconQrCode/issues",
|
||||
"source": "https://github.com/Bacon/BaconQrCode/tree/2.0.7"
|
||||
"source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8"
|
||||
},
|
||||
"install-path": "../bacon/bacon-qr-code"
|
||||
},
|
||||
|
|
@ -187,26 +187,28 @@
|
|||
},
|
||||
{
|
||||
"name": "khanamiryan/qrcode-detector-decoder",
|
||||
"version": "1.0.5.2",
|
||||
"version_normalized": "1.0.5.2",
|
||||
"version": "1.0.6",
|
||||
"version_normalized": "1.0.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/khanamiryan/php-qrcode-detector-decoder.git",
|
||||
"reference": "04fdd58d86a387065f707dc6d3cc304c719910c1"
|
||||
"reference": "45326fb83a2a375065dbb3a134b5b8a5872da569"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/khanamiryan/php-qrcode-detector-decoder/zipball/04fdd58d86a387065f707dc6d3cc304c719910c1",
|
||||
"reference": "04fdd58d86a387065f707dc6d3cc304c719910c1",
|
||||
"url": "https://api.github.com/repos/khanamiryan/php-qrcode-detector-decoder/zipball/45326fb83a2a375065dbb3a134b5b8a5872da569",
|
||||
"reference": "45326fb83a2a375065dbb3a134b5b8a5872da569",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7 | ^7.5 | ^8.0 | ^9.0"
|
||||
"phpunit/phpunit": "^5.7 | ^7.5 | ^8.0 | ^9.0",
|
||||
"rector/rector": "^0.13.6",
|
||||
"symplify/easy-coding-standard": "^11.0"
|
||||
},
|
||||
"time": "2021-07-13T18:46:38+00:00",
|
||||
"time": "2022-06-29T09:25:13+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
@ -239,7 +241,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/khanamiryan/php-qrcode-detector-decoder/issues",
|
||||
"source": "https://github.com/khanamiryan/php-qrcode-detector-decoder/tree/1.0.5.2"
|
||||
"source": "https://github.com/khanamiryan/php-qrcode-detector-decoder/tree/1.0.6"
|
||||
},
|
||||
"install-path": "../khanamiryan/qrcode-detector-decoder"
|
||||
},
|
||||
|
|
@ -297,17 +299,17 @@
|
|||
},
|
||||
{
|
||||
"name": "myclabs/php-enum",
|
||||
"version": "1.8.3",
|
||||
"version_normalized": "1.8.3.0",
|
||||
"version": "1.8.4",
|
||||
"version_normalized": "1.8.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/php-enum.git",
|
||||
"reference": "b942d263c641ddb5190929ff840c68f78713e937"
|
||||
"reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/b942d263c641ddb5190929ff840c68f78713e937",
|
||||
"reference": "b942d263c641ddb5190929ff840c68f78713e937",
|
||||
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483",
|
||||
"reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -319,13 +321,16 @@
|
|||
"squizlabs/php_codesniffer": "1.*",
|
||||
"vimeo/psalm": "^4.6.2"
|
||||
},
|
||||
"time": "2021-07-05T08:18:36+00:00",
|
||||
"time": "2022-08-04T09:53:51+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"MyCLabs\\Enum\\": "src/"
|
||||
}
|
||||
},
|
||||
"classmap": [
|
||||
"stubs/Stringable.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
|
|
@ -344,7 +349,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/php-enum/issues",
|
||||
"source": "https://github.com/myclabs/php-enum/tree/1.8.3"
|
||||
"source": "https://github.com/myclabs/php-enum/tree/1.8.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -425,8 +430,8 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v2.5.1",
|
||||
"version_normalized": "2.5.1.0",
|
||||
"version": "v2.5.2",
|
||||
"version_normalized": "2.5.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
|
|
@ -475,7 +480,7 @@
|
|||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1"
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -495,17 +500,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/intl",
|
||||
"version": "v5.4.8",
|
||||
"version_normalized": "5.4.8.0",
|
||||
"version": "v5.4.15",
|
||||
"version_normalized": "5.4.15.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/intl.git",
|
||||
"reference": "b9e17d7ab867ce99f89950ebced0fa91076ba12b"
|
||||
"reference": "2cb39da7f6e7b7344d7d5317dbee8db9d12cc714"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/intl/zipball/b9e17d7ab867ce99f89950ebced0fa91076ba12b",
|
||||
"reference": "b9e17d7ab867ce99f89950ebced0fa91076ba12b",
|
||||
"url": "https://api.github.com/repos/symfony/intl/zipball/2cb39da7f6e7b7344d7d5317dbee8db9d12cc714",
|
||||
"reference": "2cb39da7f6e7b7344d7d5317dbee8db9d12cc714",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -516,7 +521,7 @@
|
|||
"require-dev": {
|
||||
"symfony/filesystem": "^4.4|^5.0|^6.0"
|
||||
},
|
||||
"time": "2022-04-07T09:39:59+00:00",
|
||||
"time": "2022-10-19T14:28:49+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
@ -566,7 +571,7 @@
|
|||
"localization"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/intl/tree/v5.4.8"
|
||||
"source": "https://github.com/symfony/intl/tree/v5.4.15"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -586,17 +591,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
"version": "v5.4.3",
|
||||
"version_normalized": "5.4.3.0",
|
||||
"version": "v5.4.11",
|
||||
"version_normalized": "5.4.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/options-resolver.git",
|
||||
"reference": "cc1147cb11af1b43f503ac18f31aa3bec213aba8"
|
||||
"reference": "54f14e36aa73cb8f7261d7686691fd4d75ea2690"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/cc1147cb11af1b43f503ac18f31aa3bec213aba8",
|
||||
"reference": "cc1147cb11af1b43f503ac18f31aa3bec213aba8",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/54f14e36aa73cb8f7261d7686691fd4d75ea2690",
|
||||
"reference": "54f14e36aa73cb8f7261d7686691fd4d75ea2690",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -605,7 +610,7 @@
|
|||
"symfony/polyfill-php73": "~1.0",
|
||||
"symfony/polyfill-php80": "^1.16"
|
||||
},
|
||||
"time": "2022-01-02T09:53:40+00:00",
|
||||
"time": "2022-07-20T13:00:38+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
@ -638,7 +643,7 @@
|
|||
"options"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v5.4.3"
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v5.4.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -658,17 +663,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.26.0",
|
||||
"version_normalized": "1.26.0.0",
|
||||
"version": "v1.27.0",
|
||||
"version_normalized": "1.27.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
|
||||
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
|
||||
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
|
||||
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -680,11 +685,11 @@
|
|||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"time": "2022-05-24T11:49:31+00:00",
|
||||
"time": "2022-11-03T14:55:06+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
|
@ -723,7 +728,7 @@
|
|||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -743,17 +748,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-grapheme",
|
||||
"version": "v1.26.0",
|
||||
"version_normalized": "1.26.0.0",
|
||||
"version": "v1.27.0",
|
||||
"version_normalized": "1.27.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||
"reference": "433d05519ce6990bf3530fba6957499d327395c2"
|
||||
"reference": "511a08c03c1960e08a883f4cffcacd219b758354"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2",
|
||||
"reference": "433d05519ce6990bf3530fba6957499d327395c2",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354",
|
||||
"reference": "511a08c03c1960e08a883f4cffcacd219b758354",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -762,11 +767,11 @@
|
|||
"suggest": {
|
||||
"ext-intl": "For best performance"
|
||||
},
|
||||
"time": "2022-05-24T11:49:31+00:00",
|
||||
"time": "2022-11-03T14:55:06+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
|
@ -807,7 +812,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -827,17 +832,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-icu",
|
||||
"version": "v1.26.0",
|
||||
"version_normalized": "1.26.0.0",
|
||||
"version": "v1.27.0",
|
||||
"version_normalized": "1.27.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-icu.git",
|
||||
"reference": "e407643d610e5f2c8a4b14189150f68934bf5e48"
|
||||
"reference": "a3d9148e2c363588e05abbdd4ee4f971f0a5330c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/e407643d610e5f2c8a4b14189150f68934bf5e48",
|
||||
"reference": "e407643d610e5f2c8a4b14189150f68934bf5e48",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/a3d9148e2c363588e05abbdd4ee4f971f0a5330c",
|
||||
"reference": "a3d9148e2c363588e05abbdd4ee4f971f0a5330c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -846,11 +851,11 @@
|
|||
"suggest": {
|
||||
"ext-intl": "For best performance and support of other locales than \"en\""
|
||||
},
|
||||
"time": "2022-05-24T11:49:31+00:00",
|
||||
"time": "2022-11-03T14:55:06+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
|
@ -897,7 +902,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.27.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -917,17 +922,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-normalizer",
|
||||
"version": "v1.26.0",
|
||||
"version_normalized": "1.26.0.0",
|
||||
"version": "v1.27.0",
|
||||
"version_normalized": "1.27.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||
"reference": "219aa369ceff116e673852dce47c3a41794c14bd"
|
||||
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd",
|
||||
"reference": "219aa369ceff116e673852dce47c3a41794c14bd",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
|
||||
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -936,11 +941,11 @@
|
|||
"suggest": {
|
||||
"ext-intl": "For best performance"
|
||||
},
|
||||
"time": "2022-05-24T11:49:31+00:00",
|
||||
"time": "2022-11-03T14:55:06+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
|
@ -984,7 +989,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1004,17 +1009,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.26.0",
|
||||
"version_normalized": "1.26.0.0",
|
||||
"version": "v1.27.0",
|
||||
"version_normalized": "1.27.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
|
||||
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
||||
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
|
||||
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1026,11 +1031,11 @@
|
|||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"time": "2022-05-24T11:49:31+00:00",
|
||||
"time": "2022-11-03T14:55:06+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
|
@ -1070,7 +1075,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1090,27 +1095,27 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php73",
|
||||
"version": "v1.26.0",
|
||||
"version_normalized": "1.26.0.0",
|
||||
"version": "v1.27.0",
|
||||
"version_normalized": "1.27.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php73.git",
|
||||
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85"
|
||||
"reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85",
|
||||
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
|
||||
"reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"time": "2022-05-24T11:49:31+00:00",
|
||||
"time": "2022-11-03T14:55:06+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
|
@ -1152,7 +1157,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1172,27 +1177,27 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.26.0",
|
||||
"version_normalized": "1.26.0.0",
|
||||
"version": "v1.27.0",
|
||||
"version_normalized": "1.27.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
|
||||
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"time": "2022-05-10T07:21:04+00:00",
|
||||
"time": "2022-11-03T14:55:06+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
|
@ -1238,7 +1243,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1258,27 +1263,27 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php81",
|
||||
"version": "v1.26.0",
|
||||
"version_normalized": "1.26.0.0",
|
||||
"version": "v1.27.0",
|
||||
"version_normalized": "1.27.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1"
|
||||
"reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1",
|
||||
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a",
|
||||
"reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"time": "2022-05-24T11:49:31+00:00",
|
||||
"time": "2022-11-03T14:55:06+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
|
@ -1320,7 +1325,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1340,17 +1345,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/property-access",
|
||||
"version": "v5.4.8",
|
||||
"version_normalized": "5.4.8.0",
|
||||
"version": "v5.4.15",
|
||||
"version_normalized": "5.4.15.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/property-access.git",
|
||||
"reference": "fe501d498d6ec7e9efe928c90fabedf629116495"
|
||||
"reference": "0f3e8f40a1d3da90f674b3dd772e4777ccde4273"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/property-access/zipball/fe501d498d6ec7e9efe928c90fabedf629116495",
|
||||
"reference": "fe501d498d6ec7e9efe928c90fabedf629116495",
|
||||
"url": "https://api.github.com/repos/symfony/property-access/zipball/0f3e8f40a1d3da90f674b3dd772e4777ccde4273",
|
||||
"reference": "0f3e8f40a1d3da90f674b3dd772e4777ccde4273",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1365,7 +1370,7 @@
|
|||
"suggest": {
|
||||
"psr/cache-implementation": "To cache access methods."
|
||||
},
|
||||
"time": "2022-04-12T15:48:08+00:00",
|
||||
"time": "2022-10-27T07:55:40+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
@ -1404,7 +1409,7 @@
|
|||
"reflection"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/property-access/tree/v5.4.8"
|
||||
"source": "https://github.com/symfony/property-access/tree/v5.4.15"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1424,17 +1429,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/property-info",
|
||||
"version": "v5.4.9",
|
||||
"version_normalized": "5.4.9.0",
|
||||
"version": "v5.4.17",
|
||||
"version_normalized": "5.4.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/property-info.git",
|
||||
"reference": "6f0a452aaba45e763f89e328df437f73a720e18e"
|
||||
"reference": "12e1f7b3d73b1f3690aa524b92b5de9937507361"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/property-info/zipball/6f0a452aaba45e763f89e328df437f73a720e18e",
|
||||
"reference": "6f0a452aaba45e763f89e328df437f73a720e18e",
|
||||
"url": "https://api.github.com/repos/symfony/property-info/zipball/12e1f7b3d73b1f3690aa524b92b5de9937507361",
|
||||
"reference": "12e1f7b3d73b1f3690aa524b92b5de9937507361",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1449,7 +1454,7 @@
|
|||
"symfony/dependency-injection": "<4.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "^1.10.4",
|
||||
"doctrine/annotations": "^1.10.4|^2",
|
||||
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
|
||||
"phpstan/phpdoc-parser": "^1.0",
|
||||
"symfony/cache": "^4.4|^5.0|^6.0",
|
||||
|
|
@ -1462,7 +1467,7 @@
|
|||
"symfony/doctrine-bridge": "To use Doctrine metadata",
|
||||
"symfony/serializer": "To use Serializer metadata"
|
||||
},
|
||||
"time": "2022-05-17T09:47:20+00:00",
|
||||
"time": "2022-12-20T11:10:57+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
@ -1498,7 +1503,7 @@
|
|||
"validator"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/property-info/tree/v5.4.9"
|
||||
"source": "https://github.com/symfony/property-info/tree/v5.4.17"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1518,17 +1523,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v5.4.9",
|
||||
"version_normalized": "5.4.9.0",
|
||||
"version": "v5.4.17",
|
||||
"version_normalized": "5.4.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "985e6a9703ef5ce32ba617c9c7d97873bb7b2a99"
|
||||
"reference": "55733a8664b8853b003e70251c58bc8cb2d82a6b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/985e6a9703ef5ce32ba617c9c7d97873bb7b2a99",
|
||||
"reference": "985e6a9703ef5ce32ba617c9c7d97873bb7b2a99",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/55733a8664b8853b003e70251c58bc8cb2d82a6b",
|
||||
"reference": "55733a8664b8853b003e70251c58bc8cb2d82a6b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1548,7 +1553,7 @@
|
|||
"symfony/translation-contracts": "^1.1|^2",
|
||||
"symfony/var-exporter": "^4.4|^5.0|^6.0"
|
||||
},
|
||||
"time": "2022-04-19T10:40:37+00:00",
|
||||
"time": "2022-12-12T15:54:21+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
@ -1587,7 +1592,7 @@
|
|||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v5.4.9"
|
||||
"source": "https://github.com/symfony/string/tree/v5.4.17"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1607,17 +1612,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/translation-contracts",
|
||||
"version": "v2.5.1",
|
||||
"version_normalized": "2.5.1.0",
|
||||
"version": "v2.5.2",
|
||||
"version_normalized": "2.5.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation-contracts.git",
|
||||
"reference": "1211df0afa701e45a04253110e959d4af4ef0f07"
|
||||
"reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/1211df0afa701e45a04253110e959d4af4ef0f07",
|
||||
"reference": "1211df0afa701e45a04253110e959d4af4ef0f07",
|
||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe",
|
||||
"reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1626,7 +1631,7 @@
|
|||
"suggest": {
|
||||
"symfony/translation-implementation": ""
|
||||
},
|
||||
"time": "2022-01-02T09:53:40+00:00",
|
||||
"time": "2022-06-27T16:58:25+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
|
|
@ -1668,7 +1673,7 @@
|
|||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/translation-contracts/tree/v2.5.1"
|
||||
"source": "https://github.com/symfony/translation-contracts/tree/v2.5.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1688,17 +1693,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/validator",
|
||||
"version": "v5.4.8",
|
||||
"version_normalized": "5.4.8.0",
|
||||
"version": "v5.4.17",
|
||||
"version_normalized": "5.4.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/validator.git",
|
||||
"reference": "bdc6d04ba95c73ccbf906b4ad9b8775c738d83ad"
|
||||
"reference": "621b820204a238d754f7f60241fcbdb1687641ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/validator/zipball/bdc6d04ba95c73ccbf906b4ad9b8775c738d83ad",
|
||||
"reference": "bdc6d04ba95c73ccbf906b4ad9b8775c738d83ad",
|
||||
"url": "https://api.github.com/repos/symfony/validator/zipball/621b820204a238d754f7f60241fcbdb1687641ea",
|
||||
"reference": "621b820204a238d754f7f60241fcbdb1687641ea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1725,7 +1730,7 @@
|
|||
"symfony/yaml": "<4.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "^1.13",
|
||||
"doctrine/annotations": "^1.13|^2",
|
||||
"doctrine/cache": "^1.11|^2.0",
|
||||
"egulias/email-validator": "^2.1.10|^3",
|
||||
"symfony/cache": "^4.4|^5.0|^6.0",
|
||||
|
|
@ -1756,7 +1761,7 @@
|
|||
"symfony/translation": "For translating validation errors.",
|
||||
"symfony/yaml": ""
|
||||
},
|
||||
"time": "2022-04-15T08:07:45+00:00",
|
||||
"time": "2022-12-21T19:20:17+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
@ -1784,7 +1789,7 @@
|
|||
"description": "Provides tools to validate values",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/validator/tree/v5.4.8"
|
||||
"source": "https://github.com/symfony/validator/tree/v5.4.17"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1803,6 +1808,6 @@
|
|||
"install-path": "../symfony/validator"
|
||||
}
|
||||
],
|
||||
"dev": false,
|
||||
"dev": true,
|
||||
"dev-package-names": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => '__root__',
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '4f030b05af2d8e0e4875c40d35686b88e7924979',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev' => false,
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '4f030b05af2d8e0e4875c40d35686b88e7924979',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'bacon/bacon-qr-code' => array(
|
||||
'pretty_version' => '2.0.7',
|
||||
'version' => '2.0.7.0',
|
||||
'reference' => 'd70c840f68657ce49094b8d91f9ee0cc07fbf66c',
|
||||
'pretty_version' => '2.0.8',
|
||||
'version' => '2.0.8.0',
|
||||
'reference' => '8674e51bb65af933a5ffaf1c308a660387c35c22',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../bacon/bacon-qr-code',
|
||||
'aliases' => array(),
|
||||
|
|
@ -47,9 +47,9 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'khanamiryan/qrcode-detector-decoder' => array(
|
||||
'pretty_version' => '1.0.5.2',
|
||||
'version' => '1.0.5.2',
|
||||
'reference' => '04fdd58d86a387065f707dc6d3cc304c719910c1',
|
||||
'pretty_version' => '1.0.6',
|
||||
'version' => '1.0.6.0',
|
||||
'reference' => '45326fb83a2a375065dbb3a134b5b8a5872da569',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../khanamiryan/qrcode-detector-decoder',
|
||||
'aliases' => array(),
|
||||
|
|
@ -65,9 +65,9 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'myclabs/php-enum' => array(
|
||||
'pretty_version' => '1.8.3',
|
||||
'version' => '1.8.3.0',
|
||||
'reference' => 'b942d263c641ddb5190929ff840c68f78713e937',
|
||||
'pretty_version' => '1.8.4',
|
||||
'version' => '1.8.4.0',
|
||||
'reference' => 'a867478eae49c9f59ece437ae7f9506bfaa27483',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../myclabs/php-enum',
|
||||
'aliases' => array(),
|
||||
|
|
@ -83,8 +83,8 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/deprecation-contracts' => array(
|
||||
'pretty_version' => 'v2.5.1',
|
||||
'version' => '2.5.1.0',
|
||||
'pretty_version' => 'v2.5.2',
|
||||
'version' => '2.5.2.0',
|
||||
'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
|
||||
|
|
@ -92,135 +92,135 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/intl' => array(
|
||||
'pretty_version' => 'v5.4.8',
|
||||
'version' => '5.4.8.0',
|
||||
'reference' => 'b9e17d7ab867ce99f89950ebced0fa91076ba12b',
|
||||
'pretty_version' => 'v5.4.15',
|
||||
'version' => '5.4.15.0',
|
||||
'reference' => '2cb39da7f6e7b7344d7d5317dbee8db9d12cc714',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/intl',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/options-resolver' => array(
|
||||
'pretty_version' => 'v5.4.3',
|
||||
'version' => '5.4.3.0',
|
||||
'reference' => 'cc1147cb11af1b43f503ac18f31aa3bec213aba8',
|
||||
'pretty_version' => 'v5.4.11',
|
||||
'version' => '5.4.11.0',
|
||||
'reference' => '54f14e36aa73cb8f7261d7686691fd4d75ea2690',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/options-resolver',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-ctype' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'reference' => '6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '5bbc823adecdae860bb64756d639ecfec17b050a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-intl-grapheme' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'reference' => '433d05519ce6990bf3530fba6957499d327395c2',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '511a08c03c1960e08a883f4cffcacd219b758354',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-intl-icu' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'reference' => 'e407643d610e5f2c8a4b14189150f68934bf5e48',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => 'a3d9148e2c363588e05abbdd4ee4f971f0a5330c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-icu',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-intl-normalizer' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'reference' => '219aa369ceff116e673852dce47c3a41794c14bd',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '19bd1e4fcd5b91116f14d8533c57831ed00571b6',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-mbstring' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'reference' => '9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '8ad114f6b39e2c98a8b0e3bd907732c207c2b534',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php73' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'reference' => 'e440d35fa0286f77fb45b79a03fedbeda9307e85',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '9e8ecb5f92152187c4799efd3c96b78ccab18ff9',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php73',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php80' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php81' => array(
|
||||
'pretty_version' => 'v1.26.0',
|
||||
'version' => '1.26.0.0',
|
||||
'reference' => '13f6d1271c663dc5ae9fb843a8f16521db7687a1',
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '707403074c8ea6e2edaf8794b0157a0bfa52157a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php81',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/property-access' => array(
|
||||
'pretty_version' => 'v5.4.8',
|
||||
'version' => '5.4.8.0',
|
||||
'reference' => 'fe501d498d6ec7e9efe928c90fabedf629116495',
|
||||
'pretty_version' => 'v5.4.15',
|
||||
'version' => '5.4.15.0',
|
||||
'reference' => '0f3e8f40a1d3da90f674b3dd772e4777ccde4273',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/property-access',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/property-info' => array(
|
||||
'pretty_version' => 'v5.4.9',
|
||||
'version' => '5.4.9.0',
|
||||
'reference' => '6f0a452aaba45e763f89e328df437f73a720e18e',
|
||||
'pretty_version' => 'v5.4.17',
|
||||
'version' => '5.4.17.0',
|
||||
'reference' => '12e1f7b3d73b1f3690aa524b92b5de9937507361',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/property-info',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/string' => array(
|
||||
'pretty_version' => 'v5.4.9',
|
||||
'version' => '5.4.9.0',
|
||||
'reference' => '985e6a9703ef5ce32ba617c9c7d97873bb7b2a99',
|
||||
'pretty_version' => 'v5.4.17',
|
||||
'version' => '5.4.17.0',
|
||||
'reference' => '55733a8664b8853b003e70251c58bc8cb2d82a6b',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/string',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/translation-contracts' => array(
|
||||
'pretty_version' => 'v2.5.1',
|
||||
'version' => '2.5.1.0',
|
||||
'reference' => '1211df0afa701e45a04253110e959d4af4ef0f07',
|
||||
'pretty_version' => 'v2.5.2',
|
||||
'version' => '2.5.2.0',
|
||||
'reference' => '136b19dd05cdf0709db6537d058bcab6dd6e2dbe',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/translation-contracts',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/validator' => array(
|
||||
'pretty_version' => 'v5.4.8',
|
||||
'version' => '5.4.8.0',
|
||||
'reference' => 'bdc6d04ba95c73ccbf906b4ad9b8775c738d83ad',
|
||||
'pretty_version' => 'v5.4.17',
|
||||
'version' => '5.4.17.0',
|
||||
'reference' => '621b820204a238d754f7f60241fcbdb1687641ea',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/validator',
|
||||
'aliases' => array(),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ $ composer require khanamiryan/qrcode-detector-decoder
|
|||
## Usage
|
||||
```php
|
||||
require __DIR__ . "/vendor/autoload.php";
|
||||
use Zxing\QrReader;
|
||||
$qrcode = new QrReader('path/to_image');
|
||||
$text = $qrcode->text(); //return decoded text from QR Code
|
||||
```
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@
|
|||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7 | ^7.5 | ^8.0 | ^9.0"
|
||||
"phpunit/phpunit": "^5.7 | ^7.5 | ^8.0 | ^9.0",
|
||||
"rector/rector": "^0.13.6",
|
||||
"symplify/easy-coding-standard": "^11.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
@ -33,5 +35,15 @@
|
|||
"files": [
|
||||
"lib/Common/customFunctions.php"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"check-cs": "./vendor/bin/ecs check",
|
||||
"fix-cs": "./vendor/bin/ecs check --fix",
|
||||
"tests": "./vendor/bin/phpunit"
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Khanamiryan\\QrCodeTests\\": "tests/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/ecs.php
vendored
Normal file
29
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/ecs.php
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
use Rector\Set\ValueObject\LevelSetList;
|
||||
use PhpCsFixer\Fixer\Operator\ConcatSpaceFixer;
|
||||
use Symplify\EasyCodingStandard\Config\ECSConfig;
|
||||
use Symplify\EasyCodingStandard\ValueObject\Option;
|
||||
|
||||
use PhpCsFixer\Fixer\ArrayNotation\ArraySyntaxFixer;
|
||||
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ECSConfig $configurator): void {
|
||||
|
||||
// alternative to CLI arguments, easier to maintain and extend
|
||||
$configurator->paths([__DIR__ . '/lib', __DIR__ . '/tests']);
|
||||
|
||||
// choose
|
||||
$configurator->sets([
|
||||
SetList::CLEAN_CODE, SetList::PSR_12//, LevelSetList::UP_TO_PHP_81 //, SymfonySetList::SYMFONY_60
|
||||
]);
|
||||
|
||||
$configurator->ruleWithConfiguration(ConcatSpaceFixer::class, [
|
||||
'spacing' => 'one'
|
||||
]);
|
||||
|
||||
// indent and tabs/spaces
|
||||
// [default: spaces]. BUT: tabs are superiour due to accessibility reasons
|
||||
$configurator->indentation('tab');
|
||||
};
|
||||
|
|
@ -30,67 +30,64 @@ use Zxing\Common\BitMatrix;
|
|||
*/
|
||||
abstract class Binarizer
|
||||
{
|
||||
private $source;
|
||||
protected function __construct(private $source)
|
||||
{
|
||||
}
|
||||
|
||||
protected function __construct($source)
|
||||
{
|
||||
$this->source = $source;
|
||||
}
|
||||
/**
|
||||
* @return LuminanceSource
|
||||
*/
|
||||
final public function getLuminanceSource()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LuminanceSource
|
||||
*/
|
||||
public final function getLuminanceSource()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
/**
|
||||
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||
* For callers which only examine one row of pixels at a time, the same BitArray should be reused
|
||||
* and passed in with each call for performance. However it is legal to keep more than one row
|
||||
* at a time if needed.
|
||||
*
|
||||
* @param $y The row to fetch, which must be in [0, bitmap height)
|
||||
* @param An $row optional preallocated array. If null or too small, it will be ignored.
|
||||
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||
*
|
||||
* @return array The array of bits for this row (true means black).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
abstract public function getBlackRow($y, $row);
|
||||
|
||||
/**
|
||||
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||
* For callers which only examine one row of pixels at a time, the same BitArray should be reused
|
||||
* and passed in with each call for performance. However it is legal to keep more than one row
|
||||
* at a time if needed.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0, bitmap height)
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||
*
|
||||
* @return array The array of bits for this row (true means black).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
public abstract function getBlackRow($y, $row);
|
||||
/**
|
||||
* 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 BitMatrix The 2D array of bits for the image (true means black).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
abstract public function getBlackMatrix();
|
||||
|
||||
/**
|
||||
* 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 BitMatrix The 2D array of bits for the image (true means black).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
public abstract function getBlackMatrix();
|
||||
/**
|
||||
* Creates a new object with the same type as this Binarizer implementation, but with pristine
|
||||
* state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
|
||||
* of 1 bit data. See Effective Java for why we can't use Java's clone() method.
|
||||
*
|
||||
* @param $source The LuminanceSource this Binarizer will operate on.
|
||||
*
|
||||
* @return Binarizer A new concrete Binarizer implementation object.
|
||||
*/
|
||||
abstract public function createBinarizer($source);
|
||||
|
||||
/**
|
||||
* Creates a new object with the same type as this Binarizer implementation, but with pristine
|
||||
* state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
|
||||
* of 1 bit data. See Effective Java for why we can't use Java's clone() method.
|
||||
*
|
||||
* @param source The LuminanceSource this Binarizer will operate on.
|
||||
*
|
||||
* @return Binarizer A new concrete Binarizer implementation object.
|
||||
*/
|
||||
public abstract function createBinarizer($source);
|
||||
final public function getWidth()
|
||||
{
|
||||
return $this->source->getWidth();
|
||||
}
|
||||
|
||||
public final function getWidth()
|
||||
{
|
||||
return $this->source->getWidth();
|
||||
}
|
||||
|
||||
public final function getHeight()
|
||||
{
|
||||
return $this->source->getHeight();
|
||||
}
|
||||
final public function getHeight()
|
||||
{
|
||||
return $this->source->getHeight();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,140 +27,140 @@ use Zxing\Common\BitMatrix;
|
|||
*/
|
||||
final class BinaryBitmap
|
||||
{
|
||||
private $binarizer;
|
||||
private $matrix;
|
||||
private readonly \Zxing\Binarizer $binarizer;
|
||||
private ?\Zxing\Common\BitMatrix $matrix = null;
|
||||
|
||||
public function __construct(Binarizer $binarizer)
|
||||
{
|
||||
if ($binarizer === null) {
|
||||
throw new \InvalidArgumentException("Binarizer must be non-null.");
|
||||
}
|
||||
$this->binarizer = $binarizer;
|
||||
}
|
||||
public function __construct(Binarizer $binarizer)
|
||||
{
|
||||
if ($binarizer === null) {
|
||||
throw new \InvalidArgumentException("Binarizer must be non-null.");
|
||||
}
|
||||
$this->binarizer = $binarizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int The width of the bitmap.
|
||||
*/
|
||||
public function getWidth()
|
||||
{
|
||||
return $this->binarizer->getWidth();
|
||||
}
|
||||
/**
|
||||
* @return int The width of the bitmap.
|
||||
*/
|
||||
public function getWidth()
|
||||
{
|
||||
return $this->binarizer->getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int The height of the bitmap.
|
||||
*/
|
||||
public function getHeight()
|
||||
{
|
||||
return $this->binarizer->getHeight();
|
||||
}
|
||||
/**
|
||||
* @return int The height of the bitmap.
|
||||
*/
|
||||
public function getHeight()
|
||||
{
|
||||
return $this->binarizer->getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0, bitmap height)
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||
*
|
||||
* @return array The array of bits for this row (true means black).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
public function getBlackRow($y, $row)
|
||||
{
|
||||
return $this->binarizer->getBlackRow($y, $row);
|
||||
}
|
||||
/**
|
||||
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||
*
|
||||
* @param $y The row to fetch, which must be in [0, bitmap height)
|
||||
* @param An $row optional preallocated array. If null or too small, it will be ignored.
|
||||
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||
*
|
||||
* @return array The array of bits for this row (true means black).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
public function getBlackRow($y, $row)
|
||||
{
|
||||
return $this->binarizer->getBlackRow($y, $row);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Whether this bitmap can be cropped.
|
||||
*/
|
||||
public function isCropSupported()
|
||||
{
|
||||
return $this->binarizer->getLuminanceSource()->isCropSupported();
|
||||
}
|
||||
/**
|
||||
* @return bool Whether this bitmap can be cropped.
|
||||
*/
|
||||
public function isCropSupported()
|
||||
{
|
||||
return $this->binarizer->getLuminanceSource()->isCropSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||
*
|
||||
* @param left The left coordinate, which must be in [0,getWidth())
|
||||
* @param top The top coordinate, which must be in [0,getHeight())
|
||||
* @param width The width of the rectangle to crop.
|
||||
* @param height The height of the rectangle to crop.
|
||||
*
|
||||
* @return BinaryBitmap A cropped version of this object.
|
||||
*/
|
||||
public function crop($left, $top, $width, $height)
|
||||
{
|
||||
$newSource = $this->binarizer->getLuminanceSource()->crop($left, $top, $width, $height);
|
||||
/**
|
||||
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||
*
|
||||
* @param $left The left coordinate, which must be in [0,getWidth())
|
||||
* @param $top The top coordinate, which must be in [0,getHeight())
|
||||
* @param $width The width of the rectangle to crop.
|
||||
* @param $height The height of the rectangle to crop.
|
||||
*
|
||||
* @return BinaryBitmap A cropped version of this object.
|
||||
*/
|
||||
public function crop($left, $top, $width, $height): \Zxing\BinaryBitmap
|
||||
{
|
||||
$newSource = $this->binarizer->getLuminanceSource()->crop($left, $top, $width, $height);
|
||||
|
||||
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
|
||||
}
|
||||
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this bitmap supports counter-clockwise rotation.
|
||||
*/
|
||||
public function isRotateSupported()
|
||||
{
|
||||
return $this->binarizer->getLuminanceSource()->isRotateSupported();
|
||||
}
|
||||
/**
|
||||
* @return Whether this bitmap supports counter-clockwise rotation.
|
||||
*/
|
||||
public function isRotateSupported()
|
||||
{
|
||||
return $this->binarizer->getLuminanceSource()->isRotateSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return BinaryBitmap A rotated version of this object.
|
||||
*/
|
||||
public function rotateCounterClockwise()
|
||||
{
|
||||
$newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise();
|
||||
/**
|
||||
* Returns a new object with rotated image data by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return BinaryBitmap A rotated version of this object.
|
||||
*/
|
||||
public function rotateCounterClockwise(): \Zxing\BinaryBitmap
|
||||
{
|
||||
$newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise();
|
||||
|
||||
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
|
||||
}
|
||||
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return BinaryBitmap A rotated version of this object.
|
||||
*/
|
||||
public function rotateCounterClockwise45()
|
||||
{
|
||||
$newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise45();
|
||||
/**
|
||||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return BinaryBitmap A rotated version of this object.
|
||||
*/
|
||||
public function rotateCounterClockwise45(): \Zxing\BinaryBitmap
|
||||
{
|
||||
$newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise45();
|
||||
|
||||
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
|
||||
}
|
||||
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
try {
|
||||
return $this->getBlackMatrix()->toString();
|
||||
} catch (NotFoundException $e) {
|
||||
}
|
||||
public function toString()
|
||||
{
|
||||
try {
|
||||
return $this->getBlackMatrix()->toString();
|
||||
} catch (NotFoundException) {
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 2D array of luminance data to 1 bit. 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 BitMatrix The 2D array of bits for the image (true means black).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
public function getBlackMatrix()
|
||||
{
|
||||
// The matrix is created on demand the first time it is requested, then cached. There are two
|
||||
// reasons for this:
|
||||
// 1. This work will never be done if the caller only installs 1D Reader objects, or if a
|
||||
// 1D Reader finds a barcode before the 2D Readers run.
|
||||
// 2. This work will only be done once even if the caller installs multiple 2D Readers.
|
||||
if ($this->matrix === null) {
|
||||
$this->matrix = $this->binarizer->getBlackMatrix();
|
||||
}
|
||||
/**
|
||||
* Converts a 2D array of luminance data to 1 bit. 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 BitMatrix The 2D array of bits for the image (true means black).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
public function getBlackMatrix()
|
||||
{
|
||||
// The matrix is created on demand the first time it is requested, then cached. There are two
|
||||
// reasons for this:
|
||||
// 1. This work will never be done if the caller only installs 1D Reader objects, or if a
|
||||
// 1D Reader finds a barcode before the 2D Readers run.
|
||||
// 2. This work will only be done once even if the caller installs multiple 2D Readers.
|
||||
if ($this->matrix === null) {
|
||||
$this->matrix = $this->binarizer->getBlackMatrix();
|
||||
}
|
||||
|
||||
return $this->matrix;
|
||||
}
|
||||
return $this->matrix;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,18 +25,18 @@ namespace Zxing;
|
|||
*/
|
||||
final class ChecksumException extends ReaderException
|
||||
{
|
||||
private static $instance;
|
||||
private static ?\Zxing\ChecksumException $instance = null;
|
||||
|
||||
public static function getChecksumInstance($cause = null)
|
||||
{
|
||||
if (self::$isStackTrace) {
|
||||
return new ChecksumException($cause);
|
||||
} else {
|
||||
if (!self::$instance) {
|
||||
self::$instance = new ChecksumException($cause);
|
||||
}
|
||||
public static function getChecksumInstance($cause = null)
|
||||
{
|
||||
if (self::$isStackTrace) {
|
||||
return new ChecksumException($cause);
|
||||
} else {
|
||||
if (!self::$instance) {
|
||||
self::$instance = new ChecksumException($cause);
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,103 +2,95 @@
|
|||
|
||||
namespace Zxing\Common;
|
||||
|
||||
use \Zxing\NotFoundException;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* A general enum implementation until we got SplEnum.
|
||||
*/
|
||||
final class AbstractEnum
|
||||
final class AbstractEnum implements \Stringable
|
||||
{
|
||||
/**
|
||||
* Default value.
|
||||
*/
|
||||
const __default = null;
|
||||
/**
|
||||
* Current value.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
/**
|
||||
* Cache of constants.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $constants;
|
||||
/**
|
||||
* Whether to handle values strict or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $strict;
|
||||
/**
|
||||
* Default value.
|
||||
*/
|
||||
public const __default = null;
|
||||
/**
|
||||
* Current value.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
private $value;
|
||||
/**
|
||||
* Cache of constants.
|
||||
*
|
||||
* @var array<string, mixed>|null
|
||||
*/
|
||||
private ?array $constants = null;
|
||||
|
||||
/**
|
||||
* Creates a new enum.
|
||||
*
|
||||
* @param mixed $initialValue
|
||||
* @param boolean $strict
|
||||
*/
|
||||
public function __construct($initialValue = null, $strict = false)
|
||||
{
|
||||
$this->strict = $strict;
|
||||
$this->change($initialValue);
|
||||
}
|
||||
/**
|
||||
* Creates a new enum.
|
||||
*
|
||||
* @param mixed $initialValue
|
||||
* @param boolean $strict
|
||||
*/
|
||||
public function __construct($initialValue = null, private $strict = false)
|
||||
{
|
||||
$this->change($initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the value of the enum.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function change($value)
|
||||
{
|
||||
if (!in_array($value, $this->getConstList(), $this->strict)) {
|
||||
throw new \UnexpectedValueException('Value not a const in enum ' . get_class($this));
|
||||
}
|
||||
$this->value = $value;
|
||||
}
|
||||
/**
|
||||
* Changes the value of the enum.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function change($value)
|
||||
{
|
||||
if (!in_array($value, $this->getConstList(), $this->strict)) {
|
||||
throw new \UnexpectedValueException('Value not a const in enum ' . $this::class);
|
||||
}
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all constants (possible values) as an array.
|
||||
*
|
||||
* @param boolean $includeDefault
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getConstList($includeDefault = true)
|
||||
{
|
||||
if ($this->constants === null) {
|
||||
$reflection = new ReflectionClass($this);
|
||||
$this->constants = $reflection->getConstants();
|
||||
}
|
||||
if ($includeDefault) {
|
||||
return $this->constants;
|
||||
}
|
||||
$constants = $this->constants;
|
||||
unset($constants['__default']);
|
||||
/**
|
||||
* Gets all constants (possible values) as an array.
|
||||
*
|
||||
* @param boolean $includeDefault
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getConstList($includeDefault = true)
|
||||
{
|
||||
if ($this->constants === null) {
|
||||
$reflection = new ReflectionClass($this);
|
||||
$this->constants = $reflection->getConstants();
|
||||
}
|
||||
if ($includeDefault) {
|
||||
return $this->constants;
|
||||
}
|
||||
$constants = $this->constants;
|
||||
unset($constants['__default']);
|
||||
|
||||
return $constants;
|
||||
}
|
||||
return $constants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets current value.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
/**
|
||||
* Gets current value.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the enum.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string)array_search($this->value, $this->getConstList());
|
||||
}
|
||||
/**
|
||||
* Gets the name of the enum.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string)array_search($this->value, $this->getConstList());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,391 +32,394 @@ namespace Zxing\Common;
|
|||
|
||||
final class BitArray
|
||||
{
|
||||
|
||||
private $bits;
|
||||
private $size;
|
||||
/**
|
||||
* @var mixed[]|mixed|int[]|null
|
||||
*/
|
||||
private $bits;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
private $size;
|
||||
|
||||
|
||||
public function __construct($bits = [], $size = 0)
|
||||
{
|
||||
public function __construct($bits = [], $size = 0)
|
||||
{
|
||||
if (!$bits && !$size) {
|
||||
$this->$size = 0;
|
||||
$this->bits = [];
|
||||
} elseif ($bits && !$size) {
|
||||
$this->size = $bits;
|
||||
$this->bits = self::makeArray($bits);
|
||||
} else {
|
||||
$this->bits = $bits;
|
||||
$this->size = $size;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bits && !$size) {
|
||||
$this->$size = 0;
|
||||
$this->bits = [];
|
||||
} elseif ($bits && !$size) {
|
||||
$this->size = $bits;
|
||||
$this->bits = $this->makeArray($bits);
|
||||
} else {
|
||||
$this->bits = $bits;
|
||||
$this->size = $size;
|
||||
}
|
||||
private static function makeArray($size)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
public function getSize()
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
private static function makeArray($size)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
public function getSizeInBytes()
|
||||
{
|
||||
return ($this->size + 7) / 8;
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
/**
|
||||
* Sets bit i.
|
||||
*
|
||||
* @param bit $i to set
|
||||
*/
|
||||
public function set($i): void
|
||||
{
|
||||
$this->bits[(int)($i / 32)] |= 1 << ($i & 0x1F);
|
||||
$this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]);
|
||||
}
|
||||
|
||||
public function getSizeInBytes()
|
||||
{
|
||||
return ($this->size + 7) / 8;
|
||||
}
|
||||
/**
|
||||
* Flips bit i.
|
||||
*
|
||||
* @param bit $i to set
|
||||
*/
|
||||
public function flip($i): void
|
||||
{
|
||||
$this->bits[(int)($i / 32)] ^= 1 << ($i & 0x1F);
|
||||
$this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bit i.
|
||||
*
|
||||
* @param i bit to set
|
||||
*/
|
||||
public function set($i)
|
||||
{
|
||||
$this->bits[(int)($i / 32)] |= 1 << ($i & 0x1F);
|
||||
$this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]);
|
||||
}
|
||||
/**
|
||||
* @param first $from bit to check
|
||||
*
|
||||
* @return index of first bit that is set, starting from the given index, or size if none are set
|
||||
* at or beyond this given index
|
||||
* @see #getNextUnset(int)
|
||||
*/
|
||||
public function getNextSet($from)
|
||||
{
|
||||
if ($from >= $this->size) {
|
||||
return $this->size;
|
||||
}
|
||||
$bitsOffset = (int)($from / 32);
|
||||
$currentBits = (int)$this->bits[$bitsOffset];
|
||||
// mask off lesser bits first
|
||||
$currentBits &= ~((1 << ($from & 0x1F)) - 1);
|
||||
while ($currentBits == 0) {
|
||||
if (++$bitsOffset == (is_countable($this->bits) ? count($this->bits) : 0)) {
|
||||
return $this->size;
|
||||
}
|
||||
$currentBits = $this->bits[$bitsOffset];
|
||||
}
|
||||
$result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits); //numberOfTrailingZeros
|
||||
|
||||
/**
|
||||
* Flips bit i.
|
||||
*
|
||||
* @param i bit to set
|
||||
*/
|
||||
public function flip($i)
|
||||
{
|
||||
$this->bits[(int)($i / 32)] ^= 1 << ($i & 0x1F);
|
||||
$this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]);
|
||||
}
|
||||
return $result > $this->size ? $this->size : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from first bit to check
|
||||
*
|
||||
* @return index of first bit that is set, starting from the given index, or size if none are set
|
||||
* at or beyond this given index
|
||||
* @see #getNextUnset(int)
|
||||
*/
|
||||
public function getNextSet($from)
|
||||
{
|
||||
if ($from >= $this->size) {
|
||||
return $this->size;
|
||||
}
|
||||
$bitsOffset = (int)($from / 32);
|
||||
$currentBits = (int)$this->bits[$bitsOffset];
|
||||
// mask off lesser bits first
|
||||
$currentBits &= ~((1 << ($from & 0x1F)) - 1);
|
||||
while ($currentBits == 0) {
|
||||
if (++$bitsOffset == count($this->bits)) {
|
||||
return $this->size;
|
||||
}
|
||||
$currentBits = $this->bits[$bitsOffset];
|
||||
}
|
||||
$result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits); //numberOfTrailingZeros
|
||||
/**
|
||||
* @param index $from to start looking for unset bit
|
||||
*
|
||||
* @return index of next unset bit, or {@code size} if none are unset until the end
|
||||
* @see #getNextSet(int)
|
||||
*/
|
||||
public function getNextUnset($from)
|
||||
{
|
||||
if ($from >= $this->size) {
|
||||
return $this->size;
|
||||
}
|
||||
$bitsOffset = (int)($from / 32);
|
||||
$currentBits = ~$this->bits[$bitsOffset];
|
||||
// mask off lesser bits first
|
||||
$currentBits &= ~((1 << ($from & 0x1F)) - 1);
|
||||
while ($currentBits == 0) {
|
||||
if (++$bitsOffset == (is_countable($this->bits) ? count($this->bits) : 0)) {
|
||||
return $this->size;
|
||||
}
|
||||
$currentBits = (~$this->bits[$bitsOffset]);
|
||||
}
|
||||
$result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits);
|
||||
|
||||
return $result > $this->size ? $this->size : $result;
|
||||
}
|
||||
return $result > $this->size ? $this->size : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from index to start looking for unset bit
|
||||
*
|
||||
* @return index of next unset bit, or {@code size} if none are unset until the end
|
||||
* @see #getNextSet(int)
|
||||
*/
|
||||
public function getNextUnset($from)
|
||||
{
|
||||
if ($from >= $this->size) {
|
||||
return $this->size;
|
||||
}
|
||||
$bitsOffset = (int)($from / 32);
|
||||
$currentBits = ~$this->bits[$bitsOffset];
|
||||
// mask off lesser bits first
|
||||
$currentBits &= ~((1 << ($from & 0x1F)) - 1);
|
||||
while ($currentBits == 0) {
|
||||
if (++$bitsOffset == count($this->bits)) {
|
||||
return $this->size;
|
||||
}
|
||||
$currentBits = (~$this->bits[$bitsOffset]);
|
||||
}
|
||||
$result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits);
|
||||
/**
|
||||
* Sets a block of 32 bits, starting at bit i.
|
||||
*
|
||||
* @param first $i bit to set
|
||||
* @param the $newBits new value of the next 32 bits. Note again that the least-significant bit
|
||||
* corresponds to bit i, the next-least-significant to i+1, and so on.
|
||||
*/
|
||||
public function setBulk($i, $newBits): void
|
||||
{
|
||||
$this->bits[(int)($i / 32)] = $newBits;
|
||||
}
|
||||
|
||||
return $result > $this->size ? $this->size : $result;
|
||||
}
|
||||
/**
|
||||
* Sets a range of bits.
|
||||
*
|
||||
* @param start $start of range, inclusive.
|
||||
* @param end $end of range, exclusive
|
||||
*/
|
||||
public function setRange($start, $end)
|
||||
{
|
||||
if ($end < $start) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($end == $start) {
|
||||
return;
|
||||
}
|
||||
$end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||
$firstInt = (int)($start / 32);
|
||||
$lastInt = (int)($end / 32);
|
||||
for ($i = $firstInt; $i <= $lastInt; $i++) {
|
||||
$firstBit = $i > $firstInt ? 0 : $start & 0x1F;
|
||||
$lastBit = $i < $lastInt ? 31 : $end & 0x1F;
|
||||
$mask = 0;
|
||||
if ($firstBit == 0 && $lastBit == 31) {
|
||||
$mask = -1;
|
||||
} else {
|
||||
$mask = 0;
|
||||
for ($j = $firstBit; $j <= $lastBit; $j++) {
|
||||
$mask |= 1 << $j;
|
||||
}
|
||||
}
|
||||
$this->bits[$i] = ($this->bits[$i] | $mask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a block of 32 bits, starting at bit i.
|
||||
*
|
||||
* @param i first bit to set
|
||||
* @param newBits the new value of the next 32 bits. Note again that the least-significant bit
|
||||
* corresponds to bit i, the next-least-significant to i+1, and so on.
|
||||
*/
|
||||
public function setBulk($i, $newBits)
|
||||
{
|
||||
$this->bits[(int)($i / 32)] = $newBits;
|
||||
}
|
||||
/**
|
||||
* Clears all bits (sets to false).
|
||||
*/
|
||||
public function clear(): void
|
||||
{
|
||||
$max = is_countable($this->bits) ? count($this->bits) : 0;
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
$this->bits[$i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a range of bits.
|
||||
*
|
||||
* @param start start of range, inclusive.
|
||||
* @param end end of range, exclusive
|
||||
*/
|
||||
public function setRange($start, $end)
|
||||
{
|
||||
if ($end < $start) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($end == $start) {
|
||||
return;
|
||||
}
|
||||
$end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||
$firstInt = (int)($start / 32);
|
||||
$lastInt = (int)($end / 32);
|
||||
for ($i = $firstInt; $i <= $lastInt; $i++) {
|
||||
$firstBit = $i > $firstInt ? 0 : $start & 0x1F;
|
||||
$lastBit = $i < $lastInt ? 31 : $end & 0x1F;
|
||||
$mask = 0;
|
||||
if ($firstBit == 0 && $lastBit == 31) {
|
||||
$mask = -1;
|
||||
} else {
|
||||
$mask = 0;
|
||||
for ($j = $firstBit; $j <= $lastBit; $j++) {
|
||||
$mask |= 1 << $j;
|
||||
}
|
||||
}
|
||||
$this->bits[$i] = ($this->bits[$i] | $mask);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Efficient method to check if a range of bits is set, or not set.
|
||||
*
|
||||
* @param start $start of range, inclusive.
|
||||
* @param end $end of range, exclusive
|
||||
* @param if $value true, checks that bits in range are set, otherwise checks that they are not set
|
||||
*
|
||||
* @return true iff all bits are set or not set in range, according to value argument
|
||||
* @throws InvalidArgumentException if end is less than or equal to start
|
||||
*/
|
||||
public function isRange($start, $end, $value)
|
||||
{
|
||||
if ($end < $start) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($end == $start) {
|
||||
return true; // empty range matches
|
||||
}
|
||||
$end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||
$firstInt = (int)($start / 32);
|
||||
$lastInt = (int)($end / 32);
|
||||
for ($i = $firstInt; $i <= $lastInt; $i++) {
|
||||
$firstBit = $i > $firstInt ? 0 : $start & 0x1F;
|
||||
$lastBit = $i < $lastInt ? 31 : $end & 0x1F;
|
||||
$mask = 0;
|
||||
if ($firstBit == 0 && $lastBit == 31) {
|
||||
$mask = -1;
|
||||
} else {
|
||||
$mask = 0;
|
||||
for ($j = $firstBit; $j <= $lastBit; $j++) {
|
||||
$mask = ($mask | (1 << $j));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all bits (sets to false).
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$max = count($this->bits);
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
$this->bits[$i] = 0;
|
||||
}
|
||||
}
|
||||
// Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
|
||||
// equals the mask, or we're looking for 0s and the masked portion is not all 0s
|
||||
if (($this->bits[$i] & $mask) != ($value ? $mask : 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Efficient method to check if a range of bits is set, or not set.
|
||||
*
|
||||
* @param start start of range, inclusive.
|
||||
* @param end end of range, exclusive
|
||||
* @param value if true, checks that bits in range are set, otherwise checks that they are not set
|
||||
*
|
||||
* @return true iff all bits are set or not set in range, according to value argument
|
||||
* @throws InvalidArgumentException if end is less than or equal to start
|
||||
*/
|
||||
public function isRange($start, $end, $value)
|
||||
{
|
||||
if ($end < $start) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($end == $start) {
|
||||
return true; // empty range matches
|
||||
}
|
||||
$end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||
$firstInt = (int)($start / 32);
|
||||
$lastInt = (int)($end / 32);
|
||||
for ($i = $firstInt; $i <= $lastInt; $i++) {
|
||||
$firstBit = $i > $firstInt ? 0 : $start & 0x1F;
|
||||
$lastBit = $i < $lastInt ? 31 : $end & 0x1F;
|
||||
$mask = 0;
|
||||
if ($firstBit == 0 && $lastBit == 31) {
|
||||
$mask = -1;
|
||||
} else {
|
||||
$mask = 0;
|
||||
for ($j = $firstBit; $j <= $lastBit; $j++) {
|
||||
$mask = ($mask | (1 << $j));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
|
||||
// equals the mask, or we're looking for 0s and the masked portion is not all 0s
|
||||
if (($this->bits[$i] & $mask) != ($value ? $mask : 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Appends the least-significant bits, from value, in order from most-significant to
|
||||
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
|
||||
* 0, 1, 1, 1, 1, 0 in that order.
|
||||
*
|
||||
* @param $value {@code int} containing bits to append
|
||||
* @param bits $numBits from value to append
|
||||
*/
|
||||
public function appendBits($value, $numBits)
|
||||
{
|
||||
if ($numBits < 0 || $numBits > 32) {
|
||||
throw new \InvalidArgumentException("Num bits must be between 0 and 32");
|
||||
}
|
||||
$this->ensureCapacity($this->size + $numBits);
|
||||
for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) {
|
||||
$this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
private function ensureCapacity($size): void
|
||||
{
|
||||
if ($size > (is_countable($this->bits) ? count($this->bits) : 0) * 32) {
|
||||
$newBits = self::makeArray($size);
|
||||
$newBits = arraycopy($this->bits, 0, $newBits, 0, is_countable($this->bits) ? count($this->bits) : 0);
|
||||
$this->bits = $newBits;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the least-significant bits, from value, in order from most-significant to
|
||||
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
|
||||
* 0, 1, 1, 1, 1, 0 in that order.
|
||||
*
|
||||
* @param value {@code int} containing bits to append
|
||||
* @param numBits bits from value to append
|
||||
*/
|
||||
public function appendBits($value, $numBits)
|
||||
{
|
||||
if ($numBits < 0 || $numBits > 32) {
|
||||
throw new \InvalidArgumentException("Num bits must be between 0 and 32");
|
||||
}
|
||||
$this->ensureCapacity($this->size + $numBits);
|
||||
for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) {
|
||||
$this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) == 1);
|
||||
}
|
||||
}
|
||||
public function appendBit($bit): void
|
||||
{
|
||||
$this->ensureCapacity($this->size + 1);
|
||||
if ($bit) {
|
||||
$this->bits[(int)($this->size / 32)] |= 1 << ($this->size & 0x1F);
|
||||
}
|
||||
$this->size++;
|
||||
}
|
||||
|
||||
private function ensureCapacity($size)
|
||||
{
|
||||
if ($size > count($this->bits) * 32) {
|
||||
$newBits = $this->makeArray($size);
|
||||
$newBits = arraycopy($this->bits, 0, $newBits, 0, count($this->bits));
|
||||
$this->bits = $newBits;
|
||||
}
|
||||
}
|
||||
public function appendBitArray($other): void
|
||||
{
|
||||
$otherSize = $other->size;
|
||||
$this->ensureCapacity($this->size + $otherSize);
|
||||
for ($i = 0; $i < $otherSize; $i++) {
|
||||
$this->appendBit($other->get($i));
|
||||
}
|
||||
}
|
||||
|
||||
public function appendBit($bit)
|
||||
{
|
||||
$this->ensureCapacity($this->size + 1);
|
||||
if ($bit) {
|
||||
$this->bits[(int)($this->size / 32)] |= 1 << ($this->size & 0x1F);
|
||||
}
|
||||
$this->size++;
|
||||
}
|
||||
public function _xor($other)
|
||||
{
|
||||
if ((is_countable($this->bits) ? count($this->bits) : 0) !== (is_countable($other->bits) ? count($other->bits) : 0)) {
|
||||
throw new \InvalidArgumentException("Sizes don't match");
|
||||
}
|
||||
$count = is_countable($this->bits) ? count($this->bits) : 0;
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
// The last byte could be incomplete (i.e. not have 8 bits in
|
||||
// it) but there is no problem since 0 XOR 0 == 0.
|
||||
$this->bits[$i] ^= $other->bits[$i];
|
||||
}
|
||||
}
|
||||
|
||||
public function appendBitArray($other)
|
||||
{
|
||||
$otherSize = $other->size;
|
||||
$this->ensureCapacity($this->size + $otherSize);
|
||||
for ($i = 0; $i < $otherSize; $i++) {
|
||||
$this->appendBit($other->get($i));
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param first $bitOffset bit to start writing
|
||||
* @param array $array to write into. Bytes are written most-significant byte first. This is the opposite
|
||||
* of the internal representation, which is exposed by {@link #getBitArray()}
|
||||
* @param position $offset in array to start writing
|
||||
* @param how $numBytes many bytes to write
|
||||
*/
|
||||
public function toBytes($bitOffset, &$array, $offset, $numBytes): void
|
||||
{
|
||||
for ($i = 0; $i < $numBytes; $i++) {
|
||||
$theByte = 0;
|
||||
for ($j = 0; $j < 8; $j++) {
|
||||
if ($this->get($bitOffset)) {
|
||||
$theByte |= 1 << (7 - $j);
|
||||
}
|
||||
$bitOffset++;
|
||||
}
|
||||
$array[(int)($offset + $i)] = $theByte;
|
||||
}
|
||||
}
|
||||
|
||||
public function _xor($other)
|
||||
{
|
||||
if (count($this->bits) !== count($other->bits)) {
|
||||
throw new \InvalidArgumentException("Sizes don't match");
|
||||
}
|
||||
$count = count($this->bits);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
// The last byte could be incomplete (i.e. not have 8 bits in
|
||||
// it) but there is no problem since 0 XOR 0 == 0.
|
||||
$this->bits[$i] ^= $other->bits[$i];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param $i ; bit to get
|
||||
*
|
||||
* @return true iff bit i is set
|
||||
*/
|
||||
public function get($i)
|
||||
{
|
||||
$key = (int)($i / 32);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bitOffset first bit to start writing
|
||||
* @param array array to write into. Bytes are written most-significant byte first. This is the opposite
|
||||
* of the internal representation, which is exposed by {@link #getBitArray()}
|
||||
* @param offset position in array to start writing
|
||||
* @param numBytes how many bytes to write
|
||||
*/
|
||||
public function toBytes($bitOffset, &$array, $offset, $numBytes)
|
||||
{
|
||||
for ($i = 0; $i < $numBytes; $i++) {
|
||||
$theByte = 0;
|
||||
for ($j = 0; $j < 8; $j++) {
|
||||
if ($this->get($bitOffset)) {
|
||||
$theByte |= 1 << (7 - $j);
|
||||
}
|
||||
$bitOffset++;
|
||||
}
|
||||
$array[(int)($offset + $i)] = $theByte;
|
||||
}
|
||||
}
|
||||
return ($this->bits[$key] & (1 << ($i & 0x1F))) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $i ; bit to get
|
||||
*
|
||||
* @return true iff bit i is set
|
||||
*/
|
||||
public function get($i)
|
||||
{
|
||||
$key = (int)($i / 32);
|
||||
/**
|
||||
* @return array underlying array of ints. The first element holds the first 32 bits, and the least
|
||||
* significant bit is bit 0.
|
||||
*/
|
||||
public function getBitArray()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
|
||||
return ($this->bits[$key] & (1 << ($i & 0x1F))) != 0;
|
||||
}
|
||||
/**
|
||||
* Reverses all bits in the array.
|
||||
*/
|
||||
public function reverse(): void
|
||||
{
|
||||
$newBits = [];
|
||||
// reverse all int's first
|
||||
$len = (($this->size - 1) / 32);
|
||||
$oldBitsLen = $len + 1;
|
||||
for ($i = 0; $i < $oldBitsLen; $i++) {
|
||||
$x = $this->bits[$i];/*
|
||||
$x = (($x >> 1) & 0x55555555L) | (($x & 0x55555555L) << 1);
|
||||
$x = (($x >> 2) & 0x33333333L) | (($x & 0x33333333L) << 2);
|
||||
$x = (($x >> 4) & 0x0f0f0f0fL) | (($x & 0x0f0f0f0fL) << 4);
|
||||
$x = (($x >> 8) & 0x00ff00ffL) | (($x & 0x00ff00ffL) << 8);
|
||||
$x = (($x >> 16) & 0x0000ffffL) | (($x & 0x0000ffffL) << 16);*/
|
||||
$x = (($x >> 1) & 0x55555555) | (($x & 0x55555555) << 1);
|
||||
$x = (($x >> 2) & 0x33333333) | (($x & 0x33333333) << 2);
|
||||
$x = (($x >> 4) & 0x0f0f0f0f) | (($x & 0x0f0f0f0f) << 4);
|
||||
$x = (($x >> 8) & 0x00ff00ff) | (($x & 0x00ff00ff) << 8);
|
||||
$x = (($x >> 16) & 0x0000ffff) | (($x & 0x0000ffff) << 16);
|
||||
$newBits[(int)$len - $i] = (int)$x;
|
||||
}
|
||||
// now correct the int's if the bit size isn't a multiple of 32
|
||||
if ($this->size != $oldBitsLen * 32) {
|
||||
$leftOffset = $oldBitsLen * 32 - $this->size;
|
||||
$mask = 1;
|
||||
for ($i = 0; $i < 31 - $leftOffset; $i++) {
|
||||
$mask = ($mask << 1) | 1;
|
||||
}
|
||||
$currentInt = ($newBits[0] >> $leftOffset) & $mask;
|
||||
for ($i = 1; $i < $oldBitsLen; $i++) {
|
||||
$nextInt = $newBits[$i];
|
||||
$currentInt |= $nextInt << (32 - $leftOffset);
|
||||
$newBits[(int)($i) - 1] = $currentInt;
|
||||
$currentInt = ($nextInt >> $leftOffset) & $mask;
|
||||
}
|
||||
$newBits[(int)($oldBitsLen) - 1] = $currentInt;
|
||||
}
|
||||
// $bits = $newBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array underlying array of ints. The first element holds the first 32 bits, and the least
|
||||
* significant bit is bit 0.
|
||||
*/
|
||||
public function getBitArray()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
public function equals($o)
|
||||
{
|
||||
if (!($o instanceof BitArray)) {
|
||||
return false;
|
||||
}
|
||||
$other = $o;
|
||||
|
||||
/**
|
||||
* Reverses all bits in the array.
|
||||
*/
|
||||
public function reverse()
|
||||
{
|
||||
$newBits = [];
|
||||
// reverse all int's first
|
||||
$len = (($this->size - 1) / 32);
|
||||
$oldBitsLen = $len + 1;
|
||||
for ($i = 0; $i < $oldBitsLen; $i++) {
|
||||
$x = $this->bits[$i];/*
|
||||
$x = (($x >> 1) & 0x55555555L) | (($x & 0x55555555L) << 1);
|
||||
$x = (($x >> 2) & 0x33333333L) | (($x & 0x33333333L) << 2);
|
||||
$x = (($x >> 4) & 0x0f0f0f0fL) | (($x & 0x0f0f0f0fL) << 4);
|
||||
$x = (($x >> 8) & 0x00ff00ffL) | (($x & 0x00ff00ffL) << 8);
|
||||
$x = (($x >> 16) & 0x0000ffffL) | (($x & 0x0000ffffL) << 16);*/
|
||||
$x = (($x >> 1) & 0x55555555) | (($x & 0x55555555) << 1);
|
||||
$x = (($x >> 2) & 0x33333333) | (($x & 0x33333333) << 2);
|
||||
$x = (($x >> 4) & 0x0f0f0f0f) | (($x & 0x0f0f0f0f) << 4);
|
||||
$x = (($x >> 8) & 0x00ff00ff) | (($x & 0x00ff00ff) << 8);
|
||||
$x = (($x >> 16) & 0x0000ffff) | (($x & 0x0000ffff) << 16);
|
||||
$newBits[(int)$len - $i] = (int)$x;
|
||||
}
|
||||
// now correct the int's if the bit size isn't a multiple of 32
|
||||
if ($this->size != $oldBitsLen * 32) {
|
||||
$leftOffset = $oldBitsLen * 32 - $this->size;
|
||||
$mask = 1;
|
||||
for ($i = 0; $i < 31 - $leftOffset; $i++) {
|
||||
$mask = ($mask << 1) | 1;
|
||||
}
|
||||
$currentInt = ($newBits[0] >> $leftOffset) & $mask;
|
||||
for ($i = 1; $i < $oldBitsLen; $i++) {
|
||||
$nextInt = $newBits[$i];
|
||||
$currentInt |= $nextInt << (32 - $leftOffset);
|
||||
$newBits[(int)($i) - 1] = $currentInt;
|
||||
$currentInt = ($nextInt >> $leftOffset) & $mask;
|
||||
}
|
||||
$newBits[(int)($oldBitsLen) - 1] = $currentInt;
|
||||
}
|
||||
// $bits = $newBits;
|
||||
}
|
||||
return $this->size == $other->size && $this->bits === $other->bits;
|
||||
}
|
||||
|
||||
public function equals($o)
|
||||
{
|
||||
if (!($o instanceof BitArray)) {
|
||||
return false;
|
||||
}
|
||||
$other = $o;
|
||||
public function hashCode()
|
||||
{
|
||||
return 31 * $this->size + hashCode($this->bits);
|
||||
}
|
||||
|
||||
return $this->size == $other->size && $this->bits === $other->bits;
|
||||
}
|
||||
public function toString()
|
||||
{
|
||||
$result = '';
|
||||
for ($i = 0; $i < $this->size; $i++) {
|
||||
if (($i & 0x07) == 0) {
|
||||
$result .= ' ';
|
||||
}
|
||||
$result .= ($this->get($i) ? 'X' : '.');
|
||||
}
|
||||
|
||||
public function hashCode()
|
||||
{
|
||||
return 31 * $this->size + hashCode($this->bits);
|
||||
}
|
||||
return (string)$result;
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
$result = '';
|
||||
for ($i = 0; $i < $this->size; $i++) {
|
||||
if (($i & 0x07) == 0) {
|
||||
$result .= ' ';
|
||||
}
|
||||
$result .= ($this->get($i) ? 'X' : '.');
|
||||
}
|
||||
|
||||
return (string)$result;
|
||||
}
|
||||
|
||||
public function _clone()
|
||||
{
|
||||
return new BitArray($this->bits, $this->size);
|
||||
}
|
||||
public function _clone(): \Zxing\Common\BitArray
|
||||
{
|
||||
return new BitArray($this->bits, $this->size);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -28,91 +28,88 @@ namespace Zxing\Common;
|
|||
*/
|
||||
final class BitSource
|
||||
{
|
||||
private int $byteOffset = 0;
|
||||
private int $bitOffset = 0;
|
||||
|
||||
private $bytes;
|
||||
private $byteOffset = 0;
|
||||
private $bitOffset = 0;
|
||||
/**
|
||||
* @param bytes $bytes from which this will read bits. Bits will be read from the first byte first.
|
||||
* Bits are read within a byte from most-significant to least-significant bit.
|
||||
*/
|
||||
public function __construct(private $bytes)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
|
||||
* Bits are read within a byte from most-significant to least-significant bit.
|
||||
*/
|
||||
public function __construct($bytes)
|
||||
{
|
||||
$this->bytes = $bytes;
|
||||
}
|
||||
/**
|
||||
* @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
|
||||
*/
|
||||
public function getBitOffset()
|
||||
{
|
||||
return $this->bitOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
|
||||
*/
|
||||
public function getBitOffset()
|
||||
{
|
||||
return $this->bitOffset;
|
||||
}
|
||||
/**
|
||||
* @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
|
||||
*/
|
||||
public function getByteOffset()
|
||||
{
|
||||
return $this->byteOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
|
||||
*/
|
||||
public function getByteOffset()
|
||||
{
|
||||
return $this->byteOffset;
|
||||
}
|
||||
/**
|
||||
* @param number $numBits of bits to read
|
||||
*
|
||||
* @return int representing the bits read. The bits will appear as the least-significant
|
||||
* bits of the int
|
||||
* @throws InvalidArgumentException if numBits isn't in [1,32] or more than is available
|
||||
*/
|
||||
public function readBits($numBits)
|
||||
{
|
||||
if ($numBits < 1 || $numBits > 32 || $numBits > $this->available()) {
|
||||
throw new \InvalidArgumentException(strval($numBits));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param 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 InvalidArgumentException if numBits isn't in [1,32] or more than is available
|
||||
*/
|
||||
public function readBits($numBits)
|
||||
{
|
||||
if ($numBits < 1 || $numBits > 32 || $numBits > $this->available()) {
|
||||
throw new \InvalidArgumentException(strval($numBits));
|
||||
}
|
||||
$result = 0;
|
||||
|
||||
$result = 0;
|
||||
// First, read remainder from current byte
|
||||
if ($this->bitOffset > 0) {
|
||||
$bitsLeft = 8 - $this->bitOffset;
|
||||
$toRead = $numBits < $bitsLeft ? $numBits : $bitsLeft;
|
||||
$bitsToNotRead = $bitsLeft - $toRead;
|
||||
$mask = (0xFF >> (8 - $toRead)) << $bitsToNotRead;
|
||||
$result = ($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead;
|
||||
$numBits -= $toRead;
|
||||
$this->bitOffset += $toRead;
|
||||
if ($this->bitOffset == 8) {
|
||||
$this->bitOffset = 0;
|
||||
$this->byteOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
// First, read remainder from current byte
|
||||
if ($this->bitOffset > 0) {
|
||||
$bitsLeft = 8 - $this->bitOffset;
|
||||
$toRead = $numBits < $bitsLeft ? $numBits : $bitsLeft;
|
||||
$bitsToNotRead = $bitsLeft - $toRead;
|
||||
$mask = (0xFF >> (8 - $toRead)) << $bitsToNotRead;
|
||||
$result = ($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead;
|
||||
$numBits -= $toRead;
|
||||
$this->bitOffset += $toRead;
|
||||
if ($this->bitOffset == 8) {
|
||||
$this->bitOffset = 0;
|
||||
$this->byteOffset++;
|
||||
}
|
||||
}
|
||||
// Next read whole bytes
|
||||
if ($numBits > 0) {
|
||||
while ($numBits >= 8) {
|
||||
$result = ($result << 8) | ($this->bytes[$this->byteOffset] & 0xFF);
|
||||
$this->byteOffset++;
|
||||
$numBits -= 8;
|
||||
}
|
||||
|
||||
// Next read whole bytes
|
||||
if ($numBits > 0) {
|
||||
while ($numBits >= 8) {
|
||||
$result = ($result << 8) | ($this->bytes[$this->byteOffset] & 0xFF);
|
||||
$this->byteOffset++;
|
||||
$numBits -= 8;
|
||||
}
|
||||
// Finally read a partial byte
|
||||
if ($numBits > 0) {
|
||||
$bitsToNotRead = 8 - $numBits;
|
||||
$mask = (0xFF >> $bitsToNotRead) << $bitsToNotRead;
|
||||
$result = ($result << $numBits) | (($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead);
|
||||
$this->bitOffset += $numBits;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally read a partial byte
|
||||
if ($numBits > 0) {
|
||||
$bitsToNotRead = 8 - $numBits;
|
||||
$mask = (0xFF >> $bitsToNotRead) << $bitsToNotRead;
|
||||
$result = ($result << $numBits) | (($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead);
|
||||
$this->bitOffset += $numBits;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of bits that can be read successfully
|
||||
*/
|
||||
public function available()
|
||||
{
|
||||
return 8 * (count($this->bytes) - $this->byteOffset) - $this->bitOffset;
|
||||
}
|
||||
/**
|
||||
* @return number of bits that can be read successfully
|
||||
*/
|
||||
public function available()
|
||||
{
|
||||
return 8 * ((is_countable($this->bytes) ? count($this->bytes) : 0) - $this->byteOffset) - $this->bitOffset;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,151 +8,147 @@ namespace Zxing\Common;
|
|||
*/
|
||||
final class CharacterSetECI
|
||||
{
|
||||
/**#@+
|
||||
* Character set constants.
|
||||
*/
|
||||
const CP437 = 0;
|
||||
const ISO8859_1 = 1;
|
||||
const ISO8859_2 = 4;
|
||||
const ISO8859_3 = 5;
|
||||
const ISO8859_4 = 6;
|
||||
const ISO8859_5 = 7;
|
||||
const ISO8859_6 = 8;
|
||||
const ISO8859_7 = 9;
|
||||
const ISO8859_8 = 10;
|
||||
const ISO8859_9 = 11;
|
||||
const ISO8859_10 = 12;
|
||||
const ISO8859_11 = 13;
|
||||
const ISO8859_12 = 14;
|
||||
const ISO8859_13 = 15;
|
||||
const ISO8859_14 = 16;
|
||||
const ISO8859_15 = 17;
|
||||
const ISO8859_16 = 18;
|
||||
const SJIS = 20;
|
||||
const CP1250 = 21;
|
||||
const CP1251 = 22;
|
||||
const CP1252 = 23;
|
||||
const CP1256 = 24;
|
||||
const UNICODE_BIG_UNMARKED = 25;
|
||||
const UTF8 = 26;
|
||||
const ASCII = 27;
|
||||
const BIG5 = 28;
|
||||
const GB18030 = 29;
|
||||
const EUC_KR = 30;
|
||||
/**
|
||||
* Map between character names and their ECI values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $nameToEci = [
|
||||
'ISO-8859-1' => self::ISO8859_1,
|
||||
'ISO-8859-2' => self::ISO8859_2,
|
||||
'ISO-8859-3' => self::ISO8859_3,
|
||||
'ISO-8859-4' => self::ISO8859_4,
|
||||
'ISO-8859-5' => self::ISO8859_5,
|
||||
'ISO-8859-6' => self::ISO8859_6,
|
||||
'ISO-8859-7' => self::ISO8859_7,
|
||||
'ISO-8859-8' => self::ISO8859_8,
|
||||
'ISO-8859-9' => self::ISO8859_9,
|
||||
'ISO-8859-10' => self::ISO8859_10,
|
||||
'ISO-8859-11' => self::ISO8859_11,
|
||||
'ISO-8859-12' => self::ISO8859_12,
|
||||
'ISO-8859-13' => self::ISO8859_13,
|
||||
'ISO-8859-14' => self::ISO8859_14,
|
||||
'ISO-8859-15' => self::ISO8859_15,
|
||||
'ISO-8859-16' => self::ISO8859_16,
|
||||
'SHIFT-JIS' => self::SJIS,
|
||||
'WINDOWS-1250' => self::CP1250,
|
||||
'WINDOWS-1251' => self::CP1251,
|
||||
'WINDOWS-1252' => self::CP1252,
|
||||
'WINDOWS-1256' => self::CP1256,
|
||||
'UTF-16BE' => self::UNICODE_BIG_UNMARKED,
|
||||
'UTF-8' => self::UTF8,
|
||||
'ASCII' => self::ASCII,
|
||||
'GBK' => self::GB18030,
|
||||
'EUC-KR' => self::EUC_KR,
|
||||
];
|
||||
/**#@-*/
|
||||
/**
|
||||
* Additional possible values for character sets.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $additionalValues = [
|
||||
self::CP437 => 2,
|
||||
self::ASCII => 170,
|
||||
];
|
||||
private static $name = null;
|
||||
/**#@+
|
||||
* Character set constants.
|
||||
*/
|
||||
public const CP437 = 0;
|
||||
public const ISO8859_1 = 1;
|
||||
public const ISO8859_2 = 4;
|
||||
public const ISO8859_3 = 5;
|
||||
public const ISO8859_4 = 6;
|
||||
public const ISO8859_5 = 7;
|
||||
public const ISO8859_6 = 8;
|
||||
public const ISO8859_7 = 9;
|
||||
public const ISO8859_8 = 10;
|
||||
public const ISO8859_9 = 11;
|
||||
public const ISO8859_10 = 12;
|
||||
public const ISO8859_11 = 13;
|
||||
public const ISO8859_12 = 14;
|
||||
public const ISO8859_13 = 15;
|
||||
public const ISO8859_14 = 16;
|
||||
public const ISO8859_15 = 17;
|
||||
public const ISO8859_16 = 18;
|
||||
public const SJIS = 20;
|
||||
public const CP1250 = 21;
|
||||
public const CP1251 = 22;
|
||||
public const CP1252 = 23;
|
||||
public const CP1256 = 24;
|
||||
public const UNICODE_BIG_UNMARKED = 25;
|
||||
public const UTF8 = 26;
|
||||
public const ASCII = 27;
|
||||
public const BIG5 = 28;
|
||||
public const GB18030 = 29;
|
||||
public const EUC_KR = 30;
|
||||
/**
|
||||
* Map between character names and their ECI values.
|
||||
*/
|
||||
private static array $nameToEci = [
|
||||
'ISO-8859-1' => self::ISO8859_1,
|
||||
'ISO-8859-2' => self::ISO8859_2,
|
||||
'ISO-8859-3' => self::ISO8859_3,
|
||||
'ISO-8859-4' => self::ISO8859_4,
|
||||
'ISO-8859-5' => self::ISO8859_5,
|
||||
'ISO-8859-6' => self::ISO8859_6,
|
||||
'ISO-8859-7' => self::ISO8859_7,
|
||||
'ISO-8859-8' => self::ISO8859_8,
|
||||
'ISO-8859-9' => self::ISO8859_9,
|
||||
'ISO-8859-10' => self::ISO8859_10,
|
||||
'ISO-8859-11' => self::ISO8859_11,
|
||||
'ISO-8859-12' => self::ISO8859_12,
|
||||
'ISO-8859-13' => self::ISO8859_13,
|
||||
'ISO-8859-14' => self::ISO8859_14,
|
||||
'ISO-8859-15' => self::ISO8859_15,
|
||||
'ISO-8859-16' => self::ISO8859_16,
|
||||
'SHIFT-JIS' => self::SJIS,
|
||||
'WINDOWS-1250' => self::CP1250,
|
||||
'WINDOWS-1251' => self::CP1251,
|
||||
'WINDOWS-1252' => self::CP1252,
|
||||
'WINDOWS-1256' => self::CP1256,
|
||||
'UTF-16BE' => self::UNICODE_BIG_UNMARKED,
|
||||
'UTF-8' => self::UTF8,
|
||||
'ASCII' => self::ASCII,
|
||||
'GBK' => self::GB18030,
|
||||
'EUC-KR' => self::EUC_KR,
|
||||
];
|
||||
/**#@-*/
|
||||
/**
|
||||
* Additional possible values for character sets.
|
||||
*/
|
||||
private static array $additionalValues = [
|
||||
self::CP437 => 2,
|
||||
self::ASCII => 170,
|
||||
];
|
||||
private static int|string|null $name = null;
|
||||
|
||||
/**
|
||||
* Gets character set ECI by value.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return CharacterSetEci|null
|
||||
*/
|
||||
public static function getCharacterSetECIByValue($value)
|
||||
{
|
||||
if ($value < 0 || $value >= 900) {
|
||||
throw new \InvalidArgumentException('Value must be between 0 and 900');
|
||||
}
|
||||
if (false !== ($key = array_search($value, self::$additionalValues))) {
|
||||
$value = $key;
|
||||
}
|
||||
array_search($value, self::$nameToEci);
|
||||
try {
|
||||
self::setName($value);
|
||||
/**
|
||||
* Gets character set ECI by value.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return CharacterSetEci|null
|
||||
*/
|
||||
public static function getCharacterSetECIByValue($value)
|
||||
{
|
||||
if ($value < 0 || $value >= 900) {
|
||||
throw new \InvalidArgumentException('Value must be between 0 and 900');
|
||||
}
|
||||
if (false !== ($key = array_search($value, self::$additionalValues))) {
|
||||
$value = $key;
|
||||
}
|
||||
array_search($value, self::$nameToEci);
|
||||
try {
|
||||
self::setName($value);
|
||||
|
||||
return new self($value);
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new self($value);
|
||||
} catch (\UnexpectedValueException) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static function setName($value)
|
||||
{
|
||||
foreach (self::$nameToEci as $name => $key) {
|
||||
if ($key == $value) {
|
||||
self::$name = $name;
|
||||
private static function setName($value)
|
||||
{
|
||||
foreach (self::$nameToEci as $name => $key) {
|
||||
if ($key == $value) {
|
||||
self::$name = $name;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (self::$name == null) {
|
||||
foreach (self::$additionalValues as $name => $key) {
|
||||
if ($key == $value) {
|
||||
self::$name = $name;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (self::$name == null) {
|
||||
foreach (self::$additionalValues as $name => $key) {
|
||||
if ($key == $value) {
|
||||
self::$name = $name;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets character set ECI name.
|
||||
*
|
||||
* @return character set ECI name|null
|
||||
*/
|
||||
public static function name()
|
||||
{
|
||||
return self::$name;
|
||||
}
|
||||
/**
|
||||
* Gets character set ECI name.
|
||||
*
|
||||
* @return character set ECI name|null
|
||||
*/
|
||||
public static function name()
|
||||
{
|
||||
return self::$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets character set ECI by name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return CharacterSetEci|null
|
||||
*/
|
||||
public static function getCharacterSetECIByName($name)
|
||||
{
|
||||
$name = strtoupper($name);
|
||||
if (isset(self::$nameToEci[$name])) {
|
||||
return new self(self::$nameToEci[$name]);
|
||||
}
|
||||
/**
|
||||
* Gets character set ECI by name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return CharacterSetEci|null
|
||||
*/
|
||||
public static function getCharacterSetECIByName($name)
|
||||
{
|
||||
$name = strtoupper($name);
|
||||
if (isset(self::$nameToEci[$name])) {
|
||||
return new self(self::$nameToEci[$name]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,97 +26,86 @@ namespace Zxing\Common;
|
|||
*/
|
||||
final class DecoderResult
|
||||
{
|
||||
|
||||
private $rawBytes;
|
||||
private $text;
|
||||
private $byteSegments;
|
||||
private $ecLevel;
|
||||
private $errorsCorrected;
|
||||
private $erasures;
|
||||
private $other;
|
||||
private $structuredAppendParity;
|
||||
private $structuredAppendSequenceNumber;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
private $errorsCorrected;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
private $erasures;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
private $other;
|
||||
|
||||
|
||||
public function __construct(
|
||||
$rawBytes,
|
||||
$text,
|
||||
$byteSegments,
|
||||
$ecLevel,
|
||||
$saSequence = -1,
|
||||
$saParity = -1
|
||||
) {
|
||||
$this->rawBytes = $rawBytes;
|
||||
$this->text = $text;
|
||||
$this->byteSegments = $byteSegments;
|
||||
$this->ecLevel = $ecLevel;
|
||||
$this->structuredAppendParity = $saParity;
|
||||
$this->structuredAppendSequenceNumber = $saSequence;
|
||||
}
|
||||
public function __construct(private $rawBytes, private $text, private $byteSegments, private $ecLevel, private $structuredAppendSequenceNumber = -1, private $structuredAppendParity = -1)
|
||||
{
|
||||
}
|
||||
|
||||
public function getRawBytes()
|
||||
{
|
||||
return $this->rawBytes;
|
||||
}
|
||||
public function getRawBytes()
|
||||
{
|
||||
return $this->rawBytes;
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
public function getText()
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function getByteSegments()
|
||||
{
|
||||
return $this->byteSegments;
|
||||
}
|
||||
public function getByteSegments()
|
||||
{
|
||||
return $this->byteSegments;
|
||||
}
|
||||
|
||||
public function getECLevel()
|
||||
{
|
||||
return $this->ecLevel;
|
||||
}
|
||||
public function getECLevel()
|
||||
{
|
||||
return $this->ecLevel;
|
||||
}
|
||||
|
||||
public function getErrorsCorrected()
|
||||
{
|
||||
return $this->errorsCorrected;
|
||||
}
|
||||
public function getErrorsCorrected()
|
||||
{
|
||||
return $this->errorsCorrected;
|
||||
}
|
||||
|
||||
public function setErrorsCorrected($errorsCorrected)
|
||||
{
|
||||
$this->errorsCorrected = $errorsCorrected;
|
||||
}
|
||||
public function setErrorsCorrected($errorsCorrected): void
|
||||
{
|
||||
$this->errorsCorrected = $errorsCorrected;
|
||||
}
|
||||
|
||||
public function getErasures()
|
||||
{
|
||||
return $this->erasures;
|
||||
}
|
||||
public function getErasures()
|
||||
{
|
||||
return $this->erasures;
|
||||
}
|
||||
|
||||
public function setErasures($erasures)
|
||||
{
|
||||
$this->erasures = $erasures;
|
||||
}
|
||||
public function setErasures($erasures): void
|
||||
{
|
||||
$this->erasures = $erasures;
|
||||
}
|
||||
|
||||
public function getOther()
|
||||
{
|
||||
return $this->other;
|
||||
}
|
||||
public function getOther()
|
||||
{
|
||||
return $this->other;
|
||||
}
|
||||
|
||||
public function setOther($other)
|
||||
{
|
||||
$this->other = $other;
|
||||
}
|
||||
public function setOther($other): void
|
||||
{
|
||||
$this->other = $other;
|
||||
}
|
||||
|
||||
public function hasStructuredAppend()
|
||||
{
|
||||
return $this->structuredAppendParity >= 0 && $this->structuredAppendSequenceNumber >= 0;
|
||||
}
|
||||
public function hasStructuredAppend()
|
||||
{
|
||||
return $this->structuredAppendParity >= 0 && $this->structuredAppendSequenceNumber >= 0;
|
||||
}
|
||||
|
||||
public function getStructuredAppendParity()
|
||||
{
|
||||
return $this->structuredAppendParity;
|
||||
}
|
||||
|
||||
public function getStructuredAppendSequenceNumber()
|
||||
{
|
||||
return $this->structuredAppendSequenceNumber;
|
||||
}
|
||||
public function getStructuredAppendParity()
|
||||
{
|
||||
return $this->structuredAppendParity;
|
||||
}
|
||||
|
||||
public function getStructuredAppendSequenceNumber()
|
||||
{
|
||||
return $this->structuredAppendSequenceNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,70 +24,92 @@ use Zxing\NotFoundException;
|
|||
*/
|
||||
final class DefaultGridSampler extends GridSampler
|
||||
{
|
||||
//@Override
|
||||
public function sampleGrid(
|
||||
$image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$p1ToX, $p1ToY,
|
||||
$p2ToX, $p2ToY,
|
||||
$p3ToX, $p3ToY,
|
||||
$p4ToX, $p4ToY,
|
||||
$p1FromX, $p1FromY,
|
||||
$p2FromX, $p2FromY,
|
||||
$p3FromX, $p3FromY,
|
||||
$p4FromX, $p4FromY
|
||||
) {
|
||||
//@Override
|
||||
public function sampleGrid(
|
||||
$image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$p1ToX,
|
||||
$p1ToY,
|
||||
$p2ToX,
|
||||
$p2ToY,
|
||||
$p3ToX,
|
||||
$p3ToY,
|
||||
$p4ToX,
|
||||
$p4ToY,
|
||||
$p1FromX,
|
||||
$p1FromY,
|
||||
$p2FromX,
|
||||
$p2FromY,
|
||||
$p3FromX,
|
||||
$p3FromY,
|
||||
$p4FromX,
|
||||
$p4FromY
|
||||
) {
|
||||
$transform = PerspectiveTransform::quadrilateralToQuadrilateral(
|
||||
$p1ToX,
|
||||
$p1ToY,
|
||||
$p2ToX,
|
||||
$p2ToY,
|
||||
$p3ToX,
|
||||
$p3ToY,
|
||||
$p4ToX,
|
||||
$p4ToY,
|
||||
$p1FromX,
|
||||
$p1FromY,
|
||||
$p2FromX,
|
||||
$p2FromY,
|
||||
$p3FromX,
|
||||
$p3FromY,
|
||||
$p4FromX,
|
||||
$p4FromY
|
||||
);
|
||||
|
||||
$transform = PerspectiveTransform::quadrilateralToQuadrilateral(
|
||||
$p1ToX, $p1ToY, $p2ToX, $p2ToY, $p3ToX, $p3ToY, $p4ToX, $p4ToY,
|
||||
$p1FromX, $p1FromY, $p2FromX, $p2FromY, $p3FromX, $p3FromY, $p4FromX, $p4FromY);
|
||||
return $this->sampleGrid_($image, $dimensionX, $dimensionY, $transform);
|
||||
}
|
||||
|
||||
return $this->sampleGrid_($image, $dimensionX, $dimensionY, $transform);
|
||||
}
|
||||
//@Override
|
||||
public function sampleGrid_(
|
||||
$image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$transform
|
||||
) {
|
||||
if ($dimensionX <= 0 || $dimensionY <= 0) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$bits = new BitMatrix($dimensionX, $dimensionY);
|
||||
$points = fill_array(0, 2 * $dimensionX, 0.0);
|
||||
for ($y = 0; $y < $dimensionY; $y++) {
|
||||
$max = is_countable($points) ? count($points) : 0;
|
||||
$iValue = (float)$y + 0.5;
|
||||
for ($x = 0; $x < $max; $x += 2) {
|
||||
$points[$x] = (float)($x / 2) + 0.5;
|
||||
$points[$x + 1] = $iValue;
|
||||
}
|
||||
$transform->transformPoints($points);
|
||||
// Quick check to see if points transformed to something inside the image;
|
||||
// sufficient to check the endpoints
|
||||
self::checkAndNudgePoints($image, $points);
|
||||
try {
|
||||
for ($x = 0; $x < $max; $x += 2) {
|
||||
if ($image->get((int)$points[$x], (int)$points[$x + 1])) {
|
||||
// Black(-ish) pixel
|
||||
$bits->set($x / 2, $y);
|
||||
}
|
||||
}
|
||||
} catch (\Exception) {//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 NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function sampleGrid_(
|
||||
$image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$transform
|
||||
) {
|
||||
if ($dimensionX <= 0 || $dimensionY <= 0) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$bits = new BitMatrix($dimensionX, $dimensionY);
|
||||
$points = fill_array(0, 2 * $dimensionX, 0.0);
|
||||
for ($y = 0; $y < $dimensionY; $y++) {
|
||||
$max = count($points);
|
||||
$iValue = (float)$y + 0.5;
|
||||
for ($x = 0; $x < $max; $x += 2) {
|
||||
$points[$x] = (float)($x / 2) + 0.5;
|
||||
$points[$x + 1] = $iValue;
|
||||
}
|
||||
$transform->transformPoints($points);
|
||||
// Quick check to see if points transformed to something inside the image;
|
||||
// sufficient to check the endpoints
|
||||
$this->checkAndNudgePoints($image, $points);
|
||||
try {
|
||||
for ($x = 0; $x < $max; $x += 2) {
|
||||
if ($image->get((int)$points[$x], (int)$points[$x + 1])) {
|
||||
// Black(-ish) pixel
|
||||
$bits->set($x / 2, $y);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $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 NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
}
|
||||
|
||||
return $bits;
|
||||
}
|
||||
return $bits;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,31 +19,30 @@ namespace Zxing\Common\Detector;
|
|||
|
||||
final class MathUtils
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Ends up being a bit faster than {@link Math#round(float)}. This merely rounds its
|
||||
* argument to the nearest int, where x.5 rounds up to x+1. Semantics of this shortcut
|
||||
* differ slightly from {@link Math#round(float)} in that half rounds down for negative
|
||||
* values. -2.5 rounds to -3, not -2. For purposes here it makes no difference.
|
||||
*
|
||||
* @param float $d real value to round
|
||||
*
|
||||
* @return int {@code int}
|
||||
*/
|
||||
public static function round($d)
|
||||
{
|
||||
return (int)($d + ($d < 0.0 ? -0.5 : 0.5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends up being a bit faster than {@link Math#round(float)}. This merely rounds its
|
||||
* argument to the nearest int, where x.5 rounds up to x+1. Semantics of this shortcut
|
||||
* differ slightly from {@link Math#round(float)} in that half rounds down for negative
|
||||
* values. -2.5 rounds to -3, not -2. For purposes here it makes no difference.
|
||||
*
|
||||
* @param float $d real value to round
|
||||
*
|
||||
* @return int $nearest {@code int}
|
||||
*/
|
||||
public static function round($d)
|
||||
{
|
||||
return (int)($d + ($d < 0.0 ? -0.5 : 0.5));
|
||||
}
|
||||
public static function distance($aX, $aY, $bX, $bY)
|
||||
{
|
||||
$xDiff = $aX - $bX;
|
||||
$yDiff = $aY - $bY;
|
||||
|
||||
public static function distance($aX, $aY, $bX, $bY)
|
||||
{
|
||||
$xDiff = $aX - $bX;
|
||||
$yDiff = $aY - $bY;
|
||||
|
||||
return (float)sqrt($xDiff * $xDiff + $yDiff * $yDiff);
|
||||
}
|
||||
return (float)sqrt($xDiff * $xDiff + $yDiff * $yDiff);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
namespace Zxing\Common\Detector;
|
||||
|
||||
use Zxing\BinaryBitmap;
|
||||
use \Zxing\NotFoundException;
|
||||
use \Zxing\ResultPoint;
|
||||
use Zxing\NotFoundException;
|
||||
use Zxing\ResultPoint;
|
||||
|
||||
/*
|
||||
*
|
||||
|
|
@ -35,200 +35,242 @@ import com.google.zxing.common.BitMatrix;
|
|||
*/
|
||||
class MonochromeRectangleDetector
|
||||
{
|
||||
private static $MAX_MODULES = 32;
|
||||
private $image;
|
||||
private static int $MAX_MODULES = 32;
|
||||
|
||||
public function __construct(BinaryBitmap $image)
|
||||
{
|
||||
$this->image = $image;
|
||||
public function __construct(private readonly BinaryBitmap $image)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
|
||||
* white, in an image.</p>
|
||||
*
|
||||
* @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
|
||||
* last points are opposed on the diagonal, as are the second and third. The first point will be
|
||||
* the topmost point and the last, the bottommost. The second point will be leftmost and the
|
||||
* third, the rightmost
|
||||
* @throws NotFoundException if no Data Matrix Code can be found
|
||||
*/
|
||||
public function detect(): \Zxing\ResultPoint
|
||||
{
|
||||
$height = $this->image->getHeight();
|
||||
$width = $this->image->getWidth();
|
||||
$halfHeight = $height / 2;
|
||||
$halfWidth = $width / 2;
|
||||
|
||||
/**
|
||||
* <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
|
||||
* white, in an image.</p>
|
||||
*
|
||||
* @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
|
||||
* last points are opposed on the diagonal, as are the second and third. The first point will be
|
||||
* the topmost point and the last, the bottommost. The second point will be leftmost and the
|
||||
* third, the rightmost
|
||||
* @throws NotFoundException if no Data Matrix Code can be found
|
||||
*/
|
||||
public function detect()
|
||||
{
|
||||
|
||||
$height = $this->image->getHeight();
|
||||
$width = $this->image->getWidth();
|
||||
$halfHeight = $height / 2;
|
||||
$halfWidth = $width / 2;
|
||||
|
||||
$deltaY = max(1, $height / (self::$MAX_MODULES * 8));
|
||||
$deltaX = max(1, $width / (self::$MAX_MODULES * 8));
|
||||
$deltaY = max(1, $height / (self::$MAX_MODULES * 8));
|
||||
$deltaX = max(1, $width / (self::$MAX_MODULES * 8));
|
||||
|
||||
|
||||
$top = 0;
|
||||
$bottom = $height;
|
||||
$left = 0;
|
||||
$right = $width;
|
||||
$pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
|
||||
$halfHeight, -$deltaY, $top, $bottom, $halfWidth / 2);
|
||||
$top = (int)$pointA->getY() - 1;
|
||||
$pointB = $this->findCornerFromCenter($halfWidth, -$deltaX, $left, $right,
|
||||
$halfHeight, 0, $top, $bottom, $halfHeight / 2);
|
||||
$left = (int)$pointB->getX() - 1;
|
||||
$pointC = $this->findCornerFromCenter($halfWidth, $deltaX, $left, $right,
|
||||
$halfHeight, 0, $top, $bottom, $halfHeight / 2);
|
||||
$right = (int)$pointC->getX() + 1;
|
||||
$pointD = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
|
||||
$halfHeight, $deltaY, $top, $bottom, $halfWidth / 2);
|
||||
$bottom = (int)$pointD->getY() + 1;
|
||||
$top = 0;
|
||||
$bottom = $height;
|
||||
$left = 0;
|
||||
$right = $width;
|
||||
$pointA = $this->findCornerFromCenter(
|
||||
$halfWidth,
|
||||
0,
|
||||
$left,
|
||||
$right,
|
||||
$halfHeight,
|
||||
-$deltaY,
|
||||
$top,
|
||||
$bottom,
|
||||
$halfWidth / 2
|
||||
);
|
||||
$top = (int)$pointA->getY() - 1;
|
||||
$pointB = $this->findCornerFromCenter(
|
||||
$halfWidth,
|
||||
-$deltaX,
|
||||
$left,
|
||||
$right,
|
||||
$halfHeight,
|
||||
0,
|
||||
$top,
|
||||
$bottom,
|
||||
$halfHeight / 2
|
||||
);
|
||||
$left = (int)$pointB->getX() - 1;
|
||||
$pointC = $this->findCornerFromCenter(
|
||||
$halfWidth,
|
||||
$deltaX,
|
||||
$left,
|
||||
$right,
|
||||
$halfHeight,
|
||||
0,
|
||||
$top,
|
||||
$bottom,
|
||||
$halfHeight / 2
|
||||
);
|
||||
$right = (int)$pointC->getX() + 1;
|
||||
$pointD = $this->findCornerFromCenter(
|
||||
$halfWidth,
|
||||
0,
|
||||
$left,
|
||||
$right,
|
||||
$halfHeight,
|
||||
$deltaY,
|
||||
$top,
|
||||
$bottom,
|
||||
$halfWidth / 2
|
||||
);
|
||||
$bottom = (int)$pointD->getY() + 1;
|
||||
|
||||
// Go try to find po$A again with better information -- might have been off at first.
|
||||
$pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
|
||||
$halfHeight, -$deltaY, $top, $bottom, $halfWidth / 4);
|
||||
// Go try to find po$A again with better information -- might have been off at first.
|
||||
$pointA = $this->findCornerFromCenter(
|
||||
$halfWidth,
|
||||
0,
|
||||
$left,
|
||||
$right,
|
||||
$halfHeight,
|
||||
-$deltaY,
|
||||
$top,
|
||||
$bottom,
|
||||
$halfWidth / 4
|
||||
);
|
||||
|
||||
return new ResultPoint($pointA, $pointB, $pointC, $pointD);
|
||||
|
||||
}
|
||||
return new ResultPoint($pointA, $pointB, $pointC, $pointD);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
|
||||
* point which should be within the barcode.
|
||||
*
|
||||
* @param float $centerX center's x component (horizontal)
|
||||
* @param float $deltaX same as deltaY but change in x per step instead
|
||||
* @param float $left minimum value of x
|
||||
* @param float $right maximum value of x
|
||||
* @param float $centerY center's y component (vertical)
|
||||
* @param float $deltaY change in y per step. If scanning up this is negative; down, positive;
|
||||
* left or right, 0
|
||||
* @param float $top minimum value of y to search through (meaningless when di == 0)
|
||||
* @param float $bottom maximum value of y
|
||||
* @param float $maxWhiteRun maximum run of white pixels that can still be considered to be within
|
||||
* the barcode
|
||||
*
|
||||
* @return ResultPoint $a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
|
||||
* @throws NotFoundException if such a point cannot be found
|
||||
*/
|
||||
private function findCornerFromCenter($centerX,
|
||||
$deltaX,
|
||||
$left,
|
||||
$right,
|
||||
$centerY,
|
||||
$deltaY,
|
||||
$top,
|
||||
$bottom,
|
||||
$maxWhiteRun)
|
||||
{
|
||||
$lastRange = null;
|
||||
for ($y = $centerY, $x = $centerX;
|
||||
$y < $bottom && $y >= $top && $x < $right && $x >= $left;
|
||||
$y += $deltaY, $x += $deltaX) {
|
||||
$range = 0;
|
||||
if ($deltaX == 0) {
|
||||
// horizontal slices, up and down
|
||||
$range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, true);
|
||||
} else {
|
||||
// vertical slices, left and right
|
||||
$range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, false);
|
||||
}
|
||||
if ($range == null) {
|
||||
if ($lastRange == null) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
// lastRange was found
|
||||
if ($deltaX == 0) {
|
||||
$lastY = $y - $deltaY;
|
||||
if ($lastRange[0] < $centerX) {
|
||||
if ($lastRange[1] > $centerX) {
|
||||
// straddle, choose one or the other based on direction
|
||||
return new ResultPoint($deltaY > 0 ? $lastRange[0] : $lastRange[1], $lastY);
|
||||
}
|
||||
/**
|
||||
* Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
|
||||
* point which should be within the barcode.
|
||||
*
|
||||
* @param float $centerX center's x component (horizontal)
|
||||
* @param float $deltaX same as deltaY but change in x per step instead
|
||||
* @param float $left minimum value of x
|
||||
* @param float $right maximum value of x
|
||||
* @param float $centerY center's y component (vertical)
|
||||
* @param float $deltaY change in y per step. If scanning up this is negative; down, positive;
|
||||
* left or right, 0
|
||||
* @param float $top minimum value of y to search through (meaningless when di == 0)
|
||||
* @param float $bottom maximum value of y
|
||||
* @param float $maxWhiteRun maximum run of white pixels that can still be considered to be within
|
||||
* the barcode
|
||||
*
|
||||
* @return ResultPoint {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
|
||||
* @throws NotFoundException if such a point cannot be found
|
||||
*/
|
||||
private function findCornerFromCenter(
|
||||
$centerX,
|
||||
$deltaX,
|
||||
$left,
|
||||
$right,
|
||||
$centerY,
|
||||
$deltaY,
|
||||
$top,
|
||||
$bottom,
|
||||
$maxWhiteRun
|
||||
): \Zxing\ResultPoint
|
||||
{
|
||||
$lastRange = null;
|
||||
for ($y = $centerY, $x = $centerX;
|
||||
$y < $bottom && $y >= $top && $x < $right && $x >= $left;
|
||||
$y += $deltaY, $x += $deltaX) {
|
||||
$range = 0;
|
||||
if ($deltaX == 0) {
|
||||
// horizontal slices, up and down
|
||||
$range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, true);
|
||||
} else {
|
||||
// vertical slices, left and right
|
||||
$range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, false);
|
||||
}
|
||||
if ($range == null) {
|
||||
if ($lastRange == null) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
// lastRange was found
|
||||
if ($deltaX == 0) {
|
||||
$lastY = $y - $deltaY;
|
||||
if ($lastRange[0] < $centerX) {
|
||||
if ($lastRange[1] > $centerX) {
|
||||
// straddle, choose one or the other based on direction
|
||||
return new ResultPoint($deltaY > 0 ? $lastRange[0] : $lastRange[1], $lastY);
|
||||
}
|
||||
|
||||
return new ResultPoint($lastRange[0], $lastY);
|
||||
} else {
|
||||
return new ResultPoint($lastRange[1], $lastY);
|
||||
}
|
||||
} else {
|
||||
$lastX = $x - $deltaX;
|
||||
if ($lastRange[0] < $centerY) {
|
||||
if ($lastRange[1] > $centerY) {
|
||||
return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]);
|
||||
}
|
||||
return new ResultPoint($lastRange[0], $lastY);
|
||||
} else {
|
||||
return new ResultPoint($lastRange[1], $lastY);
|
||||
}
|
||||
} else {
|
||||
$lastX = $x - $deltaX;
|
||||
if ($lastRange[0] < $centerY) {
|
||||
if ($lastRange[1] > $centerY) {
|
||||
return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]);
|
||||
}
|
||||
|
||||
return new ResultPoint($lastX, $lastRange[0]);
|
||||
} else {
|
||||
return new ResultPoint($lastX, $lastRange[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$lastRange = $range;
|
||||
}
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
return new ResultPoint($lastX, $lastRange[0]);
|
||||
} else {
|
||||
return new ResultPoint($lastX, $lastRange[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$lastRange = $range;
|
||||
}
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the start and end of a region of pixels, either horizontally or vertically, that could
|
||||
* be part of a Data Matrix barcode.
|
||||
*
|
||||
* @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
|
||||
* where we are scanning. If scanning vertically it's the column, the fixed horizontal location
|
||||
* @param maxWhiteRun largest run of white pixels that can still be considered part of the
|
||||
* barcode region
|
||||
* @param minDim minimum pixel location, horizontally or vertically, to consider
|
||||
* @param maxDim maximum pixel location, horizontally or vertically, to consider
|
||||
* @param horizontal if true, we're scanning left-right, instead of up-down
|
||||
*
|
||||
* @return int[] with start and end of found range, or null if no such range is found
|
||||
* (e.g. only white was found)
|
||||
*/
|
||||
/**
|
||||
* Computes the start and end of a region of pixels, either horizontally or vertically, that could
|
||||
* be part of a Data Matrix barcode.
|
||||
*
|
||||
* @param if $fixedDimension scanning horizontally, this is the row (the fixed vertical location)
|
||||
* where we are scanning. If scanning vertically it's the column, the fixed horizontal location
|
||||
* @param largest $maxWhiteRun run of white pixels that can still be considered part of the
|
||||
* barcode region
|
||||
* @param minimum $minDim pixel location, horizontally or vertically, to consider
|
||||
* @param maximum $maxDim pixel location, horizontally or vertically, to consider
|
||||
* @param if $horizontal true, we're scanning left-right, instead of up-down
|
||||
*
|
||||
* @return int[] with start and end of found range, or null if no such range is found
|
||||
* (e.g. only white was found)
|
||||
*/
|
||||
|
||||
private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal)
|
||||
{
|
||||
$center = ($minDim + $maxDim) / 2;
|
||||
private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal)
|
||||
{
|
||||
$center = ($minDim + $maxDim) / 2;
|
||||
|
||||
// Scan left/up first
|
||||
$start = $center;
|
||||
while ($start >= $minDim) {
|
||||
if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) {
|
||||
$start--;
|
||||
} else {
|
||||
$whiteRunStart = $start;
|
||||
do {
|
||||
$start--;
|
||||
} while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) :
|
||||
$this->image->get($fixedDimension, $start)));
|
||||
$whiteRunSize = $whiteRunStart - $start;
|
||||
if ($start < $minDim || $whiteRunSize > $maxWhiteRun) {
|
||||
$start = $whiteRunStart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$start++;
|
||||
// Scan left/up first
|
||||
$start = $center;
|
||||
while ($start >= $minDim) {
|
||||
if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) {
|
||||
$start--;
|
||||
} else {
|
||||
$whiteRunStart = $start;
|
||||
do {
|
||||
$start--;
|
||||
} while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) :
|
||||
$this->image->get($fixedDimension, $start)));
|
||||
$whiteRunSize = $whiteRunStart - $start;
|
||||
if ($start < $minDim || $whiteRunSize > $maxWhiteRun) {
|
||||
$start = $whiteRunStart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$start++;
|
||||
|
||||
// Then try right/down
|
||||
$end = $center;
|
||||
while ($end < $maxDim) {
|
||||
if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) {
|
||||
$end++;
|
||||
} else {
|
||||
$whiteRunStart = $end;
|
||||
do {
|
||||
$end++;
|
||||
} while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) :
|
||||
$this->image->get($fixedDimension, $end)));
|
||||
$whiteRunSize = $end - $whiteRunStart;
|
||||
if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) {
|
||||
$end = $whiteRunStart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$end--;
|
||||
// Then try right/down
|
||||
$end = $center;
|
||||
while ($end < $maxDim) {
|
||||
if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) {
|
||||
$end++;
|
||||
} else {
|
||||
$whiteRunStart = $end;
|
||||
do {
|
||||
$end++;
|
||||
} while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) :
|
||||
$this->image->get($fixedDimension, $end)));
|
||||
$whiteRunSize = $end - $whiteRunStart;
|
||||
if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) {
|
||||
$end = $whiteRunStart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$end--;
|
||||
|
||||
return $end > $start ? [$start, $end] : null;
|
||||
}
|
||||
}
|
||||
return $end > $start ? [$start, $end] : null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,22 +26,17 @@ namespace Zxing\Common;
|
|||
*/
|
||||
class DetectorResult
|
||||
{
|
||||
private $bits;
|
||||
private $points;
|
||||
public function __construct(private $bits, private $points)
|
||||
{
|
||||
}
|
||||
|
||||
public function __construct($bits, $points)
|
||||
{
|
||||
$this->bits = $bits;
|
||||
$this->points = $points;
|
||||
}
|
||||
final public function getBits()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
|
||||
public final function getBits()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
|
||||
public final function getPoints()
|
||||
{
|
||||
return $this->points;
|
||||
}
|
||||
final public function getPoints()
|
||||
{
|
||||
return $this->points;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
namespace Zxing\Common;
|
||||
|
||||
use Zxing\Binarizer;
|
||||
use Zxing\LuminanceSource;
|
||||
use Zxing\NotFoundException;
|
||||
|
||||
/**
|
||||
|
|
@ -34,174 +33,177 @@ use Zxing\NotFoundException;
|
|||
*/
|
||||
class GlobalHistogramBinarizer extends Binarizer
|
||||
{
|
||||
private static $LUMINANCE_BITS = 5;
|
||||
private static $LUMINANCE_SHIFT = 3;
|
||||
private static $LUMINANCE_BUCKETS = 32;
|
||||
private static int $LUMINANCE_BITS = 5;
|
||||
private static int $LUMINANCE_SHIFT = 3;
|
||||
private static int $LUMINANCE_BUCKETS = 32;
|
||||
|
||||
private static $EMPTY = [];
|
||||
private static array $EMPTY = [];
|
||||
|
||||
private $luminances = [];
|
||||
private $buckets = [];
|
||||
private $source = [];
|
||||
private array $luminances = [];
|
||||
private array $buckets = [];
|
||||
/**
|
||||
* @var mixed|\Zxing\LuminanceSource
|
||||
*/
|
||||
private $source = [];
|
||||
|
||||
public function __construct($source)
|
||||
{
|
||||
self::$LUMINANCE_SHIFT = 8 - self::$LUMINANCE_BITS;
|
||||
self::$LUMINANCE_BUCKETS = 1 << self::$LUMINANCE_BITS;
|
||||
public function __construct($source)
|
||||
{
|
||||
self::$LUMINANCE_SHIFT = 8 - self::$LUMINANCE_BITS;
|
||||
self::$LUMINANCE_BUCKETS = 1 << self::$LUMINANCE_BITS;
|
||||
|
||||
parent::__construct($source);
|
||||
parent::__construct($source);
|
||||
|
||||
$this->luminances = self::$EMPTY;
|
||||
$this->buckets = fill_array(0, self::$LUMINANCE_BUCKETS, 0);
|
||||
$this->source = $source;
|
||||
}
|
||||
$this->luminances = self::$EMPTY;
|
||||
$this->buckets = fill_array(0, self::$LUMINANCE_BUCKETS, 0);
|
||||
$this->source = $source;
|
||||
}
|
||||
|
||||
// Applies simple sharpening to the row data to improve performance of the 1D Readers.
|
||||
public function getBlackRow($y, $row = null)
|
||||
{
|
||||
$this->source = $this->getLuminanceSource();
|
||||
$width = $this->source->getWidth();
|
||||
if ($row == null || $row->getSize() < $width) {
|
||||
$row = new BitArray($width);
|
||||
} else {
|
||||
$row->clear();
|
||||
}
|
||||
// Applies simple sharpening to the row data to improve performance of the 1D Readers.
|
||||
public function getBlackRow($y, $row = null)
|
||||
{
|
||||
$this->source = $this->getLuminanceSource();
|
||||
$width = $this->source->getWidth();
|
||||
if ($row == null || $row->getSize() < $width) {
|
||||
$row = new BitArray($width);
|
||||
} else {
|
||||
$row->clear();
|
||||
}
|
||||
|
||||
$this->initArrays($width);
|
||||
$localLuminances = $this->source->getRow($y, $this->luminances);
|
||||
$localBuckets = $this->buckets;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$pixel = $localLuminances[$x] & 0xff;
|
||||
$localBuckets[$pixel >> self::$LUMINANCE_SHIFT]++;
|
||||
}
|
||||
$blackPoint = self::estimateBlackPoint($localBuckets);
|
||||
$this->initArrays($width);
|
||||
$localLuminances = $this->source->getRow($y, $this->luminances);
|
||||
$localBuckets = $this->buckets;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$pixel = $localLuminances[$x] & 0xff;
|
||||
$localBuckets[$pixel >> self::$LUMINANCE_SHIFT]++;
|
||||
}
|
||||
$blackPoint = self::estimateBlackPoint($localBuckets);
|
||||
|
||||
$left = $localLuminances[0] & 0xff;
|
||||
$center = $localLuminances[1] & 0xff;
|
||||
for ($x = 1; $x < $width - 1; $x++) {
|
||||
$right = $localLuminances[$x + 1] & 0xff;
|
||||
// A simple -1 4 -1 box filter with a weight of 2.
|
||||
$luminance = (($center * 4) - $left - $right) / 2;
|
||||
if ($luminance < $blackPoint) {
|
||||
$row->set($x);
|
||||
}
|
||||
$left = $center;
|
||||
$center = $right;
|
||||
}
|
||||
$left = $localLuminances[0] & 0xff;
|
||||
$center = $localLuminances[1] & 0xff;
|
||||
for ($x = 1; $x < $width - 1; $x++) {
|
||||
$right = $localLuminances[$x + 1] & 0xff;
|
||||
// A simple -1 4 -1 box filter with a weight of 2.
|
||||
$luminance = (($center * 4) - $left - $right) / 2;
|
||||
if ($luminance < $blackPoint) {
|
||||
$row->set($x);
|
||||
}
|
||||
$left = $center;
|
||||
$center = $right;
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
|
||||
private function initArrays($luminanceSize)
|
||||
{
|
||||
if (count($this->luminances) < $luminanceSize) {
|
||||
$this->luminances = [];
|
||||
}
|
||||
for ($x = 0; $x < self::$LUMINANCE_BUCKETS; $x++) {
|
||||
$this->buckets[$x] = 0;
|
||||
}
|
||||
}
|
||||
// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
|
||||
private function initArrays($luminanceSize): void
|
||||
{
|
||||
if (count($this->luminances) < $luminanceSize) {
|
||||
$this->luminances = [];
|
||||
}
|
||||
for ($x = 0; $x < self::$LUMINANCE_BUCKETS; $x++) {
|
||||
$this->buckets[$x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static function estimateBlackPoint($buckets)
|
||||
{
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
private static function estimateBlackPoint($buckets)
|
||||
{
|
||||
// Find the tallest peak in the histogram.
|
||||
$numBuckets = is_countable($buckets) ? count($buckets) : 0;
|
||||
$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;
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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 NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
// 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 NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
return $bestValley << self::$LUMINANCE_SHIFT;
|
||||
}
|
||||
|
||||
public function getBlackMatrix()
|
||||
{
|
||||
$source = $this->getLuminanceSource();
|
||||
$width = $source->getWidth();
|
||||
$height = $source->getHeight();
|
||||
$matrix = new BitMatrix($width, $height);
|
||||
public function getBlackMatrix()
|
||||
{
|
||||
$source = $this->getLuminanceSource();
|
||||
$width = $source->getWidth();
|
||||
$height = $source->getHeight();
|
||||
$matrix = new BitMatrix($width, $height);
|
||||
|
||||
// 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.
|
||||
$this->initArrays($width);
|
||||
$localBuckets = $this->buckets;
|
||||
for ($y = 1; $y < 5; $y++) {
|
||||
$row = (int)($height * $y / 5);
|
||||
$localLuminances = $source->getRow($row, $this->luminances);
|
||||
$right = (int)(($width * 4) / 5);
|
||||
for ($x = (int)($width / 5); $x < $right; $x++) {
|
||||
$pixel = ($localLuminances[(int)($x)] & 0xff);
|
||||
$localBuckets[($pixel >> self::$LUMINANCE_SHIFT)]++;
|
||||
}
|
||||
}
|
||||
$blackPoint = self::estimateBlackPoint($localBuckets);
|
||||
// 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.
|
||||
$this->initArrays($width);
|
||||
$localBuckets = $this->buckets;
|
||||
for ($y = 1; $y < 5; $y++) {
|
||||
$row = (int)($height * $y / 5);
|
||||
$localLuminances = $source->getRow($row, $this->luminances);
|
||||
$right = (int)(($width * 4) / 5);
|
||||
for ($x = (int)($width / 5); $x < $right; $x++) {
|
||||
$pixel = ($localLuminances[(int)($x)] & 0xff);
|
||||
$localBuckets[($pixel >> self::$LUMINANCE_SHIFT)]++;
|
||||
}
|
||||
}
|
||||
$blackPoint = self::estimateBlackPoint($localBuckets);
|
||||
|
||||
// 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.
|
||||
$localLuminances = $source->getMatrix();
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$offset = $y * $width;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$pixel = (int)($localLuminances[$offset + $x] & 0xff);
|
||||
if ($pixel < $blackPoint) {
|
||||
$matrix->set($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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.
|
||||
$localLuminances = $source->getMatrix();
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$offset = $y * $width;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$pixel = (int)($localLuminances[$offset + $x] & 0xff);
|
||||
if ($pixel < $blackPoint) {
|
||||
$matrix->set($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $matrix;
|
||||
}
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
public function createBinarizer($source)
|
||||
{
|
||||
return new GlobalHistogramBinarizer($source);
|
||||
}
|
||||
public function createBinarizer($source): \Zxing\Common\GlobalHistogramBinarizer
|
||||
{
|
||||
return new GlobalHistogramBinarizer($source);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,155 +34,165 @@ use Zxing\NotFoundException;
|
|||
*/
|
||||
abstract class GridSampler
|
||||
{
|
||||
private static $gridSampler;
|
||||
/**
|
||||
* @var mixed|\Zxing\Common\DefaultGridSampler|null
|
||||
*/
|
||||
private static $gridSampler;
|
||||
|
||||
/**
|
||||
* Sets the implementation of GridSampler used by the library. One global
|
||||
* instance is stored, which may sound problematic. But, the implementation provided
|
||||
* ought to be appropriate for the entire platform, and all uses of this library
|
||||
* in the whole lifetime of the JVM. For instance, an Android activity can swap in
|
||||
* an implementation that takes advantage of native platform libraries.
|
||||
*
|
||||
* @param newGridSampler The platform-specific object to install.
|
||||
*/
|
||||
public static function setGridSampler($newGridSampler)
|
||||
{
|
||||
self::$gridSampler = $newGridSampler;
|
||||
}
|
||||
/**
|
||||
* Sets the implementation of GridSampler used by the library. One global
|
||||
* instance is stored, which may sound problematic. But, the implementation provided
|
||||
* ought to be appropriate for the entire platform, and all uses of this library
|
||||
* in the whole lifetime of the JVM. For instance, an Android activity can swap in
|
||||
* an implementation that takes advantage of native platform libraries.
|
||||
*
|
||||
* @param $newGridSampler The platform-specific object to install.
|
||||
*/
|
||||
public static function setGridSampler($newGridSampler): void
|
||||
{
|
||||
self::$gridSampler = $newGridSampler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current implementation of GridSampler
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (!self::$gridSampler) {
|
||||
self::$gridSampler = new DefaultGridSampler();
|
||||
}
|
||||
/**
|
||||
* @return GridSampler the current implementation of GridSampler
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (!self::$gridSampler) {
|
||||
self::$gridSampler = new DefaultGridSampler();
|
||||
}
|
||||
|
||||
return self::$gridSampler;
|
||||
}
|
||||
return self::$gridSampler;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param image image into which the points should map
|
||||
* @param points actual points in x1,y1,...,xn,yn form
|
||||
*
|
||||
* @throws NotFoundException if an endpoint is lies outside the image boundaries
|
||||
*/
|
||||
protected static function checkAndNudgePoints(
|
||||
$image,
|
||||
$points
|
||||
) {
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
// Check and nudge points from start until we see some that are OK:
|
||||
$nudged = true;
|
||||
for ($offset = 0; $offset < count($points) && $nudged; $offset += 2) {
|
||||
$x = (int)$points[$offset];
|
||||
$y = (int)$points[$offset + 1];
|
||||
if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$nudged = false;
|
||||
if ($x == -1) {
|
||||
$points[$offset] = 0.0;
|
||||
$nudged = true;
|
||||
} else if ($x == $width) {
|
||||
$points[$offset] = $width - 1;
|
||||
$nudged = true;
|
||||
}
|
||||
if ($y == -1) {
|
||||
$points[$offset + 1] = 0.0;
|
||||
$nudged = true;
|
||||
} else if ($y == $height) {
|
||||
$points[$offset + 1] = $height - 1;
|
||||
$nudged = true;
|
||||
}
|
||||
}
|
||||
// Check and nudge points from end:
|
||||
$nudged = true;
|
||||
for ($offset = count($points) - 2; $offset >= 0 && $nudged; $offset -= 2) {
|
||||
$x = (int)$points[$offset];
|
||||
$y = (int)$points[$offset + 1];
|
||||
if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$nudged = false;
|
||||
if ($x == -1) {
|
||||
$points[$offset] = 0.0;
|
||||
$nudged = true;
|
||||
} else if ($x == $width) {
|
||||
$points[$offset] = $width - 1;
|
||||
$nudged = true;
|
||||
}
|
||||
if ($y == -1) {
|
||||
$points[$offset + 1] = 0.0;
|
||||
$nudged = true;
|
||||
} else if ($y == $height) {
|
||||
$points[$offset + 1] = $height - 1;
|
||||
$nudged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param image $image into which the points should map
|
||||
* @param actual $points points in x1,y1,...,xn,yn form
|
||||
*
|
||||
* @throws NotFoundException if an endpoint is lies outside the image boundaries
|
||||
*/
|
||||
protected static function checkAndNudgePoints(
|
||||
$image,
|
||||
$points
|
||||
) {
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
// Check and nudge points from start until we see some that are OK:
|
||||
$nudged = true;
|
||||
for ($offset = 0; $offset < (is_countable($points) ? count($points) : 0) && $nudged; $offset += 2) {
|
||||
$x = (int)$points[$offset];
|
||||
$y = (int)$points[$offset + 1];
|
||||
if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$nudged = false;
|
||||
if ($x == -1) {
|
||||
$points[$offset] = 0.0;
|
||||
$nudged = true;
|
||||
} elseif ($x == $width) {
|
||||
$points[$offset] = $width - 1;
|
||||
$nudged = true;
|
||||
}
|
||||
if ($y == -1) {
|
||||
$points[$offset + 1] = 0.0;
|
||||
$nudged = true;
|
||||
} elseif ($y == $height) {
|
||||
$points[$offset + 1] = $height - 1;
|
||||
$nudged = true;
|
||||
}
|
||||
}
|
||||
// Check and nudge points from end:
|
||||
$nudged = true;
|
||||
for ($offset = (is_countable($points) ? count($points) : 0) - 2; $offset >= 0 && $nudged; $offset -= 2) {
|
||||
$x = (int)$points[$offset];
|
||||
$y = (int)$points[$offset + 1];
|
||||
if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$nudged = false;
|
||||
if ($x == -1) {
|
||||
$points[$offset] = 0.0;
|
||||
$nudged = true;
|
||||
} elseif ($x == $width) {
|
||||
$points[$offset] = $width - 1;
|
||||
$nudged = true;
|
||||
}
|
||||
if ($y == -1) {
|
||||
$points[$offset + 1] = 0.0;
|
||||
$nudged = true;
|
||||
} elseif ($y == $height) {
|
||||
$points[$offset + 1] = $height - 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.
|
||||
*
|
||||
* @param image image to sample
|
||||
* @param dimensionX width of {@link BitMatrix} to sample from image
|
||||
* @param dimensionY height of {@link BitMatrix} to sample from image
|
||||
* @param p1ToX point 1 preimage X
|
||||
* @param p1ToY point 1 preimage Y
|
||||
* @param p2ToX point 2 preimage X
|
||||
* @param p2ToY point 2 preimage Y
|
||||
* @param p3ToX point 3 preimage X
|
||||
* @param p3ToY point 3 preimage Y
|
||||
* @param p4ToX point 4 preimage X
|
||||
* @param p4ToY point 4 preimage Y
|
||||
* @param p1FromX point 1 image X
|
||||
* @param p1FromY point 1 image Y
|
||||
* @param p2FromX point 2 image X
|
||||
* @param p2FromY point 2 image Y
|
||||
* @param p3FromX point 3 image X
|
||||
* @param p3FromY point 3 image Y
|
||||
* @param p4FromX point 4 image X
|
||||
* @param p4FromY point 4 image Y
|
||||
*
|
||||
* @return {@link BitMatrix} representing a grid of points sampled from the image within a region
|
||||
* defined by the "from" parameters
|
||||
* @throws NotFoundException 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 abstract function sampleGrid(
|
||||
$image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$p1ToX, $p1ToY,
|
||||
$p2ToX, $p2ToY,
|
||||
$p3ToX, $p3ToY,
|
||||
$p4ToX, $p4ToY,
|
||||
$p1FromX, $p1FromY,
|
||||
$p2FromX, $p2FromY,
|
||||
$p3FromX, $p3FromY,
|
||||
$p4FromX, $p4FromY
|
||||
);
|
||||
|
||||
public abstract function sampleGrid_(
|
||||
$image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$transform
|
||||
);
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param image $image to sample
|
||||
* @param width $dimensionX of {@link BitMatrix} to sample from image
|
||||
* @param height $dimensionY of {@link BitMatrix} to sample from image
|
||||
* @param point $p1ToX 1 preimage X
|
||||
* @param point $p1ToY 1 preimage Y
|
||||
* @param point $p2ToX 2 preimage X
|
||||
* @param point $p2ToY 2 preimage Y
|
||||
* @param point $p3ToX 3 preimage X
|
||||
* @param point $p3ToY 3 preimage Y
|
||||
* @param point $p4ToX 4 preimage X
|
||||
* @param point $p4ToY 4 preimage Y
|
||||
* @param point $p1FromX 1 image X
|
||||
* @param point $p1FromY 1 image Y
|
||||
* @param point $p2FromX 2 image X
|
||||
* @param point $p2FromY 2 image Y
|
||||
* @param point $p3FromX 3 image X
|
||||
* @param point $p3FromY 3 image Y
|
||||
* @param point $p4FromX 4 image X
|
||||
* @param point $p4FromY 4 image Y
|
||||
*
|
||||
* @return {@link BitMatrix} representing a grid of points sampled from the image within a region
|
||||
* defined by the "from" parameters
|
||||
* @throws NotFoundException 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
|
||||
*/
|
||||
abstract public function sampleGrid(
|
||||
$image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$p1ToX,
|
||||
$p1ToY,
|
||||
$p2ToX,
|
||||
$p2ToY,
|
||||
$p3ToX,
|
||||
$p3ToY,
|
||||
$p4ToX,
|
||||
$p4ToY,
|
||||
$p1FromX,
|
||||
$p1FromY,
|
||||
$p2FromX,
|
||||
$p2FromY,
|
||||
$p3FromX,
|
||||
$p3FromY,
|
||||
$p4FromX,
|
||||
$p4FromY
|
||||
);
|
||||
|
||||
abstract public function sampleGrid_(
|
||||
$image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$transform
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@
|
|||
namespace Zxing\Common;
|
||||
|
||||
use Zxing\Binarizer;
|
||||
use Zxing\LuminanceSource;
|
||||
use Zxing\NotFoundException;
|
||||
|
||||
/**
|
||||
* This class implements a local thresholding algorithm, which while slower than the
|
||||
|
|
@ -40,225 +38,223 @@ use Zxing\NotFoundException;
|
|||
*/
|
||||
final class HybridBinarizer extends GlobalHistogramBinarizer
|
||||
{
|
||||
// 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 static int $BLOCK_SIZE_POWER = 3;
|
||||
private static int $BLOCK_SIZE = 8; // ...0100...00
|
||||
private static int $BLOCK_SIZE_MASK = 7; // ...0011...11
|
||||
private static int $MINIMUM_DIMENSION = 40;
|
||||
private static int $MIN_DYNAMIC_RANGE = 24;
|
||||
|
||||
// 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 static $BLOCK_SIZE_POWER = 3;
|
||||
private static $BLOCK_SIZE = 8; // ...0100...00
|
||||
private static $BLOCK_SIZE_MASK = 7; // ...0011...11
|
||||
private static $MINIMUM_DIMENSION = 40;
|
||||
private static $MIN_DYNAMIC_RANGE = 24;
|
||||
private ?\Zxing\Common\BitMatrix $matrix = null;
|
||||
|
||||
private $matrix;
|
||||
public function __construct($source)
|
||||
{
|
||||
parent::__construct($source);
|
||||
self::$BLOCK_SIZE_POWER = 3;
|
||||
self::$BLOCK_SIZE = 1 << self::$BLOCK_SIZE_POWER; // ...0100...00
|
||||
self::$BLOCK_SIZE_MASK = self::$BLOCK_SIZE - 1; // ...0011...11
|
||||
self::$MINIMUM_DIMENSION = self::$BLOCK_SIZE * 5;
|
||||
self::$MIN_DYNAMIC_RANGE = 24;
|
||||
}
|
||||
|
||||
public function __construct($source)
|
||||
{
|
||||
parent::__construct($source);
|
||||
self::$BLOCK_SIZE_POWER = 3;
|
||||
self::$BLOCK_SIZE = 1 << self::$BLOCK_SIZE_POWER; // ...0100...00
|
||||
self::$BLOCK_SIZE_MASK = self::$BLOCK_SIZE - 1; // ...0011...11
|
||||
self::$MINIMUM_DIMENSION = self::$BLOCK_SIZE * 5;
|
||||
self::$MIN_DYNAMIC_RANGE = 24;
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public function getBlackMatrix()
|
||||
{
|
||||
if ($this->matrix !== null) {
|
||||
return $this->matrix;
|
||||
}
|
||||
$source = $this->getLuminanceSource();
|
||||
$width = $source->getWidth();
|
||||
$height = $source->getHeight();
|
||||
if ($width >= self::$MINIMUM_DIMENSION && $height >= self::$MINIMUM_DIMENSION) {
|
||||
$luminances = $source->getMatrix();
|
||||
$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++;
|
||||
}
|
||||
$blackPoints = self::calculateBlackPoints($luminances, $subWidth, $subHeight, $width, $height);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public function getBlackMatrix()
|
||||
{
|
||||
if ($this->matrix !== null) {
|
||||
return $this->matrix;
|
||||
}
|
||||
$source = $this->getLuminanceSource();
|
||||
$width = $source->getWidth();
|
||||
$height = $source->getHeight();
|
||||
if ($width >= self::$MINIMUM_DIMENSION && $height >= self::$MINIMUM_DIMENSION) {
|
||||
$luminances = $source->getMatrix();
|
||||
$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++;
|
||||
}
|
||||
$blackPoints = self::calculateBlackPoints($luminances, $subWidth, $subHeight, $width, $height);
|
||||
$newMatrix = new BitMatrix($width, $height);
|
||||
self::calculateThresholdForBlock($luminances, $subWidth, $subHeight, $width, $height, $blackPoints, $newMatrix);
|
||||
$this->matrix = $newMatrix;
|
||||
} else {
|
||||
// If the image is too small, fall back to the global histogram approach.
|
||||
$this->matrix = parent::getBlackMatrix();
|
||||
}
|
||||
|
||||
$newMatrix = new BitMatrix($width, $height);
|
||||
self::calculateThresholdForBlock($luminances, $subWidth, $subHeight, $width, $height, $blackPoints, $newMatrix);
|
||||
$this->matrix = $newMatrix;
|
||||
} else {
|
||||
// If the image is too small, fall back to the global histogram approach.
|
||||
$this->matrix = parent::getBlackMatrix();
|
||||
}
|
||||
return $this->matrix;
|
||||
}
|
||||
|
||||
return $this->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:
|
||||
* http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
|
||||
*/
|
||||
private static function calculateBlackPoints(
|
||||
$luminances,
|
||||
$subWidth,
|
||||
$subHeight,
|
||||
$width,
|
||||
$height
|
||||
) {
|
||||
$blackPoints = fill_array(0, $subHeight, 0);
|
||||
foreach ($blackPoints as $key => $point) {
|
||||
$blackPoints[$key] = fill_array(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 = 0xFF;
|
||||
$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)($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 += ($luminances[$offset + $xx] & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a single black point for each block of pixels and saves it away.
|
||||
* See the following thread for a discussion of this algorithm:
|
||||
* http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
|
||||
*/
|
||||
private static function calculateBlackPoints(
|
||||
$luminances,
|
||||
$subWidth,
|
||||
$subHeight,
|
||||
$width,
|
||||
$height
|
||||
) {
|
||||
$blackPoints = fill_array(0, $subHeight, 0);
|
||||
foreach ($blackPoints as $key => $point) {
|
||||
$blackPoints[$key] = fill_array(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 = 0xFF;
|
||||
$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)($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 += ($luminances[$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 = (int)($min / 2);
|
||||
|
||||
// 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 = (int)($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.
|
||||
|
||||
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 =
|
||||
(int)(($blackPoints[$y - 1][$x] + (2 * $blackPoints[$y][$x - 1]) + $blackPoints[$y - 1][$x - 1]) / 4);
|
||||
if ($min < $averageNeighborBlackPoint) {
|
||||
$average = $averageNeighborBlackPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
$blackPoints[$y][$x] = (int)($average);
|
||||
}
|
||||
}
|
||||
|
||||
// The (min < bp) is arbitrary but works better than other heuristics that were tried.
|
||||
$averageNeighborBlackPoint =
|
||||
(int)(($blackPoints[$y - 1][$x] + (2 * $blackPoints[$y][$x - 1]) + $blackPoints[$y - 1][$x - 1]) / 4);
|
||||
if ($min < $averageNeighborBlackPoint) {
|
||||
$average = $averageNeighborBlackPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
$blackPoints[$y][$x] = (int)($average);
|
||||
}
|
||||
}
|
||||
return $blackPoints;
|
||||
}
|
||||
|
||||
return $blackPoints;
|
||||
}
|
||||
/**
|
||||
* For each block in the image, calculate the average black point using a 5x5 grid
|
||||
* of the blocks around it. 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 static function calculateThresholdForBlock(
|
||||
$luminances,
|
||||
$subWidth,
|
||||
$subHeight,
|
||||
$width,
|
||||
$height,
|
||||
$blackPoints,
|
||||
$matrix
|
||||
): void {
|
||||
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 = self::cap($x, 2, $subWidth - 3);
|
||||
$top = self::cap($y, 2, $subHeight - 3);
|
||||
$sum = 0;
|
||||
for ($z = -2; $z <= 2; $z++) {
|
||||
$blackRow = $blackPoints[$top + $z];
|
||||
$sum += $blackRow[$left - 2] + $blackRow[$left - 1] + $blackRow[$left] + $blackRow[$left + 1] + $blackRow[$left + 2];
|
||||
}
|
||||
$average = (int)($sum / 25);
|
||||
|
||||
/**
|
||||
* For each block in the image, calculate the average black point using a 5x5 grid
|
||||
* of the blocks around it. 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 static function calculateThresholdForBlock(
|
||||
$luminances,
|
||||
$subWidth,
|
||||
$subHeight,
|
||||
$width,
|
||||
$height,
|
||||
$blackPoints,
|
||||
$matrix
|
||||
) {
|
||||
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 = self::cap($x, 2, $subWidth - 3);
|
||||
$top = self::cap($y, 2, $subHeight - 3);
|
||||
$sum = 0;
|
||||
for ($z = -2; $z <= 2; $z++) {
|
||||
$blackRow = $blackPoints[$top + $z];
|
||||
$sum += $blackRow[$left - 2] + $blackRow[$left - 1] + $blackRow[$left] + $blackRow[$left + 1] + $blackRow[$left + 2];
|
||||
}
|
||||
$average = (int)($sum / 25);
|
||||
self::thresholdBlock($luminances, $xoffset, $yoffset, $average, $width, $matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::thresholdBlock($luminances, $xoffset, $yoffset, $average, $width, $matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static function cap($value, $min, $max)
|
||||
{
|
||||
if ($value < $min) {
|
||||
return $min;
|
||||
} elseif ($value > $max) {
|
||||
return $max;
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
private static function cap($value, $min, $max)
|
||||
{
|
||||
if ($value < $min) {
|
||||
return $min;
|
||||
} elseif ($value > $max) {
|
||||
return $max;
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Applies a single threshold to a block of pixels.
|
||||
*/
|
||||
private static function thresholdBlock(
|
||||
$luminances,
|
||||
$xoffset,
|
||||
$yoffset,
|
||||
$threshold,
|
||||
$stride,
|
||||
$matrix
|
||||
): void {
|
||||
for ($y = 0, $offset = $yoffset * $stride + $xoffset; $y < self::$BLOCK_SIZE; $y++, $offset += $stride) {
|
||||
for ($x = 0; $x < self::$BLOCK_SIZE; $x++) {
|
||||
// Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
|
||||
if (($luminances[$offset + $x] & 0xFF) <= $threshold) {
|
||||
$matrix->set($xoffset + $x, $yoffset + $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a single threshold to a block of pixels.
|
||||
*/
|
||||
private static function thresholdBlock(
|
||||
$luminances,
|
||||
$xoffset,
|
||||
$yoffset,
|
||||
$threshold,
|
||||
$stride,
|
||||
$matrix
|
||||
) {
|
||||
|
||||
for ($y = 0, $offset = $yoffset * $stride + $xoffset; $y < self::$BLOCK_SIZE; $y++, $offset += $stride) {
|
||||
for ($x = 0; $x < self::$BLOCK_SIZE; $x++) {
|
||||
// Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
|
||||
if (($luminances[$offset + $x] & 0xFF) <= $threshold) {
|
||||
$matrix->set($xoffset + $x, $yoffset + $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function createBinarizer($source)
|
||||
{
|
||||
return new HybridBinarizer($source);
|
||||
}
|
||||
public function createBinarizer($source): \Zxing\Common\HybridBinarizer
|
||||
{
|
||||
return new HybridBinarizer($source);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,150 +26,162 @@ namespace Zxing\Common;
|
|||
*/
|
||||
final class PerspectiveTransform
|
||||
{
|
||||
private $a11;
|
||||
private $a12;
|
||||
private $a13;
|
||||
private $a21;
|
||||
private $a22;
|
||||
private $a23;
|
||||
private $a31;
|
||||
private $a32;
|
||||
private $a33;
|
||||
private function __construct(private $a11, private $a21, private $a31, private $a12, private $a22, private $a32, private $a13, private $a23, private $a33)
|
||||
{
|
||||
}
|
||||
|
||||
private function __construct(
|
||||
$a11, $a21, $a31,
|
||||
$a12, $a22, $a32,
|
||||
$a13, $a23, $a33
|
||||
) {
|
||||
$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;
|
||||
}
|
||||
public static function quadrilateralToQuadrilateral(
|
||||
$x0,
|
||||
$y0,
|
||||
$x1,
|
||||
$y1,
|
||||
$x2,
|
||||
$y2,
|
||||
$x3,
|
||||
$y3,
|
||||
$x0p,
|
||||
$y0p,
|
||||
$x1p,
|
||||
$y1p,
|
||||
$x2p,
|
||||
$y2p,
|
||||
$x3p,
|
||||
$y3p
|
||||
) {
|
||||
$qToS = self::quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3);
|
||||
$sToQ = self::squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p);
|
||||
|
||||
public static function quadrilateralToQuadrilateral(
|
||||
$x0, $y0,
|
||||
$x1, $y1,
|
||||
$x2, $y2,
|
||||
$x3, $y3,
|
||||
$x0p, $y0p,
|
||||
$x1p, $y1p,
|
||||
$x2p, $y2p,
|
||||
$x3p, $y3p
|
||||
) {
|
||||
return $sToQ->times($qToS);
|
||||
}
|
||||
|
||||
$qToS = self::quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3);
|
||||
$sToQ = self::squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p);
|
||||
public static function quadrilateralToSquare(
|
||||
$x0,
|
||||
$y0,
|
||||
$x1,
|
||||
$y1,
|
||||
$x2,
|
||||
$y2,
|
||||
$x3,
|
||||
$y3
|
||||
) {
|
||||
// Here, the adjoint serves as the inverse:
|
||||
return self::squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)->buildAdjoint();
|
||||
}
|
||||
|
||||
return $sToQ->times($qToS);
|
||||
}
|
||||
public function buildAdjoint(): \Zxing\Common\PerspectiveTransform
|
||||
{
|
||||
// Adjoint is the transpose of the cofactor matrix:
|
||||
return new PerspectiveTransform(
|
||||
$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
|
||||
);
|
||||
}
|
||||
|
||||
public static function quadrilateralToSquare(
|
||||
$x0, $y0,
|
||||
$x1, $y1,
|
||||
$x2, $y2,
|
||||
$x3, $y3
|
||||
) {
|
||||
// Here, the adjoint serves as the inverse:
|
||||
return self::squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)->buildAdjoint();
|
||||
}
|
||||
public static function squareToQuadrilateral(
|
||||
$x0,
|
||||
$y0,
|
||||
$x1,
|
||||
$y1,
|
||||
$x2,
|
||||
$y2,
|
||||
$x3,
|
||||
$y3
|
||||
): \Zxing\Common\PerspectiveTransform {
|
||||
$dx3 = $x0 - $x1 + $x2 - $x3;
|
||||
$dy3 = $y0 - $y1 + $y2 - $y3;
|
||||
if ($dx3 == 0.0 && $dy3 == 0.0) {
|
||||
// Affine
|
||||
return new PerspectiveTransform(
|
||||
$x1 - $x0,
|
||||
$x2 - $x1,
|
||||
$x0,
|
||||
$y1 - $y0,
|
||||
$y2 - $y1,
|
||||
$y0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
} else {
|
||||
$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;
|
||||
|
||||
public function buildAdjoint()
|
||||
{
|
||||
// Adjoint is the transpose of the cofactor matrix:
|
||||
return new PerspectiveTransform($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);
|
||||
}
|
||||
return new PerspectiveTransform(
|
||||
$x1 - $x0 + $a13 * $x1,
|
||||
$x3 - $x0 + $a23 * $x3,
|
||||
$x0,
|
||||
$y1 - $y0 + $a13 * $y1,
|
||||
$y3 - $y0 + $a23 * $y3,
|
||||
$y0,
|
||||
$a13,
|
||||
$a23,
|
||||
1.0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static function squareToQuadrilateral(
|
||||
$x0, $y0,
|
||||
$x1, $y1,
|
||||
$x2, $y2,
|
||||
$x3, $y3
|
||||
) {
|
||||
$dx3 = $x0 - $x1 + $x2 - $x3;
|
||||
$dy3 = $y0 - $y1 + $y2 - $y3;
|
||||
if ($dx3 == 0.0 && $dy3 == 0.0) {
|
||||
// Affine
|
||||
return new PerspectiveTransform($x1 - $x0, $x2 - $x1, $x0,
|
||||
$y1 - $y0, $y2 - $y1, $y0,
|
||||
0.0, 0.0, 1.0);
|
||||
} else {
|
||||
$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;
|
||||
public function times($other): \Zxing\Common\PerspectiveTransform
|
||||
{
|
||||
return new PerspectiveTransform(
|
||||
$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 new PerspectiveTransform($x1 - $x0 + $a13 * $x1, $x3 - $x0 + $a23 * $x3, $x0,
|
||||
$y1 - $y0 + $a13 * $y1, $y3 - $y0 + $a23 * $y3, $y0,
|
||||
$a13, $a23, 1.0);
|
||||
}
|
||||
}
|
||||
public function transformPoints(&$points, &$yValues = 0): void
|
||||
{
|
||||
if ($yValues) {
|
||||
$this->transformPoints_($points, $yValues);
|
||||
|
||||
public function times($other)
|
||||
{
|
||||
return new PerspectiveTransform($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;
|
||||
}
|
||||
$max = is_countable($points) ? count($points) : 0;
|
||||
$a11 = $this->a11;
|
||||
$a12 = $this->a12;
|
||||
$a13 = $this->a13;
|
||||
$a21 = $this->a21;
|
||||
$a22 = $this->a22;
|
||||
$a23 = $this->a23;
|
||||
$a31 = $this->a31;
|
||||
$a32 = $this->a32;
|
||||
$a33 = $this->a33;
|
||||
for ($i = 0; $i < $max; $i += 2) {
|
||||
$x = $points[$i];
|
||||
$y = $points[$i + 1];
|
||||
$denominator = $a13 * $x + $a23 * $y + $a33;
|
||||
$points[$i] = ($a11 * $x + $a21 * $y + $a31) / $denominator;
|
||||
$points[$i + 1] = ($a12 * $x + $a22 * $y + $a32) / $denominator;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function transformPoints(&$points, &$yValues = 0)
|
||||
{
|
||||
if ($yValues) {
|
||||
$this->transformPoints_($points, $yValues);
|
||||
|
||||
return;
|
||||
}
|
||||
$max = count($points);
|
||||
$a11 = $this->a11;
|
||||
$a12 = $this->a12;
|
||||
$a13 = $this->a13;
|
||||
$a21 = $this->a21;
|
||||
$a22 = $this->a22;
|
||||
$a23 = $this->a23;
|
||||
$a31 = $this->a31;
|
||||
$a32 = $this->a32;
|
||||
$a33 = $this->a33;
|
||||
for ($i = 0; $i < $max; $i += 2) {
|
||||
$x = $points[$i];
|
||||
$y = $points[$i + 1];
|
||||
$denominator = $a13 * $x + $a23 * $y + $a33;
|
||||
$points[$i] = ($a11 * $x + $a21 * $y + $a31) / $denominator;
|
||||
$points[$i + 1] = ($a12 * $x + $a22 * $y + $a32) / $denominator;
|
||||
}
|
||||
}
|
||||
|
||||
public function transformPoints_(&$xValues, &$yValues)
|
||||
{
|
||||
$n = count($xValues);
|
||||
for ($i = 0; $i < $n; $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;
|
||||
}
|
||||
}
|
||||
public function transformPoints_(&$xValues, &$yValues): void
|
||||
{
|
||||
$n = is_countable($xValues) ? count($xValues) : 0;
|
||||
for ($i = 0; $i < $n; $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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,169 +30,158 @@ namespace Zxing\Common\Reedsolomon;
|
|||
*/
|
||||
final class GenericGF
|
||||
{
|
||||
public static $AZTEC_DATA_12;
|
||||
public static $AZTEC_DATA_10;
|
||||
public static $AZTEC_DATA_6;
|
||||
public static $AZTEC_PARAM;
|
||||
public static $QR_CODE_FIELD_256;
|
||||
public static $DATA_MATRIX_FIELD_256;
|
||||
public static $AZTEC_DATA_8;
|
||||
public static $MAXICODE_FIELD_64;
|
||||
|
||||
public static $AZTEC_DATA_12;
|
||||
public static $AZTEC_DATA_10;
|
||||
public static $AZTEC_DATA_6;
|
||||
public static $AZTEC_PARAM;
|
||||
public static $QR_CODE_FIELD_256;
|
||||
public static $DATA_MATRIX_FIELD_256;
|
||||
public static $AZTEC_DATA_8;
|
||||
public static $MAXICODE_FIELD_64;
|
||||
private array $expTable = [];
|
||||
private array $logTable = [];
|
||||
private readonly \Zxing\Common\Reedsolomon\GenericGFPoly $zero;
|
||||
private readonly \Zxing\Common\Reedsolomon\GenericGFPoly $one;
|
||||
|
||||
private $expTable;
|
||||
private $logTable;
|
||||
private $zero;
|
||||
private $one;
|
||||
private $size;
|
||||
private $primitive;
|
||||
private $generatorBase;
|
||||
/**
|
||||
* Create a representation of GF(size) using the given primitive polynomial.
|
||||
*
|
||||
* @param irreducible $primitive polynomial whose coefficients are represented by
|
||||
* the bits of an int, where the least-significant bit represents the constant
|
||||
* coefficient
|
||||
* @param the $size size of the field
|
||||
* @param the $generatorBase factor b in the generator polynomial can be 0- or 1-based
|
||||
(g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
|
||||
In most cases it should be 1, but for QR code it is 0.
|
||||
*/
|
||||
public function __construct(private $primitive, private $size, private $generatorBase)
|
||||
{
|
||||
$x = 1;
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$this->expTable[$i] = $x;
|
||||
$x *= 2; // we're assuming the generator alpha is 2
|
||||
if ($x >= $size) {
|
||||
$x ^= $primitive;
|
||||
$x &= $size - 1;
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i < $size - 1; $i++) {
|
||||
$this->logTable[$this->expTable[$i]] = $i;
|
||||
}
|
||||
// logTable[0] == 0 but this should never be used
|
||||
$this->zero = new GenericGFPoly($this, [0]);
|
||||
$this->one = new GenericGFPoly($this, [1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a representation of GF(size) using the given primitive polynomial.
|
||||
*
|
||||
* @param primitive irreducible polynomial whose coefficients are represented by
|
||||
* the bits of an int, where the least-significant bit represents the constant
|
||||
* coefficient
|
||||
* @param size the size of the field
|
||||
* @param b the factor b in the generator polynomial can be 0- or 1-based
|
||||
* (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
|
||||
* In most cases it should be 1, but for QR code it is 0.
|
||||
*/
|
||||
public function __construct($primitive, $size, $b)
|
||||
{
|
||||
$this->primitive = $primitive;
|
||||
$this->size = $size;
|
||||
$this->generatorBase = $b;
|
||||
public static function Init(): void
|
||||
{
|
||||
self::$AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
|
||||
self::$AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1
|
||||
self::$AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1
|
||||
self::$AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1
|
||||
self::$QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
|
||||
self::$DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
|
||||
self::$AZTEC_DATA_8 = self::$DATA_MATRIX_FIELD_256;
|
||||
self::$MAXICODE_FIELD_64 = self::$AZTEC_DATA_6;
|
||||
}
|
||||
|
||||
$this->expTable = [];
|
||||
$this->logTable = [];
|
||||
$x = 1;
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$this->expTable[$i] = $x;
|
||||
$x *= 2; // we're assuming the generator alpha is 2
|
||||
if ($x >= $size) {
|
||||
$x ^= $primitive;
|
||||
$x &= $size - 1;
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i < $size - 1; $i++) {
|
||||
$this->logTable[$this->expTable[$i]] = $i;
|
||||
}
|
||||
// logTable[0] == 0 but this should never be used
|
||||
$this->zero = new GenericGFPoly($this, [0]);
|
||||
$this->one = new GenericGFPoly($this, [1]);
|
||||
}
|
||||
/**
|
||||
* Implements both addition and subtraction -- they are the same in GF(size).
|
||||
*
|
||||
* @return sum/difference of a and b
|
||||
*/
|
||||
public static function addOrSubtract($a, $b)
|
||||
{
|
||||
return $a ^ $b;
|
||||
}
|
||||
|
||||
public static function Init()
|
||||
{
|
||||
self::$AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
|
||||
self::$AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1
|
||||
self::$AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1
|
||||
self::$AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1
|
||||
self::$QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
|
||||
self::$DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
|
||||
self::$AZTEC_DATA_8 = self::$DATA_MATRIX_FIELD_256;
|
||||
self::$MAXICODE_FIELD_64 = self::$AZTEC_DATA_6;
|
||||
}
|
||||
public function getZero()
|
||||
{
|
||||
return $this->zero;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements both addition and subtraction -- they are the same in GF(size).
|
||||
*
|
||||
* @return sum/difference of a and b
|
||||
*/
|
||||
public static function addOrSubtract($a, $b)
|
||||
{
|
||||
return $a ^ $b;
|
||||
}
|
||||
public function getOne()
|
||||
{
|
||||
return $this->one;
|
||||
}
|
||||
|
||||
public function getZero()
|
||||
{
|
||||
return $this->zero;
|
||||
}
|
||||
/**
|
||||
* @return GenericGFPoly the monomial representing coefficient * x^degree
|
||||
*/
|
||||
public function buildMonomial($degree, $coefficient)
|
||||
{
|
||||
if ($degree < 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($coefficient == 0) {
|
||||
return $this->zero;
|
||||
}
|
||||
$coefficients = fill_array(0, $degree + 1, 0);//new int[degree + 1];
|
||||
$coefficients[0] = $coefficient;
|
||||
|
||||
public function getOne()
|
||||
{
|
||||
return $this->one;
|
||||
}
|
||||
return new GenericGFPoly($this, $coefficients);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the monomial representing coefficient * x^degree
|
||||
*/
|
||||
public function buildMonomial($degree, $coefficient)
|
||||
{
|
||||
if ($degree < 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($coefficient == 0) {
|
||||
return $this->zero;
|
||||
}
|
||||
$coefficients = fill_array(0, $degree + 1, 0);//new int[degree + 1];
|
||||
$coefficients[0] = $coefficient;
|
||||
/**
|
||||
* @return 2 to the power of a in GF(size)
|
||||
*/
|
||||
public function exp($a)
|
||||
{
|
||||
return $this->expTable[$a];
|
||||
}
|
||||
|
||||
return new GenericGFPoly($this, $coefficients);
|
||||
}
|
||||
/**
|
||||
* @return base 2 log of a in GF(size)
|
||||
*/
|
||||
public function log($a)
|
||||
{
|
||||
if ($a == 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2 to the power of a in GF(size)
|
||||
*/
|
||||
public function exp($a)
|
||||
{
|
||||
return $this->expTable[$a];
|
||||
}
|
||||
return $this->logTable[$a];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return base 2 log of a in GF(size)
|
||||
*/
|
||||
public function log($a)
|
||||
{
|
||||
if ($a == 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
/**
|
||||
* @return multiplicative inverse of a
|
||||
*/
|
||||
public function inverse($a)
|
||||
{
|
||||
if ($a == 0) {
|
||||
throw new \Exception();
|
||||
}
|
||||
|
||||
return $this->logTable[$a];
|
||||
}
|
||||
return $this->expTable[$this->size - $this->logTable[$a] - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return multiplicative inverse of a
|
||||
*/
|
||||
public function inverse($a)
|
||||
{
|
||||
if ($a == 0) {
|
||||
throw new \Exception();
|
||||
}
|
||||
/**
|
||||
* @return int product of a and b in GF(size)
|
||||
*/
|
||||
public function multiply($a, $b)
|
||||
{
|
||||
if ($a == 0 || $b == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->expTable[$this->size - $this->logTable[$a] - 1];
|
||||
}
|
||||
return $this->expTable[($this->logTable[$a] + $this->logTable[$b]) % ($this->size - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int product of a and b in GF(size)
|
||||
*/
|
||||
public function multiply($a, $b)
|
||||
{
|
||||
if ($a == 0 || $b == 0) {
|
||||
return 0;
|
||||
}
|
||||
public function getSize()
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
return $this->expTable[($this->logTable[$a] + $this->logTable[$b]) % ($this->size - 1)];
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function getGeneratorBase()
|
||||
{
|
||||
return $this->generatorBase;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public function toString()
|
||||
{
|
||||
return "GF(0x" . dechex((int)($this->primitive)) . ',' . $this->size . ')';
|
||||
}
|
||||
public function getGeneratorBase()
|
||||
{
|
||||
return $this->generatorBase;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public function toString()
|
||||
{
|
||||
return "GF(0x" . dechex((int)($this->primitive)) . ',' . $this->size . ')';
|
||||
}
|
||||
}
|
||||
|
||||
GenericGF::Init();
|
||||
|
|
|
|||
|
|
@ -28,262 +28,276 @@ namespace Zxing\Common\Reedsolomon;
|
|||
*/
|
||||
final class GenericGFPoly
|
||||
{
|
||||
/**
|
||||
* @var int[]|mixed|null
|
||||
*/
|
||||
private $coefficients;
|
||||
|
||||
private $field;
|
||||
private $coefficients;
|
||||
/**
|
||||
* @param the $field {@link GenericGF} instance representing the field to use
|
||||
* to perform computations
|
||||
* @param array $coefficients coefficients as ints representing elements of GF(size), arranged
|
||||
* from most significant (highest-power term) coefficient to least significant
|
||||
*
|
||||
* @throws InvalidArgumentException 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(private $field, $coefficients)
|
||||
{
|
||||
if (count($coefficients) == 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
$coefficientsLength = count($coefficients);
|
||||
if ($coefficientsLength > 1 && $coefficients[0] == 0) {
|
||||
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||
$firstNonZero = 1;
|
||||
while ($firstNonZero < $coefficientsLength && $coefficients[$firstNonZero] == 0) {
|
||||
$firstNonZero++;
|
||||
}
|
||||
if ($firstNonZero == $coefficientsLength) {
|
||||
$this->coefficients = [0];
|
||||
} else {
|
||||
$this->coefficients = fill_array(0, $coefficientsLength - $firstNonZero, 0);
|
||||
$this->coefficients = arraycopy(
|
||||
$coefficients,
|
||||
$firstNonZero,
|
||||
$this->coefficients,
|
||||
0,
|
||||
is_countable($this->coefficients) ? count($this->coefficients) : 0
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$this->coefficients = $coefficients;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param field the {@link GenericGF} instance representing the field to use
|
||||
* to perform computations
|
||||
* @param coefficients array coefficients as ints representing elements of GF(size), arranged
|
||||
* from most significant (highest-power term) coefficient to least significant
|
||||
*
|
||||
* @throws InvalidArgumentException 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($field, $coefficients)
|
||||
{
|
||||
if (count($coefficients) == 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
$this->field = $field;
|
||||
$coefficientsLength = count($coefficients);
|
||||
if ($coefficientsLength > 1 && $coefficients[0] == 0) {
|
||||
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||
$firstNonZero = 1;
|
||||
while ($firstNonZero < $coefficientsLength && $coefficients[$firstNonZero] == 0) {
|
||||
$firstNonZero++;
|
||||
}
|
||||
if ($firstNonZero == $coefficientsLength) {
|
||||
$this->coefficients = [0];
|
||||
} else {
|
||||
$this->coefficients = fill_array(0, $coefficientsLength - $firstNonZero, 0);
|
||||
$this->coefficients = arraycopy($coefficients,
|
||||
$firstNonZero,
|
||||
$this->coefficients,
|
||||
0,
|
||||
count($this->coefficients));
|
||||
}
|
||||
} else {
|
||||
$this->coefficients = $coefficients;
|
||||
}
|
||||
}
|
||||
public function getCoefficients()
|
||||
{
|
||||
return $this->coefficients;
|
||||
}
|
||||
|
||||
public function getCoefficients()
|
||||
{
|
||||
return $this->coefficients;
|
||||
}
|
||||
/**
|
||||
* @return evaluation of this polynomial at a given point
|
||||
*/
|
||||
public function evaluateAt($a)
|
||||
{
|
||||
if ($a == 0) {
|
||||
// Just return the x^0 coefficient
|
||||
return $this->getCoefficient(0);
|
||||
}
|
||||
$size = is_countable($this->coefficients) ? count($this->coefficients) : 0;
|
||||
if ($a == 1) {
|
||||
// Just the sum of the coefficients
|
||||
$result = 0;
|
||||
foreach ($this->coefficients as $coefficient) {
|
||||
$result = GenericGF::addOrSubtract($result, $coefficient);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return evaluation of this polynomial at a given point
|
||||
*/
|
||||
public function evaluateAt($a)
|
||||
{
|
||||
if ($a == 0) {
|
||||
// Just return the x^0 coefficient
|
||||
return $this->getCoefficient(0);
|
||||
}
|
||||
$size = count($this->coefficients);
|
||||
if ($a == 1) {
|
||||
// Just the sum of the coefficients
|
||||
$result = 0;
|
||||
foreach ($this->coefficients as $coefficient) {
|
||||
$result = GenericGF::addOrSubtract($result, $coefficient);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
$result = $this->coefficients[0];
|
||||
for ($i = 1; $i < $size; $i++) {
|
||||
$result = GenericGF::addOrSubtract($this->field->multiply($a, $result), $this->coefficients[$i]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
$result = $this->coefficients[0];
|
||||
for ($i = 1; $i < $size; $i++) {
|
||||
$result = GenericGF::addOrSubtract($this->field->multiply($a, $result), $this->coefficients[$i]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* @return coefficient of x^degree term in this polynomial
|
||||
*/
|
||||
public function getCoefficient($degree)
|
||||
{
|
||||
return $this->coefficients[(is_countable($this->coefficients) ? count($this->coefficients) : 0) - 1 - $degree];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return coefficient of x^degree term in this polynomial
|
||||
*/
|
||||
public function getCoefficient($degree)
|
||||
{
|
||||
return $this->coefficients[count($this->coefficients) - 1 - $degree];
|
||||
}
|
||||
public function multiply($other)
|
||||
{
|
||||
$aCoefficients = [];
|
||||
$bCoefficients = [];
|
||||
$aLength = null;
|
||||
$bLength = null;
|
||||
$product = [];
|
||||
if (is_int($other)) {
|
||||
return $this->multiply_($other);
|
||||
}
|
||||
if ($this->field !== $other->field) {
|
||||
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if ($this->isZero() || $other->isZero()) {
|
||||
return $this->field->getZero();
|
||||
}
|
||||
$aCoefficients = $this->coefficients;
|
||||
$aLength = count($aCoefficients);
|
||||
$bCoefficients = $other->coefficients;
|
||||
$bLength = count($bCoefficients);
|
||||
$product = fill_array(0, $aLength + $bLength - 1, 0);
|
||||
for ($i = 0; $i < $aLength; $i++) {
|
||||
$aCoeff = $aCoefficients[$i];
|
||||
for ($j = 0; $j < $bLength; $j++) {
|
||||
$product[$i + $j] = GenericGF::addOrSubtract(
|
||||
$product[$i + $j],
|
||||
$this->field->multiply($aCoeff, $bCoefficients[$j])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function multiply($other)
|
||||
{
|
||||
if (is_int($other)) {
|
||||
return $this->multiply_($other);
|
||||
}
|
||||
if ($this->field !== $other->field) {
|
||||
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if ($this->isZero() || $other->isZero()) {
|
||||
return $this->field->getZero();
|
||||
}
|
||||
$aCoefficients = $this->coefficients;
|
||||
$aLength = count($aCoefficients);
|
||||
$bCoefficients = $other->coefficients;
|
||||
$bLength = count($bCoefficients);
|
||||
$product = fill_array(0, $aLength + $bLength - 1, 0);
|
||||
for ($i = 0; $i < $aLength; $i++) {
|
||||
$aCoeff = $aCoefficients[$i];
|
||||
for ($j = 0; $j < $bLength; $j++) {
|
||||
$product[$i + $j] = GenericGF::addOrSubtract($product[$i + $j],
|
||||
$this->field->multiply($aCoeff, $bCoefficients[$j]));
|
||||
}
|
||||
}
|
||||
return new GenericGFPoly($this->field, $product);
|
||||
}
|
||||
|
||||
return new GenericGFPoly($this->field, $product);
|
||||
}
|
||||
public function multiply_($scalar)
|
||||
{
|
||||
if ($scalar == 0) {
|
||||
return $this->field->getZero();
|
||||
}
|
||||
if ($scalar == 1) {
|
||||
return $this;
|
||||
}
|
||||
$size = is_countable($this->coefficients) ? count($this->coefficients) : 0;
|
||||
$product = fill_array(0, $size, 0);
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$product[$i] = $this->field->multiply($this->coefficients[$i], $scalar);
|
||||
}
|
||||
|
||||
public function multiply_($scalar)
|
||||
{
|
||||
if ($scalar == 0) {
|
||||
return $this->field->getZero();
|
||||
}
|
||||
if ($scalar == 1) {
|
||||
return $this;
|
||||
}
|
||||
$size = count($this->coefficients);
|
||||
$product = fill_array(0, $size, 0);
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$product[$i] = $this->field->multiply($this->coefficients[$i], $scalar);
|
||||
}
|
||||
return new GenericGFPoly($this->field, $product);
|
||||
}
|
||||
|
||||
return new GenericGFPoly($this->field, $product);
|
||||
}
|
||||
/**
|
||||
* @return true iff this polynomial is the monomial "0"
|
||||
*/
|
||||
public function isZero()
|
||||
{
|
||||
return $this->coefficients[0] == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff this polynomial is the monomial "0"
|
||||
*/
|
||||
public function isZero()
|
||||
{
|
||||
return $this->coefficients[0] == 0;
|
||||
}
|
||||
public function multiplyByMonomial($degree, $coefficient)
|
||||
{
|
||||
if ($degree < 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($coefficient == 0) {
|
||||
return $this->field->getZero();
|
||||
}
|
||||
$size = is_countable($this->coefficients) ? count($this->coefficients) : 0;
|
||||
$product = fill_array(0, $size + $degree, 0);
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$product[$i] = $this->field->multiply($this->coefficients[$i], $coefficient);
|
||||
}
|
||||
|
||||
public function multiplyByMonomial($degree, $coefficient)
|
||||
{
|
||||
if ($degree < 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($coefficient == 0) {
|
||||
return $this->field->getZero();
|
||||
}
|
||||
$size = count($this->coefficients);
|
||||
$product = fill_array(0, $size + $degree, 0);
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$product[$i] = $this->field->multiply($this->coefficients[$i], $coefficient);
|
||||
}
|
||||
return new GenericGFPoly($this->field, $product);
|
||||
}
|
||||
|
||||
return new GenericGFPoly($this->field, $product);
|
||||
}
|
||||
public function divide($other)
|
||||
{
|
||||
if ($this->field !== $other->field) {
|
||||
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if ($other->isZero()) {
|
||||
throw new \InvalidArgumentException("Divide by 0");
|
||||
}
|
||||
|
||||
public function divide($other)
|
||||
{
|
||||
if ($this->field !== $other->field) {
|
||||
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if ($other->isZero()) {
|
||||
throw new \InvalidArgumentException("Divide by 0");
|
||||
}
|
||||
$quotient = $this->field->getZero();
|
||||
$remainder = $this;
|
||||
|
||||
$quotient = $this->field->getZero();
|
||||
$remainder = $this;
|
||||
$denominatorLeadingTerm = $other->getCoefficient($other->getDegree());
|
||||
$inverseDenominatorLeadingTerm = $this->field->inverse($denominatorLeadingTerm);
|
||||
|
||||
$denominatorLeadingTerm = $other->getCoefficient($other->getDegree());
|
||||
$inverseDenominatorLeadingTerm = $this->field->inverse($denominatorLeadingTerm);
|
||||
while ($remainder->getDegree() >= $other->getDegree() && !$remainder->isZero()) {
|
||||
$degreeDifference = $remainder->getDegree() - $other->getDegree();
|
||||
$scale = $this->field->multiply($remainder->getCoefficient($remainder->getDegree()), $inverseDenominatorLeadingTerm);
|
||||
$term = $other->multiplyByMonomial($degreeDifference, $scale);
|
||||
$iterationQuotient = $this->field->buildMonomial($degreeDifference, $scale);
|
||||
$quotient = $quotient->addOrSubtract($iterationQuotient);
|
||||
$remainder = $remainder->addOrSubtract($term);
|
||||
}
|
||||
|
||||
while ($remainder->getDegree() >= $other->getDegree() && !$remainder->isZero()) {
|
||||
$degreeDifference = $remainder->getDegree() - $other->getDegree();
|
||||
$scale = $this->field->multiply($remainder->getCoefficient($remainder->getDegree()), $inverseDenominatorLeadingTerm);
|
||||
$term = $other->multiplyByMonomial($degreeDifference, $scale);
|
||||
$iterationQuotient = $this->field->buildMonomial($degreeDifference, $scale);
|
||||
$quotient = $quotient->addOrSubtract($iterationQuotient);
|
||||
$remainder = $remainder->addOrSubtract($term);
|
||||
}
|
||||
return [$quotient, $remainder];
|
||||
}
|
||||
|
||||
return [$quotient, $remainder];
|
||||
}
|
||||
/**
|
||||
* @return degree of this polynomial
|
||||
*/
|
||||
public function getDegree()
|
||||
{
|
||||
return (is_countable($this->coefficients) ? count($this->coefficients) : 0) - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return degree of this polynomial
|
||||
*/
|
||||
public function getDegree()
|
||||
{
|
||||
return count($this->coefficients) - 1;
|
||||
}
|
||||
public function addOrSubtract($other)
|
||||
{
|
||||
$smallerCoefficients = [];
|
||||
$largerCoefficients = [];
|
||||
$sumDiff = [];
|
||||
$lengthDiff = null;
|
||||
$countLargerCoefficients = null;
|
||||
if ($this->field !== $other->field) {
|
||||
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if ($this->isZero()) {
|
||||
return $other;
|
||||
}
|
||||
if ($other->isZero()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addOrSubtract($other)
|
||||
{
|
||||
if ($this->field !== $other->field) {
|
||||
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
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 = fill_array(0, count($largerCoefficients), 0);
|
||||
$lengthDiff = count($largerCoefficients) - count($smallerCoefficients);
|
||||
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
||||
$sumDiff = arraycopy($largerCoefficients, 0, $sumDiff, 0, $lengthDiff);
|
||||
|
||||
$smallerCoefficients = $this->coefficients;
|
||||
$largerCoefficients = $other->coefficients;
|
||||
if (count($smallerCoefficients) > count($largerCoefficients)) {
|
||||
$temp = $smallerCoefficients;
|
||||
$smallerCoefficients = $largerCoefficients;
|
||||
$largerCoefficients = $temp;
|
||||
}
|
||||
$sumDiff = fill_array(0, count($largerCoefficients), 0);
|
||||
$lengthDiff = count($largerCoefficients) - count($smallerCoefficients);
|
||||
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
||||
$sumDiff = arraycopy($largerCoefficients, 0, $sumDiff, 0, $lengthDiff);
|
||||
$countLargerCoefficients = count($largerCoefficients);
|
||||
for ($i = $lengthDiff; $i < $countLargerCoefficients; $i++) {
|
||||
$sumDiff[$i] = GenericGF::addOrSubtract($smallerCoefficients[$i - $lengthDiff], $largerCoefficients[$i]);
|
||||
}
|
||||
|
||||
$countLargerCoefficients = count($largerCoefficients);
|
||||
for ($i = $lengthDiff; $i < $countLargerCoefficients; $i++) {
|
||||
$sumDiff[$i] = GenericGF::addOrSubtract($smallerCoefficients[$i - $lengthDiff], $largerCoefficients[$i]);
|
||||
}
|
||||
return new GenericGFPoly($this->field, $sumDiff);
|
||||
}
|
||||
|
||||
return new GenericGFPoly($this->field, $sumDiff);
|
||||
}
|
||||
//@Override
|
||||
|
||||
//@Override
|
||||
public function toString()
|
||||
{
|
||||
$result = '';
|
||||
for ($degree = $this->getDegree(); $degree >= 0; $degree--) {
|
||||
$coefficient = $this->getCoefficient($degree);
|
||||
if ($coefficient != 0) {
|
||||
if ($coefficient < 0) {
|
||||
$result .= " - ";
|
||||
$coefficient = -$coefficient;
|
||||
} else {
|
||||
if (strlen((string) $result) > 0) {
|
||||
$result .= " + ";
|
||||
}
|
||||
}
|
||||
if ($degree == 0 || $coefficient != 1) {
|
||||
$alphaPower = $this->field->log($coefficient);
|
||||
if ($alphaPower == 0) {
|
||||
$result .= '1';
|
||||
} elseif ($alphaPower == 1) {
|
||||
$result .= 'a';
|
||||
} else {
|
||||
$result .= "a^";
|
||||
$result .= ($alphaPower);
|
||||
}
|
||||
}
|
||||
if ($degree != 0) {
|
||||
if ($degree == 1) {
|
||||
$result .= 'x';
|
||||
} else {
|
||||
$result .= "x^";
|
||||
$result .= $degree;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
$result = '';
|
||||
for ($degree = $this->getDegree(); $degree >= 0; $degree--) {
|
||||
$coefficient = $this->getCoefficient($degree);
|
||||
if ($coefficient != 0) {
|
||||
if ($coefficient < 0) {
|
||||
$result .= " - ";
|
||||
$coefficient = -$coefficient;
|
||||
} else {
|
||||
if (strlen($result) > 0) {
|
||||
$result .= " + ";
|
||||
}
|
||||
}
|
||||
if ($degree == 0 || $coefficient != 1) {
|
||||
$alphaPower = $this->field->log($coefficient);
|
||||
if ($alphaPower == 0) {
|
||||
$result .= '1';
|
||||
} else if ($alphaPower == 1) {
|
||||
$result .= 'a';
|
||||
} else {
|
||||
$result .= "a^";
|
||||
$result .= ($alphaPower);
|
||||
}
|
||||
}
|
||||
if ($degree != 0) {
|
||||
if ($degree == 1) {
|
||||
$result .= 'x';
|
||||
} else {
|
||||
$result .= "x^";
|
||||
$result .= $degree;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,161 +41,158 @@ namespace Zxing\Common\Reedsolomon;
|
|||
*/
|
||||
final class ReedSolomonDecoder
|
||||
{
|
||||
public function __construct(private $field)
|
||||
{
|
||||
}
|
||||
|
||||
private $field;
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param data $received and error-correction codewords
|
||||
* @param number $twoS of error-correction codewords available
|
||||
*
|
||||
* @throws ReedSolomonException if decoding fails for any reason
|
||||
*/
|
||||
public function decode(&$received, $twoS)
|
||||
{
|
||||
$poly = new GenericGFPoly($this->field, $received);
|
||||
$syndromeCoefficients = fill_array(0, $twoS, 0);
|
||||
$noError = true;
|
||||
for ($i = 0; $i < $twoS; $i++) {
|
||||
$eval = $poly->evaluateAt($this->field->exp($i + $this->field->getGeneratorBase()));
|
||||
$syndromeCoefficients[(is_countable($syndromeCoefficients) ? count($syndromeCoefficients) : 0) - 1 - $i] = $eval;
|
||||
if ($eval != 0) {
|
||||
$noError = false;
|
||||
}
|
||||
}
|
||||
if ($noError) {
|
||||
return;
|
||||
}
|
||||
$syndrome = new GenericGFPoly($this->field, $syndromeCoefficients);
|
||||
$sigmaOmega =
|
||||
$this->runEuclideanAlgorithm($this->field->buildMonomial($twoS, 1), $syndrome, $twoS);
|
||||
$sigma = $sigmaOmega[0];
|
||||
$omega = $sigmaOmega[1];
|
||||
$errorLocations = $this->findErrorLocations($sigma);
|
||||
$errorMagnitudes = $this->findErrorMagnitudes($omega, $errorLocations);
|
||||
$errorLocationsCount = is_countable($errorLocations) ? count($errorLocations) : 0;
|
||||
for ($i = 0; $i < $errorLocationsCount; $i++) {
|
||||
$position = (is_countable($received) ? count($received) : 0) - 1 - $this->field->log($errorLocations[$i]);
|
||||
if ($position < 0) {
|
||||
throw new ReedSolomonException("Bad error location");
|
||||
}
|
||||
$received[$position] = GenericGF::addOrSubtract($received[$position], $errorMagnitudes[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
}
|
||||
private function runEuclideanAlgorithm($a, $b, $R)
|
||||
{
|
||||
// Assume a's degree is >= b's
|
||||
if ($a->getDegree() < $b->getDegree()) {
|
||||
$temp = $a;
|
||||
$a = $b;
|
||||
$b = $temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param received data and error-correction codewords
|
||||
* @param twoS number of error-correction codewords available
|
||||
*
|
||||
* @throws ReedSolomonException if decoding fails for any reason
|
||||
*/
|
||||
public function decode(&$received, $twoS)
|
||||
{
|
||||
$poly = new GenericGFPoly($this->field, $received);
|
||||
$syndromeCoefficients = fill_array(0, $twoS, 0);
|
||||
$noError = true;
|
||||
for ($i = 0; $i < $twoS; $i++) {
|
||||
$eval = $poly->evaluateAt($this->field->exp($i + $this->field->getGeneratorBase()));
|
||||
$syndromeCoefficients[count($syndromeCoefficients) - 1 - $i] = $eval;
|
||||
if ($eval != 0) {
|
||||
$noError = false;
|
||||
}
|
||||
}
|
||||
if ($noError) {
|
||||
return;
|
||||
}
|
||||
$syndrome = new GenericGFPoly($this->field, $syndromeCoefficients);
|
||||
$sigmaOmega =
|
||||
$this->runEuclideanAlgorithm($this->field->buildMonomial($twoS, 1), $syndrome, $twoS);
|
||||
$sigma = $sigmaOmega[0];
|
||||
$omega = $sigmaOmega[1];
|
||||
$errorLocations = $this->findErrorLocations($sigma);
|
||||
$errorMagnitudes = $this->findErrorMagnitudes($omega, $errorLocations);
|
||||
$errorLocationsCount = count($errorLocations);
|
||||
for ($i = 0; $i < $errorLocationsCount; $i++) {
|
||||
$position = count($received) - 1 - $this->field->log($errorLocations[$i]);
|
||||
if ($position < 0) {
|
||||
throw new ReedSolomonException("Bad error location");
|
||||
}
|
||||
$received[$position] = GenericGF::addOrSubtract($received[$position], $errorMagnitudes[$i]);
|
||||
}
|
||||
$rLast = $a;
|
||||
$r = $b;
|
||||
$tLast = $this->field->getZero();
|
||||
$t = $this->field->getOne();
|
||||
|
||||
}
|
||||
// Run Euclidean algorithm until r's degree is less than R/2
|
||||
while ($r->getDegree() >= $R / 2) {
|
||||
$rLastLast = $rLast;
|
||||
$tLastLast = $tLast;
|
||||
$rLast = $r;
|
||||
$tLast = $t;
|
||||
|
||||
private function runEuclideanAlgorithm($a, $b, $R)
|
||||
{
|
||||
// Assume a's degree is >= b's
|
||||
if ($a->getDegree() < $b->getDegree()) {
|
||||
$temp = $a;
|
||||
$a = $b;
|
||||
$b = $temp;
|
||||
}
|
||||
// Divide rLastLast by rLast, with quotient in q and remainder in r
|
||||
if ($rLast->isZero()) {
|
||||
// Oops, Euclidean algorithm already terminated?
|
||||
throw new ReedSolomonException("r_{i-1} was zero");
|
||||
}
|
||||
$r = $rLastLast;
|
||||
$q = $this->field->getZero();
|
||||
$denominatorLeadingTerm = $rLast->getCoefficient($rLast->getDegree());
|
||||
$dltInverse = $this->field->inverse($denominatorLeadingTerm);
|
||||
while ($r->getDegree() >= $rLast->getDegree() && !$r->isZero()) {
|
||||
$degreeDiff = $r->getDegree() - $rLast->getDegree();
|
||||
$scale = $this->field->multiply($r->getCoefficient($r->getDegree()), $dltInverse);
|
||||
$q = $q->addOrSubtract($this->field->buildMonomial($degreeDiff, $scale));
|
||||
$r = $r->addOrSubtract($rLast->multiplyByMonomial($degreeDiff, $scale));
|
||||
}
|
||||
|
||||
$rLast = $a;
|
||||
$r = $b;
|
||||
$tLast = $this->field->getZero();
|
||||
$t = $this->field->getOne();
|
||||
$t = $q->multiply($tLast)->addOrSubtract($tLastLast);
|
||||
|
||||
// Run Euclidean algorithm until r's degree is less than R/2
|
||||
while ($r->getDegree() >= $R / 2) {
|
||||
$rLastLast = $rLast;
|
||||
$tLastLast = $tLast;
|
||||
$rLast = $r;
|
||||
$tLast = $t;
|
||||
if ($r->getDegree() >= $rLast->getDegree()) {
|
||||
throw new ReedSolomonException("Division algorithm failed to reduce polynomial?");
|
||||
}
|
||||
}
|
||||
|
||||
// Divide rLastLast by rLast, with quotient in q and remainder in r
|
||||
if ($rLast->isZero()) {
|
||||
// Oops, Euclidean algorithm already terminated?
|
||||
throw new ReedSolomonException("r_{i-1} was zero");
|
||||
}
|
||||
$r = $rLastLast;
|
||||
$q = $this->field->getZero();
|
||||
$denominatorLeadingTerm = $rLast->getCoefficient($rLast->getDegree());
|
||||
$dltInverse = $this->field->inverse($denominatorLeadingTerm);
|
||||
while ($r->getDegree() >= $rLast->getDegree() && !$r->isZero()) {
|
||||
$degreeDiff = $r->getDegree() - $rLast->getDegree();
|
||||
$scale = $this->field->multiply($r->getCoefficient($r->getDegree()), $dltInverse);
|
||||
$q = $q->addOrSubtract($this->field->buildMonomial($degreeDiff, $scale));
|
||||
$r = $r->addOrSubtract($rLast->multiplyByMonomial($degreeDiff, $scale));
|
||||
}
|
||||
$sigmaTildeAtZero = $t->getCoefficient(0);
|
||||
if ($sigmaTildeAtZero == 0) {
|
||||
throw new ReedSolomonException("sigmaTilde(0) was zero");
|
||||
}
|
||||
|
||||
$t = $q->multiply($tLast)->addOrSubtract($tLastLast);
|
||||
$inverse = $this->field->inverse($sigmaTildeAtZero);
|
||||
$sigma = $t->multiply($inverse);
|
||||
$omega = $r->multiply($inverse);
|
||||
|
||||
if ($r->getDegree() >= $rLast->getDegree()) {
|
||||
throw new ReedSolomonException("Division algorithm failed to reduce polynomial?");
|
||||
}
|
||||
}
|
||||
return [$sigma, $omega];
|
||||
}
|
||||
|
||||
$sigmaTildeAtZero = $t->getCoefficient(0);
|
||||
if ($sigmaTildeAtZero == 0) {
|
||||
throw new ReedSolomonException("sigmaTilde(0) was zero");
|
||||
}
|
||||
private function findErrorLocations($errorLocator)
|
||||
{
|
||||
// This is a direct application of Chien's search
|
||||
$numErrors = $errorLocator->getDegree();
|
||||
if ($numErrors == 1) { // shortcut
|
||||
return [$errorLocator->getCoefficient(1)];
|
||||
}
|
||||
$result = fill_array(0, $numErrors, 0);
|
||||
$e = 0;
|
||||
for ($i = 1; $i < $this->field->getSize() && $e < $numErrors; $i++) {
|
||||
if ($errorLocator->evaluateAt($i) == 0) {
|
||||
$result[$e] = $this->field->inverse($i);
|
||||
$e++;
|
||||
}
|
||||
}
|
||||
if ($e != $numErrors) {
|
||||
throw new ReedSolomonException("Error locator degree does not match number of roots");
|
||||
}
|
||||
|
||||
$inverse = $this->field->inverse($sigmaTildeAtZero);
|
||||
$sigma = $t->multiply($inverse);
|
||||
$omega = $r->multiply($inverse);
|
||||
return $result;
|
||||
}
|
||||
|
||||
return [$sigma, $omega];
|
||||
}
|
||||
private function findErrorMagnitudes($errorEvaluator, $errorLocations)
|
||||
{
|
||||
// This is directly applying Forney's Formula
|
||||
$s = is_countable($errorLocations) ? count($errorLocations) : 0;
|
||||
$result = fill_array(0, $s, 0);
|
||||
for ($i = 0; $i < $s; $i++) {
|
||||
$xiInverse = $this->field->inverse($errorLocations[$i]);
|
||||
$denominator = 1;
|
||||
for ($j = 0; $j < $s; $j++) {
|
||||
if ($i != $j) {
|
||||
//denominator = field.multiply(denominator,
|
||||
// GenericGF.addOrSubtract(1, field.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 = $this->field->multiply($errorLocations[$j], $xiInverse);
|
||||
$termPlus1 = ($term & 0x1) == 0 ? $term | 1 : $term & ~1;
|
||||
$denominator = $this->field->multiply($denominator, $termPlus1);
|
||||
}
|
||||
}
|
||||
$result[$i] = $this->field->multiply(
|
||||
$errorEvaluator->evaluateAt($xiInverse),
|
||||
$this->field->inverse($denominator)
|
||||
);
|
||||
if ($this->field->getGeneratorBase() != 0) {
|
||||
$result[$i] = $this->field->multiply($result[$i], $xiInverse);
|
||||
}
|
||||
}
|
||||
|
||||
private function findErrorLocations($errorLocator)
|
||||
{
|
||||
// This is a direct application of Chien's search
|
||||
$numErrors = $errorLocator->getDegree();
|
||||
if ($numErrors == 1) { // shortcut
|
||||
return [$errorLocator->getCoefficient(1)];
|
||||
}
|
||||
$result = fill_array(0, $numErrors, 0);
|
||||
$e = 0;
|
||||
for ($i = 1; $i < $this->field->getSize() && $e < $numErrors; $i++) {
|
||||
if ($errorLocator->evaluateAt($i) == 0) {
|
||||
$result[$e] = $this->field->inverse($i);
|
||||
$e++;
|
||||
}
|
||||
}
|
||||
if ($e != $numErrors) {
|
||||
throw new ReedSolomonException("Error locator degree does not match number of roots");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function findErrorMagnitudes($errorEvaluator, $errorLocations)
|
||||
{
|
||||
// This is directly applying Forney's Formula
|
||||
$s = count($errorLocations);
|
||||
$result = fill_array(0, $s, 0);
|
||||
for ($i = 0; $i < $s; $i++) {
|
||||
$xiInverse = $this->field->inverse($errorLocations[$i]);
|
||||
$denominator = 1;
|
||||
for ($j = 0; $j < $s; $j++) {
|
||||
if ($i != $j) {
|
||||
//denominator = field.multiply(denominator,
|
||||
// GenericGF.addOrSubtract(1, field.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 = $this->field->multiply($errorLocations[$j], $xiInverse);
|
||||
$termPlus1 = ($term & 0x1) == 0 ? $term | 1 : $term & ~1;
|
||||
$denominator = $this->field->multiply($denominator, $termPlus1);
|
||||
}
|
||||
}
|
||||
$result[$i] = $this->field->multiply($errorEvaluator->evaluateAt($xiInverse),
|
||||
$this->field->inverse($denominator));
|
||||
if ($this->field->getGeneratorBase() != 0) {
|
||||
$result[$i] = $this->field->multiply($result[$i], $xiInverse);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,101 +1,103 @@
|
|||
<?php
|
||||
|
||||
if (!function_exists('arraycopy')) {
|
||||
function arraycopy($srcArray, $srcPos, $destArray, $destPos, $length)
|
||||
{
|
||||
$srcArrayToCopy = array_slice($srcArray, $srcPos, $length);
|
||||
array_splice($destArray, $destPos, $length, $srcArrayToCopy);
|
||||
function arraycopy($srcArray, $srcPos, $destArray, $destPos, $length)
|
||||
{
|
||||
$srcArrayToCopy = array_slice($srcArray, $srcPos, $length);
|
||||
array_splice($destArray, $destPos, $length, $srcArrayToCopy);
|
||||
|
||||
return $destArray;
|
||||
}
|
||||
return $destArray;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('hashCode')) {
|
||||
function hashCode($s)
|
||||
{
|
||||
$h = 0;
|
||||
$len = strlen($s);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$h = (31 * $h + ord($s[$i]));
|
||||
}
|
||||
function hashCode($s)
|
||||
{
|
||||
$h = 0;
|
||||
$len = strlen((string) $s);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$h = (31 * $h + ord($s[$i]));
|
||||
}
|
||||
|
||||
return $h;
|
||||
}
|
||||
return $h;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('numberOfTrailingZeros')) {
|
||||
function numberOfTrailingZeros($i)
|
||||
{
|
||||
if ($i == 0) return 32;
|
||||
$num = 0;
|
||||
while (($i & 1) == 0) {
|
||||
$i >>= 1;
|
||||
$num++;
|
||||
}
|
||||
function numberOfTrailingZeros($i)
|
||||
{
|
||||
if ($i == 0) {
|
||||
return 32;
|
||||
}
|
||||
$num = 0;
|
||||
while (($i & 1) == 0) {
|
||||
$i >>= 1;
|
||||
$num++;
|
||||
}
|
||||
|
||||
return $num;
|
||||
}
|
||||
return $num;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('uRShift')) {
|
||||
function uRShift($a, $b)
|
||||
{
|
||||
static $mask = (8 * PHP_INT_SIZE - 1);
|
||||
if ($b === 0) {
|
||||
return $a;
|
||||
}
|
||||
function uRShift($a, $b)
|
||||
{
|
||||
static $mask = (8 * PHP_INT_SIZE - 1);
|
||||
if ($b === 0) {
|
||||
return $a;
|
||||
}
|
||||
|
||||
return ($a >> $b) & ~(1 << $mask >> ($b - 1));
|
||||
}
|
||||
return ($a >> $b) & ~(1 << $mask >> ($b - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function sdvig3($num,$count=1){//>>> 32 bit
|
||||
$s = decbin($num);
|
||||
$s = decbin($num);
|
||||
|
||||
$sarray = str_split($s,1);
|
||||
$sarray = array_slice($sarray,-32);//32bit
|
||||
$sarray = str_split($s,1);
|
||||
$sarray = array_slice($sarray,-32);//32bit
|
||||
|
||||
for($i=0;$i<=1;$i++) {
|
||||
array_pop($sarray);
|
||||
array_unshift($sarray, '0');
|
||||
}
|
||||
return bindec(implode($sarray));
|
||||
for($i=0;$i<=1;$i++) {
|
||||
array_pop($sarray);
|
||||
array_unshift($sarray, '0');
|
||||
}
|
||||
return bindec(implode($sarray));
|
||||
}
|
||||
*/
|
||||
|
||||
if (!function_exists('sdvig3')) {
|
||||
function sdvig3($a, $b)
|
||||
{
|
||||
if ($a >= 0) {
|
||||
return bindec(decbin($a >> $b)); //simply right shift for positive number
|
||||
}
|
||||
function sdvig3($a, $b)
|
||||
{
|
||||
if ($a >= 0) {
|
||||
return bindec(decbin($a >> $b)); //simply right shift for positive number
|
||||
}
|
||||
|
||||
$bin = decbin($a >> $b);
|
||||
$bin = decbin($a >> $b);
|
||||
|
||||
$bin = substr($bin, $b); // zero fill on the left side
|
||||
$bin = substr($bin, $b); // zero fill on the left side
|
||||
|
||||
return bindec($bin);
|
||||
}
|
||||
return bindec($bin);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('floatToIntBits')) {
|
||||
function floatToIntBits($float_val)
|
||||
{
|
||||
$int = unpack('i', pack('f', $float_val));
|
||||
function floatToIntBits($float_val)
|
||||
{
|
||||
$int = unpack('i', pack('f', $float_val));
|
||||
|
||||
return $int[1];
|
||||
}
|
||||
return $int[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!function_exists('fill_array')) {
|
||||
function fill_array($index, $count, $value)
|
||||
{
|
||||
if ($count <= 0) {
|
||||
return [0];
|
||||
}
|
||||
function fill_array($index, $count, $value)
|
||||
{
|
||||
if ($count <= 0) {
|
||||
return [0];
|
||||
}
|
||||
|
||||
return array_fill($index, $count, $value);
|
||||
}
|
||||
return array_fill($index, $count, $value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,24 +26,24 @@ namespace Zxing;
|
|||
*/
|
||||
final class FormatException extends ReaderException
|
||||
{
|
||||
private static $instance;
|
||||
private static ?\Zxing\FormatException $instance = null;
|
||||
|
||||
public function __construct($cause = null)
|
||||
{
|
||||
if ($cause) {
|
||||
parent::__construct($cause);
|
||||
}
|
||||
}
|
||||
public function __construct($cause = null)
|
||||
{
|
||||
if ($cause) {
|
||||
parent::__construct($cause);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getFormatInstance($cause = null)
|
||||
{
|
||||
if (!self::$instance) {
|
||||
self::$instance = new FormatException();
|
||||
}
|
||||
if (self::$isStackTrace) {
|
||||
return new FormatException($cause);
|
||||
} else {
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
||||
public static function getFormatInstance($cause = null)
|
||||
{
|
||||
if (!self::$instance) {
|
||||
self::$instance = new FormatException();
|
||||
}
|
||||
if (self::$isStackTrace) {
|
||||
return new FormatException($cause);
|
||||
} else {
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,165 +12,176 @@ namespace Zxing;
|
|||
*/
|
||||
final class GDLuminanceSource extends LuminanceSource
|
||||
{
|
||||
public $luminances;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
private $left;
|
||||
private $top;
|
||||
private $gdImage;
|
||||
public $luminances;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
/**
|
||||
* @var mixed|int
|
||||
*/
|
||||
private $left;
|
||||
/**
|
||||
* @var mixed|int
|
||||
*/
|
||||
private $top;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
private $gdImage;
|
||||
|
||||
public function __construct(
|
||||
$gdImage,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left = null,
|
||||
$top = null,
|
||||
$width = null,
|
||||
$height = null
|
||||
) {
|
||||
if (!$left && !$top && !$width && !$height) {
|
||||
$this->GDLuminanceSource($gdImage, $dataWidth, $dataHeight);
|
||||
public function __construct(
|
||||
$gdImage,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left = null,
|
||||
$top = null,
|
||||
$width = null,
|
||||
$height = null
|
||||
) {
|
||||
if (!$left && !$top && !$width && !$height) {
|
||||
$this->GDLuminanceSource($gdImage, $dataWidth, $dataHeight);
|
||||
|
||||
return;
|
||||
}
|
||||
parent::__construct($width, $height);
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
$this->luminances = $gdImage;
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
}
|
||||
return;
|
||||
}
|
||||
parent::__construct($width, $height);
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
$this->luminances = $gdImage;
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
}
|
||||
|
||||
public function GDLuminanceSource($gdImage, $width, $height)
|
||||
{
|
||||
parent::__construct($width, $height);
|
||||
public function GDLuminanceSource($gdImage, $width, $height): void
|
||||
{
|
||||
parent::__construct($width, $height);
|
||||
|
||||
$this->dataWidth = $width;
|
||||
$this->dataHeight = $height;
|
||||
$this->left = 0;
|
||||
$this->top = 0;
|
||||
$this->gdImage = $gdImage;
|
||||
$this->dataWidth = $width;
|
||||
$this->dataHeight = $height;
|
||||
$this->left = 0;
|
||||
$this->top = 0;
|
||||
$this->gdImage = $gdImage;
|
||||
|
||||
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
$this->luminances = [];
|
||||
//$this->luminances = $this->grayScaleToBitmap($this->grayscale());
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
$this->luminances = [];
|
||||
//$this->luminances = $this->grayScaleToBitmap($this->grayscale());
|
||||
|
||||
$array = [];
|
||||
$rgb = [];
|
||||
$array = [];
|
||||
$rgb = [];
|
||||
|
||||
for ($j = 0; $j < $height; $j++) {
|
||||
for ($i = 0; $i < $width; $i++) {
|
||||
$argb = imagecolorat($this->gdImage, $i, $j);
|
||||
$pixel = imagecolorsforindex($this->gdImage, $argb);
|
||||
$r = $pixel['red'];
|
||||
$g = $pixel['green'];
|
||||
$b = $pixel['blue'];
|
||||
if ($r == $g && $g == $b) {
|
||||
for ($j = 0; $j < $height; $j++) {
|
||||
for ($i = 0; $i < $width; $i++) {
|
||||
$argb = imagecolorat($this->gdImage, $i, $j);
|
||||
$pixel = imagecolorsforindex($this->gdImage, $argb);
|
||||
$r = $pixel['red'];
|
||||
$g = $pixel['green'];
|
||||
$b = $pixel['blue'];
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[] = $r;//(($r + 128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$offset = $y * $width;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$pixel = $pixels[$offset + $x];
|
||||
$r = ($pixel >> 16) & 0xff;
|
||||
$g = ($pixel >> 8) & 0xff;
|
||||
$b = $pixel & 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[] = $r;//(($r + 128) % 256) - 128;
|
||||
} else {
|
||||
$this->luminances[(int)($offset + $x)] = (($r+128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$offset = $y * $width;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$pixel = $pixels[$offset + $x];
|
||||
$r = ($pixel >> 16) & 0xff;
|
||||
$g = ($pixel >> 8) & 0xff;
|
||||
$b = $pixel & 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[(int)($offset + $x)] = (($r+128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[(int)($offset + $x)] = (((($r + 2 * $g + $b) / 4)+128)%256) - 128;
|
||||
}
|
||||
$this->luminances[(int)($offset + $x)] = (((($r + 2 * $g + $b) / 4)+128)%256) - 128;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
//}
|
||||
// $this->luminances = $this->grayScaleToBitmap($this->luminances);
|
||||
}
|
||||
}
|
||||
*/
|
||||
//}
|
||||
// $this->luminances = $this->grayScaleToBitmap($this->luminances);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getRow($y, $row = null)
|
||||
{
|
||||
if ($y < 0 || $y >= $this->getHeight()) {
|
||||
throw new \InvalidArgumentException('Requested row is outside the image: ' . $y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || count($row) < $width) {
|
||||
$row = [];
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->luminances, $offset, $row, 0, $width);
|
||||
//@Override
|
||||
public function getRow($y, $row = null)
|
||||
{
|
||||
if ($y < 0 || $y >= $this->getHeight()) {
|
||||
throw new \InvalidArgumentException('Requested row is outside the image: ' . $y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || (is_countable($row) ? count($row) : 0) < $width) {
|
||||
$row = [];
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->luminances, $offset, $row, 0, $width);
|
||||
|
||||
return $row;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getMatrix()
|
||||
{
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
//@Override
|
||||
public function getMatrix()
|
||||
{
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->luminances;
|
||||
}
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->luminances;
|
||||
}
|
||||
|
||||
$area = $width * $height;
|
||||
$matrix = [];
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
$area = $width * $height;
|
||||
$matrix = [];
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
|
||||
|
||||
return $matrix;
|
||||
}
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$rgb = $this->luminances;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$rgb = $this->luminances;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
|
||||
return $matrix;
|
||||
}
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function isCropSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//@Override
|
||||
public function isCropSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function crop($left, $top, $width, $height)
|
||||
{
|
||||
return new GDLuminanceSource($this->luminances,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height);
|
||||
}
|
||||
//@Override
|
||||
public function crop($left, $top, $width, $height): \Zxing\GDLuminanceSource
|
||||
{
|
||||
return new GDLuminanceSource(
|
||||
$this->luminances,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,143 +8,151 @@ namespace Zxing;
|
|||
*/
|
||||
final class IMagickLuminanceSource extends LuminanceSource
|
||||
{
|
||||
public $luminances;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
private $left;
|
||||
private $top;
|
||||
private $image;
|
||||
public $luminances;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
/**
|
||||
* @var mixed|int
|
||||
*/
|
||||
private $left;
|
||||
/**
|
||||
* @var mixed|int
|
||||
*/
|
||||
private $top;
|
||||
private ?\Imagick $image = null;
|
||||
|
||||
public function __construct(
|
||||
\Imagick $image,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left = null,
|
||||
$top = null,
|
||||
$width = null,
|
||||
$height = null
|
||||
) {
|
||||
if (!$left && !$top && !$width && !$height) {
|
||||
$this->_IMagickLuminanceSource($image, $dataWidth, $dataHeight);
|
||||
public function __construct(
|
||||
\Imagick $image,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left = null,
|
||||
$top = null,
|
||||
$width = null,
|
||||
$height = null
|
||||
) {
|
||||
if (!$left && !$top && !$width && !$height) {
|
||||
$this->_IMagickLuminanceSource($image, $dataWidth, $dataHeight);
|
||||
|
||||
return;
|
||||
}
|
||||
parent::__construct($width, $height);
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
$this->luminances = $image;
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
}
|
||||
return;
|
||||
}
|
||||
parent::__construct($width, $height);
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
$this->luminances = $image;
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
}
|
||||
|
||||
public function _IMagickLuminanceSource(\Imagick $image, $width, $height)
|
||||
{
|
||||
parent::__construct($width, $height);
|
||||
public function _IMagickLuminanceSource(\Imagick $image, $width, $height): void
|
||||
{
|
||||
parent::__construct($width, $height);
|
||||
|
||||
$this->dataWidth = $width;
|
||||
$this->dataHeight = $height;
|
||||
$this->left = 0;
|
||||
$this->top = 0;
|
||||
$this->image = $image;
|
||||
$this->dataWidth = $width;
|
||||
$this->dataHeight = $height;
|
||||
$this->left = 0;
|
||||
$this->top = 0;
|
||||
$this->image = $image;
|
||||
|
||||
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
$this->luminances = [];
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
$this->luminances = [];
|
||||
|
||||
$image->setImageColorspace(\Imagick::COLORSPACE_GRAY);
|
||||
// $image->newPseudoImage(0, 0, "magick:rose");
|
||||
$pixels = $image->exportImagePixels(1, 1, $width, $height, "RGB", \Imagick::PIXEL_CHAR);
|
||||
$image->setImageColorspace(\Imagick::COLORSPACE_GRAY);
|
||||
// $image->newPseudoImage(0, 0, "magick:rose");
|
||||
$pixels = $image->exportImagePixels(1, 1, $width, $height, "RGB", \Imagick::PIXEL_CHAR);
|
||||
|
||||
$array = [];
|
||||
$rgb = [];
|
||||
$array = [];
|
||||
$rgb = [];
|
||||
|
||||
$countPixels = count($pixels);
|
||||
for ($i = 0; $i < $countPixels; $i += 3) {
|
||||
$r = $pixels[$i] & 0xff;
|
||||
$g = $pixels[$i + 1] & 0xff;
|
||||
$b = $pixels[$i + 2] & 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
$countPixels = count($pixels);
|
||||
for ($i = 0; $i < $countPixels; $i += 3) {
|
||||
$r = $pixels[$i] & 0xff;
|
||||
$g = $pixels[$i + 1] & 0xff;
|
||||
$b = $pixels[$i + 2] & 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[] = $r;//(($r + 128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->luminances[] = $r;//(($r + 128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getRow($y, $row = null)
|
||||
{
|
||||
if ($y < 0 || $y >= $this->getHeight()) {
|
||||
throw new \InvalidArgumentException('Requested row is outside the image: ' . $y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || count($row) < $width) {
|
||||
$row = [];
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->luminances, $offset, $row, 0, $width);
|
||||
//@Override
|
||||
public function getRow($y, $row = null)
|
||||
{
|
||||
if ($y < 0 || $y >= $this->getHeight()) {
|
||||
throw new \InvalidArgumentException('Requested row is outside the image: ' . $y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || (is_countable($row) ? count($row) : 0) < $width) {
|
||||
$row = [];
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->luminances, $offset, $row, 0, $width);
|
||||
|
||||
return $row;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getMatrix()
|
||||
{
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
//@Override
|
||||
public function getMatrix()
|
||||
{
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->luminances;
|
||||
}
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->luminances;
|
||||
}
|
||||
|
||||
$area = $width * $height;
|
||||
$matrix = [];
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
$area = $width * $height;
|
||||
$matrix = [];
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
|
||||
|
||||
return $matrix;
|
||||
}
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$rgb = $this->luminances;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$rgb = $this->luminances;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
|
||||
return $matrix;
|
||||
}
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function isCropSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//@Override
|
||||
public function isCropSupported(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function crop($left, $top, $width, $height)
|
||||
{
|
||||
return $this->luminances->cropImage($width, $height, $left, $top);
|
||||
//@Override
|
||||
public function crop($left, $top, $width, $height)
|
||||
{
|
||||
return $this->luminances->cropImage($width, $height, $left, $top);
|
||||
|
||||
return new GDLuminanceSource($this->luminances,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height);
|
||||
}
|
||||
return new GDLuminanceSource(
|
||||
$this->luminances,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,14 +28,8 @@ namespace Zxing;
|
|||
*/
|
||||
abstract class LuminanceSource
|
||||
{
|
||||
|
||||
private $width;
|
||||
private $height;
|
||||
|
||||
public function __construct($width, $height)
|
||||
public function __construct(private $width, private $height)
|
||||
{
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -46,20 +40,20 @@ abstract class LuminanceSource
|
|||
* larger than width * height bytes on some platforms. Do not modify the contents
|
||||
* of the result.
|
||||
*/
|
||||
public abstract function getMatrix();
|
||||
abstract public function getMatrix();
|
||||
|
||||
/**
|
||||
* @return The width of the bitmap.
|
||||
* @return float The width of the bitmap.
|
||||
*/
|
||||
public final function getWidth()
|
||||
final public function getWidth(): float
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height of the bitmap.
|
||||
* @return float The height of the bitmap.
|
||||
*/
|
||||
public final function getHeight()
|
||||
final public function getHeight(): float
|
||||
{
|
||||
return $this->height;
|
||||
}
|
||||
|
|
@ -67,7 +61,7 @@ abstract class LuminanceSource
|
|||
/**
|
||||
* @return bool Whether this subclass supports cropping.
|
||||
*/
|
||||
public function isCropSupported()
|
||||
public function isCropSupported(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -76,12 +70,12 @@ abstract class LuminanceSource
|
|||
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||
*
|
||||
* @param left The left coordinate, which must be in [0,getWidth())
|
||||
* @param top The top coordinate, which must be in [0,getHeight())
|
||||
* @param width The width of the rectangle to crop.
|
||||
* @param height The height of the rectangle to crop.
|
||||
* @param $left The left coordinate, which must be in [0,getWidth())
|
||||
* @param $top The top coordinate, which must be in [0,getHeight())
|
||||
* @param $width The width of the rectangle to crop.
|
||||
* @param $height The height of the rectangle to crop.
|
||||
*
|
||||
* @return A cropped version of this object.
|
||||
* @return mixed A cropped version of this object.
|
||||
*/
|
||||
public function crop($left, $top, $width, $height)
|
||||
{
|
||||
|
|
@ -89,9 +83,9 @@ abstract class LuminanceSource
|
|||
}
|
||||
|
||||
/**
|
||||
* @return Whether this subclass supports counter-clockwise rotation.
|
||||
* @return bool Whether this subclass supports counter-clockwise rotation.
|
||||
*/
|
||||
public function isRotateSupported()
|
||||
public function isRotateSupported(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -100,16 +94,16 @@ abstract class LuminanceSource
|
|||
* @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes
|
||||
* white and vice versa, and each value becomes (255-value).
|
||||
*/
|
||||
public function invert()
|
||||
{
|
||||
return new InvertedLuminanceSource($this);
|
||||
}
|
||||
// public function invert()
|
||||
// {
|
||||
// return new InvertedLuminanceSource($this);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
* @return mixed A rotated version of this object.
|
||||
*/
|
||||
public function rotateCounterClockwise()
|
||||
{
|
||||
|
|
@ -120,27 +114,27 @@ abstract class LuminanceSource
|
|||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
* @return mixed A rotated version of this object.
|
||||
*/
|
||||
public function rotateCounterClockwise45()
|
||||
{
|
||||
throw new \Exception("This luminance source does not support rotation by 45 degrees.");
|
||||
}
|
||||
|
||||
public final function toString()
|
||||
final public function toString()
|
||||
{
|
||||
$row = [];
|
||||
$row = [];
|
||||
$result = '';
|
||||
for ($y = 0; $y < $this->height; $y++) {
|
||||
$row = $this->getRow($y, $row);
|
||||
for ($x = 0; $x < $this->width; $x++) {
|
||||
$luminance = $row[$x] & 0xFF;
|
||||
$c = '';
|
||||
$c = '';
|
||||
if ($luminance < 0x40) {
|
||||
$c = '#';
|
||||
} else if ($luminance < 0x80) {
|
||||
} elseif ($luminance < 0x80) {
|
||||
$c = '+';
|
||||
} else if ($luminance < 0xC0) {
|
||||
} elseif ($luminance < 0xC0) {
|
||||
$c = '.';
|
||||
} else {
|
||||
$c = ' ';
|
||||
|
|
@ -167,5 +161,5 @@ abstract class LuminanceSource
|
|||
* @return array
|
||||
* An array containing the luminance data.
|
||||
*/
|
||||
public abstract function getRow($y, $row);
|
||||
abstract public function getRow($y, $row);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ namespace Zxing;
|
|||
*/
|
||||
final class NotFoundException extends ReaderException
|
||||
{
|
||||
private static $instance;
|
||||
private static ?\Zxing\NotFoundException $instance = null;
|
||||
|
||||
public static function getNotFoundInstance()
|
||||
{
|
||||
if (!self::$instance) {
|
||||
self::$instance = new NotFoundException();
|
||||
}
|
||||
public static function getNotFoundInstance()
|
||||
{
|
||||
if (!self::$instance) {
|
||||
self::$instance = new NotFoundException();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,154 +29,154 @@ namespace Zxing;
|
|||
*/
|
||||
final class PlanarYUVLuminanceSource extends LuminanceSource
|
||||
{
|
||||
private static $THUMBNAIL_SCALE_FACTOR = 2;
|
||||
private static int $THUMBNAIL_SCALE_FACTOR = 2;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
private $left;
|
||||
private $top;
|
||||
|
||||
private $yuvData;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
private $left;
|
||||
private $top;
|
||||
public function __construct(
|
||||
private $yuvData,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left,
|
||||
$top,
|
||||
$width,
|
||||
$height,
|
||||
$reverseHorizontal
|
||||
)
|
||||
{
|
||||
parent::__construct($width, $height);
|
||||
|
||||
public function __construct($yuvData,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left,
|
||||
$top,
|
||||
$width,
|
||||
$height,
|
||||
$reverseHorizontal)
|
||||
{
|
||||
parent::__construct($width, $height);
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
if ($reverseHorizontal) {
|
||||
$this->reverseHorizontal($width, $height);
|
||||
}
|
||||
}
|
||||
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
//@Override
|
||||
public function getRow($y, $row = null)
|
||||
{
|
||||
if ($y < 0 || $y >= $this->getHeight()) {
|
||||
throw new \InvalidArgumentException("Requested row is outside the image: " + \Y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || (is_countable($row) ? count($row) : 0) < $width) {
|
||||
$row = [];//new byte[width];
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->yuvData, $offset, $row, 0, $width);
|
||||
|
||||
$this->yuvData = $yuvData;
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
if ($reverseHorizontal) {
|
||||
$this->reverseHorizontal($width, $height);
|
||||
}
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getRow($y, $row = null)
|
||||
{
|
||||
if ($y < 0 || $y >= getHeight()) {
|
||||
throw new \InvalidArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || count($row) < $width) {
|
||||
$row = [];//new byte[width];
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->yuvData, $offset, $row, 0, $width);
|
||||
//@Override
|
||||
public function getMatrix()
|
||||
{
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
|
||||
return $row;
|
||||
}
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->yuvData;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getMatrix()
|
||||
{
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
$area = $width * $height;
|
||||
$matrix = [];//new byte[area];
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->yuvData;
|
||||
}
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->yuvData, $inputOffset, $matrix, 0, $area);
|
||||
|
||||
$area = $width * $height;
|
||||
$matrix = [];//new byte[area];
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->yuvData, $inputOffset, $matrix, 0, $area);
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$yuv = $this->yuvData;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($this->yuvData, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
|
||||
return $matrix;
|
||||
}
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$yuv = $this->yuvData;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($this->yuvData, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
// @Override
|
||||
public function isCropSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return $matrix;
|
||||
}
|
||||
// @Override
|
||||
public function crop($left, $top, $width, $height): \Zxing\PlanarYUVLuminanceSource
|
||||
{
|
||||
return new PlanarYUVLuminanceSource(
|
||||
$this->yuvData,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
// @Override
|
||||
public function isCropSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public function renderThumbnail()
|
||||
{
|
||||
$width = (int)($this->getWidth() / self::$THUMBNAIL_SCALE_FACTOR);
|
||||
$height = (int)($this->getHeight() / self::$THUMBNAIL_SCALE_FACTOR);
|
||||
$pixels = [];//new int[width * height];
|
||||
$yuv = $this->yuvData;
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
|
||||
// @Override
|
||||
public function crop($left, $top, $width, $height)
|
||||
{
|
||||
return new PlanarYUVLuminanceSource($this->yuvData,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height,
|
||||
false);
|
||||
}
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$grey = ($yuv[$inputOffset + $x * self::$THUMBNAIL_SCALE_FACTOR] & 0xff);
|
||||
$pixels[$outputOffset + $x] = (0xFF000000 | ($grey * 0x00010101));
|
||||
}
|
||||
$inputOffset += $this->dataWidth * self::$THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
public function renderThumbnail()
|
||||
{
|
||||
$width = (int)($this->getWidth() / self::$THUMBNAIL_SCALE_FACTOR);
|
||||
$height = (int)($this->getHeight() / self::$THUMBNAIL_SCALE_FACTOR);
|
||||
$pixels = [];//new int[width * height];
|
||||
$yuv = $this->yuvData;
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
return $pixels;
|
||||
}
|
||||
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$grey = ($yuv[$inputOffset + $x * self::$THUMBNAIL_SCALE_FACTOR] & 0xff);
|
||||
$pixels[$outputOffset + $x] = (0xFF000000 | ($grey * 0x00010101));
|
||||
}
|
||||
$inputOffset += $this->dataWidth * self::$THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
return $pixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return width of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
/*
|
||||
/**
|
||||
* @return width of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
/*
|
||||
public int getThumbnailWidth() {
|
||||
return getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||
return getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* @return height of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
/*
|
||||
/**
|
||||
* @return height of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
/*
|
||||
public int getThumbnailHeight() {
|
||||
return getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||
return getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
private void reverseHorizontal(int width, int height) {
|
||||
byte[] yuvData = this.yuvData;
|
||||
for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {
|
||||
int middle = rowStart + width / 2;
|
||||
for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {
|
||||
byte temp = yuvData[x1];
|
||||
yuvData[x1] = yuvData[x2];
|
||||
yuvData[x2] = temp;
|
||||
}
|
||||
}
|
||||
byte[] yuvData = this.yuvData;
|
||||
for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {
|
||||
int middle = rowStart + width / 2;
|
||||
for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {
|
||||
byte temp = yuvData[x1];
|
||||
yuvData[x1] = yuvData[x2];
|
||||
yuvData[x2] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,99 +7,95 @@ use Zxing\Qrcode\QRCodeReader;
|
|||
|
||||
final class QrReader
|
||||
{
|
||||
const SOURCE_TYPE_FILE = 'file';
|
||||
const SOURCE_TYPE_BLOB = 'blob';
|
||||
const SOURCE_TYPE_RESOURCE = 'resource';
|
||||
public const SOURCE_TYPE_FILE = 'file';
|
||||
public const SOURCE_TYPE_BLOB = 'blob';
|
||||
public const SOURCE_TYPE_RESOURCE = 'resource';
|
||||
|
||||
private $bitmap;
|
||||
private $reader;
|
||||
private $result;
|
||||
private readonly \Zxing\BinaryBitmap $bitmap;
|
||||
private readonly \Zxing\Qrcode\QRCodeReader $reader;
|
||||
private \Zxing\Result|bool|null $result = null;
|
||||
|
||||
public function __construct($imgSource, $sourceType = QrReader::SOURCE_TYPE_FILE, $useImagickIfAvailable = true)
|
||||
{
|
||||
if (!in_array($sourceType, [
|
||||
self::SOURCE_TYPE_FILE,
|
||||
self::SOURCE_TYPE_BLOB,
|
||||
self::SOURCE_TYPE_RESOURCE,
|
||||
], true)) {
|
||||
throw new \InvalidArgumentException('Invalid image source.');
|
||||
}
|
||||
$im = null;
|
||||
switch ($sourceType) {
|
||||
case QrReader::SOURCE_TYPE_FILE:
|
||||
if ($useImagickIfAvailable && extension_loaded('imagick')) {
|
||||
$im = new \Imagick();
|
||||
$im->readImage($imgSource);
|
||||
} else {
|
||||
$image = file_get_contents($imgSource);
|
||||
$im = imagecreatefromstring($image);
|
||||
}
|
||||
break;
|
||||
public function __construct($imgSource, $sourceType = QrReader::SOURCE_TYPE_FILE, $useImagickIfAvailable = true)
|
||||
{
|
||||
if (!in_array($sourceType, [
|
||||
self::SOURCE_TYPE_FILE,
|
||||
self::SOURCE_TYPE_BLOB,
|
||||
self::SOURCE_TYPE_RESOURCE,
|
||||
], true)) {
|
||||
throw new \InvalidArgumentException('Invalid image source.');
|
||||
}
|
||||
$im = null;
|
||||
switch ($sourceType) {
|
||||
case QrReader::SOURCE_TYPE_FILE:
|
||||
if ($useImagickIfAvailable && extension_loaded('imagick')) {
|
||||
$im = new \Imagick();
|
||||
$im->readImage($imgSource);
|
||||
} else {
|
||||
$image = file_get_contents($imgSource);
|
||||
$im = imagecreatefromstring($image);
|
||||
}
|
||||
break;
|
||||
|
||||
case QrReader::SOURCE_TYPE_BLOB:
|
||||
if ($useImagickIfAvailable && extension_loaded('imagick')) {
|
||||
$im = new \Imagick();
|
||||
$im->readImageBlob($imgSource);
|
||||
} else {
|
||||
$im = imagecreatefromstring($imgSource);
|
||||
}
|
||||
break;
|
||||
case QrReader::SOURCE_TYPE_BLOB:
|
||||
if ($useImagickIfAvailable && extension_loaded('imagick')) {
|
||||
$im = new \Imagick();
|
||||
$im->readImageBlob($imgSource);
|
||||
} else {
|
||||
$im = imagecreatefromstring($imgSource);
|
||||
}
|
||||
break;
|
||||
|
||||
case QrReader::SOURCE_TYPE_RESOURCE:
|
||||
$im = $imgSource;
|
||||
if ($useImagickIfAvailable && extension_loaded('imagick')) {
|
||||
$useImagickIfAvailable = true;
|
||||
} else {
|
||||
$useImagickIfAvailable = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ($useImagickIfAvailable && extension_loaded('imagick')) {
|
||||
if (!$im instanceof \Imagick) {
|
||||
throw new \InvalidArgumentException('Invalid image source.');
|
||||
}
|
||||
$width = $im->getImageWidth();
|
||||
$height = $im->getImageHeight();
|
||||
$source = new IMagickLuminanceSource($im, $width, $height);
|
||||
} else {
|
||||
if (!is_resource($im) && !is_object($im)) {
|
||||
throw new \InvalidArgumentException('Invalid image source.');
|
||||
}
|
||||
$width = imagesx($im);
|
||||
$height = imagesy($im);
|
||||
$source = new GDLuminanceSource($im, $width, $height);
|
||||
}
|
||||
$histo = new HybridBinarizer($source);
|
||||
$this->bitmap = new BinaryBitmap($histo);
|
||||
$this->reader = new QRCodeReader();
|
||||
}
|
||||
case QrReader::SOURCE_TYPE_RESOURCE:
|
||||
$im = $imgSource;
|
||||
if ($useImagickIfAvailable && extension_loaded('imagick')) {
|
||||
$useImagickIfAvailable = true;
|
||||
} else {
|
||||
$useImagickIfAvailable = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ($useImagickIfAvailable && extension_loaded('imagick')) {
|
||||
if (!$im instanceof \Imagick) {
|
||||
throw new \InvalidArgumentException('Invalid image source.');
|
||||
}
|
||||
$width = $im->getImageWidth();
|
||||
$height = $im->getImageHeight();
|
||||
$source = new IMagickLuminanceSource($im, $width, $height);
|
||||
} else {
|
||||
if (!$im instanceof \GdImage && !is_object($im)) {
|
||||
throw new \InvalidArgumentException('Invalid image source.');
|
||||
}
|
||||
$width = imagesx($im);
|
||||
$height = imagesy($im);
|
||||
$source = new GDLuminanceSource($im, $width, $height);
|
||||
}
|
||||
$histo = new HybridBinarizer($source);
|
||||
$this->bitmap = new BinaryBitmap($histo);
|
||||
$this->reader = new QRCodeReader();
|
||||
}
|
||||
|
||||
public function decode()
|
||||
{
|
||||
try {
|
||||
$this->result = $this->reader->decode($this->bitmap);
|
||||
} catch (NotFoundException $er) {
|
||||
$this->result = false;
|
||||
} catch (FormatException $er) {
|
||||
$this->result = false;
|
||||
} catch (ChecksumException $er) {
|
||||
$this->result = false;
|
||||
}
|
||||
}
|
||||
public function decode($hints = null): void
|
||||
{
|
||||
try {
|
||||
$this->result = $this->reader->decode($this->bitmap, $hints);
|
||||
} catch (NotFoundException|FormatException|ChecksumException) {
|
||||
$this->result = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function text()
|
||||
{
|
||||
$this->decode();
|
||||
public function text($hints = null)
|
||||
{
|
||||
$this->decode($hints);
|
||||
|
||||
if ($this->result !== false && method_exists($this->result, 'toString')) {
|
||||
return $this->result->toString();
|
||||
}
|
||||
if ($this->result !== false && method_exists($this->result, 'toString')) {
|
||||
return $this->result->toString();
|
||||
}
|
||||
|
||||
return $this->result;
|
||||
}
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,247 +17,246 @@
|
|||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\FormatException;
|
||||
use Zxing\Common\BitMatrix;
|
||||
use Zxing\FormatException;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class BitMatrixParser
|
||||
{
|
||||
private $bitMatrix;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
private $parsedVersion;
|
||||
private $parsedFormatInfo;
|
||||
private $mirror;
|
||||
|
||||
private $bitMatrix;
|
||||
private $parsedVersion;
|
||||
private $parsedFormatInfo;
|
||||
private $mirror;
|
||||
/**
|
||||
* @param $bitMatrix {@link BitMatrix} to parse
|
||||
*
|
||||
* @throws FormatException if dimension is not >= 21 and 1 mod 4
|
||||
*/
|
||||
public function __construct($bitMatrix)
|
||||
{
|
||||
$dimension = $bitMatrix->getHeight();
|
||||
if ($dimension < 21 || ($dimension & 0x03) != 1) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$this->bitMatrix = $bitMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bitMatrix {@link BitMatrix} to parse
|
||||
*
|
||||
* @throws FormatException if dimension is not >= 21 and 1 mod 4
|
||||
*/
|
||||
public function __construct($bitMatrix)
|
||||
{
|
||||
$dimension = $bitMatrix->getHeight();
|
||||
if ($dimension < 21 || ($dimension & 0x03) != 1) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$this->bitMatrix = $bitMatrix;
|
||||
}
|
||||
/**
|
||||
* <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
|
||||
* correct order in order to reconstruct the codewords bytes contained within the
|
||||
* QR Code.</p>
|
||||
*
|
||||
* @return bytes encoded within the QR Code
|
||||
* @throws FormatException if the exact number of bytes expected is not read
|
||||
*/
|
||||
public function readCodewords()
|
||||
{
|
||||
$formatInfo = $this->readFormatInformation();
|
||||
$version = $this->readVersion();
|
||||
|
||||
/**
|
||||
* <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
|
||||
* correct order in order to reconstruct the codewords bytes contained within the
|
||||
* QR Code.</p>
|
||||
*
|
||||
* @return bytes encoded within the QR Code
|
||||
* @throws FormatException if the exact number of bytes expected is not read
|
||||
*/
|
||||
public function readCodewords()
|
||||
{
|
||||
// Get the data mask for the format used in this QR Code. This will exclude
|
||||
// some bits from reading as we wind through the bit matrix.
|
||||
$dataMask = DataMask::forReference($formatInfo->getDataMask());
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
$dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
|
||||
|
||||
$formatInfo = $this->readFormatInformation();
|
||||
$version = $this->readVersion();
|
||||
$functionPattern = $version->buildFunctionPattern();
|
||||
|
||||
// Get the data mask for the format used in this QR Code. This will exclude
|
||||
// some bits from reading as we wind through the bit matrix.
|
||||
$dataMask = DataMask::forReference($formatInfo->getDataMask());
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
$dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
|
||||
$readingUp = true;
|
||||
if ($version->getTotalCodewords()) {
|
||||
$result = fill_array(0, $version->getTotalCodewords(), 0);
|
||||
} else {
|
||||
$result = [];
|
||||
}
|
||||
$resultOffset = 0;
|
||||
$currentByte = 0;
|
||||
$bitsRead = 0;
|
||||
// Read columns in pairs, from right to left
|
||||
for ($j = $dimension - 1; $j > 0; $j -= 2) {
|
||||
if ($j == 6) {
|
||||
// Skip whole column with vertical alignment pattern;
|
||||
// saves time and makes the other code proceed more cleanly
|
||||
$j--;
|
||||
}
|
||||
// Read alternatingly from bottom to top then top to bottom
|
||||
for ($count = 0; $count < $dimension; $count++) {
|
||||
$i = $readingUp ? $dimension - 1 - $count : $count;
|
||||
for ($col = 0; $col < 2; $col++) {
|
||||
// Ignore bits covered by the function pattern
|
||||
if (!$functionPattern->get($j - $col, $i)) {
|
||||
// Read a bit
|
||||
$bitsRead++;
|
||||
$currentByte <<= 1;
|
||||
if ($this->bitMatrix->get($j - $col, $i)) {
|
||||
$currentByte |= 1;
|
||||
}
|
||||
// If we've made a whole byte, save it off
|
||||
if ($bitsRead == 8) {
|
||||
$result[$resultOffset++] = $currentByte; //(byte)
|
||||
$bitsRead = 0;
|
||||
$currentByte = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$readingUp ^= true; // readingUp = !readingUp; // switch directions
|
||||
}
|
||||
if ($resultOffset != $version->getTotalCodewords()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
$functionPattern = $version->buildFunctionPattern();
|
||||
return $result;
|
||||
}
|
||||
|
||||
$readingUp = true;
|
||||
if ($version->getTotalCodewords()) {
|
||||
$result = fill_array(0, $version->getTotalCodewords(), 0);
|
||||
} else {
|
||||
$result = [];
|
||||
}
|
||||
$resultOffset = 0;
|
||||
$currentByte = 0;
|
||||
$bitsRead = 0;
|
||||
// Read columns in pairs, from right to left
|
||||
for ($j = $dimension - 1; $j > 0; $j -= 2) {
|
||||
if ($j == 6) {
|
||||
// Skip whole column with vertical alignment pattern;
|
||||
// saves time and makes the other code proceed more cleanly
|
||||
$j--;
|
||||
}
|
||||
// Read alternatingly from bottom to top then top to bottom
|
||||
for ($count = 0; $count < $dimension; $count++) {
|
||||
$i = $readingUp ? $dimension - 1 - $count : $count;
|
||||
for ($col = 0; $col < 2; $col++) {
|
||||
// Ignore bits covered by the function pattern
|
||||
if (!$functionPattern->get($j - $col, $i)) {
|
||||
// Read a bit
|
||||
$bitsRead++;
|
||||
$currentByte <<= 1;
|
||||
if ($this->bitMatrix->get($j - $col, $i)) {
|
||||
$currentByte |= 1;
|
||||
}
|
||||
// If we've made a whole byte, save it off
|
||||
if ($bitsRead == 8) {
|
||||
$result[$resultOffset++] = $currentByte; //(byte)
|
||||
$bitsRead = 0;
|
||||
$currentByte = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$readingUp ^= true; // readingUp = !readingUp; // switch directions
|
||||
}
|
||||
if ($resultOffset != $version->getTotalCodewords()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
/**
|
||||
* <p>Reads format information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link FormatInformation} encapsulating the QR Code's format info
|
||||
* @throws FormatException if both format information locations cannot be parsed as
|
||||
* the valid encoding of format information
|
||||
*/
|
||||
public function readFormatInformation()
|
||||
{
|
||||
if ($this->parsedFormatInfo != null) {
|
||||
return $this->parsedFormatInfo;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
// Read top-left format info bits
|
||||
$formatInfoBits1 = 0;
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$formatInfoBits1 = $this->copyBit($i, 8, $formatInfoBits1);
|
||||
}
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
$formatInfoBits1 = $this->copyBit(7, 8, $formatInfoBits1);
|
||||
$formatInfoBits1 = $this->copyBit(8, 8, $formatInfoBits1);
|
||||
$formatInfoBits1 = $this->copyBit(8, 7, $formatInfoBits1);
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
for ($j = 5; $j >= 0; $j--) {
|
||||
$formatInfoBits1 = $this->copyBit(8, $j, $formatInfoBits1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads format information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link FormatInformation} encapsulating the QR Code's format info
|
||||
* @throws FormatException if both format information locations cannot be parsed as
|
||||
* the valid encoding of format information
|
||||
*/
|
||||
public function readFormatInformation()
|
||||
{
|
||||
// Read the top-right/bottom-left pattern too
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
$formatInfoBits2 = 0;
|
||||
$jMin = $dimension - 7;
|
||||
for ($j = $dimension - 1; $j >= $jMin; $j--) {
|
||||
$formatInfoBits2 = $this->copyBit(8, $j, $formatInfoBits2);
|
||||
}
|
||||
for ($i = $dimension - 8; $i < $dimension; $i++) {
|
||||
$formatInfoBits2 = $this->copyBit($i, 8, $formatInfoBits2);
|
||||
}
|
||||
|
||||
if ($this->parsedFormatInfo != null) {
|
||||
return $this->parsedFormatInfo;
|
||||
}
|
||||
$parsedFormatInfo = FormatInformation::decodeFormatInformation($formatInfoBits1, $formatInfoBits2);
|
||||
if ($parsedFormatInfo != null) {
|
||||
return $parsedFormatInfo;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
// Read top-left format info bits
|
||||
$formatInfoBits1 = 0;
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$formatInfoBits1 = $this->copyBit($i, 8, $formatInfoBits1);
|
||||
}
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
$formatInfoBits1 = $this->copyBit(7, 8, $formatInfoBits1);
|
||||
$formatInfoBits1 = $this->copyBit(8, 8, $formatInfoBits1);
|
||||
$formatInfoBits1 = $this->copyBit(8, 7, $formatInfoBits1);
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
for ($j = 5; $j >= 0; $j--) {
|
||||
$formatInfoBits1 = $this->copyBit(8, $j, $formatInfoBits1);
|
||||
}
|
||||
private function copyBit($i, $j, $versionBits)
|
||||
{
|
||||
$bit = $this->mirror ? $this->bitMatrix->get($j, $i) : $this->bitMatrix->get($i, $j);
|
||||
|
||||
// Read the top-right/bottom-left pattern too
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
$formatInfoBits2 = 0;
|
||||
$jMin = $dimension - 7;
|
||||
for ($j = $dimension - 1; $j >= $jMin; $j--) {
|
||||
$formatInfoBits2 = $this->copyBit(8, $j, $formatInfoBits2);
|
||||
}
|
||||
for ($i = $dimension - 8; $i < $dimension; $i++) {
|
||||
$formatInfoBits2 = $this->copyBit($i, 8, $formatInfoBits2);
|
||||
}
|
||||
return $bit ? ($versionBits << 1) | 0x1 : $versionBits << 1;
|
||||
}
|
||||
|
||||
$parsedFormatInfo = FormatInformation::decodeFormatInformation($formatInfoBits1, $formatInfoBits2);
|
||||
if ($parsedFormatInfo != null) {
|
||||
return $parsedFormatInfo;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
/**
|
||||
* <p>Reads version information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link Version} encapsulating the QR Code's version
|
||||
* @throws FormatException if both version information locations cannot be parsed as
|
||||
* the valid encoding of version information
|
||||
*/
|
||||
public function readVersion()
|
||||
{
|
||||
if ($this->parsedVersion != null) {
|
||||
return $this->parsedVersion;
|
||||
}
|
||||
|
||||
private function copyBit($i, $j, $versionBits)
|
||||
{
|
||||
$bit = $this->mirror ? $this->bitMatrix->get($j, $i) : $this->bitMatrix->get($i, $j);
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
|
||||
return $bit ? ($versionBits << 1) | 0x1 : $versionBits << 1;
|
||||
}
|
||||
$provisionalVersion = ($dimension - 17) / 4;
|
||||
if ($provisionalVersion <= 6) {
|
||||
return Version::getVersionForNumber($provisionalVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads version information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link Version} encapsulating the QR Code's version
|
||||
* @throws FormatException if both version information locations cannot be parsed as
|
||||
* the valid encoding of version information
|
||||
*/
|
||||
public function readVersion()
|
||||
{
|
||||
// Read top-right version info: 3 wide by 6 tall
|
||||
$versionBits = 0;
|
||||
$ijMin = $dimension - 11;
|
||||
for ($j = 5; $j >= 0; $j--) {
|
||||
for ($i = $dimension - 9; $i >= $ijMin; $i--) {
|
||||
$versionBits = $this->copyBit($i, $j, $versionBits);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->parsedVersion != null) {
|
||||
return $this->parsedVersion;
|
||||
}
|
||||
$theParsedVersion = Version::decodeVersionInformation($versionBits);
|
||||
if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
|
||||
$this->parsedVersion = $theParsedVersion;
|
||||
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
return $theParsedVersion;
|
||||
}
|
||||
|
||||
$provisionalVersion = ($dimension - 17) / 4;
|
||||
if ($provisionalVersion <= 6) {
|
||||
return Version::getVersionForNumber($provisionalVersion);
|
||||
}
|
||||
// Hmm, failed. Try bottom left: 6 wide by 3 tall
|
||||
$versionBits = 0;
|
||||
for ($i = 5; $i >= 0; $i--) {
|
||||
for ($j = $dimension - 9; $j >= $ijMin; $j--) {
|
||||
$versionBits = $this->copyBit($i, $j, $versionBits);
|
||||
}
|
||||
}
|
||||
|
||||
// Read top-right version info: 3 wide by 6 tall
|
||||
$versionBits = 0;
|
||||
$ijMin = $dimension - 11;
|
||||
for ($j = 5; $j >= 0; $j--) {
|
||||
for ($i = $dimension - 9; $i >= $ijMin; $i--) {
|
||||
$versionBits = $this->copyBit($i, $j, $versionBits);
|
||||
}
|
||||
}
|
||||
$theParsedVersion = Version::decodeVersionInformation($versionBits);
|
||||
if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
|
||||
$this->parsedVersion = $theParsedVersion;
|
||||
|
||||
$theParsedVersion = Version::decodeVersionInformation($versionBits);
|
||||
if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
|
||||
$this->parsedVersion = $theParsedVersion;
|
||||
return $theParsedVersion;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
return $theParsedVersion;
|
||||
}
|
||||
/**
|
||||
* Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
|
||||
*/
|
||||
public function remask(): void
|
||||
{
|
||||
if ($this->parsedFormatInfo == null) {
|
||||
return; // We have no format information, and have no data mask
|
||||
}
|
||||
$dataMask = DataMask::forReference($this->parsedFormatInfo->getDataMask());
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
$dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
|
||||
}
|
||||
|
||||
// Hmm, failed. Try bottom left: 6 wide by 3 tall
|
||||
$versionBits = 0;
|
||||
for ($i = 5; $i >= 0; $i--) {
|
||||
for ($j = $dimension - 9; $j >= $ijMin; $j--) {
|
||||
$versionBits = $this->copyBit($i, $j, $versionBits);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Prepare the parser for a mirrored operation.
|
||||
* This flag has effect only on the {@link #readFormatInformation()} and the
|
||||
* {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
|
||||
* {@link #mirror()} method should be called.
|
||||
*
|
||||
* @param Whether $mirror to read version and format information mirrored.
|
||||
*/
|
||||
public function setMirror($mirror): void
|
||||
{
|
||||
$parsedVersion = null;
|
||||
$parsedFormatInfo = null;
|
||||
$this->mirror = $mirror;
|
||||
}
|
||||
|
||||
$theParsedVersion = Version::decodeVersionInformation($versionBits);
|
||||
if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
|
||||
$this->parsedVersion = $theParsedVersion;
|
||||
|
||||
return $theParsedVersion;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
|
||||
*/
|
||||
public function remask()
|
||||
{
|
||||
if ($this->parsedFormatInfo == null) {
|
||||
return; // We have no format information, and have no data mask
|
||||
}
|
||||
$dataMask = DataMask::forReference($this->parsedFormatInfo->getDataMask());
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
$dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the parser for a mirrored operation.
|
||||
* This flag has effect only on the {@link #readFormatInformation()} and the
|
||||
* {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
|
||||
* {@link #mirror()} method should be called.
|
||||
*
|
||||
* @param mirror Whether to read version and format information mirrored.
|
||||
*/
|
||||
public function setMirror($mirror)
|
||||
{
|
||||
$parsedVersion = null;
|
||||
$parsedFormatInfo = null;
|
||||
$this->mirror = $mirror;
|
||||
}
|
||||
|
||||
/** Mirror the bit matrix in order to attempt a second reading. */
|
||||
public function mirror()
|
||||
{
|
||||
for ($x = 0; $x < $this->bitMatrix->getWidth(); $x++) {
|
||||
for ($y = $x + 1; $y < $this->bitMatrix->getHeight(); $y++) {
|
||||
if ($this->bitMatrix->get($x, $y) != $this->bitMatrix->get($y, $x)) {
|
||||
$this->bitMatrix->flip($y, $x);
|
||||
$this->bitMatrix->flip($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Mirror the bit matrix in order to attempt a second reading. */
|
||||
public function mirror(): void
|
||||
{
|
||||
for ($x = 0; $x < $this->bitMatrix->getWidth(); $x++) {
|
||||
for ($y = $x + 1; $y < $this->bitMatrix->getHeight(); $y++) {
|
||||
if ($this->bitMatrix->get($x, $y) != $this->bitMatrix->get($y, $x)) {
|
||||
$this->bitMatrix->flip($y, $x);
|
||||
$this->bitMatrix->flip($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,104 +26,102 @@ namespace Zxing\Qrcode\Decoder;
|
|||
*/
|
||||
final class DataBlock
|
||||
{
|
||||
private $numDataCodewords;
|
||||
private $codewords; //byte[]
|
||||
//byte[]
|
||||
|
||||
private function __construct($numDataCodewords, $codewords)
|
||||
{
|
||||
$this->numDataCodewords = $numDataCodewords;
|
||||
$this->codewords = $codewords;
|
||||
}
|
||||
private function __construct(private $numDataCodewords, private $codewords)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param rawCodewords bytes as read directly from the QR Code
|
||||
* @param version version of the QR Code
|
||||
* @param ecLevel error-correction level of the QR Code
|
||||
*
|
||||
* @return array DataBlocks containing original bytes, "de-interleaved" from representation in the
|
||||
* QR Code
|
||||
*/
|
||||
public static function getDataBlocks($rawCodewords,
|
||||
$version,
|
||||
$ecLevel)
|
||||
{
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param bytes $rawCodewords as read directly from the QR Code
|
||||
* @param version $version of the QR Code
|
||||
* @param error $ecLevel-correction level of the QR Code
|
||||
*
|
||||
* @return array DataBlocks containing original bytes, "de-interleaved" from representation in the
|
||||
* QR Code
|
||||
*/
|
||||
public static function getDataBlocks(
|
||||
$rawCodewords,
|
||||
$version,
|
||||
$ecLevel
|
||||
)
|
||||
{
|
||||
if ((is_countable($rawCodewords) ? count($rawCodewords) : 0) != $version->getTotalCodewords()) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
|
||||
if (count($rawCodewords) != $version->getTotalCodewords()) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
// Figure out the number and size of data blocks used by this version and
|
||||
// error correction level
|
||||
$ecBlocks = $version->getECBlocksForLevel($ecLevel);
|
||||
|
||||
// Figure out the number and size of data blocks used by this version and
|
||||
// error correction level
|
||||
$ecBlocks = $version->getECBlocksForLevel($ecLevel);
|
||||
// First count the total number of data blocks
|
||||
$totalBlocks = 0;
|
||||
$ecBlockArray = $ecBlocks->getECBlocks();
|
||||
foreach ($ecBlockArray as $ecBlock) {
|
||||
$totalBlocks += $ecBlock->getCount();
|
||||
}
|
||||
|
||||
// First count the total number of data blocks
|
||||
$totalBlocks = 0;
|
||||
$ecBlockArray = $ecBlocks->getECBlocks();
|
||||
foreach ($ecBlockArray as $ecBlock) {
|
||||
$totalBlocks += $ecBlock->getCount();
|
||||
}
|
||||
// Now establish DataBlocks of the appropriate size and number of data codewords
|
||||
$result = [];//new DataBlock[$totalBlocks];
|
||||
$numResultBlocks = 0;
|
||||
foreach ($ecBlockArray as $ecBlock) {
|
||||
$ecBlockCount = $ecBlock->getCount();
|
||||
for ($i = 0; $i < $ecBlockCount; $i++) {
|
||||
$numDataCodewords = $ecBlock->getDataCodewords();
|
||||
$numBlockCodewords = $ecBlocks->getECCodewordsPerBlock() + $numDataCodewords;
|
||||
$result[$numResultBlocks++] = new DataBlock($numDataCodewords, fill_array(0, $numBlockCodewords, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// Now establish DataBlocks of the appropriate size and number of data codewords
|
||||
$result = [];//new DataBlock[$totalBlocks];
|
||||
$numResultBlocks = 0;
|
||||
foreach ($ecBlockArray as $ecBlock) {
|
||||
$ecBlockCount = $ecBlock->getCount();
|
||||
for ($i = 0; $i < $ecBlockCount; $i++) {
|
||||
$numDataCodewords = $ecBlock->getDataCodewords();
|
||||
$numBlockCodewords = $ecBlocks->getECCodewordsPerBlock() + $numDataCodewords;
|
||||
$result[$numResultBlocks++] = new DataBlock($numDataCodewords, fill_array(0, $numBlockCodewords, 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.
|
||||
$shorterBlocksTotalCodewords = is_countable($result[0]->codewords) ? count($result[0]->codewords) : 0;
|
||||
$longerBlocksStartAt = count($result) - 1;
|
||||
while ($longerBlocksStartAt >= 0) {
|
||||
$numCodewords = is_countable($result[$longerBlocksStartAt]->codewords) ? count($result[$longerBlocksStartAt]->codewords) : 0;
|
||||
if ($numCodewords == $shorterBlocksTotalCodewords) {
|
||||
break;
|
||||
}
|
||||
$longerBlocksStartAt--;
|
||||
}
|
||||
$longerBlocksStartAt++;
|
||||
|
||||
// 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.
|
||||
$shorterBlocksTotalCodewords = count($result[0]->codewords);
|
||||
$longerBlocksStartAt = count($result) - 1;
|
||||
while ($longerBlocksStartAt >= 0) {
|
||||
$numCodewords = count($result[$longerBlocksStartAt]->codewords);
|
||||
if ($numCodewords == $shorterBlocksTotalCodewords) {
|
||||
break;
|
||||
}
|
||||
$longerBlocksStartAt--;
|
||||
}
|
||||
$longerBlocksStartAt++;
|
||||
$shorterBlocksNumDataCodewords = $shorterBlocksTotalCodewords - $ecBlocks->getECCodewordsPerBlock();
|
||||
// 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]->codewords[$i] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
// Fill out the last data block in the longer ones
|
||||
for ($j = $longerBlocksStartAt; $j < $numResultBlocks; $j++) {
|
||||
$result[$j]->codewords[$shorterBlocksNumDataCodewords] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
// Now add in error correction blocks
|
||||
$max = is_countable($result[0]->codewords) ? count($result[0]->codewords) : 0;
|
||||
for ($i = $shorterBlocksNumDataCodewords; $i < $max; $i++) {
|
||||
for ($j = 0; $j < $numResultBlocks; $j++) {
|
||||
$iOffset = $j < $longerBlocksStartAt ? $i : $i + 1;
|
||||
$result[$j]->codewords[$iOffset] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
|
||||
$shorterBlocksNumDataCodewords = $shorterBlocksTotalCodewords - $ecBlocks->getECCodewordsPerBlock();
|
||||
// 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]->codewords[$i] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
// Fill out the last data block in the longer ones
|
||||
for ($j = $longerBlocksStartAt; $j < $numResultBlocks; $j++) {
|
||||
$result[$j]->codewords[$shorterBlocksNumDataCodewords] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
// Now add in error correction blocks
|
||||
$max = count($result[0]->codewords);
|
||||
for ($i = $shorterBlocksNumDataCodewords; $i < $max; $i++) {
|
||||
for ($j = 0; $j < $numResultBlocks; $j++) {
|
||||
$iOffset = $j < $longerBlocksStartAt ? $i : $i + 1;
|
||||
$result[$j]->codewords[$iOffset] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
public function getNumDataCodewords()
|
||||
{
|
||||
return $this->numDataCodewords;
|
||||
}
|
||||
|
||||
public function getNumDataCodewords()
|
||||
{
|
||||
return $this->numDataCodewords;
|
||||
}
|
||||
|
||||
public function getCodewords()
|
||||
{
|
||||
return $this->codewords;
|
||||
}
|
||||
public function getCodewords()
|
||||
{
|
||||
return $this->codewords;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,65 +32,63 @@ use Zxing\Common\BitMatrix;
|
|||
*/
|
||||
abstract class DataMask
|
||||
{
|
||||
/**
|
||||
* See ISO 18004:2006 6.8.1
|
||||
*/
|
||||
private static array $DATA_MASKS = [];
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006 6.8.1
|
||||
*/
|
||||
private static $DATA_MASKS = [];
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
public static function Init(): void
|
||||
{
|
||||
self::$DATA_MASKS = [
|
||||
new DataMask000(),
|
||||
new DataMask001(),
|
||||
new DataMask010(),
|
||||
new DataMask011(),
|
||||
new DataMask100(),
|
||||
new DataMask101(),
|
||||
new DataMask110(),
|
||||
new DataMask111(),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* @param a $reference value between 0 and 7 indicating one of the eight possible
|
||||
* data mask patterns a QR Code may use
|
||||
*
|
||||
* @return DataMask encapsulating the data mask pattern
|
||||
*/
|
||||
public static function forReference($reference)
|
||||
{
|
||||
if ($reference < 0 || $reference > 7) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
|
||||
public static function Init()
|
||||
{
|
||||
self::$DATA_MASKS = [
|
||||
new DataMask000(),
|
||||
new DataMask001(),
|
||||
new DataMask010(),
|
||||
new DataMask011(),
|
||||
new DataMask100(),
|
||||
new DataMask101(),
|
||||
new DataMask110(),
|
||||
new DataMask111(),
|
||||
];
|
||||
}
|
||||
return self::$DATA_MASKS[$reference];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reference a value between 0 and 7 indicating one of the eight possible
|
||||
* data mask patterns a QR Code may use
|
||||
*
|
||||
* @return DataMask encapsulating the data mask pattern
|
||||
*/
|
||||
public static function forReference($reference)
|
||||
{
|
||||
if ($reference < 0 || $reference > 7) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
/**
|
||||
* <p>Implementations of this method reverse the data masking process applied to a QR Code and
|
||||
* make its bits ready to read.</p>
|
||||
*
|
||||
* @param representation $bits of QR Code bits
|
||||
* @param dimension $dimension of QR Code, represented by bits, being unmasked
|
||||
*/
|
||||
final public function unmaskBitMatrix($bits, $dimension): void
|
||||
{
|
||||
for ($i = 0; $i < $dimension; $i++) {
|
||||
for ($j = 0; $j < $dimension; $j++) {
|
||||
if ($this->isMasked($i, $j)) {
|
||||
$bits->flip($j, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::$DATA_MASKS[$reference];
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Implementations of this method reverse the data masking process applied to a QR Code and
|
||||
* make its bits ready to read.</p>
|
||||
*
|
||||
* @param bits representation of QR Code bits
|
||||
* @param dimension dimension of QR Code, represented by bits, being unmasked
|
||||
*/
|
||||
final public function unmaskBitMatrix($bits, $dimension)
|
||||
{
|
||||
for ($i = 0; $i < $dimension; $i++) {
|
||||
for ($j = 0; $j < $dimension; $j++) {
|
||||
if ($this->isMasked($i, $j)) {
|
||||
$bits->flip($j, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract public function isMasked($i, $j);
|
||||
abstract public function isMasked($i, $j);
|
||||
}
|
||||
|
||||
DataMask::Init();
|
||||
|
|
@ -100,11 +98,11 @@ DataMask::Init();
|
|||
*/
|
||||
final class DataMask000 extends DataMask
|
||||
{
|
||||
// @Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return (($i + $j) & 0x01) == 0;
|
||||
}
|
||||
// @Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return (($i + $j) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -112,11 +110,11 @@ final class DataMask000 extends DataMask
|
|||
*/
|
||||
final class DataMask001 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return ($i & 0x01) == 0;
|
||||
}
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return ($i & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -124,11 +122,11 @@ final class DataMask001 extends DataMask
|
|||
*/
|
||||
final class DataMask010 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return $j % 3 == 0;
|
||||
}
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return $j % 3 == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -136,11 +134,11 @@ final class DataMask010 extends DataMask
|
|||
*/
|
||||
final class DataMask011 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return ($i + $j) % 3 == 0;
|
||||
}
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return ($i + $j) % 3 == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -148,11 +146,11 @@ final class DataMask011 extends DataMask
|
|||
*/
|
||||
final class DataMask100 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return (int)(((int)($i / 2) + (int)($j / 3)) & 0x01) == 0;
|
||||
}
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return (int)(((int)($i / 2) + (int)($j / 3)) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -160,13 +158,13 @@ final class DataMask100 extends DataMask
|
|||
*/
|
||||
final class DataMask101 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
$temp = $i * $j;
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
$temp = $i * $j;
|
||||
|
||||
return ($temp & 0x01) + ($temp % 3) == 0;
|
||||
}
|
||||
return ($temp & 0x01) + ($temp % 3) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -174,13 +172,13 @@ final class DataMask101 extends DataMask
|
|||
*/
|
||||
final class DataMask110 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
$temp = $i * $j;
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
$temp = $i * $j;
|
||||
|
||||
return ((($temp & 0x01) + ($temp % 3)) & 0x01) == 0;
|
||||
}
|
||||
return ((($temp & 0x01) + ($temp % 3)) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -188,9 +186,9 @@ final class DataMask110 extends DataMask
|
|||
*/
|
||||
final class DataMask111 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return (((($i + $j) & 0x01) + (($i * $j) % 3)) & 0x01) == 0;
|
||||
}
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return (((($i + $j) & 0x01) + (($i * $j) % 3)) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,10 @@
|
|||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\DecodeHintType;
|
||||
use Zxing\FormatException;
|
||||
use Zxing\Common\BitSource;
|
||||
use Zxing\Common\CharacterSetECI;
|
||||
use Zxing\Common\DecoderResult;
|
||||
use Zxing\Common\StringUtils;
|
||||
|
||||
use Zxing\FormatException;
|
||||
|
||||
/**
|
||||
* <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
|
||||
|
|
@ -35,321 +32,333 @@ use Zxing\Common\StringUtils;
|
|||
*/
|
||||
final class DecodedBitStreamParser
|
||||
{
|
||||
/**
|
||||
* See ISO 18004:2006, 6.4.4 Table 5
|
||||
*/
|
||||
private static array $ALPHANUMERIC_CHARS = [
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
|
||||
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
' ', '$', '%', '*', '+', '-', '.', '/', ':',
|
||||
];
|
||||
private static int $GB2312_SUBSET = 1;
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006, 6.4.4 Table 5
|
||||
*/
|
||||
private static $ALPHANUMERIC_CHARS = [
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
|
||||
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
' ', '$', '%', '*', '+', '-', '.', '/', ':',
|
||||
];
|
||||
private static $GB2312_SUBSET = 1;
|
||||
public static function decode(
|
||||
$bytes,
|
||||
$version,
|
||||
$ecLevel,
|
||||
$hints
|
||||
): \Zxing\Common\DecoderResult
|
||||
{
|
||||
$bits = new BitSource($bytes);
|
||||
$result = '';//new StringBuilder(50);
|
||||
$byteSegments = [];
|
||||
$symbolSequence = -1;
|
||||
$parityData = -1;
|
||||
|
||||
public static function decode($bytes,
|
||||
$version,
|
||||
$ecLevel,
|
||||
$hints)
|
||||
{
|
||||
$bits = new BitSource($bytes);
|
||||
$result = '';//new StringBuilder(50);
|
||||
$byteSegments = [];
|
||||
$symbolSequence = -1;
|
||||
$parityData = -1;
|
||||
try {
|
||||
$currentCharacterSetECI = null;
|
||||
$fc1InEffect = false;
|
||||
$mode = '';
|
||||
do {
|
||||
// While still another segment to read...
|
||||
if ($bits->available() < 4) {
|
||||
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
||||
$mode = Mode::$TERMINATOR;
|
||||
} else {
|
||||
$mode = Mode::forBits($bits->readBits(4)); // mode is encoded by 4 bits
|
||||
}
|
||||
if ($mode != Mode::$TERMINATOR) {
|
||||
if ($mode == Mode::$FNC1_FIRST_POSITION || $mode == Mode::$FNC1_SECOND_POSITION) {
|
||||
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
||||
$fc1InEffect = true;
|
||||
} elseif ($mode == Mode::$STRUCTURED_APPEND) {
|
||||
if ($bits->available() < 16) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
// 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 = $bits->readBits(8);
|
||||
$parityData = $bits->readBits(8);
|
||||
} elseif ($mode == Mode::$ECI) {
|
||||
// Count doesn't apply to ECI
|
||||
$value = self::parseECIValue($bits);
|
||||
$currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue($value);
|
||||
if ($currentCharacterSetECI == null) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
} else {
|
||||
// First handle Hanzi mode which does not start with character count
|
||||
if ($mode == Mode::$HANZI) {
|
||||
//chinese mode contains a sub set indicator right after mode indicator
|
||||
$subset = $bits->readBits(4);
|
||||
$countHanzi = $bits->readBits($mode->getCharacterCountBits($version));
|
||||
if ($subset == self::$GB2312_SUBSET) {
|
||||
self::decodeHanziSegment($bits, $result, $countHanzi);
|
||||
}
|
||||
} else {
|
||||
// "Normal" QR code modes:
|
||||
// How many characters will follow, encoded in this mode?
|
||||
$count = $bits->readBits($mode->getCharacterCountBits($version));
|
||||
if ($mode == Mode::$NUMERIC) {
|
||||
self::decodeNumericSegment($bits, $result, $count);
|
||||
} elseif ($mode == Mode::$ALPHANUMERIC) {
|
||||
self::decodeAlphanumericSegment($bits, $result, $count, $fc1InEffect);
|
||||
} elseif ($mode == Mode::$BYTE) {
|
||||
self::decodeByteSegment($bits, $result, $count, $currentCharacterSetECI, $byteSegments, $hints);
|
||||
} elseif ($mode == Mode::$KANJI) {
|
||||
self::decodeKanjiSegment($bits, $result, $count);
|
||||
} else {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ($mode != Mode::$TERMINATOR);
|
||||
} catch (\InvalidArgumentException) {
|
||||
// from readBits() calls
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
try {
|
||||
$currentCharacterSetECI = null;
|
||||
$fc1InEffect = false;
|
||||
$mode = '';
|
||||
do {
|
||||
// While still another segment to read...
|
||||
if ($bits->available() < 4) {
|
||||
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
||||
$mode = Mode::$TERMINATOR;
|
||||
} else {
|
||||
$mode = Mode::forBits($bits->readBits(4)); // mode is encoded by 4 bits
|
||||
}
|
||||
if ($mode != Mode::$TERMINATOR) {
|
||||
if ($mode == Mode::$FNC1_FIRST_POSITION || $mode == Mode::$FNC1_SECOND_POSITION) {
|
||||
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
||||
$fc1InEffect = true;
|
||||
} else if ($mode == Mode::$STRUCTURED_APPEND) {
|
||||
if ($bits->available() < 16) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
// 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 = $bits->readBits(8);
|
||||
$parityData = $bits->readBits(8);
|
||||
} else if ($mode == Mode::$ECI) {
|
||||
// Count doesn't apply to ECI
|
||||
$value = self::parseECIValue($bits);
|
||||
$currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue($value);
|
||||
if ($currentCharacterSetECI == null) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
} else {
|
||||
// First handle Hanzi mode which does not start with character count
|
||||
if ($mode == Mode::$HANZI) {
|
||||
//chinese mode contains a sub set indicator right after mode indicator
|
||||
$subset = $bits->readBits(4);
|
||||
$countHanzi = $bits->readBits($mode->getCharacterCountBits($version));
|
||||
if ($subset == self::$GB2312_SUBSET) {
|
||||
self::decodeHanziSegment($bits, $result, $countHanzi);
|
||||
}
|
||||
} else {
|
||||
// "Normal" QR code modes:
|
||||
// How many characters will follow, encoded in this mode?
|
||||
$count = $bits->readBits($mode->getCharacterCountBits($version));
|
||||
if ($mode == Mode::$NUMERIC) {
|
||||
self::decodeNumericSegment($bits, $result, $count);
|
||||
} else if ($mode == Mode::$ALPHANUMERIC) {
|
||||
self::decodeAlphanumericSegment($bits, $result, $count, $fc1InEffect);
|
||||
} else if ($mode == Mode::$BYTE) {
|
||||
self::decodeByteSegment($bits, $result, $count, $currentCharacterSetECI, $byteSegments, $hints);
|
||||
} else if ($mode == Mode::$KANJI) {
|
||||
self::decodeKanjiSegment($bits, $result, $count);
|
||||
} else {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ($mode != Mode::$TERMINATOR);
|
||||
} catch (\InvalidArgumentException $iae) {
|
||||
// from readBits() calls
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
return new DecoderResult(
|
||||
$bytes,
|
||||
$result,
|
||||
empty($byteSegments) ? null : $byteSegments,
|
||||
$ecLevel == null ? null : 'L',//ErrorCorrectionLevel::toString($ecLevel),
|
||||
$symbolSequence,
|
||||
$parityData
|
||||
);
|
||||
}
|
||||
|
||||
return new DecoderResult($bytes,
|
||||
$result,
|
||||
empty($byteSegments) ? null : $byteSegments,
|
||||
$ecLevel == null ? null : 'L',//ErrorCorrectionLevel::toString($ecLevel),
|
||||
$symbolSequence,
|
||||
$parityData);
|
||||
}
|
||||
private static function parseECIValue($bits)
|
||||
{
|
||||
$firstByte = $bits->readBits(8);
|
||||
if (($firstByte & 0x80) == 0) {
|
||||
// just one byte
|
||||
return $firstByte & 0x7F;
|
||||
}
|
||||
if (($firstByte & 0xC0) == 0x80) {
|
||||
// two bytes
|
||||
$secondByte = $bits->readBits(8);
|
||||
|
||||
private static function parseECIValue($bits)
|
||||
{
|
||||
$firstByte = $bits->readBits(8);
|
||||
if (($firstByte & 0x80) == 0) {
|
||||
// just one byte
|
||||
return $firstByte & 0x7F;
|
||||
}
|
||||
if (($firstByte & 0xC0) == 0x80) {
|
||||
// two bytes
|
||||
$secondByte = $bits->readBits(8);
|
||||
return (($firstByte & 0x3F) << 8) | $secondByte;
|
||||
}
|
||||
if (($firstByte & 0xE0) == 0xC0) {
|
||||
// three bytes
|
||||
$secondThirdBytes = $bits->readBits(16);
|
||||
|
||||
return (($firstByte & 0x3F) << 8) | $secondByte;
|
||||
}
|
||||
if (($firstByte & 0xE0) == 0xC0) {
|
||||
// three bytes
|
||||
$secondThirdBytes = $bits->readBits(16);
|
||||
return (($firstByte & 0x1F) << 16) | $secondThirdBytes;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
return (($firstByte & 0x1F) << 16) | $secondThirdBytes;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
/**
|
||||
* See specification GBT 18284-2000
|
||||
*/
|
||||
private static function decodeHanziSegment(
|
||||
$bits,
|
||||
&$result,
|
||||
$count
|
||||
)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if ($count * 13 > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* See specification GBT 18284-2000
|
||||
*/
|
||||
private static function decodeHanziSegment($bits,
|
||||
&$result,
|
||||
$count)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if ($count * 13 > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as GB2312 afterwards
|
||||
$buffer = fill_array(0, 2 * $count, 0);
|
||||
$offset = 0;
|
||||
while ($count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
$twoBytes = $bits->readBits(13);
|
||||
$assembledTwoBytes = (($twoBytes / 0x060) << 8) | ($twoBytes % 0x060);
|
||||
if ($assembledTwoBytes < 0x003BF) {
|
||||
// In the 0xA1A1 to 0xAAFE range
|
||||
$assembledTwoBytes += 0x0A1A1;
|
||||
} else {
|
||||
// In the 0xB0A1 to 0xFAFE range
|
||||
$assembledTwoBytes += 0x0A6A1;
|
||||
}
|
||||
$buffer[$offset] = (($assembledTwoBytes >> 8) & 0xFF);//(byte)
|
||||
$buffer[$offset + 1] = ($assembledTwoBytes & 0xFF);//(byte)
|
||||
$offset += 2;
|
||||
$count--;
|
||||
}
|
||||
$result .= iconv('GB2312', 'UTF-8', implode($buffer));
|
||||
}
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as GB2312 afterwards
|
||||
$buffer = fill_array(0, 2 * $count, 0);
|
||||
$offset = 0;
|
||||
while ($count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
$twoBytes = $bits->readBits(13);
|
||||
$assembledTwoBytes = (($twoBytes / 0x060) << 8) | ($twoBytes % 0x060);
|
||||
if ($assembledTwoBytes < 0x003BF) {
|
||||
// In the 0xA1A1 to 0xAAFE range
|
||||
$assembledTwoBytes += 0x0A1A1;
|
||||
} else {
|
||||
// In the 0xB0A1 to 0xFAFE range
|
||||
$assembledTwoBytes += 0x0A6A1;
|
||||
}
|
||||
$buffer[$offset] = (($assembledTwoBytes >> 8) & 0xFF);//(byte)
|
||||
$buffer[$offset + 1] = ($assembledTwoBytes & 0xFF);//(byte)
|
||||
$offset += 2;
|
||||
$count--;
|
||||
}
|
||||
$result .= iconv('GB2312', 'UTF-8', implode($buffer));
|
||||
}
|
||||
private static function decodeNumericSegment(
|
||||
$bits,
|
||||
&$result,
|
||||
$count
|
||||
)
|
||||
{
|
||||
// Read three digits at a time
|
||||
while ($count >= 3) {
|
||||
// Each 10 bits encodes three digits
|
||||
if ($bits->available() < 10) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$threeDigitsBits = $bits->readBits(10);
|
||||
if ($threeDigitsBits >= 1000) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result .= (self::toAlphaNumericChar($threeDigitsBits / 100));
|
||||
$result .= (self::toAlphaNumericChar(($threeDigitsBits / 10) % 10));
|
||||
$result .= (self::toAlphaNumericChar($threeDigitsBits % 10));
|
||||
$count -= 3;
|
||||
}
|
||||
if ($count == 2) {
|
||||
// Two digits left over to read, encoded in 7 bits
|
||||
if ($bits->available() < 7) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$twoDigitsBits = $bits->readBits(7);
|
||||
if ($twoDigitsBits >= 100) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result .= (self::toAlphaNumericChar($twoDigitsBits / 10));
|
||||
$result .= (self::toAlphaNumericChar($twoDigitsBits % 10));
|
||||
} elseif ($count == 1) {
|
||||
// One digit left over to read
|
||||
if ($bits->available() < 4) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$digitBits = $bits->readBits(4);
|
||||
if ($digitBits >= 10) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result .= (self::toAlphaNumericChar($digitBits));
|
||||
}
|
||||
}
|
||||
|
||||
private static function decodeNumericSegment($bits,
|
||||
&$result,
|
||||
$count)
|
||||
{
|
||||
// Read three digits at a time
|
||||
while ($count >= 3) {
|
||||
// Each 10 bits encodes three digits
|
||||
if ($bits->available() < 10) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$threeDigitsBits = $bits->readBits(10);
|
||||
if ($threeDigitsBits >= 1000) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result .= (self::toAlphaNumericChar($threeDigitsBits / 100));
|
||||
$result .= (self::toAlphaNumericChar(($threeDigitsBits / 10) % 10));
|
||||
$result .= (self::toAlphaNumericChar($threeDigitsBits % 10));
|
||||
$count -= 3;
|
||||
}
|
||||
if ($count == 2) {
|
||||
// Two digits left over to read, encoded in 7 bits
|
||||
if ($bits->available() < 7) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$twoDigitsBits = $bits->readBits(7);
|
||||
if ($twoDigitsBits >= 100) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result .= (self::toAlphaNumericChar($twoDigitsBits / 10));
|
||||
$result .= (self::toAlphaNumericChar($twoDigitsBits % 10));
|
||||
} else if ($count == 1) {
|
||||
// One digit left over to read
|
||||
if ($bits->available() < 4) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$digitBits = $bits->readBits(4);
|
||||
if ($digitBits >= 10) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result .= (self::toAlphaNumericChar($digitBits));
|
||||
}
|
||||
}
|
||||
private static function toAlphaNumericChar($value)
|
||||
{
|
||||
if ($value >= count(self::$ALPHANUMERIC_CHARS)) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
private static function toAlphaNumericChar($value)
|
||||
{
|
||||
if ($value >= count(self::$ALPHANUMERIC_CHARS)) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
return self::$ALPHANUMERIC_CHARS[$value];
|
||||
}
|
||||
|
||||
return self::$ALPHANUMERIC_CHARS[$value];
|
||||
}
|
||||
private static function decodeAlphanumericSegment(
|
||||
$bits,
|
||||
&$result,
|
||||
$count,
|
||||
$fc1InEffect
|
||||
)
|
||||
{
|
||||
// Read two characters at a time
|
||||
$start = strlen((string) $result);
|
||||
while ($count > 1) {
|
||||
if ($bits->available() < 11) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$nextTwoCharsBits = $bits->readBits(11);
|
||||
$result .= (self::toAlphaNumericChar($nextTwoCharsBits / 45));
|
||||
$result .= (self::toAlphaNumericChar($nextTwoCharsBits % 45));
|
||||
$count -= 2;
|
||||
}
|
||||
if ($count == 1) {
|
||||
// special case: one character left
|
||||
if ($bits->available() < 6) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result .= self::toAlphaNumericChar($bits->readBits(6));
|
||||
}
|
||||
// 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:
|
||||
for ($i = $start; $i < strlen((string) $result); $i++) {
|
||||
if ($result[$i] == '%') {
|
||||
if ($i < strlen((string) $result) - 1 && $result[$i + 1] == '%') {
|
||||
// %% is rendered as %
|
||||
$result = substr_replace($result, '', $i + 1, 1);//deleteCharAt(i + 1);
|
||||
} else {
|
||||
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
||||
$result . setCharAt($i, chr(0x1D));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function decodeAlphanumericSegment($bits,
|
||||
&$result,
|
||||
$count,
|
||||
$fc1InEffect)
|
||||
{
|
||||
// Read two characters at a time
|
||||
$start = strlen($result);
|
||||
while ($count > 1) {
|
||||
if ($bits->available() < 11) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$nextTwoCharsBits = $bits->readBits(11);
|
||||
$result .= (self::toAlphaNumericChar($nextTwoCharsBits / 45));
|
||||
$result .= (self::toAlphaNumericChar($nextTwoCharsBits % 45));
|
||||
$count -= 2;
|
||||
}
|
||||
if ($count == 1) {
|
||||
// special case: one character left
|
||||
if ($bits->available() < 6) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result .= self::toAlphaNumericChar($bits->readBits(6));
|
||||
}
|
||||
// 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:
|
||||
for ($i = $start; $i < strlen($result); $i++) {
|
||||
if ($result[$i] == '%') {
|
||||
if ($i < strlen($result) - 1 && $result[$i + 1] == '%') {
|
||||
// %% is rendered as %
|
||||
$result = substr_replace($result, '', $i + 1, 1);//deleteCharAt(i + 1);
|
||||
} else {
|
||||
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
||||
$result . setCharAt($i, chr(0x1D));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private static function decodeByteSegment(
|
||||
$bits,
|
||||
&$result,
|
||||
$count,
|
||||
$currentCharacterSetECI,
|
||||
&$byteSegments,
|
||||
$hints
|
||||
)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if (8 * $count > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
private static function decodeByteSegment($bits,
|
||||
&$result,
|
||||
$count,
|
||||
$currentCharacterSetECI,
|
||||
&$byteSegments,
|
||||
$hints)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if (8 * $count > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$readBytes = fill_array(0, $count, 0);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$readBytes[$i] = $bits->readBits(8);//(byte)
|
||||
}
|
||||
$text = implode(array_map('chr', $readBytes));
|
||||
$encoding = '';
|
||||
if ($currentCharacterSetECI == 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.
|
||||
|
||||
$readBytes = fill_array(0, $count, 0);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$readBytes[$i] = $bits->readBits(8);//(byte)
|
||||
}
|
||||
$text = implode(array_map('chr', $readBytes));
|
||||
$encoding = '';
|
||||
if ($currentCharacterSetECI == 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($text, $hints);
|
||||
} else {
|
||||
$encoding = $currentCharacterSetECI->name();
|
||||
}
|
||||
// $result.= mb_convert_encoding($text ,$encoding);//(new String(readBytes, encoding));
|
||||
$result .= $text;//(new String(readBytes, encoding));
|
||||
|
||||
$encoding = mb_detect_encoding($text, $hints);
|
||||
} else {
|
||||
$encoding = $currentCharacterSetECI->name();
|
||||
}
|
||||
// $result.= mb_convert_encoding($text ,$encoding);//(new String(readBytes, encoding));
|
||||
$result .= $text;//(new String(readBytes, encoding));
|
||||
$byteSegments = array_merge($byteSegments, $readBytes);
|
||||
}
|
||||
|
||||
$byteSegments = array_merge($byteSegments, $readBytes);
|
||||
}
|
||||
private static function decodeKanjiSegment(
|
||||
$bits,
|
||||
&$result,
|
||||
$count
|
||||
)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if ($count * 13 > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
private static function decodeKanjiSegment($bits,
|
||||
&$result,
|
||||
$count)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if ($count * 13 > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as Shift_JIS afterwards
|
||||
$buffer = [0, 2 * $count, 0];
|
||||
$offset = 0;
|
||||
while ($count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
$twoBytes = $bits->readBits(13);
|
||||
$assembledTwoBytes = (($twoBytes / 0x0C0) << 8) | ($twoBytes % 0x0C0);
|
||||
if ($assembledTwoBytes < 0x01F00) {
|
||||
// In the 0x8140 to 0x9FFC range
|
||||
$assembledTwoBytes += 0x08140;
|
||||
} else {
|
||||
// In the 0xE040 to 0xEBBF range
|
||||
$assembledTwoBytes += 0x0C140;
|
||||
}
|
||||
$buffer[$offset] = ($assembledTwoBytes >> 8);//(byte)
|
||||
$buffer[$offset + 1] = $assembledTwoBytes; //(byte)
|
||||
$offset += 2;
|
||||
$count--;
|
||||
}
|
||||
// Shift_JIS may not be supported in some environments:
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as Shift_JIS afterwards
|
||||
$buffer = [0, 2 * $count, 0];
|
||||
$offset = 0;
|
||||
while ($count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
$twoBytes = $bits->readBits(13);
|
||||
$assembledTwoBytes = (($twoBytes / 0x0C0) << 8) | ($twoBytes % 0x0C0);
|
||||
if ($assembledTwoBytes < 0x01F00) {
|
||||
// In the 0x8140 to 0x9FFC range
|
||||
$assembledTwoBytes += 0x08140;
|
||||
} else {
|
||||
// In the 0xE040 to 0xEBBF range
|
||||
$assembledTwoBytes += 0x0C140;
|
||||
}
|
||||
$buffer[$offset] = ($assembledTwoBytes >> 8);//(byte)
|
||||
$buffer[$offset + 1] = $assembledTwoBytes; //(byte)
|
||||
$offset += 2;
|
||||
$count--;
|
||||
}
|
||||
// Shift_JIS may not be supported in some environments:
|
||||
$result .= iconv('shift-jis', 'utf-8', implode($buffer));
|
||||
}
|
||||
|
||||
$result .= iconv('shift-jis', 'utf-8', implode($buffer));
|
||||
}
|
||||
|
||||
private function DecodedBitStreamParser()
|
||||
{
|
||||
|
||||
}
|
||||
private function DecodedBitStreamParser(): void
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,13 +18,11 @@
|
|||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\ChecksumException;
|
||||
use Zxing\DecodeHintType;
|
||||
use Zxing\FormatException;
|
||||
use Zxing\Common\BitMatrix;
|
||||
use Zxing\Common\DecoderResult;
|
||||
use Zxing\Common\Reedsolomon\GenericGF;
|
||||
use Zxing\Common\Reedsolomon\ReedSolomonDecoder;
|
||||
use Zxing\Common\Reedsolomon\ReedSolomonException;
|
||||
use Zxing\FormatException;
|
||||
|
||||
/**
|
||||
* <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
|
||||
|
|
@ -34,180 +32,177 @@ use Zxing\Common\Reedsolomon\ReedSolomonException;
|
|||
*/
|
||||
final class Decoder
|
||||
{
|
||||
private readonly \Zxing\Common\Reedsolomon\ReedSolomonDecoder $rsDecoder;
|
||||
|
||||
private $rsDecoder;
|
||||
public function __construct()
|
||||
{
|
||||
$this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256);
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256);
|
||||
}
|
||||
public function decode($variable, $hints = null)
|
||||
{
|
||||
if (is_array($variable)) {
|
||||
return $this->decodeImage($variable, $hints);
|
||||
} elseif ($variable instanceof BitMatrix) {
|
||||
return $this->decodeBits($variable, $hints);
|
||||
} elseif ($variable instanceof BitMatrixParser) {
|
||||
return $this->decodeParser($variable, $hints);
|
||||
}
|
||||
die('decode error Decoder.php');
|
||||
}
|
||||
|
||||
public function decode($variable, $hints = null)
|
||||
{
|
||||
if (is_array($variable)) {
|
||||
return $this->decodeImage($variable, $hints);
|
||||
} elseif ($variable instanceof BitMatrix) {
|
||||
return $this->decodeBits($variable, $hints);
|
||||
} elseif ($variable instanceof BitMatrixParser) {
|
||||
return $this->decodeParser($variable, $hints);
|
||||
}
|
||||
die('decode error Decoder.php');
|
||||
}
|
||||
/**
|
||||
* <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
|
||||
* "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param array $image booleans representing white/black QR Code modules
|
||||
* @param decoding $hints hints that should be used to influence decoding
|
||||
*
|
||||
* @return text and bytes encoded within the QR Code
|
||||
* @throws FormatException if the QR Code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
public function decodeImage($image, $hints = null)
|
||||
{
|
||||
$dimension = count($image);
|
||||
$bits = new BitMatrix($dimension);
|
||||
for ($i = 0; $i < $dimension; $i++) {
|
||||
for ($j = 0; $j < $dimension; $j++) {
|
||||
if ($image[$i][$j]) {
|
||||
$bits->set($j, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
|
||||
* "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param array $image booleans representing white/black QR Code modules
|
||||
* @param hints decoding hints that should be used to influence decoding
|
||||
*
|
||||
* @return text and bytes encoded within the QR Code
|
||||
* @throws FormatException if the QR Code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
public function decodeImage($image, $hints = null)
|
||||
{
|
||||
$dimension = count($image);
|
||||
$bits = new BitMatrix($dimension);
|
||||
for ($i = 0; $i < $dimension; $i++) {
|
||||
for ($j = 0; $j < $dimension; $j++) {
|
||||
if ($image[$i][$j]) {
|
||||
$bits->set($j, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->decode($bits, $hints);
|
||||
}
|
||||
return $this->decode($bits, $hints);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param BitMatrix $bits booleans representing white/black QR Code modules
|
||||
* @param hints decoding hints that should be used to influence decoding
|
||||
*
|
||||
* @return text and bytes encoded within the QR Code
|
||||
* @throws FormatException if the QR Code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
public function decodeBits($bits, $hints = null)
|
||||
{
|
||||
/**
|
||||
* <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param BitMatrix $bits booleans representing white/black QR Code modules
|
||||
* @param decoding $hints hints that should be used to influence decoding
|
||||
*
|
||||
* @return text and bytes encoded within the QR Code
|
||||
* @throws FormatException if the QR Code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
public function decodeBits($bits, $hints = null)
|
||||
{
|
||||
|
||||
// Construct a parser and read version, error-correction level
|
||||
$parser = new BitMatrixParser($bits);
|
||||
$fe = null;
|
||||
$ce = null;
|
||||
try {
|
||||
return $this->decode($parser, $hints);
|
||||
} catch (FormatException $e) {
|
||||
$fe = $e;
|
||||
} catch (ChecksumException $e) {
|
||||
$ce = $e;
|
||||
}
|
||||
$parser = new BitMatrixParser($bits);
|
||||
$fe = null;
|
||||
$ce = null;
|
||||
try {
|
||||
return $this->decode($parser, $hints);
|
||||
} catch (FormatException $e) {
|
||||
$fe = $e;
|
||||
} catch (ChecksumException $e) {
|
||||
$ce = $e;
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
|
||||
// Revert the bit matrix
|
||||
$parser->remask();
|
||||
// Revert the bit matrix
|
||||
$parser->remask();
|
||||
|
||||
// Will be attempting a mirrored reading of the version and format info.
|
||||
$parser->setMirror(true);
|
||||
// Will be attempting a mirrored reading of the version and format info.
|
||||
$parser->setMirror(true);
|
||||
|
||||
// Preemptively read the version.
|
||||
$parser->readVersion();
|
||||
// Preemptively read the version.
|
||||
$parser->readVersion();
|
||||
|
||||
// Preemptively read the format information.
|
||||
$parser->readFormatInformation();
|
||||
// Preemptively read the format information.
|
||||
$parser->readFormatInformation();
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
// Prepare for a mirrored reading.
|
||||
$parser->mirror();
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
// Prepare for a mirrored reading.
|
||||
$parser->mirror();
|
||||
|
||||
$result = $this->decode($parser, $hints);
|
||||
$result = $this->decode($parser, $hints);
|
||||
|
||||
// Success! Notify the caller that the code was mirrored.
|
||||
$result->setOther(new QRCodeDecoderMetaData(true));
|
||||
// Success! Notify the caller that the code was mirrored.
|
||||
$result->setOther(new QRCodeDecoderMetaData(true));
|
||||
|
||||
return $result;
|
||||
return $result;
|
||||
} catch (FormatException $e) {// catch (FormatException | ChecksumException e) {
|
||||
// Throw the exception from the original reading
|
||||
if ($fe != null) {
|
||||
throw $fe;
|
||||
}
|
||||
if ($ce != null) {
|
||||
throw $ce;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (FormatException $e) {// catch (FormatException | ChecksumException e) {
|
||||
// Throw the exception from the original reading
|
||||
if ($fe != null) {
|
||||
throw $fe;
|
||||
}
|
||||
if ($ce != null) {
|
||||
throw $ce;
|
||||
}
|
||||
throw $e;
|
||||
private function decodeParser($parser, $hints = null)
|
||||
{
|
||||
$version = $parser->readVersion();
|
||||
$ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel();
|
||||
|
||||
}
|
||||
}
|
||||
// Read codewords
|
||||
$codewords = $parser->readCodewords();
|
||||
// Separate into data blocks
|
||||
$dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel);
|
||||
|
||||
private function decodeParser($parser, $hints = null)
|
||||
{
|
||||
$version = $parser->readVersion();
|
||||
$ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel();
|
||||
// Count total number of data bytes
|
||||
$totalBytes = 0;
|
||||
foreach ($dataBlocks as $dataBlock) {
|
||||
$totalBytes += $dataBlock->getNumDataCodewords();
|
||||
}
|
||||
$resultBytes = fill_array(0, $totalBytes, 0);
|
||||
$resultOffset = 0;
|
||||
|
||||
// Read codewords
|
||||
$codewords = $parser->readCodewords();
|
||||
// Separate into data blocks
|
||||
$dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel);
|
||||
// Error-correct and copy data blocks together into a stream of bytes
|
||||
foreach ($dataBlocks as $dataBlock) {
|
||||
$codewordBytes = $dataBlock->getCodewords();
|
||||
$numDataCodewords = $dataBlock->getNumDataCodewords();
|
||||
$this->correctErrors($codewordBytes, $numDataCodewords);
|
||||
for ($i = 0; $i < $numDataCodewords; $i++) {
|
||||
$resultBytes[$resultOffset++] = $codewordBytes[$i];
|
||||
}
|
||||
}
|
||||
|
||||
// Count total number of data bytes
|
||||
$totalBytes = 0;
|
||||
foreach ($dataBlocks as $dataBlock) {
|
||||
$totalBytes += $dataBlock->getNumDataCodewords();
|
||||
}
|
||||
$resultBytes = fill_array(0, $totalBytes, 0);
|
||||
$resultOffset = 0;
|
||||
// Decode the contents of that stream of bytes
|
||||
return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints);
|
||||
}
|
||||
|
||||
// Error-correct and copy data blocks together into a stream of bytes
|
||||
foreach ($dataBlocks as $dataBlock) {
|
||||
$codewordBytes = $dataBlock->getCodewords();
|
||||
$numDataCodewords = $dataBlock->getNumDataCodewords();
|
||||
$this->correctErrors($codewordBytes, $numDataCodewords);
|
||||
for ($i = 0; $i < $numDataCodewords; $i++) {
|
||||
$resultBytes[$resultOffset++] = $codewordBytes[$i];
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the contents of that stream of bytes
|
||||
return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||
* correct the errors in-place using Reed-Solomon error correction.</p>
|
||||
*
|
||||
* @param codewordBytes data and error correction codewords
|
||||
* @param numDataCodewords number of codewords that are data bytes
|
||||
*
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
private function correctErrors(&$codewordBytes, $numDataCodewords)
|
||||
{
|
||||
$numCodewords = count($codewordBytes);
|
||||
// First read into an array of ints
|
||||
$codewordsInts = fill_array(0, $numCodewords, 0);
|
||||
for ($i = 0; $i < $numCodewords; $i++) {
|
||||
$codewordsInts[$i] = $codewordBytes[$i] & 0xFF;
|
||||
}
|
||||
$numECCodewords = count($codewordBytes) - $numDataCodewords;
|
||||
try {
|
||||
$this->rsDecoder->decode($codewordsInts, $numECCodewords);
|
||||
} catch (ReedSolomonException $ignored) {
|
||||
throw ChecksumException::getChecksumInstance();
|
||||
}
|
||||
// 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] = $codewordsInts[$i];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||
* correct the errors in-place using Reed-Solomon error correction.</p>
|
||||
*
|
||||
* @param data $codewordBytes and error correction codewords
|
||||
* @param number $numDataCodewords of codewords that are data bytes
|
||||
*
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
private function correctErrors(&$codewordBytes, $numDataCodewords)
|
||||
{
|
||||
$numCodewords = is_countable($codewordBytes) ? count($codewordBytes) : 0;
|
||||
// First read into an array of ints
|
||||
$codewordsInts = fill_array(0, $numCodewords, 0);
|
||||
for ($i = 0; $i < $numCodewords; $i++) {
|
||||
$codewordsInts[$i] = $codewordBytes[$i] & 0xFF;
|
||||
}
|
||||
$numECCodewords = (is_countable($codewordBytes) ? count($codewordBytes) : 0) - $numDataCodewords;
|
||||
try {
|
||||
$this->rsDecoder->decode($codewordsInts, $numECCodewords);
|
||||
} catch (ReedSolomonException) {
|
||||
throw ChecksumException::getChecksumInstance();
|
||||
}
|
||||
// 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] = $codewordsInts[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,67 +25,66 @@ namespace Zxing\Qrcode\Decoder;
|
|||
*/
|
||||
class ErrorCorrectionLevel
|
||||
{
|
||||
private static $FOR_BITS;
|
||||
private $bits;
|
||||
private $ordinal;
|
||||
/**
|
||||
* @var \Zxing\Qrcode\Decoder\ErrorCorrectionLevel[]|null
|
||||
*/
|
||||
private static ?array $FOR_BITS = null;
|
||||
|
||||
public function __construct($bits, $ordinal = 0)
|
||||
{
|
||||
$this->bits = $bits;
|
||||
$this->ordinal = $ordinal;
|
||||
}
|
||||
public function __construct(private $bits, private $ordinal = 0)
|
||||
{
|
||||
}
|
||||
|
||||
public static function Init()
|
||||
{
|
||||
self::$FOR_BITS = [
|
||||
public static function Init(): void
|
||||
{
|
||||
self::$FOR_BITS = [
|
||||
|
||||
|
||||
new ErrorCorrectionLevel(0x00, 1), //M
|
||||
new ErrorCorrectionLevel(0x01, 0), //L
|
||||
new ErrorCorrectionLevel(0x02, 3), //H
|
||||
new ErrorCorrectionLevel(0x03, 2), //Q
|
||||
new ErrorCorrectionLevel(0x00, 1), //M
|
||||
new ErrorCorrectionLevel(0x01, 0), //L
|
||||
new ErrorCorrectionLevel(0x02, 3), //H
|
||||
new ErrorCorrectionLevel(0x03, 2), //Q
|
||||
|
||||
];
|
||||
}
|
||||
/** L = ~7% correction */
|
||||
// self::$L = new ErrorCorrectionLevel(0x01);
|
||||
/** M = ~15% correction */
|
||||
//self::$M = new ErrorCorrectionLevel(0x00);
|
||||
/** Q = ~25% correction */
|
||||
//self::$Q = new ErrorCorrectionLevel(0x03);
|
||||
/** H = ~30% correction */
|
||||
//self::$H = new ErrorCorrectionLevel(0x02);
|
||||
/**
|
||||
* @param bits int containing the two bits encoding a QR Code's error correction level
|
||||
*
|
||||
* @return ErrorCorrectionLevel representing the encoded error correction level
|
||||
*/
|
||||
public static function forBits($bits)
|
||||
{
|
||||
if ($bits < 0 || $bits >= count(self::$FOR_BITS)) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
$level = self::$FOR_BITS[$bits];
|
||||
];
|
||||
}
|
||||
/** L = ~7% correction */
|
||||
// self::$L = new ErrorCorrectionLevel(0x01);
|
||||
/** M = ~15% correction */
|
||||
//self::$M = new ErrorCorrectionLevel(0x00);
|
||||
/** Q = ~25% correction */
|
||||
//self::$Q = new ErrorCorrectionLevel(0x03);
|
||||
/** H = ~30% correction */
|
||||
//self::$H = new ErrorCorrectionLevel(0x02);
|
||||
/**
|
||||
* @param int $bits containing the two bits encoding a QR Code's error correction level
|
||||
*
|
||||
* @return ErrorCorrectionLevel representing the encoded error correction level
|
||||
*/
|
||||
public static function forBits($bits)
|
||||
{
|
||||
if ($bits < 0 || $bits >= (is_countable(self::$FOR_BITS) ? count(self::$FOR_BITS) : 0)) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
$level = self::$FOR_BITS[$bits];
|
||||
|
||||
// $lev = self::$$bit;
|
||||
return $level;
|
||||
}
|
||||
// $lev = self::$$bit;
|
||||
return $level;
|
||||
}
|
||||
|
||||
|
||||
public function getBits()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
public function getBits()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
public function toString()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
|
||||
public function getOrdinal()
|
||||
{
|
||||
return $this->ordinal;
|
||||
}
|
||||
public function getOrdinal()
|
||||
{
|
||||
return $this->ordinal;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCorrectionLevel::Init();
|
||||
|
|
|
|||
|
|
@ -27,164 +27,167 @@ namespace Zxing\Qrcode\Decoder;
|
|||
*/
|
||||
final class FormatInformation
|
||||
{
|
||||
public static $FORMAT_INFO_MASK_QR;
|
||||
public static $FORMAT_INFO_MASK_QR;
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006, Annex C, Table C.1
|
||||
*/
|
||||
public static $FORMAT_INFO_DECODE_LOOKUP;
|
||||
/**
|
||||
* Offset i holds the number of 1 bits in the binary representation of i
|
||||
*/
|
||||
private static $BITS_SET_IN_HALF_BYTE;
|
||||
/**
|
||||
* See ISO 18004:2006, Annex C, Table C.1
|
||||
*/
|
||||
public static $FORMAT_INFO_DECODE_LOOKUP;
|
||||
/**
|
||||
* Offset i holds the number of 1 bits in the binary representation of i
|
||||
* @var int[]|null
|
||||
*/
|
||||
private static ?array $BITS_SET_IN_HALF_BYTE = null;
|
||||
|
||||
private $errorCorrectionLevel;
|
||||
private $dataMask;
|
||||
private readonly \Zxing\Qrcode\Decoder\ErrorCorrectionLevel $errorCorrectionLevel;
|
||||
private readonly int $dataMask;
|
||||
|
||||
private function __construct($formatInfo)
|
||||
{
|
||||
// Bits 3,4
|
||||
$this->errorCorrectionLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x03);
|
||||
// Bottom 3 bits
|
||||
$this->dataMask = ($formatInfo & 0x07);//(byte)
|
||||
}
|
||||
private function __construct($formatInfo)
|
||||
{
|
||||
// Bits 3,4
|
||||
$this->errorCorrectionLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x03);
|
||||
// Bottom 3 bits
|
||||
$this->dataMask = ($formatInfo & 0x07);//(byte)
|
||||
}
|
||||
|
||||
public static function Init()
|
||||
{
|
||||
self::$FORMAT_INFO_MASK_QR = 0x5412;
|
||||
self::$BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
|
||||
self::$FORMAT_INFO_DECODE_LOOKUP = [
|
||||
[0x5412, 0x00],
|
||||
[0x5125, 0x01],
|
||||
[0x5E7C, 0x02],
|
||||
[0x5B4B, 0x03],
|
||||
[0x45F9, 0x04],
|
||||
[0x40CE, 0x05],
|
||||
[0x4F97, 0x06],
|
||||
[0x4AA0, 0x07],
|
||||
[0x77C4, 0x08],
|
||||
[0x72F3, 0x09],
|
||||
[0x7DAA, 0x0A],
|
||||
[0x789D, 0x0B],
|
||||
[0x662F, 0x0C],
|
||||
[0x6318, 0x0D],
|
||||
[0x6C41, 0x0E],
|
||||
[0x6976, 0x0F],
|
||||
[0x1689, 0x10],
|
||||
[0x13BE, 0x11],
|
||||
[0x1CE7, 0x12],
|
||||
[0x19D0, 0x13],
|
||||
[0x0762, 0x14],
|
||||
[0x0255, 0x15],
|
||||
[0x0D0C, 0x16],
|
||||
[0x083B, 0x17],
|
||||
[0x355F, 0x18],
|
||||
[0x3068, 0x19],
|
||||
[0x3F31, 0x1A],
|
||||
[0x3A06, 0x1B],
|
||||
[0x24B4, 0x1C],
|
||||
[0x2183, 0x1D],
|
||||
[0x2EDA, 0x1E],
|
||||
[0x2BED, 0x1F],
|
||||
];
|
||||
}
|
||||
public static function Init(): void
|
||||
{
|
||||
self::$FORMAT_INFO_MASK_QR = 0x5412;
|
||||
self::$BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
|
||||
self::$FORMAT_INFO_DECODE_LOOKUP = [
|
||||
[0x5412, 0x00],
|
||||
[0x5125, 0x01],
|
||||
[0x5E7C, 0x02],
|
||||
[0x5B4B, 0x03],
|
||||
[0x45F9, 0x04],
|
||||
[0x40CE, 0x05],
|
||||
[0x4F97, 0x06],
|
||||
[0x4AA0, 0x07],
|
||||
[0x77C4, 0x08],
|
||||
[0x72F3, 0x09],
|
||||
[0x7DAA, 0x0A],
|
||||
[0x789D, 0x0B],
|
||||
[0x662F, 0x0C],
|
||||
[0x6318, 0x0D],
|
||||
[0x6C41, 0x0E],
|
||||
[0x6976, 0x0F],
|
||||
[0x1689, 0x10],
|
||||
[0x13BE, 0x11],
|
||||
[0x1CE7, 0x12],
|
||||
[0x19D0, 0x13],
|
||||
[0x0762, 0x14],
|
||||
[0x0255, 0x15],
|
||||
[0x0D0C, 0x16],
|
||||
[0x083B, 0x17],
|
||||
[0x355F, 0x18],
|
||||
[0x3068, 0x19],
|
||||
[0x3F31, 0x1A],
|
||||
[0x3A06, 0x1B],
|
||||
[0x24B4, 0x1C],
|
||||
[0x2183, 0x1D],
|
||||
[0x2EDA, 0x1E],
|
||||
[0x2BED, 0x1F],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maskedFormatInfo1 ; format info indicator, with mask still applied
|
||||
* @param maskedFormatInfo2 ; second copy of same info; both are checked at the same time
|
||||
* to establish best match
|
||||
*
|
||||
* @return information about the format it specifies, or {@code null}
|
||||
* if doesn't seem to match any known pattern
|
||||
*/
|
||||
public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
|
||||
{
|
||||
$formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
|
||||
if ($formatInfo != null) {
|
||||
return $formatInfo;
|
||||
}
|
||||
// Should return null, but, some QR codes apparently
|
||||
// do not mask this info. Try again by actually masking the pattern
|
||||
// first
|
||||
return self::doDecodeFormatInformation($maskedFormatInfo1 ^ self::$FORMAT_INFO_MASK_QR,
|
||||
$maskedFormatInfo2 ^ self::$FORMAT_INFO_MASK_QR);
|
||||
}
|
||||
/**
|
||||
* @param $maskedFormatInfo1 ; format info indicator, with mask still applied
|
||||
* @param $maskedFormatInfo2 ; second copy of same info; both are checked at the same time
|
||||
* to establish best match
|
||||
*
|
||||
* @return information about the format it specifies, or {@code null}
|
||||
* if doesn't seem to match any known pattern
|
||||
*/
|
||||
public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
|
||||
{
|
||||
$formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
|
||||
if ($formatInfo != null) {
|
||||
return $formatInfo;
|
||||
}
|
||||
// Should return null, but, some QR codes apparently
|
||||
// do not mask this info. Try again by actually masking the pattern
|
||||
// first
|
||||
return self::doDecodeFormatInformation(
|
||||
$maskedFormatInfo1 ^ self::$FORMAT_INFO_MASK_QR,
|
||||
$maskedFormatInfo2 ^ self::$FORMAT_INFO_MASK_QR
|
||||
);
|
||||
}
|
||||
|
||||
private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
|
||||
{
|
||||
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
|
||||
$bestDifference = PHP_INT_MAX;
|
||||
$bestFormatInfo = 0;
|
||||
foreach (self::$FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) {
|
||||
$targetInfo = $decodeInfo[0];
|
||||
if ($targetInfo == $maskedFormatInfo1 || $targetInfo == $maskedFormatInfo2) {
|
||||
// Found an exact match
|
||||
return new FormatInformation($decodeInfo[1]);
|
||||
}
|
||||
$bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);
|
||||
if ($bitsDifference < $bestDifference) {
|
||||
$bestFormatInfo = $decodeInfo[1];
|
||||
$bestDifference = $bitsDifference;
|
||||
}
|
||||
if ($maskedFormatInfo1 != $maskedFormatInfo2) {
|
||||
// also try the other option
|
||||
$bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);
|
||||
if ($bitsDifference < $bestDifference) {
|
||||
$bestFormatInfo = $decodeInfo[1];
|
||||
$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 new FormatInformation($bestFormatInfo);
|
||||
}
|
||||
private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
|
||||
{
|
||||
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
|
||||
$bestDifference = PHP_INT_MAX;
|
||||
$bestFormatInfo = 0;
|
||||
foreach (self::$FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) {
|
||||
$targetInfo = $decodeInfo[0];
|
||||
if ($targetInfo == $maskedFormatInfo1 || $targetInfo == $maskedFormatInfo2) {
|
||||
// Found an exact match
|
||||
return new FormatInformation($decodeInfo[1]);
|
||||
}
|
||||
$bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);
|
||||
if ($bitsDifference < $bestDifference) {
|
||||
$bestFormatInfo = $decodeInfo[1];
|
||||
$bestDifference = $bitsDifference;
|
||||
}
|
||||
if ($maskedFormatInfo1 != $maskedFormatInfo2) {
|
||||
// also try the other option
|
||||
$bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);
|
||||
if ($bitsDifference < $bestDifference) {
|
||||
$bestFormatInfo = $decodeInfo[1];
|
||||
$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 new FormatInformation($bestFormatInfo);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function numBitsDiffering($a, $b)
|
||||
{
|
||||
$a ^= $b; // a now has a 1 bit exactly where its bit differs with b's
|
||||
// Count bits set quickly with a series of lookups:
|
||||
return self::$BITS_SET_IN_HALF_BYTE[$a & 0x0F] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(int)(uRShift($a, 4) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 8) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 12) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 16) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 20) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 24) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 28) & 0x0F)];
|
||||
}
|
||||
public static function numBitsDiffering($a, $b)
|
||||
{
|
||||
$a ^= $b; // a now has a 1 bit exactly where its bit differs with b's
|
||||
// Count bits set quickly with a series of lookups:
|
||||
return self::$BITS_SET_IN_HALF_BYTE[$a & 0x0F] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(int)(uRShift($a, 4) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 8) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 12) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 16) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 20) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 24) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 28) & 0x0F)];
|
||||
}
|
||||
|
||||
public function getErrorCorrectionLevel()
|
||||
{
|
||||
return $this->errorCorrectionLevel;
|
||||
}
|
||||
public function getErrorCorrectionLevel()
|
||||
{
|
||||
return $this->errorCorrectionLevel;
|
||||
}
|
||||
|
||||
public function getDataMask()
|
||||
{
|
||||
return $this->dataMask;
|
||||
}
|
||||
public function getDataMask()
|
||||
{
|
||||
return $this->dataMask;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function hashCode()
|
||||
{
|
||||
return ($this->errorCorrectionLevel->ordinal() << 3) | (int)($this->dataMask);
|
||||
}
|
||||
//@Override
|
||||
public function hashCode()
|
||||
{
|
||||
return ($this->errorCorrectionLevel->ordinal() << 3) | (int)($this->dataMask);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function equals($o)
|
||||
{
|
||||
if (!($o instanceof FormatInformation)) {
|
||||
return false;
|
||||
}
|
||||
$other = $o;
|
||||
//@Override
|
||||
public function equals($o)
|
||||
{
|
||||
if (!($o instanceof FormatInformation)) {
|
||||
return false;
|
||||
}
|
||||
$other = $o;
|
||||
|
||||
return $this->errorCorrectionLevel == $other->errorCorrectionLevel &&
|
||||
$this->dataMask == $other->dataMask;
|
||||
}
|
||||
return $this->errorCorrectionLevel == $other->errorCorrectionLevel &&
|
||||
$this->dataMask == $other->dataMask;
|
||||
}
|
||||
}
|
||||
|
||||
FormatInformation::Init();
|
||||
|
|
|
|||
|
|
@ -25,104 +25,84 @@ namespace Zxing\Qrcode\Decoder;
|
|||
*/
|
||||
class Mode
|
||||
{
|
||||
public static $TERMINATOR;
|
||||
public static $NUMERIC;
|
||||
public static $ALPHANUMERIC;
|
||||
public static $STRUCTURED_APPEND;
|
||||
public static $BYTE;
|
||||
public static $ECI;
|
||||
public static $KANJI;
|
||||
public static $FNC1_FIRST_POSITION;
|
||||
public static $FNC1_SECOND_POSITION;
|
||||
public static $HANZI;
|
||||
public static $TERMINATOR;
|
||||
public static $NUMERIC;
|
||||
public static $ALPHANUMERIC;
|
||||
public static $STRUCTURED_APPEND;
|
||||
public static $BYTE;
|
||||
public static $ECI;
|
||||
public static $KANJI;
|
||||
public static $FNC1_FIRST_POSITION;
|
||||
public static $FNC1_SECOND_POSITION;
|
||||
public static $HANZI;
|
||||
|
||||
private $characterCountBitsForVersions;
|
||||
private $bits;
|
||||
public function __construct(private $characterCountBitsForVersions, private $bits)
|
||||
{
|
||||
}
|
||||
|
||||
public function __construct($characterCountBitsForVersions, $bits)
|
||||
{
|
||||
$this->characterCountBitsForVersions = $characterCountBitsForVersions;
|
||||
$this->bits = $bits;
|
||||
}
|
||||
public static function Init(): void
|
||||
{
|
||||
self::$TERMINATOR = new Mode([0, 0, 0], 0x00); // Not really a mode...
|
||||
self::$NUMERIC = new Mode([10, 12, 14], 0x01);
|
||||
self::$ALPHANUMERIC = new Mode([9, 11, 13], 0x02);
|
||||
self::$STRUCTURED_APPEND = new Mode([0, 0, 0], 0x03); // Not supported
|
||||
self::$BYTE = new Mode([8, 16, 16], 0x04);
|
||||
self::$ECI = new Mode([0, 0, 0], 0x07); // character counts don't apply
|
||||
self::$KANJI = new Mode([8, 10, 12], 0x08);
|
||||
self::$FNC1_FIRST_POSITION = new Mode([0, 0, 0], 0x05);
|
||||
self::$FNC1_SECOND_POSITION = new Mode([0, 0, 0], 0x09);
|
||||
/** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
|
||||
self::$HANZI = new Mode([8, 10, 12], 0x0D);
|
||||
}
|
||||
|
||||
public static function Init()
|
||||
{
|
||||
/**
|
||||
* @param four $bits bits encoding a QR Code data mode
|
||||
*
|
||||
* @return Mode encoded by these bits
|
||||
* @throws InvalidArgumentException if bits do not correspond to a known mode
|
||||
*/
|
||||
public static function forBits($bits)
|
||||
{
|
||||
return match ($bits) {
|
||||
0x0 => self::$TERMINATOR,
|
||||
0x1 => self::$NUMERIC,
|
||||
0x2 => self::$ALPHANUMERIC,
|
||||
0x3 => self::$STRUCTURED_APPEND,
|
||||
0x4 => self::$BYTE,
|
||||
0x5 => self::$FNC1_FIRST_POSITION,
|
||||
0x7 => self::$ECI,
|
||||
0x8 => self::$KANJI,
|
||||
0x9 => self::$FNC1_SECOND_POSITION,
|
||||
0xD => self::$HANZI,
|
||||
default => throw new \InvalidArgumentException(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param version $version in question
|
||||
*
|
||||
* @return number of bits used, in this QR Code symbol {@link Version}, to encode the
|
||||
* count of characters that will follow encoded in this Mode
|
||||
*/
|
||||
public function getCharacterCountBits($version)
|
||||
{
|
||||
$number = $version->getVersionNumber();
|
||||
$offset = 0;
|
||||
if ($number <= 9) {
|
||||
$offset = 0;
|
||||
} elseif ($number <= 26) {
|
||||
$offset = 1;
|
||||
} else {
|
||||
$offset = 2;
|
||||
}
|
||||
|
||||
self::$TERMINATOR = new Mode([0, 0, 0], 0x00); // Not really a mode...
|
||||
self::$NUMERIC = new Mode([10, 12, 14], 0x01);
|
||||
self::$ALPHANUMERIC = new Mode([9, 11, 13], 0x02);
|
||||
self::$STRUCTURED_APPEND = new Mode([0, 0, 0], 0x03); // Not supported
|
||||
self::$BYTE = new Mode([8, 16, 16], 0x04);
|
||||
self::$ECI = new Mode([0, 0, 0], 0x07); // character counts don't apply
|
||||
self::$KANJI = new Mode([8, 10, 12], 0x08);
|
||||
self::$FNC1_FIRST_POSITION = new Mode([0, 0, 0], 0x05);
|
||||
self::$FNC1_SECOND_POSITION = new Mode([0, 0, 0], 0x09);
|
||||
/** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
|
||||
self::$HANZI = new Mode([8, 10, 12], 0x0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bits four bits encoding a QR Code data mode
|
||||
*
|
||||
* @return Mode encoded by these bits
|
||||
* @throws InvalidArgumentException if bits do not correspond to a known mode
|
||||
*/
|
||||
public static function forBits($bits)
|
||||
{
|
||||
switch ($bits) {
|
||||
case 0x0:
|
||||
return self::$TERMINATOR;
|
||||
case 0x1:
|
||||
return self::$NUMERIC;
|
||||
case 0x2:
|
||||
return self::$ALPHANUMERIC;
|
||||
case 0x3:
|
||||
return self::$STRUCTURED_APPEND;
|
||||
case 0x4:
|
||||
return self::$BYTE;
|
||||
case 0x5:
|
||||
return self::$FNC1_FIRST_POSITION;
|
||||
case 0x7:
|
||||
return self::$ECI;
|
||||
case 0x8:
|
||||
return self::$KANJI;
|
||||
case 0x9:
|
||||
return self::$FNC1_SECOND_POSITION;
|
||||
case 0xD:
|
||||
// 0xD is defined in GBT 18284-2000, may not be supported in foreign country
|
||||
return self::$HANZI;
|
||||
default:
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param version version in question
|
||||
*
|
||||
* @return number of bits used, in this QR Code symbol {@link Version}, to encode the
|
||||
* count of characters that will follow encoded in this Mode
|
||||
*/
|
||||
public function getCharacterCountBits($version)
|
||||
{
|
||||
$number = $version->getVersionNumber();
|
||||
$offset = 0;
|
||||
if ($number <= 9) {
|
||||
$offset = 0;
|
||||
} else if ($number <= 26) {
|
||||
$offset = 1;
|
||||
} else {
|
||||
$offset = 2;
|
||||
}
|
||||
|
||||
return $this->characterCountBitsForVersions[$offset];
|
||||
}
|
||||
|
||||
public function getBits()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
return $this->characterCountBitsForVersions[$offset];
|
||||
}
|
||||
|
||||
public function getBits()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
}
|
||||
|
||||
Mode::Init();
|
||||
|
|
|
|||
|
|
@ -4,20 +4,16 @@ namespace Zxing\Qrcode\Decoder;
|
|||
|
||||
class QRCodeDecoderMetaData
|
||||
{
|
||||
/** @var bool */
|
||||
private $mirrored;
|
||||
/**
|
||||
* QRCodeDecoderMetaData constructor.
|
||||
* @param bool $mirrored
|
||||
*/
|
||||
public function __construct(private $mirrored)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* QRCodeDecoderMetaData constructor.
|
||||
* @param bool $mirrored
|
||||
*/
|
||||
public function __construct($mirrored)
|
||||
{
|
||||
$this->mirrored = $mirrored;
|
||||
}
|
||||
|
||||
public function isMirrored()
|
||||
{
|
||||
return $this->mirrored;
|
||||
}
|
||||
public function isMirrored()
|
||||
{
|
||||
return $this->mirrored;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -27,39 +27,36 @@ use Zxing\ResultPoint;
|
|||
*/
|
||||
final class AlignmentPattern extends ResultPoint
|
||||
{
|
||||
private $estimatedModuleSize;
|
||||
public function __construct($posX, $posY, private $estimatedModuleSize)
|
||||
{
|
||||
parent::__construct($posX, $posY);
|
||||
}
|
||||
|
||||
public function __construct($posX, $posY, $estimatedModuleSize)
|
||||
{
|
||||
parent::__construct($posX, $posY);
|
||||
$this->estimatedModuleSize = $estimatedModuleSize;
|
||||
}
|
||||
/**
|
||||
* <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated
|
||||
* position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
|
||||
*/
|
||||
public function aboutEquals($moduleSize, $i, $j)
|
||||
{
|
||||
if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) {
|
||||
$moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize);
|
||||
|
||||
/**
|
||||
* <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated
|
||||
* position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
|
||||
*/
|
||||
public function aboutEquals($moduleSize, $i, $j)
|
||||
{
|
||||
if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) {
|
||||
$moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize);
|
||||
return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize;
|
||||
}
|
||||
|
||||
return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Combines this object's current estimate of a finder pattern position and module size
|
||||
* with a new estimate. It returns a new {@code FinderPattern} containing an average of the two.
|
||||
*/
|
||||
public function combineEstimate($i, $j, $newModuleSize): \Zxing\Qrcode\Detector\AlignmentPattern
|
||||
{
|
||||
$combinedX = ($this->getX() + $j) / 2.0;
|
||||
$combinedY = ($this->getY() + $i) / 2.0;
|
||||
$combinedModuleSize = ($this->estimatedModuleSize + $newModuleSize) / 2.0;
|
||||
|
||||
/**
|
||||
* Combines this object's current estimate of a finder pattern position and module size
|
||||
* with a new estimate. It returns a new {@code FinderPattern} containing an average of the two.
|
||||
*/
|
||||
public function combineEstimate($i, $j, $newModuleSize)
|
||||
{
|
||||
$combinedX = ($this->getX() + $j) / 2.0;
|
||||
$combinedY = ($this->getY() + $i) / 2.0;
|
||||
$combinedModuleSize = ($this->estimatedModuleSize + $newModuleSize) / 2.0;
|
||||
|
||||
return new AlignmentPattern($combinedX, $combinedY, $combinedModuleSize);
|
||||
}
|
||||
return new AlignmentPattern($combinedX, $combinedY, $combinedModuleSize);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@
|
|||
namespace Zxing\Qrcode\Detector;
|
||||
|
||||
use Zxing\NotFoundException;
|
||||
use Zxing\ResultPointCallback;
|
||||
use Zxing\Common\BitMatrix;
|
||||
|
||||
/**
|
||||
* <p>This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder
|
||||
|
|
@ -37,250 +35,231 @@ use Zxing\Common\BitMatrix;
|
|||
*/
|
||||
final class AlignmentPatternFinder
|
||||
{
|
||||
private $image;
|
||||
private $possibleCenters;
|
||||
private $startX;
|
||||
private $startY;
|
||||
private $width;
|
||||
private $height;
|
||||
private $moduleSize;
|
||||
private $crossCheckStateCount;
|
||||
private $resultPointCallback;
|
||||
private array $possibleCenters = [];
|
||||
private array $crossCheckStateCount = [];
|
||||
|
||||
/**
|
||||
* <p>Creates a finder that will look in a portion of the whole image.</p>
|
||||
*
|
||||
* @param image image to search
|
||||
* @param startX left column from which to start searching
|
||||
* @param startY top row from which to start searching
|
||||
* @param width width of region to search
|
||||
* @param height height of region to search
|
||||
* @param moduleSize estimated module size so far
|
||||
*/
|
||||
public function __construct($image,
|
||||
$startX,
|
||||
$startY,
|
||||
$width,
|
||||
$height,
|
||||
$moduleSize,
|
||||
$resultPointCallback)
|
||||
{
|
||||
$this->image = $image;
|
||||
$this->possibleCenters = [];
|
||||
$this->startX = $startX;
|
||||
$this->startY = $startY;
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
$this->moduleSize = $moduleSize;
|
||||
$this->crossCheckStateCount = [];
|
||||
$this->resultPointCallback = $resultPointCallback;
|
||||
}
|
||||
/**
|
||||
* <p>Creates a finder that will look in a portion of the whole image.</p>
|
||||
*
|
||||
* @param \Imagick image $image to search
|
||||
* @param int left $startX column from which to start searching
|
||||
* @param int top $startY row from which to start searching
|
||||
* @param float width $width of region to search
|
||||
* @param float height $height of region to search
|
||||
* @param float estimated $moduleSize module size so far
|
||||
*/
|
||||
public function __construct(private $image, private $startX, private $startY, private $width, private $height, private $moduleSize, private $resultPointCallback)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @return {@link AlignmentPattern} if found
|
||||
* @throws NotFoundException if not found
|
||||
*/
|
||||
public function find()
|
||||
{
|
||||
$startX = $this->startX;
|
||||
$height = $this->height;
|
||||
$maxJ = $startX + $this->width;
|
||||
$middleI = $this->startY + ($height / 2);
|
||||
// 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
|
||||
$stateCount = [];
|
||||
for ($iGen = 0; $iGen < $height; $iGen++) {
|
||||
// Search from middle outwards
|
||||
$i = $middleI + (($iGen & 0x01) == 0 ? ($iGen + 1) / 2 : -(($iGen + 1) / 2));
|
||||
$i = (int)($i);
|
||||
$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->image->get($j, $i)) {
|
||||
$j++;
|
||||
}
|
||||
$currentState = 0;
|
||||
while ($j < $maxJ) {
|
||||
if ($this->image->get($j, $i)) {
|
||||
// Black pixel
|
||||
if ($currentState == 1) { // Counting black pixels
|
||||
$stateCount[$currentState]++;
|
||||
} else { // Counting white pixels
|
||||
if ($currentState == 2) { // A winner?
|
||||
if ($this->foundPatternCross($stateCount)) { // Yes
|
||||
$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]++;
|
||||
}
|
||||
}
|
||||
} else { // White pixel
|
||||
if ($currentState == 1) { // Counting black pixels
|
||||
$currentState++;
|
||||
}
|
||||
$stateCount[$currentState]++;
|
||||
}
|
||||
$j++;
|
||||
}
|
||||
if ($this->foundPatternCross($stateCount)) {
|
||||
$confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ);
|
||||
if ($confirmed != null) {
|
||||
return $confirmed;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @return {@link AlignmentPattern} if found
|
||||
* @throws NotFoundException if not found
|
||||
*/
|
||||
public function find()
|
||||
{
|
||||
$startX = $this->startX;
|
||||
$height = $this->height;
|
||||
$maxJ = $startX + $this->width;
|
||||
$middleI = $this->startY + ($height / 2);
|
||||
// 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
|
||||
$stateCount = [];
|
||||
for ($iGen = 0; $iGen < $height; $iGen++) {
|
||||
// Search from middle outwards
|
||||
$i = $middleI + (($iGen & 0x01) == 0 ? ($iGen + 1) / 2 : -(($iGen + 1) / 2));
|
||||
$i = (int)($i);
|
||||
$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->image->get($j, $i)) {
|
||||
$j++;
|
||||
}
|
||||
$currentState = 0;
|
||||
while ($j < $maxJ) {
|
||||
if ($this->image->get($j, $i)) {
|
||||
// Black pixel
|
||||
if ($currentState == 1) { // Counting black pixels
|
||||
$stateCount[$currentState]++;
|
||||
} else { // Counting white pixels
|
||||
if ($currentState == 2) { // A winner?
|
||||
if ($this->foundPatternCross($stateCount)) { // Yes
|
||||
$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]++;
|
||||
}
|
||||
}
|
||||
} else { // White pixel
|
||||
if ($currentState == 1) { // Counting black pixels
|
||||
$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];
|
||||
}
|
||||
|
||||
// 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];
|
||||
}
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
/**
|
||||
* @param count $stateCount of black/white/black pixels just read
|
||||
*
|
||||
* @return true iff 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($stateCount)
|
||||
{
|
||||
$moduleSize = $this->moduleSize;
|
||||
$maxVariance = $moduleSize / 2.0;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
if (abs($moduleSize - $stateCount[$i]) >= $maxVariance) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stateCount count of black/white/black pixels just read
|
||||
*
|
||||
* @return true iff 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($stateCount)
|
||||
{
|
||||
$moduleSize = $this->moduleSize;
|
||||
$maxVariance = $moduleSize / 2.0;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
if (abs($moduleSize - $stateCount[$i]) >= $maxVariance) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param reading $stateCount state module counts from horizontal scan
|
||||
* @param row $i where alignment pattern may be found
|
||||
* @param end $j of possible alignment pattern in row
|
||||
*
|
||||
* @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not
|
||||
*/
|
||||
private function handlePossibleCenter($stateCount, $i, $j)
|
||||
{
|
||||
$stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2];
|
||||
$centerJ = self::centerFromEnd($stateCount, $j);
|
||||
$centerI = $this->crossCheckVertical($i, (int)$centerJ, 2 * $stateCount[1], $stateCountTotal);
|
||||
if (!is_nan($centerI)) {
|
||||
$estimatedModuleSize = (float)($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;
|
||||
if ($this->resultPointCallback != null) {
|
||||
$this->resultPointCallback->foundPossibleResultPoint($point);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param stateCount reading state module counts from horizontal scan
|
||||
* @param i row where alignment pattern may be found
|
||||
* @param j end of possible alignment pattern in row
|
||||
*
|
||||
* @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not
|
||||
*/
|
||||
private function handlePossibleCenter($stateCount, $i, $j)
|
||||
{
|
||||
$stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2];
|
||||
$centerJ = $this->centerFromEnd($stateCount, $j);
|
||||
$centerI = $this->crossCheckVertical($i, (int)$centerJ, 2 * $stateCount[1], $stateCountTotal);
|
||||
if (!is_nan($centerI)) {
|
||||
$estimatedModuleSize = (float)($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;
|
||||
if ($this->resultPointCallback != null) {
|
||||
$this->resultPointCallback->foundPossibleResultPoint($point);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
private static function centerFromEnd($stateCount, $end)
|
||||
{
|
||||
return (float)($end - $stateCount[2]) - $stateCount[1] / 2.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private static function centerFromEnd($stateCount, $end)
|
||||
{
|
||||
return (float)($end - $stateCount[2]) - $stateCount[1] / 2.0;
|
||||
}
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param int row $startI where an alignment pattern was detected
|
||||
* @param float center $centerJ of the section that appears to cross an alignment pattern
|
||||
* @param int maximum $maxCount reasonable number of modules that should be
|
||||
* observed in any reading state, based on the results of the horizontal scan
|
||||
*
|
||||
* @return float vertical center of alignment pattern, or {@link Float#NaN} if not found
|
||||
*/
|
||||
private function crossCheckVertical(
|
||||
$startI,
|
||||
$centerJ,
|
||||
$maxCount,
|
||||
$originalStateCountTotal
|
||||
)
|
||||
{
|
||||
$image = $this->image;
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param startI row where an alignment pattern was detected
|
||||
* @param centerJ center of the section that appears to cross an alignment pattern
|
||||
* @param maxCount maximum reasonable number of modules that should be
|
||||
* observed in any reading state, based on the results of the horizontal scan
|
||||
*
|
||||
* @return vertical center of alignment pattern, or {@link Float#NaN} if not found
|
||||
*/
|
||||
private function crossCheckVertical($startI, $centerJ, $maxCount,
|
||||
$originalStateCountTotal)
|
||||
{
|
||||
$image = $this->image;
|
||||
$maxI = $image->getHeight();
|
||||
$stateCount = $this->crossCheckStateCount;
|
||||
$stateCount[0] = 0;
|
||||
$stateCount[1] = 0;
|
||||
$stateCount[2] = 0;
|
||||
|
||||
$maxI = $image->getHeight();
|
||||
$stateCount = $this->crossCheckStateCount;
|
||||
$stateCount[0] = 0;
|
||||
$stateCount[1] = 0;
|
||||
$stateCount[2] = 0;
|
||||
// Start counting up from center
|
||||
$i = $startI;
|
||||
while ($i >= 0 && $image->get($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 NAN;
|
||||
}
|
||||
while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[0] <= $maxCount) {
|
||||
$stateCount[0]++;
|
||||
$i--;
|
||||
}
|
||||
if ($stateCount[0] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
// Start counting up from center
|
||||
$i = $startI;
|
||||
while ($i >= 0 && $image->get($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 NAN;
|
||||
}
|
||||
while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[0] <= $maxCount) {
|
||||
$stateCount[0]++;
|
||||
$i--;
|
||||
}
|
||||
if ($stateCount[0] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
// Now also count down from center
|
||||
$i = $startI + 1;
|
||||
while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
|
||||
$stateCount[1]++;
|
||||
$i++;
|
||||
}
|
||||
if ($i == $maxI || $stateCount[1] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[2] <= $maxCount) {
|
||||
$stateCount[2]++;
|
||||
$i++;
|
||||
}
|
||||
if ($stateCount[2] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
// Now also count down from center
|
||||
$i = $startI + 1;
|
||||
while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
|
||||
$stateCount[1]++;
|
||||
$i++;
|
||||
}
|
||||
if ($i == $maxI || $stateCount[1] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[2] <= $maxCount) {
|
||||
$stateCount[2]++;
|
||||
$i++;
|
||||
}
|
||||
if ($stateCount[2] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
$stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2];
|
||||
if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
$stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2];
|
||||
if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
return $this->foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $i) : NAN;
|
||||
}
|
||||
return $this->foundPatternCross($stateCount) ? self::centerFromEnd($stateCount, $i) : NAN;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,18 +17,16 @@
|
|||
|
||||
namespace Zxing\Qrcode\Detector;
|
||||
|
||||
use Zxing\DecodeHintType;
|
||||
use Zxing\FormatException;
|
||||
use Zxing\NotFoundException;
|
||||
use Zxing\ResultPoint;
|
||||
use Zxing\ResultPointCallback;
|
||||
use Zxing\Common\BitMatrix;
|
||||
use Zxing\Common\Detector\MathUtils;
|
||||
use Zxing\Common\DetectorResult;
|
||||
use Zxing\Common\GridSampler;
|
||||
use Zxing\Common\PerspectiveTransform;
|
||||
use Zxing\Common\Detector\MathUtils;
|
||||
use Zxing\DecodeHintType;
|
||||
use Zxing\FormatException;
|
||||
use Zxing\NotFoundException;
|
||||
use Zxing\Qrcode\Decoder\Version;
|
||||
|
||||
use Zxing\ResultPoint;
|
||||
use Zxing\ResultPointCallback;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates logic that can detect a QR Code in an image, even if the QR Code
|
||||
|
|
@ -38,383 +36,393 @@ use Zxing\Qrcode\Decoder\Version;
|
|||
*/
|
||||
class Detector
|
||||
{
|
||||
private $resultPointCallback;
|
||||
|
||||
private $image;
|
||||
private $resultPointCallback;
|
||||
public function __construct(private $image)
|
||||
{
|
||||
}
|
||||
|
||||
public function __construct($image)
|
||||
{
|
||||
$this->image = $image;
|
||||
}
|
||||
/**
|
||||
* <p>Detects a QR Code in an image.</p>
|
||||
*
|
||||
* @param array|null optional $hints hints to detector
|
||||
*
|
||||
* @return {@link DetectorResult} encapsulating results of detecting a QR Code
|
||||
* @throws NotFoundException if QR Code cannot be found
|
||||
* @throws FormatException if a QR Code cannot be decoded
|
||||
*/
|
||||
final public function detect($hints = null)
|
||||
{/*Map<DecodeHintType,?>*/
|
||||
|
||||
/**
|
||||
* <p>Detects a QR Code in an image.</p>
|
||||
*
|
||||
* @param hints optional hints to detector
|
||||
*
|
||||
* @return {@link DetectorResult} encapsulating results of detecting a QR Code
|
||||
* @throws NotFoundException if QR Code cannot be found
|
||||
* @throws FormatException if a QR Code cannot be decoded
|
||||
*/
|
||||
public final function detect($hints = null)
|
||||
{/*Map<DecodeHintType,?>*/
|
||||
$resultPointCallback = $hints == null ? null :
|
||||
$hints->get('NEED_RESULT_POINT_CALLBACK');
|
||||
/* resultPointCallback = hints == null ? null :
|
||||
(ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);*/
|
||||
$finder = new FinderPatternFinder($this->image, $resultPointCallback);
|
||||
$info = $finder->find($hints);
|
||||
|
||||
$resultPointCallback = $hints == null ? null :
|
||||
$hints->get('NEED_RESULT_POINT_CALLBACK');
|
||||
/* resultPointCallback = hints == null ? null :
|
||||
(ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);*/
|
||||
$finder = new FinderPatternFinder($this->image, $resultPointCallback);
|
||||
$info = $finder->find($hints);
|
||||
return $this->processFinderPatternInfo($info);
|
||||
}
|
||||
|
||||
return $this->processFinderPatternInfo($info);
|
||||
}
|
||||
final protected function processFinderPatternInfo($info): \Zxing\Common\DetectorResult
|
||||
{
|
||||
$topLeft = $info->getTopLeft();
|
||||
$topRight = $info->getTopRight();
|
||||
$bottomLeft = $info->getBottomLeft();
|
||||
|
||||
protected final function processFinderPatternInfo($info)
|
||||
{
|
||||
$moduleSize = (float)$this->calculateModuleSize($topLeft, $topRight, $bottomLeft);
|
||||
if ($moduleSize < 1.0) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$dimension = (int)self::computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize);
|
||||
$provisionalVersion = \Zxing\Qrcode\Decoder\Version::getProvisionalVersionForDimension($dimension);
|
||||
$modulesBetweenFPCenters = $provisionalVersion->getDimensionForVersion() - 7;
|
||||
|
||||
$topLeft = $info->getTopLeft();
|
||||
$topRight = $info->getTopRight();
|
||||
$bottomLeft = $info->getBottomLeft();
|
||||
|
||||
$moduleSize = (float)$this->calculateModuleSize($topLeft, $topRight, $bottomLeft);
|
||||
if ($moduleSize < 1.0) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$dimension = (int)self::computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize);
|
||||
$provisionalVersion = \Zxing\Qrcode\Decoder\Version::getProvisionalVersionForDimension($dimension);
|
||||
$modulesBetweenFPCenters = $provisionalVersion->getDimensionForVersion() - 7;
|
||||
|
||||
$alignmentPattern = null;
|
||||
// Anything above version 1 has an alignment pattern
|
||||
if (count($provisionalVersion->getAlignmentPatternCenters()) > 0) {
|
||||
$alignmentPattern = null;
|
||||
// Anything above version 1 has an alignment pattern
|
||||
if ((is_countable($provisionalVersion->getAlignmentPatternCenters()) ? count($provisionalVersion->getAlignmentPatternCenters()) : 0) > 0) {
|
||||
|
||||
// Guess where a "bottom right" finder pattern would have been
|
||||
$bottomRightX = $topRight->getX() - $topLeft->getX() + $bottomLeft->getX();
|
||||
$bottomRightY = $topRight->getY() - $topLeft->getY() + $bottomLeft->getY();
|
||||
$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)$modulesBetweenFPCenters;
|
||||
$estAlignmentX = (int)($topLeft->getX() + $correctionToTopLeft * ($bottomRightX - $topLeft->getX()));
|
||||
$estAlignmentY = (int)($topLeft->getY() + $correctionToTopLeft * ($bottomRightY - $topLeft->getY()));
|
||||
// Estimate that alignment pattern is closer by 3 modules
|
||||
// from "bottom right" to known top left location
|
||||
$correctionToTopLeft = 1.0 - 3.0 / (float)$modulesBetweenFPCenters;
|
||||
$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) {//??????????
|
||||
try {
|
||||
$alignmentPattern = $this->findAlignmentInRegion(
|
||||
$moduleSize,
|
||||
$estAlignmentX,
|
||||
$estAlignmentY,
|
||||
(float)$i
|
||||
);
|
||||
break;
|
||||
} catch (NotFoundException $re) {
|
||||
// try next round
|
||||
}
|
||||
}
|
||||
// If we didn't find alignment pattern... well try anyway without it
|
||||
}
|
||||
// Kind of arbitrary -- expand search radius before giving up
|
||||
for ($i = 4; $i <= 16; $i <<= 1) {//??????????
|
||||
try {
|
||||
$alignmentPattern = $this->findAlignmentInRegion(
|
||||
$moduleSize,
|
||||
$estAlignmentX,
|
||||
$estAlignmentY,
|
||||
(float)$i
|
||||
);
|
||||
break;
|
||||
} catch (NotFoundException) {
|
||||
// try next round
|
||||
}
|
||||
}
|
||||
// If we didn't find alignment pattern... well try anyway without it
|
||||
}
|
||||
|
||||
$transform = self::createTransform($topLeft, $topRight, $bottomLeft, $alignmentPattern, $dimension);
|
||||
$transform = self::createTransform($topLeft, $topRight, $bottomLeft, $alignmentPattern, $dimension);
|
||||
|
||||
$bits = self::sampleGrid($this->image, $transform, $dimension);
|
||||
$bits = self::sampleGrid($this->image, $transform, $dimension);
|
||||
|
||||
$points = [];
|
||||
if ($alignmentPattern == null) {
|
||||
$points = [$bottomLeft, $topLeft, $topRight];
|
||||
} else {
|
||||
// die('$points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};');
|
||||
$points = [$bottomLeft, $topLeft, $topRight, $alignmentPattern];
|
||||
}
|
||||
$points = [];
|
||||
if ($alignmentPattern == null) {
|
||||
$points = [$bottomLeft, $topLeft, $topRight];
|
||||
} else {
|
||||
// die('$points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};');
|
||||
$points = [$bottomLeft, $topLeft, $topRight, $alignmentPattern];
|
||||
}
|
||||
|
||||
return new DetectorResult($bits, $points);
|
||||
}
|
||||
return new DetectorResult($bits, $points);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Detects a QR Code in an image.</p>
|
||||
*
|
||||
* @return {@link DetectorResult} encapsulating results of detecting a QR Code
|
||||
* @throws NotFoundException if QR Code cannot be found
|
||||
* @throws FormatException if a QR Code cannot be decoded
|
||||
*/
|
||||
/**
|
||||
* <p>Detects a QR Code in an image.</p>
|
||||
*
|
||||
* @return {@link DetectorResult} encapsulating results of detecting a QR Code
|
||||
* @throws NotFoundException if QR Code cannot be found
|
||||
* @throws FormatException if a QR Code cannot be decoded
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>Computes an average estimated module size based on estimated derived from the positions
|
||||
* of the three finder patterns.</p>
|
||||
*
|
||||
* @param topLeft detected top-left finder pattern center
|
||||
* @param topRight detected top-right finder pattern center
|
||||
* @param bottomLeft detected bottom-left finder pattern center
|
||||
*
|
||||
* @return estimated module size
|
||||
*/
|
||||
protected final function calculateModuleSize($topLeft, $topRight, $bottomLeft)
|
||||
{
|
||||
// Take the average
|
||||
return ($this->calculateModuleSizeOneWay($topLeft, $topRight) +
|
||||
$this->calculateModuleSizeOneWay($topLeft, $bottomLeft)) / 2.0;
|
||||
}
|
||||
/**
|
||||
* <p>Computes an average estimated module size based on estimated derived from the positions
|
||||
* of the three finder patterns.</p>
|
||||
*
|
||||
* @param detected $topLeft top-left finder pattern center
|
||||
* @param detected $topRight top-right finder pattern center
|
||||
* @param detected $bottomLeft bottom-left finder pattern center
|
||||
*
|
||||
* @return estimated module size
|
||||
*/
|
||||
final protected function calculateModuleSize($topLeft, $topRight, $bottomLeft)
|
||||
{
|
||||
// Take the average
|
||||
return ($this->calculateModuleSizeOneWay($topLeft, $topRight) +
|
||||
$this->calculateModuleSizeOneWay($topLeft, $bottomLeft)) / 2.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Estimates module size based on two finder patterns -- it uses
|
||||
* {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the
|
||||
* width of each, measuring along the axis between their centers.</p>
|
||||
*/
|
||||
private function calculateModuleSizeOneWay($pattern, $otherPattern)
|
||||
{
|
||||
$moduleSizeEst1 = $this->sizeOfBlackWhiteBlackRunBothWays($pattern->getX(),
|
||||
(int)$pattern->getY(),
|
||||
(int)$otherPattern->getX(),
|
||||
(int)$otherPattern->getY());
|
||||
$moduleSizeEst2 = $this->sizeOfBlackWhiteBlackRunBothWays((int)$otherPattern->getX(),
|
||||
(int)$otherPattern->getY(),
|
||||
(int)$pattern->getX(),
|
||||
(int)$pattern->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;
|
||||
}
|
||||
/**
|
||||
* <p>Estimates module size based on two finder patterns -- it uses
|
||||
* {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the
|
||||
* width of each, measuring along the axis between their centers.</p>
|
||||
*/
|
||||
private function calculateModuleSizeOneWay($pattern, $otherPattern)
|
||||
{
|
||||
$moduleSizeEst1 = $this->sizeOfBlackWhiteBlackRunBothWays(
|
||||
$pattern->getX(),
|
||||
(int)$pattern->getY(),
|
||||
(int)$otherPattern->getX(),
|
||||
(int)$otherPattern->getY()
|
||||
);
|
||||
$moduleSizeEst2 = $this->sizeOfBlackWhiteBlackRunBothWays(
|
||||
(int)$otherPattern->getX(),
|
||||
(int)$otherPattern->getY(),
|
||||
(int)$pattern->getX(),
|
||||
(int)$pattern->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 {@link #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.</p>
|
||||
*/
|
||||
private function sizeOfBlackWhiteBlackRunBothWays($fromX, $fromY, $toX, $toY)
|
||||
{
|
||||
/**
|
||||
* See {@link #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.</p>
|
||||
*/
|
||||
private function sizeOfBlackWhiteBlackRunBothWays($fromX, $fromY, $toX, $toY)
|
||||
{
|
||||
$result = $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY);
|
||||
|
||||
$result = $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY);
|
||||
// Now count other way -- don't run off image though of course
|
||||
$scale = 1.0;
|
||||
$otherToX = $fromX - ($toX - $fromX);
|
||||
if ($otherToX < 0) {
|
||||
$scale = (float)$fromX / (float)($fromX - $otherToX);
|
||||
$otherToX = 0;
|
||||
} elseif ($otherToX >= $this->image->getWidth()) {
|
||||
$scale = (float)($this->image->getWidth() - 1 - $fromX) / (float)($otherToX - $fromX);
|
||||
$otherToX = $this->image->getWidth() - 1;
|
||||
}
|
||||
$otherToY = (int)($fromY - ($toY - $fromY) * $scale);
|
||||
|
||||
// Now count other way -- don't run off image though of course
|
||||
$scale = 1.0;
|
||||
$otherToX = $fromX - ($toX - $fromX);
|
||||
if ($otherToX < 0) {
|
||||
$scale = (float)$fromX / (float)($fromX - $otherToX);
|
||||
$otherToX = 0;
|
||||
} else if ($otherToX >= $this->image->getWidth()) {
|
||||
$scale = (float)($this->image->getWidth() - 1 - $fromX) / (float)($otherToX - $fromX);
|
||||
$otherToX = $this->image->getWidth() - 1;
|
||||
}
|
||||
$otherToY = (int)($fromY - ($toY - $fromY) * $scale);
|
||||
$scale = 1.0;
|
||||
if ($otherToY < 0) {
|
||||
$scale = (float)$fromY / (float)($fromY - $otherToY);
|
||||
$otherToY = 0;
|
||||
} elseif ($otherToY >= $this->image->getHeight()) {
|
||||
$scale = (float)($this->image->getHeight() - 1 - $fromY) / (float)($otherToY - $fromY);
|
||||
$otherToY = $this->image->getHeight() - 1;
|
||||
}
|
||||
$otherToX = (int)($fromX + ($otherToX - $fromX) * $scale);
|
||||
|
||||
$scale = 1.0;
|
||||
if ($otherToY < 0) {
|
||||
$scale = (float)$fromY / (float)($fromY - $otherToY);
|
||||
$otherToY = 0;
|
||||
} else if ($otherToY >= $this->image->getHeight()) {
|
||||
$scale = (float)($this->image->getHeight() - 1 - $fromY) / (float)($otherToY - $fromY);
|
||||
$otherToY = $this->image->getHeight() - 1;
|
||||
}
|
||||
$otherToX = (int)($fromX + ($otherToX - $fromX) * $scale);
|
||||
$result += $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $otherToX, $otherToY);
|
||||
|
||||
$result += $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $otherToX, $otherToY);
|
||||
// Middle pixel is double-counted this way; subtract 1
|
||||
return $result - 1.0;
|
||||
}
|
||||
|
||||
// Middle pixel is double-counted this way; subtract 1
|
||||
return $result - 1.0;
|
||||
}
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>This is used when figuring out how wide a finder pattern is, when the finder pattern
|
||||
* may be skewed or rotated.</p>
|
||||
*/
|
||||
private function sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY)
|
||||
{
|
||||
// Mild variant of Bresenham's algorithm;
|
||||
// see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
|
||||
$steep = abs($toY - $fromY) > abs($toX - $fromX);
|
||||
if ($steep) {
|
||||
$temp = $fromX;
|
||||
$fromX = $fromY;
|
||||
$fromY = $temp;
|
||||
$temp = $toX;
|
||||
$toX = $toY;
|
||||
$toY = $temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>This is used when figuring out how wide a finder pattern is, when the finder pattern
|
||||
* may be skewed or rotated.</p>
|
||||
*/
|
||||
private function sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY)
|
||||
{
|
||||
// Mild variant of Bresenham's algorithm;
|
||||
// see http://en.wikipedia.org/wiki/Bresenham's_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;
|
||||
|
||||
$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;
|
||||
|
||||
// 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->image->get($realX, $realY)) {
|
||||
if ($state == 2) {
|
||||
return MathUtils::distance($x, $y, $fromX, $fromY);
|
||||
}
|
||||
$state++;
|
||||
}
|
||||
|
||||
// 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->image->get($realX, $realY)) {
|
||||
if ($state == 2) {
|
||||
return MathUtils::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 MathUtils::distance($toX + $xstep, $toY, $fromX, $fromY);
|
||||
}
|
||||
|
||||
$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 MathUtils::distance($toX + $xstep, $toY, $fromX, $fromY);
|
||||
}
|
||||
// else we didn't find even black-white-black; no estimate is really possible
|
||||
return NAN;
|
||||
}
|
||||
|
||||
// else we didn't find even black-white-black; no estimate is really possible
|
||||
return NAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*/
|
||||
private static function computeDimension($topLeft,
|
||||
$topRight,
|
||||
$bottomLeft,
|
||||
$moduleSize)
|
||||
{
|
||||
$tltrCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $topRight) / $moduleSize);
|
||||
$tlblCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $bottomLeft) / $moduleSize);
|
||||
$dimension = (($tltrCentersDimension + $tlblCentersDimension) / 2) + 7;
|
||||
switch ($dimension & 0x03) { // mod 4
|
||||
case 0:
|
||||
$dimension++;
|
||||
break;
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*/
|
||||
private static function computeDimension(
|
||||
$topLeft,
|
||||
$topRight,
|
||||
$bottomLeft,
|
||||
$moduleSize
|
||||
)
|
||||
{
|
||||
$tltrCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $topRight) / $moduleSize);
|
||||
$tlblCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $bottomLeft) / $moduleSize);
|
||||
$dimension = (($tltrCentersDimension + $tlblCentersDimension) / 2) + 7;
|
||||
switch ($dimension & 0x03) { // mod 4
|
||||
case 0:
|
||||
$dimension++;
|
||||
break;
|
||||
// 1? do nothing
|
||||
case 2:
|
||||
$dimension--;
|
||||
break;
|
||||
case 3:
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
case 2:
|
||||
$dimension--;
|
||||
break;
|
||||
case 3:
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
return $dimension;
|
||||
}
|
||||
return $dimension;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Attempts to locate an alignment pattern in a limited region of the image, which is
|
||||
* guessed to contain it. This method uses {@link AlignmentPattern}.</p>
|
||||
*
|
||||
* @param overallEstModuleSize estimated module size so far
|
||||
* @param estAlignmentX x coordinate of center of area probably containing alignment pattern
|
||||
* @param estAlignmentY y coordinate of above
|
||||
* @param allowanceFactor number of pixels in all directions to search from the center
|
||||
*
|
||||
* @return {@link AlignmentPattern} if found, or null otherwise
|
||||
* @throws NotFoundException if an unexpected error occurs during detection
|
||||
*/
|
||||
protected final function findAlignmentInRegion($overallEstModuleSize,
|
||||
$estAlignmentX,
|
||||
$estAlignmentY,
|
||||
$allowanceFactor)
|
||||
{
|
||||
// Look for an alignment pattern (3 modules in size) around where it
|
||||
// should be
|
||||
$allowance = (int)($allowanceFactor * $overallEstModuleSize);
|
||||
$alignmentAreaLeftX = max(0, $estAlignmentX - $allowance);
|
||||
$alignmentAreaRightX = min($this->image->getWidth() - 1, $estAlignmentX + $allowance);
|
||||
if ($alignmentAreaRightX - $alignmentAreaLeftX < $overallEstModuleSize * 3) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
/**
|
||||
* <p>Attempts to locate an alignment pattern in a limited region of the image, which is
|
||||
* guessed to contain it. This method uses {@link AlignmentPattern}.</p>
|
||||
*
|
||||
* @param estimated $overallEstModuleSize module size so far
|
||||
* @param x $estAlignmentX coordinate of center of area probably containing alignment pattern
|
||||
* @param y $estAlignmentY coordinate of above
|
||||
* @param number $allowanceFactor of pixels in all directions to search from the center
|
||||
*
|
||||
* @return {@link AlignmentPattern} if found, or null otherwise
|
||||
* @throws NotFoundException if an unexpected error occurs during detection
|
||||
*/
|
||||
final protected function findAlignmentInRegion(
|
||||
$overallEstModuleSize,
|
||||
$estAlignmentX,
|
||||
$estAlignmentY,
|
||||
$allowanceFactor
|
||||
)
|
||||
{
|
||||
// Look for an alignment pattern (3 modules in size) around where it
|
||||
// should be
|
||||
$allowance = (int)($allowanceFactor * $overallEstModuleSize);
|
||||
$alignmentAreaLeftX = max(0, $estAlignmentX - $allowance);
|
||||
$alignmentAreaRightX = min($this->image->getWidth() - 1, $estAlignmentX + $allowance);
|
||||
if ($alignmentAreaRightX - $alignmentAreaLeftX < $overallEstModuleSize * 3) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
$alignmentAreaTopY = max(0, $estAlignmentY - $allowance);
|
||||
$alignmentAreaBottomY = min($this->image->getHeight() - 1, $estAlignmentY + $allowance);
|
||||
if ($alignmentAreaBottomY - $alignmentAreaTopY < $overallEstModuleSize * 3) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$alignmentAreaTopY = max(0, $estAlignmentY - $allowance);
|
||||
$alignmentAreaBottomY = min($this->image->getHeight() - 1, $estAlignmentY + $allowance);
|
||||
if ($alignmentAreaBottomY - $alignmentAreaTopY < $overallEstModuleSize * 3) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
$alignmentFinder =
|
||||
new AlignmentPatternFinder(
|
||||
$this->image,
|
||||
$alignmentAreaLeftX,
|
||||
$alignmentAreaTopY,
|
||||
$alignmentAreaRightX - $alignmentAreaLeftX,
|
||||
$alignmentAreaBottomY - $alignmentAreaTopY,
|
||||
$overallEstModuleSize,
|
||||
$this->resultPointCallback);
|
||||
$alignmentFinder =
|
||||
new AlignmentPatternFinder(
|
||||
$this->image,
|
||||
$alignmentAreaLeftX,
|
||||
$alignmentAreaTopY,
|
||||
$alignmentAreaRightX - $alignmentAreaLeftX,
|
||||
$alignmentAreaBottomY - $alignmentAreaTopY,
|
||||
$overallEstModuleSize,
|
||||
$this->resultPointCallback
|
||||
);
|
||||
|
||||
return $alignmentFinder->find();
|
||||
}
|
||||
return $alignmentFinder->find();
|
||||
}
|
||||
|
||||
private static function createTransform($topLeft,
|
||||
$topRight,
|
||||
$bottomLeft,
|
||||
$alignmentPattern,
|
||||
$dimension)
|
||||
{
|
||||
$dimMinusThree = (float)$dimension - 3.5;
|
||||
$bottomRightX = 0.0;
|
||||
$bottomRightY = 0.0;
|
||||
$sourceBottomRightX = 0.0;
|
||||
$sourceBottomRightY = 0.0;
|
||||
if ($alignmentPattern != null) {
|
||||
$bottomRightX = $alignmentPattern->getX();
|
||||
$bottomRightY = $alignmentPattern->getY();
|
||||
$sourceBottomRightX = $dimMinusThree - 3.0;
|
||||
$sourceBottomRightY = $sourceBottomRightX;
|
||||
} else {
|
||||
// Don't have an alignment pattern, just make up the bottom-right point
|
||||
$bottomRightX = ($topRight->getX() - $topLeft->getX()) + $bottomLeft->getX();
|
||||
$bottomRightY = ($topRight->getY() - $topLeft->getY()) + $bottomLeft->getY();
|
||||
$sourceBottomRightX = $dimMinusThree;
|
||||
$sourceBottomRightY = $dimMinusThree;
|
||||
}
|
||||
private static function createTransform(
|
||||
$topLeft,
|
||||
$topRight,
|
||||
$bottomLeft,
|
||||
$alignmentPattern,
|
||||
$dimension
|
||||
)
|
||||
{
|
||||
$dimMinusThree = (float)$dimension - 3.5;
|
||||
$bottomRightX = 0.0;
|
||||
$bottomRightY = 0.0;
|
||||
$sourceBottomRightX = 0.0;
|
||||
$sourceBottomRightY = 0.0;
|
||||
if ($alignmentPattern != null) {
|
||||
$bottomRightX = $alignmentPattern->getX();
|
||||
$bottomRightY = $alignmentPattern->getY();
|
||||
$sourceBottomRightX = $dimMinusThree - 3.0;
|
||||
$sourceBottomRightY = $sourceBottomRightX;
|
||||
} else {
|
||||
// Don't have an alignment pattern, just make up the bottom-right point
|
||||
$bottomRightX = ($topRight->getX() - $topLeft->getX()) + $bottomLeft->getX();
|
||||
$bottomRightY = ($topRight->getY() - $topLeft->getY()) + $bottomLeft->getY();
|
||||
$sourceBottomRightX = $dimMinusThree;
|
||||
$sourceBottomRightY = $dimMinusThree;
|
||||
}
|
||||
|
||||
return PerspectiveTransform::quadrilateralToQuadrilateral(
|
||||
3.5,
|
||||
3.5,
|
||||
$dimMinusThree,
|
||||
3.5,
|
||||
$sourceBottomRightX,
|
||||
$sourceBottomRightY,
|
||||
3.5,
|
||||
$dimMinusThree,
|
||||
$topLeft->getX(),
|
||||
$topLeft->getY(),
|
||||
$topRight->getX(),
|
||||
$topRight->getY(),
|
||||
$bottomRightX,
|
||||
$bottomRightY,
|
||||
$bottomLeft->getX(),
|
||||
$bottomLeft->getY());
|
||||
}
|
||||
return PerspectiveTransform::quadrilateralToQuadrilateral(
|
||||
3.5,
|
||||
3.5,
|
||||
$dimMinusThree,
|
||||
3.5,
|
||||
$sourceBottomRightX,
|
||||
$sourceBottomRightY,
|
||||
3.5,
|
||||
$dimMinusThree,
|
||||
$topLeft->getX(),
|
||||
$topLeft->getY(),
|
||||
$topRight->getX(),
|
||||
$topRight->getY(),
|
||||
$bottomRightX,
|
||||
$bottomRightY,
|
||||
$bottomLeft->getX(),
|
||||
$bottomLeft->getY()
|
||||
);
|
||||
}
|
||||
|
||||
private static function sampleGrid($image, $transform,
|
||||
$dimension)
|
||||
{
|
||||
$sampler = GridSampler::getInstance();
|
||||
private static function sampleGrid(
|
||||
$image,
|
||||
$transform,
|
||||
$dimension
|
||||
)
|
||||
{
|
||||
$sampler = GridSampler::getInstance();
|
||||
|
||||
return $sampler->sampleGrid_($image, $dimension, $dimension, $transform);
|
||||
}
|
||||
return $sampler->sampleGrid_($image, $dimension, $dimension, $transform);
|
||||
}
|
||||
|
||||
protected final function getImage()
|
||||
{
|
||||
return $this->image;
|
||||
}
|
||||
final protected function getImage()
|
||||
{
|
||||
return $this->image;
|
||||
}
|
||||
|
||||
protected final function getResultPointCallback()
|
||||
{
|
||||
return $this->resultPointCallback;
|
||||
}
|
||||
final protected function getResultPointCallback()
|
||||
{
|
||||
return $this->resultPointCallback;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,59 +28,54 @@ use Zxing\ResultPoint;
|
|||
*/
|
||||
final class FinderPattern extends ResultPoint
|
||||
{
|
||||
private $estimatedModuleSize;
|
||||
private $count;
|
||||
public function __construct($posX, $posY, private $estimatedModuleSize, private $count = 1)
|
||||
{
|
||||
parent::__construct($posX, $posY);
|
||||
}
|
||||
|
||||
public function __construct($posX, $posY, $estimatedModuleSize, $count = 1)
|
||||
{
|
||||
parent::__construct($posX, $posY);
|
||||
$this->estimatedModuleSize = $estimatedModuleSize;
|
||||
$this->count = $count;
|
||||
}
|
||||
public function getEstimatedModuleSize()
|
||||
{
|
||||
return $this->estimatedModuleSize;
|
||||
}
|
||||
|
||||
public function getEstimatedModuleSize()
|
||||
{
|
||||
return $this->estimatedModuleSize;
|
||||
}
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
/*
|
||||
void incrementCount() {
|
||||
this.count++;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
void incrementCount() {
|
||||
this.count++;
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*/
|
||||
public function aboutEquals($moduleSize, $i, $j)
|
||||
{
|
||||
if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) {
|
||||
$moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize);
|
||||
|
||||
/**
|
||||
* <p>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.</p>
|
||||
*/
|
||||
public function aboutEquals($moduleSize, $i, $j)
|
||||
{
|
||||
if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) {
|
||||
$moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize);
|
||||
return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize;
|
||||
}
|
||||
|
||||
return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Combines this object's current estimate of a finder pattern position and module size
|
||||
* with a new estimate. It returns a new {@code FinderPattern} containing a weighted average
|
||||
* based on count.
|
||||
*/
|
||||
public function combineEstimate($i, $j, $newModuleSize): \Zxing\Qrcode\Detector\FinderPattern
|
||||
{
|
||||
$combinedCount = $this->count + 1;
|
||||
$combinedX = ($this->count * $this->getX() + $j) / $combinedCount;
|
||||
$combinedY = ($this->count * $this->getY() + $i) / $combinedCount;
|
||||
$combinedModuleSize = ($this->count * $this->estimatedModuleSize + $newModuleSize) / $combinedCount;
|
||||
|
||||
/**
|
||||
* Combines this object's current estimate of a finder pattern position and module size
|
||||
* with a new estimate. It returns a new {@code FinderPattern} containing a weighted average
|
||||
* based on count.
|
||||
*/
|
||||
public function combineEstimate($i, $j, $newModuleSize)
|
||||
{
|
||||
$combinedCount = $this->count + 1;
|
||||
$combinedX = ($this->count * $this->getX() + $j) / $combinedCount;
|
||||
$combinedY = ($this->count * $this->getY() + $i) / $combinedCount;
|
||||
$combinedModuleSize = ($this->count * $this->estimatedModuleSize + $newModuleSize) / $combinedCount;
|
||||
|
||||
return new FinderPattern($combinedX, $combinedY, $combinedModuleSize, $combinedCount);
|
||||
}
|
||||
return new FinderPattern($combinedX, $combinedY, $combinedModuleSize, $combinedCount);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -25,29 +25,29 @@ namespace Zxing\Qrcode\Detector;
|
|||
*/
|
||||
final class FinderPatternInfo
|
||||
{
|
||||
private $bottomLeft;
|
||||
private $topLeft;
|
||||
private $topRight;
|
||||
private $bottomLeft;
|
||||
private $topLeft;
|
||||
private $topRight;
|
||||
|
||||
public function __construct($patternCenters)
|
||||
{
|
||||
$this->bottomLeft = $patternCenters[0];
|
||||
$this->topLeft = $patternCenters[1];
|
||||
$this->topRight = $patternCenters[2];
|
||||
}
|
||||
public function __construct($patternCenters)
|
||||
{
|
||||
$this->bottomLeft = $patternCenters[0];
|
||||
$this->topLeft = $patternCenters[1];
|
||||
$this->topRight = $patternCenters[2];
|
||||
}
|
||||
|
||||
public function getBottomLeft()
|
||||
{
|
||||
return $this->bottomLeft;
|
||||
}
|
||||
public function getBottomLeft()
|
||||
{
|
||||
return $this->bottomLeft;
|
||||
}
|
||||
|
||||
public function getTopLeft()
|
||||
{
|
||||
return $this->topLeft;
|
||||
}
|
||||
public function getTopLeft()
|
||||
{
|
||||
return $this->topLeft;
|
||||
}
|
||||
|
||||
public function getTopRight()
|
||||
{
|
||||
return $this->topRight;
|
||||
}
|
||||
public function getTopRight()
|
||||
{
|
||||
return $this->topRight;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ namespace Zxing\Qrcode;
|
|||
|
||||
use Zxing\BinaryBitmap;
|
||||
use Zxing\ChecksumException;
|
||||
use Zxing\Common\BitMatrix;
|
||||
use Zxing\FormatException;
|
||||
use Zxing\NotFoundException;
|
||||
use Zxing\Reader;
|
||||
use Zxing\Result;
|
||||
use Zxing\Common\BitMatrix;
|
||||
use Zxing\Qrcode\Decoder\Decoder;
|
||||
use Zxing\Qrcode\Detector\Detector;
|
||||
use Zxing\Reader;
|
||||
use Zxing\Result;
|
||||
|
||||
/**
|
||||
* This implementation can detect and decode QR Codes in an image.
|
||||
|
|
@ -34,189 +34,188 @@ use Zxing\Qrcode\Detector\Detector;
|
|||
*/
|
||||
class QRCodeReader implements Reader
|
||||
{
|
||||
private static $NO_POINTS = [];
|
||||
private $decoder;
|
||||
private static array $NO_POINTS = [];
|
||||
private readonly \Zxing\Qrcode\Decoder\Decoder $decoder;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->decoder = new Decoder();
|
||||
}
|
||||
public function __construct()
|
||||
{
|
||||
$this->decoder = new Decoder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BinaryBitmap $image
|
||||
* @param null $hints
|
||||
*
|
||||
* @return Result
|
||||
* @throws \Zxing\FormatException
|
||||
* @throws \Zxing\NotFoundException
|
||||
*/
|
||||
public function decode(BinaryBitmap $image, $hints = null)
|
||||
{
|
||||
$decoderResult = null;
|
||||
if ($hints !== null && $hints['PURE_BARCODE']) {
|
||||
$bits = self::extractPureBits($image->getBlackMatrix());
|
||||
$decoderResult = $this->decoder->decode($bits, $hints);
|
||||
$points = self::$NO_POINTS;
|
||||
} else {
|
||||
$detector = new Detector($image->getBlackMatrix());
|
||||
$detectorResult = $detector->detect($hints);
|
||||
/**
|
||||
* @param null $hints
|
||||
*
|
||||
* @return Result
|
||||
* @throws \Zxing\FormatException
|
||||
* @throws \Zxing\NotFoundException
|
||||
*/
|
||||
public function decode(BinaryBitmap $image, $hints = null)
|
||||
{
|
||||
$decoderResult = null;
|
||||
if ($hints !== null && $hints['PURE_BARCODE']) {
|
||||
$bits = self::extractPureBits($image->getBlackMatrix());
|
||||
$decoderResult = $this->decoder->decode($bits, $hints);
|
||||
$points = self::$NO_POINTS;
|
||||
} else {
|
||||
$detector = new Detector($image->getBlackMatrix());
|
||||
$detectorResult = $detector->detect($hints);
|
||||
|
||||
$decoderResult = $this->decoder->decode($detectorResult->getBits(), $hints);
|
||||
$points = $detectorResult->getPoints();
|
||||
}
|
||||
$result = new Result($decoderResult->getText(), $decoderResult->getRawBytes(), $points, 'QR_CODE');//BarcodeFormat.QR_CODE
|
||||
$decoderResult = $this->decoder->decode($detectorResult->getBits(), $hints);
|
||||
$points = $detectorResult->getPoints();
|
||||
}
|
||||
$result = new Result($decoderResult->getText(), $decoderResult->getRawBytes(), $points, 'QR_CODE');//BarcodeFormat.QR_CODE
|
||||
|
||||
$byteSegments = $decoderResult->getByteSegments();
|
||||
if ($byteSegments !== null) {
|
||||
$result->putMetadata('BYTE_SEGMENTS', $byteSegments);//ResultMetadataType.BYTE_SEGMENTS
|
||||
}
|
||||
$ecLevel = $decoderResult->getECLevel();
|
||||
if ($ecLevel !== null) {
|
||||
$result->putMetadata('ERROR_CORRECTION_LEVEL', $ecLevel);//ResultMetadataType.ERROR_CORRECTION_LEVEL
|
||||
}
|
||||
if ($decoderResult->hasStructuredAppend()) {
|
||||
$result->putMetadata(
|
||||
'STRUCTURED_APPEND_SEQUENCE',//ResultMetadataType.STRUCTURED_APPEND_SEQUENCE
|
||||
$decoderResult->getStructuredAppendSequenceNumber()
|
||||
);
|
||||
$result->putMetadata(
|
||||
'STRUCTURED_APPEND_PARITY',//ResultMetadataType.STRUCTURED_APPEND_PARITY
|
||||
$decoderResult->getStructuredAppendParity()
|
||||
);
|
||||
}
|
||||
$byteSegments = $decoderResult->getByteSegments();
|
||||
if ($byteSegments !== null) {
|
||||
$result->putMetadata('BYTE_SEGMENTS', $byteSegments);//ResultMetadataType.BYTE_SEGMENTS
|
||||
}
|
||||
$ecLevel = $decoderResult->getECLevel();
|
||||
if ($ecLevel !== null) {
|
||||
$result->putMetadata('ERROR_CORRECTION_LEVEL', $ecLevel);//ResultMetadataType.ERROR_CORRECTION_LEVEL
|
||||
}
|
||||
if ($decoderResult->hasStructuredAppend()) {
|
||||
$result->putMetadata(
|
||||
'STRUCTURED_APPEND_SEQUENCE',//ResultMetadataType.STRUCTURED_APPEND_SEQUENCE
|
||||
$decoderResult->getStructuredAppendSequenceNumber()
|
||||
);
|
||||
$result->putMetadata(
|
||||
'STRUCTURED_APPEND_PARITY',//ResultMetadataType.STRUCTURED_APPEND_PARITY
|
||||
$decoderResult->getStructuredAppendParity()
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates and decodes a QR code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the QR code
|
||||
* @throws NotFoundException if a QR code cannot be found
|
||||
* @throws FormatException if a QR code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
/**
|
||||
* Locates and decodes a QR code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the QR code
|
||||
* @throws NotFoundException if a QR code cannot be found
|
||||
* @throws FormatException if a QR code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
|
||||
/**
|
||||
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
||||
* which contains only an unrotated, unskewed, image of a code, with some white border
|
||||
* around it. This is a specialized method that works exceptionally fast in this special
|
||||
* case.
|
||||
*
|
||||
* @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)
|
||||
*/
|
||||
private static function extractPureBits(BitMatrix $image)
|
||||
{
|
||||
$leftTopBlack = $image->getTopLeftOnBit();
|
||||
$rightBottomBlack = $image->getBottomRightOnBit();
|
||||
if ($leftTopBlack === null || $rightBottomBlack == null) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
/**
|
||||
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
||||
* which contains only an unrotated, unskewed, image of a code, with some white border
|
||||
* around it. This is a specialized method that works exceptionally fast in this special
|
||||
* case.
|
||||
*
|
||||
* @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)
|
||||
*/
|
||||
private static function extractPureBits(BitMatrix $image)
|
||||
{
|
||||
$leftTopBlack = $image->getTopLeftOnBit();
|
||||
$rightBottomBlack = $image->getBottomRightOnBit();
|
||||
if ($leftTopBlack === null || $rightBottomBlack == null) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
$moduleSize = self::moduleSize($leftTopBlack, $image);
|
||||
$moduleSize = self::moduleSize($leftTopBlack, $image);
|
||||
|
||||
$top = $leftTopBlack[1];
|
||||
$bottom = $rightBottomBlack[1];
|
||||
$left = $leftTopBlack[0];
|
||||
$right = $rightBottomBlack[0];
|
||||
$top = $leftTopBlack[1];
|
||||
$bottom = $rightBottomBlack[1];
|
||||
$left = $leftTopBlack[0];
|
||||
$right = $rightBottomBlack[0];
|
||||
|
||||
// Sanity check!
|
||||
if ($left >= $right || $top >= $bottom) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
// Sanity check!
|
||||
if ($left >= $right || $top >= $bottom) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
if ($bottom - $top != $right - $left) {
|
||||
// Special case, where bottom-right module wasn't black so we found something else in the last row
|
||||
// Assume it's a square, so use height as the width
|
||||
$right = $left + ($bottom - $top);
|
||||
}
|
||||
if ($bottom - $top != $right - $left) {
|
||||
// Special case, where bottom-right module wasn't black so we found something else in the last row
|
||||
// Assume it's a square, so use height as the width
|
||||
$right = $left + ($bottom - $top);
|
||||
}
|
||||
|
||||
$matrixWidth = round(($right - $left + 1) / $moduleSize);
|
||||
$matrixHeight = round(($bottom - $top + 1) / $moduleSize);
|
||||
if ($matrixWidth <= 0 || $matrixHeight <= 0) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
if ($matrixHeight != $matrixWidth) {
|
||||
// Only possibly decode square regions
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$matrixWidth = round(($right - $left + 1) / $moduleSize);
|
||||
$matrixHeight = round(($bottom - $top + 1) / $moduleSize);
|
||||
if ($matrixWidth <= 0 || $matrixHeight <= 0) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
if ($matrixHeight != $matrixWidth) {
|
||||
// Only possibly decode square regions
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
// Push in the "border" by half the module width so that we start
|
||||
// sampling in the middle of the module. Just in case the image is a
|
||||
// little off, this will help recover.
|
||||
$nudge = (int)($moduleSize / 2.0);// $nudge = (int) ($moduleSize / 2.0f);
|
||||
$top += $nudge;
|
||||
$left += $nudge;
|
||||
// Push in the "border" by half the module width so that we start
|
||||
// sampling in the middle of the module. Just in case the image is a
|
||||
// little off, this will help recover.
|
||||
$nudge = (int)($moduleSize / 2.0);// $nudge = (int) ($moduleSize / 2.0f);
|
||||
$top += $nudge;
|
||||
$left += $nudge;
|
||||
|
||||
// But careful that this does not sample off the edge
|
||||
// "right" is the farthest-right valid pixel location -- right+1 is not necessarily
|
||||
// This is positive by how much the inner x loop below would be too large
|
||||
$nudgedTooFarRight = $left + (int)(($matrixWidth - 1) * $moduleSize) - $right;
|
||||
if ($nudgedTooFarRight > 0) {
|
||||
if ($nudgedTooFarRight > $nudge) {
|
||||
// Neither way fits; abort
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$left -= $nudgedTooFarRight;
|
||||
}
|
||||
// See logic above
|
||||
$nudgedTooFarDown = $top + (int)(($matrixHeight - 1) * $moduleSize) - $bottom;
|
||||
if ($nudgedTooFarDown > 0) {
|
||||
if ($nudgedTooFarDown > $nudge) {
|
||||
// Neither way fits; abort
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$top -= $nudgedTooFarDown;
|
||||
}
|
||||
// But careful that this does not sample off the edge
|
||||
// "right" is the farthest-right valid pixel location -- right+1 is not necessarily
|
||||
// This is positive by how much the inner x loop below would be too large
|
||||
$nudgedTooFarRight = $left + (int)(($matrixWidth - 1) * $moduleSize) - $right;
|
||||
if ($nudgedTooFarRight > 0) {
|
||||
if ($nudgedTooFarRight > $nudge) {
|
||||
// Neither way fits; abort
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$left -= $nudgedTooFarRight;
|
||||
}
|
||||
// See logic above
|
||||
$nudgedTooFarDown = $top + (int)(($matrixHeight - 1) * $moduleSize) - $bottom;
|
||||
if ($nudgedTooFarDown > 0) {
|
||||
if ($nudgedTooFarDown > $nudge) {
|
||||
// Neither way fits; abort
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$top -= $nudgedTooFarDown;
|
||||
}
|
||||
|
||||
// Now just read off the bits
|
||||
$bits = new BitMatrix($matrixWidth, $matrixHeight);
|
||||
for ($y = 0; $y < $matrixHeight; $y++) {
|
||||
$iOffset = $top + (int)($y * $moduleSize);
|
||||
for ($x = 0; $x < $matrixWidth; $x++) {
|
||||
if ($image->get($left + (int)($x * $moduleSize), $iOffset)) {
|
||||
$bits->set($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now just read off the bits
|
||||
$bits = new BitMatrix($matrixWidth, $matrixHeight);
|
||||
for ($y = 0; $y < $matrixHeight; $y++) {
|
||||
$iOffset = $top + (int)($y * $moduleSize);
|
||||
for ($x = 0; $x < $matrixWidth; $x++) {
|
||||
if ($image->get($left + (int)($x * $moduleSize), $iOffset)) {
|
||||
$bits->set($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $bits;
|
||||
}
|
||||
return $bits;
|
||||
}
|
||||
|
||||
private static function moduleSize($leftTopBlack, BitMatrix $image)
|
||||
{
|
||||
$height = $image->getHeight();
|
||||
$width = $image->getWidth();
|
||||
$x = $leftTopBlack[0];
|
||||
$y = $leftTopBlack[1];
|
||||
/*$x = $leftTopBlack[0];
|
||||
$y = $leftTopBlack[1];*/
|
||||
$inBlack = true;
|
||||
$transitions = 0;
|
||||
while ($x < $width && $y < $height) {
|
||||
if ($inBlack != $image->get($x, $y)) {
|
||||
if (++$transitions == 5) {
|
||||
break;
|
||||
}
|
||||
$inBlack = !$inBlack;
|
||||
}
|
||||
$x++;
|
||||
$y++;
|
||||
}
|
||||
if ($x == $width || $y == $height) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
private static function moduleSize($leftTopBlack, BitMatrix $image)
|
||||
{
|
||||
$height = $image->getHeight();
|
||||
$width = $image->getWidth();
|
||||
$x = $leftTopBlack[0];
|
||||
$y = $leftTopBlack[1];
|
||||
/*$x = $leftTopBlack[0];
|
||||
$y = $leftTopBlack[1];*/
|
||||
$inBlack = true;
|
||||
$transitions = 0;
|
||||
while ($x < $width && $y < $height) {
|
||||
if ($inBlack != $image->get($x, $y)) {
|
||||
if (++$transitions == 5) {
|
||||
break;
|
||||
}
|
||||
$inBlack = !$inBlack;
|
||||
}
|
||||
$x++;
|
||||
$y++;
|
||||
}
|
||||
if ($x == $width || $y == $height) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
return ($x - $leftTopBlack[0]) / 7.0; //return ($x - $leftTopBlack[0]) / 7.0f;
|
||||
}
|
||||
return ($x - $leftTopBlack[0]) / 7.0; //return ($x - $leftTopBlack[0]) / 7.0f;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
public function reset(): void
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
protected final function getDecoder()
|
||||
{
|
||||
return $this->decoder;
|
||||
}
|
||||
final protected function getDecoder()
|
||||
{
|
||||
return $this->decoder;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,284 +26,294 @@ namespace Zxing;
|
|||
*/
|
||||
final class RGBLuminanceSource extends LuminanceSource
|
||||
{
|
||||
public $luminances;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
private $left;
|
||||
private $top;
|
||||
private $pixels;
|
||||
public $luminances;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
/**
|
||||
* @var mixed|int
|
||||
*/
|
||||
private $left;
|
||||
/**
|
||||
* @var mixed|int
|
||||
*/
|
||||
private $top;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
private $pixels;
|
||||
|
||||
|
||||
public function __construct(
|
||||
$pixels,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left = null,
|
||||
$top = null,
|
||||
$width = null,
|
||||
$height = null
|
||||
) {
|
||||
if (!$left && !$top && !$width && !$height) {
|
||||
$this->RGBLuminanceSource_($pixels, $dataWidth, $dataHeight);
|
||||
public function __construct(
|
||||
$pixels,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left = null,
|
||||
$top = null,
|
||||
$width = null,
|
||||
$height = null
|
||||
) {
|
||||
if (!$left && !$top && !$width && !$height) {
|
||||
$this->RGBLuminanceSource_($pixels, $dataWidth, $dataHeight);
|
||||
|
||||
return;
|
||||
}
|
||||
parent::__construct($width, $height);
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
$this->luminances = $pixels;
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
}
|
||||
return;
|
||||
}
|
||||
parent::__construct($width, $height);
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
$this->luminances = $pixels;
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
}
|
||||
|
||||
public function RGBLuminanceSource_($width, $height, $pixels)
|
||||
{
|
||||
parent::__construct($width, $height);
|
||||
public function RGBLuminanceSource_($width, $height, $pixels): void
|
||||
{
|
||||
parent::__construct($width, $height);
|
||||
|
||||
$this->dataWidth = $width;
|
||||
$this->dataHeight = $height;
|
||||
$this->left = 0;
|
||||
$this->top = 0;
|
||||
$this->pixels = $pixels;
|
||||
$this->dataWidth = $width;
|
||||
$this->dataHeight = $height;
|
||||
$this->left = 0;
|
||||
$this->top = 0;
|
||||
$this->pixels = $pixels;
|
||||
|
||||
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
$this->luminances = [];
|
||||
//$this->luminances = $this->grayScaleToBitmap($this->grayscale());
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
$this->luminances = [];
|
||||
//$this->luminances = $this->grayScaleToBitmap($this->grayscale());
|
||||
|
||||
foreach ($pixels as $key => $pixel) {
|
||||
$r = $pixel['red'];
|
||||
$g = $pixel['green'];
|
||||
$b = $pixel['blue'];
|
||||
foreach ($pixels as $key => $pixel) {
|
||||
$r = $pixel['red'];
|
||||
$g = $pixel['green'];
|
||||
$b = $pixel['blue'];
|
||||
|
||||
/* if (($pixel & 0xFF000000) == 0) {
|
||||
$pixel = 0xFFFFFFFF; // = white
|
||||
}
|
||||
/* if (($pixel & 0xFF000000) == 0) {
|
||||
$pixel = 0xFFFFFFFF; // = white
|
||||
}
|
||||
|
||||
// .229R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC)
|
||||
// .229R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC)
|
||||
|
||||
$this->luminances[$key] =
|
||||
(306 * (($pixel >> 16) & 0xFF) +
|
||||
601 * (($pixel >> 8) & 0xFF) +
|
||||
117 * ($pixel & 0xFF) +
|
||||
0x200) >> 10;
|
||||
$this->luminances[$key] =
|
||||
(306 * (($pixel >> 16) & 0xFF) +
|
||||
601 * (($pixel >> 8) & 0xFF) +
|
||||
117 * ($pixel & 0xFF) +
|
||||
0x200) >> 10;
|
||||
|
||||
*/
|
||||
//$r = ($pixel >> 16) & 0xff;
|
||||
//$g = ($pixel >> 8) & 0xff;
|
||||
//$b = $pixel & 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
*/
|
||||
//$r = ($pixel >> 16) & 0xff;
|
||||
//$g = ($pixel >> 8) & 0xff;
|
||||
//$b = $pixel & 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[$key] = $r;//(($r + 128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[$key] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$offset = $y * $width;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$pixel = $pixels[$offset + $x];
|
||||
$r = ($pixel >> 16) & 0xff;
|
||||
$g = ($pixel >> 8) & 0xff;
|
||||
$b = $pixel & 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[$key] = $r;//(($r + 128) % 256) - 128;
|
||||
} else {
|
||||
$this->luminances[(int)($offset + $x)] = (($r+128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[$key] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$offset = $y * $width;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$pixel = $pixels[$offset + $x];
|
||||
$r = ($pixel >> 16) & 0xff;
|
||||
$g = ($pixel >> 8) & 0xff;
|
||||
$b = $pixel & 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[(int)($offset + $x)] = (($r+128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[(int)($offset + $x)] = (((($r + 2 * $g + $b) / 4)+128)%256) - 128;
|
||||
}
|
||||
$this->luminances[(int)($offset + $x)] = (((($r + 2 * $g + $b) / 4)+128)%256) - 128;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
//}
|
||||
// $this->luminances = $this->grayScaleToBitmap($this->luminances);
|
||||
}
|
||||
*/
|
||||
//}
|
||||
// $this->luminances = $this->grayScaleToBitmap($this->luminances);
|
||||
}
|
||||
|
||||
}
|
||||
public function grayscale()
|
||||
{
|
||||
$width = $this->dataWidth;
|
||||
$height = $this->dataHeight;
|
||||
|
||||
public function grayscale()
|
||||
{
|
||||
$width = $this->dataWidth;
|
||||
$height = $this->dataHeight;
|
||||
$ret = fill_array(0, $width * $height, 0);
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$gray = $this->getPixel($x, $y, $width, $height);
|
||||
|
||||
$ret = fill_array(0, $width * $height, 0);
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$gray = $this->getPixel($x, $y, $width, $height);
|
||||
$ret[$x + $y * $width] = $gray;
|
||||
}
|
||||
}
|
||||
|
||||
$ret[$x + $y * $width] = $gray;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
public function getPixel($x, $y, $width, $height)
|
||||
{
|
||||
$image = $this->pixels;
|
||||
if ($width < $x) {
|
||||
die('error');
|
||||
}
|
||||
if ($height < $y) {
|
||||
die('error');
|
||||
}
|
||||
$point = ($x) + ($y * $width);
|
||||
|
||||
public function getPixel($x, $y, $width, $height)
|
||||
{
|
||||
$image = $this->pixels;
|
||||
if ($width < $x) {
|
||||
die('error');
|
||||
}
|
||||
if ($height < $y) {
|
||||
die('error');
|
||||
}
|
||||
$point = ($x) + ($y * $width);
|
||||
$r = $image[$point]['red'];//($image[$point] >> 16) & 0xff;
|
||||
$g = $image[$point]['green'];//($image[$point] >> 8) & 0xff;
|
||||
$b = $image[$point]['blue'];//$image[$point] & 0xff;
|
||||
|
||||
$r = $image[$point]['red'];//($image[$point] >> 16) & 0xff;
|
||||
$g = $image[$point]['green'];//($image[$point] >> 8) & 0xff;
|
||||
$b = $image[$point]['blue'];//$image[$point] & 0xff;
|
||||
|
||||
$p = (int)(($r * 33 + $g * 34 + $b * 33) / 100);
|
||||
$p = (int)(($r * 33 + $g * 34 + $b * 33) / 100);
|
||||
|
||||
|
||||
return $p;
|
||||
return $p;
|
||||
}
|
||||
|
||||
}
|
||||
public function grayScaleToBitmap($grayScale)
|
||||
{
|
||||
$middle = $this->getMiddleBrightnessPerArea($grayScale);
|
||||
$sqrtNumArea = is_countable($middle) ? count($middle) : 0;
|
||||
$areaWidth = floor($this->dataWidth / $sqrtNumArea);
|
||||
$areaHeight = floor($this->dataHeight / $sqrtNumArea);
|
||||
$bitmap = fill_array(0, $this->dataWidth * $this->dataHeight, 0);
|
||||
|
||||
public function grayScaleToBitmap($grayScale)
|
||||
{
|
||||
$middle = $this->getMiddleBrightnessPerArea($grayScale);
|
||||
$sqrtNumArea = count($middle);
|
||||
$areaWidth = floor($this->dataWidth / $sqrtNumArea);
|
||||
$areaHeight = floor($this->dataHeight / $sqrtNumArea);
|
||||
$bitmap = fill_array(0, $this->dataWidth * $this->dataHeight, 0);
|
||||
for ($ay = 0; $ay < $sqrtNumArea; $ay++) {
|
||||
for ($ax = 0; $ax < $sqrtNumArea; $ax++) {
|
||||
for ($dy = 0; $dy < $areaHeight; $dy++) {
|
||||
for ($dx = 0; $dx < $areaWidth; $dx++) {
|
||||
$bitmap[(int)($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)] = ($grayScale[(int)($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)] < $middle[$ax][$ay]) ? 0 : 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ($ay = 0; $ay < $sqrtNumArea; $ay++) {
|
||||
for ($ax = 0; $ax < $sqrtNumArea; $ax++) {
|
||||
for ($dy = 0; $dy < $areaHeight; $dy++) {
|
||||
for ($dx = 0; $dx < $areaWidth; $dx++) {
|
||||
$bitmap[(int)($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)] = ($grayScale[(int)($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)] < $middle[$ax][$ay]) ? 0 : 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $bitmap;
|
||||
}
|
||||
|
||||
return $bitmap;
|
||||
}
|
||||
public function getMiddleBrightnessPerArea($image)
|
||||
{
|
||||
$numSqrtArea = 4;
|
||||
//obtain middle brightness((min + max) / 2) per area
|
||||
$areaWidth = floor($this->dataWidth / $numSqrtArea);
|
||||
$areaHeight = floor($this->dataHeight / $numSqrtArea);
|
||||
$minmax = fill_array(0, $numSqrtArea, 0);
|
||||
for ($i = 0; $i < $numSqrtArea; $i++) {
|
||||
$minmax[$i] = fill_array(0, $numSqrtArea, 0);
|
||||
for ($i2 = 0; $i2 < $numSqrtArea; $i2++) {
|
||||
$minmax[$i][$i2] = [0, 0];
|
||||
}
|
||||
}
|
||||
for ($ay = 0; $ay < $numSqrtArea; $ay++) {
|
||||
for ($ax = 0; $ax < $numSqrtArea; $ax++) {
|
||||
$minmax[$ax][$ay][0] = 0xFF;
|
||||
for ($dy = 0; $dy < $areaHeight; $dy++) {
|
||||
for ($dx = 0; $dx < $areaWidth; $dx++) {
|
||||
$target = $image[(int)($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)];
|
||||
if ($target < $minmax[$ax][$ay][0]) {
|
||||
$minmax[$ax][$ay][0] = $target;
|
||||
}
|
||||
if ($target > $minmax[$ax][$ay][1]) {
|
||||
$minmax[$ax][$ay][1] = $target;
|
||||
}
|
||||
}
|
||||
}
|
||||
//minmax[ax][ay][0] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2;
|
||||
}
|
||||
}
|
||||
$middle = [];
|
||||
for ($i3 = 0; $i3 < $numSqrtArea; $i3++) {
|
||||
$middle[$i3] = [];
|
||||
}
|
||||
for ($ay = 0; $ay < $numSqrtArea; $ay++) {
|
||||
for ($ax = 0; $ax < $numSqrtArea; $ax++) {
|
||||
$middle[$ax][$ay] = floor(($minmax[$ax][$ay][0] + $minmax[$ax][$ay][1]) / 2);
|
||||
//Console.out.print(middle[ax][ay] + ",");
|
||||
}
|
||||
//Console.out.println("");
|
||||
}
|
||||
|
||||
public function getMiddleBrightnessPerArea($image)
|
||||
{
|
||||
$numSqrtArea = 4;
|
||||
//obtain middle brightness((min + max) / 2) per area
|
||||
$areaWidth = floor($this->dataWidth / $numSqrtArea);
|
||||
$areaHeight = floor($this->dataHeight / $numSqrtArea);
|
||||
$minmax = fill_array(0, $numSqrtArea, 0);
|
||||
for ($i = 0; $i < $numSqrtArea; $i++) {
|
||||
$minmax[$i] = fill_array(0, $numSqrtArea, 0);
|
||||
for ($i2 = 0; $i2 < $numSqrtArea; $i2++) {
|
||||
$minmax[$i][$i2] = [0, 0];
|
||||
}
|
||||
}
|
||||
for ($ay = 0; $ay < $numSqrtArea; $ay++) {
|
||||
for ($ax = 0; $ax < $numSqrtArea; $ax++) {
|
||||
$minmax[$ax][$ay][0] = 0xFF;
|
||||
for ($dy = 0; $dy < $areaHeight; $dy++) {
|
||||
for ($dx = 0; $dx < $areaWidth; $dx++) {
|
||||
$target = $image[(int)($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)];
|
||||
if ($target < $minmax[$ax][$ay][0])
|
||||
$minmax[$ax][$ay][0] = $target;
|
||||
if ($target > $minmax[$ax][$ay][1])
|
||||
$minmax[$ax][$ay][1] = $target;
|
||||
}
|
||||
}
|
||||
//minmax[ax][ay][0] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2;
|
||||
}
|
||||
}
|
||||
$middle = [];
|
||||
for ($i3 = 0; $i3 < $numSqrtArea; $i3++) {
|
||||
$middle[$i3] = [];
|
||||
}
|
||||
for ($ay = 0; $ay < $numSqrtArea; $ay++) {
|
||||
for ($ax = 0; $ax < $numSqrtArea; $ax++) {
|
||||
$middle[$ax][$ay] = floor(($minmax[$ax][$ay][0] + $minmax[$ax][$ay][1]) / 2);
|
||||
//Console.out.print(middle[ax][ay] + ",");
|
||||
}
|
||||
//Console.out.println("");
|
||||
}
|
||||
//Console.out.println("");
|
||||
|
||||
//Console.out.println("");
|
||||
return $middle;
|
||||
}
|
||||
|
||||
return $middle;
|
||||
}
|
||||
//@Override
|
||||
public function getRow($y, $row = null)
|
||||
{
|
||||
if ($y < 0 || $y >= $this->getHeight()) {
|
||||
throw new \InvalidArgumentException("Requested row is outside the image: " + \Y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || (is_countable($row) ? count($row) : 0) < $width) {
|
||||
$row = [];
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->luminances, $offset, $row, 0, $width);
|
||||
|
||||
//@Override
|
||||
public function getRow($y, $row = null)
|
||||
{
|
||||
if ($y < 0 || $y >= $this->getHeight()) {
|
||||
throw new \InvalidArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || count($row) < $width) {
|
||||
$row = [];
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->luminances, $offset, $row, 0, $width);
|
||||
return $row;
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
//@Override
|
||||
public function getMatrix()
|
||||
{
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
|
||||
//@Override
|
||||
public function getMatrix()
|
||||
{
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->luminances;
|
||||
}
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->luminances;
|
||||
}
|
||||
$area = $width * $height;
|
||||
$matrix = [];
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
|
||||
$area = $width * $height;
|
||||
$matrix = [];
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
return $matrix;
|
||||
}
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$rgb = $this->luminances;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$rgb = $this->luminances;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
return $matrix;
|
||||
}
|
||||
//@Override
|
||||
public function isCropSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function isCropSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function crop($left, $top, $width, $height)
|
||||
{
|
||||
return new RGBLuminanceSource($this->luminances,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height);
|
||||
}
|
||||
//@Override
|
||||
public function crop($left, $top, $width, $height): \Zxing\RGBLuminanceSource
|
||||
{
|
||||
return new RGBLuminanceSource(
|
||||
$this->luminances,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ namespace Zxing;
|
|||
|
||||
interface Reader
|
||||
{
|
||||
public function decode(BinaryBitmap $image);
|
||||
public function decode(BinaryBitmap $image);
|
||||
|
||||
public function reset();
|
||||
public function reset();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,25 +26,24 @@ namespace Zxing;
|
|||
*/
|
||||
abstract class ReaderException extends \Exception
|
||||
{
|
||||
|
||||
// disable stack traces when not running inside test units
|
||||
//protected static $isStackTrace = System.getProperty("surefire.test.class.path") != null;
|
||||
protected static $isStackTrace = false;
|
||||
//protected static $isStackTrace = System.getProperty("surefire.test.class.path") != null;
|
||||
protected static bool $isStackTrace = false;
|
||||
|
||||
function ReaderException($cause = null)
|
||||
{
|
||||
if ($cause) {
|
||||
parent::__construct($cause);
|
||||
}
|
||||
}
|
||||
public function ReaderException($cause = null): void
|
||||
{
|
||||
if ($cause) {
|
||||
parent::__construct($cause);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Prevent stack traces from being taken
|
||||
// srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
|
||||
// This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
|
||||
//@Override
|
||||
public final function fillInStackTrace()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// Prevent stack traces from being taken
|
||||
// srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
|
||||
// This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
|
||||
//@Override
|
||||
final public function fillInStackTrace()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,112 +24,106 @@ namespace Zxing;
|
|||
*/
|
||||
final class Result
|
||||
{
|
||||
private $text;
|
||||
private $rawBytes;
|
||||
private $resultPoints;
|
||||
private $format;
|
||||
private $resultMetadata;
|
||||
private $timestamp;
|
||||
/**
|
||||
* @var mixed[]|mixed
|
||||
*/
|
||||
private $resultMetadata = null;
|
||||
private $timestamp;
|
||||
|
||||
public function __construct(
|
||||
$text,
|
||||
$rawBytes,
|
||||
$resultPoints,
|
||||
$format,
|
||||
$timestamp = ''
|
||||
) {
|
||||
public function __construct(
|
||||
private $text,
|
||||
private $rawBytes,
|
||||
private $resultPoints,
|
||||
private $format,
|
||||
$timestamp = ''
|
||||
) {
|
||||
$this->timestamp = $timestamp ?: time();
|
||||
}
|
||||
|
||||
$this->text = $text;
|
||||
$this->rawBytes = $rawBytes;
|
||||
$this->resultPoints = $resultPoints;
|
||||
$this->format = $format;
|
||||
$this->resultMetadata = null;
|
||||
$this->timestamp = $timestamp ?: time();
|
||||
}
|
||||
/**
|
||||
* @return raw text encoded by the barcode
|
||||
*/
|
||||
public function getText()
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw text encoded by the barcode
|
||||
*/
|
||||
public function getText()
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
/**
|
||||
* @return raw bytes encoded by the barcode, if applicable, otherwise {@code null}
|
||||
*/
|
||||
public function getRawBytes()
|
||||
{
|
||||
return $this->rawBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw bytes encoded by the barcode, if applicable, otherwise {@code null}
|
||||
*/
|
||||
public function getRawBytes()
|
||||
{
|
||||
return $this->rawBytes;
|
||||
}
|
||||
/**
|
||||
* @return points related to the barcode in the image. These are typically points
|
||||
* identifying finder patterns or the corners of the barcode. The exact meaning is
|
||||
* specific to the type of barcode that was decoded.
|
||||
*/
|
||||
public function getResultPoints()
|
||||
{
|
||||
return $this->resultPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return points related to the barcode in the image. These are typically points
|
||||
* identifying finder patterns or the corners of the barcode. The exact meaning is
|
||||
* specific to the type of barcode that was decoded.
|
||||
*/
|
||||
public function getResultPoints()
|
||||
{
|
||||
return $this->resultPoints;
|
||||
}
|
||||
/**
|
||||
* @return {@link BarcodeFormat} representing the format of the barcode that was decoded
|
||||
*/
|
||||
public function getBarcodeFormat()
|
||||
{
|
||||
return $this->format;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link BarcodeFormat} representing the format of the barcode that was decoded
|
||||
*/
|
||||
public function getBarcodeFormat()
|
||||
{
|
||||
return $this->format;
|
||||
}
|
||||
/**
|
||||
* @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be
|
||||
* {@code null}. This contains optional metadata about what was detected about the barcode,
|
||||
* like orientation.
|
||||
*/
|
||||
public function getResultMetadata()
|
||||
{
|
||||
return $this->resultMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be
|
||||
* {@code null}. This contains optional metadata about what was detected about the barcode,
|
||||
* like orientation.
|
||||
*/
|
||||
public function getResultMetadata()
|
||||
{
|
||||
return $this->resultMetadata;
|
||||
}
|
||||
public function putMetadata($type, $value): void
|
||||
{
|
||||
$resultMetadata = [];
|
||||
if ($this->resultMetadata === null) {
|
||||
$this->resultMetadata = [];
|
||||
}
|
||||
$resultMetadata[$type] = $value;
|
||||
}
|
||||
|
||||
public function putMetadata($type, $value)
|
||||
{
|
||||
if ($this->resultMetadata === null) {
|
||||
$this->resultMetadata = [];
|
||||
}
|
||||
$resultMetadata[$type] = $value;
|
||||
}
|
||||
public function putAllMetadata($metadata): void
|
||||
{
|
||||
if ($metadata !== null) {
|
||||
if ($this->resultMetadata === null) {
|
||||
$this->resultMetadata = $metadata;
|
||||
} else {
|
||||
$this->resultMetadata = array_merge($this->resultMetadata, $metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function putAllMetadata($metadata)
|
||||
{
|
||||
if ($metadata !== null) {
|
||||
if ($this->resultMetadata === null) {
|
||||
$this->resultMetadata = $metadata;
|
||||
} else {
|
||||
$this->resultMetadata = array_merge($this->resultMetadata, $metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function addResultPoints($newPoints): void
|
||||
{
|
||||
$oldPoints = $this->resultPoints;
|
||||
if ($oldPoints === null) {
|
||||
$this->resultPoints = $newPoints;
|
||||
} elseif ($newPoints !== null && (is_countable($newPoints) ? count($newPoints) : 0) > 0) {
|
||||
$allPoints = fill_array(0, (is_countable($oldPoints) ? count($oldPoints) : 0) + (is_countable($newPoints) ? count($newPoints) : 0), 0);
|
||||
$allPoints = arraycopy($oldPoints, 0, $allPoints, 0, is_countable($oldPoints) ? count($oldPoints) : 0);
|
||||
$allPoints = arraycopy($newPoints, 0, $allPoints, is_countable($oldPoints) ? count($oldPoints) : 0, is_countable($newPoints) ? count($newPoints) : 0);
|
||||
$this->resultPoints = $allPoints;
|
||||
}
|
||||
}
|
||||
|
||||
public function addResultPoints($newPoints)
|
||||
{
|
||||
$oldPoints = $this->resultPoints;
|
||||
if ($oldPoints === null) {
|
||||
$this->resultPoints = $newPoints;
|
||||
} else if ($newPoints !== null && count($newPoints) > 0) {
|
||||
$allPoints = fill_array(0, count($oldPoints) + count($newPoints), 0);
|
||||
$allPoints = arraycopy($oldPoints, 0, $allPoints, 0, count($oldPoints));
|
||||
$allPoints = arraycopy($newPoints, 0, $allPoints, count($oldPoints), count($newPoints));
|
||||
$this->resultPoints = $allPoints;
|
||||
}
|
||||
}
|
||||
public function getTimestamp()
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
public function getTimestamp()
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
public function toString()
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,129 +27,131 @@ use Zxing\Common\Detector\MathUtils;
|
|||
*/
|
||||
class ResultPoint
|
||||
{
|
||||
private $x;
|
||||
private $y;
|
||||
private float $x;
|
||||
private float $y;
|
||||
|
||||
public function __construct($x, $y)
|
||||
{
|
||||
$this->x = (float)($x);
|
||||
$this->y = (float)($y);
|
||||
}
|
||||
public function __construct($x, $y)
|
||||
{
|
||||
$this->x = (float)($x);
|
||||
$this->y = (float)($y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 patterns array of three {@code ResultPoint} to order
|
||||
*/
|
||||
public static function orderBestPatterns($patterns)
|
||||
{
|
||||
/**
|
||||
* 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 array $patterns of three {@code ResultPoint} to order
|
||||
*/
|
||||
public static function orderBestPatterns($patterns)
|
||||
{
|
||||
|
||||
// Find distances between pattern centers
|
||||
$zeroOneDistance = self::distance($patterns[0], $patterns[1]);
|
||||
$oneTwoDistance = self::distance($patterns[1], $patterns[2]);
|
||||
$zeroTwoDistance = self::distance($patterns[0], $patterns[2]);
|
||||
$zeroOneDistance = self::distance($patterns[0], $patterns[1]);
|
||||
$oneTwoDistance = self::distance($patterns[1], $patterns[2]);
|
||||
$zeroTwoDistance = self::distance($patterns[0], $patterns[2]);
|
||||
|
||||
$pointA = '';
|
||||
$pointB = '';
|
||||
$pointC = '';
|
||||
// Assume one closest to other two is B; A and C will just be guesses at first
|
||||
if ($oneTwoDistance >= $zeroOneDistance && $oneTwoDistance >= $zeroTwoDistance) {
|
||||
$pointB = $patterns[0];
|
||||
$pointA = $patterns[1];
|
||||
$pointC = $patterns[2];
|
||||
} else if ($zeroTwoDistance >= $oneTwoDistance && $zeroTwoDistance >= $zeroOneDistance) {
|
||||
$pointB = $patterns[1];
|
||||
$pointA = $patterns[0];
|
||||
$pointC = $patterns[2];
|
||||
} else {
|
||||
$pointB = $patterns[2];
|
||||
$pointA = $patterns[0];
|
||||
$pointC = $patterns[1];
|
||||
}
|
||||
$pointA = '';
|
||||
$pointB = '';
|
||||
$pointC = '';
|
||||
// Assume one closest to other two is B; A and C will just be guesses at first
|
||||
if ($oneTwoDistance >= $zeroOneDistance && $oneTwoDistance >= $zeroTwoDistance) {
|
||||
$pointB = $patterns[0];
|
||||
$pointA = $patterns[1];
|
||||
$pointC = $patterns[2];
|
||||
} elseif ($zeroTwoDistance >= $oneTwoDistance && $zeroTwoDistance >= $zeroOneDistance) {
|
||||
$pointB = $patterns[1];
|
||||
$pointA = $patterns[0];
|
||||
$pointC = $patterns[2];
|
||||
} else {
|
||||
$pointB = $patterns[2];
|
||||
$pointA = $patterns[0];
|
||||
$pointC = $patterns[1];
|
||||
}
|
||||
|
||||
// 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 (self::crossProductZ($pointA, $pointB, $pointC) < 0.0) {
|
||||
$temp = $pointA;
|
||||
$pointA = $pointC;
|
||||
$pointC = $temp;
|
||||
}
|
||||
// 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 (self::crossProductZ($pointA, $pointB, $pointC) < 0.0) {
|
||||
$temp = $pointA;
|
||||
$pointA = $pointC;
|
||||
$pointC = $temp;
|
||||
}
|
||||
|
||||
$patterns[0] = $pointA;
|
||||
$patterns[1] = $pointB;
|
||||
$patterns[2] = $pointC;
|
||||
$patterns[0] = $pointA;
|
||||
$patterns[1] = $pointB;
|
||||
$patterns[2] = $pointC;
|
||||
|
||||
return $patterns;
|
||||
}
|
||||
return $patterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pattern1 first pattern
|
||||
* @param pattern2 second pattern
|
||||
*
|
||||
* @return distance between two points
|
||||
*/
|
||||
public static function distance($pattern1, $pattern2)
|
||||
{
|
||||
return MathUtils::distance($pattern1->x, $pattern1->y, $pattern2->x, $pattern2->y);
|
||||
}
|
||||
/**
|
||||
* @param first $pattern1 pattern
|
||||
* @param second $pattern2 pattern
|
||||
*
|
||||
* @return distance between two points
|
||||
*/
|
||||
public static function distance($pattern1, $pattern2)
|
||||
{
|
||||
return MathUtils::distance($pattern1->x, $pattern1->y, $pattern2->x, $pattern2->y);
|
||||
}
|
||||
|
||||
//@Override
|
||||
//@Override
|
||||
|
||||
/**
|
||||
* Returns the z component of the cross product between vectors BC and BA.
|
||||
*/
|
||||
private static function crossProductZ($pointA,
|
||||
$pointB,
|
||||
$pointC)
|
||||
{
|
||||
$bX = $pointB->x;
|
||||
$bY = $pointB->y;
|
||||
/**
|
||||
* Returns the z component of the cross product between vectors BC and BA.
|
||||
*/
|
||||
private static function crossProductZ(
|
||||
$pointA,
|
||||
$pointB,
|
||||
$pointC
|
||||
)
|
||||
{
|
||||
$bX = $pointB->x;
|
||||
$bY = $pointB->y;
|
||||
|
||||
return (($pointC->x - $bX) * ($pointA->y - $bY)) - (($pointC->y - $bY) * ($pointA->x - $bX));
|
||||
}
|
||||
return (($pointC->x - $bX) * ($pointA->y - $bY)) - (($pointC->y - $bY) * ($pointA->x - $bX));
|
||||
}
|
||||
|
||||
//@Override
|
||||
//@Override
|
||||
|
||||
public final function getX()
|
||||
{
|
||||
return (float)($this->x);
|
||||
}
|
||||
final public function getX()
|
||||
{
|
||||
return (float)($this->x);
|
||||
}
|
||||
|
||||
//@Override
|
||||
//@Override
|
||||
|
||||
public final function getY()
|
||||
{
|
||||
return (float)($this->y);
|
||||
}
|
||||
final public function getY()
|
||||
{
|
||||
return (float)($this->y);
|
||||
}
|
||||
|
||||
public final function equals($other)
|
||||
{
|
||||
if ($other instanceof ResultPoint) {
|
||||
$otherPoint = $other;
|
||||
final public function equals($other)
|
||||
{
|
||||
if ($other instanceof ResultPoint) {
|
||||
$otherPoint = $other;
|
||||
|
||||
return $this->x == $otherPoint->x && $this->y == $otherPoint->y;
|
||||
}
|
||||
return $this->x == $otherPoint->x && $this->y == $otherPoint->y;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final function hashCode()
|
||||
{
|
||||
return 31 * floatToIntBits($this->x) + floatToIntBits($this->y);
|
||||
}
|
||||
final public function hashCode()
|
||||
{
|
||||
return 31 * floatToIntBits($this->x) + floatToIntBits($this->y);
|
||||
}
|
||||
|
||||
public final function toString()
|
||||
{
|
||||
$result = '';
|
||||
$result .= ('(');
|
||||
$result .= ($this->x);
|
||||
$result .= (',');
|
||||
$result .= ($this->y);
|
||||
$result .= (')');
|
||||
final public function toString()
|
||||
{
|
||||
$result = '';
|
||||
$result .= ('(');
|
||||
$result .= ($this->x);
|
||||
$result .= (',');
|
||||
$result .= ($this->y);
|
||||
$result .= (')');
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/autoload.php" backupGlobals="false" backupStaticAttributes="false" colors="true" verbose="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage>
|
||||
<include>
|
||||
<directory suffix=".php">tests/</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="Tests">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="./vendor/autoload.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">lib</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
<testsuite name="Tests">
|
||||
<directory suffix="Test.php">tests</directory>
|
||||
</testsuite>
|
||||
</phpunit>
|
||||
|
|
|
|||
64
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/rector.php
vendored
Normal file
64
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/rector.php
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Config\RectorConfig;
|
||||
use Rector\Nette\Set\NetteSetList;
|
||||
use Rector\Set\ValueObject\SetList;
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Symfony\Set\SymfonySetList;
|
||||
use Rector\Doctrine\Set\DoctrineSetList;
|
||||
use Rector\Set\ValueObject\LevelSetList;
|
||||
use Rector\Symfony\Set\SensiolabsSetList;
|
||||
use Rector\TypeDeclaration\Rector\Property\PropertyTypeDeclarationRector;
|
||||
use Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnNewRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationRector;
|
||||
use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByParentCallTypeRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\ArrayShapeFromConstantArrayReturnRector;
|
||||
|
||||
|
||||
return static function (RectorConfig $rectorConfig): void {
|
||||
$rectorConfig->paths([
|
||||
__DIR__ . '/lib'
|
||||
]);
|
||||
|
||||
$parameters = $rectorConfig->parameters();
|
||||
$parameters->set(
|
||||
Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER,
|
||||
__DIR__ . '/var/cache/dev/App_KernelDevDebugContainer.xml'
|
||||
);
|
||||
|
||||
$rectorConfig->sets([
|
||||
DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES,
|
||||
SymfonySetList::ANNOTATIONS_TO_ATTRIBUTES,
|
||||
NetteSetList::ANNOTATIONS_TO_ATTRIBUTES,
|
||||
SensiolabsSetList::FRAMEWORK_EXTRA_61,
|
||||
SymfonySetList::SYMFONY_60,
|
||||
LevelSetList::UP_TO_PHP_81
|
||||
]);
|
||||
|
||||
// register a single rule
|
||||
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
|
||||
$rectorConfig->rule(AddReturnTypeDeclarationRector::class);
|
||||
$rectorConfig->rules([
|
||||
AddVoidReturnTypeWhereNoReturnRector::class,
|
||||
ArrayShapeFromConstantArrayReturnRector::class,
|
||||
ParamTypeByMethodCallTypeRector::class,
|
||||
ParamTypeByParentCallTypeRector::class,
|
||||
PropertyTypeDeclarationRector::class,
|
||||
ReturnTypeFromReturnNewRector::class,
|
||||
// ReturnTypeFromStrictBoolReturnExprRector::class,
|
||||
// ReturnTypeFromStrictNativeFuncCallRector::class,
|
||||
// ReturnTypeFromStrictNewArrayRector::class,
|
||||
TypedPropertyFromAssignsRector::class
|
||||
]);
|
||||
|
||||
// define sets of rules
|
||||
// $rectorConfig->sets([
|
||||
// LevelSetList::UP_TO_PHP_80
|
||||
// ]);
|
||||
};
|
||||
|
|
@ -7,12 +7,18 @@ use Zxing\QrReader;
|
|||
|
||||
class QrReaderTest extends TestCase
|
||||
{
|
||||
public function testText1()
|
||||
{
|
||||
$image = __DIR__ . "/qrcodes/hello_world.png";
|
||||
|
||||
public function testText1()
|
||||
{
|
||||
$image = __DIR__ . "/qrcodes/hello_world.png";
|
||||
$qrcode = new QrReader($image);
|
||||
$this->assertSame("Hello world!", $qrcode->text());
|
||||
}
|
||||
|
||||
$qrcode = new QrReader($image);
|
||||
$this->assertSame("Hello world!", $qrcode->text());
|
||||
}
|
||||
public function testNoText()
|
||||
{
|
||||
$image = __DIR__ . "/qrcodes/empty.png";
|
||||
$qrcode = new QrReader($image);
|
||||
$this->assertSame(false, $qrcode->text());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__ . '/../lib/common/customFunctions.php';
|
||||
|
||||
spl_autoload_register(function (string $class) {
|
||||
$path = explode('\\', $class);
|
||||
$className = array_pop($path);
|
||||
array_shift($path);
|
||||
$path = strtolower(implode('/', $path));
|
||||
$path = __DIR__ . '/../lib/' . $path . '/' . $className . '.php';
|
||||
|
||||
if (file_exists($path)) {
|
||||
require_once $path;
|
||||
}
|
||||
});
|
||||
BIN
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/tests/qrcodes/empty.png
vendored
Normal file
BIN
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/tests/qrcodes/empty.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -1,9 +1,9 @@
|
|||
# PHP Enum implementation inspired from SplEnum
|
||||
|
||||
[](https://travis-ci.org/myclabs/php-enum)
|
||||
[![GitHub Actions][GA Image]][GA Link]
|
||||
[](https://packagist.org/packages/myclabs/php-enum)
|
||||
[](https://packagist.org/packages/myclabs/php-enum)
|
||||
[](https://shepherd.dev/github/myclabs/php-enum)
|
||||
[![Psalm Shepherd][Shepherd Image]][Shepherd Link]
|
||||
|
||||
Maintenance for this project is [supported via Tidelift](https://tidelift.com/subscription/pkg/packagist-myclabs-php-enum?utm_source=packagist-myclabs-php-enum&utm_medium=referral&utm_campaign=readme).
|
||||
|
||||
|
|
@ -130,9 +130,65 @@ final class Action extends Enum
|
|||
}
|
||||
```
|
||||
|
||||
## Native enums and migration
|
||||
Native enum arrived to PHP in version 8.1: https://www.php.net/enumerations
|
||||
If your project is running PHP 8.1+ or your library has it as a minimum requirement you should use it instead of this library.
|
||||
|
||||
When migrating from `myclabs/php-enum`, the effort should be small if the usage was in the recommended way:
|
||||
- private constants
|
||||
- final classes
|
||||
- no method overridden
|
||||
|
||||
Changes for migration:
|
||||
- Class definition should be changed from
|
||||
```php
|
||||
/**
|
||||
* @method static Action VIEW()
|
||||
* @method static Action EDIT()
|
||||
*/
|
||||
final class Action extends Enum
|
||||
{
|
||||
private const VIEW = 'view';
|
||||
private const EDIT = 'edit';
|
||||
}
|
||||
```
|
||||
to
|
||||
```php
|
||||
enum Action: string
|
||||
{
|
||||
case VIEW = 'view';
|
||||
case EDIT = 'edit';
|
||||
}
|
||||
```
|
||||
All places where the class was used as a type will continue to work.
|
||||
|
||||
Usages and the change needed:
|
||||
|
||||
| Operation | myclabs/php-enum | native enum |
|
||||
|----------------------------------------------------------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Obtain an instance will change from | `$enumCase = Action::VIEW()` | `$enumCase = Action::VIEW` |
|
||||
| Create an enum from a backed value | `$enumCase = new Action('view')` | `$enumCase = Action::from('view')` |
|
||||
| Get the backed value of the enum instance | `$enumCase->getValue()` | `$enumCase->value` |
|
||||
| Compare two enum instances | `$enumCase1 == $enumCase2` <br/> or <br/> `$enumCase1->equals($enumCase2)` | `$enumCase1 === $enumCase2` |
|
||||
| Get the key/name of the enum instance | `$enumCase->getKey()` | `$enumCase->name` |
|
||||
| Get a list of all the possible instances of the enum | `Action::values()` | `Action::cases()` |
|
||||
| Get a map of possible instances of the enum mapped by name | `Action::values()` | `array_combine(array_map(fn($case) => $case->name, Action::cases()), Action::cases())` <br/> or <br/> `(new ReflectionEnum(Action::class))->getConstants()` |
|
||||
| Get a list of all possible names of the enum | `Action::keys()` | `array_map(fn($case) => $case->name, Action::cases())` |
|
||||
| Get a list of all possible backed values of the enum | `Action::toArray()` | `array_map(fn($case) => $case->value, Action::cases())` |
|
||||
| Get a map of possible backed values of the enum mapped by name | `Action::toArray()` | `array_combine(array_map(fn($case) => $case->name, Action::cases()), array_map(fn($case) => $case->value, Action::cases()))` <br/> or <br/> `array_map(fn($case) => $case->value, (new ReflectionEnum(Action::class))->getConstants()))` |
|
||||
|
||||
## Related projects
|
||||
|
||||
- [PHP 8.1+ native enum](https://www.php.net/enumerations)
|
||||
- [Doctrine enum mapping](https://github.com/acelaya/doctrine-enum-type)
|
||||
- [Symfony ParamConverter integration](https://github.com/Ex3v/MyCLabsEnumParamConverter)
|
||||
- [PHPStan integration](https://github.com/timeweb/phpstan-enum)
|
||||
- [Yii2 enum mapping](https://github.com/KartaviK/yii2-enum)
|
||||
|
||||
|
||||
[GA Image]: https://github.com/myclabs/php-enum/workflows/CI/badge.svg
|
||||
|
||||
[GA Link]: https://github.com/myclabs/php-enum/actions?query=workflow%3A%22CI%22+branch%3Amaster
|
||||
|
||||
[Shepherd Image]: https://shepherd.dev/github/myclabs/php-enum/coverage.svg
|
||||
|
||||
[Shepherd Link]: https://shepherd.dev/github/myclabs/php-enum
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@
|
|||
"autoload": {
|
||||
"psr-4": {
|
||||
"MyCLabs\\Enum\\": "src/"
|
||||
}
|
||||
},
|
||||
"classmap": [
|
||||
"stubs/Stringable.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
totallyTyped="true"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
<directory name="static-analysis" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
<directory name="src/PHPUnit" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
|
||||
<issueHandlers>
|
||||
<MixedAssignment errorLevel="info" />
|
||||
|
||||
<ImpureStaticProperty>
|
||||
<!-- self::$... usages in Enum are used to populate an internal cache, and cause no side-effects -->
|
||||
<errorLevel type="suppress">
|
||||
<file name="src/Enum.php"/>
|
||||
</errorLevel>
|
||||
</ImpureStaticProperty>
|
||||
|
||||
<ImpureVariable>
|
||||
<!-- $this usages in Enum point themselves to an immutable instance -->
|
||||
<errorLevel type="suppress">
|
||||
<file name="src/Enum.php"/>
|
||||
</errorLevel>
|
||||
</ImpureVariable>
|
||||
</issueHandlers>
|
||||
</psalm>
|
||||
|
|
@ -19,7 +19,7 @@ namespace MyCLabs\Enum;
|
|||
* @psalm-immutable
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
abstract class Enum implements \JsonSerializable
|
||||
abstract class Enum implements \JsonSerializable, \Stringable
|
||||
{
|
||||
/**
|
||||
* Enum value
|
||||
|
|
|
|||
11
htdocs/core/modules/facture/doc/vendor/myclabs/php-enum/stubs/Stringable.php
vendored
Normal file
11
htdocs/core/modules/facture/doc/vendor/myclabs/php-enum/stubs/Stringable.php
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
if (\PHP_VERSION_ID < 80000 && !interface_exists('Stringable')) {
|
||||
interface Stringable
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ class TimezoneTransformer extends Transformer
|
|||
return $dateTime->format('\G\M\TP');
|
||||
}
|
||||
|
||||
return sprintf('GMT%s%d', ($offset >= 0 ? '+' : ''), $offset / 100);
|
||||
return sprintf('GMT%s%d', $offset >= 0 ? '+' : '', $offset / 100);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ abstract class IntlDateFormatter
|
|||
*
|
||||
* @throws MethodNotImplementedException
|
||||
*/
|
||||
public function formatObject(object $object, $format = null, string $locale = null)
|
||||
public static function formatObject(object $object, $format = null, string $locale = null)
|
||||
{
|
||||
throw new MethodNotImplementedException(__METHOD__);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ final class Intl
|
|||
*/
|
||||
public static function getIcuStubVersion(): string
|
||||
{
|
||||
return '71.1';
|
||||
return '72.1';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if ('cli' !== \PHP_SAPI) {
|
||||
throw new Exception('This script must be run from the command line.');
|
||||
}
|
||||
|
||||
define('LINE_WIDTH', 75);
|
||||
|
||||
define('LINE', str_repeat('-', LINE_WIDTH)."\n");
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ use Symfony\Component\Intl\Intl;
|
|||
use Symfony\Component\Intl\Locale;
|
||||
use Symfony\Component\Intl\Util\GitRepository;
|
||||
|
||||
if ('cli' !== \PHP_SAPI) {
|
||||
throw new Exception('This script must be run from the command line.');
|
||||
}
|
||||
|
||||
require_once __DIR__.'/common.php';
|
||||
require_once __DIR__.'/autoload.php';
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ return [
|
|||
],
|
||||
'AWG' => [
|
||||
0 => 'AWG',
|
||||
1 => 'আরুবা গিল্ডার',
|
||||
1 => 'আরুবা ফ্লোরিন',
|
||||
],
|
||||
'AZM' => [
|
||||
0 => 'AZM',
|
||||
|
|
@ -132,7 +132,7 @@ return [
|
|||
],
|
||||
'BOB' => [
|
||||
0 => 'BOB',
|
||||
1 => 'বলিভিয়ানো',
|
||||
1 => 'বলিভিয়ান বলিভিয়ানো',
|
||||
],
|
||||
'BOP' => [
|
||||
0 => 'BOP',
|
||||
|
|
@ -412,7 +412,7 @@ return [
|
|||
],
|
||||
'GYD' => [
|
||||
0 => 'GYD',
|
||||
1 => 'গাইয়েনা ডলার',
|
||||
1 => 'গায়ানিজ ডলার',
|
||||
],
|
||||
'HKD' => [
|
||||
0 => 'HK$',
|
||||
|
|
@ -452,7 +452,7 @@ return [
|
|||
],
|
||||
'ILS' => [
|
||||
0 => '₪',
|
||||
1 => 'ইস্রাইলি নতুন শেকেল',
|
||||
1 => 'ইসরায়েলি নতুন শেকেল',
|
||||
],
|
||||
'INR' => [
|
||||
0 => '₹',
|
||||
|
|
@ -500,7 +500,7 @@ return [
|
|||
],
|
||||
'KMF' => [
|
||||
0 => 'KMF',
|
||||
1 => 'কম্বোরো ফ্রাঙ্ক',
|
||||
1 => 'কমোরিয়ান ফ্রাঙ্ক',
|
||||
],
|
||||
'KPW' => [
|
||||
0 => 'KPW',
|
||||
|
|
@ -640,7 +640,7 @@ return [
|
|||
],
|
||||
'MWK' => [
|
||||
0 => 'MWK',
|
||||
1 => 'মালাউইয়ান কওয়াচ',
|
||||
1 => 'মালাউইয়ান কোয়াচা',
|
||||
],
|
||||
'MXN' => [
|
||||
0 => 'MX$',
|
||||
|
|
@ -708,7 +708,7 @@ return [
|
|||
],
|
||||
'PAB' => [
|
||||
0 => 'PAB',
|
||||
1 => 'পানামা বেলবোয়া',
|
||||
1 => 'পানামানিয়ান বালবোয়া',
|
||||
],
|
||||
'PEI' => [
|
||||
0 => 'PEI',
|
||||
|
|
@ -836,7 +836,7 @@ return [
|
|||
],
|
||||
'SRD' => [
|
||||
0 => 'SRD',
|
||||
1 => 'সুরিনাম ডলার',
|
||||
1 => 'সুরিনামিজ ডলার',
|
||||
],
|
||||
'SRG' => [
|
||||
0 => 'SRG',
|
||||
|
|
|
|||
42
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/bn_IN.php
vendored
Normal file
42
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/bn_IN.php
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Names' => [
|
||||
'ANG' => [
|
||||
0 => 'ANG',
|
||||
1 => 'নেদারল্যান্ডস অ্যান্টিলিয়ান গিল্ডার',
|
||||
],
|
||||
'AWG' => [
|
||||
0 => 'AWG',
|
||||
1 => 'আরুবান গিল্ডার',
|
||||
],
|
||||
'BMD' => [
|
||||
0 => 'BMD',
|
||||
1 => 'বারমুডান ডলার',
|
||||
],
|
||||
'GTQ' => [
|
||||
0 => 'GTQ',
|
||||
1 => 'গুয়াতেমালান কেৎসাল',
|
||||
],
|
||||
'HNL' => [
|
||||
0 => 'HNL',
|
||||
1 => 'হন্ডুরান লেম্পিরা',
|
||||
],
|
||||
'HTG' => [
|
||||
0 => 'HTG',
|
||||
1 => 'হাইতিয়ান গুর্দ',
|
||||
],
|
||||
'MXN' => [
|
||||
0 => 'MX$',
|
||||
1 => 'মেক্সিকান পেসো',
|
||||
],
|
||||
'USD' => [
|
||||
0 => '$',
|
||||
1 => 'মার্কিন ডলার',
|
||||
],
|
||||
'XCD' => [
|
||||
0 => 'EC$',
|
||||
1 => 'পূর্ব ক্যারিবিয়ান ডলার',
|
||||
],
|
||||
],
|
||||
];
|
||||
630
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/cv.php
vendored
Normal file
630
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/cv.php
vendored
Normal file
|
|
@ -0,0 +1,630 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Names' => [
|
||||
'AED' => [
|
||||
0 => 'AED',
|
||||
1 => 'АПЭ дирхамӗ',
|
||||
],
|
||||
'AFN' => [
|
||||
0 => 'AFN',
|
||||
1 => 'афганийӗ',
|
||||
],
|
||||
'ALL' => [
|
||||
0 => 'ALL',
|
||||
1 => 'Албани лекӗ',
|
||||
],
|
||||
'AMD' => [
|
||||
0 => 'AMD',
|
||||
1 => 'Армяни драмӗ',
|
||||
],
|
||||
'ANG' => [
|
||||
0 => 'ANG',
|
||||
1 => 'Нидерланд Антиллиан гульденӗ',
|
||||
],
|
||||
'AOA' => [
|
||||
0 => 'AOA',
|
||||
1 => 'Ангола кванзӗ',
|
||||
],
|
||||
'ARS' => [
|
||||
0 => 'ARS',
|
||||
1 => 'Аргентина песийӗ',
|
||||
],
|
||||
'AUD' => [
|
||||
0 => 'A$',
|
||||
1 => 'Австрали долларӗ',
|
||||
],
|
||||
'AWG' => [
|
||||
0 => 'AWG',
|
||||
1 => 'Аруба флоринӗ',
|
||||
],
|
||||
'AZN' => [
|
||||
0 => 'AZN',
|
||||
1 => 'Азербайджан маначӗ',
|
||||
],
|
||||
'BAM' => [
|
||||
0 => 'BAM',
|
||||
1 => 'Боснипе Герцеговина конвертланакан марки',
|
||||
],
|
||||
'BBD' => [
|
||||
0 => 'BBD',
|
||||
1 => 'Барбадос долларӗ',
|
||||
],
|
||||
'BDT' => [
|
||||
0 => 'BDT',
|
||||
1 => 'Бангладеш таки',
|
||||
],
|
||||
'BGN' => [
|
||||
0 => 'BGN',
|
||||
1 => 'Болгари левӗ',
|
||||
],
|
||||
'BHD' => [
|
||||
0 => 'BHD',
|
||||
1 => 'Бахрейн динарӗ',
|
||||
],
|
||||
'BIF' => [
|
||||
0 => 'BIF',
|
||||
1 => 'Бурунди франкӗ',
|
||||
],
|
||||
'BMD' => [
|
||||
0 => 'BMD',
|
||||
1 => 'Бермуд долларӗ',
|
||||
],
|
||||
'BND' => [
|
||||
0 => 'BND',
|
||||
1 => 'Бруней долларӗ',
|
||||
],
|
||||
'BOB' => [
|
||||
0 => 'BOB',
|
||||
1 => 'Боливи боливианӗ',
|
||||
],
|
||||
'BRL' => [
|
||||
0 => 'R$',
|
||||
1 => 'Бразили реалӗ',
|
||||
],
|
||||
'BSD' => [
|
||||
0 => 'BSD',
|
||||
1 => 'Багам долларӗ',
|
||||
],
|
||||
'BTN' => [
|
||||
0 => 'BTN',
|
||||
1 => 'Бутан нгултрумӗ',
|
||||
],
|
||||
'BWP' => [
|
||||
0 => 'BWP',
|
||||
1 => 'Ботсвана пули',
|
||||
],
|
||||
'BYN' => [
|
||||
0 => 'BYN',
|
||||
1 => 'Беларуҫ тенкӗ',
|
||||
],
|
||||
'BZD' => [
|
||||
0 => 'BZD',
|
||||
1 => 'Белиз долларӗ',
|
||||
],
|
||||
'CAD' => [
|
||||
0 => 'CA$',
|
||||
1 => 'Канада долларӗ',
|
||||
],
|
||||
'CDF' => [
|
||||
0 => 'CDF',
|
||||
1 => 'Конголези франкӗ',
|
||||
],
|
||||
'CHF' => [
|
||||
0 => 'CHF',
|
||||
1 => 'Швейцари франкӗ',
|
||||
],
|
||||
'CLP' => [
|
||||
0 => 'CLP',
|
||||
1 => 'Чили песийӗ',
|
||||
],
|
||||
'CNH' => [
|
||||
0 => 'CNH',
|
||||
1 => 'Китай офшор юанӗ',
|
||||
],
|
||||
'CNY' => [
|
||||
0 => 'CN¥',
|
||||
1 => 'Китай юанӗ',
|
||||
],
|
||||
'COP' => [
|
||||
0 => 'COP',
|
||||
1 => 'Колумби песийӗ',
|
||||
],
|
||||
'CRC' => [
|
||||
0 => 'CRC',
|
||||
1 => 'Коста-Рика колонӗ',
|
||||
],
|
||||
'CUC' => [
|
||||
0 => 'CUC',
|
||||
1 => 'Куба конвертланакан песийӗ',
|
||||
],
|
||||
'CUP' => [
|
||||
0 => 'CUP',
|
||||
1 => 'Куба песийӗ',
|
||||
],
|
||||
'CVE' => [
|
||||
0 => 'CVE',
|
||||
1 => 'Кабо-Верде эскудӗ',
|
||||
],
|
||||
'CZK' => [
|
||||
0 => 'CZK',
|
||||
1 => 'Чехи кронӗ',
|
||||
],
|
||||
'DJF' => [
|
||||
0 => 'DJF',
|
||||
1 => 'Джибути франкӗ',
|
||||
],
|
||||
'DKK' => [
|
||||
0 => 'DKK',
|
||||
1 => 'Дани кронӗ',
|
||||
],
|
||||
'DOP' => [
|
||||
0 => 'DOP',
|
||||
1 => 'Доминикан песийӗ',
|
||||
],
|
||||
'DZD' => [
|
||||
0 => 'DZD',
|
||||
1 => 'Алжир динарӗ',
|
||||
],
|
||||
'EGP' => [
|
||||
0 => 'EGP',
|
||||
1 => 'Египет фунчӗ',
|
||||
],
|
||||
'ERN' => [
|
||||
0 => 'ERN',
|
||||
1 => 'Эритрей накфӗ',
|
||||
],
|
||||
'ETB' => [
|
||||
0 => 'ETB',
|
||||
1 => 'Эфиопи бырӗ',
|
||||
],
|
||||
'EUR' => [
|
||||
0 => '€',
|
||||
1 => 'евро',
|
||||
],
|
||||
'FJD' => [
|
||||
0 => 'FJD',
|
||||
1 => 'Фиджи долларӗ',
|
||||
],
|
||||
'FKP' => [
|
||||
0 => 'FKP',
|
||||
1 => 'Факланд утравӗсен фунчӗ',
|
||||
],
|
||||
'GBP' => [
|
||||
0 => '£',
|
||||
1 => 'Британи фунчӗ',
|
||||
],
|
||||
'GEL' => [
|
||||
0 => 'GEL',
|
||||
1 => 'Грузи ларийӗ',
|
||||
],
|
||||
'GHS' => [
|
||||
0 => 'GHS',
|
||||
1 => 'Гана седийӗ',
|
||||
],
|
||||
'GIP' => [
|
||||
0 => 'GIP',
|
||||
1 => 'Гибралтар фунчӗ',
|
||||
],
|
||||
'GMD' => [
|
||||
0 => 'GMD',
|
||||
1 => 'Гамби даласийӗ',
|
||||
],
|
||||
'GNF' => [
|
||||
0 => 'GNF',
|
||||
1 => 'Гвиней франкӗ',
|
||||
],
|
||||
'GTQ' => [
|
||||
0 => 'GTQ',
|
||||
1 => 'Гватемала кетсалӗ',
|
||||
],
|
||||
'GYD' => [
|
||||
0 => 'GYD',
|
||||
1 => 'Гайана долларӗ',
|
||||
],
|
||||
'HKD' => [
|
||||
0 => 'HK$',
|
||||
1 => 'Гонконг долларӗ',
|
||||
],
|
||||
'HNL' => [
|
||||
0 => 'HNL',
|
||||
1 => 'Гондурас лемпирӗ',
|
||||
],
|
||||
'HRK' => [
|
||||
0 => 'HRK',
|
||||
1 => 'Хорвати куни',
|
||||
],
|
||||
'HTG' => [
|
||||
0 => 'HTG',
|
||||
1 => 'Гаити гурдӗ',
|
||||
],
|
||||
'HUF' => [
|
||||
0 => 'HUF',
|
||||
1 => 'Венгри форинчӗ',
|
||||
],
|
||||
'IDR' => [
|
||||
0 => 'IDR',
|
||||
1 => 'Индонези рупийӗ',
|
||||
],
|
||||
'ILS' => [
|
||||
0 => '₪',
|
||||
1 => 'Ҫӗнӗ Израиль шекелӗ',
|
||||
],
|
||||
'INR' => [
|
||||
0 => '₹',
|
||||
1 => 'Инди рупийӗ',
|
||||
],
|
||||
'IQD' => [
|
||||
0 => 'IQD',
|
||||
1 => 'Ирак динарӗ',
|
||||
],
|
||||
'IRR' => [
|
||||
0 => 'IRR',
|
||||
1 => 'Иран риалӗ',
|
||||
],
|
||||
'ISK' => [
|
||||
0 => 'ISK',
|
||||
1 => 'Исланди кронӗ',
|
||||
],
|
||||
'JMD' => [
|
||||
0 => 'JMD',
|
||||
1 => 'Ямайка долларӗ',
|
||||
],
|
||||
'JOD' => [
|
||||
0 => 'JOD',
|
||||
1 => 'Иордан динарӗ',
|
||||
],
|
||||
'JPY' => [
|
||||
0 => 'JP¥',
|
||||
1 => 'Япони иени',
|
||||
],
|
||||
'KES' => [
|
||||
0 => 'KES',
|
||||
1 => 'Кени шиллингӗ',
|
||||
],
|
||||
'KGS' => [
|
||||
0 => 'KGS',
|
||||
1 => 'Киргиз сомӗ',
|
||||
],
|
||||
'KHR' => [
|
||||
0 => 'KHR',
|
||||
1 => 'Камбоджа риелӗ',
|
||||
],
|
||||
'KMF' => [
|
||||
0 => 'KMF',
|
||||
1 => 'Комора франкӗ',
|
||||
],
|
||||
'KPW' => [
|
||||
0 => 'KPW',
|
||||
1 => 'КХДР вони',
|
||||
],
|
||||
'KRW' => [
|
||||
0 => '₩',
|
||||
1 => 'Корей вони',
|
||||
],
|
||||
'KWD' => [
|
||||
0 => 'KWD',
|
||||
1 => 'Кувейт динарӗ',
|
||||
],
|
||||
'KYD' => [
|
||||
0 => 'KYD',
|
||||
1 => 'Кайман утравӗсен долларӗ',
|
||||
],
|
||||
'KZT' => [
|
||||
0 => 'KZT',
|
||||
1 => 'Казах тенгейӗ',
|
||||
],
|
||||
'LAK' => [
|
||||
0 => 'LAK',
|
||||
1 => 'Лаос кипӗ',
|
||||
],
|
||||
'LBP' => [
|
||||
0 => 'LBP',
|
||||
1 => 'Ливан фунчӗ',
|
||||
],
|
||||
'LKR' => [
|
||||
0 => 'LKR',
|
||||
1 => 'Шри-ланка рупийӗ',
|
||||
],
|
||||
'LRD' => [
|
||||
0 => 'LRD',
|
||||
1 => 'Либери долларӗ',
|
||||
],
|
||||
'LSL' => [
|
||||
0 => 'LSL',
|
||||
1 => 'Лесото лотийӗ',
|
||||
],
|
||||
'LYD' => [
|
||||
0 => 'LYD',
|
||||
1 => 'Ливи динарӗ',
|
||||
],
|
||||
'MAD' => [
|
||||
0 => 'MAD',
|
||||
1 => 'Марокко дирхамӗ',
|
||||
],
|
||||
'MDL' => [
|
||||
0 => 'MDL',
|
||||
1 => 'Молдова лайӗ',
|
||||
],
|
||||
'MGA' => [
|
||||
0 => 'MGA',
|
||||
1 => 'Малагаси ариарийӗ',
|
||||
],
|
||||
'MKD' => [
|
||||
0 => 'MKD',
|
||||
1 => 'Македони денарӗ',
|
||||
],
|
||||
'MMK' => [
|
||||
0 => 'MMK',
|
||||
1 => 'Мьянман кьятӗ',
|
||||
],
|
||||
'MNT' => [
|
||||
0 => 'MNT',
|
||||
1 => 'Монголи тугрикӗ',
|
||||
],
|
||||
'MOP' => [
|
||||
0 => 'MOP',
|
||||
1 => 'Макао патаки',
|
||||
],
|
||||
'MRU' => [
|
||||
0 => 'MRU',
|
||||
1 => 'Мавритани угийӗ',
|
||||
],
|
||||
'MUR' => [
|
||||
0 => 'MUR',
|
||||
1 => 'Маврики рупийӗ',
|
||||
],
|
||||
'MVR' => [
|
||||
0 => 'MVR',
|
||||
1 => 'Мальдивсен руфийӗ',
|
||||
],
|
||||
'MWK' => [
|
||||
0 => 'MWK',
|
||||
1 => 'Малави квачӗ',
|
||||
],
|
||||
'MXN' => [
|
||||
0 => 'MX$',
|
||||
1 => 'Мексика песийӗ',
|
||||
],
|
||||
'MYR' => [
|
||||
0 => 'MYR',
|
||||
1 => 'Малайзи ринггичӗ',
|
||||
],
|
||||
'MZN' => [
|
||||
0 => 'MZN',
|
||||
1 => 'Мозамбик метикалӗ',
|
||||
],
|
||||
'NAD' => [
|
||||
0 => 'NAD',
|
||||
1 => 'Намиби долларӗ',
|
||||
],
|
||||
'NGN' => [
|
||||
0 => 'NGN',
|
||||
1 => 'Нигери найрӗ',
|
||||
],
|
||||
'NIO' => [
|
||||
0 => 'NIO',
|
||||
1 => 'Никарагуа кордобӗ',
|
||||
],
|
||||
'NOK' => [
|
||||
0 => 'NOK',
|
||||
1 => 'Норвеги кронӗ',
|
||||
],
|
||||
'NPR' => [
|
||||
0 => 'NPR',
|
||||
1 => 'Непал рупийӗ',
|
||||
],
|
||||
'NZD' => [
|
||||
0 => 'NZ$',
|
||||
1 => 'Ҫӗнӗ Зеланди долларӗ',
|
||||
],
|
||||
'OMR' => [
|
||||
0 => 'OMR',
|
||||
1 => 'Оман риалӗ',
|
||||
],
|
||||
'PAB' => [
|
||||
0 => 'PAB',
|
||||
1 => 'Панама бальбоа',
|
||||
],
|
||||
'PEN' => [
|
||||
0 => 'PEN',
|
||||
1 => 'Перу солӗ',
|
||||
],
|
||||
'PGK' => [
|
||||
0 => 'PGK',
|
||||
1 => 'Папуа – Ҫӗнӗ Гвиней кини',
|
||||
],
|
||||
'PHP' => [
|
||||
0 => '₱',
|
||||
1 => 'Филиппин песийӗ',
|
||||
],
|
||||
'PKR' => [
|
||||
0 => 'PKR',
|
||||
1 => 'пакистан рупийӗ',
|
||||
],
|
||||
'PLN' => [
|
||||
0 => 'PLN',
|
||||
1 => 'Польша злотыйӗ',
|
||||
],
|
||||
'PYG' => [
|
||||
0 => 'PYG',
|
||||
1 => 'Парагвай гуаранӗ',
|
||||
],
|
||||
'QAR' => [
|
||||
0 => 'QAR',
|
||||
1 => 'Катар риалӗ',
|
||||
],
|
||||
'RON' => [
|
||||
0 => 'RON',
|
||||
1 => 'Румыни лейӗ',
|
||||
],
|
||||
'RSD' => [
|
||||
0 => 'RSD',
|
||||
1 => 'Серби динарӗ',
|
||||
],
|
||||
'RUB' => [
|
||||
0 => '₽',
|
||||
1 => 'Раҫҫей тенкӗ',
|
||||
],
|
||||
'RWF' => [
|
||||
0 => 'RWF',
|
||||
1 => 'Руанда франкӗ',
|
||||
],
|
||||
'SAR' => [
|
||||
0 => 'SAR',
|
||||
1 => 'Сауд риялӗ',
|
||||
],
|
||||
'SBD' => [
|
||||
0 => 'SBD',
|
||||
1 => 'Соломон утравӗсен долларӗ',
|
||||
],
|
||||
'SCR' => [
|
||||
0 => 'SCR',
|
||||
1 => 'Сейшел рупийӗ',
|
||||
],
|
||||
'SDG' => [
|
||||
0 => 'SDG',
|
||||
1 => 'Судан фунчӗ',
|
||||
],
|
||||
'SEK' => [
|
||||
0 => 'SEK',
|
||||
1 => 'Швеци кронӗ',
|
||||
],
|
||||
'SGD' => [
|
||||
0 => 'SGD',
|
||||
1 => 'Сингапур долларӗ',
|
||||
],
|
||||
'SHP' => [
|
||||
0 => 'SHP',
|
||||
1 => 'Сӑваплӑ Елена утравӗн фунчӗ',
|
||||
],
|
||||
'SLL' => [
|
||||
0 => 'SLL',
|
||||
1 => 'леонӗ',
|
||||
],
|
||||
'SOS' => [
|
||||
0 => 'SOS',
|
||||
1 => 'Сомали шиллингӗ',
|
||||
],
|
||||
'SRD' => [
|
||||
0 => 'SRD',
|
||||
1 => 'Суринам долларӗ',
|
||||
],
|
||||
'SSP' => [
|
||||
0 => 'SSP',
|
||||
1 => 'Кӑнтӑр Судан фунчӗ',
|
||||
],
|
||||
'STN' => [
|
||||
0 => 'STN',
|
||||
1 => 'Сан-Томе тата Принсипи добрӗ',
|
||||
],
|
||||
'SYP' => [
|
||||
0 => 'SYP',
|
||||
1 => 'Сири фунчӗ',
|
||||
],
|
||||
'SZL' => [
|
||||
0 => 'SZL',
|
||||
1 => 'Свази лилангенийӗ',
|
||||
],
|
||||
'THB' => [
|
||||
0 => 'THB',
|
||||
1 => 'Таиланд барӗ',
|
||||
],
|
||||
'TJS' => [
|
||||
0 => 'TJS',
|
||||
1 => 'Таджик сомонийӗ',
|
||||
],
|
||||
'TMT' => [
|
||||
0 => 'TMT',
|
||||
1 => 'Туркмен маначӗ',
|
||||
],
|
||||
'TND' => [
|
||||
0 => 'TND',
|
||||
1 => 'Тунези динарӗ',
|
||||
],
|
||||
'TOP' => [
|
||||
0 => 'TOP',
|
||||
1 => 'Тонган паанги',
|
||||
],
|
||||
'TRY' => [
|
||||
0 => 'TRY',
|
||||
1 => 'Турци лири',
|
||||
],
|
||||
'TTD' => [
|
||||
0 => 'TTD',
|
||||
1 => 'Тринидад тата Тобаго долларӗ',
|
||||
],
|
||||
'TWD' => [
|
||||
0 => 'NT$',
|
||||
1 => 'Ҫӗнӗ Тайван долларӗ',
|
||||
],
|
||||
'TZS' => [
|
||||
0 => 'TZS',
|
||||
1 => 'Танзани шиллингӗ',
|
||||
],
|
||||
'UAH' => [
|
||||
0 => 'UAH',
|
||||
1 => 'Украина гривни',
|
||||
],
|
||||
'UGX' => [
|
||||
0 => 'UGX',
|
||||
1 => 'Уганда шиллингӗ',
|
||||
],
|
||||
'USD' => [
|
||||
0 => '$',
|
||||
1 => 'АПШ долларӗ',
|
||||
],
|
||||
'UYU' => [
|
||||
0 => 'UYU',
|
||||
1 => 'Уругвай песийӗ',
|
||||
],
|
||||
'UZS' => [
|
||||
0 => 'UZS',
|
||||
1 => 'Узбек сумӗ',
|
||||
],
|
||||
'VES' => [
|
||||
0 => 'VES',
|
||||
1 => 'Венесуэла боливарӗ',
|
||||
],
|
||||
'VND' => [
|
||||
0 => '₫',
|
||||
1 => 'Вьетнам донгӗ',
|
||||
],
|
||||
'VUV' => [
|
||||
0 => 'VUV',
|
||||
1 => 'Вануату ватуйӗ',
|
||||
],
|
||||
'WST' => [
|
||||
0 => 'WST',
|
||||
1 => 'Самоа тали',
|
||||
],
|
||||
'XAF' => [
|
||||
0 => 'FCFA',
|
||||
1 => 'Тӗп Африка КФА франкӗ',
|
||||
],
|
||||
'XCD' => [
|
||||
0 => 'EC$',
|
||||
1 => 'Хӗвелтухӑҫ Карибсем долларӗ',
|
||||
],
|
||||
'XOF' => [
|
||||
0 => 'F CFA',
|
||||
1 => 'КФА ВСЕАО франкӗ',
|
||||
],
|
||||
'XPF' => [
|
||||
0 => 'CFPF',
|
||||
1 => 'Франци Лӑпкӑ океан франкӗ',
|
||||
],
|
||||
'YER' => [
|
||||
0 => 'YER',
|
||||
1 => 'Йемен риалӗ',
|
||||
],
|
||||
'ZAR' => [
|
||||
0 => 'ZAR',
|
||||
1 => 'Кӑнтӑр Африка рэндӗ',
|
||||
],
|
||||
'ZMW' => [
|
||||
0 => 'ZMW',
|
||||
1 => 'Замби квачи',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
@ -824,7 +824,7 @@ return [
|
|||
],
|
||||
'QAR' => [
|
||||
0 => 'QAR',
|
||||
1 => 'Qatari Rial',
|
||||
1 => 'Qatari Riyal',
|
||||
],
|
||||
'RHD' => [
|
||||
0 => 'RHD',
|
||||
|
|
@ -900,11 +900,11 @@ return [
|
|||
],
|
||||
'SLE' => [
|
||||
0 => 'SLE',
|
||||
1 => 'Sierra Leonean New Leone',
|
||||
1 => 'Sierra Leonean Leone',
|
||||
],
|
||||
'SLL' => [
|
||||
0 => 'SLL',
|
||||
1 => 'Sierra Leonean Leone',
|
||||
1 => 'Sierra Leonean Leone (1964—2022)',
|
||||
],
|
||||
'SOS' => [
|
||||
0 => 'SOS',
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ return [
|
|||
],
|
||||
'BOB' => [
|
||||
0 => 'BOB',
|
||||
1 => 'Boliviano',
|
||||
1 => 'Bolivian boliviano',
|
||||
],
|
||||
'BRL' => [
|
||||
0 => 'BRL',
|
||||
|
|
@ -98,10 +98,6 @@ return [
|
|||
0 => 'CLP',
|
||||
1 => 'Chilean Peso',
|
||||
],
|
||||
'CNH' => [
|
||||
0 => 'CNH',
|
||||
1 => 'CNH',
|
||||
],
|
||||
'CNY' => [
|
||||
0 => 'CNY',
|
||||
1 => 'Chinese Yuan',
|
||||
|
|
@ -430,10 +426,6 @@ return [
|
|||
0 => 'SHP',
|
||||
1 => 'St Helena Pound',
|
||||
],
|
||||
'SLL' => [
|
||||
0 => 'SLL',
|
||||
1 => 'Sierra Leonean Leone',
|
||||
],
|
||||
'SOS' => [
|
||||
0 => 'SOS',
|
||||
1 => 'Somali Shilling',
|
||||
|
|
@ -508,7 +500,7 @@ return [
|
|||
],
|
||||
'VES' => [
|
||||
0 => 'VES',
|
||||
1 => 'VES',
|
||||
1 => 'Venezuelan bolívar',
|
||||
],
|
||||
'VND' => [
|
||||
0 => 'VND',
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ return [
|
|||
],
|
||||
'SHP' => [
|
||||
0 => 'SHP',
|
||||
1 => 'St Helena Pound',
|
||||
1 => 'Saint Helena Pound',
|
||||
],
|
||||
'STN' => [
|
||||
0 => 'STN',
|
||||
|
|
@ -56,7 +56,7 @@ return [
|
|||
],
|
||||
'USD' => [
|
||||
0 => 'US$',
|
||||
1 => 'U.S. Dollar',
|
||||
1 => 'US Dollar',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
return [
|
||||
'Names' => [
|
||||
'SLL' => [
|
||||
'SLE' => [
|
||||
0 => 'Le',
|
||||
1 => 'Sierra Leonean Leone',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -898,6 +898,10 @@ return [
|
|||
0 => 'SKK',
|
||||
1 => 'koroa eslovakiarra',
|
||||
],
|
||||
'SLE' => [
|
||||
0 => 'SLE',
|
||||
1 => 'leone sierraleonar berria',
|
||||
],
|
||||
'SLL' => [
|
||||
0 => 'SLL',
|
||||
1 => 'leone sierraleonarra',
|
||||
|
|
@ -1046,6 +1050,10 @@ return [
|
|||
0 => 'VEB',
|
||||
1 => 'Venezuelako bolivarra (1871–2008)',
|
||||
],
|
||||
'VED' => [
|
||||
0 => 'VED',
|
||||
1 => 'bolivar subiraua',
|
||||
],
|
||||
'VEF' => [
|
||||
0 => 'VEF',
|
||||
1 => 'Venezuelako bolivarra (2008–2018)',
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ return [
|
|||
0 => 'AED',
|
||||
1 => '𞤁𞤭𞤪𞤸𞤢𞤥𞤵 𞤋𞤥𞤢𞥄𞤪𞤢𞤼𞤭𞤲𞤳𞤮',
|
||||
],
|
||||
'AFA' => [
|
||||
0 => '𞤀𞤊𞤀',
|
||||
1 => '𞤀𞤬𞤺𞤢𞥄𞤲 𞤀𞤬𞤺𞤢𞥄𞤲𞤭 (𞥑𞥙𞥒𞥗-𞥒𞥐𞥐𞥒)',
|
||||
],
|
||||
'AFN' => [
|
||||
0 => 'AFN',
|
||||
1 => '𞤀𞤬𞤿𞤢𞤲𞤭 𞤀𞤬𞤿𞤢𞤲𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
|
|
@ -26,9 +30,25 @@ return [
|
|||
0 => 'AOA',
|
||||
1 => '𞤑𞤵𞤱𞤢𞤲𞥁𞤢 𞤀𞤲𞤺𞤮𞤤𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'ARA' => [
|
||||
0 => 'ARA',
|
||||
1 => '𞤌𞤧𞤼𞤪𞤢𞤤 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'ARL' => [
|
||||
0 => 'ARL',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤂𞤫𞤴 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢𞤲𞤳𞤮 (𞥑𞥙𞥗𞥐-𞥑𞥙𞥘𞥓)',
|
||||
],
|
||||
'ARM' => [
|
||||
0 => 'ARM',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢𞤲𞤳𞤮 (𞥑𞥘𞥘𞥑-𞥑𞥙𞥗𞥐)',
|
||||
],
|
||||
'ARP' => [
|
||||
0 => 'ARP',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢𞤲𞤳𞤮 (𞥑𞥙𞥘𞥓-𞥑𞥙𞥘𞥕)',
|
||||
],
|
||||
'ARS' => [
|
||||
0 => 'ARS',
|
||||
1 => '𞤆𞤫𞤧𞤮 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'AUD' => [
|
||||
0 => 'A$',
|
||||
|
|
@ -68,7 +88,7 @@ return [
|
|||
],
|
||||
'BMD' => [
|
||||
0 => 'BMD',
|
||||
1 => '𞤁𞤢𞤤𞤢 𞤄𞤫𞤪𞤥𞤵𞤣𞤢𞥄𞤲',
|
||||
1 => '𞤁𞤢𞤤𞤢 𞤄𞤵𞤪𞤥𞤵𞤣𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'BND' => [
|
||||
0 => 'BND',
|
||||
|
|
@ -78,10 +98,46 @@ return [
|
|||
0 => 'BOB',
|
||||
1 => '𞤄𞤮𞤤𞤭𞤾𞤭𞤴𞤢𞤲𞤮 𞤄𞤮𞤤𞤭𞤾𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'BOL' => [
|
||||
0 => 'BOL',
|
||||
1 => '𞤄𞤮𞤤𞤭𞤾𞤭𞤴𞤢𞤲𞤮 𞤄𞤮𞤤𞤭𞤾𞤭𞤴𞤢𞤲𞤳𞤮 (𞥑𞥘𞥖𞥓-𞥑𞥙𞥖𞥓)',
|
||||
],
|
||||
'BOP' => [
|
||||
0 => 'BOP',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤄𞤮𞤤𞤭𞤾𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'BOV' => [
|
||||
0 => 'BOV',
|
||||
1 => '𞤃𞤾𞤣𞤮𞤤 𞤄𞤮𞤤𞤭𞤾𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'BRB' => [
|
||||
0 => 'BRB',
|
||||
1 => '𞤑𞤫𞤪𞤮𞤧𞤫𞤪𞤮 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮 𞤑𞤫𞤧𞤮 (𞥑𞥙𞥖𞥗-𞥑𞥙𞥘𞥖)',
|
||||
],
|
||||
'BRC' => [
|
||||
0 => 'BRC',
|
||||
1 => '𞤑𞤵𞤪𞥁𞤢𞤣𞤮𞥅 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮 (𞥑𞥙𞥘𞥖-𞥑𞥙𞥘𞥙)',
|
||||
],
|
||||
'BRE' => [
|
||||
0 => 'BRE',
|
||||
1 => '𞤑𞤵𞤪𞥁𞤫𞤴𞤪𞤮 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮 (𞥑𞥙𞥙𞥐-𞥑𞥙𞥙𞥓)',
|
||||
],
|
||||
'BRL' => [
|
||||
0 => 'R$',
|
||||
1 => '𞤈𞤭𞤴𞤢𞤤 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'BRN' => [
|
||||
0 => 'BRN',
|
||||
1 => '𞤑𞤵𞤪𞥁𞤢𞤣𞤮𞥅 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮 (𞥑𞥙𞥘𞥙-𞥑𞥙𞥙𞥐)',
|
||||
],
|
||||
'BRR' => [
|
||||
0 => 'BRR',
|
||||
1 => '𞤑𞤵𞤪𞥁𞤫𞤴𞤪𞤮 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮 (𞥑𞥙𞥙𞥓-𞥑𞥙𞥙𞥔)',
|
||||
],
|
||||
'BRZ' => [
|
||||
0 => 'BRZ',
|
||||
1 => '𞤑𞤵𞤪𞥁𞤫𞤴𞤪𞤮 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮 (𞥑𞥙𞥔𞥒-𞥑𞥙𞥖𞥗)',
|
||||
],
|
||||
'BSD' => [
|
||||
0 => 'BSD',
|
||||
1 => '𞤁𞤢𞤤𞤢 𞤄𞤢𞤸𞤢𞤥𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
|
|
@ -100,7 +156,7 @@ return [
|
|||
],
|
||||
'BZD' => [
|
||||
0 => 'BZD',
|
||||
1 => '𞤁𞤢𞤤𞤢 𞤄𞤫𞤤𞤭𞥅𞤧',
|
||||
1 => '𞤁𞤢𞤤𞤢 𞤄𞤫𞤤𞤭𞥅𞤧𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'CAD' => [
|
||||
0 => 'CA$',
|
||||
|
|
@ -114,9 +170,17 @@ return [
|
|||
0 => 'CHF',
|
||||
1 => '𞤊𞤢𞤪𞤢𞤲 𞤅𞤵𞤱𞤭𞥅𞤧',
|
||||
],
|
||||
'CLE' => [
|
||||
0 => 'CLE',
|
||||
1 => '𞤉𞤧𞤳𞤵𞤣𞤮𞥅 𞤕𞤭𞤤𞤫𞥊𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'CLF' => [
|
||||
0 => 'CLF',
|
||||
1 => '𞤅𞤢𞤤𞤲𞤣𞤵 𞤂𞤭𞤥𞤮𞥅𞤪𞤫 𞤕𞤭𞤤𞤫𞥊𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'CLP' => [
|
||||
0 => 'CLP',
|
||||
1 => '𞤆𞤫𞤧𞤮 𞤕𞤭𞤤𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤕𞤭𞤤𞤫𞥊𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'CNH' => [
|
||||
0 => 'CNH',
|
||||
|
|
@ -128,11 +192,15 @@ return [
|
|||
],
|
||||
'COP' => [
|
||||
0 => 'COP',
|
||||
1 => '𞤆𞤫𞤧𞤮 𞤑𞤮𞤤𞤮𞤥𞤦𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤑𞤮𞤤𞤮𞤥𞤦𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'COU' => [
|
||||
0 => 'COU',
|
||||
1 => '𞤅𞤢𞤤𞤲𞤣𞤵 𞤔𞤢𞤪𞤮 𞤳𞤮𞤤𞤮𞤥𞤦𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'CRC' => [
|
||||
0 => 'CRC',
|
||||
1 => '𞤑𞤮𞤤𞤮𞥅𞤲 𞤑𞤮𞤧𞤼𞤢 𞤈𞤭𞤳𞤢𞤲',
|
||||
1 => '𞤑𞤮𞤤𞤮𞥅𞤲 𞤑𞤮𞤧𞤼𞤢𞤪𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'CUC' => [
|
||||
0 => 'CUC',
|
||||
|
|
@ -140,7 +208,7 @@ return [
|
|||
],
|
||||
'CUP' => [
|
||||
0 => 'CUP',
|
||||
1 => '𞤆𞤫𞤧𞤮 𞤑𞤵𞤦𞤢𞤲𞤳𞤮',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤑𞤵𞤦𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'CVE' => [
|
||||
0 => 'CVE',
|
||||
|
|
@ -166,6 +234,14 @@ return [
|
|||
0 => 'DZD',
|
||||
1 => '𞤁𞤭𞤲𞤢𞥄𞤪 𞤀𞤤𞤶𞤢𞤪𞤭𞤲𞤳𞤮',
|
||||
],
|
||||
'ECS' => [
|
||||
0 => 'ECS',
|
||||
1 => '𞤅𞤵𞥅𞤳𞤵𞤪𞤫𞥊𞥅 𞤉𞤳𞤵𞤱𞤢𞤣𞤮𞥅𞤪𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'ECV' => [
|
||||
0 => 'ECV',
|
||||
1 => '𞤅𞤢𞤤𞤲𞤣𞤵 𞤔𞤮𞤪𞤮 𞤉𞤳𞤵𞤱𞤢𞤣𞤮𞥅𞤪𞤴𞤢𞤲𞤳𞤮 𞤚𞤢𞤦𞤭𞤼𞤵𞤲𞥋𞤺𞤮',
|
||||
],
|
||||
'EGP' => [
|
||||
0 => 'EGP',
|
||||
1 => '𞤆𞤢𞤱𞤲𞤣𞤵 𞤃𞤭𞤧𞤭𞤪𞤢𞤲𞤳𞤮',
|
||||
|
|
@ -374,6 +450,10 @@ return [
|
|||
0 => 'MUR',
|
||||
1 => '𞤈𞤵𞤨𞤭𞥅 𞤃𞤮𞤪𞤭𞤧𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'MVP' => [
|
||||
0 => 'MVP',
|
||||
1 => '𞤈𞤵𞥅𞤨𞤭𞥅 𞤃𞤢𞤤𞤣𞤭𞥅𞤬 (𞥑𞥙𞥔𞥗-𞥑𞥙𞥘𞥑)',
|
||||
],
|
||||
'MVR' => [
|
||||
0 => 'MVR',
|
||||
1 => '𞤈𞤵𞤬𞤭𞤴𞤢𞥄 𞤃𞤢𞤤𞤣𞤭𞤾𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
|
|
@ -384,7 +464,15 @@ return [
|
|||
],
|
||||
'MXN' => [
|
||||
0 => 'MX$',
|
||||
1 => '𞤆𞤫𞤧𞤮 𞤃𞤫𞤳𞤧𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤃𞤫𞤳𞤧𞤭𞤳𞤮𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'MXP' => [
|
||||
0 => 'MXP',
|
||||
1 => '𞤑𞤢𞥄𞤤𞤭𞤧𞤫 𞤆𞤫𞥅𞤧𞤮𞥅 𞤃𞤫𞤳𞤧𞤭𞤳𞤮𞤴𞤢𞤲𞤳𞤮 (𞥑𞥘𞥖𞥑-𞥑𞥙𞥙𞥒)',
|
||||
],
|
||||
'MXV' => [
|
||||
0 => 'MXV',
|
||||
1 => '𞤅𞤢𞤤𞤲𞤣𞤵 𞤊𞤭𞤤𞤮 𞤃𞤫𞤳𞤧𞤭𞤳𞤮𞤴𞤢𞤲𞤳𞤮𞥅𞤪𞤵',
|
||||
],
|
||||
'MYR' => [
|
||||
0 => 'MYR',
|
||||
|
|
@ -402,6 +490,10 @@ return [
|
|||
0 => '𞤐𞤐𞤘',
|
||||
1 => '𞤐𞤢𞤴𞤪𞤢 𞤐𞤢𞤶𞤭𞤪𞤢𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'NIC' => [
|
||||
0 => 'NIC',
|
||||
1 => '𞤑𞤮𞥅𞤪𞤣𞤮𞤦𞤢 𞤐𞤭𞤳𞤢𞤪𞤢𞤺𞤵𞤱𞤢𞤲𞤳𞤮 (𞥑𞥙𞥘𞥘-𞥑𞥙𞥙𞥑)',
|
||||
],
|
||||
'NIO' => [
|
||||
0 => 'NIO',
|
||||
1 => '𞤑𞤮𞥅𞤪𞤣𞤮𞤦𞤢 𞤐𞤭𞤳𞤢𞤪𞤢𞤺𞤵𞤱𞤢𞤲𞤳𞤮',
|
||||
|
|
@ -426,10 +518,18 @@ return [
|
|||
0 => 'PAB',
|
||||
1 => '𞤄𞤢𞤤𞤦𞤮𞤱𞤢 𞤆𞤢𞤲𞤢𞤥𞤢𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'PEI' => [
|
||||
0 => 'PEI',
|
||||
1 => '𞤋𞤲𞤼𞤭 𞤨𞤫𞤪𞤵𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'PEN' => [
|
||||
0 => 'PEN',
|
||||
1 => '𞤅𞤮𞤤 𞤆𞤫𞤪𞤵𞤲𞤳𞤮',
|
||||
],
|
||||
'PES' => [
|
||||
0 => 'PES',
|
||||
1 => '𞤅𞤮𞤤 𞤆𞤫𞤪𞤵𞤴𞤢𞤲𞤳𞤮 (𞥑𞥘𞥖𞥓-𞥑𞥙𞥖𞥕)',
|
||||
],
|
||||
'PGK' => [
|
||||
0 => '𞤑𞤆𞤘',
|
||||
1 => '𞤑𞤭𞤲𞤢 𞤆𞤢𞤨𞤵𞤱𞤢 𞤐𞤫𞤱-𞤘𞤭𞤲𞤫𞤲𞤳𞤮',
|
||||
|
|
@ -510,6 +610,10 @@ return [
|
|||
0 => 'SRD',
|
||||
1 => '𞤁𞤢𞤤𞤢 𞤅𞤵𞤪𞤵𞤲𞤢𞤥𞤭𞤲𞤳𞤮',
|
||||
],
|
||||
'SRG' => [
|
||||
0 => 'SRG',
|
||||
1 => '𞤘𞤭𞤤𞤣𞤮𞥅 𞤅𞤵𞤪𞤵𞤲𞤢𞤥𞤭𞤲𞤳𞤮',
|
||||
],
|
||||
'SSP' => [
|
||||
0 => 'SSP',
|
||||
1 => '𞤆𞤢𞤱𞤲𞤣𞤵 𞤂𞤫𞤴𞤤𞤫𞤴𞤪𞤭 𞤅𞤵𞤣𞤢𞤲𞤭𞤲𞤳𞤮',
|
||||
|
|
@ -518,6 +622,10 @@ return [
|
|||
0 => 'STN',
|
||||
1 => '𞤁𞤮𞤦𞤢𞤪𞤢 𞤅𞤢𞤱𞤮-𞤚𞤮𞤥𞤫 & 𞤆𞤫𞤪𞤫𞤲𞤧𞤭𞤨',
|
||||
],
|
||||
'SVC' => [
|
||||
0 => 'SVC',
|
||||
1 => '𞤑𞤮𞤤𞤮𞥅𞤲 𞤅𞤢𞤤𞤾𞤢𞤣𞤮𞤪𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'SYP' => [
|
||||
0 => 'SYP',
|
||||
1 => '𞤆𞤢𞤱𞤲𞥋𞤣𞤵 𞤅𞤭𞤪𞤢𞤴𞤢𞤲𞤳𞤮',
|
||||
|
|
@ -574,14 +682,42 @@ return [
|
|||
0 => 'US$',
|
||||
1 => '𞤁𞤢𞤤𞤢 𞤁𞤫𞤲𞤼𞤢𞤤 𞤂𞤢𞤪𞤫 𞤀𞤥𞤫𞤪𞤭𞤳',
|
||||
],
|
||||
'USN' => [
|
||||
0 => 'USN',
|
||||
1 => '𞤣𞤢𞤤𞤢 𞤁𞤂𞤀 (𞤶𞤢𞤲𞤺𞤮 𞤥𞤵𞥅𞤯𞤵𞤲)',
|
||||
],
|
||||
'USS' => [
|
||||
0 => 'USS',
|
||||
1 => '𞤣𞤢𞤤𞤢 𞤁𞤂𞤀 (𞤸𞤢𞤲𞤣𞤫 𞤥𞤵𞥅𞤯𞤵𞤲)',
|
||||
],
|
||||
'UYI' => [
|
||||
0 => 'UYI',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤒𞤵𞤪𞤺𞤮𞤴𞤢𞤲𞤳𞤮 (𞤕𞤢𞤤𞤯𞤭 𞤔𞤮𞥅𞤨𞤢𞥄𞤯𞤭)',
|
||||
],
|
||||
'UYP' => [
|
||||
0 => 'UYP',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤒𞤵𞤪𞤺𞤮𞤴𞤢𞤲𞤳𞤮 (𞥑𞥙𞥗𞥕-𞥑𞥙𞥙𞥓)',
|
||||
],
|
||||
'UYU' => [
|
||||
0 => 'UYU',
|
||||
1 => '𞤆𞤫𞤧𞤮 𞤓𞤪𞤵𞤺𞤵𞤪𞤭𞤲𞤳𞤮',
|
||||
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤒𞤵𞤪𞤺𞤮𞤴𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'UYW' => [
|
||||
0 => 'UYW',
|
||||
1 => '𞤅𞤢𞤤𞤲𞤣𞤵 𞤐𞤶𞤮𞤩𞤣𞤭 𞤒𞤵𞤪𞤺𞤮𞤴𞤢𞤲𞤳𞤮 𞤔𞤮𞥅𞤨𞤢𞥄𞤲𞤣𞤭',
|
||||
],
|
||||
'UZS' => [
|
||||
0 => 'UZS',
|
||||
1 => '𞤅𞤮𞤥𞤵 𞤓𞥁𞤦𞤫𞤳𞤭𞤧𞤼𞤢𞤲𞤳𞤮',
|
||||
],
|
||||
'VEB' => [
|
||||
0 => 'VEB',
|
||||
1 => '𞤄𞤮𞤤𞤭𞤾𞤢𞥄𞤪 𞤜𞤫𞤲𞤭𞥅𞤧𞤫𞤤𞤢𞤲𞤳𞤮 (𞥑𞥘𞥗𞥑-𞥒𞥐𞥐𞥘)',
|
||||
],
|
||||
'VED' => [
|
||||
0 => 'VED',
|
||||
1 => '𞤄𞤮𞤤𞤭𞤾𞤢𞥄𞤪 𞤅𞤮𞤦𞤫𞥊𞤪𞤢𞤲𞤮',
|
||||
],
|
||||
'VEF' => [
|
||||
0 => 'VEF',
|
||||
1 => '𞤄𞤮𞤤𞤭𞤾𞤢𞥄𞤪 𞤜𞤫𞤲𞤭𞥅𞤧𞤫𞤤𞤢𞤲𞤳𞤮 (𞥒𞥐𞥐𞥘 - 𞥒𞥐𞥑𞥘)',
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ return [
|
|||
0 => 'GNF',
|
||||
1 => '𞤊𞤢𞤪𞤢𞤲 𞤘𞤭𞤲𞤫𞤲𞤳𞤮',
|
||||
],
|
||||
'SLL' => [
|
||||
'SLE' => [
|
||||
0 => 'Le',
|
||||
1 => '𞤂𞤫𞤴𞤮𞤲 𞤅𞤫𞤪𞤢𞤤𞤭𞤴𞤢𞤲𞤳𞤮',
|
||||
1 => 'SLE',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
return [
|
||||
'Names' => [
|
||||
'SLL' => [
|
||||
'SLE' => [
|
||||
0 => 'Le',
|
||||
1 => 'Lewoon Seraa Liyon',
|
||||
1 => 'SLE',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -898,6 +898,10 @@ return [
|
|||
0 => 'SKK',
|
||||
1 => 'Koruna Slòbhacach',
|
||||
],
|
||||
'SLE' => [
|
||||
0 => 'SLE',
|
||||
1 => 'Leone Siarra Leòmhannach ùr',
|
||||
],
|
||||
'SLL' => [
|
||||
0 => 'SLL',
|
||||
1 => 'Leone Siarra Leòmhannach',
|
||||
|
|
@ -1046,6 +1050,10 @@ return [
|
|||
0 => 'VEB',
|
||||
1 => 'Bolívar Bheinisealach (1871–2008)',
|
||||
],
|
||||
'VED' => [
|
||||
0 => 'VED',
|
||||
1 => 'Bolívar Soberano',
|
||||
],
|
||||
'VEF' => [
|
||||
0 => 'VEF',
|
||||
1 => 'Bolívar Bheinisealach (2008–2018)',
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ return [
|
|||
],
|
||||
'MDL' => [
|
||||
0 => 'MDL',
|
||||
1 => 'kuɗaɗen Moldova',
|
||||
1 => 'Kuɗaɗen Moldova',
|
||||
],
|
||||
'MGA' => [
|
||||
0 => 'MGA',
|
||||
|
|
|
|||
|
|
@ -600,7 +600,7 @@ return [
|
|||
],
|
||||
'PHP' => [
|
||||
0 => 'PHP',
|
||||
1 => 'פזו פיליפיני',
|
||||
1 => 'פסו פיליפיני',
|
||||
],
|
||||
'PKR' => [
|
||||
0 => 'PKR',
|
||||
|
|
|
|||
30
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/hi_Latn.php
vendored
Normal file
30
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/hi_Latn.php
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Names' => [
|
||||
'CRC' => [
|
||||
0 => 'CRC',
|
||||
1 => 'Costa Rican colon',
|
||||
],
|
||||
'ISK' => [
|
||||
0 => 'ISK',
|
||||
1 => 'Icelandic krona',
|
||||
],
|
||||
'NIO' => [
|
||||
0 => 'NIO',
|
||||
1 => 'Nicaraguan cordoba',
|
||||
],
|
||||
'RUB' => [
|
||||
0 => 'RUB',
|
||||
1 => 'Russian ruble',
|
||||
],
|
||||
'STN' => [
|
||||
0 => 'STN',
|
||||
1 => 'Sao Tome & Principe Dobra',
|
||||
],
|
||||
'VES' => [
|
||||
0 => 'VES',
|
||||
1 => 'Venezuelan bolivar',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
@ -375,7 +375,7 @@ return [
|
|||
1 => 'etiopski bir',
|
||||
],
|
||||
'EUR' => [
|
||||
0 => 'EUR',
|
||||
0 => '€',
|
||||
1 => 'euro',
|
||||
],
|
||||
'FIM' => [
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ return [
|
|||
],
|
||||
'LSL' => [
|
||||
0 => 'LSL',
|
||||
1 => 'This is not a translation',
|
||||
1 => 'Ego loti obodo Lesotho',
|
||||
],
|
||||
'LYD' => [
|
||||
0 => 'LYD',
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ return [
|
|||
],
|
||||
'FJD' => [
|
||||
0 => 'FJD',
|
||||
1 => 'fidjeyskur dalur',
|
||||
1 => 'fídjískur dalur',
|
||||
],
|
||||
'FKP' => [
|
||||
0 => 'FKP',
|
||||
|
|
@ -359,7 +359,7 @@ return [
|
|||
1 => 'íranskt ríal',
|
||||
],
|
||||
'ISK' => [
|
||||
0 => 'ISK',
|
||||
0 => 'kr.',
|
||||
1 => 'íslensk króna',
|
||||
],
|
||||
'ITL' => [
|
||||
|
|
@ -760,7 +760,7 @@ return [
|
|||
],
|
||||
'TND' => [
|
||||
0 => 'TND',
|
||||
1 => 'túnískur denari',
|
||||
1 => 'túniskur denari',
|
||||
],
|
||||
'TOP' => [
|
||||
0 => 'TOP',
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue