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 }