encoder.go 7.4 KB

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