git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/barcode/twooffive/encoder.go (about) 1 // Package twooffive can create interleaved and standard "2 of 5" barcodes. 2 package twooffive 3 4 import ( 5 "errors" 6 "fmt" 7 8 "git.sr.ht/~pingoo/stdx/barcode" 9 "git.sr.ht/~pingoo/stdx/barcode/utils" 10 ) 11 12 const patternWidth = 5 13 14 type pattern [patternWidth]bool 15 type encodeInfo struct { 16 start []bool 17 end []bool 18 widths map[bool]int 19 } 20 21 var ( 22 encodingTable = map[rune]pattern{ 23 '0': pattern{false, false, true, true, false}, 24 '1': pattern{true, false, false, false, true}, 25 '2': pattern{false, true, false, false, true}, 26 '3': pattern{true, true, false, false, false}, 27 '4': pattern{false, false, true, false, true}, 28 '5': pattern{true, false, true, false, false}, 29 '6': pattern{false, true, true, false, false}, 30 '7': pattern{false, false, false, true, true}, 31 '8': pattern{true, false, false, true, false}, 32 '9': pattern{false, true, false, true, false}, 33 } 34 35 modes = map[bool]encodeInfo{ 36 false: encodeInfo{ // non-interleaved 37 start: []bool{true, true, false, true, true, false, true, false}, 38 end: []bool{true, true, false, true, false, true, true}, 39 widths: map[bool]int{ 40 true: 3, 41 false: 1, 42 }, 43 }, 44 true: encodeInfo{ // interleaved 45 start: []bool{true, false, true, false}, 46 end: []bool{true, true, false, true}, 47 widths: map[bool]int{ 48 true: 3, 49 false: 1, 50 }, 51 }, 52 } 53 nonInterleavedSpace = pattern{false, false, false, false, false} 54 ) 55 56 // AddCheckSum calculates the correct check-digit and appends it to the given content. 57 func AddCheckSum(content string) (string, error) { 58 if content == "" { 59 return "", errors.New("content is empty") 60 } 61 62 even := len(content)%2 == 1 63 sum := 0 64 for _, r := range content { 65 if _, ok := encodingTable[r]; ok { 66 value := utils.RuneToInt(r) 67 if even { 68 sum += value * 3 69 } else { 70 sum += value 71 } 72 even = !even 73 } else { 74 return "", fmt.Errorf("can not encode \"%s\"", content) 75 } 76 } 77 78 return content + string(utils.IntToRune(sum%10)), nil 79 } 80 81 // Encode creates a codabar barcode for the given content 82 func Encode(content string, interleaved bool) (barcode.Barcode, error) { 83 if content == "" { 84 return nil, errors.New("content is empty") 85 } 86 87 if interleaved && len(content)%2 == 1 { 88 return nil, errors.New("can only encode even number of digits in interleaved mode") 89 } 90 91 mode := modes[interleaved] 92 resBits := new(utils.BitList) 93 resBits.AddBit(mode.start...) 94 95 var lastRune *rune 96 for _, r := range content { 97 var a, b pattern 98 if interleaved { 99 if lastRune == nil { 100 lastRune = new(rune) 101 *lastRune = r 102 continue 103 } else { 104 var o1, o2 bool 105 a, o1 = encodingTable[*lastRune] 106 b, o2 = encodingTable[r] 107 if !o1 || !o2 { 108 return nil, fmt.Errorf("can not encode \"%s\"", content) 109 } 110 lastRune = nil 111 } 112 } else { 113 var ok bool 114 a, ok = encodingTable[r] 115 if !ok { 116 return nil, fmt.Errorf("can not encode \"%s\"", content) 117 } 118 b = nonInterleavedSpace 119 } 120 121 for i := 0; i < patternWidth; i++ { 122 for x := 0; x < mode.widths[a[i]]; x++ { 123 resBits.AddBit(true) 124 } 125 for x := 0; x < mode.widths[b[i]]; x++ { 126 resBits.AddBit(false) 127 } 128 } 129 } 130 131 resBits.AddBit(mode.end...) 132 133 if interleaved { 134 return utils.New1DCode(barcode.Type2of5Interleaved, content, resBits), nil 135 } else { 136 return utils.New1DCode(barcode.Type2of5, content, resBits), nil 137 } 138 }