diff options
author | Krzysiek Madejski <krzysztof.madejski@epf.org.pl> | 2019-02-21 20:28:13 +0100 |
---|---|---|
committer | Wim <wim@42.be> | 2019-02-21 20:28:13 +0100 |
commit | 55e79063d6edbbf4560fd14edc45ce9558afaf7a (patch) | |
tree | 9ee9470119066556210a9226ae05b2c73ebda5e6 /vendor/github.com/skip2/go-qrcode/qrcode.go | |
parent | 46f4bbb3b5e93ff489c0125c66b1c29fcb001e22 (diff) | |
download | matterbridge-msglm-55e79063d6edbbf4560fd14edc45ce9558afaf7a.tar.gz matterbridge-msglm-55e79063d6edbbf4560fd14edc45ce9558afaf7a.tar.bz2 matterbridge-msglm-55e79063d6edbbf4560fd14edc45ce9558afaf7a.zip |
Add initial WhatsApp support (#711)
Diffstat (limited to 'vendor/github.com/skip2/go-qrcode/qrcode.go')
-rw-r--r-- | vendor/github.com/skip2/go-qrcode/qrcode.go | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/vendor/github.com/skip2/go-qrcode/qrcode.go b/vendor/github.com/skip2/go-qrcode/qrcode.go new file mode 100644 index 00000000..9428d863 --- /dev/null +++ b/vendor/github.com/skip2/go-qrcode/qrcode.go @@ -0,0 +1,589 @@ +// 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) + fgClr := uint8(img.Palette.Index(q.ForegroundColor)) + + 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++ { + pos := img.PixOffset(i, j) + img.Pix[pos] = fgClr + } + } + } + } + } + + 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() +} + +// 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() +} |