-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreate.go
166 lines (138 loc) · 3.86 KB
/
create.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package polishedqr
import (
"image"
)
type CreateOptions struct {
// Selects the level of error correction to use.
// "L": recover 7% of codewords
// "M": recover 15%
// "Q": recover 25%
// "H": recover 30%
// If unset, defaults to "M"
ErrorCorrectionLevel string
// The character set to encode the data with.
// If unset, the character set will be chosen automatically,
// however, encoding formats will not be mixed on the same code.
CharacterSet *CharacterSet
// The version (size) of the qr code.
// If unset, the version will be the smallest that can fit the data
Version int
}
// Create a qr code from data with options, which may be nil.
// Data that is numeric or alphanumeric should be passed in their ascii form
func CreateQRCode(data []byte, opts *CreateOptions) *image.RGBA {
if opts == nil {
opts = &CreateOptions{}
}
if opts.ErrorCorrectionLevel == "" {
opts.ErrorCorrectionLevel = "M"
}
// Encode data in correct mode
var mode CharacterSet
if opts.CharacterSet != nil {
mode = *opts.CharacterSet
} else {
mode = AutodetectCharacterSet(data)
}
var version int
if opts.Version == 0 {
// We will iteratively increase version until data fits
version = 1
} else {
version = opts.Version
}
var dataBits Bits
var totalDatawords int
for {
// Encode data
switch mode {
case Numeric:
dataBits = ConvertToNumeric(data, version)
case Alphanumeric:
dataBits = ConvertToAlphanumeric(data, version)
case Bytes:
dataBits = ConvertToBytes(data, version)
default:
panic("unsupported encoding mode")
}
// Get total data size of this symbol
totalDatawords = 0
blocks := codeWordTable[version][opts.ErrorCorrectionLevel]
for _, v := range blocks.blocks {
totalDatawords += v.dataWords * v.count
}
// Check whether the data fits
if len(dataBits) > totalDatawords*8 {
if version != opts.Version {
// Version is unset in options, try a larger symbol size
if version == 40 {
panic("data cannot fit in largest qr code")
}
version++
continue
} else {
panic("data cannot fit in designated size qr code")
}
}
break
}
// Add terminator (if required)
for i := 0; i < 4 && len(dataBits) < totalDatawords*8; i++ {
dataBits = append(dataBits, 0)
}
// Pad to nearest 8 bits
for i := 0; i < len(dataBits)%8; i++ {
dataBits = append(dataBits, 0)
}
// Convert to codewords
var codewords []uint8
for i := 0; i < len(dataBits); i += 8 {
var acc uint8
acc += dataBits[i] << 7
acc += dataBits[i+1] << 6
acc += dataBits[i+2] << 5
acc += dataBits[i+3] << 4
acc += dataBits[i+4] << 3
acc += dataBits[i+5] << 2
acc += dataBits[i+6] << 1
acc += dataBits[i+7] << 0
codewords = append(codewords, acc)
}
// Add padding codewords
for i := 0; len(codewords) < totalDatawords; i++ {
if i%2 == 0 {
codewords = append(codewords, 0b11101100)
} else {
codewords = append(codewords, 0b00010001)
}
}
// Generate error correction
allwords := generateErrorWords(codewords, version, opts.ErrorCorrectionLevel)
// Create image
i := image.NewRGBA(image.Rect(0, 0, 17+version*4, 17+version*4))
// Draw background (so we can see coding area)
iterateRect(i.Rect.Dx(), i.Rect.Dy(), func(x, y int) {
i.SetRGBA(x, y, BLUE)
})
// Draw finder patterns in three corners
drawFinderPattern(i, 0, 0)
drawFinderPattern(i, 0, i.Rect.Dy()-7)
drawFinderPattern(i, i.Rect.Dx()-7, 0)
// Place temporary format bits
drawTempFormatBits(i)
if version >= 7 {
drawTempVersionBits(i)
}
// Draw both timing patterns
drawTimingPatterns(i)
// Draw alignment patterns
drawAlignmentPatterns(i)
// Draw the data onto the qr code with a zig-zag pattern
writeData(i, allwords)
// Apply the best mask
pattern := applyBestMask(i, opts.ErrorCorrectionLevel, version)
addFormatAndVersionInfo(i, opts.ErrorCorrectionLevel, pattern, version)
// Place qr code in quiet zone
i = quietZone(i)
return i
}