From a59cf9ed0fd8c5394f6e0cbe97a8ef3e4fcd1b9c Mon Sep 17 00:00:00 2001
From: boombuler <boombuler@gmail.com>
Date: Sat, 3 Jun 2017 13:20:32 +0200
Subject: [PATCH] Added Code 93 support

---
 README.md              |   1 +
 code93/encoder.go      | 101 +++++++++++++++++++++++++++++++++++++++++
 code93/encoder_test.go |  39 ++++++++++++++++
 3 files changed, 141 insertions(+)
 create mode 100644 code93/encoder.go
 create mode 100644 code93/encoder_test.go

diff --git a/README.md b/README.md
index 81edd15..a100d8e 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@ This is a package for GO which can be used to create different types of barcodes
 * Aztec Code
 * Codabar
 * Code 128
+* Code 93
 * Code 39
 * EAN 8
 * EAN 13
diff --git a/code93/encoder.go b/code93/encoder.go
new file mode 100644
index 0000000..78613a5
--- /dev/null
+++ b/code93/encoder.go
@@ -0,0 +1,101 @@
+// Package code39 can create Code39 barcodes
+package code93
+
+import (
+	"errors"
+	"strings"
+
+	"github.com/boombuler/barcode"
+	"github.com/boombuler/barcode/utils"
+)
+
+type encodeInfo struct {
+	value int
+	data  int
+}
+
+const (
+	// Special Function 1 ($)
+	FNC1 = '\u00f1'
+	// Special Function 2 (%)
+	FNC2 = '\u00f2'
+	// Special Function 3 (/)
+	FNC3 = '\u00f3'
+	// Special Function 4 (+)
+	FNC4 = '\u00f4'
+)
+
+var encodeTable = map[rune]encodeInfo{
+	'0': encodeInfo{0, 0x114}, '1': encodeInfo{1, 0x148}, '2': encodeInfo{2, 0x144},
+	'3': encodeInfo{3, 0x142}, '4': encodeInfo{4, 0x128}, '5': encodeInfo{5, 0x124},
+	'6': encodeInfo{6, 0x122}, '7': encodeInfo{7, 0x150}, '8': encodeInfo{8, 0x112},
+	'9': encodeInfo{9, 0x10A}, 'A': encodeInfo{10, 0x1A8}, 'B': encodeInfo{11, 0x1A4},
+	'C': encodeInfo{12, 0x1A2}, 'D': encodeInfo{13, 0x194}, 'E': encodeInfo{14, 0x192},
+	'F': encodeInfo{15, 0x18A}, 'G': encodeInfo{16, 0x168}, 'H': encodeInfo{17, 0x164},
+	'I': encodeInfo{18, 0x162}, 'J': encodeInfo{19, 0x134}, 'K': encodeInfo{20, 0x11A},
+	'L': encodeInfo{21, 0x158}, 'M': encodeInfo{22, 0x14C}, 'N': encodeInfo{23, 0x146},
+	'O': encodeInfo{24, 0x12C}, 'P': encodeInfo{25, 0x116}, 'Q': encodeInfo{26, 0x1B4},
+	'R': encodeInfo{27, 0x1B2}, 'S': encodeInfo{28, 0x1AC}, 'T': encodeInfo{29, 0x1A6},
+	'U': encodeInfo{30, 0x196}, 'V': encodeInfo{31, 0x19A}, 'W': encodeInfo{32, 0x16C},
+	'X': encodeInfo{33, 0x166}, 'Y': encodeInfo{34, 0x136}, 'Z': encodeInfo{35, 0x13A},
+	'-': encodeInfo{36, 0x12E}, '.': encodeInfo{37, 0x1D4}, ' ': encodeInfo{38, 0x1D2},
+	'$': encodeInfo{39, 0x1CA}, '/': encodeInfo{40, 0x16E}, '+': encodeInfo{41, 0x176},
+	'%': encodeInfo{42, 0x1AE}, FNC1: encodeInfo{43, 0x126}, FNC2: encodeInfo{44, 0x1DA},
+	FNC3: encodeInfo{45, 0x1D6}, FNC4: encodeInfo{46, 0x132}, '*': encodeInfo{47, 0x15E},
+}
+
+// Encode returns a code93 barcode for the given content
+func Encode(content string) (barcode.Barcode, error) {
+	if strings.ContainsRune(content, '*') {
+		return nil, errors.New("invalid data! content may not contain '*'")
+	}
+
+	data := content + string(getChecksum(content, 20))
+	data += string(getChecksum(data, 15))
+
+	data = "*" + data + "*"
+	result := new(utils.BitList)
+
+	for _, r := range data {
+		info, ok := encodeTable[r]
+		if !ok {
+			return nil, errors.New("invalid data!")
+		}
+		result.AddBits(info.data, 9)
+	}
+	result.AddBit(true)
+
+	return utils.New1DCode("Code 93", content, result), nil
+}
+
+func reverse(value string) string {
+	data := []rune(value)
+	result := []rune{}
+	for i := len(data) - 1; i >= 0; i-- {
+		result = append(result, data[i])
+	}
+	return string(result)
+}
+
+func getChecksum(content string, maxWeight int) rune {
+	weight := 1
+	total := 0
+
+	for _, r := range reverse(content) {
+		info, ok := encodeTable[r]
+		if !ok {
+			return ' '
+		}
+		total += info.value * weight
+		if weight++; weight > maxWeight {
+			weight = 1
+		}
+	}
+	total = total % 47
+	for r, info := range encodeTable {
+		if info.value == total {
+			return r
+		}
+	}
+	return ' '
+}
diff --git a/code93/encoder_test.go b/code93/encoder_test.go
new file mode 100644
index 0000000..95ca827
--- /dev/null
+++ b/code93/encoder_test.go
@@ -0,0 +1,39 @@
+package code93
+
+import (
+	"image/color"
+	"testing"
+)
+
+func doTest(t *testing.T, data, testResult string) {
+	code, err := Encode(data)
+	if err != nil {
+		t.Error(err)
+	}
+	if len(testResult) != code.Bounds().Max.X {
+		t.Errorf("Invalid code size. Expected %d got %d", len(testResult), code.Bounds().Max.X)
+	}
+	for i, r := range testResult {
+		if (code.At(i, 0) == color.Black) != (r == '1') {
+			t.Errorf("Failed at position %d", i)
+		}
+	}
+}
+
+func Test_CheckSum(t *testing.T) {
+	if r := getChecksum("TEST93", 20); r != '+' {
+		t.Errorf("Checksum C-Failed. Got %s", string(r))
+	}
+	if r := getChecksum("TEST93+", 15); r != '6' {
+		t.Errorf("Checksum K-Failed. Got %s", string(r))
+	}
+}
+
+func Test_Encode(t *testing.T) {
+	doTest(t, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+		"1010111101101010001101001001101000101100101001100100101100010101011010001011001"+
+			"001011000101001101001000110101010110001010011001010001101001011001000101101101101001"+
+			"101100101101011001101001101100101101100110101011011001011001101001101101001110101000"+
+			"101001010010001010001001010000101001010001001001001001000101010100001000100101000010"+
+			"101001110101010000101010111101")
+}