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