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
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 unittest
86 {
87     auto v = drawMask(bitsStr!"--#--#-");
88     assert(v == bitsStr!"#-###-#---#-");
89 }
90 
91 enum Bits!uint[char]   table = src_table.getDict!((i,a) => tuple(a.ch, drawMask(a.mask)));
92 enum ushort[char]    checkVal = src_table.getDict!((i,a) => tuple(a.ch, i));
93 enum char[ushort] checkValInv = src_table.getDict!((i,a) => tuple(cast(ushort)i, a.ch));
94 
95 struct Sym { char ch; Bits!ushort mask; }
96 
97 unittest
98 {
99     //                               W=3
100     assert(table['0'] == bitsStr!"#-#---###-###-#-");
101 }
102 
103 enum src_table =
104 [   // used flags of width lines and gaps: - (narow), # (wide)
105     // width controls in drawMask function (X and W enums)
106     Sym('0', bitsStr!"---##-#--"),
107     Sym('1', bitsStr!"#--#----#"),
108     Sym('2', bitsStr!"--##----#"),
109     Sym('3', bitsStr!"#-##-----"),
110     Sym('4', bitsStr!"---##---#"),
111     Sym('5', bitsStr!"#--##----"),
112     Sym('6', bitsStr!"--###----"),
113     Sym('7', bitsStr!"---#--#-#"),
114     Sym('8', bitsStr!"#--#--#--"),
115     Sym('9', bitsStr!"--##--#--"),
116     Sym('A', bitsStr!"#----#--#"),
117     Sym('B', bitsStr!"--#--#--#"),
118     Sym('C', bitsStr!"#-#--#---"),
119     Sym('D', bitsStr!"----##--#"),
120     Sym('E', bitsStr!"#---##---"),
121     Sym('F', bitsStr!"--#-##---"),
122     Sym('G', bitsStr!"-----##-#"),
123     Sym('H', bitsStr!"#----##--"),
124     Sym('I', bitsStr!"--#--##--"),
125     Sym('J', bitsStr!"----###--"),
126     Sym('K', bitsStr!"#------##"),
127     Sym('L', bitsStr!"--#----##"),
128     Sym('M', bitsStr!"#-#----#-"),
129     Sym('N', bitsStr!"----#--##"),
130     Sym('O', bitsStr!"#---#--#-"),
131     Sym('P', bitsStr!"--#-#--#-"),
132     Sym('Q', bitsStr!"------###"),
133     Sym('R', bitsStr!"#-----##-"),
134     Sym('S', bitsStr!"--#---##-"),
135     Sym('T', bitsStr!"----#-##-"),
136     Sym('U', bitsStr!"##------#"),
137     Sym('V', bitsStr!"-##-----#"),
138     Sym('W', bitsStr!"###------"),
139     Sym('X', bitsStr!"-#--#---#"),
140     Sym('Y', bitsStr!"##--#----"),
141     Sym('Z', bitsStr!"-##-#----"),
142     Sym('-', bitsStr!"-#----#-#"),
143     Sym('.', bitsStr!"##----#--"),
144     Sym(' ', bitsStr!"-##---#--"),
145     Sym('$', bitsStr!"-#-#-#---"),
146     Sym('/', bitsStr!"-#-#---#-"),
147     Sym('+', bitsStr!"-#---#-#-"),
148     Sym('%', bitsStr!"---#-#-#-"),
149     Sym('*', bitsStr!"-#--#-#--")
150 ];