diff --git a/QRCoder/QRCodeData.cs b/QRCoder/QRCodeData.cs
index 0c27ff55..7fb8a68b 100644
--- a/QRCoder/QRCodeData.cs
+++ b/QRCoder/QRCodeData.cs
@@ -194,10 +194,12 @@ public void SaveRawData(string filePath, Compression compressMode)
///
/// Gets the number of modules per side from the specified version.
///
- /// The version of the QR code.
+ /// The version of the QR code (1 to 40, or -1 to -4 for Micro QR codes).
/// Returns the number of modules per side.
private static int ModulesPerSideFromVersion(int version)
- => 21 + (version - 1) * 4;
+ => version > 0
+ ? 21 + (version - 1) * 4
+ : 11 + (-version - 1) * 2;
///
/// Releases all resources used by the .
diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs
index f446a3f4..95d7b85d 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);
}
}
@@ -163,6 +150,85 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo
return GenerateQrCode(completeBitArray, eccLevel, version);
}
+ ///
+ /// Calculates the Micro QR code data which than can be used in one of the rendering classes to generate a graphical representation.
+ ///
+ /// The payload which shall be encoded in the QR code
+ /// The level of error correction data
+ /// Set fixed Micro QR code target version; must be -1 to -4 representing M1 to M4, or 0 for default.
+ /// Thrown when the payload is too big to be encoded in a QR code.
+ /// Returns the raw QR code data which can be used for rendering.
+ public static QRCodeData GenerateMicroQrCode(string plainText, ECCLevel eccLevel = ECCLevel.Default, int requestedVersion = 0)
+ {
+ if (requestedVersion < -4 || requestedVersion > 0)
+ throw new ArgumentOutOfRangeException(nameof(requestedVersion), requestedVersion, "Requested version must be -1 to -4 representing M1 to M4, or 0 for default.");
+ ValidateECCLevel(eccLevel);
+ if (eccLevel == ECCLevel.H)
+ throw new ArgumentOutOfRangeException(nameof(eccLevel), eccLevel, "Micro QR codes does not support error correction level H.");
+ if (eccLevel == ECCLevel.Q && requestedVersion != -4)
+ throw new ArgumentOutOfRangeException(nameof(eccLevel), eccLevel, "Micro QR codes only supports error correction level Q for version M4.");
+ if (eccLevel != ECCLevel.Default && requestedVersion == -1)
+ throw new ArgumentOutOfRangeException(nameof(eccLevel), eccLevel, "Please specify ECCLevel.Default for version M1.");
+ if (plainText == null)
+ throw new ArgumentNullException(nameof(plainText));
+
+ var encoding = GetEncodingFromPlaintext(plainText, false);
+ var codedText = PlainTextToBinary(plainText, encoding, EciMode.Default, false, false);
+ var dataInputLength = GetDataLength(encoding, plainText, codedText, false);
+ int version = requestedVersion;
+ int minVersion = CapacityTables.CalculateMinimumMicroVersion(dataInputLength, encoding, eccLevel);
+
+ if (version == 0)
+ {
+ version = minVersion;
+ }
+ else
+ {
+ //Version was passed as fixed version via parameter. Thus let's check if chosen version is valid.
+ if (minVersion < version)
+ {
+ var matchedEncoding = CapacityTables.GetVersionInfo(version).Details
+ .First(x => (x.ErrorCorrectionLevel == eccLevel || (eccLevel == ECCLevel.Default && x.ErrorCorrectionLevel == ECCLevel.L)))
+ .CapacityDict.TryGetValue(encoding, out var maxSizeByte);
+ if (!matchedEncoding)
+ throw new ArgumentOutOfRangeException(nameof(encoding), encoding, "Encoding not supported for this version.");
+ throw new QRCoder.Exceptions.DataTooLongException(eccLevel.ToString(), encoding.ToString(), version, maxSizeByte);
+ }
+ }
+ if (version < -1 && eccLevel == ECCLevel.Default)
+ eccLevel = ECCLevel.L;
+
+ var modeIndicatorLength = -version - 1; // 0 for M1, 1 for M2, 2 for M3, 3 for M4
+ var countIndicatorLength = GetCountIndicatorLength(version, encoding);
+ var completeBitArrayLength = modeIndicatorLength + countIndicatorLength + codedText.Length;
+
+ var completeBitArray = new BitArray(completeBitArrayLength);
+
+ // write mode indicator
+ var completeBitArrayIndex = 0;
+ if (version < 0)
+ {
+ var encodingValue =
+ encoding == EncodingMode.Numeric ? 0 :
+ encoding == EncodingMode.Alphanumeric ? 1 :
+ encoding == EncodingMode.Byte ? 2 : 3;
+ completeBitArrayIndex = DecToBin(encodingValue, modeIndicatorLength, completeBitArray, completeBitArrayIndex);
+ }
+ else
+ {
+ completeBitArrayIndex = DecToBin((int)encoding, 4, completeBitArray, completeBitArrayIndex);
+ }
+ // write count indicator
+ completeBitArrayIndex = DecToBin(dataInputLength, countIndicatorLength, completeBitArray, completeBitArrayIndex);
+ // write data
+ for (int i = 0; i < codedText.Length; i++)
+ {
+ completeBitArray[completeBitArrayIndex++] = codedText[i];
+ }
+
+ return GenerateQrCode(completeBitArray, eccLevel, version);
+ }
+
///
/// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation.
///
@@ -173,7 +239,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 +279,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();
@@ -236,7 +302,7 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i
// fills the bit array with a repeating pattern to reach the required length
void PadData()
{
- var dataLength = eccInfo.TotalDataCodewords * 8;
+ var dataLength = eccInfo.TotalDataBits;
var lengthDiff = dataLength - bitArray.Length;
if (lengthDiff > 0)
{
@@ -244,11 +310,19 @@ void PadData()
var index = bitArray.Length;
// extend bit array to required length
bitArray.Length = dataLength;
- // pad with 4 zeros (or less if lengthDiff < 4)
- index += Math.Min(lengthDiff, 4);
+ // compute padding length
+ var padLength = version > 0 ? 4 :
+ version == -1 ? 3 :
+ version == -2 ? 5 :
+ version == -3 ? 7 : 9;
+ // pad with zeros (or less if not enough room)
+ index += padLength;
// pad to nearest 8 bit boundary
if ((uint)index % 8 != 0)
index += 8 - (int)((uint)index % 8);
+ // for m1 and m3 sizes don't fill last 4 bits with repeating pattern
+ if (version == -1 || version == -3)
+ dataLength -= 4;
// pad with repeating pattern
var repeatingPatternIndex = 0;
while (index < dataLength)
@@ -277,6 +351,7 @@ List CalculateECCBlocks()
void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, int offset2, int count, Polynom generatorPolynom)
{
var groupLength = codewordsInGroup * 8;
+ groupLength = groupLength > count ? count : groupLength;
for (var i = 0; i < blocksInGroup; i++)
{
var eccWordList = CalculateECCWords(bitArray, offset2, groupLength, eccInfo, generatorPolynom);
@@ -290,7 +365,13 @@ void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, in
int CalculateInterleavedLength()
{
var length = 0;
- for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++)
+ var codewords = Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2);
+ if (version == -1 || version == -3)
+ {
+ codewords--;
+ length += 4;
+ }
+ for (var i = 0; i < codewords; i++)
{
foreach (var codeBlock in codeWordWithECC)
if ((uint)codeBlock.CodeWordsLength / 8 > i)
@@ -302,7 +383,7 @@ int CalculateInterleavedLength()
if (codeBlock.ECCWords.Length > i)
length += 8;
}
- length += _remainderBits[version - 1];
+ length += CapacityTables.GetRemainderBits(version);
return length;
}
@@ -311,6 +392,9 @@ BitArray InterleaveData()
{
var data = new BitArray(interleavedLength);
int pos = 0;
+ int codewords = Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2);
+ if (version == -1 || version == -3)
+ codewords--;
for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++)
{
foreach (var codeBlock in codeWordWithECC)
@@ -319,6 +403,10 @@ BitArray InterleaveData()
pos = bitArray.CopyTo(data, (int)((uint)i * 8) + codeBlock.CodeWordsOffset, pos, 8);
}
}
+ if (version == -1 || version == -3)
+ {
+ pos = bitArray.CopyTo(data, (int)((uint)codewords * 8) + codeWordWithECC[0].CodeWordsOffset, pos, 4);
+ }
for (var i = 0; i < eccInfo.ECCPerBlock; i++)
{
foreach (var codeBlock in codeWordWithECC)
@@ -338,14 +426,14 @@ QRCodeData PlaceModules()
using (var blockedModules = new ModulePlacer.BlockedModules(size))
{
ModulePlacer.PlaceFinderPatterns(qr, blockedModules);
- ModulePlacer.ReserveSeperatorAreas(size, blockedModules);
- ModulePlacer.PlaceAlignmentPatterns(qr, _alignmentPatternTable[version].PatternPositions, blockedModules);
+ ModulePlacer.ReserveSeperatorAreas(version, size, blockedModules);
+ ModulePlacer.PlaceAlignmentPatterns(qr, AlignmentPatterns.FromVersion(version).PatternPositions, blockedModules);
ModulePlacer.PlaceTimingPatterns(qr, blockedModules);
ModulePlacer.PlaceDarkModule(qr, version, blockedModules);
ModulePlacer.ReserveVersionAreas(size, version, blockedModules);
ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules);
var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel);
- GetFormatString(tempBitArray, eccLevel, maskVersion);
+ GetFormatString(tempBitArray, version, eccLevel, maskVersion);
ModulePlacer.PlaceFormat(qr, tempBitArray, true);
}
@@ -361,19 +449,25 @@ QRCodeData PlaceModules()
private static readonly BitArray _getFormatGenerator = new BitArray(new bool[] { true, false, true, false, false, true, true, false, true, true, true });
private static readonly BitArray _getFormatMask = new BitArray(new bool[] { true, false, true, false, true, false, false, false, false, false, true, false, false, true, false });
+ private static readonly BitArray _getFormatMicroMask = new BitArray(new bool[] { true, false, false, false, true, false, false, false, true, false, false, false, true, false, true });
+
///
/// Generates a BitArray containing the format string for a QR code based on the error correction level and mask pattern version.
/// The format string includes the error correction level, mask pattern version, and error correction coding.
///
- /// The to write to, or null to create a new one.
+ /// The to write to, or null to create a new one.
+ /// The version number of the QR Code (1-40, or -1 to -4 for Micro QR codes).
/// The error correction level to be encoded in the format string.
/// The mask pattern version to be encoded in the format string.
/// A BitArray containing the 15-bit format string used in QR code generation.
- private static void GetFormatString(BitArray fStrEcc, ECCLevel level, int maskVersion)
+ private static void GetFormatString(BitArray fStrEcc, int version, ECCLevel level, int maskVersion)
{
fStrEcc.Length = 15;
fStrEcc.SetAll(false);
- WriteEccLevelAndVersion();
+ if (version < 0)
+ WriteMicroEccLevelAndVersion();
+ else
+ WriteEccLevelAndVersion();
// Apply the format generator polynomial to add error correction to the format string.
int index = 0;
@@ -392,10 +486,13 @@ private static void GetFormatString(BitArray fStrEcc, ECCLevel level, int maskVe
// Prefix the error correction bits with the ECC level and version number.
fStrEcc.Length = 10 + 5;
ShiftAwayFromBit0(fStrEcc, (10 - count) + 5);
- WriteEccLevelAndVersion();
+ if (version < 0)
+ WriteMicroEccLevelAndVersion();
+ else
+ WriteEccLevelAndVersion();
// XOR the format string with a predefined mask to add robustness against errors.
- fStrEcc.Xor(_getFormatMask);
+ fStrEcc.Xor(version < 0 ? _getFormatMicroMask : _getFormatMask);
void WriteEccLevelAndVersion()
{
@@ -418,6 +515,46 @@ void WriteEccLevelAndVersion()
// Insert the 3-bit mask version directly after the error correction level bits.
DecToBin(maskVersion, 3, fStrEcc, 2);
}
+
+ void WriteMicroEccLevelAndVersion()
+ {
+ switch (version)
+ {
+ case -1: // M1
+ break;
+ case -2: // M2
+ fStrEcc[level == ECCLevel.L ? 2 : 1] = true; // 001 for L and 010 for M
+ break;
+ case -3: // M3
+ if (level == ECCLevel.L)
+ {
+ fStrEcc[1] = true; // 011 for L
+ fStrEcc[2] = true;
+ }
+ else
+ fStrEcc[0] = true; // 100 for M
+ break;
+ default: // M4
+ fStrEcc[0] = true;
+ if (level == ECCLevel.L) // 101 for L
+ fStrEcc[2] = true;
+ else if (level == ECCLevel.M) // 110 for M
+ fStrEcc[1] = true;
+ else // 111 for Q
+ {
+ fStrEcc[1] = true;
+ fStrEcc[2] = true;
+ }
+ break;
+ }
+
+ // Insert the 2-bit mask version directly after the version / error correction level bits.
+ var microMaskVersion =
+ maskVersion == 1 ? 0 :
+ maskVersion == 4 ? 1 :
+ maskVersion == 6 ? 2 : 3;
+ DecToBin(microMaskVersion, 2, fStrEcc, 3);
+ }
}
#if !NETFRAMEWORK || NET45_OR_GREATER
@@ -528,7 +665,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,42 +701,10 @@ 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);
+ poly[i] = new PolynomItem(GaloisField.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;
- }
- }
- }
-
- // 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);
- }
-
///
/// Determines the most efficient encoding mode for the given plain text based on its character content
/// and a flag indicating whether to force UTF-8 encoding.
@@ -614,7 +719,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
}
@@ -628,17 +733,52 @@ private static bool IsInRange(char c, char min, char max)
=> (uint)(c - min) <= (uint)(max - min);
///
- /// Calculates the message polynomial from a bit array which represents the encoded data.
+ /// Converts a segment of a BitArray representing QR code data into a polynomial,
+ /// padding the final byte if necessary (for Micro QR variants like M1 or M3).
///
- /// A polynomial representation of the message.
- private static Polynom CalculateMessagePolynom(BitArray bitArray, int offset, int count)
+ /// The full bit array representing encoded QR code data.
+ /// Starting position in the bit array.
+ /// Total number of bits to convert into codewords.
+ /// A polynomial representing the message codewords.
+ private static Polynom CalculateMessagePolynom(BitArray bitArray, int offset, int bitCount)
{
- var messagePol = new Polynom(count /= 8);
- for (var i = count - 1; i >= 0; i--)
+ // Calculate how many full 8-bit codewords are present
+ var fullBytes = bitCount / 8;
+
+ // Determine if there is a remaining partial byte (e.g., 4 bits for Micro QR M1 and M3 versions)
+ var remainingBits = bitCount % 8;
+
+ if (remainingBits > 0)
+ {
+ // Pad the last byte with zero bits to make it a full 8-bit codeword
+ var addlBits = 8 - remainingBits;
+ var minBitArrayLength = offset + bitCount + addlBits;
+
+ // Extend BitArray length if needed to fit the padded bits
+ if (bitArray.Length < minBitArrayLength)
+ bitArray.Length = minBitArrayLength;
+
+ // Pad the remaining bits with false (0) values
+ for (int i = 0; i < addlBits; i++)
+ bitArray[offset + bitCount + i] = false;
+ }
+
+ // Total number of codewords (includes extra for partial byte if present)
+ var polynomLength = fullBytes + (remainingBits > 0 ? 1 : 0);
+
+ // Initialize the polynomial
+ var messagePol = new Polynom(polynomLength);
+
+ // Exponent for polynomial terms starts from highest degree
+ int exponent = polynomLength - 1;
+
+ // Convert each 8-bit segment into a decimal value and add it to the polynomial
+ for (int i = 0; i < polynomLength; i++)
{
- messagePol.Add(new PolynomItem(BinToDec(bitArray, offset, 8), i));
+ messagePol.Add(new PolynomItem(BinToDec(bitArray, offset, 8), exponent--));
offset += 8;
}
+
return messagePol;
}
@@ -715,7 +855,33 @@ private static int DecToBin(int decNum, int bits, BitArray bitList, int index)
private static int GetCountIndicatorLength(int version, EncodingMode encMode)
{
// Different versions and encoding modes require different lengths of bits to represent the character count efficiently
- if (version < 10)
+ if (version == -1)
+ {
+ return 3;
+ }
+ else if (version == -2)
+ {
+ return encMode == EncodingMode.Numeric ? 4 : 3;
+ }
+ else if (version == -3)
+ {
+ if (encMode == EncodingMode.Numeric)
+ return 5;
+ else if (encMode == EncodingMode.Kanji)
+ return 3;
+ else
+ return 4;
+ }
+ else if (version == -4)
+ {
+ if (encMode == EncodingMode.Numeric)
+ return 6;
+ else if (encMode == EncodingMode.Kanji)
+ return 4;
+ else
+ return 5;
+ }
+ else if (version < 10)
{
if (encMode == EncodingMode.Numeric)
return 10;
@@ -795,7 +961,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 +1019,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 +1220,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 +1237,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 +1293,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..55db9cc6
--- /dev/null
+++ b/QRCoder/QRCodeGenerator/AlignmentPatterns.cs
@@ -0,0 +1,70 @@
+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 + 8);
+
+ 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
+ });
+ }
+
+ // Micro QR codes do not have alignment patterns.
+ var emptyPointList = new List();
+ localAlignmentPatternTable.Add(-1, new AlignmentPattern { Version = -1, PatternPositions = emptyPointList });
+ localAlignmentPatternTable.Add(-2, new AlignmentPattern { Version = -2, PatternPositions = emptyPointList });
+ localAlignmentPatternTable.Add(-3, new AlignmentPattern { Version = -3, PatternPositions = emptyPointList });
+ localAlignmentPatternTable.Add(-4, new AlignmentPattern { Version = -4, PatternPositions = emptyPointList });
+
+ 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..df885273
--- /dev/null
+++ b/QRCoder/QRCodeGenerator/CapacityTables.cs
@@ -0,0 +1,380 @@
+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);
+ private static readonly List _microCapacityTable = CreateMicroCapacityTable();
+
+ ///
+ /// 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, or -1 to -4 for M1 to M4).
+ /// 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, or -1 to -4 for M1 to M4).
+ ///
+ /// A object containing data capacity details for all error correction levels
+ /// and encoding modes for the specified version.
+ ///
+ public static VersionInfo GetVersionInfo(int version)
+ => version < 0 ? _microCapacityTable[-version - 1] : _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, or -1 to -4 for M1 to M4).
+ ///
+ /// The number of remainder bits (0 to 7) that must be appended to the encoded bit stream.
+ ///
+ public static int GetRemainderBits(int version)
+ => version < 0 ? 0 : _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)
+ {
+ // only iterates through non-micro QR codes
+ // 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);
+ }
+
+
+ ///
+ /// Determines the minimum Micro 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., Default, Low, Medium, Quartile, High).
+ /// The minimum version of the QR code (-1 to -4) 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 CalculateMinimumMicroVersion(int length, EncodingMode encMode, ECCLevel eccLevel)
+ {
+ // only iterates through non-micro QR codes
+ // 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 _microCapacityTable)
+ {
+ // find the requested ECC level and encoding mode in the capacity table
+ foreach (var y in x.Details)
+ {
+ // Use ECC level L for Micro QR Code versions 2, 3 and 4 when Default is specified
+ if (y.ErrorCorrectionLevel == eccLevel || (eccLevel == ECCLevel.Default && y.ErrorCorrectionLevel == ECCLevel.L))
+ {
+ // Not all versions support all encoding modes, so check if the encoding mode is supported
+ if (y.CapacityDict.TryGetValue(encMode, out int maxLength) && maxLength >= 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 = _microCapacityTable
+ .SelectMany(x => x.Details)
+ .Where(y => (y.ErrorCorrectionLevel == eccLevel || (eccLevel == ECCLevel.Default && y.ErrorCorrectionLevel == ECCLevel.L))
+ && y.CapacityDict.ContainsKey(encMode))
+ .Max(y => y.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 + 8);
+ 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]
+ )
+ });
+ }
+
+ localCapacityECCTable.AddRange(new ECCInfo[]
+ {
+ // Micro QR Code Version M1 - only supports ECCLevel.Default (none)
+ new ECCInfo(
+ version: -1,
+ errorCorrectionLevel: ECCLevel.Default,
+ totalDataCodewords: 3,
+ totalDataBits: 20,
+ eccPerBlock: 2),
+
+ // Micro QR Code Version M2
+ new ECCInfo(-2, ECCLevel.L, 5, 40, 5),
+ new ECCInfo(-2, ECCLevel.M, 4, 32, 6),
+
+ // Micro QR Code Version M3
+ new ECCInfo(-3, ECCLevel.L, 11, 84, 6),
+ new ECCInfo(-3, ECCLevel.M, 9, 68, 8),
+
+ // Micro QR Code Version M4
+ new ECCInfo(-4, ECCLevel.L, 16, 128, 8),
+ new ECCInfo(-4, ECCLevel.M, 14, 112, 10),
+ new ECCInfo(-4, ECCLevel.Q, 10, 80, 14),
+ });
+
+ 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;
+ }
+
+ ///
+ private static List CreateMicroCapacityTable()
+ {
+ var tbl = new List(4);
+
+ var m1details = new List(1)
+ {
+ new VersionInfoDetails(
+ ECCLevel.Default, // none
+ new Dictionary(1) {
+ { EncodingMode.Numeric, 5 },
+ }
+ )
+ };
+ tbl.Add(new VersionInfo(-1, m1details));
+
+ var m2details = new List(2)
+ {
+ new VersionInfoDetails(
+ ECCLevel.L,
+ new Dictionary(2) {
+ { EncodingMode.Numeric, 10 },
+ { EncodingMode.Alphanumeric, 6 },
+ }
+ ),
+ new VersionInfoDetails(
+ ECCLevel.M,
+ new Dictionary(2) {
+ { EncodingMode.Numeric, 8 },
+ { EncodingMode.Alphanumeric, 5 },
+ }
+ ),
+ };
+ tbl.Add(new VersionInfo(-2, m2details));
+
+ var m3details = new List(2)
+ {
+ new VersionInfoDetails(
+ ECCLevel.L,
+ new Dictionary(4) {
+ { EncodingMode.Numeric, 23 },
+ { EncodingMode.Alphanumeric, 14 },
+ { EncodingMode.Byte, 9 },
+ { EncodingMode.Kanji, 6 },
+ }
+ ),
+ new VersionInfoDetails(
+ ECCLevel.M,
+ new Dictionary(4) {
+ { EncodingMode.Numeric, 18 },
+ { EncodingMode.Alphanumeric, 11 },
+ { EncodingMode.Byte, 7 },
+ { EncodingMode.Kanji, 4 },
+ }
+ ),
+ };
+ tbl.Add(new VersionInfo(-3, m3details));
+
+ var m4details = new List(3)
+ {
+ new VersionInfoDetails(
+ ECCLevel.L,
+ new Dictionary(4) {
+ { EncodingMode.Numeric, 35 },
+ { EncodingMode.Alphanumeric, 21 },
+ { EncodingMode.Byte, 15 },
+ { EncodingMode.Kanji, 9 },
+ }
+ ),
+ new VersionInfoDetails(
+ ECCLevel.M,
+ new Dictionary(4) {
+ { EncodingMode.Numeric, 30 },
+ { EncodingMode.Alphanumeric, 18 },
+ { EncodingMode.Byte, 13 },
+ { EncodingMode.Kanji, 8 },
+ }
+ ),
+ new VersionInfoDetails(
+ ECCLevel.Q,
+ new Dictionary(4) {
+ { EncodingMode.Numeric, 21 },
+ { EncodingMode.Alphanumeric, 13 },
+ { EncodingMode.Byte, 9 },
+ { EncodingMode.Kanji, 5 },
+ }
+ ),
+ };
+ tbl.Add(new VersionInfo(-4, m4details));
+
+ return tbl;
+ }
+ }
+}
diff --git a/QRCoder/QRCodeGenerator/ECCInfo.cs b/QRCoder/QRCodeGenerator/ECCInfo.cs
index 919288ca..9e8b787e 100644
--- a/QRCoder/QRCodeGenerator/ECCInfo.cs
+++ b/QRCoder/QRCodeGenerator/ECCInfo.cs
@@ -24,6 +24,7 @@ public ECCInfo(int version, ECCLevel errorCorrectionLevel, int totalDataCodeword
Version = version;
ErrorCorrectionLevel = errorCorrectionLevel;
TotalDataCodewords = totalDataCodewords;
+ TotalDataBits = totalDataCodewords * 8;
ECCPerBlock = eccPerBlock;
BlocksInGroup1 = blocksInGroup1;
CodewordsInGroup1 = codewordsInGroup1;
@@ -31,6 +32,27 @@ public ECCInfo(int version, ECCLevel errorCorrectionLevel, int totalDataCodeword
CodewordsInGroup2 = codewordsInGroup2;
}
+ ///
+ /// Initializes a new instance of the ECCInfo struct with specified properties for Micro QR codes.
+ ///
+ /// The version number of the QR code.
+ /// The error correction level used in the QR code.
+ /// The total number of data codewords for this version and error correction level.
+ /// The total number of data bits for this version and error correction level.
+ /// The number of error correction codewords per block.
+ public ECCInfo(int version, ECCLevel errorCorrectionLevel, int totalDataCodewords, int totalDataBits, int eccPerBlock)
+ {
+ Version = version;
+ ErrorCorrectionLevel = errorCorrectionLevel;
+ TotalDataCodewords = totalDataCodewords;
+ TotalDataBits = totalDataBits;
+ ECCPerBlock = eccPerBlock;
+ BlocksInGroup1 = 1;
+ CodewordsInGroup1 = totalDataCodewords;
+ BlocksInGroup2 = 0;
+ CodewordsInGroup2 = 0;
+ }
+
///
/// Gets the version number of the QR code.
///
@@ -46,6 +68,11 @@ public ECCInfo(int version, ECCLevel errorCorrectionLevel, int totalDataCodeword
///
public int TotalDataCodewords { get; }
+ ///
+ /// Gets the total number of data codewords for this version and error correction level.
+ ///
+ public int TotalDataBits { get; }
+
///
/// Gets the number of error correction codewords per block.
///
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/QRCoder/QRCodeGenerator/ModulePlacer.MaskPattern.cs b/QRCoder/QRCodeGenerator/ModulePlacer.MaskPattern.cs
index 4b46dac6..1d5b80e8 100644
--- a/QRCoder/QRCodeGenerator/ModulePlacer.MaskPattern.cs
+++ b/QRCoder/QRCodeGenerator/ModulePlacer.MaskPattern.cs
@@ -78,6 +78,28 @@ public static bool Pattern7(int x, int y)
public static bool Pattern8(int x, int y)
=> (((x + y) % 2) + ((x * y) % 3)) % 2 == 0;
+ ///
+ /// Calculates a penalty score for a Micro QR code to evaluate the effectiveness of a mask pattern.
+ /// A lower score indicates a QR code that is easier for decoders to read accurately.
+ ///
+ /// The QR code data structure to be evaluated.
+ /// The total penalty score of the QR code.
+ public static int ScoreMicro(QRCodeData qrCode)
+ {
+ var size = qrCode.ModuleMatrix.Count;
+ int sum1 = 0;
+ int sum2 = 0;
+ for (var i = 1; i < size; i++)
+ {
+ if (qrCode.ModuleMatrix[size - 1][i])
+ sum1++;
+ if (qrCode.ModuleMatrix[i][size - 1])
+ sum2++;
+ }
+ int total = sum1 < sum2 ? sum1 * 16 + sum2 : sum2 * 16 + sum1;
+ return -total; // negate so that lower is better
+ }
+
///
/// Calculates a penalty score for a QR code to evaluate the effectiveness of a mask pattern.
/// A lower score indicates a QR code that is easier for decoders to read accurately.
diff --git a/QRCoder/QRCodeGenerator/ModulePlacer.cs b/QRCoder/QRCodeGenerator/ModulePlacer.cs
index f452e6b3..774d13aa 100644
--- a/QRCoder/QRCodeGenerator/ModulePlacer.cs
+++ b/QRCoder/QRCodeGenerator/ModulePlacer.cs
@@ -35,11 +35,15 @@ public static void PlaceVersion(QRCodeData qrCode, BitArray versionStr, bool off
///
/// The QR code data structure to modify.
/// The bit array containing the format information.
+ /// Specifies whether an offset should be applied.
public static void PlaceFormat(QRCodeData qrCode, BitArray formatStr, bool offset)
{
+ var isMicro = qrCode.Version < 0; // Negative versions indicate Micro QR codes.
var offsetValue = offset ? 4 : 0;
var size = qrCode.ModuleMatrix.Count - offsetValue - offsetValue;
+ // Standard QR Code Format Positions:
+ //
// { x1, y1, x2, y2 } i
// ===============================
// { 8, 0, size - 1, 8 }, // 0
@@ -58,16 +62,53 @@ public static void PlaceFormat(QRCodeData qrCode, BitArray formatStr, bool offse
// { 1, 8, 8, size - 2 }, // 13
// { 0, 8, 8, size - 1 } }; // 14
+ // Micro QR Code Format Positions:
+ //
+ // i { x1, y1 }
+ // ===============
+ // 0 { 8, 1 }
+ // 1 { 8, 2 }
+ // 2 { 8, 3 }
+ // 3 { 8, 4 }
+ // 4 { 8, 5 }
+ // 5 { 8, 6 }
+ // 6 { 8, 7 }
+ // 7 { 8, 8 }
+ // 8 { 7, 8 }
+ // 9 { 6, 8 }
+ // 10 { 5, 8 }
+ // 11 { 4, 8 }
+ // 12 { 3, 8 }
+ // 13 { 2, 8 }
+ // 14 { 1, 8 }
+
+ // The bit pattern is considered an entire 'word' and LSB goes in position 0
+ // So, we need to reverse the order of the generated bit pattern, hence the (14 - i) below
+
for (var i = 0; i < 15; i++)
{
- // values computed to follow table above
- var x1 = i < 8 ? 8 : i == 8 ? 7 : 14 - i;
- var y1 = i < 6 ? i : i < 7 ? i + 1 : 8;
- var x2 = i < 8 ? size - 1 - i : 8;
- var y2 = i < 8 ? 8 : size - (15 - i);
-
- qrCode.ModuleMatrix[y1 + offsetValue][x1 + offsetValue] = formatStr[14 - i];
- qrCode.ModuleMatrix[y2 + offsetValue][x2 + offsetValue] = formatStr[14 - i];
+ int x1, y1, x2, y2;
+
+ if (isMicro)
+ {
+ // Micro QR format positions
+ x1 = i < 8 ? 8 : 14 - i + 1;
+ y1 = i < 8 ? i + 1 : 8;
+
+ // Micro QR only uses one set of format positions, no duplication.
+ qrCode.ModuleMatrix[y1 + offsetValue][x1 + offsetValue] = formatStr[14 - i];
+ }
+ else
+ {
+ // Standard QR format positions
+ x1 = i < 8 ? 8 : i == 8 ? 7 : 14 - i;
+ y1 = i < 6 ? i : i < 7 ? i + 1 : 8;
+ x2 = i < 8 ? size - 1 - i : 8;
+ y2 = i < 8 ? 8 : size - (15 - i);
+
+ qrCode.ModuleMatrix[y1 + offsetValue][x1 + offsetValue] = formatStr[14 - i];
+ qrCode.ModuleMatrix[y2 + offsetValue][x2 + offsetValue] = formatStr[14 - i];
+ }
}
}
@@ -98,6 +139,9 @@ public static int MaskCode(QRCodeData qrCode, int version, BlockedModules blocke
var formatStr = new BitArray(15);
for (var maskPattern = 0; maskPattern < 8; maskPattern++)
{
+ if (version < 0 && (maskPattern == 0 || maskPattern == 2 || maskPattern == 3 || maskPattern == 5))
+ continue; // Micro QR codes only support certain mask patterns.
+
var patternFunc = MaskPattern.Patterns[maskPattern];
// Reset the temporary QR code to the current state of the actual QR code.
@@ -110,7 +154,7 @@ public static int MaskCode(QRCodeData qrCode, int version, BlockedModules blocke
}
// Place format information using the current mask pattern.
- GetFormatString(formatStr, eccLevel, maskPattern);
+ GetFormatString(formatStr, version, eccLevel, maskPattern);
ModulePlacer.PlaceFormat(qrTemp, formatStr, false);
// Place version information if applicable.
@@ -137,7 +181,7 @@ public static int MaskCode(QRCodeData qrCode, int version, BlockedModules blocke
}
}
- var score = MaskPattern.Score(qrTemp);
+ var score = version < 0 ? MaskPattern.ScoreMicro(qrTemp) : MaskPattern.Score(qrTemp);
// Select the pattern with the lowest score, indicating better QR code readability.
if (patternScore > score)
@@ -185,35 +229,23 @@ public static void PlaceDataWords(QRCodeData qrCode, BitArray data, BlockedModul
// Loop from the rightmost column to the leftmost column, skipping one column each time.
for (var x = size - 1; x >= 0; x -= 2)
{
- // Skip the timing pattern column at position 6.
- if (x == 6)
+ // Skip the timing pattern column at position 6 (for normal QR codes only, not Micro QR codes).
+ if (qrCode.Version > 0 && x == 6)
x = 5;
// Loop through each row in the current column set.
for (var yMod = 1; yMod <= size; yMod++)
{
- int y; // Actual y position to place data in the matrix.
-
// Determine the actual y position based on the current fill direction.
- if (up)
- {
- y = size - yMod; // Calculate y for upward direction.
- // Place data if within data length, current position is not blocked, and leftward column is in bounds.
- if (index < count && !blockedModules.IsBlocked(x, y))
- qrCode.ModuleMatrix[y + 4][x + 4] = data[index++];
- if (index < count && x > 0 && !blockedModules.IsBlocked(x - 1, y))
- qrCode.ModuleMatrix[y + 4][x - 1 + 4] = data[index++];
- }
- else
- {
- y = yMod - 1; // Calculate y for downward direction.
- // Similar checks and data placement for the downward direction.
- if (index < count && !blockedModules.IsBlocked(x, y))
- qrCode.ModuleMatrix[y + 4][x + 4] = data[index++];
- if (index < count && x > 0 && !blockedModules.IsBlocked(x - 1, y))
- qrCode.ModuleMatrix[y + 4][x - 1 + 4] = data[index++];
- }
+ int y = up ? size - yMod : yMod - 1;
+
+ // Place data if within data length and current position is not blocked.
+ if (index < count && !blockedModules.IsBlocked(x, y))
+ qrCode.ModuleMatrix[y + 4][x + 4] = data[index++];
+ if (index < count && x > 0 && !blockedModules.IsBlocked(x - 1, y))
+ qrCode.ModuleMatrix[y + 4][x - 1 + 4] = data[index++];
}
+
// Switch the fill direction after completing each column set.
up = !up;
}
@@ -224,15 +256,21 @@ public static void PlaceDataWords(QRCodeData qrCode, BitArray data, BlockedModul
///
/// The size of the QR code matrix.
/// A list of rectangles representing areas that must not be overwritten.
- public static void ReserveSeperatorAreas(int size, BlockedModules blockedModules)
+ public static void ReserveSeperatorAreas(int version, int size, BlockedModules blockedModules)
{
- // Block areas around the finder patterns, which are located near three corners of the QR code.
+ // Block areas around the top-left finder pattern
blockedModules.Add(new Rectangle(7, 0, 1, 8)); // Vertical block near the top left finder pattern
blockedModules.Add(new Rectangle(0, 7, 7, 1)); // Horizontal block near the top left finder pattern
- blockedModules.Add(new Rectangle(0, size - 8, 8, 1)); // Horizontal block near the bottom left finder pattern
- blockedModules.Add(new Rectangle(7, size - 7, 1, 7)); // Vertical block near the bottom left finder pattern
- blockedModules.Add(new Rectangle(size - 8, 0, 1, 8)); // Vertical block near the top right finder pattern
- blockedModules.Add(new Rectangle(size - 7, 7, 7, 1)); // Horizontal block near the top right finder pattern
+
+ if (version > 0) // Non-micro QR codes have 3 finder patterns
+ {
+ // Block areas around the bottom-left finder pattern
+ blockedModules.Add(new Rectangle(0, size - 8, 8, 1)); // Horizontal block near the bottom left finder pattern
+ blockedModules.Add(new Rectangle(7, size - 7, 1, 7)); // Vertical block near the bottom left finder pattern
+ // Block areas around the top-right finder pattern
+ blockedModules.Add(new Rectangle(size - 8, 0, 1, 8)); // Vertical block near the top right finder pattern
+ blockedModules.Add(new Rectangle(size - 7, 7, 7, 1)); // Horizontal block near the top right finder pattern
+ }
}
///
@@ -243,6 +281,13 @@ public static void ReserveSeperatorAreas(int size, BlockedModules blockedModules
/// A list of rectangles representing areas that must not be overwritten.
public static void ReserveVersionAreas(int size, int version, BlockedModules blockedModules)
{
+ if (version < 0) // Micro QR codes
+ {
+ blockedModules.Add(new Rectangle(0, 8, 9, 1));
+ blockedModules.Add(new Rectangle(8, 0, 1, 8));
+ return;
+ }
+
// Reserve areas near the timing patterns for version and format information.
blockedModules.Add(new Rectangle(8, 0, 1, 6)); // Near the top timing pattern
blockedModules.Add(new Rectangle(8, 7, 1, 1)); // Small square near the top left finder pattern
@@ -267,6 +312,9 @@ public static void ReserveVersionAreas(int size, int version, BlockedModules blo
/// A list of rectangles representing areas that must not be overwritten, updated to include the dark module.
public static void PlaceDarkModule(QRCodeData qrCode, int version, BlockedModules blockedModules)
{
+ // Micro QR codes do not have a dark module
+ if (version < 0)
+ return;
// Place the dark module, which is always required to be black.
qrCode.ModuleMatrix[4 * version + 9 + 4][8 + 4] = true;
// Block the dark module area to prevent overwriting during further QR code generation steps.
@@ -283,7 +331,8 @@ public static void PlaceFinderPatterns(QRCodeData qrCode, BlockedModules blocked
var size = qrCode.ModuleMatrix.Count - 8;
// Loop to place three finder patterns in the top-left, top-right, and bottom-left corners of the QR code.
- for (var i = 0; i < 3; i++)
+ var count = qrCode.Version < 0 ? 1 : 3; // Micro QR codes have only one finder pattern.
+ for (var i = 0; i < count; i++)
{
// Calculate the x and y starting positions for each finder pattern based on the index.
var locationX = i == 1 ? size - 7 : 0; // Place at top-right if i is 1, otherwise at left side (top or bottom).
@@ -355,21 +404,41 @@ public static void PlaceAlignmentPatterns(QRCodeData qrCode, List alignme
/// A list of rectangles representing areas that must not be overwritten. Updated with the areas occupied by timing patterns.
public static void PlaceTimingPatterns(QRCodeData qrCode, BlockedModules blockedModules)
{
- var size = qrCode.ModuleMatrix.Count - 8; // Get the size of the QR code matrix.
+ // Get the size of the QR code matrix excluding padding.
+ var size = qrCode.ModuleMatrix.Count - 8;
- // Place timing patterns starting from the 8th module to the size - 8 to avoid overlapping with finder patterns.
- for (var i = 8; i < size - 8; i++)
+ if (qrCode.Version > 0)
{
- if (i % 2 == 0) // Place a dark module every other module to create the alternating pattern.
+ // Place timing patterns starting from the 8th module to the size - 8 to avoid overlapping with finder patterns.
+ for (var i = 8; i < size - 8; i++)
{
- qrCode.ModuleMatrix[6 + 4][i + 4] = true; // Horizontal timing pattern
- qrCode.ModuleMatrix[i + 4][6 + 4] = true; // Vertical timing pattern
+ if (i % 2 == 0) // Place a dark module every other module to create the alternating pattern.
+ {
+ qrCode.ModuleMatrix[6 + 4][i + 4] = true; // Horizontal timing pattern
+ qrCode.ModuleMatrix[i + 4][6 + 4] = true; // Vertical timing pattern
+ }
}
+
+ // Add the areas occupied by the timing patterns to the list of blocked modules.
+ blockedModules.Add(new Rectangle(6, 8, 1, size - 16)); // Horizontal timing pattern area
+ blockedModules.Add(new Rectangle(8, 6, size - 16, 1)); // Vertical timing pattern area
}
+ else // Micro QR codes
+ {
+ // Place timing patterns starting from the 8th module to avoid overlapping with finder patterns.
+ for (var i = 8; i < size; i++)
+ {
+ if (i % 2 == 0) // Place a dark module every other module to create the alternating pattern.
+ {
+ qrCode.ModuleMatrix[4][i + 4] = true; // Horizontal timing pattern
+ qrCode.ModuleMatrix[i + 4][4] = true; // Vertical timing pattern
+ }
+ }
- // Add the areas occupied by the timing patterns to the list of blocked modules.
- blockedModules.Add(new Rectangle(6, 8, 1, size - 16)); // Horizontal timing pattern area
- blockedModules.Add(new Rectangle(8, 6, size - 16, 1)); // Vertical timing pattern area
+ // Add the areas occupied by the timing patterns to the list of blocked modules.
+ blockedModules.Add(new Rectangle(0, 8, 1, size - 8)); // Horizontal timing pattern area
+ blockedModules.Add(new Rectangle(8, 0, size - 8, 1)); // Vertical timing pattern area
+ }
}
}
}
diff --git a/QRCoder/QRCodeGenerator/Polynom.cs b/QRCoder/QRCodeGenerator/Polynom.cs
index 6fb3c302..6cb6115d 100644
--- a/QRCoder/QRCodeGenerator/Polynom.cs
+++ b/QRCoder/QRCodeGenerator/Polynom.cs
@@ -154,8 +154,9 @@ public override string ToString()
{
var sb = new StringBuilder();
- foreach (var polyItem in _polyItems)
+ for (int i = 0; i < Count; i++)
{
+ var polyItem = _polyItems[i];
sb.Append("a^" + polyItem.Coefficient + "*x^" + polyItem.Exponent + " + ");
}
diff --git a/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt b/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt
index 0a75bc99..20c384af 100644
--- a/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt
+++ b/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt
@@ -894,6 +894,7 @@ namespace QRCoder
public QRCoder.QRCodeData CreateQrCode(byte[] binaryData, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
public QRCoder.QRCodeData CreateQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
public void Dispose() { }
+ public static QRCoder.QRCodeData GenerateMicroQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel = -1, int requestedVersion = 0) { }
public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload) { }
public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
public static QRCoder.QRCodeData GenerateQrCode(byte[] binaryData, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
diff --git a/QRCoderApiTests/net60-windows/QRCoder.approved.txt b/QRCoderApiTests/net60-windows/QRCoder.approved.txt
index 99b5f961..5c4386b0 100644
--- a/QRCoderApiTests/net60-windows/QRCoder.approved.txt
+++ b/QRCoderApiTests/net60-windows/QRCoder.approved.txt
@@ -902,6 +902,7 @@ namespace QRCoder
public QRCoder.QRCodeData CreateQrCode(byte[] binaryData, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
public QRCoder.QRCodeData CreateQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
public void Dispose() { }
+ public static QRCoder.QRCodeData GenerateMicroQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel = -1, int requestedVersion = 0) { }
public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload) { }
public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
public static QRCoder.QRCodeData GenerateQrCode(byte[] binaryData, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
diff --git a/QRCoderApiTests/net60/QRCoder.approved.txt b/QRCoderApiTests/net60/QRCoder.approved.txt
index 6d269d08..8ee10f23 100644
--- a/QRCoderApiTests/net60/QRCoder.approved.txt
+++ b/QRCoderApiTests/net60/QRCoder.approved.txt
@@ -836,6 +836,7 @@ namespace QRCoder
public QRCoder.QRCodeData CreateQrCode(byte[] binaryData, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
public QRCoder.QRCodeData CreateQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
public void Dispose() { }
+ public static QRCoder.QRCodeData GenerateMicroQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel = -1, int requestedVersion = 0) { }
public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload) { }
public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
public static QRCoder.QRCodeData GenerateQrCode(byte[] binaryData, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
diff --git a/QRCoderApiTests/netstandard13/QRCoder.approved.txt b/QRCoderApiTests/netstandard13/QRCoder.approved.txt
index 343903e1..4b1075b0 100644
--- a/QRCoderApiTests/netstandard13/QRCoder.approved.txt
+++ b/QRCoderApiTests/netstandard13/QRCoder.approved.txt
@@ -800,6 +800,7 @@ namespace QRCoder
public QRCoder.QRCodeData CreateQrCode(byte[] binaryData, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
public QRCoder.QRCodeData CreateQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
public void Dispose() { }
+ public static QRCoder.QRCodeData GenerateMicroQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel = -1, int requestedVersion = 0) { }
public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload) { }
public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
public static QRCoder.QRCodeData GenerateQrCode(byte[] binaryData, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { }
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..2f5921cd 100644
--- a/QRCoderTests/QRGeneratorTests.cs
+++ b/QRCoderTests/QRGeneratorTests.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
@@ -21,7 +22,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 +31,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++)
@@ -40,6 +42,22 @@ public void validate_antilogtable()
}
#if !NETFRAMEWORK // [Theory] is not supported in xunit < 2.0.0
+ [Theory]
+ [InlineData("54321", ECCLevel.Default, "ZfnO93tpy9jjaACKXue2VsACXxY", 11)] //verified
+ [InlineData("00000000", ECCLevel.M, "lEBc3nKaK0UpMfenT5FTX02Zgfg", 13)] //verified
+ [InlineData("123456789", ECCLevel.L, "gCY4Cj1uLhI/0JjWG1F9kC4S1+I", 13)] //verified
+ [InlineData("abcd56789012345", ECCLevel.L, "kqcKfCCdu1VTjjtsmK4iBav9FTs", 17)] //verified
+ [InlineData("abc", ECCLevel.M, "334sxrtY5KkNZRGj1pBgb87/cFc", 15)] //reads fine, but unable to verify repeating pattern
+ public void validate_micro_qr_code(string input, ECCLevel eccLevel, string expectedHash, int expectedSize)
+ {
+ var qrData = QRCodeGenerator.GenerateMicroQrCode(input, eccLevel);
+ (qrData.ModuleMatrix.Count - 8).ShouldBe(expectedSize); // exclude padding
+ var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray());
+ var hash = System.Security.Cryptography.SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(result));
+ var hashString = Convert.ToBase64String(hash);
+ hashString.TrimEnd('=').ShouldBe(expectedHash);
+ }
+
[Theory]
// version 1 numeric
[InlineData("1", "KWw84nkWZLMh5LqAJ/4s/4mW/08", 21)]
@@ -369,7 +387,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}:";