barcode/ean/encoder.go

236 lines
5.2 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 {
switch r {
case '0':
return 0
case '1':
return 1
case '2':
return 2
case '3':
return 3
case '4':
return 4
case '5':
return 5
case '6':
return 6
case '7':
return 7
case '8':
return 8
case '9':
return 9
}
return -1
}
func intToRune(i int) rune {
switch i {
case 0:
return '0'
case 1:
return '1'
case 2:
return '2'
case 3:
return '3'
case 4:
return '4'
case 5:
return '5'
case 6:
return '6'
case 7:
return '7'
case 8:
return '8'
case 9:
return '9'
}
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.AddByte(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!")
}
}
var result *utils.BitList = nil
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")
}