Update dependencies

This commit is contained in:
Benoit Vianin 2023-01-07 10:54:31 +01:00
parent 4f030b05af
commit c42ebb8062
675 changed files with 26593 additions and 13121 deletions

View File

@ -34,5 +34,11 @@
"allow-plugins": { "allow-plugins": {
"ocramius/package-versions": true "ocramius/package-versions": true
} }
},
"archive": {
"exclude": [
"/test",
"/phpunit.xml.dist"
]
} }
} }

View File

@ -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>

View File

@ -89,6 +89,9 @@ final class CharacterSetEci extends AbstractEnum
*/ */
private static $nameToEci; private static $nameToEci;
/**
* @param int[] $values
*/
public function __construct(array $values, string ...$otherEncodingNames) public function __construct(array $values, string ...$otherEncodingNames)
{ {
$this->values = $values; $this->values = $values;

View File

@ -62,7 +62,7 @@ class FormatInformation
/** /**
* Offset i holds the number of 1 bits in the binary representation of i. * 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]; private const BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];

View File

@ -42,6 +42,9 @@ final class Mode extends AbstractEnum
*/ */
private $bits; private $bits;
/**
* @param int[] $characterCountBitsForVersions
*/
protected function __construct(array $characterCountBitsForVersions, int $bits) protected function __construct(array $characterCountBitsForVersions, int $bits)
{ {
$this->characterCountBitsForVersions = $characterCountBitsForVersions; $this->characterCountBitsForVersions = $characterCountBitsForVersions;

View File

@ -37,7 +37,7 @@ final class Encoder
/** /**
* Codec cache. * Codec cache.
* *
* @var array * @var array<string,ReedSolomonCodec>
*/ */
private static $codecs = []; private static $codecs = [];

View File

@ -334,7 +334,7 @@ final class SvgImageBackEnd implements ImageBackEndInterface
$this->xmlWriter->writeAttribute('stop-color', $this->getColorString($startColor)); $this->xmlWriter->writeAttribute('stop-color', $this->getColorString($startColor));
if ($startColor instanceof Alpha) { if ($startColor instanceof Alpha) {
$this->xmlWriter->writeAttribute('stop-opacity', $startColor->getAlpha()); $this->xmlWriter->writeAttribute('stop-opacity', (string) $startColor->getAlpha());
} }
$this->xmlWriter->endElement(); $this->xmlWriter->endElement();
@ -344,7 +344,7 @@ final class SvgImageBackEnd implements ImageBackEndInterface
$this->xmlWriter->writeAttribute('stop-color', $this->getColorString($endColor)); $this->xmlWriter->writeAttribute('stop-color', $this->getColorString($endColor));
if ($endColor instanceof Alpha) { if ($endColor instanceof Alpha) {
$this->xmlWriter->writeAttribute('stop-opacity', $endColor->getAlpha()); $this->xmlWriter->writeAttribute('stop-opacity', (string) $endColor->getAlpha());
} }
$this->xmlWriter->endElement(); $this->xmlWriter->endElement();

View File

@ -41,7 +41,7 @@ final class EdgeIterator implements IteratorAggregate
} }
/** /**
* @return Edge[] * @return Traversable<Edge>
*/ */
public function getIterator() : Traversable public function getIterator() : Traversable
{ {

View File

@ -136,7 +136,7 @@ final class EllipticArc implements OperationInterface
/** /**
* @return Curve[] * @return Curve[]
*/ */
private function createCurves(float $fromX, $fromY) : array private function createCurves(float $fromX, float $fromY) : array
{ {
$xAngle = deg2rad($this->xAxisAngle); $xAngle = deg2rad($this->xAxisAngle);
list($centerX, $centerY, $radiusX, $radiusY, $startAngle, $deltaAngle) = list($centerX, $centerY, $radiusX, $radiusY, $startAngle, $deltaAngle) =

View File

@ -17,10 +17,16 @@ use BaconQrCode\Writer;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Spatie\Snapshots\MatchesSnapshots; use Spatie\Snapshots\MatchesSnapshots;
/**
* @group integration
*/
final class ImagickRenderingTest extends TestCase final class ImagickRenderingTest extends TestCase
{ {
use MatchesSnapshots; use MatchesSnapshots;
/**
* @requires extension imagick
*/
public function testGenericQrCode() : void public function testGenericQrCode() : void
{ {
$renderer = new ImageRenderer( $renderer = new ImageRenderer(
@ -35,6 +41,9 @@ final class ImagickRenderingTest extends TestCase
unlink($tempName); unlink($tempName);
} }
/**
* @requires extension imagick
*/
public function testIssue79() : void public function testIssue79() : void
{ {
$eye = SquareEye::instance(); $eye = SquareEye::instance();

View File

@ -16,7 +16,7 @@ return array(
'NumberFormatter' => $vendorDir . '/symfony/polyfill-intl-icu/Resources/stubs/NumberFormatter.php', 'NumberFormatter' => $vendorDir . '/symfony/polyfill-intl-icu/Resources/stubs/NumberFormatter.php',
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.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', 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
); );

View File

@ -174,7 +174,7 @@ class ComposerStaticInit2d2f03c80bad397451a8de89740becf0
'NumberFormatter' => __DIR__ . '/..' . '/symfony/polyfill-intl-icu/Resources/stubs/NumberFormatter.php', 'NumberFormatter' => __DIR__ . '/..' . '/symfony/polyfill-intl-icu/Resources/stubs/NumberFormatter.php',
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.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', 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
); );

View File

@ -2,17 +2,17 @@
"packages": [ "packages": [
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
"version": "2.0.7", "version": "2.0.8",
"version_normalized": "2.0.7.0", "version_normalized": "2.0.8.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Bacon/BaconQrCode.git", "url": "https://github.com/Bacon/BaconQrCode.git",
"reference": "d70c840f68657ce49094b8d91f9ee0cc07fbf66c" "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/d70c840f68657ce49094b8d91f9ee0cc07fbf66c", "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22",
"reference": "d70c840f68657ce49094b8d91f9ee0cc07fbf66c", "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -29,7 +29,7 @@
"suggest": { "suggest": {
"ext-imagick": "to generate QR code images" "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", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -53,7 +53,7 @@
"homepage": "https://github.com/Bacon/BaconQrCode", "homepage": "https://github.com/Bacon/BaconQrCode",
"support": { "support": {
"issues": "https://github.com/Bacon/BaconQrCode/issues", "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" "install-path": "../bacon/bacon-qr-code"
}, },
@ -187,26 +187,28 @@
}, },
{ {
"name": "khanamiryan/qrcode-detector-decoder", "name": "khanamiryan/qrcode-detector-decoder",
"version": "1.0.5.2", "version": "1.0.6",
"version_normalized": "1.0.5.2", "version_normalized": "1.0.6.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/khanamiryan/php-qrcode-detector-decoder.git", "url": "https://github.com/khanamiryan/php-qrcode-detector-decoder.git",
"reference": "04fdd58d86a387065f707dc6d3cc304c719910c1" "reference": "45326fb83a2a375065dbb3a134b5b8a5872da569"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/khanamiryan/php-qrcode-detector-decoder/zipball/04fdd58d86a387065f707dc6d3cc304c719910c1", "url": "https://api.github.com/repos/khanamiryan/php-qrcode-detector-decoder/zipball/45326fb83a2a375065dbb3a134b5b8a5872da569",
"reference": "04fdd58d86a387065f707dc6d3cc304c719910c1", "reference": "45326fb83a2a375065dbb3a134b5b8a5872da569",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.6" "php": ">=5.6"
}, },
"require-dev": { "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", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -239,7 +241,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/khanamiryan/php-qrcode-detector-decoder/issues", "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" "install-path": "../khanamiryan/qrcode-detector-decoder"
}, },
@ -297,17 +299,17 @@
}, },
{ {
"name": "myclabs/php-enum", "name": "myclabs/php-enum",
"version": "1.8.3", "version": "1.8.4",
"version_normalized": "1.8.3.0", "version_normalized": "1.8.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/myclabs/php-enum.git", "url": "https://github.com/myclabs/php-enum.git",
"reference": "b942d263c641ddb5190929ff840c68f78713e937" "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/b942d263c641ddb5190929ff840c68f78713e937", "url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483",
"reference": "b942d263c641ddb5190929ff840c68f78713e937", "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -319,13 +321,16 @@
"squizlabs/php_codesniffer": "1.*", "squizlabs/php_codesniffer": "1.*",
"vimeo/psalm": "^4.6.2" "vimeo/psalm": "^4.6.2"
}, },
"time": "2021-07-05T08:18:36+00:00", "time": "2022-08-04T09:53:51+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"MyCLabs\\Enum\\": "src/" "MyCLabs\\Enum\\": "src/"
} },
"classmap": [
"stubs/Stringable.php"
]
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -344,7 +349,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/myclabs/php-enum/issues", "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": [ "funding": [
{ {
@ -425,8 +430,8 @@
}, },
{ {
"name": "symfony/deprecation-contracts", "name": "symfony/deprecation-contracts",
"version": "v2.5.1", "version": "v2.5.2",
"version_normalized": "2.5.1.0", "version_normalized": "2.5.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git", "url": "https://github.com/symfony/deprecation-contracts.git",
@ -475,7 +480,7 @@
"description": "A generic function and convention to trigger deprecation notices", "description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1" "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2"
}, },
"funding": [ "funding": [
{ {
@ -495,17 +500,17 @@
}, },
{ {
"name": "symfony/intl", "name": "symfony/intl",
"version": "v5.4.8", "version": "v5.4.15",
"version_normalized": "5.4.8.0", "version_normalized": "5.4.15.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/intl.git", "url": "https://github.com/symfony/intl.git",
"reference": "b9e17d7ab867ce99f89950ebced0fa91076ba12b" "reference": "2cb39da7f6e7b7344d7d5317dbee8db9d12cc714"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/intl/zipball/b9e17d7ab867ce99f89950ebced0fa91076ba12b", "url": "https://api.github.com/repos/symfony/intl/zipball/2cb39da7f6e7b7344d7d5317dbee8db9d12cc714",
"reference": "b9e17d7ab867ce99f89950ebced0fa91076ba12b", "reference": "2cb39da7f6e7b7344d7d5317dbee8db9d12cc714",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -516,7 +521,7 @@
"require-dev": { "require-dev": {
"symfony/filesystem": "^4.4|^5.0|^6.0" "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", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -566,7 +571,7 @@
"localization" "localization"
], ],
"support": { "support": {
"source": "https://github.com/symfony/intl/tree/v5.4.8" "source": "https://github.com/symfony/intl/tree/v5.4.15"
}, },
"funding": [ "funding": [
{ {
@ -586,17 +591,17 @@
}, },
{ {
"name": "symfony/options-resolver", "name": "symfony/options-resolver",
"version": "v5.4.3", "version": "v5.4.11",
"version_normalized": "5.4.3.0", "version_normalized": "5.4.11.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/options-resolver.git", "url": "https://github.com/symfony/options-resolver.git",
"reference": "cc1147cb11af1b43f503ac18f31aa3bec213aba8" "reference": "54f14e36aa73cb8f7261d7686691fd4d75ea2690"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/cc1147cb11af1b43f503ac18f31aa3bec213aba8", "url": "https://api.github.com/repos/symfony/options-resolver/zipball/54f14e36aa73cb8f7261d7686691fd4d75ea2690",
"reference": "cc1147cb11af1b43f503ac18f31aa3bec213aba8", "reference": "54f14e36aa73cb8f7261d7686691fd4d75ea2690",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -605,7 +610,7 @@
"symfony/polyfill-php73": "~1.0", "symfony/polyfill-php73": "~1.0",
"symfony/polyfill-php80": "^1.16" "symfony/polyfill-php80": "^1.16"
}, },
"time": "2022-01-02T09:53:40+00:00", "time": "2022-07-20T13:00:38+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -638,7 +643,7 @@
"options" "options"
], ],
"support": { "support": {
"source": "https://github.com/symfony/options-resolver/tree/v5.4.3" "source": "https://github.com/symfony/options-resolver/tree/v5.4.11"
}, },
"funding": [ "funding": [
{ {
@ -658,17 +663,17 @@
}, },
{ {
"name": "symfony/polyfill-ctype", "name": "symfony/polyfill-ctype",
"version": "v1.26.0", "version": "v1.27.0",
"version_normalized": "1.26.0.0", "version_normalized": "1.27.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git", "url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" "reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", "reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -680,11 +685,11 @@
"suggest": { "suggest": {
"ext-ctype": "For best performance" "ext-ctype": "For best performance"
}, },
"time": "2022-05-24T11:49:31+00:00", "time": "2022-11-03T14:55:06+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -723,7 +728,7 @@
"portable" "portable"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -743,17 +748,17 @@
}, },
{ {
"name": "symfony/polyfill-intl-grapheme", "name": "symfony/polyfill-intl-grapheme",
"version": "v1.26.0", "version": "v1.27.0",
"version_normalized": "1.26.0.0", "version_normalized": "1.27.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "433d05519ce6990bf3530fba6957499d327395c2" "reference": "511a08c03c1960e08a883f4cffcacd219b758354"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354",
"reference": "433d05519ce6990bf3530fba6957499d327395c2", "reference": "511a08c03c1960e08a883f4cffcacd219b758354",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -762,11 +767,11 @@
"suggest": { "suggest": {
"ext-intl": "For best performance" "ext-intl": "For best performance"
}, },
"time": "2022-05-24T11:49:31+00:00", "time": "2022-11-03T14:55:06+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -807,7 +812,7 @@
"shim" "shim"
], ],
"support": { "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": [ "funding": [
{ {
@ -827,17 +832,17 @@
}, },
{ {
"name": "symfony/polyfill-intl-icu", "name": "symfony/polyfill-intl-icu",
"version": "v1.26.0", "version": "v1.27.0",
"version_normalized": "1.26.0.0", "version_normalized": "1.27.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-icu.git", "url": "https://github.com/symfony/polyfill-intl-icu.git",
"reference": "e407643d610e5f2c8a4b14189150f68934bf5e48" "reference": "a3d9148e2c363588e05abbdd4ee4f971f0a5330c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/e407643d610e5f2c8a4b14189150f68934bf5e48", "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/a3d9148e2c363588e05abbdd4ee4f971f0a5330c",
"reference": "e407643d610e5f2c8a4b14189150f68934bf5e48", "reference": "a3d9148e2c363588e05abbdd4ee4f971f0a5330c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -846,11 +851,11 @@
"suggest": { "suggest": {
"ext-intl": "For best performance and support of other locales than \"en\"" "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", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -897,7 +902,7 @@
"shim" "shim"
], ],
"support": { "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": [ "funding": [
{ {
@ -917,17 +922,17 @@
}, },
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.26.0", "version": "v1.27.0",
"version_normalized": "1.26.0.0", "version_normalized": "1.27.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "219aa369ceff116e673852dce47c3a41794c14bd" "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"reference": "219aa369ceff116e673852dce47c3a41794c14bd", "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -936,11 +941,11 @@
"suggest": { "suggest": {
"ext-intl": "For best performance" "ext-intl": "For best performance"
}, },
"time": "2022-05-24T11:49:31+00:00", "time": "2022-11-03T14:55:06+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -984,7 +989,7 @@
"shim" "shim"
], ],
"support": { "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": [ "funding": [
{ {
@ -1004,17 +1009,17 @@
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.26.0", "version": "v1.27.0",
"version_normalized": "1.26.0.0", "version_normalized": "1.27.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git", "url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1026,11 +1031,11 @@
"suggest": { "suggest": {
"ext-mbstring": "For best performance" "ext-mbstring": "For best performance"
}, },
"time": "2022-05-24T11:49:31+00:00", "time": "2022-11-03T14:55:06+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1070,7 +1075,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -1090,27 +1095,27 @@
}, },
{ {
"name": "symfony/polyfill-php73", "name": "symfony/polyfill-php73",
"version": "v1.26.0", "version": "v1.27.0",
"version_normalized": "1.26.0.0", "version_normalized": "1.27.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php73.git", "url": "https://github.com/symfony/polyfill-php73.git",
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85" "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85", "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85", "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1" "php": ">=7.1"
}, },
"time": "2022-05-24T11:49:31+00:00", "time": "2022-11-03T14:55:06+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1152,7 +1157,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -1172,27 +1177,27 @@
}, },
{ {
"name": "symfony/polyfill-php80", "name": "symfony/polyfill-php80",
"version": "v1.26.0", "version": "v1.27.0",
"version_normalized": "1.26.0.0", "version_normalized": "1.27.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php80.git", "url": "https://github.com/symfony/polyfill-php80.git",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1" "php": ">=7.1"
}, },
"time": "2022-05-10T07:21:04+00:00", "time": "2022-11-03T14:55:06+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1238,7 +1243,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -1258,27 +1263,27 @@
}, },
{ {
"name": "symfony/polyfill-php81", "name": "symfony/polyfill-php81",
"version": "v1.26.0", "version": "v1.27.0",
"version_normalized": "1.26.0.0", "version_normalized": "1.27.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php81.git", "url": "https://github.com/symfony/polyfill-php81.git",
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1" "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1", "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a",
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1", "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1" "php": ">=7.1"
}, },
"time": "2022-05-24T11:49:31+00:00", "time": "2022-11-03T14:55:06+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.26-dev" "dev-main": "1.27-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -1320,7 +1325,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0" "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0"
}, },
"funding": [ "funding": [
{ {
@ -1340,17 +1345,17 @@
}, },
{ {
"name": "symfony/property-access", "name": "symfony/property-access",
"version": "v5.4.8", "version": "v5.4.15",
"version_normalized": "5.4.8.0", "version_normalized": "5.4.15.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/property-access.git", "url": "https://github.com/symfony/property-access.git",
"reference": "fe501d498d6ec7e9efe928c90fabedf629116495" "reference": "0f3e8f40a1d3da90f674b3dd772e4777ccde4273"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/property-access/zipball/fe501d498d6ec7e9efe928c90fabedf629116495", "url": "https://api.github.com/repos/symfony/property-access/zipball/0f3e8f40a1d3da90f674b3dd772e4777ccde4273",
"reference": "fe501d498d6ec7e9efe928c90fabedf629116495", "reference": "0f3e8f40a1d3da90f674b3dd772e4777ccde4273",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1365,7 +1370,7 @@
"suggest": { "suggest": {
"psr/cache-implementation": "To cache access methods." "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", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -1404,7 +1409,7 @@
"reflection" "reflection"
], ],
"support": { "support": {
"source": "https://github.com/symfony/property-access/tree/v5.4.8" "source": "https://github.com/symfony/property-access/tree/v5.4.15"
}, },
"funding": [ "funding": [
{ {
@ -1424,17 +1429,17 @@
}, },
{ {
"name": "symfony/property-info", "name": "symfony/property-info",
"version": "v5.4.9", "version": "v5.4.17",
"version_normalized": "5.4.9.0", "version_normalized": "5.4.17.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/property-info.git", "url": "https://github.com/symfony/property-info.git",
"reference": "6f0a452aaba45e763f89e328df437f73a720e18e" "reference": "12e1f7b3d73b1f3690aa524b92b5de9937507361"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/property-info/zipball/6f0a452aaba45e763f89e328df437f73a720e18e", "url": "https://api.github.com/repos/symfony/property-info/zipball/12e1f7b3d73b1f3690aa524b92b5de9937507361",
"reference": "6f0a452aaba45e763f89e328df437f73a720e18e", "reference": "12e1f7b3d73b1f3690aa524b92b5de9937507361",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1449,7 +1454,7 @@
"symfony/dependency-injection": "<4.4" "symfony/dependency-injection": "<4.4"
}, },
"require-dev": { "require-dev": {
"doctrine/annotations": "^1.10.4", "doctrine/annotations": "^1.10.4|^2",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"phpstan/phpdoc-parser": "^1.0", "phpstan/phpdoc-parser": "^1.0",
"symfony/cache": "^4.4|^5.0|^6.0", "symfony/cache": "^4.4|^5.0|^6.0",
@ -1462,7 +1467,7 @@
"symfony/doctrine-bridge": "To use Doctrine metadata", "symfony/doctrine-bridge": "To use Doctrine metadata",
"symfony/serializer": "To use Serializer 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", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -1498,7 +1503,7 @@
"validator" "validator"
], ],
"support": { "support": {
"source": "https://github.com/symfony/property-info/tree/v5.4.9" "source": "https://github.com/symfony/property-info/tree/v5.4.17"
}, },
"funding": [ "funding": [
{ {
@ -1518,17 +1523,17 @@
}, },
{ {
"name": "symfony/string", "name": "symfony/string",
"version": "v5.4.9", "version": "v5.4.17",
"version_normalized": "5.4.9.0", "version_normalized": "5.4.17.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/string.git", "url": "https://github.com/symfony/string.git",
"reference": "985e6a9703ef5ce32ba617c9c7d97873bb7b2a99" "reference": "55733a8664b8853b003e70251c58bc8cb2d82a6b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/985e6a9703ef5ce32ba617c9c7d97873bb7b2a99", "url": "https://api.github.com/repos/symfony/string/zipball/55733a8664b8853b003e70251c58bc8cb2d82a6b",
"reference": "985e6a9703ef5ce32ba617c9c7d97873bb7b2a99", "reference": "55733a8664b8853b003e70251c58bc8cb2d82a6b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1548,7 +1553,7 @@
"symfony/translation-contracts": "^1.1|^2", "symfony/translation-contracts": "^1.1|^2",
"symfony/var-exporter": "^4.4|^5.0|^6.0" "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", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -1587,7 +1592,7 @@
"utf8" "utf8"
], ],
"support": { "support": {
"source": "https://github.com/symfony/string/tree/v5.4.9" "source": "https://github.com/symfony/string/tree/v5.4.17"
}, },
"funding": [ "funding": [
{ {
@ -1607,17 +1612,17 @@
}, },
{ {
"name": "symfony/translation-contracts", "name": "symfony/translation-contracts",
"version": "v2.5.1", "version": "v2.5.2",
"version_normalized": "2.5.1.0", "version_normalized": "2.5.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/translation-contracts.git", "url": "https://github.com/symfony/translation-contracts.git",
"reference": "1211df0afa701e45a04253110e959d4af4ef0f07" "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/1211df0afa701e45a04253110e959d4af4ef0f07", "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe",
"reference": "1211df0afa701e45a04253110e959d4af4ef0f07", "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1626,7 +1631,7 @@
"suggest": { "suggest": {
"symfony/translation-implementation": "" "symfony/translation-implementation": ""
}, },
"time": "2022-01-02T09:53:40+00:00", "time": "2022-06-27T16:58:25+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
@ -1668,7 +1673,7 @@
"standards" "standards"
], ],
"support": { "support": {
"source": "https://github.com/symfony/translation-contracts/tree/v2.5.1" "source": "https://github.com/symfony/translation-contracts/tree/v2.5.2"
}, },
"funding": [ "funding": [
{ {
@ -1688,17 +1693,17 @@
}, },
{ {
"name": "symfony/validator", "name": "symfony/validator",
"version": "v5.4.8", "version": "v5.4.17",
"version_normalized": "5.4.8.0", "version_normalized": "5.4.17.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/validator.git", "url": "https://github.com/symfony/validator.git",
"reference": "bdc6d04ba95c73ccbf906b4ad9b8775c738d83ad" "reference": "621b820204a238d754f7f60241fcbdb1687641ea"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/validator/zipball/bdc6d04ba95c73ccbf906b4ad9b8775c738d83ad", "url": "https://api.github.com/repos/symfony/validator/zipball/621b820204a238d754f7f60241fcbdb1687641ea",
"reference": "bdc6d04ba95c73ccbf906b4ad9b8775c738d83ad", "reference": "621b820204a238d754f7f60241fcbdb1687641ea",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1725,7 +1730,7 @@
"symfony/yaml": "<4.4" "symfony/yaml": "<4.4"
}, },
"require-dev": { "require-dev": {
"doctrine/annotations": "^1.13", "doctrine/annotations": "^1.13|^2",
"doctrine/cache": "^1.11|^2.0", "doctrine/cache": "^1.11|^2.0",
"egulias/email-validator": "^2.1.10|^3", "egulias/email-validator": "^2.1.10|^3",
"symfony/cache": "^4.4|^5.0|^6.0", "symfony/cache": "^4.4|^5.0|^6.0",
@ -1756,7 +1761,7 @@
"symfony/translation": "For translating validation errors.", "symfony/translation": "For translating validation errors.",
"symfony/yaml": "" "symfony/yaml": ""
}, },
"time": "2022-04-15T08:07:45+00:00", "time": "2022-12-21T19:20:17+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -1784,7 +1789,7 @@
"description": "Provides tools to validate values", "description": "Provides tools to validate values",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/validator/tree/v5.4.8" "source": "https://github.com/symfony/validator/tree/v5.4.17"
}, },
"funding": [ "funding": [
{ {
@ -1803,6 +1808,6 @@
"install-path": "../symfony/validator" "install-path": "../symfony/validator"
} }
], ],
"dev": false, "dev": true,
"dev-package-names": [] "dev-package-names": []
} }

View File

@ -1,28 +1,28 @@
<?php return array( <?php return array(
'root' => array( 'root' => array(
'name' => '__root__', 'name' => '__root__',
'pretty_version' => '1.0.0+no-version-set', 'pretty_version' => 'dev-master',
'version' => '1.0.0.0', 'version' => 'dev-master',
'reference' => NULL, 'reference' => '4f030b05af2d8e0e4875c40d35686b88e7924979',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'dev' => false, 'dev' => true,
), ),
'versions' => array( 'versions' => array(
'__root__' => array( '__root__' => array(
'pretty_version' => '1.0.0+no-version-set', 'pretty_version' => 'dev-master',
'version' => '1.0.0.0', 'version' => 'dev-master',
'reference' => NULL, 'reference' => '4f030b05af2d8e0e4875c40d35686b88e7924979',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'bacon/bacon-qr-code' => array( 'bacon/bacon-qr-code' => array(
'pretty_version' => '2.0.7', 'pretty_version' => '2.0.8',
'version' => '2.0.7.0', 'version' => '2.0.8.0',
'reference' => 'd70c840f68657ce49094b8d91f9ee0cc07fbf66c', 'reference' => '8674e51bb65af933a5ffaf1c308a660387c35c22',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../bacon/bacon-qr-code', 'install_path' => __DIR__ . '/../bacon/bacon-qr-code',
'aliases' => array(), 'aliases' => array(),
@ -47,9 +47,9 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'khanamiryan/qrcode-detector-decoder' => array( 'khanamiryan/qrcode-detector-decoder' => array(
'pretty_version' => '1.0.5.2', 'pretty_version' => '1.0.6',
'version' => '1.0.5.2', 'version' => '1.0.6.0',
'reference' => '04fdd58d86a387065f707dc6d3cc304c719910c1', 'reference' => '45326fb83a2a375065dbb3a134b5b8a5872da569',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../khanamiryan/qrcode-detector-decoder', 'install_path' => __DIR__ . '/../khanamiryan/qrcode-detector-decoder',
'aliases' => array(), 'aliases' => array(),
@ -65,9 +65,9 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'myclabs/php-enum' => array( 'myclabs/php-enum' => array(
'pretty_version' => '1.8.3', 'pretty_version' => '1.8.4',
'version' => '1.8.3.0', 'version' => '1.8.4.0',
'reference' => 'b942d263c641ddb5190929ff840c68f78713e937', 'reference' => 'a867478eae49c9f59ece437ae7f9506bfaa27483',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../myclabs/php-enum', 'install_path' => __DIR__ . '/../myclabs/php-enum',
'aliases' => array(), 'aliases' => array(),
@ -83,8 +83,8 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/deprecation-contracts' => array( 'symfony/deprecation-contracts' => array(
'pretty_version' => 'v2.5.1', 'pretty_version' => 'v2.5.2',
'version' => '2.5.1.0', 'version' => '2.5.2.0',
'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66', 'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/deprecation-contracts', 'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
@ -92,135 +92,135 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/intl' => array( 'symfony/intl' => array(
'pretty_version' => 'v5.4.8', 'pretty_version' => 'v5.4.15',
'version' => '5.4.8.0', 'version' => '5.4.15.0',
'reference' => 'b9e17d7ab867ce99f89950ebced0fa91076ba12b', 'reference' => '2cb39da7f6e7b7344d7d5317dbee8db9d12cc714',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/intl', 'install_path' => __DIR__ . '/../symfony/intl',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/options-resolver' => array( 'symfony/options-resolver' => array(
'pretty_version' => 'v5.4.3', 'pretty_version' => 'v5.4.11',
'version' => '5.4.3.0', 'version' => '5.4.11.0',
'reference' => 'cc1147cb11af1b43f503ac18f31aa3bec213aba8', 'reference' => '54f14e36aa73cb8f7261d7686691fd4d75ea2690',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/options-resolver', 'install_path' => __DIR__ . '/../symfony/options-resolver',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-ctype' => array( 'symfony/polyfill-ctype' => array(
'pretty_version' => 'v1.26.0', 'pretty_version' => 'v1.27.0',
'version' => '1.26.0.0', 'version' => '1.27.0.0',
'reference' => '6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4', 'reference' => '5bbc823adecdae860bb64756d639ecfec17b050a',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-ctype', 'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-intl-grapheme' => array( 'symfony/polyfill-intl-grapheme' => array(
'pretty_version' => 'v1.26.0', 'pretty_version' => 'v1.27.0',
'version' => '1.26.0.0', 'version' => '1.27.0.0',
'reference' => '433d05519ce6990bf3530fba6957499d327395c2', 'reference' => '511a08c03c1960e08a883f4cffcacd219b758354',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-intl-icu' => array( 'symfony/polyfill-intl-icu' => array(
'pretty_version' => 'v1.26.0', 'pretty_version' => 'v1.27.0',
'version' => '1.26.0.0', 'version' => '1.27.0.0',
'reference' => 'e407643d610e5f2c8a4b14189150f68934bf5e48', 'reference' => 'a3d9148e2c363588e05abbdd4ee4f971f0a5330c',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-icu', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-icu',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-intl-normalizer' => array( 'symfony/polyfill-intl-normalizer' => array(
'pretty_version' => 'v1.26.0', 'pretty_version' => 'v1.27.0',
'version' => '1.26.0.0', 'version' => '1.27.0.0',
'reference' => '219aa369ceff116e673852dce47c3a41794c14bd', 'reference' => '19bd1e4fcd5b91116f14d8533c57831ed00571b6',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-mbstring' => array( 'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.26.0', 'pretty_version' => 'v1.27.0',
'version' => '1.26.0.0', 'version' => '1.27.0.0',
'reference' => '9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e', 'reference' => '8ad114f6b39e2c98a8b0e3bd907732c207c2b534',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-php73' => array( 'symfony/polyfill-php73' => array(
'pretty_version' => 'v1.26.0', 'pretty_version' => 'v1.27.0',
'version' => '1.26.0.0', 'version' => '1.27.0.0',
'reference' => 'e440d35fa0286f77fb45b79a03fedbeda9307e85', 'reference' => '9e8ecb5f92152187c4799efd3c96b78ccab18ff9',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php73', 'install_path' => __DIR__ . '/../symfony/polyfill-php73',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-php80' => array( 'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.26.0', 'pretty_version' => 'v1.27.0',
'version' => '1.26.0.0', 'version' => '1.27.0.0',
'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace', 'reference' => '7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80', 'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/polyfill-php81' => array( 'symfony/polyfill-php81' => array(
'pretty_version' => 'v1.26.0', 'pretty_version' => 'v1.27.0',
'version' => '1.26.0.0', 'version' => '1.27.0.0',
'reference' => '13f6d1271c663dc5ae9fb843a8f16521db7687a1', 'reference' => '707403074c8ea6e2edaf8794b0157a0bfa52157a',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php81', 'install_path' => __DIR__ . '/../symfony/polyfill-php81',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/property-access' => array( 'symfony/property-access' => array(
'pretty_version' => 'v5.4.8', 'pretty_version' => 'v5.4.15',
'version' => '5.4.8.0', 'version' => '5.4.15.0',
'reference' => 'fe501d498d6ec7e9efe928c90fabedf629116495', 'reference' => '0f3e8f40a1d3da90f674b3dd772e4777ccde4273',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/property-access', 'install_path' => __DIR__ . '/../symfony/property-access',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/property-info' => array( 'symfony/property-info' => array(
'pretty_version' => 'v5.4.9', 'pretty_version' => 'v5.4.17',
'version' => '5.4.9.0', 'version' => '5.4.17.0',
'reference' => '6f0a452aaba45e763f89e328df437f73a720e18e', 'reference' => '12e1f7b3d73b1f3690aa524b92b5de9937507361',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/property-info', 'install_path' => __DIR__ . '/../symfony/property-info',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/string' => array( 'symfony/string' => array(
'pretty_version' => 'v5.4.9', 'pretty_version' => 'v5.4.17',
'version' => '5.4.9.0', 'version' => '5.4.17.0',
'reference' => '985e6a9703ef5ce32ba617c9c7d97873bb7b2a99', 'reference' => '55733a8664b8853b003e70251c58bc8cb2d82a6b',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/string', 'install_path' => __DIR__ . '/../symfony/string',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/translation-contracts' => array( 'symfony/translation-contracts' => array(
'pretty_version' => 'v2.5.1', 'pretty_version' => 'v2.5.2',
'version' => '2.5.1.0', 'version' => '2.5.2.0',
'reference' => '1211df0afa701e45a04253110e959d4af4ef0f07', 'reference' => '136b19dd05cdf0709db6537d058bcab6dd6e2dbe',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/translation-contracts', 'install_path' => __DIR__ . '/../symfony/translation-contracts',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'symfony/validator' => array( 'symfony/validator' => array(
'pretty_version' => 'v5.4.8', 'pretty_version' => 'v5.4.17',
'version' => '5.4.8.0', 'version' => '5.4.17.0',
'reference' => 'bdc6d04ba95c73ccbf906b4ad9b8775c738d83ad', 'reference' => '621b820204a238d754f7f60241fcbdb1687641ea',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../symfony/validator', 'install_path' => __DIR__ . '/../symfony/validator',
'aliases' => array(), 'aliases' => array(),

View File

@ -16,6 +16,7 @@ $ composer require khanamiryan/qrcode-detector-decoder
## Usage ## Usage
```php ```php
require __DIR__ . "/vendor/autoload.php"; require __DIR__ . "/vendor/autoload.php";
use Zxing\QrReader;
$qrcode = new QrReader('path/to_image'); $qrcode = new QrReader('path/to_image');
$text = $qrcode->text(); //return decoded text from QR Code $text = $qrcode->text(); //return decoded text from QR Code
``` ```

View File

@ -24,7 +24,9 @@
"php": ">=5.6" "php": ">=5.6"
}, },
"require-dev": { "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": { "autoload": {
"psr-4": { "psr-4": {
@ -33,5 +35,15 @@
"files": [ "files": [
"lib/Common/customFunctions.php" "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/"
}
} }
} }

View 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');
};

View File

@ -30,67 +30,64 @@ use Zxing\Common\BitMatrix;
*/ */
abstract class Binarizer abstract class Binarizer
{ {
private $source; protected function __construct(private $source)
{
}
protected function __construct($source) /**
{ * @return LuminanceSource
$this->source = $source; */
} final public function getLuminanceSource()
{
return $this->source;
}
/** /**
* @return LuminanceSource * 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.
public final function getLuminanceSource() * 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
return $this->source; * 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 * Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
* cached data. Callers should assume this method is expensive and call it as seldom as possible. * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
* This method is intended for decoding 1D barcodes and may choose to apply sharpening. * may not apply sharpening. Therefore, a row from this matrix may not be identical to one
* For callers which only examine one row of pixels at a time, the same BitArray should be reused * fetched using getBlackRow(), so don't mix and match between them.
* and passed in with each call for performance. However it is legal to keep more than one row *
* at a time if needed. * @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
* @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. abstract public function getBlackMatrix();
* 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 * Creates a new object with the same type as this Binarizer implementation, but with pristine
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or * state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one * of 1 bit data. See Effective Java for why we can't use Java's clone() method.
* fetched using getBlackRow(), so don't mix and match between them. *
* * @param $source The LuminanceSource this Binarizer will operate on.
* @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 * @return Binarizer A new concrete Binarizer implementation object.
*/ */
public abstract function getBlackMatrix(); abstract public function createBinarizer($source);
/** final public function getWidth()
* 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 return $this->source->getWidth();
* 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);
public final function getWidth() final public function getHeight()
{ {
return $this->source->getWidth(); return $this->source->getHeight();
} }
public final function getHeight()
{
return $this->source->getHeight();
}
} }

View File

@ -27,140 +27,140 @@ use Zxing\Common\BitMatrix;
*/ */
final class BinaryBitmap final class BinaryBitmap
{ {
private $binarizer; private readonly \Zxing\Binarizer $binarizer;
private $matrix; private ?\Zxing\Common\BitMatrix $matrix = null;
public function __construct(Binarizer $binarizer) public function __construct(Binarizer $binarizer)
{ {
if ($binarizer === null) { if ($binarizer === null) {
throw new \InvalidArgumentException("Binarizer must be non-null."); throw new \InvalidArgumentException("Binarizer must be non-null.");
} }
$this->binarizer = $binarizer; $this->binarizer = $binarizer;
} }
/** /**
* @return int The width of the bitmap. * @return int The width of the bitmap.
*/ */
public function getWidth() public function getWidth()
{ {
return $this->binarizer->getWidth(); return $this->binarizer->getWidth();
} }
/** /**
* @return int The height of the bitmap. * @return int The height of the bitmap.
*/ */
public function getHeight() public function getHeight()
{ {
return $this->binarizer->getHeight(); return $this->binarizer->getHeight();
} }
/** /**
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return * 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. * 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. * 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 $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. * @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. * 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). * @return array The array of bits for this row (true means black).
* @throws NotFoundException if row can't be binarized * @throws NotFoundException if row can't be binarized
*/ */
public function getBlackRow($y, $row) public function getBlackRow($y, $row)
{ {
return $this->binarizer->getBlackRow($y, $row); return $this->binarizer->getBlackRow($y, $row);
} }
/** /**
* @return bool Whether this bitmap can be cropped. * @return bool Whether this bitmap can be cropped.
*/ */
public function isCropSupported() public function isCropSupported()
{ {
return $this->binarizer->getLuminanceSource()->isCropSupported(); return $this->binarizer->getLuminanceSource()->isCropSupported();
} }
/** /**
* Returns a new object with cropped image data. Implementations may keep a reference to the * 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. * original data rather than a copy. Only callable if isCropSupported() is true.
* *
* @param left The left coordinate, which must be in [0,getWidth()) * @param $left The left coordinate, which must be in [0,getWidth())
* @param top The top coordinate, which must be in [0,getHeight()) * @param $top The top coordinate, which must be in [0,getHeight())
* @param width The width of the rectangle to crop. * @param $width The width of the rectangle to crop.
* @param height The height of the rectangle to crop. * @param $height The height of the rectangle to crop.
* *
* @return BinaryBitmap A cropped version of this object. * @return BinaryBitmap A cropped version of this object.
*/ */
public function crop($left, $top, $width, $height) public function crop($left, $top, $width, $height): \Zxing\BinaryBitmap
{ {
$newSource = $this->binarizer->getLuminanceSource()->crop($left, $top, $width, $height); $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. * @return Whether this bitmap supports counter-clockwise rotation.
*/ */
public function isRotateSupported() public function isRotateSupported()
{ {
return $this->binarizer->getLuminanceSource()->isRotateSupported(); return $this->binarizer->getLuminanceSource()->isRotateSupported();
} }
/** /**
* Returns a new object with rotated image data by 90 degrees counterclockwise. * Returns a new object with rotated image data by 90 degrees counterclockwise.
* Only callable if {@link #isRotateSupported()} is true. * Only callable if {@link #isRotateSupported()} is true.
* *
* @return BinaryBitmap A rotated version of this object. * @return BinaryBitmap A rotated version of this object.
*/ */
public function rotateCounterClockwise() public function rotateCounterClockwise(): \Zxing\BinaryBitmap
{ {
$newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise(); $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. * Returns a new object with rotated image data by 45 degrees counterclockwise.
* Only callable if {@link #isRotateSupported()} is true. * Only callable if {@link #isRotateSupported()} is true.
* *
* @return BinaryBitmap A rotated version of this object. * @return BinaryBitmap A rotated version of this object.
*/ */
public function rotateCounterClockwise45() public function rotateCounterClockwise45(): \Zxing\BinaryBitmap
{ {
$newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise45(); $newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise45();
return new BinaryBitmap($this->binarizer->createBinarizer($newSource)); return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
} }
public function toString() public function toString()
{ {
try { try {
return $this->getBlackMatrix()->toString(); return $this->getBlackMatrix()->toString();
} catch (NotFoundException $e) { } catch (NotFoundException) {
} }
return ''; return '';
} }
/** /**
* Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive * 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 * 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 * 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. * fetched using getBlackRow(), so don't mix and match between them.
* *
* @return BitMatrix The 2D array of bits for the image (true means black). * @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 * @throws NotFoundException if image can't be binarized to make a matrix
*/ */
public function getBlackMatrix() public function getBlackMatrix()
{ {
// The matrix is created on demand the first time it is requested, then cached. There are two // The matrix is created on demand the first time it is requested, then cached. There are two
// reasons for this: // reasons for this:
// 1. This work will never be done if the caller only installs 1D Reader objects, or if a // 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. // 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. // 2. This work will only be done once even if the caller installs multiple 2D Readers.
if ($this->matrix === null) { if ($this->matrix === null) {
$this->matrix = $this->binarizer->getBlackMatrix(); $this->matrix = $this->binarizer->getBlackMatrix();
} }
return $this->matrix; return $this->matrix;
} }
} }

View File

@ -25,18 +25,18 @@ namespace Zxing;
*/ */
final class ChecksumException extends ReaderException final class ChecksumException extends ReaderException
{ {
private static $instance; private static ?\Zxing\ChecksumException $instance = null;
public static function getChecksumInstance($cause = null) public static function getChecksumInstance($cause = null)
{ {
if (self::$isStackTrace) { if (self::$isStackTrace) {
return new ChecksumException($cause); return new ChecksumException($cause);
} else { } else {
if (!self::$instance) { if (!self::$instance) {
self::$instance = new ChecksumException($cause); self::$instance = new ChecksumException($cause);
} }
return self::$instance; return self::$instance;
} }
} }
} }

View File

@ -2,103 +2,95 @@
namespace Zxing\Common; namespace Zxing\Common;
use \Zxing\NotFoundException;
use ReflectionClass; use ReflectionClass;
/** /**
* A general enum implementation until we got SplEnum. * A general enum implementation until we got SplEnum.
*/ */
final class AbstractEnum final class AbstractEnum implements \Stringable
{ {
/** /**
* Default value. * Default value.
*/ */
const __default = null; public const __default = null;
/** /**
* Current value. * Current value.
* *
* @var mixed * @var mixed
*/ */
protected $value; private $value;
/** /**
* Cache of constants. * Cache of constants.
* *
* @var array * @var array<string, mixed>|null
*/ */
protected $constants; private ?array $constants = null;
/**
* Whether to handle values strict or not.
*
* @var boolean
*/
protected $strict;
/** /**
* Creates a new enum. * Creates a new enum.
* *
* @param mixed $initialValue * @param mixed $initialValue
* @param boolean $strict * @param boolean $strict
*/ */
public function __construct($initialValue = null, $strict = false) public function __construct($initialValue = null, private $strict = false)
{ {
$this->strict = $strict; $this->change($initialValue);
$this->change($initialValue); }
}
/** /**
* Changes the value of the enum. * Changes the value of the enum.
* *
* @param mixed $value * @param mixed $value
* *
* @return void * @return void
*/ */
public function change($value) public function change($value)
{ {
if (!in_array($value, $this->getConstList(), $this->strict)) { if (!in_array($value, $this->getConstList(), $this->strict)) {
throw new \UnexpectedValueException('Value not a const in enum ' . get_class($this)); throw new \UnexpectedValueException('Value not a const in enum ' . $this::class);
} }
$this->value = $value; $this->value = $value;
} }
/** /**
* Gets all constants (possible values) as an array. * Gets all constants (possible values) as an array.
* *
* @param boolean $includeDefault * @param boolean $includeDefault
* *
* @return array * @return array
*/ */
public function getConstList($includeDefault = true) public function getConstList($includeDefault = true)
{ {
if ($this->constants === null) { if ($this->constants === null) {
$reflection = new ReflectionClass($this); $reflection = new ReflectionClass($this);
$this->constants = $reflection->getConstants(); $this->constants = $reflection->getConstants();
} }
if ($includeDefault) { if ($includeDefault) {
return $this->constants; return $this->constants;
} }
$constants = $this->constants; $constants = $this->constants;
unset($constants['__default']); unset($constants['__default']);
return $constants; return $constants;
} }
/** /**
* Gets current value. * Gets current value.
* *
* @return mixed * @return mixed
*/ */
public function get() public function get()
{ {
return $this->value; return $this->value;
} }
/** /**
* Gets the name of the enum. * Gets the name of the enum.
* *
* @return string * @return string
*/ */
public function __toString() public function __toString(): string
{ {
return (string)array_search($this->value, $this->getConstList()); return (string)array_search($this->value, $this->getConstList());
} }
} }

View File

@ -32,391 +32,394 @@ namespace Zxing\Common;
final class BitArray final class BitArray
{ {
/**
private $bits; * @var mixed[]|mixed|int[]|null
private $size; */
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) { private static function makeArray($size)
$this->$size = 0; {
$this->bits = []; return [];
} elseif ($bits && !$size) { }
$this->size = $bits;
$this->bits = $this->makeArray($bits);
} else {
$this->bits = $bits;
$this->size = $size;
}
} public function getSize()
{
return $this->size;
}
private static function makeArray($size) public function getSizeInBytes()
{ {
return []; return ($this->size + 7) / 8;
} }
public function getSize() /**
{ * Sets bit i.
return $this->size; *
} * @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() /**
{ * Flips bit i.
return ($this->size + 7) / 8; *
} * @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 first $from bit to check
* *
* @param i bit to set * @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
public function set($i) * @see #getNextUnset(int)
{ */
$this->bits[(int)($i / 32)] |= 1 << ($i & 0x1F); public function getNextSet($from)
$this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]); {
} 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
/** return $result > $this->size ? $this->size : $result;
* 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)]);
}
/** /**
* @param from first bit to check * @param index $from to start looking for unset bit
* *
* @return index of first bit that is set, starting from the given index, or size if none are set * @return index of next unset bit, or {@code size} if none are unset until the end
* at or beyond this given index * @see #getNextSet(int)
* @see #getNextUnset(int) */
*/ public function getNextUnset($from)
public function getNextSet($from) {
{ if ($from >= $this->size) {
if ($from >= $this->size) { return $this->size;
return $this->size; }
} $bitsOffset = (int)($from / 32);
$bitsOffset = (int)($from / 32); $currentBits = ~$this->bits[$bitsOffset];
$currentBits = (int)$this->bits[$bitsOffset]; // mask off lesser bits first
// mask off lesser bits first $currentBits &= ~((1 << ($from & 0x1F)) - 1);
$currentBits &= ~((1 << ($from & 0x1F)) - 1); while ($currentBits == 0) {
while ($currentBits == 0) { if (++$bitsOffset == (is_countable($this->bits) ? count($this->bits) : 0)) {
if (++$bitsOffset == count($this->bits)) { return $this->size;
return $this->size; }
} $currentBits = (~$this->bits[$bitsOffset]);
$currentBits = $this->bits[$bitsOffset]; }
} $result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits);
$result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits); //numberOfTrailingZeros
return $result > $this->size ? $this->size : $result; return $result > $this->size ? $this->size : $result;
} }
/** /**
* @param from index to start looking for unset bit * Sets a block of 32 bits, starting at bit i.
* *
* @return index of next unset bit, or {@code size} if none are unset until the end * @param first $i bit to set
* @see #getNextSet(int) * @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 getNextUnset($from) */
{ public function setBulk($i, $newBits): void
if ($from >= $this->size) { {
return $this->size; $this->bits[(int)($i / 32)] = $newBits;
} }
$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);
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. * Clears all bits (sets to false).
* */
* @param i first bit to set public function clear(): void
* @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. $max = is_countable($this->bits) ? count($this->bits) : 0;
*/ for ($i = 0; $i < $max; $i++) {
public function setBulk($i, $newBits) $this->bits[$i] = 0;
{ }
$this->bits[(int)($i / 32)] = $newBits; }
}
/** /**
* Sets a range of bits. * Efficient method to check if a range of bits is set, or not set.
* *
* @param start start of range, inclusive. * @param start $start of range, inclusive.
* @param end end of range, exclusive * @param end $end of range, exclusive
*/ * @param if $value true, checks that bits in range are set, otherwise checks that they are not set
public function setRange($start, $end) *
{ * @return true iff all bits are set or not set in range, according to value argument
if ($end < $start) { * @throws InvalidArgumentException if end is less than or equal to start
throw new \InvalidArgumentException(); */
} public function isRange($start, $end, $value)
if ($end == $start) { {
return; if ($end < $start) {
} throw new \InvalidArgumentException();
$end--; // will be easier to treat this as the last actually set bit -- inclusive }
$firstInt = (int)($start / 32); if ($end == $start) {
$lastInt = (int)($end / 32); return true; // empty range matches
for ($i = $firstInt; $i <= $lastInt; $i++) { }
$firstBit = $i > $firstInt ? 0 : $start & 0x1F; $end--; // will be easier to treat this as the last actually set bit -- inclusive
$lastBit = $i < $lastInt ? 31 : $end & 0x1F; $firstInt = (int)($start / 32);
$mask = 0; $lastInt = (int)($end / 32);
if ($firstBit == 0 && $lastBit == 31) { for ($i = $firstInt; $i <= $lastInt; $i++) {
$mask = -1; $firstBit = $i > $firstInt ? 0 : $start & 0x1F;
} else { $lastBit = $i < $lastInt ? 31 : $end & 0x1F;
$mask = 0; $mask = 0;
for ($j = $firstBit; $j <= $lastBit; $j++) { if ($firstBit == 0 && $lastBit == 31) {
$mask |= 1 << $j; $mask = -1;
} } else {
} $mask = 0;
$this->bits[$i] = ($this->bits[$i] | $mask); for ($j = $firstBit; $j <= $lastBit; $j++) {
} $mask = ($mask | (1 << $j));
} }
}
/** // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
* Clears all bits (sets to false). // 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)) {
public function clear() return false;
{ }
$max = count($this->bits); }
for ($i = 0; $i < $max; $i++) {
$this->bits[$i] = 0;
}
}
/** return true;
* 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 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 * Appends the least-significant bits, from value, in order from most-significant to
if (($this->bits[$i] & $mask) != ($value ? $mask : 0)) { * least-significant. For example, appending 6 bits from 0x000001E will append the bits
return false; * 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;
}
}
/** public function appendBit($bit): void
* 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 $this->ensureCapacity($this->size + 1);
* 0, 1, 1, 1, 1, 0 in that order. if ($bit) {
* $this->bits[(int)($this->size / 32)] |= 1 << ($this->size & 0x1F);
* @param value {@code int} containing bits to append }
* @param numBits bits from value to append $this->size++;
*/ }
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);
}
}
private function ensureCapacity($size) public function appendBitArray($other): void
{ {
if ($size > count($this->bits) * 32) { $otherSize = $other->size;
$newBits = $this->makeArray($size); $this->ensureCapacity($this->size + $otherSize);
$newBits = arraycopy($this->bits, 0, $newBits, 0, count($this->bits)); for ($i = 0; $i < $otherSize; $i++) {
$this->bits = $newBits; $this->appendBit($other->get($i));
} }
} }
public function appendBit($bit) public function _xor($other)
{ {
$this->ensureCapacity($this->size + 1); if ((is_countable($this->bits) ? count($this->bits) : 0) !== (is_countable($other->bits) ? count($other->bits) : 0)) {
if ($bit) { throw new \InvalidArgumentException("Sizes don't match");
$this->bits[(int)($this->size / 32)] |= 1 << ($this->size & 0x1F); }
} $count = is_countable($this->bits) ? count($this->bits) : 0;
$this->size++; 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; * @param first $bitOffset bit to start writing
$this->ensureCapacity($this->size + $otherSize); * @param array $array to write into. Bytes are written most-significant byte first. This is the opposite
for ($i = 0; $i < $otherSize; $i++) { * of the internal representation, which is exposed by {@link #getBitArray()}
$this->appendBit($other->get($i)); * @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) /**
{ * @param $i ; bit to get
if (count($this->bits) !== count($other->bits)) { *
throw new \InvalidArgumentException("Sizes don't match"); * @return true iff bit i is set
} */
$count = count($this->bits); public function get($i)
for ($i = 0; $i < $count; $i++) { {
// The last byte could be incomplete (i.e. not have 8 bits in $key = (int)($i / 32);
// it) but there is no problem since 0 XOR 0 == 0.
$this->bits[$i] ^= $other->bits[$i];
}
}
/** return ($this->bits[$key] & (1 << ($i & 0x1F))) != 0;
* }
* @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;
}
}
/** /**
* @param $i ; bit to get * @return array underlying array of ints. The first element holds the first 32 bits, and the least
* * significant bit is bit 0.
* @return true iff bit i is set */
*/ public function getBitArray()
public function get($i) {
{ return $this->bits;
$key = (int)($i / 32); }
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;
}
/** public function equals($o)
* @return array underlying array of ints. The first element holds the first 32 bits, and the least {
* significant bit is bit 0. if (!($o instanceof BitArray)) {
*/ return false;
public function getBitArray() }
{ $other = $o;
return $this->bits;
}
/** return $this->size == $other->size && $this->bits === $other->bits;
* 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;
}
public function equals($o) public function hashCode()
{ {
if (!($o instanceof BitArray)) { return 31 * $this->size + hashCode($this->bits);
return false; }
}
$other = $o;
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 (string)$result;
{ }
return 31 * $this->size + hashCode($this->bits);
}
public function toString() public function _clone(): \Zxing\Common\BitArray
{ {
$result = ''; return new BitArray($this->bits, $this->size);
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);
}
} }

File diff suppressed because one or more lines are too long

View File

@ -28,91 +28,88 @@ namespace Zxing\Common;
*/ */
final class BitSource final class BitSource
{ {
private int $byteOffset = 0;
private int $bitOffset = 0;
private $bytes; /**
private $byteOffset = 0; * @param bytes $bytes from which this will read bits. Bits will be read from the first byte first.
private $bitOffset = 0; * 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. * @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
* Bits are read within a byte from most-significant to least-significant bit. */
*/ public function getBitOffset()
public function __construct($bytes) {
{ return $this->bitOffset;
$this->bytes = $bytes; }
}
/** /**
* @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}. * @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
*/ */
public function getBitOffset() public function getByteOffset()
{ {
return $this->bitOffset; return $this->byteOffset;
} }
/** /**
* @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}. * @param number $numBits of bits to read
*/ *
public function getByteOffset() * @return int representing the bits read. The bits will appear as the least-significant
{ * bits of the int
return $this->byteOffset; * @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;
* @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; // 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 // Next read whole bytes
if ($this->bitOffset > 0) { if ($numBits > 0) {
$bitsLeft = 8 - $this->bitOffset; while ($numBits >= 8) {
$toRead = $numBits < $bitsLeft ? $numBits : $bitsLeft; $result = ($result << 8) | ($this->bytes[$this->byteOffset] & 0xFF);
$bitsToNotRead = $bitsLeft - $toRead; $this->byteOffset++;
$mask = (0xFF >> (8 - $toRead)) << $bitsToNotRead; $numBits -= 8;
$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 // Finally read a partial byte
if ($numBits > 0) { if ($numBits > 0) {
while ($numBits >= 8) { $bitsToNotRead = 8 - $numBits;
$result = ($result << 8) | ($this->bytes[$this->byteOffset] & 0xFF); $mask = (0xFF >> $bitsToNotRead) << $bitsToNotRead;
$this->byteOffset++; $result = ($result << $numBits) | (($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead);
$numBits -= 8; $this->bitOffset += $numBits;
} }
}
// Finally read a partial byte return $result;
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 number of bits that can be read successfully
*/
/** public function available()
* @return number of bits that can be read successfully {
*/ return 8 * ((is_countable($this->bytes) ? count($this->bytes) : 0) - $this->byteOffset) - $this->bitOffset;
public function available() }
{
return 8 * (count($this->bytes) - $this->byteOffset) - $this->bitOffset;
}
} }

View File

@ -8,151 +8,147 @@ namespace Zxing\Common;
*/ */
final class CharacterSetECI final class CharacterSetECI
{ {
/**#@+ /**#@+
* Character set constants. * Character set constants.
*/ */
const CP437 = 0; public const CP437 = 0;
const ISO8859_1 = 1; public const ISO8859_1 = 1;
const ISO8859_2 = 4; public const ISO8859_2 = 4;
const ISO8859_3 = 5; public const ISO8859_3 = 5;
const ISO8859_4 = 6; public const ISO8859_4 = 6;
const ISO8859_5 = 7; public const ISO8859_5 = 7;
const ISO8859_6 = 8; public const ISO8859_6 = 8;
const ISO8859_7 = 9; public const ISO8859_7 = 9;
const ISO8859_8 = 10; public const ISO8859_8 = 10;
const ISO8859_9 = 11; public const ISO8859_9 = 11;
const ISO8859_10 = 12; public const ISO8859_10 = 12;
const ISO8859_11 = 13; public const ISO8859_11 = 13;
const ISO8859_12 = 14; public const ISO8859_12 = 14;
const ISO8859_13 = 15; public const ISO8859_13 = 15;
const ISO8859_14 = 16; public const ISO8859_14 = 16;
const ISO8859_15 = 17; public const ISO8859_15 = 17;
const ISO8859_16 = 18; public const ISO8859_16 = 18;
const SJIS = 20; public const SJIS = 20;
const CP1250 = 21; public const CP1250 = 21;
const CP1251 = 22; public const CP1251 = 22;
const CP1252 = 23; public const CP1252 = 23;
const CP1256 = 24; public const CP1256 = 24;
const UNICODE_BIG_UNMARKED = 25; public const UNICODE_BIG_UNMARKED = 25;
const UTF8 = 26; public const UTF8 = 26;
const ASCII = 27; public const ASCII = 27;
const BIG5 = 28; public const BIG5 = 28;
const GB18030 = 29; public const GB18030 = 29;
const EUC_KR = 30; public const EUC_KR = 30;
/** /**
* Map between character names and their ECI values. * Map between character names and their ECI values.
* */
* @var array private static array $nameToEci = [
*/ 'ISO-8859-1' => self::ISO8859_1,
protected static $nameToEci = [ 'ISO-8859-2' => self::ISO8859_2,
'ISO-8859-1' => self::ISO8859_1, 'ISO-8859-3' => self::ISO8859_3,
'ISO-8859-2' => self::ISO8859_2, 'ISO-8859-4' => self::ISO8859_4,
'ISO-8859-3' => self::ISO8859_3, 'ISO-8859-5' => self::ISO8859_5,
'ISO-8859-4' => self::ISO8859_4, 'ISO-8859-6' => self::ISO8859_6,
'ISO-8859-5' => self::ISO8859_5, 'ISO-8859-7' => self::ISO8859_7,
'ISO-8859-6' => self::ISO8859_6, 'ISO-8859-8' => self::ISO8859_8,
'ISO-8859-7' => self::ISO8859_7, 'ISO-8859-9' => self::ISO8859_9,
'ISO-8859-8' => self::ISO8859_8, 'ISO-8859-10' => self::ISO8859_10,
'ISO-8859-9' => self::ISO8859_9, 'ISO-8859-11' => self::ISO8859_11,
'ISO-8859-10' => self::ISO8859_10, 'ISO-8859-12' => self::ISO8859_12,
'ISO-8859-11' => self::ISO8859_11, 'ISO-8859-13' => self::ISO8859_13,
'ISO-8859-12' => self::ISO8859_12, 'ISO-8859-14' => self::ISO8859_14,
'ISO-8859-13' => self::ISO8859_13, 'ISO-8859-15' => self::ISO8859_15,
'ISO-8859-14' => self::ISO8859_14, 'ISO-8859-16' => self::ISO8859_16,
'ISO-8859-15' => self::ISO8859_15, 'SHIFT-JIS' => self::SJIS,
'ISO-8859-16' => self::ISO8859_16, 'WINDOWS-1250' => self::CP1250,
'SHIFT-JIS' => self::SJIS, 'WINDOWS-1251' => self::CP1251,
'WINDOWS-1250' => self::CP1250, 'WINDOWS-1252' => self::CP1252,
'WINDOWS-1251' => self::CP1251, 'WINDOWS-1256' => self::CP1256,
'WINDOWS-1252' => self::CP1252, 'UTF-16BE' => self::UNICODE_BIG_UNMARKED,
'WINDOWS-1256' => self::CP1256, 'UTF-8' => self::UTF8,
'UTF-16BE' => self::UNICODE_BIG_UNMARKED, 'ASCII' => self::ASCII,
'UTF-8' => self::UTF8, 'GBK' => self::GB18030,
'ASCII' => self::ASCII, 'EUC-KR' => self::EUC_KR,
'GBK' => self::GB18030, ];
'EUC-KR' => self::EUC_KR, /**#@-*/
]; /**
/**#@-*/ * Additional possible values for character sets.
/** */
* Additional possible values for character sets. private static array $additionalValues = [
* self::CP437 => 2,
* @var array self::ASCII => 170,
*/ ];
protected static $additionalValues = [ private static int|string|null $name = null;
self::CP437 => 2,
self::ASCII => 170,
];
private static $name = null;
/** /**
* Gets character set ECI by value. * Gets character set ECI by value.
* *
* @param string $name * @param string $value
* *
* @return CharacterSetEci|null * @return CharacterSetEci|null
*/ */
public static function getCharacterSetECIByValue($value) public static function getCharacterSetECIByValue($value)
{ {
if ($value < 0 || $value >= 900) { if ($value < 0 || $value >= 900) {
throw new \InvalidArgumentException('Value must be between 0 and 900'); throw new \InvalidArgumentException('Value must be between 0 and 900');
} }
if (false !== ($key = array_search($value, self::$additionalValues))) { if (false !== ($key = array_search($value, self::$additionalValues))) {
$value = $key; $value = $key;
} }
array_search($value, self::$nameToEci); array_search($value, self::$nameToEci);
try { try {
self::setName($value); self::setName($value);
return new self($value); return new self($value);
} catch (\UnexpectedValueException $e) { } catch (\UnexpectedValueException) {
return null; return null;
} }
} }
private static function setName($value) private static function setName($value)
{ {
foreach (self::$nameToEci as $name => $key) { foreach (self::$nameToEci as $name => $key) {
if ($key == $value) { if ($key == $value) {
self::$name = $name; self::$name = $name;
return true; return true;
} }
} }
if (self::$name == null) { if (self::$name == null) {
foreach (self::$additionalValues as $name => $key) { foreach (self::$additionalValues as $name => $key) {
if ($key == $value) { if ($key == $value) {
self::$name = $name; self::$name = $name;
return true; return true;
} }
} }
} }
} }
/** /**
* Gets character set ECI name. * Gets character set ECI name.
* *
* @return character set ECI name|null * @return character set ECI name|null
*/ */
public static function name() public static function name()
{ {
return self::$name; return self::$name;
} }
/** /**
* Gets character set ECI by name. * Gets character set ECI by name.
* *
* @param string $name * @param string $name
* *
* @return CharacterSetEci|null * @return CharacterSetEci|null
*/ */
public static function getCharacterSetECIByName($name) public static function getCharacterSetECIByName($name)
{ {
$name = strtoupper($name); $name = strtoupper($name);
if (isset(self::$nameToEci[$name])) { if (isset(self::$nameToEci[$name])) {
return new self(self::$nameToEci[$name]); return new self(self::$nameToEci[$name]);
} }
return null; return null;
} }
} }

View File

@ -26,97 +26,86 @@ namespace Zxing\Common;
*/ */
final class DecoderResult final class DecoderResult
{ {
/**
private $rawBytes; * @var mixed|null
private $text; */
private $byteSegments; private $errorsCorrected;
private $ecLevel; /**
private $errorsCorrected; * @var mixed|null
private $erasures; */
private $other; private $erasures;
private $structuredAppendParity; /**
private $structuredAppendSequenceNumber; * @var mixed|null
*/
private $other;
public function __construct( public function __construct(private $rawBytes, private $text, private $byteSegments, private $ecLevel, private $structuredAppendSequenceNumber = -1, private $structuredAppendParity = -1)
$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 getRawBytes() public function getRawBytes()
{ {
return $this->rawBytes; return $this->rawBytes;
} }
public function getText() public function getText()
{ {
return $this->text; return $this->text;
} }
public function getByteSegments() public function getByteSegments()
{ {
return $this->byteSegments; return $this->byteSegments;
} }
public function getECLevel() public function getECLevel()
{ {
return $this->ecLevel; return $this->ecLevel;
} }
public function getErrorsCorrected() public function getErrorsCorrected()
{ {
return $this->errorsCorrected; return $this->errorsCorrected;
} }
public function setErrorsCorrected($errorsCorrected) public function setErrorsCorrected($errorsCorrected): void
{ {
$this->errorsCorrected = $errorsCorrected; $this->errorsCorrected = $errorsCorrected;
} }
public function getErasures() public function getErasures()
{ {
return $this->erasures; return $this->erasures;
} }
public function setErasures($erasures) public function setErasures($erasures): void
{ {
$this->erasures = $erasures; $this->erasures = $erasures;
} }
public function getOther() public function getOther()
{ {
return $this->other; return $this->other;
} }
public function setOther($other) public function setOther($other): void
{ {
$this->other = $other; $this->other = $other;
} }
public function hasStructuredAppend() public function hasStructuredAppend()
{ {
return $this->structuredAppendParity >= 0 && $this->structuredAppendSequenceNumber >= 0; return $this->structuredAppendParity >= 0 && $this->structuredAppendSequenceNumber >= 0;
} }
public function getStructuredAppendParity() public function getStructuredAppendParity()
{ {
return $this->structuredAppendParity; return $this->structuredAppendParity;
} }
public function getStructuredAppendSequenceNumber()
{
return $this->structuredAppendSequenceNumber;
}
public function getStructuredAppendSequenceNumber()
{
return $this->structuredAppendSequenceNumber;
}
} }

View File

@ -24,70 +24,92 @@ use Zxing\NotFoundException;
*/ */
final class DefaultGridSampler extends GridSampler final class DefaultGridSampler extends GridSampler
{ {
//@Override //@Override
public function sampleGrid( public function sampleGrid(
$image, $image,
$dimensionX, $dimensionX,
$dimensionY, $dimensionY,
$p1ToX, $p1ToY, $p1ToX,
$p2ToX, $p2ToY, $p1ToY,
$p3ToX, $p3ToY, $p2ToX,
$p4ToX, $p4ToY, $p2ToY,
$p1FromX, $p1FromY, $p3ToX,
$p2FromX, $p2FromY, $p3ToY,
$p3FromX, $p3FromY, $p4ToX,
$p4FromX, $p4FromY $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( return $this->sampleGrid_($image, $dimensionX, $dimensionY, $transform);
$p1ToX, $p1ToY, $p2ToX, $p2ToY, $p3ToX, $p3ToY, $p4ToX, $p4ToY, }
$p1FromX, $p1FromY, $p2FromX, $p2FromY, $p3FromX, $p3FromY, $p4FromX, $p4FromY);
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 return $bits;
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;
}
} }

View File

@ -19,31 +19,30 @@ namespace Zxing\Common\Detector;
final class MathUtils 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));
}
/** public static function distance($aX, $aY, $bX, $bY)
* 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 $xDiff = $aX - $bX;
* differ slightly from {@link Math#round(float)} in that half rounds down for negative $yDiff = $aY - $bY;
* 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) return (float)sqrt($xDiff * $xDiff + $yDiff * $yDiff);
{ }
$xDiff = $aX - $bX;
$yDiff = $aY - $bY;
return (float)sqrt($xDiff * $xDiff + $yDiff * $yDiff);
}
} }

View File

@ -9,8 +9,8 @@
namespace Zxing\Common\Detector; namespace Zxing\Common\Detector;
use Zxing\BinaryBitmap; use Zxing\BinaryBitmap;
use \Zxing\NotFoundException; use Zxing\NotFoundException;
use \Zxing\ResultPoint; use Zxing\ResultPoint;
/* /*
* *
@ -35,200 +35,242 @@ import com.google.zxing.common.BitMatrix;
*/ */
class MonochromeRectangleDetector class MonochromeRectangleDetector
{ {
private static $MAX_MODULES = 32; private static int $MAX_MODULES = 32;
private $image;
public function __construct(BinaryBitmap $image) public function __construct(private readonly BinaryBitmap $image)
{ {
$this->image = $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;
/** $deltaY = max(1, $height / (self::$MAX_MODULES * 8));
* <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly $deltaX = max(1, $width / (self::$MAX_MODULES * 8));
* 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));
$top = 0; $top = 0;
$bottom = $height; $bottom = $height;
$left = 0; $left = 0;
$right = $width; $right = $width;
$pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right, $pointA = $this->findCornerFromCenter(
$halfHeight, -$deltaY, $top, $bottom, $halfWidth / 2); $halfWidth,
$top = (int)$pointA->getY() - 1; 0,
$pointB = $this->findCornerFromCenter($halfWidth, -$deltaX, $left, $right, $left,
$halfHeight, 0, $top, $bottom, $halfHeight / 2); $right,
$left = (int)$pointB->getX() - 1; $halfHeight,
$pointC = $this->findCornerFromCenter($halfWidth, $deltaX, $left, $right, -$deltaY,
$halfHeight, 0, $top, $bottom, $halfHeight / 2); $top,
$right = (int)$pointC->getX() + 1; $bottom,
$pointD = $this->findCornerFromCenter($halfWidth, 0, $left, $right, $halfWidth / 2
$halfHeight, $deltaY, $top, $bottom, $halfWidth / 2); );
$bottom = (int)$pointD->getY() + 1; $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. // Go try to find po$A again with better information -- might have been off at first.
$pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right, $pointA = $this->findCornerFromCenter(
$halfHeight, -$deltaY, $top, $bottom, $halfWidth / 4); $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 * 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. * point which should be within the barcode.
* *
* @param float $centerX center's x component (horizontal) * @param float $centerX center's x component (horizontal)
* @param float $deltaX same as deltaY but change in x per step instead * @param float $deltaX same as deltaY but change in x per step instead
* @param float $left minimum value of x * @param float $left minimum value of x
* @param float $right maximum value of x * @param float $right maximum value of x
* @param float $centerY center's y component (vertical) * @param float $centerY center's y component (vertical)
* @param float $deltaY change in y per step. If scanning up this is negative; down, positive; * @param float $deltaY change in y per step. If scanning up this is negative; down, positive;
* left or right, 0 * left or right, 0
* @param float $top minimum value of y to search through (meaningless when di == 0) * @param float $top minimum value of y to search through (meaningless when di == 0)
* @param float $bottom maximum value of y * @param float $bottom maximum value of y
* @param float $maxWhiteRun maximum run of white pixels that can still be considered to be within * @param float $maxWhiteRun maximum run of white pixels that can still be considered to be within
* the barcode * the barcode
* *
* @return ResultPoint $a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found * @return ResultPoint {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
* @throws NotFoundException if such a point cannot be found * @throws NotFoundException if such a point cannot be found
*/ */
private function findCornerFromCenter($centerX, private function findCornerFromCenter(
$deltaX, $centerX,
$left, $deltaX,
$right, $left,
$centerY, $right,
$deltaY, $centerY,
$top, $deltaY,
$bottom, $top,
$maxWhiteRun) $bottom,
{ $maxWhiteRun
$lastRange = null; ): \Zxing\ResultPoint
for ($y = $centerY, $x = $centerX; {
$y < $bottom && $y >= $top && $x < $right && $x >= $left; $lastRange = null;
$y += $deltaY, $x += $deltaX) { for ($y = $centerY, $x = $centerX;
$range = 0; $y < $bottom && $y >= $top && $x < $right && $x >= $left;
if ($deltaX == 0) { $y += $deltaY, $x += $deltaX) {
// horizontal slices, up and down $range = 0;
$range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, true); if ($deltaX == 0) {
} else { // horizontal slices, up and down
// vertical slices, left and right $range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, true);
$range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, false); } else {
} // vertical slices, left and right
if ($range == null) { $range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, false);
if ($lastRange == null) { }
throw NotFoundException::getNotFoundInstance(); if ($range == null) {
} if ($lastRange == null) {
// lastRange was found throw NotFoundException::getNotFoundInstance();
if ($deltaX == 0) { }
$lastY = $y - $deltaY; // lastRange was found
if ($lastRange[0] < $centerX) { if ($deltaX == 0) {
if ($lastRange[1] > $centerX) { $lastY = $y - $deltaY;
// straddle, choose one or the other based on direction if ($lastRange[0] < $centerX) {
return new ResultPoint($deltaY > 0 ? $lastRange[0] : $lastRange[1], $lastY); 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); return new ResultPoint($lastRange[0], $lastY);
} else { } else {
return new ResultPoint($lastRange[1], $lastY); return new ResultPoint($lastRange[1], $lastY);
} }
} else { } else {
$lastX = $x - $deltaX; $lastX = $x - $deltaX;
if ($lastRange[0] < $centerY) { if ($lastRange[0] < $centerY) {
if ($lastRange[1] > $centerY) { if ($lastRange[1] > $centerY) {
return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]); return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]);
} }
return new ResultPoint($lastX, $lastRange[0]); return new ResultPoint($lastX, $lastRange[0]);
} else { } else {
return new ResultPoint($lastX, $lastRange[1]); return new ResultPoint($lastX, $lastRange[1]);
} }
} }
} }
$lastRange = $range; $lastRange = $range;
} }
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
/** /**
* Computes the start and end of a region of pixels, either horizontally or vertically, that could * Computes the start and end of a region of pixels, either horizontally or vertically, that could
* be part of a Data Matrix barcode. * be part of a Data Matrix barcode.
* *
* @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location) * @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 * 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 * @param largest $maxWhiteRun run of white pixels that can still be considered part of the
* barcode region * barcode region
* @param minDim minimum pixel location, horizontally or vertically, to consider * @param minimum $minDim pixel location, horizontally or vertically, to consider
* @param maxDim maximum pixel location, horizontally or vertically, to consider * @param maximum $maxDim pixel location, horizontally or vertically, to consider
* @param horizontal if true, we're scanning left-right, instead of up-down * @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 * @return int[] with start and end of found range, or null if no such range is found
* (e.g. only white was found) * (e.g. only white was found)
*/ */
private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal) private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal)
{ {
$center = ($minDim + $maxDim) / 2; $center = ($minDim + $maxDim) / 2;
// Scan left/up first // Scan left/up first
$start = $center; $start = $center;
while ($start >= $minDim) { while ($start >= $minDim) {
if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) { if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) {
$start--; $start--;
} else { } else {
$whiteRunStart = $start; $whiteRunStart = $start;
do { do {
$start--; $start--;
} while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) : } while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) :
$this->image->get($fixedDimension, $start))); $this->image->get($fixedDimension, $start)));
$whiteRunSize = $whiteRunStart - $start; $whiteRunSize = $whiteRunStart - $start;
if ($start < $minDim || $whiteRunSize > $maxWhiteRun) { if ($start < $minDim || $whiteRunSize > $maxWhiteRun) {
$start = $whiteRunStart; $start = $whiteRunStart;
break; break;
} }
} }
} }
$start++; $start++;
// Then try right/down // Then try right/down
$end = $center; $end = $center;
while ($end < $maxDim) { while ($end < $maxDim) {
if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) { if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) {
$end++; $end++;
} else { } else {
$whiteRunStart = $end; $whiteRunStart = $end;
do { do {
$end++; $end++;
} while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) : } while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) :
$this->image->get($fixedDimension, $end))); $this->image->get($fixedDimension, $end)));
$whiteRunSize = $end - $whiteRunStart; $whiteRunSize = $end - $whiteRunStart;
if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) { if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) {
$end = $whiteRunStart; $end = $whiteRunStart;
break; break;
} }
} }
} }
$end--; $end--;
return $end > $start ? [$start, $end] : null; return $end > $start ? [$start, $end] : null;
} }
} }

View File

@ -26,22 +26,17 @@ namespace Zxing\Common;
*/ */
class DetectorResult class DetectorResult
{ {
private $bits; public function __construct(private $bits, private $points)
private $points; {
}
public function __construct($bits, $points) final public function getBits()
{ {
$this->bits = $bits; return $this->bits;
$this->points = $points; }
}
public final function getBits() final public function getPoints()
{ {
return $this->bits; return $this->points;
} }
public final function getPoints()
{
return $this->points;
}
} }

View File

@ -18,7 +18,6 @@
namespace Zxing\Common; namespace Zxing\Common;
use Zxing\Binarizer; use Zxing\Binarizer;
use Zxing\LuminanceSource;
use Zxing\NotFoundException; use Zxing\NotFoundException;
/** /**
@ -34,174 +33,177 @@ use Zxing\NotFoundException;
*/ */
class GlobalHistogramBinarizer extends Binarizer class GlobalHistogramBinarizer extends Binarizer
{ {
private static $LUMINANCE_BITS = 5; private static int $LUMINANCE_BITS = 5;
private static $LUMINANCE_SHIFT = 3; private static int $LUMINANCE_SHIFT = 3;
private static $LUMINANCE_BUCKETS = 32; private static int $LUMINANCE_BUCKETS = 32;
private static $EMPTY = []; private static array $EMPTY = [];
private $luminances = []; private array $luminances = [];
private $buckets = []; private array $buckets = [];
private $source = []; /**
* @var mixed|\Zxing\LuminanceSource
*/
private $source = [];
public function __construct($source) public function __construct($source)
{ {
self::$LUMINANCE_SHIFT = 8 - self::$LUMINANCE_BITS; self::$LUMINANCE_SHIFT = 8 - self::$LUMINANCE_BITS;
self::$LUMINANCE_BUCKETS = 1 << self::$LUMINANCE_BITS; self::$LUMINANCE_BUCKETS = 1 << self::$LUMINANCE_BITS;
parent::__construct($source); parent::__construct($source);
$this->luminances = self::$EMPTY; $this->luminances = self::$EMPTY;
$this->buckets = fill_array(0, self::$LUMINANCE_BUCKETS, 0); $this->buckets = fill_array(0, self::$LUMINANCE_BUCKETS, 0);
$this->source = $source; $this->source = $source;
} }
// Applies simple sharpening to the row data to improve performance of the 1D Readers. // Applies simple sharpening to the row data to improve performance of the 1D Readers.
public function getBlackRow($y, $row = null) public function getBlackRow($y, $row = null)
{ {
$this->source = $this->getLuminanceSource(); $this->source = $this->getLuminanceSource();
$width = $this->source->getWidth(); $width = $this->source->getWidth();
if ($row == null || $row->getSize() < $width) { if ($row == null || $row->getSize() < $width) {
$row = new BitArray($width); $row = new BitArray($width);
} else { } else {
$row->clear(); $row->clear();
} }
$this->initArrays($width); $this->initArrays($width);
$localLuminances = $this->source->getRow($y, $this->luminances); $localLuminances = $this->source->getRow($y, $this->luminances);
$localBuckets = $this->buckets; $localBuckets = $this->buckets;
for ($x = 0; $x < $width; $x++) { for ($x = 0; $x < $width; $x++) {
$pixel = $localLuminances[$x] & 0xff; $pixel = $localLuminances[$x] & 0xff;
$localBuckets[$pixel >> self::$LUMINANCE_SHIFT]++; $localBuckets[$pixel >> self::$LUMINANCE_SHIFT]++;
} }
$blackPoint = self::estimateBlackPoint($localBuckets); $blackPoint = self::estimateBlackPoint($localBuckets);
$left = $localLuminances[0] & 0xff; $left = $localLuminances[0] & 0xff;
$center = $localLuminances[1] & 0xff; $center = $localLuminances[1] & 0xff;
for ($x = 1; $x < $width - 1; $x++) { for ($x = 1; $x < $width - 1; $x++) {
$right = $localLuminances[$x + 1] & 0xff; $right = $localLuminances[$x + 1] & 0xff;
// A simple -1 4 -1 box filter with a weight of 2. // A simple -1 4 -1 box filter with a weight of 2.
$luminance = (($center * 4) - $left - $right) / 2; $luminance = (($center * 4) - $left - $right) / 2;
if ($luminance < $blackPoint) { if ($luminance < $blackPoint) {
$row->set($x); $row->set($x);
} }
$left = $center; $left = $center;
$center = $right; $center = $right;
} }
return $row; return $row;
} }
// Does not sharpen the data, as this call is intended to only be used by 2D Readers. // Does not sharpen the data, as this call is intended to only be used by 2D Readers.
private function initArrays($luminanceSize) private function initArrays($luminanceSize): void
{ {
if (count($this->luminances) < $luminanceSize) { if (count($this->luminances) < $luminanceSize) {
$this->luminances = []; $this->luminances = [];
} }
for ($x = 0; $x < self::$LUMINANCE_BUCKETS; $x++) { for ($x = 0; $x < self::$LUMINANCE_BUCKETS; $x++) {
$this->buckets[$x] = 0; $this->buckets[$x] = 0;
} }
} }
private static function estimateBlackPoint($buckets) private static function estimateBlackPoint($buckets)
{ {
// Find the tallest peak in the histogram. // Find the tallest peak in the histogram.
$numBuckets = count($buckets); $numBuckets = is_countable($buckets) ? count($buckets) : 0;
$maxBucketCount = 0; $maxBucketCount = 0;
$firstPeak = 0; $firstPeak = 0;
$firstPeakSize = 0; $firstPeakSize = 0;
for ($x = 0; $x < $numBuckets; $x++) { for ($x = 0; $x < $numBuckets; $x++) {
if ($buckets[$x] > $firstPeakSize) { if ($buckets[$x] > $firstPeakSize) {
$firstPeak = $x; $firstPeak = $x;
$firstPeakSize = $buckets[$x]; $firstPeakSize = $buckets[$x];
} }
if ($buckets[$x] > $maxBucketCount) { if ($buckets[$x] > $maxBucketCount) {
$maxBucketCount = $buckets[$x]; $maxBucketCount = $buckets[$x];
} }
} }
// Find the second-tallest peak which is somewhat far from the tallest peak. // Find the second-tallest peak which is somewhat far from the tallest peak.
$secondPeak = 0; $secondPeak = 0;
$secondPeakScore = 0; $secondPeakScore = 0;
for ($x = 0; $x < $numBuckets; $x++) { for ($x = 0; $x < $numBuckets; $x++) {
$distanceToBiggest = $x - $firstPeak; $distanceToBiggest = $x - $firstPeak;
// Encourage more distant second peaks by multiplying by square of distance. // Encourage more distant second peaks by multiplying by square of distance.
$score = $buckets[$x] * $distanceToBiggest * $distanceToBiggest; $score = $buckets[$x] * $distanceToBiggest * $distanceToBiggest;
if ($score > $secondPeakScore) { if ($score > $secondPeakScore) {
$secondPeak = $x; $secondPeak = $x;
$secondPeakScore = $score; $secondPeakScore = $score;
} }
} }
// Make sure firstPeak corresponds to the black peak. // Make sure firstPeak corresponds to the black peak.
if ($firstPeak > $secondPeak) { if ($firstPeak > $secondPeak) {
$temp = $firstPeak; $temp = $firstPeak;
$firstPeak = $secondPeak; $firstPeak = $secondPeak;
$secondPeak = $temp; $secondPeak = $temp;
} }
// If there is too little contrast in the image to pick a meaningful black point, throw rather // 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. // than waste time trying to decode the image, and risk false positives.
if ($secondPeak - $firstPeak <= $numBuckets / 16) { if ($secondPeak - $firstPeak <= $numBuckets / 16) {
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
// Find a valley between them that is low and closer to the white peak. // Find a valley between them that is low and closer to the white peak.
$bestValley = $secondPeak - 1; $bestValley = $secondPeak - 1;
$bestValleyScore = -1; $bestValleyScore = -1;
for ($x = $secondPeak - 1; $x > $firstPeak; $x--) { for ($x = $secondPeak - 1; $x > $firstPeak; $x--) {
$fromFirst = $x - $firstPeak; $fromFirst = $x - $firstPeak;
$score = $fromFirst * $fromFirst * ($secondPeak - $x) * ($maxBucketCount - $buckets[$x]); $score = $fromFirst * $fromFirst * ($secondPeak - $x) * ($maxBucketCount - $buckets[$x]);
if ($score > $bestValleyScore) { if ($score > $bestValleyScore) {
$bestValley = $x; $bestValley = $x;
$bestValleyScore = $score; $bestValleyScore = $score;
} }
} }
return ($bestValley << self::$LUMINANCE_SHIFT); return $bestValley << self::$LUMINANCE_SHIFT;
} }
public function getBlackMatrix() public function getBlackMatrix()
{ {
$source = $this->getLuminanceSource(); $source = $this->getLuminanceSource();
$width = $source->getWidth(); $width = $source->getWidth();
$height = $source->getHeight(); $height = $source->getHeight();
$matrix = new BitMatrix($width, $height); $matrix = new BitMatrix($width, $height);
// Quickly calculates the histogram by sampling four rows from the image. This proved to be // 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. // more robust on the blackbox tests than sampling a diagonal as we used to do.
$this->initArrays($width); $this->initArrays($width);
$localBuckets = $this->buckets; $localBuckets = $this->buckets;
for ($y = 1; $y < 5; $y++) { for ($y = 1; $y < 5; $y++) {
$row = (int)($height * $y / 5); $row = (int)($height * $y / 5);
$localLuminances = $source->getRow($row, $this->luminances); $localLuminances = $source->getRow($row, $this->luminances);
$right = (int)(($width * 4) / 5); $right = (int)(($width * 4) / 5);
for ($x = (int)($width / 5); $x < $right; $x++) { for ($x = (int)($width / 5); $x < $right; $x++) {
$pixel = ($localLuminances[(int)($x)] & 0xff); $pixel = ($localLuminances[(int)($x)] & 0xff);
$localBuckets[($pixel >> self::$LUMINANCE_SHIFT)]++; $localBuckets[($pixel >> self::$LUMINANCE_SHIFT)]++;
} }
} }
$blackPoint = self::estimateBlackPoint($localBuckets); $blackPoint = self::estimateBlackPoint($localBuckets);
// We delay reading the entire image luminance until the black point estimation succeeds. // 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 // Although we end up reading four rows twice, it is consistent with our motto of
// "fail quickly" which is necessary for continuous scanning. // "fail quickly" which is necessary for continuous scanning.
$localLuminances = $source->getMatrix(); $localLuminances = $source->getMatrix();
for ($y = 0; $y < $height; $y++) { for ($y = 0; $y < $height; $y++) {
$offset = $y * $width; $offset = $y * $width;
for ($x = 0; $x < $width; $x++) { for ($x = 0; $x < $width; $x++) {
$pixel = (int)($localLuminances[$offset + $x] & 0xff); $pixel = (int)($localLuminances[$offset + $x] & 0xff);
if ($pixel < $blackPoint) { if ($pixel < $blackPoint) {
$matrix->set($x, $y); $matrix->set($x, $y);
} }
} }
} }
return $matrix; return $matrix;
} }
public function createBinarizer($source) public function createBinarizer($source): \Zxing\Common\GlobalHistogramBinarizer
{ {
return new GlobalHistogramBinarizer($source); return new GlobalHistogramBinarizer($source);
} }
} }

View File

@ -34,155 +34,165 @@ use Zxing\NotFoundException;
*/ */
abstract class GridSampler 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 * Sets the implementation of GridSampler used by the library. One global
* instance is stored, which may sound problematic. But, the implementation provided * 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 * 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 * in the whole lifetime of the JVM. For instance, an Android activity can swap in
* an implementation that takes advantage of native platform libraries. * an implementation that takes advantage of native platform libraries.
* *
* @param newGridSampler The platform-specific object to install. * @param $newGridSampler The platform-specific object to install.
*/ */
public static function setGridSampler($newGridSampler) public static function setGridSampler($newGridSampler): void
{ {
self::$gridSampler = $newGridSampler; self::$gridSampler = $newGridSampler;
} }
/** /**
* @return the current implementation of GridSampler * @return GridSampler the current implementation of GridSampler
*/ */
public static function getInstance() public static function getInstance()
{ {
if (!self::$gridSampler) { if (!self::$gridSampler) {
self::$gridSampler = new DefaultGridSampler(); 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 * <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> * 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 * <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 * 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> * 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 * <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> * 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 image $image into which the points should map
* @param points actual points in x1,y1,...,xn,yn form * @param actual $points points in x1,y1,...,xn,yn form
* *
* @throws NotFoundException if an endpoint is lies outside the image boundaries * @throws NotFoundException if an endpoint is lies outside the image boundaries
*/ */
protected static function checkAndNudgePoints( protected static function checkAndNudgePoints(
$image, $image,
$points $points
) { ) {
$width = $image->getWidth(); $width = $image->getWidth();
$height = $image->getHeight(); $height = $image->getHeight();
// Check and nudge points from start until we see some that are OK: // Check and nudge points from start until we see some that are OK:
$nudged = true; $nudged = true;
for ($offset = 0; $offset < count($points) && $nudged; $offset += 2) { for ($offset = 0; $offset < (is_countable($points) ? count($points) : 0) && $nudged; $offset += 2) {
$x = (int)$points[$offset]; $x = (int)$points[$offset];
$y = (int)$points[$offset + 1]; $y = (int)$points[$offset + 1];
if ($x < -1 || $x > $width || $y < -1 || $y > $height) { if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
$nudged = false; $nudged = false;
if ($x == -1) { if ($x == -1) {
$points[$offset] = 0.0; $points[$offset] = 0.0;
$nudged = true; $nudged = true;
} else if ($x == $width) { } elseif ($x == $width) {
$points[$offset] = $width - 1; $points[$offset] = $width - 1;
$nudged = true; $nudged = true;
} }
if ($y == -1) { if ($y == -1) {
$points[$offset + 1] = 0.0; $points[$offset + 1] = 0.0;
$nudged = true; $nudged = true;
} else if ($y == $height) { } elseif ($y == $height) {
$points[$offset + 1] = $height - 1; $points[$offset + 1] = $height - 1;
$nudged = true; $nudged = true;
} }
} }
// Check and nudge points from end: // Check and nudge points from end:
$nudged = true; $nudged = true;
for ($offset = count($points) - 2; $offset >= 0 && $nudged; $offset -= 2) { for ($offset = (is_countable($points) ? count($points) : 0) - 2; $offset >= 0 && $nudged; $offset -= 2) {
$x = (int)$points[$offset]; $x = (int)$points[$offset];
$y = (int)$points[$offset + 1]; $y = (int)$points[$offset + 1];
if ($x < -1 || $x > $width || $y < -1 || $y > $height) { if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
$nudged = false; $nudged = false;
if ($x == -1) { if ($x == -1) {
$points[$offset] = 0.0; $points[$offset] = 0.0;
$nudged = true; $nudged = true;
} else if ($x == $width) { } elseif ($x == $width) {
$points[$offset] = $width - 1; $points[$offset] = $width - 1;
$nudged = true; $nudged = true;
} }
if ($y == -1) { if ($y == -1) {
$points[$offset + 1] = 0.0; $points[$offset + 1] = 0.0;
$nudged = true; $nudged = true;
} else if ($y == $height) { } elseif ($y == $height) {
$points[$offset + 1] = $height - 1; $points[$offset + 1] = $height - 1;
$nudged = true; $nudged = true;
} }
} }
} }
/** /**
* Samples an image for a rectangular matrix of bits of the given dimension. The sampling * 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 * transformation is determined by the coordinates of 4 points, in the original and transformed
* image space. * image space.
* *
* @param image image to sample * @param image $image to sample
* @param dimensionX width of {@link BitMatrix} to sample from image * @param width $dimensionX of {@link BitMatrix} to sample from image
* @param dimensionY height of {@link BitMatrix} to sample from image * @param height $dimensionY of {@link BitMatrix} to sample from image
* @param p1ToX point 1 preimage X * @param point $p1ToX 1 preimage X
* @param p1ToY point 1 preimage Y * @param point $p1ToY 1 preimage Y
* @param p2ToX point 2 preimage X * @param point $p2ToX 2 preimage X
* @param p2ToY point 2 preimage Y * @param point $p2ToY 2 preimage Y
* @param p3ToX point 3 preimage X * @param point $p3ToX 3 preimage X
* @param p3ToY point 3 preimage Y * @param point $p3ToY 3 preimage Y
* @param p4ToX point 4 preimage X * @param point $p4ToX 4 preimage X
* @param p4ToY point 4 preimage Y * @param point $p4ToY 4 preimage Y
* @param p1FromX point 1 image X * @param point $p1FromX 1 image X
* @param p1FromY point 1 image Y * @param point $p1FromY 1 image Y
* @param p2FromX point 2 image X * @param point $p2FromX 2 image X
* @param p2FromY point 2 image Y * @param point $p2FromY 2 image Y
* @param p3FromX point 3 image X * @param point $p3FromX 3 image X
* @param p3FromY point 3 image Y * @param point $p3FromY 3 image Y
* @param p4FromX point 4 image X * @param point $p4FromX 4 image X
* @param p4FromY point 4 image Y * @param point $p4FromY 4 image Y
* *
* @return {@link BitMatrix} representing a grid of points sampled from the image within a region * @return {@link BitMatrix} representing a grid of points sampled from the image within a region
* defined by the "from" parameters * defined by the "from" parameters
* @throws NotFoundException if image can't be sampled, for example, if the transformation defined * @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 * by the given points is invalid or results in sampling outside the image boundaries
*/ */
public abstract function sampleGrid( abstract public function sampleGrid(
$image, $image,
$dimensionX, $dimensionX,
$dimensionY, $dimensionY,
$p1ToX, $p1ToY, $p1ToX,
$p2ToX, $p2ToY, $p1ToY,
$p3ToX, $p3ToY, $p2ToX,
$p4ToX, $p4ToY, $p2ToY,
$p1FromX, $p1FromY, $p3ToX,
$p2FromX, $p2FromY, $p3ToY,
$p3FromX, $p3FromY, $p4ToX,
$p4FromX, $p4FromY $p4ToY,
); $p1FromX,
$p1FromY,
public abstract function sampleGrid_( $p2FromX,
$image, $p2FromY,
$dimensionX, $p3FromX,
$dimensionY, $p3FromY,
$transform $p4FromX,
); $p4FromY
);
abstract public function sampleGrid_(
$image,
$dimensionX,
$dimensionY,
$transform
);
} }

View File

@ -18,8 +18,6 @@
namespace Zxing\Common; namespace Zxing\Common;
use Zxing\Binarizer; use Zxing\Binarizer;
use Zxing\LuminanceSource;
use Zxing\NotFoundException;
/** /**
* This class implements a local thresholding algorithm, which while slower than the * This class implements a local thresholding algorithm, which while slower than the
@ -40,225 +38,223 @@ use Zxing\NotFoundException;
*/ */
final class HybridBinarizer extends GlobalHistogramBinarizer 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. private ?\Zxing\Common\BitMatrix $matrix = null;
// 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 $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) /**
{ * Calculates the final BitMatrix once for all requests. This could be called once from the
parent::__construct($source); * constructor instead, but there are some advantages to doing it lazily, such as making
self::$BLOCK_SIZE_POWER = 3; * profiling easier, and not doing heavy lifting when callers don't expect it.
self::$BLOCK_SIZE = 1 << self::$BLOCK_SIZE_POWER; // ...0100...00 */
self::$BLOCK_SIZE_MASK = self::$BLOCK_SIZE - 1; // ...0011...11 public function getBlackMatrix()
self::$MINIMUM_DIMENSION = self::$BLOCK_SIZE * 5; {
self::$MIN_DYNAMIC_RANGE = 24; 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);
* Calculates the final BitMatrix once for all requests. This could be called once from the self::calculateThresholdForBlock($luminances, $subWidth, $subHeight, $width, $height, $blackPoints, $newMatrix);
* constructor instead, but there are some advantages to doing it lazily, such as making $this->matrix = $newMatrix;
* profiling easier, and not doing heavy lifting when callers don't expect it. } else {
*/ // If the image is too small, fall back to the global histogram approach.
public function getBlackMatrix() $this->matrix = parent::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); return $this->matrix;
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; /**
} * 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.
* Calculates a single black point for each block of pixels and saves it away. $average = ($sum >> (self::$BLOCK_SIZE_POWER * 2));
* See the following thread for a discussion of this algorithm: if ($max - $min <= self::$MIN_DYNAMIC_RANGE) {
* http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0 // 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
private static function calculateBlackPoints( // low contrast area into black and white pixels, essentially creating data out of noise.
$luminances, //
$subWidth, // The default assumption is that the block is light/background. Since no estimate for
$subHeight, // the level of dark pixels exists locally, use half the min for the block.
$width, $average = (int)($min / 2);
$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. if ($y > 0 && $x > 0) {
$average = ($sum >> (self::$BLOCK_SIZE_POWER * 2)); // Correct the "white background" assumption for blocks that have neighbors by comparing
if ($max - $min <= self::$MIN_DYNAMIC_RANGE) { // the pixels in this block to the previously calculated black points. This is based on
// If variation within the block is low, assume this is a block with only light or only // the fact that dark barcode symbology is always surrounded by some amount of light
// dark pixels. In that case we do not want to use the average, as it would divide this // background for which reasonable black point estimates were made. The bp estimated at
// low contrast area into black and white pixels, essentially creating data out of noise. // the boundaries is used for the interior.
//
// 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) { // The (min < bp) is arbitrary but works better than other heuristics that were tried.
// Correct the "white background" assumption for blocks that have neighbors by comparing $averageNeighborBlackPoint =
// the pixels in this block to the previously calculated black points. This is based on (int)(($blackPoints[$y - 1][$x] + (2 * $blackPoints[$y][$x - 1]) + $blackPoints[$y - 1][$x - 1]) / 4);
// the fact that dark barcode symbology is always surrounded by some amount of light if ($min < $averageNeighborBlackPoint) {
// background for which reasonable black point estimates were made. The bp estimated at $average = $averageNeighborBlackPoint;
// the boundaries is used for the interior. }
}
}
$blackPoints[$y][$x] = (int)($average);
}
}
// The (min < bp) is arbitrary but works better than other heuristics that were tried. return $blackPoints;
$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; /**
} * 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);
/** self::thresholdBlock($luminances, $xoffset, $yoffset, $average, $width, $matrix);
* 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); 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) /**
{ * Applies a single threshold to a block of pixels.
if ($value < $min) { */
return $min; private static function thresholdBlock(
} elseif ($value > $max) { $luminances,
return $max; $xoffset,
} else { $yoffset,
return $value; $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);
}
}
}
}
/** public function createBinarizer($source): \Zxing\Common\HybridBinarizer
* Applies a single threshold to a block of pixels. {
*/ return new HybridBinarizer($source);
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);
}
} }

View File

@ -26,150 +26,162 @@ namespace Zxing\Common;
*/ */
final class PerspectiveTransform final class PerspectiveTransform
{ {
private $a11; private function __construct(private $a11, private $a21, private $a31, private $a12, private $a22, private $a32, private $a13, private $a23, private $a33)
private $a12; {
private $a13; }
private $a21;
private $a22;
private $a23;
private $a31;
private $a32;
private $a33;
private function __construct( public static function quadrilateralToQuadrilateral(
$a11, $a21, $a31, $x0,
$a12, $a22, $a32, $y0,
$a13, $a23, $a33 $x1,
) { $y1,
$this->a11 = $a11; $x2,
$this->a12 = $a12; $y2,
$this->a13 = $a13; $x3,
$this->a21 = $a21; $y3,
$this->a22 = $a22; $x0p,
$this->a23 = $a23; $y0p,
$this->a31 = $a31; $x1p,
$this->a32 = $a32; $y1p,
$this->a33 = $a33; $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( return $sToQ->times($qToS);
$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); public static function quadrilateralToSquare(
$sToQ = self::squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p); $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( public static function squareToQuadrilateral(
$x0, $y0, $x0,
$x1, $y1, $y0,
$x2, $y2, $x1,
$x3, $y3 $y1,
) { $x2,
// Here, the adjoint serves as the inverse: $y2,
return self::squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)->buildAdjoint(); $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() return new PerspectiveTransform(
{ $x1 - $x0 + $a13 * $x1,
// Adjoint is the transpose of the cofactor matrix: $x3 - $x0 + $a23 * $x3,
return new PerspectiveTransform($this->a22 * $this->a33 - $this->a23 * $this->a32, $x0,
$this->a23 * $this->a31 - $this->a21 * $this->a33, $y1 - $y0 + $a13 * $y1,
$this->a21 * $this->a32 - $this->a22 * $this->a31, $y3 - $y0 + $a23 * $y3,
$this->a13 * $this->a32 - $this->a12 * $this->a33, $y0,
$this->a11 * $this->a33 - $this->a13 * $this->a31, $a13,
$this->a12 * $this->a31 - $this->a11 * $this->a32, $a23,
$this->a12 * $this->a23 - $this->a13 * $this->a22, 1.0
$this->a13 * $this->a21 - $this->a11 * $this->a23, );
$this->a11 * $this->a22 - $this->a12 * $this->a21); }
} }
public static function squareToQuadrilateral( public function times($other): \Zxing\Common\PerspectiveTransform
$x0, $y0, {
$x1, $y1, return new PerspectiveTransform(
$x2, $y2, $this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13,
$x3, $y3 $this->a11 * $other->a21 + $this->a21 * $other->a22 + $this->a31 * $other->a23,
) { $this->a11 * $other->a31 + $this->a21 * $other->a32 + $this->a31 * $other->a33,
$dx3 = $x0 - $x1 + $x2 - $x3; $this->a12 * $other->a11 + $this->a22 * $other->a12 + $this->a32 * $other->a13,
$dy3 = $y0 - $y1 + $y2 - $y3; $this->a12 * $other->a21 + $this->a22 * $other->a22 + $this->a32 * $other->a23,
if ($dx3 == 0.0 && $dy3 == 0.0) { $this->a12 * $other->a31 + $this->a22 * $other->a32 + $this->a32 * $other->a33,
// Affine $this->a13 * $other->a11 + $this->a23 * $other->a12 + $this->a33 * $other->a13,
return new PerspectiveTransform($x1 - $x0, $x2 - $x1, $x0, $this->a13 * $other->a21 + $this->a23 * $other->a22 + $this->a33 * $other->a23,
$y1 - $y0, $y2 - $y1, $y0, $this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33
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;
return new PerspectiveTransform($x1 - $x0 + $a13 * $x1, $x3 - $x0 + $a23 * $x3, $x0, public function transformPoints(&$points, &$yValues = 0): void
$y1 - $y0 + $a13 * $y1, $y3 - $y0 + $a23 * $y3, $y0, {
$a13, $a23, 1.0); if ($yValues) {
} $this->transformPoints_($points, $yValues);
}
public function times($other) return;
{ }
return new PerspectiveTransform($this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13, $max = is_countable($points) ? count($points) : 0;
$this->a11 * $other->a21 + $this->a21 * $other->a22 + $this->a31 * $other->a23, $a11 = $this->a11;
$this->a11 * $other->a31 + $this->a21 * $other->a32 + $this->a31 * $other->a33, $a12 = $this->a12;
$this->a12 * $other->a11 + $this->a22 * $other->a12 + $this->a32 * $other->a13, $a13 = $this->a13;
$this->a12 * $other->a21 + $this->a22 * $other->a22 + $this->a32 * $other->a23, $a21 = $this->a21;
$this->a12 * $other->a31 + $this->a22 * $other->a32 + $this->a32 * $other->a33, $a22 = $this->a22;
$this->a13 * $other->a11 + $this->a23 * $other->a12 + $this->a33 * $other->a13, $a23 = $this->a23;
$this->a13 * $other->a21 + $this->a23 * $other->a22 + $this->a33 * $other->a23, $a31 = $this->a31;
$this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33); $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): void
{
public function transformPoints(&$points, &$yValues = 0) $n = is_countable($xValues) ? count($xValues) : 0;
{ for ($i = 0; $i < $n; $i++) {
if ($yValues) { $x = $xValues[$i];
$this->transformPoints_($points, $yValues); $y = $yValues[$i];
$denominator = $this->a13 * $x + $this->a23 * $y + $this->a33;
return; $xValues[$i] = ($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator;
} $yValues[$i] = ($this->a12 * $x + $this->a22 * $y + $this->a32) / $denominator;
$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;
}
}
} }

View File

@ -30,169 +30,158 @@ namespace Zxing\Common\Reedsolomon;
*/ */
final class GenericGF 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; private array $expTable = [];
public static $AZTEC_DATA_10; private array $logTable = [];
public static $AZTEC_DATA_6; private readonly \Zxing\Common\Reedsolomon\GenericGFPoly $zero;
public static $AZTEC_PARAM; private readonly \Zxing\Common\Reedsolomon\GenericGFPoly $one;
public static $QR_CODE_FIELD_256;
public static $DATA_MATRIX_FIELD_256;
public static $AZTEC_DATA_8;
public static $MAXICODE_FIELD_64;
private $expTable; /**
private $logTable; * Create a representation of GF(size) using the given primitive polynomial.
private $zero; *
private $one; * @param irreducible $primitive polynomial whose coefficients are represented by
private $size; * the bits of an int, where the least-significant bit represents the constant
private $primitive; * coefficient
private $generatorBase; * @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]);
}
/** public static function Init(): void
* Create a representation of GF(size) using the given primitive polynomial. {
* self::$AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
* @param primitive irreducible polynomial whose coefficients are represented by self::$AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1
* the bits of an int, where the least-significant bit represents the constant self::$AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1
* coefficient self::$AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1
* @param size the size of the field self::$QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
* @param b the factor b in the generator polynomial can be 0- or 1-based self::$DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
* (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))). self::$AZTEC_DATA_8 = self::$DATA_MATRIX_FIELD_256;
* In most cases it should be 1, but for QR code it is 0. self::$MAXICODE_FIELD_64 = self::$AZTEC_DATA_6;
*/ }
public function __construct($primitive, $size, $b)
{
$this->primitive = $primitive;
$this->size = $size;
$this->generatorBase = $b;
$this->expTable = []; /**
$this->logTable = []; * Implements both addition and subtraction -- they are the same in GF(size).
$x = 1; *
for ($i = 0; $i < $size; $i++) { * @return sum/difference of a and b
$this->expTable[$i] = $x; */
$x *= 2; // we're assuming the generator alpha is 2 public static function addOrSubtract($a, $b)
if ($x >= $size) { {
$x ^= $primitive; return $a ^ $b;
$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]);
}
public static function Init() public function getZero()
{ {
self::$AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1 return $this->zero;
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 getOne()
* Implements both addition and subtraction -- they are the same in GF(size). {
* return $this->one;
* @return sum/difference of a and b }
*/
public static function addOrSubtract($a, $b)
{
return $a ^ $b;
}
public function getZero() /**
{ * @return GenericGFPoly the monomial representing coefficient * x^degree
return $this->zero; */
} 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 new GenericGFPoly($this, $coefficients);
{ }
return $this->one;
}
/** /**
* @return the monomial representing coefficient * x^degree * @return 2 to the power of a in GF(size)
*/ */
public function buildMonomial($degree, $coefficient) public function exp($a)
{ {
if ($degree < 0) { return $this->expTable[$a];
throw new \InvalidArgumentException(); }
}
if ($coefficient == 0) {
return $this->zero;
}
$coefficients = fill_array(0, $degree + 1, 0);//new int[degree + 1];
$coefficients[0] = $coefficient;
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 $this->logTable[$a];
* @return 2 to the power of a in GF(size) }
*/
public function exp($a)
{
return $this->expTable[$a];
}
/** /**
* @return base 2 log of a in GF(size) * @return multiplicative inverse of a
*/ */
public function log($a) public function inverse($a)
{ {
if ($a == 0) { if ($a == 0) {
throw new \InvalidArgumentException(); throw new \Exception();
} }
return $this->logTable[$a]; return $this->expTable[$this->size - $this->logTable[$a] - 1];
} }
/** /**
* @return multiplicative inverse of a * @return int product of a and b in GF(size)
*/ */
public function inverse($a) public function multiply($a, $b)
{ {
if ($a == 0) { if ($a == 0 || $b == 0) {
throw new \Exception(); return 0;
} }
return $this->expTable[$this->size - $this->logTable[$a] - 1]; return $this->expTable[($this->logTable[$a] + $this->logTable[$b]) % ($this->size - 1)];
} }
/** public function getSize()
* @return int product of a and b in GF(size) {
*/ return $this->size;
public function multiply($a, $b) }
{
if ($a == 0 || $b == 0) {
return 0;
}
return $this->expTable[($this->logTable[$a] + $this->logTable[$b]) % ($this->size - 1)]; public function getGeneratorBase()
} {
return $this->generatorBase;
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 . ')';
}
// @Override
public function toString()
{
return "GF(0x" . dechex((int)($this->primitive)) . ',' . $this->size . ')';
}
} }
GenericGF::Init(); GenericGF::Init();

View File

@ -28,262 +28,276 @@ namespace Zxing\Common\Reedsolomon;
*/ */
final class GenericGFPoly 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;
}
}
/** public function getCoefficients()
* @param field the {@link GenericGF} instance representing the field to use {
* to perform computations return $this->coefficients;
* @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 evaluation of this polynomial at a given point
return $this->coefficients; */
} 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 $result;
* @return evaluation of this polynomial at a given point }
*/ $result = $this->coefficients[0];
public function evaluateAt($a) for ($i = 1; $i < $size; $i++) {
{ $result = GenericGF::addOrSubtract($this->field->multiply($a, $result), $this->coefficients[$i]);
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; 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 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];
}
/** public function multiply($other)
* @return coefficient of x^degree term in this polynomial {
*/ $aCoefficients = [];
public function getCoefficient($degree) $bCoefficients = [];
{ $aLength = null;
return $this->coefficients[count($this->coefficients) - 1 - $degree]; $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) return new GenericGFPoly($this->field, $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]));
}
}
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) return new GenericGFPoly($this->field, $product);
{ }
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 true iff this polynomial is the monomial "0"
*/
public function isZero()
{
return $this->coefficients[0] == 0;
}
/** public function multiplyByMonomial($degree, $coefficient)
* @return true iff this polynomial is the monomial "0" {
*/ if ($degree < 0) {
public function isZero() throw new \InvalidArgumentException();
{ }
return $this->coefficients[0] == 0; 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) return new GenericGFPoly($this->field, $product);
{ }
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); 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) $quotient = $this->field->getZero();
{ $remainder = $this;
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(); $denominatorLeadingTerm = $other->getCoefficient($other->getDegree());
$remainder = $this; $inverseDenominatorLeadingTerm = $this->field->inverse($denominatorLeadingTerm);
$denominatorLeadingTerm = $other->getCoefficient($other->getDegree()); while ($remainder->getDegree() >= $other->getDegree() && !$remainder->isZero()) {
$inverseDenominatorLeadingTerm = $this->field->inverse($denominatorLeadingTerm); $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()) { return [$quotient, $remainder];
$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 degree of this polynomial
*/
public function getDegree()
{
return (is_countable($this->coefficients) ? count($this->coefficients) : 0) - 1;
}
/** public function addOrSubtract($other)
* @return degree of this polynomial {
*/ $smallerCoefficients = [];
public function getDegree() $largerCoefficients = [];
{ $sumDiff = [];
return count($this->coefficients) - 1; $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) $smallerCoefficients = $this->coefficients;
{ $largerCoefficients = $other->coefficients;
if ($this->field !== $other->field) { if (count($smallerCoefficients) > count($largerCoefficients)) {
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field"); $temp = $smallerCoefficients;
} $smallerCoefficients = $largerCoefficients;
if ($this->isZero()) { $largerCoefficients = $temp;
return $other; }
} $sumDiff = fill_array(0, count($largerCoefficients), 0);
if ($other->isZero()) { $lengthDiff = count($largerCoefficients) - count($smallerCoefficients);
return $this; // Copy high-order terms only found in higher-degree polynomial's coefficients
} $sumDiff = arraycopy($largerCoefficients, 0, $sumDiff, 0, $lengthDiff);
$smallerCoefficients = $this->coefficients; $countLargerCoefficients = count($largerCoefficients);
$largerCoefficients = $other->coefficients; for ($i = $lengthDiff; $i < $countLargerCoefficients; $i++) {
if (count($smallerCoefficients) > count($largerCoefficients)) { $sumDiff[$i] = GenericGF::addOrSubtract($smallerCoefficients[$i - $lengthDiff], $largerCoefficients[$i]);
$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); return new GenericGFPoly($this->field, $sumDiff);
for ($i = $lengthDiff; $i < $countLargerCoefficients; $i++) { }
$sumDiff[$i] = GenericGF::addOrSubtract($smallerCoefficients[$i - $lengthDiff], $largerCoefficients[$i]);
}
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() return $result;
{ }
$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;
}
} }

View File

@ -41,161 +41,158 @@ namespace Zxing\Common\Reedsolomon;
*/ */
final class ReedSolomonDecoder 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) private function runEuclideanAlgorithm($a, $b, $R)
{ {
$this->field = $field; // Assume a's degree is >= b's
} if ($a->getDegree() < $b->getDegree()) {
$temp = $a;
$a = $b;
$b = $temp;
}
/** $rLast = $a;
* <p>Decodes given set of received codewords, which include both data and error-correction $r = $b;
* codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place, $tLast = $this->field->getZero();
* in the input.</p> $t = $this->field->getOne();
*
* @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]);
}
} // 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) // Divide rLastLast by rLast, with quotient in q and remainder in r
{ if ($rLast->isZero()) {
// Assume a's degree is >= b's // Oops, Euclidean algorithm already terminated?
if ($a->getDegree() < $b->getDegree()) { throw new ReedSolomonException("r_{i-1} was zero");
$temp = $a; }
$a = $b; $r = $rLastLast;
$b = $temp; $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; $t = $q->multiply($tLast)->addOrSubtract($tLastLast);
$r = $b;
$tLast = $this->field->getZero();
$t = $this->field->getOne();
// Run Euclidean algorithm until r's degree is less than R/2 if ($r->getDegree() >= $rLast->getDegree()) {
while ($r->getDegree() >= $R / 2) { throw new ReedSolomonException("Division algorithm failed to reduce polynomial?");
$rLastLast = $rLast; }
$tLastLast = $tLast; }
$rLast = $r;
$tLast = $t;
// Divide rLastLast by rLast, with quotient in q and remainder in r $sigmaTildeAtZero = $t->getCoefficient(0);
if ($rLast->isZero()) { if ($sigmaTildeAtZero == 0) {
// Oops, Euclidean algorithm already terminated? throw new ReedSolomonException("sigmaTilde(0) was zero");
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));
}
$t = $q->multiply($tLast)->addOrSubtract($tLastLast); $inverse = $this->field->inverse($sigmaTildeAtZero);
$sigma = $t->multiply($inverse);
$omega = $r->multiply($inverse);
if ($r->getDegree() >= $rLast->getDegree()) { return [$sigma, $omega];
throw new ReedSolomonException("Division algorithm failed to reduce polynomial?"); }
}
}
$sigmaTildeAtZero = $t->getCoefficient(0); private function findErrorLocations($errorLocator)
if ($sigmaTildeAtZero == 0) { {
throw new ReedSolomonException("sigmaTilde(0) was zero"); // 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); return $result;
$sigma = $t->multiply($inverse); }
$omega = $r->multiply($inverse);
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) return $result;
{ }
// 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;
}
} }

View File

@ -1,101 +1,103 @@
<?php <?php
if (!function_exists('arraycopy')) { if (!function_exists('arraycopy')) {
function arraycopy($srcArray, $srcPos, $destArray, $destPos, $length) function arraycopy($srcArray, $srcPos, $destArray, $destPos, $length)
{ {
$srcArrayToCopy = array_slice($srcArray, $srcPos, $length); $srcArrayToCopy = array_slice($srcArray, $srcPos, $length);
array_splice($destArray, $destPos, $length, $srcArrayToCopy); array_splice($destArray, $destPos, $length, $srcArrayToCopy);
return $destArray; return $destArray;
} }
} }
if (!function_exists('hashCode')) { if (!function_exists('hashCode')) {
function hashCode($s) function hashCode($s)
{ {
$h = 0; $h = 0;
$len = strlen($s); $len = strlen((string) $s);
for ($i = 0; $i < $len; $i++) { for ($i = 0; $i < $len; $i++) {
$h = (31 * $h + ord($s[$i])); $h = (31 * $h + ord($s[$i]));
} }
return $h; return $h;
} }
} }
if (!function_exists('numberOfTrailingZeros')) { if (!function_exists('numberOfTrailingZeros')) {
function numberOfTrailingZeros($i) function numberOfTrailingZeros($i)
{ {
if ($i == 0) return 32; if ($i == 0) {
$num = 0; return 32;
while (($i & 1) == 0) { }
$i >>= 1; $num = 0;
$num++; while (($i & 1) == 0) {
} $i >>= 1;
$num++;
}
return $num; return $num;
} }
} }
if (!function_exists('uRShift')) { if (!function_exists('uRShift')) {
function uRShift($a, $b) function uRShift($a, $b)
{ {
static $mask = (8 * PHP_INT_SIZE - 1); static $mask = (8 * PHP_INT_SIZE - 1);
if ($b === 0) { if ($b === 0) {
return $a; return $a;
} }
return ($a >> $b) & ~(1 << $mask >> ($b - 1)); return ($a >> $b) & ~(1 << $mask >> ($b - 1));
} }
} }
/* /*
function sdvig3($num,$count=1){//>>> 32 bit function sdvig3($num,$count=1){//>>> 32 bit
$s = decbin($num); $s = decbin($num);
$sarray = str_split($s,1); $sarray = str_split($s,1);
$sarray = array_slice($sarray,-32);//32bit $sarray = array_slice($sarray,-32);//32bit
for($i=0;$i<=1;$i++) { for($i=0;$i<=1;$i++) {
array_pop($sarray); array_pop($sarray);
array_unshift($sarray, '0'); array_unshift($sarray, '0');
} }
return bindec(implode($sarray)); return bindec(implode($sarray));
} }
*/ */
if (!function_exists('sdvig3')) { if (!function_exists('sdvig3')) {
function sdvig3($a, $b) function sdvig3($a, $b)
{ {
if ($a >= 0) { if ($a >= 0) {
return bindec(decbin($a >> $b)); //simply right shift for positive number 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')) { if (!function_exists('floatToIntBits')) {
function floatToIntBits($float_val) function floatToIntBits($float_val)
{ {
$int = unpack('i', pack('f', $float_val)); $int = unpack('i', pack('f', $float_val));
return $int[1]; return $int[1];
} }
} }
if (!function_exists('fill_array')) { if (!function_exists('fill_array')) {
function fill_array($index, $count, $value) function fill_array($index, $count, $value)
{ {
if ($count <= 0) { if ($count <= 0) {
return [0]; return [0];
} }
return array_fill($index, $count, $value); return array_fill($index, $count, $value);
} }
} }

View File

@ -26,24 +26,24 @@ namespace Zxing;
*/ */
final class FormatException extends ReaderException final class FormatException extends ReaderException
{ {
private static $instance; private static ?\Zxing\FormatException $instance = null;
public function __construct($cause = null) public function __construct($cause = null)
{ {
if ($cause) { if ($cause) {
parent::__construct($cause); parent::__construct($cause);
} }
} }
public static function getFormatInstance($cause = null) public static function getFormatInstance($cause = null)
{ {
if (!self::$instance) { if (!self::$instance) {
self::$instance = new FormatException(); self::$instance = new FormatException();
} }
if (self::$isStackTrace) { if (self::$isStackTrace) {
return new FormatException($cause); return new FormatException($cause);
} else { } else {
return self::$instance; return self::$instance;
} }
} }
} }

View File

@ -12,165 +12,176 @@ namespace Zxing;
*/ */
final class GDLuminanceSource extends LuminanceSource final class GDLuminanceSource extends LuminanceSource
{ {
public $luminances; public $luminances;
private $dataWidth; private $dataWidth;
private $dataHeight; private $dataHeight;
private $left; /**
private $top; * @var mixed|int
private $gdImage; */
private $left;
/**
* @var mixed|int
*/
private $top;
/**
* @var mixed|null
*/
private $gdImage;
public function __construct( public function __construct(
$gdImage, $gdImage,
$dataWidth, $dataWidth,
$dataHeight, $dataHeight,
$left = null, $left = null,
$top = null, $top = null,
$width = null, $width = null,
$height = null $height = null
) { ) {
if (!$left && !$top && !$width && !$height) { if (!$left && !$top && !$width && !$height) {
$this->GDLuminanceSource($gdImage, $dataWidth, $dataHeight); $this->GDLuminanceSource($gdImage, $dataWidth, $dataHeight);
return; return;
} }
parent::__construct($width, $height); parent::__construct($width, $height);
if ($left + $width > $dataWidth || $top + $height > $dataHeight) { if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
throw new \InvalidArgumentException("Crop rectangle does not fit within image data."); throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
} }
$this->luminances = $gdImage; $this->luminances = $gdImage;
$this->dataWidth = $dataWidth; $this->dataWidth = $dataWidth;
$this->dataHeight = $dataHeight; $this->dataHeight = $dataHeight;
$this->left = $left; $this->left = $left;
$this->top = $top; $this->top = $top;
} }
public function GDLuminanceSource($gdImage, $width, $height) public function GDLuminanceSource($gdImage, $width, $height): void
{ {
parent::__construct($width, $height); parent::__construct($width, $height);
$this->dataWidth = $width; $this->dataWidth = $width;
$this->dataHeight = $height; $this->dataHeight = $height;
$this->left = 0; $this->left = 0;
$this->top = 0; $this->top = 0;
$this->gdImage = $gdImage; $this->gdImage = $gdImage;
// In order to measure pure decoding speed, we convert the entire image to a greyscale array // 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. // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
$this->luminances = []; $this->luminances = [];
//$this->luminances = $this->grayScaleToBitmap($this->grayscale()); //$this->luminances = $this->grayScaleToBitmap($this->grayscale());
$array = []; $array = [];
$rgb = []; $rgb = [];
for ($j = 0; $j < $height; $j++) { for ($j = 0; $j < $height; $j++) {
for ($i = 0; $i < $width; $i++) { for ($i = 0; $i < $width; $i++) {
$argb = imagecolorat($this->gdImage, $i, $j); $argb = imagecolorat($this->gdImage, $i, $j);
$pixel = imagecolorsforindex($this->gdImage, $argb); $pixel = imagecolorsforindex($this->gdImage, $argb);
$r = $pixel['red']; $r = $pixel['red'];
$g = $pixel['green']; $g = $pixel['green'];
$b = $pixel['blue']; $b = $pixel['blue'];
if ($r == $g && $g == $b) { 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. // Image is already greyscale, so pick any channel.
$this->luminances[] = $r;//(($r + 128) % 256) - 128; $this->luminances[(int)($offset + $x)] = (($r+128) % 256) - 128;
} else { } else {
// Calculate luminance cheaply, favoring green. // Calculate luminance cheaply, favoring green.
$this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128; $this->luminances[(int)($offset + $x)] = (((($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 = $this->grayScaleToBitmap($this->luminances); // $this->luminances = $this->grayScaleToBitmap($this->luminances);
} }
//@Override //@Override
public function getRow($y, $row = null) public function getRow($y, $row = null)
{ {
if ($y < 0 || $y >= $this->getHeight()) { if ($y < 0 || $y >= $this->getHeight()) {
throw new \InvalidArgumentException('Requested row is outside the image: ' . $y); throw new \InvalidArgumentException('Requested row is outside the image: ' . $y);
} }
$width = $this->getWidth(); $width = $this->getWidth();
if ($row == null || count($row) < $width) { if ($row == null || (is_countable($row) ? count($row) : 0) < $width) {
$row = []; $row = [];
} }
$offset = ($y + $this->top) * $this->dataWidth + $this->left; $offset = ($y + $this->top) * $this->dataWidth + $this->left;
$row = arraycopy($this->luminances, $offset, $row, 0, $width); $row = arraycopy($this->luminances, $offset, $row, 0, $width);
return $row; return $row;
} }
//@Override //@Override
public function getMatrix() public function getMatrix()
{ {
$width = $this->getWidth(); $width = $this->getWidth();
$height = $this->getHeight(); $height = $this->getHeight();
// If the caller asks for the entire underlying image, save the copy and give them the // 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. // original data. The docs specifically warn that result.length must be ignored.
if ($width == $this->dataWidth && $height == $this->dataHeight) { if ($width == $this->dataWidth && $height == $this->dataHeight) {
return $this->luminances; return $this->luminances;
} }
$area = $width * $height; $area = $width * $height;
$matrix = []; $matrix = [];
$inputOffset = $this->top * $this->dataWidth + $this->left; $inputOffset = $this->top * $this->dataWidth + $this->left;
// If the width matches the full width of the underlying data, perform a single copy. // If the width matches the full width of the underlying data, perform a single copy.
if ($width == $this->dataWidth) { if ($width == $this->dataWidth) {
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area); $matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
return $matrix; return $matrix;
} }
// Otherwise copy one cropped row at a time. // Otherwise copy one cropped row at a time.
$rgb = $this->luminances; $rgb = $this->luminances;
for ($y = 0; $y < $height; $y++) { for ($y = 0; $y < $height; $y++) {
$outputOffset = $y * $width; $outputOffset = $y * $width;
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width); $matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
$inputOffset += $this->dataWidth; $inputOffset += $this->dataWidth;
} }
return $matrix; return $matrix;
} }
//@Override //@Override
public function isCropSupported() public function isCropSupported()
{ {
return true; return true;
} }
//@Override //@Override
public function crop($left, $top, $width, $height) public function crop($left, $top, $width, $height): \Zxing\GDLuminanceSource
{ {
return new GDLuminanceSource($this->luminances, return new GDLuminanceSource(
$this->dataWidth, $this->luminances,
$this->dataHeight, $this->dataWidth,
$this->left + $left, $this->dataHeight,
$this->top + $top, $this->left + $left,
$width, $this->top + $top,
$height); $width,
} $height
);
}
} }

View File

@ -8,143 +8,151 @@ namespace Zxing;
*/ */
final class IMagickLuminanceSource extends LuminanceSource final class IMagickLuminanceSource extends LuminanceSource
{ {
public $luminances; public $luminances;
private $dataWidth; private $dataWidth;
private $dataHeight; private $dataHeight;
private $left; /**
private $top; * @var mixed|int
private $image; */
private $left;
/**
* @var mixed|int
*/
private $top;
private ?\Imagick $image = null;
public function __construct( public function __construct(
\Imagick $image, \Imagick $image,
$dataWidth, $dataWidth,
$dataHeight, $dataHeight,
$left = null, $left = null,
$top = null, $top = null,
$width = null, $width = null,
$height = null $height = null
) { ) {
if (!$left && !$top && !$width && !$height) { if (!$left && !$top && !$width && !$height) {
$this->_IMagickLuminanceSource($image, $dataWidth, $dataHeight); $this->_IMagickLuminanceSource($image, $dataWidth, $dataHeight);
return; return;
} }
parent::__construct($width, $height); parent::__construct($width, $height);
if ($left + $width > $dataWidth || $top + $height > $dataHeight) { if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
throw new \InvalidArgumentException("Crop rectangle does not fit within image data."); throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
} }
$this->luminances = $image; $this->luminances = $image;
$this->dataWidth = $dataWidth; $this->dataWidth = $dataWidth;
$this->dataHeight = $dataHeight; $this->dataHeight = $dataHeight;
$this->left = $left; $this->left = $left;
$this->top = $top; $this->top = $top;
} }
public function _IMagickLuminanceSource(\Imagick $image, $width, $height) public function _IMagickLuminanceSource(\Imagick $image, $width, $height): void
{ {
parent::__construct($width, $height); parent::__construct($width, $height);
$this->dataWidth = $width; $this->dataWidth = $width;
$this->dataHeight = $height; $this->dataHeight = $height;
$this->left = 0; $this->left = 0;
$this->top = 0; $this->top = 0;
$this->image = $image; $this->image = $image;
// In order to measure pure decoding speed, we convert the entire image to a greyscale array // 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. // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
$this->luminances = []; $this->luminances = [];
$image->setImageColorspace(\Imagick::COLORSPACE_GRAY); $image->setImageColorspace(\Imagick::COLORSPACE_GRAY);
// $image->newPseudoImage(0, 0, "magick:rose"); // $image->newPseudoImage(0, 0, "magick:rose");
$pixels = $image->exportImagePixels(1, 1, $width, $height, "RGB", \Imagick::PIXEL_CHAR); $pixels = $image->exportImagePixels(1, 1, $width, $height, "RGB", \Imagick::PIXEL_CHAR);
$array = []; $array = [];
$rgb = []; $rgb = [];
$countPixels = count($pixels); $countPixels = count($pixels);
for ($i = 0; $i < $countPixels; $i += 3) { for ($i = 0; $i < $countPixels; $i += 3) {
$r = $pixels[$i] & 0xff; $r = $pixels[$i] & 0xff;
$g = $pixels[$i + 1] & 0xff; $g = $pixels[$i + 1] & 0xff;
$b = $pixels[$i + 2] & 0xff; $b = $pixels[$i + 2] & 0xff;
if ($r == $g && $g == $b) { if ($r == $g && $g == $b) {
// Image is already greyscale, so pick any channel. // Image is already greyscale, so pick any channel.
$this->luminances[] = $r;//(($r + 128) % 256) - 128; $this->luminances[] = $r;//(($r + 128) % 256) - 128;
} else { } else {
// Calculate luminance cheaply, favoring green. // Calculate luminance cheaply, favoring green.
$this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128; $this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
} }
} }
} }
//@Override //@Override
public function getRow($y, $row = null) public function getRow($y, $row = null)
{ {
if ($y < 0 || $y >= $this->getHeight()) { if ($y < 0 || $y >= $this->getHeight()) {
throw new \InvalidArgumentException('Requested row is outside the image: ' . $y); throw new \InvalidArgumentException('Requested row is outside the image: ' . $y);
} }
$width = $this->getWidth(); $width = $this->getWidth();
if ($row == null || count($row) < $width) { if ($row == null || (is_countable($row) ? count($row) : 0) < $width) {
$row = []; $row = [];
} }
$offset = ($y + $this->top) * $this->dataWidth + $this->left; $offset = ($y + $this->top) * $this->dataWidth + $this->left;
$row = arraycopy($this->luminances, $offset, $row, 0, $width); $row = arraycopy($this->luminances, $offset, $row, 0, $width);
return $row; return $row;
} }
//@Override //@Override
public function getMatrix() public function getMatrix()
{ {
$width = $this->getWidth(); $width = $this->getWidth();
$height = $this->getHeight(); $height = $this->getHeight();
// If the caller asks for the entire underlying image, save the copy and give them the // 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. // original data. The docs specifically warn that result.length must be ignored.
if ($width == $this->dataWidth && $height == $this->dataHeight) { if ($width == $this->dataWidth && $height == $this->dataHeight) {
return $this->luminances; return $this->luminances;
} }
$area = $width * $height; $area = $width * $height;
$matrix = []; $matrix = [];
$inputOffset = $this->top * $this->dataWidth + $this->left; $inputOffset = $this->top * $this->dataWidth + $this->left;
// If the width matches the full width of the underlying data, perform a single copy. // If the width matches the full width of the underlying data, perform a single copy.
if ($width == $this->dataWidth) { if ($width == $this->dataWidth) {
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area); $matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
return $matrix; return $matrix;
} }
// Otherwise copy one cropped row at a time. // Otherwise copy one cropped row at a time.
$rgb = $this->luminances; $rgb = $this->luminances;
for ($y = 0; $y < $height; $y++) { for ($y = 0; $y < $height; $y++) {
$outputOffset = $y * $width; $outputOffset = $y * $width;
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width); $matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
$inputOffset += $this->dataWidth; $inputOffset += $this->dataWidth;
} }
return $matrix; return $matrix;
} }
//@Override //@Override
public function isCropSupported() public function isCropSupported(): bool
{ {
return true; return true;
} }
//@Override //@Override
public function crop($left, $top, $width, $height) public function crop($left, $top, $width, $height)
{ {
return $this->luminances->cropImage($width, $height, $left, $top); return $this->luminances->cropImage($width, $height, $left, $top);
return new GDLuminanceSource($this->luminances, return new GDLuminanceSource(
$this->dataWidth, $this->luminances,
$this->dataHeight, $this->dataWidth,
$this->left + $left, $this->dataHeight,
$this->top + $top, $this->left + $left,
$width, $this->top + $top,
$height); $width,
} $height
);
}
} }

View File

@ -28,14 +28,8 @@ namespace Zxing;
*/ */
abstract class LuminanceSource abstract class LuminanceSource
{ {
public function __construct(private $width, private $height)
private $width;
private $height;
public function __construct($width, $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 * larger than width * height bytes on some platforms. Do not modify the contents
* of the result. * 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 $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; return $this->height;
} }
@ -67,7 +61,7 @@ abstract class LuminanceSource
/** /**
* @return bool Whether this subclass supports cropping. * @return bool Whether this subclass supports cropping.
*/ */
public function isCropSupported() public function isCropSupported(): bool
{ {
return false; return false;
} }
@ -76,12 +70,12 @@ abstract class LuminanceSource
* Returns a new object with cropped image data. Implementations may keep a reference to the * 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. * original data rather than a copy. Only callable if isCropSupported() is true.
* *
* @param left The left coordinate, which must be in [0,getWidth()) * @param $left The left coordinate, which must be in [0,getWidth())
* @param top The top coordinate, which must be in [0,getHeight()) * @param $top The top coordinate, which must be in [0,getHeight())
* @param width The width of the rectangle to crop. * @param $width The width of the rectangle to crop.
* @param height The height 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) 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; return false;
} }
@ -100,16 +94,16 @@ abstract class LuminanceSource
* @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes * @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). * white and vice versa, and each value becomes (255-value).
*/ */
public function invert() // public function invert()
{ // {
return new InvertedLuminanceSource($this); // return new InvertedLuminanceSource($this);
} // }
/** /**
* Returns a new object with rotated image data by 90 degrees counterclockwise. * Returns a new object with rotated image data by 90 degrees counterclockwise.
* Only callable if {@link #isRotateSupported()} is true. * 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() public function rotateCounterClockwise()
{ {
@ -120,27 +114,27 @@ abstract class LuminanceSource
* Returns a new object with rotated image data by 45 degrees counterclockwise. * Returns a new object with rotated image data by 45 degrees counterclockwise.
* Only callable if {@link #isRotateSupported()} is true. * 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() public function rotateCounterClockwise45()
{ {
throw new \Exception("This luminance source does not support rotation by 45 degrees."); throw new \Exception("This luminance source does not support rotation by 45 degrees.");
} }
public final function toString() final public function toString()
{ {
$row = []; $row = [];
$result = ''; $result = '';
for ($y = 0; $y < $this->height; $y++) { for ($y = 0; $y < $this->height; $y++) {
$row = $this->getRow($y, $row); $row = $this->getRow($y, $row);
for ($x = 0; $x < $this->width; $x++) { for ($x = 0; $x < $this->width; $x++) {
$luminance = $row[$x] & 0xFF; $luminance = $row[$x] & 0xFF;
$c = ''; $c = '';
if ($luminance < 0x40) { if ($luminance < 0x40) {
$c = '#'; $c = '#';
} else if ($luminance < 0x80) { } elseif ($luminance < 0x80) {
$c = '+'; $c = '+';
} else if ($luminance < 0xC0) { } elseif ($luminance < 0xC0) {
$c = '.'; $c = '.';
} else { } else {
$c = ' '; $c = ' ';
@ -167,5 +161,5 @@ abstract class LuminanceSource
* @return array * @return array
* An array containing the luminance data. * An array containing the luminance data.
*/ */
public abstract function getRow($y, $row); abstract public function getRow($y, $row);
} }

View File

@ -25,14 +25,14 @@ namespace Zxing;
*/ */
final class NotFoundException extends ReaderException final class NotFoundException extends ReaderException
{ {
private static $instance; private static ?\Zxing\NotFoundException $instance = null;
public static function getNotFoundInstance() public static function getNotFoundInstance()
{ {
if (!self::$instance) { if (!self::$instance) {
self::$instance = new NotFoundException(); self::$instance = new NotFoundException();
} }
return self::$instance; return self::$instance;
} }
} }

View File

@ -29,154 +29,154 @@ namespace Zxing;
*/ */
final class PlanarYUVLuminanceSource extends LuminanceSource 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; public function __construct(
private $dataWidth; private $yuvData,
private $dataHeight; $dataWidth,
private $left; $dataHeight,
private $top; $left,
$top,
$width,
$height,
$reverseHorizontal
)
{
parent::__construct($width, $height);
public function __construct($yuvData, if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
$dataWidth, throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
$dataHeight, }
$left, $this->dataWidth = $dataWidth;
$top, $this->dataHeight = $dataHeight;
$width, $this->left = $left;
$height, $this->top = $top;
$reverseHorizontal) if ($reverseHorizontal) {
{ $this->reverseHorizontal($width, $height);
parent::__construct($width, $height); }
}
if ($left + $width > $dataWidth || $top + $height > $dataHeight) { //@Override
throw new \InvalidArgumentException("Crop rectangle does not fit within image data."); 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; return $row;
$this->dataWidth = $dataWidth; }
$this->dataHeight = $dataHeight;
$this->left = $left;
$this->top = $top;
if ($reverseHorizontal) {
$this->reverseHorizontal($width, $height);
}
}
//@Override //@Override
public function getRow($y, $row = null) public function getMatrix()
{ {
if ($y < 0 || $y >= getHeight()) { $width = $this->getWidth();
throw new \InvalidArgumentException("Requested row is outside the image: " + y); $height = $this->getHeight();
}
$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);
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 $area = $width * $height;
public function getMatrix() $matrix = [];//new byte[area];
{ $inputOffset = $this->top * $this->dataWidth + $this->left;
$width = $this->getWidth();
$height = $this->getHeight();
// If the caller asks for the entire underlying image, save the copy and give them the // If the width matches the full width of the underlying data, perform a single copy.
// original data. The docs specifically warn that result.length must be ignored. if ($width == $this->dataWidth) {
if ($width == $this->dataWidth && $height == $this->dataHeight) { $matrix = arraycopy($this->yuvData, $inputOffset, $matrix, 0, $area);
return $this->yuvData;
}
$area = $width * $height; return $matrix;
$matrix = [];//new byte[area]; }
$inputOffset = $this->top * $this->dataWidth + $this->left;
// If the width matches the full width of the underlying data, perform a single copy. // Otherwise copy one cropped row at a time.
if ($width == $this->dataWidth) { $yuv = $this->yuvData;
$matrix = arraycopy($this->yuvData, $inputOffset, $matrix, 0, $area); 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. // @Override
$yuv = $this->yuvData; public function isCropSupported()
for ($y = 0; $y < $height; $y++) { {
$outputOffset = $y * $width; return true;
$matrix = arraycopy($this->yuvData, $inputOffset, $matrix, $outputOffset, $width); }
$inputOffset += $this->dataWidth;
}
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 renderThumbnail()
public function isCropSupported() {
{ $width = (int)($this->getWidth() / self::$THUMBNAIL_SCALE_FACTOR);
return true; $height = (int)($this->getHeight() / self::$THUMBNAIL_SCALE_FACTOR);
} $pixels = [];//new int[width * height];
$yuv = $this->yuvData;
$inputOffset = $this->top * $this->dataWidth + $this->left;
// @Override for ($y = 0; $y < $height; $y++) {
public function crop($left, $top, $width, $height) $outputOffset = $y * $width;
{ for ($x = 0; $x < $width; $x++) {
return new PlanarYUVLuminanceSource($this->yuvData, $grey = ($yuv[$inputOffset + $x * self::$THUMBNAIL_SCALE_FACTOR] & 0xff);
$this->dataWidth, $pixels[$outputOffset + $x] = (0xFF000000 | ($grey * 0x00010101));
$this->dataHeight, }
$this->left + $left, $inputOffset += $this->dataWidth * self::$THUMBNAIL_SCALE_FACTOR;
$this->top + $top, }
$width,
$height,
false);
}
public function renderThumbnail() return $pixels;
{ }
$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;
for ($y = 0; $y < $height; $y++) { /**
$outputOffset = $y * $width; * @return width of image from {@link #renderThumbnail()}
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()}
*/
/*
public int getThumbnailWidth() { 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() { public int getThumbnailHeight() {
return getHeight() / THUMBNAIL_SCALE_FACTOR; return getHeight() / THUMBNAIL_SCALE_FACTOR;
} }
private void reverseHorizontal(int width, int height) { private void reverseHorizontal(int width, int height) {
byte[] yuvData = this.yuvData; byte[] yuvData = this.yuvData;
for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) { for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {
int middle = rowStart + width / 2; int middle = rowStart + width / 2;
for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) { for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {
byte temp = yuvData[x1]; byte temp = yuvData[x1];
yuvData[x1] = yuvData[x2]; yuvData[x1] = yuvData[x2];
yuvData[x2] = temp; yuvData[x2] = temp;
} }
} }
} }
*/ */
} }

View File

@ -7,99 +7,95 @@ use Zxing\Qrcode\QRCodeReader;
final class QrReader final class QrReader
{ {
const SOURCE_TYPE_FILE = 'file'; public const SOURCE_TYPE_FILE = 'file';
const SOURCE_TYPE_BLOB = 'blob'; public const SOURCE_TYPE_BLOB = 'blob';
const SOURCE_TYPE_RESOURCE = 'resource'; public const SOURCE_TYPE_RESOURCE = 'resource';
private $bitmap; private readonly \Zxing\BinaryBitmap $bitmap;
private $reader; private readonly \Zxing\Qrcode\QRCodeReader $reader;
private $result; private \Zxing\Result|bool|null $result = null;
public function __construct($imgSource, $sourceType = QrReader::SOURCE_TYPE_FILE, $useImagickIfAvailable = true) public function __construct($imgSource, $sourceType = QrReader::SOURCE_TYPE_FILE, $useImagickIfAvailable = true)
{ {
if (!in_array($sourceType, [ if (!in_array($sourceType, [
self::SOURCE_TYPE_FILE, self::SOURCE_TYPE_FILE,
self::SOURCE_TYPE_BLOB, self::SOURCE_TYPE_BLOB,
self::SOURCE_TYPE_RESOURCE, self::SOURCE_TYPE_RESOURCE,
], true)) { ], true)) {
throw new \InvalidArgumentException('Invalid image source.'); throw new \InvalidArgumentException('Invalid image source.');
} }
$im = null; $im = null;
switch ($sourceType) { switch ($sourceType) {
case QrReader::SOURCE_TYPE_FILE: case QrReader::SOURCE_TYPE_FILE:
if ($useImagickIfAvailable && extension_loaded('imagick')) { if ($useImagickIfAvailable && extension_loaded('imagick')) {
$im = new \Imagick(); $im = new \Imagick();
$im->readImage($imgSource); $im->readImage($imgSource);
} else { } else {
$image = file_get_contents($imgSource); $image = file_get_contents($imgSource);
$im = imagecreatefromstring($image); $im = imagecreatefromstring($image);
} }
break; break;
case QrReader::SOURCE_TYPE_BLOB: case QrReader::SOURCE_TYPE_BLOB:
if ($useImagickIfAvailable && extension_loaded('imagick')) { if ($useImagickIfAvailable && extension_loaded('imagick')) {
$im = new \Imagick(); $im = new \Imagick();
$im->readImageBlob($imgSource); $im->readImageBlob($imgSource);
} else { } else {
$im = imagecreatefromstring($imgSource); $im = imagecreatefromstring($imgSource);
} }
break; break;
case QrReader::SOURCE_TYPE_RESOURCE: case QrReader::SOURCE_TYPE_RESOURCE:
$im = $imgSource; $im = $imgSource;
if ($useImagickIfAvailable && extension_loaded('imagick')) { if ($useImagickIfAvailable && extension_loaded('imagick')) {
$useImagickIfAvailable = true; $useImagickIfAvailable = true;
} else { } else {
$useImagickIfAvailable = false; $useImagickIfAvailable = false;
} }
break; break;
} }
if ($useImagickIfAvailable && extension_loaded('imagick')) { if ($useImagickIfAvailable && extension_loaded('imagick')) {
if (!$im instanceof \Imagick) { if (!$im instanceof \Imagick) {
throw new \InvalidArgumentException('Invalid image source.'); throw new \InvalidArgumentException('Invalid image source.');
} }
$width = $im->getImageWidth(); $width = $im->getImageWidth();
$height = $im->getImageHeight(); $height = $im->getImageHeight();
$source = new IMagickLuminanceSource($im, $width, $height); $source = new IMagickLuminanceSource($im, $width, $height);
} else { } else {
if (!is_resource($im) && !is_object($im)) { if (!$im instanceof \GdImage && !is_object($im)) {
throw new \InvalidArgumentException('Invalid image source.'); throw new \InvalidArgumentException('Invalid image source.');
} }
$width = imagesx($im); $width = imagesx($im);
$height = imagesy($im); $height = imagesy($im);
$source = new GDLuminanceSource($im, $width, $height); $source = new GDLuminanceSource($im, $width, $height);
} }
$histo = new HybridBinarizer($source); $histo = new HybridBinarizer($source);
$this->bitmap = new BinaryBitmap($histo); $this->bitmap = new BinaryBitmap($histo);
$this->reader = new QRCodeReader(); $this->reader = new QRCodeReader();
} }
public function decode() public function decode($hints = null): void
{ {
try { try {
$this->result = $this->reader->decode($this->bitmap); $this->result = $this->reader->decode($this->bitmap, $hints);
} catch (NotFoundException $er) { } catch (NotFoundException|FormatException|ChecksumException) {
$this->result = false; $this->result = false;
} catch (FormatException $er) { }
$this->result = false; }
} catch (ChecksumException $er) {
$this->result = false;
}
}
public function text() public function text($hints = null)
{ {
$this->decode(); $this->decode($hints);
if ($this->result !== false && method_exists($this->result, 'toString')) { if ($this->result !== false && method_exists($this->result, 'toString')) {
return $this->result->toString(); return $this->result->toString();
} }
return $this->result; return $this->result;
} }
public function getResult() public function getResult()
{ {
return $this->result; return $this->result;
} }
} }

View File

@ -17,247 +17,246 @@
namespace Zxing\Qrcode\Decoder; namespace Zxing\Qrcode\Decoder;
use Zxing\FormatException;
use Zxing\Common\BitMatrix; use Zxing\Common\BitMatrix;
use Zxing\FormatException;
/** /**
* @author Sean Owen * @author Sean Owen
*/ */
final class BitMatrixParser final class BitMatrixParser
{ {
private $bitMatrix;
/**
* @var mixed|null
*/
private $parsedVersion;
private $parsedFormatInfo;
private $mirror;
private $bitMatrix; /**
private $parsedVersion; * @param $bitMatrix {@link BitMatrix} to parse
private $parsedFormatInfo; *
private $mirror; * @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 * <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
* @throws FormatException if dimension is not >= 21 and 1 mod 4 * QR Code.</p>
*/ *
public function __construct($bitMatrix) * @return bytes encoded within the QR Code
{ * @throws FormatException if the exact number of bytes expected is not read
$dimension = $bitMatrix->getHeight(); */
if ($dimension < 21 || ($dimension & 0x03) != 1) { public function readCodewords()
throw FormatException::getFormatInstance(); {
} $formatInfo = $this->readFormatInformation();
$this->bitMatrix = $bitMatrix; $version = $this->readVersion();
}
/** // Get the data mask for the format used in this QR Code. This will exclude
* <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the // some bits from reading as we wind through the bit matrix.
* correct order in order to reconstruct the codewords bytes contained within the $dataMask = DataMask::forReference($formatInfo->getDataMask());
* QR Code.</p> $dimension = $this->bitMatrix->getHeight();
* $dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
* @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(); $functionPattern = $version->buildFunctionPattern();
$version = $this->readVersion();
// Get the data mask for the format used in this QR Code. This will exclude $readingUp = true;
// some bits from reading as we wind through the bit matrix. if ($version->getTotalCodewords()) {
$dataMask = DataMask::forReference($formatInfo->getDataMask()); $result = fill_array(0, $version->getTotalCodewords(), 0);
$dimension = $this->bitMatrix->getHeight(); } else {
$dataMask->unmaskBitMatrix($this->bitMatrix, $dimension); $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()) { * <p>Reads format information from one of its two locations within the QR Code.</p>
$result = fill_array(0, $version->getTotalCodewords(), 0); *
} else { * @return {@link FormatInformation} encapsulating the QR Code's format info
$result = []; * @throws FormatException if both format information locations cannot be parsed as
} * the valid encoding of format information
$resultOffset = 0; */
$currentByte = 0; public function readFormatInformation()
$bitsRead = 0; {
// Read columns in pairs, from right to left if ($this->parsedFormatInfo != null) {
for ($j = $dimension - 1; $j > 0; $j -= 2) { return $this->parsedFormatInfo;
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();
}
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);
}
/** // Read the top-right/bottom-left pattern too
* <p>Reads format information from one of its two locations within the QR Code.</p> $dimension = $this->bitMatrix->getHeight();
* $formatInfoBits2 = 0;
* @return {@link FormatInformation} encapsulating the QR Code's format info $jMin = $dimension - 7;
* @throws FormatException if both format information locations cannot be parsed as for ($j = $dimension - 1; $j >= $jMin; $j--) {
* the valid encoding of format information $formatInfoBits2 = $this->copyBit(8, $j, $formatInfoBits2);
*/ }
public function readFormatInformation() for ($i = $dimension - 8; $i < $dimension; $i++) {
{ $formatInfoBits2 = $this->copyBit($i, 8, $formatInfoBits2);
}
if ($this->parsedFormatInfo != null) { $parsedFormatInfo = FormatInformation::decodeFormatInformation($formatInfoBits1, $formatInfoBits2);
return $this->parsedFormatInfo; if ($parsedFormatInfo != null) {
} return $parsedFormatInfo;
}
throw FormatException::getFormatInstance();
}
// Read top-left format info bits private function copyBit($i, $j, $versionBits)
$formatInfoBits1 = 0; {
for ($i = 0; $i < 6; $i++) { $bit = $this->mirror ? $this->bitMatrix->get($j, $i) : $this->bitMatrix->get($i, $j);
$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);
}
// Read the top-right/bottom-left pattern too return $bit ? ($versionBits << 1) | 0x1 : $versionBits << 1;
$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);
}
$parsedFormatInfo = FormatInformation::decodeFormatInformation($formatInfoBits1, $formatInfoBits2); /**
if ($parsedFormatInfo != null) { * <p>Reads version information from one of its two locations within the QR Code.</p>
return $parsedFormatInfo; *
} * @return {@link Version} encapsulating the QR Code's version
throw FormatException::getFormatInstance(); * @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) $dimension = $this->bitMatrix->getHeight();
{
$bit = $this->mirror ? $this->bitMatrix->get($j, $i) : $this->bitMatrix->get($i, $j);
return $bit ? ($versionBits << 1) | 0x1 : $versionBits << 1; $provisionalVersion = ($dimension - 17) / 4;
} if ($provisionalVersion <= 6) {
return Version::getVersionForNumber($provisionalVersion);
}
/** // Read top-right version info: 3 wide by 6 tall
* <p>Reads version information from one of its two locations within the QR Code.</p> $versionBits = 0;
* $ijMin = $dimension - 11;
* @return {@link Version} encapsulating the QR Code's version for ($j = 5; $j >= 0; $j--) {
* @throws FormatException if both version information locations cannot be parsed as for ($i = $dimension - 9; $i >= $ijMin; $i--) {
* the valid encoding of version information $versionBits = $this->copyBit($i, $j, $versionBits);
*/ }
public function readVersion() }
{
if ($this->parsedVersion != null) { $theParsedVersion = Version::decodeVersionInformation($versionBits);
return $this->parsedVersion; if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
} $this->parsedVersion = $theParsedVersion;
$dimension = $this->bitMatrix->getHeight(); return $theParsedVersion;
}
$provisionalVersion = ($dimension - 17) / 4; // Hmm, failed. Try bottom left: 6 wide by 3 tall
if ($provisionalVersion <= 6) { $versionBits = 0;
return Version::getVersionForNumber($provisionalVersion); 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 $theParsedVersion = Version::decodeVersionInformation($versionBits);
$versionBits = 0; if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
$ijMin = $dimension - 11; $this->parsedVersion = $theParsedVersion;
for ($j = 5; $j >= 0; $j--) {
for ($i = $dimension - 9; $i >= $ijMin; $i--) {
$versionBits = $this->copyBit($i, $j, $versionBits);
}
}
$theParsedVersion = Version::decodeVersionInformation($versionBits); return $theParsedVersion;
if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) { }
$this->parsedVersion = $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; * Prepare the parser for a mirrored operation.
for ($i = 5; $i >= 0; $i--) { * This flag has effect only on the {@link #readFormatInformation()} and the
for ($j = $dimension - 9; $j >= $ijMin; $j--) { * {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
$versionBits = $this->copyBit($i, $j, $versionBits); * {@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); /** Mirror the bit matrix in order to attempt a second reading. */
if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) { public function mirror(): void
$this->parsedVersion = $theParsedVersion; {
for ($x = 0; $x < $this->bitMatrix->getWidth(); $x++) {
return $theParsedVersion; for ($y = $x + 1; $y < $this->bitMatrix->getHeight(); $y++) {
} if ($this->bitMatrix->get($x, $y) != $this->bitMatrix->get($y, $x)) {
throw FormatException::getFormatInstance(); $this->bitMatrix->flip($y, $x);
} $this->bitMatrix->flip($x, $y);
}
/** }
* 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);
}
}
}
}
} }

View File

@ -26,104 +26,102 @@ namespace Zxing\Qrcode\Decoder;
*/ */
final class DataBlock final class DataBlock
{ {
private $numDataCodewords; //byte[]
private $codewords; //byte[]
private function __construct($numDataCodewords, $codewords) private function __construct(private $numDataCodewords, private $codewords)
{ {
$this->numDataCodewords = $numDataCodewords; }
$this->codewords = $codewords;
}
/** /**
* <p>When QR Codes use multiple data blocks, they are actually interleaved. * <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 * 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> * method will separate the data into original blocks.</p>
* *
* @param rawCodewords bytes as read directly from the QR Code * @param bytes $rawCodewords as read directly from the QR Code
* @param version version of the QR Code * @param version $version of the QR Code
* @param ecLevel error-correction level 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 * @return array DataBlocks containing original bytes, "de-interleaved" from representation in the
* QR Code * QR Code
*/ */
public static function getDataBlocks($rawCodewords, public static function getDataBlocks(
$version, $rawCodewords,
$ecLevel) $version,
{ $ecLevel
)
{
if ((is_countable($rawCodewords) ? count($rawCodewords) : 0) != $version->getTotalCodewords()) {
throw new \InvalidArgumentException();
}
if (count($rawCodewords) != $version->getTotalCodewords()) { // Figure out the number and size of data blocks used by this version and
throw new \InvalidArgumentException(); // error correction level
} $ecBlocks = $version->getECBlocksForLevel($ecLevel);
// Figure out the number and size of data blocks used by this version and // First count the total number of data blocks
// error correction level $totalBlocks = 0;
$ecBlocks = $version->getECBlocksForLevel($ecLevel); $ecBlockArray = $ecBlocks->getECBlocks();
foreach ($ecBlockArray as $ecBlock) {
$totalBlocks += $ecBlock->getCount();
}
// First count the total number of data blocks // Now establish DataBlocks of the appropriate size and number of data codewords
$totalBlocks = 0; $result = [];//new DataBlock[$totalBlocks];
$ecBlockArray = $ecBlocks->getECBlocks(); $numResultBlocks = 0;
foreach ($ecBlockArray as $ecBlock) { foreach ($ecBlockArray as $ecBlock) {
$totalBlocks += $ecBlock->getCount(); $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 // All blocks have the same amount of data, except that the last n
$result = [];//new DataBlock[$totalBlocks]; // (where n may be 0) have 1 more byte. Figure out where these start.
$numResultBlocks = 0; $shorterBlocksTotalCodewords = is_countable($result[0]->codewords) ? count($result[0]->codewords) : 0;
foreach ($ecBlockArray as $ecBlock) { $longerBlocksStartAt = count($result) - 1;
$ecBlockCount = $ecBlock->getCount(); while ($longerBlocksStartAt >= 0) {
for ($i = 0; $i < $ecBlockCount; $i++) { $numCodewords = is_countable($result[$longerBlocksStartAt]->codewords) ? count($result[$longerBlocksStartAt]->codewords) : 0;
$numDataCodewords = $ecBlock->getDataCodewords(); if ($numCodewords == $shorterBlocksTotalCodewords) {
$numBlockCodewords = $ecBlocks->getECCodewordsPerBlock() + $numDataCodewords; break;
$result[$numResultBlocks++] = new DataBlock($numDataCodewords, fill_array(0, $numBlockCodewords, 0)); }
} $longerBlocksStartAt--;
} }
$longerBlocksStartAt++;
// All blocks have the same amount of data, except that the last n $shorterBlocksNumDataCodewords = $shorterBlocksTotalCodewords - $ecBlocks->getECCodewordsPerBlock();
// (where n may be 0) have 1 more byte. Figure out where these start. // The last elements of result may be 1 element longer;
$shorterBlocksTotalCodewords = count($result[0]->codewords); // first fill out as many elements as all of them have
$longerBlocksStartAt = count($result) - 1; $rawCodewordsOffset = 0;
while ($longerBlocksStartAt >= 0) { for ($i = 0; $i < $shorterBlocksNumDataCodewords; $i++) {
$numCodewords = count($result[$longerBlocksStartAt]->codewords); for ($j = 0; $j < $numResultBlocks; $j++) {
if ($numCodewords == $shorterBlocksTotalCodewords) { $result[$j]->codewords[$i] = $rawCodewords[$rawCodewordsOffset++];
break; }
} }
$longerBlocksStartAt--; // Fill out the last data block in the longer ones
} for ($j = $longerBlocksStartAt; $j < $numResultBlocks; $j++) {
$longerBlocksStartAt++; $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(); return $result;
// 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; public function getNumDataCodewords()
} {
return $this->numDataCodewords;
}
public function getNumDataCodewords() public function getCodewords()
{ {
return $this->numDataCodewords; return $this->codewords;
} }
public function getCodewords()
{
return $this->codewords;
}
} }

View File

@ -32,65 +32,63 @@ use Zxing\Common\BitMatrix;
*/ */
abstract class DataMask abstract class DataMask
{ {
/**
* See ISO 18004:2006 6.8.1
*/
private static array $DATA_MASKS = [];
/** public function __construct()
* See ISO 18004:2006 6.8.1 {
*/ }
private static $DATA_MASKS = [];
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() return self::$DATA_MASKS[$reference];
{ }
self::$DATA_MASKS = [
new DataMask000(),
new DataMask001(),
new DataMask010(),
new DataMask011(),
new DataMask100(),
new DataMask101(),
new DataMask110(),
new DataMask111(),
];
}
/** /**
* @param reference a value between 0 and 7 indicating one of the eight possible * <p>Implementations of this method reverse the data masking process applied to a QR Code and
* data mask patterns a QR Code may use * make its bits ready to read.</p>
* *
* @return DataMask encapsulating the data mask pattern * @param representation $bits of QR Code bits
*/ * @param dimension $dimension of QR Code, represented by bits, being unmasked
public static function forReference($reference) */
{ final public function unmaskBitMatrix($bits, $dimension): void
if ($reference < 0 || $reference > 7) { {
throw new \InvalidArgumentException(); 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]; abstract public function isMasked($i, $j);
}
/**
* <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);
} }
DataMask::Init(); DataMask::Init();
@ -100,11 +98,11 @@ DataMask::Init();
*/ */
final class DataMask000 extends DataMask final class DataMask000 extends DataMask
{ {
// @Override // @Override
public function isMasked($i, $j) public function isMasked($i, $j)
{ {
return (($i + $j) & 0x01) == 0; return (($i + $j) & 0x01) == 0;
} }
} }
/** /**
@ -112,11 +110,11 @@ final class DataMask000 extends DataMask
*/ */
final class DataMask001 extends DataMask final class DataMask001 extends DataMask
{ {
//@Override //@Override
public function isMasked($i, $j) public function isMasked($i, $j)
{ {
return ($i & 0x01) == 0; return ($i & 0x01) == 0;
} }
} }
/** /**
@ -124,11 +122,11 @@ final class DataMask001 extends DataMask
*/ */
final class DataMask010 extends DataMask final class DataMask010 extends DataMask
{ {
//@Override //@Override
public function isMasked($i, $j) public function isMasked($i, $j)
{ {
return $j % 3 == 0; return $j % 3 == 0;
} }
} }
/** /**
@ -136,11 +134,11 @@ final class DataMask010 extends DataMask
*/ */
final class DataMask011 extends DataMask final class DataMask011 extends DataMask
{ {
//@Override //@Override
public function isMasked($i, $j) public function isMasked($i, $j)
{ {
return ($i + $j) % 3 == 0; return ($i + $j) % 3 == 0;
} }
} }
/** /**
@ -148,11 +146,11 @@ final class DataMask011 extends DataMask
*/ */
final class DataMask100 extends DataMask final class DataMask100 extends DataMask
{ {
//@Override //@Override
public function isMasked($i, $j) public function isMasked($i, $j)
{ {
return (int)(((int)($i / 2) + (int)($j / 3)) & 0x01) == 0; return (int)(((int)($i / 2) + (int)($j / 3)) & 0x01) == 0;
} }
} }
/** /**
@ -160,13 +158,13 @@ final class DataMask100 extends DataMask
*/ */
final class DataMask101 extends DataMask final class DataMask101 extends DataMask
{ {
//@Override //@Override
public function isMasked($i, $j) public function isMasked($i, $j)
{ {
$temp = $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 final class DataMask110 extends DataMask
{ {
//@Override //@Override
public function isMasked($i, $j) public function isMasked($i, $j)
{ {
$temp = $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 final class DataMask111 extends DataMask
{ {
//@Override //@Override
public function isMasked($i, $j) public function isMasked($i, $j)
{ {
return (((($i + $j) & 0x01) + (($i * $j) % 3)) & 0x01) == 0; return (((($i + $j) & 0x01) + (($i * $j) % 3)) & 0x01) == 0;
} }
} }

View File

@ -17,13 +17,10 @@
namespace Zxing\Qrcode\Decoder; namespace Zxing\Qrcode\Decoder;
use Zxing\DecodeHintType;
use Zxing\FormatException;
use Zxing\Common\BitSource; use Zxing\Common\BitSource;
use Zxing\Common\CharacterSetECI; use Zxing\Common\CharacterSetECI;
use Zxing\Common\DecoderResult; 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 * <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 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;
/** public static function decode(
* See ISO 18004:2006, 6.4.4 Table 5 $bytes,
*/ $version,
private static $ALPHANUMERIC_CHARS = [ $ecLevel,
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', $hints
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', ): \Zxing\Common\DecoderResult
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', {
' ', '$', '%', '*', '+', '-', '.', '/', ':', $bits = new BitSource($bytes);
]; $result = '';//new StringBuilder(50);
private static $GB2312_SUBSET = 1; $byteSegments = [];
$symbolSequence = -1;
$parityData = -1;
public static function decode($bytes, try {
$version, $currentCharacterSetECI = null;
$ecLevel, $fc1InEffect = false;
$hints) $mode = '';
{ do {
$bits = new BitSource($bytes); // While still another segment to read...
$result = '';//new StringBuilder(50); if ($bits->available() < 4) {
$byteSegments = []; // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
$symbolSequence = -1; $mode = Mode::$TERMINATOR;
$parityData = -1; } 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 { return new DecoderResult(
$currentCharacterSetECI = null; $bytes,
$fc1InEffect = false; $result,
$mode = ''; empty($byteSegments) ? null : $byteSegments,
do { $ecLevel == null ? null : 'L',//ErrorCorrectionLevel::toString($ecLevel),
// While still another segment to read... $symbolSequence,
if ($bits->available() < 4) { $parityData
// 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, private static function parseECIValue($bits)
$result, {
empty($byteSegments) ? null : $byteSegments, $firstByte = $bits->readBits(8);
$ecLevel == null ? null : 'L',//ErrorCorrectionLevel::toString($ecLevel), if (($firstByte & 0x80) == 0) {
$symbolSequence, // just one byte
$parityData); return $firstByte & 0x7F;
} }
if (($firstByte & 0xC0) == 0x80) {
// two bytes
$secondByte = $bits->readBits(8);
private static function parseECIValue($bits) return (($firstByte & 0x3F) << 8) | $secondByte;
{ }
$firstByte = $bits->readBits(8); if (($firstByte & 0xE0) == 0xC0) {
if (($firstByte & 0x80) == 0) { // three bytes
// just one byte $secondThirdBytes = $bits->readBits(16);
return $firstByte & 0x7F;
}
if (($firstByte & 0xC0) == 0x80) {
// two bytes
$secondByte = $bits->readBits(8);
return (($firstByte & 0x3F) << 8) | $secondByte; return (($firstByte & 0x1F) << 16) | $secondThirdBytes;
} }
if (($firstByte & 0xE0) == 0xC0) { throw FormatException::getFormatInstance();
// three bytes }
$secondThirdBytes = $bits->readBits(16);
return (($firstByte & 0x1F) << 16) | $secondThirdBytes; /**
} * See specification GBT 18284-2000
throw FormatException::getFormatInstance(); */
} 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
* See specification GBT 18284-2000 // and decode as GB2312 afterwards
*/ $buffer = fill_array(0, 2 * $count, 0);
private static function decodeHanziSegment($bits, $offset = 0;
&$result, while ($count > 0) {
$count) // Each 13 bits encodes a 2-byte character
{ $twoBytes = $bits->readBits(13);
// Don't crash trying to read more bits than we have available. $assembledTwoBytes = (($twoBytes / 0x060) << 8) | ($twoBytes % 0x060);
if ($count * 13 > $bits->available()) { if ($assembledTwoBytes < 0x003BF) {
throw FormatException::getFormatInstance(); // 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 private static function decodeNumericSegment(
// and decode as GB2312 afterwards $bits,
$buffer = fill_array(0, 2 * $count, 0); &$result,
$offset = 0; $count
while ($count > 0) { )
// Each 13 bits encodes a 2-byte character {
$twoBytes = $bits->readBits(13); // Read three digits at a time
$assembledTwoBytes = (($twoBytes / 0x060) << 8) | ($twoBytes % 0x060); while ($count >= 3) {
if ($assembledTwoBytes < 0x003BF) { // Each 10 bits encodes three digits
// In the 0xA1A1 to 0xAAFE range if ($bits->available() < 10) {
$assembledTwoBytes += 0x0A1A1; throw FormatException::getFormatInstance();
} else { }
// In the 0xB0A1 to 0xFAFE range $threeDigitsBits = $bits->readBits(10);
$assembledTwoBytes += 0x0A6A1; if ($threeDigitsBits >= 1000) {
} throw FormatException::getFormatInstance();
$buffer[$offset] = (($assembledTwoBytes >> 8) & 0xFF);//(byte) }
$buffer[$offset + 1] = ($assembledTwoBytes & 0xFF);//(byte) $result .= (self::toAlphaNumericChar($threeDigitsBits / 100));
$offset += 2; $result .= (self::toAlphaNumericChar(($threeDigitsBits / 10) % 10));
$count--; $result .= (self::toAlphaNumericChar($threeDigitsBits % 10));
} $count -= 3;
$result .= iconv('GB2312', 'UTF-8', implode($buffer)); }
} 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, private static function toAlphaNumericChar($value)
&$result, {
$count) if ($value >= count(self::$ALPHANUMERIC_CHARS)) {
{ throw FormatException::getFormatInstance();
// 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) return self::$ALPHANUMERIC_CHARS[$value];
{ }
if ($value >= count(self::$ALPHANUMERIC_CHARS)) {
throw FormatException::getFormatInstance();
}
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, private static function decodeByteSegment(
&$result, $bits,
$count, &$result,
$fc1InEffect) $count,
{ $currentCharacterSetECI,
// Read two characters at a time &$byteSegments,
$start = strlen($result); $hints
while ($count > 1) { )
if ($bits->available() < 11) { {
throw FormatException::getFormatInstance(); // Don't crash trying to read more bits than we have available.
} if (8 * $count > $bits->available()) {
$nextTwoCharsBits = $bits->readBits(11); throw FormatException::getFormatInstance();
$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, $readBytes = fill_array(0, $count, 0);
&$result, for ($i = 0; $i < $count; $i++) {
$count, $readBytes[$i] = $bits->readBits(8);//(byte)
$currentCharacterSetECI, }
&$byteSegments, $text = implode(array_map('chr', $readBytes));
$hints) $encoding = '';
{ if ($currentCharacterSetECI == null) {
// Don't crash trying to read more bits than we have available. // The spec isn't clear on this mode; see
if (8 * $count > $bits->available()) { // section 6.4.5: t does not say which encoding to assuming
throw FormatException::getFormatInstance(); // 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); $encoding = mb_detect_encoding($text, $hints);
for ($i = 0; $i < $count; $i++) { } else {
$readBytes[$i] = $bits->readBits(8);//(byte) $encoding = $currentCharacterSetECI->name();
} }
$text = implode(array_map('chr', $readBytes)); // $result.= mb_convert_encoding($text ,$encoding);//(new String(readBytes, encoding));
$encoding = ''; $result .= $text;//(new String(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); $byteSegments = array_merge($byteSegments, $readBytes);
} 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); 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, // Each character will require 2 bytes. Read the characters as 2-byte pairs
&$result, // and decode as Shift_JIS afterwards
$count) $buffer = [0, 2 * $count, 0];
{ $offset = 0;
// Don't crash trying to read more bits than we have available. while ($count > 0) {
if ($count * 13 > $bits->available()) { // Each 13 bits encodes a 2-byte character
throw FormatException::getFormatInstance(); $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 $result .= iconv('shift-jis', 'utf-8', implode($buffer));
// 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)); private function DecodedBitStreamParser(): void
} {
}
private function DecodedBitStreamParser()
{
}
} }

View File

@ -18,13 +18,11 @@
namespace Zxing\Qrcode\Decoder; namespace Zxing\Qrcode\Decoder;
use Zxing\ChecksumException; use Zxing\ChecksumException;
use Zxing\DecodeHintType;
use Zxing\FormatException;
use Zxing\Common\BitMatrix; use Zxing\Common\BitMatrix;
use Zxing\Common\DecoderResult;
use Zxing\Common\Reedsolomon\GenericGF; use Zxing\Common\Reedsolomon\GenericGF;
use Zxing\Common\Reedsolomon\ReedSolomonDecoder; use Zxing\Common\Reedsolomon\ReedSolomonDecoder;
use Zxing\Common\Reedsolomon\ReedSolomonException; use Zxing\Common\Reedsolomon\ReedSolomonException;
use Zxing\FormatException;
/** /**
* <p>The main class which implements QR Code decoding -- as opposed to locating and extracting * <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 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() public function decode($variable, $hints = null)
{ {
$this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256); 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) /**
{ * <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
if (is_array($variable)) { * "true" is taken to mean a black module.</p>
return $this->decodeImage($variable, $hints); *
} elseif ($variable instanceof BitMatrix) { * @param array $image booleans representing white/black QR Code modules
return $this->decodeBits($variable, $hints); * @param decoding $hints hints that should be used to influence decoding
} elseif ($variable instanceof BitMatrixParser) { *
return $this->decodeParser($variable, $hints); * @return text and bytes encoded within the QR Code
} * @throws FormatException if the QR Code cannot be decoded
die('decode error Decoder.php'); * @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);
* <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);
}
/** /**
* <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p> * <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 BitMatrix $bits booleans representing white/black QR Code modules
* @param hints decoding hints that should be used to influence decoding * @param decoding $hints hints that should be used to influence decoding
* *
* @return text and bytes encoded within the QR Code * @return text and bytes encoded within the QR Code
* @throws FormatException if the QR Code cannot be decoded * @throws FormatException if the QR Code cannot be decoded
* @throws ChecksumException if error correction fails * @throws ChecksumException if error correction fails
*/ */
public function decodeBits($bits, $hints = null) public function decodeBits($bits, $hints = null)
{ {
// Construct a parser and read version, error-correction level // Construct a parser and read version, error-correction level
$parser = new BitMatrixParser($bits); $parser = new BitMatrixParser($bits);
$fe = null; $fe = null;
$ce = null; $ce = null;
try { try {
return $this->decode($parser, $hints); return $this->decode($parser, $hints);
} catch (FormatException $e) { } catch (FormatException $e) {
$fe = $e; $fe = $e;
} catch (ChecksumException $e) { } catch (ChecksumException $e) {
$ce = $e; $ce = $e;
} }
try { try {
// Revert the bit matrix // Revert the bit matrix
$parser->remask(); $parser->remask();
// Will be attempting a mirrored reading of the version and format info. // Will be attempting a mirrored reading of the version and format info.
$parser->setMirror(true); $parser->setMirror(true);
// Preemptively read the version. // Preemptively read the version.
$parser->readVersion(); $parser->readVersion();
// Preemptively read the format information. // Preemptively read the format information.
$parser->readFormatInformation(); $parser->readFormatInformation();
/* /*
* Since we're here, this means we have successfully detected some kind * Since we're here, this means we have successfully detected some kind
* of version and format information when mirrored. This is a good sign, * 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 * that the QR code may be mirrored, and we should try once more with a
* mirrored content. * mirrored content.
*/ */
// Prepare for a mirrored reading. // Prepare for a mirrored reading.
$parser->mirror(); $parser->mirror();
$result = $this->decode($parser, $hints); $result = $this->decode($parser, $hints);
// Success! Notify the caller that the code was mirrored. // Success! Notify the caller that the code was mirrored.
$result->setOther(new QRCodeDecoderMetaData(true)); $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) { private function decodeParser($parser, $hints = null)
// Throw the exception from the original reading {
if ($fe != null) { $version = $parser->readVersion();
throw $fe; $ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel();
}
if ($ce != null) {
throw $ce;
}
throw $e;
} // Read codewords
} $codewords = $parser->readCodewords();
// Separate into data blocks
$dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel);
private function decodeParser($parser, $hints = null) // Count total number of data bytes
{ $totalBytes = 0;
$version = $parser->readVersion(); foreach ($dataBlocks as $dataBlock) {
$ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel(); $totalBytes += $dataBlock->getNumDataCodewords();
}
$resultBytes = fill_array(0, $totalBytes, 0);
$resultOffset = 0;
// Read codewords // Error-correct and copy data blocks together into a stream of bytes
$codewords = $parser->readCodewords(); foreach ($dataBlocks as $dataBlock) {
// Separate into data blocks $codewordBytes = $dataBlock->getCodewords();
$dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel); $numDataCodewords = $dataBlock->getNumDataCodewords();
$this->correctErrors($codewordBytes, $numDataCodewords);
for ($i = 0; $i < $numDataCodewords; $i++) {
$resultBytes[$resultOffset++] = $codewordBytes[$i];
}
}
// Count total number of data bytes // Decode the contents of that stream of bytes
$totalBytes = 0; return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints);
foreach ($dataBlocks as $dataBlock) { }
$totalBytes += $dataBlock->getNumDataCodewords();
}
$resultBytes = fill_array(0, $totalBytes, 0);
$resultOffset = 0;
// Error-correct and copy data blocks together into a stream of bytes /**
foreach ($dataBlocks as $dataBlock) { * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
$codewordBytes = $dataBlock->getCodewords(); * correct the errors in-place using Reed-Solomon error correction.</p>
$numDataCodewords = $dataBlock->getNumDataCodewords(); *
$this->correctErrors($codewordBytes, $numDataCodewords); * @param data $codewordBytes and error correction codewords
for ($i = 0; $i < $numDataCodewords; $i++) { * @param number $numDataCodewords of codewords that are data bytes
$resultBytes[$resultOffset++] = $codewordBytes[$i]; *
} * @throws ChecksumException if error correction fails
} */
private function correctErrors(&$codewordBytes, $numDataCodewords)
// Decode the contents of that stream of bytes {
return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints); $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++) {
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to $codewordsInts[$i] = $codewordBytes[$i] & 0xFF;
* correct the errors in-place using Reed-Solomon error correction.</p> }
* $numECCodewords = (is_countable($codewordBytes) ? count($codewordBytes) : 0) - $numDataCodewords;
* @param codewordBytes data and error correction codewords try {
* @param numDataCodewords number of codewords that are data bytes $this->rsDecoder->decode($codewordsInts, $numECCodewords);
* } catch (ReedSolomonException) {
* @throws ChecksumException if error correction fails throw ChecksumException::getChecksumInstance();
*/ }
private function correctErrors(&$codewordBytes, $numDataCodewords) // Copy back into array of bytes -- only need to worry about the bytes that were data
{ // We don't care about errors in the error-correction codewords
$numCodewords = count($codewordBytes); for ($i = 0; $i < $numDataCodewords; $i++) {
// First read into an array of ints $codewordBytes[$i] = $codewordsInts[$i];
$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];
}
}
} }

View File

@ -25,67 +25,66 @@ namespace Zxing\Qrcode\Decoder;
*/ */
class ErrorCorrectionLevel class ErrorCorrectionLevel
{ {
private static $FOR_BITS; /**
private $bits; * @var \Zxing\Qrcode\Decoder\ErrorCorrectionLevel[]|null
private $ordinal; */
private static ?array $FOR_BITS = null;
public function __construct($bits, $ordinal = 0) public function __construct(private $bits, private $ordinal = 0)
{ {
$this->bits = $bits; }
$this->ordinal = $ordinal;
}
public static function Init() public static function Init(): void
{ {
self::$FOR_BITS = [ self::$FOR_BITS = [
new ErrorCorrectionLevel(0x00, 1), //M new ErrorCorrectionLevel(0x00, 1), //M
new ErrorCorrectionLevel(0x01, 0), //L new ErrorCorrectionLevel(0x01, 0), //L
new ErrorCorrectionLevel(0x02, 3), //H new ErrorCorrectionLevel(0x02, 3), //H
new ErrorCorrectionLevel(0x03, 2), //Q new ErrorCorrectionLevel(0x03, 2), //Q
]; ];
} }
/** L = ~7% correction */ /** L = ~7% correction */
// self::$L = new ErrorCorrectionLevel(0x01); // self::$L = new ErrorCorrectionLevel(0x01);
/** M = ~15% correction */ /** M = ~15% correction */
//self::$M = new ErrorCorrectionLevel(0x00); //self::$M = new ErrorCorrectionLevel(0x00);
/** Q = ~25% correction */ /** Q = ~25% correction */
//self::$Q = new ErrorCorrectionLevel(0x03); //self::$Q = new ErrorCorrectionLevel(0x03);
/** H = ~30% correction */ /** H = ~30% correction */
//self::$H = new ErrorCorrectionLevel(0x02); //self::$H = new ErrorCorrectionLevel(0x02);
/** /**
* @param bits int containing the two bits encoding a QR Code's error correction level * @param int $bits containing the two bits encoding a QR Code's error correction level
* *
* @return ErrorCorrectionLevel representing the encoded error correction level * @return ErrorCorrectionLevel representing the encoded error correction level
*/ */
public static function forBits($bits) public static function forBits($bits)
{ {
if ($bits < 0 || $bits >= count(self::$FOR_BITS)) { if ($bits < 0 || $bits >= (is_countable(self::$FOR_BITS) ? count(self::$FOR_BITS) : 0)) {
throw new \InvalidArgumentException(); throw new \InvalidArgumentException();
} }
$level = self::$FOR_BITS[$bits]; $level = self::$FOR_BITS[$bits];
// $lev = self::$$bit; // $lev = self::$$bit;
return $level; return $level;
} }
public function getBits() public function getBits()
{ {
return $this->bits; return $this->bits;
} }
public function toString() public function toString()
{ {
return $this->bits; return $this->bits;
} }
public function getOrdinal() public function getOrdinal()
{ {
return $this->ordinal; return $this->ordinal;
} }
} }
ErrorCorrectionLevel::Init(); ErrorCorrectionLevel::Init();

View File

@ -27,164 +27,167 @@ namespace Zxing\Qrcode\Decoder;
*/ */
final class FormatInformation final class FormatInformation
{ {
public static $FORMAT_INFO_MASK_QR; public static $FORMAT_INFO_MASK_QR;
/** /**
* See ISO 18004:2006, Annex C, Table C.1 * See ISO 18004:2006, Annex C, Table C.1
*/ */
public static $FORMAT_INFO_DECODE_LOOKUP; public static $FORMAT_INFO_DECODE_LOOKUP;
/** /**
* Offset i holds the number of 1 bits in the binary representation of i * Offset i holds the number of 1 bits in the binary representation of i
*/ * @var int[]|null
private static $BITS_SET_IN_HALF_BYTE; */
private static ?array $BITS_SET_IN_HALF_BYTE = null;
private $errorCorrectionLevel; private readonly \Zxing\Qrcode\Decoder\ErrorCorrectionLevel $errorCorrectionLevel;
private $dataMask; private readonly int $dataMask;
private function __construct($formatInfo) private function __construct($formatInfo)
{ {
// Bits 3,4 // Bits 3,4
$this->errorCorrectionLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x03); $this->errorCorrectionLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x03);
// Bottom 3 bits // Bottom 3 bits
$this->dataMask = ($formatInfo & 0x07);//(byte) $this->dataMask = ($formatInfo & 0x07);//(byte)
} }
public static function Init() public static function Init(): void
{ {
self::$FORMAT_INFO_MASK_QR = 0x5412; 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::$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 = [ self::$FORMAT_INFO_DECODE_LOOKUP = [
[0x5412, 0x00], [0x5412, 0x00],
[0x5125, 0x01], [0x5125, 0x01],
[0x5E7C, 0x02], [0x5E7C, 0x02],
[0x5B4B, 0x03], [0x5B4B, 0x03],
[0x45F9, 0x04], [0x45F9, 0x04],
[0x40CE, 0x05], [0x40CE, 0x05],
[0x4F97, 0x06], [0x4F97, 0x06],
[0x4AA0, 0x07], [0x4AA0, 0x07],
[0x77C4, 0x08], [0x77C4, 0x08],
[0x72F3, 0x09], [0x72F3, 0x09],
[0x7DAA, 0x0A], [0x7DAA, 0x0A],
[0x789D, 0x0B], [0x789D, 0x0B],
[0x662F, 0x0C], [0x662F, 0x0C],
[0x6318, 0x0D], [0x6318, 0x0D],
[0x6C41, 0x0E], [0x6C41, 0x0E],
[0x6976, 0x0F], [0x6976, 0x0F],
[0x1689, 0x10], [0x1689, 0x10],
[0x13BE, 0x11], [0x13BE, 0x11],
[0x1CE7, 0x12], [0x1CE7, 0x12],
[0x19D0, 0x13], [0x19D0, 0x13],
[0x0762, 0x14], [0x0762, 0x14],
[0x0255, 0x15], [0x0255, 0x15],
[0x0D0C, 0x16], [0x0D0C, 0x16],
[0x083B, 0x17], [0x083B, 0x17],
[0x355F, 0x18], [0x355F, 0x18],
[0x3068, 0x19], [0x3068, 0x19],
[0x3F31, 0x1A], [0x3F31, 0x1A],
[0x3A06, 0x1B], [0x3A06, 0x1B],
[0x24B4, 0x1C], [0x24B4, 0x1C],
[0x2183, 0x1D], [0x2183, 0x1D],
[0x2EDA, 0x1E], [0x2EDA, 0x1E],
[0x2BED, 0x1F], [0x2BED, 0x1F],
]; ];
} }
/** /**
* @param maskedFormatInfo1 ; format info indicator, with mask still applied * @param $maskedFormatInfo1 ; format info indicator, with mask still applied
* @param maskedFormatInfo2 ; second copy of same info; both are checked at the same time * @param $maskedFormatInfo2 ; second copy of same info; both are checked at the same time
* to establish best match * to establish best match
* *
* @return information about the format it specifies, or {@code null} * @return information about the format it specifies, or {@code null}
* if doesn't seem to match any known pattern * if doesn't seem to match any known pattern
*/ */
public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
{ {
$formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2); $formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
if ($formatInfo != null) { if ($formatInfo != null) {
return $formatInfo; return $formatInfo;
} }
// Should return null, but, some QR codes apparently // Should return null, but, some QR codes apparently
// do not mask this info. Try again by actually masking the pattern // do not mask this info. Try again by actually masking the pattern
// first // first
return self::doDecodeFormatInformation($maskedFormatInfo1 ^ self::$FORMAT_INFO_MASK_QR, return self::doDecodeFormatInformation(
$maskedFormatInfo2 ^ self::$FORMAT_INFO_MASK_QR); $maskedFormatInfo1 ^ self::$FORMAT_INFO_MASK_QR,
} $maskedFormatInfo2 ^ self::$FORMAT_INFO_MASK_QR
);
}
private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
{ {
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
$bestDifference = PHP_INT_MAX; $bestDifference = PHP_INT_MAX;
$bestFormatInfo = 0; $bestFormatInfo = 0;
foreach (self::$FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) { foreach (self::$FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) {
$targetInfo = $decodeInfo[0]; $targetInfo = $decodeInfo[0];
if ($targetInfo == $maskedFormatInfo1 || $targetInfo == $maskedFormatInfo2) { if ($targetInfo == $maskedFormatInfo1 || $targetInfo == $maskedFormatInfo2) {
// Found an exact match // Found an exact match
return new FormatInformation($decodeInfo[1]); return new FormatInformation($decodeInfo[1]);
} }
$bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo); $bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);
if ($bitsDifference < $bestDifference) { if ($bitsDifference < $bestDifference) {
$bestFormatInfo = $decodeInfo[1]; $bestFormatInfo = $decodeInfo[1];
$bestDifference = $bitsDifference; $bestDifference = $bitsDifference;
} }
if ($maskedFormatInfo1 != $maskedFormatInfo2) { if ($maskedFormatInfo1 != $maskedFormatInfo2) {
// also try the other option // also try the other option
$bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo); $bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);
if ($bitsDifference < $bestDifference) { if ($bitsDifference < $bestDifference) {
$bestFormatInfo = $decodeInfo[1]; $bestFormatInfo = $decodeInfo[1];
$bestDifference = $bitsDifference; $bestDifference = $bitsDifference;
} }
} }
} }
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
// differing means we found a match // differing means we found a match
if ($bestDifference <= 3) { if ($bestDifference <= 3) {
return new FormatInformation($bestFormatInfo); return new FormatInformation($bestFormatInfo);
} }
return null; return null;
} }
public static function numBitsDiffering($a, $b) public static function numBitsDiffering($a, $b)
{ {
$a ^= $b; // a now has a 1 bit exactly where its bit differs with b's $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: // Count bits set quickly with a series of lookups:
return self::$BITS_SET_IN_HALF_BYTE[$a & 0x0F] + 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[(int)(uRShift($a, 4) & 0x0F)] +
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 8) & 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, 12) & 0x0F)] +
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 16) & 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, 20) & 0x0F)] +
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 24) & 0x0F)] + self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 24) & 0x0F)] +
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 28) & 0x0F)]; self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 28) & 0x0F)];
} }
public function getErrorCorrectionLevel() public function getErrorCorrectionLevel()
{ {
return $this->errorCorrectionLevel; return $this->errorCorrectionLevel;
} }
public function getDataMask() public function getDataMask()
{ {
return $this->dataMask; return $this->dataMask;
} }
//@Override //@Override
public function hashCode() public function hashCode()
{ {
return ($this->errorCorrectionLevel->ordinal() << 3) | (int)($this->dataMask); return ($this->errorCorrectionLevel->ordinal() << 3) | (int)($this->dataMask);
} }
//@Override //@Override
public function equals($o) public function equals($o)
{ {
if (!($o instanceof FormatInformation)) { if (!($o instanceof FormatInformation)) {
return false; return false;
} }
$other = $o; $other = $o;
return $this->errorCorrectionLevel == $other->errorCorrectionLevel && return $this->errorCorrectionLevel == $other->errorCorrectionLevel &&
$this->dataMask == $other->dataMask; $this->dataMask == $other->dataMask;
} }
} }
FormatInformation::Init(); FormatInformation::Init();

View File

@ -25,104 +25,84 @@ namespace Zxing\Qrcode\Decoder;
*/ */
class Mode class Mode
{ {
public static $TERMINATOR; public static $TERMINATOR;
public static $NUMERIC; public static $NUMERIC;
public static $ALPHANUMERIC; public static $ALPHANUMERIC;
public static $STRUCTURED_APPEND; public static $STRUCTURED_APPEND;
public static $BYTE; public static $BYTE;
public static $ECI; public static $ECI;
public static $KANJI; public static $KANJI;
public static $FNC1_FIRST_POSITION; public static $FNC1_FIRST_POSITION;
public static $FNC1_SECOND_POSITION; public static $FNC1_SECOND_POSITION;
public static $HANZI; public static $HANZI;
private $characterCountBitsForVersions; public function __construct(private $characterCountBitsForVersions, private $bits)
private $bits; {
}
public function __construct($characterCountBitsForVersions, $bits) public static function Init(): void
{ {
$this->characterCountBitsForVersions = $characterCountBitsForVersions; self::$TERMINATOR = new Mode([0, 0, 0], 0x00); // Not really a mode...
$this->bits = $bits; 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... return $this->characterCountBitsForVersions[$offset];
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;
}
public function getBits()
{
return $this->bits;
}
} }
Mode::Init(); Mode::Init();

View File

@ -4,20 +4,16 @@ namespace Zxing\Qrcode\Decoder;
class QRCodeDecoderMetaData class QRCodeDecoderMetaData
{ {
/** @var bool */ /**
private $mirrored; * QRCodeDecoderMetaData constructor.
* @param bool $mirrored
*/
public function __construct(private $mirrored)
{
}
/** public function isMirrored()
* QRCodeDecoderMetaData constructor. {
* @param bool $mirrored return $this->mirrored;
*/ }
public function __construct($mirrored)
{
$this->mirrored = $mirrored;
}
public function isMirrored()
{
return $this->mirrored;
}
} }

View File

@ -27,39 +27,36 @@ use Zxing\ResultPoint;
*/ */
final class AlignmentPattern extends 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) /**
{ * <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated
parent::__construct($posX, $posY); * position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
$this->estimatedModuleSize = $estimatedModuleSize; */
} 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;
* <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 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;
/** return new AlignmentPattern($combinedX, $combinedY, $combinedModuleSize);
* 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);
}
} }

View File

@ -18,8 +18,6 @@
namespace Zxing\Qrcode\Detector; namespace Zxing\Qrcode\Detector;
use Zxing\NotFoundException; 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 * <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 final class AlignmentPatternFinder
{ {
private $image; private array $possibleCenters = [];
private $possibleCenters; private array $crossCheckStateCount = [];
private $startX;
private $startY;
private $width;
private $height;
private $moduleSize;
private $crossCheckStateCount;
private $resultPointCallback;
/** /**
* <p>Creates a finder that will look in a portion of the whole image.</p> * <p>Creates a finder that will look in a portion of the whole image.</p>
* *
* @param image image to search * @param \Imagick image $image to search
* @param startX left column from which to start searching * @param int left $startX column from which to start searching
* @param startY top row from which to start searching * @param int top $startY row from which to start searching
* @param width width of region to search * @param float width $width of region to search
* @param height height of region to search * @param float height $height of region to search
* @param moduleSize estimated module size so far * @param float estimated $moduleSize module size so far
*/ */
public function __construct($image, public function __construct(private $image, private $startX, private $startY, private $width, private $height, private $moduleSize, private $resultPointCallback)
$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>This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since * <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> * it's pretty performance-critical and so is written to be fast foremost.</p>
* *
* @return {@link AlignmentPattern} if found * @return {@link AlignmentPattern} if found
* @throws NotFoundException if not found * @throws NotFoundException if not found
*/ */
public function find() public function find()
{ {
$startX = $this->startX; $startX = $this->startX;
$height = $this->height; $height = $this->height;
$maxJ = $startX + $this->width; $maxJ = $startX + $this->width;
$middleI = $this->startY + ($height / 2); $middleI = $this->startY + ($height / 2);
// We are looking for black/white/black modules in 1:1:1 ratio; // 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 // this tracks the number of black/white/black modules seen so far
$stateCount = []; $stateCount = [];
for ($iGen = 0; $iGen < $height; $iGen++) { for ($iGen = 0; $iGen < $height; $iGen++) {
// Search from middle outwards // Search from middle outwards
$i = $middleI + (($iGen & 0x01) == 0 ? ($iGen + 1) / 2 : -(($iGen + 1) / 2)); $i = $middleI + (($iGen & 0x01) == 0 ? ($iGen + 1) / 2 : -(($iGen + 1) / 2));
$i = (int)($i); $i = (int)($i);
$stateCount[0] = 0; $stateCount[0] = 0;
$stateCount[1] = 0; $stateCount[1] = 0;
$stateCount[2] = 0; $stateCount[2] = 0;
$j = $startX; $j = $startX;
// Burn off leading white pixels before anything else; if we start in the middle of // 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 // 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 // white run continued to the left of the start point
while ($j < $maxJ && !$this->image->get($j, $i)) { while ($j < $maxJ && !$this->image->get($j, $i)) {
$j++; $j++;
} }
$currentState = 0; $currentState = 0;
while ($j < $maxJ) { while ($j < $maxJ) {
if ($this->image->get($j, $i)) { if ($this->image->get($j, $i)) {
// Black pixel // Black pixel
if ($currentState == 1) { // Counting black pixels if ($currentState == 1) { // Counting black pixels
$stateCount[$currentState]++; $stateCount[$currentState]++;
} else { // Counting white pixels } else { // Counting white pixels
if ($currentState == 2) { // A winner? if ($currentState == 2) { // A winner?
if ($this->foundPatternCross($stateCount)) { // Yes if ($this->foundPatternCross($stateCount)) { // Yes
$confirmed = $this->handlePossibleCenter($stateCount, $i, $j); $confirmed = $this->handlePossibleCenter($stateCount, $i, $j);
if ($confirmed != null) { if ($confirmed != null) {
return $confirmed; return $confirmed;
} }
} }
$stateCount[0] = $stateCount[2]; $stateCount[0] = $stateCount[2];
$stateCount[1] = 1; $stateCount[1] = 1;
$stateCount[2] = 0; $stateCount[2] = 0;
$currentState = 1; $currentState = 1;
} else { } else {
$stateCount[++$currentState]++; $stateCount[++$currentState]++;
} }
} }
} else { // White pixel } else { // White pixel
if ($currentState == 1) { // Counting black pixels if ($currentState == 1) { // Counting black pixels
$currentState++; $currentState++;
} }
$stateCount[$currentState]++; $stateCount[$currentState]++;
} }
$j++; $j++;
} }
if ($this->foundPatternCross($stateCount)) { if ($this->foundPatternCross($stateCount)) {
$confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ); $confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ);
if ($confirmed != null) { if ($confirmed != null) {
return $confirmed; 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 throw NotFoundException::getNotFoundInstance();
// any guess at all, return it. }
if (count($this->possibleCenters)) {
return $this->possibleCenters[0];
}
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;
}
}
/** return true;
* @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; /**
} * <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);
}
}
/** return null;
* <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; /**
} * 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, * <p>After a horizontal scan finds a potential alignment pattern, this method
* figures the location of the center of this black/white/black run. * "cross-checks" by scanning down vertically through the center of the possible
*/ * alignment pattern to see if the same proportion is detected.</p>
private static function centerFromEnd($stateCount, $end) *
{ * @param int row $startI where an alignment pattern was detected
return (float)($end - $stateCount[2]) - $stateCount[1] / 2.0; * @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;
/** $maxI = $image->getHeight();
* <p>After a horizontal scan finds a potential alignment pattern, this method $stateCount = $this->crossCheckStateCount;
* "cross-checks" by scanning down vertically through the center of the possible $stateCount[0] = 0;
* alignment pattern to see if the same proportion is detected.</p> $stateCount[1] = 0;
* $stateCount[2] = 0;
* @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(); // Start counting up from center
$stateCount = $this->crossCheckStateCount; $i = $startI;
$stateCount[0] = 0; while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
$stateCount[1] = 0; $stateCount[1]++;
$stateCount[2] = 0; $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 // Now also count down from center
$i = $startI; $i = $startI + 1;
while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) { while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
$stateCount[1]++; $stateCount[1]++;
$i--; $i++;
} }
// If already too many modules in this state or ran off the edge: if ($i == $maxI || $stateCount[1] > $maxCount) {
if ($i < 0 || $stateCount[1] > $maxCount) { return NAN;
return NAN; }
} while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[2] <= $maxCount) {
while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[0] <= $maxCount) { $stateCount[2]++;
$stateCount[0]++; $i++;
$i--; }
} if ($stateCount[2] > $maxCount) {
if ($stateCount[0] > $maxCount) { return NAN;
return NAN; }
}
// Now also count down from center $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2];
$i = $startI + 1; if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) {
while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) { return NAN;
$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]; return $this->foundPatternCross($stateCount) ? self::centerFromEnd($stateCount, $i) : NAN;
if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) { }
return NAN;
}
return $this->foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $i) : NAN;
}
} }

View File

@ -17,18 +17,16 @@
namespace Zxing\Qrcode\Detector; namespace Zxing\Qrcode\Detector;
use Zxing\DecodeHintType; use Zxing\Common\Detector\MathUtils;
use Zxing\FormatException;
use Zxing\NotFoundException;
use Zxing\ResultPoint;
use Zxing\ResultPointCallback;
use Zxing\Common\BitMatrix;
use Zxing\Common\DetectorResult; use Zxing\Common\DetectorResult;
use Zxing\Common\GridSampler; use Zxing\Common\GridSampler;
use Zxing\Common\PerspectiveTransform; 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\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 * <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 class Detector
{ {
private $resultPointCallback;
private $image; public function __construct(private $image)
private $resultPointCallback; {
}
public function __construct($image) /**
{ * <p>Detects a QR Code in an image.</p>
$this->image = $image; *
} * @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,?>*/
/** $resultPointCallback = $hints == null ? null :
* <p>Detects a QR Code in an image.</p> $hints->get('NEED_RESULT_POINT_CALLBACK');
* /* resultPointCallback = hints == null ? null :
* @param hints optional hints to detector (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);*/
* $finder = new FinderPatternFinder($this->image, $resultPointCallback);
* @return {@link DetectorResult} encapsulating results of detecting a QR Code $info = $finder->find($hints);
* @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 : return $this->processFinderPatternInfo($info);
$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); 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(); $alignmentPattern = null;
$topRight = $info->getTopRight(); // Anything above version 1 has an alignment pattern
$bottomLeft = $info->getBottomLeft(); if ((is_countable($provisionalVersion->getAlignmentPatternCenters()) ? count($provisionalVersion->getAlignmentPatternCenters()) : 0) > 0) {
$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) {
// Guess where a "bottom right" finder pattern would have been // Guess where a "bottom right" finder pattern would have been
$bottomRightX = $topRight->getX() - $topLeft->getX() + $bottomLeft->getX(); $bottomRightX = $topRight->getX() - $topLeft->getX() + $bottomLeft->getX();
$bottomRightY = $topRight->getY() - $topLeft->getY() + $bottomLeft->getY(); $bottomRightY = $topRight->getY() - $topLeft->getY() + $bottomLeft->getY();
// Estimate that alignment pattern is closer by 3 modules // Estimate that alignment pattern is closer by 3 modules
// from "bottom right" to known top left location // from "bottom right" to known top left location
$correctionToTopLeft = 1.0 - 3.0 / (float)$modulesBetweenFPCenters; $correctionToTopLeft = 1.0 - 3.0 / (float)$modulesBetweenFPCenters;
$estAlignmentX = (int)($topLeft->getX() + $correctionToTopLeft * ($bottomRightX - $topLeft->getX())); $estAlignmentX = (int)($topLeft->getX() + $correctionToTopLeft * ($bottomRightX - $topLeft->getX()));
$estAlignmentY = (int)($topLeft->getY() + $correctionToTopLeft * ($bottomRightY - $topLeft->getY())); $estAlignmentY = (int)($topLeft->getY() + $correctionToTopLeft * ($bottomRightY - $topLeft->getY()));
// Kind of arbitrary -- expand search radius before giving up // Kind of arbitrary -- expand search radius before giving up
for ($i = 4; $i <= 16; $i <<= 1) {//?????????? for ($i = 4; $i <= 16; $i <<= 1) {//??????????
try { try {
$alignmentPattern = $this->findAlignmentInRegion( $alignmentPattern = $this->findAlignmentInRegion(
$moduleSize, $moduleSize,
$estAlignmentX, $estAlignmentX,
$estAlignmentY, $estAlignmentY,
(float)$i (float)$i
); );
break; break;
} catch (NotFoundException $re) { } catch (NotFoundException) {
// try next round // try next round
} }
} }
// If we didn't find alignment pattern... well try anyway without it // 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 = []; $points = [];
if ($alignmentPattern == null) { if ($alignmentPattern == null) {
$points = [$bottomLeft, $topLeft, $topRight]; $points = [$bottomLeft, $topLeft, $topRight];
} else { } else {
// die('$points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};'); // die('$points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};');
$points = [$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> * <p>Detects a QR Code in an image.</p>
* *
* @return {@link DetectorResult} encapsulating results of detecting a QR Code * @return {@link DetectorResult} encapsulating results of detecting a QR Code
* @throws NotFoundException if QR Code cannot be found * @throws NotFoundException if QR Code cannot be found
* @throws FormatException if a QR Code cannot be decoded * @throws FormatException if a QR Code cannot be decoded
*/ */
/** /**
* <p>Computes an average estimated module size based on estimated derived from the positions * <p>Computes an average estimated module size based on estimated derived from the positions
* of the three finder patterns.</p> * of the three finder patterns.</p>
* *
* @param topLeft detected top-left finder pattern center * @param detected $topLeft top-left finder pattern center
* @param topRight detected top-right finder pattern center * @param detected $topRight top-right finder pattern center
* @param bottomLeft detected bottom-left finder pattern center * @param detected $bottomLeft bottom-left finder pattern center
* *
* @return estimated module size * @return estimated module size
*/ */
protected final function calculateModuleSize($topLeft, $topRight, $bottomLeft) final protected function calculateModuleSize($topLeft, $topRight, $bottomLeft)
{ {
// Take the average // Take the average
return ($this->calculateModuleSizeOneWay($topLeft, $topRight) + return ($this->calculateModuleSizeOneWay($topLeft, $topRight) +
$this->calculateModuleSizeOneWay($topLeft, $bottomLeft)) / 2.0; $this->calculateModuleSizeOneWay($topLeft, $bottomLeft)) / 2.0;
} }
/** /**
* <p>Estimates module size based on two finder patterns -- it uses * <p>Estimates module size based on two finder patterns -- it uses
* {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the
* width of each, measuring along the axis between their centers.</p> * width of each, measuring along the axis between their centers.</p>
*/ */
private function calculateModuleSizeOneWay($pattern, $otherPattern) private function calculateModuleSizeOneWay($pattern, $otherPattern)
{ {
$moduleSizeEst1 = $this->sizeOfBlackWhiteBlackRunBothWays($pattern->getX(), $moduleSizeEst1 = $this->sizeOfBlackWhiteBlackRunBothWays(
(int)$pattern->getY(), $pattern->getX(),
(int)$otherPattern->getX(), (int)$pattern->getY(),
(int)$otherPattern->getY()); (int)$otherPattern->getX(),
$moduleSizeEst2 = $this->sizeOfBlackWhiteBlackRunBothWays((int)$otherPattern->getX(), (int)$otherPattern->getY()
(int)$otherPattern->getY(), );
(int)$pattern->getX(), $moduleSizeEst2 = $this->sizeOfBlackWhiteBlackRunBothWays(
(int)$pattern->getY()); (int)$otherPattern->getX(),
if (is_nan($moduleSizeEst1)) { (int)$otherPattern->getY(),
return $moduleSizeEst2 / 7.0; (int)$pattern->getX(),
} (int)$pattern->getY()
if (is_nan($moduleSizeEst2)) { );
return $moduleSizeEst1 / 7.0; if (is_nan($moduleSizeEst1)) {
} return $moduleSizeEst2 / 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. if (is_nan($moduleSizeEst2)) {
return ($moduleSizeEst1 + $moduleSizeEst2) / 14.0; 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 * 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 * 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> * of another po$(another finder pattern center), and in the opposite direction too.</p>
*/ */
private function sizeOfBlackWhiteBlackRunBothWays($fromX, $fromY, $toX, $toY) 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;
$scale = 1.0; if ($otherToY < 0) {
$otherToX = $fromX - ($toX - $fromX); $scale = (float)$fromY / (float)($fromY - $otherToY);
if ($otherToX < 0) { $otherToY = 0;
$scale = (float)$fromX / (float)($fromX - $otherToX); } elseif ($otherToY >= $this->image->getHeight()) {
$otherToX = 0; $scale = (float)($this->image->getHeight() - 1 - $fromY) / (float)($otherToY - $fromY);
} else if ($otherToX >= $this->image->getWidth()) { $otherToY = $this->image->getHeight() - 1;
$scale = (float)($this->image->getWidth() - 1 - $fromX) / (float)($otherToX - $fromX); }
$otherToX = $this->image->getWidth() - 1; $otherToX = (int)($fromX + ($otherToX - $fromX) * $scale);
}
$otherToY = (int)($fromY - ($toY - $fromY) * $scale);
$scale = 1.0; $result += $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $otherToX, $otherToY);
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); // 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;
}
/** $dx = abs($toX - $fromX);
* <p>This method traces a line from a po$in the image, in the direction towards another point. $dy = abs($toY - $fromY);
* It begins in a black region, and keeps going until it finds white, then black, then white again. $error = -$dx / 2;
* It reports the distance from the start to this point.</p> $xstep = $fromX < $toX ? 1 : -1;
* $ystep = $fromY < $toY ? 1 : -1;
* <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); // In black pixels, looking for white, first or second time.
$dy = abs($toY - $fromY); $state = 0;
$error = -$dx / 2; // Loop up until x == toX, but not beyond
$xstep = $fromX < $toX ? 1 : -1; $xLimit = $toX + $xstep;
$ystep = $fromY < $toY ? 1 : -1; 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. // Does current pixel mean we have moved white to black or vice versa?
$state = 0; // Scanning black in state 0,2 and white in state 1, so if we find the wrong
// Loop up until x == toX, but not beyond // color, advance to next state or end if we are in state 2 already
$xLimit = $toX + $xstep; if (($state == 1) == $this->image->get($realX, $realY)) {
for ($x = $fromX, $y = $fromY; $x != $xLimit; $x += $xstep) { if ($state == 2) {
$realX = $steep ? $y : $x; return MathUtils::distance($x, $y, $fromX, $fromY);
$realY = $steep ? $x : $y; }
$state++;
}
// Does current pixel mean we have moved white to black or vice versa? $error += $dy;
// Scanning black in state 0,2 and white in state 1, so if we find the wrong if ($error > 0) {
// color, advance to next state or end if we are in state 2 already if ($y == $toY) {
if (($state == 1) == $this->image->get($realX, $realY)) { break;
if ($state == 2) { }
return MathUtils::distance($x, $y, $fromX, $fromY); $y += $ystep;
} $error -= $dx;
$state++; }
} }
// 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; // else we didn't find even black-white-black; no estimate is really possible
if ($error > 0) { return NAN;
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; * <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(
* <p>Computes the dimension (number of modules on a size) of the QR Code based on the position $topLeft,
* of the finder patterns and estimated module size.</p> $topRight,
*/ $bottomLeft,
private static function computeDimension($topLeft, $moduleSize
$topRight, )
$bottomLeft, {
$moduleSize) $tltrCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $topRight) / $moduleSize);
{ $tlblCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $bottomLeft) / $moduleSize);
$tltrCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $topRight) / $moduleSize); $dimension = (($tltrCentersDimension + $tlblCentersDimension) / 2) + 7;
$tlblCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $bottomLeft) / $moduleSize); switch ($dimension & 0x03) { // mod 4
$dimension = (($tltrCentersDimension + $tlblCentersDimension) / 2) + 7; case 0:
switch ($dimension & 0x03) { // mod 4 $dimension++;
case 0: break;
$dimension++;
break;
// 1? do nothing // 1? do nothing
case 2: case 2:
$dimension--; $dimension--;
break; break;
case 3: case 3:
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
return $dimension; return $dimension;
} }
/** /**
* <p>Attempts to locate an alignment pattern in a limited region of the image, which is * <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> * guessed to contain it. This method uses {@link AlignmentPattern}.</p>
* *
* @param overallEstModuleSize estimated module size so far * @param estimated $overallEstModuleSize module size so far
* @param estAlignmentX x coordinate of center of area probably containing alignment pattern * @param x $estAlignmentX coordinate of center of area probably containing alignment pattern
* @param estAlignmentY y coordinate of above * @param y $estAlignmentY coordinate of above
* @param allowanceFactor number of pixels in all directions to search from the center * @param number $allowanceFactor of pixels in all directions to search from the center
* *
* @return {@link AlignmentPattern} if found, or null otherwise * @return {@link AlignmentPattern} if found, or null otherwise
* @throws NotFoundException if an unexpected error occurs during detection * @throws NotFoundException if an unexpected error occurs during detection
*/ */
protected final function findAlignmentInRegion($overallEstModuleSize, final protected function findAlignmentInRegion(
$estAlignmentX, $overallEstModuleSize,
$estAlignmentY, $estAlignmentX,
$allowanceFactor) $estAlignmentY,
{ $allowanceFactor
// Look for an alignment pattern (3 modules in size) around where it )
// should be {
$allowance = (int)($allowanceFactor * $overallEstModuleSize); // Look for an alignment pattern (3 modules in size) around where it
$alignmentAreaLeftX = max(0, $estAlignmentX - $allowance); // should be
$alignmentAreaRightX = min($this->image->getWidth() - 1, $estAlignmentX + $allowance); $allowance = (int)($allowanceFactor * $overallEstModuleSize);
if ($alignmentAreaRightX - $alignmentAreaLeftX < $overallEstModuleSize * 3) { $alignmentAreaLeftX = max(0, $estAlignmentX - $allowance);
throw NotFoundException::getNotFoundInstance(); $alignmentAreaRightX = min($this->image->getWidth() - 1, $estAlignmentX + $allowance);
} if ($alignmentAreaRightX - $alignmentAreaLeftX < $overallEstModuleSize * 3) {
throw NotFoundException::getNotFoundInstance();
}
$alignmentAreaTopY = max(0, $estAlignmentY - $allowance); $alignmentAreaTopY = max(0, $estAlignmentY - $allowance);
$alignmentAreaBottomY = min($this->image->getHeight() - 1, $estAlignmentY + $allowance); $alignmentAreaBottomY = min($this->image->getHeight() - 1, $estAlignmentY + $allowance);
if ($alignmentAreaBottomY - $alignmentAreaTopY < $overallEstModuleSize * 3) { if ($alignmentAreaBottomY - $alignmentAreaTopY < $overallEstModuleSize * 3) {
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
$alignmentFinder = $alignmentFinder =
new AlignmentPatternFinder( new AlignmentPatternFinder(
$this->image, $this->image,
$alignmentAreaLeftX, $alignmentAreaLeftX,
$alignmentAreaTopY, $alignmentAreaTopY,
$alignmentAreaRightX - $alignmentAreaLeftX, $alignmentAreaRightX - $alignmentAreaLeftX,
$alignmentAreaBottomY - $alignmentAreaTopY, $alignmentAreaBottomY - $alignmentAreaTopY,
$overallEstModuleSize, $overallEstModuleSize,
$this->resultPointCallback); $this->resultPointCallback
);
return $alignmentFinder->find(); return $alignmentFinder->find();
} }
private static function createTransform($topLeft, private static function createTransform(
$topRight, $topLeft,
$bottomLeft, $topRight,
$alignmentPattern, $bottomLeft,
$dimension) $alignmentPattern,
{ $dimension
$dimMinusThree = (float)$dimension - 3.5; )
$bottomRightX = 0.0; {
$bottomRightY = 0.0; $dimMinusThree = (float)$dimension - 3.5;
$sourceBottomRightX = 0.0; $bottomRightX = 0.0;
$sourceBottomRightY = 0.0; $bottomRightY = 0.0;
if ($alignmentPattern != null) { $sourceBottomRightX = 0.0;
$bottomRightX = $alignmentPattern->getX(); $sourceBottomRightY = 0.0;
$bottomRightY = $alignmentPattern->getY(); if ($alignmentPattern != null) {
$sourceBottomRightX = $dimMinusThree - 3.0; $bottomRightX = $alignmentPattern->getX();
$sourceBottomRightY = $sourceBottomRightX; $bottomRightY = $alignmentPattern->getY();
} else { $sourceBottomRightX = $dimMinusThree - 3.0;
// Don't have an alignment pattern, just make up the bottom-right point $sourceBottomRightY = $sourceBottomRightX;
$bottomRightX = ($topRight->getX() - $topLeft->getX()) + $bottomLeft->getX(); } else {
$bottomRightY = ($topRight->getY() - $topLeft->getY()) + $bottomLeft->getY(); // Don't have an alignment pattern, just make up the bottom-right point
$sourceBottomRightX = $dimMinusThree; $bottomRightX = ($topRight->getX() - $topLeft->getX()) + $bottomLeft->getX();
$sourceBottomRightY = $dimMinusThree; $bottomRightY = ($topRight->getY() - $topLeft->getY()) + $bottomLeft->getY();
} $sourceBottomRightX = $dimMinusThree;
$sourceBottomRightY = $dimMinusThree;
}
return PerspectiveTransform::quadrilateralToQuadrilateral( return PerspectiveTransform::quadrilateralToQuadrilateral(
3.5, 3.5,
3.5, 3.5,
$dimMinusThree, $dimMinusThree,
3.5, 3.5,
$sourceBottomRightX, $sourceBottomRightX,
$sourceBottomRightY, $sourceBottomRightY,
3.5, 3.5,
$dimMinusThree, $dimMinusThree,
$topLeft->getX(), $topLeft->getX(),
$topLeft->getY(), $topLeft->getY(),
$topRight->getX(), $topRight->getX(),
$topRight->getY(), $topRight->getY(),
$bottomRightX, $bottomRightX,
$bottomRightY, $bottomRightY,
$bottomLeft->getX(), $bottomLeft->getX(),
$bottomLeft->getY()); $bottomLeft->getY()
} );
}
private static function sampleGrid($image, $transform, private static function sampleGrid(
$dimension) $image,
{ $transform,
$sampler = GridSampler::getInstance(); $dimension
)
{
$sampler = GridSampler::getInstance();
return $sampler->sampleGrid_($image, $dimension, $dimension, $transform); return $sampler->sampleGrid_($image, $dimension, $dimension, $transform);
} }
protected final function getImage() final protected function getImage()
{ {
return $this->image; return $this->image;
} }
protected final function getResultPointCallback() final protected function getResultPointCallback()
{ {
return $this->resultPointCallback; return $this->resultPointCallback;
} }
} }

View File

@ -28,59 +28,54 @@ use Zxing\ResultPoint;
*/ */
final class FinderPattern extends ResultPoint final class FinderPattern extends ResultPoint
{ {
private $estimatedModuleSize; public function __construct($posX, $posY, private $estimatedModuleSize, private $count = 1)
private $count; {
parent::__construct($posX, $posY);
}
public function __construct($posX, $posY, $estimatedModuleSize, $count = 1) public function getEstimatedModuleSize()
{ {
parent::__construct($posX, $posY); return $this->estimatedModuleSize;
$this->estimatedModuleSize = $estimatedModuleSize; }
$this->count = $count;
}
public function getEstimatedModuleSize() public function getCount()
{ {
return $this->estimatedModuleSize; return $this->count;
} }
public function getCount() /*
{ void incrementCount() {
return $this->count; this.count++;
} }
*/
/* /**
void incrementCount() { * <p>Determines if this finder pattern "about equals" a finder pattern at the stated
this.count++; * 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;
* <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 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;
/** return new FinderPattern($combinedX, $combinedY, $combinedModuleSize, $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);
}
} }

View File

@ -25,29 +25,29 @@ namespace Zxing\Qrcode\Detector;
*/ */
final class FinderPatternInfo final class FinderPatternInfo
{ {
private $bottomLeft; private $bottomLeft;
private $topLeft; private $topLeft;
private $topRight; private $topRight;
public function __construct($patternCenters) public function __construct($patternCenters)
{ {
$this->bottomLeft = $patternCenters[0]; $this->bottomLeft = $patternCenters[0];
$this->topLeft = $patternCenters[1]; $this->topLeft = $patternCenters[1];
$this->topRight = $patternCenters[2]; $this->topRight = $patternCenters[2];
} }
public function getBottomLeft() public function getBottomLeft()
{ {
return $this->bottomLeft; return $this->bottomLeft;
} }
public function getTopLeft() public function getTopLeft()
{ {
return $this->topLeft; return $this->topLeft;
} }
public function getTopRight() public function getTopRight()
{ {
return $this->topRight; return $this->topRight;
} }
} }

View File

@ -19,13 +19,13 @@ namespace Zxing\Qrcode;
use Zxing\BinaryBitmap; use Zxing\BinaryBitmap;
use Zxing\ChecksumException; use Zxing\ChecksumException;
use Zxing\Common\BitMatrix;
use Zxing\FormatException; use Zxing\FormatException;
use Zxing\NotFoundException; use Zxing\NotFoundException;
use Zxing\Reader;
use Zxing\Result;
use Zxing\Common\BitMatrix;
use Zxing\Qrcode\Decoder\Decoder; use Zxing\Qrcode\Decoder\Decoder;
use Zxing\Qrcode\Detector\Detector; use Zxing\Qrcode\Detector\Detector;
use Zxing\Reader;
use Zxing\Result;
/** /**
* This implementation can detect and decode QR Codes in an image. * This implementation can detect and decode QR Codes in an image.
@ -34,189 +34,188 @@ use Zxing\Qrcode\Detector\Detector;
*/ */
class QRCodeReader implements Reader class QRCodeReader implements Reader
{ {
private static $NO_POINTS = []; private static array $NO_POINTS = [];
private $decoder; private readonly \Zxing\Qrcode\Decoder\Decoder $decoder;
public function __construct() public function __construct()
{ {
$this->decoder = new Decoder(); $this->decoder = new Decoder();
} }
/** /**
* @param BinaryBitmap $image * @param null $hints
* @param null $hints *
* * @return Result
* @return Result * @throws \Zxing\FormatException
* @throws \Zxing\FormatException * @throws \Zxing\NotFoundException
* @throws \Zxing\NotFoundException */
*/ public function decode(BinaryBitmap $image, $hints = null)
public function decode(BinaryBitmap $image, $hints = null) {
{ $decoderResult = null;
$decoderResult = null; if ($hints !== null && $hints['PURE_BARCODE']) {
if ($hints !== null && $hints['PURE_BARCODE']) { $bits = self::extractPureBits($image->getBlackMatrix());
$bits = self::extractPureBits($image->getBlackMatrix()); $decoderResult = $this->decoder->decode($bits, $hints);
$decoderResult = $this->decoder->decode($bits, $hints); $points = self::$NO_POINTS;
$points = self::$NO_POINTS; } else {
} else { $detector = new Detector($image->getBlackMatrix());
$detector = new Detector($image->getBlackMatrix()); $detectorResult = $detector->detect($hints);
$detectorResult = $detector->detect($hints);
$decoderResult = $this->decoder->decode($detectorResult->getBits(), $hints); $decoderResult = $this->decoder->decode($detectorResult->getBits(), $hints);
$points = $detectorResult->getPoints(); $points = $detectorResult->getPoints();
} }
$result = new Result($decoderResult->getText(), $decoderResult->getRawBytes(), $points, 'QR_CODE');//BarcodeFormat.QR_CODE $result = new Result($decoderResult->getText(), $decoderResult->getRawBytes(), $points, 'QR_CODE');//BarcodeFormat.QR_CODE
$byteSegments = $decoderResult->getByteSegments(); $byteSegments = $decoderResult->getByteSegments();
if ($byteSegments !== null) { if ($byteSegments !== null) {
$result->putMetadata('BYTE_SEGMENTS', $byteSegments);//ResultMetadataType.BYTE_SEGMENTS $result->putMetadata('BYTE_SEGMENTS', $byteSegments);//ResultMetadataType.BYTE_SEGMENTS
} }
$ecLevel = $decoderResult->getECLevel(); $ecLevel = $decoderResult->getECLevel();
if ($ecLevel !== null) { if ($ecLevel !== null) {
$result->putMetadata('ERROR_CORRECTION_LEVEL', $ecLevel);//ResultMetadataType.ERROR_CORRECTION_LEVEL $result->putMetadata('ERROR_CORRECTION_LEVEL', $ecLevel);//ResultMetadataType.ERROR_CORRECTION_LEVEL
} }
if ($decoderResult->hasStructuredAppend()) { if ($decoderResult->hasStructuredAppend()) {
$result->putMetadata( $result->putMetadata(
'STRUCTURED_APPEND_SEQUENCE',//ResultMetadataType.STRUCTURED_APPEND_SEQUENCE 'STRUCTURED_APPEND_SEQUENCE',//ResultMetadataType.STRUCTURED_APPEND_SEQUENCE
$decoderResult->getStructuredAppendSequenceNumber() $decoderResult->getStructuredAppendSequenceNumber()
); );
$result->putMetadata( $result->putMetadata(
'STRUCTURED_APPEND_PARITY',//ResultMetadataType.STRUCTURED_APPEND_PARITY 'STRUCTURED_APPEND_PARITY',//ResultMetadataType.STRUCTURED_APPEND_PARITY
$decoderResult->getStructuredAppendParity() $decoderResult->getStructuredAppendParity()
); );
} }
return $result; return $result;
} }
/** /**
* Locates and decodes a QR code in an image. * Locates and decodes a QR code in an image.
* *
* @return a String representing the content encoded by the QR code * @return a String representing the content encoded by the QR code
* @throws NotFoundException if a QR code cannot be found * @throws NotFoundException if a QR code cannot be found
* @throws FormatException if a QR code cannot be decoded * @throws FormatException if a QR code cannot be decoded
* @throws ChecksumException if error correction fails * @throws ChecksumException if error correction fails
*/ */
/** /**
* This method detects a code in a "pure" image -- that is, pure monochrome image * 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 * 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 * around it. This is a specialized method that works exceptionally fast in this special
* case. * case.
* *
* @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix) * @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)
*/ */
private static function extractPureBits(BitMatrix $image) private static function extractPureBits(BitMatrix $image)
{ {
$leftTopBlack = $image->getTopLeftOnBit(); $leftTopBlack = $image->getTopLeftOnBit();
$rightBottomBlack = $image->getBottomRightOnBit(); $rightBottomBlack = $image->getBottomRightOnBit();
if ($leftTopBlack === null || $rightBottomBlack == null) { if ($leftTopBlack === null || $rightBottomBlack == null) {
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
$moduleSize = self::moduleSize($leftTopBlack, $image); $moduleSize = self::moduleSize($leftTopBlack, $image);
$top = $leftTopBlack[1]; $top = $leftTopBlack[1];
$bottom = $rightBottomBlack[1]; $bottom = $rightBottomBlack[1];
$left = $leftTopBlack[0]; $left = $leftTopBlack[0];
$right = $rightBottomBlack[0]; $right = $rightBottomBlack[0];
// Sanity check! // Sanity check!
if ($left >= $right || $top >= $bottom) { if ($left >= $right || $top >= $bottom) {
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
if ($bottom - $top != $right - $left) { if ($bottom - $top != $right - $left) {
// Special case, where bottom-right module wasn't black so we found something else in the last row // 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 // Assume it's a square, so use height as the width
$right = $left + ($bottom - $top); $right = $left + ($bottom - $top);
} }
$matrixWidth = round(($right - $left + 1) / $moduleSize); $matrixWidth = round(($right - $left + 1) / $moduleSize);
$matrixHeight = round(($bottom - $top + 1) / $moduleSize); $matrixHeight = round(($bottom - $top + 1) / $moduleSize);
if ($matrixWidth <= 0 || $matrixHeight <= 0) { if ($matrixWidth <= 0 || $matrixHeight <= 0) {
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
if ($matrixHeight != $matrixWidth) { if ($matrixHeight != $matrixWidth) {
// Only possibly decode square regions // Only possibly decode square regions
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
// Push in the "border" by half the module width so that we start // 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 // sampling in the middle of the module. Just in case the image is a
// little off, this will help recover. // little off, this will help recover.
$nudge = (int)($moduleSize / 2.0);// $nudge = (int) ($moduleSize / 2.0f); $nudge = (int)($moduleSize / 2.0);// $nudge = (int) ($moduleSize / 2.0f);
$top += $nudge; $top += $nudge;
$left += $nudge; $left += $nudge;
// But careful that this does not sample off the edge // But careful that this does not sample off the edge
// "right" is the farthest-right valid pixel location -- right+1 is not necessarily // "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 // This is positive by how much the inner x loop below would be too large
$nudgedTooFarRight = $left + (int)(($matrixWidth - 1) * $moduleSize) - $right; $nudgedTooFarRight = $left + (int)(($matrixWidth - 1) * $moduleSize) - $right;
if ($nudgedTooFarRight > 0) { if ($nudgedTooFarRight > 0) {
if ($nudgedTooFarRight > $nudge) { if ($nudgedTooFarRight > $nudge) {
// Neither way fits; abort // Neither way fits; abort
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
$left -= $nudgedTooFarRight; $left -= $nudgedTooFarRight;
} }
// See logic above // See logic above
$nudgedTooFarDown = $top + (int)(($matrixHeight - 1) * $moduleSize) - $bottom; $nudgedTooFarDown = $top + (int)(($matrixHeight - 1) * $moduleSize) - $bottom;
if ($nudgedTooFarDown > 0) { if ($nudgedTooFarDown > 0) {
if ($nudgedTooFarDown > $nudge) { if ($nudgedTooFarDown > $nudge) {
// Neither way fits; abort // Neither way fits; abort
throw NotFoundException::getNotFoundInstance(); throw NotFoundException::getNotFoundInstance();
} }
$top -= $nudgedTooFarDown; $top -= $nudgedTooFarDown;
} }
// Now just read off the bits // Now just read off the bits
$bits = new BitMatrix($matrixWidth, $matrixHeight); $bits = new BitMatrix($matrixWidth, $matrixHeight);
for ($y = 0; $y < $matrixHeight; $y++) { for ($y = 0; $y < $matrixHeight; $y++) {
$iOffset = $top + (int)($y * $moduleSize); $iOffset = $top + (int)($y * $moduleSize);
for ($x = 0; $x < $matrixWidth; $x++) { for ($x = 0; $x < $matrixWidth; $x++) {
if ($image->get($left + (int)($x * $moduleSize), $iOffset)) { if ($image->get($left + (int)($x * $moduleSize), $iOffset)) {
$bits->set($x, $y); $bits->set($x, $y);
} }
} }
} }
return $bits; return $bits;
} }
private static function moduleSize($leftTopBlack, BitMatrix $image) private static function moduleSize($leftTopBlack, BitMatrix $image)
{ {
$height = $image->getHeight(); $height = $image->getHeight();
$width = $image->getWidth(); $width = $image->getWidth();
$x = $leftTopBlack[0]; $x = $leftTopBlack[0];
$y = $leftTopBlack[1]; $y = $leftTopBlack[1];
/*$x = $leftTopBlack[0]; /*$x = $leftTopBlack[0];
$y = $leftTopBlack[1];*/ $y = $leftTopBlack[1];*/
$inBlack = true; $inBlack = true;
$transitions = 0; $transitions = 0;
while ($x < $width && $y < $height) { while ($x < $width && $y < $height) {
if ($inBlack != $image->get($x, $y)) { if ($inBlack != $image->get($x, $y)) {
if (++$transitions == 5) { if (++$transitions == 5) {
break; break;
} }
$inBlack = !$inBlack; $inBlack = !$inBlack;
} }
$x++; $x++;
$y++; $y++;
} }
if ($x == $width || $y == $height) { if ($x == $width || $y == $height) {
throw NotFoundException::getNotFoundInstance(); 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() public function reset(): void
{ {
// do nothing // do nothing
} }
protected final function getDecoder() final protected function getDecoder()
{ {
return $this->decoder; return $this->decoder;
} }
} }

View File

@ -26,284 +26,294 @@ namespace Zxing;
*/ */
final class RGBLuminanceSource extends LuminanceSource final class RGBLuminanceSource extends LuminanceSource
{ {
public $luminances; public $luminances;
private $dataWidth; private $dataWidth;
private $dataHeight; private $dataHeight;
private $left; /**
private $top; * @var mixed|int
private $pixels; */
private $left;
/**
* @var mixed|int
*/
private $top;
/**
* @var mixed|null
*/
private $pixels;
public function __construct( public function __construct(
$pixels, $pixels,
$dataWidth, $dataWidth,
$dataHeight, $dataHeight,
$left = null, $left = null,
$top = null, $top = null,
$width = null, $width = null,
$height = null $height = null
) { ) {
if (!$left && !$top && !$width && !$height) { if (!$left && !$top && !$width && !$height) {
$this->RGBLuminanceSource_($pixels, $dataWidth, $dataHeight); $this->RGBLuminanceSource_($pixels, $dataWidth, $dataHeight);
return; return;
} }
parent::__construct($width, $height); parent::__construct($width, $height);
if ($left + $width > $dataWidth || $top + $height > $dataHeight) { if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
throw new \InvalidArgumentException("Crop rectangle does not fit within image data."); throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
} }
$this->luminances = $pixels; $this->luminances = $pixels;
$this->dataWidth = $dataWidth; $this->dataWidth = $dataWidth;
$this->dataHeight = $dataHeight; $this->dataHeight = $dataHeight;
$this->left = $left; $this->left = $left;
$this->top = $top; $this->top = $top;
} }
public function RGBLuminanceSource_($width, $height, $pixels) public function RGBLuminanceSource_($width, $height, $pixels): void
{ {
parent::__construct($width, $height); parent::__construct($width, $height);
$this->dataWidth = $width; $this->dataWidth = $width;
$this->dataHeight = $height; $this->dataHeight = $height;
$this->left = 0; $this->left = 0;
$this->top = 0; $this->top = 0;
$this->pixels = $pixels; $this->pixels = $pixels;
// In order to measure pure decoding speed, we convert the entire image to a greyscale array // 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. // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
$this->luminances = []; $this->luminances = [];
//$this->luminances = $this->grayScaleToBitmap($this->grayscale()); //$this->luminances = $this->grayScaleToBitmap($this->grayscale());
foreach ($pixels as $key => $pixel) { foreach ($pixels as $key => $pixel) {
$r = $pixel['red']; $r = $pixel['red'];
$g = $pixel['green']; $g = $pixel['green'];
$b = $pixel['blue']; $b = $pixel['blue'];
/* if (($pixel & 0xFF000000) == 0) { /* if (($pixel & 0xFF000000) == 0) {
$pixel = 0xFFFFFFFF; // = white $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] = $this->luminances[$key] =
(306 * (($pixel >> 16) & 0xFF) + (306 * (($pixel >> 16) & 0xFF) +
601 * (($pixel >> 8) & 0xFF) + 601 * (($pixel >> 8) & 0xFF) +
117 * ($pixel & 0xFF) + 117 * ($pixel & 0xFF) +
0x200) >> 10; 0x200) >> 10;
*/ */
//$r = ($pixel >> 16) & 0xff; //$r = ($pixel >> 16) & 0xff;
//$g = ($pixel >> 8) & 0xff; //$g = ($pixel >> 8) & 0xff;
//$b = $pixel & 0xff; //$b = $pixel & 0xff;
if ($r == $g && $g == $b) { 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. // Image is already greyscale, so pick any channel.
$this->luminances[$key] = $r;//(($r + 128) % 256) - 128; $this->luminances[(int)($offset + $x)] = (($r+128) % 256) - 128;
} else { } else {
// Calculate luminance cheaply, favoring green. // Calculate luminance cheaply, favoring green.
$this->luminances[$key] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128; $this->luminances[(int)($offset + $x)] = (((($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 = $this->grayScaleToBitmap($this->luminances); // $this->luminances = $this->grayScaleToBitmap($this->luminances);
}
} public function grayscale()
{
$width = $this->dataWidth;
$height = $this->dataHeight;
public function grayscale() $ret = fill_array(0, $width * $height, 0);
{ for ($y = 0; $y < $height; $y++) {
$width = $this->dataWidth; for ($x = 0; $x < $width; $x++) {
$height = $this->dataHeight; $gray = $this->getPixel($x, $y, $width, $height);
$ret = fill_array(0, $width * $height, 0); $ret[$x + $y * $width] = $gray;
for ($y = 0; $y < $height; $y++) { }
for ($x = 0; $x < $width; $x++) { }
$gray = $this->getPixel($x, $y, $width, $height);
$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) $r = $image[$point]['red'];//($image[$point] >> 16) & 0xff;
{ $g = $image[$point]['green'];//($image[$point] >> 8) & 0xff;
$image = $this->pixels; $b = $image[$point]['blue'];//$image[$point] & 0xff;
if ($width < $x) {
die('error');
}
if ($height < $y) {
die('error');
}
$point = ($x) + ($y * $width);
$r = $image[$point]['red'];//($image[$point] >> 16) & 0xff; $p = (int)(($r * 33 + $g * 34 + $b * 33) / 100);
$g = $image[$point]['green'];//($image[$point] >> 8) & 0xff;
$b = $image[$point]['blue'];//$image[$point] & 0xff;
$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) for ($ay = 0; $ay < $sqrtNumArea; $ay++) {
{ for ($ax = 0; $ax < $sqrtNumArea; $ax++) {
$middle = $this->getMiddleBrightnessPerArea($grayScale); for ($dy = 0; $dy < $areaHeight; $dy++) {
$sqrtNumArea = count($middle); for ($dx = 0; $dx < $areaWidth; $dx++) {
$areaWidth = floor($this->dataWidth / $sqrtNumArea); $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;
$areaHeight = floor($this->dataHeight / $sqrtNumArea); }
$bitmap = fill_array(0, $this->dataWidth * $this->dataHeight, 0); }
}
}
for ($ay = 0; $ay < $sqrtNumArea; $ay++) { return $bitmap;
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; 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) //Console.out.println("");
{
$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(""); 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 return $row;
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; //@Override
} public function getMatrix()
{
$width = $this->getWidth();
$height = $this->getHeight();
//@Override // If the caller asks for the entire underlying image, save the copy and give them the
public function getMatrix() // original data. The docs specifically warn that result.length must be ignored.
{ if ($width == $this->dataWidth && $height == $this->dataHeight) {
$width = $this->getWidth(); return $this->luminances;
$height = $this->getHeight(); }
// If the caller asks for the entire underlying image, save the copy and give them the $area = $width * $height;
// original data. The docs specifically warn that result.length must be ignored. $matrix = [];
if ($width == $this->dataWidth && $height == $this->dataHeight) { $inputOffset = $this->top * $this->dataWidth + $this->left;
return $this->luminances;
}
$area = $width * $height; // If the width matches the full width of the underlying data, perform a single copy.
$matrix = []; if ($width == $this->dataWidth) {
$inputOffset = $this->top * $this->dataWidth + $this->left; $matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
// If the width matches the full width of the underlying data, perform a single copy. return $matrix;
if ($width == $this->dataWidth) { }
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
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. return $matrix;
$rgb = $this->luminances; }
for ($y = 0; $y < $height; $y++) {
$outputOffset = $y * $width;
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
$inputOffset += $this->dataWidth;
}
return $matrix; //@Override
} public function isCropSupported()
{
return true;
}
//@Override //@Override
public function isCropSupported() public function crop($left, $top, $width, $height): \Zxing\RGBLuminanceSource
{ {
return true; return new RGBLuminanceSource(
} $this->luminances,
$this->dataWidth,
//@Override $this->dataHeight,
public function crop($left, $top, $width, $height) $this->left + $left,
{ $this->top + $top,
return new RGBLuminanceSource($this->luminances, $width,
$this->dataWidth, $height
$this->dataHeight, );
$this->left + $left, }
$this->top + $top,
$width,
$height);
}
} }

View File

@ -4,7 +4,7 @@ namespace Zxing;
interface Reader interface Reader
{ {
public function decode(BinaryBitmap $image); public function decode(BinaryBitmap $image);
public function reset(); public function reset();
} }

View File

@ -26,25 +26,24 @@ namespace Zxing;
*/ */
abstract class ReaderException extends \Exception abstract class ReaderException extends \Exception
{ {
// disable stack traces when not running inside test units // disable stack traces when not running inside test units
//protected static $isStackTrace = System.getProperty("surefire.test.class.path") != null; //protected static $isStackTrace = System.getProperty("surefire.test.class.path") != null;
protected static $isStackTrace = false; protected static bool $isStackTrace = false;
function ReaderException($cause = null) public function ReaderException($cause = null): void
{ {
if ($cause) { if ($cause) {
parent::__construct($cause); parent::__construct($cause);
} }
} }
// Prevent stack traces from being taken // Prevent stack traces from being taken
// srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden? // 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. // This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
//@Override //@Override
public final function fillInStackTrace() final public function fillInStackTrace()
{ {
return null; return null;
} }
} }

View File

@ -24,112 +24,106 @@ namespace Zxing;
*/ */
final class Result final class Result
{ {
private $text; /**
private $rawBytes; * @var mixed[]|mixed
private $resultPoints; */
private $format; private $resultMetadata = null;
private $resultMetadata; private $timestamp;
private $timestamp;
public function __construct( public function __construct(
$text, private $text,
$rawBytes, private $rawBytes,
$resultPoints, private $resultPoints,
$format, private $format,
$timestamp = '' $timestamp = ''
) { ) {
$this->timestamp = $timestamp ?: time();
}
$this->text = $text; /**
$this->rawBytes = $rawBytes; * @return raw text encoded by the barcode
$this->resultPoints = $resultPoints; */
$this->format = $format; public function getText()
$this->resultMetadata = null; {
$this->timestamp = $timestamp ?: time(); return $this->text;
} }
/** /**
* @return raw text encoded by the barcode * @return raw bytes encoded by the barcode, if applicable, otherwise {@code null}
*/ */
public function getText() public function getRawBytes()
{ {
return $this->text; return $this->rawBytes;
} }
/** /**
* @return raw bytes encoded by the barcode, if applicable, otherwise {@code null} * @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
public function getRawBytes() * specific to the type of barcode that was decoded.
{ */
return $this->rawBytes; public function getResultPoints()
} {
return $this->resultPoints;
}
/** /**
* @return points related to the barcode in the image. These are typically points * @return {@link BarcodeFormat} representing the format of the barcode that was decoded
* identifying finder patterns or the corners of the barcode. The exact meaning is */
* specific to the type of barcode that was decoded. public function getBarcodeFormat()
*/ {
public function getResultPoints() return $this->format;
{ }
return $this->resultPoints;
}
/** /**
* @return {@link BarcodeFormat} representing the format of the barcode that was decoded * @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be
*/ * {@code null}. This contains optional metadata about what was detected about the barcode,
public function getBarcodeFormat() * like orientation.
{ */
return $this->format; public function getResultMetadata()
} {
return $this->resultMetadata;
}
/** public function putMetadata($type, $value): void
* @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be {
* {@code null}. This contains optional metadata about what was detected about the barcode, $resultMetadata = [];
* like orientation. if ($this->resultMetadata === null) {
*/ $this->resultMetadata = [];
public function getResultMetadata() }
{ $resultMetadata[$type] = $value;
return $this->resultMetadata; }
}
public function putMetadata($type, $value) public function putAllMetadata($metadata): void
{ {
if ($this->resultMetadata === null) { if ($metadata !== null) {
$this->resultMetadata = []; if ($this->resultMetadata === null) {
} $this->resultMetadata = $metadata;
$resultMetadata[$type] = $value; } else {
} $this->resultMetadata = array_merge($this->resultMetadata, $metadata);
}
}
}
public function putAllMetadata($metadata) public function addResultPoints($newPoints): void
{ {
if ($metadata !== null) { $oldPoints = $this->resultPoints;
if ($this->resultMetadata === null) { if ($oldPoints === null) {
$this->resultMetadata = $metadata; $this->resultPoints = $newPoints;
} else { } elseif ($newPoints !== null && (is_countable($newPoints) ? count($newPoints) : 0) > 0) {
$this->resultMetadata = array_merge($this->resultMetadata, $metadata); $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) public function getTimestamp()
{ {
$oldPoints = $this->resultPoints; return $this->timestamp;
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() public function toString()
{ {
return $this->timestamp; return $this->text;
} }
public function toString()
{
return $this->text;
}
} }

View File

@ -27,129 +27,131 @@ use Zxing\Common\Detector\MathUtils;
*/ */
class ResultPoint class ResultPoint
{ {
private $x; private float $x;
private $y; private float $y;
public function __construct($x, $y) public function __construct($x, $y)
{ {
$this->x = (float)($x); $this->x = (float)($x);
$this->y = (float)($y); $this->y = (float)($y);
} }
/** /**
* Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC * 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. * 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 * @param array $patterns of three {@code ResultPoint} to order
*/ */
public static function orderBestPatterns($patterns) public static function orderBestPatterns($patterns)
{ {
// Find distances between pattern centers // Find distances between pattern centers
$zeroOneDistance = self::distance($patterns[0], $patterns[1]); $zeroOneDistance = self::distance($patterns[0], $patterns[1]);
$oneTwoDistance = self::distance($patterns[1], $patterns[2]); $oneTwoDistance = self::distance($patterns[1], $patterns[2]);
$zeroTwoDistance = self::distance($patterns[0], $patterns[2]); $zeroTwoDistance = self::distance($patterns[0], $patterns[2]);
$pointA = ''; $pointA = '';
$pointB = ''; $pointB = '';
$pointC = ''; $pointC = '';
// Assume one closest to other two is B; A and C will just be guesses at first // Assume one closest to other two is B; A and C will just be guesses at first
if ($oneTwoDistance >= $zeroOneDistance && $oneTwoDistance >= $zeroTwoDistance) { if ($oneTwoDistance >= $zeroOneDistance && $oneTwoDistance >= $zeroTwoDistance) {
$pointB = $patterns[0]; $pointB = $patterns[0];
$pointA = $patterns[1]; $pointA = $patterns[1];
$pointC = $patterns[2]; $pointC = $patterns[2];
} else if ($zeroTwoDistance >= $oneTwoDistance && $zeroTwoDistance >= $zeroOneDistance) { } elseif ($zeroTwoDistance >= $oneTwoDistance && $zeroTwoDistance >= $zeroOneDistance) {
$pointB = $patterns[1]; $pointB = $patterns[1];
$pointA = $patterns[0]; $pointA = $patterns[0];
$pointC = $patterns[2]; $pointC = $patterns[2];
} else { } else {
$pointB = $patterns[2]; $pointB = $patterns[2];
$pointA = $patterns[0]; $pointA = $patterns[0];
$pointC = $patterns[1]; $pointC = $patterns[1];
} }
// Use cross product to figure out whether A and C are correct or flipped. // 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 // 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 // we want for A, B, C. If it's negative, then we've got it flipped around and
// should swap A and C. // should swap A and C.
if (self::crossProductZ($pointA, $pointB, $pointC) < 0.0) { if (self::crossProductZ($pointA, $pointB, $pointC) < 0.0) {
$temp = $pointA; $temp = $pointA;
$pointA = $pointC; $pointA = $pointC;
$pointC = $temp; $pointC = $temp;
} }
$patterns[0] = $pointA; $patterns[0] = $pointA;
$patterns[1] = $pointB; $patterns[1] = $pointB;
$patterns[2] = $pointC; $patterns[2] = $pointC;
return $patterns; return $patterns;
} }
/** /**
* @param pattern1 first pattern * @param first $pattern1 pattern
* @param pattern2 second pattern * @param second $pattern2 pattern
* *
* @return distance between two points * @return distance between two points
*/ */
public static function distance($pattern1, $pattern2) public static function distance($pattern1, $pattern2)
{ {
return MathUtils::distance($pattern1->x, $pattern1->y, $pattern2->x, $pattern2->y); 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. * Returns the z component of the cross product between vectors BC and BA.
*/ */
private static function crossProductZ($pointA, private static function crossProductZ(
$pointB, $pointA,
$pointC) $pointB,
{ $pointC
$bX = $pointB->x; )
$bY = $pointB->y; {
$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() final public function getX()
{ {
return (float)($this->x); return (float)($this->x);
} }
//@Override //@Override
public final function getY() final public function getY()
{ {
return (float)($this->y); return (float)($this->y);
} }
public final function equals($other) final public function equals($other)
{ {
if ($other instanceof ResultPoint) { if ($other instanceof ResultPoint) {
$otherPoint = $other; $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() final public function hashCode()
{ {
return 31 * floatToIntBits($this->x) + floatToIntBits($this->y); return 31 * floatToIntBits($this->x) + floatToIntBits($this->y);
} }
public final function toString() final public function toString()
{ {
$result = ''; $result = '';
$result .= ('('); $result .= ('(');
$result .= ($this->x); $result .= ($this->x);
$result .= (','); $result .= (',');
$result .= ($this->y); $result .= ($this->y);
$result .= (')'); $result .= (')');
return $result; return $result;
} }
} }

View File

@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <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> <coverage processUncoveredFiles="true">
<include> <include>
<directory suffix=".php">tests/</directory> <directory suffix=".php">lib</directory>
</include> </include>
</coverage> </coverage>
<testsuites> <testsuite name="Tests">
<testsuite name="Tests"> <directory suffix="Test.php">tests</directory>
<directory>tests</directory> </testsuite>
</testsuite>
</testsuites>
</phpunit> </phpunit>

View 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
// ]);
};

View File

@ -7,12 +7,18 @@ use Zxing\QrReader;
class QrReaderTest extends TestCase class QrReaderTest extends TestCase
{ {
public function testText1()
{
$image = __DIR__ . "/qrcodes/hello_world.png";
public function testText1() $qrcode = new QrReader($image);
{ $this->assertSame("Hello world!", $qrcode->text());
$image = __DIR__ . "/qrcodes/hello_world.png"; }
$qrcode = new QrReader($image); public function testNoText()
$this->assertSame("Hello world!", $qrcode->text()); {
} $image = __DIR__ . "/qrcodes/empty.png";
$qrcode = new QrReader($image);
$this->assertSame(false, $qrcode->text());
}
} }

View File

@ -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;
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,9 +1,9 @@
# PHP Enum implementation inspired from SplEnum # PHP Enum implementation inspired from SplEnum
[![Build Status](https://travis-ci.org/myclabs/php-enum.png?branch=master)](https://travis-ci.org/myclabs/php-enum) [![GitHub Actions][GA Image]][GA Link]
[![Latest Stable Version](https://poser.pugx.org/myclabs/php-enum/version.png)](https://packagist.org/packages/myclabs/php-enum) [![Latest Stable Version](https://poser.pugx.org/myclabs/php-enum/version.png)](https://packagist.org/packages/myclabs/php-enum)
[![Total Downloads](https://poser.pugx.org/myclabs/php-enum/downloads.png)](https://packagist.org/packages/myclabs/php-enum) [![Total Downloads](https://poser.pugx.org/myclabs/php-enum/downloads.png)](https://packagist.org/packages/myclabs/php-enum)
[![psalm](https://shepherd.dev/github/myclabs/php-enum/coverage.svg)](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). 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 ## Related projects
- [PHP 8.1+ native enum](https://www.php.net/enumerations)
- [Doctrine enum mapping](https://github.com/acelaya/doctrine-enum-type) - [Doctrine enum mapping](https://github.com/acelaya/doctrine-enum-type)
- [Symfony ParamConverter integration](https://github.com/Ex3v/MyCLabsEnumParamConverter) - [Symfony ParamConverter integration](https://github.com/Ex3v/MyCLabsEnumParamConverter)
- [PHPStan integration](https://github.com/timeweb/phpstan-enum) - [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

View File

@ -14,7 +14,10 @@
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"MyCLabs\\Enum\\": "src/" "MyCLabs\\Enum\\": "src/"
} },
"classmap": [
"stubs/Stringable.php"
]
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {

View File

@ -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>

View File

@ -19,7 +19,7 @@ namespace MyCLabs\Enum;
* @psalm-immutable * @psalm-immutable
* @psalm-consistent-constructor * @psalm-consistent-constructor
*/ */
abstract class Enum implements \JsonSerializable abstract class Enum implements \JsonSerializable, \Stringable
{ {
/** /**
* Enum value * Enum value

View File

@ -0,0 +1,11 @@
<?php
if (\PHP_VERSION_ID < 80000 && !interface_exists('Stringable')) {
interface Stringable
{
/**
* @return string
*/
public function __toString();
}
}

View File

@ -59,7 +59,7 @@ class TimezoneTransformer extends Transformer
return $dateTime->format('\G\M\TP'); return $dateTime->format('\G\M\TP');
} }
return sprintf('GMT%s%d', ($offset >= 0 ? '+' : ''), $offset / 100); return sprintf('GMT%s%d', $offset >= 0 ? '+' : '', $offset / 100);
} }
/** /**

View File

@ -244,7 +244,7 @@ abstract class IntlDateFormatter
* *
* @throws MethodNotImplementedException * @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__); throw new MethodNotImplementedException(__METHOD__);
} }

View File

@ -117,7 +117,7 @@ final class Intl
*/ */
public static function getIcuStubVersion(): string public static function getIcuStubVersion(): string
{ {
return '71.1'; return '72.1';
} }
/** /**

View File

@ -9,6 +9,10 @@
* file that was distributed with this source code. * 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_WIDTH', 75);
define('LINE', str_repeat('-', LINE_WIDTH)."\n"); define('LINE', str_repeat('-', LINE_WIDTH)."\n");

View File

@ -23,6 +23,10 @@ use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\Locale; use Symfony\Component\Intl\Locale;
use Symfony\Component\Intl\Util\GitRepository; 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__.'/common.php';
require_once __DIR__.'/autoload.php'; require_once __DIR__.'/autoload.php';

View File

@ -68,7 +68,7 @@ return [
], ],
'AWG' => [ 'AWG' => [
0 => 'AWG', 0 => 'AWG',
1 => 'আরুবা গিল্ডার', 1 => 'আরুবা ফ্লোরিন',
], ],
'AZM' => [ 'AZM' => [
0 => 'AZM', 0 => 'AZM',
@ -132,7 +132,7 @@ return [
], ],
'BOB' => [ 'BOB' => [
0 => 'BOB', 0 => 'BOB',
1 => 'বলিভিয়ানো', 1 => 'বলিভিয়ান বলিভিয়ানো',
], ],
'BOP' => [ 'BOP' => [
0 => 'BOP', 0 => 'BOP',
@ -412,7 +412,7 @@ return [
], ],
'GYD' => [ 'GYD' => [
0 => 'GYD', 0 => 'GYD',
1 => 'গায়েনা ডলার', 1 => 'গায়ানিজ ডলার',
], ],
'HKD' => [ 'HKD' => [
0 => 'HK$', 0 => 'HK$',
@ -452,7 +452,7 @@ return [
], ],
'ILS' => [ 'ILS' => [
0 => '₪', 0 => '₪',
1 => 'ইস্রাইলি নতুন শেকেল', 1 => 'ইসরায়েলি নতুন শেকেল',
], ],
'INR' => [ 'INR' => [
0 => '₹', 0 => '₹',
@ -500,7 +500,7 @@ return [
], ],
'KMF' => [ 'KMF' => [
0 => 'KMF', 0 => 'KMF',
1 => 'কম্বোরো ফ্রাঙ্ক', 1 => 'কমোরিয়ান ফ্রাঙ্ক',
], ],
'KPW' => [ 'KPW' => [
0 => 'KPW', 0 => 'KPW',
@ -640,7 +640,7 @@ return [
], ],
'MWK' => [ 'MWK' => [
0 => 'MWK', 0 => 'MWK',
1 => 'মালাউইয়ান কওয়াচ', 1 => 'মালাউইয়ান কোয়াচা',
], ],
'MXN' => [ 'MXN' => [
0 => 'MX$', 0 => 'MX$',
@ -708,7 +708,7 @@ return [
], ],
'PAB' => [ 'PAB' => [
0 => 'PAB', 0 => 'PAB',
1 => 'পানামা বেলবোয়া', 1 => 'পানামানিয়ান বালবোয়া',
], ],
'PEI' => [ 'PEI' => [
0 => 'PEI', 0 => 'PEI',
@ -836,7 +836,7 @@ return [
], ],
'SRD' => [ 'SRD' => [
0 => 'SRD', 0 => 'SRD',
1 => 'সুরিনাম ডলার', 1 => 'সুরিনামিজ ডলার',
], ],
'SRG' => [ 'SRG' => [
0 => 'SRG', 0 => 'SRG',

View 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 => 'পূর্ব ক্যারিবিয়ান ডলার',
],
],
];

View 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 => 'FCFA',
1 => 'КФА ВСЕАО франкӗ',
],
'XPF' => [
0 => 'CFPF',
1 => 'Франци Лӑпкӑ океан франкӗ',
],
'YER' => [
0 => 'YER',
1 => 'Йемен риалӗ',
],
'ZAR' => [
0 => 'ZAR',
1 => 'Кӑнтӑр Африка рэндӗ',
],
'ZMW' => [
0 => 'ZMW',
1 => 'Замби квачи',
],
],
];

View File

@ -824,7 +824,7 @@ return [
], ],
'QAR' => [ 'QAR' => [
0 => 'QAR', 0 => 'QAR',
1 => 'Qatari Rial', 1 => 'Qatari Riyal',
], ],
'RHD' => [ 'RHD' => [
0 => 'RHD', 0 => 'RHD',
@ -900,11 +900,11 @@ return [
], ],
'SLE' => [ 'SLE' => [
0 => 'SLE', 0 => 'SLE',
1 => 'Sierra Leonean New Leone', 1 => 'Sierra Leonean Leone',
], ],
'SLL' => [ 'SLL' => [
0 => 'SLL', 0 => 'SLL',
1 => 'Sierra Leonean Leone', 1 => 'Sierra Leonean Leone (1964—2022)',
], ],
'SOS' => [ 'SOS' => [
0 => 'SOS', 0 => 'SOS',

View File

@ -68,7 +68,7 @@ return [
], ],
'BOB' => [ 'BOB' => [
0 => 'BOB', 0 => 'BOB',
1 => 'Boliviano', 1 => 'Bolivian boliviano',
], ],
'BRL' => [ 'BRL' => [
0 => 'BRL', 0 => 'BRL',
@ -98,10 +98,6 @@ return [
0 => 'CLP', 0 => 'CLP',
1 => 'Chilean Peso', 1 => 'Chilean Peso',
], ],
'CNH' => [
0 => 'CNH',
1 => 'CNH',
],
'CNY' => [ 'CNY' => [
0 => 'CNY', 0 => 'CNY',
1 => 'Chinese Yuan', 1 => 'Chinese Yuan',
@ -430,10 +426,6 @@ return [
0 => 'SHP', 0 => 'SHP',
1 => 'St Helena Pound', 1 => 'St Helena Pound',
], ],
'SLL' => [
0 => 'SLL',
1 => 'Sierra Leonean Leone',
],
'SOS' => [ 'SOS' => [
0 => 'SOS', 0 => 'SOS',
1 => 'Somali Shilling', 1 => 'Somali Shilling',
@ -508,7 +500,7 @@ return [
], ],
'VES' => [ 'VES' => [
0 => 'VES', 0 => 'VES',
1 => 'VES', 1 => 'Venezuelan bolívar',
], ],
'VND' => [ 'VND' => [
0 => 'VND', 0 => 'VND',

View File

@ -40,7 +40,7 @@ return [
], ],
'SHP' => [ 'SHP' => [
0 => 'SHP', 0 => 'SHP',
1 => 'St Helena Pound', 1 => 'Saint Helena Pound',
], ],
'STN' => [ 'STN' => [
0 => 'STN', 0 => 'STN',
@ -56,7 +56,7 @@ return [
], ],
'USD' => [ 'USD' => [
0 => 'US$', 0 => 'US$',
1 => 'U.S. Dollar', 1 => 'US Dollar',
], ],
], ],
]; ];

View File

@ -2,7 +2,7 @@
return [ return [
'Names' => [ 'Names' => [
'SLL' => [ 'SLE' => [
0 => 'Le', 0 => 'Le',
1 => 'Sierra Leonean Leone', 1 => 'Sierra Leonean Leone',
], ],

View File

@ -898,6 +898,10 @@ return [
0 => 'SKK', 0 => 'SKK',
1 => 'koroa eslovakiarra', 1 => 'koroa eslovakiarra',
], ],
'SLE' => [
0 => 'SLE',
1 => 'leone sierraleonar berria',
],
'SLL' => [ 'SLL' => [
0 => 'SLL', 0 => 'SLL',
1 => 'leone sierraleonarra', 1 => 'leone sierraleonarra',
@ -1046,6 +1050,10 @@ return [
0 => 'VEB', 0 => 'VEB',
1 => 'Venezuelako bolivarra (18712008)', 1 => 'Venezuelako bolivarra (18712008)',
], ],
'VED' => [
0 => 'VED',
1 => 'bolivar subiraua',
],
'VEF' => [ 'VEF' => [
0 => 'VEF', 0 => 'VEF',
1 => 'Venezuelako bolivarra (20082018)', 1 => 'Venezuelako bolivarra (20082018)',

View File

@ -6,6 +6,10 @@ return [
0 => 'AED', 0 => 'AED',
1 => '𞤁𞤭𞤪𞤸𞤢𞤥𞤵 𞤋𞤥𞤢𞥄𞤪𞤢𞤼𞤭𞤲𞤳𞤮', 1 => '𞤁𞤭𞤪𞤸𞤢𞤥𞤵 𞤋𞤥𞤢𞥄𞤪𞤢𞤼𞤭𞤲𞤳𞤮',
], ],
'AFA' => [
0 => '𞤀𞤊𞤀',
1 => '𞤀𞤬𞤺𞤢𞥄𞤲 𞤀𞤬𞤺𞤢𞥄𞤲𞤭 (𞥑𞥙𞥒𞥗-𞥒𞥐𞥐𞥒)',
],
'AFN' => [ 'AFN' => [
0 => 'AFN', 0 => 'AFN',
1 => '𞤀𞤬𞤿𞤢𞤲𞤭 𞤀𞤬𞤿𞤢𞤲𞤭𞤴𞤢𞤲𞤳𞤮', 1 => '𞤀𞤬𞤿𞤢𞤲𞤭 𞤀𞤬𞤿𞤢𞤲𞤭𞤴𞤢𞤲𞤳𞤮',
@ -26,9 +30,25 @@ return [
0 => 'AOA', 0 => 'AOA',
1 => '𞤑𞤵𞤱𞤢𞤲𞥁𞤢 𞤀𞤲𞤺𞤮𞤤𞤢𞤲𞤳𞤮', 1 => '𞤑𞤵𞤱𞤢𞤲𞥁𞤢 𞤀𞤲𞤺𞤮𞤤𞤢𞤲𞤳𞤮',
], ],
'ARA' => [
0 => 'ARA',
1 => '𞤌𞤧𞤼𞤪𞤢𞤤 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢𞤲𞤳𞤮',
],
'ARL' => [
0 => 'ARL',
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤂𞤫𞤴 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢𞤲𞤳𞤮 (𞥑𞥙𞥗𞥐-𞥑𞥙𞥘𞥓)',
],
'ARM' => [
0 => 'ARM',
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢𞤲𞤳𞤮 (𞥑𞥘𞥘𞥑-𞥑𞥙𞥗𞥐)',
],
'ARP' => [
0 => 'ARP',
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢𞤲𞤳𞤮 (𞥑𞥙𞥘𞥓-𞥑𞥙𞥘𞥕)',
],
'ARS' => [ 'ARS' => [
0 => 'ARS', 0 => 'ARS',
1 => '𞤆𞤫𞤧𞤮 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢', 1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤀𞤪𞤶𞤢𞤲𞤼𞤭𞤲𞤢𞤲𞤳𞤮',
], ],
'AUD' => [ 'AUD' => [
0 => 'A$', 0 => 'A$',
@ -68,7 +88,7 @@ return [
], ],
'BMD' => [ 'BMD' => [
0 => 'BMD', 0 => 'BMD',
1 => '𞤁𞤢𞤤𞤢 𞤄𞤫𞤪𞤥𞤵𞤣𞤢𞥄𞤲', 1 => '𞤁𞤢𞤤𞤢 𞤄𞤵𞤪𞤥𞤵𞤣𞤢𞤲𞤳𞤮',
], ],
'BND' => [ 'BND' => [
0 => 'BND', 0 => 'BND',
@ -78,10 +98,46 @@ return [
0 => 'BOB', 0 => 'BOB',
1 => '𞤄𞤮𞤤𞤭𞤾𞤭𞤴𞤢𞤲𞤮 𞤄𞤮𞤤𞤭𞤾𞤭𞤴𞤢𞤲𞤳𞤮', 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' => [ 'BRL' => [
0 => 'R$', 0 => 'R$',
1 => '𞤈𞤭𞤴𞤢𞤤 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮', 1 => '𞤈𞤭𞤴𞤢𞤤 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮',
], ],
'BRN' => [
0 => 'BRN',
1 => '𞤑𞤵𞤪𞥁𞤢𞤣𞤮𞥅 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮 (𞥑𞥙𞥘𞥙-𞥑𞥙𞥙𞥐)',
],
'BRR' => [
0 => 'BRR',
1 => '𞤑𞤵𞤪𞥁𞤫𞤴𞤪𞤮 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮 (𞥑𞥙𞥙𞥓-𞥑𞥙𞥙𞥔)',
],
'BRZ' => [
0 => 'BRZ',
1 => '𞤑𞤵𞤪𞥁𞤫𞤴𞤪𞤮 𞤄𞤪𞤢𞤧𞤭𞤤𞤴𞤢𞤲𞤳𞤮 (𞥑𞥙𞥔𞥒-𞥑𞥙𞥖𞥗)',
],
'BSD' => [ 'BSD' => [
0 => 'BSD', 0 => 'BSD',
1 => '𞤁𞤢𞤤𞤢 𞤄𞤢𞤸𞤢𞤥𞤭𞤴𞤢𞤲𞤳𞤮', 1 => '𞤁𞤢𞤤𞤢 𞤄𞤢𞤸𞤢𞤥𞤭𞤴𞤢𞤲𞤳𞤮',
@ -100,7 +156,7 @@ return [
], ],
'BZD' => [ 'BZD' => [
0 => 'BZD', 0 => 'BZD',
1 => '𞤁𞤢𞤤𞤢 𞤄𞤫𞤤𞤭𞥅𞤧', 1 => '𞤁𞤢𞤤𞤢 𞤄𞤫𞤤𞤭𞥅𞤧𞤴𞤢𞤲𞤳𞤮',
], ],
'CAD' => [ 'CAD' => [
0 => 'CA$', 0 => 'CA$',
@ -114,9 +170,17 @@ return [
0 => 'CHF', 0 => 'CHF',
1 => '𞤊𞤢𞤪𞤢𞤲 𞤅𞤵𞤱𞤭𞥅𞤧', 1 => '𞤊𞤢𞤪𞤢𞤲 𞤅𞤵𞤱𞤭𞥅𞤧',
], ],
'CLE' => [
0 => 'CLE',
1 => '𞤉𞤧𞤳𞤵𞤣𞤮𞥅 𞤕𞤭𞤤𞤫𞥊𞤴𞤢𞤲𞤳𞤮',
],
'CLF' => [
0 => 'CLF',
1 => '𞤅𞤢𞤤𞤲𞤣𞤵 𞤂𞤭𞤥𞤮𞥅𞤪𞤫 𞤕𞤭𞤤𞤫𞥊𞤴𞤢𞤲𞤳𞤮',
],
'CLP' => [ 'CLP' => [
0 => 'CLP', 0 => 'CLP',
1 => '𞤆𞤫𞤧𞤮 𞤕𞤭𞤤𞤭𞤴𞤢𞤲𞤳𞤮', 1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤕𞤭𞤤𞤫𞥊𞤴𞤢𞤲𞤳𞤮',
], ],
'CNH' => [ 'CNH' => [
0 => 'CNH', 0 => 'CNH',
@ -128,11 +192,15 @@ return [
], ],
'COP' => [ 'COP' => [
0 => 'COP', 0 => 'COP',
1 => '𞤆𞤫𞤧𞤮 𞤑𞤮𞤤𞤮𞤥𞤦𞤭𞤴𞤢𞤲𞤳𞤮', 1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤑𞤮𞤤𞤮𞤥𞤦𞤭𞤴𞤢𞤲𞤳𞤮',
],
'COU' => [
0 => 'COU',
1 => '𞤅𞤢𞤤𞤲𞤣𞤵 𞤔𞤢𞤪𞤮 𞤳𞤮𞤤𞤮𞤥𞤦𞤭𞤴𞤢𞤲𞤳𞤮',
], ],
'CRC' => [ 'CRC' => [
0 => 'CRC', 0 => 'CRC',
1 => '𞤑𞤮𞤤𞤮𞥅𞤲 𞤑𞤮𞤧𞤼𞤢 𞤈𞤭𞤳𞤢𞤲', 1 => '𞤑𞤮𞤤𞤮𞥅𞤲 𞤑𞤮𞤧𞤼𞤢𞤪𞤭𞤴𞤢𞤲𞤳𞤮',
], ],
'CUC' => [ 'CUC' => [
0 => 'CUC', 0 => 'CUC',
@ -140,7 +208,7 @@ return [
], ],
'CUP' => [ 'CUP' => [
0 => 'CUP', 0 => 'CUP',
1 => '𞤆𞤫𞤧𞤮 𞤑𞤵𞤦𞤢𞤲𞤳𞤮', 1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤑𞤵𞤦𞤢𞤲𞤳𞤮',
], ],
'CVE' => [ 'CVE' => [
0 => 'CVE', 0 => 'CVE',
@ -166,6 +234,14 @@ return [
0 => 'DZD', 0 => 'DZD',
1 => '𞤁𞤭𞤲𞤢𞥄𞤪 𞤀𞤤𞤶𞤢𞤪𞤭𞤲𞤳𞤮', 1 => '𞤁𞤭𞤲𞤢𞥄𞤪 𞤀𞤤𞤶𞤢𞤪𞤭𞤲𞤳𞤮',
], ],
'ECS' => [
0 => 'ECS',
1 => '𞤅𞤵𞥅𞤳𞤵𞤪𞤫𞥊𞥅 𞤉𞤳𞤵𞤱𞤢𞤣𞤮𞥅𞤪𞤴𞤢𞤲𞤳𞤮',
],
'ECV' => [
0 => 'ECV',
1 => '𞤅𞤢𞤤𞤲𞤣𞤵 𞤔𞤮𞤪𞤮 𞤉𞤳𞤵𞤱𞤢𞤣𞤮𞥅𞤪𞤴𞤢𞤲𞤳𞤮 𞤚𞤢𞤦𞤭𞤼𞤵𞤲𞥋𞤺𞤮',
],
'EGP' => [ 'EGP' => [
0 => 'EGP', 0 => 'EGP',
1 => '𞤆𞤢𞤱𞤲𞤣𞤵 𞤃𞤭𞤧𞤭𞤪𞤢𞤲𞤳𞤮', 1 => '𞤆𞤢𞤱𞤲𞤣𞤵 𞤃𞤭𞤧𞤭𞤪𞤢𞤲𞤳𞤮',
@ -374,6 +450,10 @@ return [
0 => 'MUR', 0 => 'MUR',
1 => '𞤈𞤵𞤨𞤭𞥅 𞤃𞤮𞤪𞤭𞤧𞤭𞤴𞤢𞤲𞤳𞤮', 1 => '𞤈𞤵𞤨𞤭𞥅 𞤃𞤮𞤪𞤭𞤧𞤭𞤴𞤢𞤲𞤳𞤮',
], ],
'MVP' => [
0 => 'MVP',
1 => '𞤈𞤵𞥅𞤨𞤭𞥅 𞤃𞤢𞤤𞤣𞤭𞥅𞤬 (𞥑𞥙𞥔𞥗-𞥑𞥙𞥘𞥑)',
],
'MVR' => [ 'MVR' => [
0 => 'MVR', 0 => 'MVR',
1 => '𞤈𞤵𞤬𞤭𞤴𞤢𞥄 𞤃𞤢𞤤𞤣𞤭𞤾𞤭𞤴𞤢𞤲𞤳𞤮', 1 => '𞤈𞤵𞤬𞤭𞤴𞤢𞥄 𞤃𞤢𞤤𞤣𞤭𞤾𞤭𞤴𞤢𞤲𞤳𞤮',
@ -384,7 +464,15 @@ return [
], ],
'MXN' => [ 'MXN' => [
0 => 'MX$', 0 => 'MX$',
1 => '𞤆𞤫𞤧𞤮 𞤃𞤫𞤳𞤧𞤭𞤴𞤢𞤲𞤳𞤮', 1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤃𞤫𞤳𞤧𞤭𞤳𞤮𞤴𞤢𞤲𞤳𞤮',
],
'MXP' => [
0 => 'MXP',
1 => '𞤑𞤢𞥄𞤤𞤭𞤧𞤫 𞤆𞤫𞥅𞤧𞤮𞥅 𞤃𞤫𞤳𞤧𞤭𞤳𞤮𞤴𞤢𞤲𞤳𞤮 (𞥑𞥘𞥖𞥑-𞥑𞥙𞥙𞥒)',
],
'MXV' => [
0 => 'MXV',
1 => '𞤅𞤢𞤤𞤲𞤣𞤵 𞤊𞤭𞤤𞤮 𞤃𞤫𞤳𞤧𞤭𞤳𞤮𞤴𞤢𞤲𞤳𞤮𞥅𞤪𞤵',
], ],
'MYR' => [ 'MYR' => [
0 => 'MYR', 0 => 'MYR',
@ -402,6 +490,10 @@ return [
0 => '𞤐𞤐𞤘', 0 => '𞤐𞤐𞤘',
1 => '𞤐𞤢𞤴𞤪𞤢 𞤐𞤢𞤶𞤭𞤪𞤢𞤴𞤢𞤲𞤳𞤮', 1 => '𞤐𞤢𞤴𞤪𞤢 𞤐𞤢𞤶𞤭𞤪𞤢𞤴𞤢𞤲𞤳𞤮',
], ],
'NIC' => [
0 => 'NIC',
1 => '𞤑𞤮𞥅𞤪𞤣𞤮𞤦𞤢 𞤐𞤭𞤳𞤢𞤪𞤢𞤺𞤵𞤱𞤢𞤲𞤳𞤮 (𞥑𞥙𞥘𞥘-𞥑𞥙𞥙𞥑)',
],
'NIO' => [ 'NIO' => [
0 => 'NIO', 0 => 'NIO',
1 => '𞤑𞤮𞥅𞤪𞤣𞤮𞤦𞤢 𞤐𞤭𞤳𞤢𞤪𞤢𞤺𞤵𞤱𞤢𞤲𞤳𞤮', 1 => '𞤑𞤮𞥅𞤪𞤣𞤮𞤦𞤢 𞤐𞤭𞤳𞤢𞤪𞤢𞤺𞤵𞤱𞤢𞤲𞤳𞤮',
@ -426,10 +518,18 @@ return [
0 => 'PAB', 0 => 'PAB',
1 => '𞤄𞤢𞤤𞤦𞤮𞤱𞤢 𞤆𞤢𞤲𞤢𞤥𞤢𞤴𞤢𞤲𞤳𞤮', 1 => '𞤄𞤢𞤤𞤦𞤮𞤱𞤢 𞤆𞤢𞤲𞤢𞤥𞤢𞤴𞤢𞤲𞤳𞤮',
], ],
'PEI' => [
0 => 'PEI',
1 => '𞤋𞤲𞤼𞤭 𞤨𞤫𞤪𞤵𞤴𞤢𞤲𞤳𞤮',
],
'PEN' => [ 'PEN' => [
0 => 'PEN', 0 => 'PEN',
1 => '𞤅𞤮𞤤 𞤆𞤫𞤪𞤵𞤲𞤳𞤮', 1 => '𞤅𞤮𞤤 𞤆𞤫𞤪𞤵𞤲𞤳𞤮',
], ],
'PES' => [
0 => 'PES',
1 => '𞤅𞤮𞤤 𞤆𞤫𞤪𞤵𞤴𞤢𞤲𞤳𞤮 (𞥑𞥘𞥖𞥓-𞥑𞥙𞥖𞥕)',
],
'PGK' => [ 'PGK' => [
0 => '𞤑𞤆𞤘', 0 => '𞤑𞤆𞤘',
1 => '𞤑𞤭𞤲𞤢 𞤆𞤢𞤨𞤵𞤱𞤢 𞤐𞤫𞤱-𞤘𞤭𞤲𞤫𞤲𞤳𞤮', 1 => '𞤑𞤭𞤲𞤢 𞤆𞤢𞤨𞤵𞤱𞤢 𞤐𞤫𞤱-𞤘𞤭𞤲𞤫𞤲𞤳𞤮',
@ -510,6 +610,10 @@ return [
0 => 'SRD', 0 => 'SRD',
1 => '𞤁𞤢𞤤𞤢 𞤅𞤵𞤪𞤵𞤲𞤢𞤥𞤭𞤲𞤳𞤮', 1 => '𞤁𞤢𞤤𞤢 𞤅𞤵𞤪𞤵𞤲𞤢𞤥𞤭𞤲𞤳𞤮',
], ],
'SRG' => [
0 => 'SRG',
1 => '𞤘𞤭𞤤𞤣𞤮𞥅 𞤅𞤵𞤪𞤵𞤲𞤢𞤥𞤭𞤲𞤳𞤮',
],
'SSP' => [ 'SSP' => [
0 => 'SSP', 0 => 'SSP',
1 => '𞤆𞤢𞤱𞤲𞤣𞤵 𞤂𞤫𞤴𞤤𞤫𞤴𞤪𞤭 𞤅𞤵𞤣𞤢𞤲𞤭𞤲𞤳𞤮', 1 => '𞤆𞤢𞤱𞤲𞤣𞤵 𞤂𞤫𞤴𞤤𞤫𞤴𞤪𞤭 𞤅𞤵𞤣𞤢𞤲𞤭𞤲𞤳𞤮',
@ -518,6 +622,10 @@ return [
0 => 'STN', 0 => 'STN',
1 => '𞤁𞤮𞤦𞤢𞤪𞤢 𞤅𞤢𞤱𞤮-𞤚𞤮𞤥𞤫 & 𞤆𞤫𞤪𞤫𞤲𞤧𞤭𞤨', 1 => '𞤁𞤮𞤦𞤢𞤪𞤢 𞤅𞤢𞤱𞤮-𞤚𞤮𞤥𞤫 & 𞤆𞤫𞤪𞤫𞤲𞤧𞤭𞤨',
], ],
'SVC' => [
0 => 'SVC',
1 => '𞤑𞤮𞤤𞤮𞥅𞤲 𞤅𞤢𞤤𞤾𞤢𞤣𞤮𞤪𞤢𞤲𞤳𞤮',
],
'SYP' => [ 'SYP' => [
0 => 'SYP', 0 => 'SYP',
1 => '𞤆𞤢𞤱𞤲𞥋𞤣𞤵 𞤅𞤭𞤪𞤢𞤴𞤢𞤲𞤳𞤮', 1 => '𞤆𞤢𞤱𞤲𞥋𞤣𞤵 𞤅𞤭𞤪𞤢𞤴𞤢𞤲𞤳𞤮',
@ -574,14 +682,42 @@ return [
0 => 'US$', 0 => 'US$',
1 => '𞤁𞤢𞤤𞤢 𞤁𞤫𞤲𞤼𞤢𞤤 𞤂𞤢𞤪𞤫 𞤀𞤥𞤫𞤪𞤭𞤳', 1 => '𞤁𞤢𞤤𞤢 𞤁𞤫𞤲𞤼𞤢𞤤 𞤂𞤢𞤪𞤫 𞤀𞤥𞤫𞤪𞤭𞤳',
], ],
'USN' => [
0 => 'USN',
1 => '𞤣𞤢𞤤𞤢 𞤁𞤂𞤀 (𞤶𞤢𞤲𞤺𞤮 𞤥𞤵𞥅𞤯𞤵𞤲)',
],
'USS' => [
0 => 'USS',
1 => '𞤣𞤢𞤤𞤢 𞤁𞤂𞤀 (𞤸𞤢𞤲𞤣𞤫 𞤥𞤵𞥅𞤯𞤵𞤲)',
],
'UYI' => [
0 => 'UYI',
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤒𞤵𞤪𞤺𞤮𞤴𞤢𞤲𞤳𞤮 (𞤕𞤢𞤤𞤯𞤭 𞤔𞤮𞥅𞤨𞤢𞥄𞤯𞤭)',
],
'UYP' => [
0 => 'UYP',
1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤒𞤵𞤪𞤺𞤮𞤴𞤢𞤲𞤳𞤮 (𞥑𞥙𞥗𞥕-𞥑𞥙𞥙𞥓)',
],
'UYU' => [ 'UYU' => [
0 => 'UYU', 0 => 'UYU',
1 => '𞤆𞤫𞤧𞤮 𞤓𞤪𞤵𞤺𞤵𞤪𞤭𞤲𞤳𞤮', 1 => '𞤆𞤫𞥅𞤧𞤮𞥅 𞤒𞤵𞤪𞤺𞤮𞤴𞤢𞤲𞤳𞤮',
],
'UYW' => [
0 => 'UYW',
1 => '𞤅𞤢𞤤𞤲𞤣𞤵 𞤐𞤶𞤮𞤩𞤣𞤭 𞤒𞤵𞤪𞤺𞤮𞤴𞤢𞤲𞤳𞤮 𞤔𞤮𞥅𞤨𞤢𞥄𞤲𞤣𞤭',
], ],
'UZS' => [ 'UZS' => [
0 => 'UZS', 0 => 'UZS',
1 => '𞤅𞤮𞤥𞤵 𞤓𞥁𞤦𞤫𞤳𞤭𞤧𞤼𞤢𞤲𞤳𞤮', 1 => '𞤅𞤮𞤥𞤵 𞤓𞥁𞤦𞤫𞤳𞤭𞤧𞤼𞤢𞤲𞤳𞤮',
], ],
'VEB' => [
0 => 'VEB',
1 => '𞤄𞤮𞤤𞤭𞤾𞤢𞥄𞤪 𞤜𞤫𞤲𞤭𞥅𞤧𞤫𞤤𞤢𞤲𞤳𞤮 (𞥑𞥘𞥗𞥑-𞥒𞥐𞥐𞥘)',
],
'VED' => [
0 => 'VED',
1 => '𞤄𞤮𞤤𞤭𞤾𞤢𞥄𞤪 𞤅𞤮𞤦𞤫𞥊𞤪𞤢𞤲𞤮',
],
'VEF' => [ 'VEF' => [
0 => 'VEF', 0 => 'VEF',
1 => '𞤄𞤮𞤤𞤭𞤾𞤢𞥄𞤪 𞤜𞤫𞤲𞤭𞥅𞤧𞤫𞤤𞤢𞤲𞤳𞤮 (𞥒𞥐𞥐𞥘 - 𞥒𞥐𞥑𞥘)', 1 => '𞤄𞤮𞤤𞤭𞤾𞤢𞥄𞤪 𞤜𞤫𞤲𞤭𞥅𞤧𞤫𞤤𞤢𞤲𞤳𞤮 (𞥒𞥐𞥐𞥘 - 𞥒𞥐𞥑𞥘)',

View File

@ -6,9 +6,9 @@ return [
0 => 'GNF', 0 => 'GNF',
1 => '𞤊𞤢𞤪𞤢𞤲 𞤘𞤭𞤲𞤫𞤲𞤳𞤮', 1 => '𞤊𞤢𞤪𞤢𞤲 𞤘𞤭𞤲𞤫𞤲𞤳𞤮',
], ],
'SLL' => [ 'SLE' => [
0 => 'Le', 0 => 'Le',
1 => '𞤂𞤫𞤴𞤮𞤲 𞤅𞤫𞤪𞤢𞤤𞤭𞤴𞤢𞤲𞤳𞤮', 1 => 'SLE',
], ],
], ],
]; ];

View File

@ -2,9 +2,9 @@
return [ return [
'Names' => [ 'Names' => [
'SLL' => [ 'SLE' => [
0 => 'Le', 0 => 'Le',
1 => 'Lewoon Seraa Liyon', 1 => 'SLE',
], ],
], ],
]; ];

View File

@ -898,6 +898,10 @@ return [
0 => 'SKK', 0 => 'SKK',
1 => 'Koruna Slòbhacach', 1 => 'Koruna Slòbhacach',
], ],
'SLE' => [
0 => 'SLE',
1 => 'Leone Siarra Leòmhannach ùr',
],
'SLL' => [ 'SLL' => [
0 => 'SLL', 0 => 'SLL',
1 => 'Leone Siarra Leòmhannach', 1 => 'Leone Siarra Leòmhannach',
@ -1046,6 +1050,10 @@ return [
0 => 'VEB', 0 => 'VEB',
1 => 'Bolívar Bheinisealach (18712008)', 1 => 'Bolívar Bheinisealach (18712008)',
], ],
'VED' => [
0 => 'VED',
1 => 'Bolívar Soberano',
],
'VEF' => [ 'VEF' => [
0 => 'VEF', 0 => 'VEF',
1 => 'Bolívar Bheinisealach (20082018)', 1 => 'Bolívar Bheinisealach (20082018)',

View File

@ -352,7 +352,7 @@ return [
], ],
'MDL' => [ 'MDL' => [
0 => 'MDL', 0 => 'MDL',
1 => 'kuɗaɗen Moldova', 1 => 'Kuɗaɗen Moldova',
], ],
'MGA' => [ 'MGA' => [
0 => 'MGA', 0 => 'MGA',

View File

@ -600,7 +600,7 @@ return [
], ],
'PHP' => [ 'PHP' => [
0 => 'PHP', 0 => 'PHP',
1 => זו פיליפיני', 1 => סו פיליפיני',
], ],
'PKR' => [ 'PKR' => [
0 => 'PKR', 0 => 'PKR',

View 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',
],
],
];

View File

@ -375,7 +375,7 @@ return [
1 => 'etiopski bir', 1 => 'etiopski bir',
], ],
'EUR' => [ 'EUR' => [
0 => 'EUR', 0 => '',
1 => 'euro', 1 => 'euro',
], ],
'FIM' => [ 'FIM' => [

View File

@ -332,7 +332,7 @@ return [
], ],
'LSL' => [ 'LSL' => [
0 => 'LSL', 0 => 'LSL',
1 => 'This is not a translation', 1 => 'Ego loti obodo Lesotho',
], ],
'LYD' => [ 'LYD' => [
0 => 'LYD', 0 => 'LYD',

View File

@ -260,7 +260,7 @@ return [
], ],
'FJD' => [ 'FJD' => [
0 => 'FJD', 0 => 'FJD',
1 => 'fidjeyskur dalur', 1 => 'fídjískur dalur',
], ],
'FKP' => [ 'FKP' => [
0 => 'FKP', 0 => 'FKP',
@ -359,7 +359,7 @@ return [
1 => 'íranskt ríal', 1 => 'íranskt ríal',
], ],
'ISK' => [ 'ISK' => [
0 => 'ISK', 0 => 'kr.',
1 => 'íslensk króna', 1 => 'íslensk króna',
], ],
'ITL' => [ 'ITL' => [
@ -760,7 +760,7 @@ return [
], ],
'TND' => [ 'TND' => [
0 => 'TND', 0 => 'TND',
1 => 'túnískur denari', 1 => 'túniskur denari',
], ],
'TOP' => [ 'TOP' => [
0 => 'TOP', 0 => 'TOP',

Some files were not shown because too many files have changed in this diff Show More