New upstream snapshot.
Debian Janitor
2 years ago
17 | 17 | |
18 | 18 | import qrcode "github.com/skip2/go-qrcode" |
19 | 19 | |
20 | - **Create a PNG image:** | |
20 | - **Create a 256x256 PNG image:** | |
21 | 21 | |
22 | 22 | var png []byte |
23 | 23 | png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256) |
24 | 24 | |
25 | - **Create a PNG image and write to a file:** | |
25 | - **Create a 256x256 PNG image and write to a file:** | |
26 | 26 | |
27 | 27 | err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png") |
28 | 28 | |
29 | - **Create a PNG image with custom colors and write to file:** | |
29 | - **Create a 256x256 PNG image with custom colors and write to file:** | |
30 | 30 | |
31 | 31 | err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png") |
32 | 32 | |
33 | All examples use the qrcode.Medium error Recovery Level and create a fixed | |
34 | 256x256px size QR Code. The last function creates a white on black instead of black | |
35 | on white QR Code. | |
36 | ||
37 | The maximum capacity of a QR Code varies according to the content encoded and | |
38 | the error recovery level. The maximum capacity is 2,953 bytes, 4,296 | |
39 | alphanumeric characters, 7,089 numeric digits, or a combination of these. | |
33 | All examples use the qrcode.Medium error Recovery Level and create a fixed 256x256px size QR Code. The last function creates a white on black instead of black on white QR Code. | |
40 | 34 | |
41 | 35 | ## Documentation |
42 | 36 | |
55 | 49 | https://github.com/skip2/go-qrcode |
56 | 50 | |
57 | 51 | Flags: |
52 | -d disable QR Code border | |
53 | -i invert black and white | |
58 | 54 | -o string |
59 | out PNG file prefix, empty for stdout | |
55 | out PNG file prefix, empty for stdout | |
60 | 56 | -s int |
61 | image size (pixel) (default 256) | |
57 | image size (pixel) (default 256) | |
58 | -t print as text-art on stdout | |
62 | 59 | |
63 | 60 | Usage: |
64 | 61 | 1. Arguments except for flags are joined by " " and used to generate QR code. |
70 | 67 | 2. Save to file if "display" not available: |
71 | 68 | |
72 | 69 | qrcode "homepage: https://github.com/skip2/go-qrcode" > out.png |
70 | ||
73 | 71 | ``` |
72 | ## Maximum capacity | |
73 | The maximum capacity of a QR Code varies according to the content encoded and the error recovery level. The maximum capacity is 2,953 bytes, 4,296 alphanumeric characters, 7,089 numeric digits, or a combination of these. | |
74 | ||
75 | ## Borderless QR Codes | |
76 | ||
77 | To aid QR Code reading software, QR codes have a built in whitespace border. | |
78 | ||
79 | If you know what you're doing, and don't want a border, see https://gist.github.com/skip2/7e3d8a82f5317df9be437f8ec8ec0b7d for how to do it. It's still recommended you include a border manually. | |
74 | 80 | |
75 | 81 | ## Links |
76 | 82 |
0 | go-qrcode (0.0~git20190110.dc11ecd-3) UNRELEASED; urgency=low | |
0 | go-qrcode (0.0~git20200617.da1b656-1) UNRELEASED; urgency=low | |
1 | 1 | |
2 | 2 | * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, |
3 | 3 | Repository-Browse. |
4 | 4 | * Update standards version to 4.5.0, no changes needed. |
5 | * New upstream snapshot. | |
5 | 6 | |
6 | -- Debian Janitor <janitor@jelmer.uk> Sat, 06 Jun 2020 18:33:43 -0000 | |
7 | -- Debian Janitor <janitor@jelmer.uk> Sun, 08 Aug 2021 10:34:31 -0000 | |
7 | 8 | |
8 | 9 | go-qrcode (0.0~git20190110.dc11ecd-2) unstable; urgency=medium |
9 | 10 |
171 | 171 | } |
172 | 172 | |
173 | 173 | // Classify data into unoptimised segments. |
174 | d.classifyDataModes() | |
174 | highestRequiredMode := d.classifyDataModes() | |
175 | 175 | |
176 | 176 | // Optimise segments. |
177 | 177 | err := d.optimiseDataModes() |
179 | 179 | return nil, err |
180 | 180 | } |
181 | 181 | |
182 | // Check if a single byte encoded segment would be more efficient. | |
183 | optimizedLength := 0 | |
184 | for _, s := range d.optimised { | |
185 | length, err := d.encodedLength(s.dataMode, len(s.data)) | |
186 | if err != nil { | |
187 | return nil, err | |
188 | } | |
189 | optimizedLength += length | |
190 | } | |
191 | ||
192 | singleByteSegmentLength, err := d.encodedLength(highestRequiredMode, len(d.data)) | |
193 | if err != nil { | |
194 | return nil, err | |
195 | } | |
196 | ||
197 | if singleByteSegmentLength <= optimizedLength { | |
198 | d.optimised = []segment{segment{dataMode: highestRequiredMode, data: d.data}} | |
199 | } | |
200 | ||
182 | 201 | // Encode data. |
183 | 202 | encoded := bitset.New() |
184 | 203 | for _, s := range d.optimised { |
191 | 210 | // classifyDataModes classifies the raw data into unoptimised segments. |
192 | 211 | // e.g. "123ZZ#!#!" => |
193 | 212 | // [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"]. |
194 | func (d *dataEncoder) classifyDataModes() { | |
213 | // | |
214 | // Returns the highest data mode needed to encode the data. e.g. for a mixed | |
215 | // numeric/alphanumeric input, the highest is alphanumeric. | |
216 | // | |
217 | // dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte | |
218 | func (d *dataEncoder) classifyDataModes() dataMode { | |
195 | 219 | var start int |
196 | 220 | mode := dataModeNone |
221 | highestRequiredMode := mode | |
197 | 222 | |
198 | 223 | for i, v := range d.data { |
199 | 224 | newMode := dataModeNone |
216 | 241 | |
217 | 242 | mode = newMode |
218 | 243 | } |
244 | ||
245 | if newMode > highestRequiredMode { | |
246 | highestRequiredMode = newMode | |
247 | } | |
219 | 248 | } |
220 | 249 | |
221 | 250 | d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]}) |
251 | ||
252 | return highestRequiredMode | |
222 | 253 | } |
223 | 254 | |
224 | 255 | // optimiseDataModes optimises the list of segments to reduce the overall output |
179 | 179 | { |
180 | 180 | dataEncoderType1To9, |
181 | 181 | []testModeSegment{ |
182 | {dataModeAlphanumeric, 1}, | |
183 | {dataModeByte, 1}, | |
184 | {dataModeNumeric, 1}, | |
185 | }, | |
186 | []testModeSegment{ | |
187 | {dataModeAlphanumeric, 1}, | |
182 | {dataModeAlphanumeric, 100}, | |
183 | {dataModeByte, 1}, | |
184 | {dataModeNumeric, 1}, | |
185 | }, | |
186 | []testModeSegment{ | |
187 | {dataModeAlphanumeric, 100}, | |
188 | 188 | {dataModeByte, 2}, |
189 | 189 | }, |
190 | 190 | }, |
191 | // https://www.google.com/123 | |
192 | // BBBBBAAABBBABBBBBBABBBANNN | |
191 | // Sometimes encoding everything as bytes is more efficient. | |
192 | { | |
193 | dataEncoderType1To9, | |
194 | []testModeSegment{ | |
195 | {dataModeAlphanumeric, 1}, | |
196 | {dataModeByte, 1}, | |
197 | {dataModeNumeric, 1}, | |
198 | }, | |
199 | []testModeSegment{ | |
200 | {dataModeByte, 3}, | |
201 | }, | |
202 | }, | |
203 | // https://www.google.com/123456789012345678901234567890 | |
204 | // BBBBBAAABBBABBBBBBABBBANNNNNNNNNNNNNNNNNNNNNNNNNNNNNN | |
193 | 205 | { |
194 | 206 | dataEncoderType1To9, |
195 | 207 | []testModeSegment{ |
200 | 212 | {dataModeByte, 6}, |
201 | 213 | {dataModeAlphanumeric, 1}, |
202 | 214 | {dataModeAlphanumeric, 4}, |
203 | {dataModeNumeric, 3}, | |
215 | {dataModeNumeric, 30}, | |
204 | 216 | }, |
205 | 217 | []testModeSegment{ |
206 | 218 | {dataModeByte, 23}, |
207 | {dataModeNumeric, 3}, | |
219 | {dataModeNumeric, 30}, | |
220 | }, | |
221 | }, | |
222 | // https://www.google.com/123 | |
223 | // BBBBBAAABBBABBBBBBABBBANNN | |
224 | // Small segments are inefficient because of additional metadata. | |
225 | { | |
226 | dataEncoderType1To9, | |
227 | []testModeSegment{ | |
228 | {dataModeByte, 5}, | |
229 | {dataModeAlphanumeric, 3}, | |
230 | {dataModeByte, 3}, | |
231 | {dataModeAlphanumeric, 1}, | |
232 | {dataModeByte, 6}, | |
233 | {dataModeAlphanumeric, 1}, | |
234 | {dataModeAlphanumeric, 4}, | |
235 | {dataModeNumeric, 3}, | |
236 | }, | |
237 | []testModeSegment{ | |
238 | {dataModeByte, 26}, | |
208 | 239 | }, |
209 | 240 | }, |
210 | 241 | // HTTPS://WWW.GOOGLE.COM/123 |
233 | 264 | }, |
234 | 265 | []testModeSegment{ |
235 | 266 | {dataModeByte, 8}, |
267 | }, | |
268 | }, | |
269 | // HTTPS://ABC.DE/Q/393AABB6998877XYZ0518AUQCRVJN25 | |
270 | // AAAAAAAAAAAAAAAAANNNAAAANNNNNNNAAANNNNAAAAAAAANN | |
271 | // different to below---------^-------------------- | |
272 | { | |
273 | dataEncoderType1To9, | |
274 | []testModeSegment{ | |
275 | {dataModeAlphanumeric, 17}, | |
276 | {dataModeNumeric, 3}, | |
277 | {dataModeAlphanumeric, 4}, | |
278 | {dataModeNumeric, 7}, | |
279 | {dataModeAlphanumeric, 3}, | |
280 | {dataModeNumeric, 4}, | |
281 | {dataModeAlphanumeric, 8}, | |
282 | {dataModeNumeric, 2}, | |
283 | }, | |
284 | []testModeSegment{ | |
285 | {dataModeAlphanumeric, 48}, | |
286 | }, | |
287 | }, | |
288 | // HTTPS://ABC.DE/Q/393AABB699E877XYZ0518AUQCRVJN25 | |
289 | // AAAAAAAAAAAAAAAAANNNAAAANNNANNNAAANNNNAAAAAAAANN | |
290 | // different to above---------^-------------------- | |
291 | { | |
292 | dataEncoderType1To9, | |
293 | []testModeSegment{ | |
294 | {dataModeAlphanumeric, 17}, | |
295 | {dataModeNumeric, 3}, | |
296 | {dataModeAlphanumeric, 4}, | |
297 | {dataModeNumeric, 3}, | |
298 | {dataModeAlphanumeric, 1}, | |
299 | {dataModeNumeric, 3}, | |
300 | {dataModeAlphanumeric, 3}, | |
301 | {dataModeNumeric, 4}, | |
302 | {dataModeAlphanumeric, 8}, | |
303 | {dataModeNumeric, 2}, | |
304 | }, | |
305 | []testModeSegment{ | |
306 | {dataModeAlphanumeric, 48}, | |
307 | }, | |
308 | }, | |
309 | // 0123456789 | |
310 | // NNNNNNNNNN | |
311 | { | |
312 | dataEncoderType1To9, | |
313 | []testModeSegment{ | |
314 | {dataModeNumeric, 10}, | |
315 | }, | |
316 | []testModeSegment{ | |
317 | {dataModeNumeric, 10}, | |
236 | 318 | }, |
237 | 319 | }, |
238 | 320 | } |
8 | 8 | |
9 | 9 | import ( |
10 | 10 | "fmt" |
11 | "image/color" | |
11 | 12 | "os" |
12 | 13 | "testing" |
13 | 14 | ) |
28 | 29 | } |
29 | 30 | } |
30 | 31 | } |
32 | ||
33 | func TestExampleEncodeWithColourAndWithoutBorder(t *testing.T) { | |
34 | q, err := New("https://example.org", Medium) | |
35 | if err != nil { | |
36 | t.Errorf("Error: %s", err) | |
37 | return | |
38 | } | |
39 | ||
40 | // Optionally, disable the QR Code border. | |
41 | q.DisableBorder = true | |
42 | ||
43 | // Optionally, set the colours. | |
44 | q.ForegroundColor = color.RGBA{R: 0x33, G: 0x33, B: 0x66, A: 0xff} | |
45 | q.BackgroundColor = color.RGBA{R: 0xef, G: 0xef, B: 0xef, A: 0xff} | |
46 | ||
47 | err = q.WriteFile(256, "example2.png") | |
48 | if err != nil { | |
49 | t.Errorf("Error: %s", err) | |
50 | return | |
51 | } | |
52 | } |
16 | 16 | size := flag.Int("s", 256, "image size (pixel)") |
17 | 17 | textArt := flag.Bool("t", false, "print as text-art on stdout") |
18 | 18 | negative := flag.Bool("i", false, "invert black and white") |
19 | disableBorder := flag.Bool("d", false, "disable QR Code border") | |
19 | 20 | flag.Usage = func() { |
20 | 21 | fmt.Fprintf(os.Stderr, `qrcode -- QR Code encoder in Go |
21 | 22 | https://github.com/skip2/go-qrcode |
51 | 52 | q, err = qrcode.New(content, qrcode.Highest) |
52 | 53 | checkError(err) |
53 | 54 | |
55 | if *disableBorder { | |
56 | q.DisableBorder = true | |
57 | } | |
58 | ||
54 | 59 | if *textArt { |
55 | 60 | art := q.ToString(*negative) |
56 | 61 | fmt.Println(art) |
50 | 50 | import ( |
51 | 51 | "bytes" |
52 | 52 | "errors" |
53 | "fmt" | |
53 | 54 | "image" |
54 | 55 | "image/color" |
55 | 56 | "image/png" |
134 | 135 | ForegroundColor color.Color |
135 | 136 | BackgroundColor color.Color |
136 | 137 | |
138 | // Disable the QR Code border. | |
139 | DisableBorder bool | |
140 | ||
137 | 141 | encoder *dataEncoder |
138 | 142 | version qrCodeVersion |
139 | 143 | |
192 | 196 | version: *chosenVersion, |
193 | 197 | } |
194 | 198 | |
195 | q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len())) | |
196 | ||
197 | 199 | return q, nil |
198 | 200 | } |
199 | 201 | |
200 | func newWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) { | |
202 | // NewWithForcedVersion constructs a QRCode of a specific version. | |
203 | // | |
204 | // var q *qrcode.QRCode | |
205 | // q, err := qrcode.NewWithForcedVersion("my content", 25, qrcode.Medium) | |
206 | // | |
207 | // An error occurs in case of invalid version. | |
208 | func NewWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) { | |
201 | 209 | var encoder *dataEncoder |
202 | 210 | |
203 | 211 | switch { |
208 | 216 | case version >= 27 && version <= 40: |
209 | 217 | encoder = newDataEncoder(dataEncoderType27To40) |
210 | 218 | default: |
211 | log.Fatalf("Invalid version %d (expected 1-40 inclusive)", version) | |
219 | return nil, fmt.Errorf("Invalid version %d (expected 1-40 inclusive)", version) | |
212 | 220 | } |
213 | 221 | |
214 | 222 | var encoded *bitset.Bitset |
222 | 230 | |
223 | 231 | if chosenVersion == nil { |
224 | 232 | return nil, errors.New("cannot find QR Code version") |
233 | } | |
234 | ||
235 | if encoded.Len() > chosenVersion.numDataBits() { | |
236 | return nil, fmt.Errorf("Cannot encode QR code: content too large for fixed size QR Code version %d (encoded length is %d bits, maximum length is %d bits)", | |
237 | version, | |
238 | encoded.Len(), | |
239 | chosenVersion.numDataBits()) | |
225 | 240 | } |
226 | 241 | |
227 | 242 | q := &QRCode{ |
238 | 253 | version: *chosenVersion, |
239 | 254 | } |
240 | 255 | |
241 | q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len())) | |
242 | ||
243 | 256 | return q, nil |
244 | 257 | } |
245 | 258 | |
250 | 263 | // The bitmap includes the required "quiet zone" around the QR Code to aid |
251 | 264 | // decoding. |
252 | 265 | func (q *QRCode) Bitmap() [][]bool { |
266 | // Build QR code. | |
267 | q.encode() | |
268 | ||
253 | 269 | return q.symbol.bitmap() |
254 | 270 | } |
255 | 271 | |
267 | 283 | // negative number to increase the scale of the image. e.g. a size of -5 causes |
268 | 284 | // each module (QR Code "pixel") to be 5px in size. |
269 | 285 | func (q *QRCode) Image(size int) image.Image { |
286 | // Build QR code. | |
287 | q.encode() | |
288 | ||
270 | 289 | // Minimum pixels (both width and height) required. |
271 | 290 | realSize := q.symbol.size |
272 | 291 | |
281 | 300 | size = realSize |
282 | 301 | } |
283 | 302 | |
284 | // Size of each module drawn. | |
285 | pixelsPerModule := size / realSize | |
286 | ||
287 | // Center the symbol within the image. | |
288 | offset := (size - realSize*pixelsPerModule) / 2 | |
289 | ||
303 | // Output image. | |
290 | 304 | rect := image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{size, size}} |
291 | 305 | |
292 | 306 | // Saves a few bytes to have them in this order |
294 | 308 | img := image.NewPaletted(rect, p) |
295 | 309 | fgClr := uint8(img.Palette.Index(q.ForegroundColor)) |
296 | 310 | |
311 | // QR code bitmap. | |
297 | 312 | bitmap := q.symbol.bitmap() |
298 | for y, row := range bitmap { | |
299 | for x, v := range row { | |
313 | ||
314 | // Map each image pixel to the nearest QR code module. | |
315 | modulesPerPixel := float64(realSize) / float64(size) | |
316 | for y := 0; y < size; y++ { | |
317 | y2 := int(float64(y) * modulesPerPixel) | |
318 | for x := 0; x < size; x++ { | |
319 | x2 := int(float64(x) * modulesPerPixel) | |
320 | ||
321 | v := bitmap[y2][x2] | |
322 | ||
300 | 323 | if v { |
301 | startX := x*pixelsPerModule + offset | |
302 | startY := y*pixelsPerModule + offset | |
303 | for i := startX; i < startX+pixelsPerModule; i++ { | |
304 | for j := startY; j < startY+pixelsPerModule; j++ { | |
305 | pos := img.PixOffset(i, j) | |
306 | img.Pix[pos] = fgClr | |
307 | } | |
308 | } | |
324 | pos := img.PixOffset(x, y) | |
325 | img.Pix[pos] = fgClr | |
309 | 326 | } |
310 | 327 | } |
311 | 328 | } |
370 | 387 | // encode completes the steps required to encode the QR Code. These include |
371 | 388 | // adding the terminator bits and padding, splitting the data into blocks and |
372 | 389 | // applying the error correction, and selecting the best data mask. |
373 | func (q *QRCode) encode(numTerminatorBits int) { | |
390 | func (q *QRCode) encode() { | |
391 | numTerminatorBits := q.version.numTerminatorBitsRequired(q.data.Len()) | |
392 | ||
374 | 393 | q.addTerminatorBits(numTerminatorBits) |
375 | 394 | q.addPadding() |
376 | 395 | |
383 | 402 | var s *symbol |
384 | 403 | var err error |
385 | 404 | |
386 | s, err = buildRegularSymbol(q.version, mask, encoded) | |
405 | s, err = buildRegularSymbol(q.version, mask, encoded, !q.DisableBorder) | |
387 | 406 | |
388 | 407 | if err != nil { |
389 | 408 | log.Panic(err.Error()) |
92 | 92 | version, |
93 | 93 | level) |
94 | 94 | |
95 | q, err := newWithForcedVersion( | |
95 | q, err := NewWithForcedVersion( | |
96 | 96 | fmt.Sprintf("v-%d l-%d", version, level), version, level) |
97 | 97 | if err != nil { |
98 | 98 | t.Fatal(err.Error()) |
195 | 195 | } |
196 | 196 | |
197 | 197 | cmd := exec.Command("zbarimg", "--quiet", "-Sdisable", |
198 | "-Sqrcode.enable", "/dev/stdin") | |
198 | "-Sqrcode.enable", "-") | |
199 | 199 | |
200 | 200 | var out bytes.Buffer |
201 | 201 |
150 | 150 | err.Error()) |
151 | 151 | } |
152 | 152 | |
153 | q.encode() | |
154 | ||
153 | 155 | const expectedMask int = 2 |
154 | 156 | |
155 | 157 | if q.mask != 2 { |
104 | 104 | ) |
105 | 105 | |
106 | 106 | func buildRegularSymbol(version qrCodeVersion, mask int, |
107 | data *bitset.Bitset) (*symbol, error) { | |
107 | data *bitset.Bitset, includeQuietZone bool) (*symbol, error) { | |
108 | ||
109 | quietZoneSize := 0 | |
110 | if includeQuietZone { | |
111 | quietZoneSize = version.quietZoneSize() | |
112 | } | |
113 | ||
108 | 114 | m := ®ularSymbol{ |
109 | 115 | version: version, |
110 | 116 | mask: mask, |
111 | 117 | data: data, |
112 | 118 | |
113 | symbol: newSymbol(version.symbolSize(), version.quietZoneSize()), | |
119 | symbol: newSymbol(version.symbolSize(), quietZoneSize), | |
114 | 120 | size: version.symbolSize(), |
115 | 121 | } |
116 | 122 |