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  }