// go-qrcode // Copyright 2014 Tom Harwood /* Package qrcode implements a QR Code encoder. A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be encoded. A QR Code contains error recovery information to aid reading damaged or obscured codes. There are four levels of error recovery: qrcode.{Low, Medium, High, Highest}. QR Codes with a higher recovery level are more robust to damage, at the cost of being physically larger. Three functions cover most use cases: - Create a PNG image: var png []byte png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256) - Create a PNG image and write to a file: err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png") - Create a PNG image with custom colors and write to file: err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png") 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. To generate a variable sized image instead, specify a negative size (in place of the 256 above), such as -4 or -5. Larger negative numbers create larger images: A size of -5 sets each module (QR Code "pixel") to be 5px wide/high. - Create a PNG image (variable size, with minimum white padding) and write to a file: err := qrcode.WriteFile("https://example.org", qrcode.Medium, -5, "qr.png") 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. This package implements a subset of QR Code 2005, as defined in ISO/IEC 18004:2006. */ package qrcode import ( "bytes" "errors" "image" "image/color" "image/png" "io" "io/ioutil" "log" "os" bitset "github.com/skip2/go-qrcode/bitset" reedsolomon "github.com/skip2/go-qrcode/reedsolomon" ) // Encode a QR Code and return a raw PNG image. // // size is both the image width and height in pixels. If size is too small then // a larger image is silently returned. Negative values for size cause a // variable sized image to be returned: See the documentation for Image(). // // To serve over HTTP, remember to send a Content-Type: image/png header. func Encode(content string, level RecoveryLevel, size int) ([]byte, error) { var q *QRCode q, err := New(content, level) if err != nil { return nil, err } return q.PNG(size) } // WriteFile encodes, then writes a QR Code to the given filename in PNG format. // // size is both the image width and height in pixels. If size is too small then // a larger image is silently written. Negative values for size cause a variable // sized image to be written: See the documentation for Image(). func WriteFile(content string, level RecoveryLevel, size int, filename string) error { var q *QRCode q, err := New(content, level) if err != nil { return err } return q.WriteFile(size, filename) } // WriteColorFile encodes, then writes a QR Code to the given filename in PNG format. // With WriteColorFile you can also specify the colors you want to use. // // size is both the image width and height in pixels. If size is too small then // a larger image is silently written. Negative values for size cause a variable // sized image to be written: See the documentation for Image(). func WriteColorFile(content string, level RecoveryLevel, size int, background, foreground color.Color, filename string) error { var q *QRCode q, err := New(content, level) q.BackgroundColor = background q.ForegroundColor = foreground if err != nil { return err } return q.WriteFile(size, filename) } // A QRCode represents a valid encoded QRCode. type QRCode struct { // Original content encoded. Content string // QR Code type. Level RecoveryLevel VersionNumber int // User settable drawing options. ForegroundColor color.Color BackgroundColor color.Color encoder *dataEncoder version qrCodeVersion data *bitset.Bitset symbol *symbol mask int } // New constructs a QRCode. // // var q *qrcode.QRCode // q, err := qrcode.New("my content", qrcode.Medium) // // An error occurs if the content is too long. func New(content string, level RecoveryLevel) (*QRCode, error) { encoders := []dataEncoderType{dataEncoderType1To9, dataEncoderType10To26, dataEncoderType27To40} var encoder *dataEncoder var encoded *bitset.Bitset var chosenVersion *qrCodeVersion var err error for _, t := range encoders { encoder = newDataEncoder(t) encoded, err = encoder.encode([]byte(content)) if err != nil { continue } chosenVersion = chooseQRCodeVersion(level, encoder, encoded.Len()) if chosenVersion != nil { break } } if err != nil { return nil, err } else if chosenVersion == nil { return nil, errors.New("content too long to encode") } q := &QRCode{ Content: content, Level: level, VersionNumber: chosenVersion.version, ForegroundColor: color.Black, BackgroundColor: color.White, encoder: encoder, data: encoded, version: *chosenVersion, } q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len())) return q, nil } func newWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) { var encoder *dataEncoder switch { case version >= 1 && version <= 9: encoder = newDataEncoder(dataEncoderType1To9) case version >= 10 && version <= 26: encoder = newDataEncoder(dataEncoderType10To26) case version >= 27 && version <= 40: encoder = newDataEncoder(dataEncoderType27To40) default: log.Fatalf("Invalid version %d (expected 1-40 inclusive)", version) } var encoded *bitset.Bitset encoded, err := encoder.encode([]byte(content)) if err != nil { return nil, err } chosenVersion := getQRCodeVersion(level, version) if chosenVersion == nil { return nil, errors.New("cannot find QR Code version") } q := &QRCode{ Content: content, Level: level, VersionNumber: chosenVersion.version, ForegroundColor: color.Black, BackgroundColor: color.White, encoder: encoder, data: encoded, version: *chosenVersion, } q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len())) return q, nil } // Bitmap returns the QR Code as a 2D array of 1-bit pixels. // // bitmap[y][x] is true if the pixel at (x, y) is set. // // The bitmap includes the required "quiet zone" around the QR Code to aid // decoding. func (q *QRCode) Bitmap() [][]bool { return q.symbol.bitmap() } // Image returns the QR Code as an image.Image. // // A positive size sets a fixed image width and height (e.g. 256 yields an // 256x256px image). // // Depending on the amount of data encoded, fixed size images can have different // amounts of padding (white space around the QR Code). As an alternative, a // variable sized image can be generated instead: // // A negative size causes a variable sized image to be returned. The image // returned is the minimum size required for the QR Code. Choose a larger // negative number to increase the scale of the image. e.g. a size of -5 causes // each module (QR Code "pixel") to be 5px in size. func (q *QRCode) Image(size int) image.Image { // Minimum pixels (both width and height) required. realSize := q.symbol.size // Variable size support. if size < 0 { size = size * -1 * realSize } // Actual pixels available to draw the symbol. Automatically increase the // image size if it's not large enough. if size < realSize { size = realSize } // Size of each module drawn. pixelsPerModule := size / realSize // Center the symbol within the image. offset := (size - realSize*pixelsPerModule) / 2 rect := image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{size, size}} // Saves a few bytes to have them in this order p := color.Palette([]color.Color{q.BackgroundColor, q.ForegroundColor}) img := image.NewPaletted(rect, p) for i := 0; i < size; i++ { for j := 0; j < size; j++ { img.Set(i, j, q.BackgroundColor) } } bitmap := q.symbol.bitmap() for y, row := range bitmap { for x, v := range row { if v { startX := x*pixelsPerModule + offset startY := y*pixelsPerModule + offset for i := startX; i < startX+pixelsPerModule; i++ { for j := startY; j < startY+pixelsPerModule; j++ { img.Set(i, j, q.ForegroundColor) } } } } } return img } // PNG returns the QR Code as a PNG image. // // size is both the image width and height in pixels. If size is too small then // a larger image is silently returned. Negative values for size cause a // variable sized image to be returned: See the documentation for Image(). func (q *QRCode) PNG(size int) ([]byte, error) { img := q.Image(size) encoder := png.Encoder{CompressionLevel: png.BestCompression} var b bytes.Buffer err := encoder.Encode(&b, img) if err != nil { return nil, err } return b.Bytes(), nil } // Write writes the QR Code as a PNG image to io.Writer. // // size is both the image width and height in pixels. If size is too small then // a larger image is silently written. Negative values for size cause a // variable sized image to be written: See the documentation for Image(). func (q *QRCode) Write(size int, out io.Writer) error { var png []byte png, err := q.PNG(size) if err != nil { return err } _, err = out.Write(png) return err } // WriteFile writes the QR Code as a PNG image to the specified file. // // size is both the image width and height in pixels. If size is too small then // a larger image is silently written. Negative values for size cause a // variable sized image to be written: See the documentation for Image(). func (q *QRCode) WriteFile(size int, filename string) error { var png []byte png, err := q.PNG(size) if err != nil { return err } return ioutil.WriteFile(filename, png, os.FileMode(0644)) } // encode completes the steps required to encode the QR Code. These include // adding the terminator bits and padding, splitting the data into blocks and // applying the error correction, and selecting the best data mask. func (q *QRCode) encode(numTerminatorBits int) { q.addTerminatorBits(numTerminatorBits) q.addPadding() encoded := q.encodeBlocks() const numMasks int = 8 penalty := 0 for mask := 0; mask < numMasks; mask++ { var s *symbol var err error s, err = buildRegularSymbol(q.version, mask, encoded) if err != nil { log.Panic(err.Error()) } numEmptyModules := s.numEmptyModules() if numEmptyModules != 0 { log.Panicf("bug: numEmptyModules is %d (expected 0) (version=%d)", numEmptyModules, q.VersionNumber) } p := s.penaltyScore() //log.Printf("mask=%d p=%3d p1=%3d p2=%3d p3=%3d p4=%d\n", mask, p, s.penalty1(), s.penalty2(), s.penalty3(), s.penalty4()) if q.symbol == nil || p < penalty { q.symbol = s q.mask = mask penalty = p } } } // addTerminatorBits adds final terminator bits to the encoded data. // // The number of terminator bits required is determined when the QR Code version // is chosen (which itself depends on the length of the data encoded). The // terminator bits are thus added after the QR Code version // is chosen, rather than at the data encoding stage. func (q *QRCode) addTerminatorBits(numTerminatorBits int) { q.data.AppendNumBools(numTerminatorBits, false) } // encodeBlocks takes the completed (terminated & padded) encoded data, splits // the data into blocks (as specified by the QR Code version), applies error // correction to each block, then interleaves the blocks together. // // The QR Code's final data sequence is returned. func (q *QRCode) encodeBlocks() *bitset.Bitset { // Split into blocks. type dataBlock struct { data *bitset.Bitset ecStartOffset int } block := make([]dataBlock, q.version.numBlocks()) start := 0 end := 0 blockID := 0 for _, b := range q.version.block { for j := 0; j < b.numBlocks; j++ { start = end end = start + b.numDataCodewords*8 // Apply error correction to each block. numErrorCodewords := b.numCodewords - b.numDataCodewords block[blockID].data = reedsolomon.Encode(q.data.Substr(start, end), numErrorCodewords) block[blockID].ecStartOffset = end - start blockID++ } } // Interleave the blocks. result := bitset.New() // Combine data blocks. working := true for i := 0; working; i += 8 { working = false for j, b := range block { if i >= block[j].ecStartOffset { continue } result.Append(b.data.Substr(i, i+8)) working = true } } // Combine error correction blocks. working = true for i := 0; working; i += 8 { working = false for j, b := range block { offset := i + block[j].ecStartOffset if offset >= block[j].data.Len() { continue } result.Append(b.data.Substr(offset, offset+8)) working = true } } // Append remainder bits. result.AppendNumBools(q.version.numRemainderBits, false) return result } // max returns the maximum of a and b. func max(a int, b int) int { if a > b { return a } return b } // addPadding pads the encoded data upto the full length required. func (q *QRCode) addPadding() { numDataBits := q.version.numDataBits() if q.data.Len() == numDataBits { return } // Pad to the nearest codeword boundary. q.data.AppendNumBools(q.version.numBitsToPadToCodeword(q.data.Len()), false) // Pad codewords 0b11101100 and 0b00010001. padding := [2]*bitset.Bitset{ bitset.New(true, true, true, false, true, true, false, false), bitset.New(false, false, false, true, false, false, false, true), } // Insert pad codewords alternately. i := 0 for numDataBits-q.data.Len() >= 8 { q.data.Append(padding[i]) i = 1 - i // Alternate between 0 and 1. } if q.data.Len() != numDataBits { log.Panicf("BUG: got len %d, expected %d", q.data.Len(), numDataBits) } } // ToString produces a multi-line string that forms a QR-code image. func (q *QRCode) ToString(inverseColor bool) string { bits := q.Bitmap() var buf bytes.Buffer for y := range bits { for x := range bits[y] { if bits[y][x] != inverseColor { buf.WriteString(" ") } else { buf.WriteString("██") } } buf.WriteString("\n") } return buf.String() }