1
2 package pdf417
3
4 import (
5 "fmt"
6
7 "github.com/boombuler/barcode"
8 "github.com/boombuler/barcode/utils"
9 )
10
11 const (
12 padding_codeword = 900
13 )
14
15
16
17
18 func Encode(data string, securityLevel byte) (barcode.Barcode, error) {
19 if securityLevel >= 9 {
20 return nil, fmt.Errorf("Invalid security level %d", securityLevel)
21 }
22
23 sl := securitylevel(securityLevel)
24
25 dataWords, err := highlevelEncode(data)
26 if err != nil {
27 return nil, err
28 }
29
30 columns, rows := calcDimensions(len(dataWords), sl.ErrorCorrectionWordCount())
31 if columns < minCols || columns > maxCols || rows < minRows || rows > maxRows {
32 return nil, fmt.Errorf("Unable to fit data in barcode")
33 }
34
35 barcode := new(pdfBarcode)
36 barcode.data = data
37
38 codeWords, err := encodeData(dataWords, columns, sl)
39 if err != nil {
40 return nil, err
41 }
42
43 grid := [][]int{}
44 for i := 0; i < len(codeWords); i += columns {
45 grid = append(grid, codeWords[i:min(i+columns, len(codeWords))])
46 }
47
48 codes := [][]int{}
49
50 for rowNum, row := range grid {
51 table := rowNum % 3
52 rowCodes := make([]int, 0, columns+4)
53
54 rowCodes = append(rowCodes, start_word)
55 rowCodes = append(rowCodes, getCodeword(table, getLeftCodeWord(rowNum, rows, columns, securityLevel)))
56
57 for _, word := range row {
58 rowCodes = append(rowCodes, getCodeword(table, word))
59 }
60
61 rowCodes = append(rowCodes, getCodeword(table, getRightCodeWord(rowNum, rows, columns, securityLevel)))
62 rowCodes = append(rowCodes, stop_word)
63
64 codes = append(codes, rowCodes)
65 }
66
67 barcode.code = renderBarcode(codes)
68 barcode.width = (columns+4)*17 + 1
69
70 return barcode, nil
71 }
72
73 func encodeData(dataWords []int, columns int, sl securitylevel) ([]int, error) {
74 dataCount := len(dataWords)
75
76 ecCount := sl.ErrorCorrectionWordCount()
77
78 padWords := getPadding(dataCount, ecCount, columns)
79 dataWords = append(dataWords, padWords...)
80
81 length := len(dataWords) + 1
82 dataWords = append([]int{length}, dataWords...)
83
84 ecWords := sl.Compute(dataWords)
85
86 return append(dataWords, ecWords...), nil
87 }
88
89 func getLeftCodeWord(rowNum int, rows int, columns int, securityLevel byte) int {
90 tableId := rowNum % 3
91
92 var x int
93
94 switch tableId {
95 case 0:
96 x = (rows - 3) / 3
97 case 1:
98 x = int(securityLevel) * 3
99 x += (rows - 1) % 3
100 case 2:
101 x = columns - 1
102 }
103
104 return 30*(rowNum/3) + x
105 }
106
107 func getRightCodeWord(rowNum int, rows int, columns int, securityLevel byte) int {
108 tableId := rowNum % 3
109
110 var x int
111
112 switch tableId {
113 case 0:
114 x = columns - 1
115 case 1:
116 x = (rows - 1) / 3
117 case 2:
118 x = int(securityLevel) * 3
119 x += (rows - 1) % 3
120 }
121
122 return 30*(rowNum/3) + x
123 }
124
125 func min(a, b int) int {
126 if a <= b {
127 return a
128 }
129 return b
130 }
131
132 func getPadding(dataCount int, ecCount int, columns int) []int {
133 totalCount := dataCount + ecCount + 1
134 mod := totalCount % columns
135
136 padding := []int{}
137
138 if mod > 0 {
139 padCount := columns - mod
140 padding = make([]int, padCount)
141 for i := 0; i < padCount; i++ {
142 padding[i] = padding_codeword
143 }
144 }
145
146 return padding
147 }
148
149 func renderBarcode(codes [][]int) *utils.BitList {
150 bl := new(utils.BitList)
151 for _, row := range codes {
152 lastIdx := len(row) - 1
153 for i, col := range row {
154 if i == lastIdx {
155 bl.AddBits(col, 18)
156 } else {
157 bl.AddBits(col, 17)
158 }
159 }
160 }
161 return bl
162 }
163
View as plain text