1 package pdf417
2
3 import (
4 "errors"
5 "math/big"
6
7 "github.com/boombuler/barcode/utils"
8 )
9
10 type encodingMode byte
11
12 type subMode byte
13
14 const (
15 encText encodingMode = iota
16 encNumeric
17 encBinary
18
19 subUpper subMode = iota
20 subLower
21 subMixed
22 subPunct
23
24 latch_to_text = 900
25 latch_to_byte_padded = 901
26 latch_to_numeric = 902
27 latch_to_byte = 924
28 shift_to_byte = 913
29
30 min_numeric_count = 13
31 )
32
33 var (
34 mixedMap map[rune]int
35 punctMap map[rune]int
36 )
37
38 func init() {
39 mixedMap = make(map[rune]int)
40 mixedRaw := []rune{
41 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 38, 13, 9, 44, 58,
42 35, 45, 46, 36, 47, 43, 37, 42, 61, 94, 0, 32, 0, 0, 0,
43 }
44 for idx, ch := range mixedRaw {
45 if ch > 0 {
46 mixedMap[ch] = idx
47 }
48 }
49
50 punctMap = make(map[rune]int)
51 punctRaw := []rune{
52 59, 60, 62, 64, 91, 92, 93, 95, 96, 126, 33, 13, 9, 44, 58,
53 10, 45, 46, 36, 47, 34, 124, 42, 40, 41, 63, 123, 125, 39, 0,
54 }
55 for idx, ch := range punctRaw {
56 if ch > 0 {
57 punctMap[ch] = idx
58 }
59 }
60 }
61
62 func determineConsecutiveDigitCount(data []rune) int {
63 cnt := 0
64 for _, r := range data {
65 if utils.RuneToInt(r) == -1 {
66 break
67 }
68 cnt++
69 }
70 return cnt
71 }
72
73 func encodeNumeric(digits []rune) ([]int, error) {
74 digitCount := len(digits)
75 chunkCount := digitCount / 44
76 if digitCount%44 != 0 {
77 chunkCount++
78 }
79
80 codeWords := []int{}
81
82 for i := 0; i < chunkCount; i++ {
83 start := i * 44
84 end := start + 44
85 if end > digitCount {
86 end = digitCount
87 }
88 chunk := digits[start:end]
89
90 chunkNum := big.NewInt(0)
91 _, ok := chunkNum.SetString("1"+string(chunk), 10)
92
93 if !ok {
94 return nil, errors.New("Failed converting: " + string(chunk))
95 }
96
97 cws := []int{}
98
99 for chunkNum.Cmp(big.NewInt(0)) > 0 {
100 newChunk, cw := chunkNum.DivMod(chunkNum, big.NewInt(900), big.NewInt(0))
101 chunkNum = newChunk
102 cws = append([]int{int(cw.Int64())}, cws...)
103 }
104
105 codeWords = append(codeWords, cws...)
106 }
107
108 return codeWords, nil
109 }
110
111 func determineConsecutiveTextCount(msg []rune) int {
112 result := 0
113
114 isText := func(ch rune) bool {
115 return ch == '\t' || ch == '\n' || ch == '\r' || (ch >= 32 && ch <= 126)
116 }
117
118 for i, ch := range msg {
119 numericCount := determineConsecutiveDigitCount(msg[i:])
120 if numericCount >= min_numeric_count || (numericCount == 0 && !isText(ch)) {
121 break
122 }
123
124 result++
125 }
126 return result
127 }
128
129 func encodeText(text []rune, submode subMode) (subMode, []int) {
130 isAlphaUpper := func(ch rune) bool {
131 return ch == ' ' || (ch >= 'A' && ch <= 'Z')
132 }
133 isAlphaLower := func(ch rune) bool {
134 return ch == ' ' || (ch >= 'a' && ch <= 'z')
135 }
136 isMixed := func(ch rune) bool {
137 _, ok := mixedMap[ch]
138 return ok
139 }
140 isPunctuation := func(ch rune) bool {
141 _, ok := punctMap[ch]
142 return ok
143 }
144
145 idx := 0
146 var tmp []int
147 for idx < len(text) {
148 ch := text[idx]
149 switch submode {
150 case subUpper:
151 if isAlphaUpper(ch) {
152 if ch == ' ' {
153 tmp = append(tmp, 26)
154 } else {
155 tmp = append(tmp, int(ch-'A'))
156 }
157 } else {
158 if isAlphaLower(ch) {
159 submode = subLower
160 tmp = append(tmp, 27)
161 continue
162 } else if isMixed(ch) {
163 submode = subMixed
164 tmp = append(tmp, 28)
165 continue
166 } else {
167 tmp = append(tmp, 29)
168 tmp = append(tmp, punctMap[ch])
169 break
170 }
171 }
172 break
173 case subLower:
174 if isAlphaLower(ch) {
175 if ch == ' ' {
176 tmp = append(tmp, 26)
177 } else {
178 tmp = append(tmp, int(ch-'a'))
179 }
180 } else {
181 if isAlphaUpper(ch) {
182 tmp = append(tmp, 27)
183 tmp = append(tmp, int(ch-'A'))
184 break
185 } else if isMixed(ch) {
186 submode = subMixed
187 tmp = append(tmp, 28)
188 continue
189 } else {
190 tmp = append(tmp, 29)
191 tmp = append(tmp, punctMap[ch])
192 break
193 }
194 }
195 break
196 case subMixed:
197 if isMixed(ch) {
198 tmp = append(tmp, mixedMap[ch])
199 } else {
200 if isAlphaUpper(ch) {
201 submode = subUpper
202 tmp = append(tmp, 28)
203 continue
204 } else if isAlphaLower(ch) {
205 submode = subLower
206 tmp = append(tmp, 27)
207 continue
208 } else {
209 if idx+1 < len(text) {
210 next := text[idx+1]
211 if isPunctuation(next) {
212 submode = subPunct
213 tmp = append(tmp, 25)
214 continue
215 }
216 }
217 tmp = append(tmp, 29)
218 tmp = append(tmp, punctMap[ch])
219 }
220 }
221 break
222 default:
223 if isPunctuation(ch) {
224 tmp = append(tmp, punctMap[ch])
225 } else {
226 submode = subUpper
227 tmp = append(tmp, 29)
228 continue
229 }
230 }
231 idx++
232 }
233
234 h := 0
235 result := []int{}
236 for i, val := range tmp {
237 if i%2 != 0 {
238 h = (h * 30) + val
239 result = append(result, h)
240 } else {
241 h = val
242 }
243 }
244 if len(tmp)%2 != 0 {
245 result = append(result, (h*30)+29)
246 }
247 return submode, result
248 }
249
250 func determineConsecutiveBinaryCount(msg []byte) int {
251 result := 0
252
253 for i, _ := range msg {
254 numericCount := determineConsecutiveDigitCount([]rune(string(msg[i:])))
255 if numericCount >= min_numeric_count {
256 break
257 }
258 textCount := determineConsecutiveTextCount([]rune(string(msg[i:])))
259 if textCount > 5 {
260 break
261 }
262 result++
263 }
264 return result
265 }
266
267 func encodeBinary(data []byte, startmode encodingMode) []int {
268 result := []int{}
269
270 count := len(data)
271 if count == 1 && startmode == encText {
272 result = append(result, shift_to_byte)
273 } else if (count % 6) == 0 {
274 result = append(result, latch_to_byte)
275 } else {
276 result = append(result, latch_to_byte_padded)
277 }
278
279 idx := 0
280
281 if count >= 6 {
282 words := make([]int, 5)
283 for (count - idx) >= 6 {
284 var t int64 = 0
285 for i := 0; i < 6; i++ {
286 t = t << 8
287 t += int64(data[idx+i])
288 }
289 for i := 0; i < 5; i++ {
290 words[4-i] = int(t % 900)
291 t = t / 900
292 }
293 result = append(result, words...)
294 idx += 6
295 }
296 }
297
298 for i := idx; i < count; i++ {
299 result = append(result, int(data[i]&0xff))
300 }
301 return result
302 }
303
304 func highlevelEncode(dataStr string) ([]int, error) {
305 encodingMode := encText
306 textSubMode := subUpper
307
308 result := []int{}
309
310 data := []byte(dataStr)
311
312 for len(data) > 0 {
313 numericCount := determineConsecutiveDigitCount([]rune(string(data)))
314 if numericCount >= min_numeric_count || numericCount == len(data) {
315 result = append(result, latch_to_numeric)
316 encodingMode = encNumeric
317 textSubMode = subUpper
318 numData, err := encodeNumeric([]rune(string(data[:numericCount])))
319 if err != nil {
320 return nil, err
321 }
322 result = append(result, numData...)
323 data = data[numericCount:]
324 } else {
325 textCount := determineConsecutiveTextCount([]rune(string(data)))
326 if textCount >= 5 || textCount == len(data) {
327 if encodingMode != encText {
328 result = append(result, latch_to_text)
329 encodingMode = encText
330 textSubMode = subUpper
331 }
332 var txtData []int
333 textSubMode, txtData = encodeText([]rune(string(data[:textCount])), textSubMode)
334 result = append(result, txtData...)
335 data = data[textCount:]
336 } else {
337 binaryCount := determineConsecutiveBinaryCount(data)
338 if binaryCount == 0 {
339 binaryCount = 1
340 }
341 bytes := data[:binaryCount]
342 if len(bytes) != 1 || encodingMode != encText {
343 encodingMode = encBinary
344 textSubMode = subUpper
345 }
346 byteData := encodeBinary(bytes, encodingMode)
347 result = append(result, byteData...)
348 data = data[binaryCount:]
349 }
350 }
351 }
352
353 return result, nil
354 }
355
View as plain text