Update dependencies
This commit is contained in:
parent
4f030b05af
commit
c42ebb8062
|
|
@ -34,5 +34,11 @@
|
||||||
"allow-plugins": {
|
"allow-plugins": {
|
||||||
"ocramius/package-versions": true
|
"ocramius/package-versions": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"archive": {
|
||||||
|
"exclude": [
|
||||||
|
"/test",
|
||||||
|
"/phpunit.xml.dist"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true">
|
||||||
|
<coverage processUncoveredFiles="true">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">src</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="BaconQrCode Tests">
|
||||||
|
<directory>./test</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
||||||
|
|
@ -89,6 +89,9 @@ final class CharacterSetEci extends AbstractEnum
|
||||||
*/
|
*/
|
||||||
private static $nameToEci;
|
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;
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ final class Encoder
|
||||||
/**
|
/**
|
||||||
* Codec cache.
|
* Codec cache.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array<string,ReedSolomonCodec>
|
||||||
*/
|
*/
|
||||||
private static $codecs = [];
|
private static $codecs = [];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ final class EdgeIterator implements IteratorAggregate
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Edge[]
|
* @return Traversable<Edge>
|
||||||
*/
|
*/
|
||||||
public function getIterator() : Traversable
|
public function getIterator() : Traversable
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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) =
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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": []
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -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/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/ecs.php
vendored
Normal file
29
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/ecs.php
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Rector\Set\ValueObject\LevelSetList;
|
||||||
|
use PhpCsFixer\Fixer\Operator\ConcatSpaceFixer;
|
||||||
|
use Symplify\EasyCodingStandard\Config\ECSConfig;
|
||||||
|
use Symplify\EasyCodingStandard\ValueObject\Option;
|
||||||
|
|
||||||
|
use PhpCsFixer\Fixer\ArrayNotation\ArraySyntaxFixer;
|
||||||
|
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;
|
||||||
|
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||||
|
|
||||||
|
return static function (ECSConfig $configurator): void {
|
||||||
|
|
||||||
|
// alternative to CLI arguments, easier to maintain and extend
|
||||||
|
$configurator->paths([__DIR__ . '/lib', __DIR__ . '/tests']);
|
||||||
|
|
||||||
|
// choose
|
||||||
|
$configurator->sets([
|
||||||
|
SetList::CLEAN_CODE, SetList::PSR_12//, LevelSetList::UP_TO_PHP_81 //, SymfonySetList::SYMFONY_60
|
||||||
|
]);
|
||||||
|
|
||||||
|
$configurator->ruleWithConfiguration(ConcatSpaceFixer::class, [
|
||||||
|
'spacing' => 'one'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// indent and tabs/spaces
|
||||||
|
// [default: spaces]. BUT: tabs are superiour due to accessibility reasons
|
||||||
|
$configurator->indentation('tab');
|
||||||
|
};
|
||||||
|
|
@ -30,67 +30,64 @@ use Zxing\Common\BitMatrix;
|
||||||
*/
|
*/
|
||||||
abstract class Binarizer
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
64
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/rector.php
vendored
Normal file
64
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/rector.php
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Rector\Config\RectorConfig;
|
||||||
|
use Rector\Nette\Set\NetteSetList;
|
||||||
|
use Rector\Set\ValueObject\SetList;
|
||||||
|
use Rector\Core\Configuration\Option;
|
||||||
|
use Rector\Symfony\Set\SymfonySetList;
|
||||||
|
use Rector\Doctrine\Set\DoctrineSetList;
|
||||||
|
use Rector\Set\ValueObject\LevelSetList;
|
||||||
|
use Rector\Symfony\Set\SensiolabsSetList;
|
||||||
|
use Rector\TypeDeclaration\Rector\Property\PropertyTypeDeclarationRector;
|
||||||
|
use Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector;
|
||||||
|
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnNewRector;
|
||||||
|
use Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationRector;
|
||||||
|
use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
|
||||||
|
use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector;
|
||||||
|
use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByParentCallTypeRector;
|
||||||
|
use Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector;
|
||||||
|
use Rector\TypeDeclaration\Rector\ClassMethod\ArrayShapeFromConstantArrayReturnRector;
|
||||||
|
|
||||||
|
|
||||||
|
return static function (RectorConfig $rectorConfig): void {
|
||||||
|
$rectorConfig->paths([
|
||||||
|
__DIR__ . '/lib'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$parameters = $rectorConfig->parameters();
|
||||||
|
$parameters->set(
|
||||||
|
Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER,
|
||||||
|
__DIR__ . '/var/cache/dev/App_KernelDevDebugContainer.xml'
|
||||||
|
);
|
||||||
|
|
||||||
|
$rectorConfig->sets([
|
||||||
|
DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES,
|
||||||
|
SymfonySetList::ANNOTATIONS_TO_ATTRIBUTES,
|
||||||
|
NetteSetList::ANNOTATIONS_TO_ATTRIBUTES,
|
||||||
|
SensiolabsSetList::FRAMEWORK_EXTRA_61,
|
||||||
|
SymfonySetList::SYMFONY_60,
|
||||||
|
LevelSetList::UP_TO_PHP_81
|
||||||
|
]);
|
||||||
|
|
||||||
|
// register a single rule
|
||||||
|
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
|
||||||
|
$rectorConfig->rule(AddReturnTypeDeclarationRector::class);
|
||||||
|
$rectorConfig->rules([
|
||||||
|
AddVoidReturnTypeWhereNoReturnRector::class,
|
||||||
|
ArrayShapeFromConstantArrayReturnRector::class,
|
||||||
|
ParamTypeByMethodCallTypeRector::class,
|
||||||
|
ParamTypeByParentCallTypeRector::class,
|
||||||
|
PropertyTypeDeclarationRector::class,
|
||||||
|
ReturnTypeFromReturnNewRector::class,
|
||||||
|
// ReturnTypeFromStrictBoolReturnExprRector::class,
|
||||||
|
// ReturnTypeFromStrictNativeFuncCallRector::class,
|
||||||
|
// ReturnTypeFromStrictNewArrayRector::class,
|
||||||
|
TypedPropertyFromAssignsRector::class
|
||||||
|
]);
|
||||||
|
|
||||||
|
// define sets of rules
|
||||||
|
// $rectorConfig->sets([
|
||||||
|
// LevelSetList::UP_TO_PHP_80
|
||||||
|
// ]);
|
||||||
|
};
|
||||||
|
|
@ -7,12 +7,18 @@ use Zxing\QrReader;
|
||||||
|
|
||||||
class QrReaderTest extends TestCase
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../lib/common/customFunctions.php';
|
|
||||||
|
|
||||||
spl_autoload_register(function (string $class) {
|
|
||||||
$path = explode('\\', $class);
|
|
||||||
$className = array_pop($path);
|
|
||||||
array_shift($path);
|
|
||||||
$path = strtolower(implode('/', $path));
|
|
||||||
$path = __DIR__ . '/../lib/' . $path . '/' . $className . '.php';
|
|
||||||
|
|
||||||
if (file_exists($path)) {
|
|
||||||
require_once $path;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
BIN
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/tests/qrcodes/empty.png
vendored
Normal file
BIN
htdocs/core/modules/facture/doc/vendor/khanamiryan/qrcode-detector-decoder/tests/qrcodes/empty.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -1,9 +1,9 @@
|
||||||
# PHP Enum implementation inspired from SplEnum
|
# PHP Enum implementation inspired from SplEnum
|
||||||
|
|
||||||
[](https://travis-ci.org/myclabs/php-enum)
|
[![GitHub Actions][GA Image]][GA Link]
|
||||||
[](https://packagist.org/packages/myclabs/php-enum)
|
[](https://packagist.org/packages/myclabs/php-enum)
|
||||||
[](https://packagist.org/packages/myclabs/php-enum)
|
[](https://packagist.org/packages/myclabs/php-enum)
|
||||||
[](https://shepherd.dev/github/myclabs/php-enum)
|
[![Psalm Shepherd][Shepherd Image]][Shepherd Link]
|
||||||
|
|
||||||
Maintenance for this project is [supported via Tidelift](https://tidelift.com/subscription/pkg/packagist-myclabs-php-enum?utm_source=packagist-myclabs-php-enum&utm_medium=referral&utm_campaign=readme).
|
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
|
||||||
|
|
|
||||||
|
|
@ -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": {
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<psalm
|
|
||||||
totallyTyped="true"
|
|
||||||
resolveFromConfigFile="true"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xmlns="https://getpsalm.org/schema/config"
|
|
||||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
|
||||||
>
|
|
||||||
<projectFiles>
|
|
||||||
<directory name="src" />
|
|
||||||
<directory name="static-analysis" />
|
|
||||||
<ignoreFiles>
|
|
||||||
<directory name="vendor" />
|
|
||||||
<directory name="src/PHPUnit" />
|
|
||||||
</ignoreFiles>
|
|
||||||
</projectFiles>
|
|
||||||
|
|
||||||
<issueHandlers>
|
|
||||||
<MixedAssignment errorLevel="info" />
|
|
||||||
|
|
||||||
<ImpureStaticProperty>
|
|
||||||
<!-- self::$... usages in Enum are used to populate an internal cache, and cause no side-effects -->
|
|
||||||
<errorLevel type="suppress">
|
|
||||||
<file name="src/Enum.php"/>
|
|
||||||
</errorLevel>
|
|
||||||
</ImpureStaticProperty>
|
|
||||||
|
|
||||||
<ImpureVariable>
|
|
||||||
<!-- $this usages in Enum point themselves to an immutable instance -->
|
|
||||||
<errorLevel type="suppress">
|
|
||||||
<file name="src/Enum.php"/>
|
|
||||||
</errorLevel>
|
|
||||||
</ImpureVariable>
|
|
||||||
</issueHandlers>
|
|
||||||
</psalm>
|
|
||||||
|
|
@ -19,7 +19,7 @@ namespace MyCLabs\Enum;
|
||||||
* @psalm-immutable
|
* @psalm-immutable
|
||||||
* @psalm-consistent-constructor
|
* @psalm-consistent-constructor
|
||||||
*/
|
*/
|
||||||
abstract class Enum implements \JsonSerializable
|
abstract class Enum implements \JsonSerializable, \Stringable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Enum value
|
* Enum value
|
||||||
|
|
|
||||||
11
htdocs/core/modules/facture/doc/vendor/myclabs/php-enum/stubs/Stringable.php
vendored
Normal file
11
htdocs/core/modules/facture/doc/vendor/myclabs/php-enum/stubs/Stringable.php
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if (\PHP_VERSION_ID < 80000 && !interface_exists('Stringable')) {
|
||||||
|
interface Stringable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -59,7 +59,7 @@ class TimezoneTransformer extends Transformer
|
||||||
return $dateTime->format('\G\M\TP');
|
return $dateTime->format('\G\M\TP');
|
||||||
}
|
}
|
||||||
|
|
||||||
return sprintf('GMT%s%d', ($offset >= 0 ? '+' : ''), $offset / 100);
|
return sprintf('GMT%s%d', $offset >= 0 ? '+' : '', $offset / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ abstract class IntlDateFormatter
|
||||||
*
|
*
|
||||||
* @throws MethodNotImplementedException
|
* @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__);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ final class Intl
|
||||||
*/
|
*/
|
||||||
public static function getIcuStubVersion(): string
|
public static function getIcuStubVersion(): string
|
||||||
{
|
{
|
||||||
return '71.1';
|
return '72.1';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
42
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/bn_IN.php
vendored
Normal file
42
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/bn_IN.php
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Names' => [
|
||||||
|
'ANG' => [
|
||||||
|
0 => 'ANG',
|
||||||
|
1 => 'নেদারল্যান্ডস অ্যান্টিলিয়ান গিল্ডার',
|
||||||
|
],
|
||||||
|
'AWG' => [
|
||||||
|
0 => 'AWG',
|
||||||
|
1 => 'আরুবান গিল্ডার',
|
||||||
|
],
|
||||||
|
'BMD' => [
|
||||||
|
0 => 'BMD',
|
||||||
|
1 => 'বারমুডান ডলার',
|
||||||
|
],
|
||||||
|
'GTQ' => [
|
||||||
|
0 => 'GTQ',
|
||||||
|
1 => 'গুয়াতেমালান কেৎসাল',
|
||||||
|
],
|
||||||
|
'HNL' => [
|
||||||
|
0 => 'HNL',
|
||||||
|
1 => 'হন্ডুরান লেম্পিরা',
|
||||||
|
],
|
||||||
|
'HTG' => [
|
||||||
|
0 => 'HTG',
|
||||||
|
1 => 'হাইতিয়ান গুর্দ',
|
||||||
|
],
|
||||||
|
'MXN' => [
|
||||||
|
0 => 'MX$',
|
||||||
|
1 => 'মেক্সিকান পেসো',
|
||||||
|
],
|
||||||
|
'USD' => [
|
||||||
|
0 => '$',
|
||||||
|
1 => 'মার্কিন ডলার',
|
||||||
|
],
|
||||||
|
'XCD' => [
|
||||||
|
0 => 'EC$',
|
||||||
|
1 => 'পূর্ব ক্যারিবিয়ান ডলার',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
630
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/cv.php
vendored
Normal file
630
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/cv.php
vendored
Normal file
|
|
@ -0,0 +1,630 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Names' => [
|
||||||
|
'AED' => [
|
||||||
|
0 => 'AED',
|
||||||
|
1 => 'АПЭ дирхамӗ',
|
||||||
|
],
|
||||||
|
'AFN' => [
|
||||||
|
0 => 'AFN',
|
||||||
|
1 => 'афганийӗ',
|
||||||
|
],
|
||||||
|
'ALL' => [
|
||||||
|
0 => 'ALL',
|
||||||
|
1 => 'Албани лекӗ',
|
||||||
|
],
|
||||||
|
'AMD' => [
|
||||||
|
0 => 'AMD',
|
||||||
|
1 => 'Армяни драмӗ',
|
||||||
|
],
|
||||||
|
'ANG' => [
|
||||||
|
0 => 'ANG',
|
||||||
|
1 => 'Нидерланд Антиллиан гульденӗ',
|
||||||
|
],
|
||||||
|
'AOA' => [
|
||||||
|
0 => 'AOA',
|
||||||
|
1 => 'Ангола кванзӗ',
|
||||||
|
],
|
||||||
|
'ARS' => [
|
||||||
|
0 => 'ARS',
|
||||||
|
1 => 'Аргентина песийӗ',
|
||||||
|
],
|
||||||
|
'AUD' => [
|
||||||
|
0 => 'A$',
|
||||||
|
1 => 'Австрали долларӗ',
|
||||||
|
],
|
||||||
|
'AWG' => [
|
||||||
|
0 => 'AWG',
|
||||||
|
1 => 'Аруба флоринӗ',
|
||||||
|
],
|
||||||
|
'AZN' => [
|
||||||
|
0 => 'AZN',
|
||||||
|
1 => 'Азербайджан маначӗ',
|
||||||
|
],
|
||||||
|
'BAM' => [
|
||||||
|
0 => 'BAM',
|
||||||
|
1 => 'Боснипе Герцеговина конвертланакан марки',
|
||||||
|
],
|
||||||
|
'BBD' => [
|
||||||
|
0 => 'BBD',
|
||||||
|
1 => 'Барбадос долларӗ',
|
||||||
|
],
|
||||||
|
'BDT' => [
|
||||||
|
0 => 'BDT',
|
||||||
|
1 => 'Бангладеш таки',
|
||||||
|
],
|
||||||
|
'BGN' => [
|
||||||
|
0 => 'BGN',
|
||||||
|
1 => 'Болгари левӗ',
|
||||||
|
],
|
||||||
|
'BHD' => [
|
||||||
|
0 => 'BHD',
|
||||||
|
1 => 'Бахрейн динарӗ',
|
||||||
|
],
|
||||||
|
'BIF' => [
|
||||||
|
0 => 'BIF',
|
||||||
|
1 => 'Бурунди франкӗ',
|
||||||
|
],
|
||||||
|
'BMD' => [
|
||||||
|
0 => 'BMD',
|
||||||
|
1 => 'Бермуд долларӗ',
|
||||||
|
],
|
||||||
|
'BND' => [
|
||||||
|
0 => 'BND',
|
||||||
|
1 => 'Бруней долларӗ',
|
||||||
|
],
|
||||||
|
'BOB' => [
|
||||||
|
0 => 'BOB',
|
||||||
|
1 => 'Боливи боливианӗ',
|
||||||
|
],
|
||||||
|
'BRL' => [
|
||||||
|
0 => 'R$',
|
||||||
|
1 => 'Бразили реалӗ',
|
||||||
|
],
|
||||||
|
'BSD' => [
|
||||||
|
0 => 'BSD',
|
||||||
|
1 => 'Багам долларӗ',
|
||||||
|
],
|
||||||
|
'BTN' => [
|
||||||
|
0 => 'BTN',
|
||||||
|
1 => 'Бутан нгултрумӗ',
|
||||||
|
],
|
||||||
|
'BWP' => [
|
||||||
|
0 => 'BWP',
|
||||||
|
1 => 'Ботсвана пули',
|
||||||
|
],
|
||||||
|
'BYN' => [
|
||||||
|
0 => 'BYN',
|
||||||
|
1 => 'Беларуҫ тенкӗ',
|
||||||
|
],
|
||||||
|
'BZD' => [
|
||||||
|
0 => 'BZD',
|
||||||
|
1 => 'Белиз долларӗ',
|
||||||
|
],
|
||||||
|
'CAD' => [
|
||||||
|
0 => 'CA$',
|
||||||
|
1 => 'Канада долларӗ',
|
||||||
|
],
|
||||||
|
'CDF' => [
|
||||||
|
0 => 'CDF',
|
||||||
|
1 => 'Конголези франкӗ',
|
||||||
|
],
|
||||||
|
'CHF' => [
|
||||||
|
0 => 'CHF',
|
||||||
|
1 => 'Швейцари франкӗ',
|
||||||
|
],
|
||||||
|
'CLP' => [
|
||||||
|
0 => 'CLP',
|
||||||
|
1 => 'Чили песийӗ',
|
||||||
|
],
|
||||||
|
'CNH' => [
|
||||||
|
0 => 'CNH',
|
||||||
|
1 => 'Китай офшор юанӗ',
|
||||||
|
],
|
||||||
|
'CNY' => [
|
||||||
|
0 => 'CN¥',
|
||||||
|
1 => 'Китай юанӗ',
|
||||||
|
],
|
||||||
|
'COP' => [
|
||||||
|
0 => 'COP',
|
||||||
|
1 => 'Колумби песийӗ',
|
||||||
|
],
|
||||||
|
'CRC' => [
|
||||||
|
0 => 'CRC',
|
||||||
|
1 => 'Коста-Рика колонӗ',
|
||||||
|
],
|
||||||
|
'CUC' => [
|
||||||
|
0 => 'CUC',
|
||||||
|
1 => 'Куба конвертланакан песийӗ',
|
||||||
|
],
|
||||||
|
'CUP' => [
|
||||||
|
0 => 'CUP',
|
||||||
|
1 => 'Куба песийӗ',
|
||||||
|
],
|
||||||
|
'CVE' => [
|
||||||
|
0 => 'CVE',
|
||||||
|
1 => 'Кабо-Верде эскудӗ',
|
||||||
|
],
|
||||||
|
'CZK' => [
|
||||||
|
0 => 'CZK',
|
||||||
|
1 => 'Чехи кронӗ',
|
||||||
|
],
|
||||||
|
'DJF' => [
|
||||||
|
0 => 'DJF',
|
||||||
|
1 => 'Джибути франкӗ',
|
||||||
|
],
|
||||||
|
'DKK' => [
|
||||||
|
0 => 'DKK',
|
||||||
|
1 => 'Дани кронӗ',
|
||||||
|
],
|
||||||
|
'DOP' => [
|
||||||
|
0 => 'DOP',
|
||||||
|
1 => 'Доминикан песийӗ',
|
||||||
|
],
|
||||||
|
'DZD' => [
|
||||||
|
0 => 'DZD',
|
||||||
|
1 => 'Алжир динарӗ',
|
||||||
|
],
|
||||||
|
'EGP' => [
|
||||||
|
0 => 'EGP',
|
||||||
|
1 => 'Египет фунчӗ',
|
||||||
|
],
|
||||||
|
'ERN' => [
|
||||||
|
0 => 'ERN',
|
||||||
|
1 => 'Эритрей накфӗ',
|
||||||
|
],
|
||||||
|
'ETB' => [
|
||||||
|
0 => 'ETB',
|
||||||
|
1 => 'Эфиопи бырӗ',
|
||||||
|
],
|
||||||
|
'EUR' => [
|
||||||
|
0 => '€',
|
||||||
|
1 => 'евро',
|
||||||
|
],
|
||||||
|
'FJD' => [
|
||||||
|
0 => 'FJD',
|
||||||
|
1 => 'Фиджи долларӗ',
|
||||||
|
],
|
||||||
|
'FKP' => [
|
||||||
|
0 => 'FKP',
|
||||||
|
1 => 'Факланд утравӗсен фунчӗ',
|
||||||
|
],
|
||||||
|
'GBP' => [
|
||||||
|
0 => '£',
|
||||||
|
1 => 'Британи фунчӗ',
|
||||||
|
],
|
||||||
|
'GEL' => [
|
||||||
|
0 => 'GEL',
|
||||||
|
1 => 'Грузи ларийӗ',
|
||||||
|
],
|
||||||
|
'GHS' => [
|
||||||
|
0 => 'GHS',
|
||||||
|
1 => 'Гана седийӗ',
|
||||||
|
],
|
||||||
|
'GIP' => [
|
||||||
|
0 => 'GIP',
|
||||||
|
1 => 'Гибралтар фунчӗ',
|
||||||
|
],
|
||||||
|
'GMD' => [
|
||||||
|
0 => 'GMD',
|
||||||
|
1 => 'Гамби даласийӗ',
|
||||||
|
],
|
||||||
|
'GNF' => [
|
||||||
|
0 => 'GNF',
|
||||||
|
1 => 'Гвиней франкӗ',
|
||||||
|
],
|
||||||
|
'GTQ' => [
|
||||||
|
0 => 'GTQ',
|
||||||
|
1 => 'Гватемала кетсалӗ',
|
||||||
|
],
|
||||||
|
'GYD' => [
|
||||||
|
0 => 'GYD',
|
||||||
|
1 => 'Гайана долларӗ',
|
||||||
|
],
|
||||||
|
'HKD' => [
|
||||||
|
0 => 'HK$',
|
||||||
|
1 => 'Гонконг долларӗ',
|
||||||
|
],
|
||||||
|
'HNL' => [
|
||||||
|
0 => 'HNL',
|
||||||
|
1 => 'Гондурас лемпирӗ',
|
||||||
|
],
|
||||||
|
'HRK' => [
|
||||||
|
0 => 'HRK',
|
||||||
|
1 => 'Хорвати куни',
|
||||||
|
],
|
||||||
|
'HTG' => [
|
||||||
|
0 => 'HTG',
|
||||||
|
1 => 'Гаити гурдӗ',
|
||||||
|
],
|
||||||
|
'HUF' => [
|
||||||
|
0 => 'HUF',
|
||||||
|
1 => 'Венгри форинчӗ',
|
||||||
|
],
|
||||||
|
'IDR' => [
|
||||||
|
0 => 'IDR',
|
||||||
|
1 => 'Индонези рупийӗ',
|
||||||
|
],
|
||||||
|
'ILS' => [
|
||||||
|
0 => '₪',
|
||||||
|
1 => 'Ҫӗнӗ Израиль шекелӗ',
|
||||||
|
],
|
||||||
|
'INR' => [
|
||||||
|
0 => '₹',
|
||||||
|
1 => 'Инди рупийӗ',
|
||||||
|
],
|
||||||
|
'IQD' => [
|
||||||
|
0 => 'IQD',
|
||||||
|
1 => 'Ирак динарӗ',
|
||||||
|
],
|
||||||
|
'IRR' => [
|
||||||
|
0 => 'IRR',
|
||||||
|
1 => 'Иран риалӗ',
|
||||||
|
],
|
||||||
|
'ISK' => [
|
||||||
|
0 => 'ISK',
|
||||||
|
1 => 'Исланди кронӗ',
|
||||||
|
],
|
||||||
|
'JMD' => [
|
||||||
|
0 => 'JMD',
|
||||||
|
1 => 'Ямайка долларӗ',
|
||||||
|
],
|
||||||
|
'JOD' => [
|
||||||
|
0 => 'JOD',
|
||||||
|
1 => 'Иордан динарӗ',
|
||||||
|
],
|
||||||
|
'JPY' => [
|
||||||
|
0 => 'JP¥',
|
||||||
|
1 => 'Япони иени',
|
||||||
|
],
|
||||||
|
'KES' => [
|
||||||
|
0 => 'KES',
|
||||||
|
1 => 'Кени шиллингӗ',
|
||||||
|
],
|
||||||
|
'KGS' => [
|
||||||
|
0 => 'KGS',
|
||||||
|
1 => 'Киргиз сомӗ',
|
||||||
|
],
|
||||||
|
'KHR' => [
|
||||||
|
0 => 'KHR',
|
||||||
|
1 => 'Камбоджа риелӗ',
|
||||||
|
],
|
||||||
|
'KMF' => [
|
||||||
|
0 => 'KMF',
|
||||||
|
1 => 'Комора франкӗ',
|
||||||
|
],
|
||||||
|
'KPW' => [
|
||||||
|
0 => 'KPW',
|
||||||
|
1 => 'КХДР вони',
|
||||||
|
],
|
||||||
|
'KRW' => [
|
||||||
|
0 => '₩',
|
||||||
|
1 => 'Корей вони',
|
||||||
|
],
|
||||||
|
'KWD' => [
|
||||||
|
0 => 'KWD',
|
||||||
|
1 => 'Кувейт динарӗ',
|
||||||
|
],
|
||||||
|
'KYD' => [
|
||||||
|
0 => 'KYD',
|
||||||
|
1 => 'Кайман утравӗсен долларӗ',
|
||||||
|
],
|
||||||
|
'KZT' => [
|
||||||
|
0 => 'KZT',
|
||||||
|
1 => 'Казах тенгейӗ',
|
||||||
|
],
|
||||||
|
'LAK' => [
|
||||||
|
0 => 'LAK',
|
||||||
|
1 => 'Лаос кипӗ',
|
||||||
|
],
|
||||||
|
'LBP' => [
|
||||||
|
0 => 'LBP',
|
||||||
|
1 => 'Ливан фунчӗ',
|
||||||
|
],
|
||||||
|
'LKR' => [
|
||||||
|
0 => 'LKR',
|
||||||
|
1 => 'Шри-ланка рупийӗ',
|
||||||
|
],
|
||||||
|
'LRD' => [
|
||||||
|
0 => 'LRD',
|
||||||
|
1 => 'Либери долларӗ',
|
||||||
|
],
|
||||||
|
'LSL' => [
|
||||||
|
0 => 'LSL',
|
||||||
|
1 => 'Лесото лотийӗ',
|
||||||
|
],
|
||||||
|
'LYD' => [
|
||||||
|
0 => 'LYD',
|
||||||
|
1 => 'Ливи динарӗ',
|
||||||
|
],
|
||||||
|
'MAD' => [
|
||||||
|
0 => 'MAD',
|
||||||
|
1 => 'Марокко дирхамӗ',
|
||||||
|
],
|
||||||
|
'MDL' => [
|
||||||
|
0 => 'MDL',
|
||||||
|
1 => 'Молдова лайӗ',
|
||||||
|
],
|
||||||
|
'MGA' => [
|
||||||
|
0 => 'MGA',
|
||||||
|
1 => 'Малагаси ариарийӗ',
|
||||||
|
],
|
||||||
|
'MKD' => [
|
||||||
|
0 => 'MKD',
|
||||||
|
1 => 'Македони денарӗ',
|
||||||
|
],
|
||||||
|
'MMK' => [
|
||||||
|
0 => 'MMK',
|
||||||
|
1 => 'Мьянман кьятӗ',
|
||||||
|
],
|
||||||
|
'MNT' => [
|
||||||
|
0 => 'MNT',
|
||||||
|
1 => 'Монголи тугрикӗ',
|
||||||
|
],
|
||||||
|
'MOP' => [
|
||||||
|
0 => 'MOP',
|
||||||
|
1 => 'Макао патаки',
|
||||||
|
],
|
||||||
|
'MRU' => [
|
||||||
|
0 => 'MRU',
|
||||||
|
1 => 'Мавритани угийӗ',
|
||||||
|
],
|
||||||
|
'MUR' => [
|
||||||
|
0 => 'MUR',
|
||||||
|
1 => 'Маврики рупийӗ',
|
||||||
|
],
|
||||||
|
'MVR' => [
|
||||||
|
0 => 'MVR',
|
||||||
|
1 => 'Мальдивсен руфийӗ',
|
||||||
|
],
|
||||||
|
'MWK' => [
|
||||||
|
0 => 'MWK',
|
||||||
|
1 => 'Малави квачӗ',
|
||||||
|
],
|
||||||
|
'MXN' => [
|
||||||
|
0 => 'MX$',
|
||||||
|
1 => 'Мексика песийӗ',
|
||||||
|
],
|
||||||
|
'MYR' => [
|
||||||
|
0 => 'MYR',
|
||||||
|
1 => 'Малайзи ринггичӗ',
|
||||||
|
],
|
||||||
|
'MZN' => [
|
||||||
|
0 => 'MZN',
|
||||||
|
1 => 'Мозамбик метикалӗ',
|
||||||
|
],
|
||||||
|
'NAD' => [
|
||||||
|
0 => 'NAD',
|
||||||
|
1 => 'Намиби долларӗ',
|
||||||
|
],
|
||||||
|
'NGN' => [
|
||||||
|
0 => 'NGN',
|
||||||
|
1 => 'Нигери найрӗ',
|
||||||
|
],
|
||||||
|
'NIO' => [
|
||||||
|
0 => 'NIO',
|
||||||
|
1 => 'Никарагуа кордобӗ',
|
||||||
|
],
|
||||||
|
'NOK' => [
|
||||||
|
0 => 'NOK',
|
||||||
|
1 => 'Норвеги кронӗ',
|
||||||
|
],
|
||||||
|
'NPR' => [
|
||||||
|
0 => 'NPR',
|
||||||
|
1 => 'Непал рупийӗ',
|
||||||
|
],
|
||||||
|
'NZD' => [
|
||||||
|
0 => 'NZ$',
|
||||||
|
1 => 'Ҫӗнӗ Зеланди долларӗ',
|
||||||
|
],
|
||||||
|
'OMR' => [
|
||||||
|
0 => 'OMR',
|
||||||
|
1 => 'Оман риалӗ',
|
||||||
|
],
|
||||||
|
'PAB' => [
|
||||||
|
0 => 'PAB',
|
||||||
|
1 => 'Панама бальбоа',
|
||||||
|
],
|
||||||
|
'PEN' => [
|
||||||
|
0 => 'PEN',
|
||||||
|
1 => 'Перу солӗ',
|
||||||
|
],
|
||||||
|
'PGK' => [
|
||||||
|
0 => 'PGK',
|
||||||
|
1 => 'Папуа – Ҫӗнӗ Гвиней кини',
|
||||||
|
],
|
||||||
|
'PHP' => [
|
||||||
|
0 => '₱',
|
||||||
|
1 => 'Филиппин песийӗ',
|
||||||
|
],
|
||||||
|
'PKR' => [
|
||||||
|
0 => 'PKR',
|
||||||
|
1 => 'пакистан рупийӗ',
|
||||||
|
],
|
||||||
|
'PLN' => [
|
||||||
|
0 => 'PLN',
|
||||||
|
1 => 'Польша злотыйӗ',
|
||||||
|
],
|
||||||
|
'PYG' => [
|
||||||
|
0 => 'PYG',
|
||||||
|
1 => 'Парагвай гуаранӗ',
|
||||||
|
],
|
||||||
|
'QAR' => [
|
||||||
|
0 => 'QAR',
|
||||||
|
1 => 'Катар риалӗ',
|
||||||
|
],
|
||||||
|
'RON' => [
|
||||||
|
0 => 'RON',
|
||||||
|
1 => 'Румыни лейӗ',
|
||||||
|
],
|
||||||
|
'RSD' => [
|
||||||
|
0 => 'RSD',
|
||||||
|
1 => 'Серби динарӗ',
|
||||||
|
],
|
||||||
|
'RUB' => [
|
||||||
|
0 => '₽',
|
||||||
|
1 => 'Раҫҫей тенкӗ',
|
||||||
|
],
|
||||||
|
'RWF' => [
|
||||||
|
0 => 'RWF',
|
||||||
|
1 => 'Руанда франкӗ',
|
||||||
|
],
|
||||||
|
'SAR' => [
|
||||||
|
0 => 'SAR',
|
||||||
|
1 => 'Сауд риялӗ',
|
||||||
|
],
|
||||||
|
'SBD' => [
|
||||||
|
0 => 'SBD',
|
||||||
|
1 => 'Соломон утравӗсен долларӗ',
|
||||||
|
],
|
||||||
|
'SCR' => [
|
||||||
|
0 => 'SCR',
|
||||||
|
1 => 'Сейшел рупийӗ',
|
||||||
|
],
|
||||||
|
'SDG' => [
|
||||||
|
0 => 'SDG',
|
||||||
|
1 => 'Судан фунчӗ',
|
||||||
|
],
|
||||||
|
'SEK' => [
|
||||||
|
0 => 'SEK',
|
||||||
|
1 => 'Швеци кронӗ',
|
||||||
|
],
|
||||||
|
'SGD' => [
|
||||||
|
0 => 'SGD',
|
||||||
|
1 => 'Сингапур долларӗ',
|
||||||
|
],
|
||||||
|
'SHP' => [
|
||||||
|
0 => 'SHP',
|
||||||
|
1 => 'Сӑваплӑ Елена утравӗн фунчӗ',
|
||||||
|
],
|
||||||
|
'SLL' => [
|
||||||
|
0 => 'SLL',
|
||||||
|
1 => 'леонӗ',
|
||||||
|
],
|
||||||
|
'SOS' => [
|
||||||
|
0 => 'SOS',
|
||||||
|
1 => 'Сомали шиллингӗ',
|
||||||
|
],
|
||||||
|
'SRD' => [
|
||||||
|
0 => 'SRD',
|
||||||
|
1 => 'Суринам долларӗ',
|
||||||
|
],
|
||||||
|
'SSP' => [
|
||||||
|
0 => 'SSP',
|
||||||
|
1 => 'Кӑнтӑр Судан фунчӗ',
|
||||||
|
],
|
||||||
|
'STN' => [
|
||||||
|
0 => 'STN',
|
||||||
|
1 => 'Сан-Томе тата Принсипи добрӗ',
|
||||||
|
],
|
||||||
|
'SYP' => [
|
||||||
|
0 => 'SYP',
|
||||||
|
1 => 'Сири фунчӗ',
|
||||||
|
],
|
||||||
|
'SZL' => [
|
||||||
|
0 => 'SZL',
|
||||||
|
1 => 'Свази лилангенийӗ',
|
||||||
|
],
|
||||||
|
'THB' => [
|
||||||
|
0 => 'THB',
|
||||||
|
1 => 'Таиланд барӗ',
|
||||||
|
],
|
||||||
|
'TJS' => [
|
||||||
|
0 => 'TJS',
|
||||||
|
1 => 'Таджик сомонийӗ',
|
||||||
|
],
|
||||||
|
'TMT' => [
|
||||||
|
0 => 'TMT',
|
||||||
|
1 => 'Туркмен маначӗ',
|
||||||
|
],
|
||||||
|
'TND' => [
|
||||||
|
0 => 'TND',
|
||||||
|
1 => 'Тунези динарӗ',
|
||||||
|
],
|
||||||
|
'TOP' => [
|
||||||
|
0 => 'TOP',
|
||||||
|
1 => 'Тонган паанги',
|
||||||
|
],
|
||||||
|
'TRY' => [
|
||||||
|
0 => 'TRY',
|
||||||
|
1 => 'Турци лири',
|
||||||
|
],
|
||||||
|
'TTD' => [
|
||||||
|
0 => 'TTD',
|
||||||
|
1 => 'Тринидад тата Тобаго долларӗ',
|
||||||
|
],
|
||||||
|
'TWD' => [
|
||||||
|
0 => 'NT$',
|
||||||
|
1 => 'Ҫӗнӗ Тайван долларӗ',
|
||||||
|
],
|
||||||
|
'TZS' => [
|
||||||
|
0 => 'TZS',
|
||||||
|
1 => 'Танзани шиллингӗ',
|
||||||
|
],
|
||||||
|
'UAH' => [
|
||||||
|
0 => 'UAH',
|
||||||
|
1 => 'Украина гривни',
|
||||||
|
],
|
||||||
|
'UGX' => [
|
||||||
|
0 => 'UGX',
|
||||||
|
1 => 'Уганда шиллингӗ',
|
||||||
|
],
|
||||||
|
'USD' => [
|
||||||
|
0 => '$',
|
||||||
|
1 => 'АПШ долларӗ',
|
||||||
|
],
|
||||||
|
'UYU' => [
|
||||||
|
0 => 'UYU',
|
||||||
|
1 => 'Уругвай песийӗ',
|
||||||
|
],
|
||||||
|
'UZS' => [
|
||||||
|
0 => 'UZS',
|
||||||
|
1 => 'Узбек сумӗ',
|
||||||
|
],
|
||||||
|
'VES' => [
|
||||||
|
0 => 'VES',
|
||||||
|
1 => 'Венесуэла боливарӗ',
|
||||||
|
],
|
||||||
|
'VND' => [
|
||||||
|
0 => '₫',
|
||||||
|
1 => 'Вьетнам донгӗ',
|
||||||
|
],
|
||||||
|
'VUV' => [
|
||||||
|
0 => 'VUV',
|
||||||
|
1 => 'Вануату ватуйӗ',
|
||||||
|
],
|
||||||
|
'WST' => [
|
||||||
|
0 => 'WST',
|
||||||
|
1 => 'Самоа тали',
|
||||||
|
],
|
||||||
|
'XAF' => [
|
||||||
|
0 => 'FCFA',
|
||||||
|
1 => 'Тӗп Африка КФА франкӗ',
|
||||||
|
],
|
||||||
|
'XCD' => [
|
||||||
|
0 => 'EC$',
|
||||||
|
1 => 'Хӗвелтухӑҫ Карибсем долларӗ',
|
||||||
|
],
|
||||||
|
'XOF' => [
|
||||||
|
0 => 'F CFA',
|
||||||
|
1 => 'КФА ВСЕАО франкӗ',
|
||||||
|
],
|
||||||
|
'XPF' => [
|
||||||
|
0 => 'CFPF',
|
||||||
|
1 => 'Франци Лӑпкӑ океан франкӗ',
|
||||||
|
],
|
||||||
|
'YER' => [
|
||||||
|
0 => 'YER',
|
||||||
|
1 => 'Йемен риалӗ',
|
||||||
|
],
|
||||||
|
'ZAR' => [
|
||||||
|
0 => 'ZAR',
|
||||||
|
1 => 'Кӑнтӑр Африка рэндӗ',
|
||||||
|
],
|
||||||
|
'ZMW' => [
|
||||||
|
0 => 'ZMW',
|
||||||
|
1 => 'Замби квачи',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
@ -824,7 +824,7 @@ return [
|
||||||
],
|
],
|
||||||
'QAR' => [
|
'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',
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'Names' => [
|
'Names' => [
|
||||||
'SLL' => [
|
'SLE' => [
|
||||||
0 => 'Le',
|
0 => 'Le',
|
||||||
1 => 'Sierra Leonean Leone',
|
1 => 'Sierra Leonean Leone',
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -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 (1871–2008)',
|
1 => 'Venezuelako bolivarra (1871–2008)',
|
||||||
],
|
],
|
||||||
|
'VED' => [
|
||||||
|
0 => 'VED',
|
||||||
|
1 => 'bolivar subiraua',
|
||||||
|
],
|
||||||
'VEF' => [
|
'VEF' => [
|
||||||
0 => 'VEF',
|
0 => 'VEF',
|
||||||
1 => 'Venezuelako bolivarra (2008–2018)',
|
1 => 'Venezuelako bolivarra (2008–2018)',
|
||||||
|
|
|
||||||
|
|
@ -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 => '𞤄𞤮𞤤𞤭𞤾𞤢𞥄𞤪 𞤜𞤫𞤲𞤭𞥅𞤧𞤫𞤤𞤢𞤲𞤳𞤮 (𞥒𞥐𞥐𞥘 - 𞥒𞥐𞥑𞥘)',
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ return [
|
||||||
0 => 'GNF',
|
0 => 'GNF',
|
||||||
1 => '𞤊𞤢𞤪𞤢𞤲 𞤘𞤭𞤲𞤫𞤲𞤳𞤮',
|
1 => '𞤊𞤢𞤪𞤢𞤲 𞤘𞤭𞤲𞤫𞤲𞤳𞤮',
|
||||||
],
|
],
|
||||||
'SLL' => [
|
'SLE' => [
|
||||||
0 => 'Le',
|
0 => 'Le',
|
||||||
1 => '𞤂𞤫𞤴𞤮𞤲 𞤅𞤫𞤪𞤢𞤤𞤭𞤴𞤢𞤲𞤳𞤮',
|
1 => 'SLE',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'Names' => [
|
'Names' => [
|
||||||
'SLL' => [
|
'SLE' => [
|
||||||
0 => 'Le',
|
0 => 'Le',
|
||||||
1 => 'Lewoon Seraa Liyon',
|
1 => 'SLE',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -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 (1871–2008)',
|
1 => 'Bolívar Bheinisealach (1871–2008)',
|
||||||
],
|
],
|
||||||
|
'VED' => [
|
||||||
|
0 => 'VED',
|
||||||
|
1 => 'Bolívar Soberano',
|
||||||
|
],
|
||||||
'VEF' => [
|
'VEF' => [
|
||||||
0 => 'VEF',
|
0 => 'VEF',
|
||||||
1 => 'Bolívar Bheinisealach (2008–2018)',
|
1 => 'Bolívar Bheinisealach (2008–2018)',
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -600,7 +600,7 @@ return [
|
||||||
],
|
],
|
||||||
'PHP' => [
|
'PHP' => [
|
||||||
0 => 'PHP',
|
0 => 'PHP',
|
||||||
1 => 'פזו פיליפיני',
|
1 => 'פסו פיליפיני',
|
||||||
],
|
],
|
||||||
'PKR' => [
|
'PKR' => [
|
||||||
0 => 'PKR',
|
0 => 'PKR',
|
||||||
|
|
|
||||||
30
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/hi_Latn.php
vendored
Normal file
30
htdocs/core/modules/facture/doc/vendor/symfony/intl/Resources/data/currencies/hi_Latn.php
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Names' => [
|
||||||
|
'CRC' => [
|
||||||
|
0 => 'CRC',
|
||||||
|
1 => 'Costa Rican colon',
|
||||||
|
],
|
||||||
|
'ISK' => [
|
||||||
|
0 => 'ISK',
|
||||||
|
1 => 'Icelandic krona',
|
||||||
|
],
|
||||||
|
'NIO' => [
|
||||||
|
0 => 'NIO',
|
||||||
|
1 => 'Nicaraguan cordoba',
|
||||||
|
],
|
||||||
|
'RUB' => [
|
||||||
|
0 => 'RUB',
|
||||||
|
1 => 'Russian ruble',
|
||||||
|
],
|
||||||
|
'STN' => [
|
||||||
|
0 => 'STN',
|
||||||
|
1 => 'Sao Tome & Principe Dobra',
|
||||||
|
],
|
||||||
|
'VES' => [
|
||||||
|
0 => 'VES',
|
||||||
|
1 => 'Venezuelan bolivar',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
@ -375,7 +375,7 @@ return [
|
||||||
1 => 'etiopski bir',
|
1 => 'etiopski bir',
|
||||||
],
|
],
|
||||||
'EUR' => [
|
'EUR' => [
|
||||||
0 => 'EUR',
|
0 => '€',
|
||||||
1 => 'euro',
|
1 => 'euro',
|
||||||
],
|
],
|
||||||
'FIM' => [
|
'FIM' => [
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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
Loading…
Reference in New Issue