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