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 @safe
85 unittest
86 {
87     assert (combine(bitsStr!"##", bitsStr!"--") == bitsStr!"#-#-");
88     assert (combine(bitsStr!"#-", bitsStr!"-#") == bitsStr!"#--#");
89     assert (combine(bitsStr!"#", bitsStr!"-") == bitsStr!"#-");
90 }
91 
92 ubyte checkSum(ubyte[] data) pure @safe
93 {
94     uint a, b;
95 
96     foreach (i, ch; data)
97         if (i%2) a += ch;
98         else b += ch;
99 
100     if (data.length%2) b *= 3;
101     else a *= 3;
102 
103     return (10 - (a+b)%10)%10;
104 }
105 
106 @safe
107 unittest
108 {
109     assert(checkSum("1937".map!(a=>cast(ubyte)(a-'0')).array) == 8);
110 }
111 
112 Bits!ulong drawMask(T)(auto ref const Bits!T mask, bool bar=true)
113 {
114     enum X = 1;
115     enum W = 3;
116 
117     uint val, cur;
118 
119     bool black = !(bar ^ (mask.count%2));
120 
121     foreach (i; 0 .. mask.count)
122     {
123         foreach (k; 0 .. (mask[i] ? W : X))
124             val |= black << cur++;
125         black = !black;
126     }
127 
128     return Bits!ulong(cur, val);
129 }
130 
131 @safe
132 unittest
133 {
134     immutable v1 = ITF.start.drawMask;
135     assert(v1 == bitsStr!"#-#-");
136     const v2 = ITF.stop.drawMask;
137     assert(v2 == bitsStr!"###-#");
138     auto v3 = ITF.stop.drawMask(false);
139     assert(v3 == bitsStr!"---#-");
140 }