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];