From 603decabf6c44df7d331356e1f1c2d44b2239f3b Mon Sep 17 00:00:00 2001 From: Dmitry Verkhoturov Date: Mon, 31 Jan 2022 00:07:05 +0300 Subject: [PATCH] add QR API endpoint for Telegram auth and notifications Telegram authentication requires you to open a chat on the phone. It's convenient to have a QR code for the case when you want to log in on the computer but have Telegram only on your phone and would be able to scan the QR instead of copy-pasting the link from the computer to the phone any other way. Originally we thought of generating QR on the client but found backend-generated QR a better alternative because we avoid adding one more JavaScript dependency to the frontend that way. --- backend/_example/memory_store/go.sum | 1 + backend/app/rest/api/rest.go | 1 + backend/app/rest/api/rest_public.go | 29 + backend/app/rest/api/rest_public_test.go | 39 + backend/app/rest/api/testdata/qr_test.png | Bin 0 -> 430 bytes backend/go.mod | 1 + backend/go.sum | 2 + backend/remark.rest | 3 + .../github.com/skip2/go-qrcode/.gitignore | 4 + .../github.com/skip2/go-qrcode/.travis.yml | 8 + .../vendor/github.com/skip2/go-qrcode/LICENSE | 19 + .../github.com/skip2/go-qrcode/README.md | 86 + .../skip2/go-qrcode/bitset/bitset.go | 273 ++ .../github.com/skip2/go-qrcode/encoder.go | 486 +++ .../vendor/github.com/skip2/go-qrcode/go.mod | 3 + .../github.com/skip2/go-qrcode/qrcode.go | 608 ++++ .../skip2/go-qrcode/reedsolomon/gf2_8.go | 387 +++ .../skip2/go-qrcode/reedsolomon/gf_poly.go | 216 ++ .../go-qrcode/reedsolomon/reed_solomon.go | 73 + .../skip2/go-qrcode/regular_symbol.go | 315 ++ .../github.com/skip2/go-qrcode/symbol.go | 309 ++ .../github.com/skip2/go-qrcode/version.go | 3050 +++++++++++++++++ backend/vendor/modules.txt | 5 + 23 files changed, 5918 insertions(+) create mode 100644 backend/app/rest/api/testdata/qr_test.png create mode 100644 backend/vendor/github.com/skip2/go-qrcode/.gitignore create mode 100644 backend/vendor/github.com/skip2/go-qrcode/.travis.yml create mode 100644 backend/vendor/github.com/skip2/go-qrcode/LICENSE create mode 100644 backend/vendor/github.com/skip2/go-qrcode/README.md create mode 100644 backend/vendor/github.com/skip2/go-qrcode/bitset/bitset.go create mode 100644 backend/vendor/github.com/skip2/go-qrcode/encoder.go create mode 100644 backend/vendor/github.com/skip2/go-qrcode/go.mod create mode 100644 backend/vendor/github.com/skip2/go-qrcode/qrcode.go create mode 100644 backend/vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go create mode 100644 backend/vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go create mode 100644 backend/vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go create mode 100644 backend/vendor/github.com/skip2/go-qrcode/regular_symbol.go create mode 100644 backend/vendor/github.com/skip2/go-qrcode/symbol.go create mode 100644 backend/vendor/github.com/skip2/go-qrcode/version.go diff --git a/backend/_example/memory_store/go.sum b/backend/_example/memory_store/go.sum index 12c9b481..c2df98dc 100644 --- a/backend/_example/memory_store/go.sum +++ b/backend/_example/memory_store/go.sum @@ -278,6 +278,7 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/slack-go/slack v0.10.1/go.mod h1:wWL//kk0ho+FcQXcBTmEafUI5dz4qz5f4mMk8oIkioQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index d96fd54a..9c3309ab 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -272,6 +272,7 @@ func (s *Rest) routes() chi.Router { ropen.Use(tollbooth_chi.LimitHandler(tollbooth.NewLimiter(10, nil))) ropen.Use(authMiddleware.Trace, logInfoWithBody) ropen.Get("/picture/{user}/{id}", s.pubRest.loadPictureCtrl) + ropen.Get("/qr/telegram", s.pubRest.telegramQrCtrl) }) // protected routes, require auth diff --git a/backend/app/rest/api/rest_public.go b/backend/app/rest/api/rest_public.go index 4c70ab80..1ce0ed4e 100644 --- a/backend/app/rest/api/rest_public.go +++ b/backend/app/rest/api/rest_public.go @@ -18,6 +18,7 @@ import ( log "github.com/go-pkgz/lgr" R "github.com/go-pkgz/rest" "github.com/pkg/errors" + "github.com/skip2/go-qrcode" "github.com/umputun/remark42/backend/app/rest" "github.com/umputun/remark42/backend/app/store" @@ -364,6 +365,34 @@ func (s *public) robotsCtrl(w http.ResponseWriter, r *http.Request) { render.PlainText(w, r, "User-agent: *\nDisallow: /auth/\nDisallow: /api/\n"+strings.Join(allowed, "\n")+"\n") } +// GET /qr/telegram - generates QR for provided URL, used for Telegram auth and notifications subscription. The first +// step of both is the user opening a link to Telegram and writing bot a message, and the user might try to log in +// from a computer but have Telegram installed only on the mobile device. +// Provided text should start with https://t.me/. +func (s *public) telegramQrCtrl(w http.ResponseWriter, r *http.Request) { + text := r.URL.Query().Get("url") + if text == "" { + rest.SendErrorJSON(w, r, http.StatusBadRequest, errors.New("missing parameter"), "text parameter is required", rest.ErrInternal) + return + } + + if !strings.HasPrefix(text, "https://t.me/") { + rest.SendErrorJSON(w, r, http.StatusBadRequest, errors.New("wrong parameter"), "text parameter should start with https://t.me/", rest.ErrInternal) + return + } + + png, err := qrcode.Encode(text, qrcode.Medium, 256) + if err != nil { + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't generate QR", rest.ErrInternal) + return + } + + w.Header().Set("Content-Type", "image/png") + if _, err = w.Write(png); err != nil { + log.Printf("[WARN] can't render qr, %v", err) + } +} + func (s *public) applyView(comments []store.Comment, view string) []store.Comment { if strings.EqualFold(view, "user") { projection := make([]store.Comment, 0, len(comments)) diff --git a/backend/app/rest/api/rest_public_test.go b/backend/app/rest/api/rest_public_test.go index d007ba15..aa7ba661 100644 --- a/backend/app/rest/api/rest_public_test.go +++ b/backend/app/rest/api/rest_public_test.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net/http" + "os" "strings" "testing" "time" @@ -615,6 +616,44 @@ func TestRest_Config(t *testing.T) { assert.Equal(t, false, j["admin_edit"].(bool)) } +func TestRest_QR(t *testing.T) { + ts, _, teardown := startupT(t) + defer teardown() + + // missing parameter + body, code := get(t, ts.URL+"/api/v1/qr/telegram") + assert.Equal(t, 400, code) + assert.Equal(t, "{\"code\":0,\"details\":\"text parameter is required\",\"error\":\"missing parameter\"}\n", body) + + // too long request to build the qr + body, code = get(t, ts.URL+"/api/v1/qr/telegram?url=https://t.me/"+strings.Repeat("string", 1000)) + assert.Equal(t, 500, code) + assert.Equal(t, "{\"code\":0,\"details\":\"can't generate QR\",\"error\":\"content too long to encode\"}\n", body) + + // wrong request + body, code = get(t, ts.URL+"/api/v1/qr/telegram?url=nonsense") + assert.Equal(t, 400, code) + assert.Equal(t, "{\"code\":0,\"details\":\"text parameter should start with https://t.me/\",\"error\":\"wrong parameter\"}\n", body) + + // correct request + r, err := http.Get(ts.URL + "/api/v1/qr/telegram?url=https://t.me/BotFather") + require.NoError(t, err) + bdy, err := io.ReadAll(r.Body) + require.NoError(t, err) + require.NoError(t, r.Body.Close()) + require.NotEmpty(t, bdy) + assert.Equal(t, "image/png", r.Header.Get("Content-Type")) + assert.Equal(t, 200, r.StatusCode) + + // compare the image + fh, err := os.Open("testdata/qr_test.png") + defer func() { assert.NoError(t, fh.Close()) }() + assert.NoError(t, err) + img, err := io.ReadAll(fh) + assert.NoError(t, err) + assert.Equal(t, img, bdy) +} + func TestRest_Info(t *testing.T) { ts, srv, teardown := startupT(t) defer teardown() diff --git a/backend/app/rest/api/testdata/qr_test.png b/backend/app/rest/api/testdata/qr_test.png new file mode 100644 index 0000000000000000000000000000000000000000..66bf2a0839b48f92d48460784edbebb4c1fa2d74 GIT binary patch literal 430 zcmV;f0a5;mP)1d*}fzHcFMhWOIN6h949FG!h^I#W2vd zVN3%a+%NoLQ(*amD0GUzqj7czG$$n=T5PdC1nKU;OMz|CBI?t4+#Hy0x*-jN9>qD7 z3gxmH01b$M9vTNBq!brl*Z`)(^uXYLSW}Tw$=5Z>2H>V_NeuA7h+3yl!x#YoO!BKx zoDbYEx#U}rZU7MH1Ic#4`5<-3<>Ct?0Q-X11MvmtLrs-_8pixRVXXK=W6b+!MQJ> zXAsvR-2=iF;I&R|jf3yE!R-O{MZkI#Q(s)iq6y&kNb + +Package qrcode implements a QR Code encoder. [![Build Status](https://travis-ci.org/skip2/go-qrcode.svg?branch=master)](https://travis-ci.org/skip2/go-qrcode) + +A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be encoded, with URLs being a popular choice :) + +Each QR Code contains error recovery information to aid reading damaged or obscured codes. There are four levels of error recovery: Low, medium, high and highest. QR Codes with a higher recovery level are more robust to damage, at the cost of being physically larger. + +## Install + + go get -u github.com/skip2/go-qrcode/... + +A command-line tool `qrcode` will be built into `$GOPATH/bin/`. + +## Usage + + import qrcode "github.com/skip2/go-qrcode" + +- **Create a 256x256 PNG image:** + + var png []byte + png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256) + +- **Create a 256x256 PNG image and write to a file:** + + err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png") + +- **Create a 256x256 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. + +## Documentation + +[![godoc](https://godoc.org/github.com/skip2/go-qrcode?status.png)](https://godoc.org/github.com/skip2/go-qrcode) + +## Demoapp + +[http://go-qrcode.appspot.com](http://go-qrcode.appspot.com) + +## CLI + +A command-line tool `qrcode` will be built into `$GOPATH/bin/`. + +``` +qrcode -- QR Code encoder in Go +https://github.com/skip2/go-qrcode + +Flags: + -d disable QR Code border + -i invert black and white + -o string + out PNG file prefix, empty for stdout + -s int + image size (pixel) (default 256) + -t print as text-art on stdout + +Usage: + 1. Arguments except for flags are joined by " " and used to generate QR code. + Default output is STDOUT, pipe to imagemagick command "display" to display + on any X server. + + qrcode hello word | display + + 2. Save to file if "display" not available: + + qrcode "homepage: https://github.com/skip2/go-qrcode" > out.png + +``` +## Maximum capacity +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. + +## Borderless QR Codes + +To aid QR Code reading software, QR codes have a built in whitespace border. + +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. + +## Links + +- [http://en.wikipedia.org/wiki/QR_code](http://en.wikipedia.org/wiki/QR_code) +- [ISO/IEC 18004:2006](http://www.iso.org/iso/catalogue_detail.htm?csnumber=43655) - Main QR Code specification (approx CHF 198,00)
+- [https://github.com/qpliu/qrencode-go/](https://github.com/qpliu/qrencode-go/) - alternative Go QR encoding library based on [ZXing](https://github.com/zxing/zxing) diff --git a/backend/vendor/github.com/skip2/go-qrcode/bitset/bitset.go b/backend/vendor/github.com/skip2/go-qrcode/bitset/bitset.go new file mode 100644 index 00000000..fba3bf4d --- /dev/null +++ b/backend/vendor/github.com/skip2/go-qrcode/bitset/bitset.go @@ -0,0 +1,273 @@ +// go-qrcode +// Copyright 2014 Tom Harwood + +// Package bitset implements an append only bit array. +// +// To create a Bitset and append some bits: +// // Bitset Contents +// b := bitset.New() // {} +// b.AppendBools(true, true, false) // {1, 1, 0} +// b.AppendBools(true) // {1, 1, 0, 1} +// b.AppendValue(0x02, 4) // {1, 1, 0, 1, 0, 0, 1, 0} +// +// To read values: +// +// len := b.Len() // 8 +// v := b.At(0) // 1 +// v = b.At(1) // 1 +// v = b.At(2) // 0 +// v = b.At(8) // 0 +package bitset + +import ( + "bytes" + "fmt" + "log" +) + +const ( + b0 = false + b1 = true +) + +// Bitset stores an array of bits. +type Bitset struct { + // The number of bits stored. + numBits int + + // Storage for individual bits. + bits []byte +} + +// New returns an initialised Bitset with optional initial bits v. +func New(v ...bool) *Bitset { + b := &Bitset{numBits: 0, bits: make([]byte, 0)} + b.AppendBools(v...) + + return b +} + +// Clone returns a copy. +func Clone(from *Bitset) *Bitset { + return &Bitset{numBits: from.numBits, bits: from.bits[:]} +} + +// Substr returns a substring, consisting of the bits from indexes start to end. +func (b *Bitset) Substr(start int, end int) *Bitset { + if start > end || end > b.numBits { + log.Panicf("Out of range start=%d end=%d numBits=%d", start, end, b.numBits) + } + + result := New() + result.ensureCapacity(end - start) + + for i := start; i < end; i++ { + if b.At(i) { + result.bits[result.numBits/8] |= 0x80 >> uint(result.numBits%8) + } + result.numBits++ + } + + return result +} + +// NewFromBase2String constructs and returns a Bitset from a string. The string +// consists of '1', '0' or ' ' characters, e.g. "1010 0101". The '1' and '0' +// characters represent true/false bits respectively, and ' ' characters are +// ignored. +// +// The function panics if the input string contains other characters. +func NewFromBase2String(b2string string) *Bitset { + b := &Bitset{numBits: 0, bits: make([]byte, 0)} + + for _, c := range b2string { + switch c { + case '1': + b.AppendBools(true) + case '0': + b.AppendBools(false) + case ' ': + default: + log.Panicf("Invalid char %c in NewFromBase2String", c) + } + } + + return b +} + +// AppendBytes appends a list of whole bytes. +func (b *Bitset) AppendBytes(data []byte) { + for _, d := range data { + b.AppendByte(d, 8) + } +} + +// AppendByte appends the numBits least significant bits from value. +func (b *Bitset) AppendByte(value byte, numBits int) { + b.ensureCapacity(numBits) + + if numBits > 8 { + log.Panicf("numBits %d out of range 0-8", numBits) + } + + for i := numBits - 1; i >= 0; i-- { + if value&(1<> uint(b.numBits%8) + } + + b.numBits++ + } +} + +// AppendUint32 appends the numBits least significant bits from value. +func (b *Bitset) AppendUint32(value uint32, numBits int) { + b.ensureCapacity(numBits) + + if numBits > 32 { + log.Panicf("numBits %d out of range 0-32", numBits) + } + + for i := numBits - 1; i >= 0; i-- { + if value&(1<> uint(b.numBits%8) + } + + b.numBits++ + } +} + +// ensureCapacity ensures the Bitset can store an additional |numBits|. +// +// The underlying array is expanded if necessary. To prevent frequent +// reallocation, expanding the underlying array at least doubles its capacity. +func (b *Bitset) ensureCapacity(numBits int) { + numBits += b.numBits + + newNumBytes := numBits / 8 + if numBits%8 != 0 { + newNumBytes++ + } + + if len(b.bits) >= newNumBytes { + return + } + + b.bits = append(b.bits, make([]byte, newNumBytes+2*len(b.bits))...) +} + +// Append bits copied from |other|. +// +// The new length is b.Len() + other.Len(). +func (b *Bitset) Append(other *Bitset) { + b.ensureCapacity(other.numBits) + + for i := 0; i < other.numBits; i++ { + if other.At(i) { + b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) + } + b.numBits++ + } +} + +// AppendBools appends bits to the Bitset. +func (b *Bitset) AppendBools(bits ...bool) { + b.ensureCapacity(len(bits)) + + for _, v := range bits { + if v { + b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) + } + b.numBits++ + } +} + +// AppendNumBools appends num bits of value value. +func (b *Bitset) AppendNumBools(num int, value bool) { + for i := 0; i < num; i++ { + b.AppendBools(value) + } +} + +// String returns a human readable representation of the Bitset's contents. +func (b *Bitset) String() string { + var bitString string + for i := 0; i < b.numBits; i++ { + if (i % 8) == 0 { + bitString += " " + } + + if (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 { + bitString += "1" + } else { + bitString += "0" + } + } + + return fmt.Sprintf("numBits=%d, bits=%s", b.numBits, bitString) +} + +// Len returns the length of the Bitset in bits. +func (b *Bitset) Len() int { + return b.numBits +} + +// Bits returns the contents of the Bitset. +func (b *Bitset) Bits() []bool { + result := make([]bool, b.numBits) + + var i int + for i = 0; i < b.numBits; i++ { + result[i] = (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 + } + + return result +} + +// At returns the value of the bit at |index|. +func (b *Bitset) At(index int) bool { + if index >= b.numBits { + log.Panicf("Index %d out of range", index) + } + + return (b.bits[index/8] & (0x80 >> byte(index%8))) != 0 +} + +// Equals returns true if the Bitset equals other. +func (b *Bitset) Equals(other *Bitset) bool { + if b.numBits != other.numBits { + return false + } + + if !bytes.Equal(b.bits[0:b.numBits/8], other.bits[0:b.numBits/8]) { + return false + } + + for i := 8 * (b.numBits / 8); i < b.numBits; i++ { + a := (b.bits[i/8] & (0x80 >> byte(i%8))) + b := (other.bits[i/8] & (0x80 >> byte(i%8))) + + if a != b { + return false + } + } + + return true +} + +// ByteAt returns a byte consisting of upto 8 bits starting at index. +func (b *Bitset) ByteAt(index int) byte { + if index < 0 || index >= b.numBits { + log.Panicf("Index %d out of range", index) + } + + var result byte + + for i := index; i < index+8 && i < b.numBits; i++ { + result <<= 1 + if b.At(i) { + result |= 1 + } + } + + return result +} diff --git a/backend/vendor/github.com/skip2/go-qrcode/encoder.go b/backend/vendor/github.com/skip2/go-qrcode/encoder.go new file mode 100644 index 00000000..6a809cfe --- /dev/null +++ b/backend/vendor/github.com/skip2/go-qrcode/encoder.go @@ -0,0 +1,486 @@ +// go-qrcode +// Copyright 2014 Tom Harwood + +package qrcode + +import ( + "errors" + "log" + + bitset "github.com/skip2/go-qrcode/bitset" +) + +// Data encoding. +// +// The main data portion of a QR Code consists of one or more segments of data. +// A segment consists of: +// +// - The segment Data Mode: numeric, alphanumeric, or byte. +// - The length of segment in bits. +// - Encoded data. +// +// For example, the string "123ZZ#!#!" may be represented as: +// +// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"] +// +// Multiple data modes exist to minimise the size of encoded data. For example, +// 8-bit bytes require 8 bits to encode each, but base 10 numeric data can be +// encoded at a higher density of 3 numbers (e.g. 123) per 10 bits. +// +// Some data can be represented in multiple modes. Numeric data can be +// represented in all three modes, whereas alphanumeric data (e.g. 'A') can be +// represented in alphanumeric and byte mode. +// +// Starting a new segment (to use a different Data Mode) has a cost, the bits to +// state the new segment Data Mode and length. To minimise each QR Code's symbol +// size, an optimisation routine coalesces segment types where possible, to +// reduce the encoded data length. +// +// There are several other data modes available (e.g. Kanji mode) which are not +// implemented here. + +// A segment encoding mode. +type dataMode uint8 + +const ( + // Each dataMode is a subset of the subsequent dataMode: + // dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte + // + // This ordering is important for determining which data modes a character can + // be encoded with. E.g. 'E' can be encoded in both dataModeAlphanumeric and + // dataModeByte. + dataModeNone dataMode = 1 << iota + dataModeNumeric + dataModeAlphanumeric + dataModeByte +) + +// dataModeString returns d as a short printable string. +func dataModeString(d dataMode) string { + switch d { + case dataModeNone: + return "none" + case dataModeNumeric: + return "numeric" + case dataModeAlphanumeric: + return "alphanumeric" + case dataModeByte: + return "byte" + } + + return "unknown" +} + +type dataEncoderType uint8 + +const ( + dataEncoderType1To9 dataEncoderType = iota + dataEncoderType10To26 + dataEncoderType27To40 +) + +// segment is a single segment of data. +type segment struct { + // Data Mode (e.g. numeric). + dataMode dataMode + + // segment data (e.g. "abc"). + data []byte +} + +// A dataEncoder encodes data for a particular QR Code version. +type dataEncoder struct { + // Minimum & maximum versions supported. + minVersion int + maxVersion int + + // Mode indicator bit sequences. + numericModeIndicator *bitset.Bitset + alphanumericModeIndicator *bitset.Bitset + byteModeIndicator *bitset.Bitset + + // Character count lengths. + numNumericCharCountBits int + numAlphanumericCharCountBits int + numByteCharCountBits int + + // The raw input data. + data []byte + + // The data classified into unoptimised segments. + actual []segment + + // The data classified into optimised segments. + optimised []segment +} + +// newDataEncoder constructs a dataEncoder. +func newDataEncoder(t dataEncoderType) *dataEncoder { + d := &dataEncoder{} + + switch t { + case dataEncoderType1To9: + d = &dataEncoder{ + minVersion: 1, + maxVersion: 9, + numericModeIndicator: bitset.New(b0, b0, b0, b1), + alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), + byteModeIndicator: bitset.New(b0, b1, b0, b0), + numNumericCharCountBits: 10, + numAlphanumericCharCountBits: 9, + numByteCharCountBits: 8, + } + case dataEncoderType10To26: + d = &dataEncoder{ + minVersion: 10, + maxVersion: 26, + numericModeIndicator: bitset.New(b0, b0, b0, b1), + alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), + byteModeIndicator: bitset.New(b0, b1, b0, b0), + numNumericCharCountBits: 12, + numAlphanumericCharCountBits: 11, + numByteCharCountBits: 16, + } + case dataEncoderType27To40: + d = &dataEncoder{ + minVersion: 27, + maxVersion: 40, + numericModeIndicator: bitset.New(b0, b0, b0, b1), + alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), + byteModeIndicator: bitset.New(b0, b1, b0, b0), + numNumericCharCountBits: 14, + numAlphanumericCharCountBits: 13, + numByteCharCountBits: 16, + } + default: + log.Panic("Unknown dataEncoderType") + } + + return d +} + +// encode data as one or more segments and return the encoded data. +// +// The returned data does not include the terminator bit sequence. +func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) { + d.data = data + d.actual = nil + d.optimised = nil + + if len(data) == 0 { + return nil, errors.New("no data to encode") + } + + // Classify data into unoptimised segments. + highestRequiredMode := d.classifyDataModes() + + // Optimise segments. + err := d.optimiseDataModes() + if err != nil { + return nil, err + } + + // Check if a single byte encoded segment would be more efficient. + optimizedLength := 0 + for _, s := range d.optimised { + length, err := d.encodedLength(s.dataMode, len(s.data)) + if err != nil { + return nil, err + } + optimizedLength += length + } + + singleByteSegmentLength, err := d.encodedLength(highestRequiredMode, len(d.data)) + if err != nil { + return nil, err + } + + if singleByteSegmentLength <= optimizedLength { + d.optimised = []segment{segment{dataMode: highestRequiredMode, data: d.data}} + } + + // Encode data. + encoded := bitset.New() + for _, s := range d.optimised { + d.encodeDataRaw(s.data, s.dataMode, encoded) + } + + return encoded, nil +} + +// classifyDataModes classifies the raw data into unoptimised segments. +// e.g. "123ZZ#!#!" => +// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"]. +// +// Returns the highest data mode needed to encode the data. e.g. for a mixed +// numeric/alphanumeric input, the highest is alphanumeric. +// +// dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte +func (d *dataEncoder) classifyDataModes() dataMode { + var start int + mode := dataModeNone + highestRequiredMode := mode + + for i, v := range d.data { + newMode := dataModeNone + switch { + case v >= 0x30 && v <= 0x39: + newMode = dataModeNumeric + case v == 0x20 || v == 0x24 || v == 0x25 || v == 0x2a || v == 0x2b || v == + 0x2d || v == 0x2e || v == 0x2f || v == 0x3a || (v >= 0x41 && v <= 0x5a): + newMode = dataModeAlphanumeric + default: + newMode = dataModeByte + } + + if newMode != mode { + if i > 0 { + d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:i]}) + + start = i + } + + mode = newMode + } + + if newMode > highestRequiredMode { + highestRequiredMode = newMode + } + } + + d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]}) + + return highestRequiredMode +} + +// optimiseDataModes optimises the list of segments to reduce the overall output +// encoded data length. +// +// The algorithm coalesces adjacent segments. segments are only coalesced when +// the Data Modes are compatible, and when the coalesced segment has a shorter +// encoded length than separate segments. +// +// Multiple segments may be coalesced. For example a string of alternating +// alphanumeric/numeric segments ANANANANA can be optimised to just A. +func (d *dataEncoder) optimiseDataModes() error { + for i := 0; i < len(d.actual); { + mode := d.actual[i].dataMode + numChars := len(d.actual[i].data) + + j := i + 1 + for j < len(d.actual) { + nextNumChars := len(d.actual[j].data) + nextMode := d.actual[j].dataMode + + if nextMode > mode { + break + } + + coalescedLength, err := d.encodedLength(mode, numChars+nextNumChars) + + if err != nil { + return err + } + + seperateLength1, err := d.encodedLength(mode, numChars) + + if err != nil { + return err + } + + seperateLength2, err := d.encodedLength(nextMode, nextNumChars) + + if err != nil { + return err + } + + if coalescedLength < seperateLength1+seperateLength2 { + j++ + numChars += nextNumChars + } else { + break + } + } + + optimised := segment{dataMode: mode, + data: make([]byte, 0, numChars)} + + for k := i; k < j; k++ { + optimised.data = append(optimised.data, d.actual[k].data...) + } + + d.optimised = append(d.optimised, optimised) + + i = j + } + + return nil +} + +// encodeDataRaw encodes data in dataMode. The encoded data is appended to +// encoded. +func (d *dataEncoder) encodeDataRaw(data []byte, dataMode dataMode, encoded *bitset.Bitset) { + modeIndicator := d.modeIndicator(dataMode) + charCountBits := d.charCountBits(dataMode) + + // Append mode indicator. + encoded.Append(modeIndicator) + + // Append character count. + encoded.AppendUint32(uint32(len(data)), charCountBits) + + // Append data. + switch dataMode { + case dataModeNumeric: + for i := 0; i < len(data); i += 3 { + charsRemaining := len(data) - i + + var value uint32 + bitsUsed := 1 + + for j := 0; j < charsRemaining && j < 3; j++ { + value *= 10 + value += uint32(data[i+j] - 0x30) + bitsUsed += 3 + } + encoded.AppendUint32(value, bitsUsed) + } + case dataModeAlphanumeric: + for i := 0; i < len(data); i += 2 { + charsRemaining := len(data) - i + + var value uint32 + for j := 0; j < charsRemaining && j < 2; j++ { + value *= 45 + value += encodeAlphanumericCharacter(data[i+j]) + } + + bitsUsed := 6 + if charsRemaining > 1 { + bitsUsed = 11 + } + + encoded.AppendUint32(value, bitsUsed) + } + case dataModeByte: + for _, b := range data { + encoded.AppendByte(b, 8) + } + } +} + +// modeIndicator returns the segment header bits for a segment of type dataMode. +func (d *dataEncoder) modeIndicator(dataMode dataMode) *bitset.Bitset { + switch dataMode { + case dataModeNumeric: + return d.numericModeIndicator + case dataModeAlphanumeric: + return d.alphanumericModeIndicator + case dataModeByte: + return d.byteModeIndicator + default: + log.Panic("Unknown data mode") + } + + return nil +} + +// charCountBits returns the number of bits used to encode the length of a data +// segment of type dataMode. +func (d *dataEncoder) charCountBits(dataMode dataMode) int { + switch dataMode { + case dataModeNumeric: + return d.numNumericCharCountBits + case dataModeAlphanumeric: + return d.numAlphanumericCharCountBits + case dataModeByte: + return d.numByteCharCountBits + default: + log.Panic("Unknown data mode") + } + + return 0 +} + +// encodedLength returns the number of bits required to encode n symbols in +// dataMode. +// +// The number of bits required is affected by: +// - QR code type - Mode Indicator length. +// - Data mode - number of bits used to represent data length. +// - Data mode - how the data is encoded. +// - Number of symbols encoded. +// +// An error is returned if the mode is not supported, or the length requested is +// too long to be represented. +func (d *dataEncoder) encodedLength(dataMode dataMode, n int) (int, error) { + modeIndicator := d.modeIndicator(dataMode) + charCountBits := d.charCountBits(dataMode) + + if modeIndicator == nil { + return 0, errors.New("mode not supported") + } + + maxLength := (1 << uint8(charCountBits)) - 1 + + if n > maxLength { + return 0, errors.New("length too long to be represented") + } + + length := modeIndicator.Len() + charCountBits + + switch dataMode { + case dataModeNumeric: + length += 10 * (n / 3) + + if n%3 != 0 { + length += 1 + 3*(n%3) + } + case dataModeAlphanumeric: + length += 11 * (n / 2) + length += 6 * (n % 2) + case dataModeByte: + length += 8 * n + } + + return length, nil +} + +// encodeAlphanumericChar returns the QR Code encoded value of v. +// +// v must be a QR Code defined alphanumeric character: 0-9, A-Z, SP, $%*+-./ or +// :. The characters are mapped to values in the range 0-44 respectively. +func encodeAlphanumericCharacter(v byte) uint32 { + c := uint32(v) + + switch { + case c >= '0' && c <= '9': + // 0-9 encoded as 0-9. + return c - '0' + case c >= 'A' && c <= 'Z': + // A-Z encoded as 10-35. + return c - 'A' + 10 + case c == ' ': + return 36 + case c == '$': + return 37 + case c == '%': + return 38 + case c == '*': + return 39 + case c == '+': + return 40 + case c == '-': + return 41 + case c == '.': + return 42 + case c == '/': + return 43 + case c == ':': + return 44 + default: + log.Panicf("encodeAlphanumericCharacter() with non alphanumeric char %v.", v) + } + + return 0 +} diff --git a/backend/vendor/github.com/skip2/go-qrcode/go.mod b/backend/vendor/github.com/skip2/go-qrcode/go.mod new file mode 100644 index 00000000..a915813d --- /dev/null +++ b/backend/vendor/github.com/skip2/go-qrcode/go.mod @@ -0,0 +1,3 @@ +module github.com/skip2/go-qrcode + +go 1.13 diff --git a/backend/vendor/github.com/skip2/go-qrcode/qrcode.go b/backend/vendor/github.com/skip2/go-qrcode/qrcode.go new file mode 100644 index 00000000..d0541bcb --- /dev/null +++ b/backend/vendor/github.com/skip2/go-qrcode/qrcode.go @@ -0,0 +1,608 @@ +// 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" + "fmt" + "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 + + // Disable the QR Code border. + DisableBorder bool + + 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 +} + +// 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) { + 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: + return nil, fmt.Errorf("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") + } + + 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()) + } + + 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 { + // Build QR code. + q.encode() + + 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 { + // Build QR code. + q.encode() + + // 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 + } + + // Output image. + 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)) + + // QR code bitmap. + bitmap := q.symbol.bitmap() + + // 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] + + if v { + pos := img.PixOffset(x, y) + 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 := q.version.numTerminatorBitsRequired(q.data.Len()) + + 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, !q.DisableBorder) + + 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() +} diff --git a/backend/vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go b/backend/vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go new file mode 100644 index 00000000..6a7003f7 --- /dev/null +++ b/backend/vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go @@ -0,0 +1,387 @@ +// go-qrcode +// Copyright 2014 Tom Harwood + +package reedsolomon + +// Addition, subtraction, multiplication, and division in GF(2^8). +// Operations are performed modulo x^8 + x^4 + x^3 + x^2 + 1. + +// http://en.wikipedia.org/wiki/Finite_field_arithmetic + +import "log" + +const ( + gfZero = gfElement(0) + gfOne = gfElement(1) +) + +var ( + gfExpTable = [256]gfElement{ + /* 0 - 9 */ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, + /* 10 - 19 */ 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, + /* 20 - 29 */ 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, + /* 30 - 39 */ 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, + /* 40 - 49 */ 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, + /* 50 - 59 */ 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, + /* 60 - 69 */ 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, + /* 70 - 79 */ 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, + /* 80 - 89 */ 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, + /* 90 - 99 */ 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, + /* 100 - 109 */ 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, + /* 110 - 119 */ 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, + /* 120 - 129 */ 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, + /* 130 - 139 */ 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, + /* 140 - 149 */ 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, + /* 150 - 159 */ 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, + /* 160 - 169 */ 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, + /* 170 - 179 */ 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, + /* 180 - 189 */ 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, + /* 190 - 199 */ 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, + /* 200 - 209 */ 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, + /* 210 - 219 */ 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, + /* 220 - 229 */ 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, + /* 230 - 239 */ 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, + /* 240 - 249 */ 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, + /* 250 - 255 */ 108, 216, 173, 71, 142, 1} + + gfLogTable = [256]int{ + /* 0 - 9 */ -1, 0, 1, 25, 2, 50, 26, 198, 3, 223, + /* 10 - 19 */ 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, + /* 20 - 29 */ 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, + /* 30 - 39 */ 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, + /* 40 - 49 */ 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, + /* 50 - 59 */ 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, + /* 60 - 69 */ 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, + /* 70 - 79 */ 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, + /* 80 - 89 */ 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, + /* 90 - 99 */ 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, + /* 100 - 109 */ 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, + /* 110 - 119 */ 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, + /* 120 - 129 */ 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, + /* 130 - 139 */ 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, + /* 140 - 149 */ 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, + /* 150 - 159 */ 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, + /* 160 - 169 */ 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, + /* 170 - 179 */ 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, + /* 180 - 189 */ 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, + /* 190 - 199 */ 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, + /* 200 - 209 */ 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, + /* 210 - 219 */ 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, + /* 220 - 229 */ 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, + /* 230 - 239 */ 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, + /* 240 - 249 */ 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, + /* 250 - 255 */ 244, 234, 168, 80, 88, 175} +) + +// gfElement is an element in GF(2^8). +type gfElement uint8 + +// newGFElement creates and returns a new gfElement. +func newGFElement(data byte) gfElement { + return gfElement(data) +} + +// gfAdd returns a + b. +func gfAdd(a, b gfElement) gfElement { + return a ^ b +} + +// gfSub returns a - b. +// +// Note addition is equivalent to subtraction in GF(2). +func gfSub(a, b gfElement) gfElement { + return a ^ b +} + +// gfMultiply returns a * b. +func gfMultiply(a, b gfElement) gfElement { + if a == gfZero || b == gfZero { + return gfZero + } + + return gfExpTable[(gfLogTable[a]+gfLogTable[b])%255] +} + +// gfDivide returns a / b. +// +// Divide by zero results in a panic. +func gfDivide(a, b gfElement) gfElement { + if a == gfZero { + return gfZero + } else if b == gfZero { + log.Panicln("Divide by zero") + } + + return gfMultiply(a, gfInverse(b)) +} + +// gfInverse returns the multiplicative inverse of a, a^-1. +// +// a * a^-1 = 1 +func gfInverse(a gfElement) gfElement { + if a == gfZero { + log.Panicln("No multiplicative inverse of 0") + } + + return gfExpTable[255-gfLogTable[a]] +} + +// a^i | bits | polynomial | decimal +// -------------------------------------------------------------------------- +// 0 | 000000000 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 0 +// a^0 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1 +// a^1 | 000000010 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 2 +// a^2 | 000000100 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 4 +// a^3 | 000001000 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 8 +// a^4 | 000010000 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 16 +// a^5 | 000100000 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 32 +// a^6 | 001000000 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 64 +// a^7 | 010000000 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 128 +// a^8 | 000011101 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 29 +// a^9 | 000111010 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 58 +// a^10 | 001110100 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 116 +// a^11 | 011101000 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 232 +// a^12 | 011001101 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 205 +// a^13 | 010000111 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 135 +// a^14 | 000010011 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 19 +// a^15 | 000100110 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 38 +// a^16 | 001001100 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 76 +// a^17 | 010011000 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 152 +// a^18 | 000101101 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 45 +// a^19 | 001011010 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 90 +// a^20 | 010110100 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 180 +// a^21 | 001110101 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 117 +// a^22 | 011101010 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 234 +// a^23 | 011001001 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 201 +// a^24 | 010001111 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 143 +// a^25 | 000000011 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 3 +// a^26 | 000000110 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 6 +// a^27 | 000001100 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 12 +// a^28 | 000011000 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 24 +// a^29 | 000110000 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 48 +// a^30 | 001100000 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 96 +// a^31 | 011000000 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 192 +// a^32 | 010011101 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 157 +// a^33 | 000100111 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 39 +// a^34 | 001001110 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 78 +// a^35 | 010011100 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 156 +// a^36 | 000100101 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 37 +// a^37 | 001001010 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 74 +// a^38 | 010010100 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 148 +// a^39 | 000110101 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 53 +// a^40 | 001101010 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 106 +// a^41 | 011010100 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 212 +// a^42 | 010110101 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 181 +// a^43 | 001110111 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 119 +// a^44 | 011101110 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 238 +// a^45 | 011000001 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 193 +// a^46 | 010011111 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 159 +// a^47 | 000100011 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 35 +// a^48 | 001000110 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 70 +// a^49 | 010001100 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 140 +// a^50 | 000000101 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 5 +// a^51 | 000001010 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 10 +// a^52 | 000010100 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 20 +// a^53 | 000101000 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 40 +// a^54 | 001010000 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 80 +// a^55 | 010100000 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 160 +// a^56 | 001011101 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 93 +// a^57 | 010111010 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 186 +// a^58 | 001101001 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 105 +// a^59 | 011010010 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 210 +// a^60 | 010111001 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 185 +// a^61 | 001101111 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 111 +// a^62 | 011011110 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 222 +// a^63 | 010100001 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 161 +// a^64 | 001011111 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 95 +// a^65 | 010111110 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 190 +// a^66 | 001100001 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 97 +// a^67 | 011000010 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 194 +// a^68 | 010011001 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 153 +// a^69 | 000101111 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 47 +// a^70 | 001011110 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 94 +// a^71 | 010111100 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 188 +// a^72 | 001100101 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 101 +// a^73 | 011001010 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 202 +// a^74 | 010001001 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 137 +// a^75 | 000001111 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 15 +// a^76 | 000011110 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 30 +// a^77 | 000111100 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 60 +// a^78 | 001111000 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 120 +// a^79 | 011110000 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 240 +// a^80 | 011111101 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 253 +// a^81 | 011100111 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 231 +// a^82 | 011010011 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 211 +// a^83 | 010111011 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 187 +// a^84 | 001101011 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 107 +// a^85 | 011010110 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 214 +// a^86 | 010110001 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 177 +// a^87 | 001111111 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 127 +// a^88 | 011111110 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 254 +// a^89 | 011100001 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 225 +// a^90 | 011011111 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 223 +// a^91 | 010100011 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 163 +// a^92 | 001011011 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 91 +// a^93 | 010110110 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 182 +// a^94 | 001110001 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 113 +// a^95 | 011100010 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 226 +// a^96 | 011011001 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 217 +// a^97 | 010101111 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 175 +// a^98 | 001000011 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 67 +// a^99 | 010000110 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 134 +// a^100 | 000010001 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 17 +// a^101 | 000100010 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 34 +// a^102 | 001000100 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 68 +// a^103 | 010001000 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 136 +// a^104 | 000001101 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 13 +// a^105 | 000011010 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 26 +// a^106 | 000110100 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 52 +// a^107 | 001101000 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 104 +// a^108 | 011010000 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 208 +// a^109 | 010111101 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 189 +// a^110 | 001100111 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 103 +// a^111 | 011001110 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 206 +// a^112 | 010000001 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 129 +// a^113 | 000011111 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 31 +// a^114 | 000111110 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 62 +// a^115 | 001111100 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 124 +// a^116 | 011111000 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 248 +// a^117 | 011101101 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 237 +// a^118 | 011000111 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 199 +// a^119 | 010010011 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 147 +// a^120 | 000111011 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 59 +// a^121 | 001110110 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 118 +// a^122 | 011101100 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 236 +// a^123 | 011000101 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 197 +// a^124 | 010010111 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 151 +// a^125 | 000110011 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 51 +// a^126 | 001100110 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 102 +// a^127 | 011001100 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 204 +// a^128 | 010000101 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 133 +// a^129 | 000010111 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 23 +// a^130 | 000101110 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 46 +// a^131 | 001011100 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 92 +// a^132 | 010111000 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 184 +// a^133 | 001101101 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 109 +// a^134 | 011011010 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 218 +// a^135 | 010101001 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 169 +// a^136 | 001001111 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 79 +// a^137 | 010011110 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 158 +// a^138 | 000100001 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 33 +// a^139 | 001000010 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 66 +// a^140 | 010000100 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 132 +// a^141 | 000010101 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 21 +// a^142 | 000101010 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 42 +// a^143 | 001010100 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 84 +// a^144 | 010101000 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 168 +// a^145 | 001001101 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 77 +// a^146 | 010011010 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 154 +// a^147 | 000101001 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 41 +// a^148 | 001010010 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 82 +// a^149 | 010100100 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 164 +// a^150 | 001010101 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 85 +// a^151 | 010101010 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 170 +// a^152 | 001001001 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 73 +// a^153 | 010010010 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 146 +// a^154 | 000111001 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 57 +// a^155 | 001110010 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 114 +// a^156 | 011100100 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 228 +// a^157 | 011010101 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 213 +// a^158 | 010110111 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 183 +// a^159 | 001110011 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 115 +// a^160 | 011100110 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 230 +// a^161 | 011010001 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 209 +// a^162 | 010111111 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 191 +// a^163 | 001100011 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 99 +// a^164 | 011000110 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 198 +// a^165 | 010010001 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 145 +// a^166 | 000111111 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 63 +// a^167 | 001111110 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 126 +// a^168 | 011111100 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 252 +// a^169 | 011100101 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 229 +// a^170 | 011010111 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 215 +// a^171 | 010110011 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 179 +// a^172 | 001111011 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 123 +// a^173 | 011110110 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 246 +// a^174 | 011110001 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 241 +// a^175 | 011111111 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 255 +// a^176 | 011100011 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 227 +// a^177 | 011011011 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 219 +// a^178 | 010101011 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 171 +// a^179 | 001001011 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 75 +// a^180 | 010010110 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 150 +// a^181 | 000110001 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 49 +// a^182 | 001100010 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 98 +// a^183 | 011000100 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 196 +// a^184 | 010010101 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 149 +// a^185 | 000110111 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 55 +// a^186 | 001101110 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 110 +// a^187 | 011011100 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 220 +// a^188 | 010100101 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 165 +// a^189 | 001010111 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 87 +// a^190 | 010101110 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 174 +// a^191 | 001000001 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 65 +// a^192 | 010000010 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 130 +// a^193 | 000011001 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 25 +// a^194 | 000110010 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 50 +// a^195 | 001100100 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 100 +// a^196 | 011001000 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 200 +// a^197 | 010001101 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 141 +// a^198 | 000000111 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 7 +// a^199 | 000001110 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 14 +// a^200 | 000011100 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 28 +// a^201 | 000111000 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 56 +// a^202 | 001110000 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 112 +// a^203 | 011100000 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 224 +// a^204 | 011011101 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 221 +// a^205 | 010100111 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 167 +// a^206 | 001010011 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 83 +// a^207 | 010100110 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 166 +// a^208 | 001010001 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 81 +// a^209 | 010100010 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 162 +// a^210 | 001011001 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 89 +// a^211 | 010110010 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 178 +// a^212 | 001111001 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 121 +// a^213 | 011110010 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 242 +// a^214 | 011111001 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 249 +// a^215 | 011101111 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 239 +// a^216 | 011000011 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 195 +// a^217 | 010011011 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 155 +// a^218 | 000101011 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 43 +// a^219 | 001010110 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 86 +// a^220 | 010101100 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 172 +// a^221 | 001000101 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 69 +// a^222 | 010001010 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 138 +// a^223 | 000001001 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 9 +// a^224 | 000010010 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 18 +// a^225 | 000100100 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 36 +// a^226 | 001001000 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 72 +// a^227 | 010010000 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 144 +// a^228 | 000111101 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 61 +// a^229 | 001111010 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 122 +// a^230 | 011110100 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 244 +// a^231 | 011110101 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 245 +// a^232 | 011110111 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 247 +// a^233 | 011110011 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 243 +// a^234 | 011111011 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 251 +// a^235 | 011101011 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 235 +// a^236 | 011001011 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 203 +// a^237 | 010001011 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 139 +// a^238 | 000001011 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 11 +// a^239 | 000010110 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 22 +// a^240 | 000101100 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 44 +// a^241 | 001011000 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 88 +// a^242 | 010110000 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 176 +// a^243 | 001111101 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 125 +// a^244 | 011111010 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 250 +// a^245 | 011101001 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 233 +// a^246 | 011001111 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 207 +// a^247 | 010000011 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 131 +// a^248 | 000011011 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 27 +// a^249 | 000110110 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 54 +// a^250 | 001101100 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 108 +// a^251 | 011011000 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 216 +// a^252 | 010101101 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 173 +// a^253 | 001000111 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 71 +// a^254 | 010001110 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 142 +// a^255 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1 diff --git a/backend/vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go b/backend/vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go new file mode 100644 index 00000000..962f5454 --- /dev/null +++ b/backend/vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go @@ -0,0 +1,216 @@ +// go-qrcode +// Copyright 2014 Tom Harwood + +package reedsolomon + +import ( + "fmt" + "log" + + bitset "github.com/skip2/go-qrcode/bitset" +) + +// gfPoly is a polynomial over GF(2^8). +type gfPoly struct { + // The ith value is the coefficient of the ith degree of x. + // term[0]*(x^0) + term[1]*(x^1) + term[2]*(x^2) ... + term []gfElement +} + +// newGFPolyFromData returns |data| as a polynomial over GF(2^8). +// +// Each data byte becomes the coefficient of an x term. +// +// For an n byte input the polynomial is: +// data[n-1]*(x^n-1) + data[n-2]*(x^n-2) ... + data[0]*(x^0). +func newGFPolyFromData(data *bitset.Bitset) gfPoly { + numTotalBytes := data.Len() / 8 + if data.Len()%8 != 0 { + numTotalBytes++ + } + + result := gfPoly{term: make([]gfElement, numTotalBytes)} + + i := numTotalBytes - 1 + for j := 0; j < data.Len(); j += 8 { + result.term[i] = gfElement(data.ByteAt(j)) + i-- + } + + return result +} + +// newGFPolyMonomial returns term*(x^degree). +func newGFPolyMonomial(term gfElement, degree int) gfPoly { + if term == gfZero { + return gfPoly{} + } + + result := gfPoly{term: make([]gfElement, degree+1)} + result.term[degree] = term + + return result +} + +func (e gfPoly) data(numTerms int) []byte { + result := make([]byte, numTerms) + + i := numTerms - len(e.term) + for j := len(e.term) - 1; j >= 0; j-- { + result[i] = byte(e.term[j]) + i++ + } + + return result +} + +// numTerms returns the number of +func (e gfPoly) numTerms() int { + return len(e.term) +} + +// gfPolyMultiply returns a * b. +func gfPolyMultiply(a, b gfPoly) gfPoly { + numATerms := a.numTerms() + numBTerms := b.numTerms() + + result := gfPoly{term: make([]gfElement, numATerms+numBTerms)} + + for i := 0; i < numATerms; i++ { + for j := 0; j < numBTerms; j++ { + if a.term[i] != 0 && b.term[j] != 0 { + monomial := gfPoly{term: make([]gfElement, i+j+1)} + monomial.term[i+j] = gfMultiply(a.term[i], b.term[j]) + + result = gfPolyAdd(result, monomial) + } + } + } + + return result.normalised() +} + +// gfPolyRemainder return the remainder of numerator / denominator. +func gfPolyRemainder(numerator, denominator gfPoly) gfPoly { + if denominator.equals(gfPoly{}) { + log.Panicln("Remainder by zero") + } + + remainder := numerator + + for remainder.numTerms() >= denominator.numTerms() { + degree := remainder.numTerms() - denominator.numTerms() + coefficient := gfDivide(remainder.term[remainder.numTerms()-1], + denominator.term[denominator.numTerms()-1]) + + divisor := gfPolyMultiply(denominator, + newGFPolyMonomial(coefficient, degree)) + + remainder = gfPolyAdd(remainder, divisor) + } + + return remainder.normalised() +} + +// gfPolyAdd returns a + b. +func gfPolyAdd(a, b gfPoly) gfPoly { + numATerms := a.numTerms() + numBTerms := b.numTerms() + + numTerms := numATerms + if numBTerms > numTerms { + numTerms = numBTerms + } + + result := gfPoly{term: make([]gfElement, numTerms)} + + for i := 0; i < numTerms; i++ { + switch { + case numATerms > i && numBTerms > i: + result.term[i] = gfAdd(a.term[i], b.term[i]) + case numATerms > i: + result.term[i] = a.term[i] + default: + result.term[i] = b.term[i] + } + } + + return result.normalised() +} + +func (e gfPoly) normalised() gfPoly { + numTerms := e.numTerms() + maxNonzeroTerm := numTerms - 1 + + for i := numTerms - 1; i >= 0; i-- { + if e.term[i] != 0 { + break + } + + maxNonzeroTerm = i - 1 + } + + if maxNonzeroTerm < 0 { + return gfPoly{} + } else if maxNonzeroTerm < numTerms-1 { + e.term = e.term[0 : maxNonzeroTerm+1] + } + + return e +} + +func (e gfPoly) string(useIndexForm bool) string { + var str string + numTerms := e.numTerms() + + for i := numTerms - 1; i >= 0; i-- { + if e.term[i] > 0 { + if len(str) > 0 { + str += " + " + } + + if !useIndexForm { + str += fmt.Sprintf("%dx^%d", e.term[i], i) + } else { + str += fmt.Sprintf("a^%dx^%d", gfLogTable[e.term[i]], i) + } + } + } + + if len(str) == 0 { + str = "0" + } + + return str +} + +// equals returns true if e == other. +func (e gfPoly) equals(other gfPoly) bool { + var minecPoly *gfPoly + var maxecPoly *gfPoly + + if e.numTerms() > other.numTerms() { + minecPoly = &other + maxecPoly = &e + } else { + minecPoly = &e + maxecPoly = &other + } + + numMinTerms := minecPoly.numTerms() + numMaxTerms := maxecPoly.numTerms() + + for i := 0; i < numMinTerms; i++ { + if e.term[i] != other.term[i] { + return false + } + } + + for i := numMinTerms; i < numMaxTerms; i++ { + if maxecPoly.term[i] != 0 { + return false + } + } + + return true +} diff --git a/backend/vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go b/backend/vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go new file mode 100644 index 00000000..561697b4 --- /dev/null +++ b/backend/vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go @@ -0,0 +1,73 @@ +// go-qrcode +// Copyright 2014 Tom Harwood + +// Package reedsolomon provides error correction encoding for QR Code 2005. +// +// QR Code 2005 uses a Reed-Solomon error correcting code to detect and correct +// errors encountered during decoding. +// +// The generated RS codes are systematic, and consist of the input data with +// error correction bytes appended. +package reedsolomon + +import ( + "log" + + bitset "github.com/skip2/go-qrcode/bitset" +) + +// Encode data for QR Code 2005 using the appropriate Reed-Solomon code. +// +// numECBytes is the number of error correction bytes to append, and is +// determined by the target QR Code's version and error correction level. +// +// ISO/IEC 18004 table 9 specifies the numECBytes required. e.g. a 1-L code has +// numECBytes=7. +func Encode(data *bitset.Bitset, numECBytes int) *bitset.Bitset { + // Create a polynomial representing |data|. + // + // The bytes are interpreted as the sequence of coefficients of a polynomial. + // The last byte's value becomes the x^0 coefficient, the second to last + // becomes the x^1 coefficient and so on. + ecpoly := newGFPolyFromData(data) + ecpoly = gfPolyMultiply(ecpoly, newGFPolyMonomial(gfOne, numECBytes)) + + // Pick the generator polynomial. + generator := rsGeneratorPoly(numECBytes) + + // Generate the error correction bytes. + remainder := gfPolyRemainder(ecpoly, generator) + + // Combine the data & error correcting bytes. + // The mathematically correct answer is: + // + // result := gfPolyAdd(ecpoly, remainder). + // + // The encoding used by QR Code 2005 is slightly different this result: To + // preserve the original |data| bit sequence exactly, the data and remainder + // are combined manually below. This ensures any most significant zero bits + // are preserved (and not optimised away). + result := bitset.Clone(data) + result.AppendBytes(remainder.data(numECBytes)) + + return result +} + +// rsGeneratorPoly returns the Reed-Solomon generator polynomial with |degree|. +// +// The generator polynomial is calculated as: +// (x + a^0)(x + a^1)...(x + a^degree-1) +func rsGeneratorPoly(degree int) gfPoly { + if degree < 2 { + log.Panic("degree < 2") + } + + generator := gfPoly{term: []gfElement{1}} + + for i := 0; i < degree; i++ { + nextPoly := gfPoly{term: []gfElement{gfExpTable[i], 1}} + generator = gfPolyMultiply(generator, nextPoly) + } + + return generator +} diff --git a/backend/vendor/github.com/skip2/go-qrcode/regular_symbol.go b/backend/vendor/github.com/skip2/go-qrcode/regular_symbol.go new file mode 100644 index 00000000..51eb148d --- /dev/null +++ b/backend/vendor/github.com/skip2/go-qrcode/regular_symbol.go @@ -0,0 +1,315 @@ +// go-qrcode +// Copyright 2014 Tom Harwood + +package qrcode + +import ( + bitset "github.com/skip2/go-qrcode/bitset" +) + +type regularSymbol struct { + version qrCodeVersion + mask int + + data *bitset.Bitset + + symbol *symbol + size int +} + +// Abbreviated true/false. +const ( + b0 = false + b1 = true +) + +var ( + alignmentPatternCenter = [][]int{ + {}, // Version 0 doesn't exist. + {}, // Version 1 doesn't use alignment patterns. + {6, 18}, + {6, 22}, + {6, 26}, + {6, 30}, + {6, 34}, + {6, 22, 38}, + {6, 24, 42}, + {6, 26, 46}, + {6, 28, 50}, + {6, 30, 54}, + {6, 32, 58}, + {6, 34, 62}, + {6, 26, 46, 66}, + {6, 26, 48, 70}, + {6, 26, 50, 74}, + {6, 30, 54, 78}, + {6, 30, 56, 82}, + {6, 30, 58, 86}, + {6, 34, 62, 90}, + {6, 28, 50, 72, 94}, + {6, 26, 50, 74, 98}, + {6, 30, 54, 78, 102}, + {6, 28, 54, 80, 106}, + {6, 32, 58, 84, 110}, + {6, 30, 58, 86, 114}, + {6, 34, 62, 90, 118}, + {6, 26, 50, 74, 98, 122}, + {6, 30, 54, 78, 102, 126}, + {6, 26, 52, 78, 104, 130}, + {6, 30, 56, 82, 108, 134}, + {6, 34, 60, 86, 112, 138}, + {6, 30, 58, 86, 114, 142}, + {6, 34, 62, 90, 118, 146}, + {6, 30, 54, 78, 102, 126, 150}, + {6, 24, 50, 76, 102, 128, 154}, + {6, 28, 54, 80, 106, 132, 158}, + {6, 32, 58, 84, 110, 136, 162}, + {6, 26, 54, 82, 110, 138, 166}, + {6, 30, 58, 86, 114, 142, 170}, + } + + finderPattern = [][]bool{ + {b1, b1, b1, b1, b1, b1, b1}, + {b1, b0, b0, b0, b0, b0, b1}, + {b1, b0, b1, b1, b1, b0, b1}, + {b1, b0, b1, b1, b1, b0, b1}, + {b1, b0, b1, b1, b1, b0, b1}, + {b1, b0, b0, b0, b0, b0, b1}, + {b1, b1, b1, b1, b1, b1, b1}, + } + + finderPatternSize = 7 + + finderPatternHorizontalBorder = [][]bool{ + {b0, b0, b0, b0, b0, b0, b0, b0}, + } + + finderPatternVerticalBorder = [][]bool{ + {b0}, + {b0}, + {b0}, + {b0}, + {b0}, + {b0}, + {b0}, + {b0}, + } + + alignmentPattern = [][]bool{ + {b1, b1, b1, b1, b1}, + {b1, b0, b0, b0, b1}, + {b1, b0, b1, b0, b1}, + {b1, b0, b0, b0, b1}, + {b1, b1, b1, b1, b1}, + } +) + +func buildRegularSymbol(version qrCodeVersion, mask int, + data *bitset.Bitset, includeQuietZone bool) (*symbol, error) { + + quietZoneSize := 0 + if includeQuietZone { + quietZoneSize = version.quietZoneSize() + } + + m := ®ularSymbol{ + version: version, + mask: mask, + data: data, + + symbol: newSymbol(version.symbolSize(), quietZoneSize), + size: version.symbolSize(), + } + + m.addFinderPatterns() + m.addAlignmentPatterns() + m.addTimingPatterns() + m.addFormatInfo() + m.addVersionInfo() + + ok, err := m.addData() + if !ok { + return nil, err + } + + return m.symbol, nil +} + +func (m *regularSymbol) addFinderPatterns() { + fpSize := finderPatternSize + fp := finderPattern + fpHBorder := finderPatternHorizontalBorder + fpVBorder := finderPatternVerticalBorder + + // Top left Finder Pattern. + m.symbol.set2dPattern(0, 0, fp) + m.symbol.set2dPattern(0, fpSize, fpHBorder) + m.symbol.set2dPattern(fpSize, 0, fpVBorder) + + // Top right Finder Pattern. + m.symbol.set2dPattern(m.size-fpSize, 0, fp) + m.symbol.set2dPattern(m.size-fpSize-1, fpSize, fpHBorder) + m.symbol.set2dPattern(m.size-fpSize-1, 0, fpVBorder) + + // Bottom left Finder Pattern. + m.symbol.set2dPattern(0, m.size-fpSize, fp) + m.symbol.set2dPattern(0, m.size-fpSize-1, fpHBorder) + m.symbol.set2dPattern(fpSize, m.size-fpSize-1, fpVBorder) +} + +func (m *regularSymbol) addAlignmentPatterns() { + for _, x := range alignmentPatternCenter[m.version.version] { + for _, y := range alignmentPatternCenter[m.version.version] { + if !m.symbol.empty(x, y) { + continue + } + + m.symbol.set2dPattern(x-2, y-2, alignmentPattern) + } + } +} + +func (m *regularSymbol) addTimingPatterns() { + value := true + + for i := finderPatternSize + 1; i < m.size-finderPatternSize; i++ { + m.symbol.set(i, finderPatternSize-1, value) + m.symbol.set(finderPatternSize-1, i, value) + + value = !value + } +} + +func (m *regularSymbol) addFormatInfo() { + fpSize := finderPatternSize + l := formatInfoLengthBits - 1 + + f := m.version.formatInfo(m.mask) + + // Bits 0-7, under the top right finder pattern. + for i := 0; i <= 7; i++ { + m.symbol.set(m.size-i-1, fpSize+1, f.At(l-i)) + } + + // Bits 0-5, right of the top left finder pattern. + for i := 0; i <= 5; i++ { + m.symbol.set(fpSize+1, i, f.At(l-i)) + } + + // Bits 6-8 on the corner of the top left finder pattern. + m.symbol.set(fpSize+1, fpSize, f.At(l-6)) + m.symbol.set(fpSize+1, fpSize+1, f.At(l-7)) + m.symbol.set(fpSize, fpSize+1, f.At(l-8)) + + // Bits 9-14 on the underside of the top left finder pattern. + for i := 9; i <= 14; i++ { + m.symbol.set(14-i, fpSize+1, f.At(l-i)) + } + + // Bits 8-14 on the right side of the bottom left finder pattern. + for i := 8; i <= 14; i++ { + m.symbol.set(fpSize+1, m.size-fpSize+i-8, f.At(l-i)) + } + + // Always dark symbol. + m.symbol.set(fpSize+1, m.size-fpSize-1, true) +} + +func (m *regularSymbol) addVersionInfo() { + fpSize := finderPatternSize + + v := m.version.versionInfo() + l := versionInfoLengthBits - 1 + + if v == nil { + return + } + + for i := 0; i < v.Len(); i++ { + // Above the bottom left finder pattern. + m.symbol.set(i/3, m.size-fpSize-4+i%3, v.At(l-i)) + + // Left of the top right finder pattern. + m.symbol.set(m.size-fpSize-4+i%3, i/3, v.At(l-i)) + } +} + +type direction uint8 + +const ( + up direction = iota + down +) + +func (m *regularSymbol) addData() (bool, error) { + xOffset := 1 + dir := up + + x := m.size - 2 + y := m.size - 1 + + for i := 0; i < m.data.Len(); i++ { + var mask bool + switch m.mask { + case 0: + mask = (y+x+xOffset)%2 == 0 + case 1: + mask = y%2 == 0 + case 2: + mask = (x+xOffset)%3 == 0 + case 3: + mask = (y+x+xOffset)%3 == 0 + case 4: + mask = (y/2+(x+xOffset)/3)%2 == 0 + case 5: + mask = (y*(x+xOffset))%2+(y*(x+xOffset))%3 == 0 + case 6: + mask = ((y*(x+xOffset))%2+((y*(x+xOffset))%3))%2 == 0 + case 7: + mask = ((y+x+xOffset)%2+((y*(x+xOffset))%3))%2 == 0 + } + + // != is equivalent to XOR. + m.symbol.set(x+xOffset, y, mask != m.data.At(i)) + + if i == m.data.Len()-1 { + break + } + + // Find next free bit in the symbol. + for { + if xOffset == 1 { + xOffset = 0 + } else { + xOffset = 1 + + if dir == up { + if y > 0 { + y-- + } else { + dir = down + x -= 2 + } + } else { + if y < m.size-1 { + y++ + } else { + dir = up + x -= 2 + } + } + } + + // Skip over the vertical timing pattern entirely. + if x == 5 { + x-- + } + + if m.symbol.empty(x+xOffset, y) { + break + } + } + } + + return true, nil +} diff --git a/backend/vendor/github.com/skip2/go-qrcode/symbol.go b/backend/vendor/github.com/skip2/go-qrcode/symbol.go new file mode 100644 index 00000000..0cb1327c --- /dev/null +++ b/backend/vendor/github.com/skip2/go-qrcode/symbol.go @@ -0,0 +1,309 @@ +// go-qrcode +// Copyright 2014 Tom Harwood + +package qrcode + +// symbol is a 2D array of bits representing a QR Code symbol. +// +// A symbol consists of size*size modules, with each module normally drawn as a +// black or white square. The symbol also has a border of quietZoneSize modules. +// +// A (fictional) size=2, quietZoneSize=1 QR Code looks like: +// +// +----+ +// | | +// | ab | +// | cd | +// | | +// +----+ +// +// For ease of implementation, the functions to set/get bits ignore the border, +// so (0,0)=a, (0,1)=b, (1,0)=c, and (1,1)=d. The entire symbol (including the +// border) is returned by bitmap(). +// +type symbol struct { + // Value of module at [y][x]. True is set. + module [][]bool + + // True if the module at [y][x] is used (to either true or false). + // Used to identify unused modules. + isUsed [][]bool + + // Combined width/height of the symbol and quiet zones. + // + // size = symbolSize + 2*quietZoneSize. + size int + + // Width/height of the symbol only. + symbolSize int + + // Width/height of a single quiet zone. + quietZoneSize int +} + +// newSymbol constructs a symbol of size size*size, with a border of +// quietZoneSize. +func newSymbol(size int, quietZoneSize int) *symbol { + var m symbol + + m.module = make([][]bool, size+2*quietZoneSize) + m.isUsed = make([][]bool, size+2*quietZoneSize) + + for i := range m.module { + m.module[i] = make([]bool, size+2*quietZoneSize) + m.isUsed[i] = make([]bool, size+2*quietZoneSize) + } + + m.size = size + 2*quietZoneSize + m.symbolSize = size + m.quietZoneSize = quietZoneSize + + return &m +} + +// get returns the module value at (x, y). +func (m *symbol) get(x int, y int) (v bool) { + v = m.module[y+m.quietZoneSize][x+m.quietZoneSize] + return +} + +// empty returns true if the module at (x, y) has not been set (to either true +// or false). +func (m *symbol) empty(x int, y int) bool { + return !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] +} + +// numEmptyModules returns the number of empty modules. +// +// Initially numEmptyModules is symbolSize * symbolSize. After every module has +// been set (to either true or false), the number of empty modules is zero. +func (m *symbol) numEmptyModules() int { + var count int + for y := 0; y < m.symbolSize; y++ { + for x := 0; x < m.symbolSize; x++ { + if !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] { + count++ + } + } + } + + return count +} + +// set sets the module at (x, y) to v. +func (m *symbol) set(x int, y int, v bool) { + m.module[y+m.quietZoneSize][x+m.quietZoneSize] = v + m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] = true +} + +// set2dPattern sets a 2D array of modules, starting at (x, y). +func (m *symbol) set2dPattern(x int, y int, v [][]bool) { + for j, row := range v { + for i, value := range row { + m.set(x+i, y+j, value) + } + } +} + +// bitmap returns the entire symbol, including the quiet zone. +func (m *symbol) bitmap() [][]bool { + module := make([][]bool, len(m.module)) + + for i := range m.module { + module[i] = m.module[i][:] + } + + return module +} + +// string returns a pictorial representation of the symbol, suitable for +// printing in a TTY. +func (m *symbol) string() string { + var result string + + for _, row := range m.module { + for _, value := range row { + switch value { + case true: + result += " " + case false: + // Unicode 'FULL BLOCK' (U+2588). + result += "██" + } + } + result += "\n" + } + + return result +} + +// Constants used to weight penalty calculations. Specified by ISO/IEC +// 18004:2006. +const ( + penaltyWeight1 = 3 + penaltyWeight2 = 3 + penaltyWeight3 = 40 + penaltyWeight4 = 10 +) + +// penaltyScore returns the penalty score of the symbol. The penalty score +// consists of the sum of the four individual penalty types. +func (m *symbol) penaltyScore() int { + return m.penalty1() + m.penalty2() + m.penalty3() + m.penalty4() +} + +// penalty1 returns the penalty score for "adjacent modules in row/column with +// same colour". +// +// The numbers of adjacent matching modules and scores are: +// 0-5: score = 0 +// 6+ : score = penaltyWeight1 + (numAdjacentModules - 5) +func (m *symbol) penalty1() int { + penalty := 0 + + for x := 0; x < m.symbolSize; x++ { + lastValue := m.get(x, 0) + count := 1 + + for y := 1; y < m.symbolSize; y++ { + v := m.get(x, y) + + if v != lastValue { + count = 1 + lastValue = v + } else { + count++ + if count == 6 { + penalty += penaltyWeight1 + 1 + } else if count > 6 { + penalty++ + } + } + } + } + + for y := 0; y < m.symbolSize; y++ { + lastValue := m.get(0, y) + count := 1 + + for x := 1; x < m.symbolSize; x++ { + v := m.get(x, y) + + if v != lastValue { + count = 1 + lastValue = v + } else { + count++ + if count == 6 { + penalty += penaltyWeight1 + 1 + } else if count > 6 { + penalty++ + } + } + } + } + + return penalty +} + +// penalty2 returns the penalty score for "block of modules in the same colour". +// +// m*n: score = penaltyWeight2 * (m-1) * (n-1). +func (m *symbol) penalty2() int { + penalty := 0 + + for y := 1; y < m.symbolSize; y++ { + for x := 1; x < m.symbolSize; x++ { + topLeft := m.get(x-1, y-1) + above := m.get(x, y-1) + left := m.get(x-1, y) + current := m.get(x, y) + + if current == left && current == above && current == topLeft { + penalty++ + } + } + } + + return penalty * penaltyWeight2 +} + +// penalty3 returns the penalty score for "1:1:3:1:1 ratio +// (dark:light:dark:light:dark) pattern in row/column, preceded or followed by +// light area 4 modules wide". +// +// Existence of the pattern scores penaltyWeight3. +func (m *symbol) penalty3() int { + penalty := 0 + + for y := 0; y < m.symbolSize; y++ { + var bitBuffer int16 = 0x00 + + for x := 0; x < m.symbolSize; x++ { + bitBuffer <<= 1 + if v := m.get(x, y); v { + bitBuffer |= 1 + } + + switch bitBuffer & 0x7ff { + // 0b000 0101 1101 or 0b10111010000 + // 0x05d or 0x5d0 + case 0x05d, 0x5d0: + penalty += penaltyWeight3 + bitBuffer = 0xFF + default: + if x == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d { + penalty += penaltyWeight3 + bitBuffer = 0xFF + } + } + } + } + + for x := 0; x < m.symbolSize; x++ { + var bitBuffer int16 = 0x00 + + for y := 0; y < m.symbolSize; y++ { + bitBuffer <<= 1 + if v := m.get(x, y); v { + bitBuffer |= 1 + } + + switch bitBuffer & 0x7ff { + // 0b000 0101 1101 or 0b10111010000 + // 0x05d or 0x5d0 + case 0x05d, 0x5d0: + penalty += penaltyWeight3 + bitBuffer = 0xFF + default: + if y == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d { + penalty += penaltyWeight3 + bitBuffer = 0xFF + } + } + } + } + + return penalty +} + +// penalty4 returns the penalty score... +func (m *symbol) penalty4() int { + numModules := m.symbolSize * m.symbolSize + numDarkModules := 0 + + for x := 0; x < m.symbolSize; x++ { + for y := 0; y < m.symbolSize; y++ { + if v := m.get(x, y); v { + numDarkModules++ + } + } + } + + numDarkModuleDeviation := numModules/2 - numDarkModules + if numDarkModuleDeviation < 0 { + numDarkModuleDeviation *= -1 + } + + return penaltyWeight4 * (numDarkModuleDeviation / (numModules / 20)) +} diff --git a/backend/vendor/github.com/skip2/go-qrcode/version.go b/backend/vendor/github.com/skip2/go-qrcode/version.go new file mode 100644 index 00000000..738cf3d3 --- /dev/null +++ b/backend/vendor/github.com/skip2/go-qrcode/version.go @@ -0,0 +1,3050 @@ +// go-qrcode +// Copyright 2014 Tom Harwood + +package qrcode + +import ( + "log" + + bitset "github.com/skip2/go-qrcode/bitset" +) + +// Error detection/recovery capacity. +// +// There are several levels of error detection/recovery capacity. Higher levels +// of error recovery are able to correct more errors, with the trade-off of +// increased symbol size. +type RecoveryLevel int + +const ( + // Level L: 7% error recovery. + Low RecoveryLevel = iota + + // Level M: 15% error recovery. Good default choice. + Medium + + // Level Q: 25% error recovery. + High + + // Level H: 30% error recovery. + Highest +) + +// qrCodeVersion describes the data length and encoding order of a single QR +// Code version. There are 40 versions numbers x 4 recovery levels == 160 +// possible qrCodeVersion structures. +type qrCodeVersion struct { + // Version number (1-40 inclusive). + version int + + // Error recovery level. + level RecoveryLevel + + dataEncoderType dataEncoderType + + // Encoded data can be split into multiple blocks. Each block contains data + // and error recovery bytes. + // + // Larger QR Codes contain more blocks. + block []block + + // Number of bits required to pad the combined data & error correction bit + // stream up to the symbol's full capacity. + numRemainderBits int +} + +type block struct { + numBlocks int + + // Total codewords (numCodewords == numErrorCodewords+numDataCodewords). + numCodewords int + + // Number of data codewords. + numDataCodewords int +} + +var ( + versions = []qrCodeVersion{ + { + 1, + Low, + dataEncoderType1To9, + []block{ + { + 1, + 26, + 19, + }, + }, + 0, + }, + { + 1, + Medium, + dataEncoderType1To9, + []block{ + { + 1, + 26, + 16, + }, + }, + 0, + }, + { + 1, + High, + dataEncoderType1To9, + []block{ + { + 1, + 26, + 13, + }, + }, + 0, + }, + { + 1, + Highest, + dataEncoderType1To9, + []block{ + { + 1, + 26, + 9, + }, + }, + 0, + }, + { + 2, + Low, + dataEncoderType1To9, + []block{ + { + 1, + 44, + 34, + }, + }, + 7, + }, + { + 2, + Medium, + dataEncoderType1To9, + []block{ + { + 1, + 44, + 28, + }, + }, + 7, + }, + { + 2, + High, + dataEncoderType1To9, + []block{ + { + 1, + 44, + 22, + }, + }, + 7, + }, + { + 2, + Highest, + dataEncoderType1To9, + []block{ + { + 1, + 44, + 16, + }, + }, + 7, + }, + { + 3, + Low, + dataEncoderType1To9, + []block{ + { + 1, + 70, + 55, + }, + }, + 7, + }, + { + 3, + Medium, + dataEncoderType1To9, + []block{ + { + 1, + 70, + 44, + }, + }, + 7, + }, + { + 3, + High, + dataEncoderType1To9, + []block{ + { + 2, + 35, + 17, + }, + }, + 7, + }, + { + 3, + Highest, + dataEncoderType1To9, + []block{ + { + 2, + 35, + 13, + }, + }, + 7, + }, + { + 4, + Low, + dataEncoderType1To9, + []block{ + { + 1, + 100, + 80, + }, + }, + 7, + }, + { + 4, + Medium, + dataEncoderType1To9, + []block{ + { + 2, + 50, + 32, + }, + }, + 7, + }, + { + 4, + High, + dataEncoderType1To9, + []block{ + { + 2, + 50, + 24, + }, + }, + 7, + }, + { + 4, + Highest, + dataEncoderType1To9, + []block{ + { + 4, + 25, + 9, + }, + }, + 7, + }, + { + 5, + Low, + dataEncoderType1To9, + []block{ + { + 1, + 134, + 108, + }, + }, + 7, + }, + { + 5, + Medium, + dataEncoderType1To9, + []block{ + { + 2, + 67, + 43, + }, + }, + 7, + }, + { + 5, + High, + dataEncoderType1To9, + []block{ + { + 2, + 33, + 15, + }, + { + 2, + 34, + 16, + }, + }, + 7, + }, + { + 5, + Highest, + dataEncoderType1To9, + []block{ + { + 2, + 33, + 11, + }, + { + 2, + 34, + 12, + }, + }, + 7, + }, + { + 6, + Low, + dataEncoderType1To9, + []block{ + { + 2, + 86, + 68, + }, + }, + 7, + }, + { + 6, + Medium, + dataEncoderType1To9, + []block{ + { + 4, + 43, + 27, + }, + }, + 7, + }, + { + 6, + High, + dataEncoderType1To9, + []block{ + { + 4, + 43, + 19, + }, + }, + 7, + }, + { + 6, + Highest, + dataEncoderType1To9, + []block{ + { + 4, + 43, + 15, + }, + }, + 7, + }, + { + 7, + Low, + dataEncoderType1To9, + []block{ + { + 2, + 98, + 78, + }, + }, + 0, + }, + { + 7, + Medium, + dataEncoderType1To9, + []block{ + { + 4, + 49, + 31, + }, + }, + 0, + }, + { + 7, + High, + dataEncoderType1To9, + []block{ + { + 2, + 32, + 14, + }, + { + 4, + 33, + 15, + }, + }, + 0, + }, + { + 7, + Highest, + dataEncoderType1To9, + []block{ + { + 4, + 39, + 13, + }, + { + 1, + 40, + 14, + }, + }, + 0, + }, + { + 8, + Low, + dataEncoderType1To9, + []block{ + { + 2, + 121, + 97, + }, + }, + 0, + }, + { + 8, + Medium, + dataEncoderType1To9, + []block{ + { + 2, + 60, + 38, + }, + { + 2, + 61, + 39, + }, + }, + 0, + }, + { + 8, + High, + dataEncoderType1To9, + []block{ + { + 4, + 40, + 18, + }, + { + 2, + 41, + 19, + }, + }, + 0, + }, + { + 8, + Highest, + dataEncoderType1To9, + []block{ + { + 4, + 40, + 14, + }, + { + 2, + 41, + 15, + }, + }, + 0, + }, + { + 9, + Low, + dataEncoderType1To9, + []block{ + { + 2, + 146, + 116, + }, + }, + 0, + }, + { + 9, + Medium, + dataEncoderType1To9, + []block{ + { + 3, + 58, + 36, + }, + { + 2, + 59, + 37, + }, + }, + 0, + }, + { + 9, + High, + dataEncoderType1To9, + []block{ + { + 4, + 36, + 16, + }, + { + 4, + 37, + 17, + }, + }, + 0, + }, + { + 9, + Highest, + dataEncoderType1To9, + []block{ + { + 4, + 36, + 12, + }, + { + 4, + 37, + 13, + }, + }, + 0, + }, + { + 10, + Low, + dataEncoderType10To26, + []block{ + { + 2, + 86, + 68, + }, + { + 2, + 87, + 69, + }, + }, + 0, + }, + { + 10, + Medium, + dataEncoderType10To26, + []block{ + { + 4, + 69, + 43, + }, + { + 1, + 70, + 44, + }, + }, + 0, + }, + { + 10, + High, + dataEncoderType10To26, + []block{ + { + 6, + 43, + 19, + }, + { + 2, + 44, + 20, + }, + }, + 0, + }, + { + 10, + Highest, + dataEncoderType10To26, + []block{ + { + 6, + 43, + 15, + }, + { + 2, + 44, + 16, + }, + }, + 0, + }, + { + 11, + Low, + dataEncoderType10To26, + []block{ + { + 4, + 101, + 81, + }, + }, + 0, + }, + { + 11, + Medium, + dataEncoderType10To26, + []block{ + { + 1, + 80, + 50, + }, + { + 4, + 81, + 51, + }, + }, + 0, + }, + { + 11, + High, + dataEncoderType10To26, + []block{ + { + 4, + 50, + 22, + }, + { + 4, + 51, + 23, + }, + }, + 0, + }, + { + 11, + Highest, + dataEncoderType10To26, + []block{ + { + 3, + 36, + 12, + }, + { + 8, + 37, + 13, + }, + }, + 0, + }, + { + 12, + Low, + dataEncoderType10To26, + []block{ + { + 2, + 116, + 92, + }, + { + 2, + 117, + 93, + }, + }, + 0, + }, + { + 12, + Medium, + dataEncoderType10To26, + []block{ + { + 6, + 58, + 36, + }, + { + 2, + 59, + 37, + }, + }, + 0, + }, + { + 12, + High, + dataEncoderType10To26, + []block{ + { + 4, + 46, + 20, + }, + { + 6, + 47, + 21, + }, + }, + 0, + }, + { + 12, + Highest, + dataEncoderType10To26, + []block{ + { + 7, + 42, + 14, + }, + { + 4, + 43, + 15, + }, + }, + 0, + }, + { + 13, + Low, + dataEncoderType10To26, + []block{ + { + 4, + 133, + 107, + }, + }, + 0, + }, + { + 13, + Medium, + dataEncoderType10To26, + []block{ + { + 8, + 59, + 37, + }, + { + 1, + 60, + 38, + }, + }, + 0, + }, + { + 13, + High, + dataEncoderType10To26, + []block{ + { + 8, + 44, + 20, + }, + { + 4, + 45, + 21, + }, + }, + 0, + }, + { + 13, + Highest, + dataEncoderType10To26, + []block{ + { + 12, + 33, + 11, + }, + { + 4, + 34, + 12, + }, + }, + 0, + }, + { + 14, + Low, + dataEncoderType10To26, + []block{ + { + 3, + 145, + 115, + }, + { + 1, + 146, + 116, + }, + }, + 3, + }, + { + 14, + Medium, + dataEncoderType10To26, + []block{ + { + 4, + 64, + 40, + }, + { + 5, + 65, + 41, + }, + }, + 3, + }, + { + 14, + High, + dataEncoderType10To26, + []block{ + { + 11, + 36, + 16, + }, + { + 5, + 37, + 17, + }, + }, + 3, + }, + { + 14, + Highest, + dataEncoderType10To26, + []block{ + { + 11, + 36, + 12, + }, + { + 5, + 37, + 13, + }, + }, + 3, + }, + { + 15, + Low, + dataEncoderType10To26, + []block{ + { + 5, + 109, + 87, + }, + { + 1, + 110, + 88, + }, + }, + 3, + }, + { + 15, + Medium, + dataEncoderType10To26, + []block{ + { + 5, + 65, + 41, + }, + { + 5, + 66, + 42, + }, + }, + 3, + }, + { + 15, + High, + dataEncoderType10To26, + []block{ + { + 5, + 54, + 24, + }, + { + 7, + 55, + 25, + }, + }, + 3, + }, + { + 15, + Highest, + dataEncoderType10To26, + []block{ + { + 11, + 36, + 12, + }, + { + 7, + 37, + 13, + }, + }, + 3, + }, + { + 16, + Low, + dataEncoderType10To26, + []block{ + { + 5, + 122, + 98, + }, + { + 1, + 123, + 99, + }, + }, + 3, + }, + { + 16, + Medium, + dataEncoderType10To26, + []block{ + { + 7, + 73, + 45, + }, + { + 3, + 74, + 46, + }, + }, + 3, + }, + { + 16, + High, + dataEncoderType10To26, + []block{ + { + 15, + 43, + 19, + }, + { + 2, + 44, + 20, + }, + }, + 3, + }, + { + 16, + Highest, + dataEncoderType10To26, + []block{ + { + 3, + 45, + 15, + }, + { + 13, + 46, + 16, + }, + }, + 3, + }, + { + 17, + Low, + dataEncoderType10To26, + []block{ + { + 1, + 135, + 107, + }, + { + 5, + 136, + 108, + }, + }, + 3, + }, + { + 17, + Medium, + dataEncoderType10To26, + []block{ + { + 10, + 74, + 46, + }, + { + 1, + 75, + 47, + }, + }, + 3, + }, + { + 17, + High, + dataEncoderType10To26, + []block{ + { + 1, + 50, + 22, + }, + { + 15, + 51, + 23, + }, + }, + 3, + }, + { + 17, + Highest, + dataEncoderType10To26, + []block{ + { + 2, + 42, + 14, + }, + { + 17, + 43, + 15, + }, + }, + 3, + }, + { + 18, + Low, + dataEncoderType10To26, + []block{ + { + 5, + 150, + 120, + }, + { + 1, + 151, + 121, + }, + }, + 3, + }, + { + 18, + Medium, + dataEncoderType10To26, + []block{ + { + 9, + 69, + 43, + }, + { + 4, + 70, + 44, + }, + }, + 3, + }, + { + 18, + High, + dataEncoderType10To26, + []block{ + { + 17, + 50, + 22, + }, + { + 1, + 51, + 23, + }, + }, + 3, + }, + { + 18, + Highest, + dataEncoderType10To26, + []block{ + { + 2, + 42, + 14, + }, + { + 19, + 43, + 15, + }, + }, + 3, + }, + { + 19, + Low, + dataEncoderType10To26, + []block{ + { + 3, + 141, + 113, + }, + { + 4, + 142, + 114, + }, + }, + 3, + }, + { + 19, + Medium, + dataEncoderType10To26, + []block{ + { + 3, + 70, + 44, + }, + { + 11, + 71, + 45, + }, + }, + 3, + }, + { + 19, + High, + dataEncoderType10To26, + []block{ + { + 17, + 47, + 21, + }, + { + 4, + 48, + 22, + }, + }, + 3, + }, + { + 19, + Highest, + dataEncoderType10To26, + []block{ + { + 9, + 39, + 13, + }, + { + 16, + 40, + 14, + }, + }, + 3, + }, + { + 20, + Low, + dataEncoderType10To26, + []block{ + { + 3, + 135, + 107, + }, + { + 5, + 136, + 108, + }, + }, + 3, + }, + { + 20, + Medium, + dataEncoderType10To26, + []block{ + { + 3, + 67, + 41, + }, + { + 13, + 68, + 42, + }, + }, + 3, + }, + { + 20, + High, + dataEncoderType10To26, + []block{ + { + 15, + 54, + 24, + }, + { + 5, + 55, + 25, + }, + }, + 3, + }, + { + 20, + Highest, + dataEncoderType10To26, + []block{ + { + 15, + 43, + 15, + }, + { + 10, + 44, + 16, + }, + }, + 3, + }, + { + 21, + Low, + dataEncoderType10To26, + []block{ + { + 4, + 144, + 116, + }, + { + 4, + 145, + 117, + }, + }, + 4, + }, + { + 21, + Medium, + dataEncoderType10To26, + []block{ + { + 17, + 68, + 42, + }, + }, + 4, + }, + { + 21, + High, + dataEncoderType10To26, + []block{ + { + 17, + 50, + 22, + }, + { + 6, + 51, + 23, + }, + }, + 4, + }, + { + 21, + Highest, + dataEncoderType10To26, + []block{ + { + 19, + 46, + 16, + }, + { + 6, + 47, + 17, + }, + }, + 4, + }, + { + 22, + Low, + dataEncoderType10To26, + []block{ + { + 2, + 139, + 111, + }, + { + 7, + 140, + 112, + }, + }, + 4, + }, + { + 22, + Medium, + dataEncoderType10To26, + []block{ + { + 17, + 74, + 46, + }, + }, + 4, + }, + { + 22, + High, + dataEncoderType10To26, + []block{ + { + 7, + 54, + 24, + }, + { + 16, + 55, + 25, + }, + }, + 4, + }, + { + 22, + Highest, + dataEncoderType10To26, + []block{ + { + 34, + 37, + 13, + }, + }, + 4, + }, + { + 23, + Low, + dataEncoderType10To26, + []block{ + { + 4, + 151, + 121, + }, + { + 5, + 152, + 122, + }, + }, + 4, + }, + { + 23, + Medium, + dataEncoderType10To26, + []block{ + { + 4, + 75, + 47, + }, + { + 14, + 76, + 48, + }, + }, + 4, + }, + { + 23, + High, + dataEncoderType10To26, + []block{ + { + 11, + 54, + 24, + }, + { + 14, + 55, + 25, + }, + }, + 4, + }, + { + 23, + Highest, + dataEncoderType10To26, + []block{ + { + 16, + 45, + 15, + }, + { + 14, + 46, + 16, + }, + }, + 4, + }, + { + 24, + Low, + dataEncoderType10To26, + []block{ + { + 6, + 147, + 117, + }, + { + 4, + 148, + 118, + }, + }, + 4, + }, + { + 24, + Medium, + dataEncoderType10To26, + []block{ + { + 6, + 73, + 45, + }, + { + 14, + 74, + 46, + }, + }, + 4, + }, + { + 24, + High, + dataEncoderType10To26, + []block{ + { + 11, + 54, + 24, + }, + { + 16, + 55, + 25, + }, + }, + 4, + }, + { + 24, + Highest, + dataEncoderType10To26, + []block{ + { + 30, + 46, + 16, + }, + { + 2, + 47, + 17, + }, + }, + 4, + }, + { + 25, + Low, + dataEncoderType10To26, + []block{ + { + 8, + 132, + 106, + }, + { + 4, + 133, + 107, + }, + }, + 4, + }, + { + 25, + Medium, + dataEncoderType10To26, + []block{ + { + 8, + 75, + 47, + }, + { + 13, + 76, + 48, + }, + }, + 4, + }, + { + 25, + High, + dataEncoderType10To26, + []block{ + { + 7, + 54, + 24, + }, + { + 22, + 55, + 25, + }, + }, + 4, + }, + { + 25, + Highest, + dataEncoderType10To26, + []block{ + { + 22, + 45, + 15, + }, + { + 13, + 46, + 16, + }, + }, + 4, + }, + { + 26, + Low, + dataEncoderType10To26, + []block{ + { + 10, + 142, + 114, + }, + { + 2, + 143, + 115, + }, + }, + 4, + }, + { + 26, + Medium, + dataEncoderType10To26, + []block{ + { + 19, + 74, + 46, + }, + { + 4, + 75, + 47, + }, + }, + 4, + }, + { + 26, + High, + dataEncoderType10To26, + []block{ + { + 28, + 50, + 22, + }, + { + 6, + 51, + 23, + }, + }, + 4, + }, + { + 26, + Highest, + dataEncoderType10To26, + []block{ + { + 33, + 46, + 16, + }, + { + 4, + 47, + 17, + }, + }, + 4, + }, + { + 27, + Low, + dataEncoderType27To40, + []block{ + { + 8, + 152, + 122, + }, + { + 4, + 153, + 123, + }, + }, + 4, + }, + { + 27, + Medium, + dataEncoderType27To40, + []block{ + { + 22, + 73, + 45, + }, + { + 3, + 74, + 46, + }, + }, + 4, + }, + { + 27, + High, + dataEncoderType27To40, + []block{ + { + 8, + 53, + 23, + }, + { + 26, + 54, + 24, + }, + }, + 4, + }, + { + 27, + Highest, + dataEncoderType27To40, + []block{ + { + 12, + 45, + 15, + }, + { + 28, + 46, + 16, + }, + }, + 4, + }, + { + 28, + Low, + dataEncoderType27To40, + []block{ + { + 3, + 147, + 117, + }, + { + 10, + 148, + 118, + }, + }, + 3, + }, + { + 28, + Medium, + dataEncoderType27To40, + []block{ + { + 3, + 73, + 45, + }, + { + 23, + 74, + 46, + }, + }, + 3, + }, + { + 28, + High, + dataEncoderType27To40, + []block{ + { + 4, + 54, + 24, + }, + { + 31, + 55, + 25, + }, + }, + 3, + }, + { + 28, + Highest, + dataEncoderType27To40, + []block{ + { + 11, + 45, + 15, + }, + { + 31, + 46, + 16, + }, + }, + 3, + }, + { + 29, + Low, + dataEncoderType27To40, + []block{ + { + 7, + 146, + 116, + }, + { + 7, + 147, + 117, + }, + }, + 3, + }, + { + 29, + Medium, + dataEncoderType27To40, + []block{ + { + 21, + 73, + 45, + }, + { + 7, + 74, + 46, + }, + }, + 3, + }, + { + 29, + High, + dataEncoderType27To40, + []block{ + { + 1, + 53, + 23, + }, + { + 37, + 54, + 24, + }, + }, + 3, + }, + { + 29, + Highest, + dataEncoderType27To40, + []block{ + { + 19, + 45, + 15, + }, + { + 26, + 46, + 16, + }, + }, + 3, + }, + { + 30, + Low, + dataEncoderType27To40, + []block{ + { + 5, + 145, + 115, + }, + { + 10, + 146, + 116, + }, + }, + 3, + }, + { + 30, + Medium, + dataEncoderType27To40, + []block{ + { + 19, + 75, + 47, + }, + { + 10, + 76, + 48, + }, + }, + 3, + }, + { + 30, + High, + dataEncoderType27To40, + []block{ + { + 15, + 54, + 24, + }, + { + 25, + 55, + 25, + }, + }, + 3, + }, + { + 30, + Highest, + dataEncoderType27To40, + []block{ + { + 23, + 45, + 15, + }, + { + 25, + 46, + 16, + }, + }, + 3, + }, + { + 31, + Low, + dataEncoderType27To40, + []block{ + { + 13, + 145, + 115, + }, + { + 3, + 146, + 116, + }, + }, + 3, + }, + { + 31, + Medium, + dataEncoderType27To40, + []block{ + { + 2, + 74, + 46, + }, + { + 29, + 75, + 47, + }, + }, + 3, + }, + { + 31, + High, + dataEncoderType27To40, + []block{ + { + 42, + 54, + 24, + }, + { + 1, + 55, + 25, + }, + }, + 3, + }, + { + 31, + Highest, + dataEncoderType27To40, + []block{ + { + 23, + 45, + 15, + }, + { + 28, + 46, + 16, + }, + }, + 3, + }, + { + 32, + Low, + dataEncoderType27To40, + []block{ + { + 17, + 145, + 115, + }, + }, + 3, + }, + { + 32, + Medium, + dataEncoderType27To40, + []block{ + { + 10, + 74, + 46, + }, + { + 23, + 75, + 47, + }, + }, + 3, + }, + { + 32, + High, + dataEncoderType27To40, + []block{ + { + 10, + 54, + 24, + }, + { + 35, + 55, + 25, + }, + }, + 3, + }, + { + 32, + Highest, + dataEncoderType27To40, + []block{ + { + 19, + 45, + 15, + }, + { + 35, + 46, + 16, + }, + }, + 3, + }, + { + 33, + Low, + dataEncoderType27To40, + []block{ + { + 17, + 145, + 115, + }, + { + 1, + 146, + 116, + }, + }, + 3, + }, + { + 33, + Medium, + dataEncoderType27To40, + []block{ + { + 14, + 74, + 46, + }, + { + 21, + 75, + 47, + }, + }, + 3, + }, + { + 33, + High, + dataEncoderType27To40, + []block{ + { + 29, + 54, + 24, + }, + { + 19, + 55, + 25, + }, + }, + 3, + }, + { + 33, + Highest, + dataEncoderType27To40, + []block{ + { + 11, + 45, + 15, + }, + { + 46, + 46, + 16, + }, + }, + 3, + }, + { + 34, + Low, + dataEncoderType27To40, + []block{ + { + 13, + 145, + 115, + }, + { + 6, + 146, + 116, + }, + }, + 3, + }, + { + 34, + Medium, + dataEncoderType27To40, + []block{ + { + 14, + 74, + 46, + }, + { + 23, + 75, + 47, + }, + }, + 3, + }, + { + 34, + High, + dataEncoderType27To40, + []block{ + { + 44, + 54, + 24, + }, + { + 7, + 55, + 25, + }, + }, + 3, + }, + { + 34, + Highest, + dataEncoderType27To40, + []block{ + { + 59, + 46, + 16, + }, + { + 1, + 47, + 17, + }, + }, + 3, + }, + { + 35, + Low, + dataEncoderType27To40, + []block{ + { + 12, + 151, + 121, + }, + { + 7, + 152, + 122, + }, + }, + 0, + }, + { + 35, + Medium, + dataEncoderType27To40, + []block{ + { + 12, + 75, + 47, + }, + { + 26, + 76, + 48, + }, + }, + 0, + }, + { + 35, + High, + dataEncoderType27To40, + []block{ + { + 39, + 54, + 24, + }, + { + 14, + 55, + 25, + }, + }, + 0, + }, + { + 35, + Highest, + dataEncoderType27To40, + []block{ + { + 22, + 45, + 15, + }, + { + 41, + 46, + 16, + }, + }, + 0, + }, + { + 36, + Low, + dataEncoderType27To40, + []block{ + { + 6, + 151, + 121, + }, + { + 14, + 152, + 122, + }, + }, + 0, + }, + { + 36, + Medium, + dataEncoderType27To40, + []block{ + { + 6, + 75, + 47, + }, + { + 34, + 76, + 48, + }, + }, + 0, + }, + { + 36, + High, + dataEncoderType27To40, + []block{ + { + 46, + 54, + 24, + }, + { + 10, + 55, + 25, + }, + }, + 0, + }, + { + 36, + Highest, + dataEncoderType27To40, + []block{ + { + 2, + 45, + 15, + }, + { + 64, + 46, + 16, + }, + }, + 0, + }, + { + 37, + Low, + dataEncoderType27To40, + []block{ + { + 17, + 152, + 122, + }, + { + 4, + 153, + 123, + }, + }, + 0, + }, + { + 37, + Medium, + dataEncoderType27To40, + []block{ + { + 29, + 74, + 46, + }, + { + 14, + 75, + 47, + }, + }, + 0, + }, + { + 37, + High, + dataEncoderType27To40, + []block{ + { + 49, + 54, + 24, + }, + { + 10, + 55, + 25, + }, + }, + 0, + }, + { + 37, + Highest, + dataEncoderType27To40, + []block{ + { + 24, + 45, + 15, + }, + { + 46, + 46, + 16, + }, + }, + 0, + }, + { + 38, + Low, + dataEncoderType27To40, + []block{ + { + 4, + 152, + 122, + }, + { + 18, + 153, + 123, + }, + }, + 0, + }, + { + 38, + Medium, + dataEncoderType27To40, + []block{ + { + 13, + 74, + 46, + }, + { + 32, + 75, + 47, + }, + }, + 0, + }, + { + 38, + High, + dataEncoderType27To40, + []block{ + { + 48, + 54, + 24, + }, + { + 14, + 55, + 25, + }, + }, + 0, + }, + { + 38, + Highest, + dataEncoderType27To40, + []block{ + { + 42, + 45, + 15, + }, + { + 32, + 46, + 16, + }, + }, + 0, + }, + { + 39, + Low, + dataEncoderType27To40, + []block{ + { + 20, + 147, + 117, + }, + { + 4, + 148, + 118, + }, + }, + 0, + }, + { + 39, + Medium, + dataEncoderType27To40, + []block{ + { + 40, + 75, + 47, + }, + { + 7, + 76, + 48, + }, + }, + 0, + }, + { + 39, + High, + dataEncoderType27To40, + []block{ + { + 43, + 54, + 24, + }, + { + 22, + 55, + 25, + }, + }, + 0, + }, + { + 39, + Highest, + dataEncoderType27To40, + []block{ + { + 10, + 45, + 15, + }, + { + 67, + 46, + 16, + }, + }, + 0, + }, + { + 40, + Low, + dataEncoderType27To40, + []block{ + { + 19, + 148, + 118, + }, + { + 6, + 149, + 119, + }, + }, + 0, + }, + { + 40, + Medium, + dataEncoderType27To40, + []block{ + { + 18, + 75, + 47, + }, + { + 31, + 76, + 48, + }, + }, + 0, + }, + { + 40, + High, + dataEncoderType27To40, + []block{ + { + 34, + 54, + 24, + }, + { + 34, + 55, + 25, + }, + }, + 0, + }, + { + 40, + Highest, + dataEncoderType27To40, + []block{ + { + 20, + 45, + 15, + }, + { + 61, + 46, + 16, + }, + }, + 0, + }, + } +) + +var ( + // Each QR Code contains a 15-bit Format Information value. The 15 bits + // consist of 5 data bits concatenated with 10 error correction bits. + // + // The 5 data bits consist of: + // - 2 bits for the error correction level (L=01, M=00, G=11, H=10). + // - 3 bits for the data mask pattern identifier. + // + // formatBitSequence is a mapping from the 5 data bits to the completed 15-bit + // Format Information value. + // + // For example, a QR Code using error correction level L, and data mask + // pattern identifier 001: + // + // 01 | 001 = 01001 = 0x9 + // formatBitSequence[0x9].qrCode = 0x72f3 = 111001011110011 + formatBitSequence = []struct { + regular uint32 + micro uint32 + }{ + {0x5412, 0x4445}, + {0x5125, 0x4172}, + {0x5e7c, 0x4e2b}, + {0x5b4b, 0x4b1c}, + {0x45f9, 0x55ae}, + {0x40ce, 0x5099}, + {0x4f97, 0x5fc0}, + {0x4aa0, 0x5af7}, + {0x77c4, 0x6793}, + {0x72f3, 0x62a4}, + {0x7daa, 0x6dfd}, + {0x789d, 0x68ca}, + {0x662f, 0x7678}, + {0x6318, 0x734f}, + {0x6c41, 0x7c16}, + {0x6976, 0x7921}, + {0x1689, 0x06de}, + {0x13be, 0x03e9}, + {0x1ce7, 0x0cb0}, + {0x19d0, 0x0987}, + {0x0762, 0x1735}, + {0x0255, 0x1202}, + {0x0d0c, 0x1d5b}, + {0x083b, 0x186c}, + {0x355f, 0x2508}, + {0x3068, 0x203f}, + {0x3f31, 0x2f66}, + {0x3a06, 0x2a51}, + {0x24b4, 0x34e3}, + {0x2183, 0x31d4}, + {0x2eda, 0x3e8d}, + {0x2bed, 0x3bba}, + } + + // QR Codes version 7 and higher contain an 18-bit Version Information value, + // consisting of a 6 data bits and 12 error correction bits. + // + // versionBitSequence is a mapping from QR Code version to the completed + // 18-bit Version Information value. + // + // For example, a QR code of version 7: + // versionBitSequence[0x7] = 0x07c94 = 000111110010010100 + versionBitSequence = []uint32{ + 0x00000, + 0x00000, + 0x00000, + 0x00000, + 0x00000, + 0x00000, + 0x00000, + 0x07c94, + 0x085bc, + 0x09a99, + 0x0a4d3, + 0x0bbf6, + 0x0c762, + 0x0d847, + 0x0e60d, + 0x0f928, + 0x10b78, + 0x1145d, + 0x12a17, + 0x13532, + 0x149a6, + 0x15683, + 0x168c9, + 0x177ec, + 0x18ec4, + 0x191e1, + 0x1afab, + 0x1b08e, + 0x1cc1a, + 0x1d33f, + 0x1ed75, + 0x1f250, + 0x209d5, + 0x216f0, + 0x228ba, + 0x2379f, + 0x24b0b, + 0x2542e, + 0x26a64, + 0x27541, + 0x28c69, + } +) + +const ( + formatInfoLengthBits = 15 + versionInfoLengthBits = 18 +) + +// formatInfo returns the 15-bit Format Information value for a QR +// code. +func (v qrCodeVersion) formatInfo(maskPattern int) *bitset.Bitset { + formatID := 0 + + switch v.level { + case Low: + formatID = 0x08 // 0b01000 + case Medium: + formatID = 0x00 // 0b00000 + case High: + formatID = 0x18 // 0b11000 + case Highest: + formatID = 0x10 // 0b10000 + default: + log.Panicf("Invalid level %d", v.level) + } + + if maskPattern < 0 || maskPattern > 7 { + log.Panicf("Invalid maskPattern %d", maskPattern) + } + + formatID |= maskPattern & 0x7 + + result := bitset.New() + + result.AppendUint32(formatBitSequence[formatID].regular, formatInfoLengthBits) + + return result +} + +// versionInfo returns the 18-bit Version Information value for a QR Code. +// +// Version Information is applicable only to QR Codes versions 7-40 inclusive. +// nil is returned if Version Information is not required. +func (v qrCodeVersion) versionInfo() *bitset.Bitset { + if v.version < 7 { + return nil + } + + result := bitset.New() + result.AppendUint32(versionBitSequence[v.version], 18) + + return result +} + +// numDataBits returns the data capacity in bits. +func (v qrCodeVersion) numDataBits() int { + numDataBits := 0 + for _, b := range v.block { + numDataBits += 8 * b.numBlocks * b.numDataCodewords // 8 bits in a byte + } + + return numDataBits +} + +// chooseQRCodeVersion chooses the most suitable QR Code version for a stated +// data length in bits, the error recovery level required, and the data encoder +// used. +// +// The chosen QR Code version is the smallest version able to fit numDataBits +// and the optional terminator bits required by the specified encoder. +// +// On success the chosen QR Code version is returned. +func chooseQRCodeVersion(level RecoveryLevel, encoder *dataEncoder, numDataBits int) *qrCodeVersion { + var chosenVersion *qrCodeVersion + + for _, v := range versions { + if v.level != level { + continue + } else if v.version < encoder.minVersion { + continue + } else if v.version > encoder.maxVersion { + break + } + + numFreeBits := v.numDataBits() - numDataBits + + if numFreeBits >= 0 { + chosenVersion = &v + break + } + } + + return chosenVersion +} + +func (v qrCodeVersion) numTerminatorBitsRequired(numDataBits int) int { + numFreeBits := v.numDataBits() - numDataBits + + var numTerminatorBits int + + switch { + case numFreeBits >= 4: + numTerminatorBits = 4 + default: + numTerminatorBits = numFreeBits + } + + return numTerminatorBits +} + +// numBlocks returns the number of blocks. +func (v qrCodeVersion) numBlocks() int { + numBlocks := 0 + + for _, b := range v.block { + numBlocks += b.numBlocks + } + + return numBlocks +} + +// numBitsToPadToCodeword returns the number of bits required to pad data of +// length numDataBits upto the nearest codeword size. +func (v qrCodeVersion) numBitsToPadToCodeword(numDataBits int) int { + if numDataBits == v.numDataBits() { + return 0 + } + + return (8 - numDataBits%8) % 8 +} + +// symbolSize returns the size of the QR Code symbol in number of modules (which +// is both the width and height, since QR codes are square). The QR Code has +// size symbolSize() x symbolSize() pixels. This does not include the quiet +// zone. +func (v qrCodeVersion) symbolSize() int { + return 21 + (v.version-1)*4 +} + +// quietZoneSize returns the number of pixels of border space on each side of +// the QR Code. The quiet space assists with decoding. +func (v qrCodeVersion) quietZoneSize() int { + return 4 +} + +// getQRCodeVersion returns the QR Code version by version number and recovery +// level. Returns nil if the requested combination is not defined. +func getQRCodeVersion(level RecoveryLevel, version int) *qrCodeVersion { + for _, v := range versions { + if v.level == level && v.version == version { + return &v + } + } + + return nil +} diff --git a/backend/vendor/modules.txt b/backend/vendor/modules.txt index d82e7040..03816bb0 100644 --- a/backend/vendor/modules.txt +++ b/backend/vendor/modules.txt @@ -177,6 +177,11 @@ github.com/rs/xid # github.com/russross/blackfriday/v2 v2.1.0 ## explicit github.com/russross/blackfriday/v2 +# github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e +## explicit +github.com/skip2/go-qrcode +github.com/skip2/go-qrcode/bitset +github.com/skip2/go-qrcode/reedsolomon # github.com/slack-go/slack v0.10.1 ## explicit github.com/slack-go/slack