diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index f446a3f4..8dcf7928 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -16,19 +16,6 @@ namespace QRCoder; /// public partial class QRCodeGenerator : IDisposable { - private static readonly char[] _alphanumEncTable = { ' ', '$', '%', '*', '+', '-', '.', '/', ':' }; - private static readonly int[] _capacityBaseValues = { 41, 25, 17, 10, 34, 20, 14, 8, 27, 16, 11, 7, 17, 10, 7, 4, 77, 47, 32, 20, 63, 38, 26, 16, 48, 29, 20, 12, 34, 20, 14, 8, 127, 77, 53, 32, 101, 61, 42, 26, 77, 47, 32, 20, 58, 35, 24, 15, 187, 114, 78, 48, 149, 90, 62, 38, 111, 67, 46, 28, 82, 50, 34, 21, 255, 154, 106, 65, 202, 122, 84, 52, 144, 87, 60, 37, 106, 64, 44, 27, 322, 195, 134, 82, 255, 154, 106, 65, 178, 108, 74, 45, 139, 84, 58, 36, 370, 224, 154, 95, 293, 178, 122, 75, 207, 125, 86, 53, 154, 93, 64, 39, 461, 279, 192, 118, 365, 221, 152, 93, 259, 157, 108, 66, 202, 122, 84, 52, 552, 335, 230, 141, 432, 262, 180, 111, 312, 189, 130, 80, 235, 143, 98, 60, 652, 395, 271, 167, 513, 311, 213, 131, 364, 221, 151, 93, 288, 174, 119, 74, 772, 468, 321, 198, 604, 366, 251, 155, 427, 259, 177, 109, 331, 200, 137, 85, 883, 535, 367, 226, 691, 419, 287, 177, 489, 296, 203, 125, 374, 227, 155, 96, 1022, 619, 425, 262, 796, 483, 331, 204, 580, 352, 241, 149, 427, 259, 177, 109, 1101, 667, 458, 282, 871, 528, 362, 223, 621, 376, 258, 159, 468, 283, 194, 120, 1250, 758, 520, 320, 991, 600, 412, 254, 703, 426, 292, 180, 530, 321, 220, 136, 1408, 854, 586, 361, 1082, 656, 450, 277, 775, 470, 322, 198, 602, 365, 250, 154, 1548, 938, 644, 397, 1212, 734, 504, 310, 876, 531, 364, 224, 674, 408, 280, 173, 1725, 1046, 718, 442, 1346, 816, 560, 345, 948, 574, 394, 243, 746, 452, 310, 191, 1903, 1153, 792, 488, 1500, 909, 624, 384, 1063, 644, 442, 272, 813, 493, 338, 208, 2061, 1249, 858, 528, 1600, 970, 666, 410, 1159, 702, 482, 297, 919, 557, 382, 235, 2232, 1352, 929, 572, 1708, 1035, 711, 438, 1224, 742, 509, 314, 969, 587, 403, 248, 2409, 1460, 1003, 618, 1872, 1134, 779, 480, 1358, 823, 565, 348, 1056, 640, 439, 270, 2620, 1588, 1091, 672, 2059, 1248, 857, 528, 1468, 890, 611, 376, 1108, 672, 461, 284, 2812, 1704, 1171, 721, 2188, 1326, 911, 561, 1588, 963, 661, 407, 1228, 744, 511, 315, 3057, 1853, 1273, 784, 2395, 1451, 997, 614, 1718, 1041, 715, 440, 1286, 779, 535, 330, 3283, 1990, 1367, 842, 2544, 1542, 1059, 652, 1804, 1094, 751, 462, 1425, 864, 593, 365, 3517, 2132, 1465, 902, 2701, 1637, 1125, 692, 1933, 1172, 805, 496, 1501, 910, 625, 385, 3669, 2223, 1528, 940, 2857, 1732, 1190, 732, 2085, 1263, 868, 534, 1581, 958, 658, 405, 3909, 2369, 1628, 1002, 3035, 1839, 1264, 778, 2181, 1322, 908, 559, 1677, 1016, 698, 430, 4158, 2520, 1732, 1066, 3289, 1994, 1370, 843, 2358, 1429, 982, 604, 1782, 1080, 742, 457, 4417, 2677, 1840, 1132, 3486, 2113, 1452, 894, 2473, 1499, 1030, 634, 1897, 1150, 790, 486, 4686, 2840, 1952, 1201, 3693, 2238, 1538, 947, 2670, 1618, 1112, 684, 2022, 1226, 842, 518, 4965, 3009, 2068, 1273, 3909, 2369, 1628, 1002, 2805, 1700, 1168, 719, 2157, 1307, 898, 553, 5253, 3183, 2188, 1347, 4134, 2506, 1722, 1060, 2949, 1787, 1228, 756, 2301, 1394, 958, 590, 5529, 3351, 2303, 1417, 4343, 2632, 1809, 1113, 3081, 1867, 1283, 790, 2361, 1431, 983, 605, 5836, 3537, 2431, 1496, 4588, 2780, 1911, 1176, 3244, 1966, 1351, 832, 2524, 1530, 1051, 647, 6153, 3729, 2563, 1577, 4775, 2894, 1989, 1224, 3417, 2071, 1423, 876, 2625, 1591, 1093, 673, 6479, 3927, 2699, 1661, 5039, 3054, 2099, 1292, 3599, 2181, 1499, 923, 2735, 1658, 1139, 701, 6743, 4087, 2809, 1729, 5313, 3220, 2213, 1362, 3791, 2298, 1579, 972, 2927, 1774, 1219, 750, 7089, 4296, 2953, 1817, 5596, 3391, 2331, 1435, 3993, 2420, 1663, 1024, 3057, 1852, 1273, 784 }; - private static readonly int[] _capacityECCBaseValues = { 19, 7, 1, 19, 0, 0, 16, 10, 1, 16, 0, 0, 13, 13, 1, 13, 0, 0, 9, 17, 1, 9, 0, 0, 34, 10, 1, 34, 0, 0, 28, 16, 1, 28, 0, 0, 22, 22, 1, 22, 0, 0, 16, 28, 1, 16, 0, 0, 55, 15, 1, 55, 0, 0, 44, 26, 1, 44, 0, 0, 34, 18, 2, 17, 0, 0, 26, 22, 2, 13, 0, 0, 80, 20, 1, 80, 0, 0, 64, 18, 2, 32, 0, 0, 48, 26, 2, 24, 0, 0, 36, 16, 4, 9, 0, 0, 108, 26, 1, 108, 0, 0, 86, 24, 2, 43, 0, 0, 62, 18, 2, 15, 2, 16, 46, 22, 2, 11, 2, 12, 136, 18, 2, 68, 0, 0, 108, 16, 4, 27, 0, 0, 76, 24, 4, 19, 0, 0, 60, 28, 4, 15, 0, 0, 156, 20, 2, 78, 0, 0, 124, 18, 4, 31, 0, 0, 88, 18, 2, 14, 4, 15, 66, 26, 4, 13, 1, 14, 194, 24, 2, 97, 0, 0, 154, 22, 2, 38, 2, 39, 110, 22, 4, 18, 2, 19, 86, 26, 4, 14, 2, 15, 232, 30, 2, 116, 0, 0, 182, 22, 3, 36, 2, 37, 132, 20, 4, 16, 4, 17, 100, 24, 4, 12, 4, 13, 274, 18, 2, 68, 2, 69, 216, 26, 4, 43, 1, 44, 154, 24, 6, 19, 2, 20, 122, 28, 6, 15, 2, 16, 324, 20, 4, 81, 0, 0, 254, 30, 1, 50, 4, 51, 180, 28, 4, 22, 4, 23, 140, 24, 3, 12, 8, 13, 370, 24, 2, 92, 2, 93, 290, 22, 6, 36, 2, 37, 206, 26, 4, 20, 6, 21, 158, 28, 7, 14, 4, 15, 428, 26, 4, 107, 0, 0, 334, 22, 8, 37, 1, 38, 244, 24, 8, 20, 4, 21, 180, 22, 12, 11, 4, 12, 461, 30, 3, 115, 1, 116, 365, 24, 4, 40, 5, 41, 261, 20, 11, 16, 5, 17, 197, 24, 11, 12, 5, 13, 523, 22, 5, 87, 1, 88, 415, 24, 5, 41, 5, 42, 295, 30, 5, 24, 7, 25, 223, 24, 11, 12, 7, 13, 589, 24, 5, 98, 1, 99, 453, 28, 7, 45, 3, 46, 325, 24, 15, 19, 2, 20, 253, 30, 3, 15, 13, 16, 647, 28, 1, 107, 5, 108, 507, 28, 10, 46, 1, 47, 367, 28, 1, 22, 15, 23, 283, 28, 2, 14, 17, 15, 721, 30, 5, 120, 1, 121, 563, 26, 9, 43, 4, 44, 397, 28, 17, 22, 1, 23, 313, 28, 2, 14, 19, 15, 795, 28, 3, 113, 4, 114, 627, 26, 3, 44, 11, 45, 445, 26, 17, 21, 4, 22, 341, 26, 9, 13, 16, 14, 861, 28, 3, 107, 5, 108, 669, 26, 3, 41, 13, 42, 485, 30, 15, 24, 5, 25, 385, 28, 15, 15, 10, 16, 932, 28, 4, 116, 4, 117, 714, 26, 17, 42, 0, 0, 512, 28, 17, 22, 6, 23, 406, 30, 19, 16, 6, 17, 1006, 28, 2, 111, 7, 112, 782, 28, 17, 46, 0, 0, 568, 30, 7, 24, 16, 25, 442, 24, 34, 13, 0, 0, 1094, 30, 4, 121, 5, 122, 860, 28, 4, 47, 14, 48, 614, 30, 11, 24, 14, 25, 464, 30, 16, 15, 14, 16, 1174, 30, 6, 117, 4, 118, 914, 28, 6, 45, 14, 46, 664, 30, 11, 24, 16, 25, 514, 30, 30, 16, 2, 17, 1276, 26, 8, 106, 4, 107, 1000, 28, 8, 47, 13, 48, 718, 30, 7, 24, 22, 25, 538, 30, 22, 15, 13, 16, 1370, 28, 10, 114, 2, 115, 1062, 28, 19, 46, 4, 47, 754, 28, 28, 22, 6, 23, 596, 30, 33, 16, 4, 17, 1468, 30, 8, 122, 4, 123, 1128, 28, 22, 45, 3, 46, 808, 30, 8, 23, 26, 24, 628, 30, 12, 15, 28, 16, 1531, 30, 3, 117, 10, 118, 1193, 28, 3, 45, 23, 46, 871, 30, 4, 24, 31, 25, 661, 30, 11, 15, 31, 16, 1631, 30, 7, 116, 7, 117, 1267, 28, 21, 45, 7, 46, 911, 30, 1, 23, 37, 24, 701, 30, 19, 15, 26, 16, 1735, 30, 5, 115, 10, 116, 1373, 28, 19, 47, 10, 48, 985, 30, 15, 24, 25, 25, 745, 30, 23, 15, 25, 16, 1843, 30, 13, 115, 3, 116, 1455, 28, 2, 46, 29, 47, 1033, 30, 42, 24, 1, 25, 793, 30, 23, 15, 28, 16, 1955, 30, 17, 115, 0, 0, 1541, 28, 10, 46, 23, 47, 1115, 30, 10, 24, 35, 25, 845, 30, 19, 15, 35, 16, 2071, 30, 17, 115, 1, 116, 1631, 28, 14, 46, 21, 47, 1171, 30, 29, 24, 19, 25, 901, 30, 11, 15, 46, 16, 2191, 30, 13, 115, 6, 116, 1725, 28, 14, 46, 23, 47, 1231, 30, 44, 24, 7, 25, 961, 30, 59, 16, 1, 17, 2306, 30, 12, 121, 7, 122, 1812, 28, 12, 47, 26, 48, 1286, 30, 39, 24, 14, 25, 986, 30, 22, 15, 41, 16, 2434, 30, 6, 121, 14, 122, 1914, 28, 6, 47, 34, 48, 1354, 30, 46, 24, 10, 25, 1054, 30, 2, 15, 64, 16, 2566, 30, 17, 122, 4, 123, 1992, 28, 29, 46, 14, 47, 1426, 30, 49, 24, 10, 25, 1096, 30, 24, 15, 46, 16, 2702, 30, 4, 122, 18, 123, 2102, 28, 13, 46, 32, 47, 1502, 30, 48, 24, 14, 25, 1142, 30, 42, 15, 32, 16, 2812, 30, 20, 117, 4, 118, 2216, 28, 40, 47, 7, 48, 1582, 30, 43, 24, 22, 25, 1222, 30, 10, 15, 67, 16, 2956, 30, 19, 118, 6, 119, 2334, 28, 18, 47, 31, 48, 1666, 30, 34, 24, 34, 25, 1276, 30, 20, 15, 61, 16 }; - private static readonly int[] _alignmentPatternBaseValues = { 0, 0, 0, 0, 0, 0, 0, 6, 18, 0, 0, 0, 0, 0, 6, 22, 0, 0, 0, 0, 0, 6, 26, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 6, 34, 0, 0, 0, 0, 0, 6, 22, 38, 0, 0, 0, 0, 6, 24, 42, 0, 0, 0, 0, 6, 26, 46, 0, 0, 0, 0, 6, 28, 50, 0, 0, 0, 0, 6, 30, 54, 0, 0, 0, 0, 6, 32, 58, 0, 0, 0, 0, 6, 34, 62, 0, 0, 0, 0, 6, 26, 46, 66, 0, 0, 0, 6, 26, 48, 70, 0, 0, 0, 6, 26, 50, 74, 0, 0, 0, 6, 30, 54, 78, 0, 0, 0, 6, 30, 56, 82, 0, 0, 0, 6, 30, 58, 86, 0, 0, 0, 6, 34, 62, 90, 0, 0, 0, 6, 28, 50, 72, 94, 0, 0, 6, 26, 50, 74, 98, 0, 0, 6, 30, 54, 78, 102, 0, 0, 6, 28, 54, 80, 106, 0, 0, 6, 32, 58, 84, 110, 0, 0, 6, 30, 58, 86, 114, 0, 0, 6, 34, 62, 90, 118, 0, 0, 6, 26, 50, 74, 98, 122, 0, 6, 30, 54, 78, 102, 126, 0, 6, 26, 52, 78, 104, 130, 0, 6, 30, 56, 82, 108, 134, 0, 6, 34, 60, 86, 112, 138, 0, 6, 30, 58, 86, 114, 142, 0, 6, 34, 62, 90, 118, 146, 0, 6, 30, 54, 78, 102, 126, 150, 6, 24, 50, 76, 102, 128, 154, 6, 28, 54, 80, 106, 132, 158, 6, 32, 58, 84, 110, 136, 162, 6, 26, 54, 82, 110, 138, 166, 6, 30, 58, 86, 114, 142, 170 }; - private static readonly int[] _remainderBits = { 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0 }; - - private static readonly Dictionary _alignmentPatternTable = CreateAlignmentPatternTable(); - private static readonly List _capacityECCTable = CreateCapacityECCTable(); - private static readonly List _capacityTable = CreateCapacityTable(); - private static readonly int[] _galoisFieldByExponentAlpha = { 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1 }; - private static readonly int[] _galoisFieldByIntegerValue = { 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175 }; - private static readonly Dictionary _alphanumEncDict = CreateAlphanumEncDict(); - /// /// Initializes the QR code generator /// @@ -123,7 +110,7 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo var codedText = PlainTextToBinary(plainText, encoding, eciMode, utf8BOM, forceUtf8); var dataInputLength = GetDataLength(encoding, plainText, codedText, forceUtf8); int version = requestedVersion; - int minVersion = GetVersion(dataInputLength + (eciMode != EciMode.Default ? 2 : 0), encoding, eccLevel); + int minVersion = CapacityTables.CalculateMinimumVersion(dataInputLength + (eciMode != EciMode.Default ? 2 : 0), encoding, eccLevel); if (version == -1) { version = minVersion; @@ -133,7 +120,7 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo //Version was passed as fixed version via parameter. Thus let's check if chosen version is valid. if (minVersion > version) { - var maxSizeByte = _capacityTable[version - 1].Details.First(x => x.ErrorCorrectionLevel == eccLevel).CapacityDict[encoding]; + var maxSizeByte = CapacityTables.GetVersionInfo(version).Details.First(x => x.ErrorCorrectionLevel == eccLevel).CapacityDict[encoding]; throw new QRCoder.Exceptions.DataTooLongException(eccLevel.ToString(), encoding.ToString(), version, maxSizeByte); } } @@ -173,7 +160,7 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) { eccLevel = ValidateECCLevel(eccLevel); - int version = GetVersion(binaryData.Length, EncodingMode.Byte, eccLevel); + int version = CapacityTables.CalculateMinimumVersion(binaryData.Length, EncodingMode.Byte, eccLevel); int countIndicatorLen = GetCountIndicatorLength(version, EncodingMode.Byte); // Convert byte array to bit array, with prefix padding for mode indicator and count indicator @@ -213,7 +200,7 @@ private static ECCLevel ValidateECCLevel(ECCLevel eccLevel) /// A QRCodeData structure containing the full QR code matrix, which can be used for rendering or analysis. private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, int version) { - var eccInfo = _capacityECCTable.Single(x => x.Version == version && x.ErrorCorrectionLevel == eccLevel); + var eccInfo = CapacityTables.GetEccInfo(version, eccLevel); // Fill up data code word PadData(); @@ -302,7 +289,7 @@ int CalculateInterleavedLength() if (codeBlock.ECCWords.Length > i) length += 8; } - length += _remainderBits[version - 1]; + length += CapacityTables.GetRemainderBits(version); return length; } @@ -339,7 +326,7 @@ QRCodeData PlaceModules() { ModulePlacer.PlaceFinderPatterns(qr, blockedModules); ModulePlacer.ReserveSeperatorAreas(size, blockedModules); - ModulePlacer.PlaceAlignmentPatterns(qr, _alignmentPatternTable[version].PatternPositions, blockedModules); + ModulePlacer.PlaceAlignmentPatterns(qr, AlignmentPatterns.FromVersion(version).PatternPositions, blockedModules); ModulePlacer.PlaceTimingPatterns(qr, blockedModules); ModulePlacer.PlaceDarkModule(qr, version, blockedModules); ModulePlacer.ReserveVersionAreas(size, version, blockedModules); @@ -528,7 +515,7 @@ private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count // Convert the first coefficient to its corresponding alpha exponent unless it's zero. // Coefficients that are zero remain zero because log(0) is undefined. var index0Coefficient = leadTermSource[0].Coefficient; - index0Coefficient = index0Coefficient == 0 ? 0 : GetAlphaExpFromIntVal(index0Coefficient); + index0Coefficient = index0Coefficient == 0 ? 0 : GaloisField.GetAlphaExpFromIntVal(index0Coefficient); var alphaNotation = new PolynomItem(index0Coefficient, leadTermSource[0].Exponent); var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, alphaNotation, i); ConvertToDecNotationInPlace(resPoly); @@ -564,40 +551,8 @@ private static void ConvertToDecNotationInPlace(Polynom poly) for (var i = 0; i < poly.Count; i++) { // Convert the alpha exponent of the coefficient to its decimal value and create a new polynomial item with the updated coefficient. - poly[i] = new PolynomItem(GetIntValFromAlphaExp(poly[i].Coefficient), poly[i].Exponent); - } - } - - /// - /// Determines the minimum QR code version required to encode a given amount of data with a specific encoding mode and error correction level. - /// If no suitable version is found, it throws an exception indicating that the data length exceeds the maximum capacity for the given settings. - /// - /// The length of the data to be encoded. - /// The encoding mode (e.g., Numeric, Alphanumeric, Byte). - /// The error correction level (e.g., Low, Medium, Quartile, High). - /// The minimum version of the QR code that can accommodate the given data and settings. - private static int GetVersion(int length, EncodingMode encMode, ECCLevel eccLevel) - { - // capacity table is already sorted by version number ascending, so the smallest version that can hold the data is the first one found - foreach (var x in _capacityTable) - { - // find the requested ECC level and encoding mode in the capacity table - foreach (var y in x.Details) - { - if (y.ErrorCorrectionLevel == eccLevel && y.CapacityDict[encMode] >= length) - { - // if the capacity of the current version is enough, return the version number - return x.Version; - } - } + poly[i] = new PolynomItem(GaloisField.GetIntValFromAlphaExp(poly[i].Coefficient), poly[i].Exponent); } - - // if no version was found, throw an exception - var maxSizeByte = _capacityTable.Where( - x => x.Details.Any( - y => (y.ErrorCorrectionLevel == eccLevel)) - ).Max(x => x.Details.Single(y => y.ErrorCorrectionLevel == eccLevel).CapacityDict[encMode]); - throw new QRCoder.Exceptions.DataTooLongException(eccLevel.ToString(), encMode.ToString(), maxSizeByte); } /// @@ -614,7 +569,7 @@ private static EncodingMode GetEncodingFromPlaintext(string plainText, bool forc if (IsInRange(c, '0', '9')) continue; // numeric - char.IsDigit() for Latin1 result = EncodingMode.Alphanumeric; // not numeric, assume alphanumeric - if (IsInRange(c, 'A', 'Z') || _alphanumEncTable.Contains(c)) + if (AlphanumericEncoder.CanEncode(c)) continue; // alphanumeric return EncodingMode.Byte; // not numeric or alphanumeric, assume byte } @@ -795,7 +750,7 @@ private static BitArray PlainTextToBinary(string plainText, EncodingMode encMode { return encMode switch { - EncodingMode.Alphanumeric => PlainTextToBinaryAlphanumeric(plainText), + EncodingMode.Alphanumeric => AlphanumericEncoder.GetBitArray(plainText), EncodingMode.Numeric => PlainTextToBinaryNumeric(plainText), EncodingMode.Byte => PlainTextToBinaryByte(plainText, eciMode, utf8BOM, forceUtf8), _ => _emptyBitArray, @@ -853,40 +808,6 @@ private static BitArray PlainTextToBinaryNumeric(string plainText) return bitArray; } - /// - /// Converts alphanumeric plain text into a binary format optimized for QR codes. - /// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters, - /// and 6 bits for a single remaining character if the total count is odd. - /// - /// The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode. - /// A BitArray representing the binary data of the encoded alphanumeric text. - private static BitArray PlainTextToBinaryAlphanumeric(string plainText) - { - // Calculate the length of the BitArray needed based on the number of character pairs. - var codeText = new BitArray((plainText.Length / 2) * 11 + (plainText.Length & 1) * 6); - var codeIndex = 0; - var index = 0; - var count = plainText.Length; - - // Process each pair of characters. - while (count >= 2) - { - // Convert each pair of characters to a number by looking them up in the alphanumeric dictionary and calculating. - var dec = _alphanumEncDict[plainText[index++]] * 45 + _alphanumEncDict[plainText[index++]]; - // Convert the number to binary and store it in the BitArray. - codeIndex = DecToBin(dec, 11, codeText, codeIndex); - count -= 2; - } - - // Handle the last character if the length is odd. - if (count > 0) - { - DecToBin(_alphanumEncDict[plainText[index]], 6, codeText, codeIndex); - } - - return codeText; - } - private static readonly Encoding _iso8859_1 = #if NET5_0_OR_GREATER Encoding.Latin1; @@ -1088,7 +1009,7 @@ private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polyno // Create a new polynomial term with the coefficients added (as exponents) and exponents summed. var polItemRes = new PolynomItem ( - ShrinkAlphaExp(polItemBase.Coefficient + polItemMulti.Coefficient), + GaloisField.ShrinkAlphaExp(polItemBase.Coefficient + polItemMulti.Coefficient), (polItemBase.Exponent + polItemMulti.Exponent) ); resultPolynom.Add(polItemRes); @@ -1105,11 +1026,11 @@ private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polyno foreach (var polynomOld in resultPolynom) { if (polynomOld.Exponent == exponent) - coefficient ^= GetIntValFromAlphaExp(polynomOld.Coefficient); + coefficient ^= GaloisField.GetIntValFromAlphaExp(polynomOld.Coefficient); } // Fix the polynomial terms by recalculating the coefficients based on XORed results. - var polynomFixed = new PolynomItem(GetAlphaExpFromIntVal(coefficient), exponent); + var polynomFixed = new PolynomItem(GaloisField.GetAlphaExpFromIntVal(coefficient), exponent); gluedPolynoms[gluedPolynomsIndex++] = polynomFixed; } @@ -1161,210 +1082,6 @@ int[] GetNotUniqueExponents(Polynom list) } } - /// - /// Retrieves the integer value from the Galois field that corresponds to a given exponent. - /// This is used in Reed-Solomon and other error correction calculations involving Galois fields. - /// - private static int GetIntValFromAlphaExp(int exp) - => _galoisFieldByExponentAlpha[exp]; - - /// - /// Retrieves the exponent from the Galois field that corresponds to a given integer value. - /// Throws an exception if the integer value is zero, as zero does not have a logarithmic representation in the field. - /// - private static int GetAlphaExpFromIntVal(int intVal) - { - if (intVal == 0) - ThrowIntValOutOfRangeException(); // Zero is not valid as it does not have an exponent representation. - return _galoisFieldByIntegerValue[intVal]; - - void ThrowIntValOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(intVal), "The provided integer value is out of range, as zero is not representable."); - } - - /// - /// Normalizes a Galois field exponent to ensure it remains within the bounds of the field's size. - /// This is particularly necessary when performing multiplications in the field which can result in exponents exceeding the field's maximum. - /// - private static int ShrinkAlphaExp(int alphaExp) - => (int)((alphaExp % 256) + Math.Floor((double)(alphaExp / 256))); - - /// - /// Creates a dictionary mapping alphanumeric characters to their respective positions used in QR code encoding. - /// This includes digits 0-9, uppercase letters A-Z, and some special characters. - /// - /// A dictionary mapping each supported alphanumeric character to its corresponding value. - private static Dictionary CreateAlphanumEncDict() - { - var localAlphanumEncDict = new Dictionary(45); - for (int i = 0; i < 10; i++) - localAlphanumEncDict.Add($"{i}"[0], i); - // Add uppercase alphabetic characters. - for (char c = 'A'; c <= 'Z'; c++) - localAlphanumEncDict.Add(c, localAlphanumEncDict.Count); - // Add special characters from a predefined table. - for (int i = 0; i < _alphanumEncTable.Length; i++) - localAlphanumEncDict.Add(_alphanumEncTable[i], localAlphanumEncDict.Count); - return localAlphanumEncDict; - } - - /// - /// Creates a lookup table mapping QR code versions to their corresponding alignment patterns. - /// Alignment patterns are used in QR codes to help scanners accurately read the code at high speeds and when partially obscured. - /// This table provides the necessary patterns based on the QR code version which dictates the size and complexity of the QR code. - /// - /// A dictionary where keys are QR code version numbers and values are AlignmentPattern structures detailing the positions of alignment patterns for each version. - private static Dictionary CreateAlignmentPatternTable() - { - var localAlignmentPatternTable = new Dictionary(40); - - for (var i = 0; i < (7 * 40); i += 7) - { - var points = new List(50); - for (var x = 0; x < 7; x++) - { - if (_alignmentPatternBaseValues[i + x] != 0) - { - for (var y = 0; y < 7; y++) - { - if (_alignmentPatternBaseValues[i + y] != 0) - { - var p = new Point(_alignmentPatternBaseValues[i + x] - 2, _alignmentPatternBaseValues[i + y] - 2); - if (!points.Contains(p)) - points.Add(p); - } - } - } - } - - var version = (i + 7) / 7; - localAlignmentPatternTable.Add(version, new AlignmentPattern() - { - Version = version, - PatternPositions = points - } - ); - } - return localAlignmentPatternTable; - } - - /// - /// Generates a table containing the error correction capacities and data codeword information for different QR code versions and error correction levels. - /// This table is essential for determining how much data can be encoded in a QR code of a specific version and ECC level, - /// as well as how robust the QR code will be against distortions or obstructions. - /// - /// A list of ECCInfo structures, each representing the ECC data and capacities for different combinations of QR code versions and ECC levels. - private static List CreateCapacityECCTable() - { - var localCapacityECCTable = new List(160); - for (var i = 0; i < (4 * 6 * 40); i += (4 * 6)) - { - localCapacityECCTable.AddRange( - new[] - { - new ECCInfo( - (i+24) / 24, - ECCLevel.L, - _capacityECCBaseValues[i], - _capacityECCBaseValues[i+1], - _capacityECCBaseValues[i+2], - _capacityECCBaseValues[i+3], - _capacityECCBaseValues[i+4], - _capacityECCBaseValues[i+5]), - new ECCInfo - ( - version: (i + 24) / 24, - errorCorrectionLevel: ECCLevel.M, - totalDataCodewords: _capacityECCBaseValues[i+6], - eccPerBlock: _capacityECCBaseValues[i+7], - blocksInGroup1: _capacityECCBaseValues[i+8], - codewordsInGroup1: _capacityECCBaseValues[i+9], - blocksInGroup2: _capacityECCBaseValues[i+10], - codewordsInGroup2: _capacityECCBaseValues[i+11] - ), - new ECCInfo - ( - version: (i + 24) / 24, - errorCorrectionLevel: ECCLevel.Q, - totalDataCodewords: _capacityECCBaseValues[i+12], - eccPerBlock: _capacityECCBaseValues[i+13], - blocksInGroup1: _capacityECCBaseValues[i+14], - codewordsInGroup1: _capacityECCBaseValues[i+15], - blocksInGroup2: _capacityECCBaseValues[i+16], - codewordsInGroup2: _capacityECCBaseValues[i+17] - ), - new ECCInfo - ( - version: (i + 24) / 24, - errorCorrectionLevel: ECCLevel.H, - totalDataCodewords: _capacityECCBaseValues[i+18], - eccPerBlock: _capacityECCBaseValues[i+19], - blocksInGroup1: _capacityECCBaseValues[i+20], - codewordsInGroup1: _capacityECCBaseValues[i+21], - blocksInGroup2: _capacityECCBaseValues[i+22], - codewordsInGroup2: _capacityECCBaseValues[i+23] - ) - }); - } - return localCapacityECCTable; - } - - /// - /// Generates a list containing detailed capacity information for various versions of QR codes. - /// This table includes capacities for different encoding modes (numeric, alphanumeric, byte, etc.) under each error correction level. - /// The capacity table is crucial for QR code generation, as it determines how much data each QR code version can store depending on the encoding mode and error correction level used. - /// - private static List CreateCapacityTable() - { - var localCapacityTable = new List(40); - for (var i = 0; i < (16 * 40); i += 16) - { - localCapacityTable.Add(new VersionInfo( - - (i + 16) / 16, - new List(4) - { - new VersionInfoDetails( - ECCLevel.L, - new Dictionary(){ - { EncodingMode.Numeric, _capacityBaseValues[i] }, - { EncodingMode.Alphanumeric, _capacityBaseValues[i+1] }, - { EncodingMode.Byte, _capacityBaseValues[i+2] }, - { EncodingMode.Kanji, _capacityBaseValues[i+3] }, - } - ), - new VersionInfoDetails( - ECCLevel.M, - new Dictionary(){ - { EncodingMode.Numeric, _capacityBaseValues[i+4] }, - { EncodingMode.Alphanumeric, _capacityBaseValues[i+5] }, - { EncodingMode.Byte, _capacityBaseValues[i+6] }, - { EncodingMode.Kanji, _capacityBaseValues[i+7] }, - } - ), - new VersionInfoDetails( - ECCLevel.Q, - new Dictionary(){ - { EncodingMode.Numeric, _capacityBaseValues[i+8] }, - { EncodingMode.Alphanumeric, _capacityBaseValues[i+9] }, - { EncodingMode.Byte, _capacityBaseValues[i+10] }, - { EncodingMode.Kanji, _capacityBaseValues[i+11] }, - } - ), - new VersionInfoDetails( - ECCLevel.H, - new Dictionary(){ - { EncodingMode.Numeric, _capacityBaseValues[i+12] }, - { EncodingMode.Alphanumeric, _capacityBaseValues[i+13] }, - { EncodingMode.Byte, _capacityBaseValues[i+14] }, - { EncodingMode.Kanji, _capacityBaseValues[i+15] }, - } - ) - } - )); - } - return localCapacityTable; - } - /// public void Dispose() { diff --git a/QRCoder/QRCodeGenerator/AlignmentPatterns.cs b/QRCoder/QRCodeGenerator/AlignmentPatterns.cs new file mode 100644 index 00000000..fe879623 --- /dev/null +++ b/QRCoder/QRCodeGenerator/AlignmentPatterns.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; + +namespace QRCoder; + +public partial class QRCodeGenerator +{ + /// + /// This class contains the alignment patterns used in QR codes. + /// + private static class AlignmentPatterns + { + /// + /// A lookup table mapping QR code versions to their corresponding alignment patterns. + /// + private static readonly Dictionary _alignmentPatternTable = CreateAlignmentPatternTable(); + + /// + /// Retrieves the alignment pattern for a specific QR code version. + /// + public static AlignmentPattern FromVersion(int version) => _alignmentPatternTable[version]; + + /// + /// Creates a lookup table mapping QR code versions to their corresponding alignment patterns. + /// Alignment patterns are used in QR codes to help scanners accurately read the code at high speeds and when partially obscured. + /// This table provides the necessary patterns based on the QR code version which dictates the size and complexity of the QR code. + /// + /// A dictionary where keys are QR code version numbers and values are AlignmentPattern structures detailing the positions of alignment patterns for each version. + private static Dictionary CreateAlignmentPatternTable() + { + var alignmentPatternBaseValues = new int[] { 0, 0, 0, 0, 0, 0, 0, 6, 18, 0, 0, 0, 0, 0, 6, 22, 0, 0, 0, 0, 0, 6, 26, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 6, 34, 0, 0, 0, 0, 0, 6, 22, 38, 0, 0, 0, 0, 6, 24, 42, 0, 0, 0, 0, 6, 26, 46, 0, 0, 0, 0, 6, 28, 50, 0, 0, 0, 0, 6, 30, 54, 0, 0, 0, 0, 6, 32, 58, 0, 0, 0, 0, 6, 34, 62, 0, 0, 0, 0, 6, 26, 46, 66, 0, 0, 0, 6, 26, 48, 70, 0, 0, 0, 6, 26, 50, 74, 0, 0, 0, 6, 30, 54, 78, 0, 0, 0, 6, 30, 56, 82, 0, 0, 0, 6, 30, 58, 86, 0, 0, 0, 6, 34, 62, 90, 0, 0, 0, 6, 28, 50, 72, 94, 0, 0, 6, 26, 50, 74, 98, 0, 0, 6, 30, 54, 78, 102, 0, 0, 6, 28, 54, 80, 106, 0, 0, 6, 32, 58, 84, 110, 0, 0, 6, 30, 58, 86, 114, 0, 0, 6, 34, 62, 90, 118, 0, 0, 6, 26, 50, 74, 98, 122, 0, 6, 30, 54, 78, 102, 126, 0, 6, 26, 52, 78, 104, 130, 0, 6, 30, 56, 82, 108, 134, 0, 6, 34, 60, 86, 112, 138, 0, 6, 30, 58, 86, 114, 142, 0, 6, 34, 62, 90, 118, 146, 0, 6, 30, 54, 78, 102, 126, 150, 6, 24, 50, 76, 102, 128, 154, 6, 28, 54, 80, 106, 132, 158, 6, 32, 58, 84, 110, 136, 162, 6, 26, 54, 82, 110, 138, 166, 6, 30, 58, 86, 114, 142, 170 }; + var localAlignmentPatternTable = new Dictionary(40); + + for (var i = 0; i < (7 * 40); i += 7) + { + var points = new List(50); + for (var x = 0; x < 7; x++) + { + if (alignmentPatternBaseValues[i + x] != 0) + { + for (var y = 0; y < 7; y++) + { + if (alignmentPatternBaseValues[i + y] != 0) + { + var p = new Point(alignmentPatternBaseValues[i + x] - 2, alignmentPatternBaseValues[i + y] - 2); + if (!points.Contains(p)) + points.Add(p); + } + } + } + } + + var version = (i + 7) / 7; + localAlignmentPatternTable.Add(version, new AlignmentPattern() + { + Version = version, + PatternPositions = points + }); + } + return localAlignmentPatternTable; + } + } +} diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs new file mode 100644 index 00000000..b0e46aca --- /dev/null +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace QRCoder; + +public partial class QRCodeGenerator +{ + /// + /// Encodes alphanumeric characters (0–9, A–Z (uppercase), space, $, %, *, +, -, period, /, colon) into a binary format suitable for QR codes. + /// + private static class AlphanumericEncoder + { + private static readonly char[] _alphanumEncTable = { ' ', '$', '%', '*', '+', '-', '.', '/', ':' }; + + /// + /// A dictionary mapping alphanumeric characters to their respective positions used in QR code encoding. + /// This includes digits 0-9, uppercase letters A-Z, and some special characters. + /// + private static readonly Dictionary _alphanumEncDict = CreateAlphanumEncDict(_alphanumEncTable); + + /// + /// Creates a dictionary mapping alphanumeric characters to their respective positions used in QR code encoding. + /// This includes digits 0-9, uppercase letters A-Z, and some special characters. + /// + /// A dictionary mapping each supported alphanumeric character to its corresponding value. + private static Dictionary CreateAlphanumEncDict(char[] alphanumEncTable) + { + var localAlphanumEncDict = new Dictionary(45); + // Add 0-9 + for (char c = '0'; c <= '9'; c++) + localAlphanumEncDict.Add(c, c - '0'); + // Add uppercase alphabetic characters. + for (char c = 'A'; c <= 'Z'; c++) + localAlphanumEncDict.Add(c, localAlphanumEncDict.Count); + // Add special characters from a predefined table. + for (int i = 0; i < _alphanumEncTable.Length; i++) + localAlphanumEncDict.Add(alphanumEncTable[i], localAlphanumEncDict.Count); + return localAlphanumEncDict; + } + + /// + /// Checks if a character is present in the alphanumeric encoding table. + /// + public static bool CanEncode(char c) => IsInRange(c, 'A', 'Z') || Array.IndexOf(_alphanumEncTable, c) >= 0; + + /// + /// Converts alphanumeric plain text into a binary format optimized for QR codes. + /// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters, + /// and 6 bits for a single remaining character if the total count is odd. + /// + /// The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode. + /// A BitArray representing the binary data of the encoded alphanumeric text. + public static BitArray GetBitArray(string plainText) + { + // Calculate the length of the BitArray needed based on the number of character pairs. + var codeText = new BitArray((plainText.Length / 2) * 11 + (plainText.Length & 1) * 6); + var codeIndex = 0; + var index = 0; + var count = plainText.Length; + + // Process each pair of characters. + while (count >= 2) + { + // Convert each pair of characters to a number by looking them up in the alphanumeric dictionary and calculating. + var dec = _alphanumEncDict[plainText[index++]] * 45 + _alphanumEncDict[plainText[index++]]; + // Convert the number to binary and store it in the BitArray. + codeIndex = DecToBin(dec, 11, codeText, codeIndex); + count -= 2; + } + + // Handle the last character if the length is odd. + if (count > 0) + { + DecToBin(_alphanumEncDict[plainText[index]], 6, codeText, codeIndex); + } + + return codeText; + } + } +} diff --git a/QRCoder/QRCodeGenerator/CapacityTables.cs b/QRCoder/QRCodeGenerator/CapacityTables.cs new file mode 100644 index 00000000..59f0d2c9 --- /dev/null +++ b/QRCoder/QRCodeGenerator/CapacityTables.cs @@ -0,0 +1,216 @@ +using System.Collections.Generic; +using System.Linq; + +namespace QRCoder; + +public partial class QRCodeGenerator +{ + /// + /// Provides QR code capacity and error correction data for each version and encoding mode. + /// Used to determine how much data can be stored in a QR code and which version is required. + /// + private static class CapacityTables + { + private static readonly int[] _capacityBaseValues = { 41, 25, 17, 10, 34, 20, 14, 8, 27, 16, 11, 7, 17, 10, 7, 4, 77, 47, 32, 20, 63, 38, 26, 16, 48, 29, 20, 12, 34, 20, 14, 8, 127, 77, 53, 32, 101, 61, 42, 26, 77, 47, 32, 20, 58, 35, 24, 15, 187, 114, 78, 48, 149, 90, 62, 38, 111, 67, 46, 28, 82, 50, 34, 21, 255, 154, 106, 65, 202, 122, 84, 52, 144, 87, 60, 37, 106, 64, 44, 27, 322, 195, 134, 82, 255, 154, 106, 65, 178, 108, 74, 45, 139, 84, 58, 36, 370, 224, 154, 95, 293, 178, 122, 75, 207, 125, 86, 53, 154, 93, 64, 39, 461, 279, 192, 118, 365, 221, 152, 93, 259, 157, 108, 66, 202, 122, 84, 52, 552, 335, 230, 141, 432, 262, 180, 111, 312, 189, 130, 80, 235, 143, 98, 60, 652, 395, 271, 167, 513, 311, 213, 131, 364, 221, 151, 93, 288, 174, 119, 74, 772, 468, 321, 198, 604, 366, 251, 155, 427, 259, 177, 109, 331, 200, 137, 85, 883, 535, 367, 226, 691, 419, 287, 177, 489, 296, 203, 125, 374, 227, 155, 96, 1022, 619, 425, 262, 796, 483, 331, 204, 580, 352, 241, 149, 427, 259, 177, 109, 1101, 667, 458, 282, 871, 528, 362, 223, 621, 376, 258, 159, 468, 283, 194, 120, 1250, 758, 520, 320, 991, 600, 412, 254, 703, 426, 292, 180, 530, 321, 220, 136, 1408, 854, 586, 361, 1082, 656, 450, 277, 775, 470, 322, 198, 602, 365, 250, 154, 1548, 938, 644, 397, 1212, 734, 504, 310, 876, 531, 364, 224, 674, 408, 280, 173, 1725, 1046, 718, 442, 1346, 816, 560, 345, 948, 574, 394, 243, 746, 452, 310, 191, 1903, 1153, 792, 488, 1500, 909, 624, 384, 1063, 644, 442, 272, 813, 493, 338, 208, 2061, 1249, 858, 528, 1600, 970, 666, 410, 1159, 702, 482, 297, 919, 557, 382, 235, 2232, 1352, 929, 572, 1708, 1035, 711, 438, 1224, 742, 509, 314, 969, 587, 403, 248, 2409, 1460, 1003, 618, 1872, 1134, 779, 480, 1358, 823, 565, 348, 1056, 640, 439, 270, 2620, 1588, 1091, 672, 2059, 1248, 857, 528, 1468, 890, 611, 376, 1108, 672, 461, 284, 2812, 1704, 1171, 721, 2188, 1326, 911, 561, 1588, 963, 661, 407, 1228, 744, 511, 315, 3057, 1853, 1273, 784, 2395, 1451, 997, 614, 1718, 1041, 715, 440, 1286, 779, 535, 330, 3283, 1990, 1367, 842, 2544, 1542, 1059, 652, 1804, 1094, 751, 462, 1425, 864, 593, 365, 3517, 2132, 1465, 902, 2701, 1637, 1125, 692, 1933, 1172, 805, 496, 1501, 910, 625, 385, 3669, 2223, 1528, 940, 2857, 1732, 1190, 732, 2085, 1263, 868, 534, 1581, 958, 658, 405, 3909, 2369, 1628, 1002, 3035, 1839, 1264, 778, 2181, 1322, 908, 559, 1677, 1016, 698, 430, 4158, 2520, 1732, 1066, 3289, 1994, 1370, 843, 2358, 1429, 982, 604, 1782, 1080, 742, 457, 4417, 2677, 1840, 1132, 3486, 2113, 1452, 894, 2473, 1499, 1030, 634, 1897, 1150, 790, 486, 4686, 2840, 1952, 1201, 3693, 2238, 1538, 947, 2670, 1618, 1112, 684, 2022, 1226, 842, 518, 4965, 3009, 2068, 1273, 3909, 2369, 1628, 1002, 2805, 1700, 1168, 719, 2157, 1307, 898, 553, 5253, 3183, 2188, 1347, 4134, 2506, 1722, 1060, 2949, 1787, 1228, 756, 2301, 1394, 958, 590, 5529, 3351, 2303, 1417, 4343, 2632, 1809, 1113, 3081, 1867, 1283, 790, 2361, 1431, 983, 605, 5836, 3537, 2431, 1496, 4588, 2780, 1911, 1176, 3244, 1966, 1351, 832, 2524, 1530, 1051, 647, 6153, 3729, 2563, 1577, 4775, 2894, 1989, 1224, 3417, 2071, 1423, 876, 2625, 1591, 1093, 673, 6479, 3927, 2699, 1661, 5039, 3054, 2099, 1292, 3599, 2181, 1499, 923, 2735, 1658, 1139, 701, 6743, 4087, 2809, 1729, 5313, 3220, 2213, 1362, 3791, 2298, 1579, 972, 2927, 1774, 1219, 750, 7089, 4296, 2953, 1817, 5596, 3391, 2331, 1435, 3993, 2420, 1663, 1024, 3057, 1852, 1273, 784 }; + private static readonly int[] _capacityECCBaseValues = { 19, 7, 1, 19, 0, 0, 16, 10, 1, 16, 0, 0, 13, 13, 1, 13, 0, 0, 9, 17, 1, 9, 0, 0, 34, 10, 1, 34, 0, 0, 28, 16, 1, 28, 0, 0, 22, 22, 1, 22, 0, 0, 16, 28, 1, 16, 0, 0, 55, 15, 1, 55, 0, 0, 44, 26, 1, 44, 0, 0, 34, 18, 2, 17, 0, 0, 26, 22, 2, 13, 0, 0, 80, 20, 1, 80, 0, 0, 64, 18, 2, 32, 0, 0, 48, 26, 2, 24, 0, 0, 36, 16, 4, 9, 0, 0, 108, 26, 1, 108, 0, 0, 86, 24, 2, 43, 0, 0, 62, 18, 2, 15, 2, 16, 46, 22, 2, 11, 2, 12, 136, 18, 2, 68, 0, 0, 108, 16, 4, 27, 0, 0, 76, 24, 4, 19, 0, 0, 60, 28, 4, 15, 0, 0, 156, 20, 2, 78, 0, 0, 124, 18, 4, 31, 0, 0, 88, 18, 2, 14, 4, 15, 66, 26, 4, 13, 1, 14, 194, 24, 2, 97, 0, 0, 154, 22, 2, 38, 2, 39, 110, 22, 4, 18, 2, 19, 86, 26, 4, 14, 2, 15, 232, 30, 2, 116, 0, 0, 182, 22, 3, 36, 2, 37, 132, 20, 4, 16, 4, 17, 100, 24, 4, 12, 4, 13, 274, 18, 2, 68, 2, 69, 216, 26, 4, 43, 1, 44, 154, 24, 6, 19, 2, 20, 122, 28, 6, 15, 2, 16, 324, 20, 4, 81, 0, 0, 254, 30, 1, 50, 4, 51, 180, 28, 4, 22, 4, 23, 140, 24, 3, 12, 8, 13, 370, 24, 2, 92, 2, 93, 290, 22, 6, 36, 2, 37, 206, 26, 4, 20, 6, 21, 158, 28, 7, 14, 4, 15, 428, 26, 4, 107, 0, 0, 334, 22, 8, 37, 1, 38, 244, 24, 8, 20, 4, 21, 180, 22, 12, 11, 4, 12, 461, 30, 3, 115, 1, 116, 365, 24, 4, 40, 5, 41, 261, 20, 11, 16, 5, 17, 197, 24, 11, 12, 5, 13, 523, 22, 5, 87, 1, 88, 415, 24, 5, 41, 5, 42, 295, 30, 5, 24, 7, 25, 223, 24, 11, 12, 7, 13, 589, 24, 5, 98, 1, 99, 453, 28, 7, 45, 3, 46, 325, 24, 15, 19, 2, 20, 253, 30, 3, 15, 13, 16, 647, 28, 1, 107, 5, 108, 507, 28, 10, 46, 1, 47, 367, 28, 1, 22, 15, 23, 283, 28, 2, 14, 17, 15, 721, 30, 5, 120, 1, 121, 563, 26, 9, 43, 4, 44, 397, 28, 17, 22, 1, 23, 313, 28, 2, 14, 19, 15, 795, 28, 3, 113, 4, 114, 627, 26, 3, 44, 11, 45, 445, 26, 17, 21, 4, 22, 341, 26, 9, 13, 16, 14, 861, 28, 3, 107, 5, 108, 669, 26, 3, 41, 13, 42, 485, 30, 15, 24, 5, 25, 385, 28, 15, 15, 10, 16, 932, 28, 4, 116, 4, 117, 714, 26, 17, 42, 0, 0, 512, 28, 17, 22, 6, 23, 406, 30, 19, 16, 6, 17, 1006, 28, 2, 111, 7, 112, 782, 28, 17, 46, 0, 0, 568, 30, 7, 24, 16, 25, 442, 24, 34, 13, 0, 0, 1094, 30, 4, 121, 5, 122, 860, 28, 4, 47, 14, 48, 614, 30, 11, 24, 14, 25, 464, 30, 16, 15, 14, 16, 1174, 30, 6, 117, 4, 118, 914, 28, 6, 45, 14, 46, 664, 30, 11, 24, 16, 25, 514, 30, 30, 16, 2, 17, 1276, 26, 8, 106, 4, 107, 1000, 28, 8, 47, 13, 48, 718, 30, 7, 24, 22, 25, 538, 30, 22, 15, 13, 16, 1370, 28, 10, 114, 2, 115, 1062, 28, 19, 46, 4, 47, 754, 28, 28, 22, 6, 23, 596, 30, 33, 16, 4, 17, 1468, 30, 8, 122, 4, 123, 1128, 28, 22, 45, 3, 46, 808, 30, 8, 23, 26, 24, 628, 30, 12, 15, 28, 16, 1531, 30, 3, 117, 10, 118, 1193, 28, 3, 45, 23, 46, 871, 30, 4, 24, 31, 25, 661, 30, 11, 15, 31, 16, 1631, 30, 7, 116, 7, 117, 1267, 28, 21, 45, 7, 46, 911, 30, 1, 23, 37, 24, 701, 30, 19, 15, 26, 16, 1735, 30, 5, 115, 10, 116, 1373, 28, 19, 47, 10, 48, 985, 30, 15, 24, 25, 25, 745, 30, 23, 15, 25, 16, 1843, 30, 13, 115, 3, 116, 1455, 28, 2, 46, 29, 47, 1033, 30, 42, 24, 1, 25, 793, 30, 23, 15, 28, 16, 1955, 30, 17, 115, 0, 0, 1541, 28, 10, 46, 23, 47, 1115, 30, 10, 24, 35, 25, 845, 30, 19, 15, 35, 16, 2071, 30, 17, 115, 1, 116, 1631, 28, 14, 46, 21, 47, 1171, 30, 29, 24, 19, 25, 901, 30, 11, 15, 46, 16, 2191, 30, 13, 115, 6, 116, 1725, 28, 14, 46, 23, 47, 1231, 30, 44, 24, 7, 25, 961, 30, 59, 16, 1, 17, 2306, 30, 12, 121, 7, 122, 1812, 28, 12, 47, 26, 48, 1286, 30, 39, 24, 14, 25, 986, 30, 22, 15, 41, 16, 2434, 30, 6, 121, 14, 122, 1914, 28, 6, 47, 34, 48, 1354, 30, 46, 24, 10, 25, 1054, 30, 2, 15, 64, 16, 2566, 30, 17, 122, 4, 123, 1992, 28, 29, 46, 14, 47, 1426, 30, 49, 24, 10, 25, 1096, 30, 24, 15, 46, 16, 2702, 30, 4, 122, 18, 123, 2102, 28, 13, 46, 32, 47, 1502, 30, 48, 24, 14, 25, 1142, 30, 42, 15, 32, 16, 2812, 30, 20, 117, 4, 118, 2216, 28, 40, 47, 7, 48, 1582, 30, 43, 24, 22, 25, 1222, 30, 10, 15, 67, 16, 2956, 30, 19, 118, 6, 119, 2334, 28, 18, 47, 31, 48, 1666, 30, 34, 24, 34, 25, 1276, 30, 20, 15, 61, 16 }; + private static readonly int[] _remainderBits = { 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0 }; + + /// + /// A list containing detailed capacity information for each version of QR codes. + /// The index in the capacity table corresponds to one less than the version number. + /// + private static readonly List _capacityTable = CreateCapacityTable(_capacityBaseValues); + + /// + /// A table containing the error correction capacities and data codeword information for different combinations of QR code versions and error correction levels. + /// + private static readonly List _capacityECCTable = CreateCapacityECCTable(_capacityECCBaseValues); + + /// + /// Retrieves the error correction information for a specific QR code version and error correction level. + /// + /// The version of the QR code (1 to 40). + /// The desired error correction level (L, M, Q, or H). Do not supply . + /// + /// An object containing the total number of data codewords, ECC per block, + /// block group details, and other parameters required for encoding error correction data. + /// + public static ECCInfo GetEccInfo(int version, ECCLevel eccLevel) + => _capacityECCTable.Single(x => x.Version == version && x.ErrorCorrectionLevel == eccLevel); + + /// + /// Retrieves the capacity information for a specific QR code version. + /// The returned structure contains detailed data capacity values for each error correction level (L, M, Q, H) + /// and encoding mode (Numeric, Alphanumeric, Byte, Kanji), indicating the maximum number of characters + /// that can be stored in a QR code of the specified version under each configuration. + /// + /// The version of the QR code (1 to 40). + /// + /// A object containing data capacity details for all error correction levels + /// and encoding modes for the specified version. + /// + public static VersionInfo GetVersionInfo(int version) + => _capacityTable[version - 1]; + + /// + /// Retrieves the number of remainder bits required for a specific QR code version. + /// Remainder bits are added to the final bit stream to ensure proper alignment with byte boundaries, + /// as required by the QR code specification. + /// + /// The version of the QR code (1 to 40). + /// + /// The number of remainder bits (0 to 7) that must be appended to the encoded bit stream. + /// + public static int GetRemainderBits(int version) + => _remainderBits[version - 1]; + + /// + /// Determines the minimum QR code version required to encode a given amount of data with a specific encoding mode and error correction level. + /// If no suitable version is found, it throws an exception indicating that the data length exceeds the maximum capacity for the given settings. + /// + /// The length of the data to be encoded. + /// The encoding mode (e.g., Numeric, Alphanumeric, Byte). + /// The error correction level (e.g., Low, Medium, Quartile, High). + /// The minimum version of the QR code that can accommodate the given data and settings. + /// + /// Thrown when the data length exceeds the maximum capacity for the specified encoding mode and error correction level. + /// + public static int CalculateMinimumVersion(int length, EncodingMode encMode, ECCLevel eccLevel) + { + // capacity table is already sorted by version number ascending, so the smallest version that can hold the data is the first one found + foreach (var x in _capacityTable) + { + // find the requested ECC level and encoding mode in the capacity table + foreach (var y in x.Details) + { + if (y.ErrorCorrectionLevel == eccLevel && y.CapacityDict[encMode] >= length) + { + // if the capacity of the current version is enough, return the version number + return x.Version; + } + } + } + + // if no version was found, throw an exception + var maxSizeByte = _capacityTable.Where( + x => x.Details.Any( + y => (y.ErrorCorrectionLevel == eccLevel)) + ).Max(x => x.Details.Single(y => y.ErrorCorrectionLevel == eccLevel).CapacityDict[encMode]); + throw new QRCoder.Exceptions.DataTooLongException(eccLevel.ToString(), encMode.ToString(), maxSizeByte); + } + + /// + /// Generates a table containing the error correction capacities and data codeword information for different QR code versions and error correction levels. + /// This table is essential for determining how much data can be encoded in a QR code of a specific version and ECC level, + /// as well as how robust the QR code will be against distortions or obstructions. + /// + /// A list of ECCInfo structures, each representing the ECC data and capacities for different combinations of QR code versions and ECC levels. + private static List CreateCapacityECCTable(int[] capacityECCBaseValues) + { + var localCapacityECCTable = new List(160); + for (var i = 0; i < (4 * 6 * 40); i += (4 * 6)) + { + localCapacityECCTable.AddRange(new[] + { + new ECCInfo( + version: (i + 24) / 24, + errorCorrectionLevel: ECCLevel.L, + totalDataCodewords: capacityECCBaseValues[i], + eccPerBlock: capacityECCBaseValues[i + 1], + blocksInGroup1: capacityECCBaseValues[i + 2], + codewordsInGroup1: capacityECCBaseValues[i + 3], + blocksInGroup2: capacityECCBaseValues[i + 4], + codewordsInGroup2: capacityECCBaseValues[i + 5]), + new ECCInfo( + version: (i + 24) / 24, + errorCorrectionLevel: ECCLevel.M, + totalDataCodewords: capacityECCBaseValues[i + 6], + eccPerBlock: capacityECCBaseValues[i + 7], + blocksInGroup1: capacityECCBaseValues[i + 8], + codewordsInGroup1: capacityECCBaseValues[i + 9], + blocksInGroup2: capacityECCBaseValues[i + 10], + codewordsInGroup2: capacityECCBaseValues[i + 11] + ), + new ECCInfo( + version: (i + 24) / 24, + errorCorrectionLevel: ECCLevel.Q, + totalDataCodewords: capacityECCBaseValues[i + 12], + eccPerBlock: capacityECCBaseValues[i + 13], + blocksInGroup1: capacityECCBaseValues[i + 14], + codewordsInGroup1: capacityECCBaseValues[i + 15], + blocksInGroup2: capacityECCBaseValues[i + 16], + codewordsInGroup2: capacityECCBaseValues[i + 17] + ), + new ECCInfo( + version: (i + 24) / 24, + errorCorrectionLevel: ECCLevel.H, + totalDataCodewords: capacityECCBaseValues[i + 18], + eccPerBlock: capacityECCBaseValues[i + 19], + blocksInGroup1: capacityECCBaseValues[i + 20], + codewordsInGroup1: capacityECCBaseValues[i + 21], + blocksInGroup2: capacityECCBaseValues[i + 22], + codewordsInGroup2: capacityECCBaseValues[i + 23] + ) + }); + } + return localCapacityECCTable; + } + + /// + /// Generates a list containing detailed capacity information for various versions of QR codes. + /// This table includes capacities for different encoding modes (numeric, alphanumeric, byte, etc.) under each error correction level. + /// The capacity table is crucial for QR code generation, as it determines how much data each QR code version can store depending on the encoding mode and error correction level used. + /// The index in the capacity table corresponds to one less than the version number. + /// + private static List CreateCapacityTable(int[] capacityBaseValues) + { + var localCapacityTable = new List(40); + for (var i = 0; i < (16 * 40); i += 16) + { + localCapacityTable.Add(new VersionInfo( + version: (i + 16) / 16, + versionInfoDetails: new List(4) + { + new VersionInfoDetails( + ECCLevel.L, + new Dictionary(){ + { EncodingMode.Numeric, capacityBaseValues[i] }, + { EncodingMode.Alphanumeric, capacityBaseValues[i + 1] }, + { EncodingMode.Byte, capacityBaseValues[i + 2] }, + { EncodingMode.Kanji, capacityBaseValues[i + 3] }, + } + ), + new VersionInfoDetails( + ECCLevel.M, + new Dictionary(){ + { EncodingMode.Numeric, capacityBaseValues[i + 4] }, + { EncodingMode.Alphanumeric, capacityBaseValues[i + 5] }, + { EncodingMode.Byte, capacityBaseValues[i + 6] }, + { EncodingMode.Kanji, capacityBaseValues[i + 7] }, + } + ), + new VersionInfoDetails( + ECCLevel.Q, + new Dictionary(){ + { EncodingMode.Numeric, capacityBaseValues[i + 8] }, + { EncodingMode.Alphanumeric, capacityBaseValues[i + 9] }, + { EncodingMode.Byte, capacityBaseValues[i + 10] }, + { EncodingMode.Kanji, capacityBaseValues[i + 11] }, + } + ), + new VersionInfoDetails( + ECCLevel.H, + new Dictionary(){ + { EncodingMode.Numeric, capacityBaseValues[i + 12] }, + { EncodingMode.Alphanumeric, capacityBaseValues[i + 13] }, + { EncodingMode.Byte, capacityBaseValues[i + 14] }, + { EncodingMode.Kanji, capacityBaseValues[i + 15] }, + } + ) + } + )); + } + return localCapacityTable; + } + } +} diff --git a/QRCoder/QRCodeGenerator/GaloisField.cs b/QRCoder/QRCodeGenerator/GaloisField.cs new file mode 100644 index 00000000..d1e95f96 --- /dev/null +++ b/QRCoder/QRCodeGenerator/GaloisField.cs @@ -0,0 +1,48 @@ +using System; + +namespace QRCoder; + +public partial class QRCodeGenerator +{ + /// + /// Represents a Galois field of 256 elements (GF(256)) used in finite field arithmetic, + /// typically for error correction algorithms such as Reed-Solomon. + /// + /// Provides mappings between exponential and integer representations of field elements + /// using a primitive element (α). The field is constructed with respect to a generator + /// polynomial and used for efficient encoding and decoding operations. + /// + /// + private static class GaloisField + { + private static readonly int[] _galoisFieldByExponentAlpha = { 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1 }; + private static readonly int[] _galoisFieldByIntegerValue = { 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175 }; + + /// + /// Retrieves the integer value from the Galois field that corresponds to a given exponent. + /// This is used in Reed-Solomon and other error correction calculations involving Galois fields. + /// + public static int GetIntValFromAlphaExp(int exp) + => _galoisFieldByExponentAlpha[exp]; + + /// + /// Retrieves the exponent from the Galois field that corresponds to a given integer value. + /// Throws an exception if the integer value is zero, as zero does not have a logarithmic representation in the field. + /// + public static int GetAlphaExpFromIntVal(int intVal) + { + if (intVal == 0) + ThrowIntValOutOfRangeException(); // Zero is not valid as it does not have an exponent representation. + return _galoisFieldByIntegerValue[intVal]; + + void ThrowIntValOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(intVal), "The provided integer value is out of range, as zero is not representable."); + } + + /// + /// Normalizes a Galois field exponent to ensure it remains within the bounds of the field's size. + /// This is particularly necessary when performing multiplications in the field which can result in exponents exceeding the field's maximum. + /// + public static int ShrinkAlphaExp(int alphaExp) + => (alphaExp % 256) + (alphaExp / 256); + } +} diff --git a/QRCoderDemoUWP/MainPage.xaml.cs b/QRCoderDemoUWP/MainPage.xaml.cs index 7b2fca02..6c58f3eb 100644 --- a/QRCoderDemoUWP/MainPage.xaml.cs +++ b/QRCoderDemoUWP/MainPage.xaml.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; +using QRCoder; using Windows.Storage.Streams; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Imaging; -using QRCoder; // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 diff --git a/QRCoderDemoUWP/Properties/AssemblyInfo.cs b/QRCoderDemoUWP/Properties/AssemblyInfo.cs index dc05967c..3a4387d4 100644 --- a/QRCoderDemoUWP/Properties/AssemblyInfo.cs +++ b/QRCoderDemoUWP/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -26,4 +26,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: ComVisible(false)] \ No newline at end of file +[assembly: ComVisible(false)] diff --git a/QRCoderTests/PngByteQRCodeRendererTests.cs b/QRCoderTests/PngByteQRCodeRendererTests.cs index d73b8e43..f2850c30 100644 --- a/QRCoderTests/PngByteQRCodeRendererTests.cs +++ b/QRCoderTests/PngByteQRCodeRendererTests.cs @@ -113,7 +113,6 @@ public void can_render_pngbyte_qrcode_color_without_quietzones() var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); result.ShouldBe("07f760b3eb54901840b094d31e299713"); #else - File.WriteAllBytes(@"C:\Temp\pngbyte_35.png", pngCodeGfx); using var mStream = new MemoryStream(pngCodeGfx); var bmp = (Bitmap)Image.FromStream(mStream); bmp.MakeTransparent(Color.Transparent); diff --git a/QRCoderTests/QRGeneratorTests.cs b/QRCoderTests/QRGeneratorTests.cs index 56cf7ca7..b2ec4441 100644 --- a/QRCoderTests/QRGeneratorTests.cs +++ b/QRCoderTests/QRGeneratorTests.cs @@ -21,7 +21,8 @@ public void validate_antilogtable() var gen = new QRCodeGenerator(); var checkString = string.Empty; - var gField = gen.GetType().GetField("_galoisFieldByExponentAlpha", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); + var tablesType = Type.GetType("QRCoder.QRCodeGenerator+GaloisField, QRCoder"); + var gField = tablesType.GetField("_galoisFieldByExponentAlpha", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); gField.Length.ShouldBe(256); for (int i = 0; i < gField.Length; i++) { @@ -29,7 +30,7 @@ public void validate_antilogtable() } checkString.ShouldBe("0,1,:1,2,:2,4,:3,8,:4,16,:5,32,:6,64,:7,128,:8,29,:9,58,:10,116,:11,232,:12,205,:13,135,:14,19,:15,38,:16,76,:17,152,:18,45,:19,90,:20,180,:21,117,:22,234,:23,201,:24,143,:25,3,:26,6,:27,12,:28,24,:29,48,:30,96,:31,192,:32,157,:33,39,:34,78,:35,156,:36,37,:37,74,:38,148,:39,53,:40,106,:41,212,:42,181,:43,119,:44,238,:45,193,:46,159,:47,35,:48,70,:49,140,:50,5,:51,10,:52,20,:53,40,:54,80,:55,160,:56,93,:57,186,:58,105,:59,210,:60,185,:61,111,:62,222,:63,161,:64,95,:65,190,:66,97,:67,194,:68,153,:69,47,:70,94,:71,188,:72,101,:73,202,:74,137,:75,15,:76,30,:77,60,:78,120,:79,240,:80,253,:81,231,:82,211,:83,187,:84,107,:85,214,:86,177,:87,127,:88,254,:89,225,:90,223,:91,163,:92,91,:93,182,:94,113,:95,226,:96,217,:97,175,:98,67,:99,134,:100,17,:101,34,:102,68,:103,136,:104,13,:105,26,:106,52,:107,104,:108,208,:109,189,:110,103,:111,206,:112,129,:113,31,:114,62,:115,124,:116,248,:117,237,:118,199,:119,147,:120,59,:121,118,:122,236,:123,197,:124,151,:125,51,:126,102,:127,204,:128,133,:129,23,:130,46,:131,92,:132,184,:133,109,:134,218,:135,169,:136,79,:137,158,:138,33,:139,66,:140,132,:141,21,:142,42,:143,84,:144,168,:145,77,:146,154,:147,41,:148,82,:149,164,:150,85,:151,170,:152,73,:153,146,:154,57,:155,114,:156,228,:157,213,:158,183,:159,115,:160,230,:161,209,:162,191,:163,99,:164,198,:165,145,:166,63,:167,126,:168,252,:169,229,:170,215,:171,179,:172,123,:173,246,:174,241,:175,255,:176,227,:177,219,:178,171,:179,75,:180,150,:181,49,:182,98,:183,196,:184,149,:185,55,:186,110,:187,220,:188,165,:189,87,:190,174,:191,65,:192,130,:193,25,:194,50,:195,100,:196,200,:197,141,:198,7,:199,14,:200,28,:201,56,:202,112,:203,224,:204,221,:205,167,:206,83,:207,166,:208,81,:209,162,:210,89,:211,178,:212,121,:213,242,:214,249,:215,239,:216,195,:217,155,:218,43,:219,86,:220,172,:221,69,:222,138,:223,9,:224,18,:225,36,:226,72,:227,144,:228,61,:229,122,:230,244,:231,245,:232,247,:233,243,:234,251,:235,235,:236,203,:237,139,:238,11,:239,22,:240,44,:241,88,:242,176,:243,125,:244,250,:245,233,:246,207,:247,131,:248,27,:249,54,:250,108,:251,216,:252,173,:253,71,:254,142,:255,1,:"); - var gField2 = gen.GetType().GetField("_galoisFieldByIntegerValue", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); + var gField2 = tablesType.GetField("_galoisFieldByIntegerValue", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); gField2.Length.ShouldBe(256); var checkString2 = string.Empty; for (int i = 0; i < gField2.Length; i++) @@ -369,7 +370,8 @@ public void validate_alphanumencdict() var gen = new QRCodeGenerator(); var checkString = string.Empty; - var gField = gen.GetType().GetField("_alphanumEncDict", BindingFlags.NonPublic | BindingFlags.Static); + var encoderType = Type.GetType("QRCoder.QRCodeGenerator+AlphanumericEncoder, QRCoder"); + var gField = encoderType.GetField("_alphanumEncDict", BindingFlags.NonPublic | BindingFlags.Static); foreach (var listitem in (Dictionary)gField.GetValue(gen)) { checkString += $"{listitem.Key},{listitem.Value}:";