├── .gitignore ├── LICENSE.txt ├── README.md ├── examples └── QRCode │ └── QRCode.ino ├── generate_table.py ├── keywords.txt ├── library.properties ├── src ├── qrcode.c └── qrcode.h └── tests ├── BitBuffer.cpp ├── BitBuffer.hpp ├── QrCode.cpp ├── QrCode.hpp ├── QrSegment.cpp ├── QrSegment.hpp ├── README.md ├── run-tests.cpp └── run.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | This library is written and maintained by Richard Moore. 4 | Major parts were derived from Project Nayuki's library. 5 | 6 | Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode) 7 | Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | QRCode 2 | ====== 3 | 4 | A simple library for generating [QR codes](https://en.wikipedia.org/wiki/QR_code) in C, 5 | optimized for processing and memory constrained systems. 6 | 7 | **Features:** 8 | 9 | - Stack-based (no heap necessary; but you can use heap if you want) 10 | - Low-memory foot print (relatively) 11 | - Compile-time stripping of unecessary logic and constants 12 | - MIT License; do with this as you please 13 | 14 | 15 | Installing 16 | ---------- 17 | 18 | To install this library, download and save it to your Arduino libraries directory. 19 | 20 | Rename the directory to QRCode (if downloaded from GitHub, the filename may be 21 | qrcode-master; library names may not contain the hyphen, so it must be renamed) 22 | 23 | 24 | API 25 | --- 26 | 27 | **Generate a QR Code** 28 | 29 | ```c 30 | // The structure to manage the QR code 31 | QRCode qrcode; 32 | 33 | // Allocate a chunk of memory to store the QR code 34 | uint8_t qrcodeBytes[qrcode_getBufferSize()]; 35 | 36 | qrcode_initText(&qrcode, qrcodeBytes, 3, ECC_LOW, "HELLO WORLD"); 37 | ``` 38 | 39 | **Draw a QR Code** 40 | 41 | How a QR code is used will vary greatly from project to project. For example: 42 | 43 | - Display on an OLED screen (128x64 nicely supports 2 side-by-side version 3 QR codes) 44 | - Print as a bitmap on a thermal printer 45 | - Store as a BMP (or with a some extra work, possibly a PNG) on an SD card 46 | 47 | The following example prints a QR code to the Serial Monitor (it likely will 48 | not be scannable, but is just for demonstration purposes). 49 | 50 | ```c 51 | for (uint8 y = 0; y < qrcode.size; y++) { 52 | for (uint8 x = 0; x < qrcode.size; x++) { 53 | if (qrcode_getModule(&qrcode, x, y) { 54 | Serial.print("**"); 55 | } else { 56 | Serial.print(" "); 57 | } 58 | } 59 | Serial.print("\n"); 60 | } 61 | ``` 62 | 63 | 64 | What is Version, Error Correction and Mode? 65 | ------------------------------------------- 66 | 67 | A QR code is composed of many little squares, called **modules**, which represent 68 | encoded data, with additional error correction (allowing partially damaged QR 69 | codes to still be read). 70 | 71 | The **version** of a QR code is a number between 1 and 40 (inclusive), which indicates 72 | the size of the QR code. The width and height of a QR code are always equal (it is 73 | square) and are equal to `4 * version + 17`. 74 | 75 | The level of **error correction** is a number between 0 and 3 (inclusive), or can be 76 | one of the symbolic names ECC_LOW, ECC_MEDIUM, ECC_QUARTILE and ECC_HIGH. Higher 77 | levels of error correction sacrifice data capacity, but allow a larger portion of 78 | the QR code to be damaged or unreadable. 79 | 80 | The **mode** of a QR code is determined by the data being encoded. Each mode is encoded 81 | internally using a compact representation, so lower modes can contain more data. 82 | 83 | - **NUMERIC:** numbers (`0-9`) 84 | - **ALPHANUMERIC:** uppercase letters (`A-Z`), numbers (`0-9`), the space (` `), dollar sign (`$`), percent sign (`%`), asterisk (`*`), plus (`+`), minus (`-`), decimal point (`.`), slash (`/`) and colon (`:`). 85 | - **BYTE:** any character 86 | 87 | 88 | Data Capacities 89 | --------------- 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 |
VersionSizeError CorrectionMode
NumericAlphanumericByte
121 x 21LOW412517
MEDIUM342014
QUARTILE271611
HIGH17107
225 x 25LOW774732
MEDIUM633826
QUARTILE482920
HIGH342014
329 x 29LOW1277753
MEDIUM1016142
QUARTILE774732
HIGH583524
433 x 33LOW18711478
MEDIUM1499062
QUARTILE1116746
HIGH825034
537 x 37LOW255154106
MEDIUM20212284
QUARTILE1448760
HIGH1066444
641 x 41LOW322195134
MEDIUM255154106
QUARTILE17810874
HIGH1398458
745 x 45LOW370224154
MEDIUM293178122
QUARTILE20712586
HIGH1549364
849 x 49LOW461279192
MEDIUM365221152
QUARTILE259157108
HIGH20212284
953 x 53LOW552335230
MEDIUM432262180
QUARTILE312189130
HIGH23514398
1057 x 57LOW652395271
MEDIUM513311213
QUARTILE364221151
HIGH288174119
1161 x 61LOW772468321
MEDIUM604366251
QUARTILE427259177
HIGH331200137
1265 x 65LOW883535367
MEDIUM691419287
QUARTILE489296203
HIGH374227155
1369 x 69LOW1022619425
MEDIUM796483331
QUARTILE580352241
HIGH427259177
1473 x 73LOW1101667458
MEDIUM871528362
QUARTILE621376258
HIGH468283194
1577 x 77LOW1250758520
MEDIUM991600412
QUARTILE703426292
HIGH530321220
1681 x 81LOW1408854586
MEDIUM1082656450
QUARTILE775470322
HIGH602365250
1785 x 85LOW1548938644
MEDIUM1212734504
QUARTILE876531364
HIGH674408280
1889 x 89LOW17251046718
MEDIUM1346816560
QUARTILE948574394
HIGH746452310
1993 x 93LOW19031153792
MEDIUM1500909624
QUARTILE1063644442
HIGH813493338
2097 x 97LOW20611249858
MEDIUM1600970666
QUARTILE1159702482
HIGH919557382
21101 x 101LOW22321352929
MEDIUM17081035711
QUARTILE1224742509
HIGH969587403
22105 x 105LOW240914601003
MEDIUM18721134779
QUARTILE1358823565
HIGH1056640439
23109 x 109LOW262015881091
MEDIUM20591248857
QUARTILE1468890611
HIGH1108672461
24113 x 113LOW281217041171
MEDIUM21881326911
QUARTILE1588963661
HIGH1228744511
25117 x 117LOW305718531273
MEDIUM23951451997
QUARTILE17181041715
HIGH1286779535
26121 x 121LOW328319901367
MEDIUM254415421059
QUARTILE18041094751
HIGH1425864593
27125 x 125LOW351721321465
MEDIUM270116371125
QUARTILE19331172805
HIGH1501910625
28129 x 129LOW366922231528
MEDIUM285717321190
QUARTILE20851263868
HIGH1581958658
29133 x 133LOW390923691628
MEDIUM303518391264
QUARTILE21811322908
HIGH16771016698
30137 x 137LOW415825201732
MEDIUM328919941370
QUARTILE23581429982
HIGH17821080742
31141 x 141LOW441726771840
MEDIUM348621131452
QUARTILE247314991030
HIGH18971150790
32145 x 145LOW468628401952
MEDIUM369322381538
QUARTILE267016181112
HIGH20221226842
33149 x 149LOW496530092068
MEDIUM390923691628
QUARTILE280517001168
HIGH21571307898
34153 x 153LOW525331832188
MEDIUM413425061722
QUARTILE294917871228
HIGH23011394958
35157 x 157LOW552933512303
MEDIUM434326321809
QUARTILE308118671283
HIGH23611431983
36161 x 161LOW583635372431
MEDIUM458827801911
QUARTILE324419661351
HIGH252415301051
37165 x 165LOW615337292563
MEDIUM477528941989
QUARTILE341720711423
HIGH262515911093
38169 x 169LOW647939272699
MEDIUM503930542099
QUARTILE359921811499
HIGH273516581139
39173 x 173LOW674340872809
MEDIUM531332202213
QUARTILE379122981579
HIGH292717741219
40177 x 177LOW708942962953
MEDIUM559633912331
QUARTILE399324201663
HIGH305718521273
664 | 665 | 666 | Special Thanks 667 | -------------- 668 | 669 | A HUGE thank you to [Project Nayuki](https://www.nayuki.io/) for the 670 | [QR code C++ library](https://github.com/nayuki/QR-Code-generator/tree/master/cpp) 671 | which was critical in development of this library. 672 | 673 | 674 | License 675 | ------- 676 | 677 | MIT License. 678 | -------------------------------------------------------------------------------- /examples/QRCode/QRCode.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * QRCode 3 | * 4 | * A quick example of generating a QR code. 5 | * 6 | * This prints the QR code to the serial monitor as solid blocks. Each module 7 | * is two characters wide, since the monospace font used in the serial monitor 8 | * is approximately twice as tall as wide. 9 | * 10 | */ 11 | 12 | #include "qrcode.h" 13 | 14 | void setup() { 15 | Serial.begin(115200); 16 | 17 | // Start time 18 | uint32_t dt = millis(); 19 | 20 | // Create the QR code 21 | QRCode qrcode; 22 | uint8_t qrcodeData[qrcode_getBufferSize(3)]; 23 | qrcode_initText(&qrcode, qrcodeData, 3, 0, "HELLO WORLD"); 24 | 25 | // Delta time 26 | dt = millis() - dt; 27 | Serial.print("QR Code Generation Time: "); 28 | Serial.print(dt); 29 | Serial.print("\n"); 30 | 31 | // Top quiet zone 32 | Serial.print("\n\n\n\n"); 33 | 34 | for (uint8_t y = 0; y < qrcode.size; y++) { 35 | 36 | // Left quiet zone 37 | Serial.print(" "); 38 | 39 | // Each horizontal module 40 | for (uint8_t x = 0; x < qrcode.size; x++) { 41 | 42 | // Print each module (UTF-8 \u2588 is a solid block) 43 | Serial.print(qrcode_getModule(&qrcode, x, y) ? "\u2588\u2588": " "); 44 | 45 | } 46 | 47 | Serial.print("\n"); 48 | } 49 | 50 | // Bottom quiet zone 51 | Serial.print("\n\n\n\n"); 52 | } 53 | 54 | void loop() { 55 | 56 | } 57 | -------------------------------------------------------------------------------- /generate_table.py: -------------------------------------------------------------------------------- 1 | Data = [ 2 | ["1", "41", "25", "17", "34", "20", "14","27", "16", "11","17", "10", "7"], 3 | ["2", "77", "47", "32", "63", "38", "26", "48", "29", "20", "34", "20", "14"], 4 | ["3", "127", "77", "53", "101", "61", "42", "77", "47", "32", "58", "35", "24"], 5 | ["4", "187", "114", "78", "149", "90", "62", "111", "67", "46", "82", "50", "34"], 6 | ["5", "255", "154", "106", "202", "122", "84", "144", "87", "60", "106", "64", "44"], 7 | ["6", "322", "195", "134", "255", "154", "106", "178", "108", "74", "139", "84", "58"], 8 | ["7", "370", "224", "154", "293", "178", "122", "207", "125", "86", "154", "93", "64"], 9 | ["8", "461", "279", "192", "365", "221", "152", "259", "157", "108", "202", "122", "84"], 10 | ["9", "552", "335", "230", "432", "262", "180", "312", "189", "130", "235", "143", "98"], 11 | ["10", "652", "395", "271", "513", "311", "213", "364", "221", "151", "288", "174", "119"], 12 | ["11", "772", "468", "321", "604", "366", "251", "427", "259", "177", "331", "200", "137"], 13 | ["12", "883", "535", "367", "691", "419", "287", "489", "296", "203", "374", "227", "155"], 14 | ["13", "1022", "619", "425", "796", "483", "331", "580", "352", "241", "427", "259", "177"], 15 | ["14", "1101", "667", "458", "871", "528", "362", "621", "376", "258", "468", "283", "194"], 16 | ["15", "1250", "758", "520", "991", "600", "412", "703", "426", "292", "530", "321", "220"], 17 | ["16", "1408", "854", "586", "1082", "656", "450", "775", "470", "322", "602", "365", "250"], 18 | ["17", "1548", "938", "644", "1212", "734", "504", "876", "531", "364", "674", "408", "280"], 19 | ["18", "1725", "1046", "718", "1346", "816", "560", "948", "574", "394", "746", "452", "310"], 20 | ["19", "1903", "1153", "792", "1500", "909", "624", "1063", "644", "442", "813", "493", "338"], 21 | ["20", "2061", "1249", "858", "1600", "970", "666", "1159", "702", "482", "919", "557", "382"], 22 | ["21", "2232", "1352", "929", "1708", "1035", "711", "1224", "742", "509", "969", "587", "403"], 23 | ["22", "2409", "1460", "1003", "1872", "1134", "779", "1358", "823", "565", "1056", "640", "439"], 24 | ["23", "2620", "1588", "1091", "2059", "1248", "857", "1468", "890", "611", "1108", "672", "461"], 25 | ["24", "2812", "1704", "1171", "2188", "1326", "911", "1588", "963", "661", "1228", "744", "511"], 26 | ["25", "3057", "1853", "1273", "2395", "1451", "997", "1718", "1041", "715", "1286", "779", "535"], 27 | ["26", "3283", "1990", "1367", "2544", "1542", "1059", "1804", "1094", "751", "1425", "864", "593"], 28 | ["27", "3517", "2132", "1465", "2701", "1637", "1125", "1933", "1172", "805", "1501", "910", "625"], 29 | ["28", "3669", "2223", "1528", "2857", "1732", "1190", "2085", "1263", "868", "1581", "958", "658"], 30 | ["29", "3909", "2369", "1628", "3035", "1839", "1264", "2181", "1322", "908", "1677", "1016", "698"], 31 | ["30", "4158", "2520", "1732", "3289", "1994", "1370", "2358", "1429", "982", "1782", "1080", "742"], 32 | ["31", "4417", "2677", "1840", "3486", "2113", "1452", "2473", "1499", "1030", "1897", "1150", "790"], 33 | ["32", "4686", "2840", "1952", "3693", "2238", "1538", "2670", "1618", "1112", "2022", "1226", "842"], 34 | ["33", "4965", "3009", "2068", "3909", "2369", "1628", "2805", "1700", "1168", "2157", "1307", "898"], 35 | ["34", "5253", "3183", "2188", "4134", "2506", "1722", "2949", "1787", "1228", "2301", "1394", "958"], 36 | ["35", "5529", "3351", "2303", "4343", "2632", "1809", "3081", "1867", "1283", "2361", "1431", "983"], 37 | ["36", "5836", "3537", "2431", "4588", "2780", "1911", "3244", "1966", "1351", "2524", "1530", "1051"], 38 | ["37", "6153", "3729", "2563", "4775", "2894", "1989", "3417", "2071", "1423", "2625", "1591", "1093"], 39 | ["38", "6479", "3927", "2699", "5039", "3054", "2099", "3599", "2181", "1499", "2735", "1658", "1139"], 40 | ["39", "6743", "4087", "2809", "5313", "3220", "2213", "3791", "2298", "1579", "2927", "1774", "1219"], 41 | ["40", "7089", "4296", "2953", "5596", "3391", "2331", "3993", "2420", "1663", "3057", "1852", "1273"], 42 | ] 43 | Template = ''' 44 | %s 45 | %s 46 | LOW%s%s%s 47 | 48 | 49 | MEDIUM%s%s%s 50 | 51 | 52 | QUARTILE%s%s%s 53 | 54 | 55 | HIGH%s%s%s 56 | ''' 57 | 58 | for data in Data: 59 | data = data[:] 60 | size = 4 * int(data[0]) + 17 61 | data.insert(1, "%d x %d" % (size, size)) 62 | print Template % tuple(data) 63 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | 2 | # Datatypes (KEYWORD1) 3 | 4 | bool KEYWORD1 5 | uint8_t KEYWORD1 6 | QRCode KEYWORD1 7 | 8 | 9 | # Methods and Functions (KEYWORD2) 10 | 11 | qrcode_getBufferSize KEYWORD2 12 | qrcode_initText KEYWORD2 13 | qrcode_initBytes KEYWORD2 14 | qrcode_getModule KEYWORD2 15 | 16 | 17 | # Instances (KEYWORD2) 18 | 19 | 20 | # Constants (LITERAL1) 21 | 22 | false LITERAL1 23 | true LITERAL1 24 | 25 | ECC_LOW LITERAL1 26 | ECC_MEDIUM LITERAL1 27 | ECC_QUARTILE LITERAL1 28 | ECC_HIGH LITERAL1 29 | MODE_NUMERIC LITERAL1 30 | MODE_ALPHANUMERIC LITERAL1 31 | MODE_BYTE LITERAL1 32 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=QRCode 2 | version=0.0.1 3 | author=Richard Moore 4 | maintainer=Richard Moore 5 | sentence=A simple QR code generation library. 6 | paragraph=A simple QR code generation library. 7 | category=Other 8 | url=https://github.com/ricmoo/qrcode/ 9 | architectures=* 10 | includes=qrcode.h 11 | -------------------------------------------------------------------------------- /src/qrcode.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * This library is written and maintained by Richard Moore. 5 | * Major parts were derived from Project Nayuki's library. 6 | * 7 | * Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode) 8 | * Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library) 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in 18 | * all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | * THE SOFTWARE. 27 | */ 28 | 29 | /** 30 | * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was 31 | * heavily inspired and compared against. 32 | * 33 | * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp 34 | */ 35 | 36 | #include "qrcode.h" 37 | 38 | #include 39 | #include 40 | 41 | #pragma mark - Error Correction Lookup tables 42 | 43 | #if LOCK_VERSION == 0 44 | 45 | static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = { 46 | // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level 47 | { 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium 48 | { 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low 49 | { 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High 50 | { 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile 51 | }; 52 | 53 | static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = { 54 | // Version: (note that index 0 is for padding, and is set to an illegal value) 55 | // 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level 56 | { 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium 57 | { 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low 58 | { 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High 59 | { 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile 60 | }; 61 | 62 | static const uint16_t NUM_RAW_DATA_MODULES[40] = { 63 | // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 64 | 208, 359, 567, 807, 1079, 1383, 1568, 1936, 2336, 2768, 3232, 3728, 4256, 4651, 5243, 5867, 6523, 65 | // 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 66 | 7211, 7931, 8683, 9252, 10068, 10916, 11796, 12708, 13652, 14628, 15371, 16411, 17483, 18587, 67 | // 32, 33, 34, 35, 36, 37, 38, 39, 40 68 | 19723, 20891, 22091, 23008, 24272, 25568, 26896, 28256, 29648 69 | }; 70 | 71 | // @TODO: Put other LOCK_VERSIONS here 72 | #elif LOCK_VERSION == 3 73 | 74 | static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = { 75 | 26, 15, 44, 36 76 | }; 77 | 78 | static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = { 79 | 1, 1, 2, 2 80 | }; 81 | 82 | static const uint16_t NUM_RAW_DATA_MODULES = 567; 83 | 84 | #else 85 | 86 | #error Unsupported LOCK_VERSION (add it...) 87 | 88 | #endif 89 | 90 | 91 | static int max(int a, int b) { 92 | if (a > b) { return a; } 93 | return b; 94 | } 95 | 96 | /* 97 | static int abs(int value) { 98 | if (value < 0) { return -value; } 99 | return value; 100 | } 101 | */ 102 | 103 | 104 | #pragma mark - Mode testing and conversion 105 | 106 | static int8_t getAlphanumeric(char c) { 107 | 108 | if (c >= '0' && c <= '9') { return (c - '0'); } 109 | if (c >= 'A' && c <= 'Z') { return (c - 'A' + 10); } 110 | 111 | switch (c) { 112 | case ' ': return 36; 113 | case '$': return 37; 114 | case '%': return 38; 115 | case '*': return 39; 116 | case '+': return 40; 117 | case '-': return 41; 118 | case '.': return 42; 119 | case '/': return 43; 120 | case ':': return 44; 121 | } 122 | 123 | return -1; 124 | } 125 | 126 | static bool isAlphanumeric(const char *text, uint16_t length) { 127 | while (length != 0) { 128 | if (getAlphanumeric(text[--length]) == -1) { return false; } 129 | } 130 | return true; 131 | } 132 | 133 | 134 | static bool isNumeric(const char *text, uint16_t length) { 135 | while (length != 0) { 136 | char c = text[--length]; 137 | if (c < '0' || c > '9') { return false; } 138 | } 139 | return true; 140 | } 141 | 142 | 143 | #pragma mark - Counting 144 | 145 | // We store the following tightly packed (less 8) in modeInfo 146 | // <=9 <=26 <= 40 147 | // NUMERIC ( 10, 12, 14); 148 | // ALPHANUMERIC ( 9, 11, 13); 149 | // BYTE ( 8, 16, 16); 150 | static char getModeBits(uint8_t version, uint8_t mode) { 151 | // Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits 152 | // hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2)) 153 | unsigned int modeInfo = 0x7bbb80a; 154 | 155 | #if LOCK_VERSION == 0 || LOCK_VERSION > 9 156 | if (version > 9) { modeInfo >>= 9; } 157 | #endif 158 | 159 | #if LOCK_VERSION == 0 || LOCK_VERSION > 26 160 | if (version > 26) { modeInfo >>= 9; } 161 | #endif 162 | 163 | char result = 8 + ((modeInfo >> (3 * mode)) & 0x07); 164 | if (result == 15) { result = 16; } 165 | 166 | return result; 167 | } 168 | 169 | 170 | #pragma mark - BitBucket 171 | 172 | typedef struct BitBucket { 173 | uint32_t bitOffsetOrWidth; 174 | uint16_t capacityBytes; 175 | uint8_t *data; 176 | } BitBucket; 177 | 178 | /* 179 | void bb_dump(BitBucket *bitBuffer) { 180 | printf("Buffer: "); 181 | for (uint32_t i = 0; i < bitBuffer->capacityBytes; i++) { 182 | printf("%02x", bitBuffer->data[i]); 183 | if ((i % 4) == 3) { printf(" "); } 184 | } 185 | printf("\n"); 186 | } 187 | */ 188 | 189 | static uint16_t bb_getGridSizeBytes(uint8_t size) { 190 | return (((size * size) + 7) / 8); 191 | } 192 | 193 | static uint16_t bb_getBufferSizeBytes(uint32_t bits) { 194 | return ((bits + 7) / 8); 195 | } 196 | 197 | static void bb_initBuffer(BitBucket *bitBuffer, uint8_t *data, int32_t capacityBytes) { 198 | bitBuffer->bitOffsetOrWidth = 0; 199 | bitBuffer->capacityBytes = capacityBytes; 200 | bitBuffer->data = data; 201 | 202 | memset(data, 0, bitBuffer->capacityBytes); 203 | } 204 | 205 | static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) { 206 | bitGrid->bitOffsetOrWidth = size; 207 | bitGrid->capacityBytes = bb_getGridSizeBytes(size); 208 | bitGrid->data = data; 209 | 210 | memset(data, 0, bitGrid->capacityBytes); 211 | } 212 | 213 | static void bb_appendBits(BitBucket *bitBuffer, uint32_t val, uint8_t length) { 214 | uint32_t offset = bitBuffer->bitOffsetOrWidth; 215 | for (int8_t i = length - 1; i >= 0; i--, offset++) { 216 | bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7)); 217 | } 218 | bitBuffer->bitOffsetOrWidth = offset; 219 | } 220 | /* 221 | void bb_setBits(BitBucket *bitBuffer, uint32_t val, int offset, uint8_t length) { 222 | for (int8_t i = length - 1; i >= 0; i--, offset++) { 223 | bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7)); 224 | } 225 | } 226 | */ 227 | static void bb_setBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool on) { 228 | uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; 229 | uint8_t mask = 1 << (7 - (offset & 0x07)); 230 | if (on) { 231 | bitGrid->data[offset >> 3] |= mask; 232 | } else { 233 | bitGrid->data[offset >> 3] &= ~mask; 234 | } 235 | } 236 | 237 | static void bb_invertBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool invert) { 238 | uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; 239 | uint8_t mask = 1 << (7 - (offset & 0x07)); 240 | bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0); 241 | if (on ^ invert) { 242 | bitGrid->data[offset >> 3] |= mask; 243 | } else { 244 | bitGrid->data[offset >> 3] &= ~mask; 245 | } 246 | } 247 | 248 | static bool bb_getBit(BitBucket *bitGrid, uint8_t x, uint8_t y) { 249 | uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; 250 | return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; 251 | } 252 | 253 | 254 | #pragma mark - Drawing Patterns 255 | 256 | // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical 257 | // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. 258 | // This means it is possible to apply a mask, undo it, and try another mask. Note that a final 259 | // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). 260 | static void applyMask(BitBucket *modules, BitBucket *isFunction, uint8_t mask) { 261 | uint8_t size = modules->bitOffsetOrWidth; 262 | 263 | for (uint8_t y = 0; y < size; y++) { 264 | for (uint8_t x = 0; x < size; x++) { 265 | if (bb_getBit(isFunction, x, y)) { continue; } 266 | 267 | bool invert = 0; 268 | switch (mask) { 269 | case 0: invert = (x + y) % 2 == 0; break; 270 | case 1: invert = y % 2 == 0; break; 271 | case 2: invert = x % 3 == 0; break; 272 | case 3: invert = (x + y) % 3 == 0; break; 273 | case 4: invert = (x / 3 + y / 2) % 2 == 0; break; 274 | case 5: invert = x * y % 2 + x * y % 3 == 0; break; 275 | case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; 276 | case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; 277 | } 278 | bb_invertBit(modules, x, y, invert); 279 | } 280 | } 281 | } 282 | 283 | static void setFunctionModule(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y, bool on) { 284 | bb_setBit(modules, x, y, on); 285 | bb_setBit(isFunction, x, y, true); 286 | } 287 | 288 | // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). 289 | static void drawFinderPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) { 290 | uint8_t size = modules->bitOffsetOrWidth; 291 | 292 | for (int8_t i = -4; i <= 4; i++) { 293 | for (int8_t j = -4; j <= 4; j++) { 294 | uint8_t dist = max(abs(i), abs(j)); // Chebyshev/infinity norm 295 | int16_t xx = x + j, yy = y + i; 296 | if (0 <= xx && xx < size && 0 <= yy && yy < size) { 297 | setFunctionModule(modules, isFunction, xx, yy, dist != 2 && dist != 4); 298 | } 299 | } 300 | } 301 | } 302 | 303 | // Draws a 5*5 alignment pattern, with the center module at (x, y). 304 | static void drawAlignmentPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) { 305 | for (int8_t i = -2; i <= 2; i++) { 306 | for (int8_t j = -2; j <= 2; j++) { 307 | setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1); 308 | } 309 | } 310 | } 311 | 312 | // Draws two copies of the format bits (with its own error correction code) 313 | // based on the given mask and this object's error correction level field. 314 | static void drawFormatBits(BitBucket *modules, BitBucket *isFunction, uint8_t ecc, uint8_t mask) { 315 | 316 | uint8_t size = modules->bitOffsetOrWidth; 317 | 318 | // Calculate error correction code and pack bits 319 | uint32_t data = ecc << 3 | mask; // errCorrLvl is uint2, mask is uint3 320 | uint32_t rem = data; 321 | for (int i = 0; i < 10; i++) { 322 | rem = (rem << 1) ^ ((rem >> 9) * 0x537); 323 | } 324 | 325 | data = data << 10 | rem; 326 | data ^= 0x5412; // uint15 327 | 328 | // Draw first copy 329 | for (uint8_t i = 0; i <= 5; i++) { 330 | setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0); 331 | } 332 | 333 | setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0); 334 | setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0); 335 | setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0); 336 | 337 | for (int8_t i = 9; i < 15; i++) { 338 | setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0); 339 | } 340 | 341 | // Draw second copy 342 | for (int8_t i = 0; i <= 7; i++) { 343 | setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0); 344 | } 345 | 346 | for (int8_t i = 8; i < 15; i++) { 347 | setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0); 348 | } 349 | 350 | setFunctionModule(modules, isFunction, 8, size - 8, true); 351 | } 352 | 353 | 354 | // Draws two copies of the version bits (with its own error correction code), 355 | // based on this object's version field (which only has an effect for 7 <= version <= 40). 356 | static void drawVersion(BitBucket *modules, BitBucket *isFunction, uint8_t version) { 357 | 358 | int8_t size = modules->bitOffsetOrWidth; 359 | 360 | #if LOCK_VERSION != 0 && LOCK_VERSION < 7 361 | return; 362 | 363 | #else 364 | if (version < 7) { return; } 365 | 366 | // Calculate error correction code and pack bits 367 | uint32_t rem = version; // version is uint6, in the range [7, 40] 368 | for (uint8_t i = 0; i < 12; i++) { 369 | rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); 370 | } 371 | 372 | uint32_t data = version << 12 | rem; // uint18 373 | 374 | // Draw two copies 375 | for (uint8_t i = 0; i < 18; i++) { 376 | bool bit = ((data >> i) & 1) != 0; 377 | uint8_t a = size - 11 + i % 3, b = i / 3; 378 | setFunctionModule(modules, isFunction, a, b, bit); 379 | setFunctionModule(modules, isFunction, b, a, bit); 380 | } 381 | 382 | #endif 383 | } 384 | 385 | static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint8_t version, uint8_t ecc) { 386 | 387 | uint8_t size = modules->bitOffsetOrWidth; 388 | 389 | // Draw the horizontal and vertical timing patterns 390 | for (uint8_t i = 0; i < size; i++) { 391 | setFunctionModule(modules, isFunction, 6, i, i % 2 == 0); 392 | setFunctionModule(modules, isFunction, i, 6, i % 2 == 0); 393 | } 394 | 395 | // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) 396 | drawFinderPattern(modules, isFunction, 3, 3); 397 | drawFinderPattern(modules, isFunction, size - 4, 3); 398 | drawFinderPattern(modules, isFunction, 3, size - 4); 399 | 400 | #if LOCK_VERSION == 0 || LOCK_VERSION > 1 401 | 402 | if (version > 1) { 403 | 404 | // Draw the numerous alignment patterns 405 | 406 | uint8_t alignCount = version / 7 + 2; 407 | uint8_t step; 408 | if (version != 32) { 409 | step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2 410 | } else { // C-C-C-Combo breaker! 411 | step = 26; 412 | } 413 | 414 | uint8_t alignPositionIndex = alignCount - 1; 415 | uint8_t alignPosition[alignCount]; 416 | 417 | alignPosition[0] = 6; 418 | 419 | uint8_t size = version * 4 + 17; 420 | for (uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) { 421 | alignPosition[alignPositionIndex--] = pos; 422 | } 423 | 424 | for (uint8_t i = 0; i < alignCount; i++) { 425 | for (uint8_t j = 0; j < alignCount; j++) { 426 | if ((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) || (i == alignCount - 1 && j == 0)) { 427 | continue; // Skip the three finder corners 428 | } else { 429 | drawAlignmentPattern(modules, isFunction, alignPosition[i], alignPosition[j]); 430 | } 431 | } 432 | } 433 | } 434 | 435 | #endif 436 | 437 | // Draw configuration data 438 | drawFormatBits(modules, isFunction, ecc, 0); // Dummy mask value; overwritten later in the constructor 439 | drawVersion(modules, isFunction, version); 440 | } 441 | 442 | 443 | // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire 444 | // data area of this QR Code symbol. Function modules need to be marked off before this is called. 445 | static void drawCodewords(BitBucket *modules, BitBucket *isFunction, BitBucket *codewords) { 446 | 447 | uint32_t bitLength = codewords->bitOffsetOrWidth; 448 | uint8_t *data = codewords->data; 449 | 450 | uint8_t size = modules->bitOffsetOrWidth; 451 | 452 | // Bit index into the data 453 | uint32_t i = 0; 454 | 455 | // Do the funny zigzag scan 456 | for (int16_t right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair 457 | if (right == 6) { right = 5; } 458 | 459 | for (uint8_t vert = 0; vert < size; vert++) { // Vertical counter 460 | for (int j = 0; j < 2; j++) { 461 | uint8_t x = right - j; // Actual x coordinate 462 | bool upwards = ((right & 2) == 0) ^ (x < 6); 463 | uint8_t y = upwards ? size - 1 - vert : vert; // Actual y coordinate 464 | if (!bb_getBit(isFunction, x, y) && i < bitLength) { 465 | bb_setBit(modules, x, y, ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0); 466 | i++; 467 | } 468 | // If there are any remainder bits (0 to 7), they are already 469 | // set to 0/false/white when the grid of modules was initialized 470 | } 471 | } 472 | } 473 | } 474 | 475 | 476 | 477 | #pragma mark - Penalty Calculation 478 | 479 | #define PENALTY_N1 3 480 | #define PENALTY_N2 3 481 | #define PENALTY_N3 40 482 | #define PENALTY_N4 10 483 | 484 | // Calculates and returns the penalty score based on state of this QR Code's current modules. 485 | // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. 486 | // @TODO: This can be optimized by working with the bytes instead of bits. 487 | static uint32_t getPenaltyScore(BitBucket *modules) { 488 | uint32_t result = 0; 489 | 490 | uint8_t size = modules->bitOffsetOrWidth; 491 | 492 | // Adjacent modules in row having same color 493 | for (uint8_t y = 0; y < size; y++) { 494 | 495 | bool colorX = bb_getBit(modules, 0, y); 496 | for (uint8_t x = 1, runX = 1; x < size; x++) { 497 | bool cx = bb_getBit(modules, x, y); 498 | if (cx != colorX) { 499 | colorX = cx; 500 | runX = 1; 501 | 502 | } else { 503 | runX++; 504 | if (runX == 5) { 505 | result += PENALTY_N1; 506 | } else if (runX > 5) { 507 | result++; 508 | } 509 | } 510 | } 511 | } 512 | 513 | // Adjacent modules in column having same color 514 | for (uint8_t x = 0; x < size; x++) { 515 | bool colorY = bb_getBit(modules, x, 0); 516 | for (uint8_t y = 1, runY = 1; y < size; y++) { 517 | bool cy = bb_getBit(modules, x, y); 518 | if (cy != colorY) { 519 | colorY = cy; 520 | runY = 1; 521 | } else { 522 | runY++; 523 | if (runY == 5) { 524 | result += PENALTY_N1; 525 | } else if (runY > 5) { 526 | result++; 527 | } 528 | } 529 | } 530 | } 531 | 532 | uint16_t black = 0; 533 | for (uint8_t y = 0; y < size; y++) { 534 | uint16_t bitsRow = 0, bitsCol = 0; 535 | for (uint8_t x = 0; x < size; x++) { 536 | bool color = bb_getBit(modules, x, y); 537 | 538 | // 2*2 blocks of modules having same color 539 | if (x > 0 && y > 0) { 540 | bool colorUL = bb_getBit(modules, x - 1, y - 1); 541 | bool colorUR = bb_getBit(modules, x, y - 1); 542 | bool colorL = bb_getBit(modules, x - 1, y); 543 | if (color == colorUL && color == colorUR && color == colorL) { 544 | result += PENALTY_N2; 545 | } 546 | } 547 | 548 | // Finder-like pattern in rows and columns 549 | bitsRow = ((bitsRow << 1) & 0x7FF) | color; 550 | bitsCol = ((bitsCol << 1) & 0x7FF) | bb_getBit(modules, y, x); 551 | 552 | // Needs 11 bits accumulated 553 | if (x >= 10) { 554 | if (bitsRow == 0x05D || bitsRow == 0x5D0) { 555 | result += PENALTY_N3; 556 | } 557 | if (bitsCol == 0x05D || bitsCol == 0x5D0) { 558 | result += PENALTY_N3; 559 | } 560 | } 561 | 562 | // Balance of black and white modules 563 | if (color) { black++; } 564 | } 565 | } 566 | 567 | // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% 568 | uint16_t total = size * size; 569 | for (uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) { 570 | result += PENALTY_N4; 571 | } 572 | 573 | return result; 574 | } 575 | 576 | 577 | #pragma mark - Reed-Solomon Generator 578 | 579 | static uint8_t rs_multiply(uint8_t x, uint8_t y) { 580 | // Russian peasant multiplication 581 | // See: https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication 582 | uint16_t z = 0; 583 | for (int8_t i = 7; i >= 0; i--) { 584 | z = (z << 1) ^ ((z >> 7) * 0x11D); 585 | z ^= ((y >> i) & 1) * x; 586 | } 587 | return z; 588 | } 589 | 590 | static void rs_init(uint8_t degree, uint8_t *coeff) { 591 | memset(coeff, 0, degree); 592 | coeff[degree - 1] = 1; 593 | 594 | // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), 595 | // drop the highest term, and store the rest of the coefficients in order of descending powers. 596 | // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). 597 | uint16_t root = 1; 598 | for (uint8_t i = 0; i < degree; i++) { 599 | // Multiply the current product by (x - r^i) 600 | for (uint8_t j = 0; j < degree; j++) { 601 | coeff[j] = rs_multiply(coeff[j], root); 602 | if (j + 1 < degree) { 603 | coeff[j] ^= coeff[j + 1]; 604 | } 605 | } 606 | root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) 607 | } 608 | } 609 | 610 | static void rs_getRemainder(uint8_t degree, uint8_t *coeff, uint8_t *data, uint8_t length, uint8_t *result, uint8_t stride) { 611 | // Compute the remainder by performing polynomial division 612 | 613 | //for (uint8_t i = 0; i < degree; i++) { result[] = 0; } 614 | //memset(result, 0, degree); 615 | 616 | for (uint8_t i = 0; i < length; i++) { 617 | uint8_t factor = data[i] ^ result[0]; 618 | for (uint8_t j = 1; j < degree; j++) { 619 | result[(j - 1) * stride] = result[j * stride]; 620 | } 621 | result[(degree - 1) * stride] = 0; 622 | 623 | for (uint8_t j = 0; j < degree; j++) { 624 | result[j * stride] ^= rs_multiply(coeff[j], factor); 625 | } 626 | } 627 | } 628 | 629 | 630 | 631 | #pragma mark - QrCode 632 | 633 | static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, uint16_t length, uint8_t version) { 634 | int8_t mode = MODE_BYTE; 635 | 636 | if (isNumeric((char*)text, length)) { 637 | mode = MODE_NUMERIC; 638 | bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4); 639 | bb_appendBits(dataCodewords, length, getModeBits(version, MODE_NUMERIC)); 640 | 641 | uint16_t accumData = 0; 642 | uint8_t accumCount = 0; 643 | for (uint16_t i = 0; i < length; i++) { 644 | accumData = accumData * 10 + ((char)(text[i]) - '0'); 645 | accumCount++; 646 | if (accumCount == 3) { 647 | bb_appendBits(dataCodewords, accumData, 10); 648 | accumData = 0; 649 | accumCount = 0; 650 | } 651 | } 652 | 653 | // 1 or 2 digits remaining 654 | if (accumCount > 0) { 655 | bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1); 656 | } 657 | 658 | } else if (isAlphanumeric((char*)text, length)) { 659 | mode = MODE_ALPHANUMERIC; 660 | bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4); 661 | bb_appendBits(dataCodewords, length, getModeBits(version, MODE_ALPHANUMERIC)); 662 | 663 | uint16_t accumData = 0; 664 | uint8_t accumCount = 0; 665 | for (uint16_t i = 0; i < length; i++) { 666 | accumData = accumData * 45 + getAlphanumeric((char)(text[i])); 667 | accumCount++; 668 | if (accumCount == 2) { 669 | bb_appendBits(dataCodewords, accumData, 11); 670 | accumData = 0; 671 | accumCount = 0; 672 | } 673 | } 674 | 675 | // 1 character remaining 676 | if (accumCount > 0) { 677 | bb_appendBits(dataCodewords, accumData, 6); 678 | } 679 | 680 | } else { 681 | bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4); 682 | bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE)); 683 | for (uint16_t i = 0; i < length; i++) { 684 | bb_appendBits(dataCodewords, (char)(text[i]), 8); 685 | } 686 | } 687 | 688 | //bb_setBits(dataCodewords, length, 4, getModeBits(version, mode)); 689 | 690 | return mode; 691 | } 692 | 693 | static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data) { 694 | 695 | // See: http://www.thonky.com/qr-code-tutorial/structure-final-message 696 | 697 | #if LOCK_VERSION == 0 698 | uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1]; 699 | uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1]; 700 | uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; 701 | #else 702 | uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc]; 703 | uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc]; 704 | uint16_t moduleCount = NUM_RAW_DATA_MODULES; 705 | #endif 706 | 707 | uint8_t blockEccLen = totalEcc / numBlocks; 708 | uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks; 709 | uint8_t shortBlockLen = moduleCount / 8 / numBlocks; 710 | 711 | uint8_t shortDataBlockLen = shortBlockLen - blockEccLen; 712 | 713 | uint8_t result[data->capacityBytes]; 714 | memset(result, 0, sizeof(result)); 715 | 716 | uint8_t coeff[blockEccLen]; 717 | rs_init(blockEccLen, coeff); 718 | 719 | uint16_t offset = 0; 720 | uint8_t *dataBytes = data->data; 721 | 722 | 723 | // Interleave all short blocks 724 | for (uint8_t i = 0; i < shortDataBlockLen; i++) { 725 | uint16_t index = i; 726 | uint8_t stride = shortDataBlockLen; 727 | for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { 728 | result[offset++] = dataBytes[index]; 729 | 730 | #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 731 | if (blockNum == numShortBlocks) { stride++; } 732 | #endif 733 | index += stride; 734 | } 735 | } 736 | 737 | // Version less than 5 only have short blocks 738 | #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 739 | { 740 | // Interleave long blocks 741 | uint16_t index = shortDataBlockLen * (numShortBlocks + 1); 742 | uint8_t stride = shortDataBlockLen; 743 | for (uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) { 744 | result[offset++] = dataBytes[index]; 745 | 746 | if (blockNum == 0) { stride++; } 747 | index += stride; 748 | } 749 | } 750 | #endif 751 | 752 | // Add all ecc blocks, interleaved 753 | uint8_t blockSize = shortDataBlockLen; 754 | for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { 755 | 756 | #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 757 | if (blockNum == numShortBlocks) { blockSize++; } 758 | #endif 759 | rs_getRemainder(blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks); 760 | dataBytes += blockSize; 761 | } 762 | 763 | memcpy(data->data, result, data->capacityBytes); 764 | data->bitOffsetOrWidth = moduleCount; 765 | } 766 | 767 | // We store the Format bits tightly packed into a single byte (each of the 4 modes is 2 bits) 768 | // The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc) 769 | static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0); 770 | 771 | 772 | #pragma mark - Public QRCode functions 773 | 774 | uint16_t qrcode_getBufferSize(uint8_t version) { 775 | return bb_getGridSizeBytes(4 * version + 17); 776 | } 777 | 778 | // @TODO: Return error if data is too big. 779 | int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length) { 780 | uint8_t size = version * 4 + 17; 781 | qrcode->version = version; 782 | qrcode->size = size; 783 | qrcode->ecc = ecc; 784 | qrcode->modules = modules; 785 | 786 | uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03; 787 | 788 | #if LOCK_VERSION == 0 789 | uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; 790 | uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1]; 791 | #else 792 | version = LOCK_VERSION; 793 | uint16_t moduleCount = NUM_RAW_DATA_MODULES; 794 | uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits]; 795 | #endif 796 | 797 | struct BitBucket codewords; 798 | uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)]; 799 | bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes)); 800 | 801 | // Place the data code words into the buffer 802 | int8_t mode = encodeDataCodewords(&codewords, data, length, version); 803 | 804 | if (mode < 0) { return -1; } 805 | qrcode->mode = mode; 806 | 807 | // Add terminator and pad up to a byte if applicable 808 | uint32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth; 809 | if (padding > 4) { padding = 4; } 810 | bb_appendBits(&codewords, 0, padding); 811 | bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8); 812 | 813 | // Pad with alternate bytes until data capacity is reached 814 | for (uint8_t padByte = 0xEC; codewords.bitOffsetOrWidth < (dataCapacity * 8); padByte ^= 0xEC ^ 0x11) { 815 | bb_appendBits(&codewords, padByte, 8); 816 | } 817 | 818 | BitBucket modulesGrid; 819 | bb_initGrid(&modulesGrid, modules, size); 820 | 821 | BitBucket isFunctionGrid; 822 | uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)]; 823 | bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size); 824 | 825 | // Draw function patterns, draw all codewords, do masking 826 | drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits); 827 | performErrorCorrection(version, eccFormatBits, &codewords); 828 | drawCodewords(&modulesGrid, &isFunctionGrid, &codewords); 829 | 830 | // Find the best (lowest penalty) mask 831 | uint8_t mask = 0; 832 | int32_t minPenalty = INT32_MAX; 833 | for (uint8_t i = 0; i < 8; i++) { 834 | drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, i); 835 | applyMask(&modulesGrid, &isFunctionGrid, i); 836 | int penalty = getPenaltyScore(&modulesGrid); 837 | if (penalty < minPenalty) { 838 | mask = i; 839 | minPenalty = penalty; 840 | } 841 | applyMask(&modulesGrid, &isFunctionGrid, i); // Undoes the mask due to XOR 842 | } 843 | 844 | qrcode->mask = mask; 845 | 846 | // Overwrite old format bits 847 | drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask); 848 | 849 | // Apply the final choice of mask 850 | applyMask(&modulesGrid, &isFunctionGrid, mask); 851 | 852 | return 0; 853 | } 854 | 855 | int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data) { 856 | return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data)); 857 | } 858 | 859 | bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) { 860 | if (x < 0 || x >= qrcode->size || y < 0 || y >= qrcode->size) { 861 | return false; 862 | } 863 | 864 | uint32_t offset = y * qrcode->size + x; 865 | return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; 866 | } 867 | 868 | /* 869 | uint8_t qrcode_getHexLength(QRCode *qrcode) { 870 | return ((qrcode->size * qrcode->size) + 7) / 4; 871 | } 872 | 873 | void qrcode_getHex(QRCode *qrcode, char *result) { 874 | 875 | } 876 | */ 877 | -------------------------------------------------------------------------------- /src/qrcode.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * This library is written and maintained by Richard Moore. 5 | * Major parts were derived from Project Nayuki's library. 6 | * 7 | * Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode) 8 | * Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library) 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in 18 | * all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | * THE SOFTWARE. 27 | */ 28 | 29 | /** 30 | * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was 31 | * heavily inspired and compared against. 32 | * 33 | * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp 34 | */ 35 | 36 | 37 | #ifndef __QRCODE_H_ 38 | #define __QRCODE_H_ 39 | 40 | #ifndef __cplusplus 41 | typedef unsigned char bool; 42 | static const bool false = 0; 43 | static const bool true = 1; 44 | #endif 45 | 46 | #include 47 | 48 | 49 | // QR Code Format Encoding 50 | #define MODE_NUMERIC 0 51 | #define MODE_ALPHANUMERIC 1 52 | #define MODE_BYTE 2 53 | 54 | 55 | // Error Correction Code Levels 56 | #define ECC_LOW 0 57 | #define ECC_MEDIUM 1 58 | #define ECC_QUARTILE 2 59 | #define ECC_HIGH 3 60 | 61 | 62 | // If set to non-zero, this library can ONLY produce QR codes at that version 63 | // This saves a lot of dynamic memory, as the codeword tables are skipped 64 | #ifndef LOCK_VERSION 65 | #define LOCK_VERSION 0 66 | #endif 67 | 68 | 69 | typedef struct QRCode { 70 | uint8_t version; 71 | uint8_t size; 72 | uint8_t ecc; 73 | uint8_t mode; 74 | uint8_t mask; 75 | uint8_t *modules; 76 | } QRCode; 77 | 78 | 79 | #ifdef __cplusplus 80 | extern "C"{ 81 | #endif /* __cplusplus */ 82 | 83 | 84 | 85 | uint16_t qrcode_getBufferSize(uint8_t version); 86 | 87 | int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data); 88 | int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length); 89 | 90 | bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y); 91 | 92 | 93 | 94 | #ifdef __cplusplus 95 | } 96 | #endif /* __cplusplus */ 97 | 98 | 99 | #endif /* __QRCODE_H_ */ 100 | -------------------------------------------------------------------------------- /tests/BitBuffer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * QR Code generator library (C++) 3 | * 4 | * Copyright (c) Project Nayuki 5 | * https://www.nayuki.io/page/qr-code-generator-library 6 | * 7 | * (MIT License) 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * - The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * - The Software is provided "as is", without warranty of any kind, express or 17 | * implied, including but not limited to the warranties of merchantability, 18 | * fitness for a particular purpose and noninfringement. In no event shall the 19 | * authors or copyright holders be liable for any claim, damages or other 20 | * liability, whether in an action of contract, tort or otherwise, arising from, 21 | * out of or in connection with the Software or the use or other dealings in the 22 | * Software. 23 | */ 24 | 25 | #include 26 | #include "BitBuffer.hpp" 27 | 28 | 29 | qrcodegen::BitBuffer::BitBuffer() : 30 | data(), 31 | bitLength(0) {} 32 | 33 | 34 | int qrcodegen::BitBuffer::getBitLength() const { 35 | return bitLength; 36 | } 37 | 38 | 39 | std::vector qrcodegen::BitBuffer::getBytes() const { 40 | return data; 41 | } 42 | 43 | 44 | void qrcodegen::BitBuffer::appendBits(uint32_t val, int len) { 45 | if (len < 0 || len > 32 || (len < 32 && (val >> len) != 0)) 46 | throw "Value out of range"; 47 | size_t newBitLen = bitLength + len; 48 | while (data.size() * 8 < newBitLen) 49 | data.push_back(0); 50 | for (int i = len - 1; i >= 0; i--, bitLength++) // Append bit by bit 51 | data.at(bitLength >> 3) |= ((val >> i) & 1) << (7 - (bitLength & 7)); 52 | } 53 | 54 | 55 | void qrcodegen::BitBuffer::appendData(const QrSegment &seg) { 56 | size_t newBitLen = bitLength + seg.bitLength; 57 | while (data.size() * 8 < newBitLen) 58 | data.push_back(0); 59 | for (int i = 0; i < seg.bitLength; i++, bitLength++) { // Append bit by bit 60 | int bit = (seg.data.at(i >> 3) >> (7 - (i & 7))) & 1; 61 | data.at(bitLength >> 3) |= bit << (7 - (bitLength & 7)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/BitBuffer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * QR Code generator library (C++) 3 | * 4 | * Copyright (c) Project Nayuki 5 | * https://www.nayuki.io/page/qr-code-generator-library 6 | * 7 | * (MIT License) 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * - The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * - The Software is provided "as is", without warranty of any kind, express or 17 | * implied, including but not limited to the warranties of merchantability, 18 | * fitness for a particular purpose and noninfringement. In no event shall the 19 | * authors or copyright holders be liable for any claim, damages or other 20 | * liability, whether in an action of contract, tort or otherwise, arising from, 21 | * out of or in connection with the Software or the use or other dealings in the 22 | * Software. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include 28 | #include 29 | #include "QrSegment.hpp" 30 | 31 | 32 | namespace qrcodegen { 33 | 34 | /* 35 | * An appendable sequence of bits. Bits are packed in big endian within a byte. 36 | */ 37 | class BitBuffer { 38 | 39 | /*---- Fields ----*/ 40 | private: 41 | 42 | std::vector data; 43 | int bitLength; 44 | 45 | 46 | 47 | /*---- Constructor ----*/ 48 | public: 49 | 50 | // Creates an empty bit buffer (length 0). 51 | BitBuffer(); 52 | 53 | 54 | 55 | /*---- Methods ----*/ 56 | public: 57 | 58 | // Returns the number of bits in the buffer, which is a non-negative value. 59 | int getBitLength() const; 60 | 61 | 62 | // Returns a copy of all bytes, padding up to the nearest byte. 63 | std::vector getBytes() const; 64 | 65 | 66 | // Appends the given number of bits of the given value to this sequence. 67 | // If 0 <= len <= 31, then this requires 0 <= val < 2^len. 68 | void appendBits(uint32_t val, int len); 69 | 70 | 71 | // Appends the data of the given segment to this bit buffer. 72 | void appendData(const QrSegment &seg); 73 | 74 | }; 75 | 76 | } 77 | -------------------------------------------------------------------------------- /tests/QrCode.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * QR Code generator library (C++) 3 | * 4 | * Copyright (c) Project Nayuki 5 | * https://www.nayuki.io/page/qr-code-generator-library 6 | * 7 | * (MIT License) 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * - The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * - The Software is provided "as is", without warranty of any kind, express or 17 | * implied, including but not limited to the warranties of merchantability, 18 | * fitness for a particular purpose and noninfringement. In no event shall the 19 | * authors or copyright holders be liable for any claim, damages or other 20 | * liability, whether in an action of contract, tort or otherwise, arising from, 21 | * out of or in connection with the Software or the use or other dealings in the 22 | * Software. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "BitBuffer.hpp" 31 | #include "QrCode.hpp" 32 | 33 | 34 | qrcodegen::QrCode::Ecc::Ecc(int ord, int fb) : 35 | ordinal(ord), 36 | formatBits(fb) {} 37 | 38 | 39 | const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::LOW (0, 1); 40 | const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::MEDIUM (1, 0); 41 | const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::QUARTILE(2, 3); 42 | const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::HIGH (3, 2); 43 | 44 | 45 | qrcodegen::QrCode qrcodegen::QrCode::encodeText(const char *text, int version, const Ecc &ecl) { 46 | std::vector segs(QrSegment::makeSegments(text)); 47 | return encodeSegments(segs, ecl, version, version, -1, false); 48 | } 49 | 50 | 51 | qrcodegen::QrCode qrcodegen::QrCode::encodeBinary(const std::vector &data, const Ecc &ecl) { 52 | std::vector segs; 53 | segs.push_back(QrSegment::makeBytes(data)); 54 | return encodeSegments(segs, ecl); 55 | } 56 | 57 | 58 | qrcodegen::QrCode qrcodegen::QrCode::encodeSegments(const std::vector &segs, const Ecc &ecl, 59 | int minVersion, int maxVersion, int mask, bool boostEcl) { 60 | if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7) 61 | throw "Invalid value"; 62 | 63 | // Find the minimal version number to use 64 | int version, dataUsedBits; 65 | for (version = minVersion; ; version++) { 66 | int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available 67 | dataUsedBits = QrSegment::getTotalBits(segs, version); 68 | if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) 69 | break; // This version number is found to be suitable 70 | if (version >= maxVersion) // All versions in the range could not fit the given data 71 | throw "Data too long"; 72 | } 73 | if (dataUsedBits == -1) 74 | throw "Assertion error"; 75 | 76 | // Increase the error correction level while the data still fits in the current version number 77 | const Ecc *newEcl = &ecl; 78 | if (boostEcl) { 79 | if (dataUsedBits <= getNumDataCodewords(version, Ecc::MEDIUM ) * 8) newEcl = &Ecc::MEDIUM ; 80 | if (dataUsedBits <= getNumDataCodewords(version, Ecc::QUARTILE) * 8) newEcl = &Ecc::QUARTILE; 81 | if (dataUsedBits <= getNumDataCodewords(version, Ecc::HIGH ) * 8) newEcl = &Ecc::HIGH ; 82 | } 83 | 84 | // Create the data bit string by concatenating all segments 85 | int dataCapacityBits = getNumDataCodewords(version, *newEcl) * 8; 86 | BitBuffer bb; 87 | for (size_t i = 0; i < segs.size(); i++) { 88 | const QrSegment &seg(segs.at(i)); 89 | bb.appendBits(seg.mode.modeBits, 4); 90 | bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); 91 | bb.appendData(seg); 92 | } 93 | 94 | // Add terminator and pad up to a byte if applicable 95 | bb.appendBits(0, std::min(4, dataCapacityBits - bb.getBitLength())); 96 | bb.appendBits(0, (8 - bb.getBitLength() % 8) % 8); 97 | 98 | // Pad with alternate bytes until data capacity is reached 99 | for (uint8_t padByte = 0xEC; bb.getBitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) 100 | bb.appendBits(padByte, 8); 101 | if (bb.getBitLength() % 8 != 0) 102 | throw "Assertion error"; 103 | 104 | // Create the QR Code symbol 105 | return QrCode(version, *newEcl, bb.getBytes(), mask); 106 | } 107 | 108 | 109 | qrcodegen::QrCode::QrCode(int ver, const Ecc &ecl, const std::vector &dataCodewords, int mask) : 110 | // Initialize scalar fields 111 | version(ver), 112 | size(1 <= ver && ver <= 40 ? ver * 4 + 17 : -1), // Avoid signed overflow undefined behavior 113 | errorCorrectionLevel(ecl) { 114 | 115 | // Check arguments 116 | if (ver < 1 || ver > 40 || mask < -1 || mask > 7) 117 | throw "Value out of range"; 118 | 119 | std::vector row(size); 120 | for (int i = 0; i < size; i++) { 121 | modules.push_back(row); 122 | isFunction.push_back(row); 123 | } 124 | 125 | // Draw function patterns, draw all codewords, do masking 126 | drawFunctionPatterns(); 127 | const std::vector allCodewords(appendErrorCorrection(dataCodewords)); 128 | drawCodewords(allCodewords); 129 | this->mask = handleConstructorMasking(mask); 130 | } 131 | 132 | 133 | qrcodegen::QrCode::QrCode(const QrCode &qr, int mask) : 134 | // Copy scalar fields 135 | version(qr.version), 136 | size(qr.size), 137 | errorCorrectionLevel(qr.errorCorrectionLevel) { 138 | 139 | // Check arguments 140 | if (mask < -1 || mask > 7) 141 | throw "Mask value out of range"; 142 | 143 | // Handle grid fields 144 | modules = qr.modules; 145 | isFunction = qr.isFunction; 146 | 147 | // Handle masking 148 | applyMask(qr.mask); // Undo old mask 149 | this->mask = handleConstructorMasking(mask); 150 | } 151 | 152 | 153 | int qrcodegen::QrCode::getMask() const { 154 | return mask; 155 | } 156 | 157 | 158 | int qrcodegen::QrCode::getModule(int x, int y) const { 159 | if (0 <= x && x < size && 0 <= y && y < size) 160 | return modules.at(y).at(x) ? 1 : 0; 161 | else 162 | return 0; // Infinite white border 163 | } 164 | 165 | 166 | std::string qrcodegen::QrCode::toSvgString(int border) const { 167 | if (border < 0) 168 | throw "Border must be non-negative"; 169 | std::ostringstream sb; 170 | sb << "\n"; 171 | sb << "\n"; 172 | sb << "\n"; 174 | sb << "\t\n"; 175 | sb << "\t\n"; 189 | sb << "\n"; 190 | return sb.str(); 191 | } 192 | 193 | 194 | void qrcodegen::QrCode::drawFunctionPatterns() { 195 | // Draw the horizontal and vertical timing patterns 196 | for (int i = 0; i < size; i++) { 197 | setFunctionModule(6, i, i % 2 == 0); 198 | setFunctionModule(i, 6, i % 2 == 0); 199 | } 200 | 201 | // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) 202 | drawFinderPattern(3, 3); 203 | drawFinderPattern(size - 4, 3); 204 | drawFinderPattern(3, size - 4); 205 | 206 | // Draw the numerous alignment patterns 207 | const std::vector alignPatPos(getAlignmentPatternPositions(version)); 208 | int numAlign = alignPatPos.size(); 209 | for (int i = 0; i < numAlign; i++) { 210 | for (int j = 0; j < numAlign; j++) { 211 | if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) 212 | continue; // Skip the three finder corners 213 | else 214 | drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); 215 | } 216 | } 217 | 218 | // Draw configuration data 219 | drawFormatBits(0); // Dummy mask value; overwritten later in the constructor 220 | drawVersion(); 221 | } 222 | 223 | 224 | void qrcodegen::QrCode::drawFormatBits(int mask) { 225 | // Calculate error correction code and pack bits 226 | int data = errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 227 | int rem = data; 228 | for (int i = 0; i < 10; i++) 229 | rem = (rem << 1) ^ ((rem >> 9) * 0x537); 230 | data = data << 10 | rem; 231 | data ^= 0x5412; // uint15 232 | if (data >> 15 != 0) 233 | throw "Assertion error"; 234 | 235 | // Draw first copy 236 | for (int i = 0; i <= 5; i++) 237 | setFunctionModule(8, i, ((data >> i) & 1) != 0); 238 | setFunctionModule(8, 7, ((data >> 6) & 1) != 0); 239 | setFunctionModule(8, 8, ((data >> 7) & 1) != 0); 240 | setFunctionModule(7, 8, ((data >> 8) & 1) != 0); 241 | for (int i = 9; i < 15; i++) 242 | setFunctionModule(14 - i, 8, ((data >> i) & 1) != 0); 243 | 244 | // Draw second copy 245 | for (int i = 0; i <= 7; i++) 246 | setFunctionModule(size - 1 - i, 8, ((data >> i) & 1) != 0); 247 | for (int i = 8; i < 15; i++) 248 | setFunctionModule(8, size - 15 + i, ((data >> i) & 1) != 0); 249 | setFunctionModule(8, size - 8, true); 250 | } 251 | 252 | 253 | void qrcodegen::QrCode::drawVersion() { 254 | if (version < 7) 255 | return; 256 | 257 | // Calculate error correction code and pack bits 258 | int rem = version; // version is uint6, in the range [7, 40] 259 | for (int i = 0; i < 12; i++) 260 | rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); 261 | int data = version << 12 | rem; // uint18 262 | if (data >> 18 != 0) 263 | throw "Assertion error"; 264 | 265 | // Draw two copies 266 | for (int i = 0; i < 18; i++) { 267 | bool bit = ((data >> i) & 1) != 0; 268 | int a = size - 11 + i % 3, b = i / 3; 269 | setFunctionModule(a, b, bit); 270 | setFunctionModule(b, a, bit); 271 | } 272 | } 273 | 274 | 275 | void qrcodegen::QrCode::drawFinderPattern(int x, int y) { 276 | for (int i = -4; i <= 4; i++) { 277 | for (int j = -4; j <= 4; j++) { 278 | int dist = std::max(std::abs(i), std::abs(j)); // Chebyshev/infinity norm 279 | int xx = x + j, yy = y + i; 280 | if (0 <= xx && xx < size && 0 <= yy && yy < size) 281 | setFunctionModule(xx, yy, dist != 2 && dist != 4); 282 | } 283 | } 284 | } 285 | 286 | 287 | void qrcodegen::QrCode::drawAlignmentPattern(int x, int y) { 288 | for (int i = -2; i <= 2; i++) { 289 | for (int j = -2; j <= 2; j++) 290 | setFunctionModule(x + j, y + i, std::max(std::abs(i), std::abs(j)) != 1); 291 | } 292 | } 293 | 294 | 295 | void qrcodegen::QrCode::setFunctionModule(int x, int y, bool isBlack) { 296 | modules.at(y).at(x) = isBlack; 297 | isFunction.at(y).at(x) = true; 298 | } 299 | 300 | 301 | std::vector qrcodegen::QrCode::appendErrorCorrection(const std::vector &data) const { 302 | if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) 303 | throw "Invalid argument"; 304 | 305 | // Calculate parameter numbers 306 | int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal][version]; 307 | int totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[errorCorrectionLevel.ordinal][version]; 308 | if (totalEcc % numBlocks != 0) 309 | throw "Assertion error"; 310 | int blockEccLen = totalEcc / numBlocks; 311 | int numShortBlocks = numBlocks - getNumRawDataModules(version) / 8 % numBlocks; 312 | int shortBlockLen = getNumRawDataModules(version) / 8 / numBlocks; 313 | 314 | // Split data into blocks and append ECC to each block 315 | std::vector > blocks; 316 | const ReedSolomonGenerator rs(blockEccLen); 317 | for (int i = 0, k = 0; i < numBlocks; i++) { 318 | std::vector dat; 319 | dat.insert(dat.begin(), data.begin() + k, data.begin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); 320 | k += dat.size(); 321 | const std::vector ecc(rs.getRemainder(dat)); 322 | if (i < numShortBlocks) 323 | dat.push_back(0); 324 | dat.insert(dat.end(), ecc.begin(), ecc.end()); 325 | blocks.push_back(dat); 326 | } 327 | 328 | // Interleave (not concatenate) the bytes from every block into a single sequence 329 | std::vector result; 330 | for (int i = 0; static_cast(i) < blocks.at(0).size(); i++) { 331 | for (int j = 0; static_cast(j) < blocks.size(); j++) { 332 | // Skip the padding byte in short blocks 333 | if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) 334 | result.push_back(blocks.at(j).at(i)); 335 | } 336 | } 337 | if (result.size() != static_cast(getNumRawDataModules(version) / 8)) 338 | throw "Assertion error"; 339 | return result; 340 | } 341 | 342 | 343 | void qrcodegen::QrCode::drawCodewords(const std::vector &data) { 344 | if (data.size() != static_cast(getNumRawDataModules(version) / 8)) 345 | throw "Invalid argument"; 346 | 347 | size_t i = 0; // Bit index into the data 348 | // Do the funny zigzag scan 349 | for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair 350 | if (right == 6) 351 | right = 5; 352 | for (int vert = 0; vert < size; vert++) { // Vertical counter 353 | for (int j = 0; j < 2; j++) { 354 | int x = right - j; // Actual x coordinate 355 | bool upwards = ((right & 2) == 0) ^ (x < 6); 356 | int y = upwards ? size - 1 - vert : vert; // Actual y coordinate 357 | if (!isFunction.at(y).at(x) && i < data.size() * 8) { 358 | modules.at(y).at(x) = ((data.at(i >> 3) >> (7 - (i & 7))) & 1) != 0; 359 | i++; 360 | } 361 | // If there are any remainder bits (0 to 7), they are already 362 | // set to 0/false/white when the grid of modules was initialized 363 | } 364 | } 365 | } 366 | if (static_cast(i) != data.size() * 8) 367 | throw "Assertion error"; 368 | } 369 | 370 | 371 | void qrcodegen::QrCode::applyMask(int mask) { 372 | if (mask < 0 || mask > 7) 373 | throw "Mask value out of range"; 374 | for (int y = 0; y < size; y++) { 375 | for (int x = 0; x < size; x++) { 376 | bool invert; 377 | switch (mask) { 378 | case 0: invert = (x + y) % 2 == 0; break; 379 | case 1: invert = y % 2 == 0; break; 380 | case 2: invert = x % 3 == 0; break; 381 | case 3: invert = (x + y) % 3 == 0; break; 382 | case 4: invert = (x / 3 + y / 2) % 2 == 0; break; 383 | case 5: invert = x * y % 2 + x * y % 3 == 0; break; 384 | case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; 385 | case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; 386 | default: throw "Assertion error"; 387 | } 388 | modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); 389 | } 390 | } 391 | } 392 | 393 | 394 | int qrcodegen::QrCode::handleConstructorMasking(int mask) { 395 | if (mask == -1) { // Automatically choose best mask 396 | int32_t minPenalty = INT32_MAX; 397 | for (int i = 0; i < 8; i++) { 398 | drawFormatBits(i); 399 | applyMask(i); 400 | int penalty = getPenaltyScore(); 401 | if (penalty < minPenalty) { 402 | mask = i; 403 | minPenalty = penalty; 404 | } 405 | applyMask(i); // Undoes the mask due to XOR 406 | } 407 | } 408 | if (mask < 0 || mask > 7) 409 | throw "Assertion error"; 410 | drawFormatBits(mask); // Overwrite old format bits 411 | applyMask(mask); // Apply the final choice of mask 412 | return mask; // The caller shall assign this value to the final-declared field 413 | } 414 | 415 | 416 | int qrcodegen::QrCode::getPenaltyScore() const { 417 | int result = 0; 418 | 419 | // Adjacent modules in row having same color 420 | for (int y = 0; y < size; y++) { 421 | bool colorX = modules.at(y).at(0); 422 | for (int x = 1, runX = 1; x < size; x++) { 423 | if (modules.at(y).at(x) != colorX) { 424 | colorX = modules.at(y).at(x); 425 | runX = 1; 426 | } else { 427 | runX++; 428 | if (runX == 5) 429 | result += PENALTY_N1; 430 | else if (runX > 5) 431 | result++; 432 | } 433 | } 434 | } 435 | // Adjacent modules in column having same color 436 | for (int x = 0; x < size; x++) { 437 | bool colorY = modules.at(0).at(x); 438 | for (int y = 1, runY = 1; y < size; y++) { 439 | if (modules.at(y).at(x) != colorY) { 440 | colorY = modules.at(y).at(x); 441 | runY = 1; 442 | } else { 443 | runY++; 444 | if (runY == 5) 445 | result += PENALTY_N1; 446 | else if (runY > 5) 447 | result++; 448 | } 449 | } 450 | } 451 | 452 | // 2*2 blocks of modules having same color 453 | for (int y = 0; y < size - 1; y++) { 454 | for (int x = 0; x < size - 1; x++) { 455 | bool color = modules.at(y).at(x); 456 | if ( color == modules.at(y).at(x + 1) && 457 | color == modules.at(y + 1).at(x) && 458 | color == modules.at(y + 1).at(x + 1)) 459 | result += PENALTY_N2; 460 | } 461 | } 462 | 463 | // Finder-like pattern in rows 464 | for (int y = 0; y < size; y++) { 465 | for (int x = 0, bits = 0; x < size; x++) { 466 | bits = ((bits << 1) & 0x7FF) | (modules.at(y).at(x) ? 1 : 0); 467 | if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated 468 | result += PENALTY_N3; 469 | } 470 | } 471 | // Finder-like pattern in columns 472 | for (int x = 0; x < size; x++) { 473 | for (int y = 0, bits = 0; y < size; y++) { 474 | bits = ((bits << 1) & 0x7FF) | (modules.at(y).at(x) ? 1 : 0); 475 | if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated 476 | result += PENALTY_N3; 477 | } 478 | } 479 | 480 | // Balance of black and white modules 481 | int black = 0; 482 | for (int y = 0; y < size; y++) { 483 | for (int x = 0; x < size; x++) { 484 | if (modules.at(y).at(x)) 485 | black++; 486 | } 487 | } 488 | int total = size * size; 489 | // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% 490 | for (int k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) 491 | result += PENALTY_N4; 492 | return result; 493 | } 494 | 495 | 496 | std::vector qrcodegen::QrCode::getAlignmentPatternPositions(int ver) { 497 | if (ver < 1 || ver > 40) 498 | throw "Version number out of range"; 499 | else if (ver == 1) 500 | return std::vector(); 501 | else { 502 | int numAlign = ver / 7 + 2; 503 | int step; 504 | if (ver != 32) 505 | step = (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2 506 | else // C-C-C-Combo breaker! 507 | step = 26; 508 | 509 | std::vector result; 510 | int size = ver * 4 + 17; 511 | for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) 512 | result.insert(result.begin(), pos); 513 | result.insert(result.begin(), 6); 514 | return result; 515 | } 516 | } 517 | 518 | 519 | int qrcodegen::QrCode::getNumRawDataModules(int ver) { 520 | if (ver < 1 || ver > 40) 521 | throw "Version number out of range"; 522 | int result = (16 * ver + 128) * ver + 64; 523 | if (ver >= 2) { 524 | int numAlign = ver / 7 + 2; 525 | result -= (25 * numAlign - 10) * numAlign - 55; 526 | if (ver >= 7) 527 | result -= 18 * 2; // Subtract version information 528 | } 529 | return result; 530 | } 531 | 532 | 533 | int qrcodegen::QrCode::getNumDataCodewords(int ver, const Ecc &ecl) { 534 | if (ver < 1 || ver > 40) 535 | throw "Version number out of range"; 536 | return getNumRawDataModules(ver) / 8 - NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal][ver]; 537 | } 538 | 539 | 540 | /*---- Tables of constants ----*/ 541 | 542 | const int qrcodegen::QrCode::PENALTY_N1 = 3; 543 | const int qrcodegen::QrCode::PENALTY_N2 = 3; 544 | const int qrcodegen::QrCode::PENALTY_N3 = 40; 545 | const int qrcodegen::QrCode::PENALTY_N4 = 10; 546 | 547 | 548 | const int16_t qrcodegen::QrCode::NUM_ERROR_CORRECTION_CODEWORDS[4][41] = { 549 | // Version: (note that index 0 is for padding, and is set to an illegal value) 550 | //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level 551 | {-1, 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low 552 | {-1, 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium 553 | {-1, 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile 554 | {-1, 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High 555 | }; 556 | 557 | const int8_t qrcodegen::QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { 558 | // Version: (note that index 0 is for padding, and is set to an illegal value) 559 | //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level 560 | {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low 561 | {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium 562 | {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile 563 | {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High 564 | }; 565 | 566 | 567 | qrcodegen::QrCode::ReedSolomonGenerator::ReedSolomonGenerator(int degree) : 568 | coefficients() { 569 | if (degree < 1 || degree > 255) 570 | throw "Degree out of range"; 571 | 572 | // Start with the monomial x^0 573 | coefficients.resize(degree); 574 | coefficients.at(degree - 1) = 1; 575 | 576 | // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), 577 | // drop the highest term, and store the rest of the coefficients in order of descending powers. 578 | // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). 579 | int root = 1; 580 | for (int i = 0; i < degree; i++) { 581 | // Multiply the current product by (x - r^i) 582 | for (size_t j = 0; j < coefficients.size(); j++) { 583 | coefficients.at(j) = multiply(coefficients.at(j), static_cast(root)); 584 | if (j + 1 < coefficients.size()) 585 | coefficients.at(j) ^= coefficients.at(j + 1); 586 | } 587 | root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) 588 | } 589 | } 590 | 591 | 592 | std::vector qrcodegen::QrCode::ReedSolomonGenerator::getRemainder(const std::vector &data) const { 593 | // Compute the remainder by performing polynomial division 594 | std::vector result(coefficients.size()); 595 | for (size_t i = 0; i < data.size(); i++) { 596 | uint8_t factor = data.at(i) ^ result.at(0); 597 | result.erase(result.begin()); 598 | result.push_back(0); 599 | for (size_t j = 0; j < result.size(); j++) 600 | result.at(j) ^= multiply(coefficients.at(j), factor); 601 | } 602 | return result; 603 | } 604 | 605 | 606 | uint8_t qrcodegen::QrCode::ReedSolomonGenerator::multiply(uint8_t x, uint8_t y) { 607 | // Russian peasant multiplication 608 | int z = 0; 609 | for (int i = 7; i >= 0; i--) { 610 | z = (z << 1) ^ ((z >> 7) * 0x11D); 611 | z ^= ((y >> i) & 1) * x; 612 | } 613 | if (z >> 8 != 0) 614 | throw "Assertion error"; 615 | return static_cast(z); 616 | } 617 | -------------------------------------------------------------------------------- /tests/QrCode.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * QR Code generator library (C++) 3 | * 4 | * Copyright (c) Project Nayuki 5 | * https://www.nayuki.io/page/qr-code-generator-library 6 | * 7 | * (MIT License) 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * - The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * - The Software is provided "as is", without warranty of any kind, express or 17 | * implied, including but not limited to the warranties of merchantability, 18 | * fitness for a particular purpose and noninfringement. In no event shall the 19 | * authors or copyright holders be liable for any claim, damages or other 20 | * liability, whether in an action of contract, tort or otherwise, arising from, 21 | * out of or in connection with the Software or the use or other dealings in the 22 | * Software. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include 28 | #include 29 | #include 30 | #include "QrSegment.hpp" 31 | 32 | 33 | namespace qrcodegen { 34 | 35 | /* 36 | * Represents an immutable square grid of black and white cells for a QR Code symbol, and 37 | * provides static functions to create a QR Code from user-supplied textual or binary data. 38 | * This class covers the QR Code model 2 specification, supporting all versions (sizes) 39 | * from 1 to 40, all 4 error correction levels, and only 3 character encoding modes. 40 | */ 41 | class QrCode { 42 | 43 | /*---- Public helper enumeration ----*/ 44 | public: 45 | 46 | /* 47 | * Represents the error correction level used in a QR Code symbol. 48 | */ 49 | class Ecc { 50 | // Constants declared in ascending order of error protection. 51 | public: 52 | const static Ecc LOW, MEDIUM, QUARTILE, HIGH; 53 | 54 | // Fields. 55 | public: 56 | const int ordinal; // (Public) In the range 0 to 3 (unsigned 2-bit integer). 57 | const int formatBits; // (Package-private) In the range 0 to 3 (unsigned 2-bit integer). 58 | 59 | // Constructor. 60 | private: 61 | Ecc(int ord, int fb); 62 | }; 63 | 64 | 65 | 66 | /*---- Public static factory functions ----*/ 67 | public: 68 | 69 | /* 70 | * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. 71 | * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode 72 | * code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. 73 | * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. 74 | */ 75 | static QrCode encodeText(const char *text, int version, const Ecc &ecl); 76 | 77 | 78 | /* 79 | * Returns a QR Code symbol representing the given binary data string at the given error correction level. 80 | * This function always encodes using the binary segment mode, not any text mode. The maximum number of 81 | * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. 82 | * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. 83 | */ 84 | static QrCode encodeBinary(const std::vector &data, const Ecc &ecl); 85 | 86 | 87 | /* 88 | * Returns a QR Code symbol representing the given data segments with the given encoding parameters. 89 | * The smallest possible QR Code version within the given range is automatically chosen for the output. 90 | * This function allows the user to create a custom sequence of segments that switches 91 | * between modes (such as alphanumeric and binary) to encode text more efficiently. 92 | * This function is considered to be lower level than simply encoding text or binary data. 93 | */ 94 | static QrCode encodeSegments(const std::vector &segs, const Ecc &ecl, 95 | int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters 96 | 97 | 98 | 99 | /*---- Instance fields ----*/ 100 | 101 | // Public immutable scalar parameters 102 | public: 103 | 104 | /* This QR Code symbol's version number, which is always between 1 and 40 (inclusive). */ 105 | const int version; 106 | 107 | /* The width and height of this QR Code symbol, measured in modules. 108 | * Always equal to version × 4 + 17, in the range 21 to 177. */ 109 | const int size; 110 | 111 | /* The error correction level used in this QR Code symbol. */ 112 | const Ecc &errorCorrectionLevel; 113 | 114 | /* The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). 115 | * Note that even if a constructor was called with automatic masking requested 116 | * (mask = -1), the resulting object will still have a mask value between 0 and 7. */ 117 | private: 118 | int mask; 119 | 120 | // Private grids of modules/pixels (conceptually immutable) 121 | private: 122 | std::vector > modules; // The modules of this QR Code symbol (false = white, true = black) 123 | std::vector > isFunction; // Indicates function modules that are not subjected to masking 124 | 125 | 126 | 127 | /*---- Constructors ----*/ 128 | public: 129 | 130 | /* 131 | * Creates a new QR Code symbol with the given version number, error correction level, binary data array, 132 | * and mask number. This is a cumbersome low-level constructor that should not be invoked directly by the user. 133 | * To go one level up, see the encodeSegments() function. 134 | */ 135 | QrCode(int ver, const Ecc &ecl, const std::vector &dataCodewords, int mask); 136 | 137 | 138 | /* 139 | * Creates a new QR Code symbol based on the given existing object, but with a potentially 140 | * different mask pattern. The version, error correction level, codewords, etc. of the newly 141 | * created object are all identical to the argument object; only the mask may differ. 142 | */ 143 | QrCode(const QrCode &qr, int mask); 144 | 145 | 146 | 147 | /*---- Public instance methods ----*/ 148 | public: 149 | 150 | int getMask() const; 151 | 152 | 153 | /* 154 | * Returns the color of the module (pixel) at the given coordinates, which is either 0 for white or 1 for black. The top 155 | * left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then 0 (white) is returned. 156 | */ 157 | int getModule(int x, int y) const; 158 | 159 | 160 | /* 161 | * Based on the given number of border modules to add as padding, this returns a 162 | * string whose contents represents an SVG XML file that depicts this QR Code symbol. 163 | * Note that Unix newlines (\n) are always used, regardless of the platform. 164 | */ 165 | std::string toSvgString(int border) const; 166 | 167 | 168 | 169 | /*---- Private helper methods for constructor: Drawing function modules ----*/ 170 | private: 171 | 172 | void drawFunctionPatterns(); 173 | 174 | 175 | // Draws two copies of the format bits (with its own error correction code) 176 | // based on the given mask and this object's error correction level field. 177 | void drawFormatBits(int mask); 178 | 179 | 180 | // Draws two copies of the version bits (with its own error correction code), 181 | // based on this object's version field (which only has an effect for 7 <= version <= 40). 182 | void drawVersion(); 183 | 184 | 185 | // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). 186 | void drawFinderPattern(int x, int y); 187 | 188 | 189 | // Draws a 5*5 alignment pattern, with the center module at (x, y). 190 | void drawAlignmentPattern(int x, int y); 191 | 192 | 193 | // Sets the color of a module and marks it as a function module. 194 | // Only used by the constructor. Coordinates must be in range. 195 | void setFunctionModule(int x, int y, bool isBlack); 196 | 197 | 198 | /*---- Private helper methods for constructor: Codewords and masking ----*/ 199 | private: 200 | 201 | // Returns a new byte string representing the given data with the appropriate error correction 202 | // codewords appended to it, based on this object's version and error correction level. 203 | std::vector appendErrorCorrection(const std::vector &data) const; 204 | 205 | 206 | // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire 207 | // data area of this QR Code symbol. Function modules need to be marked off before this is called. 208 | void drawCodewords(const std::vector &data); 209 | 210 | 211 | // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical 212 | // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. 213 | // This means it is possible to apply a mask, undo it, and try another mask. Note that a final 214 | // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). 215 | void applyMask(int mask); 216 | 217 | 218 | // A messy helper function for the constructors. This QR Code must be in an unmasked state when this 219 | // method is called. The given argument is the requested mask, which is -1 for auto or 0 to 7 for fixed. 220 | // This method applies and returns the actual mask chosen, from 0 to 7. 221 | int handleConstructorMasking(int mask); 222 | 223 | 224 | // Calculates and returns the penalty score based on state of this QR Code's current modules. 225 | // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. 226 | int getPenaltyScore() const; 227 | 228 | 229 | 230 | /*---- Private static helper functions ----*/ 231 | private: 232 | 233 | // Returns a set of positions of the alignment patterns in ascending order. These positions are 234 | // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). 235 | // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. 236 | static std::vector getAlignmentPatternPositions(int ver); 237 | 238 | 239 | // Returns the number of raw data modules (bits) available at the given version number. 240 | // These data modules are used for both user data codewords and error correction codewords. 241 | // This stateless pure function could be implemented as a 40-entry lookup table. 242 | static int getNumRawDataModules(int ver); 243 | 244 | 245 | // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any 246 | // QR Code of the given version number and error correction level, with remainder bits discarded. 247 | // This stateless pure function could be implemented as a (40*4)-cell lookup table. 248 | static int getNumDataCodewords(int ver, const Ecc &ecl); 249 | 250 | 251 | /*---- Private tables of constants ----*/ 252 | private: 253 | 254 | // For use in getPenaltyScore(), when evaluating which mask is best. 255 | static const int PENALTY_N1; 256 | static const int PENALTY_N2; 257 | static const int PENALTY_N3; 258 | static const int PENALTY_N4; 259 | 260 | static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4][41]; 261 | static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; 262 | 263 | 264 | 265 | /*---- Private helper class ----*/ 266 | private: 267 | 268 | /* 269 | * Computes the Reed-Solomon error correction codewords for a sequence of data codewords 270 | * at a given degree. Objects are immutable, and the state only depends on the degree. 271 | * This class exists because the divisor polynomial does not need to be recalculated for every input. 272 | */ 273 | class ReedSolomonGenerator { 274 | 275 | /*-- Immutable field --*/ 276 | private: 277 | 278 | // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which 279 | // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. 280 | std::vector coefficients; 281 | 282 | 283 | /*-- Constructor --*/ 284 | public: 285 | 286 | /* 287 | * Creates a Reed-Solomon ECC generator for the given degree. This could be implemented 288 | * as a lookup table over all possible parameter values, instead of as an algorithm. 289 | */ 290 | ReedSolomonGenerator(int degree); 291 | 292 | 293 | /*-- Method --*/ 294 | public: 295 | 296 | /* 297 | * Computes and returns the Reed-Solomon error correction codewords for the given sequence of data codewords. 298 | * The returned object is always a new byte array. This method does not alter this object's state (because it is immutable). 299 | */ 300 | std::vector getRemainder(const std::vector &data) const; 301 | 302 | 303 | /*-- Static function --*/ 304 | private: 305 | 306 | // Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result 307 | // are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8. 308 | static uint8_t multiply(uint8_t x, uint8_t y); 309 | 310 | }; 311 | 312 | }; 313 | 314 | } 315 | -------------------------------------------------------------------------------- /tests/QrSegment.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * QR Code generator library (C++) 3 | * 4 | * Copyright (c) Project Nayuki 5 | * https://www.nayuki.io/page/qr-code-generator-library 6 | * 7 | * (MIT License) 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * - The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * - The Software is provided "as is", without warranty of any kind, express or 17 | * implied, including but not limited to the warranties of merchantability, 18 | * fitness for a particular purpose and noninfringement. In no event shall the 19 | * authors or copyright holders be liable for any claim, damages or other 20 | * liability, whether in an action of contract, tort or otherwise, arising from, 21 | * out of or in connection with the Software or the use or other dealings in the 22 | * Software. 23 | */ 24 | 25 | #include 26 | #include "BitBuffer.hpp" 27 | #include "QrSegment.hpp" 28 | 29 | 30 | qrcodegen::QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : 31 | modeBits(mode) { 32 | numBitsCharCount[0] = cc0; 33 | numBitsCharCount[1] = cc1; 34 | numBitsCharCount[2] = cc2; 35 | } 36 | 37 | 38 | int qrcodegen::QrSegment::Mode::numCharCountBits(int ver) const { 39 | if ( 1 <= ver && ver <= 9) return numBitsCharCount[0]; 40 | else if (10 <= ver && ver <= 26) return numBitsCharCount[1]; 41 | else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; 42 | else throw "Version number out of range"; 43 | } 44 | 45 | 46 | const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); 47 | const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); 48 | const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::BYTE (0x4, 8, 16, 16); 49 | const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::KANJI (0x8, 8, 10, 12); 50 | 51 | 52 | 53 | qrcodegen::QrSegment qrcodegen::QrSegment::makeBytes(const std::vector &data) { 54 | return QrSegment(Mode::BYTE, data.size(), data, data.size() * 8); 55 | } 56 | 57 | 58 | qrcodegen::QrSegment qrcodegen::QrSegment::makeNumeric(const char *digits) { 59 | BitBuffer bb; 60 | int accumData = 0; 61 | int accumCount = 0; 62 | int charCount = 0; 63 | for (; *digits != '\0'; digits++, charCount++) { 64 | char c = *digits; 65 | if (c < '0' || c > '9') 66 | throw "String contains non-numeric characters"; 67 | accumData = accumData * 10 + (c - '0'); 68 | accumCount++; 69 | if (accumCount == 3) { 70 | bb.appendBits(accumData, 10); 71 | accumData = 0; 72 | accumCount = 0; 73 | } 74 | } 75 | if (accumCount > 0) // 1 or 2 digits remaining 76 | bb.appendBits(accumData, accumCount * 3 + 1); 77 | return QrSegment(Mode::NUMERIC, charCount, bb.getBytes(), bb.getBitLength()); 78 | } 79 | 80 | 81 | qrcodegen::QrSegment qrcodegen::QrSegment::makeAlphanumeric(const char *text) { 82 | BitBuffer bb; 83 | int accumData = 0; 84 | int accumCount = 0; 85 | int charCount = 0; 86 | for (; *text != '\0'; text++, charCount++) { 87 | char c = *text; 88 | if (c < ' ' || c > 'Z') 89 | throw "String contains unencodable characters in alphanumeric mode"; 90 | accumData = accumData * 45 + ALPHANUMERIC_ENCODING_TABLE[c - ' ']; 91 | accumCount++; 92 | if (accumCount == 2) { 93 | bb.appendBits(accumData, 11); 94 | accumData = 0; 95 | accumCount = 0; 96 | } 97 | } 98 | if (accumCount > 0) // 1 character remaining 99 | bb.appendBits(accumData, 6); 100 | return QrSegment(Mode::ALPHANUMERIC, charCount, bb.getBytes(), bb.getBitLength()); 101 | } 102 | 103 | 104 | std::vector qrcodegen::QrSegment::makeSegments(const char *text) { 105 | // Select the most efficient segment encoding automatically 106 | std::vector result; 107 | if (*text == '\0'); // Leave the vector empty 108 | else if (QrSegment::isNumeric(text)) 109 | result.push_back(QrSegment::makeNumeric(text)); 110 | else if (QrSegment::isAlphanumeric(text)) 111 | result.push_back(QrSegment::makeAlphanumeric(text)); 112 | else { 113 | std::vector bytes; 114 | for (; *text != '\0'; text++) 115 | bytes.push_back(static_cast(*text)); 116 | result.push_back(QrSegment::makeBytes(bytes)); 117 | } 118 | return result; 119 | } 120 | 121 | 122 | qrcodegen::QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &b, int bitLen) : 123 | mode(md), 124 | numChars(numCh), 125 | data(b), 126 | bitLength(bitLen) { 127 | if (numCh < 0 || bitLen < 0 || b.size() != static_cast((bitLen + 7) / 8)) 128 | throw "Invalid value"; 129 | } 130 | 131 | 132 | int qrcodegen::QrSegment::getTotalBits(const std::vector &segs, int version) { 133 | if (version < 1 || version > 40) 134 | throw "Version number out of range"; 135 | int result = 0; 136 | for (size_t i = 0; i < segs.size(); i++) { 137 | const QrSegment &seg(segs.at(i)); 138 | int ccbits = seg.mode.numCharCountBits(version); 139 | // Fail if segment length value doesn't fit in the length field's bit-width 140 | if (seg.numChars >= (1 << ccbits)) 141 | return -1; 142 | result += 4 + ccbits + seg.bitLength; 143 | } 144 | return result; 145 | } 146 | 147 | 148 | bool qrcodegen::QrSegment::isAlphanumeric(const char *text) { 149 | for (; *text != '\0'; text++) { 150 | char c = *text; 151 | if (c < ' ' || c > 'Z' || ALPHANUMERIC_ENCODING_TABLE[c - ' '] == -1) 152 | return false; 153 | } 154 | return true; 155 | } 156 | 157 | 158 | bool qrcodegen::QrSegment::isNumeric(const char *text) { 159 | for (; *text != '\0'; text++) { 160 | char c = *text; 161 | if (c < '0' || c > '9') 162 | return false; 163 | } 164 | return true; 165 | } 166 | 167 | 168 | const int8_t qrcodegen::QrSegment::ALPHANUMERIC_ENCODING_TABLE[59] = { 169 | // SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, // ASCII codes 32 to 64 170 | 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, // Array indices 0 to 32 171 | 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, // Array indices 33 to 58 172 | // A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, // ASCII codes 65 to 90 173 | }; 174 | -------------------------------------------------------------------------------- /tests/QrSegment.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * QR Code generator library (C++) 3 | * 4 | * Copyright (c) Project Nayuki 5 | * https://www.nayuki.io/page/qr-code-generator-library 6 | * 7 | * (MIT License) 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * - The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * - The Software is provided "as is", without warranty of any kind, express or 17 | * implied, including but not limited to the warranties of merchantability, 18 | * fitness for a particular purpose and noninfringement. In no event shall the 19 | * authors or copyright holders be liable for any claim, damages or other 20 | * liability, whether in an action of contract, tort or otherwise, arising from, 21 | * out of or in connection with the Software or the use or other dealings in the 22 | * Software. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include 28 | #include 29 | 30 | 31 | namespace qrcodegen { 32 | 33 | /* 34 | * Represents a character string to be encoded in a QR Code symbol. Each segment has 35 | * a mode, and a sequence of characters that is already encoded as a sequence of bits. 36 | * Instances of this class are immutable. 37 | * This segment class imposes no length restrictions, but QR Codes have restrictions. 38 | * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. 39 | * Any segment longer than this is meaningless for the purpose of generating QR Codes. 40 | */ 41 | class QrSegment { 42 | 43 | /*---- Public helper enumeration ----*/ 44 | 45 | /* 46 | * The mode field of a segment. Immutable. Provides methods to retrieve closely related values. 47 | */ 48 | public: 49 | class Mode { 50 | 51 | /*-- Constants --*/ 52 | public: 53 | 54 | static const Mode NUMERIC; 55 | static const Mode ALPHANUMERIC; 56 | static const Mode BYTE; 57 | static const Mode KANJI; 58 | 59 | 60 | /*-- Fields --*/ 61 | 62 | /* (Package-private) An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. */ 63 | public: 64 | const int modeBits; 65 | 66 | private: 67 | int numBitsCharCount[3]; 68 | 69 | 70 | /*-- Constructor --*/ 71 | 72 | private: 73 | Mode(int mode, int cc0, int cc1, int cc2); 74 | 75 | 76 | /*-- Method --*/ 77 | 78 | /* 79 | * (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. 80 | */ 81 | public: 82 | int numCharCountBits(int ver) const; 83 | 84 | }; 85 | 86 | 87 | 88 | /*---- Public static factory functions ----*/ 89 | public: 90 | 91 | /* 92 | * Returns a segment representing the given binary data encoded in byte mode. 93 | */ 94 | static QrSegment makeBytes(const std::vector &data); 95 | 96 | 97 | /* 98 | * Returns a segment representing the given string of decimal digits encoded in numeric mode. 99 | */ 100 | static QrSegment makeNumeric(const char *digits); 101 | 102 | 103 | /* 104 | * Returns a segment representing the given text string encoded in alphanumeric mode. The characters allowed are: 105 | * 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. 106 | */ 107 | static QrSegment makeAlphanumeric(const char *text); 108 | 109 | 110 | /* 111 | * Returns a list of zero or more segments to represent the given text string. 112 | * The result may use various segment modes and switch modes to optimize the length of the bit stream. 113 | */ 114 | static std::vector makeSegments(const char *text); 115 | 116 | 117 | /*---- Public static helper functions ----*/ 118 | public: 119 | 120 | /* 121 | * Tests whether the given string can be encoded as a segment in alphanumeric mode. 122 | */ 123 | static bool isAlphanumeric(const char *text); 124 | 125 | 126 | /* 127 | * Tests whether the given string can be encoded as a segment in numeric mode. 128 | */ 129 | static bool isNumeric(const char *text); 130 | 131 | 132 | 133 | /*---- Instance fields ----*/ 134 | public: 135 | 136 | /* The mode indicator for this segment. */ 137 | const Mode mode; 138 | 139 | /* The length of this segment's unencoded data, measured in characters. Always zero or positive. */ 140 | const int numChars; 141 | 142 | /* The bits of this segment packed into a byte array in big endian. */ 143 | const std::vector data; 144 | 145 | /* The length of this segment's encoded data, measured in bits. Satisfies ceil(bitLength / 8) = data.size(). */ 146 | const int bitLength; 147 | 148 | 149 | /*---- Constructor ----*/ 150 | public: 151 | 152 | /* 153 | * Creates a new QR Code data segment with the given parameters and data. 154 | */ 155 | QrSegment(const Mode &md, int numCh, const std::vector &b, int bitLen); 156 | 157 | 158 | // Package-private helper function. 159 | static int getTotalBits(const std::vector &segs, int version); 160 | 161 | 162 | /*---- Private constant ----*/ 163 | private: 164 | 165 | /* Maps shifted ASCII codes to alphanumeric mode character codes. */ 166 | static const int8_t ALPHANUMERIC_ENCODING_TABLE[59]; 167 | 168 | }; 169 | 170 | } 171 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | Testing 2 | ======= 3 | 4 | The testcases work by using the Nayuki QR code generating library, generating a QR code 5 | in both libraries and comparing them. 6 | 7 | Running 8 | ------- 9 | 10 | ``` 11 | ./run.sh 12 | ``` 13 | -------------------------------------------------------------------------------- /tests/run-tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../src/qrcode.h" 5 | #include "QrCode.hpp" 6 | 7 | static uint32_t check(const qrcodegen::QrCode &nayuki, QRCode *ricmoo) { 8 | uint32_t wrong = 0; 9 | 10 | if (nayuki.size != ricmoo->size) { wrong += (1 << 20); } 11 | 12 | int border = 4; 13 | for (int y = -border; y < nayuki.size + border; y++) { 14 | for (int x = -border; x < nayuki.size + border; x++) { 15 | if (!!nayuki.getModule(x, y) != qrcode_getModule(ricmoo, x, y)) { 16 | wrong++; 17 | } 18 | } 19 | } 20 | 21 | return wrong; 22 | } 23 | 24 | int main() { 25 | std::clock_t t0, totalNayuki, totalRicMoo; 26 | 27 | int total = 0, passed = 0; 28 | for (char version = 1; version <= 40; version++) { 29 | if (LOCK_VERSION != 0 && LOCK_VERSION != version) { continue; } 30 | 31 | for (char ecc = 0; ecc < 4; ecc++) { 32 | const qrcodegen::QrCode::Ecc *errCorLvl; 33 | switch (ecc) { 34 | case 0: 35 | errCorLvl = &qrcodegen::QrCode::Ecc::LOW; 36 | break; 37 | case 1: 38 | errCorLvl = &qrcodegen::QrCode::Ecc::MEDIUM; 39 | break; 40 | case 2: 41 | errCorLvl = &qrcodegen::QrCode::Ecc::QUARTILE; 42 | break; 43 | case 3: 44 | errCorLvl = &qrcodegen::QrCode::Ecc::HIGH; 45 | break; 46 | } 47 | 48 | for (char tc = 0; tc < 3; tc++) { 49 | char *data; 50 | switch(tc) { 51 | case 0: 52 | data = (char*)"HELLO"; 53 | break; 54 | case 1: 55 | data = (char*)"Hello"; 56 | break; 57 | case 2: 58 | data = (char*)"1234"; 59 | break; 60 | } 61 | t0 = std::clock(); 62 | const qrcodegen::QrCode nayuki = qrcodegen::QrCode::encodeText(data, version, *errCorLvl); 63 | totalNayuki += std::clock() - t0; 64 | 65 | t0 = std::clock(); 66 | QRCode ricmoo; 67 | uint8_t ricmooBytes[qrcode_getBufferSize(version)]; 68 | qrcode_initText(&ricmoo, ricmooBytes, version, ecc, data); 69 | totalRicMoo += std::clock() - t0; 70 | 71 | uint32_t badModules = check(nayuki, &ricmoo); 72 | if (badModules) { 73 | printf("Failed test case: version=%d, ecc=%d, data=\"%s\", faliured=%d\n", version, ecc, data, badModules); 74 | } else { 75 | passed++; 76 | } 77 | 78 | total++; 79 | } 80 | } 81 | } 82 | 83 | printf("Tests complete: %d passed (out of %d)\n", passed, total); 84 | printf("Timing: Nayuki=%lu, RicMoo=%lu\n", totalNayuki, totalRicMoo); 85 | } 86 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clang++ run-tests.cpp QrCode.cpp QrSegment.cpp BitBuffer.cpp ../src/qrcode.c -o test && ./test 4 | clang++ run-tests.cpp QrCode.cpp QrSegment.cpp BitBuffer.cpp ../src/qrcode.c -o test -D LOCK_VERSION=3 && ./test 5 | 6 | --------------------------------------------------------------------------------