summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/skip2/go-qrcode/qrcode.go
diff options
context:
space:
mode:
authorKrzysiek Madejski <krzysztof.madejski@epf.org.pl>2019-02-21 20:28:13 +0100
committerWim <wim@42.be>2019-02-21 20:28:13 +0100
commit55e79063d6edbbf4560fd14edc45ce9558afaf7a (patch)
tree9ee9470119066556210a9226ae05b2c73ebda5e6 /vendor/github.com/skip2/go-qrcode/qrcode.go
parent46f4bbb3b5e93ff489c0125c66b1c29fcb001e22 (diff)
downloadmatterbridge-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.go589
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()
+}