barcode/ean/encoder.go

198 lines
4.8 KiB
Go

// package for EAN barcode generation.
package ean
import (
"errors"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/utils"
)
type encodedNumber struct {
LeftOdd []bool
LeftEven []bool
Right []bool
CheckSum []bool
}
var encoderTable map[rune]encodedNumber = map[rune]encodedNumber{
'0': encodedNumber{
[]bool{false, false, false, true, true, false, true},
[]bool{false, true, false, false, true, true, true},
[]bool{true, true, true, false, false, true, false},
[]bool{false, false, false, false, false, false},
},
'1': encodedNumber{
[]bool{false, false, true, true, false, false, true},
[]bool{false, true, true, false, false, true, true},
[]bool{true, true, false, false, true, true, false},
[]bool{false, false, true, false, true, true},
},
'2': encodedNumber{
[]bool{false, false, true, false, false, true, true},
[]bool{false, false, true, true, false, true, true},
[]bool{true, true, false, true, true, false, false},
[]bool{false, false, true, true, false, true},
},
'3': encodedNumber{
[]bool{false, true, true, true, true, false, true},
[]bool{false, true, false, false, false, false, true},
[]bool{true, false, false, false, false, true, false},
[]bool{false, false, true, true, true, false},
},
'4': encodedNumber{
[]bool{false, true, false, false, false, true, true},
[]bool{false, false, true, true, true, false, true},
[]bool{true, false, true, true, true, false, false},
[]bool{false, true, false, false, true, true},
},
'5': encodedNumber{
[]bool{false, true, true, false, false, false, true},
[]bool{false, true, true, true, false, false, true},
[]bool{true, false, false, true, true, true, false},
[]bool{false, true, true, false, false, true},
},
'6': encodedNumber{
[]bool{false, true, false, true, true, true, true},
[]bool{false, false, false, false, true, false, true},
[]bool{true, false, true, false, false, false, false},
[]bool{false, true, true, true, false, false},
},
'7': encodedNumber{
[]bool{false, true, true, true, false, true, true},
[]bool{false, false, true, false, false, false, true},
[]bool{true, false, false, false, true, false, false},
[]bool{false, true, false, true, false, true},
},
'8': encodedNumber{
[]bool{false, true, true, false, true, true, true},
[]bool{false, false, false, true, false, false, true},
[]bool{true, false, false, true, false, false, false},
[]bool{false, true, false, true, true, false},
},
'9': encodedNumber{
[]bool{false, false, false, true, false, true, true},
[]bool{false, false, true, false, true, true, true},
[]bool{true, true, true, false, true, false, false},
[]bool{false, true, true, false, true, false},
},
}
func runeToInt(r rune) int {
if r >= '0' || r <= '9' {
return int(r - '0')
}
return -1
}
func intToRune(i int) rune {
if i >= 0 || i <= 9 {
return rune(i + '0')
}
return 'F'
}
func calcCheckNum(code string) rune {
x3 := len(code) == 7
sum := 0
for _, r := range code {
curNum := runeToInt(r)
if curNum < 0 || curNum > 9 {
return 'B'
}
if x3 {
curNum = curNum * 3
}
x3 = !x3
sum += curNum
}
return intToRune((10 - (sum % 10)) % 10)
}
func encodeEAN8(code string) *utils.BitList {
result := new(utils.BitList)
result.AddBit(true, false, true)
for cpos, r := range code {
num, ok := encoderTable[r]
if !ok {
return nil
}
var data []bool
if cpos < 4 {
data = num.LeftOdd
} else {
data = num.Right
}
if cpos == 4 {
result.AddBit(false, true, false, true, false)
}
result.AddBit(data...)
}
result.AddBit(true, false, true)
return result
}
func encodeEAN13(code string) *utils.BitList {
result := new(utils.BitList)
result.AddBit(true, false, true)
var firstNum []bool
for cpos, r := range code {
num, ok := encoderTable[r]
if !ok {
return nil
}
if cpos == 0 {
firstNum = num.CheckSum
continue
}
var data []bool
if cpos < 7 { // Left
if firstNum[cpos-1] {
data = num.LeftEven
} else {
data = num.LeftOdd
}
} else {
data = num.Right
}
if cpos == 7 {
result.AddBit(false, true, false, true, false)
}
result.AddBit(data...)
}
result.AddBit(true, false, true)
return result
}
// encodes the given EAN 8 or EAN 13 number to a barcode image
func Encode(code string) (barcode.Barcode, error) {
if len(code) == 7 || len(code) == 12 {
code += string(calcCheckNum(code))
} else if len(code) == 8 || len(code) == 13 {
check := code[0 : len(code)-1]
check += string(calcCheckNum(check))
if check != code {
return nil, errors.New("checksum missmatch!")
}
}
if len(code) == 8 {
result := encodeEAN8(code)
if result != nil {
return utils.New1DCode("EAN 8", code, result), nil
}
} else if len(code) == 13 {
result := encodeEAN13(code)
if result != nil {
return utils.New1DCode("EAN 13", code, result), nil
}
}
return nil, errors.New("invalid ean code data")
}