Passed
Push — main ( 02a232...b79bf8 )
by smiley
11:40
created

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