|
|
<?php |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace Zxing\Qrcode\Decoder; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final class FormatInformation |
|
|
{ |
|
|
public static $FORMAT_INFO_MASK_QR; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static $FORMAT_INFO_DECODE_LOOKUP; |
|
|
|
|
|
|
|
|
|
|
|
private static $BITS_SET_IN_HALF_BYTE; |
|
|
|
|
|
private $errorCorrectionLevel; |
|
|
private $dataMask; |
|
|
|
|
|
private function __construct($formatInfo) |
|
|
{ |
|
|
|
|
|
$this->errorCorrectionLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x03); |
|
|
|
|
|
$this->dataMask = ($formatInfo & 0x07); |
|
|
} |
|
|
|
|
|
public static function Init() |
|
|
{ |
|
|
self::$FORMAT_INFO_MASK_QR = 0x5412; |
|
|
self::$BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4]; |
|
|
self::$FORMAT_INFO_DECODE_LOOKUP = [ |
|
|
[0x5412, 0x00], |
|
|
[0x5125, 0x01], |
|
|
[0x5E7C, 0x02], |
|
|
[0x5B4B, 0x03], |
|
|
[0x45F9, 0x04], |
|
|
[0x40CE, 0x05], |
|
|
[0x4F97, 0x06], |
|
|
[0x4AA0, 0x07], |
|
|
[0x77C4, 0x08], |
|
|
[0x72F3, 0x09], |
|
|
[0x7DAA, 0x0A], |
|
|
[0x789D, 0x0B], |
|
|
[0x662F, 0x0C], |
|
|
[0x6318, 0x0D], |
|
|
[0x6C41, 0x0E], |
|
|
[0x6976, 0x0F], |
|
|
[0x1689, 0x10], |
|
|
[0x13BE, 0x11], |
|
|
[0x1CE7, 0x12], |
|
|
[0x19D0, 0x13], |
|
|
[0x0762, 0x14], |
|
|
[0x0255, 0x15], |
|
|
[0x0D0C, 0x16], |
|
|
[0x083B, 0x17], |
|
|
[0x355F, 0x18], |
|
|
[0x3068, 0x19], |
|
|
[0x3F31, 0x1A], |
|
|
[0x3A06, 0x1B], |
|
|
[0x24B4, 0x1C], |
|
|
[0x2183, 0x1D], |
|
|
[0x2EDA, 0x1E], |
|
|
[0x2BED, 0x1F], |
|
|
]; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) |
|
|
{ |
|
|
$formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2); |
|
|
if ($formatInfo != null) { |
|
|
return $formatInfo; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return self::doDecodeFormatInformation($maskedFormatInfo1 ^ self::$FORMAT_INFO_MASK_QR, |
|
|
$maskedFormatInfo2 ^ self::$FORMAT_INFO_MASK_QR); |
|
|
} |
|
|
|
|
|
private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) |
|
|
{ |
|
|
|
|
|
$bestDifference = PHP_INT_MAX; |
|
|
$bestFormatInfo = 0; |
|
|
foreach (self::$FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) { |
|
|
$targetInfo = $decodeInfo[0]; |
|
|
if ($targetInfo == $maskedFormatInfo1 || $targetInfo == $maskedFormatInfo2) { |
|
|
|
|
|
return new FormatInformation($decodeInfo[1]); |
|
|
} |
|
|
$bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo); |
|
|
if ($bitsDifference < $bestDifference) { |
|
|
$bestFormatInfo = $decodeInfo[1]; |
|
|
$bestDifference = $bitsDifference; |
|
|
} |
|
|
if ($maskedFormatInfo1 != $maskedFormatInfo2) { |
|
|
|
|
|
$bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo); |
|
|
if ($bitsDifference < $bestDifference) { |
|
|
$bestFormatInfo = $decodeInfo[1]; |
|
|
$bestDifference = $bitsDifference; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if ($bestDifference <= 3) { |
|
|
return new FormatInformation($bestFormatInfo); |
|
|
} |
|
|
|
|
|
return null; |
|
|
} |
|
|
|
|
|
public static function numBitsDiffering($a, $b) |
|
|
{ |
|
|
$a ^= $b; |
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
|
|
|
public function hashCode() |
|
|
{ |
|
|
return ($this->errorCorrectionLevel->ordinal() << 3) | (int)($this->dataMask); |
|
|
} |
|
|
|
|
|
|
|
|
public function equals($o) |
|
|
{ |
|
|
if (!($o instanceof FormatInformation)) { |
|
|
return false; |
|
|
} |
|
|
$other = $o; |
|
|
|
|
|
return $this->errorCorrectionLevel == $other->errorCorrectionLevel && |
|
|
$this->dataMask == $other->dataMask; |
|
|
} |
|
|
} |
|
|
|
|
|
FormatInformation::Init(); |
|
|
|