From d58c2044f1834ce29efd1c7c5caec0d989a5ee39 Mon Sep 17 00:00:00 2001 From: codemasher Date: Sun, 22 Nov 2020 03:04:42 +0100 Subject: [PATCH] :bath: extract EccLevel --- src/Common/EccLevel.php | 254 ++++++++++++++++++++++++++++++++++ src/Common/Version.php | 56 -------- src/Data/QRData.php | 59 +------- src/Data/QRMatrix.php | 87 ++---------- src/QRCode.php | 27 ---- src/QROptionsTrait.php | 6 +- tests/Data/QRMatrixTest.php | 28 ++-- tests/Output/QRStringTest.php | 4 +- 8 files changed, 290 insertions(+), 231 deletions(-) create mode 100644 src/Common/EccLevel.php diff --git a/src/Common/EccLevel.php b/src/Common/EccLevel.php new file mode 100644 index 000000000..4091843ed --- /dev/null +++ b/src/Common/EccLevel.php @@ -0,0 +1,254 @@ + + * @copyright 2020 smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Common; + +use chillerlan\QRCode\QRCodeException; + +/** + */ +class EccLevel{ + + // ISO/IEC 18004:2000 Tables 12, 25 + + /** @var int */ + public const L = 0b01; // 7%. + /** @var int */ + public const M = 0b00; // 15%. + /** @var int */ + public const Q = 0b11; // 25%. + /** @var int */ + public const H = 0b10; // 30%. + + /** + * References to the keys of the following tables: + * + * @see \chillerlan\QRCode\Common\Version::MAX_BITS + * @see \chillerlan\QRCode\Common\EccLevel::RSBLOCKS + * @see \chillerlan\QRCode\Data\QRMatrix::formatPattern + * + * @var int[] + */ + public const MODES = [ + self::L => 0, + self::M => 1, + self::Q => 2, + self::H => 3, + ]; + + /** + * ISO/IEC 18004:2000 Tables 13-22 + * + * @see http://www.thonky.com/qr-code-tutorial/error-correction-table + * + * @var int [][][] + */ + const RSBLOCKS = [ + 1 => [[ 1, 0, 26, 19], [ 1, 0, 26, 16], [ 1, 0, 26, 13], [ 1, 0, 26, 9]], + 2 => [[ 1, 0, 44, 34], [ 1, 0, 44, 28], [ 1, 0, 44, 22], [ 1, 0, 44, 16]], + 3 => [[ 1, 0, 70, 55], [ 1, 0, 70, 44], [ 2, 0, 35, 17], [ 2, 0, 35, 13]], + 4 => [[ 1, 0, 100, 80], [ 2, 0, 50, 32], [ 2, 0, 50, 24], [ 4, 0, 25, 9]], + 5 => [[ 1, 0, 134, 108], [ 2, 0, 67, 43], [ 2, 2, 33, 15], [ 2, 2, 33, 11]], + 6 => [[ 2, 0, 86, 68], [ 4, 0, 43, 27], [ 4, 0, 43, 19], [ 4, 0, 43, 15]], + 7 => [[ 2, 0, 98, 78], [ 4, 0, 49, 31], [ 2, 4, 32, 14], [ 4, 1, 39, 13]], + 8 => [[ 2, 0, 121, 97], [ 2, 2, 60, 38], [ 4, 2, 40, 18], [ 4, 2, 40, 14]], + 9 => [[ 2, 0, 146, 116], [ 3, 2, 58, 36], [ 4, 4, 36, 16], [ 4, 4, 36, 12]], + 10 => [[ 2, 2, 86, 68], [ 4, 1, 69, 43], [ 6, 2, 43, 19], [ 6, 2, 43, 15]], + 11 => [[ 4, 0, 101, 81], [ 1, 4, 80, 50], [ 4, 4, 50, 22], [ 3, 8, 36, 12]], + 12 => [[ 2, 2, 116, 92], [ 6, 2, 58, 36], [ 4, 6, 46, 20], [ 7, 4, 42, 14]], + 13 => [[ 4, 0, 133, 107], [ 8, 1, 59, 37], [ 8, 4, 44, 20], [12, 4, 33, 11]], + 14 => [[ 3, 1, 145, 115], [ 4, 5, 64, 40], [11, 5, 36, 16], [11, 5, 36, 12]], + 15 => [[ 5, 1, 109, 87], [ 5, 5, 65, 41], [ 5, 7, 54, 24], [11, 7, 36, 12]], + 16 => [[ 5, 1, 122, 98], [ 7, 3, 73, 45], [15, 2, 43, 19], [ 3, 13, 45, 15]], + 17 => [[ 1, 5, 135, 107], [10, 1, 74, 46], [ 1, 15, 50, 22], [ 2, 17, 42, 14]], + 18 => [[ 5, 1, 150, 120], [ 9, 4, 69, 43], [17, 1, 50, 22], [ 2, 19, 42, 14]], + 19 => [[ 3, 4, 141, 113], [ 3, 11, 70, 44], [17, 4, 47, 21], [ 9, 16, 39, 13]], + 20 => [[ 3, 5, 135, 107], [ 3, 13, 67, 41], [15, 5, 54, 24], [15, 10, 43, 15]], + 21 => [[ 4, 4, 144, 116], [17, 0, 68, 42], [17, 6, 50, 22], [19, 6, 46, 16]], + 22 => [[ 2, 7, 139, 111], [17, 0, 74, 46], [ 7, 16, 54, 24], [34, 0, 37, 13]], + 23 => [[ 4, 5, 151, 121], [ 4, 14, 75, 47], [11, 14, 54, 24], [16, 14, 45, 15]], + 24 => [[ 6, 4, 147, 117], [ 6, 14, 73, 45], [11, 16, 54, 24], [30, 2, 46, 16]], + 25 => [[ 8, 4, 132, 106], [ 8, 13, 75, 47], [ 7, 22, 54, 24], [22, 13, 45, 15]], + 26 => [[10, 2, 142, 114], [19, 4, 74, 46], [28, 6, 50, 22], [33, 4, 46, 16]], + 27 => [[ 8, 4, 152, 122], [22, 3, 73, 45], [ 8, 26, 53, 23], [12, 28, 45, 15]], + 28 => [[ 3, 10, 147, 117], [ 3, 23, 73, 45], [ 4, 31, 54, 24], [11, 31, 45, 15]], + 29 => [[ 7, 7, 146, 116], [21, 7, 73, 45], [ 1, 37, 53, 23], [19, 26, 45, 15]], + 30 => [[ 5, 10, 145, 115], [19, 10, 75, 47], [15, 25, 54, 24], [23, 25, 45, 15]], + 31 => [[13, 3, 145, 115], [ 2, 29, 74, 46], [42, 1, 54, 24], [23, 28, 45, 15]], + 32 => [[17, 0, 145, 115], [10, 23, 74, 46], [10, 35, 54, 24], [19, 35, 45, 15]], + 33 => [[17, 1, 145, 115], [14, 21, 74, 46], [29, 19, 54, 24], [11, 46, 45, 15]], + 34 => [[13, 6, 145, 115], [14, 23, 74, 46], [44, 7, 54, 24], [59, 1, 46, 16]], + 35 => [[12, 7, 151, 121], [12, 26, 75, 47], [39, 14, 54, 24], [22, 41, 45, 15]], + 36 => [[ 6, 14, 151, 121], [ 6, 34, 75, 47], [46, 10, 54, 24], [ 2, 64, 45, 15]], + 37 => [[17, 4, 152, 122], [29, 14, 74, 46], [49, 10, 54, 24], [24, 46, 45, 15]], + 38 => [[ 4, 18, 152, 122], [13, 32, 74, 46], [48, 14, 54, 24], [42, 32, 45, 15]], + 39 => [[20, 4, 147, 117], [40, 7, 75, 47], [43, 22, 54, 24], [10, 67, 45, 15]], + 40 => [[19, 6, 148, 118], [18, 31, 75, 47], [34, 34, 54, 24], [20, 61, 45, 15]], + ]; + + /** + * ISO/IEC 18004:2000 Tables 7-11 - Number of symbol characters and input data capacity for versions 1 to 40 + * + * @var int [][] + */ + const MAX_BITS = [ + // v => [ L, M, Q, H] // modules + 1 => [ 152, 128, 104, 72], // 21 + 2 => [ 272, 224, 176, 128], // 25 + 3 => [ 440, 352, 272, 208], // 29 + 4 => [ 640, 512, 384, 288], // 33 + 5 => [ 864, 688, 496, 368], // 37 + 6 => [ 1088, 864, 608, 480], // 41 + 7 => [ 1248, 992, 704, 528], // 45 + 8 => [ 1552, 1232, 880, 688], // 49 + 9 => [ 1856, 1456, 1056, 800], // 53 + 10 => [ 2192, 1728, 1232, 976], // 57 + 11 => [ 2592, 2032, 1440, 1120], // 61 + 12 => [ 2960, 2320, 1648, 1264], // 65 + 13 => [ 3424, 2672, 1952, 1440], // 69 NICE! + 14 => [ 3688, 2920, 2088, 1576], // 73 + 15 => [ 4184, 3320, 2360, 1784], // 77 + 16 => [ 4712, 3624, 2600, 2024], // 81 + 17 => [ 5176, 4056, 2936, 2264], // 85 + 18 => [ 5768, 4504, 3176, 2504], // 89 + 19 => [ 6360, 5016, 3560, 2728], // 93 + 20 => [ 6888, 5352, 3880, 3080], // 97 + 21 => [ 7456, 5712, 4096, 3248], // 101 + 22 => [ 8048, 6256, 4544, 3536], // 105 + 23 => [ 8752, 6880, 4912, 3712], // 109 + 24 => [ 9392, 7312, 5312, 4112], // 113 + 25 => [10208, 8000, 5744, 4304], // 117 + 26 => [10960, 8496, 6032, 4768], // 121 + 27 => [11744, 9024, 6464, 5024], // 125 + 28 => [12248, 9544, 6968, 5288], // 129 + 29 => [13048, 10136, 7288, 5608], // 133 + 30 => [13880, 10984, 7880, 5960], // 137 + 31 => [14744, 11640, 8264, 6344], // 141 + 32 => [15640, 12328, 8920, 6760], // 145 + 33 => [16568, 13048, 9368, 7208], // 149 + 34 => [17528, 13800, 9848, 7688], // 153 + 35 => [18448, 14496, 10288, 7888], // 157 + 36 => [19472, 15312, 10832, 8432], // 161 + 37 => [20528, 15936, 11408, 8768], // 165 + 38 => [21616, 16816, 12016, 9136], // 169 + 39 => [22496, 17728, 12656, 9776], // 173 + 40 => [23648, 18672, 13328, 10208], // 177 + ]; + + /** + * ISO/IEC 18004:2000 Section 8.9 - Format Information + * + * ECC level -> mask pattern + * + * @var int[][] + */ + protected const formatPattern = [ + [ // L + 0b111011111000100, + 0b111001011110011, + 0b111110110101010, + 0b111100010011101, + 0b110011000101111, + 0b110001100011000, + 0b110110001000001, + 0b110100101110110, + ], + [ // M + 0b101010000010010, + 0b101000100100101, + 0b101111001111100, + 0b101101101001011, + 0b100010111111001, + 0b100000011001110, + 0b100111110010111, + 0b100101010100000, + ], + [ // Q + 0b011010101011111, + 0b011000001101000, + 0b011111100110001, + 0b011101000000110, + 0b010010010110100, + 0b010000110000011, + 0b010111011011010, + 0b010101111101101, + ], + [ // H + 0b001011010001001, + 0b001001110111110, + 0b001110011100111, + 0b001100111010000, + 0b000011101100010, + 0b000001001010101, + 0b000110100001100, + 0b000100000111011, + ], + ]; + + private int $eccLevel; + + /** + * @param int $eccLevel containing the two bits encoding a QR Code's error correction level + * + * @throws \chillerlan\QRCode\QRCodeException + */ + public function __construct(int $eccLevel){ + + if((0b11 & $eccLevel) !== $eccLevel){ + throw new QRCodeException('invalid ECC level'); + } + + $this->eccLevel = $eccLevel; + } + + public function getOrdinal():int{ + return $this->eccLevel; + } + + /** + * returns ECC block information for the given $version and $eccLevel + * + * @return int[] + * @throws \chillerlan\QRCode\QRCodeException + */ + public function getRSBlocks(int $version):array{ + + if($version < 1 || $version > 40){ + throw new QRCodeException('invalid version'); + } + + return self::RSBLOCKS[$version][self::MODES[$this->eccLevel]]; + } + + /** + * returns the format pattern for the given $eccLevel and $maskPattern + * + * @return int + * @throws \chillerlan\QRCode\QRCodeException + */ + public function getformatPattern(int $maskPattern):int{ + + if((0b111 & $maskPattern) !== $maskPattern){ + throw new QRCodeException('invalid mask pattern'); + } + + return self::formatPattern[self::MODES[$this->eccLevel]][$maskPattern]; + } + + public function getMaxBits():array{ + return array_combine( + array_keys(self::MAX_BITS), + array_column(self::MAX_BITS, self::MODES[$this->eccLevel]) + ); + } + +} diff --git a/src/Common/Version.php b/src/Common/Version.php index 4fd7bf6b2..9c75914b1 100644 --- a/src/Common/Version.php +++ b/src/Common/Version.php @@ -21,55 +21,6 @@ use function array_column, array_combine, array_keys; */ class Version{ - /** - * ISO/IEC 18004:2000 Tables 7-11 - Number of symbol characters and input data capacity for versions 1 to 40 - * - * @var int [][] - */ - const MAX_BITS = [ - // v => [ L, M, Q, H] // modules - 1 => [ 152, 128, 104, 72], // 21 - 2 => [ 272, 224, 176, 128], // 25 - 3 => [ 440, 352, 272, 208], // 29 - 4 => [ 640, 512, 384, 288], // 33 - 5 => [ 864, 688, 496, 368], // 37 - 6 => [ 1088, 864, 608, 480], // 41 - 7 => [ 1248, 992, 704, 528], // 45 - 8 => [ 1552, 1232, 880, 688], // 49 - 9 => [ 1856, 1456, 1056, 800], // 53 - 10 => [ 2192, 1728, 1232, 976], // 57 - 11 => [ 2592, 2032, 1440, 1120], // 61 - 12 => [ 2960, 2320, 1648, 1264], // 65 - 13 => [ 3424, 2672, 1952, 1440], // 69 NICE! - 14 => [ 3688, 2920, 2088, 1576], // 73 - 15 => [ 4184, 3320, 2360, 1784], // 77 - 16 => [ 4712, 3624, 2600, 2024], // 81 - 17 => [ 5176, 4056, 2936, 2264], // 85 - 18 => [ 5768, 4504, 3176, 2504], // 89 - 19 => [ 6360, 5016, 3560, 2728], // 93 - 20 => [ 6888, 5352, 3880, 3080], // 97 - 21 => [ 7456, 5712, 4096, 3248], // 101 - 22 => [ 8048, 6256, 4544, 3536], // 105 - 23 => [ 8752, 6880, 4912, 3712], // 109 - 24 => [ 9392, 7312, 5312, 4112], // 113 - 25 => [10208, 8000, 5744, 4304], // 117 - 26 => [10960, 8496, 6032, 4768], // 121 - 27 => [11744, 9024, 6464, 5024], // 125 - 28 => [12248, 9544, 6968, 5288], // 129 - 29 => [13048, 10136, 7288, 5608], // 133 - 30 => [13880, 10984, 7880, 5960], // 137 - 31 => [14744, 11640, 8264, 6344], // 141 - 32 => [15640, 12328, 8920, 6760], // 145 - 33 => [16568, 13048, 9368, 7208], // 149 - 34 => [17528, 13800, 9848, 7688], // 153 - 35 => [18448, 14496, 10288, 7888], // 157 - 36 => [19472, 15312, 10832, 8432], // 161 - 37 => [20528, 15936, 11408, 8768], // 165 - 38 => [21616, 16816, 12016, 9136], // 169 - 39 => [22496, 17728, 12656, 9776], // 173 - 40 => [23648, 18672, 13328, 10208], // 177 - ]; - /** * ISO/IEC 18004:2000 Annex E, Table E.1 - Row/column coordinates of center module of Alignment Patterns * @@ -199,11 +150,4 @@ class Version{ return self::ALIGNMENT_PATTERN[$this->version]; } - public static function getMaxBitsForEcc(int $eccLevel):array{ - return array_combine( - array_keys(self::MAX_BITS), - array_column(self::MAX_BITS, QRCode::ECC_MODES[$eccLevel]) - ); - } - } diff --git a/src/Data/QRData.php b/src/Data/QRData.php index 16b6dcf40..c934c063b 100644 --- a/src/Data/QRData.php +++ b/src/Data/QRData.php @@ -12,7 +12,7 @@ namespace chillerlan\QRCode\Data; -use chillerlan\QRCode\Common\{Mode, Version}; +use chillerlan\QRCode\Common\{EccLevel, Mode, Version}; use chillerlan\QRCode\QRCode; use chillerlan\QRCode\Helpers\{BitBuffer, Polynomial}; use chillerlan\Settings\SettingsContainerInterface; @@ -24,54 +24,6 @@ use function array_fill, array_merge, count, max, range, sprintf; */ class QRData{ - /** - * @see http://www.thonky.com/qr-code-tutorial/error-correction-table - * - * @var int [][][] - */ - const RSBLOCKS = [ - 1 => [[ 1, 0, 26, 19], [ 1, 0, 26, 16], [ 1, 0, 26, 13], [ 1, 0, 26, 9]], - 2 => [[ 1, 0, 44, 34], [ 1, 0, 44, 28], [ 1, 0, 44, 22], [ 1, 0, 44, 16]], - 3 => [[ 1, 0, 70, 55], [ 1, 0, 70, 44], [ 2, 0, 35, 17], [ 2, 0, 35, 13]], - 4 => [[ 1, 0, 100, 80], [ 2, 0, 50, 32], [ 2, 0, 50, 24], [ 4, 0, 25, 9]], - 5 => [[ 1, 0, 134, 108], [ 2, 0, 67, 43], [ 2, 2, 33, 15], [ 2, 2, 33, 11]], - 6 => [[ 2, 0, 86, 68], [ 4, 0, 43, 27], [ 4, 0, 43, 19], [ 4, 0, 43, 15]], - 7 => [[ 2, 0, 98, 78], [ 4, 0, 49, 31], [ 2, 4, 32, 14], [ 4, 1, 39, 13]], - 8 => [[ 2, 0, 121, 97], [ 2, 2, 60, 38], [ 4, 2, 40, 18], [ 4, 2, 40, 14]], - 9 => [[ 2, 0, 146, 116], [ 3, 2, 58, 36], [ 4, 4, 36, 16], [ 4, 4, 36, 12]], - 10 => [[ 2, 2, 86, 68], [ 4, 1, 69, 43], [ 6, 2, 43, 19], [ 6, 2, 43, 15]], - 11 => [[ 4, 0, 101, 81], [ 1, 4, 80, 50], [ 4, 4, 50, 22], [ 3, 8, 36, 12]], - 12 => [[ 2, 2, 116, 92], [ 6, 2, 58, 36], [ 4, 6, 46, 20], [ 7, 4, 42, 14]], - 13 => [[ 4, 0, 133, 107], [ 8, 1, 59, 37], [ 8, 4, 44, 20], [12, 4, 33, 11]], - 14 => [[ 3, 1, 145, 115], [ 4, 5, 64, 40], [11, 5, 36, 16], [11, 5, 36, 12]], - 15 => [[ 5, 1, 109, 87], [ 5, 5, 65, 41], [ 5, 7, 54, 24], [11, 7, 36, 12]], - 16 => [[ 5, 1, 122, 98], [ 7, 3, 73, 45], [15, 2, 43, 19], [ 3, 13, 45, 15]], - 17 => [[ 1, 5, 135, 107], [10, 1, 74, 46], [ 1, 15, 50, 22], [ 2, 17, 42, 14]], - 18 => [[ 5, 1, 150, 120], [ 9, 4, 69, 43], [17, 1, 50, 22], [ 2, 19, 42, 14]], - 19 => [[ 3, 4, 141, 113], [ 3, 11, 70, 44], [17, 4, 47, 21], [ 9, 16, 39, 13]], - 20 => [[ 3, 5, 135, 107], [ 3, 13, 67, 41], [15, 5, 54, 24], [15, 10, 43, 15]], - 21 => [[ 4, 4, 144, 116], [17, 0, 68, 42], [17, 6, 50, 22], [19, 6, 46, 16]], - 22 => [[ 2, 7, 139, 111], [17, 0, 74, 46], [ 7, 16, 54, 24], [34, 0, 37, 13]], - 23 => [[ 4, 5, 151, 121], [ 4, 14, 75, 47], [11, 14, 54, 24], [16, 14, 45, 15]], - 24 => [[ 6, 4, 147, 117], [ 6, 14, 73, 45], [11, 16, 54, 24], [30, 2, 46, 16]], - 25 => [[ 8, 4, 132, 106], [ 8, 13, 75, 47], [ 7, 22, 54, 24], [22, 13, 45, 15]], - 26 => [[10, 2, 142, 114], [19, 4, 74, 46], [28, 6, 50, 22], [33, 4, 46, 16]], - 27 => [[ 8, 4, 152, 122], [22, 3, 73, 45], [ 8, 26, 53, 23], [12, 28, 45, 15]], - 28 => [[ 3, 10, 147, 117], [ 3, 23, 73, 45], [ 4, 31, 54, 24], [11, 31, 45, 15]], - 29 => [[ 7, 7, 146, 116], [21, 7, 73, 45], [ 1, 37, 53, 23], [19, 26, 45, 15]], - 30 => [[ 5, 10, 145, 115], [19, 10, 75, 47], [15, 25, 54, 24], [23, 25, 45, 15]], - 31 => [[13, 3, 145, 115], [ 2, 29, 74, 46], [42, 1, 54, 24], [23, 28, 45, 15]], - 32 => [[17, 0, 145, 115], [10, 23, 74, 46], [10, 35, 54, 24], [19, 35, 45, 15]], - 33 => [[17, 1, 145, 115], [14, 21, 74, 46], [29, 19, 54, 24], [11, 46, 45, 15]], - 34 => [[13, 6, 145, 115], [14, 23, 74, 46], [44, 7, 54, 24], [59, 1, 46, 16]], - 35 => [[12, 7, 151, 121], [12, 26, 75, 47], [39, 14, 54, 24], [22, 41, 45, 15]], - 36 => [[ 6, 14, 151, 121], [ 6, 34, 75, 47], [46, 10, 54, 24], [ 2, 64, 45, 15]], - 37 => [[17, 4, 152, 122], [29, 14, 74, 46], [49, 10, 54, 24], [24, 46, 45, 15]], - 38 => [[ 4, 18, 152, 122], [13, 32, 74, 46], [48, 14, 54, 24], [42, 32, 45, 15]], - 39 => [[20, 4, 147, 117], [40, 7, 75, 47], [43, 22, 54, 24], [10, 67, 45, 15]], - 40 => [[19, 6, 148, 118], [18, 31, 75, 47], [34, 34, 54, 24], [20, 61, 45, 15]], - ]; - /** * current QR Code version */ @@ -111,6 +63,8 @@ class QRData{ */ protected BitBuffer $bitBuffer; + protected EccLevel $eccLevel; + /** * QRData constructor. * @@ -120,7 +74,8 @@ class QRData{ public function __construct(SettingsContainerInterface $options, array $dataSegments = null){ $this->options = $options; $this->bitBuffer = new BitBuffer; - $this->maxBitsForEcc = Version::getMaxBitsForEcc($this->options->eccLevel); + $this->eccLevel = new EccLevel($this->options->eccLevel); + $this->maxBitsForEcc = $this->eccLevel->getMaxBits(); if(!empty($dataSegments)){ $this->setData($dataSegments); @@ -154,7 +109,7 @@ class QRData{ * returns a fresh matrix object with the data written for the given $maskPattern */ public function initMatrix(int $maskPattern, bool $test = null):QRMatrix{ - return (new QRMatrix($this->version, $this->options->eccLevel)) + return (new QRMatrix($this->version, $this->eccLevel)) ->init($maskPattern, $test) ->mapData($this->maskECC(), $maskPattern) ; @@ -270,7 +225,7 @@ class QRData{ * @see http://www.thonky.com/qr-code-tutorial/error-correction-coding */ protected function maskECC():array{ - [$l1, $l2, $b1, $b2] = $this::RSBLOCKS[$this->version->getVersionNumber()][QRCode::ECC_MODES[$this->options->eccLevel]]; + [$l1, $l2, $b1, $b2] = $this->eccLevel->getRSBlocks($this->version->getVersionNumber()); $rsBlocks = array_fill(0, $l1, [$b1, $b2]); $rsCount = $l1 + $l2; diff --git a/src/Data/QRMatrix.php b/src/Data/QRMatrix.php index 742bc8f71..d4cbae5b4 100755 --- a/src/Data/QRMatrix.php +++ b/src/Data/QRMatrix.php @@ -12,11 +12,11 @@ namespace chillerlan\QRCode\Data; -use chillerlan\QRCode\Common\Version; +use chillerlan\QRCode\Common\{EccLevel, Version}; use chillerlan\QRCode\QRCode; use Closure; -use function array_fill, array_key_exists, array_push, array_unshift, count, floor, max, min, range; +use function array_fill, array_push, array_unshift, count, floor, max, min, range; /** * Holds a numerical representation of the final QR Code; @@ -53,61 +53,6 @@ final class QRMatrix{ /** @var int */ public const M_TEST = 0xff; - /** - * ISO/IEC 18004:2000 Section 8.9 - Format Information - * - * ECC level -> mask pattern - * - * @var int[][] - */ - protected const formatPattern = [ - [ // L - 0b111011111000100, - 0b111001011110011, - 0b111110110101010, - 0b111100010011101, - 0b110011000101111, - 0b110001100011000, - 0b110110001000001, - 0b110100101110110, - ], - [ // M - 0b101010000010010, - 0b101000100100101, - 0b101111001111100, - 0b101101101001011, - 0b100010111111001, - 0b100000011001110, - 0b100111110010111, - 0b100101010100000, - ], - [ // Q - 0b011010101011111, - 0b011000001101000, - 0b011111100110001, - 0b011101000000110, - 0b010010010110100, - 0b010000110000011, - 0b010111011011010, - 0b010101111101101, - ], - [ // H - 0b001011010001001, - 0b001001110111110, - 0b001110011100111, - 0b001100111010000, - 0b000011101100010, - 0b000001001010101, - 0b000110100001100, - 0b000100000111011, - ], - ]; - - /** - * the current ECC level - */ - protected int $eclevel; - /** * the used mask pattern, set via QRMatrix::mapData() */ @@ -125,6 +70,11 @@ final class QRMatrix{ */ protected array $matrix; + /** + * the current ECC level + */ + protected EccLevel $ecclevel; + /** * a Version instance */ @@ -132,17 +82,10 @@ final class QRMatrix{ /** * QRMatrix constructor. - * - * @throws \chillerlan\QRCode\Data\QRCodeDataException */ - public function __construct(Version $version, int $eclevel){ - - if(!array_key_exists($eclevel, QRCode::ECC_MODES)){ - throw new QRCodeDataException('invalid ecc level'); - } - + public function __construct(Version $version, EccLevel $eclevel){ $this->version = $version; - $this->eclevel = $eclevel; + $this->ecclevel = $eclevel; $this->moduleCount = $this->version->getDimension(); $this->matrix = array_fill(0, $this->moduleCount, array_fill(0, $this->moduleCount, $this::M_NULL)); } @@ -189,15 +132,15 @@ final class QRMatrix{ /** * Returns the current version number */ - public function version():int{ - return $this->version->getVersionNumber(); + public function version():Version{ + return $this->version; } /** * Returns the current ECC level */ - public function eccLevel():int{ - return $this->eclevel; + public function eccLevel():EccLevel{ + return $this->ecclevel; } /** @@ -406,7 +349,7 @@ final class QRMatrix{ * ISO/IEC 18004:2000 Section 8.9 */ public function setFormatInfo(int $maskPattern, bool $test = null):QRMatrix{ - $bits = $this::formatPattern[QRCode::ECC_MODES[$this->eclevel]][$maskPattern] ?? 0; + $bits = $this->ecclevel->getformatPattern($maskPattern); for($i = 0; $i < 15; $i++){ $v = !$test && (($bits >> $i) & 1) === 1; @@ -495,7 +438,7 @@ final class QRMatrix{ public function setLogoSpace(int $width, int $height, int $startX = null, int $startY = null):QRMatrix{ // for logos we operate in ECC H (30%) only - if($this->eclevel !== QRCode::ECC_H){ + if($this->ecclevel->getOrdinal() !== EccLevel::H){ throw new QRCodeDataException('ECC level "H" required to add logo space'); } diff --git a/src/QRCode.php b/src/QRCode.php index 5de32ce4a..b8dffce50 100755 --- a/src/QRCode.php +++ b/src/QRCode.php @@ -37,33 +37,6 @@ class QRCode{ /** @var int */ public const MASK_PATTERN_AUTO = -1; - // ISO/IEC 18004:2000 Tables 12, 25 - - /** @var int */ - public const ECC_L = 0b01; // 7%. - /** @var int */ - public const ECC_M = 0b00; // 15%. - /** @var int */ - public const ECC_Q = 0b11; // 25%. - /** @var int */ - public const ECC_H = 0b10; // 30%. - - /** - * References to the keys of the following tables: - * - * @see \chillerlan\QRCode\Common\Version::MAX_BITS - * @see \chillerlan\QRCode\Data\QRData::RSBLOCKS - * @see \chillerlan\QRCode\Data\QRMatrix::formatPattern - * - * @var int[] - */ - public const ECC_MODES = [ - self::ECC_L => 0, - self::ECC_M => 1, - self::ECC_Q => 2, - self::ECC_H => 3, - ]; - /** @var string */ public const OUTPUT_MARKUP_HTML = 'html'; /** @var string */ diff --git a/src/QROptionsTrait.php b/src/QROptionsTrait.php index 204ec04bc..0bae75f02 100644 --- a/src/QROptionsTrait.php +++ b/src/QROptionsTrait.php @@ -14,6 +14,8 @@ namespace chillerlan\QRCode; +use chillerlan\QRCode\Common\EccLevel; + use function array_values, count, in_array, is_numeric, max, min, sprintf, strtolower; /** @@ -50,7 +52,7 @@ trait QROptionsTrait{ * - Q => 25% * - H => 30% */ - protected int $eccLevel = QRCode::ECC_L; + protected int $eccLevel = EccLevel::L; /** * Mask Pattern to use @@ -251,7 +253,7 @@ trait QROptionsTrait{ */ protected function set_eccLevel(int $eccLevel):void{ - if(!isset(QRCode::ECC_MODES[$eccLevel])){ + if(!isset(EccLevel::MODES[$eccLevel])){ throw new QRCodeException(sprintf('Invalid error correct level: %s', $eccLevel)); } diff --git a/tests/Data/QRMatrixTest.php b/tests/Data/QRMatrixTest.php index 77d9813d4..bcf6312c0 100755 --- a/tests/Data/QRMatrixTest.php +++ b/tests/Data/QRMatrixTest.php @@ -12,12 +12,10 @@ namespace chillerlan\QRCodeTest\Data; -use chillerlan\QRCode\Common\Version; -use chillerlan\QRCode\QRCode; -use chillerlan\QRCode\QROptions; +use chillerlan\QRCode\Common\{EccLevel, Version}; +use chillerlan\QRCode\{QRCode, QROptions}; use chillerlan\QRCode\Data\{QRCodeDataException, QRMatrix}; use PHPUnit\Framework\TestCase; -use ReflectionClass; /** * Tests the QRMatix class @@ -44,7 +42,7 @@ final class QRMatrixTest extends TestCase{ * @internal */ protected function getMatrix(int $version):QRMatrix{ - return new QRMatrix(new Version($version), QRCode::ECC_L); + return new QRMatrix(new Version($version), new EccLevel(EccLevel::L)); } /** @@ -54,16 +52,6 @@ final class QRMatrixTest extends TestCase{ $this::assertInstanceOf(QRMatrix::class, $this->matrix); } - /** - * Tests if an exception is thrown when an invalid ECC level was given - */ - public function testInvalidEccException():void{ - $this->expectException(QRCodeDataException::class); - $this->expectExceptionMessage('invalid ecc level'); - - $this->matrix = new QRMatrix(new Version(1), 42); - } - /** * Tests if size() returns the actual matrix size/count */ @@ -75,14 +63,14 @@ final class QRMatrixTest extends TestCase{ * Tests if version() returns the current (given) version */ public function testVersion():void{ - $this::assertSame($this::version, $this->matrix->version()); + $this::assertSame($this::version, $this->matrix->version()->getVersionNumber()); } /** * Tests if eccLevel() returns the current (given) ECC level */ public function testECC():void{ - $this::assertSame(QRCode::ECC_L, $this->matrix->eccLevel()); + $this::assertSame(EccLevel::L, $this->matrix->eccLevel()->getOrdinal()); } /** @@ -303,7 +291,7 @@ final class QRMatrixTest extends TestCase{ public function testSetLogoSpaceOrientation():void{ $o = new QROptions; $o->version = 10; - $o->eccLevel = QRCode::ECC_H; + $o->eccLevel = EccLevel::H; $o->addQuietzone = false; $matrix = (new QRCode($o))->addByteSegment('testdata')->getMatrix(); @@ -322,7 +310,7 @@ final class QRMatrixTest extends TestCase{ public function testSetLogoSpacePosition():void{ $o = new QROptions; $o->version = 10; - $o->eccLevel = QRCode::ECC_H; + $o->eccLevel = EccLevel::H; $o->addQuietzone = true; $o->quietzoneSize = 10; @@ -360,7 +348,7 @@ final class QRMatrixTest extends TestCase{ $o = new QROptions; $o->version = 5; - $o->eccLevel = QRCode::ECC_H; + $o->eccLevel = EccLevel::H; (new QRCode($o))->addByteSegment('testdata')->getMatrix()->setLogoSpace(50, 50); } diff --git a/tests/Output/QRStringTest.php b/tests/Output/QRStringTest.php index c41d109be..9206c8f10 100644 --- a/tests/Output/QRStringTest.php +++ b/tests/Output/QRStringTest.php @@ -13,7 +13,7 @@ namespace chillerlan\QRCodeTest\Output; use chillerlan\QRCodeExamples\MyCustomOutput; -use chillerlan\QRCode\{QRCode, QROptions}; +use chillerlan\QRCode\{Common\EccLevel, QRCode, QROptions}; use chillerlan\QRCode\Output\{QROutputInterface, QRString}; /** @@ -63,7 +63,7 @@ class QRStringTest extends QROutputTestAbstract{ */ public function testCustomOutput():void{ $this->options->version = 5; - $this->options->eccLevel = QRCode::ECC_L; + $this->options->eccLevel = EccLevel::L; $this->options->outputType = QRCode::OUTPUT_CUSTOM; $this->options->outputInterface = MyCustomOutput::class;