1 ///
2 module barcode.code128;
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 Code128 : BarCodeEncoder
17 {
18     enum stopSymbol = bitsStr!"##---###-#-##";
19 
20     this() { }
21 
22     override BarCode encode(string str)
23     {
24         auto syms = parseStrToSymbol(str);
25         auto chsm = calcCheckSumm(syms);
26 
27         BitArray ret;
28 
29         foreach (s; syms)
30             ret.addBits(s.mask);
31 
32         ret.addBits(chsm.mask);
33         ret.addBits(stopSymbol);
34         return BarCode(ret.length, ret, "code128");
35     }
36 }
37 
38 private:
39 
40 struct Sym
41 {
42     size_t num;
43     string A, B, C;
44     Bits!ushort mask;
45 
46     this(string a, string b, string c, Bits!ushort bts) pure
47     {
48         A = a;
49         B = b;
50         C = c;
51         mask = bts;
52     }
53 }
54 
55 Sym symByNum(size_t num) pure { return src_table[num]; }
56 
57 Sym symByA(const(char)[] ch...) { return src_table[sym_by_a[ch.idup]]; }
58 Sym symByB(const(char)[] ch...) { return src_table[sym_by_b[ch.idup]]; }
59 Sym symByC(const(char)[] ch...) { return src_table[sym_by_c[ch.idup]]; }
60 
61 unittest
62 {
63     assert(symByA("E") == symByB("E"));
64     char[] tmp = ['E'];
65     assert(symByB(tmp) == symByB("E"));
66 }
67 
68 enum size_t[string] sym_by_a = src_table.getDict!((i,v) => tuple(v.A, i));
69 enum size_t[string] sym_by_b = src_table.getDict!((i,v) => tuple(v.B, i));
70 enum size_t[string] sym_by_c = src_table.getDict!((i,v) => tuple(v.C, i));
71 
72 Sym[] parseStrToSymbol(string str, int state=0)
73 {
74     import std.algorithm;
75     import std.ascii;
76 
77     enforce(0 <= state && state <= 3, "unknown last state");
78 
79     Sym[] setA()
80     {
81         auto ret = [0: [symByA(StartA)],
82                     1: [/+ already in A mode +/],
83                     2: [symByB(CODE_A)],
84                     3: [symByC(CODE_A)]][state];
85         state = 1;
86         return ret;
87     }
88 
89     Sym[] setB()
90     {
91         auto ret = [0: [symByA(StartB)],
92                     1: [symByA(CODE_B)],
93                     2: [/+ already in B mode +/],
94                     3: [symByC(CODE_B)]][state];
95         state = 2;
96         return ret;
97     }
98 
99     Sym[] setC()
100     {
101         auto ret = [0: [symByA(StartC)],
102                     1: [symByA(CODE_C)],
103                     2: [symByB(CODE_C)],
104                     3: [/+ already in C +/]][state];
105         state = 3;
106         return ret;
107     }
108 
109     Sym[] encA(const(char)[] ch...) { return setA ~ symByA(ch); }
110     Sym[] encB(const(char)[] ch...) { return setB ~ symByB(ch); }
111     Sym[] encC(const(char)[] ch...) { return setC ~ symByC(ch); }
112 
113     Sym[] encState(const(char)[] ch...)
114     {
115         if (state == 1) return [symByA(ch)];
116         else return setB ~ symByB(ch);
117     }
118 
119     Sym[] encShift(const(char)[] ch...)
120     {
121              if (state == 1) return [symByA(Shift)] ~ symByB(ch);
122         else if (state == 2) return [symByB(Shift)] ~ symByA(ch);
123         else assert(0, "logic error in code");
124     }
125 
126     Sym[] encSwitch(const(char)[] ch...)
127     { return [1: encB(ch), 2: encA(ch)][state]; }
128 
129     bool isOtherSpec(const(char)[] ch...)
130     {
131              if (state == 1) return SpecB.canFind(ch);
132         else if (state == 2) return SpecA.canFind(ch);
133         else return false;
134     }
135 
136     bool isSpec(const(char)[] ch...)
137     {
138              if (state == 1) return SpecA.canFind(ch);
139         else if (state == 2) return SpecB.canFind(ch);
140         else return false;
141     }
142 
143     Sym[] ret;
144 
145     while (str.length)
146     {
147         auto dc = str.digitsSequenceCount;
148         if (dc >= 4 && (state == 0 || (dc % 2)==0))
149         {
150             while (str.digitsSequenceCount >= 2)
151             {
152                 ret ~= encC(str[0..2]);
153                 str = str[2..$];
154             }
155         }
156         else
157         {
158             auto ch = str[0];
159             auto sAc = str.specACount, sBc = str.specBCount;
160                  if (!sAc && !sBc) ret ~= encState(ch);
161             else if ( sAc && !sBc) ret ~= encA(ch);
162             else if ( sBc && !sAc) ret ~= encB(ch);
163             else if (str.length >= 2) // if str.length == 1 one of first 3 statements are was true
164             {
165                 if (isOtherSpec(ch))
166                 {
167                     if (isSpec(str[1])) ret ~= encShift(ch);
168                     else ret ~= encSwitch(ch);
169                 }
170                 else
171                     ret ~= encState(ch);
172             }
173             else assert(0, "logic error in code");
174 
175             str = str[1..$];
176         }
177     }
178 
179     return ret;
180 }
181 
182 unittest
183 {
184     import std.algorithm : equal;
185     import std.conv : text;
186 
187     {
188         auto g = parseStrToSymbol("EIA50-1234-123456");
189         auto m = getSymbol(StartB, "E", "I", "A", "5", "0",
190                     "-", CODE_C, "12", "34",
191                     CODE_B, "-", CODE_C, "12", "34", "56");
192         assert(equal(m, g));
193     }
194     {
195         auto g = parseStrToSymbol("EIA50-12345-1234567");
196         auto m = getSymbol(StartB, "E", "I", "A", "5", "0",
197                     "-", "1", CODE_C, "23", "45",
198                     CODE_B, "-", "1", CODE_C, "23", "45", "67");
199         assert(equal(m, g), text("\n", m, "\n", g));
200     }
201     {
202         auto g = parseStrToSymbol("oneOFthis\0ABC");
203         auto m = getSymbol(StartB, "o", "n", "e", "O", "F", "t", "h", "i", "s",
204                            CODE_A, NUL, "A", "B", "C");
205         assert(equal(m, g), text("\n", m, "\n", g));
206     }
207 }
208 
209 size_t digitsSequenceCount(string str)
210 {
211     import std.ascii;
212     foreach (i; 0 .. str.length)
213         if (!str[i].isDigit)
214             return i;
215     return str.length;
216 }
217 
218 unittest
219 {
220     assert("0123".digitsSequenceCount == 4);
221     assert("01ab23".digitsSequenceCount == 2);
222     assert("0431ab23".digitsSequenceCount == 4);
223     assert("ab0431ab23".digitsSequenceCount == 0);
224 }
225 
226 size_t specACount(string str)
227 {
228     import std.algorithm;
229     size_t ret;
230     foreach (i; 0 .. str.length)
231         if (SpecA.canFind(""~str[i]))
232             ret++;
233     return ret;
234 }
235 
236 size_t specBCount(string str)
237 {
238     import std.algorithm;
239     size_t ret;
240     foreach (i; 0 .. str.length)
241         if (SpecB.canFind(""~str[i]))
242             ret++;
243     return ret;
244 }
245 
246 Sym calcCheckSumm(Sym[] symbol) pure
247 {
248     enforce(symbol.length >= 1);
249 
250     size_t tmp = symbol[0].num;
251     foreach(i, sym; symbol)
252         tmp += sym.num * i; // first in tmp yet
253     tmp %= 103;
254     return symByNum(tmp);
255 }
256 
257 unittest
258 {
259     auto arr = [StartB, "A", "I", "M", CODE_C, "12", "34"];
260     assert(calcCheckSumm(getSymbol(arr)).num == 87);
261 }
262 
263 Sym[] getSymbol(string[] arr...)
264 {
265     enum State { A,B,C }
266     State curr;
267     switch (arr[0])
268     {
269         case StartA: curr = State.A; break;
270         case StartB: curr = State.B; break;
271         case StartC: curr = State.C; break;
272         default: throw new Exception("arr mush starts from StartX symbol");
273     }
274     bool shift;
275     Sym symByCurr(string v)
276     {
277         final switch (curr)
278         {
279             case State.A: return shift ? symByB(v) : symByA(v);
280             case State.B: return shift ? symByA(v) : symByB(v);
281             case State.C: return symByC(v);
282         }
283     }
284 
285     Sym[] ret = [symByA(arr[0])];
286 
287     foreach (v; arr[1..$])
288     {
289         ret ~= symByCurr(v);
290         shift = false;
291         switch (v)
292         {
293             case CODE_A: curr = State.A; break;
294             case CODE_B: curr = State.B; break;
295             case CODE_C: curr = State.C; break;
296             case Shift: shift = true; break;
297             default: break;
298         }
299     }
300 
301     return ret;
302 }
303 
304 auto setNum(Sym[] tbl) { foreach (i, ref v; tbl) v.num = i; return tbl; }
305 
306 enum src_table =
307 [   // used origin mask: # (black), - (white)
308     Sym(   ` `,    ` `,   "00", bitsStr!"##-##--##--"),
309     Sym(   `!`,    `!`,   "01", bitsStr!"##--##-##--"),
310     Sym(   `"`,    `"`,   "02", bitsStr!"##--##--##-"),
311     Sym(   `#`,    `#`,   "03", bitsStr!"#--#--##---"),
312     Sym(   `$`,    `$`,   "04", bitsStr!"#--#---##--"),
313     Sym(   `%`,    `%`,   "05", bitsStr!"#---#--##--"),
314     Sym(   `&`,    `&`,   "06", bitsStr!"#--##--#---"),
315     Sym(   `'`,    `'`,   "07", bitsStr!"#--##---#--"),
316     Sym(   `(`,    `(`,   "08", bitsStr!"#---##--#--"),
317     Sym(   `)`,    `)`,   "09", bitsStr!"##--#--#---"),
318     Sym(   `*`,    `*`,   "10", bitsStr!"##--#---#--"),
319     Sym(   `+`,    `+`,   "11", bitsStr!"##---#--#--"),
320     Sym(   `,`,    `,`,   "12", bitsStr!"#-##--###--"),
321     Sym(   `-`,    `-`,   "13", bitsStr!"#--##-###--"),
322     Sym(   `.`,    `.`,   "14", bitsStr!"#--##--###-"),
323     Sym(   `/`,    `/`,   "15", bitsStr!"#-###--##--"),
324     Sym(   `0`,    `0`,   "16", bitsStr!"#--###-##--"),
325     Sym(   `1`,    `1`,   "17", bitsStr!"#--###--##-"),
326     Sym(   `2`,    `2`,   "18", bitsStr!"##--###--#-"),
327     Sym(   `3`,    `3`,   "19", bitsStr!"##--#-###--"),
328     Sym(   `4`,    `4`,   "20", bitsStr!"##--#--###-"),
329     Sym(   `5`,    `5`,   "21", bitsStr!"##-###--#--"),
330     Sym(   `6`,    `6`,   "22", bitsStr!"##--###-#--"),
331     Sym(   `7`,    `7`,   "23", bitsStr!"###-##-###-"),
332     Sym(   `8`,    `8`,   "24", bitsStr!"###-#--##--"),
333     Sym(   `9`,    `9`,   "25", bitsStr!"###--#-##--"),
334     Sym(   `:`,    `:`,   "26", bitsStr!"###--#--##-"),
335     Sym(   `;`,    `;`,   "27", bitsStr!"###-##--#--"),
336     Sym(   `<`,    `<`,   "28", bitsStr!"###--##-#--"),
337     Sym(   `=`,    `=`,   "29", bitsStr!"###--##--#-"),
338     Sym(   `>`,    `>`,   "30", bitsStr!"##-##-##---"),
339     Sym(   `?`,    `?`,   "31", bitsStr!"##-##---##-"),
340     Sym(   `@`,    `@`,   "32", bitsStr!"##---##-##-"),
341     Sym(   `A`,    `A`,   "33", bitsStr!"#-#---##---"),
342     Sym(   `B`,    `B`,   "34", bitsStr!"#---#-##---"),
343     Sym(   `C`,    `C`,   "35", bitsStr!"#---#---##-"),
344     Sym(   `D`,    `D`,   "36", bitsStr!"#-##---#---"),
345     Sym(   `E`,    `E`,   "37", bitsStr!"#---##-#---"),
346     Sym(   `F`,    `F`,   "38", bitsStr!"#---##---#-"),
347 
348     Sym(   `G`,    `G`,   "39", bitsStr!"##-#---#---"),
349     Sym(   `H`,    `H`,   "40", bitsStr!"##---#-#---"),
350     Sym(   `I`,    `I`,   "41", bitsStr!"##---#---#-"),
351     Sym(   `J`,    `J`,   "42", bitsStr!"#-##-###---"),
352     Sym(   `K`,    `K`,   "43", bitsStr!"#-##---###-"),
353     Sym(   `L`,    `L`,   "44", bitsStr!"#---##-###-"),
354     Sym(   `M`,    `M`,   "45", bitsStr!"#-###-##---"),
355     Sym(   `N`,    `N`,   "46", bitsStr!"#-###---##-"),
356     Sym(   `O`,    `O`,   "47", bitsStr!"#---###-##-"),
357     Sym(   `P`,    `P`,   "48", bitsStr!"###-###-##-"),
358     Sym(   `Q`,    `Q`,   "49", bitsStr!"##-#---###-"),
359     Sym(   `R`,    `R`,   "50", bitsStr!"##---#-###-"),
360     Sym(   `S`,    `S`,   "51", bitsStr!"##-###-#---"),
361     Sym(   `T`,    `T`,   "52", bitsStr!"##-###---#-"),
362     Sym(   `U`,    `U`,   "53", bitsStr!"##-###-###-"),
363     Sym(   `V`,    `V`,   "54", bitsStr!"###-#-##---"),
364     Sym(   `W`,    `W`,   "55", bitsStr!"###-#---##-"),
365     Sym(   `X`,    `X`,   "56", bitsStr!"###---#-##-"),
366     Sym(   `Y`,    `Y`,   "57", bitsStr!"###-##-#---"),
367     Sym(   `Z`,    `Z`,   "58", bitsStr!"###-##---#-"),
368     Sym(   `[`,    `[`,   "59", bitsStr!"###---##-#-"),
369     Sym(   `\`,    `\`,   "60", bitsStr!"###-####-#-"),
370     Sym(   `]`,    `]`,   "61", bitsStr!"##--#----#-"),
371     Sym(   `^`,    `^`,   "62", bitsStr!"####---#-#-"),
372     Sym(   `_`,    `_`,   "63", bitsStr!"#-#--##----"),
373     Sym(   NUL,    "`",   "64", bitsStr!"#-#----##--"),
374     Sym(   SOH,    "a",   "65", bitsStr!"#--#-##----"),
375     Sym(   STX,    "b",   "66", bitsStr!"#--#----##-"),
376     Sym(   ETX,    "c",   "67", bitsStr!"#----#-##--"),
377     Sym(   EOT,    "d",   "68", bitsStr!"#----#--##-"),
378     Sym(   ENQ,    "e",   "69", bitsStr!"#-##--#----"),
379     Sym(   ACK,    "f",   "70", bitsStr!"#-##----#--"),
380     Sym(   BEL,    "g",   "71", bitsStr!"#--##-#----"),
381     Sym(    BS,    "h",   "72", bitsStr!"#--##----#-"),
382     Sym(    HT,    "i",   "73", bitsStr!"#----##-#--"),
383     Sym(    LF,    "j",   "74", bitsStr!"#----##--#-"),
384     Sym(    VT,    "k",   "75", bitsStr!"##----#--#-"),
385     Sym(    FF,    "l",   "76", bitsStr!"##--#-#----"),
386     Sym(    CR,    "m",   "77", bitsStr!"####-###-#-"),
387     Sym(    SO,    "n",   "78", bitsStr!"##----#-#--"),
388     Sym(    SI,    "o",   "79", bitsStr!"#---####-#-"),
389 
390     Sym(   DLE,    "p",   "80", bitsStr!"#-#--####--"),
391     Sym(   DC1,    "q",   "81", bitsStr!"#--#-####--"),
392     Sym(   DC2,    "r",   "82", bitsStr!"#--#--####-"),
393     Sym(   DC3,    "s",   "83", bitsStr!"#-####--#--"),
394     Sym(   DC4,    "t",   "84", bitsStr!"#--####-#--"),
395     Sym(   NAK,    "u",   "85", bitsStr!"#--####--#-"),
396     Sym(   SYN,    "v",   "86", bitsStr!"####-#--#--"),
397     Sym(   ETB,    "w",   "87", bitsStr!"####--#-#--"),
398     Sym(   CAN,    "x",   "88", bitsStr!"####--#--#-"),
399     Sym(    EM,    "y",   "89", bitsStr!"##-##-####-"),
400     Sym(   SUB,    "z",   "90", bitsStr!"##-####-##-"),
401     Sym(   ESC,    "{",   "91", bitsStr!"####-##-##-"),
402     Sym(    FS,    "|",   "92", bitsStr!"#-#-####---"),
403     Sym(    GS,    "}",   "93", bitsStr!"#-#---####-"),
404     Sym(    RS,    "~",   "94", bitsStr!"#---#-####-"),
405     Sym(    US,    DEL,   "95", bitsStr!"#-####-#---"),
406     Sym(  FNC3,   FNC3,   "96", bitsStr!"#-####---#-"),
407     Sym(  FNC2,   FNC2,   "97", bitsStr!"####-#-#---"),
408     Sym( Shift,  Shift,   "98", bitsStr!"####-#---#-"),
409     Sym(CODE_C, CODE_C,   "99", bitsStr!"#-###-####-"),
410     Sym(CODE_B,   FNC4, CODE_B, bitsStr!"#-####-###-"),
411     Sym(  FNC4, CODE_A, CODE_A, bitsStr!"###-#-####-"),
412     Sym(  FNC1,   FNC1,   FNC1, bitsStr!"####-#-###-"),
413     Sym(StartA, StartA, StartA, bitsStr!"##-#----#--"),
414     Sym(StartB, StartB, StartB, bitsStr!"##-#--#----"),
415     Sym(StartC, StartC, StartC, bitsStr!"##-#--###--"),
416 ].setNum;
417 
418 unittest
419 {
420     assert(symByB(StartB).num == 104);
421     assert(symByB("A").num == 33);
422     assert(symByB("I").num == 41);
423     assert(symByB("M").num == 45);
424     assert(symByB(CODE_C).num == 99);
425     assert(symByC("12").num == 12);
426     assert(symByC("34").num == 34);
427 
428     assert(symByB(CODE_A).num == 101);
429 
430     assert(symByA(FS) == symByB("|"));
431     assert(symByA(FS) == symByC("92"));
432 }
433 
434 string chSym(ubyte v) { return "" ~ cast(char)(v); }
435 
436 enum NUL = chSym( 0);
437 enum SOH = chSym( 1);
438 enum STX = chSym( 2);
439 enum ETX = chSym( 3);
440 enum EOT = chSym( 4);
441 enum ENQ = chSym( 5);
442 enum ACK = chSym( 6);
443 enum BEL = chSym( 7);
444 enum  BS = chSym( 8);
445 enum  HT = chSym( 9);
446 enum  LF = chSym(10);
447 enum  VT = chSym(11);
448 enum  FF = chSym(12);
449 enum  CR = chSym(13);
450 enum  SO = chSym(14);
451 enum  SI = chSym(15);
452 
453 enum DLE = chSym(16);
454 enum DC1 = chSym(17);
455 enum DC2 = chSym(18);
456 enum DC3 = chSym(19);
457 enum DC4 = chSym(20);
458 enum NAK = chSym(21);
459 enum SYN = chSym(22);
460 enum ETB = chSym(23);
461 enum CAN = chSym(24);
462 enum  EM = chSym(25);
463 enum SUB = chSym(26);
464 enum ESC = chSym(27);
465 enum  FS = chSym(28);
466 enum  GS = chSym(29);
467 enum  RS = chSym(30);
468 enum  US = chSym(31);
469 enum DEL = chSym(127);
470 
471 enum   FNC1 = NUL ~ "!FNC1";
472 enum   FNC2 = NUL ~ "!FNC2";
473 enum   FNC3 = NUL ~ "!FNC3";
474 enum   FNC4 = NUL ~ "!FNC4";
475 enum  Shift = NUL ~ "!Shift";
476 enum CODE_A = NUL ~ "!CODE_A";
477 enum CODE_B = NUL ~ "!CODE_B";
478 enum CODE_C = NUL ~ "!CODE_C";
479 enum StartA = NUL ~ "!StartA";
480 enum StartB = NUL ~ "!StartB";
481 enum StartC = NUL ~ "!StartC";
482 
483 enum SpecA = [ NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, BS, HT, LF, VT, FF,
484                CR, SO, SI, DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB, CAN,
485                EM, SUB, ESC, FS, GS, RS, US ];
486 
487 enum SpecB = ["`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l",
488               "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y",
489               "z", "{", "|", "}", "~", DEL];