aboutsummaryrefslogtreecommitdiff
path: root/source/codes/Codes.mc
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-10-20 02:43:08 +0200
committercirakg <cirakg@cirakg.xyz>2026-10-20 02:43:08 +0200
commit776a563b97b99b054323dc06f02b4d374805777a (patch)
tree1d37e37cb401ceeb5a519ee36c036dab118f59e2 /source/codes/Codes.mc
parent0edb4bb6d76939aa456cbbc8deafeed50fd6182d (diff)
feat(codes): add EAN/UPC, Code 128, Code 39, ITF and Codabar encoders
Check digits added, verified, fixed or off; canonical UPC-E zero suppression; Code 128 sets B and C with zxing's switching rules; module rows match zxing-cpp (Code 128 by length, the tie-breaking differs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'source/codes/Codes.mc')
-rw-r--r--source/codes/Codes.mc149
1 files changed, 149 insertions, 0 deletions
diff --git a/source/codes/Codes.mc b/source/codes/Codes.mc
new file mode 100644
index 0000000..70c6c2d
--- /dev/null
+++ b/source/codes/Codes.mc
@@ -0,0 +1,149 @@
+// Author: cirakg & claude ai
+// Summary: Barcode symbology IDs, option bits, result types and the dispatcher to the encoders (README 12).
+//
+// A code entry stores its symbology in Entry.type and its options in
+// Entry.options (bits below). encode() turns the payload text into either a
+// LinearCode (one row of modules) or, from M6b, a matrix; validation errors
+// come back as a CodeError with a message for the user, never an exception.
+
+import Toybox.Lang;
+
+module Codes {
+
+ // Symbology IDs (Entry.type of code entries). Stored: never renumber.
+ const QR = 0;
+ const CODE128 = 1;
+ const CODE39 = 2;
+ const EAN13 = 3;
+ const EAN8 = 4;
+ const UPCA = 5;
+ const UPCE = 6;
+ const ITF = 7;
+ const CODABAR = 8;
+ const DATAMATRIX = 9;
+ const PDF417 = 10;
+ const AZTEC = 11;
+
+ const NAMES = ["QR", "Code 128", "Code 39", "EAN-13", "EAN-8", "UPC-A", "UPC-E", "ITF", "Codabar", "Data Matrix",
+ "PDF417", "Aztec"];
+
+ // Entry.options bits.
+ const OPT_EC_MASK = 0x3; // error correction: 0 low, 1 medium, 2 quartile, 3 high
+ const OPT_CHECK_SHIFT = 2; // check digit: 0 add or verify, 1 fix if wrong, 2 off
+ const OPT_CHECK_MASK = 0xC;
+ const OPT_SHOW_TEXT = 0x10; // human-readable text under linear codes
+ const DEFAULT_OPTIONS = 0x11; // medium, add or verify, text on
+
+ const CHECK_ADD_VERIFY = 0;
+ const CHECK_FIX = 1;
+ const CHECK_OFF = 2;
+
+ // A row of modules: modules[i] is 1 for a bar, 0 for a space. The quiet
+ // zone is not included (the renderer adds it).
+ class LinearCode {
+ var modules as ByteArray;
+ // The human-readable text (digits as encoded, check digit included).
+ var text as String;
+ // A remark for the user (check digit added or fixed), or null.
+ var note as String? = null;
+
+ function initialize(rowModules as ByteArray, readable as String) {
+ modules = rowModules;
+ text = readable;
+ }
+ }
+
+ // Why a payload can't be encoded, in words for the user.
+ class CodeError {
+ var message as String;
+
+ function initialize(msg as String) {
+ message = msg;
+ }
+ }
+
+ function checkMode(options as Number) as Number {
+ return (options & OPT_CHECK_MASK) >> OPT_CHECK_SHIFT;
+ }
+
+ function name(symbology as Number) as String {
+ return symbology >= 0 && symbology < NAMES.size() ? NAMES[symbology] as String : "Code";
+ }
+
+ function isLinear(symbology as Number) as Boolean {
+ return symbology >= CODE128 && symbology <= CODABAR;
+ }
+
+ // Encode a payload; unsupported symbologies give a CodeError until
+ // their milestone (README 16, M6b-M6d).
+ function encode(symbology as Number, payload as String, options as Number) as LinearCode or CodeError {
+ var check = checkMode(options);
+ if (symbology == EAN13) {
+ return Ean.ean13(payload, check, "EAN-13", false);
+ }
+ if (symbology == UPCA) {
+ return Ean.ean13(payload, check, "UPC-A", true);
+ }
+ if (symbology == EAN8) {
+ return Ean.ean8(payload, check);
+ }
+ if (symbology == UPCE) {
+ return Ean.upce(payload, check);
+ }
+ if (symbology == CODE128) {
+ return Code128.encode(payload);
+ }
+ if (symbology == CODE39) {
+ return Code39.encode(payload, check != CHECK_OFF);
+ }
+ if (symbology == ITF) {
+ return Itf.encode(payload, check != CHECK_OFF);
+ }
+ if (symbology == CODABAR) {
+ return Codabar.encode(payload);
+ }
+ return new CodeError(name(symbology) + " is not supported yet");
+ }
+
+ // Append bar/space runs to modules: widths alternate bar, space, ...,
+ // starting with a bar when startWithBar.
+ function addRuns(modules as ByteArray, widths as Array<Number>, startWithBar as Boolean) as Void {
+ var bar = startWithBar;
+ for (var i = 0; i < widths.size(); i++) {
+ var w = widths[i];
+ for (var k = 0; k < w; k++) {
+ modules.add(bar ? 1 : 0);
+ }
+ bar = !bar;
+ }
+ }
+
+ // Append a bit pattern of `count` modules, most significant bit first.
+ function addBits(modules as ByteArray, bits as Number, count as Number) as Void {
+ for (var i = count - 1; i >= 0; i--) {
+ modules.add((bits >> i) & 1);
+ }
+ }
+
+ // The digit values of text, or null when it holds anything else.
+ function digits(text as String) as Array<Number>? {
+ var chars = text.toCharArray();
+ var out = [] as Array<Number>;
+ for (var i = 0; i < chars.size(); i++) {
+ var c = chars[i];
+ if (c < '0' || c > '9') {
+ return null;
+ }
+ out.add(c.toNumber() - 48);
+ }
+ return out;
+ }
+
+ function digitsText(values as Array<Number>) as String {
+ var s = "";
+ for (var i = 0; i < values.size(); i++) {
+ s += values[i].toString();
+ }
+ return s;
+ }
+}