git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/barcode/code128/encode.go (about) 1 // Package code128 can create Code128 barcodes 2 package code128 3 4 import ( 5 "fmt" 6 "strings" 7 "unicode/utf8" 8 9 "git.sr.ht/~pingoo/stdx/barcode" 10 "git.sr.ht/~pingoo/stdx/barcode/utils" 11 ) 12 13 func strToRunes(str string) []rune { 14 result := make([]rune, utf8.RuneCountInString(str)) 15 i := 0 16 for _, r := range str { 17 result[i] = r 18 i++ 19 } 20 return result 21 } 22 23 func shouldUseCTable(nextRunes []rune, curEncoding byte) bool { 24 requiredDigits := 4 25 if curEncoding == startCSymbol { 26 requiredDigits = 2 27 } 28 if len(nextRunes) < requiredDigits { 29 return false 30 } 31 for i := 0; i < requiredDigits; i++ { 32 if i%2 == 0 && nextRunes[i] == FNC1 { 33 requiredDigits++ 34 if len(nextRunes) < requiredDigits { 35 return false 36 } 37 continue 38 } 39 if nextRunes[i] < '0' || nextRunes[i] > '9' { 40 return false 41 } 42 } 43 return true 44 } 45 46 func tableContainsRune(table string, r rune) bool { 47 return strings.ContainsRune(table, r) || r == FNC1 || r == FNC2 || r == FNC3 || r == FNC4 48 } 49 50 func shouldUseATable(nextRunes []rune, curEncoding byte) bool { 51 nextRune := nextRunes[0] 52 if !tableContainsRune(bTable, nextRune) || curEncoding == startASymbol { 53 return tableContainsRune(aTable, nextRune) 54 } 55 if curEncoding == 0 { 56 for _, r := range nextRunes { 57 if tableContainsRune(abTable, r) { 58 continue 59 } 60 if strings.ContainsRune(aOnlyTable, r) { 61 return true 62 } 63 break 64 } 65 } 66 return false 67 } 68 69 func getCodeIndexList(content []rune) *utils.BitList { 70 result := new(utils.BitList) 71 curEncoding := byte(0) 72 for i := 0; i < len(content); i++ { 73 if shouldUseCTable(content[i:], curEncoding) { 74 if curEncoding != startCSymbol { 75 if curEncoding == byte(0) { 76 result.AddByte(startCSymbol) 77 } else { 78 result.AddByte(codeCSymbol) 79 } 80 curEncoding = startCSymbol 81 } 82 if content[i] == FNC1 { 83 result.AddByte(102) 84 } else { 85 idx := (content[i] - '0') * 10 86 i++ 87 idx = idx + (content[i] - '0') 88 result.AddByte(byte(idx)) 89 } 90 } else if shouldUseATable(content[i:], curEncoding) { 91 if curEncoding != startASymbol { 92 if curEncoding == byte(0) { 93 result.AddByte(startASymbol) 94 } else { 95 result.AddByte(codeASymbol) 96 } 97 curEncoding = startASymbol 98 } 99 var idx int 100 switch content[i] { 101 case FNC1: 102 idx = 102 103 break 104 case FNC2: 105 idx = 97 106 break 107 case FNC3: 108 idx = 96 109 break 110 case FNC4: 111 idx = 101 112 break 113 default: 114 idx = strings.IndexRune(aTable, content[i]) 115 break 116 } 117 if idx < 0 { 118 return nil 119 } 120 result.AddByte(byte(idx)) 121 } else { 122 if curEncoding != startBSymbol { 123 if curEncoding == byte(0) { 124 result.AddByte(startBSymbol) 125 } else { 126 result.AddByte(codeBSymbol) 127 } 128 curEncoding = startBSymbol 129 } 130 var idx int 131 switch content[i] { 132 case FNC1: 133 idx = 102 134 break 135 case FNC2: 136 idx = 97 137 break 138 case FNC3: 139 idx = 96 140 break 141 case FNC4: 142 idx = 100 143 break 144 default: 145 idx = strings.IndexRune(bTable, content[i]) 146 break 147 } 148 149 if idx < 0 { 150 return nil 151 } 152 result.AddByte(byte(idx)) 153 } 154 } 155 return result 156 } 157 158 // Encode creates a Code 128 barcode for the given content 159 func Encode(content string) (barcode.BarcodeIntCS, error) { 160 contentRunes := strToRunes(content) 161 if len(contentRunes) <= 0 || len(contentRunes) > 80 { 162 return nil, fmt.Errorf("content length should be between 1 and 80 runes but got %d", len(contentRunes)) 163 } 164 idxList := getCodeIndexList(contentRunes) 165 166 if idxList == nil { 167 return nil, fmt.Errorf("\"%s\" could not be encoded", content) 168 } 169 170 result := new(utils.BitList) 171 sum := 0 172 for i, idx := range idxList.GetBytes() { 173 if i == 0 { 174 sum = int(idx) 175 } else { 176 sum += i * int(idx) 177 } 178 result.AddBit(encodingTable[idx]...) 179 } 180 sum = sum % 103 181 result.AddBit(encodingTable[sum]...) 182 result.AddBit(encodingTable[stopSymbol]...) 183 return utils.New1DCodeIntCheckSum(barcode.TypeCode128, content, result, sum), nil 184 } 185 186 func EncodeWithoutChecksum(content string) (barcode.Barcode, error) { 187 contentRunes := strToRunes(content) 188 if len(contentRunes) <= 0 || len(contentRunes) > 80 { 189 return nil, fmt.Errorf("content length should be between 1 and 80 runes but got %d", len(contentRunes)) 190 } 191 idxList := getCodeIndexList(contentRunes) 192 193 if idxList == nil { 194 return nil, fmt.Errorf("\"%s\" could not be encoded", content) 195 } 196 197 result := new(utils.BitList) 198 for _, idx := range idxList.GetBytes() { 199 result.AddBit(encodingTable[idx]...) 200 } 201 result.AddBit(encodingTable[stopSymbol]...) 202 return utils.New1DCode(barcode.TypeCode128, content, result), nil 203 }