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") +}