git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/barcode/ean/encoder.go (about) 1 // Package ean can create EAN 8 and EAN 13 barcodes. 2 package ean 3 4 import ( 5 "errors" 6 7 "git.sr.ht/~pingoo/stdx/barcode" 8 "git.sr.ht/~pingoo/stdx/barcode/utils" 9 ) 10 11 type encodedNumber struct { 12 LeftOdd []bool 13 LeftEven []bool 14 Right []bool 15 CheckSum []bool 16 } 17 18 var encoderTable = map[rune]encodedNumber{ 19 '0': encodedNumber{ 20 []bool{false, false, false, true, true, false, true}, 21 []bool{false, true, false, false, true, true, true}, 22 []bool{true, true, true, false, false, true, false}, 23 []bool{false, false, false, false, false, false}, 24 }, 25 '1': encodedNumber{ 26 []bool{false, false, true, true, false, false, true}, 27 []bool{false, true, true, false, false, true, true}, 28 []bool{true, true, false, false, true, true, false}, 29 []bool{false, false, true, false, true, true}, 30 }, 31 '2': encodedNumber{ 32 []bool{false, false, true, false, false, true, true}, 33 []bool{false, false, true, true, false, true, true}, 34 []bool{true, true, false, true, true, false, false}, 35 []bool{false, false, true, true, false, true}, 36 }, 37 '3': encodedNumber{ 38 []bool{false, true, true, true, true, false, true}, 39 []bool{false, true, false, false, false, false, true}, 40 []bool{true, false, false, false, false, true, false}, 41 []bool{false, false, true, true, true, false}, 42 }, 43 '4': encodedNumber{ 44 []bool{false, true, false, false, false, true, true}, 45 []bool{false, false, true, true, true, false, true}, 46 []bool{true, false, true, true, true, false, false}, 47 []bool{false, true, false, false, true, true}, 48 }, 49 '5': encodedNumber{ 50 []bool{false, true, true, false, false, false, true}, 51 []bool{false, true, true, true, false, false, true}, 52 []bool{true, false, false, true, true, true, false}, 53 []bool{false, true, true, false, false, true}, 54 }, 55 '6': encodedNumber{ 56 []bool{false, true, false, true, true, true, true}, 57 []bool{false, false, false, false, true, false, true}, 58 []bool{true, false, true, false, false, false, false}, 59 []bool{false, true, true, true, false, false}, 60 }, 61 '7': encodedNumber{ 62 []bool{false, true, true, true, false, true, true}, 63 []bool{false, false, true, false, false, false, true}, 64 []bool{true, false, false, false, true, false, false}, 65 []bool{false, true, false, true, false, true}, 66 }, 67 '8': encodedNumber{ 68 []bool{false, true, true, false, true, true, true}, 69 []bool{false, false, false, true, false, false, true}, 70 []bool{true, false, false, true, false, false, false}, 71 []bool{false, true, false, true, true, false}, 72 }, 73 '9': encodedNumber{ 74 []bool{false, false, false, true, false, true, true}, 75 []bool{false, false, true, false, true, true, true}, 76 []bool{true, true, true, false, true, false, false}, 77 []bool{false, true, true, false, true, false}, 78 }, 79 } 80 81 func calcCheckNum(code string) rune { 82 x3 := len(code) == 7 83 sum := 0 84 for _, r := range code { 85 curNum := utils.RuneToInt(r) 86 if curNum < 0 || curNum > 9 { 87 return 'B' 88 } 89 if x3 { 90 curNum = curNum * 3 91 } 92 x3 = !x3 93 sum += curNum 94 } 95 96 return utils.IntToRune((10 - (sum % 10)) % 10) 97 } 98 99 func encodeEAN8(code string) *utils.BitList { 100 result := new(utils.BitList) 101 result.AddBit(true, false, true) 102 103 for cpos, r := range code { 104 num, ok := encoderTable[r] 105 if !ok { 106 return nil 107 } 108 var data []bool 109 if cpos < 4 { 110 data = num.LeftOdd 111 } else { 112 data = num.Right 113 } 114 115 if cpos == 4 { 116 result.AddBit(false, true, false, true, false) 117 } 118 result.AddBit(data...) 119 } 120 result.AddBit(true, false, true) 121 122 return result 123 } 124 125 func encodeEAN13(code string) *utils.BitList { 126 result := new(utils.BitList) 127 result.AddBit(true, false, true) 128 129 var firstNum []bool 130 for cpos, r := range code { 131 num, ok := encoderTable[r] 132 if !ok { 133 return nil 134 } 135 if cpos == 0 { 136 firstNum = num.CheckSum 137 continue 138 } 139 140 var data []bool 141 if cpos < 7 { // Left 142 if firstNum[cpos-1] { 143 data = num.LeftEven 144 } else { 145 data = num.LeftOdd 146 } 147 } else { 148 data = num.Right 149 } 150 151 if cpos == 7 { 152 result.AddBit(false, true, false, true, false) 153 } 154 result.AddBit(data...) 155 } 156 result.AddBit(true, false, true) 157 return result 158 } 159 160 // Encode returns a EAN 8 or EAN 13 barcode for the given code 161 func Encode(code string) (barcode.BarcodeIntCS, error) { 162 var checkSum int 163 if len(code) == 7 || len(code) == 12 { 164 code += string(calcCheckNum(code)) 165 checkSum = utils.RuneToInt(calcCheckNum(code)) 166 } else if len(code) == 8 || len(code) == 13 { 167 check := code[0 : len(code)-1] 168 check += string(calcCheckNum(check)) 169 if check != code { 170 return nil, errors.New("checksum missmatch") 171 } 172 checkSum = utils.RuneToInt(rune(code[len(code)-1])) 173 } 174 175 if len(code) == 8 { 176 result := encodeEAN8(code) 177 if result != nil { 178 return utils.New1DCodeIntCheckSum(barcode.TypeEAN8, code, result, checkSum), nil 179 } 180 } else if len(code) == 13 { 181 result := encodeEAN13(code) 182 if result != nil { 183 return utils.New1DCodeIntCheckSum(barcode.TypeEAN13, code, result, checkSum), nil 184 } 185 } 186 return nil, errors.New("invalid ean code data") 187 }