feature/ui - zatim ne uplne uhlazena ale celkem pouzitelna appka #1

Merged
luke-20 merged 17 commits from feature/ui into main 2025-09-28 21:05:52 +02:00
30 changed files with 8916 additions and 28 deletions
Showing only changes of commit d6a0457f33 - Show all commits

2
go.mod
View File

@ -28,10 +28,12 @@ require (
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
github.com/kr/text v0.2.0 // 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/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rymdport/portal v0.4.1 // 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/spf13/pflag v1.0.9 // indirect
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect

8
go.sum
View File

@ -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/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/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/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= 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/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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 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= 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/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 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4= 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 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= 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= 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/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 h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= 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 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= 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 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 h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

44
qr_support.go Normal file
View File

@ -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
}

324
ui.go
View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
encrypt "fckeuspy-go/lib" encrypt "fckeuspy-go/lib"
"image/color" "image/color"
"io"
"os" "os"
"time" "time"
@ -11,30 +12,57 @@ import (
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
) )
type uiParts struct { type uiParts struct {
outKey *widget.Entry outKey *widget.Entry
msg *widget.Entry msg *widget.Entry
peer *widget.Entry peer *widget.Entry
cipherOut *widget.Entry cipherOut *widget.Entry
payload *widget.Entry payload *widget.Entry
plainOut *widget.Entry plainOut *widget.Entry
toastLabel *widget.Label 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 { func buildEntries() *uiParts {
p := &uiParts{ p := &uiParts{
outKey: widget.NewMultiLineEntry(), outKey: widget.NewMultiLineEntry(),
msg: widget.NewMultiLineEntry(), msg: widget.NewMultiLineEntry(),
peer: widget.NewMultiLineEntry(), peer: widget.NewMultiLineEntry(),
cipherOut: widget.NewMultiLineEntry(), cipherOut: widget.NewMultiLineEntry(),
payload: widget.NewMultiLineEntry(), payload: widget.NewMultiLineEntry(),
plainOut: widget.NewMultiLineEntry(), plainOut: widget.NewMultiLineEntry(),
toastLabel: widget.NewLabel(""), 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.outKey.SetPlaceHolder("Veřejný klíč / certifikát…")
p.msg.SetPlaceHolder("Sem napiš zprávu…") p.msg.SetPlaceHolder("Sem napiš zprávu…")
p.peer.SetPlaceHolder("-----BEGIN PUBLIC KEY----- … nebo CERTIFICATE …") 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()) }) btnPaste := widget.NewButton("Paste", func() { parts.outKey.SetText(fyne.CurrentApp().Clipboard().Content()) })
deleteBtn := widget.NewButton("Smazat identitu", func() { deleteBtn := widget.NewButton("Smazat identitu", func() {
// dialog pro heslo
pwEntry := widget.NewPasswordEntry() pwEntry := widget.NewPasswordEntry()
pwEntry.SetPlaceHolder("Heslo pro potvrzení…") pwEntry.SetPlaceHolder("Heslo pro potvrzení…")
content := widget.NewForm(widget.NewFormItem("Heslo", pwEntry)) 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() dialog.NewError(err, fyne.CurrentApp().Driver().AllWindows()[0]).Show()
return return
} }
// Quit app po odstranění (uživatel musí znovu spustit a vytvořit nový vault)
fyne.CurrentApp().Quit() fyne.CurrentApp().Quit()
}, fyne.CurrentApp().Driver().AllWindows()[0]) }, 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( group := container.NewVBox(
widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
tileIdentity, tileIdentity,
parts.outKey, identityContainer,
) )
return container.NewVScroll(group) return container.NewVScroll(group)
} }
@ -148,11 +230,71 @@ func copyClip(s string, parts *uiParts) {
// assembleResponsive builds split view that collapses for narrow widths // assembleResponsive builds split view that collapses for narrow widths
// Tab: Encrypt // Tab: Encrypt
func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject { func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
parts.cipherOut.Disable() // read only output parts.cipherOut.Disable()
peerBtns := buttonTile( peerBtns := buttonTile(
widget.NewButton("Paste", func() { parts.peer.SetText(fyne.CurrentApp().Clipboard().Content()) }), widget.NewButton("Paste", func() { parts.peer.SetText(fyne.CurrentApp().Clipboard().Content()) }),
widget.NewButton("Clear", func() { parts.peer.SetText("") }), 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() { encAction := func() {
m := parts.msg.Text m := parts.msg.Text
p := parts.peer.Text p := parts.peer.Text
@ -163,23 +305,74 @@ func buildEncryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
go func(msg, peer string) { go func(msg, peer string) {
res, err := svc.Encrypt(msg, peer) res, err := svc.Encrypt(msg, peer)
if err != nil { if err != nil {
fyne.Do(func() { parts.cipherOut.SetText(""); parts.showToast("Chyba") }) fyne.Do(func() { parts.cipherOut.SetText(""); updateQR(""); parts.showToast("Chyba") })
return 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) }(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( msgBtns := buttonTile(
widget.NewButton("Clear+Paste", func() { parts.msg.SetText(""); parts.msg.SetText(fyne.CurrentApp().Clipboard().Content()) }), widget.NewButton("Clear+Paste", func() { parts.msg.SetText(""); parts.msg.SetText(fyne.CurrentApp().Clipboard().Content()) }),
widget.NewButton("Encrypt", encAction), widget.NewButton("Encrypt", func() { encAction(); updateMode() }),
widget.NewButton("Copy encrypted", func() { copyClip(parts.cipherOut.Text, parts) }), widget.NewButton("Copy", func() { copyClip(parts.cipherOut.Text, parts) }),
widget.NewButton("Import Key QR", importPeerQR),
) )
group := container.NewVBox( group := container.NewVBox(
widget.NewLabelWithStyle("Šifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), 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("Zpráva"), msgBtns, parts.msg,
widget.NewLabel("Výsledek"), parts.cipherOut, container.NewHBox(widget.NewLabel("Výstup"), toggleBtn),
outputContainer,
) )
return container.NewVScroll(group) 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") }) fyne.Do(func() { parts.plainOut.SetText(res); parts.showToast("OK") })
}(pl) }(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( payloadBtns := buttonTile(
widget.NewButton("Paste+Decrypt", func() { widget.NewButton("Paste+Decrypt", func() {
clip := fyne.CurrentApp().Clipboard().Content() clip := fyne.CurrentApp().Clipboard().Content()
parts.payload.SetText(clip) parts.payload.SetText(clip)
if parts.showPayloadQR {
updatePayloadQR(clip)
}
decryptAction() decryptAction()
}), }),
// widget.NewButton("Decrypt", decryptAction), widget.NewButton("Clear", func() {
widget.NewButton("Clear", func() { parts.payload.SetText(""); parts.plainOut.SetText("") }), 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) })) plainBtns := buttonTile(widget.NewButton("Copy", func() { copyClip(parts.plainOut.Text, parts) }))
group := container.NewVBox( group := container.NewVBox(
widget.NewLabelWithStyle("Dešifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), 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, widget.NewLabel("Výsledek"), plainBtns, parts.plainOut,
) )
return container.NewVScroll(group) return container.NewVScroll(group)

23
vendor/github.com/liyue201/goqr/.travis.yml generated vendored Normal file
View File

@ -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="

165
vendor/github.com/liyue201/goqr/LICENSE generated vendored Normal file
View File

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
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.

54
vendor/github.com/liyue201/goqr/README.md generated vendored Normal file
View File

@ -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")
}
```

716
vendor/github.com/liyue201/goqr/decoding.go generated vendored Normal file
View File

@ -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
}

83
vendor/github.com/liyue201/goqr/define.go generated vendored Normal file
View File

@ -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
}

15
vendor/github.com/liyue201/goqr/errors.go generated vendored Normal file
View File

@ -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")
)

36
vendor/github.com/liyue201/goqr/qr_const.go generated vendored Normal file
View File

@ -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
)

146
vendor/github.com/liyue201/goqr/qrcode.go generated vendored Normal file
View File

@ -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
}

74
vendor/github.com/liyue201/goqr/recognition.go generated vendored Normal file
View File

@ -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)
}

976
vendor/github.com/liyue201/goqr/recognizer.go generated vendored Normal file
View File

@ -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)
}

31
vendor/github.com/liyue201/goqr/utils.go generated vendored Normal file
View File

@ -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
}

405
vendor/github.com/liyue201/goqr/version_db.go generated vendored Normal file
View File

@ -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},
},
},
}

4
vendor/github.com/skip2/go-qrcode/.gitignore generated vendored Normal file
View File

@ -0,0 +1,4 @@
*.sw*
*.png
*.directory
qrcode/qrcode

8
vendor/github.com/skip2/go-qrcode/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,8 @@
language: go
go:
- 1.7
script:
- go test -v ./...

19
vendor/github.com/skip2/go-qrcode/LICENSE generated vendored Normal file
View File

@ -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.

86
vendor/github.com/skip2/go-qrcode/README.md generated vendored Normal file
View File

@ -0,0 +1,86 @@
# go-qrcode #
<img src='https://skip.org/img/nyancat-youtube-qr.png' align='right'>
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)<br>
- [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)

273
vendor/github.com/skip2/go-qrcode/bitset/bitset.go generated vendored Normal file
View File

@ -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(i)) != 0 {
b.bits[b.numBits/8] |= 0x80 >> 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(i)) != 0 {
b.bits[b.numBits/8] |= 0x80 >> 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
}

486
vendor/github.com/skip2/go-qrcode/encoder.go generated vendored Normal file
View File

@ -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
}

608
vendor/github.com/skip2/go-qrcode/qrcode.go generated vendored Normal file
View File

@ -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()
}

387
vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go generated vendored Normal file
View File

@ -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

View File

@ -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
}

View File

@ -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
}

315
vendor/github.com/skip2/go-qrcode/regular_symbol.go generated vendored Normal file
View File

@ -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 := &regularSymbol{
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
}

309
vendor/github.com/skip2/go-qrcode/symbol.go generated vendored Normal file
View File

@ -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))
}

3050
vendor/github.com/skip2/go-qrcode/version.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

8
vendor/modules.txt vendored
View File

@ -135,6 +135,9 @@ github.com/jeandeaual/go-locale
github.com/jsummers/gobmp github.com/jsummers/gobmp
# github.com/kr/text v0.2.0 # github.com/kr/text v0.2.0
## explicit ## 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 # github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
## explicit ## explicit
github.com/nfnt/resize github.com/nfnt/resize
@ -158,6 +161,11 @@ github.com/rymdport/portal/notification
github.com/rymdport/portal/openuri github.com/rymdport/portal/openuri
github.com/rymdport/portal/settings github.com/rymdport/portal/settings
github.com/rymdport/portal/settings/appearance 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 # github.com/spf13/cobra v1.10.1
## explicit; go 1.15 ## explicit; go 1.15
github.com/spf13/cobra github.com/spf13/cobra