1
2 package qr
3
4 import (
5 "image"
6
7 "github.com/boombuler/barcode"
8 "github.com/boombuler/barcode/utils"
9 )
10
11 type encodeFn func(content string, eccLevel ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error)
12
13
14 type Encoding byte
15
16 const (
17
18 Auto Encoding = iota
19
20 Numeric
21
22 AlphaNumeric
23
24 Unicode
25
26 unknownEncoding
27 )
28
29 func (e Encoding) getEncoder() encodeFn {
30 switch e {
31 case Auto:
32 return encodeAuto
33 case Numeric:
34 return encodeNumeric
35 case AlphaNumeric:
36 return encodeAlphaNumeric
37 case Unicode:
38 return encodeUnicode
39 }
40 return nil
41 }
42
43 func (e Encoding) String() string {
44 switch e {
45 case Auto:
46 return "Auto"
47 case Numeric:
48 return "Numeric"
49 case AlphaNumeric:
50 return "AlphaNumeric"
51 case Unicode:
52 return "Unicode"
53 }
54 return ""
55 }
56
57
58 func Encode(content string, level ErrorCorrectionLevel, mode Encoding) (barcode.Barcode, error) {
59 bits, vi, err := mode.getEncoder()(content, level)
60 if err != nil {
61 return nil, err
62 }
63
64 blocks := splitToBlocks(bits.IterateBytes(), vi)
65 data := blocks.interleave(vi)
66 result := render(data, vi)
67 result.content = content
68 return result, nil
69 }
70
71 func render(data []byte, vi *versionInfo) *qrcode {
72 dim := vi.modulWidth()
73 results := make([]*qrcode, 8)
74 for i := 0; i < 8; i++ {
75 results[i] = newBarcode(dim)
76 }
77
78 occupied := newBarcode(dim)
79
80 setAll := func(x int, y int, val bool) {
81 occupied.Set(x, y, true)
82 for i := 0; i < 8; i++ {
83 results[i].Set(x, y, val)
84 }
85 }
86
87 drawFinderPatterns(vi, setAll)
88 drawAlignmentPatterns(occupied, vi, setAll)
89
90
91 var i int
92 for i = 0; i < dim; i++ {
93 if !occupied.Get(i, 6) {
94 setAll(i, 6, i%2 == 0)
95 }
96 if !occupied.Get(6, i) {
97 setAll(6, i, i%2 == 0)
98 }
99 }
100
101 setAll(8, dim-8, true)
102
103 drawVersionInfo(vi, setAll)
104 drawFormatInfo(vi, -1, occupied.Set)
105 for i := 0; i < 8; i++ {
106 drawFormatInfo(vi, i, results[i].Set)
107 }
108
109
110 var curBitNo int
111
112 for pos := range iterateModules(occupied) {
113 var curBit bool
114 if curBitNo < len(data)*8 {
115 curBit = ((data[curBitNo/8] >> uint(7-(curBitNo%8))) & 1) == 1
116 } else {
117 curBit = false
118 }
119
120 for i := 0; i < 8; i++ {
121 setMasked(pos.X, pos.Y, curBit, i, results[i].Set)
122 }
123 curBitNo++
124 }
125
126 lowestPenalty := ^uint(0)
127 lowestPenaltyIdx := -1
128 for i := 0; i < 8; i++ {
129 p := results[i].calcPenalty()
130 if p < lowestPenalty {
131 lowestPenalty = p
132 lowestPenaltyIdx = i
133 }
134 }
135 return results[lowestPenaltyIdx]
136 }
137
138 func setMasked(x, y int, val bool, mask int, set func(int, int, bool)) {
139 switch mask {
140 case 0:
141 val = val != (((y + x) % 2) == 0)
142 break
143 case 1:
144 val = val != ((y % 2) == 0)
145 break
146 case 2:
147 val = val != ((x % 3) == 0)
148 break
149 case 3:
150 val = val != (((y + x) % 3) == 0)
151 break
152 case 4:
153 val = val != (((y/2 + x/3) % 2) == 0)
154 break
155 case 5:
156 val = val != (((y*x)%2)+((y*x)%3) == 0)
157 break
158 case 6:
159 val = val != ((((y*x)%2)+((y*x)%3))%2 == 0)
160 break
161 case 7:
162 val = val != ((((y+x)%2)+((y*x)%3))%2 == 0)
163 }
164 set(x, y, val)
165 }
166
167 func iterateModules(occupied *qrcode) <-chan image.Point {
168 result := make(chan image.Point)
169 allPoints := make(chan image.Point)
170 go func() {
171 curX := occupied.dimension - 1
172 curY := occupied.dimension - 1
173 isUpward := true
174
175 for true {
176 if isUpward {
177 allPoints <- image.Pt(curX, curY)
178 allPoints <- image.Pt(curX-1, curY)
179 curY--
180 if curY < 0 {
181 curY = 0
182 curX -= 2
183 if curX == 6 {
184 curX--
185 }
186 if curX < 0 {
187 break
188 }
189 isUpward = false
190 }
191 } else {
192 allPoints <- image.Pt(curX, curY)
193 allPoints <- image.Pt(curX-1, curY)
194 curY++
195 if curY >= occupied.dimension {
196 curY = occupied.dimension - 1
197 curX -= 2
198 if curX == 6 {
199 curX--
200 }
201 isUpward = true
202 if curX < 0 {
203 break
204 }
205 }
206 }
207 }
208
209 close(allPoints)
210 }()
211 go func() {
212 for pt := range allPoints {
213 if !occupied.Get(pt.X, pt.Y) {
214 result <- pt
215 }
216 }
217 close(result)
218 }()
219 return result
220 }
221
222 func drawFinderPatterns(vi *versionInfo, set func(int, int, bool)) {
223 dim := vi.modulWidth()
224 drawPattern := func(xoff int, yoff int) {
225 for x := -1; x < 8; x++ {
226 for y := -1; y < 8; y++ {
227 val := (x == 0 || x == 6 || y == 0 || y == 6 || (x > 1 && x < 5 && y > 1 && y < 5)) && (x <= 6 && y <= 6 && x >= 0 && y >= 0)
228
229 if x+xoff >= 0 && x+xoff < dim && y+yoff >= 0 && y+yoff < dim {
230 set(x+xoff, y+yoff, val)
231 }
232 }
233 }
234 }
235 drawPattern(0, 0)
236 drawPattern(0, dim-7)
237 drawPattern(dim-7, 0)
238 }
239
240 func drawAlignmentPatterns(occupied *qrcode, vi *versionInfo, set func(int, int, bool)) {
241 drawPattern := func(xoff int, yoff int) {
242 for x := -2; x <= 2; x++ {
243 for y := -2; y <= 2; y++ {
244 val := x == -2 || x == 2 || y == -2 || y == 2 || (x == 0 && y == 0)
245 set(x+xoff, y+yoff, val)
246 }
247 }
248 }
249 positions := vi.alignmentPatternPlacements()
250
251 for _, x := range positions {
252 for _, y := range positions {
253 if occupied.Get(x, y) {
254 continue
255 }
256 drawPattern(x, y)
257 }
258 }
259 }
260
261 var formatInfos = map[ErrorCorrectionLevel]map[int][]bool{
262 L: {
263 0: []bool{true, true, true, false, true, true, true, true, true, false, false, false, true, false, false},
264 1: []bool{true, true, true, false, false, true, false, true, true, true, true, false, false, true, true},
265 2: []bool{true, true, true, true, true, false, true, true, false, true, false, true, false, true, false},
266 3: []bool{true, true, true, true, false, false, false, true, false, false, true, true, true, false, true},
267 4: []bool{true, true, false, false, true, true, false, false, false, true, false, true, true, true, true},
268 5: []bool{true, true, false, false, false, true, true, false, false, false, true, true, false, false, false},
269 6: []bool{true, true, false, true, true, false, false, false, true, false, false, false, false, false, true},
270 7: []bool{true, true, false, true, false, false, true, false, true, true, true, false, true, true, false},
271 },
272 M: {
273 0: []bool{true, false, true, false, true, false, false, false, false, false, true, false, false, true, false},
274 1: []bool{true, false, true, false, false, false, true, false, false, true, false, false, true, false, true},
275 2: []bool{true, false, true, true, true, true, false, false, true, true, true, true, true, false, false},
276 3: []bool{true, false, true, true, false, true, true, false, true, false, false, true, false, true, true},
277 4: []bool{true, false, false, false, true, false, true, true, true, true, true, true, false, false, true},
278 5: []bool{true, false, false, false, false, false, false, true, true, false, false, true, true, true, false},
279 6: []bool{true, false, false, true, true, true, true, true, false, false, true, false, true, true, true},
280 7: []bool{true, false, false, true, false, true, false, true, false, true, false, false, false, false, false},
281 },
282 Q: {
283 0: []bool{false, true, true, false, true, false, true, false, true, false, true, true, true, true, true},
284 1: []bool{false, true, true, false, false, false, false, false, true, true, false, true, false, false, false},
285 2: []bool{false, true, true, true, true, true, true, false, false, true, true, false, false, false, true},
286 3: []bool{false, true, true, true, false, true, false, false, false, false, false, false, true, true, false},
287 4: []bool{false, true, false, false, true, false, false, true, false, true, true, false, true, false, false},
288 5: []bool{false, true, false, false, false, false, true, true, false, false, false, false, false, true, true},
289 6: []bool{false, true, false, true, true, true, false, true, true, false, true, true, false, true, false},
290 7: []bool{false, true, false, true, false, true, true, true, true, true, false, true, true, false, true},
291 },
292 H: {
293 0: []bool{false, false, true, false, true, true, false, true, false, false, false, true, false, false, true},
294 1: []bool{false, false, true, false, false, true, true, true, false, true, true, true, true, true, false},
295 2: []bool{false, false, true, true, true, false, false, true, true, true, false, false, true, true, true},
296 3: []bool{false, false, true, true, false, false, true, true, true, false, true, false, false, false, false},
297 4: []bool{false, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
298 5: []bool{false, false, false, false, false, true, false, false, true, false, true, false, true, false, true},
299 6: []bool{false, false, false, true, true, false, true, false, false, false, false, true, true, false, false},
300 7: []bool{false, false, false, true, false, false, false, false, false, true, true, true, false, true, true},
301 },
302 }
303
304 func drawFormatInfo(vi *versionInfo, usedMask int, set func(int, int, bool)) {
305 var formatInfo []bool
306
307 if usedMask == -1 {
308 formatInfo = []bool{true, true, true, true, true, true, true, true, true, true, true, true, true, true, true}
309 } else {
310 formatInfo = formatInfos[vi.Level][usedMask]
311 }
312
313 if len(formatInfo) == 15 {
314 dim := vi.modulWidth()
315 set(0, 8, formatInfo[0])
316 set(1, 8, formatInfo[1])
317 set(2, 8, formatInfo[2])
318 set(3, 8, formatInfo[3])
319 set(4, 8, formatInfo[4])
320 set(5, 8, formatInfo[5])
321 set(7, 8, formatInfo[6])
322 set(8, 8, formatInfo[7])
323 set(8, 7, formatInfo[8])
324 set(8, 5, formatInfo[9])
325 set(8, 4, formatInfo[10])
326 set(8, 3, formatInfo[11])
327 set(8, 2, formatInfo[12])
328 set(8, 1, formatInfo[13])
329 set(8, 0, formatInfo[14])
330
331 set(8, dim-1, formatInfo[0])
332 set(8, dim-2, formatInfo[1])
333 set(8, dim-3, formatInfo[2])
334 set(8, dim-4, formatInfo[3])
335 set(8, dim-5, formatInfo[4])
336 set(8, dim-6, formatInfo[5])
337 set(8, dim-7, formatInfo[6])
338 set(dim-8, 8, formatInfo[7])
339 set(dim-7, 8, formatInfo[8])
340 set(dim-6, 8, formatInfo[9])
341 set(dim-5, 8, formatInfo[10])
342 set(dim-4, 8, formatInfo[11])
343 set(dim-3, 8, formatInfo[12])
344 set(dim-2, 8, formatInfo[13])
345 set(dim-1, 8, formatInfo[14])
346 }
347 }
348
349 var versionInfoBitsByVersion = map[byte][]bool{
350 7: []bool{false, false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false},
351 8: []bool{false, false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false},
352 9: []bool{false, false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true},
353 10: []bool{false, false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true},
354 11: []bool{false, false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false},
355 12: []bool{false, false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
356 13: []bool{false, false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true},
357 14: []bool{false, false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true},
358 15: []bool{false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false},
359 16: []bool{false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false},
360 17: []bool{false, true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true},
361 18: []bool{false, true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true},
362 19: []bool{false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false},
363 20: []bool{false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true, false},
364 21: []bool{false, true, false, true, false, true, false, true, true, false, true, false, false, false, false, false, true, true},
365 22: []bool{false, true, false, true, true, false, true, false, false, false, true, true, false, false, true, false, false, true},
366 23: []bool{false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false, false},
367 24: []bool{false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false, false},
368 25: []bool{false, true, true, false, false, true, false, false, false, true, true, true, true, false, false, false, false, true},
369 26: []bool{false, true, true, false, true, false, true, true, true, true, true, false, true, false, true, false, true, true},
370 27: []bool{false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true, false},
371 28: []bool{false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true, false},
372 29: []bool{false, true, true, true, false, true, false, false, true, true, false, false, true, true, true, true, true, true},
373 30: []bool{false, true, true, true, true, false, true, true, false, true, false, true, true, true, false, true, false, true},
374 31: []bool{false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false, false},
375 32: []bool{true, false, false, false, false, false, true, false, false, true, true, true, false, true, false, true, false, true},
376 33: []bool{true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false, false},
377 34: []bool{true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true, false},
378 35: []bool{true, false, false, false, true, true, false, true, true, true, true, false, false, true, true, true, true, true},
379 36: []bool{true, false, false, true, false, false, true, false, true, true, false, false, false, false, true, false, true, true},
380 37: []bool{true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true, false},
381 38: []bool{true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false, false},
382 39: []bool{true, false, false, true, true, true, false, true, false, true, false, true, false, false, false, false, false, true},
383 40: []bool{true, false, true, false, false, false, true, true, false, false, false, true, true, false, true, false, false, true},
384 }
385
386 func drawVersionInfo(vi *versionInfo, set func(int, int, bool)) {
387 versionInfoBits, ok := versionInfoBitsByVersion[vi.Version]
388
389 if ok && len(versionInfoBits) > 0 {
390 for i := 0; i < len(versionInfoBits); i++ {
391 x := (vi.modulWidth() - 11) + i%3
392 y := i / 3
393 set(x, y, versionInfoBits[len(versionInfoBits)-i-1])
394 set(y, x, versionInfoBits[len(versionInfoBits)-i-1])
395 }
396 }
397
398 }
399
400 func addPaddingAndTerminator(bl *utils.BitList, vi *versionInfo) {
401 for i := 0; i < 4 && bl.Len() < vi.totalDataBytes()*8; i++ {
402 bl.AddBit(false)
403 }
404
405 for bl.Len()%8 != 0 {
406 bl.AddBit(false)
407 }
408
409 for i := 0; bl.Len() < vi.totalDataBytes()*8; i++ {
410 if i%2 == 0 {
411 bl.AddByte(236)
412 } else {
413 bl.AddByte(17)
414 }
415 }
416 }
417
View as plain text