PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.4
Booking for Appointments and Events Calendar – Amelia v2.4.4
2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / vendor / bacon / bacon-qr-code / src / Common / FormatInformation.php
ameliabooking / vendor / bacon / bacon-qr-code / src / Common Last commit date
BitArray.php 8 months ago BitMatrix.php 8 months ago BitUtils.php 8 months ago CharacterSetEci.php 8 months ago EcBlock.php 8 months ago EcBlocks.php 8 months ago ErrorCorrectionLevel.php 8 months ago FormatInformation.php 8 months ago Mode.php 8 months ago ReedSolomonCodec.php 8 months ago Version.php 8 months ago
FormatInformation.php
204 lines
1 <?php
2 /**
3 * BaconQrCode
4 *
5 * @link http://github.com/Bacon/BaconQrCode For the canonical source repository
6 * @copyright 2013 Ben 'DASPRiD' Scholzen
7 * @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
8 */
9
10 namespace BaconQrCode\Common;
11
12 /**
13 * Encapsulates a QR Code's format information, including the data mask used and error correction level.
14 */
15 class FormatInformation
16 {
17 /**
18 * Mask for format information.
19 */
20 private const FORMAT_INFO_MASK_QR = 0x5412;
21
22 /**
23 * Lookup table for decoding format information.
24 *
25 * See ISO 18004:2006, Annex C, Table C.1
26 */
27 private const FORMAT_INFO_DECODE_LOOKUP = [
28 [0x5412, 0x00],
29 [0x5125, 0x01],
30 [0x5e7c, 0x02],
31 [0x5b4b, 0x03],
32 [0x45f9, 0x04],
33 [0x40ce, 0x05],
34 [0x4f97, 0x06],
35 [0x4aa0, 0x07],
36 [0x77c4, 0x08],
37 [0x72f3, 0x09],
38 [0x7daa, 0x0a],
39 [0x789d, 0x0b],
40 [0x662f, 0x0c],
41 [0x6318, 0x0d],
42 [0x6c41, 0x0e],
43 [0x6976, 0x0f],
44 [0x1689, 0x10],
45 [0x13be, 0x11],
46 [0x1ce7, 0x12],
47 [0x19d0, 0x13],
48 [0x0762, 0x14],
49 [0x0255, 0x15],
50 [0x0d0c, 0x16],
51 [0x083b, 0x17],
52 [0x355f, 0x18],
53 [0x3068, 0x19],
54 [0x3f31, 0x1a],
55 [0x3a06, 0x1b],
56 [0x24b4, 0x1c],
57 [0x2183, 0x1d],
58 [0x2eda, 0x1e],
59 [0x2bed, 0x1f],
60 ];
61
62 /**
63 * Offset i holds the number of 1 bits in the binary representation of i.
64 *
65 * @var int[]
66 */
67 private const BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
68
69 /**
70 * Error correction level.
71 *
72 * @var ErrorCorrectionLevel
73 */
74 private $ecLevel;
75
76 /**
77 * Data mask.
78 *
79 * @var int
80 */
81 private $dataMask;
82
83 protected function __construct(int $formatInfo)
84 {
85 $this->ecLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x3);
86 $this->dataMask = $formatInfo & 0x7;
87 }
88
89 /**
90 * Checks how many bits are different between two integers.
91 */
92 public static function numBitsDiffering(int $a, int $b) : int
93 {
94 $a ^= $b;
95
96 return (
97 self::BITS_SET_IN_HALF_BYTE[$a & 0xf]
98 + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 4) & 0xf)]
99 + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 8) & 0xf)]
100 + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 12) & 0xf)]
101 + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 16) & 0xf)]
102 + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 20) & 0xf)]
103 + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 24) & 0xf)]
104 + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 28) & 0xf)]
105 );
106 }
107
108 /**
109 * Decodes format information.
110 */
111 public static function decodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2) : ?self
112 {
113 $formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
114
115 if (null !== $formatInfo) {
116 return $formatInfo;
117 }
118
119 // Should return null, but, some QR codes apparently do not mask this info. Try again by actually masking the
120 // pattern first.
121 return self::doDecodeFormatInformation(
122 $maskedFormatInfo1 ^ self::FORMAT_INFO_MASK_QR,
123 $maskedFormatInfo2 ^ self::FORMAT_INFO_MASK_QR
124 );
125 }
126
127 /**
128 * Internal method for decoding format information.
129 */
130 private static function doDecodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2) : ?self
131 {
132 $bestDifference = PHP_INT_MAX;
133 $bestFormatInfo = 0;
134
135 foreach (self::FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) {
136 $targetInfo = $decodeInfo[0];
137
138 if ($targetInfo === $maskedFormatInfo1 || $targetInfo === $maskedFormatInfo2) {
139 // Found an exact match
140 return new self($decodeInfo[1]);
141 }
142
143 $bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);
144
145 if ($bitsDifference < $bestDifference) {
146 $bestFormatInfo = $decodeInfo[1];
147 $bestDifference = $bitsDifference;
148 }
149
150 if ($maskedFormatInfo1 !== $maskedFormatInfo2) {
151 // Also try the other option
152 $bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);
153
154 if ($bitsDifference < $bestDifference) {
155 $bestFormatInfo = $decodeInfo[1];
156 $bestDifference = $bitsDifference;
157 }
158 }
159 }
160
161 // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match.
162 if ($bestDifference <= 3) {
163 return new self($bestFormatInfo);
164 }
165
166 return null;
167 }
168
169 /**
170 * Returns the error correction level.
171 */
172 public function getErrorCorrectionLevel() : ErrorCorrectionLevel
173 {
174 return $this->ecLevel;
175 }
176
177 /**
178 * Returns the data mask.
179 */
180 public function getDataMask() : int
181 {
182 return $this->dataMask;
183 }
184
185 /**
186 * Hashes the code of the EC level.
187 */
188 public function hashCode() : int
189 {
190 return ($this->ecLevel->getBits() << 3) | $this->dataMask;
191 }
192
193 /**
194 * Verifies if this instance equals another one.
195 */
196 public function equals(self $other) : bool
197 {
198 return (
199 $this->ecLevel === $other->ecLevel
200 && $this->dataMask === $other->dataMask
201 );
202 }
203 }
204