Passed
Push — v5 ( 96135b...28d116 )
by smiley
02:03
created

EccLevel::getLevel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Class EccLevel
4
 *
5
 * @filesource   EccLevel.php
6
 * @created      19.11.2020
7
 * @package      chillerlan\QRCode\Common
8
 * @author       smiley <[email protected]>
9
 * @copyright    2020 smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\QRCode\Common;
14
15
use chillerlan\QRCode\QRCodeException;
16
17
use function array_column, array_combine, array_keys;
18
19
/**
20
 *
21
 */
22
final class EccLevel{
23
24
	// ISO/IEC 18004:2000 Tables 12, 25
25
26
	/** @var int */
27
	public const L = 0b01; // 7%.
28
	/** @var int */
29
	public const M = 0b00; // 15%.
30
	/** @var int */
31
	public const Q = 0b11; // 25%.
32
	/** @var int */
33
	public const H = 0b10; // 30%.
34
35
	/**
36
	 * References to the keys of the following tables:
37
	 *
38
	 * @see \chillerlan\QRCode\Common\Version::MAX_BITS
39
	 * @see \chillerlan\QRCode\Common\EccLevel::RSBLOCKS
40
	 * @see \chillerlan\QRCode\Data\QRMatrix::formatPattern
41
	 *
42
	 * @var int[]
43
	 */
44
	public const MODES = [
45
		self::L => 0,
46
		self::M => 1,
47
		self::Q => 2,
48
		self::H => 3,
49
	];
50
51
	/**
52
	 * ISO/IEC 18004:2000 Tables 13-22
53
	 *
54
	 * @see http://www.thonky.com/qr-code-tutorial/error-correction-table
55
	 *
56
	 * @var int [][][]
57
	 */
58
	private const RSBLOCKS = [
59
		1  => [[ 1,  0,  26,  19], [ 1,  0, 26, 16], [ 1,  0, 26, 13], [ 1,  0, 26,  9]],
60
		2  => [[ 1,  0,  44,  34], [ 1,  0, 44, 28], [ 1,  0, 44, 22], [ 1,  0, 44, 16]],
61
		3  => [[ 1,  0,  70,  55], [ 1,  0, 70, 44], [ 2,  0, 35, 17], [ 2,  0, 35, 13]],
62
		4  => [[ 1,  0, 100,  80], [ 2,  0, 50, 32], [ 2,  0, 50, 24], [ 4,  0, 25,  9]],
63
		5  => [[ 1,  0, 134, 108], [ 2,  0, 67, 43], [ 2,  2, 33, 15], [ 2,  2, 33, 11]],
64
		6  => [[ 2,  0,  86,  68], [ 4,  0, 43, 27], [ 4,  0, 43, 19], [ 4,  0, 43, 15]],
65
		7  => [[ 2,  0,  98,  78], [ 4,  0, 49, 31], [ 2,  4, 32, 14], [ 4,  1, 39, 13]],
66
		8  => [[ 2,  0, 121,  97], [ 2,  2, 60, 38], [ 4,  2, 40, 18], [ 4,  2, 40, 14]],
67
		9  => [[ 2,  0, 146, 116], [ 3,  2, 58, 36], [ 4,  4, 36, 16], [ 4,  4, 36, 12]],
68
		10 => [[ 2,  2,  86,  68], [ 4,  1, 69, 43], [ 6,  2, 43, 19], [ 6,  2, 43, 15]],
69
		11 => [[ 4,  0, 101,  81], [ 1,  4, 80, 50], [ 4,  4, 50, 22], [ 3,  8, 36, 12]],
70
		12 => [[ 2,  2, 116,  92], [ 6,  2, 58, 36], [ 4,  6, 46, 20], [ 7,  4, 42, 14]],
71
		13 => [[ 4,  0, 133, 107], [ 8,  1, 59, 37], [ 8,  4, 44, 20], [12,  4, 33, 11]],
72
		14 => [[ 3,  1, 145, 115], [ 4,  5, 64, 40], [11,  5, 36, 16], [11,  5, 36, 12]],
73
		15 => [[ 5,  1, 109,  87], [ 5,  5, 65, 41], [ 5,  7, 54, 24], [11,  7, 36, 12]],
74
		16 => [[ 5,  1, 122,  98], [ 7,  3, 73, 45], [15,  2, 43, 19], [ 3, 13, 45, 15]],
75
		17 => [[ 1,  5, 135, 107], [10,  1, 74, 46], [ 1, 15, 50, 22], [ 2, 17, 42, 14]],
76
		18 => [[ 5,  1, 150, 120], [ 9,  4, 69, 43], [17,  1, 50, 22], [ 2, 19, 42, 14]],
77
		19 => [[ 3,  4, 141, 113], [ 3, 11, 70, 44], [17,  4, 47, 21], [ 9, 16, 39, 13]],
78
		20 => [[ 3,  5, 135, 107], [ 3, 13, 67, 41], [15,  5, 54, 24], [15, 10, 43, 15]],
79
		21 => [[ 4,  4, 144, 116], [17,  0, 68, 42], [17,  6, 50, 22], [19,  6, 46, 16]],
80
		22 => [[ 2,  7, 139, 111], [17,  0, 74, 46], [ 7, 16, 54, 24], [34,  0, 37, 13]],
81
		23 => [[ 4,  5, 151, 121], [ 4, 14, 75, 47], [11, 14, 54, 24], [16, 14, 45, 15]],
82
		24 => [[ 6,  4, 147, 117], [ 6, 14, 73, 45], [11, 16, 54, 24], [30,  2, 46, 16]],
83
		25 => [[ 8,  4, 132, 106], [ 8, 13, 75, 47], [ 7, 22, 54, 24], [22, 13, 45, 15]],
84
		26 => [[10,  2, 142, 114], [19,  4, 74, 46], [28,  6, 50, 22], [33,  4, 46, 16]],
85
		27 => [[ 8,  4, 152, 122], [22,  3, 73, 45], [ 8, 26, 53, 23], [12, 28, 45, 15]],
86
		28 => [[ 3, 10, 147, 117], [ 3, 23, 73, 45], [ 4, 31, 54, 24], [11, 31, 45, 15]],
87
		29 => [[ 7,  7, 146, 116], [21,  7, 73, 45], [ 1, 37, 53, 23], [19, 26, 45, 15]],
88
		30 => [[ 5, 10, 145, 115], [19, 10, 75, 47], [15, 25, 54, 24], [23, 25, 45, 15]],
89
		31 => [[13,  3, 145, 115], [ 2, 29, 74, 46], [42,  1, 54, 24], [23, 28, 45, 15]],
90
		32 => [[17,  0, 145, 115], [10, 23, 74, 46], [10, 35, 54, 24], [19, 35, 45, 15]],
91
		33 => [[17,  1, 145, 115], [14, 21, 74, 46], [29, 19, 54, 24], [11, 46, 45, 15]],
92
		34 => [[13,  6, 145, 115], [14, 23, 74, 46], [44,  7, 54, 24], [59,  1, 46, 16]],
93
		35 => [[12,  7, 151, 121], [12, 26, 75, 47], [39, 14, 54, 24], [22, 41, 45, 15]],
94
		36 => [[ 6, 14, 151, 121], [ 6, 34, 75, 47], [46, 10, 54, 24], [ 2, 64, 45, 15]],
95
		37 => [[17,  4, 152, 122], [29, 14, 74, 46], [49, 10, 54, 24], [24, 46, 45, 15]],
96
		38 => [[ 4, 18, 152, 122], [13, 32, 74, 46], [48, 14, 54, 24], [42, 32, 45, 15]],
97
		39 => [[20,  4, 147, 117], [40,  7, 75, 47], [43, 22, 54, 24], [10, 67, 45, 15]],
98
		40 => [[19,  6, 148, 118], [18, 31, 75, 47], [34, 34, 54, 24], [20, 61, 45, 15]],
99
	];
100
101
	/**
102
	 * ISO/IEC 18004:2000 Tables 7-11 - Number of symbol characters and input data capacity for versions 1 to 40
103
	 *
104
	 * @var int [][]
105
	 */
106
	private const MAX_BITS = [
107
	//  v  => [    L,     M,     Q,     H]  // modules
108
		1  => [  152,   128,   104,    72], //  21
109
		2  => [  272,   224,   176,   128], //  25
110
		3  => [  440,   352,   272,   208], //  29
111
		4  => [  640,   512,   384,   288], //  33
112
		5  => [  864,   688,   496,   368], //  37
113
		6  => [ 1088,   864,   608,   480], //  41
114
		7  => [ 1248,   992,   704,   528], //  45
115
		8  => [ 1552,  1232,   880,   688], //  49
116
		9  => [ 1856,  1456,  1056,   800], //  53
117
		10 => [ 2192,  1728,  1232,   976], //  57
118
		11 => [ 2592,  2032,  1440,  1120], //  61
119
		12 => [ 2960,  2320,  1648,  1264], //  65
120
		13 => [ 3424,  2672,  1952,  1440], //  69 NICE!
121
		14 => [ 3688,  2920,  2088,  1576], //  73
122
		15 => [ 4184,  3320,  2360,  1784], //  77
123
		16 => [ 4712,  3624,  2600,  2024], //  81
124
		17 => [ 5176,  4056,  2936,  2264], //  85
125
		18 => [ 5768,  4504,  3176,  2504], //  89
126
		19 => [ 6360,  5016,  3560,  2728], //  93
127
		20 => [ 6888,  5352,  3880,  3080], //  97
128
		21 => [ 7456,  5712,  4096,  3248], // 101
129
		22 => [ 8048,  6256,  4544,  3536], // 105
130
		23 => [ 8752,  6880,  4912,  3712], // 109
131
		24 => [ 9392,  7312,  5312,  4112], // 113
132
		25 => [10208,  8000,  5744,  4304], // 117
133
		26 => [10960,  8496,  6032,  4768], // 121
134
		27 => [11744,  9024,  6464,  5024], // 125
135
		28 => [12248,  9544,  6968,  5288], // 129
136
		29 => [13048, 10136,  7288,  5608], // 133
137
		30 => [13880, 10984,  7880,  5960], // 137
138
		31 => [14744, 11640,  8264,  6344], // 141
139
		32 => [15640, 12328,  8920,  6760], // 145
140
		33 => [16568, 13048,  9368,  7208], // 149
141
		34 => [17528, 13800,  9848,  7688], // 153
142
		35 => [18448, 14496, 10288,  7888], // 157
143
		36 => [19472, 15312, 10832,  8432], // 161
144
		37 => [20528, 15936, 11408,  8768], // 165
145
		38 => [21616, 16816, 12016,  9136], // 169
146
		39 => [22496, 17728, 12656,  9776], // 173
147
		40 => [23648, 18672, 13328, 10208], // 177
148
	];
149
150
	/**
151
	 * ISO/IEC 18004:2000 Section 8.9 - Format Information
152
	 *
153
	 * ECC level -> mask pattern
154
	 *
155
	 * @var int[][]
156
	 */
157
	private const FORMAT_PATTERN = [
158
		[ // L
159
		  0b111011111000100,
160
		  0b111001011110011,
161
		  0b111110110101010,
162
		  0b111100010011101,
163
		  0b110011000101111,
164
		  0b110001100011000,
165
		  0b110110001000001,
166
		  0b110100101110110,
167
		],
168
		[ // M
169
		  0b101010000010010,
170
		  0b101000100100101,
171
		  0b101111001111100,
172
		  0b101101101001011,
173
		  0b100010111111001,
174
		  0b100000011001110,
175
		  0b100111110010111,
176
		  0b100101010100000,
177
		],
178
		[ // Q
179
		  0b011010101011111,
180
		  0b011000001101000,
181
		  0b011111100110001,
182
		  0b011101000000110,
183
		  0b010010010110100,
184
		  0b010000110000011,
185
		  0b010111011011010,
186
		  0b010101111101101,
187
		],
188
		[ // H
189
		  0b001011010001001,
190
		  0b001001110111110,
191
		  0b001110011100111,
192
		  0b001100111010000,
193
		  0b000011101100010,
194
		  0b000001001010101,
195
		  0b000110100001100,
196
		  0b000100000111011,
197
		],
198
	];
199
200
	private int $eccLevel;
201
202
	/**
203
	 * @param int $eccLevel containing the two bits encoding a QR Code's error correction level
204
	 *
205
	 * @throws \chillerlan\QRCode\QRCodeException
206
	 */
207
	public function __construct(int $eccLevel){
208
209
		if((0b11 & $eccLevel) !== $eccLevel){
210
			throw new QRCodeException('invalid ECC level');
211
		}
212
213
		$this->eccLevel = $eccLevel;
214
	}
215
216
	/**
217
	 * returns the current ECC level
218
	 */
219
	public function getLevel():int{
220
		return $this->eccLevel;
221
	}
222
223
	/**
224
	 * returns the ordinal value of the current ECC level
225
	 */
226
	public function getOrdinal():int{
227
		return self::MODES[$this->eccLevel];
228
	}
229
230
	/**
231
	 * returns ECC block information for the given $version and $eccLevel
232
	 *
233
	 * @return int[]
234
	 * @throws \chillerlan\QRCode\QRCodeException
235
	 */
236
	public function getRSBlocks(int $version):array{
237
238
		if($version < 1 || $version > 40){
239
			throw new QRCodeException('invalid version');
240
		}
241
242
		return self::RSBLOCKS[$version][self::MODES[$this->eccLevel]];
243
	}
244
245
	/**
246
	 * returns the format pattern for the given $eccLevel and $maskPattern
247
	 *
248
	 * @throws \chillerlan\QRCode\QRCodeException
249
	 */
250
	public function getformatPattern(int $maskPattern):int{
251
252
		if((0b111 & $maskPattern) !== $maskPattern){
253
			throw new QRCodeException('invalid mask pattern');
254
		}
255
256
		return self::FORMAT_PATTERN[self::MODES[$this->eccLevel]][$maskPattern];
257
	}
258
259
	/**
260
	 * returns an array wit the max bit lengths for version 1-40 and the current ECC level
261
	 */
262
	public function getMaxBits():array{
263
		return array_combine(
264
			array_keys(self::MAX_BITS),
265
			array_column(self::MAX_BITS, self::MODES[$this->eccLevel])
266
		);
267
	}
268
269
}
270