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  }