From d6a0457f339609b9006a9533ab5ad73f5390f43a Mon Sep 17 00:00:00 2001 From: Lukas Batelka Date: Mon, 22 Sep 2025 19:42:23 +0200 Subject: [PATCH] feature/ui kopirovani a vkladani qr --- go.mod | 2 + go.sum | 8 + qr_support.go | 44 + ui.go | 324 +- vendor/github.com/liyue201/goqr/.travis.yml | 23 + vendor/github.com/liyue201/goqr/LICENSE | 165 + vendor/github.com/liyue201/goqr/README.md | 54 + vendor/github.com/liyue201/goqr/decoding.go | 716 ++++ vendor/github.com/liyue201/goqr/define.go | 83 + vendor/github.com/liyue201/goqr/errors.go | 15 + vendor/github.com/liyue201/goqr/qr_const.go | 36 + vendor/github.com/liyue201/goqr/qrcode.go | 146 + .../github.com/liyue201/goqr/recognition.go | 74 + vendor/github.com/liyue201/goqr/recognizer.go | 976 ++++++ vendor/github.com/liyue201/goqr/utils.go | 31 + vendor/github.com/liyue201/goqr/version_db.go | 405 +++ vendor/github.com/skip2/go-qrcode/.gitignore | 4 + vendor/github.com/skip2/go-qrcode/.travis.yml | 8 + vendor/github.com/skip2/go-qrcode/LICENSE | 19 + vendor/github.com/skip2/go-qrcode/README.md | 86 + .../skip2/go-qrcode/bitset/bitset.go | 273 ++ vendor/github.com/skip2/go-qrcode/encoder.go | 486 +++ vendor/github.com/skip2/go-qrcode/qrcode.go | 608 ++++ .../skip2/go-qrcode/reedsolomon/gf2_8.go | 387 +++ .../skip2/go-qrcode/reedsolomon/gf_poly.go | 216 ++ .../go-qrcode/reedsolomon/reed_solomon.go | 73 + .../skip2/go-qrcode/regular_symbol.go | 315 ++ vendor/github.com/skip2/go-qrcode/symbol.go | 309 ++ vendor/github.com/skip2/go-qrcode/version.go | 3050 +++++++++++++++++ vendor/modules.txt | 8 + 30 files changed, 8916 insertions(+), 28 deletions(-) create mode 100644 qr_support.go create mode 100644 vendor/github.com/liyue201/goqr/.travis.yml create mode 100644 vendor/github.com/liyue201/goqr/LICENSE create mode 100644 vendor/github.com/liyue201/goqr/README.md create mode 100644 vendor/github.com/liyue201/goqr/decoding.go create mode 100644 vendor/github.com/liyue201/goqr/define.go create mode 100644 vendor/github.com/liyue201/goqr/errors.go create mode 100644 vendor/github.com/liyue201/goqr/qr_const.go create mode 100644 vendor/github.com/liyue201/goqr/qrcode.go create mode 100644 vendor/github.com/liyue201/goqr/recognition.go create mode 100644 vendor/github.com/liyue201/goqr/recognizer.go create mode 100644 vendor/github.com/liyue201/goqr/utils.go create mode 100644 vendor/github.com/liyue201/goqr/version_db.go create mode 100644 vendor/github.com/skip2/go-qrcode/.gitignore create mode 100644 vendor/github.com/skip2/go-qrcode/.travis.yml create mode 100644 vendor/github.com/skip2/go-qrcode/LICENSE create mode 100644 vendor/github.com/skip2/go-qrcode/README.md create mode 100644 vendor/github.com/skip2/go-qrcode/bitset/bitset.go create mode 100644 vendor/github.com/skip2/go-qrcode/encoder.go create mode 100644 vendor/github.com/skip2/go-qrcode/qrcode.go create mode 100644 vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go create mode 100644 vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go create mode 100644 vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go create mode 100644 vendor/github.com/skip2/go-qrcode/regular_symbol.go create mode 100644 vendor/github.com/skip2/go-qrcode/symbol.go create mode 100644 vendor/github.com/skip2/go-qrcode/version.go 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 +[![GoDoc](https://godoc.org/github.com/liyue201/goqr?status.svg)](https://godoc.org/github.com/liyue201/goqr) +[![Go Report Card](https://goreportcard.com/badge/github.com/liyue201/goqr)](https://goreportcard.com/report/github.com/liyue201/goqr) +[![Build Status](https://travis-ci.org/liyue201/goqr.svg?branch=master)](https://travis-ci.org/liyue201/goqr) +[![Coverall](https://coveralls.io/repos/github/liyue201/goqr/badge.svg?branch=master)](https://coveralls.io/github/liyue201/goqr) +[![License](https://img.shields.io/badge/license-GPLv3-brightgreen.svg)](/LICENSE) +[![Example](https://img.shields.io/badge/learn-example-brightgreen.svg)](/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. [![Build Status](https://travis-ci.org/skip2/go-qrcode.svg?branch=master)](https://travis-ci.org/skip2/go-qrcode) + +A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be encoded, with URLs being a popular choice :) + +Each QR Code contains error recovery information to aid reading damaged or obscured codes. There are four levels of error recovery: Low, medium, high and highest. QR Codes with a higher recovery level are more robust to damage, at the cost of being physically larger. + +## Install + + go get -u github.com/skip2/go-qrcode/... + +A command-line tool `qrcode` will be built into `$GOPATH/bin/`. + +## Usage + + import qrcode "github.com/skip2/go-qrcode" + +- **Create a 256x256 PNG image:** + + var png []byte + png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256) + +- **Create a 256x256 PNG image and write to a file:** + + err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png") + +- **Create a 256x256 PNG image with custom colors and write to file:** + + err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png") + +All examples use the qrcode.Medium error Recovery Level and create a fixed 256x256px size QR Code. The last function creates a white on black instead of black on white QR Code. + +## Documentation + +[![godoc](https://godoc.org/github.com/skip2/go-qrcode?status.png)](https://godoc.org/github.com/skip2/go-qrcode) + +## Demoapp + +[http://go-qrcode.appspot.com](http://go-qrcode.appspot.com) + +## CLI + +A command-line tool `qrcode` will be built into `$GOPATH/bin/`. + +``` +qrcode -- QR Code encoder in Go +https://github.com/skip2/go-qrcode + +Flags: + -d disable QR Code border + -i invert black and white + -o string + out PNG file prefix, empty for stdout + -s int + image size (pixel) (default 256) + -t print as text-art on stdout + +Usage: + 1. Arguments except for flags are joined by " " and used to generate QR code. + Default output is STDOUT, pipe to imagemagick command "display" to display + on any X server. + + qrcode hello word | display + + 2. Save to file if "display" not available: + + qrcode "homepage: https://github.com/skip2/go-qrcode" > out.png + +``` +## Maximum capacity +The maximum capacity of a QR Code varies according to the content encoded and the error recovery level. The maximum capacity is 2,953 bytes, 4,296 alphanumeric characters, 7,089 numeric digits, or a combination of these. + +## Borderless QR Codes + +To aid QR Code reading software, QR codes have a built in whitespace border. + +If you know what you're doing, and don't want a border, see https://gist.github.com/skip2/7e3d8a82f5317df9be437f8ec8ec0b7d for how to do it. It's still recommended you include a border manually. + +## Links + +- [http://en.wikipedia.org/wiki/QR_code](http://en.wikipedia.org/wiki/QR_code) +- [ISO/IEC 18004:2006](http://www.iso.org/iso/catalogue_detail.htm?csnumber=43655) - Main QR Code specification (approx CHF 198,00)
+- [https://github.com/qpliu/qrencode-go/](https://github.com/qpliu/qrencode-go/) - alternative Go QR encoding library based on [ZXing](https://github.com/zxing/zxing) diff --git a/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