v1.0 Initial commit of project
This commit is contained in:
262
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/BitMatrixParser.php
vendored
Normal file
262
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/BitMatrixParser.php
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\Common\BitMatrix;
|
||||
use Zxing\FormatException;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class BitMatrixParser
|
||||
{
|
||||
private $bitMatrix;
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
private $parsedVersion;
|
||||
private $parsedFormatInfo;
|
||||
private $mirror;
|
||||
|
||||
/**
|
||||
* @param $bitMatrix {@link BitMatrix} to parse
|
||||
*
|
||||
* @throws FormatException if dimension is not >= 21 and 1 mod 4
|
||||
*/
|
||||
public function __construct($bitMatrix)
|
||||
{
|
||||
$dimension = $bitMatrix->getHeight();
|
||||
if ($dimension < 21 || ($dimension & 0x03) != 1) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$this->bitMatrix = $bitMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
|
||||
* correct order in order to reconstruct the codewords bytes contained within the
|
||||
* QR Code.</p>
|
||||
*
|
||||
* @return bytes encoded within the QR Code
|
||||
* @throws FormatException if the exact number of bytes expected is not read
|
||||
*/
|
||||
public function readCodewords()
|
||||
{
|
||||
$formatInfo = $this->readFormatInformation();
|
||||
$version = $this->readVersion();
|
||||
|
||||
// Get the data mask for the format used in this QR Code. This will exclude
|
||||
// some bits from reading as we wind through the bit matrix.
|
||||
$dataMask = DataMask::forReference($formatInfo->getDataMask());
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
$dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
|
||||
|
||||
$functionPattern = $version->buildFunctionPattern();
|
||||
|
||||
$readingUp = true;
|
||||
if ($version->getTotalCodewords()) {
|
||||
$result = fill_array(0, $version->getTotalCodewords(), 0);
|
||||
} else {
|
||||
$result = [];
|
||||
}
|
||||
$resultOffset = 0;
|
||||
$currentByte = 0;
|
||||
$bitsRead = 0;
|
||||
// Read columns in pairs, from right to left
|
||||
for ($j = $dimension - 1; $j > 0; $j -= 2) {
|
||||
if ($j == 6) {
|
||||
// Skip whole column with vertical alignment pattern;
|
||||
// saves time and makes the other code proceed more cleanly
|
||||
$j--;
|
||||
}
|
||||
// Read alternatingly from bottom to top then top to bottom
|
||||
for ($count = 0; $count < $dimension; $count++) {
|
||||
$i = $readingUp ? $dimension - 1 - $count : $count;
|
||||
for ($col = 0; $col < 2; $col++) {
|
||||
// Ignore bits covered by the function pattern
|
||||
if (!$functionPattern->get($j - $col, $i)) {
|
||||
// Read a bit
|
||||
$bitsRead++;
|
||||
$currentByte <<= 1;
|
||||
if ($this->bitMatrix->get($j - $col, $i)) {
|
||||
$currentByte |= 1;
|
||||
}
|
||||
// If we've made a whole byte, save it off
|
||||
if ($bitsRead == 8) {
|
||||
$result[$resultOffset++] = $currentByte; //(byte)
|
||||
$bitsRead = 0;
|
||||
$currentByte = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$readingUp ^= true; // readingUp = !readingUp; // switch directions
|
||||
}
|
||||
if ($resultOffset != $version->getTotalCodewords()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads format information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link FormatInformation} encapsulating the QR Code's format info
|
||||
* @throws FormatException if both format information locations cannot be parsed as
|
||||
* the valid encoding of format information
|
||||
*/
|
||||
public function readFormatInformation()
|
||||
{
|
||||
if ($this->parsedFormatInfo != null) {
|
||||
return $this->parsedFormatInfo;
|
||||
}
|
||||
|
||||
// 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
|
||||
$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) {
|
||||
return $parsedFormatInfo;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
private function copyBit($i, $j, $versionBits)
|
||||
{
|
||||
$bit = $this->mirror ? $this->bitMatrix->get($j, $i) : $this->bitMatrix->get($i, $j);
|
||||
|
||||
return $bit ? ($versionBits << 1) | 0x1 : $versionBits << 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads version information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link Version} encapsulating the QR Code's version
|
||||
* @throws FormatException if both version information locations cannot be parsed as
|
||||
* the valid encoding of version information
|
||||
*/
|
||||
public function readVersion()
|
||||
{
|
||||
if ($this->parsedVersion != null) {
|
||||
return $this->parsedVersion;
|
||||
}
|
||||
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
|
||||
$provisionalVersion = ($dimension - 17) / 4;
|
||||
if ($provisionalVersion <= 6) {
|
||||
return Version::getVersionForNumber($provisionalVersion);
|
||||
}
|
||||
|
||||
// Read top-right version info: 3 wide by 6 tall
|
||||
$versionBits = 0;
|
||||
$ijMin = $dimension - 11;
|
||||
for ($j = 5; $j >= 0; $j--) {
|
||||
for ($i = $dimension - 9; $i >= $ijMin; $i--) {
|
||||
$versionBits = $this->copyBit($i, $j, $versionBits);
|
||||
}
|
||||
}
|
||||
|
||||
$theParsedVersion = Version::decodeVersionInformation($versionBits);
|
||||
if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
|
||||
$this->parsedVersion = $theParsedVersion;
|
||||
|
||||
return $theParsedVersion;
|
||||
}
|
||||
|
||||
// Hmm, failed. Try bottom left: 6 wide by 3 tall
|
||||
$versionBits = 0;
|
||||
for ($i = 5; $i >= 0; $i--) {
|
||||
for ($j = $dimension - 9; $j >= $ijMin; $j--) {
|
||||
$versionBits = $this->copyBit($i, $j, $versionBits);
|
||||
}
|
||||
}
|
||||
|
||||
$theParsedVersion = Version::decodeVersionInformation($versionBits);
|
||||
if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
|
||||
$this->parsedVersion = $theParsedVersion;
|
||||
|
||||
return $theParsedVersion;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
|
||||
*/
|
||||
public function remask(): 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the parser for a mirrored operation.
|
||||
* This flag has effect only on the {@link #readFormatInformation()} and the
|
||||
* {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
|
||||
* {@link #mirror()} method should be called.
|
||||
*
|
||||
* @param Whether $mirror to read version and format information mirrored.
|
||||
*/
|
||||
public function setMirror($mirror): void
|
||||
{
|
||||
$parsedVersion = null;
|
||||
$parsedFormatInfo = null;
|
||||
$this->mirror = $mirror;
|
||||
}
|
||||
|
||||
/** Mirror the bit matrix in order to attempt a second reading. */
|
||||
public function mirror(): void
|
||||
{
|
||||
for ($x = 0; $x < $this->bitMatrix->getWidth(); $x++) {
|
||||
for ($y = $x + 1; $y < $this->bitMatrix->getHeight(); $y++) {
|
||||
if ($this->bitMatrix->get($x, $y) != $this->bitMatrix->get($y, $x)) {
|
||||
$this->bitMatrix->flip($y, $x);
|
||||
$this->bitMatrix->flip($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
127
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/DataBlock.php
vendored
Normal file
127
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/DataBlock.php
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into
|
||||
* multiple blocks, each of which is a unit of data and error-correction codewords. Each
|
||||
* is represented by an instance of this class.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class DataBlock
|
||||
{
|
||||
//byte[]
|
||||
|
||||
private function __construct(private $numDataCodewords, private $codewords)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>When QR Codes use multiple data blocks, they are actually interleaved.
|
||||
* That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
|
||||
* method will separate the data into original blocks.</p>
|
||||
*
|
||||
* @param bytes $rawCodewords as read directly from the QR Code
|
||||
* @param version $version of the QR Code
|
||||
* @param error $ecLevel-correction level of the QR Code
|
||||
*
|
||||
* @return array DataBlocks containing original bytes, "de-interleaved" from representation in the
|
||||
* QR Code
|
||||
*/
|
||||
public static function getDataBlocks(
|
||||
$rawCodewords,
|
||||
$version,
|
||||
$ecLevel
|
||||
)
|
||||
{
|
||||
if ((is_countable($rawCodewords) ? count($rawCodewords) : 0) != $version->getTotalCodewords()) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
|
||||
// Figure out the number and size of data blocks used by this version and
|
||||
// error correction level
|
||||
$ecBlocks = $version->getECBlocksForLevel($ecLevel);
|
||||
|
||||
// First count the total number of data blocks
|
||||
$totalBlocks = 0;
|
||||
$ecBlockArray = $ecBlocks->getECBlocks();
|
||||
foreach ($ecBlockArray as $ecBlock) {
|
||||
$totalBlocks += $ecBlock->getCount();
|
||||
}
|
||||
|
||||
// Now establish DataBlocks of the appropriate size and number of data codewords
|
||||
$result = [];//new DataBlock[$totalBlocks];
|
||||
$numResultBlocks = 0;
|
||||
foreach ($ecBlockArray as $ecBlock) {
|
||||
$ecBlockCount = $ecBlock->getCount();
|
||||
for ($i = 0; $i < $ecBlockCount; $i++) {
|
||||
$numDataCodewords = $ecBlock->getDataCodewords();
|
||||
$numBlockCodewords = $ecBlocks->getECCodewordsPerBlock() + $numDataCodewords;
|
||||
$result[$numResultBlocks++] = new DataBlock($numDataCodewords, fill_array(0, $numBlockCodewords, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// All blocks have the same amount of data, except that the last n
|
||||
// (where n may be 0) have 1 more byte. Figure out where these start.
|
||||
$shorterBlocksTotalCodewords = is_countable($result[0]->codewords) ? count($result[0]->codewords) : 0;
|
||||
$longerBlocksStartAt = count($result) - 1;
|
||||
while ($longerBlocksStartAt >= 0) {
|
||||
$numCodewords = is_countable($result[$longerBlocksStartAt]->codewords) ? count($result[$longerBlocksStartAt]->codewords) : 0;
|
||||
if ($numCodewords == $shorterBlocksTotalCodewords) {
|
||||
break;
|
||||
}
|
||||
$longerBlocksStartAt--;
|
||||
}
|
||||
$longerBlocksStartAt++;
|
||||
|
||||
$shorterBlocksNumDataCodewords = $shorterBlocksTotalCodewords - $ecBlocks->getECCodewordsPerBlock();
|
||||
// The last elements of result may be 1 element longer;
|
||||
// first fill out as many elements as all of them have
|
||||
$rawCodewordsOffset = 0;
|
||||
for ($i = 0; $i < $shorterBlocksNumDataCodewords; $i++) {
|
||||
for ($j = 0; $j < $numResultBlocks; $j++) {
|
||||
$result[$j]->codewords[$i] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
// Fill out the last data block in the longer ones
|
||||
for ($j = $longerBlocksStartAt; $j < $numResultBlocks; $j++) {
|
||||
$result[$j]->codewords[$shorterBlocksNumDataCodewords] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
// Now add in error correction blocks
|
||||
$max = is_countable($result[0]->codewords) ? count($result[0]->codewords) : 0;
|
||||
for ($i = $shorterBlocksNumDataCodewords; $i < $max; $i++) {
|
||||
for ($j = 0; $j < $numResultBlocks; $j++) {
|
||||
$iOffset = $j < $longerBlocksStartAt ? $i : $i + 1;
|
||||
$result[$j]->codewords[$iOffset] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getNumDataCodewords()
|
||||
{
|
||||
return $this->numDataCodewords;
|
||||
}
|
||||
|
||||
public function getCodewords()
|
||||
{
|
||||
return $this->codewords;
|
||||
}
|
||||
}
|
||||
194
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/DataMask.php
vendored
Normal file
194
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/DataMask.php
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\Common\BitMatrix;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
|
||||
* of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
|
||||
* including areas used for finder patterns, timing patterns, etc. These areas should be unused
|
||||
* after the point they are unmasked anyway.</p>
|
||||
*
|
||||
* <p>Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
|
||||
* and j is row position. In fact, as the text says, i is row position and j is column position.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
abstract class DataMask
|
||||
{
|
||||
/**
|
||||
* See ISO 18004:2006 6.8.1
|
||||
*/
|
||||
private static array $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();
|
||||
}
|
||||
|
||||
return self::$DATA_MASKS[$reference];
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Implementations of this method reverse the data masking process applied to a QR Code and
|
||||
* make its bits ready to read.</p>
|
||||
*
|
||||
* @param representation $bits of QR Code bits
|
||||
* @param dimension $dimension of QR Code, represented by bits, being unmasked
|
||||
*/
|
||||
final public function unmaskBitMatrix($bits, $dimension): void
|
||||
{
|
||||
for ($i = 0; $i < $dimension; $i++) {
|
||||
for ($j = 0; $j < $dimension; $j++) {
|
||||
if ($this->isMasked($i, $j)) {
|
||||
$bits->flip($j, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract public function isMasked($i, $j);
|
||||
}
|
||||
|
||||
DataMask::Init();
|
||||
|
||||
/**
|
||||
* 000: mask bits for which (x + y) mod 2 == 0
|
||||
*/
|
||||
final class DataMask000 extends DataMask
|
||||
{
|
||||
// @Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return (($i + $j) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 001: mask bits for which x mod 2 == 0
|
||||
*/
|
||||
final class DataMask001 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return ($i & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 010: mask bits for which y mod 3 == 0
|
||||
*/
|
||||
final class DataMask010 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return $j % 3 == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 011: mask bits for which (x + y) mod 3 == 0
|
||||
*/
|
||||
final class DataMask011 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return ($i + $j) % 3 == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 100: mask bits for which (x/2 + y/3) mod 2 == 0
|
||||
*/
|
||||
final class DataMask100 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return (int)(((int)($i / 2) + (int)($j / 3)) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 101: mask bits for which xy mod 2 + xy mod 3 == 0
|
||||
*/
|
||||
final class DataMask101 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
$temp = $i * $j;
|
||||
|
||||
return ($temp & 0x01) + ($temp % 3) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
|
||||
*/
|
||||
final class DataMask110 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
$temp = $i * $j;
|
||||
|
||||
return ((($temp & 0x01) + ($temp % 3)) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
|
||||
*/
|
||||
final class DataMask111 extends DataMask
|
||||
{
|
||||
//@Override
|
||||
public function isMasked($i, $j)
|
||||
{
|
||||
return (((($i + $j) & 0x01) + (($i * $j) % 3)) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
364
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/DecodedBitStreamParser.php
vendored
Normal file
364
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/DecodedBitStreamParser.php
vendored
Normal file
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\Common\BitSource;
|
||||
use Zxing\Common\CharacterSetECI;
|
||||
use Zxing\Common\DecoderResult;
|
||||
use Zxing\FormatException;
|
||||
|
||||
/**
|
||||
* <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
|
||||
* in one QR Code. This class decodes the bits back into text.</p>
|
||||
*
|
||||
* <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
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(
|
||||
$bytes,
|
||||
$version,
|
||||
$ecLevel,
|
||||
$hints
|
||||
): \Zxing\Common\DecoderResult
|
||||
{
|
||||
$bits = new BitSource($bytes);
|
||||
$result = '';//new StringBuilder(50);
|
||||
$byteSegments = [];
|
||||
$symbolSequence = -1;
|
||||
$parityData = -1;
|
||||
|
||||
try {
|
||||
$currentCharacterSetECI = null;
|
||||
$fc1InEffect = false;
|
||||
$mode = '';
|
||||
do {
|
||||
// While still another segment to read...
|
||||
if ($bits->available() < 4) {
|
||||
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
||||
$mode = Mode::$TERMINATOR;
|
||||
} else {
|
||||
$mode = Mode::forBits($bits->readBits(4)); // mode is encoded by 4 bits
|
||||
}
|
||||
if ($mode != Mode::$TERMINATOR) {
|
||||
if ($mode == Mode::$FNC1_FIRST_POSITION || $mode == Mode::$FNC1_SECOND_POSITION) {
|
||||
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
||||
$fc1InEffect = true;
|
||||
} elseif ($mode == Mode::$STRUCTURED_APPEND) {
|
||||
if ($bits->available() < 16) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
// sequence number and parity is added later to the result metadata
|
||||
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
|
||||
$symbolSequence = $bits->readBits(8);
|
||||
$parityData = $bits->readBits(8);
|
||||
} elseif ($mode == Mode::$ECI) {
|
||||
// Count doesn't apply to ECI
|
||||
$value = self::parseECIValue($bits);
|
||||
$currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue($value);
|
||||
if ($currentCharacterSetECI == null) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
} else {
|
||||
// First handle Hanzi mode which does not start with character count
|
||||
if ($mode == Mode::$HANZI) {
|
||||
//chinese mode contains a sub set indicator right after mode indicator
|
||||
$subset = $bits->readBits(4);
|
||||
$countHanzi = $bits->readBits($mode->getCharacterCountBits($version));
|
||||
if ($subset == self::$GB2312_SUBSET) {
|
||||
self::decodeHanziSegment($bits, $result, $countHanzi);
|
||||
}
|
||||
} else {
|
||||
// "Normal" QR code modes:
|
||||
// How many characters will follow, encoded in this mode?
|
||||
$count = $bits->readBits($mode->getCharacterCountBits($version));
|
||||
if ($mode == Mode::$NUMERIC) {
|
||||
self::decodeNumericSegment($bits, $result, $count);
|
||||
} elseif ($mode == Mode::$ALPHANUMERIC) {
|
||||
self::decodeAlphanumericSegment($bits, $result, $count, $fc1InEffect);
|
||||
} elseif ($mode == Mode::$BYTE) {
|
||||
self::decodeByteSegment($bits, $result, $count, $currentCharacterSetECI, $byteSegments, $hints);
|
||||
} elseif ($mode == Mode::$KANJI) {
|
||||
self::decodeKanjiSegment($bits, $result, $count);
|
||||
} else {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ($mode != Mode::$TERMINATOR);
|
||||
} catch (\InvalidArgumentException) {
|
||||
// from readBits() calls
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
return new DecoderResult(
|
||||
$bytes,
|
||||
$result,
|
||||
empty($byteSegments) ? null : $byteSegments,
|
||||
$ecLevel == null ? null : 'L',//ErrorCorrectionLevel::toString($ecLevel),
|
||||
$symbolSequence,
|
||||
$parityData
|
||||
);
|
||||
}
|
||||
|
||||
private static function parseECIValue($bits)
|
||||
{
|
||||
$firstByte = $bits->readBits(8);
|
||||
if (($firstByte & 0x80) == 0) {
|
||||
// just one byte
|
||||
return $firstByte & 0x7F;
|
||||
}
|
||||
if (($firstByte & 0xC0) == 0x80) {
|
||||
// two bytes
|
||||
$secondByte = $bits->readBits(8);
|
||||
|
||||
return (($firstByte & 0x3F) << 8) | $secondByte;
|
||||
}
|
||||
if (($firstByte & 0xE0) == 0xC0) {
|
||||
// three bytes
|
||||
$secondThirdBytes = $bits->readBits(16);
|
||||
|
||||
return (($firstByte & 0x1F) << 16) | $secondThirdBytes;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* See specification GBT 18284-2000
|
||||
*/
|
||||
private static function decodeHanziSegment(
|
||||
$bits,
|
||||
&$result,
|
||||
$count
|
||||
)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if ($count * 13 > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as GB2312 afterwards
|
||||
$buffer = fill_array(0, 2 * $count, 0);
|
||||
$offset = 0;
|
||||
while ($count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
$twoBytes = $bits->readBits(13);
|
||||
$assembledTwoBytes = (($twoBytes / 0x060) << 8) | ($twoBytes % 0x060);
|
||||
if ($assembledTwoBytes < 0x003BF) {
|
||||
// In the 0xA1A1 to 0xAAFE range
|
||||
$assembledTwoBytes += 0x0A1A1;
|
||||
} else {
|
||||
// In the 0xB0A1 to 0xFAFE range
|
||||
$assembledTwoBytes += 0x0A6A1;
|
||||
}
|
||||
$buffer[$offset] = (($assembledTwoBytes >> 8) & 0xFF);//(byte)
|
||||
$buffer[$offset + 1] = ($assembledTwoBytes & 0xFF);//(byte)
|
||||
$offset += 2;
|
||||
$count--;
|
||||
}
|
||||
$result .= iconv('GB2312', 'UTF-8', implode($buffer));
|
||||
}
|
||||
|
||||
private static function decodeNumericSegment(
|
||||
$bits,
|
||||
&$result,
|
||||
$count
|
||||
)
|
||||
{
|
||||
// Read three digits at a time
|
||||
while ($count >= 3) {
|
||||
// Each 10 bits encodes three digits
|
||||
if ($bits->available() < 10) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$threeDigitsBits = $bits->readBits(10);
|
||||
if ($threeDigitsBits >= 1000) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result .= (self::toAlphaNumericChar($threeDigitsBits / 100));
|
||||
$result .= (self::toAlphaNumericChar(($threeDigitsBits / 10) % 10));
|
||||
$result .= (self::toAlphaNumericChar($threeDigitsBits % 10));
|
||||
$count -= 3;
|
||||
}
|
||||
if ($count == 2) {
|
||||
// Two digits left over to read, encoded in 7 bits
|
||||
if ($bits->available() < 7) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$twoDigitsBits = $bits->readBits(7);
|
||||
if ($twoDigitsBits >= 100) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result .= (self::toAlphaNumericChar($twoDigitsBits / 10));
|
||||
$result .= (self::toAlphaNumericChar($twoDigitsBits % 10));
|
||||
} elseif ($count == 1) {
|
||||
// One digit left over to read
|
||||
if ($bits->available() < 4) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$digitBits = $bits->readBits(4);
|
||||
if ($digitBits >= 10) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result .= (self::toAlphaNumericChar($digitBits));
|
||||
}
|
||||
}
|
||||
|
||||
private static function toAlphaNumericChar($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 decodeByteSegment(
|
||||
$bits,
|
||||
&$result,
|
||||
$count,
|
||||
$currentCharacterSetECI,
|
||||
&$byteSegments,
|
||||
$hints
|
||||
)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if (8 * $count > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
$readBytes = fill_array(0, $count, 0);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$readBytes[$i] = $bits->readBits(8);//(byte)
|
||||
}
|
||||
$text = implode(array_map('chr', $readBytes));
|
||||
$encoding = '';
|
||||
if ($currentCharacterSetECI == null) {
|
||||
// The spec isn't clear on this mode; see
|
||||
// section 6.4.5: t does not say which encoding to assuming
|
||||
// upon decoding. I have seen ISO-8859-1 used as well as
|
||||
// Shift_JIS -- without anything like an ECI designator to
|
||||
// give a hint.
|
||||
|
||||
$encoding = mb_detect_encoding($text, $hints);
|
||||
} else {
|
||||
$encoding = $currentCharacterSetECI->name();
|
||||
}
|
||||
// $result.= mb_convert_encoding($text ,$encoding);//(new String(readBytes, encoding));
|
||||
$result .= $text;//(new String(readBytes, encoding));
|
||||
|
||||
$byteSegments = array_merge($byteSegments, $readBytes);
|
||||
}
|
||||
|
||||
private static function decodeKanjiSegment(
|
||||
$bits,
|
||||
&$result,
|
||||
$count
|
||||
)
|
||||
{
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if ($count * 13 > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as Shift_JIS afterwards
|
||||
$buffer = [0, 2 * $count, 0];
|
||||
$offset = 0;
|
||||
while ($count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
$twoBytes = $bits->readBits(13);
|
||||
$assembledTwoBytes = (($twoBytes / 0x0C0) << 8) | ($twoBytes % 0x0C0);
|
||||
if ($assembledTwoBytes < 0x01F00) {
|
||||
// In the 0x8140 to 0x9FFC range
|
||||
$assembledTwoBytes += 0x08140;
|
||||
} else {
|
||||
// In the 0xE040 to 0xEBBF range
|
||||
$assembledTwoBytes += 0x0C140;
|
||||
}
|
||||
$buffer[$offset] = ($assembledTwoBytes >> 8);//(byte)
|
||||
$buffer[$offset + 1] = $assembledTwoBytes; //(byte)
|
||||
$offset += 2;
|
||||
$count--;
|
||||
}
|
||||
// Shift_JIS may not be supported in some environments:
|
||||
|
||||
$result .= iconv('shift-jis', 'utf-8', implode($buffer));
|
||||
}
|
||||
|
||||
private function DecodedBitStreamParser(): void
|
||||
{
|
||||
}
|
||||
}
|
||||
208
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/Decoder.php
vendored
Normal file
208
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/Decoder.php
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\ChecksumException;
|
||||
use Zxing\Common\BitMatrix;
|
||||
use Zxing\Common\Reedsolomon\GenericGF;
|
||||
use Zxing\Common\Reedsolomon\ReedSolomonDecoder;
|
||||
use Zxing\Common\Reedsolomon\ReedSolomonException;
|
||||
use Zxing\FormatException;
|
||||
|
||||
/**
|
||||
* <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
|
||||
* the QR Code from an image.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class Decoder
|
||||
{
|
||||
private readonly \Zxing\Common\Reedsolomon\ReedSolomonDecoder $rsDecoder;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256);
|
||||
}
|
||||
|
||||
public function decode($variable, $hints = null)
|
||||
{
|
||||
if (is_array($variable)) {
|
||||
return $this->decodeImage($variable, $hints);
|
||||
} elseif ($variable instanceof BitMatrix) {
|
||||
return $this->decodeBits($variable, $hints);
|
||||
} elseif ($variable instanceof BitMatrixParser) {
|
||||
return $this->decodeParser($variable, $hints);
|
||||
}
|
||||
die('decode error Decoder.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
|
||||
* "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param array $image booleans representing white/black QR Code modules
|
||||
* @param decoding $hints hints that should be used to influence decoding
|
||||
*
|
||||
* @return text and bytes encoded within the QR Code
|
||||
* @throws FormatException if the QR Code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
public function decodeImage($image, $hints = null)
|
||||
{
|
||||
$dimension = count($image);
|
||||
$bits = new BitMatrix($dimension);
|
||||
for ($i = 0; $i < $dimension; $i++) {
|
||||
for ($j = 0; $j < $dimension; $j++) {
|
||||
if ($image[$i][$j]) {
|
||||
$bits->set($j, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->decode($bits, $hints);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param BitMatrix $bits booleans representing white/black QR Code modules
|
||||
* @param decoding $hints hints that should be used to influence decoding
|
||||
*
|
||||
* @return text and bytes encoded within the QR Code
|
||||
* @throws FormatException if the QR Code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
public function decodeBits($bits, $hints = null)
|
||||
{
|
||||
|
||||
// Construct a parser and read version, error-correction level
|
||||
$parser = new BitMatrixParser($bits);
|
||||
$fe = null;
|
||||
$ce = null;
|
||||
try {
|
||||
return $this->decode($parser, $hints);
|
||||
} catch (FormatException $e) {
|
||||
$fe = $e;
|
||||
} catch (ChecksumException $e) {
|
||||
$ce = $e;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// Revert the bit matrix
|
||||
$parser->remask();
|
||||
|
||||
// Will be attempting a mirrored reading of the version and format info.
|
||||
$parser->setMirror(true);
|
||||
|
||||
// Preemptively read the version.
|
||||
$parser->readVersion();
|
||||
|
||||
// Preemptively read the format information.
|
||||
$parser->readFormatInformation();
|
||||
|
||||
/*
|
||||
* Since we're here, this means we have successfully detected some kind
|
||||
* of version and format information when mirrored. This is a good sign,
|
||||
* that the QR code may be mirrored, and we should try once more with a
|
||||
* mirrored content.
|
||||
*/
|
||||
// Prepare for a mirrored reading.
|
||||
$parser->mirror();
|
||||
|
||||
$result = $this->decode($parser, $hints);
|
||||
|
||||
// Success! Notify the caller that the code was mirrored.
|
||||
$result->setOther(new QRCodeDecoderMetaData(true));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private function decodeParser($parser, $hints = null)
|
||||
{
|
||||
$version = $parser->readVersion();
|
||||
$ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel();
|
||||
|
||||
// Read codewords
|
||||
$codewords = $parser->readCodewords();
|
||||
// Separate into data blocks
|
||||
$dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel);
|
||||
|
||||
// Count total number of data bytes
|
||||
$totalBytes = 0;
|
||||
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) {
|
||||
$codewordBytes = $dataBlock->getCodewords();
|
||||
$numDataCodewords = $dataBlock->getNumDataCodewords();
|
||||
$this->correctErrors($codewordBytes, $numDataCodewords);
|
||||
for ($i = 0; $i < $numDataCodewords; $i++) {
|
||||
$resultBytes[$resultOffset++] = $codewordBytes[$i];
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the contents of that stream of bytes
|
||||
return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||
* correct the errors in-place using Reed-Solomon error correction.</p>
|
||||
*
|
||||
* @param data $codewordBytes and error correction codewords
|
||||
* @param number $numDataCodewords of codewords that are data bytes
|
||||
*
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
private function correctErrors(&$codewordBytes, $numDataCodewords)
|
||||
{
|
||||
$numCodewords = is_countable($codewordBytes) ? count($codewordBytes) : 0;
|
||||
// First read into an array of ints
|
||||
$codewordsInts = fill_array(0, $numCodewords, 0);
|
||||
for ($i = 0; $i < $numCodewords; $i++) {
|
||||
$codewordsInts[$i] = $codewordBytes[$i] & 0xFF;
|
||||
}
|
||||
$numECCodewords = (is_countable($codewordBytes) ? count($codewordBytes) : 0) - $numDataCodewords;
|
||||
try {
|
||||
$this->rsDecoder->decode($codewordsInts, $numECCodewords);
|
||||
} catch (ReedSolomonException) {
|
||||
throw ChecksumException::getChecksumInstance();
|
||||
}
|
||||
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||
// We don't care about errors in the error-correction codewords
|
||||
for ($i = 0; $i < $numDataCodewords; $i++) {
|
||||
$codewordBytes[$i] = $codewordsInts[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
90
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/ErrorCorrectionLevel.php
vendored
Normal file
90
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/ErrorCorrectionLevel.php
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
/**
|
||||
* <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
|
||||
* defined by the QR code standard.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
class ErrorCorrectionLevel
|
||||
{
|
||||
/**
|
||||
* @var \Zxing\Qrcode\Decoder\ErrorCorrectionLevel[]|null
|
||||
*/
|
||||
private static ?array $FOR_BITS = null;
|
||||
|
||||
public function __construct(private $bits, private $ordinal = 0)
|
||||
{
|
||||
}
|
||||
|
||||
public static function Init(): void
|
||||
{
|
||||
self::$FOR_BITS = [
|
||||
|
||||
|
||||
new ErrorCorrectionLevel(0x00, 1), //M
|
||||
new ErrorCorrectionLevel(0x01, 0), //L
|
||||
new ErrorCorrectionLevel(0x02, 3), //H
|
||||
new ErrorCorrectionLevel(0x03, 2), //Q
|
||||
|
||||
];
|
||||
}
|
||||
/** L = ~7% correction */
|
||||
// self::$L = new ErrorCorrectionLevel(0x01);
|
||||
/** M = ~15% correction */
|
||||
//self::$M = new ErrorCorrectionLevel(0x00);
|
||||
/** Q = ~25% correction */
|
||||
//self::$Q = new ErrorCorrectionLevel(0x03);
|
||||
/** H = ~30% correction */
|
||||
//self::$H = new ErrorCorrectionLevel(0x02);
|
||||
/**
|
||||
* @param int $bits containing the two bits encoding a QR Code's error correction level
|
||||
*
|
||||
* @return ErrorCorrectionLevel representing the encoded error correction level
|
||||
*/
|
||||
public static function forBits($bits)
|
||||
{
|
||||
if ($bits < 0 || $bits >= (is_countable(self::$FOR_BITS) ? count(self::$FOR_BITS) : 0)) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
$level = self::$FOR_BITS[$bits];
|
||||
|
||||
// $lev = self::$$bit;
|
||||
return $level;
|
||||
}
|
||||
|
||||
|
||||
public function getBits()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
|
||||
public function getOrdinal()
|
||||
{
|
||||
return $this->ordinal;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCorrectionLevel::Init();
|
||||
193
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/FormatInformation.php
vendored
Normal file
193
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/FormatInformation.php
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a QR Code's format information, including the data mask used and
|
||||
* error correction level.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @see DataMask
|
||||
* @see ErrorCorrectionLevel
|
||||
*/
|
||||
final class FormatInformation
|
||||
{
|
||||
public static $FORMAT_INFO_MASK_QR;
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006, Annex C, Table C.1
|
||||
*/
|
||||
public static $FORMAT_INFO_DECODE_LOOKUP;
|
||||
/**
|
||||
* Offset i holds the number of 1 bits in the binary representation of i
|
||||
* @var int[]|null
|
||||
*/
|
||||
private static ?array $BITS_SET_IN_HALF_BYTE = null;
|
||||
|
||||
private readonly \Zxing\Qrcode\Decoder\ErrorCorrectionLevel $errorCorrectionLevel;
|
||||
private readonly int $dataMask;
|
||||
|
||||
private function __construct($formatInfo)
|
||||
{
|
||||
// Bits 3,4
|
||||
$this->errorCorrectionLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x03);
|
||||
// Bottom 3 bits
|
||||
$this->dataMask = ($formatInfo & 0x07);//(byte)
|
||||
}
|
||||
|
||||
public static function Init(): void
|
||||
{
|
||||
self::$FORMAT_INFO_MASK_QR = 0x5412;
|
||||
self::$BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
|
||||
self::$FORMAT_INFO_DECODE_LOOKUP = [
|
||||
[0x5412, 0x00],
|
||||
[0x5125, 0x01],
|
||||
[0x5E7C, 0x02],
|
||||
[0x5B4B, 0x03],
|
||||
[0x45F9, 0x04],
|
||||
[0x40CE, 0x05],
|
||||
[0x4F97, 0x06],
|
||||
[0x4AA0, 0x07],
|
||||
[0x77C4, 0x08],
|
||||
[0x72F3, 0x09],
|
||||
[0x7DAA, 0x0A],
|
||||
[0x789D, 0x0B],
|
||||
[0x662F, 0x0C],
|
||||
[0x6318, 0x0D],
|
||||
[0x6C41, 0x0E],
|
||||
[0x6976, 0x0F],
|
||||
[0x1689, 0x10],
|
||||
[0x13BE, 0x11],
|
||||
[0x1CE7, 0x12],
|
||||
[0x19D0, 0x13],
|
||||
[0x0762, 0x14],
|
||||
[0x0255, 0x15],
|
||||
[0x0D0C, 0x16],
|
||||
[0x083B, 0x17],
|
||||
[0x355F, 0x18],
|
||||
[0x3068, 0x19],
|
||||
[0x3F31, 0x1A],
|
||||
[0x3A06, 0x1B],
|
||||
[0x24B4, 0x1C],
|
||||
[0x2183, 0x1D],
|
||||
[0x2EDA, 0x1E],
|
||||
[0x2BED, 0x1F],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $maskedFormatInfo1 ; format info indicator, with mask still applied
|
||||
* @param $maskedFormatInfo2 ; second copy of same info; both are checked at the same time
|
||||
* to establish best match
|
||||
*
|
||||
* @return information about the format it specifies, or {@code null}
|
||||
* if doesn't seem to match any known pattern
|
||||
*/
|
||||
public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
|
||||
{
|
||||
$formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
|
||||
if ($formatInfo != null) {
|
||||
return $formatInfo;
|
||||
}
|
||||
// Should return null, but, some QR codes apparently
|
||||
// do not mask this info. Try again by actually masking the pattern
|
||||
// first
|
||||
return self::doDecodeFormatInformation(
|
||||
$maskedFormatInfo1 ^ self::$FORMAT_INFO_MASK_QR,
|
||||
$maskedFormatInfo2 ^ self::$FORMAT_INFO_MASK_QR
|
||||
);
|
||||
}
|
||||
|
||||
private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
|
||||
{
|
||||
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
|
||||
$bestDifference = PHP_INT_MAX;
|
||||
$bestFormatInfo = 0;
|
||||
foreach (self::$FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) {
|
||||
$targetInfo = $decodeInfo[0];
|
||||
if ($targetInfo == $maskedFormatInfo1 || $targetInfo == $maskedFormatInfo2) {
|
||||
// Found an exact match
|
||||
return new FormatInformation($decodeInfo[1]);
|
||||
}
|
||||
$bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);
|
||||
if ($bitsDifference < $bestDifference) {
|
||||
$bestFormatInfo = $decodeInfo[1];
|
||||
$bestDifference = $bitsDifference;
|
||||
}
|
||||
if ($maskedFormatInfo1 != $maskedFormatInfo2) {
|
||||
// also try the other option
|
||||
$bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);
|
||||
if ($bitsDifference < $bestDifference) {
|
||||
$bestFormatInfo = $decodeInfo[1];
|
||||
$bestDifference = $bitsDifference;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
|
||||
// differing means we found a match
|
||||
if ($bestDifference <= 3) {
|
||||
return new FormatInformation($bestFormatInfo);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function numBitsDiffering($a, $b)
|
||||
{
|
||||
$a ^= $b; // a now has a 1 bit exactly where its bit differs with b's
|
||||
// Count bits set quickly with a series of lookups:
|
||||
return self::$BITS_SET_IN_HALF_BYTE[$a & 0x0F] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(int)(uRShift($a, 4) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 8) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 12) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 16) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 20) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 24) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 28) & 0x0F)];
|
||||
}
|
||||
|
||||
public function getErrorCorrectionLevel()
|
||||
{
|
||||
return $this->errorCorrectionLevel;
|
||||
}
|
||||
|
||||
public function getDataMask()
|
||||
{
|
||||
return $this->dataMask;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function hashCode()
|
||||
{
|
||||
return ($this->errorCorrectionLevel->ordinal() << 3) | (int)($this->dataMask);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function equals($o)
|
||||
{
|
||||
if (!($o instanceof FormatInformation)) {
|
||||
return false;
|
||||
}
|
||||
$other = $o;
|
||||
|
||||
return $this->errorCorrectionLevel == $other->errorCorrectionLevel &&
|
||||
$this->dataMask == $other->dataMask;
|
||||
}
|
||||
}
|
||||
|
||||
FormatInformation::Init();
|
||||
108
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/Mode.php
vendored
Normal file
108
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/Mode.php
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
/**
|
||||
* <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
|
||||
* data can be encoded to bits in the QR code standard.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
class Mode
|
||||
{
|
||||
public static $TERMINATOR;
|
||||
public static $NUMERIC;
|
||||
public static $ALPHANUMERIC;
|
||||
public static $STRUCTURED_APPEND;
|
||||
public static $BYTE;
|
||||
public static $ECI;
|
||||
public static $KANJI;
|
||||
public static $FNC1_FIRST_POSITION;
|
||||
public static $FNC1_SECOND_POSITION;
|
||||
public static $HANZI;
|
||||
|
||||
public function __construct(private $characterCountBitsForVersions, private $bits)
|
||||
{
|
||||
}
|
||||
|
||||
public static function Init(): void
|
||||
{
|
||||
self::$TERMINATOR = new Mode([0, 0, 0], 0x00); // Not really a mode...
|
||||
self::$NUMERIC = new Mode([10, 12, 14], 0x01);
|
||||
self::$ALPHANUMERIC = new Mode([9, 11, 13], 0x02);
|
||||
self::$STRUCTURED_APPEND = new Mode([0, 0, 0], 0x03); // Not supported
|
||||
self::$BYTE = new Mode([8, 16, 16], 0x04);
|
||||
self::$ECI = new Mode([0, 0, 0], 0x07); // character counts don't apply
|
||||
self::$KANJI = new Mode([8, 10, 12], 0x08);
|
||||
self::$FNC1_FIRST_POSITION = new Mode([0, 0, 0], 0x05);
|
||||
self::$FNC1_SECOND_POSITION = new Mode([0, 0, 0], 0x09);
|
||||
/** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
|
||||
self::$HANZI = new Mode([8, 10, 12], 0x0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
return $this->characterCountBitsForVersions[$offset];
|
||||
}
|
||||
|
||||
public function getBits()
|
||||
{
|
||||
return $this->bits;
|
||||
}
|
||||
}
|
||||
|
||||
Mode::Init();
|
||||
19
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/QRCodeDecoderMetaData.php
vendored
Normal file
19
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/QRCodeDecoderMetaData.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
class QRCodeDecoderMetaData
|
||||
{
|
||||
/**
|
||||
* QRCodeDecoderMetaData constructor.
|
||||
* @param bool $mirrored
|
||||
*/
|
||||
public function __construct(private $mirrored)
|
||||
{
|
||||
}
|
||||
|
||||
public function isMirrored()
|
||||
{
|
||||
return $this->mirrored;
|
||||
}
|
||||
}
|
||||
715
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/Version.php
vendored
Normal file
715
vendor/khanamiryan/qrcode-detector-decoder/lib/Qrcode/Decoder/Version.php
vendored
Normal file
@@ -0,0 +1,715 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\Common\BitMatrix;
|
||||
use Zxing\FormatException;
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006 Annex D
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
class Version
|
||||
{
|
||||
/**
|
||||
* See ISO 18004:2006 Annex D.
|
||||
* Element i represents the raw version bits that specify version i + 7
|
||||
*/
|
||||
private static array $VERSION_DECODE_INFO = [
|
||||
0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6,
|
||||
0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,
|
||||
0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683,
|
||||
0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,
|
||||
0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250,
|
||||
0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,
|
||||
0x2542E, 0x26A64, 0x27541, 0x28C69
|
||||
];
|
||||
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
private static $VERSIONS;
|
||||
private readonly float|int $totalCodewords;
|
||||
|
||||
public function __construct(
|
||||
private $versionNumber,
|
||||
private $alignmentPatternCenters,
|
||||
private $ecBlocks
|
||||
)
|
||||
{$total = 0;
|
||||
if (is_array($ecBlocks)) {
|
||||
$ecCodewords = $ecBlocks[0]->getECCodewordsPerBlock();
|
||||
$ecbArray = $ecBlocks[0]->getECBlocks();
|
||||
} else {
|
||||
$ecCodewords = $ecBlocks->getECCodewordsPerBlock();
|
||||
$ecbArray = $ecBlocks->getECBlocks();
|
||||
}
|
||||
foreach ($ecbArray as $ecBlock) {
|
||||
$total += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords);
|
||||
}
|
||||
$this->totalCodewords = $total;
|
||||
}
|
||||
public function getVersionNumber()
|
||||
{
|
||||
return $this->versionNumber;
|
||||
}
|
||||
|
||||
public function getAlignmentPatternCenters()
|
||||
{
|
||||
return $this->alignmentPatternCenters;
|
||||
}
|
||||
|
||||
public function getTotalCodewords()
|
||||
{
|
||||
return $this->totalCodewords;
|
||||
}
|
||||
|
||||
public function getDimensionForVersion()
|
||||
{
|
||||
return 17 + 4 * $this->versionNumber;
|
||||
}
|
||||
|
||||
public function getECBlocksForLevel($ecLevel)
|
||||
{
|
||||
return $this->ecBlocks[$ecLevel->getOrdinal()];
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deduces version information purely from QR Code dimensions.</p>
|
||||
*
|
||||
* @param dimension $dimension in modules
|
||||
* @return Version for a QR Code of that dimension
|
||||
* @throws FormatException if dimension is not 1 mod 4
|
||||
*/
|
||||
public static function getProvisionalVersionForDimension($dimension)
|
||||
{
|
||||
if ($dimension % 4 != 1) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
try {
|
||||
return self::getVersionForNumber(($dimension - 17) / 4);
|
||||
} catch (\InvalidArgumentException) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public static function getVersionForNumber($versionNumber)
|
||||
{
|
||||
if ($versionNumber < 1 || $versionNumber > 40) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if (!self::$VERSIONS) {
|
||||
self::$VERSIONS = self::buildVersions();
|
||||
}
|
||||
return self::$VERSIONS[$versionNumber - 1];
|
||||
}
|
||||
|
||||
public static function decodeVersionInformation($versionBits)
|
||||
{
|
||||
$bestDifference = PHP_INT_MAX;
|
||||
$bestVersion = 0;
|
||||
for ($i = 0; $i < count(self::$VERSION_DECODE_INFO); $i++) {
|
||||
$targetVersion = self::$VERSION_DECODE_INFO[$i];
|
||||
// Do the version info bits match exactly? done.
|
||||
if ($targetVersion == $versionBits) {
|
||||
return self::getVersionForNumber($i + 7);
|
||||
}
|
||||
// Otherwise see if this is the closest to a real version info bit string
|
||||
// we have seen so far
|
||||
$bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion);
|
||||
if ($bitsDifference < $bestDifference) {
|
||||
$bestVersion = $i + 7;
|
||||
$bestDifference = $bitsDifference;
|
||||
}
|
||||
}
|
||||
// We can tolerate up to 3 bits of error since no two version info codewords will
|
||||
// differ in less than 8 bits.
|
||||
if ($bestDifference <= 3) {
|
||||
return self::getVersionForNumber($bestVersion);
|
||||
}
|
||||
// If we didn't find a close enough match, fail
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006 Annex E
|
||||
*/
|
||||
public function buildFunctionPattern()
|
||||
{
|
||||
$dimension = self::getDimensionForVersion();
|
||||
$bitMatrix = new BitMatrix($dimension);
|
||||
|
||||
// Top left finder pattern + separator + format
|
||||
$bitMatrix->setRegion(0, 0, 9, 9);
|
||||
// Top right finder pattern + separator + format
|
||||
$bitMatrix->setRegion($dimension - 8, 0, 8, 9);
|
||||
// Bottom left finder pattern + separator + format
|
||||
$bitMatrix->setRegion(0, $dimension - 8, 9, 8);
|
||||
|
||||
// Alignment patterns
|
||||
$max = is_countable($this->alignmentPatternCenters) ? count($this->alignmentPatternCenters) : 0;
|
||||
for ($x = 0; $x < $max; $x++) {
|
||||
$i = $this->alignmentPatternCenters[$x] - 2;
|
||||
for ($y = 0; $y < $max; $y++) {
|
||||
if (($x == 0 && ($y == 0 || $y == $max - 1)) || ($x == $max - 1 && $y == 0)) {
|
||||
// No alignment patterns near the three finder paterns
|
||||
continue;
|
||||
}
|
||||
$bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5);
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical timing pattern
|
||||
$bitMatrix->setRegion(6, 9, 1, $dimension - 17);
|
||||
// Horizontal timing pattern
|
||||
$bitMatrix->setRegion(9, 6, $dimension - 17, 1);
|
||||
|
||||
if ($this->versionNumber > 6) {
|
||||
// Version info, top right
|
||||
$bitMatrix->setRegion($dimension - 11, 0, 3, 6);
|
||||
// Version info, bottom left
|
||||
$bitMatrix->setRegion(0, $dimension - 11, 6, 3);
|
||||
}
|
||||
|
||||
return $bitMatrix;
|
||||
}
|
||||
/**
|
||||
* See ISO 18004:2006 6.5.1 Table 9
|
||||
*/
|
||||
private static function buildVersions()
|
||||
{
|
||||
return [
|
||||
new Version(
|
||||
1,
|
||||
[],
|
||||
[new ECBlocks(7, [new ECB(1, 19)]),
|
||||
new ECBlocks(10, [new ECB(1, 16)]),
|
||||
new ECBlocks(13, [new ECB(1, 13)]),
|
||||
new ECBlocks(17, [new ECB(1, 9)])]
|
||||
),
|
||||
new Version(
|
||||
2,
|
||||
[6, 18],
|
||||
[new ECBlocks(10, [new ECB(1, 34)]),
|
||||
new ECBlocks(16, [new ECB(1, 28)]),
|
||||
new ECBlocks(22, [new ECB(1, 22)]),
|
||||
new ECBlocks(28, [new ECB(1, 16)])]
|
||||
),
|
||||
new Version(
|
||||
3,
|
||||
[6, 22],
|
||||
[ new ECBlocks(15, [new ECB(1, 55)]),
|
||||
new ECBlocks(26, [new ECB(1, 44)]),
|
||||
new ECBlocks(18, [new ECB(2, 17)]),
|
||||
new ECBlocks(22, [new ECB(2, 13)])]
|
||||
),
|
||||
new Version(
|
||||
4,
|
||||
[6, 26],
|
||||
[new ECBlocks(20, [new ECB(1, 80)]),
|
||||
new ECBlocks(18, [new ECB(2, 32)]),
|
||||
new ECBlocks(26, [new ECB(2, 24)]),
|
||||
new ECBlocks(16, [new ECB(4, 9)])]
|
||||
),
|
||||
new Version(
|
||||
5,
|
||||
[6, 30],
|
||||
[new ECBlocks(26, [new ECB(1, 108)]),
|
||||
new ECBlocks(24, [new ECB(2, 43)]),
|
||||
new ECBlocks(18, [new ECB(2, 15),
|
||||
new ECB(2, 16)]),
|
||||
new ECBlocks(22, [new ECB(2, 11),
|
||||
new ECB(2, 12)])]
|
||||
),
|
||||
new Version(
|
||||
6,
|
||||
[6, 34],
|
||||
[new ECBlocks(18, [new ECB(2, 68)]),
|
||||
new ECBlocks(16, [new ECB(4, 27)]),
|
||||
new ECBlocks(24, [new ECB(4, 19)]),
|
||||
new ECBlocks(28, [new ECB(4, 15)])]
|
||||
),
|
||||
new Version(
|
||||
7,
|
||||
[6, 22, 38],
|
||||
[new ECBlocks(20, [new ECB(2, 78)]),
|
||||
new ECBlocks(18, [new ECB(4, 31)]),
|
||||
new ECBlocks(18, [new ECB(2, 14),
|
||||
new ECB(4, 15)]),
|
||||
new ECBlocks(26, [new ECB(4, 13),
|
||||
new ECB(1, 14)])]
|
||||
),
|
||||
new Version(
|
||||
8,
|
||||
[6, 24, 42],
|
||||
[new ECBlocks(24, [new ECB(2, 97)]),
|
||||
new ECBlocks(22, [new ECB(2, 38),
|
||||
new ECB(2, 39)]),
|
||||
new ECBlocks(22, [new ECB(4, 18),
|
||||
new ECB(2, 19)]),
|
||||
new ECBlocks(26, [new ECB(4, 14),
|
||||
new ECB(2, 15)])]
|
||||
),
|
||||
new Version(
|
||||
9,
|
||||
[6, 26, 46],
|
||||
[new ECBlocks(30, [new ECB(2, 116)]),
|
||||
new ECBlocks(22, [new ECB(3, 36),
|
||||
new ECB(2, 37)]),
|
||||
new ECBlocks(20, [new ECB(4, 16),
|
||||
new ECB(4, 17)]),
|
||||
new ECBlocks(24, [new ECB(4, 12),
|
||||
new ECB(4, 13)])]
|
||||
),
|
||||
new Version(
|
||||
10,
|
||||
[6, 28, 50],
|
||||
[new ECBlocks(18, [new ECB(2, 68),
|
||||
new ECB(2, 69)]),
|
||||
new ECBlocks(26, [new ECB(4, 43),
|
||||
new ECB(1, 44)]),
|
||||
new ECBlocks(24, [new ECB(6, 19),
|
||||
new ECB(2, 20)]),
|
||||
new ECBlocks(28, [new ECB(6, 15),
|
||||
new ECB(2, 16)])]
|
||||
),
|
||||
new Version(
|
||||
11,
|
||||
[6, 30, 54],
|
||||
[new ECBlocks(20, [new ECB(4, 81)]),
|
||||
new ECBlocks(30, [new ECB(1, 50),
|
||||
new ECB(4, 51)]),
|
||||
new ECBlocks(28, [new ECB(4, 22),
|
||||
new ECB(4, 23)]),
|
||||
new ECBlocks(24, [new ECB(3, 12),
|
||||
new ECB(8, 13)])]
|
||||
),
|
||||
new Version(
|
||||
12,
|
||||
[6, 32, 58],
|
||||
[new ECBlocks(24, [new ECB(2, 92),
|
||||
new ECB(2, 93)]),
|
||||
new ECBlocks(22, [new ECB(6, 36),
|
||||
new ECB(2, 37)]),
|
||||
new ECBlocks(26, [new ECB(4, 20),
|
||||
new ECB(6, 21)]),
|
||||
new ECBlocks(28, [new ECB(7, 14),
|
||||
new ECB(4, 15)])]
|
||||
),
|
||||
new Version(
|
||||
13,
|
||||
[6, 34, 62],
|
||||
[new ECBlocks(26, [new ECB(4, 107)]),
|
||||
new ECBlocks(22, [new ECB(8, 37),
|
||||
new ECB(1, 38)]),
|
||||
new ECBlocks(24, [new ECB(8, 20),
|
||||
new ECB(4, 21)]),
|
||||
new ECBlocks(22, [new ECB(12, 11),
|
||||
new ECB(4, 12)])]
|
||||
),
|
||||
new Version(
|
||||
14,
|
||||
[6, 26, 46, 66],
|
||||
[new ECBlocks(30, [new ECB(3, 115),
|
||||
new ECB(1, 116)]),
|
||||
new ECBlocks(24, [new ECB(4, 40),
|
||||
new ECB(5, 41)]),
|
||||
new ECBlocks(20, [new ECB(11, 16),
|
||||
new ECB(5, 17)]),
|
||||
new ECBlocks(24, [new ECB(11, 12),
|
||||
new ECB(5, 13)])]
|
||||
),
|
||||
new Version(
|
||||
15,
|
||||
[6, 26, 48, 70],
|
||||
[new ECBlocks(22, [new ECB(5, 87),
|
||||
new ECB(1, 88)]),
|
||||
new ECBlocks(24, [new ECB(5, 41),
|
||||
new ECB(5, 42)]),
|
||||
new ECBlocks(30, [new ECB(5, 24),
|
||||
new ECB(7, 25)]),
|
||||
new ECBlocks(24, [new ECB(11, 12),
|
||||
new ECB(7, 13)])]
|
||||
),
|
||||
new Version(
|
||||
16,
|
||||
[6, 26, 50, 74],
|
||||
[new ECBlocks(24, [new ECB(5, 98),
|
||||
new ECB(1, 99)]),
|
||||
new ECBlocks(28, [new ECB(7, 45),
|
||||
new ECB(3, 46)]),
|
||||
new ECBlocks(24, [new ECB(15, 19),
|
||||
new ECB(2, 20)]),
|
||||
new ECBlocks(30, [new ECB(3, 15),
|
||||
new ECB(13, 16)])]
|
||||
),
|
||||
new Version(
|
||||
17,
|
||||
[6, 30, 54, 78],
|
||||
[new ECBlocks(28, [new ECB(1, 107),
|
||||
new ECB(5, 108)]),
|
||||
new ECBlocks(28, [new ECB(10, 46),
|
||||
new ECB(1, 47)]),
|
||||
new ECBlocks(28, [new ECB(1, 22),
|
||||
new ECB(15, 23)]),
|
||||
new ECBlocks(28, [new ECB(2, 14),
|
||||
new ECB(17, 15)])]
|
||||
),
|
||||
new Version(
|
||||
18,
|
||||
[6, 30, 56, 82],
|
||||
[new ECBlocks(30, [new ECB(5, 120),
|
||||
new ECB(1, 121)]),
|
||||
new ECBlocks(26, [new ECB(9, 43),
|
||||
new ECB(4, 44)]),
|
||||
new ECBlocks(28, [new ECB(17, 22),
|
||||
new ECB(1, 23)]),
|
||||
new ECBlocks(28, [new ECB(2, 14),
|
||||
new ECB(19, 15)])]
|
||||
),
|
||||
new Version(
|
||||
19,
|
||||
[6, 30, 58, 86],
|
||||
[new ECBlocks(28, [new ECB(3, 113),
|
||||
new ECB(4, 114)]),
|
||||
new ECBlocks(26, [new ECB(3, 44),
|
||||
new ECB(11, 45)]),
|
||||
new ECBlocks(26, [new ECB(17, 21),
|
||||
new ECB(4, 22)]),
|
||||
new ECBlocks(26, [new ECB(9, 13),
|
||||
new ECB(16, 14)])]
|
||||
),
|
||||
new Version(
|
||||
20,
|
||||
[6, 34, 62, 90],
|
||||
[new ECBlocks(28, [new ECB(3, 107),
|
||||
new ECB(5, 108)]),
|
||||
new ECBlocks(26, [new ECB(3, 41),
|
||||
new ECB(13, 42)]),
|
||||
new ECBlocks(30, [new ECB(15, 24),
|
||||
new ECB(5, 25)]),
|
||||
new ECBlocks(28, [new ECB(15, 15),
|
||||
new ECB(10, 16)])]
|
||||
),
|
||||
new Version(
|
||||
21,
|
||||
[6, 28, 50, 72, 94],
|
||||
[ new ECBlocks(28, [new ECB(4, 116),
|
||||
new ECB(4, 117)]),
|
||||
new ECBlocks(26, [new ECB(17, 42)]),
|
||||
new ECBlocks(28, [new ECB(17, 22),
|
||||
new ECB(6, 23)]),
|
||||
new ECBlocks(30, [new ECB(19, 16),
|
||||
new ECB(6, 17)])]
|
||||
),
|
||||
new Version(
|
||||
22,
|
||||
[6, 26, 50, 74, 98],
|
||||
[new ECBlocks(28, [new ECB(2, 111),
|
||||
new ECB(7, 112)]),
|
||||
new ECBlocks(28, [new ECB(17, 46)]),
|
||||
new ECBlocks(30, [new ECB(7, 24),
|
||||
new ECB(16, 25)]),
|
||||
new ECBlocks(24, [new ECB(34, 13)])]
|
||||
),
|
||||
new Version(
|
||||
23,
|
||||
[6, 30, 54, 78, 102],
|
||||
new ECBlocks(30, [new ECB(4, 121),
|
||||
new ECB(5, 122)]),
|
||||
new ECBlocks(28, [new ECB(4, 47),
|
||||
new ECB(14, 48)]),
|
||||
new ECBlocks(30, [new ECB(11, 24),
|
||||
new ECB(14, 25)]),
|
||||
new ECBlocks(30, [new ECB(16, 15),
|
||||
new ECB(14, 16)])
|
||||
),
|
||||
new Version(
|
||||
24,
|
||||
[6, 28, 54, 80, 106],
|
||||
[new ECBlocks(30, [new ECB(6, 117),
|
||||
new ECB(4, 118)]),
|
||||
new ECBlocks(28, [new ECB(6, 45),
|
||||
new ECB(14, 46)]),
|
||||
new ECBlocks(30, [new ECB(11, 24),
|
||||
new ECB(16, 25)]),
|
||||
new ECBlocks(30, [new ECB(30, 16),
|
||||
new ECB(2, 17)])]
|
||||
),
|
||||
new Version(
|
||||
25,
|
||||
[6, 32, 58, 84, 110],
|
||||
[new ECBlocks(26, [new ECB(8, 106),
|
||||
new ECB(4, 107)]),
|
||||
new ECBlocks(28, [new ECB(8, 47),
|
||||
new ECB(13, 48)]),
|
||||
new ECBlocks(30, [new ECB(7, 24),
|
||||
new ECB(22, 25)]),
|
||||
new ECBlocks(30, [new ECB(22, 15),
|
||||
new ECB(13, 16)])]
|
||||
),
|
||||
new Version(
|
||||
26,
|
||||
[6, 30, 58, 86, 114],
|
||||
[new ECBlocks(28, [new ECB(10, 114),
|
||||
new ECB(2, 115)]),
|
||||
new ECBlocks(28, [new ECB(19, 46),
|
||||
new ECB(4, 47)]),
|
||||
new ECBlocks(28, [new ECB(28, 22),
|
||||
new ECB(6, 23)]),
|
||||
new ECBlocks(30, [new ECB(33, 16),
|
||||
new ECB(4, 17)])]
|
||||
),
|
||||
new Version(
|
||||
27,
|
||||
[6, 34, 62, 90, 118],
|
||||
[new ECBlocks(30, [new ECB(8, 122),
|
||||
new ECB(4, 123)]),
|
||||
new ECBlocks(28, [new ECB(22, 45),
|
||||
new ECB(3, 46)]),
|
||||
new ECBlocks(30, [new ECB(8, 23),
|
||||
new ECB(26, 24)]),
|
||||
new ECBlocks(30, [new ECB(12, 15),
|
||||
new ECB(28, 16)])]
|
||||
),
|
||||
new Version(
|
||||
28,
|
||||
[6, 26, 50, 74, 98, 122],
|
||||
[new ECBlocks(30, [new ECB(3, 117),
|
||||
new ECB(10, 118)]),
|
||||
new ECBlocks(28, [new ECB(3, 45),
|
||||
new ECB(23, 46)]),
|
||||
new ECBlocks(30, [new ECB(4, 24),
|
||||
new ECB(31, 25)]),
|
||||
new ECBlocks(30, [new ECB(11, 15),
|
||||
new ECB(31, 16)])]
|
||||
),
|
||||
new Version(
|
||||
29,
|
||||
[6, 30, 54, 78, 102, 126],
|
||||
[new ECBlocks(30, [new ECB(7, 116),
|
||||
new ECB(7, 117)]),
|
||||
new ECBlocks(28, [new ECB(21, 45),
|
||||
new ECB(7, 46)]),
|
||||
new ECBlocks(30, [new ECB(1, 23),
|
||||
new ECB(37, 24)]),
|
||||
new ECBlocks(30, [new ECB(19, 15),
|
||||
new ECB(26, 16)])]
|
||||
),
|
||||
new Version(
|
||||
30,
|
||||
[6, 26, 52, 78, 104, 130],
|
||||
[new ECBlocks(30, [new ECB(5, 115),
|
||||
new ECB(10, 116)]),
|
||||
new ECBlocks(28, [new ECB(19, 47),
|
||||
new ECB(10, 48)]),
|
||||
new ECBlocks(30, [new ECB(15, 24),
|
||||
new ECB(25, 25)]),
|
||||
new ECBlocks(30, [new ECB(23, 15),
|
||||
new ECB(25, 16)])]
|
||||
),
|
||||
new Version(
|
||||
31,
|
||||
[6, 30, 56, 82, 108, 134],
|
||||
[new ECBlocks(30, [new ECB(13, 115),
|
||||
new ECB(3, 116)]),
|
||||
new ECBlocks(28, [new ECB(2, 46),
|
||||
new ECB(29, 47)]),
|
||||
new ECBlocks(30, [new ECB(42, 24),
|
||||
new ECB(1, 25)]),
|
||||
new ECBlocks(30, [new ECB(23, 15),
|
||||
new ECB(28, 16)])]
|
||||
),
|
||||
new Version(
|
||||
32,
|
||||
[6, 34, 60, 86, 112, 138],
|
||||
[new ECBlocks(30, [new ECB(17, 115)]),
|
||||
new ECBlocks(28, [new ECB(10, 46),
|
||||
new ECB(23, 47)]),
|
||||
new ECBlocks(30, [new ECB(10, 24),
|
||||
new ECB(35, 25)]),
|
||||
new ECBlocks(30, [new ECB(19, 15),
|
||||
new ECB(35, 16)])]
|
||||
),
|
||||
new Version(
|
||||
33,
|
||||
[6, 30, 58, 86, 114, 142],
|
||||
[new ECBlocks(30, [new ECB(17, 115),
|
||||
new ECB(1, 116)]),
|
||||
new ECBlocks(28, [new ECB(14, 46),
|
||||
new ECB(21, 47)]),
|
||||
new ECBlocks(30, [new ECB(29, 24),
|
||||
new ECB(19, 25)]),
|
||||
new ECBlocks(30, [new ECB(11, 15),
|
||||
new ECB(46, 16)])]
|
||||
),
|
||||
new Version(
|
||||
34,
|
||||
[6, 34, 62, 90, 118, 146],
|
||||
[new ECBlocks(30, [new ECB(13, 115),
|
||||
new ECB(6, 116)]),
|
||||
new ECBlocks(28, [new ECB(14, 46),
|
||||
new ECB(23, 47)]),
|
||||
new ECBlocks(30, [new ECB(44, 24),
|
||||
new ECB(7, 25)]),
|
||||
new ECBlocks(30, [new ECB(59, 16),
|
||||
new ECB(1, 17)])]
|
||||
),
|
||||
new Version(
|
||||
35,
|
||||
[6, 30, 54, 78, 102, 126, 150],
|
||||
[new ECBlocks(30, [new ECB(12, 121),
|
||||
new ECB(7, 122)]),
|
||||
new ECBlocks(28, [new ECB(12, 47),
|
||||
new ECB(26, 48)]),
|
||||
new ECBlocks(30, [new ECB(39, 24),
|
||||
new ECB(14, 25)]),
|
||||
new ECBlocks(30, [new ECB(22, 15),
|
||||
new ECB(41, 16)])]
|
||||
),
|
||||
new Version(
|
||||
36,
|
||||
[6, 24, 50, 76, 102, 128, 154],
|
||||
[new ECBlocks(30, [new ECB(6, 121),
|
||||
new ECB(14, 122)]),
|
||||
new ECBlocks(28, [new ECB(6, 47),
|
||||
new ECB(34, 48)]),
|
||||
new ECBlocks(30, [new ECB(46, 24),
|
||||
new ECB(10, 25)]),
|
||||
new ECBlocks(30, [new ECB(2, 15),
|
||||
new ECB(64, 16)])]
|
||||
),
|
||||
new Version(
|
||||
37,
|
||||
[6, 28, 54, 80, 106, 132, 158],
|
||||
[new ECBlocks(30, [new ECB(17, 122),
|
||||
new ECB(4, 123)]),
|
||||
new ECBlocks(28, [new ECB(29, 46),
|
||||
new ECB(14, 47)]),
|
||||
new ECBlocks(30, [new ECB(49, 24),
|
||||
new ECB(10, 25)]),
|
||||
new ECBlocks(30, [new ECB(24, 15),
|
||||
new ECB(46, 16)])]
|
||||
),
|
||||
new Version(
|
||||
38,
|
||||
[6, 32, 58, 84, 110, 136, 162],
|
||||
[new ECBlocks(30, [new ECB(4, 122),
|
||||
new ECB(18, 123)]),
|
||||
new ECBlocks(28, [new ECB(13, 46),
|
||||
new ECB(32, 47)]),
|
||||
new ECBlocks(30, [new ECB(48, 24),
|
||||
new ECB(14, 25)]),
|
||||
new ECBlocks(30, [new ECB(42, 15),
|
||||
new ECB(32, 16)])]
|
||||
),
|
||||
new Version(
|
||||
39,
|
||||
[6, 26, 54, 82, 110, 138, 166],
|
||||
[new ECBlocks(30, [new ECB(20, 117),
|
||||
new ECB(4, 118)]),
|
||||
new ECBlocks(28, [new ECB(40, 47),
|
||||
new ECB(7, 48)]),
|
||||
new ECBlocks(30, [new ECB(43, 24),
|
||||
new ECB(22, 25)]),
|
||||
new ECBlocks(30, [new ECB(10, 15),
|
||||
new ECB(67, 16)])]
|
||||
),
|
||||
new Version(
|
||||
40,
|
||||
[6, 30, 58, 86, 114, 142, 170],
|
||||
[new ECBlocks(30, [new ECB(19, 118),
|
||||
new ECB(6, 119)]),
|
||||
new ECBlocks(28, [new ECB(18, 47),
|
||||
new ECB(31, 48)]),
|
||||
new ECBlocks(30, [new ECB(34, 24),
|
||||
new ECB(34, 25)]),
|
||||
new ECBlocks(30, [new ECB(20, 15),
|
||||
new ECB(61, 16)])]
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
|
||||
* use blocks of differing sizes within one version, so, this encapsulates the parameters for
|
||||
* each set of blocks. It also holds the number of error-correction codewords per block since it
|
||||
* will be the same across all blocks within one version.</p>
|
||||
*/
|
||||
final class ECBlocks
|
||||
{
|
||||
public function __construct(private $ecCodewordsPerBlock, private $ecBlocks)
|
||||
{
|
||||
}
|
||||
|
||||
public function getECCodewordsPerBlock()
|
||||
{
|
||||
return $this->ecCodewordsPerBlock;
|
||||
}
|
||||
|
||||
public function getNumBlocks()
|
||||
{
|
||||
$total = 0;
|
||||
foreach ($this->ecBlocks as $ecBlock) {
|
||||
$total += $ecBlock->getCount();
|
||||
}
|
||||
return $total;
|
||||
}
|
||||
|
||||
public function getTotalECCodewords()
|
||||
{
|
||||
return $this->ecCodewordsPerBlock * $this->getNumBlocks();
|
||||
}
|
||||
|
||||
public function getECBlocks()
|
||||
{
|
||||
return $this->ecBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Encapsualtes the parameters for one error-correction block in one symbol version.
|
||||
* This includes the number of data codewords, and the number of times a block with these
|
||||
* parameters is used consecutively in the QR code version's format.</p>
|
||||
*/
|
||||
final class ECB
|
||||
{
|
||||
public function __construct(private $count, private $dataCodewords)
|
||||
{
|
||||
}
|
||||
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
public function getDataCodewords()
|
||||
{
|
||||
return $this->dataCodewords;
|
||||
}
|
||||
|
||||
|
||||
//@Override
|
||||
public function toString(): never
|
||||
{
|
||||
die('Version ECB toString()');
|
||||
// return parent::$versionNumber;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user