1 /// Interleaved 2 of 5
2 module barcode.itf;
3 
4 import std.algorithm;
5 import std.exception;
6 import std..string;
7 import std.range;
8 import std.array;
9 import std.typecons : tuple;
10 import std.ascii;
11 import std.conv : to;
12 
13 import barcode.types;
14 import barcode.util;
15 
16 ///
17 class ITF : BarCodeEncoder
18 {
19 protected:
20 
21     // used flags of width lines and gaps: - (narow), # (wide)
22     // width controls in drawMask function (X and W enums)
23     enum start = bitsStr!"----";
24     enum stop = bitsStr!"#--";
25 
26     enum Bits!ubyte[10] modules = [
27         bitsStr!"--##-",
28         bitsStr!"#---#",
29         bitsStr!"-#--#",
30         bitsStr!"##---",
31         bitsStr!"--#-#",
32         bitsStr!"#-#--",
33         bitsStr!"-##--",
34         bitsStr!"---##",
35         bitsStr!"#--#-",
36         bitsStr!"-#-#-",
37     ];
38 
39 public:
40 pure:
41     ///
42     this(AppendCheckSum acs=AppendCheckSum.no) { appendCheckSum = acs; }
43 
44     ///
45     AppendCheckSum appendCheckSum;
46 
47     ///
48     override BarCode encode(string sdata)
49     {
50         enforce(sdata.all!isDigit, "all symbols must be a numbers");
51 
52         auto data = sdata.map!(a=>cast(ubyte)(a-'0')).array;
53 
54         if (appendCheckSum) data ~= checkSum(data);
55         if (data.length%2) data = [ubyte(0)] ~ data;
56 
57         assert (data.length%2 == 0);
58 
59         BitArray ret;
60         ret.addBits(start.drawMask); 
61 
62         for (auto i = 0; i < data.length; i+=2)
63             ret.addBits(combine(modules[data[i]], modules[data[i+1]]).drawMask);
64 
65         ret.addBits(stop.drawMask); 
66         return BarCode(ret.length, ret, "itf");
67     }
68 }
69 
70 private:
71 
72 Bits!ulong combine(A, B)(auto ref const Bits!A a, auto ref const Bits!B b)
73 {
74     enforce(a.count == b.count);
75 
76     uint val;
77 
78     foreach (i; 0..a.count)
79         val |= (b[i] | (a[i] << 1)) << (i*2);
80 
81     return Bits!ulong(a.count*2, val);
82 }
83 
84 unittest
85 {
86     assert (combine(bitsStr!"##", bitsStr!"--") == bitsStr!"#-#-");
87     assert (combine(bitsStr!"#-", bitsStr!"-#") == bitsStr!"#--#");
88     assert (combine(bitsStr!"#", bitsStr!"-") == bitsStr!"#-");
89 }
90 
91 ubyte checkSum(ubyte[] data) pure
92 {
93     uint a, b;
94 
95     foreach (i, ch; data)
96         if (i%2) a += ch;
97         else b += ch;
98 
99     if (data.length%2) b *= 3;
100     else a *= 3;
101 
102     return (10 - (a+b)%10)%10;
103 }
104 
105 unittest
106 {
107     assert(checkSum("1937".map!(a=>cast(ubyte)(a-'0')).array) == 8);
108 }
109 
110 Bits!ulong drawMask(T)(auto ref const Bits!T mask, bool bar=true)
111 {
112     enum X = 1;
113     enum W = 3;
114 
115     uint val, cur;
116 
117     bool black = !(bar ^ (mask.count%2));
118 
119     foreach (i; 0 .. mask.count)
120     {
121         foreach (k; 0 .. (mask[i] ? W : X))
122             val |= black << cur++;
123         black = !black;
124     }
125 
126     return Bits!ulong(cur, val);
127 }
128 
129 @system
130 unittest
131 {
132     immutable v1 = ITF.start.drawMask;
133     assert(v1 == bitsStr!"#-#-");
134     const v2 = ITF.stop.drawMask;
135     assert(v2 == bitsStr!"###-#");
136     auto v3 = ITF.stop.drawMask(false);
137     assert(v3 == bitsStr!"---#-");
138 }