Passed
Push — v5 ( 28d116...eb2ec6 )
by smiley
01:38
created

EccLevel::getMaxBits()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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