浏览代码

added 2 of 5 barcodes

boombuler 11 年之前
父节点
当前提交
bc131f9157
共有 2 个文件被更改,包括 183 次插入0 次删除
  1. 138 0
      twooffive/encoder.go
  2. 45 0
      twooffive/encoder_test.go

+ 138 - 0
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
+}

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