From bc131f9157a09aca5051966fcff3624c46637049 Mon Sep 17 00:00:00 2001 From: boombuler Date: Tue, 12 Aug 2014 20:29:52 +0200 Subject: [PATCH] added 2 of 5 barcodes --- twooffive/encoder.go | 138 ++++++++++++++++++++++++++++++++++++++ twooffive/encoder_test.go | 45 +++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 twooffive/encoder.go create mode 100644 twooffive/encoder_test.go diff --git a/twooffive/encoder.go b/twooffive/encoder.go new file mode 100644 index 0000000..ea16299 --- /dev/null +++ b/twooffive/encoder.go @@ -0,0 +1,138 @@ +// Package twooffive can create interleaved and standard "2 of 5" barcodes. +package twooffive + +import ( + "errors" + "fmt" + + "github.com/boombuler/barcode" + "github.com/boombuler/barcode/utils" +) + +const patternWidth = 5 + +type pattern [patternWidth]bool +type encodeInfo struct { + start []bool + end []bool + widths map[bool]int +} + +var ( + encodingTable = map[rune]pattern{ + '0': pattern{false, false, true, true, false}, + '1': pattern{true, false, false, false, true}, + '2': pattern{false, true, false, false, true}, + '3': pattern{true, true, false, false, false}, + '4': pattern{false, false, true, false, true}, + '5': pattern{true, false, true, false, false}, + '6': pattern{false, true, true, false, false}, + '7': pattern{false, false, false, true, true}, + '8': pattern{true, false, false, true, false}, + '9': pattern{false, true, false, true, false}, + } + + modes = map[bool]encodeInfo{ + false: encodeInfo{ // non-interleaved + start: []bool{true, true, false, true, true, false, true, false}, + end: []bool{true, true, false, true, false, true, true}, + widths: map[bool]int{ + true: 3, + false: 1, + }, + }, + true: encodeInfo{ // interleaved + start: []bool{true, false, true, false}, + end: []bool{true, true, false, true}, + widths: map[bool]int{ + true: 2, + false: 1, + }, + }, + } + nonInterleavedSpace = pattern{false, false, false, false, false} +) + +// AddCheckSum calculates the correct check-digit and appends it to the given content. +func AddCheckSum(content string) (string, error) { + if content == "" { + return "", errors.New("content is empty") + } + + even := len(content)%2 == 1 + sum := 0 + for _, r := range content { + if _, ok := encodingTable[r]; ok { + value := utils.RuneToInt(r) + if even { + sum += value * 3 + } else { + sum += value + } + even = !even + } else { + return "", fmt.Errorf("can not encode \"%s\"", content) + } + } + + return content + string(utils.IntToRune(sum%10)), nil +} + +// Encode creates a codabar barcode for the given content +func Encode(content string, interleaved bool) (barcode.Barcode, error) { + if content == "" { + return nil, errors.New("content is empty") + } + + if interleaved && len(content)%2 == 1 { + return nil, errors.New("can only encode even number of digits in interleaved mode") + } + + mode := modes[interleaved] + resBits := new(utils.BitList) + resBits.AddBit(mode.start...) + + var lastRune *rune + for _, r := range content { + var a, b pattern + if interleaved { + if lastRune == nil { + lastRune = new(rune) + *lastRune = r + continue + } else { + var o1, o2 bool + a, o1 = encodingTable[*lastRune] + b, o2 = encodingTable[r] + if !o1 || !o2 { + return nil, fmt.Errorf("can not encode \"%s\"", content) + } + lastRune = nil + } + } else { + var ok bool + a, ok = encodingTable[r] + if !ok { + return nil, fmt.Errorf("can not encode \"%s\"", content) + } + b = nonInterleavedSpace + } + + for i := 0; i < patternWidth; i++ { + for x := 0; x < mode.widths[a[i]]; x++ { + resBits.AddBit(true) + } + for x := 0; x < mode.widths[b[i]]; x++ { + resBits.AddBit(false) + } + } + } + + resBits.AddBit(mode.end...) + + kindTxt := "" + if interleaved { + kindTxt = " (interleaved)" + } + return utils.New1DCode("2 of 5"+kindTxt, content, resBits), nil +} diff --git a/twooffive/encoder_test.go b/twooffive/encoder_test.go new file mode 100644 index 0000000..007e5c6 --- /dev/null +++ b/twooffive/encoder_test.go @@ -0,0 +1,45 @@ +package twooffive + +import ( + "image/color" + "testing" +) + +func Test_AddCheckSum(t *testing.T) { + if sum, err := AddCheckSum("1234567"); err != nil || sum != "12345670" { + t.Fail() + } + if _, err := AddCheckSum("1ABC"); err == nil { + t.Fail() + } + if _, err := AddCheckSum(""); err == nil { + t.Fail() + } +} + +func Test_Encode(t *testing.T) { + _, err := Encode("FOOBAR", false) + if err == nil { + t.Error("\"FOOBAR\" should not be encodable") + } + + testEncode := func(interleaved bool, txt, testResult string) { + code, err := Encode(txt, interleaved) + if err != nil || code == nil { + t.Fail() + } else { + if code.Bounds().Max.X != len(testResult) { + t.Errorf("%v: length missmatch! %v != %v", txt, code.Bounds().Max.X, len(testResult)) + } else { + for i, r := range testResult { + if (code.At(i, 0) == color.Black) != (r == '1') { + t.Errorf("%v: code missmatch on position %d", txt, i) + } + } + } + } + } + + testEncode(false, "12345670", "1101101011101010101110101110101011101110111010101010101110101110111010111010101011101110101010101011101110101011101110101101011") + testEncode(true, "12345670", "1010110100101011001101101001010011010011001010101010011001101101") +}