2019-02-21 11:28:13 -08:00
// 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
256 x256px 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 5 px 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 , 0 89 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"
2021-06-16 12:00:49 -07:00
"fmt"
2019-02-21 11:28:13 -08:00
"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
2021-06-16 12:00:49 -07:00
// Disable the QR Code border.
DisableBorder bool
2019-02-21 11:28:13 -08:00
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 ,
}
return q , nil
}
2021-06-16 12:00:49 -07:00
// NewWithForcedVersion constructs a QRCode of a specific version.
//
// var q *qrcode.QRCode
// q, err := qrcode.NewWithForcedVersion("my content", 25, qrcode.Medium)
//
// An error occurs in case of invalid version.
func NewWithForcedVersion ( content string , version int , level RecoveryLevel ) ( * QRCode , error ) {
2019-02-21 11:28:13 -08:00
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 :
2021-06-16 12:00:49 -07:00
return nil , fmt . Errorf ( "Invalid version %d (expected 1-40 inclusive)" , version )
2019-02-21 11:28:13 -08:00
}
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" )
}
2021-06-16 12:00:49 -07:00
if encoded . Len ( ) > chosenVersion . numDataBits ( ) {
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)" ,
version ,
encoded . Len ( ) ,
chosenVersion . numDataBits ( ) )
}
2019-02-21 11:28:13 -08:00
q := & QRCode {
Content : content ,
Level : level ,
VersionNumber : chosenVersion . version ,
ForegroundColor : color . Black ,
BackgroundColor : color . White ,
encoder : encoder ,
data : encoded ,
version : * chosenVersion ,
}
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 {
2021-06-16 12:00:49 -07:00
// Build QR code.
q . encode ( )
2019-02-21 11:28:13 -08:00
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 {
2021-06-16 12:00:49 -07:00
// Build QR code.
q . encode ( )
2019-02-21 11:28:13 -08:00
// 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
}
2021-06-16 12:00:49 -07:00
// Output image.
2019-02-21 11:28:13 -08:00
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 )
fgClr := uint8 ( img . Palette . Index ( q . ForegroundColor ) )
2021-06-16 12:00:49 -07:00
// QR code bitmap.
2019-02-21 11:28:13 -08:00
bitmap := q . symbol . bitmap ( )
2021-06-16 12:00:49 -07:00
// Map each image pixel to the nearest QR code module.
modulesPerPixel := float64 ( realSize ) / float64 ( size )
for y := 0 ; y < size ; y ++ {
y2 := int ( float64 ( y ) * modulesPerPixel )
for x := 0 ; x < size ; x ++ {
x2 := int ( float64 ( x ) * modulesPerPixel )
v := bitmap [ y2 ] [ x2 ]
2019-02-21 11:28:13 -08:00
if v {
2021-06-16 12:00:49 -07:00
pos := img . PixOffset ( x , y )
img . Pix [ pos ] = fgClr
2019-02-21 11:28:13 -08:00
}
}
}
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.
2021-06-16 12:00:49 -07:00
func ( q * QRCode ) encode ( ) {
numTerminatorBits := q . version . numTerminatorBitsRequired ( q . data . Len ( ) )
2019-02-21 11:28:13 -08:00
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
2021-06-16 12:00:49 -07:00
s , err = buildRegularSymbol ( q . version , mask , encoded , ! q . DisableBorder )
2019-02-21 11:28:13 -08:00
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 ( )
}
// ToSmallString produces a multi-line string that forms a QR-code image, a
// factor two smaller in x and y then ToString.
func ( q * QRCode ) ToSmallString ( inverseColor bool ) string {
bits := q . Bitmap ( )
var buf bytes . Buffer
// if there is an odd number of rows, the last one needs special treatment
for y := 0 ; y < len ( bits ) - 1 ; y += 2 {
for x := range bits [ y ] {
if bits [ y ] [ x ] == bits [ y + 1 ] [ x ] {
if bits [ y ] [ x ] != inverseColor {
buf . WriteString ( " " )
} else {
buf . WriteString ( "█" )
}
} else {
if bits [ y ] [ x ] != inverseColor {
buf . WriteString ( "▄" )
} else {
buf . WriteString ( "▀" )
}
}
}
buf . WriteString ( "\n" )
}
// special treatment for the last row if odd
if len ( bits ) % 2 == 1 {
y := len ( bits ) - 1
for x := range bits [ y ] {
if bits [ y ] [ x ] != inverseColor {
buf . WriteString ( " " )
} else {
buf . WriteString ( "▀" )
}
}
buf . WriteString ( "\n" )
}
return buf . String ( )
}