1// Package qr can be used to create QR barcodes.
2package qr
3
4import (
5	"image"
6
7	"github.com/boombuler/barcode"
8	"github.com/boombuler/barcode/utils"
9)
10
11type encodeFn func(content string, eccLevel ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error)
12
13// Encoding mode for QR Codes.
14type Encoding byte
15
16const (
17	// Auto will choose ths best matching encoding
18	Auto Encoding = iota
19	// Numeric encoding only encodes numbers [0-9]
20	Numeric
21	// AlphaNumeric encoding only encodes uppercase letters, numbers and  [Space], $, %, *, +, -, ., /, :
22	AlphaNumeric
23	// Unicode encoding encodes the string as utf-8
24	Unicode
25	// only for testing purpose
26	unknownEncoding
27)
28
29func (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
43func (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// Encode returns a QR barcode with the given content, error correction level and uses the given encoding
58func 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
71func 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	//Timing Pattern:
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	// Dark Module
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	// Write the data
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
138func 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
167func 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
222func 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
240func 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
261var 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
304func 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} // Set all to true cause -1 --> occupied mask.
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
349var 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
386func 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
400func 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