git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/barcode/code93/encoder.go (about) 1 // Package code93 can create Code93 barcodes 2 package code93 3 4 import ( 5 "errors" 6 "strings" 7 8 "git.sr.ht/~pingoo/stdx/barcode" 9 "git.sr.ht/~pingoo/stdx/barcode/utils" 10 ) 11 12 type encodeInfo struct { 13 value int 14 data int 15 } 16 17 const ( 18 // Special Function 1 ($) 19 FNC1 = '\u00f1' 20 // Special Function 2 (%) 21 FNC2 = '\u00f2' 22 // Special Function 3 (/) 23 FNC3 = '\u00f3' 24 // Special Function 4 (+) 25 FNC4 = '\u00f4' 26 ) 27 28 var encodeTable = map[rune]encodeInfo{ 29 '0': encodeInfo{0, 0x114}, '1': encodeInfo{1, 0x148}, '2': encodeInfo{2, 0x144}, 30 '3': encodeInfo{3, 0x142}, '4': encodeInfo{4, 0x128}, '5': encodeInfo{5, 0x124}, 31 '6': encodeInfo{6, 0x122}, '7': encodeInfo{7, 0x150}, '8': encodeInfo{8, 0x112}, 32 '9': encodeInfo{9, 0x10A}, 'A': encodeInfo{10, 0x1A8}, 'B': encodeInfo{11, 0x1A4}, 33 'C': encodeInfo{12, 0x1A2}, 'D': encodeInfo{13, 0x194}, 'E': encodeInfo{14, 0x192}, 34 'F': encodeInfo{15, 0x18A}, 'G': encodeInfo{16, 0x168}, 'H': encodeInfo{17, 0x164}, 35 'I': encodeInfo{18, 0x162}, 'J': encodeInfo{19, 0x134}, 'K': encodeInfo{20, 0x11A}, 36 'L': encodeInfo{21, 0x158}, 'M': encodeInfo{22, 0x14C}, 'N': encodeInfo{23, 0x146}, 37 'O': encodeInfo{24, 0x12C}, 'P': encodeInfo{25, 0x116}, 'Q': encodeInfo{26, 0x1B4}, 38 'R': encodeInfo{27, 0x1B2}, 'S': encodeInfo{28, 0x1AC}, 'T': encodeInfo{29, 0x1A6}, 39 'U': encodeInfo{30, 0x196}, 'V': encodeInfo{31, 0x19A}, 'W': encodeInfo{32, 0x16C}, 40 'X': encodeInfo{33, 0x166}, 'Y': encodeInfo{34, 0x136}, 'Z': encodeInfo{35, 0x13A}, 41 '-': encodeInfo{36, 0x12E}, '.': encodeInfo{37, 0x1D4}, ' ': encodeInfo{38, 0x1D2}, 42 '$': encodeInfo{39, 0x1CA}, '/': encodeInfo{40, 0x16E}, '+': encodeInfo{41, 0x176}, 43 '%': encodeInfo{42, 0x1AE}, FNC1: encodeInfo{43, 0x126}, FNC2: encodeInfo{44, 0x1DA}, 44 FNC3: encodeInfo{45, 0x1D6}, FNC4: encodeInfo{46, 0x132}, '*': encodeInfo{47, 0x15E}, 45 } 46 47 var extendedTable = []string{ 48 "\u00f2U", "\u00f1A", "\u00f1B", "\u00f1C", "\u00f1D", "\u00f1E", "\u00f1F", "\u00f1G", 49 "\u00f1H", "\u00f1I", "\u00f1J", "\u00f1K", "\u00f1L", "\u00f1M", "\u00f1N", "\u00f1O", 50 "\u00f1P", "\u00f1Q", "\u00f1R", "\u00f1S", "\u00f1T", "\u00f1U", "\u00f1V", "\u00f1W", 51 "\u00f1X", "\u00f1Y", "\u00f1Z", "\u00f2A", "\u00f2B", "\u00f2C", "\u00f2D", "\u00f2E", 52 " ", "\u00f3A", "\u00f3B", "\u00f3C", "\u00f3D", "\u00f3E", "\u00f3F", "\u00f3G", 53 "\u00f3H", "\u00f3I", "\u00f3J", "\u00f3K", "\u00f3L", "-", ".", "\u00f3O", 54 "0", "1", "2", "3", "4", "5", "6", "7", 55 "8", "9", "\u00f3Z", "\u00f2F", "\u00f2G", "\u00f2H", "\u00f2I", "\u00f2J", 56 "\u00f2V", "A", "B", "C", "D", "E", "F", "G", 57 "H", "I", "J", "K", "L", "M", "N", "O", 58 "P", "Q", "R", "S", "T", "U", "V", "W", 59 "X", "Y", "Z", "\u00f2K", "\u00f2L", "\u00f2M", "\u00f2N", "\u00f2O", 60 "\u00f2W", "\u00f4A", "\u00f4B", "\u00f4C", "\u00f4D", "\u00f4E", "\u00f4F", "\u00f4G", 61 "\u00f4H", "\u00f4I", "\u00f4J", "\u00f4K", "\u00f4L", "\u00f4M", "\u00f4N", "\u00f4O", 62 "\u00f4P", "\u00f4Q", "\u00f4R", "\u00f4S", "\u00f4T", "\u00f4U", "\u00f4V", "\u00f4W", 63 "\u00f4X", "\u00f4Y", "\u00f4Z", "\u00f2P", "\u00f2Q", "\u00f2R", "\u00f2S", "\u00f2T", 64 } 65 66 func prepare(content string) (string, error) { 67 result := "" 68 for _, r := range content { 69 if r > 127 { 70 return "", errors.New("Only ASCII strings can be encoded") 71 } 72 result += extendedTable[int(r)] 73 } 74 return result, nil 75 } 76 77 // Encode returns a code93 barcode for the given content 78 // if includeChecksum is set to true, two checksum characters are calculated and added to the content 79 func Encode(content string, includeChecksum bool, fullASCIIMode bool) (barcode.Barcode, error) { 80 if fullASCIIMode { 81 var err error 82 content, err = prepare(content) 83 if err != nil { 84 return nil, err 85 } 86 } else if strings.ContainsRune(content, '*') { 87 return nil, errors.New("invalid data! content may not contain '*'") 88 } 89 90 data := content + string(getChecksum(content, 20)) 91 data += string(getChecksum(data, 15)) 92 93 data = "*" + data + "*" 94 result := new(utils.BitList) 95 96 for _, r := range data { 97 info, ok := encodeTable[r] 98 if !ok { 99 return nil, errors.New("invalid data!") 100 } 101 result.AddBits(info.data, 9) 102 } 103 result.AddBit(true) 104 105 return utils.New1DCode(barcode.TypeCode93, content, result), nil 106 } 107 108 func getChecksum(content string, maxWeight int) rune { 109 weight := 1 110 total := 0 111 112 data := []rune(content) 113 for i := len(data) - 1; i >= 0; i-- { 114 r := data[i] 115 info, ok := encodeTable[r] 116 if !ok { 117 return ' ' 118 } 119 total += info.value * weight 120 if weight++; weight > maxWeight { 121 weight = 1 122 } 123 } 124 total = total % 47 125 for r, info := range encodeTable { 126 if info.value == total { 127 return r 128 } 129 } 130 return ' ' 131 }