git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/barcode/code39/encoder.go (about) 1 // Package code39 can create Code39 barcodes 2 package code39 3 4 import ( 5 "errors" 6 "strconv" 7 "strings" 8 9 "git.sr.ht/~pingoo/stdx/barcode" 10 "git.sr.ht/~pingoo/stdx/barcode/utils" 11 ) 12 13 type encodeInfo struct { 14 value int 15 data []bool 16 } 17 18 var encodeTable = map[rune]encodeInfo{ 19 '0': encodeInfo{0, []bool{true, false, true, false, false, true, true, false, true, true, false, true}}, 20 '1': encodeInfo{1, []bool{true, true, false, true, false, false, true, false, true, false, true, true}}, 21 '2': encodeInfo{2, []bool{true, false, true, true, false, false, true, false, true, false, true, true}}, 22 '3': encodeInfo{3, []bool{true, true, false, true, true, false, false, true, false, true, false, true}}, 23 '4': encodeInfo{4, []bool{true, false, true, false, false, true, true, false, true, false, true, true}}, 24 '5': encodeInfo{5, []bool{true, true, false, true, false, false, true, true, false, true, false, true}}, 25 '6': encodeInfo{6, []bool{true, false, true, true, false, false, true, true, false, true, false, true}}, 26 '7': encodeInfo{7, []bool{true, false, true, false, false, true, false, true, true, false, true, true}}, 27 '8': encodeInfo{8, []bool{true, true, false, true, false, false, true, false, true, true, false, true}}, 28 '9': encodeInfo{9, []bool{true, false, true, true, false, false, true, false, true, true, false, true}}, 29 'A': encodeInfo{10, []bool{true, true, false, true, false, true, false, false, true, false, true, true}}, 30 'B': encodeInfo{11, []bool{true, false, true, true, false, true, false, false, true, false, true, true}}, 31 'C': encodeInfo{12, []bool{true, true, false, true, true, false, true, false, false, true, false, true}}, 32 'D': encodeInfo{13, []bool{true, false, true, false, true, true, false, false, true, false, true, true}}, 33 'E': encodeInfo{14, []bool{true, true, false, true, false, true, true, false, false, true, false, true}}, 34 'F': encodeInfo{15, []bool{true, false, true, true, false, true, true, false, false, true, false, true}}, 35 'G': encodeInfo{16, []bool{true, false, true, false, true, false, false, true, true, false, true, true}}, 36 'H': encodeInfo{17, []bool{true, true, false, true, false, true, false, false, true, true, false, true}}, 37 'I': encodeInfo{18, []bool{true, false, true, true, false, true, false, false, true, true, false, true}}, 38 'J': encodeInfo{19, []bool{true, false, true, false, true, true, false, false, true, true, false, true}}, 39 'K': encodeInfo{20, []bool{true, true, false, true, false, true, false, true, false, false, true, true}}, 40 'L': encodeInfo{21, []bool{true, false, true, true, false, true, false, true, false, false, true, true}}, 41 'M': encodeInfo{22, []bool{true, true, false, true, true, false, true, false, true, false, false, true}}, 42 'N': encodeInfo{23, []bool{true, false, true, false, true, true, false, true, false, false, true, true}}, 43 'O': encodeInfo{24, []bool{true, true, false, true, false, true, true, false, true, false, false, true}}, 44 'P': encodeInfo{25, []bool{true, false, true, true, false, true, true, false, true, false, false, true}}, 45 'Q': encodeInfo{26, []bool{true, false, true, false, true, false, true, true, false, false, true, true}}, 46 'R': encodeInfo{27, []bool{true, true, false, true, false, true, false, true, true, false, false, true}}, 47 'S': encodeInfo{28, []bool{true, false, true, true, false, true, false, true, true, false, false, true}}, 48 'T': encodeInfo{29, []bool{true, false, true, false, true, true, false, true, true, false, false, true}}, 49 'U': encodeInfo{30, []bool{true, true, false, false, true, false, true, false, true, false, true, true}}, 50 'V': encodeInfo{31, []bool{true, false, false, true, true, false, true, false, true, false, true, true}}, 51 'W': encodeInfo{32, []bool{true, true, false, false, true, true, false, true, false, true, false, true}}, 52 'X': encodeInfo{33, []bool{true, false, false, true, false, true, true, false, true, false, true, true}}, 53 'Y': encodeInfo{34, []bool{true, true, false, false, true, false, true, true, false, true, false, true}}, 54 'Z': encodeInfo{35, []bool{true, false, false, true, true, false, true, true, false, true, false, true}}, 55 '-': encodeInfo{36, []bool{true, false, false, true, false, true, false, true, true, false, true, true}}, 56 '.': encodeInfo{37, []bool{true, true, false, false, true, false, true, false, true, true, false, true}}, 57 ' ': encodeInfo{38, []bool{true, false, false, true, true, false, true, false, true, true, false, true}}, 58 '$': encodeInfo{39, []bool{true, false, false, true, false, false, true, false, false, true, false, true}}, 59 '/': encodeInfo{40, []bool{true, false, false, true, false, false, true, false, true, false, false, true}}, 60 '+': encodeInfo{41, []bool{true, false, false, true, false, true, false, false, true, false, false, true}}, 61 '%': encodeInfo{42, []bool{true, false, true, false, false, true, false, false, true, false, false, true}}, 62 '*': encodeInfo{-1, []bool{true, false, false, true, false, true, true, false, true, true, false, true}}, 63 } 64 65 var extendedTable = map[rune]string{ 66 0: `%U`, 1: `$A`, 2: `$B`, 3: `$C`, 4: `$D`, 5: `$E`, 6: `$F`, 7: `$G`, 8: `$H`, 9: `$I`, 10: `$J`, 67 11: `$K`, 12: `$L`, 13: `$M`, 14: `$N`, 15: `$O`, 16: `$P`, 17: `$Q`, 18: `$R`, 19: `$S`, 20: `$T`, 68 21: `$U`, 22: `$V`, 23: `$W`, 24: `$X`, 25: `$Y`, 26: `$Z`, 27: `%A`, 28: `%B`, 29: `%C`, 30: `%D`, 69 31: `%E`, 33: `/A`, 34: `/B`, 35: `/C`, 36: `/D`, 37: `/E`, 38: `/F`, 39: `/G`, 40: `/H`, 41: `/I`, 70 42: `/J`, 43: `/K`, 44: `/L`, 47: `/O`, 58: `/Z`, 59: `%F`, 60: `%G`, 61: `%H`, 62: `%I`, 63: `%J`, 71 64: `%V`, 91: `%K`, 92: `%L`, 93: `%M`, 94: `%N`, 95: `%O`, 96: `%W`, 97: `+A`, 98: `+B`, 99: `+C`, 72 100: `+D`, 101: `+E`, 102: `+F`, 103: `+G`, 104: `+H`, 105: `+I`, 106: `+J`, 107: `+K`, 108: `+L`, 73 109: `+M`, 110: `+N`, 111: `+O`, 112: `+P`, 113: `+Q`, 114: `+R`, 115: `+S`, 116: `+T`, 117: `+U`, 74 118: `+V`, 119: `+W`, 120: `+X`, 121: `+Y`, 122: `+Z`, 123: `%P`, 124: `%Q`, 125: `%R`, 126: `%S`, 75 127: `%T`, 76 } 77 78 func getChecksum(content string) string { 79 sum := 0 80 for _, r := range content { 81 info, ok := encodeTable[r] 82 if !ok || info.value < 0 { 83 return "#" 84 } 85 86 sum += info.value 87 } 88 89 sum = sum % 43 90 for r, v := range encodeTable { 91 if v.value == sum { 92 return string(r) 93 } 94 } 95 return "#" 96 } 97 98 func prepare(content string) (string, error) { 99 result := "" 100 for _, r := range content { 101 if r > 127 { 102 return "", errors.New("Only ASCII strings can be encoded") 103 } 104 val, ok := extendedTable[r] 105 if ok { 106 result += val 107 } else { 108 result += string([]rune{r}) 109 } 110 } 111 return result, nil 112 } 113 114 // Encode returns a code39 barcode for the given content 115 // if includeChecksum is set to true, a checksum character is calculated and added to the content 116 func Encode(content string, includeChecksum bool, fullASCIIMode bool) (barcode.BarcodeIntCS, error) { 117 if fullASCIIMode { 118 var err error 119 content, err = prepare(content) 120 if err != nil { 121 return nil, err 122 } 123 } else if strings.ContainsRune(content, '*') { 124 return nil, errors.New("invalid data! try full ascii mode") 125 } 126 127 data := "*" + content 128 if includeChecksum { 129 data += getChecksum(content) 130 } 131 data += "*" 132 133 result := new(utils.BitList) 134 135 for i, r := range data { 136 if i != 0 { 137 result.AddBit(false) 138 } 139 140 info, ok := encodeTable[r] 141 if !ok { 142 return nil, errors.New("invalid data! try full ascii mode") 143 } 144 result.AddBit(info.data...) 145 } 146 147 checkSum, err := strconv.ParseInt(getChecksum(content), 10, 64) 148 if err != nil { 149 checkSum = 0 150 } 151 return utils.New1DCodeIntCheckSum(barcode.TypeCode39, content, result, int(checkSum)), nil 152 }