1 ///
2 module barcode.code39;
3 
4 import std.experimental.logger;
5 
6 import std.exception;
7 import std..string;
8 import std.range;
9 import std.array;
10 import std.typecons : tuple;
11 
12 import barcode.types;
13 import barcode.util;
14 
15 ///
16 class Code39 : BarCodeEncoder
17 {
18 pure:
19     ///
20     this(AppendCheckSum acs=AppendCheckSum.no) { appendCheckSum = acs; }
21 
22     ///
23     AppendCheckSum appendCheckSum;
24 
25     ///
26     override BarCode encode(string str)
27     {
28         checkStr(str);
29 
30         BitArray ret;
31 
32         void append(char ch) { ret.addBits(table[ch]); }
33 
34         append('*'); // start
35 
36         ushort checkSum;
37 
38         foreach (char c; str)
39         {
40             append(c);
41             checkSum += checkVal[c];
42         }
43 
44         checkSum %= 43;
45 
46         if (appendCheckSum)
47             append(checkValInv[checkSum]);
48 
49         append('*'); // stop
50 
51         return BarCode(ret.length, ret, "code39");
52     }
53 }
54 
55 private:
56 
57 void checkStr(string str) pure @safe
58 {
59     foreach (char c; str)
60     {
61         enforce(c != '*', "symbol '*' is not allowed in code39");
62         enforce(c in table, "symbol '" ~ c ~ "' is not allowed in code39");
63     }
64 }
65 
66 Bits!uint drawMask()(auto ref const Bits!ushort mask)
67 {
68     enum X = 1;
69     enum W = 3;
70 
71     uint val;
72     uint cur = X; // past gap
73 
74     foreach (i; 0 .. mask.count)
75     {
76         auto black = (i%2) == 0;
77         auto n = mask[i] ? W : X;
78         foreach (k; 0 .. n)
79             val |= black << cur++;
80     }
81 
82     return Bits!uint(cur, val);
83 }
84 
85 @safe
86 unittest
87 {
88     auto v = drawMask(bitsStr!"--#--#-");
89     assert(v == bitsStr!"#-###-#---#-");
90 }
91 
92 enum Bits!uint[char]   table = src_table.getDict!((i,a) => tuple(a.ch, drawMask(a.mask)));
93 enum ushort[char]    checkVal = src_table.getDict!((i,a) => tuple(a.ch, i));
94 enum char[ushort] checkValInv = src_table.getDict!((i,a) => tuple(cast(ushort)i, a.ch));
95 
96 struct Sym { char ch; Bits!ushort mask; }
97 
98 @safe
99 unittest
100 {
101     //                               W=3
102     assert(table['0'] == bitsStr!"#-#---###-###-#-");
103 }
104 
105 enum src_table =
106 [   // used flags of width lines and gaps: - (narow), # (wide)
107     // width controls in drawMask function (X and W enums)
108     Sym('0', bitsStr!"---##-#--"),
109     Sym('1', bitsStr!"#--#----#"),
110     Sym('2', bitsStr!"--##----#"),
111     Sym('3', bitsStr!"#-##-----"),
112     Sym('4', bitsStr!"---##---#"),
113     Sym('5', bitsStr!"#--##----"),
114     Sym('6', bitsStr!"--###----"),
115     Sym('7', bitsStr!"---#--#-#"),
116     Sym('8', bitsStr!"#--#--#--"),
117     Sym('9', bitsStr!"--##--#--"),
118     Sym('A', bitsStr!"#----#--#"),
119     Sym('B', bitsStr!"--#--#--#"),
120     Sym('C', bitsStr!"#-#--#---"),
121     Sym('D', bitsStr!"----##--#"),
122     Sym('E', bitsStr!"#---##---"),
123     Sym('F', bitsStr!"--#-##---"),
124     Sym('G', bitsStr!"-----##-#"),
125     Sym('H', bitsStr!"#----##--"),
126     Sym('I', bitsStr!"--#--##--"),
127     Sym('J', bitsStr!"----###--"),
128     Sym('K', bitsStr!"#------##"),
129     Sym('L', bitsStr!"--#----##"),
130     Sym('M', bitsStr!"#-#----#-"),
131     Sym('N', bitsStr!"----#--##"),
132     Sym('O', bitsStr!"#---#--#-"),
133     Sym('P', bitsStr!"--#-#--#-"),
134     Sym('Q', bitsStr!"------###"),
135     Sym('R', bitsStr!"#-----##-"),
136     Sym('S', bitsStr!"--#---##-"),
137     Sym('T', bitsStr!"----#-##-"),
138     Sym('U', bitsStr!"##------#"),
139     Sym('V', bitsStr!"-##-----#"),
140     Sym('W', bitsStr!"###------"),
141     Sym('X', bitsStr!"-#--#---#"),
142     Sym('Y', bitsStr!"##--#----"),
143     Sym('Z', bitsStr!"-##-#----"),
144     Sym('-', bitsStr!"-#----#-#"),
145     Sym('.', bitsStr!"##----#--"),
146     Sym(' ', bitsStr!"-##---#--"),
147     Sym('$', bitsStr!"-#-#-#---"),
148     Sym('/', bitsStr!"-#-#---#-"),
149     Sym('+', bitsStr!"-#---#-#-"),
150     Sym('%', bitsStr!"---#-#-#-"),
151     Sym('*', bitsStr!"-#--#-#--")
152 ];