diff --git a/go.mod b/go.mod
index 9463902..3a33fc3 100644
--- a/go.mod
+++ b/go.mod
@@ -28,10 +28,12 @@ require (
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
github.com/kr/text v0.2.0 // indirect
+ github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rymdport/portal v0.4.1 // indirect
+ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
diff --git a/go.sum b/go.sum
index 65f6351..c629956 100644
--- a/go.sum
+++ b/go.sum
@@ -6,6 +6,7 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
@@ -48,6 +49,8 @@ github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe9
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea h1:uyJ13zfy6l79CM3HnVhDalIyZ4RJAyVfDrbnfFeJoC4=
+github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea/go.mod h1:w4pGU9PkiX2hAWyF0yuHEHmYTQFAd6WHzp6+IY7JVjE=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
@@ -61,6 +64,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
+github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
+github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
@@ -69,6 +74,8 @@ github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiY
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
@@ -92,5 +99,6 @@ golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/qr_support.go b/qr_support.go
new file mode 100644
index 0000000..b7a3f17
--- /dev/null
+++ b/qr_support.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "bytes"
+ "errors"
+ "image"
+ "image/png"
+
+ "github.com/liyue201/goqr"
+ "github.com/skip2/go-qrcode"
+)
+
+// GenerateQRPNG returns PNG bytes for the given text.
+func GenerateQRPNG(text string, size int) ([]byte, error) {
+ if size <= 0 {
+ size = 256
+ }
+ png, err := qrcode.Encode(text, qrcode.Medium, size)
+ if err != nil {
+ return nil, err
+ }
+ return png, nil
+}
+
+// DecodeQR decodes first QR code text from an image.
+func DecodeQR(img image.Image) (string, error) {
+ codes, err := goqr.Recognize(img)
+ if err != nil {
+ return "", err
+ }
+ if len(codes) == 0 {
+ return "", errors.New("no qr code found")
+ }
+ return string(codes[0].Payload), nil
+}
+
+// LoadPNG decodes raw PNG bytes to image.Image.
+func LoadPNG(b []byte) (image.Image, error) {
+ im, err := png.Decode(bytes.NewReader(b))
+ if err != nil {
+ return nil, err
+ }
+ return im, nil
+}
diff --git a/ui.go b/ui.go
index 96b8e71..b6e1c49 100644
--- a/ui.go
+++ b/ui.go
@@ -4,6 +4,7 @@ import (
"errors"
encrypt "fckeuspy-go/lib"
"image/color"
+ "io"
"os"
"time"
@@ -11,30 +12,57 @@ import (
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
+ "fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type uiParts struct {
- outKey *widget.Entry
- msg *widget.Entry
- peer *widget.Entry
- cipherOut *widget.Entry
- payload *widget.Entry
- plainOut *widget.Entry
- toastLabel *widget.Label
+ outKey *widget.Entry
+ msg *widget.Entry
+ peer *widget.Entry
+ cipherOut *widget.Entry
+ payload *widget.Entry
+ plainOut *widget.Entry
+ toastLabel *widget.Label
+ cipherQR *canvas.Image
+ pubQR *canvas.Image
+ crtQR *canvas.Image
+ showQR bool
+ peerQR *canvas.Image
+ showPeerQR bool
+ payloadQR *canvas.Image
+ showPayloadQR bool
}
func buildEntries() *uiParts {
p := &uiParts{
- outKey: widget.NewMultiLineEntry(),
- msg: widget.NewMultiLineEntry(),
- peer: widget.NewMultiLineEntry(),
- cipherOut: widget.NewMultiLineEntry(),
- payload: widget.NewMultiLineEntry(),
- plainOut: widget.NewMultiLineEntry(),
- toastLabel: widget.NewLabel(""),
+ outKey: widget.NewMultiLineEntry(),
+ msg: widget.NewMultiLineEntry(),
+ peer: widget.NewMultiLineEntry(),
+ cipherOut: widget.NewMultiLineEntry(),
+ payload: widget.NewMultiLineEntry(),
+ plainOut: widget.NewMultiLineEntry(),
+ toastLabel: widget.NewLabel(""),
+ cipherQR: canvas.NewImageFromImage(nil),
+ pubQR: canvas.NewImageFromImage(nil),
+ crtQR: canvas.NewImageFromImage(nil),
+ showQR: true,
+ peerQR: canvas.NewImageFromImage(nil),
+ showPeerQR: true,
+ payloadQR: canvas.NewImageFromImage(nil),
+ showPayloadQR: true,
}
+ p.cipherQR.FillMode = canvas.ImageFillContain
+ p.cipherQR.SetMinSize(fyne.NewSize(260, 260))
+ p.pubQR.FillMode = canvas.ImageFillContain
+ p.pubQR.SetMinSize(fyne.NewSize(220, 220))
+ p.crtQR.FillMode = canvas.ImageFillContain
+ p.crtQR.SetMinSize(fyne.NewSize(220, 220))
+ p.peerQR.FillMode = canvas.ImageFillContain
+ p.peerQR.SetMinSize(fyne.NewSize(220, 220))
+ p.payloadQR.FillMode = canvas.ImageFillContain
+ p.payloadQR.SetMinSize(fyne.NewSize(260, 260))
p.outKey.SetPlaceHolder("Veřejný klíč / certifikát…")
p.msg.SetPlaceHolder("Sem napiš zprávu…")
p.peer.SetPlaceHolder("-----BEGIN PUBLIC KEY----- … nebo CERTIFICATE …")
@@ -93,7 +121,6 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.
btnPaste := widget.NewButton("Paste", func() { parts.outKey.SetText(fyne.CurrentApp().Clipboard().Content()) })
deleteBtn := widget.NewButton("Smazat identitu", func() {
- // dialog pro heslo
pwEntry := widget.NewPasswordEntry()
pwEntry.SetPlaceHolder("Heslo pro potvrzení…")
content := widget.NewForm(widget.NewFormItem("Heslo", pwEntry))
@@ -114,16 +141,71 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.
dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
return
}
- // Quit app po odstranění (uživatel musí znovu spustit a vytvořit nový vault)
fyne.CurrentApp().Quit()
}, fyne.CurrentApp().Driver().AllWindows()[0])
})
- tileIdentity := buttonTile(btnCopyPub, btnCopyCrt, btnPaste, btnShowPub, btnShowCrt, btnClear, deleteBtn)
+ makeQR := func(data string, target *canvas.Image) {
+ if data == "" {
+ target.Image = nil
+ target.Refresh()
+ return
+ }
+ pngBytes, err := GenerateQRPNG(data, 512)
+ if err != nil {
+ return
+ }
+ img, err := LoadPNG(pngBytes)
+ if err != nil {
+ return
+ }
+ target.Image = img
+ target.Refresh()
+ }
+ updateQRImages := func() {
+ if parts.showQR {
+ makeQR(svc.PublicPEM(), parts.pubQR)
+ makeQR(svc.PublicCert(), parts.crtQR)
+ } else {
+ parts.pubQR.Image = nil
+ parts.pubQR.Refresh()
+ parts.crtQR.Image = nil
+ parts.crtQR.Refresh()
+ }
+ }
+
+ identityContainer := container.NewVBox()
+
+ toggleBtn := widget.NewButton("", nil)
+ var rebuild func()
+ rebuild = func() {
+ identityContainer.Objects = nil
+ if parts.showQR {
+ updateQRImages()
+ // Wrap each QR with its copy button
+ pubBox := container.NewVBox(parts.pubQR, widget.NewButton("Copy", func() { copyClip(svc.PublicPEM(), parts) }))
+ crtBox := container.NewVBox(parts.crtQR, widget.NewButton("Copy", func() { copyClip(svc.PublicCert(), parts) }))
+ identityContainer.Add(container.NewGridWithColumns(2, pubBox, crtBox))
+ } else {
+ // show combined text for convenience
+ parts.outKey.SetText(svc.PublicPEM() + "\n" + svc.PublicCert())
+ identityContainer.Add(parts.outKey)
+ }
+ identityContainer.Refresh()
+ if parts.showQR {
+ toggleBtn.SetText("Zobrazit plaintext")
+ } else {
+ toggleBtn.SetText("Zobrazit QR")
+ }
+ }
+ toggleBtn.OnTapped = func() { parts.showQR = !parts.showQR; rebuild() }
+ rebuild()
+
+ tileIdentity := buttonTile(btnCopyPub, btnCopyCrt, btnPaste, btnShowPub, btnShowCrt, btnClear, deleteBtn, toggleBtn)
group := container.NewVBox(
widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
tileIdentity,
- parts.outKey,
+ identityContainer,
)
return container.NewVScroll(group)
}
@@ -148,11 +230,71 @@ func copyClip(s string, parts *uiParts) {
// assembleResponsive builds split view that collapses for narrow widths
// Tab: Encrypt
func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
- parts.cipherOut.Disable() // read only output
+ parts.cipherOut.Disable()
peerBtns := buttonTile(
widget.NewButton("Paste", func() { parts.peer.SetText(fyne.CurrentApp().Clipboard().Content()) }),
widget.NewButton("Clear", func() { parts.peer.SetText("") }),
+ widget.NewButton("Copy", func() { copyClip(parts.peer.Text, parts) }),
)
+
+ peerContainer := container.NewVBox()
+ peerToggle := widget.NewButton("", nil)
+ var updatePeer func()
+ updatePeerQR := func(text string) {
+ if text == "" {
+ parts.peerQR.Image = nil
+ parts.peerQR.Refresh()
+ return
+ }
+ pngBytes, err := GenerateQRPNG(text, 512)
+ if err != nil {
+ return
+ }
+ img, err := LoadPNG(pngBytes)
+ if err != nil {
+ return
+ }
+ parts.peerQR.Image = img
+ parts.peerQR.Refresh()
+ }
+ updatePeer = func() {
+ peerContainer.Objects = nil
+ if parts.showPeerQR {
+ updatePeerQR(parts.peer.Text)
+ peerContainer.Add(container.NewVBox(parts.peerQR, widget.NewButton("Copy", func() { copyClip(parts.peer.Text, parts) })))
+ peerToggle.SetText("Zobrazit plaintext")
+ } else {
+ peerContainer.Add(parts.peer)
+ peerToggle.SetText("Zobrazit QR")
+ }
+ peerContainer.Refresh()
+ }
+ peerToggle.OnTapped = func() { parts.showPeerQR = !parts.showPeerQR; updatePeer() }
+ parts.peer.OnChanged = func(string) {
+ if parts.showPeerQR {
+ updatePeerQR(parts.peer.Text)
+ }
+ }
+ updatePeer()
+ updateQR := func(text string) {
+ if text == "" {
+ parts.cipherQR.Image = nil
+ parts.cipherQR.Refresh()
+ return
+ }
+ pngBytes, err := GenerateQRPNG(text, 512)
+ if err != nil {
+ parts.showToast("QR error")
+ return
+ }
+ img, err := LoadPNG(pngBytes)
+ if err != nil {
+ parts.showToast("PNG err")
+ return
+ }
+ parts.cipherQR.Image = img
+ parts.cipherQR.Refresh()
+ }
encAction := func() {
m := parts.msg.Text
p := parts.peer.Text
@@ -163,23 +305,74 @@ func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
go func(msg, peer string) {
res, err := svc.Encrypt(msg, peer)
if err != nil {
- fyne.Do(func() { parts.cipherOut.SetText(""); parts.showToast("Chyba") })
+ fyne.Do(func() { parts.cipherOut.SetText(""); updateQR(""); parts.showToast("Chyba") })
return
}
- fyne.Do(func() { parts.cipherOut.SetText(res); parts.showToast("OK") })
+ fyne.Do(func() {
+ parts.cipherOut.SetText(res)
+ if parts.showQR {
+ updateQR(res)
+ }
+ parts.showToast("OK")
+ })
}(m, p)
}
+ importPeerQR := func() {
+ fd := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) {
+ if err != nil || r == nil {
+ return
+ }
+ defer r.Close()
+ data, _ := io.ReadAll(r)
+ img, err := LoadPNG(data)
+ if err != nil {
+ dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
+ return
+ }
+ text, err := DecodeQR(img)
+ if err != nil {
+ dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
+ return
+ }
+ parts.peer.SetText(text)
+ }, fyne.CurrentApp().Driver().AllWindows()[0])
+ fd.SetFilter(storage.NewExtensionFileFilter([]string{".png"}))
+ fd.Show()
+ }
+ outputContainer := container.NewVBox()
+ toggleBtn := widget.NewButton("", nil)
+ var updateMode func()
+ updateMode = func() {
+ outputContainer.Objects = nil
+ if parts.showQR {
+ updateQR(parts.cipherOut.Text)
+ outputContainer.Add(container.NewVBox(parts.cipherQR, widget.NewButton("Copy", func() { copyClip(parts.cipherOut.Text, parts) })))
+ } else {
+ outputContainer.Add(parts.cipherOut)
+ }
+ if parts.showQR {
+ toggleBtn.SetText("Zobrazit plaintext")
+ } else {
+ toggleBtn.SetText("Zobrazit QR")
+ }
+ outputContainer.Refresh()
+ }
+ toggleBtn.OnTapped = func() { parts.showQR = !parts.showQR; updateMode() }
+ updateMode()
+
msgBtns := buttonTile(
widget.NewButton("Clear+Paste", func() { parts.msg.SetText(""); parts.msg.SetText(fyne.CurrentApp().Clipboard().Content()) }),
- widget.NewButton("Encrypt", encAction),
- widget.NewButton("Copy encrypted", func() { copyClip(parts.cipherOut.Text, parts) }),
+ widget.NewButton("Encrypt", func() { encAction(); updateMode() }),
+ widget.NewButton("Copy", func() { copyClip(parts.cipherOut.Text, parts) }),
+ widget.NewButton("Import Key QR", importPeerQR),
)
group := container.NewVBox(
widget.NewLabelWithStyle("Šifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
- widget.NewLabel("Veřejný klíč příjemce"), peerBtns, parts.peer,
+ container.NewHBox(widget.NewLabel("Veřejný klíč příjemce"), peerToggle), peerBtns, peerContainer,
widget.NewLabel("Zpráva"), msgBtns, parts.msg,
- widget.NewLabel("Výsledek"), parts.cipherOut,
+ container.NewHBox(widget.NewLabel("Výstup"), toggleBtn),
+ outputContainer,
)
return container.NewVScroll(group)
}
@@ -202,19 +395,94 @@ func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
fyne.Do(func() { parts.plainOut.SetText(res); parts.showToast("OK") })
}(pl)
}
+ updatePayloadQR := func(text string) {
+ if text == "" {
+ parts.payloadQR.Image = nil
+ parts.payloadQR.Refresh()
+ return
+ }
+ pngBytes, err := GenerateQRPNG(text, 512)
+ if err != nil {
+ return
+ }
+ img, err := LoadPNG(pngBytes)
+ if err != nil {
+ return
+ }
+ parts.payloadQR.Image = img
+ parts.payloadQR.Refresh()
+ }
+ importPayloadQR := func() {
+ fd := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) {
+ if err != nil || r == nil {
+ return
+ }
+ defer r.Close()
+ data, _ := io.ReadAll(r)
+ img, err := LoadPNG(data)
+ if err != nil {
+ dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
+ return
+ }
+ text, err := DecodeQR(img)
+ if err != nil {
+ dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
+ return
+ }
+ parts.payload.SetText(text)
+ if parts.showPayloadQR {
+ updatePayloadQR(text)
+ }
+ decryptAction()
+ }, fyne.CurrentApp().Driver().AllWindows()[0])
+ fd.SetFilter(storage.NewExtensionFileFilter([]string{".png"}))
+ fd.Show()
+ }
payloadBtns := buttonTile(
widget.NewButton("Paste+Decrypt", func() {
clip := fyne.CurrentApp().Clipboard().Content()
parts.payload.SetText(clip)
+ if parts.showPayloadQR {
+ updatePayloadQR(clip)
+ }
decryptAction()
}),
- // widget.NewButton("Decrypt", decryptAction),
- widget.NewButton("Clear", func() { parts.payload.SetText(""); parts.plainOut.SetText("") }),
+ widget.NewButton("Clear", func() {
+ parts.payload.SetText("")
+ parts.plainOut.SetText("")
+ if parts.showPayloadQR {
+ updatePayloadQR("")
+ }
+ }),
+ widget.NewButton("QR Import", importPayloadQR),
)
+
+ payloadContainer := container.NewVBox()
+ payloadToggle := widget.NewButton("", nil)
+ var updateMode func()
+ updateMode = func() {
+ payloadContainer.Objects = nil
+ if parts.showPayloadQR {
+ updatePayloadQR(parts.payload.Text)
+ payloadContainer.Add(container.NewVBox(parts.payloadQR, widget.NewButton("Copy", func() { copyClip(parts.payload.Text, parts) })))
+ payloadToggle.SetText("Zobrazit plaintext")
+ } else {
+ payloadContainer.Add(parts.payload)
+ payloadToggle.SetText("Zobrazit QR")
+ }
+ payloadContainer.Refresh()
+ }
+ payloadToggle.OnTapped = func() { parts.showPayloadQR = !parts.showPayloadQR; updateMode() }
+ parts.payload.OnChanged = func(string) {
+ if parts.showPayloadQR {
+ updatePayloadQR(parts.payload.Text)
+ }
+ }
+ updateMode()
plainBtns := buttonTile(widget.NewButton("Copy", func() { copyClip(parts.plainOut.Text, parts) }))
group := container.NewVBox(
widget.NewLabelWithStyle("Dešifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
- widget.NewLabel("Payload"), payloadBtns, parts.payload,
+ container.NewHBox(widget.NewLabel("Payload"), payloadToggle), payloadBtns, payloadContainer,
widget.NewLabel("Výsledek"), plainBtns, parts.plainOut,
)
return container.NewVScroll(group)
diff --git a/vendor/github.com/liyue201/goqr/.travis.yml b/vendor/github.com/liyue201/goqr/.travis.yml
new file mode 100644
index 0000000..7371418
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/.travis.yml
@@ -0,0 +1,23 @@
+language: go
+
+notifications:
+ email:
+ recipients:
+ - liyue201@126.com
+ on_success: change
+ on_failure: always
+
+go:
+ - 1.12
+
+install:
+ - go get github.com/mattn/goveralls
+
+script:
+ - go test ./... -v -covermode=count -coverprofile=coverage.out
+ - $GOPATH/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN
+
+env:
+ global:
+ - GO111MODULE=on
+ - secure: "aOzEdb/ST82OpO4LqLpwrdRs6GnkmdLJbm3o9+2HmJCyiHkhqPY1bGY1TiYxQLl3GkjFkuVP+3RB+E0p2R/1f4xYkg4cIpPoLm+4xJ7c1NsEGTaWVS8rCNTicqdym7C9kJart6KcAeWWoElFxob3+lTdS6sz4kFaURrVQ/lpbzBESH0B5Rwa35u1WasmHkPxsHJ/2a7Xnknv+SerjgMUDiXUYM5IjUWt2fV2Zq/9blTSReng6+6Lj9EdziZcQfWNJxBK/NDBtpNIpOdY+bZEUDQi7InB3IioKpQeVjMGKSAzqhedqnGbZsaO734Z8VjRjcFRJPuuYMMkMw4hGoplDqqrTa2aFgln54NiNk2R6WIcpPKAFdLEL7rCPx6qRLMRq1EEn6h+lAy5mJ6NIpmJmY/IbDEVH9D4z4YVLJbHhU+yAN4Tk5mwW9N2bLceSNxIRSJcCcDlRiBNba5XLdNb/tHq7PEM2lX1oXp4WzdYgGh08a69OAweY/CRQjiTZrbhmbsZ5qo3UMLA34HTfEVWt8UWeAFry5fhtzeLqQa5h0BPwtFsy9bsdukEL3nAWcWF4HaQ2jh2CNO6OvpzRb8i45qj1QpIvGqy/lJbuO5WswFQaZcKK2nmPa2M4f7+EBGFe3VvmOJcJvAt2ZRYVZalbdTLDKMmMgcdYIL4jzzcSs4="
\ No newline at end of file
diff --git a/vendor/github.com/liyue201/goqr/LICENSE b/vendor/github.com/liyue201/goqr/LICENSE
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/vendor/github.com/liyue201/goqr/README.md b/vendor/github.com/liyue201/goqr/README.md
new file mode 100644
index 0000000..b3de6a2
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/README.md
@@ -0,0 +1,54 @@
+# goqr
+[](https://godoc.org/github.com/liyue201/goqr)
+[](https://goreportcard.com/report/github.com/liyue201/goqr)
+[](https://travis-ci.org/liyue201/goqr)
+[](https://coveralls.io/github/liyue201/goqr)
+[](/LICENSE)
+[](/example)
+
+
+This is a QR Code recognition and decoding library in pure go. It can recognize most of images into QR Code string.
+
+# Example
+
+```
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "github.com/liyue201/goqr"
+ "image"
+ _ "image/jpeg"
+ _ "image/png"
+ "io/ioutil"
+)
+
+func recognizeFile(path string) {
+ fmt.Printf("recognize file: %v\n", path)
+ imgdata, err := ioutil.ReadFile(path)
+ if err != nil {
+ fmt.Printf("%v\n", err)
+ return
+ }
+
+ img, _, err := image.Decode(bytes.NewReader(imgdata))
+ if err != nil {
+ fmt.Printf("image.Decode error: %v\n", err)
+ return
+ }
+ qrCodes, err := goqr.Recognize(img)
+ if err != nil {
+ fmt.Printf("Recognize failed: %v\n", err)
+ return
+ }
+ for _, qrCode := range qrCodes {
+ fmt.Printf("qrCode text: %s\n", qrCode.Payload)
+ }
+}
+
+func main() {
+ recognizeFile("testdata/008.png")
+}
+
+```
diff --git a/vendor/github.com/liyue201/goqr/decoding.go b/vendor/github.com/liyue201/goqr/decoding.go
new file mode 100644
index 0000000..22fa3fb
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/decoding.go
@@ -0,0 +1,716 @@
+package goqr
+
+import (
+ "math"
+)
+
+const maxPoly = 64
+
+type galoisField struct {
+ p int
+ log []uint8
+ exp []uint8
+}
+
+var gf16Exp = []uint8{
+ 0x01, 0x02, 0x04, 0x08, 0x03, 0x06, 0x0c, 0x0b,
+ 0x05, 0x0a, 0x07, 0x0e, 0x0f, 0x0d, 0x09, 0x01,
+}
+
+var gf16Log = []uint8{
+ 0x00, 0x0f, 0x01, 0x04, 0x02, 0x08, 0x05, 0x0a,
+ 0x03, 0x0e, 0x09, 0x07, 0x06, 0x0d, 0x0b, 0x0c,
+}
+
+var gf16 = galoisField{
+ p: 15,
+ log: gf16Log,
+ exp: gf16Exp,
+}
+
+var gf256Exp = [256]uint8{
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+ 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
+ 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9,
+ 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
+ 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35,
+ 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
+ 0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0,
+ 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
+ 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc,
+ 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
+ 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f,
+ 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
+ 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88,
+ 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
+ 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93,
+ 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
+ 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9,
+ 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
+ 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa,
+ 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
+ 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e,
+ 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
+ 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4,
+ 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
+ 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e,
+ 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
+ 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef,
+ 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
+ 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5,
+ 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
+ 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83,
+ 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01,
+}
+
+var gf256Log = [256]uint8{
+ 0x00, 0xff, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6,
+ 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
+ 0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81,
+ 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
+ 0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21,
+ 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
+ 0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9,
+ 0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
+ 0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd,
+ 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
+ 0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd,
+ 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
+ 0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e,
+ 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
+ 0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b,
+ 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
+ 0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d,
+ 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
+ 0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c,
+ 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
+ 0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd,
+ 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
+ 0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e,
+ 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
+ 0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76,
+ 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
+ 0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa,
+ 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
+ 0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51,
+ 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
+ 0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8,
+ 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf,
+}
+
+var gf256 = galoisField{
+ 255,
+ gf256Log[:],
+ gf256Exp[:],
+}
+
+/************************************************************************
+ * Polynomial operations
+ */
+
+func polyAdd(dst, src []uint8, c uint8, shift int, gf *galoisField) {
+
+ logC := gf.log[c]
+ if c == 0 {
+ return
+ }
+
+ for i := 0; i < maxPoly; i++ {
+ p := i + shift
+ v := src[i]
+ if p < 0 || p >= maxPoly {
+ continue
+ }
+ if v == 0 {
+ continue
+ }
+ pos := (int(gf.log[v]) + int(logC)) % gf.p
+
+ dst[p] ^= gf.exp[pos]
+ }
+}
+
+func polyEval(s []uint8, x uint8, gf *galoisField) uint8 {
+ sum := uint8(0)
+ logX := gf.log[x]
+
+ if x == 0 {
+ return s[0]
+ }
+
+ for i := 0; i < maxPoly; i++ {
+ c := s[i]
+ if c == 0 {
+ continue
+ }
+ sum ^= gf.exp[(int(gf.log[c])+int(logX)*i)%gf.p]
+ }
+ return sum
+}
+
+/************************************************************************
+ * Berlekamp-Massey algorithm for finding error locator polynomials.
+ */
+
+func berlekampMassey(s []uint8, num int, gf *galoisField, sigma []uint8) {
+
+ C := make([]uint8, maxPoly)
+ B := make([]uint8, maxPoly)
+
+ L := 0
+ m := 1
+ b := uint8(1)
+
+ B[0] = 1
+ C[0] = 1
+
+ for n := 0; n < num; n++ {
+ d := s[n]
+
+ for i := 1; i <= L; i++ {
+ if !(C[i] > 0 && s[n-i] > 0) {
+ continue
+ }
+ d ^= gf.exp[(int(gf.log[C[i]])+int(gf.log[s[n-i]]))%gf.p]
+ }
+
+ mult := gf.exp[(gf.p-int(gf.log[b])+int(gf.log[d]))%gf.p]
+
+ if d == 0 {
+ m++
+ } else if L*2 <= n {
+ T := make([]uint8, 0)
+ T = append(T, C...)
+
+ polyAdd(C, B, mult, m, gf)
+
+ B = B[:0]
+ B = append(B, T...)
+
+ L = n + 1 - L
+ b = d
+ m = 1
+
+ } else {
+ polyAdd(C, B, mult, m, gf)
+ m++
+ }
+ }
+ sigma = sigma[:0]
+ sigma = append(sigma, C...)
+}
+
+/************************************************************************
+ * Code stream error correction
+ *
+ * Generator polynomial for GF(2^8) is x^8 + x^4 + x^3 + x^2 + 1
+ */
+
+func blockSyndromes(data []uint8, bs, npar int, s []uint8) int {
+ nonzero := 0
+ for i := 0; i < maxPoly; i++ {
+ s[i] = 0
+ }
+ for i := 0; i < npar; i++ {
+ for j := 0; j < bs; j++ {
+ c := data[bs-j-1]
+ if c == 0 {
+ continue
+ }
+ s[i] ^= gf256Exp[((int)(gf256Log[c])+i*j)%255]
+ }
+ if s[i] != 0 {
+ nonzero = 1
+ }
+ }
+ return nonzero
+}
+
+func elocPoly(omega []uint8, s []uint8, sigma []uint8, npar int) {
+
+ for i := 0; i < maxPoly; i++ {
+ omega[i] = 0
+ }
+
+ for i := 0; i < npar; i++ {
+ a := sigma[i]
+ logA := gf256Log[a]
+
+ if a == 0 {
+ continue
+ }
+
+ for j := 0; j+1 < maxPoly; j++ {
+ b := s[j+1]
+ if i+j >= npar {
+ break
+ }
+
+ if b == 0 {
+ continue
+ }
+ omega[i+j] ^= gf256Exp[(int(logA)+int(gf256Log[b]))%255]
+ }
+ }
+}
+
+func correctBlock(data []uint8, ecc *qrRsParams) error {
+
+ npar := ecc.bs - ecc.dw
+ s := make([]uint8, maxPoly)
+
+ // Compute syndrome vector
+ if 0 == blockSyndromes(data, ecc.bs, npar, s) {
+ return nil
+ }
+
+ sigma := make([]uint8, maxPoly)
+ berlekampMassey(s, npar, &gf256, sigma)
+
+ // Compute derivative of sigma
+ sigmaDeriv := make([]uint8, maxPoly)
+ for i := 0; i+1 < maxPoly; i += 2 {
+ sigmaDeriv[i] = sigma[i+1]
+ }
+
+ // Compute error evaluator polynomial
+ omega := make([]uint8, maxPoly)
+ elocPoly(omega, s, sigma, npar-1)
+
+ // Find error locations and magnitudes
+ for i := 0; i < ecc.bs; i++ {
+ xinv := gf256Exp[255-i]
+ if 0 == polyEval(sigma, xinv, &gf256) {
+ sdX := polyEval(sigmaDeriv, xinv, &gf256)
+ omegaX := polyEval(omega, xinv, &gf256)
+ error := gf256Exp[(255-int(gf256Log[sdX])+int(gf256Log[omegaX]))%255]
+ data[ecc.bs-i-1] ^= error
+ }
+ }
+
+ if blockSyndromes(data, ecc.bs, npar, s) != 0 {
+ return ErrDataEcc
+ }
+ return nil
+}
+
+/************************************************************************
+ * Format value error correction
+ *
+ * Generator polynomial for GF(2^4) is x^4 + x + 1
+ */
+
+const formatMaxError = 3
+const formatSyndrome = formatMaxError * 2
+const formatBits = 15
+
+func formatSyndromes(u uint16, s []uint8) int {
+ nonzero := 0
+ for i := 0; i < maxPoly; i++ {
+ s[i] = 0
+ }
+ for i := 0; i < formatSyndrome; i++ {
+ s[i] = 0
+ for j := 0; j < formatBits; j++ {
+ if u&(uint16(1 << uint(j))) != 0 {
+ s[i] ^= gf16Exp[((i+1)*j)%15]
+ }
+ }
+ if s[i] != 0 {
+ nonzero = 1
+ }
+ }
+ return nonzero
+}
+
+func correctFormat(fRet *uint16) error {
+ u := *fRet
+
+ // Evaluate U (received codeword) at each of alpha_1 .. alpha_6
+ // to get S_1 .. S_6 (but we index them from 0).
+
+ s := make([]uint8, maxPoly)
+ if 0 == formatSyndromes(u, s) {
+ return nil
+ }
+
+ sigma := make([]uint8, maxPoly)
+ berlekampMassey(s, formatSyndrome, &gf16, sigma)
+
+ // Now, find the roots of the polynomial
+ for i := 0; i < 15; i++ {
+ if 0 == polyEval(sigma, gf16Exp[15-i], &gf16) {
+ u ^= 1 << uint16(i)
+ }
+ }
+ if 0 != formatSyndromes(u, s) {
+ return ErrFormatEcc
+ }
+
+ *fRet = u
+ return nil
+}
+
+/************************************************************************
+ * Decoder algorithm
+ */
+
+type datastream struct {
+ raw [qrMaxPayload]uint8
+ dataBits int
+ ptr int
+ data [qrMaxPayload]uint8
+}
+
+func maskBit(mask, i, j int) int {
+ k := 0
+ switch mask {
+ case 0:
+ k = (i + j) % 2
+ case 1:
+ k = i % 2
+ case 2:
+ k = j % 3
+ case 3:
+ k = (i + j) % 3
+ case 4:
+ k = ((i / 2) + (j / 3)) % 2
+ case 5:
+ k = (i*j)%2 + (i*j)%3
+ case 6:
+ k = ((i*j)%2 + (i*j)%3) % 2
+ case 7:
+ k = ((i*j)%3 + (i+j)%2) % 2
+ default:
+ return 0
+ }
+ if k != 0 {
+ return 0
+ }
+ return 1
+}
+
+func reservedCell(version, i, j int) int {
+ ver := &qrVersionDb[version]
+ size := version*4 + 17
+ ai := -1
+ aj := -1
+ a := 0
+
+ // Finder + format: top left
+ if i < 9 && j < 9 {
+ return 1
+ }
+
+ // Finder + format: bottom left
+ if i+8 >= size && j < 9 {
+ return 1
+ }
+
+ // Finder + format: top right
+ if i < 9 && j+8 >= size {
+ return 1
+ }
+ // Exclude timing patterns
+ if i == 6 || j == 6 {
+ return 1
+ }
+
+ // Exclude Version info, if it exists. Version info sits adjacent to
+ // the top-right and bottom-left finders in three rows, bounded by
+ // the timing pattern.
+
+ if version >= 7 {
+ if i < 6 && j+11 >= size {
+ return 1
+ }
+
+ if i+11 >= size && j < 6 {
+ return 1
+ }
+ }
+ // Exclude alignment patterns
+ for a = 0; a < qrMaxAliment && ver.apat[a] != 0; a++ {
+ p := ver.apat[a]
+
+ if int(math.Abs(float64(p-i))) < 3 {
+ ai = a
+ }
+ if int(math.Abs(float64(p-j))) < 3 {
+ aj = a
+ }
+ }
+
+ if ai >= 0 && aj >= 0 {
+ a--
+ if ai > 0 && ai < a {
+ return 1
+ }
+ if aj > 0 && aj < a {
+ return 1
+ }
+ if aj == a && ai == a {
+ return 1
+ }
+ }
+ return 0
+}
+
+func codestreamEcc(data *QRData, ds *datastream) error {
+ ver := &qrVersionDb[data.Version]
+ sbEcc := &ver.ecc[data.EccLevel]
+ var lbEcc qrRsParams
+ lbCount := (ver.dataBytes - sbEcc.bs*sbEcc.ns) / (sbEcc.bs + 1)
+
+ bc := lbCount + sbEcc.ns
+ eccOffset := sbEcc.dw*bc + lbCount
+
+ dstOffset := 0
+ lbEcc = *sbEcc
+
+ lbEcc.dw++
+ lbEcc.bs++
+
+ for i := 0; i < bc; i++ {
+ dst := ds.data[dstOffset:]
+ ecc := sbEcc
+ if i < sbEcc.ns {
+ ecc = sbEcc
+ } else {
+ ecc = &lbEcc
+ }
+ numEc := ecc.bs - ecc.dw
+
+ for j := 0; j < ecc.dw; j++ {
+ dst[j] = ds.raw[j*bc+i]
+ }
+
+ for j := 0; j < numEc; j++ {
+ dst[ecc.dw+j] = ds.raw[eccOffset+j*bc+i]
+ }
+
+ err := correctBlock(dst, ecc)
+ if err != nil {
+ return err
+ }
+
+ dstOffset += ecc.dw
+ }
+
+ ds.dataBits = dstOffset * 8
+ return nil
+}
+
+func bitsRemaining(ds *datastream) int {
+ return ds.dataBits - ds.ptr
+}
+
+func takeBits(ds *datastream, len int) int {
+ ret := 0
+ for len > 0 && (ds.ptr < ds.dataBits) {
+ b := ds.data[ds.ptr>>3]
+ bitpos := ds.ptr & 7
+ ret <<= 1
+ if ((b << uint(bitpos)) & 0x80) != 0 {
+ ret |= 1
+ }
+ ds.ptr++
+ len--
+ }
+ return ret
+}
+
+func numericTuple(data *QRData, ds *datastream, bits, digits int) int {
+ if bitsRemaining(ds) < bits {
+ return -1
+ }
+ tuple := takeBits(ds, bits)
+
+ for i := digits - 1; i >= 0; i-- {
+ data.Payload = append(data.Payload, uint8(tuple%10)+uint8('0'))
+
+ tuple /= 10
+ }
+ return 0
+}
+
+func decodeNumeric(data *QRData, ds *datastream) error {
+ bits := 14
+ if data.Version < 10 {
+ bits = 10
+ } else if data.Version < 27 {
+ bits = 12
+ }
+
+ count := takeBits(ds, bits)
+ if len(data.Payload)+count+1 > qrMaxPayload {
+ return ErrDataOverflow
+ }
+ for count >= 3 {
+ if numericTuple(data, ds, 10, 3) < 0 {
+ return ErrDataUnderflow
+ }
+ count -= 3
+ }
+ if count >= 2 {
+ if numericTuple(data, ds, 7, 2) < 0 {
+ return ErrDataUnderflow
+
+ }
+ count -= 2
+ }
+ if count > 0 {
+ if numericTuple(data, ds, 4, 1) < 0 {
+ return ErrDataUnderflow
+ }
+ }
+ return nil
+}
+
+var alphaMap = []byte("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")
+
+func alphaTuple(data *QRData, ds *datastream, bits, digits int) int {
+ if bitsRemaining(ds) < bits {
+ return -1
+ }
+ tuple := takeBits(ds, bits)
+ payloadLen := len(data.Payload)
+ data.Payload = append(data.Payload, make([]uint8, digits)...)
+ for i := 0; i < digits; i++ {
+ data.Payload[payloadLen+digits-i-1] = alphaMap[tuple%45]
+ tuple /= 45
+ }
+ return 0
+}
+
+func decodeAlpha(data *QRData, ds *datastream) error {
+ bits := 13
+ if data.Version < 10 {
+ bits = 9
+ } else if data.Version < 27 {
+ bits = 11
+ }
+ count := takeBits(ds, bits)
+ if len(data.Payload)+count+1 > qrMaxPayload {
+ return ErrDataOverflow
+ }
+ for count >= 2 {
+ if alphaTuple(data, ds, 11, 2) < 0 {
+ return ErrDataUnderflow
+ }
+ count -= 2
+ }
+ if count > 0 {
+ if alphaTuple(data, ds, 6, 1) < 0 {
+ return ErrDataUnderflow
+ }
+ }
+ return nil
+}
+
+func decodeByte(data *QRData, ds *datastream) error {
+ bits := 16
+ if data.Version < 10 {
+ bits = 8
+ }
+ count := takeBits(ds, bits)
+ if len(data.Payload)+count+1 > qrMaxPayload {
+ return ErrDataOverflow
+ }
+ if bitsRemaining(ds) < count*8 {
+ return ErrDataUnderflow
+ }
+ for i := 0; i < count; i++ {
+ data.Payload = append(data.Payload, uint8(takeBits(ds, 8)))
+ }
+ return nil
+}
+
+func decodeKanji(data *QRData, ds *datastream) error {
+ bits := 12
+
+ if data.Version < 10 {
+ bits = 8
+ } else if data.Version < 27 {
+ bits = 10
+ }
+ count := takeBits(ds, bits)
+
+ if len(data.Payload)+count*2+1 > qrMaxPayload {
+ return ErrDataOverflow
+ }
+
+ if bitsRemaining(ds) < count*13 {
+ return ErrDataUnderflow
+ }
+
+ for i := 0; i < count; i++ {
+ d := takeBits(ds, 13)
+ msB := d / 0xc0
+ lsB := d % 0xc0
+ intermediate := uint16((msB << 8) | lsB)
+ var sjw uint16
+
+ if intermediate+0x8140 <= 0x9ffc {
+ // bytes are in the range 0x8140 to 0x9FFC
+ sjw = intermediate + 0x8140
+ } else {
+ // bytes are in the range 0xE040 to 0xEBBF
+ sjw = intermediate + 0xc140
+ }
+ data.Payload = append(data.Payload, uint8(sjw>>8))
+ data.Payload = append(data.Payload, uint8(sjw&0xff))
+ }
+ return nil
+}
+
+func decodeEci(data *QRData, ds *datastream) error {
+ if bitsRemaining(ds) < 8 {
+ return ErrDataOverflow
+ }
+ data.Eci = uint32(takeBits(ds, 8))
+ if (data.Eci & 0xc0) == 0x80 {
+ if bitsRemaining(ds) < 8 {
+ return ErrDataUnderflow
+ }
+ data.Eci = (data.Eci << 8) | uint32(takeBits(ds, 8))
+ } else if (data.Eci & 0xe0) == 0xc0 {
+ if bitsRemaining(ds) < 16 {
+ return ErrDataUnderflow
+ }
+ data.Eci = (data.Eci << 16) | uint32(takeBits(ds, 16))
+ }
+ return nil
+}
+
+func decodePayload(data *QRData, ds *datastream) error {
+
+ for bitsRemaining(ds) >= 4 {
+ var err error
+ _type := takeBits(ds, 4)
+ switch _type {
+ case qrDataTypeNumeric:
+ err = decodeNumeric(data, ds)
+ case qrDataTypeAlpha:
+ err = decodeAlpha(data, ds)
+ case qrDataTypeByte:
+ err = decodeByte(data, ds)
+ case qrDataTypeKanji:
+ err = decodeKanji(data, ds)
+ case 7:
+ err = decodeEci(data, ds)
+ default:
+ goto done
+ }
+
+ if err != nil {
+ return err
+ }
+ if 0 == (_type&(_type-1)) && (_type > data.DataType) {
+ data.DataType = _type
+ }
+ }
+
+done:
+ return nil
+}
diff --git a/vendor/github.com/liyue201/goqr/define.go b/vendor/github.com/liyue201/goqr/define.go
new file mode 100644
index 0000000..c8d71ae
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/define.go
@@ -0,0 +1,83 @@
+package goqr
+
+const (
+ qrPixelWhite = 0
+ qrPixelBlack = 1
+ qrPixelRegion = 2
+ qrMaxRegion = 254
+ qrMaxCastones = 32
+ qrMaxGrids = 8
+ qrPerspectiveParams = 8
+)
+
+type qrPixelType = uint8
+
+type point struct {
+ x int
+ y int
+}
+
+type qrRegion struct {
+ seed point
+ count int
+ capstone int
+}
+
+type qrCapstone struct {
+ ring int
+ stone int
+ corners [4]point
+ center point
+ c [qrPerspectiveParams]float64
+ qrGrid int
+}
+
+type qrGrid struct {
+ // Capstone indices
+ caps [3]int
+
+ // Alignment pattern region and corner
+ alignRegion int
+ align point
+
+ // Timing pattern endpoints
+ tpep [3]point
+ hscan int
+ vscan int
+
+ // Grid size and perspective transform
+ gridSize int
+ c [qrPerspectiveParams]float64
+}
+
+/************************************************************************
+ * QR-code Version information database
+ */
+
+const (
+ qrMaxVersion = 40
+ qrMaxAliment = 7
+)
+
+type qrRsParams struct {
+ bs int // Small block size
+ dw int // Small data words
+ ns int // Number of small blocks
+}
+
+type qrVersionInfo struct {
+ dataBytes int
+ apat [qrMaxAliment]int
+ ecc [4]qrRsParams
+}
+
+type polygonScoreData struct {
+ ref point
+ scores [4]int
+ corners []point
+}
+
+type neighbour struct {
+ index int
+ distance float64
+}
diff --git a/vendor/github.com/liyue201/goqr/errors.go b/vendor/github.com/liyue201/goqr/errors.go
new file mode 100644
index 0000000..cd938b1
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/errors.go
@@ -0,0 +1,15 @@
+package goqr
+
+import "errors"
+
+// Error definition
+var (
+ ErrNoQRCode = errors.New("no QR code in image")
+ ErrInvalidGridSize = errors.New("invalid grid size")
+ ErrInvalidVersion = errors.New("invalid version")
+ ErrFormatEcc = errors.New("ecc format error")
+ ErrDataEcc = errors.New("ecc data error")
+ ErrUnknownDataType = errors.New("unknown data type")
+ ErrDataOverflow = errors.New("data overflow")
+ ErrDataUnderflow = errors.New("data underflow")
+)
diff --git a/vendor/github.com/liyue201/goqr/qr_const.go b/vendor/github.com/liyue201/goqr/qr_const.go
new file mode 100644
index 0000000..799f740
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/qr_const.go
@@ -0,0 +1,36 @@
+package goqr
+
+// Limits on the maximum size of QR-codes and their content
+const (
+ qrMaxBimap = 3917
+ qrMaxPayload = 8896
+
+ // QR-code ECC types
+ qrEccLevelM = 0
+ qrEccLevelL = 1
+ qrEccLevelH = 2
+ qrEccLevelQ = 3
+
+ // QR-code data types
+ qrDataTypeNumeric = 1
+ qrDataTypeAlpha = 2
+ qrDataTypeByte = 4
+ qrDataTypeKanji = 8
+
+ // Common character encodings
+ qrEciIos8859_1 = 1
+ qrEciIbm437 = 2
+ qrEciIos8859_2 = 4
+ qrEciIso8859_3 = 5
+ qrEciIso8859_4 = 6
+ qrEciIso8859_5 = 7
+ qrEciIso8859_6 = 8
+ qrEciIso8859_7 = 9
+ qrEciIso8859_8 = 10
+ qrEciIso8859_9 = 11
+ qrEciWindows874 = 13
+ qrEciIso8859_13 = 15
+ qrEciIso8859_15 = 17
+ qrEciShiftJis = 20
+ qrEciUtf8 = 26
+)
diff --git a/vendor/github.com/liyue201/goqr/qrcode.go b/vendor/github.com/liyue201/goqr/qrcode.go
new file mode 100644
index 0000000..83e38c5
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/qrcode.go
@@ -0,0 +1,146 @@
+package goqr
+
+// qrCode is used to return information about detected QR codes
+// in the input image.
+type qrCode struct {
+ // The four corners of the QR-code, from top left, clockwise
+ corners [4]point
+
+ // The number of cells across in the QR-code. The cell bitmap
+ // is a bitmask giving the actual values of cells. If the cell
+ // at (x, y) is black, then the following bit is set:
+ //
+ // cell_bitmap[i >> 3] & (1 << (i & 7))
+ //
+ // where i = (y * size) + x.
+ //
+
+ size int
+ cellBitmap [qrMaxBimap]uint8
+}
+
+// QRData holds the decoded QR-code data
+type QRData struct {
+ // Various parameters of the QR-code. These can mostly be
+ // ignored if you only care about the data.
+ Version int
+ EccLevel int
+ Mask int
+
+ // This field is the highest-valued data type found in the QR code.
+ DataType int
+
+ // Data Payload. For the Kanji datatype, Payload is encoded as
+ // Shift-JIS. For all other datatypes, Payload is ASCII text.
+ Payload []uint8
+
+ // ECI assignment number
+ Eci uint32
+}
+
+func gridBit(code *qrCode, x, y int) int {
+ p := uint(y*code.size + x)
+ return int((code.cellBitmap[p>>3])>>(p&7)) & 1
+}
+
+func readFormat(code *qrCode, data *QRData, which int) error {
+ format := uint16(0)
+ if which != 0 {
+ for i := 0; i < 7; i++ {
+ format = (format << 1) | uint16(gridBit(code, 8, code.size-1-i))
+ }
+ for i := 0; i < 8; i++ {
+ format = (format << 1) | uint16(gridBit(code, code.size-8+i, 8))
+ }
+ } else {
+ xs := [15]int{8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 4, 3, 2, 1, 0}
+ ys := [15]int{0, 1, 2, 3, 4, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8}
+ for i := 14; i >= 0; i-- {
+ format = (format << 1) | uint16(gridBit(code, xs[i], ys[i]))
+ }
+ }
+
+ format ^= 0x5412
+ err := correctFormat(&format)
+ if err != nil {
+ return err
+ }
+
+ fdata := format >> 10
+ data.EccLevel = int(fdata) >> 3
+ data.Mask = int(fdata) & 7
+ return nil
+}
+
+func readBit(code *qrCode, data *QRData, ds *datastream, i, j int) {
+ bitpos := ds.dataBits & 7
+ bytepos := ds.dataBits >> 3
+ v := gridBit(code, j, i)
+
+ if maskBit(data.Mask, i, j) != 0 {
+ v ^= 1
+ }
+
+ if v != 0 {
+ ds.raw[bytepos] |= 0x80 >> uint32(bitpos)
+ }
+ ds.dataBits++
+}
+
+func readData(code *qrCode, data *QRData, ds *datastream) {
+ y := code.size - 1
+ x := code.size - 1
+ dir := -1
+ for x > 0 {
+ if x == 6 {
+ x--
+ }
+
+ if 0 == reservedCell(data.Version, y, x) {
+ readBit(code, data, ds, y, x)
+ }
+
+ if 0 == reservedCell(data.Version, y, x-1) {
+ readBit(code, data, ds, y, x-1)
+ }
+ y += dir
+ if y < 0 || y >= code.size {
+ dir = -dir
+ x -= 2
+ y += dir
+ }
+ }
+}
+
+func decode(code *qrCode, data *QRData) error {
+ var ds datastream
+ if (code.size-17)%4 != 0 {
+ return ErrInvalidGridSize
+ }
+ data.Version = (code.size - 17) / 4
+
+ if data.Version < 1 || data.Version > qrMaxVersion {
+ return ErrInvalidVersion
+ }
+
+ // Read format information -- try both locations
+ err := readFormat(code, data, 0)
+ if err != nil {
+ err = readFormat(code, data, 1)
+ }
+ if err != nil {
+ return err
+ }
+
+ readData(code, data, &ds)
+
+ err = codestreamEcc(data, &ds)
+ if err != nil {
+ return err
+ }
+ err = decodePayload(data, &ds)
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/vendor/github.com/liyue201/goqr/recognition.go b/vendor/github.com/liyue201/goqr/recognition.go
new file mode 100644
index 0000000..6001471
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/recognition.go
@@ -0,0 +1,74 @@
+package goqr
+
+import (
+ "image"
+ "image/color"
+ "math"
+)
+
+// Recognize recognizes the passed image and returns a slice of QRData.
+func Recognize(img image.Image) ([]*QRData, error) {
+ b := img.Bounds()
+
+ r := NewRecognizer(b.Max.X, b.Max.Y)
+ r.Begin()
+ switch m := img.(type) {
+ case *image.Gray:
+ off := 0
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ gray := m.GrayAt(x, y)
+ r.SetPixel(x-b.Min.X, y-b.Min.Y, byte(gray.Y))
+ off++
+ }
+ }
+ case *image.RGBA:
+ off := 0
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ pix := toGrayLuminance(m.At(x, y))
+ r.SetPixel(x-b.Min.X, y-b.Min.Y, byte(pix))
+ off++
+ }
+ }
+ default:
+ off := 0
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ rgba := color.RGBAModel.Convert(m.At(x, y)).(color.RGBA)
+ pix := toGrayLuminance(rgba)
+ r.SetPixel(x-b.Min.X, y-b.Min.Y, byte(pix))
+ off++
+ }
+ }
+ }
+ r.End()
+
+ count := r.Count()
+ if count == 0 {
+ return nil, ErrNoQRCode
+ }
+
+ qrCodes := make([]*QRData, 0)
+ for i := 0; i < count; i++ {
+ code, err := r.Decode(i)
+ if err != nil {
+ continue
+ }
+ qrCodes = append(qrCodes, code)
+ }
+ if len(qrCodes) == 0 {
+ return nil, ErrNoQRCode
+ }
+ return qrCodes, nil
+}
+
+func toGrayLuminance(c color.Color) uint8 {
+ rr, gg, bb, _ := c.RGBA()
+ r := math.Pow(float64(rr), 2.2)
+ g := math.Pow(float64(gg), 2.2)
+ b := math.Pow(float64(bb), 2.2)
+ y := math.Pow(0.2125*r+0.7154*g+0.0721*b, 1/2.2)
+ Y := uint16(y + 0.5)
+ return uint8(Y >> 8)
+}
diff --git a/vendor/github.com/liyue201/goqr/recognizer.go b/vendor/github.com/liyue201/goqr/recognizer.go
new file mode 100644
index 0000000..50069e0
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/recognizer.go
@@ -0,0 +1,976 @@
+package goqr
+
+import (
+ "errors"
+ "math"
+)
+
+const (
+ thresholdSMin = 1
+ thresholdSDen = 8
+ thresholdT = 5
+)
+
+// Error definition
+var (
+ ErrOutOfRange = errors.New("out of range")
+)
+
+// Recognizer is a Qr Code recognizer interface
+type Recognizer interface {
+ SetPixel(x, y int, val uint8)
+ Begin()
+ End()
+ Count() int
+ Decode(index int) (*QRData, error)
+}
+
+type recognizer struct {
+ pixels []uint8
+ w int
+ h int
+ regions []qrRegion
+ capstones []qrCapstone
+ grids []qrGrid
+}
+
+// NewRecognizer news a recognizer
+func NewRecognizer(w, h int) Recognizer {
+ if w <= 0 || h <= 0 {
+ return nil
+ }
+ return &recognizer{
+ h: h,
+ w: w,
+ pixels: make([]qrPixelType, w*h),
+ }
+}
+
+func (q *recognizer) SetPixel(x, y int, val uint8) {
+ q.pixels[x+y*q.w] = val
+}
+
+func (q *recognizer) Count() int {
+ return len(q.grids)
+}
+
+func (q *recognizer) Begin() {
+ q.regions = make([]qrRegion, qrPixelRegion)
+}
+
+func (q *recognizer) End() {
+ q.threshold()
+ for i := 0; i < q.h; i++ {
+ q.finderScan(i)
+ }
+ for i := 0; i < len(q.capstones); i++ {
+ q.testGrouping(i)
+ }
+}
+
+func (q *recognizer) extract(index int) (*qrCode, error) {
+ code := &qrCode{}
+
+ qr := &q.grids[index]
+
+ if index < 0 || index >= len(q.grids) {
+ return nil, ErrOutOfRange
+ }
+
+ perspectiveMap(qr.c[:], 0.0, 0.0, &code.corners[0])
+ perspectiveMap(qr.c[:], float64(qr.gridSize), 0.0, &code.corners[1])
+ perspectiveMap(qr.c[:], float64(qr.gridSize), float64(qr.gridSize), &code.corners[2])
+ perspectiveMap(qr.c[:], 0.0, float64(qr.gridSize), &code.corners[3])
+
+ code.size = qr.gridSize
+ i := uint(0)
+ for y := 0; y < qr.gridSize; y++ {
+ for x := 0; x < qr.gridSize; x++ {
+ if q.readCell(index, x, y) > 0 {
+ code.cellBitmap[i>>3] |= uint8(1 << (i & 7))
+ }
+ i++
+ }
+ }
+ return code, nil
+}
+
+func (q *recognizer) Decode(index int) (*QRData, error) {
+ code, err := q.extract(index)
+ if err != nil {
+ return nil, err
+ }
+ var data QRData
+ data.Payload = make([]uint8, 0)
+
+ err = decode(code, &data)
+ return &data, err
+}
+
+func (q *recognizer) threshold() {
+ var x, y int
+ avgW := 0
+ avgU := 0
+ thresholds := q.w / thresholdSDen
+
+ // Ensure a sane, non-zero value for threshold_s.
+ // threshold_s can be zero if the image width is small. We need to avoid
+ // SIGFPE as it will be used as divisor.
+ if thresholds < thresholdSMin {
+ thresholds = thresholdSMin
+ }
+
+ for y = 0; y < q.h; y++ {
+ row := q.pixels[q.w*y : q.w*(y+1)]
+ rowAverage := make([]int, q.w)
+ for x = 0; x < q.w; x++ {
+ var w, u int
+ if y&1 == 1 {
+ w = x
+ u = q.w - 1 - x
+ } else {
+ w = q.w - 1 - x
+ u = x
+ }
+
+ avgW = (avgW*(thresholds-1))/thresholds + int(row[w])
+ avgU = (avgU*(thresholds-1))/thresholds + int(row[u])
+
+ rowAverage[w] += avgW
+ rowAverage[u] += avgU
+ }
+
+ for x = 0; x < q.w; x++ {
+ if int(row[x]) < rowAverage[x]*(100-thresholdT)/(200*thresholds) {
+ row[x] = qrPixelBlack
+ } else {
+ row[x] = qrPixelWhite
+ }
+ }
+ }
+}
+
+const floodFileMaxDepth = 4096
+
+type spanFunc func(userData interface{}, y, left, right int)
+
+func (q *recognizer) floodFillSeed(x, y, from, to int, span spanFunc, userData interface{}, depth int) {
+ left := x
+ right := x
+ row := q.pixels[y*q.w : (y+1)*q.w]
+
+ if depth >= floodFileMaxDepth {
+ return
+ }
+
+ for left > 0 && int(row[left-1]) == from {
+ left--
+ }
+
+ for right < q.w-1 && int(row[right+1]) == from {
+ right++
+ }
+
+ // Fill the extent
+ for i := left; i <= right; i++ {
+ row[i] = qrPixelType(to)
+ }
+
+ if span != nil {
+ span(userData, y, left, right)
+ }
+
+ // Seed new flood-fills
+ if y > 0 {
+ row = q.pixels[(y-1)*q.w : (y)*q.w]
+ for i := left; i <= right; i++ {
+ if int(row[i]) == from {
+ q.floodFillSeed(i, y-1, from, to, span, userData, depth+1)
+ }
+ }
+ }
+
+ if y < q.h-1 {
+ row = q.pixels[(y+1)*q.w : (y+2)*q.w]
+ for i := left; i <= right; i++ {
+ if int(row[i]) == from {
+ q.floodFillSeed(i, y+1, from, to, span, userData, depth+1)
+ }
+ }
+ }
+}
+
+func areaCount(userData interface{}, y, left, right int) {
+ region := userData.(*qrRegion)
+ region.count += right - left + 1
+}
+
+func (q *recognizer) regionCode(x, y int) int {
+ if x < 0 || y < 0 || x >= q.w || y >= q.h {
+ return -1
+ }
+ pixel := int(q.pixels[y*q.w+x])
+ if pixel >= qrPixelRegion {
+ return pixel
+ }
+ if pixel == qrPixelWhite {
+ return -1
+ }
+ if len(q.regions) >= qrMaxRegion {
+ return -1
+ }
+
+ region := len(q.regions)
+ q.regions = append(q.regions, qrRegion{})
+ box := &q.regions[region]
+
+ box.seed.x = x
+ box.seed.y = y
+ box.count = 0
+ box.capstone = -1
+
+ q.floodFillSeed(x, y, pixel, region, areaCount, box, 0)
+ return region
+}
+
+func findOneCorner(userData interface{}, y, left, right int) {
+ psd := userData.(*polygonScoreData)
+ xs := [2]int{left, right}
+ dy := y - psd.ref.y
+
+ for i := 0; i < 2; i++ {
+ dx := xs[i] - psd.ref.x
+ d := dx*dx + dy*dy
+ if d > psd.scores[0] {
+ psd.scores[0] = d
+ psd.corners[0].x = xs[i]
+ psd.corners[0].y = y
+ }
+ }
+}
+
+func findOtherCorners(userData interface{}, y, left, right int) {
+ psd := userData.(*polygonScoreData)
+ xs := [2]int{left, right}
+
+ for i := 0; i < 2; i++ {
+ up := xs[i]*psd.ref.x + y*psd.ref.y
+ right := xs[i]*-psd.ref.y + y*psd.ref.x
+ scores := [4]int{up, right, -up, -right}
+ for j := 0; j < 4; j++ {
+ if scores[j] > psd.scores[j] {
+ psd.scores[j] = scores[j]
+ psd.corners[j].x = xs[i]
+ psd.corners[j].y = y
+ }
+ }
+ }
+}
+
+func (q *recognizer) findRegionCorners(rcode int, ref *point, corners []point) {
+ region := &q.regions[rcode]
+ psd := polygonScoreData{}
+ psd.corners = corners[:]
+
+ psd.ref = *ref
+ psd.scores[0] = -1
+
+ q.floodFillSeed(region.seed.x, region.seed.y, rcode, qrPixelBlack, findOneCorner, &psd, 0)
+
+ psd.ref.x = psd.corners[0].x - psd.ref.x
+ psd.ref.y = psd.corners[0].y - psd.ref.y
+
+ for i := 0; i < 4; i++ {
+ psd.corners[i] = region.seed
+ }
+
+ i := region.seed.x*psd.ref.x + region.seed.y*psd.ref.y
+ psd.scores[0] = i
+ psd.scores[2] = -i
+
+ i = region.seed.x*-psd.ref.y + region.seed.y*psd.ref.x
+ psd.scores[1] = i
+ psd.scores[3] = -i
+
+ q.floodFillSeed(region.seed.x, region.seed.y, qrPixelBlack, rcode, findOtherCorners, &psd, 0)
+}
+
+func (q *recognizer) recordCapstone(ring, stone int) {
+
+ stoneReg := &q.regions[stone]
+ ringReg := &q.regions[ring]
+
+ if len(q.capstones) >= qrMaxCastones {
+ return
+ }
+
+ csIndex := len(q.capstones)
+ q.capstones = append(q.capstones, qrCapstone{})
+ capstone := &q.capstones[csIndex]
+
+ capstone.qrGrid = -1
+ capstone.ring = ring
+ capstone.stone = stone
+ stoneReg.capstone = csIndex
+ ringReg.capstone = csIndex
+
+ // Find the corners of the ring
+ q.findRegionCorners(ring, &stoneReg.seed, capstone.corners[:])
+
+ // Set up the perspective transform and find the center
+ perspectiveSetup(capstone.c[:], capstone.corners[:], 7.0, 7.0)
+ perspectiveMap(capstone.c[:], 3.5, 3.5, &capstone.center)
+
+}
+
+func perspectiveSetup(c []float64, rect []point, w, h float64) {
+ x0 := float64(rect[0].x)
+ y0 := float64(rect[0].y)
+ x1 := float64(rect[1].x)
+ y1 := float64(rect[1].y)
+ x2 := float64(rect[2].x)
+ y2 := float64(rect[2].y)
+ x3 := float64(rect[3].x)
+ y3 := float64(rect[3].y)
+ wden := w * (x2*y3 - x3*y2 + (x3-x2)*y1 + x1*(y2-y3))
+ hden := h * (x2*y3 + x1*(y2-y3) - x3*y2 + (x3-x2)*y1)
+ c[0] = (x1*(x2*y3-x3*y2) + x0*(-x2*y3+x3*y2+(x2-x3)*y1) + x1*(x3-x2)*y0) / wden
+ c[1] = -(x0*(x2*y3+x1*(y2-y3)-x2*y1) - x1*x3*y2 + x2*x3*y1 + (x1*x3-x2*x3)*y0) / hden
+ c[2] = x0
+ c[3] = (y0*(x1*(y3-y2)-x2*y3+x3*y2) + y1*(x2*y3-x3*y2) + x0*y1*(y2-y3)) / wden
+ c[4] = (x0*(y1*y3-y2*y3) + x1*y2*y3 - x2*y1*y3 + y0*(x3*y2-x1*y2+(x2-x3)*y1)) / hden
+ c[5] = y0
+ c[6] = (x1*(y3-y2) + x0*(y2-y3) + (x2-x3)*y1 + (x3-x2)*y0) / wden
+ c[7] = (-x2*y3 + x1*y3 + x3*y2 + x0*(y1-y2) - x3*y1 + (x2-x1)*y0) / hden
+}
+
+func perspectiveMap(c []float64, u, v float64, ret *point) {
+ den := c[6]*u + c[7]*v + 1.0
+ x := (c[0]*u + c[1]*v + c[2]) / den
+ y := (c[3]*u + c[4]*v + c[5]) / den
+ ret.x = int(x + 0.5)
+ ret.y = int(y + 0.5)
+}
+
+func perspectiveUnmap(c []float64, in *point, u, v *float64) {
+ x := float64(in.x)
+ y := float64(in.y)
+ den := -c[0]*c[7]*y + c[1]*c[6]*y + (c[3]*c[7]-c[4]*c[6])*x + c[0]*c[4] - c[1]*c[3]
+ *u = -(c[1]*(y-c[5]) - c[2]*c[7]*y + (c[5]*c[7]-c[4])*x + c[2]*c[4]) / den
+ *v = (c[0]*(y-c[5]) - c[2]*c[6]*y + (c[5]*c[6]-c[3])*x + c[2]*c[3]) / den
+}
+
+func (q *recognizer) testCapstone(x, y int, pb []int) {
+
+ ringRight := q.regionCode(x-pb[4], y)
+ stone := q.regionCode(x-pb[4]-pb[3]-pb[2], y)
+ ringLeft := q.regionCode(x-pb[4]-pb[3]-pb[2]-pb[1]-pb[0], y)
+
+ if ringLeft < 0 || ringRight < 0 || stone < 0 {
+ return
+ }
+ // Left and ring of ring should be connected
+ if ringLeft != ringRight {
+ return
+ }
+ // Ring should be disconnected from stone
+ if ringLeft == stone {
+ return
+ }
+ stoneReg := &q.regions[stone]
+ ringReg := &q.regions[ringLeft]
+ /* Already detected */
+ if stoneReg.capstone >= 0 || ringReg.capstone >= 0 {
+ return
+ }
+ // Ratio should ideally be 37.5
+ ratio := stoneReg.count * 100 / ringReg.count
+ if ratio < 10 || ratio > 70 {
+ return
+ }
+ q.recordCapstone(ringLeft, stone)
+}
+
+func (q *recognizer) finderScan(y int) {
+
+ row := q.pixels[y*q.w : (y+1)*q.w]
+ x := 0
+ lastColor := 0
+ runLength := 0
+ runCount := 0
+ pb := make([]int, 5)
+ check := [5]int{1, 1, 3, 1, 1}
+
+ for x = 0; x < q.w; x++ {
+ color := 0
+ if row[x] > 0 {
+ color = 1
+ }
+
+ if x > 0 && color != lastColor {
+ for i := 0; i < 4; i++ {
+ pb[i] = pb[i+1]
+ }
+ pb[4] = runLength
+ runLength = 0
+ runCount++
+
+ if color == 0 && runCount >= 5 {
+ var avg, err int
+ ok := true
+ avg = (pb[0] + pb[1] + pb[3] + pb[4]) / 4
+ err = avg * 3 / 4
+ for i := 0; i < 5; i++ {
+ if pb[i] < check[i]*avg-err || pb[i] > check[i]*avg+err {
+ ok = false
+ break
+ }
+ }
+ if ok {
+ q.testCapstone(x, y, pb)
+ }
+ }
+ }
+ runLength++
+ lastColor = color
+ }
+}
+
+func (q *recognizer) findAlignmentPattern(index int) {
+ qr := &q.grids[index]
+ c0 := &q.capstones[qr.caps[0]]
+ c2 := &q.capstones[qr.caps[2]]
+
+ var a, b, c point
+ stepSize := 1
+ dir := 0
+ var u, v float64
+
+ // Grab our previous estimate of the alignment pattern corner
+ b = qr.align
+
+ // Guess another two corners of the alignment pattern so that we
+ // can estimate its size.
+
+ perspectiveUnmap(c0.c[:], &b, &u, &v)
+ perspectiveMap(c0.c[:], u, v+1.0, &a)
+ perspectiveUnmap(c2.c[:], &b, &u, &v)
+ perspectiveMap(c2.c[:], u+1.0, v, &c)
+ sizeEstimate := int(math.Abs(float64((a.x-b.x)*-(c.y-b.y) + (a.y-b.y)*(c.x-b.x))))
+
+ // Spiral outwards from the estimate point until we find something
+ // roughly the right size. Don't look too far from the estimate point
+
+ for stepSize*stepSize < sizeEstimate*100 {
+ dxMap := []int{1, 0, -1, 0}
+ dyMap := []int{0, -1, 0, 1}
+
+ for i := 0; i < stepSize; i++ {
+ code := q.regionCode(b.x, b.y)
+
+ if code >= 0 {
+ reg := &q.regions[code]
+
+ if reg.count >= sizeEstimate/2 && reg.count <= sizeEstimate*2 {
+ qr.alignRegion = code
+ return
+ }
+ }
+
+ b.x += dxMap[dir]
+ b.y += dyMap[dir]
+ }
+
+ dir = (dir + 1) % 4
+ if (dir & 1) == 1 {
+ stepSize++
+ }
+ }
+}
+
+// readCell read a cell from a grid using the currently set perspective
+// transform. Returns +/- 1 for black/white, 0 for cells which are
+// out of image bounds.
+func (q *recognizer) readCell(index, x, y int) int {
+ qr := &q.grids[index]
+ var p point
+
+ perspectiveMap(qr.c[:], float64(x)+0.5, float64(y)+0.5, &p)
+ if p.y < 0 || p.y >= q.h || p.x < 0 || p.x >= q.w {
+ return 0
+ }
+ if q.pixels[p.y*q.w+p.x] != 0 {
+ return 1
+ }
+ return -1
+}
+
+func (q *recognizer) fitnessCell(index, x, y int) int {
+ qr := &q.grids[index]
+ score := 0
+ offsets := []float64{0.3, 0.5, 0.7}
+ for v := 0; v < 3; v++ {
+ for u := 0; u < 3; u++ {
+ var p point
+ perspectiveMap(qr.c[:], float64(x)+offsets[u], float64(y)+offsets[v], &p)
+
+ if p.y < 0 || p.y >= q.h || p.x < 0 || p.x >= q.w {
+ continue
+ }
+ if q.pixels[p.y*q.w+p.x] != 0 {
+ score++
+ } else {
+ score--
+ }
+ }
+ }
+ return score
+}
+
+func (q *recognizer) fitnessRing(index, cx, cy, radius int) int {
+ score := 0
+ for i := 0; i < radius*2; i++ {
+ score += q.fitnessCell(index, cx-radius+i, cy-radius)
+ score += q.fitnessCell(index, cx-radius, cy+radius-i)
+ score += q.fitnessCell(index, cx+radius, cy-radius+i)
+ score += q.fitnessCell(index, cx+radius-i, cy+radius)
+ }
+ return score
+}
+
+func (q *recognizer) fitnessApat(index, cx, cy int) int {
+ return q.fitnessCell(index, cx, cy) -
+ q.fitnessRing(index, cx, cy, 1) +
+ q.fitnessRing(index, cx, cy, 2)
+}
+
+func (q *recognizer) fitnessCapstone(index, x, y int) int {
+ x += 3
+ y += 3
+ return q.fitnessCell(index, x, y) +
+ q.fitnessRing(index, x, y, 1) -
+ q.fitnessRing(index, x, y, 2) +
+ q.fitnessRing(index, x, y, 3)
+}
+
+// fitnessAll compute a fitness score for the currently configured perspective
+// transform, using the features we expect to find by scanning the
+// grid.
+func (q *recognizer) fitnessAll(index int) int {
+ qr := &q.grids[index]
+ version := (qr.gridSize - 17) / 4
+ info := &qrVersionDb[version]
+ score := 0
+
+ // Check the timing pattern
+ for i := 0; i < qr.gridSize-14; i++ {
+ expect := 1
+ if i&1 == 0 {
+ expect = -1
+ }
+ score += q.fitnessCell(index, i+7, 6) * expect
+ score += q.fitnessCell(index, 6, i+7) * expect
+ }
+
+ // Check capstones
+ score += q.fitnessCapstone(index, 0, 0)
+ score += q.fitnessCapstone(index, qr.gridSize-7, 0)
+ score += q.fitnessCapstone(index, 0, qr.gridSize-7)
+
+ if version < 0 || version > qrMaxVersion {
+ return score
+ }
+
+ // Check alignment patterns
+ apCount := 0
+ for (apCount < qrMaxAliment) && info.apat[apCount] != 0 {
+ apCount++
+ }
+
+ for i := 1; i+1 < apCount; i++ {
+ score += q.fitnessApat(index, 6, info.apat[i])
+ score += q.fitnessApat(index, info.apat[i], 6)
+ }
+
+ for i := 1; i < apCount; i++ {
+ for j := 1; j < apCount; j++ {
+ score += q.fitnessApat(index, info.apat[i], info.apat[j])
+ }
+ }
+
+ return score
+}
+
+func (q *recognizer) jigglePerspective(index int) {
+ qr := &q.grids[index]
+ best := q.fitnessAll(index)
+
+ adjustments := make([]float64, 8)
+ for i := 0; i < 8; i++ {
+ adjustments[i] = qr.c[i] * 0.02
+ }
+
+ for pass := 0; pass < 5; pass++ {
+ for i := 0; i < 16; i++ {
+ j := i >> 1
+ old := qr.c[j]
+ step := adjustments[j]
+ var new float64
+
+ if i&1 == 1 {
+ new = old + step
+ } else {
+ new = old - step
+ }
+ qr.c[j] = new
+ test := q.fitnessAll(index)
+
+ if test > best {
+ best = test
+ } else {
+ qr.c[j] = old
+ }
+ }
+ for i := 0; i < 8; i++ {
+ adjustments[i] *= 0.5
+ }
+ }
+}
+
+// Once the capstones are in place and an alignment point has been chosen,
+// we call this function to set up a grid-reading perspective transform.
+func (q *recognizer) setupQrPerspective(index int) {
+ qr := &q.grids[index]
+ var rect [4]point
+
+ /* Set up the perspective map for reading the grid */
+ rect[0] = q.capstones[qr.caps[1]].corners[0]
+ rect[1] = q.capstones[qr.caps[2]].corners[0]
+ rect[2] = qr.align
+ rect[3] = q.capstones[qr.caps[0]].corners[0]
+
+ perspectiveSetup(qr.c[:], rect[:], float64(qr.gridSize-7), float64(qr.gridSize-7))
+ q.jigglePerspective(index)
+}
+
+func rotateCapstone(cap *qrCapstone, h0, hd *point) {
+ copy := [4]point{}
+
+ var best int
+ var bestScore int
+
+ for j := 0; j < 4; j++ {
+ p := &cap.corners[j]
+ score := (p.x-h0.x)*-hd.y + (p.y-h0.y)*hd.x
+ if j == 0 || score < bestScore {
+ best = j
+ bestScore = score
+ }
+ }
+ ///* Rotate the capstone */
+ for j := 0; j < 4; j++ {
+ copy[j] = cap.corners[(j+best)%4]
+ }
+ for j := 0; j < 4; j++ {
+ cap.corners[j] = copy[j]
+ }
+ perspectiveSetup(cap.c[:], cap.corners[:], 7.0, 7.0)
+}
+
+func (q *recognizer) timingScan(p0, p1 *point) int {
+ n := p1.x - p0.x
+ d := p1.y - p0.y
+ x := p0.x
+ y := p0.y
+ a := 0
+ runlength := 0
+ count := 0
+
+ var dom, nondom *int
+ var domStep int
+ var nondomStep int
+
+ if p0.x < 0 || p0.y < 0 || p0.x >= q.w || p0.y >= q.h {
+ return -1
+ }
+
+ if p1.x < 0 || p1.y < 0 || p1.x >= q.w || p1.y >= q.h {
+ return -1
+ }
+
+ if math.Abs(float64(n)) > math.Abs(float64(d)) {
+ n, d = d, n
+ dom = &x
+ nondom = &y
+ } else {
+ dom = &y
+ nondom = &x
+ }
+
+ if n < 0 {
+ n = -n
+ nondomStep = -1
+ } else {
+ nondomStep = 1
+ }
+
+ if d < 0 {
+ d = -d
+ domStep = -1
+ } else {
+ domStep = 1
+ }
+
+ x = p0.x
+ y = p0.y
+ for i := 0; i <= d; i++ {
+ if y < 0 || y >= q.h || x < 0 || x >= q.w {
+ break
+ }
+ pixel := q.pixels[y*q.w+x]
+
+ if pixel > 0 {
+ if runlength >= 2 {
+ count++
+ }
+ runlength = 0
+ } else {
+ runlength++
+ }
+
+ a += n
+ *dom += domStep
+ if a >= d {
+ *nondom += nondomStep
+ a -= d
+ }
+ }
+ return count
+}
+
+func findLeftMostToLine(userData interface{}, y, left, right int) {
+ psd := userData.(*polygonScoreData)
+ xs := []int{left, right}
+ for i := 0; i < 2; i++ {
+ d := -psd.ref.y*xs[i] + psd.ref.x*y
+ if d < psd.scores[0] {
+ psd.scores[0] = d
+ psd.corners[0].x = xs[i]
+ psd.corners[0].y = y
+ }
+ }
+}
+
+// Try the measure the timing pattern for a given QR code. This does
+// not require the global perspective to have been set up, but it
+// does require that the capstone corners have been set to their
+// canonical rotation.
+//
+// For each capstone, we find a point in the middle of the ring band
+// which is nearest the centre of the code. Using these points, we do
+// a horizontal and a vertical timing scan.
+func (q *recognizer) measureTimingPattern(index int) int {
+ qr := &q.grids[index]
+ for i := 0; i < 3; i++ {
+ us := []float64{6.5, 6.5, 0.5}
+ vs := []float64{0.5, 6.5, 6.5}
+ cap := &q.capstones[qr.caps[i]]
+ perspectiveMap(cap.c[:], us[i], vs[i], &qr.tpep[i])
+ }
+
+ qr.hscan = q.timingScan(&qr.tpep[1], &qr.tpep[2])
+ qr.vscan = q.timingScan(&qr.tpep[1], &qr.tpep[0])
+ scan := qr.hscan
+ if qr.vscan > scan {
+ scan = qr.vscan
+ }
+
+ // If neither scan worked, we can't go any further.
+ if scan < 0 {
+ return -1
+ }
+
+ // Choose the nearest allowable grid size
+ size := scan*2 + 13
+ ver := (size - 15) / 4
+ qr.gridSize = ver*4 + 17
+
+ return 0
+}
+
+func (q *recognizer) recordQrGrid(a, b, c int) {
+
+ if len(q.grids) >= qrMaxGrids {
+ return
+ }
+
+ // Construct the hypotenuse line from A to C. B should be tothe left of this line.
+
+ h0 := q.capstones[a].center
+ var hd point
+ hd.x = q.capstones[c].center.x - q.capstones[a].center.x
+ hd.y = q.capstones[c].center.y - q.capstones[a].center.y
+
+ // Make sure A-B-C is clockwise
+ if (q.capstones[b].center.x-h0.x)*-hd.y+(q.capstones[b].center.y-h0.y)*hd.x > 0 {
+ a, c = c, a
+ hd.x = -hd.x
+ hd.y = -hd.y
+ }
+
+ qrIndex := len(q.grids)
+ q.grids = append(q.grids, qrGrid{})
+ qr := &q.grids[qrIndex]
+
+ qr.caps[0] = a
+ qr.caps[1] = b
+ qr.caps[2] = c
+ qr.alignRegion = -1
+
+ // Rotate each capstone so that corner 0 is top-left with respect
+ // to the grid.
+
+ for i := 0; i < 3; i++ {
+ cap := &q.capstones[qr.caps[i]]
+ rotateCapstone(cap, &h0, &hd)
+ cap.qrGrid = qrIndex
+ }
+
+ // Check the timing pattern. This doesn't require a perspective transform.
+
+ if q.measureTimingPattern(qrIndex) < 0 {
+ goto fail
+ }
+
+ // Make an estimate based for the alignment pattern based on extending lines from capstones A and C.
+ if !lineIntersect(&q.capstones[a].corners[0],
+ &q.capstones[a].corners[1],
+ &q.capstones[c].corners[0],
+ &q.capstones[c].corners[3],
+ &qr.align) {
+
+ goto fail
+ }
+
+ // On V2+ grids, we should use the alignment pattern.
+
+ if qr.gridSize > 21 {
+ // Try to find the actual location of the alignment pattern.
+
+ q.findAlignmentPattern(qrIndex)
+
+ // Find the point of the alignment pattern closest to the
+ // top-left of the QR grid.
+
+ if qr.alignRegion >= 0 {
+ var psd polygonScoreData
+ psd.corners = make([]point, 1)
+ reg := &q.regions[qr.alignRegion]
+
+ // Start from some point inside the alignment pattern
+ qr.align = reg.seed
+ psd.ref = hd
+ psd.corners[0] = qr.align
+
+ psd.scores[0] = -hd.y*qr.align.x + hd.x*qr.align.y
+
+ q.floodFillSeed(reg.seed.x, reg.seed.y, qr.alignRegion, qrPixelBlack, nil, nil, 0)
+
+ q.floodFillSeed(reg.seed.x, reg.seed.y, qrPixelBlack, qr.alignRegion, findLeftMostToLine, &psd, 0)
+ qr.align = psd.corners[0]
+
+ }
+ }
+
+ q.setupQrPerspective(qrIndex)
+ return
+
+ // We've been unable to complete setup for this grid. Undo what we've
+ // recorded and pretend it never happened.
+
+fail:
+ for i := 0; i < 3; i++ {
+ q.capstones[qr.caps[i]].qrGrid = -1
+ }
+ q.grids = q.grids[:len(q.grids)-1]
+
+}
+
+func (q *recognizer) testNeighbours(i int, hlist []*neighbour, vlist []*neighbour) {
+ bestScore := 0.0
+ bestH := -1
+ bestV := -1
+
+ // Test each possible grouping
+
+ for j := 0; j < len(hlist); j++ {
+ hn := hlist[j]
+
+ for k := 0; k < len(vlist); k++ {
+ vn := vlist[k]
+ score := math.Abs(1.0 - hn.distance/vn.distance)
+
+ if score > 2.5 {
+ continue
+ }
+
+ if bestH < 0 || score < bestScore {
+ bestH = hn.index
+ bestV = vn.index
+ bestScore = score
+ }
+ }
+ }
+
+ if bestH < 0 || bestV < 0 {
+ return
+ }
+
+ q.recordQrGrid(bestH, i, bestV)
+}
+
+func (q *recognizer) testGrouping(i int) {
+ c1 := &q.capstones[i]
+
+ hlist := make([]*neighbour, 0)
+ vlist := make([]*neighbour, 0)
+
+ if c1.qrGrid >= 0 {
+ return
+ }
+
+ // Look for potential neighbours by examining the relative gradients
+ // from this capstone to others.
+
+ for j := 0; j < len(q.capstones); j++ {
+ c2 := &q.capstones[j]
+
+ if i == j || c2.qrGrid >= 0 {
+ continue
+ }
+ var u, v float64
+
+ perspectiveUnmap(c1.c[:], &c2.center, &u, &v)
+
+ u = math.Abs(u - 3.5)
+ v = math.Abs(v - 3.5)
+
+ if u < 0.2*v {
+ n := &neighbour{}
+ n.index = j
+ n.distance = v
+ hlist = append(hlist, n)
+ }
+ if v < 0.2*u {
+ n := &neighbour{}
+ n.index = j
+ n.distance = u
+ vlist = append(vlist, n)
+ }
+ }
+
+ if !(len(hlist) > 0 && len(vlist) > 0) {
+ return
+ }
+ q.testNeighbours(i, hlist, vlist)
+}
diff --git a/vendor/github.com/liyue201/goqr/utils.go b/vendor/github.com/liyue201/goqr/utils.go
new file mode 100644
index 0000000..0865425
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/utils.go
@@ -0,0 +1,31 @@
+package goqr
+
+func lineIntersect(p0, p1, q0, q1, r *point) bool {
+ // (a, b) is perpendicular to line p
+ a := -(p1.y - p0.y)
+ b := p1.x - p0.x
+
+ // (c, d) is perpendicular to line q
+ c := -(q1.y - q0.y)
+ d := q1.x - q0.x
+
+ // e and f are dot products of the respective vectors with p and q
+ e := a*p1.x + b*p1.y
+ f := c*q1.x + d*q1.y
+
+ // Now we need to solve:
+ // [a b] [rx] [e]
+ // [c d] [ry] = [f]
+ //
+ // We do this by inverting the matrix and applying it to (e, f):
+ // [ d -b] [e] [rx]
+ // 1/det [-c a] [f] = [ry]
+ //
+ det := (a * d) - (b * c)
+ if det == 0 {
+ return false
+ }
+ r.x = (d*e - b*f) / det
+ r.y = (-c*e + a*f) / det
+ return true
+}
diff --git a/vendor/github.com/liyue201/goqr/version_db.go b/vendor/github.com/liyue201/goqr/version_db.go
new file mode 100644
index 0000000..76a23a4
--- /dev/null
+++ b/vendor/github.com/liyue201/goqr/version_db.go
@@ -0,0 +1,405 @@
+package goqr
+
+var qrVersionDb = [qrMaxVersion + 1]qrVersionInfo{
+ {},
+ { // Version 1
+ 26,
+ [qrMaxAliment]int{0},
+ [4]qrRsParams{
+ {26, 16, 1},
+ {26, 19, 1},
+ {26, 9, 1},
+ {26, 13, 1},
+ },
+ },
+ { // Version 2
+ 44,
+ [qrMaxAliment]int{6, 18, 0},
+ [4]qrRsParams{
+ {44, 28, 1},
+ {44, 34, 1},
+ {44, 16, 1},
+ {44, 22, 1},
+ },
+ },
+ { // Version 3
+ 70,
+ [qrMaxAliment]int{6, 22, 0},
+ [4]qrRsParams{
+ {70, 44, 1},
+ {70, 55, 1},
+ {35, 13, 2},
+ {35, 17, 2},
+ },
+ },
+ { // Version 4
+ 100,
+ [qrMaxAliment]int{6, 26, 0},
+ [4]qrRsParams{
+ {50, 32, 2},
+ {100, 80, 1},
+ {25, 9, 4},
+ {50, 24, 2},
+ },
+ },
+ { // Version 5
+ 134,
+ [qrMaxAliment]int{6, 30, 0},
+ [4]qrRsParams{
+ {67, 43, 2},
+ {134, 108, 1},
+ {33, 11, 2},
+ {33, 15, 2},
+ },
+ },
+ { // Version 6
+ 172,
+ [qrMaxAliment]int{6, 34, 0},
+ [4]qrRsParams{
+ {43, 27, 4},
+ {86, 68, 2},
+ {43, 15, 4},
+ {43, 19, 4},
+ },
+ },
+ { // Version 7
+ 196,
+ [qrMaxAliment]int{6, 22, 38, 0},
+ [4]qrRsParams{
+ {49, 31, 4},
+ {98, 78, 2},
+ {39, 13, 4},
+ {32, 14, 2},
+ },
+ },
+ { // Version 8
+ 242,
+ [qrMaxAliment]int{6, 24, 42, 0},
+ [4]qrRsParams{
+ {60, 38, 2},
+ {121, 97, 2},
+ {40, 14, 4},
+ {40, 18, 4},
+ },
+ },
+ { // Version 9
+ 292,
+ [qrMaxAliment]int{6, 26, 46, 0},
+ [4]qrRsParams{
+ {58, 36, 3},
+ {146, 116, 2},
+ {36, 12, 4},
+ {36, 16, 4},
+ },
+ },
+ { // Version 10
+ 346,
+ [qrMaxAliment]int{6, 28, 50, 0},
+ [4]qrRsParams{
+ {69, 43, 4},
+ {86, 68, 2},
+ {43, 15, 6},
+ {43, 19, 6},
+ },
+ },
+ { // Version 11
+ 404,
+ [qrMaxAliment]int{6, 30, 54, 0},
+ [4]qrRsParams{
+ {80, 50, 1},
+ {101, 81, 4},
+ {36, 12, 3},
+ {50, 22, 4},
+ },
+ },
+ { // Version 12
+ 466,
+ [qrMaxAliment]int{6, 32, 58, 0},
+ [4]qrRsParams{
+ {58, 36, 6},
+ {116, 92, 2},
+ {42, 14, 7},
+ {46, 20, 4},
+ },
+ },
+ { // Version 13
+ 532,
+ [qrMaxAliment]int{6, 34, 62, 0},
+ [4]qrRsParams{
+ {59, 37, 8},
+ {133, 107, 4},
+ {33, 11, 12},
+ {44, 20, 8},
+ },
+ },
+ { // Version 14
+ 581,
+ [qrMaxAliment]int{6, 26, 46, 66, 0},
+ [4]qrRsParams{
+ {64, 40, 4},
+ {145, 115, 3},
+ {36, 12, 11},
+ {36, 16, 11},
+ },
+ },
+ { // Version 15
+ 655,
+ [qrMaxAliment]int{6, 26, 48, 70, 0},
+ [4]qrRsParams{
+ {65, 41, 5},
+ {109, 87, 5},
+ {36, 12, 11},
+ {54, 24, 5},
+ },
+ },
+ { // Version 16
+ 733,
+ [qrMaxAliment]int{6, 26, 50, 74, 0},
+ [4]qrRsParams{
+ {73, 45, 7},
+ {122, 98, 5},
+ {45, 15, 3},
+ {43, 19, 15},
+ },
+ },
+ { // Version 17
+ 815,
+ [qrMaxAliment]int{6, 30, 54, 78, 0},
+ [4]qrRsParams{
+ {74, 46, 10},
+ {135, 107, 1},
+ {42, 14, 2},
+ {50, 22, 1},
+ },
+ },
+ { // Version 18
+ 901,
+ [qrMaxAliment]int{6, 30, 56, 82, 0},
+ [4]qrRsParams{
+ {69, 43, 9},
+ {150, 120, 5},
+ {42, 14, 2},
+ {50, 22, 17},
+ },
+ },
+ { // Version 19
+ 991,
+ [qrMaxAliment]int{6, 30, 58, 86, 0},
+ [4]qrRsParams{
+ {70, 44, 3},
+ {141, 113, 3},
+ {39, 13, 9},
+ {47, 21, 17},
+ },
+ },
+ { // Version 20
+ 1085,
+ [qrMaxAliment]int{6, 34, 62, 90, 0},
+ [4]qrRsParams{
+ {67, 41, 3},
+ {135, 107, 3},
+ {43, 15, 15},
+ {54, 24, 15},
+ },
+ },
+ { // Version 21
+ 1156,
+ [qrMaxAliment]int{6, 28, 50, 72, 92, 0},
+ [4]qrRsParams{
+ {68, 42, 17},
+ {144, 116, 4},
+ {46, 16, 19},
+ {50, 22, 17},
+ },
+ },
+ { // Version 22
+ 1258,
+ [qrMaxAliment]int{6, 26, 50, 74, 98, 0},
+ [4]qrRsParams{
+ {74, 46, 17},
+ {139, 111, 2},
+ {37, 13, 34},
+ {54, 24, 7},
+ },
+ },
+ { // Version 23
+ 1364,
+ [qrMaxAliment]int{6, 30, 54, 78, 102, 0},
+ [4]qrRsParams{
+ {75, 47, 4},
+ {151, 121, 4},
+ {45, 15, 16},
+ {54, 24, 11},
+ },
+ },
+ { // Version 24
+ 1474,
+ [qrMaxAliment]int{6, 28, 54, 80, 106, 0},
+ [4]qrRsParams{
+ {73, 45, 6},
+ {147, 117, 6},
+ {46, 16, 30},
+ {54, 24, 11},
+ },
+ },
+ { // Version 25
+ 1588,
+ [qrMaxAliment]int{6, 32, 58, 84, 110, 0},
+ [4]qrRsParams{
+ {75, 47, 8},
+ {132, 106, 8},
+ {45, 15, 22},
+ {54, 24, 7},
+ },
+ },
+ { // Version 26
+ 1706,
+ [qrMaxAliment]int{6, 30, 58, 86, 114, 0},
+ [4]qrRsParams{
+ {74, 46, 19},
+ {142, 114, 10},
+ {46, 16, 33},
+ {50, 22, 28},
+ },
+ },
+ { // Version 27
+ 1828,
+ [qrMaxAliment]int{6, 34, 62, 90, 118, 0},
+ [4]qrRsParams{
+ {73, 45, 22},
+ {152, 122, 8},
+ {45, 15, 12},
+ {53, 23, 8},
+ },
+ },
+ { // Version 28
+ 1921,
+ [qrMaxAliment]int{6, 26, 50, 74, 98, 122, 0},
+ [4]qrRsParams{
+ {73, 45, 3},
+ {147, 117, 3},
+ {45, 15, 11},
+ {54, 24, 4},
+ },
+ },
+ { // Version 29
+ 2051,
+ [qrMaxAliment]int{6, 30, 54, 78, 102, 126, 0},
+ [4]qrRsParams{
+ {73, 45, 21},
+ {146, 116, 7},
+ {45, 15, 19},
+ {53, 23, 1},
+ },
+ },
+ { // Version 30
+ 2185,
+ [qrMaxAliment]int{6, 26, 52, 78, 104, 130, 0},
+ [4]qrRsParams{
+ {75, 47, 19},
+ {145, 115, 5},
+ {45, 15, 23},
+ {54, 24, 15},
+ },
+ },
+ { // Version 31
+ 2323,
+ [qrMaxAliment]int{6, 30, 56, 82, 108, 134, 0},
+ [4]qrRsParams{
+ {74, 46, 2},
+ {145, 115, 13},
+ {45, 15, 23},
+ {54, 24, 42},
+ },
+ },
+ { // Version 32
+ 2465,
+ [qrMaxAliment]int{6, 34, 60, 86, 112, 138, 0},
+ [4]qrRsParams{
+ {74, 46, 10},
+ {145, 115, 17},
+ {45, 15, 19},
+ {54, 24, 10},
+ },
+ },
+ { // Version 33
+ 2611,
+ [qrMaxAliment]int{6, 30, 58, 86, 114, 142, 0},
+ [4]qrRsParams{
+ {74, 46, 14},
+ {145, 115, 17},
+ {45, 15, 11},
+ {54, 24, 29},
+ },
+ },
+ { // Version 34
+ 2761,
+ [qrMaxAliment]int{6, 34, 62, 90, 118, 146, 0},
+ [4]qrRsParams{
+ {74, 46, 14},
+ {145, 115, 13},
+ {46, 16, 59},
+ {54, 24, 44},
+ },
+ },
+ { // Version 35
+ 2876,
+ [qrMaxAliment]int{6, 30, 54, 78, 102, 126, 150},
+ [4]qrRsParams{
+ {75, 47, 12},
+ {151, 121, 12},
+ {45, 15, 22},
+ {54, 24, 39},
+ },
+ },
+ { // Version 36
+ 3034,
+ [qrMaxAliment]int{6, 24, 50, 76, 102, 128, 154},
+ [4]qrRsParams{
+ {75, 47, 6},
+ {151, 121, 6},
+ {45, 15, 2},
+ {54, 24, 46},
+ },
+ },
+ { // Version 37
+ 3196,
+ [qrMaxAliment]int{6, 28, 54, 80, 106, 132, 158},
+ [4]qrRsParams{
+ {74, 46, 29},
+ {152, 122, 17},
+ {45, 15, 24},
+ {54, 24, 49},
+ },
+ },
+ { // Version 38
+ 3362,
+ [qrMaxAliment]int{6, 32, 58, 84, 110, 136, 162},
+ [4]qrRsParams{
+ {74, 46, 13},
+ {152, 122, 4},
+ {45, 15, 42},
+ {54, 24, 48},
+ },
+ },
+ { // Version 39
+ 3532,
+ [qrMaxAliment]int{6, 26, 54, 82, 110, 138, 166},
+ [4]qrRsParams{
+ {75, 47, 40},
+ {147, 117, 20},
+ {45, 15, 10},
+ {54, 24, 43},
+ },
+ },
+ { // Version 40
+ 3706,
+ [qrMaxAliment]int{6, 30, 58, 86, 114, 142, 170},
+ [4]qrRsParams{
+ {75, 47, 18},
+ {148, 118, 19},
+ {45, 15, 20},
+ {54, 24, 34},
+ },
+ },
+}
diff --git a/vendor/github.com/skip2/go-qrcode/.gitignore b/vendor/github.com/skip2/go-qrcode/.gitignore
new file mode 100644
index 0000000..bc1be2b
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/.gitignore
@@ -0,0 +1,4 @@
+*.sw*
+*.png
+*.directory
+qrcode/qrcode
diff --git a/vendor/github.com/skip2/go-qrcode/.travis.yml b/vendor/github.com/skip2/go-qrcode/.travis.yml
new file mode 100644
index 0000000..7ced8fb
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/.travis.yml
@@ -0,0 +1,8 @@
+language: go
+
+go:
+ - 1.7
+
+script:
+ - go test -v ./...
+
diff --git a/vendor/github.com/skip2/go-qrcode/LICENSE b/vendor/github.com/skip2/go-qrcode/LICENSE
new file mode 100644
index 0000000..342c5e5
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Tom Harwood
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/skip2/go-qrcode/README.md b/vendor/github.com/skip2/go-qrcode/README.md
new file mode 100644
index 0000000..0a800b7
--- /dev/null
+++ b/vendor/github.com/skip2/go-qrcode/README.md
@@ -0,0 +1,86 @@
+# go-qrcode #
+
+
+
+Package qrcode implements a QR Code encoder. [](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
+
+[](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/vendor/github.com/skip2/go-qrcode/bitset/bitset.go b/vendor/github.com/skip2/go-qrcode/bitset/bitset.go
new file mode 100644
index 0000000..fba3bf4
--- /dev/null
+++ b/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/vendor/github.com/skip2/go-qrcode/encoder.go b/vendor/github.com/skip2/go-qrcode/encoder.go
new file mode 100644
index 0000000..6a809cf
--- /dev/null
+++ b/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/vendor/github.com/skip2/go-qrcode/qrcode.go b/vendor/github.com/skip2/go-qrcode/qrcode.go
new file mode 100644
index 0000000..d0541bc
--- /dev/null
+++ b/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/vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go b/vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go
new file mode 100644
index 0000000..6a7003f
--- /dev/null
+++ b/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/vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go b/vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go
new file mode 100644
index 0000000..962f545
--- /dev/null
+++ b/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/vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go b/vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go
new file mode 100644
index 0000000..561697b
--- /dev/null
+++ b/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/vendor/github.com/skip2/go-qrcode/regular_symbol.go b/vendor/github.com/skip2/go-qrcode/regular_symbol.go
new file mode 100644
index 0000000..51eb148
--- /dev/null
+++ b/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/vendor/github.com/skip2/go-qrcode/symbol.go b/vendor/github.com/skip2/go-qrcode/symbol.go
new file mode 100644
index 0000000..0cb1327
--- /dev/null
+++ b/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/vendor/github.com/skip2/go-qrcode/version.go b/vendor/github.com/skip2/go-qrcode/version.go
new file mode 100644
index 0000000..738cf3d
--- /dev/null
+++ b/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/vendor/modules.txt b/vendor/modules.txt
index 57ebe2e..aeb3d4e 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -135,6 +135,9 @@ github.com/jeandeaual/go-locale
github.com/jsummers/gobmp
# github.com/kr/text v0.2.0
## explicit
+# github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea
+## explicit; go 1.12
+github.com/liyue201/goqr
# github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
## explicit
github.com/nfnt/resize
@@ -158,6 +161,11 @@ github.com/rymdport/portal/notification
github.com/rymdport/portal/openuri
github.com/rymdport/portal/settings
github.com/rymdport/portal/settings/appearance
+# github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
+## explicit; go 1.13
+github.com/skip2/go-qrcode
+github.com/skip2/go-qrcode/bitset
+github.com/skip2/go-qrcode/reedsolomon
# github.com/spf13/cobra v1.10.1
## explicit; go 1.15
github.com/spf13/cobra