1 module barcode.qr.qrcode; 2 3 import std.experimental.logger; 4 5 import std.math : abs; 6 import std.algorithm; 7 import std.exception; 8 import std..string; 9 import std.range; 10 import std.bitmanip : BitArray; 11 import std.array; 12 import std.traits : EnumMembers; 13 import std.typecons : Tuple, tuple; 14 15 import barcode.qr.qrsegment; 16 import barcode.qr.util; 17 import barcode.qr.ecl; 18 19 struct QrCode 20 { 21 pure @safe: 22 static QrCode encodeSegments(QrSegment[] segs, ECL ecl, 23 int minVer=1, int maxVer=40, int mask=-1, bool boostecl=true) 24 { 25 int sv = -1; 26 int usedbits; 27 foreach (v; minVer .. maxVer+1) 28 { 29 auto capasity = getNumDataCodewords(v, ecl) * 8; 30 usedbits = QrSegment.getTotalBits(segs, v); 31 if (usedbits != -1 && usedbits <= capasity) 32 { 33 sv = v; 34 break; 35 } 36 enforce(v != maxVer, new Exception("Too big data for qr")); 37 } 38 39 if (boostecl) 40 { 41 foreach (newecl; [ECL.medium, ECL.quartile, ECL.high]) 42 if (usedbits <= getNumDataCodewords(sv, newecl) * 8) 43 ecl = newecl; 44 } 45 46 auto capacitybits = getNumDataCodewords(sv, ecl) * 8; 47 48 BitBuffer bb; 49 foreach (i, seg; segs) 50 { 51 bb.appendBits(seg.mode.bits, 4); 52 bb.appendBits(seg.numChars, seg.mode.numCharCountBits(sv)); 53 bb.appendData(seg.data, seg.bitLength); 54 } 55 56 bb.appendBits(0, min(4, capacitybits - bb.length)); 57 bb.appendBits(0, (8 - bb.length % 8) % 8); 58 59 foreach (pad; [0xEC, 0x11].cycle) 60 { 61 if (bb.length >= capacitybits) break; 62 bb.appendBits(pad, 8); 63 } 64 65 assert (bb.length % 8 == 0); 66 67 return QrCode(bb, mask, sv, ecl); 68 } 69 70 int vers; 71 int size; 72 ECL ecl; 73 int mask; 74 75 BitArray modules; 76 BitArray isFunction; 77 78 size_t crd(int x, int y) const nothrow { return size * y + x; } 79 80 this(BitBuffer bb, int mask, uint vers, ECL ecl) @trusted 81 { 82 enforce(-1 <= mask && mask <= 7, "unknown mask"); 83 enforce(1 <= vers && vers <= 40, "unknown vers"); 84 enforce(bb.length == getNumDataCodewords(vers, ecl) * 8); 85 86 this.vers = vers; 87 this.ecl = ecl; 88 this.size = vers * 4 + 17; 89 90 modules.length = size*size; 91 isFunction.length = size*size; 92 93 drawFunctionPatterns(); 94 auto allcw = appendErrorCorrection(bb.getBytes); 95 drawCodewords(allcw); 96 97 if (mask == -1) 98 { 99 auto minPenalty = int.max; 100 foreach (i; 0 .. 8) 101 { 102 drawFormatBits(i); 103 applyMask(i); 104 int penalty = getPenaltyScore(); 105 if (penalty < minPenalty) 106 { 107 mask = i; 108 minPenalty = penalty; 109 } 110 applyMask(i); // undoes the mask due to XOR 111 } 112 } 113 114 assert (0 <= mask && mask <= 7); 115 116 drawFormatBits(mask); 117 applyMask(mask); 118 this.mask = mask; 119 } 120 121 void drawFunctionPatterns() 122 { 123 foreach (i; 0 .. size) 124 { 125 setFunctionModule(6, i, i % 2 == 0); 126 setFunctionModule(i, 6, i % 2 == 0); 127 } 128 129 drawFinderPattern(3, 3); 130 drawFinderPattern(size - 4, 3); 131 drawFinderPattern(3, size - 4); 132 133 auto alignPatPos = getAlignmentPatternPosition(vers); 134 auto n = cast(int)alignPatPos.length; 135 auto skips = [[0,0], [0, n-1], [n-1,0]]; 136 137 foreach (int i; 0 .. n) 138 foreach (int j; 0 .. n) 139 if (!skips.canFind([i,j])) 140 drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); 141 142 drawFormatBits(0); 143 drawVersion(); 144 } 145 146 void drawFormatBits(int mask) 147 { 148 auto data = ecl.formatBits << 3 | mask; 149 auto rem = data; 150 151 foreach (i; 0 .. 10) 152 rem = (rem << 1) ^ ((rem >> 9) * 0x537); 153 154 data = data << 10 | rem; 155 data ^= 0x5412; 156 assert (data >> 15 == 0); 157 158 bool checkI(int i) { return ((data >> i) & 1) != 0; } 159 160 foreach (i; 0 .. 6) 161 setFunctionModule(8, i, checkI(i)); 162 163 setFunctionModule(8, 7, checkI(6)); 164 setFunctionModule(8, 8, checkI(7)); 165 setFunctionModule(7, 8, checkI(8)); 166 167 foreach (i; 9 .. 15) 168 setFunctionModule(14 - i, 8, checkI(i)); 169 170 foreach (i; 0 .. 8) 171 setFunctionModule(size - 1 - i, 8, checkI(i)); 172 foreach (i; 8 .. 15) 173 setFunctionModule(8, size - 15 + i, checkI(i)); 174 175 setFunctionModule(8, size - 8, true); 176 } 177 178 void drawVersion() 179 { 180 if (vers < 7) return; 181 auto rem = vers; 182 foreach (i; 0 .. 12) 183 rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); 184 auto data = vers << 12 | rem; 185 assert (data >> 18 == 0); 186 187 foreach (i; 0 .. 18) 188 { 189 auto bit = ((data >> i) & 1) != 0; 190 auto a = size - 11 + i % 3, b = i / 3; 191 setFunctionModule(a, b, bit); 192 setFunctionModule(b, a, bit); 193 } 194 } 195 196 void drawFinderPattern(int x, int y) 197 { 198 foreach (i; -4 .. 5) 199 foreach (j; -4 .. 5) 200 { 201 auto dist = max(abs(i), abs(j)); 202 auto xx = x + j, yy = y + i; 203 204 if (0 <= xx && xx < size && 205 0 <= yy && yy < size) 206 setFunctionModule(xx, yy, dist != 2 && dist != 4); 207 } 208 } 209 210 void drawAlignmentPattern(int x, int y) 211 { 212 foreach (i; -2 .. 3) 213 foreach (j; -2 .. 3) 214 setFunctionModule(x+j, y+i, max(abs(i), abs(j)) != 1); 215 } 216 217 void setFunctionModule(int x, int y, bool isBlack) @trusted 218 { 219 modules[crd(x,y)] = isBlack; 220 isFunction[crd(x,y)] = true; 221 } 222 223 auto appendErrorCorrection(const(ubyte)[] data) const 224 { 225 assert (data.length == getNumDataCodewords(vers, ecl)); 226 227 auto nb = numErrorCorrectionBlocks[ecl][vers]; // numblocks 228 auto tc = numErrorCorrectionCodewords[ecl][vers]; // totalecc 229 230 assert (tc % nb == 0); 231 auto blen = tc / nb; // blockecclen 232 // numshortblocks 233 auto nsb = nb - getNumRawDataModules(vers) / 8 % nb; 234 // shortblocklen 235 auto sblen = getNumRawDataModules(vers) / 8 / nb; 236 237 ubyte[][] blocks; 238 auto rs = ReadSolomonGenerator(blen); 239 int k = 0; 240 241 foreach (i; 0 .. nb) 242 { 243 auto l = k+sblen-blen+(i<nsb?0:1); 244 auto dat = data[k..l]; 245 k += dat.length; 246 auto ecc = rs.getRemainder(dat); 247 if (i < nsb) dat ~= 0; 248 dat ~= ecc; 249 blocks ~= dat.dup; 250 } 251 252 ubyte[] res; 253 foreach (i; 0 .. blocks[0].length) 254 foreach(j, blk; blocks) 255 if (i != sblen - blen || j >= nsb) 256 res ~= blk[i]; 257 258 assert (res.length == getNumRawDataModules(vers) / 8); 259 260 return res; 261 } 262 263 void drawCodewords(const(ubyte)[] data) @trusted 264 { 265 size_t i = 0; 266 for (int right = size - 1; right >= 1; right -= 2) 267 { 268 if (right == 6) right = 5; 269 for (int vert = 0; vert < size; vert++) 270 { 271 for (int j = 0; j < 2; j++) 272 { 273 int x = right - j; // Actual x coordinate 274 bool upwards = ((right & 2) == 0) ^ (x < 6); 275 int y = upwards ? size - 1 - vert : vert; // Actual y coordinate 276 if (!isFunction[crd(x,y)] && i < data.length * 8) { 277 modules[crd(x,y)] = ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0; 278 i++; 279 } 280 } 281 } 282 } 283 284 assert (i == data.length*8); 285 } 286 287 void applyMask(int mask) @trusted 288 { 289 enforce(0 <= mask && mask <= 7, new Exception("unknown mask")); 290 291 auto masker = maskPatterns[mask]; 292 foreach (y; 0 .. size) 293 foreach (x; 0 .. size) 294 modules[crd(x,y)] = modules[crd(x,y)] ^ 295 ((masker(x, y) == 0) && 296 (!isFunction[crd(x,y)])); 297 } 298 299 enum maskPatterns = [ 300 (int x, int y) @safe @nogc pure nothrow { return (x + y) % 2; }, 301 (int x, int y) @safe @nogc pure nothrow { return y % 2; }, 302 (int x, int y) @safe @nogc pure nothrow { return x % 3; }, 303 (int x, int y) @safe @nogc pure nothrow { return (x + y) % 3; }, 304 (int x, int y) @safe @nogc pure nothrow { return (x / 3 + y / 2) % 2; }, 305 (int x, int y) @safe @nogc pure nothrow { return x * y % 2 + x * y % 3; }, 306 (int x, int y) @safe @nogc pure nothrow { return (x * y % 2 + x * y % 3) % 2; }, 307 (int x, int y) @safe @nogc pure nothrow { return ((x + y) % 2 + x * y % 3) % 2; } 308 ]; 309 310 int getPenaltyScore() @trusted 311 { 312 int res; 313 314 foreach (y; 0 .. size) 315 { 316 auto clrx = modules[crd(0,y)]; 317 auto runx = 1; 318 foreach (x; 1 .. size) 319 { 320 if (modules[crd(x,y)] != clrx) 321 { 322 clrx = modules[crd(x,y)]; 323 runx = 1; 324 } 325 else 326 { 327 runx++; 328 if (runx == 5) res += Penalty.N1; 329 else if (runx > 5) res++; 330 } 331 } 332 } 333 334 foreach (x; 0 .. size) 335 { 336 auto clry = modules[crd(x,0)]; 337 auto runy = 1; 338 foreach (y; 1 .. size) 339 { 340 if (modules[crd(x,y)] != clry) 341 { 342 clry = modules[crd(x,y)]; 343 runy = 1; 344 } 345 else 346 { 347 runy += 1; 348 if (runy == 5) res += Penalty.N1; 349 else if (runy > 5) res += 1; 350 } 351 } 352 } 353 354 foreach (y; 0 .. size-1) 355 foreach (x; 0 .. size-1) 356 if (modules[crd(x,y)] == modules[crd(x+1,y)] && 357 modules[crd(x,y)] == modules[crd(x,y+1)] && 358 modules[crd(x,y)] == modules[crd(x+1,y+1)]) 359 res += Penalty.N2; 360 361 foreach (y; 0 .. size) 362 { 363 auto bits = 0; 364 foreach (x; 0 .. size) 365 { 366 bits = ((bits << 1) & 0x7FF) | (modules[crd(x,y)] ? 1 : 0); 367 if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) 368 res += Penalty.N3; 369 } 370 } 371 372 foreach (x; 0 .. size) 373 { 374 auto bits = 0; 375 foreach (y; 0 .. size) 376 { 377 bits = ((bits << 1) & 0x7FF) | (modules[crd(x,y)] ? 1 : 0); 378 if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) 379 res += Penalty.N3; 380 } 381 } 382 383 int black; 384 foreach (i; 0..modules.length) if (modules[i]) black++; 385 auto total = size*size; 386 387 for (int k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) 388 res += Penalty.N4; 389 390 return res; 391 } 392 393 int[] getAlignmentPatternPosition(int ver) 394 { 395 enforce(1 <= ver && ver <= 40, "Version number out of range"); 396 397 if (ver == 1) return []; 398 399 int numAlign = ver / 7 + 2; 400 int step = ver == 32 ? 26 : 401 (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; 402 int[] res; 403 int sz = ver * 4 + 17; 404 for (int i = 0, pos = sz - 7; i < numAlign - 1; i++, pos -= step) 405 res = [pos] ~ res; 406 return [6] ~ res; 407 } 408 409 static int getNumRawDataModules(int ver) 410 { 411 enforce(1 <= ver && ver <= 40, "Version number out of range"); 412 413 int res = (16 * ver + 128) * ver + 64; 414 415 if (ver >= 2) { 416 int numAlign = ver / 7 + 2; 417 res -= (25 * numAlign - 10) * numAlign - 55; 418 if (ver >= 7) res -= 18 * 2; 419 } 420 421 return res; 422 } 423 424 static int getNumDataCodewords(int ver, ECL ecl) 425 { 426 enforce(1 <= ver && ver <= 40, "unknown version"); 427 return getNumRawDataModules(ver) / 8 428 - numErrorCorrectionCodewords[ecl][ver]; 429 } 430 431 enum Penalty 432 { 433 N1 = 3, 434 N2 = 3, 435 N3 = 40, 436 N4 = 10 437 } 438 439 enum numErrorCorrectionCodewords = [ 440 // Low 441 [-1, 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 442 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 443 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 444 480, 510, 540, 570, 570, 600, 630, 660, 720, 750], 445 446 // Medium 447 [-1, 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 448 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 449 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 450 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372], 451 452 // Quartile 453 [-1, 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 454 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 455 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 456 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040], 457 458 // High 459 [-1, 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 460 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 461 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 462 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430] 463 ]; 464 465 enum numErrorCorrectionBlocks = [ 466 [-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 467 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 468 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 469 16, 17, 18, 19, 19, 20, 21, 22, 24, 25], 470 471 [-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 472 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 473 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 474 31, 33, 35, 37, 38, 40, 43, 45, 47, 49], 475 476 [-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 477 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 478 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 479 43, 45, 48, 51, 53, 56, 59, 62, 65, 68], 480 481 [-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 482 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 483 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 484 51, 54, 57, 60, 63, 66, 70, 74, 77, 81] 485 ]; 486 487 static struct ReadSolomonGenerator 488 { 489 pure: 490 private: 491 ubyte[] coefficients; 492 493 static ubyte multiply(ubyte x, ubyte y) 494 { 495 // Russian peasant multiplication 496 int z = 0; 497 for (int i = 7; i >= 0; i--) 498 { 499 z = (z << 1) ^ ((z >> 7) * 0x11D); 500 z ^= ((y >> i) & 1) * x; 501 } 502 assert (z >> 8 == 0, "Assertion error"); 503 return cast(ubyte)z; 504 } 505 506 public: 507 508 this(int degree) 509 { 510 enforce(2 <= degree && degree <= 254, "Degree out of range"); 511 512 // Start with the monomial x^0 513 coefficients.length = degree; 514 coefficients[$-1] = 1; 515 516 // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), 517 // drop the highest term, and store the rest of the coefficients in order of descending powers. 518 // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). 519 int root = 1; 520 foreach (i; 0 .. degree) 521 { 522 // Multiply the current product by (x - r^i) 523 foreach (j, ref c; coefficients) 524 { 525 c = multiply(c, cast(ubyte)root); 526 if (j + 1 < coefficients.length) 527 c ^= coefficients[j+1]; 528 } 529 root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) 530 } 531 } 532 533 ubyte[] getRemainder(const(ubyte)[] data) const 534 { 535 // Compute the remainder by performing polynomial division 536 auto result = new ubyte[](coefficients.length);//coefficients.dup; 537 foreach (ref val; data) 538 { 539 ubyte factor = val ^ result.front; 540 result.popFront; 541 result ~= 0; 542 foreach (j; 0 .. result.length) 543 result[j] ^= multiply(coefficients[j], factor); 544 } 545 return result; 546 } 547 } 548 }