extends RefCounted const BitStream := preload("res://addons/qr_code/bit_stream.gd") const ReedSolomon := preload("res://addons/qr_code/reed_solomon.gd") const ShiftJIS := preload("res://addons/qr_code/shift_jis.gd") ## Encoding Mode enum Mode { ## 0001 NUMERIC = 1, ## 0010 ALPHANUMERIC = 2, ## 0100 BYTE = 4, ## 1000 KANJI = 8 } ## Error Correction enum ErrorCorrection { LOW = 1, MEDIUM = 0, QUARTILE = 3, HIGH = 2 } ## Extended Channel Interpretation enum ECI { CODE_PAGE_437 = 2, ISO_8859_1 = 3, ISO_8859_2 = 4, ISO_8859_3 = 5, ISO_8859_4 = 6, ISO_8859_5 = 7, ISO_8859_6 = 8, ISO_8859_7 = 9, ISO_8859_8 = 10, ISO_8859_9 = 11, ISO_8859_10 = 12, ISO_8859_11 = 13, ISO_8859_12 = 14, ISO_8859_13 = 15, ISO_8859_14 = 16, ISO_8859_15 = 17, ISO_8859_16 = 18, SHIFT_JIS = 20, WINDOWS_1250 = 21, WINDOWS_1251 = 22, WINDOWS_1252 = 23, WINDOWS_1256 = 24, UTF_16 = 25, UTF_8 = 26, US_ASCII = 27, BIG_5 = 28, GB_18030 = 29, EUC_KR = 30 } const _DATA_CAPACITY: Array[Dictionary] = [ # 1 { ErrorCorrection.LOW: { Mode.NUMERIC: 41, Mode.ALPHANUMERIC: 25, Mode.BYTE: 17, Mode.KANJI: 10 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 34, Mode.ALPHANUMERIC: 20, Mode.BYTE: 14, Mode.KANJI: 8 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 27, Mode.ALPHANUMERIC: 16, Mode.BYTE: 11, Mode.KANJI: 7 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 17, Mode.ALPHANUMERIC: 10, Mode.BYTE: 7, Mode.KANJI: 4 }, }, # 2 { ErrorCorrection.LOW: { Mode.NUMERIC: 77, Mode.ALPHANUMERIC: 47, Mode.BYTE: 32, Mode.KANJI: 20 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 63, Mode.ALPHANUMERIC: 38, Mode.BYTE: 26, Mode.KANJI: 16 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 48, Mode.ALPHANUMERIC: 29, Mode.BYTE: 20, Mode.KANJI: 12 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 34, Mode.ALPHANUMERIC: 20, Mode.BYTE: 14, Mode.KANJI: 8 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 127, Mode.ALPHANUMERIC: 77, Mode.BYTE: 53, Mode.KANJI: 32 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 101, Mode.ALPHANUMERIC: 61, Mode.BYTE: 42, Mode.KANJI: 26 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 77, Mode.ALPHANUMERIC: 47, Mode.BYTE: 32, Mode.KANJI: 20 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 58, Mode.ALPHANUMERIC: 35, Mode.BYTE: 24, Mode.KANJI: 15 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 187, Mode.ALPHANUMERIC: 114, Mode.BYTE: 78, Mode.KANJI: 48 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 149, Mode.ALPHANUMERIC: 90, Mode.BYTE: 62, Mode.KANJI: 38 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 111, Mode.ALPHANUMERIC: 67, Mode.BYTE: 46, Mode.KANJI: 28 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 82, Mode.ALPHANUMERIC: 50, Mode.BYTE: 34, Mode.KANJI: 21 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 255, Mode.ALPHANUMERIC: 154, Mode.BYTE: 106, Mode.KANJI: 65 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 202, Mode.ALPHANUMERIC: 122, Mode.BYTE: 84, Mode.KANJI: 52 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 144, Mode.ALPHANUMERIC: 87, Mode.BYTE: 60, Mode.KANJI: 37 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 106, Mode.ALPHANUMERIC: 64, Mode.BYTE: 44, Mode.KANJI: 27 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 322, Mode.ALPHANUMERIC: 195, Mode.BYTE: 134, Mode.KANJI: 82 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 255, Mode.ALPHANUMERIC: 154, Mode.BYTE: 106, Mode.KANJI: 65 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 178, Mode.ALPHANUMERIC: 108, Mode.BYTE: 74, Mode.KANJI: 45 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 139, Mode.ALPHANUMERIC: 84, Mode.BYTE: 58, Mode.KANJI: 36 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 370, Mode.ALPHANUMERIC: 224, Mode.BYTE: 154, Mode.KANJI: 95 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 293, Mode.ALPHANUMERIC: 178, Mode.BYTE: 122, Mode.KANJI: 75 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 207, Mode.ALPHANUMERIC: 125, Mode.BYTE: 86, Mode.KANJI: 53 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 154, Mode.ALPHANUMERIC: 93, Mode.BYTE: 64, Mode.KANJI: 39 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 461, Mode.ALPHANUMERIC: 279, Mode.BYTE: 192, Mode.KANJI: 118 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 365, Mode.ALPHANUMERIC: 221, Mode.BYTE: 152, Mode.KANJI: 93 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 259, Mode.ALPHANUMERIC: 157, Mode.BYTE: 108, Mode.KANJI: 66 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 202, Mode.ALPHANUMERIC: 122, Mode.BYTE: 84, Mode.KANJI: 52 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 552, Mode.ALPHANUMERIC: 335, Mode.BYTE: 230, Mode.KANJI: 141 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 432, Mode.ALPHANUMERIC: 262, Mode.BYTE: 180, Mode.KANJI: 111 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 312, Mode.ALPHANUMERIC: 189, Mode.BYTE: 130, Mode.KANJI: 80 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 235, Mode.ALPHANUMERIC: 143, Mode.BYTE: 98, Mode.KANJI: 60 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 652, Mode.ALPHANUMERIC: 395, Mode.BYTE: 271, Mode.KANJI: 167 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 513, Mode.ALPHANUMERIC: 311, Mode.BYTE: 213, Mode.KANJI: 131 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 364, Mode.ALPHANUMERIC: 221, Mode.BYTE: 151, Mode.KANJI: 93 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 288, Mode.ALPHANUMERIC: 174, Mode.BYTE: 119, Mode.KANJI: 74 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 772, Mode.ALPHANUMERIC: 468, Mode.BYTE: 321, Mode.KANJI: 198 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 604, Mode.ALPHANUMERIC: 366, Mode.BYTE: 251, Mode.KANJI: 155 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 427, Mode.ALPHANUMERIC: 259, Mode.BYTE: 177, Mode.KANJI: 109 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 331, Mode.ALPHANUMERIC: 200, Mode.BYTE: 137, Mode.KANJI: 85 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 883, Mode.ALPHANUMERIC: 535, Mode.BYTE: 367, Mode.KANJI: 226 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 691, Mode.ALPHANUMERIC: 419, Mode.BYTE: 287, Mode.KANJI: 177 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 489, Mode.ALPHANUMERIC: 296, Mode.BYTE: 203, Mode.KANJI: 125 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 374, Mode.ALPHANUMERIC: 227, Mode.BYTE: 155, Mode.KANJI: 96 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 1022, Mode.ALPHANUMERIC: 619, Mode.BYTE: 425, Mode.KANJI: 262 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 796, Mode.ALPHANUMERIC: 483, Mode.BYTE: 331, Mode.KANJI: 204 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 580, Mode.ALPHANUMERIC: 352, Mode.BYTE: 241, Mode.KANJI: 149 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 427, Mode.ALPHANUMERIC: 259, Mode.BYTE: 177, Mode.KANJI: 109 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 1101, Mode.ALPHANUMERIC: 667, Mode.BYTE: 458, Mode.KANJI: 282 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 871, Mode.ALPHANUMERIC: 528, Mode.BYTE: 362, Mode.KANJI: 223 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 621, Mode.ALPHANUMERIC: 376, Mode.BYTE: 258, Mode.KANJI: 159 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 468, Mode.ALPHANUMERIC: 283, Mode.BYTE: 194, Mode.KANJI: 120 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 1250, Mode.ALPHANUMERIC: 758, Mode.BYTE: 520, Mode.KANJI: 320 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 991, Mode.ALPHANUMERIC: 600, Mode.BYTE: 412, Mode.KANJI: 254 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 703, Mode.ALPHANUMERIC: 426, Mode.BYTE: 292, Mode.KANJI: 180 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 530, Mode.ALPHANUMERIC: 321, Mode.BYTE: 220, Mode.KANJI: 136 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 1408, Mode.ALPHANUMERIC: 854, Mode.BYTE: 586, Mode.KANJI: 361 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 1082, Mode.ALPHANUMERIC: 656, Mode.BYTE: 450, Mode.KANJI: 277 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 775, Mode.ALPHANUMERIC: 470, Mode.BYTE: 322, Mode.KANJI: 198 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 602, Mode.ALPHANUMERIC: 365, Mode.BYTE: 250, Mode.KANJI: 154 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 1548, Mode.ALPHANUMERIC: 938, Mode.BYTE: 644, Mode.KANJI: 397 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 1212, Mode.ALPHANUMERIC: 734, Mode.BYTE: 504, Mode.KANJI: 310 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 876, Mode.ALPHANUMERIC: 531, Mode.BYTE: 364, Mode.KANJI: 224 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 674, Mode.ALPHANUMERIC: 408, Mode.BYTE: 280, Mode.KANJI: 173 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 1725, Mode.ALPHANUMERIC: 1046, Mode.BYTE: 718, Mode.KANJI: 442 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 1346, Mode.ALPHANUMERIC: 816, Mode.BYTE: 560, Mode.KANJI: 345 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 948, Mode.ALPHANUMERIC: 574, Mode.BYTE: 394, Mode.KANJI: 243 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 746, Mode.ALPHANUMERIC: 452, Mode.BYTE: 310, Mode.KANJI: 191 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 1903, Mode.ALPHANUMERIC: 1153, Mode.BYTE: 792, Mode.KANJI: 488 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 1500, Mode.ALPHANUMERIC: 909, Mode.BYTE: 624, Mode.KANJI: 384 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 1063, Mode.ALPHANUMERIC: 644, Mode.BYTE: 442, Mode.KANJI: 272 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 813, Mode.ALPHANUMERIC: 493, Mode.BYTE: 338, Mode.KANJI: 208 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 2061, Mode.ALPHANUMERIC: 1249, Mode.BYTE: 858, Mode.KANJI: 528 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 1600, Mode.ALPHANUMERIC: 970, Mode.BYTE: 666, Mode.KANJI: 410 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 1159, Mode.ALPHANUMERIC: 702, Mode.BYTE: 482, Mode.KANJI: 297 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 919, Mode.ALPHANUMERIC: 557, Mode.BYTE: 382, Mode.KANJI: 235 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 2232, Mode.ALPHANUMERIC: 1352, Mode.BYTE: 929, Mode.KANJI: 572 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 1708, Mode.ALPHANUMERIC: 1035, Mode.BYTE: 711, Mode.KANJI: 438 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 1224, Mode.ALPHANUMERIC: 742, Mode.BYTE: 509, Mode.KANJI: 314 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 969, Mode.ALPHANUMERIC: 587, Mode.BYTE: 403, Mode.KANJI: 248 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 2409, Mode.ALPHANUMERIC: 1460, Mode.BYTE: 1003, Mode.KANJI: 618 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 1872, Mode.ALPHANUMERIC: 1134, Mode.BYTE: 779, Mode.KANJI: 480 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 1358, Mode.ALPHANUMERIC: 823, Mode.BYTE: 565, Mode.KANJI: 348 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 1056, Mode.ALPHANUMERIC: 640, Mode.BYTE: 439, Mode.KANJI: 270 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 2620, Mode.ALPHANUMERIC: 1588, Mode.BYTE: 1091, Mode.KANJI: 672 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 2059, Mode.ALPHANUMERIC: 1248, Mode.BYTE: 857, Mode.KANJI: 528 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 1468, Mode.ALPHANUMERIC: 890, Mode.BYTE: 611, Mode.KANJI: 376 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 1108, Mode.ALPHANUMERIC: 672, Mode.BYTE: 461, Mode.KANJI: 284 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 2812, Mode.ALPHANUMERIC: 1704, Mode.BYTE: 1171, Mode.KANJI: 721 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 2188, Mode.ALPHANUMERIC: 1326, Mode.BYTE: 911, Mode.KANJI: 561 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 1588, Mode.ALPHANUMERIC: 963, Mode.BYTE: 661, Mode.KANJI: 407 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 1228, Mode.ALPHANUMERIC: 744, Mode.BYTE: 511, Mode.KANJI: 315 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 3057, Mode.ALPHANUMERIC: 1853, Mode.BYTE: 1273, Mode.KANJI: 784 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 2395, Mode.ALPHANUMERIC: 1451, Mode.BYTE: 997, Mode.KANJI: 614 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 1718, Mode.ALPHANUMERIC: 1041, Mode.BYTE: 715, Mode.KANJI: 440 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 1286, Mode.ALPHANUMERIC: 779, Mode.BYTE: 535, Mode.KANJI: 330 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 3283, Mode.ALPHANUMERIC: 1990, Mode.BYTE: 1367, Mode.KANJI: 842 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 2544, Mode.ALPHANUMERIC: 1542, Mode.BYTE: 1059, Mode.KANJI: 652 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 1804, Mode.ALPHANUMERIC: 1094, Mode.BYTE: 751, Mode.KANJI: 462 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 1425, Mode.ALPHANUMERIC: 864, Mode.BYTE: 593, Mode.KANJI: 365 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 3514, Mode.ALPHANUMERIC: 2132, Mode.BYTE: 1465, Mode.KANJI: 902 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 2701, Mode.ALPHANUMERIC: 1637, Mode.BYTE: 1125, Mode.KANJI: 692 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 1933, Mode.ALPHANUMERIC: 1172, Mode.BYTE: 805, Mode.KANJI: 496 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 1501, Mode.ALPHANUMERIC: 910, Mode.BYTE: 625, Mode.KANJI: 385 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 3669, Mode.ALPHANUMERIC: 2223, Mode.BYTE: 1528, Mode.KANJI: 940 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 2857, Mode.ALPHANUMERIC: 1732, Mode.BYTE: 1190, Mode.KANJI: 732 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 2085, Mode.ALPHANUMERIC: 1263, Mode.BYTE: 868, Mode.KANJI: 534 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 1581, Mode.ALPHANUMERIC: 958, Mode.BYTE: 658, Mode.KANJI: 405 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 3909, Mode.ALPHANUMERIC: 2369, Mode.BYTE: 1628, Mode.KANJI: 1002 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 3035, Mode.ALPHANUMERIC: 1839, Mode.BYTE: 1264, Mode.KANJI: 778 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 2181, Mode.ALPHANUMERIC: 1322, Mode.BYTE: 908, Mode.KANJI: 559 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 1677, Mode.ALPHANUMERIC: 1016, Mode.BYTE: 698, Mode.KANJI: 430 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 4158, Mode.ALPHANUMERIC: 2520, Mode.BYTE: 1732, Mode.KANJI: 1066 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 3289, Mode.ALPHANUMERIC: 1994, Mode.BYTE: 1370, Mode.KANJI: 843 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 2358, Mode.ALPHANUMERIC: 1429, Mode.BYTE: 982, Mode.KANJI: 604 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 1782, Mode.ALPHANUMERIC: 1080, Mode.BYTE: 742, Mode.KANJI: 457 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 4417, Mode.ALPHANUMERIC: 2677, Mode.BYTE: 1840, Mode.KANJI: 1132 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 3486, Mode.ALPHANUMERIC: 2113, Mode.BYTE: 1452, Mode.KANJI: 894 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 2473, Mode.ALPHANUMERIC: 1499, Mode.BYTE: 1030, Mode.KANJI: 634 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 1897, Mode.ALPHANUMERIC: 1150, Mode.BYTE: 790, Mode.KANJI: 486 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 4686, Mode.ALPHANUMERIC: 2840, Mode.BYTE: 1952, Mode.KANJI: 1201 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 3693, Mode.ALPHANUMERIC: 2238, Mode.BYTE: 1538, Mode.KANJI: 947 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 2670, Mode.ALPHANUMERIC: 1618, Mode.BYTE: 1112, Mode.KANJI: 684 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 2022, Mode.ALPHANUMERIC: 1226, Mode.BYTE: 842, Mode.KANJI: 518 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 4965, Mode.ALPHANUMERIC: 3009, Mode.BYTE: 2068, Mode.KANJI: 1273 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 3909, Mode.ALPHANUMERIC: 2369, Mode.BYTE: 1628, Mode.KANJI: 1002 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 2805, Mode.ALPHANUMERIC: 1700, Mode.BYTE: 1168, Mode.KANJI: 719 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 2157, Mode.ALPHANUMERIC: 1307, Mode.BYTE: 898, Mode.KANJI: 553 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 5253, Mode.ALPHANUMERIC: 3183, Mode.BYTE: 2188, Mode.KANJI: 1347 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 4134, Mode.ALPHANUMERIC: 2506, Mode.BYTE: 1722, Mode.KANJI: 1060 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 2949, Mode.ALPHANUMERIC: 1787, Mode.BYTE: 1228, Mode.KANJI: 756 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 2301, Mode.ALPHANUMERIC: 1394, Mode.BYTE: 958, Mode.KANJI: 590 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 5529, Mode.ALPHANUMERIC: 3351, Mode.BYTE: 2303, Mode.KANJI: 1417 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 4343, Mode.ALPHANUMERIC: 2632, Mode.BYTE: 1809, Mode.KANJI: 1113 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 3081, Mode.ALPHANUMERIC: 1867, Mode.BYTE: 1283, Mode.KANJI: 790 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 2361, Mode.ALPHANUMERIC: 1431, Mode.BYTE: 983, Mode.KANJI: 605 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 5836, Mode.ALPHANUMERIC: 3537, Mode.BYTE: 2431, Mode.KANJI: 1496 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 4588, Mode.ALPHANUMERIC: 2780, Mode.BYTE: 1911, Mode.KANJI: 1176 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 3244, Mode.ALPHANUMERIC: 1966, Mode.BYTE: 1351, Mode.KANJI: 832 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 2524, Mode.ALPHANUMERIC: 1530, Mode.BYTE: 1051, Mode.KANJI: 647 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 6153, Mode.ALPHANUMERIC: 3729, Mode.BYTE: 2563, Mode.KANJI: 1577 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 4775, Mode.ALPHANUMERIC: 2894, Mode.BYTE: 1989, Mode.KANJI: 1224 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 3417, Mode.ALPHANUMERIC: 2071, Mode.BYTE: 1423, Mode.KANJI: 876 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 2625, Mode.ALPHANUMERIC: 1591, Mode.BYTE: 1093, Mode.KANJI: 673 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 6479, Mode.ALPHANUMERIC: 3927, Mode.BYTE: 2699, Mode.KANJI: 1661 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 5039, Mode.ALPHANUMERIC: 3054, Mode.BYTE: 2099, Mode.KANJI: 1292 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 3599, Mode.ALPHANUMERIC: 2181, Mode.BYTE: 1499, Mode.KANJI: 923 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 2735, Mode.ALPHANUMERIC: 1658, Mode.BYTE: 1139, Mode.KANJI: 701 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 6743, Mode.ALPHANUMERIC: 4087, Mode.BYTE: 2809, Mode.KANJI: 1729 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 5313, Mode.ALPHANUMERIC: 3220, Mode.BYTE: 2213, Mode.KANJI: 1362 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 3791, Mode.ALPHANUMERIC: 2298, Mode.BYTE: 1579, Mode.KANJI: 972 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 2927, Mode.ALPHANUMERIC: 1774, Mode.BYTE: 1219, Mode.KANJI: 750 }, }, { ErrorCorrection.LOW: { Mode.NUMERIC: 7089, Mode.ALPHANUMERIC: 4296, Mode.BYTE: 2953, Mode.KANJI: 1817 }, ErrorCorrection.MEDIUM: { Mode.NUMERIC: 5596, Mode.ALPHANUMERIC: 3391, Mode.BYTE: 2331, Mode.KANJI: 1435 }, ErrorCorrection.QUARTILE: { Mode.NUMERIC: 3993, Mode.ALPHANUMERIC: 2420, Mode.BYTE: 1663, Mode.KANJI: 1024 }, ErrorCorrection.HIGH: { Mode.NUMERIC: 3057, Mode.ALPHANUMERIC: 1852, Mode.BYTE: 1273, Mode.KANJI: 784 } }, ] const _ALPHANUMERIC_CHARACTERS: Dictionary = { "0" : 0, "1" : 1, "2" : 2, "3" : 3, "4" : 4, "5" : 5, "6" : 6, "7" : 7, "8" : 8, "9" : 9, "A" : 10, "B" : 11, "C" : 12, "D" : 13, "E" : 14, "F" : 15, "G" : 16, "H" : 17, "I" : 18, "J" : 19, "K" : 20, "L" : 21, "M" : 22, "N" : 23, "O" : 24, "P" : 25, "Q" : 26, "R" : 27, "S" : 28, "T" : 29, "U" : 30, "V" : 31, "W" : 32, "X" : 33, "Y" : 34, "Z" : 35, " " : 36, "$" : 37, "%" : 38, "*" : 39, "+" : 40, "-" : 41, "." : 42, "/" : 43, ":" : 44, } ## https://www.thonky.com/qr-code-tutorial/error-correction-table ## [total data codewords, EC codewords per block, number of blocks in group 1, number of data codewords in group 1 blocks, number of blocks in group 2, number of data codewords in group 2 blocks] const _ERROR_CORRECTION: Array = [ # 1 { ErrorCorrection.LOW: [19, 7, 1, 19, 0, 0], ErrorCorrection.MEDIUM: [16, 10, 1, 16, 0, 0], ErrorCorrection.QUARTILE: [13, 13, 1, 13, 0, 0], ErrorCorrection.HIGH: [9, 17, 1, 9, 0, 0], }, # 2 { ErrorCorrection.LOW: [34, 10, 1, 34, 0, 0], ErrorCorrection.MEDIUM: [28, 16, 1, 28, 0, 0], ErrorCorrection.QUARTILE: [22, 22, 1, 22, 0, 0], ErrorCorrection.HIGH: [16, 28, 1, 16, 0, 0], }, # 3 { ErrorCorrection.LOW: [55, 15, 1, 55, 0, 0], ErrorCorrection.MEDIUM: [44, 26, 1, 44, 0, 0], ErrorCorrection.QUARTILE: [34, 18, 2, 17, 0, 0], ErrorCorrection.HIGH: [26, 22, 2, 13, 0, 0], }, # 4 { ErrorCorrection.LOW: [80, 20, 1, 80, 0, 0], ErrorCorrection.MEDIUM: [64, 18, 2, 32, 0, 0], ErrorCorrection.QUARTILE: [48, 26, 2, 24, 0, 0], ErrorCorrection.HIGH: [36, 16, 4, 9, 0, 0], }, # 5 { ErrorCorrection.LOW: [108, 26, 1, 108, 0, 0], ErrorCorrection.MEDIUM: [86, 24, 2, 43, 0, 0], ErrorCorrection.QUARTILE: [62, 18, 2, 15, 2, 16], ErrorCorrection.HIGH: [46, 22, 2, 11, 2, 12], }, # 6 { ErrorCorrection.LOW: [136, 18, 2, 68, 0, 0], ErrorCorrection.MEDIUM: [108, 16, 4, 27, 0, 0], ErrorCorrection.QUARTILE: [76, 24, 4, 19, 0, 0], ErrorCorrection.HIGH: [60, 28, 4, 15, 0, 0], }, # 7 { ErrorCorrection.LOW: [156, 20, 2, 78, 0, 0], ErrorCorrection.MEDIUM: [124, 18, 4, 31, 0, 0], ErrorCorrection.QUARTILE: [88, 18, 2, 14, 4, 15], ErrorCorrection.HIGH: [66, 26, 4, 13, 1, 14], }, # 8 { ErrorCorrection.LOW: [194, 24, 2, 97, 0, 0], ErrorCorrection.MEDIUM: [154, 22, 2, 38, 2, 39], ErrorCorrection.QUARTILE: [110, 22, 4, 18, 2, 19], ErrorCorrection.HIGH: [86, 26, 4, 14, 2, 15], }, # 9 { ErrorCorrection.LOW: [232, 30, 2, 116, 0, 0], ErrorCorrection.MEDIUM: [182, 22, 3, 36, 2, 37], ErrorCorrection.QUARTILE: [132, 20, 4, 16, 4, 17], ErrorCorrection.HIGH: [100, 24, 4, 12, 4, 13], }, # 10 { ErrorCorrection.LOW: [274, 18, 2, 68, 2, 69], ErrorCorrection.MEDIUM: [216, 26, 4, 43, 1, 44], ErrorCorrection.QUARTILE: [154, 24, 6, 19, 2, 20], ErrorCorrection.HIGH: [122, 28, 6, 15, 2, 16], }, # 11 { ErrorCorrection.LOW: [324, 20, 4, 81, 0, 0], ErrorCorrection.MEDIUM: [254, 30, 1, 50, 4, 51], ErrorCorrection.QUARTILE: [180, 28, 4, 22, 4, 23], ErrorCorrection.HIGH: [140, 24, 3, 12, 8, 13], }, # 12 { ErrorCorrection.LOW: [370, 24, 2, 92, 2, 93], ErrorCorrection.MEDIUM: [290, 22, 6, 36, 2, 37], ErrorCorrection.QUARTILE: [206, 26, 4, 20, 6, 21], ErrorCorrection.HIGH: [158, 28, 7, 14, 4, 15], }, # 13 { ErrorCorrection.LOW: [428, 26, 4, 107, 0, 0], ErrorCorrection.MEDIUM: [334, 22, 8, 37, 1, 38], ErrorCorrection.QUARTILE: [244, 24, 8, 20, 4, 21], ErrorCorrection.HIGH: [180, 22, 12, 11, 4, 12], }, # 14 { ErrorCorrection.LOW: [461, 30, 3, 115, 1, 116], ErrorCorrection.MEDIUM: [365, 24, 4, 40, 5, 41], ErrorCorrection.QUARTILE: [261, 20, 11, 16, 5, 17], ErrorCorrection.HIGH: [197, 24, 11, 12, 5, 13], }, # 15 { ErrorCorrection.LOW: [523, 22, 5, 87, 1, 88], ErrorCorrection.MEDIUM: [415, 24, 5, 41, 5, 42], ErrorCorrection.QUARTILE: [295, 30, 5, 24, 7, 25], ErrorCorrection.HIGH: [223, 24, 11, 12, 7, 13], }, # 16 { ErrorCorrection.LOW: [589, 24, 5, 98, 1, 99], ErrorCorrection.MEDIUM: [453, 28, 7, 45, 3, 46], ErrorCorrection.QUARTILE: [325, 24, 15, 19, 2, 20], ErrorCorrection.HIGH: [253, 30, 3, 15, 13, 16], }, # 17 { ErrorCorrection.LOW: [647, 28, 1, 107, 5, 108], ErrorCorrection.MEDIUM: [507, 28, 10, 46, 1, 47], ErrorCorrection.QUARTILE: [367, 28, 1, 22, 15, 23], ErrorCorrection.HIGH: [283, 28, 2, 14, 17, 15], }, # 18 { ErrorCorrection.LOW: [721, 30, 5, 120, 1, 121], ErrorCorrection.MEDIUM: [563, 26, 9, 43, 4, 44], ErrorCorrection.QUARTILE: [397, 28, 17, 22, 1, 23], ErrorCorrection.HIGH: [313, 28, 2, 14, 19, 15], }, # 19 { ErrorCorrection.LOW: [795, 28, 3, 113, 4, 114], ErrorCorrection.MEDIUM: [627, 26, 3, 44, 11, 45], ErrorCorrection.QUARTILE: [445, 26, 17, 21, 4, 22], ErrorCorrection.HIGH: [341, 26, 9, 13, 16, 14], }, # 20 { ErrorCorrection.LOW: [861, 28, 3, 107, 5, 108], ErrorCorrection.MEDIUM: [669, 26, 3, 41, 13, 42], ErrorCorrection.QUARTILE: [485, 30, 15, 24, 5, 25], ErrorCorrection.HIGH: [385, 28, 15, 15, 10, 16], }, # 21 { ErrorCorrection.LOW: [932, 28, 4, 116, 4, 117], ErrorCorrection.MEDIUM: [714, 26, 17, 42, 0, 0], ErrorCorrection.QUARTILE: [512, 28, 17, 22, 6, 23], ErrorCorrection.HIGH: [406, 30, 19, 16, 6, 17], }, # 22 { ErrorCorrection.LOW: [1006, 28, 2, 111, 7, 112], ErrorCorrection.MEDIUM: [782, 28, 17, 46, 0, 0], ErrorCorrection.QUARTILE: [568, 30, 7, 24, 16, 25], ErrorCorrection.HIGH: [442, 24, 34, 13, 0, 0], }, # 23 { ErrorCorrection.LOW: [1094, 30, 4, 121, 5, 122], ErrorCorrection.MEDIUM: [860, 28, 4, 47, 14, 48], ErrorCorrection.QUARTILE: [614, 30, 11, 24, 14, 25], ErrorCorrection.HIGH: [464, 30, 16, 15, 14, 16], }, # 24 { ErrorCorrection.LOW: [1174, 30, 6, 117, 4, 118], ErrorCorrection.MEDIUM: [914, 28, 6, 45, 14, 46], ErrorCorrection.QUARTILE: [664, 30, 11, 24, 16, 25], ErrorCorrection.HIGH: [514, 30, 30, 16, 2, 17], }, # 25 { ErrorCorrection.LOW: [1276, 26, 8, 106, 4, 107], ErrorCorrection.MEDIUM: [1000, 28, 8, 47, 13, 48], ErrorCorrection.QUARTILE: [718, 30, 7, 24, 22, 25], ErrorCorrection.HIGH: [538, 30, 22, 15, 13, 16], }, # 26 { ErrorCorrection.LOW: [1370, 28, 10, 114, 2, 115], ErrorCorrection.MEDIUM: [1062, 28, 19, 46, 4, 47], ErrorCorrection.QUARTILE: [754, 28, 28, 22, 6, 23], ErrorCorrection.HIGH: [596, 30, 33, 16, 4, 17], }, # 27 { ErrorCorrection.LOW: [1468, 30, 8, 122, 4, 123], ErrorCorrection.MEDIUM: [1128, 28, 22, 45, 3, 46], ErrorCorrection.QUARTILE: [808, 30, 8, 23, 26, 24], ErrorCorrection.HIGH: [628, 30, 12, 15, 28, 16], }, # 28 { ErrorCorrection.LOW: [1531, 30, 3, 117, 10, 118], ErrorCorrection.MEDIUM: [1193, 28, 3, 45, 23, 46], ErrorCorrection.QUARTILE: [871, 30, 4, 24, 31, 25], ErrorCorrection.HIGH: [661, 30, 11, 15, 31, 16], }, # 29 { ErrorCorrection.LOW: [1631, 30, 7, 116, 7, 117], ErrorCorrection.MEDIUM: [1267, 28, 21, 45, 7, 46], ErrorCorrection.QUARTILE: [911, 30, 1, 23, 37, 24], ErrorCorrection.HIGH: [701, 30, 19, 15, 26, 16], }, # 30 { ErrorCorrection.LOW: [1735, 30, 5, 115, 10, 116], ErrorCorrection.MEDIUM: [1373, 28, 19, 47, 10, 48], ErrorCorrection.QUARTILE: [985, 30, 15, 24, 25, 25], ErrorCorrection.HIGH: [745, 30, 23, 15, 25, 16], }, # 31 { ErrorCorrection.LOW: [1843, 30, 13, 115, 3, 116], ErrorCorrection.MEDIUM: [1455, 28, 2, 46, 29, 47], ErrorCorrection.QUARTILE: [1033, 30, 42, 24, 1, 25], ErrorCorrection.HIGH: [793, 30, 23, 15, 28, 16], }, # 32 { ErrorCorrection.LOW: [1955, 30, 17, 115, 0, 0], ErrorCorrection.MEDIUM: [1541, 28, 10, 46, 23, 47], ErrorCorrection.QUARTILE: [1115, 30, 10, 24, 35, 25], ErrorCorrection.HIGH: [845, 30, 19, 15, 35, 16], }, # 33 { ErrorCorrection.LOW: [2071, 30, 17, 115, 1, 116], ErrorCorrection.MEDIUM: [1631, 28, 14, 46, 21, 47], ErrorCorrection.QUARTILE: [1171, 30, 29, 24, 19, 25], ErrorCorrection.HIGH: [901, 30, 11, 15, 46, 16], }, # 34 { ErrorCorrection.LOW: [2191, 30, 13, 115, 6, 116], ErrorCorrection.MEDIUM: [1725, 28, 14, 46, 23, 47], ErrorCorrection.QUARTILE: [1231, 30, 44, 24, 7, 25], ErrorCorrection.HIGH: [961, 30, 59, 16, 1, 17], }, # 35 { ErrorCorrection.LOW: [2306, 30, 12, 121, 7, 122], ErrorCorrection.MEDIUM: [1812, 28, 12, 47, 26, 48], ErrorCorrection.QUARTILE: [1286, 30, 39, 24, 14, 25], ErrorCorrection.HIGH: [986, 30, 22, 15, 41, 16], }, # 36 { ErrorCorrection.LOW: [2434, 30, 6, 121, 14, 122], ErrorCorrection.MEDIUM: [1914, 28, 6, 47, 34, 48], ErrorCorrection.QUARTILE: [1354, 30, 46, 24, 10, 25], ErrorCorrection.HIGH: [1054, 30, 2, 15, 64, 16], }, # 37 { ErrorCorrection.LOW: [2566, 30, 17, 122, 4, 123], ErrorCorrection.MEDIUM: [1992, 28, 29, 46, 14, 47], ErrorCorrection.QUARTILE: [1426, 30, 49, 24, 10, 25], ErrorCorrection.HIGH: [1096, 30, 24, 15, 46, 16], }, # 38 { ErrorCorrection.LOW: [2702, 30, 4, 122, 18, 123], ErrorCorrection.MEDIUM: [2102, 28, 13, 46, 32, 47], ErrorCorrection.QUARTILE: [1502, 30, 48, 24, 14, 25], ErrorCorrection.HIGH: [1142, 30, 42, 15, 32, 16], }, # 39 { ErrorCorrection.LOW: [2812, 30, 20, 117, 4, 118], ErrorCorrection.MEDIUM: [2216, 28, 40, 47, 7, 48], ErrorCorrection.QUARTILE: [1582, 30, 43, 24, 22, 25], ErrorCorrection.HIGH: [1222, 30, 10, 15, 67, 16], }, # 40 { ErrorCorrection.LOW: [2956, 30, 19, 118, 6, 119], ErrorCorrection.MEDIUM: [2334, 28, 18, 47, 31, 48], ErrorCorrection.QUARTILE: [1666, 30, 34, 24, 34, 25], ErrorCorrection.HIGH: [1276, 30, 20, 15, 61, 16], }, ] ## sorted by version const _ALIGNMENT_PATTERN_POSITIONS: Array = [ [], [6, 18], [6, 22], [6, 26], [6, 30], [6, 34], [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54], [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74], [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90], [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106], [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122], [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134], [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146], [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], ] ## remainder bits after structured data bits const _REMAINDER_BITS: Array = [ 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 ] static var _number_rx: RegEx = RegEx.create_from_string("[^\\d]*") static var _alphanumeric_rx: RegEx = RegEx.create_from_string("[^0-9A-Z $%*+\\-.\\/:]*") ## cached qr data var _cached_qr: PackedByteArray = [] ## this can be either a String or PackedByteArray, based on the current encoding mode var _input_data: Variant = "": get = get_input_data ## Encoding Mode var mode: Mode = Mode.NUMERIC: set = set_mode ## Error Correction var error_correction: ErrorCorrection = ErrorCorrection.LOW: set = set_error_correction ## Set to true if you want to specify an ECI value. var use_eci: bool = false: set = set_use_eci ## Extended Channel Interpretation (ECI) Value. Is only used if `use_eci` is true. var eci_value: int = ECI.ISO_8859_1: set = set_eci_value ## Use automatically the smallest version var auto_version: bool = true: set = set_auto_version ## Version ## Will be changed on encoding to the used version if auto_version is true var version: int = 1: set = set_version var auto_mask_pattern: bool = true: set = set_auto_mask_pattern ## Will be changed on encoding to the used mask pattern if auto_mask_pattern is true var mask_pattern: int = 0: set = set_mask_pattern func set_auto_version(new_auto_version: bool) -> void: if new_auto_version == auto_version: return auto_version = new_auto_version self._clear_cache() func set_version(new_version: int) -> void: if new_version == version: return version = clampi(new_version, 1, 40) self._clear_cache() func set_error_correction(new_error_correction: ErrorCorrection) -> void: if new_error_correction == error_correction: return error_correction = new_error_correction self._clear_cache() func set_mode(new_mode: Mode) -> void: if new_mode == mode: return mode = new_mode match mode: Mode.NUMERIC, Mode.ALPHANUMERIC, Mode.KANJI: self._input_data = "" Mode.BYTE: self._input_data = PackedByteArray() self._clear_cache() func set_use_eci(new_use_eci: bool) -> void: if new_use_eci == use_eci: return use_eci = new_use_eci self._clear_cache() func set_eci_value(new_eci_value: int) -> void: if new_eci_value == eci_value: return eci_value = new_eci_value self._clear_cache() func set_auto_mask_pattern(new_auto_mask_pattern: bool) -> void: if new_auto_mask_pattern == auto_mask_pattern: return auto_mask_pattern = new_auto_mask_pattern self._clear_cache() func set_mask_pattern(new_mask_pattern: int) -> void: if new_mask_pattern == mask_pattern: return mask_pattern = clampi(new_mask_pattern, 0, 7) self._clear_cache() ## return the data which was put in func get_input_data() -> Variant: return _input_data ## get module count of one axis func get_module_count() -> int: return _calc_module_count(self.version) ## returns ONE minimum version which fits the data ## the returned version is just an approach ## returns -1 if too huge func calc_min_version() -> int: var input_size: int = self._get_input_data_size() for idx: int in range(_DATA_CAPACITY.size()): var cap: int = _DATA_CAPACITY[idx][self.error_correction][self.mode] if self.eci_value != ECI.ISO_8859_1: # subtract roughly eci header size cap -= 4 if input_size <= cap: return idx + 1 return -1 static func _get_alphanumeric_number(char: String) -> int: return _ALPHANUMERIC_CHARACTERS[char] # functions are adapted to our starting point 0, 0 static func _mask_pattern_fns() -> Array[Callable]: return [ func (pos: Vector2i) -> bool: return (pos.x + pos.y) % 2 == 0, func (pos: Vector2i) -> bool: return pos.y % 2 == 0, func (pos: Vector2i) -> bool: return pos.x % 3 == 0, func (pos: Vector2i) -> bool: return (pos.x + pos.y) % 3 == 0, func (pos: Vector2i) -> bool: return (pos.x / 3 + pos.y / 2) % 2 == 0, func (pos: Vector2i) -> bool: return (pos.x * pos.y % 2) + (pos.x * pos.y) % 3 == 0, func (pos: Vector2i) -> bool: return ((pos.x * pos.y % 2) + (pos.x * pos.y) % 3) % 2 == 0, func (pos: Vector2i) -> bool: return (((pos.x + pos.y) % 2) + (pos.x * pos.y) % 3) % 2 == 0, ] # helper function check if a bit is set static func _get_state(value: int, idx: int) -> bool: return (value & (1 << idx)) func _get_data_codeword_count() -> int: return _ERROR_CORRECTION[self.version - 1][self.error_correction][0] func _get_ec_codeword_count() -> int: return _ERROR_CORRECTION[self.version - 1][self.error_correction][1] func _get_ec_block_count(group: int) -> int: return _ERROR_CORRECTION[self.version - 1][self.error_correction][2 + (group - 1) * 2] func _get_ec_block_codeword_count(group: int) -> int: return _ERROR_CORRECTION[self.version - 1][self.error_correction][3 + (group - 1) * 2] static func _calc_module_count(version: int) -> int: return 21 + 4 * (version - 1) func _get_allignment_pattern_positions() -> Array[Vector2i]: var module_count: int = self.get_module_count() var positions: Array[Vector2i] = [] for row: int in _ALIGNMENT_PATTERN_POSITIONS[self.version - 1]: for col: int in _ALIGNMENT_PATTERN_POSITIONS[self.version - 1]: # do not overlap finder positions if row - 2 < 8 && col - 2 < 8 || \ row > module_count - 8 && col - 2 < 8 || \ row - 2 < 8 && col > module_count - 8: continue positions.append(Vector2i(row, col)) return positions static func _get_remainder_bits(version: int) -> int: return _REMAINDER_BITS[version - 1] func _get_input_data_size() -> int: match typeof(self._input_data): TYPE_STRING: return self._input_data.length() TYPE_PACKED_BYTE_ARRAY: return self._input_data.size() return 0 func _get_char_count_size() -> int: if self.version < 10: match self.mode: Mode.NUMERIC: return 10 Mode.ALPHANUMERIC: return 9 Mode.BYTE: return 8 Mode.KANJI: return 8 elif self.version < 27: match self.mode: Mode.NUMERIC: return 12 Mode.ALPHANUMERIC: return 11 Mode.BYTE: return 16 Mode.KANJI: return 10 else: match self.mode: Mode.NUMERIC: return 14 Mode.ALPHANUMERIC: return 13 Mode.BYTE: return 16 Mode.KANJI: return 12 return 0 # TODO: TEST IF STATIC VAR WORKS static func _static_init() -> void: # TODO: static init is not called in editor if not @tool if _number_rx == null: _number_rx = RegEx.create_from_string("[^\\d]*") # TODO: static init is not called in editor if not @tool if _alphanumeric_rx == null: _alphanumeric_rx = RegEx.create_from_string("[^0-9A-Z $%*+\\-.\\/:]*") func _init(error_correction_: ErrorCorrection = ErrorCorrection.LOW) -> void: self.error_correction = error_correction_ # TODO: static init is not called in editor if not @tool if Engine.is_editor_hint(): _static_init() ## generate an QR code image func generate_image(module_px_size: int = 1, light_module_color: Color = Color.WHITE, dark_module_color: Color = Color.BLACK, quiet_zone_size: int = 4) -> Image: module_px_size = maxi(1, module_px_size) quiet_zone_size = maxi(0, quiet_zone_size) var qr_code: PackedByteArray = self.encode() var module_count: int = self.get_module_count() var image_size: int = (module_count + 2 * quiet_zone_size) * module_px_size var image: Image = Image.create(image_size, image_size, false, Image.FORMAT_RGB8) image.fill(light_module_color) for y: int in range(module_count): for x: int in range(module_count): var color: Color = Color.PINK match qr_code[x + y * module_count]: 0: color = light_module_color 1: color = dark_module_color for offset_x: int in range(module_px_size): for offset_y: int in range(module_px_size): image.set_pixel((x + quiet_zone_size) * module_px_size + offset_x, (y + quiet_zone_size) * module_px_size + offset_y, color) return image func put_numeric(number: String) -> void: if self.mode != Mode.NUMERIC || number != self._input_data: self._clear_cache() self.mode = Mode.NUMERIC self._input_data = _number_rx.sub(number, "", true) func put_alphanumeric(text: String) -> void: if self.mode != Mode.ALPHANUMERIC || text != self._input_data: self._clear_cache() self.mode = Mode.ALPHANUMERIC self._input_data = _alphanumeric_rx.sub(text, "", true) func put_byte(data: PackedByteArray) -> void: if self.mode != Mode.BYTE || data != self._input_data: self._clear_cache() self.mode = Mode.BYTE self._input_data = data func put_kanji(data: String) -> void: if self.mode != Mode.KANJI || data != self._input_data: self._clear_cache() self.mode = Mode.KANJI self._input_data = ShiftJIS.get_string_from_shift_jis_2004(ShiftJIS.to_shift_jis_2004_buffer(data)) ## returns row by row ## to get row size use get_module_count func encode() -> PackedByteArray: if !self._cached_qr.is_empty(): return self._cached_qr.duplicate() if self.auto_version: self.version = self.calc_min_version() var data_stream: BitStream = self._encode_data() var err_correction: Array = self._error_correction(data_stream) var structured_data: BitStream = self._structure_data(data_stream, err_correction) var qr_data: PackedByteArray = self._place_modules(structured_data) qr_data = self._mask_qr(qr_data) self._cached_qr = qr_data.duplicate() return qr_data func _encode_data() -> BitStream: var stream: BitStream = BitStream.new() # add ECI header if self.use_eci: stream.append(0b0111, 4) if self.eci_value <= 127: stream.append(0, 1) stream.append(self.eci_value, 7) elif self.eci_value <= 16383: stream.append(0b10, 2) stream.append(self.eci_value, 14) else: stream.append(0b110, 3) stream.append(self.eci_value, 21) # add mode stream.append(int(self.mode), 4) # add character count indicator stream.append(self._get_input_data_size(), self._get_char_count_size()) # add encoded data match self.mode: Mode.NUMERIC: self._encode_numeric(stream) Mode.ALPHANUMERIC: self._encode_alphanumeric(stream) Mode.BYTE: self._encode_byte(stream) Mode.KANJI: self._encode_kanji(stream) # add terminator var required_bytes: int = self._get_data_codeword_count() var terminator_size: int = mini(8 * required_bytes - stream.size(), 4) stream.append(0, terminator_size) # add bits to multiple of 8 stream.append(0, (8 - stream.size() % 8) % 8) # pad bytes to capacity var missing_bytes = required_bytes - stream.size() / 8 for idx: int in range(missing_bytes): if idx % 2 == 0: stream.append(236, 8) else: stream.append(17, 8) return stream func _clear_cache() -> void: self._cached_qr.clear() func _encode_numeric(stream: BitStream) -> void: assert(typeof(self._input_data) == TYPE_STRING) const GROUP_SIZE: int = 3 for idx: int in range(ceili(self._input_data.length() / float(GROUP_SIZE))): var chars: String = self._input_data.substr(idx * GROUP_SIZE, GROUP_SIZE) var number: int = chars.to_int() var bit_count: int = 0 match chars.length(): 3: bit_count = 10 2: bit_count = 7 1: bit_count = 4 stream.append(number, bit_count) func _encode_alphanumeric(stream: BitStream) -> void: assert(typeof(self._input_data) == TYPE_STRING) const GROUP_SIZE: int = 2 for idx: int in range(ceili(self._input_data.length() / float(GROUP_SIZE))): var chars: String = self._input_data.substr(idx * GROUP_SIZE, GROUP_SIZE) var number: int = _get_alphanumeric_number(chars[0]) if chars.length() == 2: number = 45 * number + _get_alphanumeric_number(chars[1]) stream.append(number, 11) else: stream.append(number, 6) func _encode_byte(stream: BitStream) -> void: assert(typeof(self._input_data) == TYPE_PACKED_BYTE_ARRAY) for val: int in self._input_data: stream.append(val, 8) func _encode_kanji(stream: BitStream) -> void: assert(typeof(self._input_data) == TYPE_STRING) var jis_bytes: PackedByteArray = ShiftJIS.to_shift_jis_2004_buffer(self._input_data) for idx: int in range(jis_bytes.size() / 2): var value = jis_bytes.decode_u16(idx * 2) if value >= 0x8140 && value <= 0x9FFC: value = value - 0x8140 elif value >= 0xE040 && value <= 0xEBBF: value = value - 0xC140 value = ((value & 0xFF00) >> 8) * 0xC0 + (value & 0x00FF) stream.append(value, 13) # returns an array of PackedByteArray's, structured as Group, Block [G1B1, G1B2, G1B3, G2B1, G2B2, ...] func _error_correction(stream: BitStream) -> Array[PackedByteArray]: var data: PackedByteArray = stream.to_byte_array() var ec_words: int = self._get_ec_codeword_count() var group_blocks: PackedByteArray = [ self._get_ec_block_count(1), self._get_ec_block_count(2), ] var group_codewords: PackedByteArray = [ self._get_ec_block_codeword_count(1), self._get_ec_block_codeword_count(2), ] var groups: int = 1 if group_blocks[1] > 0: groups += 1 var err_corr: Array[PackedByteArray] = [] for group_idx: int in range(groups): var block_size: int = group_codewords[group_idx] for block_idx: int in range(group_blocks[group_idx]): var start_idx: int = 0 # add offset to current group for group_off: int in range(group_idx): start_idx += group_blocks[group_off] * group_codewords[group_off] start_idx = start_idx + block_idx * block_size var cur_data: PackedByteArray = data.slice(start_idx, start_idx + block_size) err_corr.append(ReedSolomon.encode(cur_data, ec_words)) return err_corr func _structure_data(data_stream: BitStream, err_correction: Array[PackedByteArray]) -> BitStream: if err_correction.size() == 1: var res: BitStream = data_stream.duplicate() res.append_byte_array(err_correction[0]) # append remainder bits res.append(0, _get_remainder_bits(self.version)) return res var res: BitStream = BitStream.new() var data_arr: PackedByteArray = data_stream.to_byte_array() var group_blocks: PackedByteArray = [ self._get_ec_block_count(1), self._get_ec_block_count(2), ] var group_codewords: Array[int] = [ self._get_ec_block_codeword_count(1), self._get_ec_block_codeword_count(2), ] var groups: int = 1 if group_blocks[1] > 0: groups += 1 # interleave data code words var max_code_words: int = group_codewords.max() for codeword_idx: int in range(max_code_words): for group_idx: int in range(groups): # if current group/block has not this much codewords skip if codeword_idx >= group_codewords[group_idx]: continue var group_offset: int = 0 for group_off: int in range(group_idx): group_offset += group_blocks[group_off] * group_codewords[group_off] for block_idx: int in range(group_blocks[group_idx]): var idx: int = group_offset + codeword_idx + block_idx * group_codewords[group_idx] res.append(data_arr[idx], 8) # interleave error code words for word_idx: int in range(self._get_ec_codeword_count()): for block: int in range(err_correction.size()): res.append(err_correction[block][word_idx], 8) # append remainder bits res.append(0, _get_remainder_bits(self.version)) return res # pos is upper left black corner # 7 x 7 size static func _place_finder(data: PackedByteArray, module_count: int, pos: Vector2i) -> void: for row: int in range(7): for col: int in range(7): data[(pos.x + row) + (pos.y + col) * module_count] = 1 for idx: int in range(5): data[(pos.x + 1 + idx) + (pos.y + 1) * module_count] = 0 data[(pos.x + 1 + idx) + (pos.y + 5) * module_count] = 0 for idx: int in range(3): data[(pos.x + 1) + (pos.y + 2 + idx) * module_count] = 0 data[(pos.x + 5) + (pos.y + 2 + idx) * module_count] = 0 # pos is center # 5 x 5 size static func _place_align_pattern(data: PackedByteArray, module_count: int, pos: Vector2i) -> void: for row: int in range(5): for col: int in range(5): data[(pos.x - 2 + row) + (pos.y - 2 + col) * module_count] = 1 for idx: int in range(3): data[(pos.x - 1 + idx) + (pos.y - 1) * module_count] = 0 data[(pos.x - 1 + idx) + (pos.y + 1) * module_count] = 0 data[(pos.x - 1) + (pos.y) * module_count] = 0 data[(pos.x + 1) + (pos.y) * module_count] = 0 static func _place_separators(data: PackedByteArray, module_count: int) -> void: for idx: int in range(8): # upper left data[idx + 7 * module_count] = 0 data[7 + idx * module_count] = 0 # lower left data[idx + (module_count - 8) * module_count] = 0 data[(module_count - 8) + idx * module_count] = 0 # upper right data[(module_count - idx - 1) + 7 * module_count] = 0 data[7 + (module_count - idx - 1) * module_count] = 0 static func _place_timing_patterns(data: PackedByteArray, module_count: int) -> void: for idx: int in range(module_count - 6 * 2): data[6 + idx + 6 * module_count] = (idx + 1) % 2 data[6 + (6 + idx) * module_count] = (idx + 1) % 2 static func _is_data_module(module_count: int, alignment_pattern_pos: Array[Vector2i], pos: Vector2i) -> bool: # finder with separation and format information area: upper left finder, upper right finder, lower left finder # dark module is also included if (pos.x <= 8 && pos.y <= 8) || (pos.x >= (module_count - 8) && pos.y <= 8) || (pos.x <= 8 && pos.y >= (module_count - 8)): return false # timing pattern if pos.x == 6 || pos.y == 6: return false # version information area # for version >= 7, upper and lower # this check, will also success if it is in a finder area if module_count >= 45 && ((pos.x >= module_count - 11 && pos.y <= 5) || (pos.x <= 5 && pos.y >= module_count - 11)): return false # check if in alignment pattern for align_pos: Vector2i in alignment_pattern_pos: if pos.x >= align_pos.x - 2 && pos.x <= align_pos.x + 2 && pos.y >= align_pos.y - 2 && pos.y <= align_pos.y + 2: return false return true static func _place_data(data: PackedByteArray, module_count: int, alignment_pattern_pos: Array[Vector2i], structured_data: BitStream) -> void: var data_idx: int = 0 # base column where to go up or down var base_col: int = module_count - 1 var upwards: bool = true while base_col > 0: # skip vertical timing pattern if base_col == 6: base_col -= 1 for row: int in range(module_count): if upwards: row = module_count - 1 - row for offset: int in range(2): var pos: Vector2i = Vector2i(base_col - offset, row) if _is_data_module(module_count, alignment_pattern_pos, pos): data[pos.x + pos.y * module_count] = int(structured_data.get_bit(data_idx)) data_idx += 1 base_col -= 2 upwards = !upwards # all data modules placed assert(data_idx == structured_data.size(), "failed to place all data (%d of %d)" % [data_idx, structured_data.size()]) static func _calc_mask_rating(data: PackedByteArray, module_count: int) -> int: var rating: int = 0 # condition 1 # horizontal for y: int in range(module_count): var count: int = 0 var block_value: int = 0 for x: int in range(module_count): var cur_value: int = data[x + y * module_count] if cur_value == block_value: count += 1 else: if count >= 5: rating += count - 2 count = 1 block_value = cur_value if count >= 5: rating += count - 2 # vertical for x: int in range(module_count): var count: int = 0 var block_value: int = 0 for y: int in range(module_count): var cur_value: int = data[x + y * module_count] if cur_value == block_value: count += 1 else: if count >= 5: rating += count - 2 count = 1 block_value = cur_value if count >= 5: rating += count - 2 # condition 2 for x: int in range(module_count - 1): for y: int in range(module_count - 1): var val: int = data[x + y * module_count] + data[x + 1 + y * module_count] + data[x + (y + 1) * module_count] + data[x + 1 + (y + 1) * module_count] if val == 0 || val == 4: rating += 3 # condition 3 for y: int in range(module_count): for x: int in range(module_count - 6): var start_idx: int = x + y * module_count if (!data[start_idx] && data[start_idx + 1] && !data[start_idx + 2] && !data[start_idx + 3] && !data[start_idx + 4] && data[start_idx + 5] && !data[start_idx + 6]): if x >= 4 && data[start_idx - 1] && data[start_idx - 2] && data[start_idx - 3] && data[start_idx - 4]: rating += 40 if x <= (module_count - 10) && data[start_idx + 7] && data[start_idx + 8] && data[start_idx + 9] && data[start_idx + 10]: rating += 40 for x: int in range(module_count): for y: int in range(module_count - 6): if (!data[x + y * module_count] && data[x + (y + 1) * module_count] && !data[x + (y + 2) * module_count] && !data[x + (y + 3) * module_count] && !data[x + (y + 4) * module_count] && data[x + (y + 5) * module_count] && !data[x + (y + 6) * module_count]): if y >= 4 && data[x + (y - 1) * module_count] && data[x + (y - 2) * module_count] && data[x + (y - 3) * module_count] && data[x + (y - 4) * module_count]: rating += 40 if y <= (module_count - 11) && data[x + (y + 7) * module_count] && data[x + (y + 8) * module_count] && data[x + (y + 9) * module_count] && data[x + (y + 10) * module_count]: rating += 40 # condition 4 var dark_mods: int = data.count(0) var ratio: float = dark_mods / float(module_count * module_count) var percent: int = int((ratio * 100) - 50) rating += absi(percent) / 5 * 10 return rating static func _place_format(qr_data: PackedByteArray, module_count: int, error_corr: ErrorCorrection, mask_pattern_val: int) -> void: var base_code: int = (int(error_corr) << 3) | mask_pattern_val var code: int = base_code for _idx: int in range(10): code = (code << 1) ^ ((code >> 9) * 0x537) code = (base_code << 10 | code) ^ 0x5412 # upper left finder for idx: int in range(8): # skip timing pattern var pos: int = idx if idx > 5: pos += 1 # horizontal qr_data[pos + 8 * module_count] = int(_get_state(code, 14 - idx)) # vertical qr_data[8 + pos * module_count] = int(_get_state(code, idx)) # lower left finder for idx: int in range(7): qr_data[8 + (module_count - 1 - idx) * module_count] = int(_get_state(code, 14 - idx)) # upper right finder for idx: int in range(8): qr_data[(module_count - 1 - idx) + 8 * module_count] = int(_get_state(code, idx)) static func _place_version(qr_data: PackedByteArray, version: int) -> void: if version < 7: return var code: int = version for _idx: int in range(12): code = (code << 1) ^ ((code >> 11) * 0x1F25) code = version << 12 | code var module_count: int = _calc_module_count(version) for idx: int in range(18): var x: int = idx / 3 var y: int = module_count - 11 + idx % 3 qr_data[x + y * module_count] = int(_get_state(code, idx)) qr_data[y + x * module_count] = int(_get_state(code, idx)) # returns qr module data, ordered by rows # (col/x, row/y) | index # (0, 0) (1, 0) (2, 0) | 0, 1, 2 # (0, 1) (1, 1) (2, 1) | 3, 4, 5 # (0, 2) (1, 2) (2, 2) | 6, 7, 8 func _place_modules(structured_data: BitStream) -> PackedByteArray: var qr_data: PackedByteArray = PackedByteArray() var module_count: int = self.get_module_count() qr_data.resize(module_count * module_count) # place upper left finder _place_finder(qr_data, module_count, Vector2i(0, 0)) # place lower left finder _place_finder(qr_data, module_count, Vector2i(0, module_count - 7)) # place upper right finder _place_finder(qr_data, module_count, Vector2i(module_count - 7, 0)) _place_separators(qr_data, module_count) var alignment_pattern_pos: Array[Vector2i] = self._get_allignment_pattern_positions() for pos: Vector2i in alignment_pattern_pos: _place_align_pattern(qr_data, module_count, pos) _place_timing_patterns(qr_data, module_count) # dark module qr_data[8 + (module_count - 8) * module_count] = 1 # place data _place_data(qr_data, module_count, alignment_pattern_pos, structured_data) return qr_data static func _mask(qr_data: PackedByteArray, module_count: int, alignment_pattern_pos: Array[Vector2i], mask_pattern: int) -> void: var mask_fn: Callable = _mask_pattern_fns()[mask_pattern] for x: int in range(module_count): for y: int in range(module_count): var pos: Vector2i = Vector2i(x, y) if _is_data_module(module_count, alignment_pattern_pos, pos): var idx: int = x + y * module_count qr_data[idx] = int(mask_fn.call(pos)) ^ qr_data[idx] # return mask pattern number func _get_best_qr_mask(masked_qrs: Array[PackedByteArray], module_count: int) -> int: var min_idx: int = 0 # integer max var cur_min_value: int = 9223372036854775807 for idx: int in range(masked_qrs.size()): var rating: int = _calc_mask_rating(masked_qrs[idx], module_count) if rating < cur_min_value: min_idx = idx cur_min_value = rating return min_idx func _mask_qr(qr_data: PackedByteArray) -> PackedByteArray: var module_count: int = self.get_module_count() var alignment_pattern_pos: Array[Vector2i] = self._get_allignment_pattern_positions() # apply mask pattern if !self.auto_mask_pattern: _mask(qr_data, module_count, alignment_pattern_pos, self.mask_pattern) _place_format(qr_data, module_count, self.error_correction, self.mask_pattern) _place_version(qr_data, self.version) return qr_data # get best mask pattern var masked_qr: Array[PackedByteArray] = [] var mask_fns: Array[Callable] = _mask_pattern_fns() for pattern_idx: int in range(mask_fns.size()): var cur_qr: PackedByteArray = qr_data.duplicate() _mask(cur_qr, module_count, alignment_pattern_pos, pattern_idx) # normally the format version is applied AFTER getting the best pattern, but will produce worse qr codes _place_format(cur_qr, module_count, self.error_correction, pattern_idx) _place_version(cur_qr, self.version) masked_qr.append(cur_qr) var best_mask: int = _get_best_qr_mask(masked_qr, module_count) self.mask_pattern = best_mask qr_data = masked_qr[best_mask] return qr_data #### DEVEL TOOLS static func _print_qr(data: PackedByteArray, module_count: int) -> void: for y: int in range(module_count): var row: String = "" for x: int in range(module_count): var value: int = data[y * module_count + x] match value: 0: row += "⬜" 1: row += "⬛" 2: row += "🟨" 3: row += "🟦" _: row += "🟥" print(row) static func _bin_to_string(value: int, bits: int = 8) -> String: var val: String = "" for idx: int in range(bits): if idx % 4 == 0: val = " " + val val = str(int(bool(value & (1 << idx)))) + val return val.strip_edges() static func _arr_to_string(arr: PackedByteArray) -> String: var val: String = "" for byte: int in arr: val += "[" + _bin_to_string(byte, 8) + "] " return val.strip_edges()