// Author: cirakg & claude ai // Summary: Code 128 encoder (sets B and C) with zxing's look-ahead rules for when to switch to digit pairs. // // Accepts printable ASCII (32-126). Set C packs two digits per symbol; the // encoder starts in C for leading digit pairs and switches to C for runs // of at least four digits, choosing the switch point so an odd digit is // encoded in B first (the rules of zxing's Code128Writer). zxing-cpp 3, // the test reference, writes through zint, which breaks ties between equally // short encodings differently; the tests require the same length (it was // equal for 5,000 random strings) and decoding. GS1-128 (FNC1) is deferred. import Toybox.Lang; module Code128 { // Bar/space widths of symbol values 0-105 (6 elements, starting with a // bar); STOP (106) has 7 elements. const PATTERNS = [ "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231", "113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121", "111323", "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131", "213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", "112214", "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212", "421112", "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214", "211232", "2331112" ]; // lint: allow-literal (published Code 128 symbol widths) const CODE_C = 99; const CODE_B = 100; const START_B = 104; const START_C = 105; const STOP = 106; // Look-ahead classes. const UNCODABLE = 0; // not a digit, or the end const ONE_DIGIT = 1; const TWO_DIGITS = 2; function encode(text as String) as Codes.LinearCode or Codes.CodeError { var chars = text.toCharArray(); if (chars.size() == 0) { return new Codes.CodeError("Code 128 needs some text"); } for (var i = 0; i < chars.size(); i++) { var c = chars[i].toNumber(); if (c < 32 || c > 126) { return new Codes.CodeError("Code 128: '" + chars[i] + "' (position " + (i + 1) + ") is not a printable ASCII character"); } } var values = [] as Array; var codeSet = 0; var position = 0; while (position < chars.size()) { var next = choose(chars, position, codeSet); var value = 0; if (next == codeSet) { if (codeSet == CODE_C) { value = (chars[position].toNumber() - 48) * 10 + chars[position + 1].toNumber() - 48; position += 2; } else { value = chars[position].toNumber() - 32; position += 1; } } else { value = codeSet == 0 ? (next == CODE_C ? START_C : START_B) : next; codeSet = next; } values.add(value); } // Checksum: start value plus each following value times its position. var sum = values[0]; for (var i = 1; i < values.size(); i++) { sum += values[i] * i; } values.add(sum % 103); values.add(STOP); var m = []b; for (var i = 0; i < values.size(); i++) { Codes.addRuns(m, widths(PATTERNS[values[i]] as String), true); } return new Codes.LinearCode(m, text); } function widths(pattern as String) as Array { var chars = pattern.toCharArray(); var out = [] as Array; for (var i = 0; i < chars.size(); i++) { out.add(chars[i].toNumber() - 48); } return out; } function cType(chars as Array, start as Number) as Number { if (start >= chars.size() || !isDigit(chars[start])) { return UNCODABLE; } if (start + 1 >= chars.size() || !isDigit(chars[start + 1])) { return ONE_DIGIT; } return TWO_DIGITS; } function isDigit(c as Char) as Boolean { return c >= '0' && c <= '9'; } // The code set for the symbol at `start` (zxing's chooseCode, without // set A and FNC1, which this encoder doesn't use). function choose(chars as Array, start as Number, oldCode as Number) as Number { var lookahead = cType(chars, start); if (lookahead != TWO_DIGITS) { return CODE_B; } if (oldCode == CODE_C) { return CODE_C; } if (oldCode == CODE_B) { lookahead = cType(chars, start + 2); if (lookahead != TWO_DIGITS) { return CODE_B; // fewer than 4 digits: not worth switching } var index = start + 4; lookahead = cType(chars, index); while (lookahead == TWO_DIGITS) { index += 2; lookahead = cType(chars, index); } // An odd number of digits: encode one in B, then switch. return lookahead == ONE_DIGIT ? CODE_B : CODE_C; } return CODE_C; // the first symbol: start in C for a digit pair } }