mirror of
https://github.com/Luzifer/nginx-sso.git
synced 2024-12-20 12:51:17 +00:00
Implement MFA verification for logins (#10)
This commit is contained in:
parent
3bf7477e98
commit
f6d622d1b8
93 changed files with 8739 additions and 44 deletions
25
Gopkg.lock
generated
25
Gopkg.lock
generated
|
@ -25,6 +25,18 @@
|
|||
revision = "7aef1d393c1e2d0758901853b59981c7adc67c7e"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b529f4bf748979caa18b599d40d13e8b6e591a74b340f315ce4f95e119c288c2"
|
||||
name = "github.com/boombuler/barcode"
|
||||
packages = [
|
||||
".",
|
||||
"qr",
|
||||
"utils",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "3cfea5ab600ae37946be2b763b8ec2c1cf2d272d"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:b7eb152b61f41f0c9bcb6cf1187e1470a91d5e01d1e08f8b7bfd722578851720"
|
||||
|
@ -81,6 +93,18 @@
|
|||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:4aea8409c0c3472587636033ab1fa10a2cec1d716449c55d06de4ad96dec0ec1"
|
||||
name = "github.com/pquerna/otp"
|
||||
packages = [
|
||||
".",
|
||||
"hotp",
|
||||
"totp",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "b7b89250c468c06871d3837bee02e2d5c155ae19"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:42a42c4bc67bed17f40fddf0f24d4403e25e7b96488456cf4248e6d16659d370"
|
||||
name = "github.com/sirupsen/logrus"
|
||||
|
@ -164,6 +188,7 @@
|
|||
"github.com/gorilla/sessions",
|
||||
"github.com/jda/go-crowd",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/pquerna/otp/totp",
|
||||
"github.com/sirupsen/logrus",
|
||||
"golang.org/x/crypto/bcrypt",
|
||||
"gopkg.in/ldap.v2",
|
||||
|
|
56
README.md
56
README.md
|
@ -92,6 +92,7 @@ The login form can be customized with its wording and the default login method.
|
|||
login:
|
||||
title: "luzifer.io - Login"
|
||||
default_method: "simple"
|
||||
hide_mfa_field: false
|
||||
names:
|
||||
simple: "Username / Password"
|
||||
yubikey: "Yubikey"
|
||||
|
@ -99,6 +100,8 @@ login:
|
|||
|
||||
Most options should explain themselves, the `names` dictionary maps IDs of the authentication methods (shown in the title of their config section below) to human readable strings. You can set any string you need and your user recognizes.
|
||||
|
||||
In case you don't want to show up the "MFA Token" fields even though the providers being used does support them (for example if you are not using MFA and don't want to confuse your users) you can set the `hide_mfa_field` flag to hide them.
|
||||
|
||||
### Main configuration: Cookie Settings
|
||||
|
||||
Most of the cookie settings are pre-set to sane defaults but you definitly need to configure some.
|
||||
|
@ -175,6 +178,50 @@ Each `rules` entry has two mandantory and three optional fields of which at leas
|
|||
|
||||
The `allow` and `deny` directives are arrays of users and groups. Groups are prefixed using an `@` sign. There is a simple logic: Users before groups, denies before allows. So if you allow the group `@test` containing the user `mike` but deny the user `mike`, mike will not be able to access the matching sites.
|
||||
|
||||
### MFA Configuration
|
||||
|
||||
Each provider supporting MFA does have some kind of configuration for the MFA providers. As there are multiple MFA providers the configuration sadly isn't that simple and needs to have the following format:
|
||||
|
||||
```yaml
|
||||
provider: <provider name>
|
||||
attributes:
|
||||
<mapping of attributes>
|
||||
```
|
||||
|
||||
#### Google Authenticator
|
||||
|
||||
The provider name here is `google` while the only supported argument at the moment is `secret`. The secret is what you need to provide to your users for them to add the config to their authenticator. (It MUST be base32 encoded!)
|
||||
|
||||
Here is an example of the URI to provide in a QRCode:
|
||||
|
||||
```yaml
|
||||
provider: google
|
||||
attributes:
|
||||
secret: MZXW6YTBOIFA
|
||||
```
|
||||
|
||||
`otpauth://totp/Example:myusername?secret=myverysecretsecret` ([Docs](https://github.com/google/google-authenticator/wiki/Key-Uri-Format))
|
||||
|
||||
#### Yubikey
|
||||
|
||||
This provider needs a configuration to function correctly:
|
||||
|
||||
```yaml
|
||||
mfa:
|
||||
yubikey:
|
||||
# Get your client / secret from https://upgrade.yubico.com/getapikey/
|
||||
client_id: "12345"
|
||||
secret_key: "foobar"
|
||||
```
|
||||
|
||||
The corresponding expected MFA configuration is as following:
|
||||
|
||||
```yaml
|
||||
provider: yubikey
|
||||
attributes:
|
||||
device: ccccccfcvuul
|
||||
```
|
||||
|
||||
### Provider configuration: Atlassian Crowd (`crowd`)
|
||||
|
||||
The crowd auth provider connects nginx-sso with an Atlassian Crowd directory server. The SSO authentication cookie used by Jira and Confluence is also used by nginx-sso which means a login in Jira will also perform a login on nginx-sso and vice versa.
|
||||
|
@ -270,12 +317,21 @@ providers:
|
|||
groups:
|
||||
admins: ["luzifer"]
|
||||
users: ["mike"]
|
||||
|
||||
# MFA configs: Username to configs mapping
|
||||
mfa:
|
||||
luzifer:
|
||||
- provider: google
|
||||
attributes:
|
||||
secret: asdgsdfhgshf
|
||||
```
|
||||
|
||||
You can see how to configure the provider the example above: No surprises, just ensure you are using bcrypt hashes for the passwords, no other hash functions are supported.
|
||||
|
||||
If `enable_basic_auth` is set to `true` the credentials can also be submitted through basic auth. This is useful for services whose clients does not support other types of authentication.
|
||||
|
||||
When there is at least one MFA configuration provided for the user inside the `mfa` block the user will be forced to enter a MFA token during login or otherwise the login will fail.
|
||||
|
||||
### Provider configuration: Token Auth (`token`)
|
||||
|
||||
The token auth provider is intended to give machines access to endpoints. Users will not be able to "login" using tokens when they see the login form.
|
||||
|
|
|
@ -28,7 +28,7 @@ func (a authCrowd) AuthenticatorID() string { return "crowd" }
|
|||
// Configure loads the configuration for the Authenticator from the
|
||||
// global config.yaml file which is passed as a byte-slice.
|
||||
// If no configuration for the Authenticator is supplied the function
|
||||
// needs to return the errAuthenticatorUnconfigured
|
||||
// needs to return the errProviderUnconfigured
|
||||
func (a *authCrowd) Configure(yamlSource []byte) error {
|
||||
envelope := struct {
|
||||
Providers struct {
|
||||
|
@ -41,7 +41,7 @@ func (a *authCrowd) Configure(yamlSource []byte) error {
|
|||
}
|
||||
|
||||
if envelope.Providers.Crowd == nil {
|
||||
return errAuthenticatorUnconfigured
|
||||
return errProviderUnconfigured
|
||||
}
|
||||
|
||||
a.URL = envelope.Providers.Crowd.URL
|
||||
|
@ -49,7 +49,7 @@ func (a *authCrowd) Configure(yamlSource []byte) error {
|
|||
a.AppPassword = envelope.Providers.Crowd.AppPassword
|
||||
|
||||
if a.AppName == "" || a.AppPassword == "" {
|
||||
return errAuthenticatorUnconfigured
|
||||
return errProviderUnconfigured
|
||||
}
|
||||
|
||||
var err error
|
||||
|
@ -106,13 +106,13 @@ func (a authCrowd) DetectUser(res http.ResponseWriter, r *http.Request) (string,
|
|||
// in order to use DetectUser for the next login.
|
||||
// If the user did not login correctly the errNoValidUserFound
|
||||
// needs to be returned
|
||||
func (a authCrowd) Login(res http.ResponseWriter, r *http.Request) error {
|
||||
func (a authCrowd) Login(res http.ResponseWriter, r *http.Request) (string, []mfaConfig, error) {
|
||||
username := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "username"}, "-"))
|
||||
password := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "password"}, "-"))
|
||||
|
||||
cc, err := a.crowd.GetCookieConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
sess, err := a.crowd.NewSession(username, password, r.RemoteAddr)
|
||||
|
@ -120,7 +120,7 @@ func (a authCrowd) Login(res http.ResponseWriter, r *http.Request) error {
|
|||
log.WithFields(log.Fields{
|
||||
"username": username,
|
||||
}).WithError(err).Debug("Crowd authentication failed")
|
||||
return errNoValidUserFound
|
||||
return "", nil, errNoValidUserFound
|
||||
}
|
||||
|
||||
http.SetCookie(res, &http.Cookie{
|
||||
|
@ -133,7 +133,7 @@ func (a authCrowd) Login(res http.ResponseWriter, r *http.Request) error {
|
|||
HttpOnly: true,
|
||||
})
|
||||
|
||||
return nil
|
||||
return username, nil, nil
|
||||
}
|
||||
|
||||
// LoginFields needs to return the fields required for this login
|
||||
|
@ -176,3 +176,10 @@ func (a authCrowd) Logout(res http.ResponseWriter, r *http.Request) (err error)
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SupportsMFA returns the MFA detection capabilities of the login
|
||||
// provider. If the provider can provide mfaConfig objects from its
|
||||
// configuration return true. If this is true the login interface
|
||||
// will display an additional field for this provider for the user
|
||||
// to fill in their MFA token.
|
||||
func (a authCrowd) SupportsMFA() bool { return false }
|
||||
|
|
17
auth_ldap.go
17
auth_ldap.go
|
@ -39,7 +39,7 @@ func (a authLDAP) AuthenticatorID() string { return "ldap" }
|
|||
// Configure loads the configuration for the Authenticator from the
|
||||
// global config.yaml file which is passed as a byte-slice.
|
||||
// If no configuration for the Authenticator is supplied the function
|
||||
// needs to return the errAuthenticatorUnconfigured
|
||||
// needs to return the errProviderUnconfigured
|
||||
func (a *authLDAP) Configure(yamlSource []byte) error {
|
||||
envelope := struct {
|
||||
Providers struct {
|
||||
|
@ -52,7 +52,7 @@ func (a *authLDAP) Configure(yamlSource []byte) error {
|
|||
}
|
||||
|
||||
if envelope.Providers.LDAP == nil {
|
||||
return errAuthenticatorUnconfigured
|
||||
return errProviderUnconfigured
|
||||
}
|
||||
|
||||
a.EnableBasicAuth = envelope.Providers.LDAP.EnableBasicAuth
|
||||
|
@ -141,7 +141,7 @@ func (a authLDAP) DetectUser(res http.ResponseWriter, r *http.Request) (string,
|
|||
// in order to use DetectUser for the next login.
|
||||
// If the user did not login correctly the errNoValidUserFound
|
||||
// needs to be returned
|
||||
func (a authLDAP) Login(res http.ResponseWriter, r *http.Request) error {
|
||||
func (a authLDAP) Login(res http.ResponseWriter, r *http.Request) (string, []mfaConfig, error) {
|
||||
username := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "username"}, "-"))
|
||||
password := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "password"}, "-"))
|
||||
|
||||
|
@ -152,14 +152,14 @@ func (a authLDAP) Login(res http.ResponseWriter, r *http.Request) error {
|
|||
)
|
||||
|
||||
if userDN, alias, err = a.checkLogin(username, password, a.UsernameAttribute); err != nil {
|
||||
return err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
sess, _ := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-"))
|
||||
sess.Options = mainCfg.GetSessionOpts()
|
||||
sess.Values["user"] = userDN
|
||||
sess.Values["alias"] = alias
|
||||
return sess.Save(r, res)
|
||||
return userDN, nil, sess.Save(r, res)
|
||||
}
|
||||
|
||||
// LoginFields needs to return the fields required for this login
|
||||
|
@ -328,3 +328,10 @@ func (a authLDAP) getUserGroups(userDN, alias string) ([]string, error) {
|
|||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
// SupportsMFA returns the MFA detection capabilities of the login
|
||||
// provider. If the provider can provide mfaConfig objects from its
|
||||
// configuration return true. If this is true the login interface
|
||||
// will display an additional field for this provider for the user
|
||||
// to fill in their MFA token.
|
||||
func (a authLDAP) SupportsMFA() bool { return false } // TODO: Implement
|
||||
|
|
|
@ -4,9 +4,10 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Luzifer/go_helpers/str"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/Luzifer/go_helpers/str"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -14,9 +15,10 @@ func init() {
|
|||
}
|
||||
|
||||
type authSimple struct {
|
||||
EnableBasicAuth bool `yaml:"enable_basic_auth"`
|
||||
Users map[string]string `yaml:"users"`
|
||||
Groups map[string][]string `yaml:"groups"`
|
||||
EnableBasicAuth bool `yaml:"enable_basic_auth"`
|
||||
Users map[string]string `yaml:"users"`
|
||||
Groups map[string][]string `yaml:"groups"`
|
||||
MFA map[string][]mfaConfig `yaml:"mfa"`
|
||||
}
|
||||
|
||||
// AuthenticatorID needs to return an unique string to identify
|
||||
|
@ -26,7 +28,7 @@ func (a authSimple) AuthenticatorID() string { return "simple" }
|
|||
// Configure loads the configuration for the Authenticator from the
|
||||
// global config.yaml file which is passed as a byte-slice.
|
||||
// If no configuration for the Authenticator is supplied the function
|
||||
// needs to return the errAuthenticatorUnconfigured
|
||||
// needs to return the errProviderUnconfigured
|
||||
func (a *authSimple) Configure(yamlSource []byte) error {
|
||||
envelope := struct {
|
||||
Providers struct {
|
||||
|
@ -39,12 +41,13 @@ func (a *authSimple) Configure(yamlSource []byte) error {
|
|||
}
|
||||
|
||||
if envelope.Providers.Simple == nil {
|
||||
return errAuthenticatorUnconfigured
|
||||
return errProviderUnconfigured
|
||||
}
|
||||
|
||||
a.EnableBasicAuth = envelope.Providers.Simple.EnableBasicAuth
|
||||
a.Users = envelope.Providers.Simple.Users
|
||||
a.Groups = envelope.Providers.Simple.Groups
|
||||
a.MFA = envelope.Providers.Simple.MFA
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -106,7 +109,7 @@ func (a authSimple) DetectUser(res http.ResponseWriter, r *http.Request) (string
|
|||
// in order to use DetectUser for the next login.
|
||||
// If the user did not login correctly the errNoValidUserFound
|
||||
// needs to be returned
|
||||
func (a authSimple) Login(res http.ResponseWriter, r *http.Request) error {
|
||||
func (a authSimple) Login(res http.ResponseWriter, r *http.Request) (string, []mfaConfig, error) {
|
||||
username := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "username"}, "-"))
|
||||
password := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "password"}, "-"))
|
||||
|
||||
|
@ -121,10 +124,10 @@ func (a authSimple) Login(res http.ResponseWriter, r *http.Request) error {
|
|||
sess, _ := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-"))
|
||||
sess.Options = mainCfg.GetSessionOpts()
|
||||
sess.Values["user"] = u
|
||||
return sess.Save(r, res)
|
||||
return u, a.MFA[u], sess.Save(r, res)
|
||||
}
|
||||
|
||||
return errNoValidUserFound
|
||||
return "", nil, errNoValidUserFound
|
||||
}
|
||||
|
||||
// LoginFields needs to return the fields required for this login
|
||||
|
@ -155,3 +158,10 @@ func (a authSimple) Logout(res http.ResponseWriter, r *http.Request) (err error)
|
|||
sess.Options.MaxAge = -1 // Instant delete
|
||||
return sess.Save(r, res)
|
||||
}
|
||||
|
||||
// SupportsMFA returns the MFA detection capabilities of the login
|
||||
// provider. If the provider can provide mfaConfig objects from its
|
||||
// configuration return true. If this is true the login interface
|
||||
// will display an additional field for this provider for the user
|
||||
// to fill in their MFA token.
|
||||
func (a authSimple) SupportsMFA() bool { return true }
|
||||
|
|
|
@ -25,7 +25,7 @@ func (a authToken) AuthenticatorID() string { return "token" }
|
|||
// Configure loads the configuration for the Authenticator from the
|
||||
// global config.yaml file which is passed as a byte-slice.
|
||||
// If no configuration for the Authenticator is supplied the function
|
||||
// needs to return the errAuthenticatorUnconfigured
|
||||
// needs to return the errProviderUnconfigured
|
||||
func (a *authToken) Configure(yamlSource []byte) error {
|
||||
envelope := struct {
|
||||
Providers struct {
|
||||
|
@ -38,7 +38,7 @@ func (a *authToken) Configure(yamlSource []byte) error {
|
|||
}
|
||||
|
||||
if envelope.Providers.Token == nil {
|
||||
return errAuthenticatorUnconfigured
|
||||
return errProviderUnconfigured
|
||||
}
|
||||
|
||||
a.Tokens = envelope.Providers.Token.Tokens
|
||||
|
@ -92,7 +92,9 @@ func (a authToken) DetectUser(res http.ResponseWriter, r *http.Request) (string,
|
|||
// in order to use DetectUser for the next login.
|
||||
// If the user did not login correctly the errNoValidUserFound
|
||||
// needs to be returned
|
||||
func (a authToken) Login(res http.ResponseWriter, r *http.Request) error { return errNoValidUserFound }
|
||||
func (a authToken) Login(res http.ResponseWriter, r *http.Request) (string, []mfaConfig, error) {
|
||||
return "", nil, errNoValidUserFound
|
||||
}
|
||||
|
||||
// LoginFields needs to return the fields required for this login
|
||||
// method. If no login using this method is possible the function
|
||||
|
@ -102,3 +104,10 @@ func (a authToken) LoginFields() []loginField { return nil }
|
|||
// Logout is called when the user visits the logout endpoint and
|
||||
// needs to destroy any persistent stored cookies
|
||||
func (a authToken) Logout(res http.ResponseWriter, r *http.Request) error { return nil }
|
||||
|
||||
// SupportsMFA returns the MFA detection capabilities of the login
|
||||
// provider. If the provider can provide mfaConfig objects from its
|
||||
// configuration return true. If this is true the login interface
|
||||
// will display an additional field for this provider for the user
|
||||
// to fill in their MFA token.
|
||||
func (a authToken) SupportsMFA() bool { return false }
|
||||
|
|
|
@ -28,7 +28,7 @@ func (a authYubikey) AuthenticatorID() string { return "yubikey" }
|
|||
// Configure loads the configuration for the Authenticator from the
|
||||
// global config.yaml file which is passed as a byte-slice.
|
||||
// If no configuration for the Authenticator is supplied the function
|
||||
// needs to return the errAuthenticatorUnconfigured
|
||||
// needs to return the errProviderUnconfigured
|
||||
func (a *authYubikey) Configure(yamlSource []byte) error {
|
||||
envelope := struct {
|
||||
Providers struct {
|
||||
|
@ -41,7 +41,7 @@ func (a *authYubikey) Configure(yamlSource []byte) error {
|
|||
}
|
||||
|
||||
if envelope.Providers.Yubikey == nil {
|
||||
return errAuthenticatorUnconfigured
|
||||
return errProviderUnconfigured
|
||||
}
|
||||
|
||||
a.ClientID = envelope.Providers.Yubikey.ClientID
|
||||
|
@ -89,34 +89,34 @@ func (a authYubikey) DetectUser(res http.ResponseWriter, r *http.Request) (strin
|
|||
// in order to use DetectUser for the next login.
|
||||
// If the user did not login correctly the errNoValidUserFound
|
||||
// needs to be returned
|
||||
func (a authYubikey) Login(res http.ResponseWriter, r *http.Request) error {
|
||||
func (a authYubikey) Login(res http.ResponseWriter, r *http.Request) (string, []mfaConfig, error) {
|
||||
keyInput := r.FormValue(strings.Join([]string{a.AuthenticatorID(), "key-input"}, "-"))
|
||||
|
||||
yubiAuth, err := yubigo.NewYubiAuth(a.ClientID, a.SecretKey)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
_, ok, err := yubiAuth.Verify(keyInput)
|
||||
if err != nil && !strings.Contains(err.Error(), "OTP has wrong length.") {
|
||||
return err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
// Not a valid authentication
|
||||
return errNoValidUserFound
|
||||
return "", nil, errNoValidUserFound
|
||||
}
|
||||
|
||||
user, ok := a.Devices[keyInput[:12]]
|
||||
if !ok {
|
||||
// We do not have a definition for that key
|
||||
return errNoValidUserFound
|
||||
return "", nil, errNoValidUserFound
|
||||
}
|
||||
|
||||
sess, _ := cookieStore.Get(r, strings.Join([]string{mainCfg.Cookie.Prefix, a.AuthenticatorID()}, "-"))
|
||||
sess.Options = mainCfg.GetSessionOpts()
|
||||
sess.Values["user"] = user
|
||||
return sess.Save(r, res)
|
||||
return user, nil, sess.Save(r, res)
|
||||
}
|
||||
|
||||
// LoginFields needs to return the fields required for this login
|
||||
|
@ -141,3 +141,10 @@ func (a authYubikey) Logout(res http.ResponseWriter, r *http.Request) (err error
|
|||
sess.Options.MaxAge = -1 // Instant delete
|
||||
return sess.Save(r, res)
|
||||
}
|
||||
|
||||
// SupportsMFA returns the MFA detection capabilities of the login
|
||||
// provider. If the provider can provide mfaConfig objects from its
|
||||
// configuration return true. If this is true the login interface
|
||||
// will display an additional field for this provider for the user
|
||||
// to fill in their MFA token.
|
||||
func (a authYubikey) SupportsMFA() bool { return false }
|
||||
|
|
19
config.yaml
19
config.yaml
|
@ -3,6 +3,7 @@
|
|||
login:
|
||||
title: "luzifer.io - Login"
|
||||
default_method: "simple"
|
||||
hide_mfa_field: false
|
||||
names:
|
||||
simple: "Username / Password"
|
||||
yubikey: "Yubikey"
|
||||
|
@ -36,6 +37,12 @@ acl:
|
|||
regexp: "^/api"
|
||||
allow: ["luzifer", "@admins"]
|
||||
|
||||
mfa:
|
||||
yubikey:
|
||||
# Get your client / secret from https://upgrade.yubico.com/getapikey/
|
||||
client_id: "12345"
|
||||
secret_key: "foobar"
|
||||
|
||||
providers:
|
||||
# Authentication against an Atlassian Crowd directory server
|
||||
# Supports: Users, Groups
|
||||
|
@ -74,7 +81,7 @@ providers:
|
|||
allow_insecure: false
|
||||
|
||||
# Authentication against embedded user database
|
||||
# Supports: Users, Groups
|
||||
# Supports: Users, Groups, MFA
|
||||
simple:
|
||||
enable_basic_auth: false
|
||||
|
||||
|
@ -86,6 +93,16 @@ providers:
|
|||
groups:
|
||||
admins: ["luzifer"]
|
||||
|
||||
# MFA configs: Username to configs mapping
|
||||
mfa:
|
||||
luzifer:
|
||||
- provider: google
|
||||
attributes:
|
||||
secret: MZXW6YTBOIFA
|
||||
- provider: yubikey
|
||||
attributes:
|
||||
device: ccccccfcvuul
|
||||
|
||||
# Authentication against embedded token directory
|
||||
# Supports: Users, Groups
|
||||
token:
|
||||
|
|
24
main.go
24
main.go
|
@ -36,6 +36,7 @@ type mainConfig struct {
|
|||
Login struct {
|
||||
Title string `yaml:"title"`
|
||||
DefaultMethod string `yaml:"default_method"`
|
||||
HideMFAField bool `yaml:"hide_mfa_field"`
|
||||
Names map[string]string `yaml:"names"`
|
||||
} `yaml:"login"`
|
||||
}
|
||||
|
@ -103,6 +104,10 @@ func loadConfiguration() error {
|
|||
return fmt.Errorf("Unable to configure authentication: %s", err)
|
||||
}
|
||||
|
||||
if err = initializeMFAProviders(yamlSource); err != nil {
|
||||
log.WithError(err).Fatal("Unable to configure MFA providers")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -176,11 +181,27 @@ func handleLoginRequest(res http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if r.Method == "POST" {
|
||||
err := loginUser(res, r)
|
||||
// Simple authentication
|
||||
user, mfaCfgs, err := loginUser(res, r)
|
||||
switch err {
|
||||
case errNoValidUserFound:
|
||||
http.Redirect(res, r, "/login?go="+url.QueryEscape(r.FormValue("go")), http.StatusFound)
|
||||
return
|
||||
case nil:
|
||||
// Don't handle for now, MFA validation comes first
|
||||
default:
|
||||
log.WithError(err).Error("Login failed with unexpected error")
|
||||
http.Redirect(res, r, "/login?go="+url.QueryEscape(r.FormValue("go")), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
// MFA validation against configs from login
|
||||
err = validateMFA(res, r, user, mfaCfgs)
|
||||
switch err {
|
||||
case errNoValidUserFound:
|
||||
auditFields["reason"] = "invalid credentials"
|
||||
mainCfg.AuditLog.Log(auditEventLoginFailure, r, auditFields)
|
||||
res.Header().Del("Set-Cookie") // Remove login cookie
|
||||
http.Redirect(res, r, "/login?go="+url.QueryEscape(r.FormValue("go")), http.StatusFound)
|
||||
return
|
||||
|
||||
|
@ -194,6 +215,7 @@ func handleLoginRequest(res http.ResponseWriter, r *http.Request) {
|
|||
auditFields["error"] = err.Error()
|
||||
mainCfg.AuditLog.Log(auditEventLoginFailure, r, auditFields)
|
||||
log.WithError(err).Error("Login failed with unexpected error")
|
||||
res.Header().Del("Set-Cookie") // Remove login cookie
|
||||
http.Redirect(res, r, "/login?go="+url.QueryEscape(r.FormValue("go")), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
|
115
mfa.go
Normal file
115
mfa.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const mfaLoginFieldName = "mfa-token"
|
||||
|
||||
var mfaLoginField = loginField{
|
||||
Label: "MFA Token",
|
||||
Name: mfaLoginFieldName,
|
||||
Placeholder: "(optional)",
|
||||
Type: "text",
|
||||
}
|
||||
|
||||
type mfaConfig struct {
|
||||
Provider string `yaml:"provider"`
|
||||
Attributes map[string]interface{} `yaml:"attributes"`
|
||||
}
|
||||
|
||||
func newMFAConfig(provider string, attrs map[string]interface{}) mfaConfig {
|
||||
return mfaConfig{Provider: provider, Attributes: attrs}
|
||||
}
|
||||
|
||||
func (m mfaConfig) AttributeString(key string) string {
|
||||
if v, ok := m.Attributes[key]; ok {
|
||||
if sv, ok := v.(string); ok {
|
||||
return sv
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
type mfaProvider interface {
|
||||
// ProviderID needs to return an unique string to identify
|
||||
// this special MFA provider
|
||||
ProviderID() (id string)
|
||||
|
||||
// Configure loads the configuration for the Authenticator from the
|
||||
// global config.yaml file which is passed as a byte-slice.
|
||||
// If no configuration for the Authenticator is supplied the function
|
||||
// needs to return the errProviderUnconfigured
|
||||
Configure(yamlSource []byte) (err error)
|
||||
|
||||
// ValidateMFA takes the user from the login cookie and performs a
|
||||
// validation against the provided MFA configuration for this user
|
||||
ValidateMFA(res http.ResponseWriter, r *http.Request, user string, mfaCfgs []mfaConfig) error
|
||||
}
|
||||
|
||||
var (
|
||||
mfaRegistry = []mfaProvider{}
|
||||
mfaRegistryMutex sync.RWMutex
|
||||
|
||||
activeMFAProviders = []mfaProvider{}
|
||||
)
|
||||
|
||||
func registerMFAProvider(m mfaProvider) {
|
||||
mfaRegistryMutex.Lock()
|
||||
defer mfaRegistryMutex.Unlock()
|
||||
|
||||
mfaRegistry = append(mfaRegistry, m)
|
||||
}
|
||||
|
||||
func initializeMFAProviders(yamlSource []byte) error {
|
||||
mfaRegistryMutex.Lock()
|
||||
defer mfaRegistryMutex.Unlock()
|
||||
|
||||
for _, m := range mfaRegistry {
|
||||
err := m.Configure(yamlSource)
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
activeMFAProviders = append(activeMFAProviders, m)
|
||||
log.WithFields(log.Fields{"mfa_provider": m.ProviderID()}).Debug("Activated MFA provider")
|
||||
case errProviderUnconfigured:
|
||||
log.WithFields(log.Fields{"mfa_provider": m.ProviderID()}).Debug("MFA provider unconfigured")
|
||||
// This is okay.
|
||||
default:
|
||||
return fmt.Errorf("MFA provider configuration caused an error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateMFA(res http.ResponseWriter, r *http.Request, user string, mfaCfgs []mfaConfig) error {
|
||||
if mfaCfgs == nil || len(mfaCfgs) == 0 {
|
||||
// User has no configured MFA devices, their MFA is automatically valid
|
||||
return nil
|
||||
}
|
||||
|
||||
mfaRegistryMutex.RLock()
|
||||
defer mfaRegistryMutex.RUnlock()
|
||||
|
||||
for _, m := range activeMFAProviders {
|
||||
err := m.ValidateMFA(res, r, user, mfaCfgs)
|
||||
switch err {
|
||||
case nil:
|
||||
// Validated successfully
|
||||
return nil
|
||||
case errNoValidUserFound:
|
||||
// This is fine for now
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// No method could verify the user
|
||||
return errNoValidUserFound
|
||||
}
|
63
mfa_google.go
Normal file
63
mfa_google.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerMFAProvider(&mfaGoogle{})
|
||||
}
|
||||
|
||||
type mfaGoogle struct{}
|
||||
|
||||
// ProviderID needs to return an unique string to identify
|
||||
// this special MFA provider
|
||||
func (m mfaGoogle) ProviderID() (id string) {
|
||||
return "google"
|
||||
}
|
||||
|
||||
// Configure loads the configuration for the Authenticator from the
|
||||
// global config.yaml file which is passed as a byte-slice.
|
||||
// If no configuration for the Authenticator is supplied the function
|
||||
// needs to return the errProviderUnconfigured
|
||||
func (m mfaGoogle) Configure(yamlSource []byte) (err error) { return nil }
|
||||
|
||||
// ValidateMFA takes the user from the login cookie and performs a
|
||||
// validation against the provided MFA configuration for this user
|
||||
func (m mfaGoogle) ValidateMFA(res http.ResponseWriter, r *http.Request, user string, mfaCfgs []mfaConfig) error {
|
||||
// Look for mfaConfigs with own provider name
|
||||
for _, c := range mfaCfgs {
|
||||
if c.Provider != m.ProviderID() {
|
||||
continue
|
||||
}
|
||||
|
||||
token, err := m.exec(c)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Generating the MFA token failed")
|
||||
}
|
||||
|
||||
for key, values := range r.Form {
|
||||
if strings.HasSuffix(key, mfaLoginFieldName) && values[0] == token {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Report this provider was not able to verify the MFA request
|
||||
return errNoValidUserFound
|
||||
}
|
||||
|
||||
func (m mfaGoogle) exec(c mfaConfig) (string, error) {
|
||||
secret := c.AttributeString("secret")
|
||||
|
||||
if n := len(secret) % 8; n != 0 {
|
||||
secret = secret + strings.Repeat("=", 8-n)
|
||||
}
|
||||
|
||||
return totp.GenerateCode(strings.ToUpper(secret), time.Now())
|
||||
}
|
87
mfa_yubikey.go
Normal file
87
mfa_yubikey.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/GeertJohan/yubigo"
|
||||
"github.com/pkg/errors"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerMFAProvider(&mfaYubikey{})
|
||||
}
|
||||
|
||||
type mfaYubikey struct {
|
||||
ClientID string `yaml:"client_id"`
|
||||
SecretKey string `yaml:"secret_key"`
|
||||
}
|
||||
|
||||
// ProviderID needs to return an unique string to identify
|
||||
// this special MFA provider
|
||||
func (m mfaYubikey) ProviderID() (id string) { return "yubikey" }
|
||||
|
||||
// Configure loads the configuration for the Authenticator from the
|
||||
// global config.yaml file which is passed as a byte-slice.
|
||||
// If no configuration for the Authenticator is supplied the function
|
||||
// needs to return the errProviderUnconfigured
|
||||
func (m *mfaYubikey) Configure(yamlSource []byte) (err error) {
|
||||
envelope := struct {
|
||||
MFA struct {
|
||||
Yubikey *mfaYubikey `yaml:"yubikey"`
|
||||
} `yaml:"mfa"`
|
||||
}{}
|
||||
|
||||
if err := yaml.Unmarshal(yamlSource, &envelope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if envelope.MFA.Yubikey == nil {
|
||||
return errProviderUnconfigured
|
||||
}
|
||||
|
||||
m.ClientID = envelope.MFA.Yubikey.ClientID
|
||||
m.SecretKey = envelope.MFA.Yubikey.SecretKey
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateMFA takes the user from the login cookie and performs a
|
||||
// validation against the provided MFA configuration for this user
|
||||
func (m mfaYubikey) ValidateMFA(res http.ResponseWriter, r *http.Request, user string, mfaCfgs []mfaConfig) error {
|
||||
var keyInput string
|
||||
|
||||
yubiAuth, err := yubigo.NewYubiAuth(m.ClientID, m.SecretKey)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Unable to create Yubikey client")
|
||||
}
|
||||
|
||||
for _, c := range mfaCfgs {
|
||||
if c.Provider != m.ProviderID() {
|
||||
continue
|
||||
}
|
||||
|
||||
for key, values := range r.Form {
|
||||
if strings.HasSuffix(key, mfaLoginFieldName) && strings.HasPrefix(values[0], c.AttributeString("device")) {
|
||||
keyInput = values[0]
|
||||
}
|
||||
}
|
||||
|
||||
if keyInput == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
_, ok, err := yubiAuth.Verify(keyInput)
|
||||
if err != nil && !strings.Contains(err.Error(), "OTP has wrong length.") {
|
||||
return errors.Wrap(err, "OTP verification failed")
|
||||
}
|
||||
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Not a valid authentication
|
||||
return errNoValidUserFound
|
||||
}
|
34
registry.go
34
registry.go
|
@ -17,7 +17,7 @@ type authenticator interface {
|
|||
// Configure loads the configuration for the Authenticator from the
|
||||
// global config.yaml file which is passed as a byte-slice.
|
||||
// If no configuration for the Authenticator is supplied the function
|
||||
// needs to return the errAuthenticatorUnconfigured
|
||||
// needs to return the errProviderUnconfigured
|
||||
Configure(yamlSource []byte) (err error)
|
||||
|
||||
// DetectUser is used to detect a user without a login form from
|
||||
|
@ -30,9 +30,12 @@ type authenticator interface {
|
|||
// to authenticate the user or throw an error. If the user has
|
||||
// successfully logged in the persistent cookie should be written
|
||||
// in order to use DetectUser for the next login.
|
||||
// With the login result an array of mfaConfig must be returned. In
|
||||
// case there is no MFA config or the provider does not support MFA
|
||||
// return nil.
|
||||
// If the user did not login correctly the errNoValidUserFound
|
||||
// needs to be returned
|
||||
Login(res http.ResponseWriter, r *http.Request) (err error)
|
||||
Login(res http.ResponseWriter, r *http.Request) (user string, mfaConfigs []mfaConfig, err error)
|
||||
|
||||
// LoginFields needs to return the fields required for this login
|
||||
// method. If no login using this method is possible the function
|
||||
|
@ -42,6 +45,13 @@ type authenticator interface {
|
|||
// Logout is called when the user visits the logout endpoint and
|
||||
// needs to destroy any persistent stored cookies
|
||||
Logout(res http.ResponseWriter, r *http.Request) (err error)
|
||||
|
||||
// SupportsMFA returns the MFA detection capabilities of the login
|
||||
// provider. If the provider can provide mfaConfig objects from its
|
||||
// configuration return true. If this is true the login interface
|
||||
// will display an additional field for this provider for the user
|
||||
// to fill in their MFA token.
|
||||
SupportsMFA() bool
|
||||
}
|
||||
|
||||
type loginField struct {
|
||||
|
@ -52,8 +62,8 @@ type loginField struct {
|
|||
}
|
||||
|
||||
var (
|
||||
errAuthenticatorUnconfigured = errors.New("No valid configuration found for this authenticator")
|
||||
errNoValidUserFound = errors.New("No valid users found")
|
||||
errProviderUnconfigured = errors.New("No valid configuration found for this provider")
|
||||
errNoValidUserFound = errors.New("No valid users found")
|
||||
|
||||
authenticatorRegistry = []authenticator{}
|
||||
authenticatorRegistryMutex sync.RWMutex
|
||||
|
@ -80,7 +90,7 @@ func initializeAuthenticators(yamlSource []byte) error {
|
|||
case nil:
|
||||
tmp = append(tmp, a)
|
||||
log.WithFields(log.Fields{"authenticator": a.AuthenticatorID()}).Debug("Activated authenticator")
|
||||
case errAuthenticatorUnconfigured:
|
||||
case errProviderUnconfigured:
|
||||
log.WithFields(log.Fields{"authenticator": a.AuthenticatorID()}).Debug("Authenticator unconfigured")
|
||||
// This is okay.
|
||||
default:
|
||||
|
@ -116,23 +126,23 @@ func detectUser(res http.ResponseWriter, r *http.Request) (string, []string, err
|
|||
return "", nil, errNoValidUserFound
|
||||
}
|
||||
|
||||
func loginUser(res http.ResponseWriter, r *http.Request) error {
|
||||
func loginUser(res http.ResponseWriter, r *http.Request) (string, []mfaConfig, error) {
|
||||
authenticatorRegistryMutex.RLock()
|
||||
defer authenticatorRegistryMutex.RUnlock()
|
||||
|
||||
for _, a := range activeAuthenticators {
|
||||
err := a.Login(res, r)
|
||||
user, mfaCfgs, err := a.Login(res, r)
|
||||
switch err {
|
||||
case nil:
|
||||
return nil
|
||||
return user, mfaCfgs, nil
|
||||
case errNoValidUserFound:
|
||||
// This is okay.
|
||||
default:
|
||||
return err
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return errNoValidUserFound
|
||||
return "", nil, errNoValidUserFound
|
||||
}
|
||||
|
||||
func logoutUser(res http.ResponseWriter, r *http.Request) error {
|
||||
|
@ -158,6 +168,10 @@ func getFrontendAuthenticators() map[string][]loginField {
|
|||
continue
|
||||
}
|
||||
output[a.AuthenticatorID()] = a.LoginFields()
|
||||
|
||||
if a.SupportsMFA() && !mainCfg.Login.HideMFAField {
|
||||
output[a.AuthenticatorID()] = append(output[a.AuthenticatorID()], mfaLoginField)
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
|
|
1
vendor/github.com/boombuler/barcode/.gitignore
generated
vendored
Normal file
1
vendor/github.com/boombuler/barcode/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.vscode/
|
21
vendor/github.com/boombuler/barcode/LICENSE
generated
vendored
Normal file
21
vendor/github.com/boombuler/barcode/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Florian Sundermann
|
||||
|
||||
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.
|
53
vendor/github.com/boombuler/barcode/README.md
generated
vendored
Normal file
53
vendor/github.com/boombuler/barcode/README.md
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
[![Join the chat at https://gitter.im/golang-barcode/Lobby](https://badges.gitter.im/golang-barcode/Lobby.svg)](https://gitter.im/golang-barcode/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
## Introduction ##
|
||||
|
||||
This is a package for GO which can be used to create different types of barcodes.
|
||||
|
||||
## Supported Barcode Types ##
|
||||
* 2 of 5
|
||||
* Aztec Code
|
||||
* Codabar
|
||||
* Code 128
|
||||
* Code 39
|
||||
* Code 93
|
||||
* Datamatrix
|
||||
* EAN 13
|
||||
* EAN 8
|
||||
* PDF 417
|
||||
* QR Code
|
||||
|
||||
## Example ##
|
||||
|
||||
This is a simple example on how to create a QR-Code and write it to a png-file
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/png"
|
||||
"os"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/qr"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create the barcode
|
||||
qrCode, _ := qr.Encode("Hello World", qr.M, qr.Auto)
|
||||
|
||||
// Scale the barcode to 200x200 pixels
|
||||
qrCode, _ = barcode.Scale(qrCode, 200, 200)
|
||||
|
||||
// create the output file
|
||||
file, _ := os.Create("qrcode.png")
|
||||
defer file.Close()
|
||||
|
||||
// encode the barcode as png
|
||||
png.Encode(file, qrCode)
|
||||
}
|
||||
```
|
||||
|
||||
## Documentation ##
|
||||
See [GoDoc](https://godoc.org/github.com/boombuler/barcode)
|
||||
|
||||
To create a barcode use the Encode function from one of the subpackages.
|
94
vendor/github.com/boombuler/barcode/aztec/aztec_test.go
generated
vendored
Normal file
94
vendor/github.com/boombuler/barcode/aztec/aztec_test.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
package aztec
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func encodeTest(t *testing.T, data, wanted string) {
|
||||
result, err := Encode([]byte(data), DEFAULT_EC_PERCENT, DEFAULT_LAYERS)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
ac, ok := result.(*aztecCode)
|
||||
if !ok {
|
||||
t.Error("returned barcode is no aztec code...")
|
||||
} else if draw := ac.string(); draw != wanted {
|
||||
t.Errorf("Invalid Barcode returned:\n%s", draw)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Encode1(t *testing.T) {
|
||||
encodeTest(t, "This is an example Aztec symbol for Wikipedia.",
|
||||
"X X X X X X X X \n"+
|
||||
"X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X \n"+
|
||||
" X X X \n"+
|
||||
" X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X \n")
|
||||
}
|
||||
|
||||
func Test_Encode2(t *testing.T) {
|
||||
encodeTest(t, "Aztec Code is a public domain 2D matrix barcode symbology"+
|
||||
" of nominally square symbols built on a square grid with a "+
|
||||
"distinctive square bullseye pattern at their center.",
|
||||
" X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X X X X X X X X X \n"+
|
||||
" X X X X X X X X X X X X X X X X \n"+
|
||||
"X X X X X X X X X X X X X \n")
|
||||
}
|
62
vendor/github.com/boombuler/barcode/aztec/azteccode.go
generated
vendored
Normal file
62
vendor/github.com/boombuler/barcode/aztec/azteccode.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
package aztec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type aztecCode struct {
|
||||
*utils.BitList
|
||||
size int
|
||||
content []byte
|
||||
}
|
||||
|
||||
func newAztecCode(size int) *aztecCode {
|
||||
return &aztecCode{utils.NewBitList(size * size), size, nil}
|
||||
}
|
||||
|
||||
func (c *aztecCode) Content() string {
|
||||
return string(c.content)
|
||||
}
|
||||
|
||||
func (c *aztecCode) Metadata() barcode.Metadata {
|
||||
return barcode.Metadata{barcode.TypeAztec, 2}
|
||||
}
|
||||
|
||||
func (c *aztecCode) ColorModel() color.Model {
|
||||
return color.Gray16Model
|
||||
}
|
||||
|
||||
func (c *aztecCode) Bounds() image.Rectangle {
|
||||
return image.Rect(0, 0, c.size, c.size)
|
||||
}
|
||||
|
||||
func (c *aztecCode) At(x, y int) color.Color {
|
||||
if c.GetBit(x*c.size + y) {
|
||||
return color.Black
|
||||
}
|
||||
return color.White
|
||||
}
|
||||
|
||||
func (c *aztecCode) set(x, y int) {
|
||||
c.SetBit(x*c.size+y, true)
|
||||
}
|
||||
|
||||
func (c *aztecCode) string() string {
|
||||
buf := new(bytes.Buffer)
|
||||
for y := 0; y < c.size; y++ {
|
||||
for x := 0; x < c.size; x++ {
|
||||
if c.GetBit(x*c.size + y) {
|
||||
buf.WriteString("X ")
|
||||
} else {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
}
|
||||
buf.WriteRune('\n')
|
||||
}
|
||||
return buf.String()
|
||||
}
|
268
vendor/github.com/boombuler/barcode/aztec/encoder.go
generated
vendored
Normal file
268
vendor/github.com/boombuler/barcode/aztec/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,268 @@
|
|||
// Package aztec can create Aztec Code barcodes
|
||||
package aztec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_EC_PERCENT = 33
|
||||
DEFAULT_LAYERS = 0
|
||||
max_nb_bits = 32
|
||||
max_nb_bits_compact = 4
|
||||
)
|
||||
|
||||
var (
|
||||
word_size = []int{
|
||||
4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||
}
|
||||
)
|
||||
|
||||
func totalBitsInLayer(layers int, compact bool) int {
|
||||
tmp := 112
|
||||
if compact {
|
||||
tmp = 88
|
||||
}
|
||||
return (tmp + 16*layers) * layers
|
||||
}
|
||||
|
||||
func stuffBits(bits *utils.BitList, wordSize int) *utils.BitList {
|
||||
out := new(utils.BitList)
|
||||
n := bits.Len()
|
||||
mask := (1 << uint(wordSize)) - 2
|
||||
for i := 0; i < n; i += wordSize {
|
||||
word := 0
|
||||
for j := 0; j < wordSize; j++ {
|
||||
if i+j >= n || bits.GetBit(i+j) {
|
||||
word |= 1 << uint(wordSize-1-j)
|
||||
}
|
||||
}
|
||||
if (word & mask) == mask {
|
||||
out.AddBits(word&mask, byte(wordSize))
|
||||
i--
|
||||
} else if (word & mask) == 0 {
|
||||
out.AddBits(word|1, byte(wordSize))
|
||||
i--
|
||||
} else {
|
||||
out.AddBits(word, byte(wordSize))
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func generateModeMessage(compact bool, layers, messageSizeInWords int) *utils.BitList {
|
||||
modeMessage := new(utils.BitList)
|
||||
if compact {
|
||||
modeMessage.AddBits(layers-1, 2)
|
||||
modeMessage.AddBits(messageSizeInWords-1, 6)
|
||||
modeMessage = generateCheckWords(modeMessage, 28, 4)
|
||||
} else {
|
||||
modeMessage.AddBits(layers-1, 5)
|
||||
modeMessage.AddBits(messageSizeInWords-1, 11)
|
||||
modeMessage = generateCheckWords(modeMessage, 40, 4)
|
||||
}
|
||||
return modeMessage
|
||||
}
|
||||
|
||||
func drawModeMessage(matrix *aztecCode, compact bool, matrixSize int, modeMessage *utils.BitList) {
|
||||
center := matrixSize / 2
|
||||
if compact {
|
||||
for i := 0; i < 7; i++ {
|
||||
offset := center - 3 + i
|
||||
if modeMessage.GetBit(i) {
|
||||
matrix.set(offset, center-5)
|
||||
}
|
||||
if modeMessage.GetBit(i + 7) {
|
||||
matrix.set(center+5, offset)
|
||||
}
|
||||
if modeMessage.GetBit(20 - i) {
|
||||
matrix.set(offset, center+5)
|
||||
}
|
||||
if modeMessage.GetBit(27 - i) {
|
||||
matrix.set(center-5, offset)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < 10; i++ {
|
||||
offset := center - 5 + i + i/5
|
||||
if modeMessage.GetBit(i) {
|
||||
matrix.set(offset, center-7)
|
||||
}
|
||||
if modeMessage.GetBit(i + 10) {
|
||||
matrix.set(center+7, offset)
|
||||
}
|
||||
if modeMessage.GetBit(29 - i) {
|
||||
matrix.set(offset, center+7)
|
||||
}
|
||||
if modeMessage.GetBit(39 - i) {
|
||||
matrix.set(center-7, offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func drawBullsEye(matrix *aztecCode, center, size int) {
|
||||
for i := 0; i < size; i += 2 {
|
||||
for j := center - i; j <= center+i; j++ {
|
||||
matrix.set(j, center-i)
|
||||
matrix.set(j, center+i)
|
||||
matrix.set(center-i, j)
|
||||
matrix.set(center+i, j)
|
||||
}
|
||||
}
|
||||
matrix.set(center-size, center-size)
|
||||
matrix.set(center-size+1, center-size)
|
||||
matrix.set(center-size, center-size+1)
|
||||
matrix.set(center+size, center-size)
|
||||
matrix.set(center+size, center-size+1)
|
||||
matrix.set(center+size, center+size-1)
|
||||
}
|
||||
|
||||
// Encode returns an aztec barcode with the given content
|
||||
func Encode(data []byte, minECCPercent int, userSpecifiedLayers int) (barcode.Barcode, error) {
|
||||
bits := highlevelEncode(data)
|
||||
eccBits := ((bits.Len() * minECCPercent) / 100) + 11
|
||||
totalSizeBits := bits.Len() + eccBits
|
||||
var layers, TotalBitsInLayer, wordSize int
|
||||
var compact bool
|
||||
var stuffedBits *utils.BitList
|
||||
if userSpecifiedLayers != DEFAULT_LAYERS {
|
||||
compact = userSpecifiedLayers < 0
|
||||
if compact {
|
||||
layers = -userSpecifiedLayers
|
||||
} else {
|
||||
layers = userSpecifiedLayers
|
||||
}
|
||||
if (compact && layers > max_nb_bits_compact) || (!compact && layers > max_nb_bits) {
|
||||
return nil, fmt.Errorf("Illegal value %d for layers", userSpecifiedLayers)
|
||||
}
|
||||
TotalBitsInLayer = totalBitsInLayer(layers, compact)
|
||||
wordSize = word_size[layers]
|
||||
usableBitsInLayers := TotalBitsInLayer - (TotalBitsInLayer % wordSize)
|
||||
stuffedBits = stuffBits(bits, wordSize)
|
||||
if stuffedBits.Len()+eccBits > usableBitsInLayers {
|
||||
return nil, fmt.Errorf("Data to large for user specified layer")
|
||||
}
|
||||
if compact && stuffedBits.Len() > wordSize*64 {
|
||||
return nil, fmt.Errorf("Data to large for user specified layer")
|
||||
}
|
||||
} else {
|
||||
wordSize = 0
|
||||
stuffedBits = nil
|
||||
// We look at the possible table sizes in the order Compact1, Compact2, Compact3,
|
||||
// Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
|
||||
// is the same size, but has more data.
|
||||
for i := 0; ; i++ {
|
||||
if i > max_nb_bits {
|
||||
return nil, fmt.Errorf("Data too large for an aztec code")
|
||||
}
|
||||
compact = i <= 3
|
||||
layers = i
|
||||
if compact {
|
||||
layers = i + 1
|
||||
}
|
||||
TotalBitsInLayer = totalBitsInLayer(layers, compact)
|
||||
if totalSizeBits > TotalBitsInLayer {
|
||||
continue
|
||||
}
|
||||
// [Re]stuff the bits if this is the first opportunity, or if the
|
||||
// wordSize has changed
|
||||
if wordSize != word_size[layers] {
|
||||
wordSize = word_size[layers]
|
||||
stuffedBits = stuffBits(bits, wordSize)
|
||||
}
|
||||
usableBitsInLayers := TotalBitsInLayer - (TotalBitsInLayer % wordSize)
|
||||
if compact && stuffedBits.Len() > wordSize*64 {
|
||||
// Compact format only allows 64 data words, though C4 can hold more words than that
|
||||
continue
|
||||
}
|
||||
if stuffedBits.Len()+eccBits <= usableBitsInLayers {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
messageBits := generateCheckWords(stuffedBits, TotalBitsInLayer, wordSize)
|
||||
messageSizeInWords := stuffedBits.Len() / wordSize
|
||||
modeMessage := generateModeMessage(compact, layers, messageSizeInWords)
|
||||
|
||||
// allocate symbol
|
||||
var baseMatrixSize int
|
||||
if compact {
|
||||
baseMatrixSize = 11 + layers*4
|
||||
} else {
|
||||
baseMatrixSize = 14 + layers*4
|
||||
}
|
||||
alignmentMap := make([]int, baseMatrixSize)
|
||||
var matrixSize int
|
||||
|
||||
if compact {
|
||||
// no alignment marks in compact mode, alignmentMap is a no-op
|
||||
matrixSize = baseMatrixSize
|
||||
for i := 0; i < len(alignmentMap); i++ {
|
||||
alignmentMap[i] = i
|
||||
}
|
||||
} else {
|
||||
matrixSize = baseMatrixSize + 1 + 2*((baseMatrixSize/2-1)/15)
|
||||
origCenter := baseMatrixSize / 2
|
||||
center := matrixSize / 2
|
||||
for i := 0; i < origCenter; i++ {
|
||||
newOffset := i + i/15
|
||||
alignmentMap[origCenter-i-1] = center - newOffset - 1
|
||||
alignmentMap[origCenter+i] = center + newOffset + 1
|
||||
}
|
||||
}
|
||||
code := newAztecCode(matrixSize)
|
||||
code.content = data
|
||||
|
||||
// draw data bits
|
||||
for i, rowOffset := 0, 0; i < layers; i++ {
|
||||
rowSize := (layers - i) * 4
|
||||
if compact {
|
||||
rowSize += 9
|
||||
} else {
|
||||
rowSize += 12
|
||||
}
|
||||
|
||||
for j := 0; j < rowSize; j++ {
|
||||
columnOffset := j * 2
|
||||
for k := 0; k < 2; k++ {
|
||||
if messageBits.GetBit(rowOffset + columnOffset + k) {
|
||||
code.set(alignmentMap[i*2+k], alignmentMap[i*2+j])
|
||||
}
|
||||
if messageBits.GetBit(rowOffset + rowSize*2 + columnOffset + k) {
|
||||
code.set(alignmentMap[i*2+j], alignmentMap[baseMatrixSize-1-i*2-k])
|
||||
}
|
||||
if messageBits.GetBit(rowOffset + rowSize*4 + columnOffset + k) {
|
||||
code.set(alignmentMap[baseMatrixSize-1-i*2-k], alignmentMap[baseMatrixSize-1-i*2-j])
|
||||
}
|
||||
if messageBits.GetBit(rowOffset + rowSize*6 + columnOffset + k) {
|
||||
code.set(alignmentMap[baseMatrixSize-1-i*2-j], alignmentMap[i*2+k])
|
||||
}
|
||||
}
|
||||
}
|
||||
rowOffset += rowSize * 8
|
||||
}
|
||||
|
||||
// draw mode message
|
||||
drawModeMessage(code, compact, matrixSize, modeMessage)
|
||||
|
||||
// draw alignment marks
|
||||
if compact {
|
||||
drawBullsEye(code, matrixSize/2, 5)
|
||||
} else {
|
||||
drawBullsEye(code, matrixSize/2, 7)
|
||||
for i, j := 0, 0; i < baseMatrixSize/2-1; i, j = i+15, j+16 {
|
||||
for k := (matrixSize / 2) & 1; k < matrixSize; k += 2 {
|
||||
code.set(matrixSize/2-j, k)
|
||||
code.set(matrixSize/2+j, k)
|
||||
code.set(k, matrixSize/2-j)
|
||||
code.set(k, matrixSize/2+j)
|
||||
}
|
||||
}
|
||||
}
|
||||
return code, nil
|
||||
}
|
58
vendor/github.com/boombuler/barcode/aztec/encoder_test.go
generated
vendored
Normal file
58
vendor/github.com/boombuler/barcode/aztec/encoder_test.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
package aztec
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
func Test_StuffBits(t *testing.T) {
|
||||
testStuffBits := func(wordSize int, bits string, expected string) {
|
||||
bl := new(utils.BitList)
|
||||
for _, r := range bits {
|
||||
if r == 'X' {
|
||||
bl.AddBit(true)
|
||||
} else if r == '.' {
|
||||
bl.AddBit(false)
|
||||
}
|
||||
}
|
||||
stuffed := stuffBits(bl, wordSize)
|
||||
expectedBits := strings.Replace(expected, " ", "", -1)
|
||||
result := bitStr(stuffed)
|
||||
|
||||
if result != expectedBits {
|
||||
t.Errorf("stuffBits failed for %q\nGot: %q", bits, result)
|
||||
}
|
||||
}
|
||||
|
||||
testStuffBits(5, ".X.X. X.X.X .X.X.",
|
||||
".X.X. X.X.X .X.X.")
|
||||
testStuffBits(5, ".X.X. ..... .X.X",
|
||||
".X.X. ....X ..X.X")
|
||||
testStuffBits(3, "XX. ... ... ..X XXX .X. ..",
|
||||
"XX. ..X ..X ..X ..X .XX XX. .X. ..X")
|
||||
testStuffBits(6, ".X.X.. ...... ..X.XX",
|
||||
".X.X.. .....X. ..X.XX XXXX.")
|
||||
testStuffBits(6, ".X.X.. ...... ...... ..X.X.",
|
||||
".X.X.. .....X .....X ....X. X.XXXX")
|
||||
testStuffBits(6, ".X.X.. XXXXXX ...... ..X.XX",
|
||||
".X.X.. XXXXX. X..... ...X.X XXXXX.")
|
||||
testStuffBits(6,
|
||||
"...... ..XXXX X..XX. .X.... .X.X.X .....X .X.... ...X.X .....X ....XX ..X... ....X. X..XXX X.XX.X",
|
||||
".....X ...XXX XX..XX ..X... ..X.X. X..... X.X... ....X. X..... X....X X..X.. .....X X.X..X XXX.XX .XXXXX")
|
||||
}
|
||||
|
||||
func Test_ModeMessage(t *testing.T) {
|
||||
testModeMessage := func(compact bool, layers, words int, expected string) {
|
||||
result := bitStr(generateModeMessage(compact, layers, words))
|
||||
expectedBits := strings.Replace(expected, " ", "", -1)
|
||||
if result != expectedBits {
|
||||
t.Errorf("generateModeMessage(%v, %d, %d) failed.\nGot:%s", compact, layers, words, result)
|
||||
}
|
||||
}
|
||||
testModeMessage(true, 2, 29, ".X .XXX.. ...X XX.. ..X .XX. .XX.X")
|
||||
testModeMessage(true, 4, 64, "XX XXXXXX .X.. ...X ..XX .X.. XX..")
|
||||
testModeMessage(false, 21, 660, "X.X.. .X.X..X..XX .XXX ..X.. .XXX. .X... ..XXX")
|
||||
testModeMessage(false, 32, 4096, "XXXXX XXXXXXXXXXX X.X. ..... XXX.X ..X.. X.XXX")
|
||||
}
|
61
vendor/github.com/boombuler/barcode/aztec/errorcorrection.go
generated
vendored
Normal file
61
vendor/github.com/boombuler/barcode/aztec/errorcorrection.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
package aztec
|
||||
|
||||
import (
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
func bitsToWords(stuffedBits *utils.BitList, wordSize int, wordCount int) []int {
|
||||
message := make([]int, wordCount)
|
||||
|
||||
for i := 0; i < wordCount; i++ {
|
||||
value := 0
|
||||
for j := 0; j < wordSize; j++ {
|
||||
if stuffedBits.GetBit(i*wordSize + j) {
|
||||
value |= (1 << uint(wordSize-j-1))
|
||||
}
|
||||
}
|
||||
message[i] = value
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
func generateCheckWords(bits *utils.BitList, totalBits, wordSize int) *utils.BitList {
|
||||
rs := utils.NewReedSolomonEncoder(getGF(wordSize))
|
||||
|
||||
// bits is guaranteed to be a multiple of the wordSize, so no padding needed
|
||||
messageWordCount := bits.Len() / wordSize
|
||||
totalWordCount := totalBits / wordSize
|
||||
eccWordCount := totalWordCount - messageWordCount
|
||||
|
||||
messageWords := bitsToWords(bits, wordSize, messageWordCount)
|
||||
eccWords := rs.Encode(messageWords, eccWordCount)
|
||||
startPad := totalBits % wordSize
|
||||
|
||||
messageBits := new(utils.BitList)
|
||||
messageBits.AddBits(0, byte(startPad))
|
||||
|
||||
for _, messageWord := range messageWords {
|
||||
messageBits.AddBits(messageWord, byte(wordSize))
|
||||
}
|
||||
for _, eccWord := range eccWords {
|
||||
messageBits.AddBits(eccWord, byte(wordSize))
|
||||
}
|
||||
return messageBits
|
||||
}
|
||||
|
||||
func getGF(wordSize int) *utils.GaloisField {
|
||||
switch wordSize {
|
||||
case 4:
|
||||
return utils.NewGaloisField(0x13, 16, 1)
|
||||
case 6:
|
||||
return utils.NewGaloisField(0x43, 64, 1)
|
||||
case 8:
|
||||
return utils.NewGaloisField(0x012D, 256, 1)
|
||||
case 10:
|
||||
return utils.NewGaloisField(0x409, 1024, 1)
|
||||
case 12:
|
||||
return utils.NewGaloisField(0x1069, 4096, 1)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
171
vendor/github.com/boombuler/barcode/aztec/highlevel.go
generated
vendored
Normal file
171
vendor/github.com/boombuler/barcode/aztec/highlevel.go
generated
vendored
Normal file
|
@ -0,0 +1,171 @@
|
|||
package aztec
|
||||
|
||||
import (
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
func highlevelEncode(data []byte) *utils.BitList {
|
||||
states := stateSlice{initialState}
|
||||
|
||||
for index := 0; index < len(data); index++ {
|
||||
pairCode := 0
|
||||
nextChar := byte(0)
|
||||
if index+1 < len(data) {
|
||||
nextChar = data[index+1]
|
||||
}
|
||||
|
||||
switch cur := data[index]; {
|
||||
case cur == '\r' && nextChar == '\n':
|
||||
pairCode = 2
|
||||
case cur == '.' && nextChar == ' ':
|
||||
pairCode = 3
|
||||
case cur == ',' && nextChar == ' ':
|
||||
pairCode = 4
|
||||
case cur == ':' && nextChar == ' ':
|
||||
pairCode = 5
|
||||
}
|
||||
if pairCode > 0 {
|
||||
// We have one of the four special PUNCT pairs. Treat them specially.
|
||||
// Get a new set of states for the two new characters.
|
||||
states = updateStateListForPair(states, data, index, pairCode)
|
||||
index++
|
||||
} else {
|
||||
// Get a new set of states for the new character.
|
||||
states = updateStateListForChar(states, data, index)
|
||||
}
|
||||
}
|
||||
minBitCnt := int((^uint(0)) >> 1)
|
||||
var result *state = nil
|
||||
for _, s := range states {
|
||||
if s.bitCount < minBitCnt {
|
||||
minBitCnt = s.bitCount
|
||||
result = s
|
||||
}
|
||||
}
|
||||
if result != nil {
|
||||
return result.toBitList(data)
|
||||
} else {
|
||||
return new(utils.BitList)
|
||||
}
|
||||
}
|
||||
|
||||
func simplifyStates(states stateSlice) stateSlice {
|
||||
var result stateSlice = nil
|
||||
for _, newState := range states {
|
||||
add := true
|
||||
var newResult stateSlice = nil
|
||||
|
||||
for _, oldState := range result {
|
||||
if add && oldState.isBetterThanOrEqualTo(newState) {
|
||||
add = false
|
||||
}
|
||||
if !(add && newState.isBetterThanOrEqualTo(oldState)) {
|
||||
newResult = append(newResult, oldState)
|
||||
}
|
||||
}
|
||||
|
||||
if add {
|
||||
result = append(newResult, newState)
|
||||
} else {
|
||||
result = newResult
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// We update a set of states for a new character by updating each state
|
||||
// for the new character, merging the results, and then removing the
|
||||
// non-optimal states.
|
||||
func updateStateListForChar(states stateSlice, data []byte, index int) stateSlice {
|
||||
var result stateSlice = nil
|
||||
for _, s := range states {
|
||||
if r := updateStateForChar(s, data, index); len(r) > 0 {
|
||||
result = append(result, r...)
|
||||
}
|
||||
}
|
||||
return simplifyStates(result)
|
||||
}
|
||||
|
||||
// Return a set of states that represent the possible ways of updating this
|
||||
// state for the next character. The resulting set of states are added to
|
||||
// the "result" list.
|
||||
func updateStateForChar(s *state, data []byte, index int) stateSlice {
|
||||
var result stateSlice = nil
|
||||
ch := data[index]
|
||||
charInCurrentTable := charMap[s.mode][ch] > 0
|
||||
|
||||
var stateNoBinary *state = nil
|
||||
for mode := mode_upper; mode <= mode_punct; mode++ {
|
||||
charInMode := charMap[mode][ch]
|
||||
if charInMode > 0 {
|
||||
if stateNoBinary == nil {
|
||||
// Only create stateNoBinary the first time it's required.
|
||||
stateNoBinary = s.endBinaryShift(index)
|
||||
}
|
||||
// Try generating the character by latching to its mode
|
||||
if !charInCurrentTable || mode == s.mode || mode == mode_digit {
|
||||
// If the character is in the current table, we don't want to latch to
|
||||
// any other mode except possibly digit (which uses only 4 bits). Any
|
||||
// other latch would be equally successful *after* this character, and
|
||||
// so wouldn't save any bits.
|
||||
res := stateNoBinary.latchAndAppend(mode, charInMode)
|
||||
result = append(result, res)
|
||||
}
|
||||
// Try generating the character by switching to its mode.
|
||||
if _, ok := shiftTable[s.mode][mode]; !charInCurrentTable && ok {
|
||||
// It never makes sense to temporarily shift to another mode if the
|
||||
// character exists in the current mode. That can never save bits.
|
||||
res := stateNoBinary.shiftAndAppend(mode, charInMode)
|
||||
result = append(result, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.bShiftByteCount > 0 || charMap[s.mode][ch] == 0 {
|
||||
// It's never worthwhile to go into binary shift mode if you're not already
|
||||
// in binary shift mode, and the character exists in your current mode.
|
||||
// That can never save bits over just outputting the char in the current mode.
|
||||
res := s.addBinaryShiftChar(index)
|
||||
result = append(result, res)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// We update a set of states for a new character by updating each state
|
||||
// for the new character, merging the results, and then removing the
|
||||
// non-optimal states.
|
||||
func updateStateListForPair(states stateSlice, data []byte, index int, pairCode int) stateSlice {
|
||||
var result stateSlice = nil
|
||||
for _, s := range states {
|
||||
if r := updateStateForPair(s, data, index, pairCode); len(r) > 0 {
|
||||
result = append(result, r...)
|
||||
}
|
||||
}
|
||||
return simplifyStates(result)
|
||||
}
|
||||
|
||||
func updateStateForPair(s *state, data []byte, index int, pairCode int) stateSlice {
|
||||
var result stateSlice
|
||||
stateNoBinary := s.endBinaryShift(index)
|
||||
// Possibility 1. Latch to MODE_PUNCT, and then append this code
|
||||
result = append(result, stateNoBinary.latchAndAppend(mode_punct, pairCode))
|
||||
if s.mode != mode_punct {
|
||||
// Possibility 2. Shift to MODE_PUNCT, and then append this code.
|
||||
// Every state except MODE_PUNCT (handled above) can shift
|
||||
result = append(result, stateNoBinary.shiftAndAppend(mode_punct, pairCode))
|
||||
}
|
||||
if pairCode == 3 || pairCode == 4 {
|
||||
// both characters are in DIGITS. Sometimes better to just add two digits
|
||||
digitState := stateNoBinary.
|
||||
latchAndAppend(mode_digit, 16-pairCode). // period or comma in DIGIT
|
||||
latchAndAppend(mode_digit, 1) // space in DIGIT
|
||||
result = append(result, digitState)
|
||||
}
|
||||
if s.bShiftByteCount > 0 {
|
||||
// It only makes sense to do the characters as binary if we're already
|
||||
// in binary mode.
|
||||
result = append(result, s.addBinaryShiftChar(index).addBinaryShiftChar(index+1))
|
||||
}
|
||||
return result
|
||||
}
|
132
vendor/github.com/boombuler/barcode/aztec/highlevel_test.go
generated
vendored
Normal file
132
vendor/github.com/boombuler/barcode/aztec/highlevel_test.go
generated
vendored
Normal file
|
@ -0,0 +1,132 @@
|
|||
package aztec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
func bitStr(bl *utils.BitList) string {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
for i := 0; i < bl.Len(); i++ {
|
||||
if bl.GetBit(i) {
|
||||
buf.WriteRune('X')
|
||||
} else {
|
||||
buf.WriteRune('.')
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func testHighLevelEncodeString(t *testing.T, s, expectedBits string) {
|
||||
bits := highlevelEncode([]byte(s))
|
||||
result := bitStr(bits)
|
||||
expectedBits = strings.Replace(expectedBits, " ", "", -1)
|
||||
|
||||
if result != expectedBits {
|
||||
t.Errorf("invalid result for highlevelEncode(%q). Got:\n%s", s, result)
|
||||
}
|
||||
}
|
||||
func testHighLevelEncodeStringCnt(t *testing.T, s string, expectedBitCnt int) {
|
||||
bits := highlevelEncode([]byte(s))
|
||||
|
||||
if bits.Len() != expectedBitCnt {
|
||||
t.Errorf("invalid result for highlevelEncode(%q). Got %d, expected %d bits", s, bits.Len(), expectedBitCnt)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_HighLevelEncode(t *testing.T) {
|
||||
testHighLevelEncodeString(t, "A. b.",
|
||||
// 'A' P/S '. ' L/L b D/L '.'
|
||||
"...X. ..... ...XX XXX.. ...XX XXXX. XX.X")
|
||||
testHighLevelEncodeString(t, "Lorem ipsum.",
|
||||
// 'L' L/L 'o' 'r' 'e' 'm' ' ' 'i' 'p' 's' 'u' 'm' D/L '.'
|
||||
".XX.X XXX.. X.... X..XX ..XX. .XXX. ....X .X.X. X...X X.X.. X.XX. .XXX. XXXX. XX.X")
|
||||
testHighLevelEncodeString(t, "Lo. Test 123.",
|
||||
// 'L' L/L 'o' P/S '. ' U/S 'T' 'e' 's' 't' D/L ' ' '1' '2' '3' '.'
|
||||
".XX.X XXX.. X.... ..... ...XX XXX.. X.X.X ..XX. X.X.. X.X.X XXXX. ...X ..XX .X.. .X.X XX.X")
|
||||
testHighLevelEncodeString(t, "Lo...x",
|
||||
// 'L' L/L 'o' D/L '.' '.' '.' U/L L/L 'x'
|
||||
".XX.X XXX.. X.... XXXX. XX.X XX.X XX.X XXX. XXX.. XX..X")
|
||||
testHighLevelEncodeString(t, ". x://abc/.",
|
||||
//P/S '. ' L/L 'x' P/S ':' P/S '/' P/S '/' 'a' 'b' 'c' P/S '/' D/L '.'
|
||||
"..... ...XX XXX.. XX..X ..... X.X.X ..... X.X.. ..... X.X.. ...X. ...XX ..X.. ..... X.X.. XXXX. XX.X")
|
||||
// Uses Binary/Shift rather than Lower/Shift to save two bits.
|
||||
testHighLevelEncodeString(t, "ABCdEFG",
|
||||
//'A' 'B' 'C' B/S =1 'd' 'E' 'F' 'G'
|
||||
"...X. ...XX ..X.. XXXXX ....X .XX..X.. ..XX. ..XXX .X...")
|
||||
|
||||
testHighLevelEncodeStringCnt(t,
|
||||
// Found on an airline boarding pass. Several stretches of Binary shift are
|
||||
// necessary to keep the bitcount so low.
|
||||
"09 UAG ^160MEUCIQC0sYS/HpKxnBELR1uB85R20OoqqwFGa0q2uEi"+
|
||||
"Ygh6utAIgLl1aBVM4EOTQtMQQYH9M2Z3Dp4qnA/fwWuQ+M8L3V8U=",
|
||||
823)
|
||||
}
|
||||
|
||||
func Test_HighLevelEncodeBinary(t *testing.T) {
|
||||
// binary short form single byte
|
||||
testHighLevelEncodeString(t, "N\u0000N",
|
||||
// 'N' B/S =1 '\0' N
|
||||
".XXXX XXXXX ....X ........ .XXXX") // Encode "N" in UPPER
|
||||
|
||||
testHighLevelEncodeString(t, "N\u0000n",
|
||||
// 'N' B/S =2 '\0' 'n'
|
||||
".XXXX XXXXX ...X. ........ .XX.XXX.") // Encode "n" in BINARY
|
||||
|
||||
// binary short form consecutive bytes
|
||||
testHighLevelEncodeString(t, "N\x00\x80 A",
|
||||
// 'N' B/S =2 '\0' \u0080 ' ' 'A'
|
||||
".XXXX XXXXX ...X. ........ X....... ....X ...X.")
|
||||
|
||||
// binary skipping over single character
|
||||
testHighLevelEncodeString(t, "\x00a\xFF\x80 A",
|
||||
// B/S =4 '\0' 'a' '\3ff' '\200' ' ' 'A'
|
||||
"XXXXX ..X.. ........ .XX....X XXXXXXXX X....... ....X ...X.")
|
||||
|
||||
// getting into binary mode from digit mode
|
||||
testHighLevelEncodeString(t, "1234\u0000",
|
||||
//D/L '1' '2' '3' '4' U/L B/S =1 \0
|
||||
"XXXX. ..XX .X.. .X.X .XX. XXX. XXXXX ....X ........")
|
||||
|
||||
// Create a string in which every character requires binary
|
||||
sb := new(bytes.Buffer)
|
||||
for i := 0; i <= 3000; i++ {
|
||||
sb.WriteByte(byte(128 + (i % 30)))
|
||||
}
|
||||
|
||||
// Test the output generated by Binary/Switch, particularly near the
|
||||
// places where the encoding changes: 31, 62, and 2047+31=2078
|
||||
for _, i := range []int{1, 2, 3, 10, 29, 30, 31, 32, 33, 60, 61, 62, 63, 64, 2076, 2077, 2078, 2079, 2080, 2100} {
|
||||
// This is the expected length of a binary string of length "i"
|
||||
expectedLength := (8 * i)
|
||||
switch {
|
||||
case i <= 31:
|
||||
expectedLength += 10
|
||||
case i <= 62:
|
||||
expectedLength += 20
|
||||
case i <= 2078:
|
||||
expectedLength += 21
|
||||
default:
|
||||
expectedLength += 31
|
||||
}
|
||||
data := string(sb.Bytes()[:i])
|
||||
|
||||
// Verify that we are correct about the length.
|
||||
testHighLevelEncodeStringCnt(t, data, expectedLength)
|
||||
if i != 1 && i != 32 && i != 2079 {
|
||||
// The addition of an 'a' at the beginning or end gets merged into the binary code
|
||||
// in those cases where adding another binary character only adds 8 or 9 bits to the result.
|
||||
// So we exclude the border cases i=1,32,2079
|
||||
// A lower case letter at the beginning will be merged into binary mode
|
||||
testHighLevelEncodeStringCnt(t, "a"+string(sb.Bytes()[:i-1]), expectedLength)
|
||||
// A lower case letter at the end will also be merged into binary mode
|
||||
testHighLevelEncodeStringCnt(t, string(sb.Bytes()[:i-1])+"a", expectedLength)
|
||||
}
|
||||
// A lower case letter at both ends will enough to latch us into LOWER.
|
||||
testHighLevelEncodeStringCnt(t, "a"+data+"b", expectedLength+15)
|
||||
}
|
||||
}
|
264
vendor/github.com/boombuler/barcode/aztec/state.go
generated
vendored
Normal file
264
vendor/github.com/boombuler/barcode/aztec/state.go
generated
vendored
Normal file
|
@ -0,0 +1,264 @@
|
|||
package aztec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type encodingMode byte
|
||||
|
||||
const (
|
||||
mode_upper encodingMode = iota // 5 bits
|
||||
mode_lower // 5 bits
|
||||
mode_digit // 4 bits
|
||||
mode_mixed // 5 bits
|
||||
mode_punct // 5 bits
|
||||
)
|
||||
|
||||
var (
|
||||
// The Latch Table shows, for each pair of Modes, the optimal method for
|
||||
// getting from one mode to another. In the worst possible case, this can
|
||||
// be up to 14 bits. In the best possible case, we are already there!
|
||||
// The high half-word of each entry gives the number of bits.
|
||||
// The low half-word of each entry are the actual bits necessary to change
|
||||
latchTable = map[encodingMode]map[encodingMode]int{
|
||||
mode_upper: {
|
||||
mode_upper: 0,
|
||||
mode_lower: (5 << 16) + 28,
|
||||
mode_digit: (5 << 16) + 30,
|
||||
mode_mixed: (5 << 16) + 29,
|
||||
mode_punct: (10 << 16) + (29 << 5) + 30,
|
||||
},
|
||||
mode_lower: {
|
||||
mode_upper: (9 << 16) + (30 << 4) + 14,
|
||||
mode_lower: 0,
|
||||
mode_digit: (5 << 16) + 30,
|
||||
mode_mixed: (5 << 16) + 29,
|
||||
mode_punct: (10 << 16) + (29 << 5) + 30,
|
||||
},
|
||||
mode_digit: {
|
||||
mode_upper: (4 << 16) + 14,
|
||||
mode_lower: (9 << 16) + (14 << 5) + 28,
|
||||
mode_digit: 0,
|
||||
mode_mixed: (9 << 16) + (14 << 5) + 29,
|
||||
mode_punct: (14 << 16) + (14 << 10) + (29 << 5) + 30,
|
||||
},
|
||||
mode_mixed: {
|
||||
mode_upper: (5 << 16) + 29,
|
||||
mode_lower: (5 << 16) + 28,
|
||||
mode_digit: (10 << 16) + (29 << 5) + 30,
|
||||
mode_mixed: 0,
|
||||
mode_punct: (5 << 16) + 30,
|
||||
},
|
||||
mode_punct: {
|
||||
mode_upper: (5 << 16) + 31,
|
||||
mode_lower: (10 << 16) + (31 << 5) + 28,
|
||||
mode_digit: (10 << 16) + (31 << 5) + 30,
|
||||
mode_mixed: (10 << 16) + (31 << 5) + 29,
|
||||
mode_punct: 0,
|
||||
},
|
||||
}
|
||||
// A map showing the available shift codes. (The shifts to BINARY are not shown)
|
||||
shiftTable = map[encodingMode]map[encodingMode]int{
|
||||
mode_upper: {
|
||||
mode_punct: 0,
|
||||
},
|
||||
mode_lower: {
|
||||
mode_punct: 0,
|
||||
mode_upper: 28,
|
||||
},
|
||||
mode_mixed: {
|
||||
mode_punct: 0,
|
||||
},
|
||||
mode_digit: {
|
||||
mode_punct: 0,
|
||||
mode_upper: 15,
|
||||
},
|
||||
}
|
||||
charMap map[encodingMode][]int
|
||||
)
|
||||
|
||||
type state struct {
|
||||
mode encodingMode
|
||||
tokens token
|
||||
bShiftByteCount int
|
||||
bitCount int
|
||||
}
|
||||
type stateSlice []*state
|
||||
|
||||
var initialState *state = &state{
|
||||
mode: mode_upper,
|
||||
tokens: nil,
|
||||
bShiftByteCount: 0,
|
||||
bitCount: 0,
|
||||
}
|
||||
|
||||
func init() {
|
||||
charMap = make(map[encodingMode][]int)
|
||||
charMap[mode_upper] = make([]int, 256)
|
||||
charMap[mode_lower] = make([]int, 256)
|
||||
charMap[mode_digit] = make([]int, 256)
|
||||
charMap[mode_mixed] = make([]int, 256)
|
||||
charMap[mode_punct] = make([]int, 256)
|
||||
|
||||
charMap[mode_upper][' '] = 1
|
||||
for c := 'A'; c <= 'Z'; c++ {
|
||||
charMap[mode_upper][int(c)] = int(c - 'A' + 2)
|
||||
}
|
||||
|
||||
charMap[mode_lower][' '] = 1
|
||||
for c := 'a'; c <= 'z'; c++ {
|
||||
charMap[mode_lower][c] = int(c - 'a' + 2)
|
||||
}
|
||||
charMap[mode_digit][' '] = 1
|
||||
for c := '0'; c <= '9'; c++ {
|
||||
charMap[mode_digit][c] = int(c - '0' + 2)
|
||||
}
|
||||
charMap[mode_digit][','] = 12
|
||||
charMap[mode_digit]['.'] = 13
|
||||
|
||||
mixedTable := []int{
|
||||
0, ' ', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||
11, 12, 13, 27, 28, 29, 30, 31, '@', '\\', '^',
|
||||
'_', '`', '|', '~', 127,
|
||||
}
|
||||
for i, v := range mixedTable {
|
||||
charMap[mode_mixed][v] = i
|
||||
}
|
||||
|
||||
punctTable := []int{
|
||||
0, '\r', 0, 0, 0, 0, '!', '\'', '#', '$', '%', '&', '\'',
|
||||
'(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
|
||||
'[', ']', '{', '}',
|
||||
}
|
||||
for i, v := range punctTable {
|
||||
if v > 0 {
|
||||
charMap[mode_punct][v] = i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (em encodingMode) BitCount() byte {
|
||||
if em == mode_digit {
|
||||
return 4
|
||||
}
|
||||
return 5
|
||||
}
|
||||
|
||||
// Create a new state representing this state with a latch to a (not
|
||||
// necessary different) mode, and then a code.
|
||||
func (s *state) latchAndAppend(mode encodingMode, value int) *state {
|
||||
bitCount := s.bitCount
|
||||
tokens := s.tokens
|
||||
|
||||
if mode != s.mode {
|
||||
latch := latchTable[s.mode][mode]
|
||||
tokens = newSimpleToken(tokens, latch&0xFFFF, byte(latch>>16))
|
||||
bitCount += latch >> 16
|
||||
}
|
||||
tokens = newSimpleToken(tokens, value, mode.BitCount())
|
||||
return &state{
|
||||
mode: mode,
|
||||
tokens: tokens,
|
||||
bShiftByteCount: 0,
|
||||
bitCount: bitCount + int(mode.BitCount()),
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new state representing this state, with a temporary shift
|
||||
// to a different mode to output a single value.
|
||||
func (s *state) shiftAndAppend(mode encodingMode, value int) *state {
|
||||
tokens := s.tokens
|
||||
|
||||
// Shifts exist only to UPPER and PUNCT, both with tokens size 5.
|
||||
tokens = newSimpleToken(tokens, shiftTable[s.mode][mode], s.mode.BitCount())
|
||||
tokens = newSimpleToken(tokens, value, 5)
|
||||
|
||||
return &state{
|
||||
mode: s.mode,
|
||||
tokens: tokens,
|
||||
bShiftByteCount: 0,
|
||||
bitCount: s.bitCount + int(s.mode.BitCount()) + 5,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new state representing this state, but an additional character
|
||||
// output in Binary Shift mode.
|
||||
func (s *state) addBinaryShiftChar(index int) *state {
|
||||
tokens := s.tokens
|
||||
mode := s.mode
|
||||
bitCnt := s.bitCount
|
||||
if s.mode == mode_punct || s.mode == mode_digit {
|
||||
latch := latchTable[s.mode][mode_upper]
|
||||
tokens = newSimpleToken(tokens, latch&0xFFFF, byte(latch>>16))
|
||||
bitCnt += latch >> 16
|
||||
mode = mode_upper
|
||||
}
|
||||
deltaBitCount := 8
|
||||
if s.bShiftByteCount == 0 || s.bShiftByteCount == 31 {
|
||||
deltaBitCount = 18
|
||||
} else if s.bShiftByteCount == 62 {
|
||||
deltaBitCount = 9
|
||||
}
|
||||
result := &state{
|
||||
mode: mode,
|
||||
tokens: tokens,
|
||||
bShiftByteCount: s.bShiftByteCount + 1,
|
||||
bitCount: bitCnt + deltaBitCount,
|
||||
}
|
||||
if result.bShiftByteCount == 2047+31 {
|
||||
// The string is as long as it's allowed to be. We should end it.
|
||||
result = result.endBinaryShift(index + 1)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Create the state identical to this one, but we are no longer in
|
||||
// Binary Shift mode.
|
||||
func (s *state) endBinaryShift(index int) *state {
|
||||
if s.bShiftByteCount == 0 {
|
||||
return s
|
||||
}
|
||||
tokens := newShiftToken(s.tokens, index-s.bShiftByteCount, s.bShiftByteCount)
|
||||
return &state{
|
||||
mode: s.mode,
|
||||
tokens: tokens,
|
||||
bShiftByteCount: 0,
|
||||
bitCount: s.bitCount,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if "this" state is better (or equal) to be in than "that"
|
||||
// state under all possible circumstances.
|
||||
func (this *state) isBetterThanOrEqualTo(other *state) bool {
|
||||
mySize := this.bitCount + (latchTable[this.mode][other.mode] >> 16)
|
||||
|
||||
if other.bShiftByteCount > 0 && (this.bShiftByteCount == 0 || this.bShiftByteCount > other.bShiftByteCount) {
|
||||
mySize += 10 // Cost of entering Binary Shift mode.
|
||||
}
|
||||
return mySize <= other.bitCount
|
||||
}
|
||||
|
||||
func (s *state) toBitList(text []byte) *utils.BitList {
|
||||
tokens := make([]token, 0)
|
||||
se := s.endBinaryShift(len(text))
|
||||
|
||||
for t := se.tokens; t != nil; t = t.prev() {
|
||||
tokens = append(tokens, t)
|
||||
}
|
||||
res := new(utils.BitList)
|
||||
for i := len(tokens) - 1; i >= 0; i-- {
|
||||
tokens[i].appendTo(res, text)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *state) String() string {
|
||||
tokens := make([]token, 0)
|
||||
for t := s.tokens; t != nil; t = t.prev() {
|
||||
tokens = append([]token{t}, tokens...)
|
||||
}
|
||||
return fmt.Sprintf("M:%d bits=%d bytes=%d: %v", s.mode, s.bitCount, s.bShiftByteCount, tokens)
|
||||
}
|
75
vendor/github.com/boombuler/barcode/aztec/token.go
generated
vendored
Normal file
75
vendor/github.com/boombuler/barcode/aztec/token.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
package aztec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type token interface {
|
||||
fmt.Stringer
|
||||
prev() token
|
||||
appendTo(bits *utils.BitList, text []byte)
|
||||
}
|
||||
|
||||
type simpleToken struct {
|
||||
token
|
||||
value int
|
||||
bitCount byte
|
||||
}
|
||||
|
||||
type binaryShiftToken struct {
|
||||
token
|
||||
bShiftStart int
|
||||
bShiftByteCnt int
|
||||
}
|
||||
|
||||
func newSimpleToken(prev token, value int, bitCount byte) token {
|
||||
return &simpleToken{prev, value, bitCount}
|
||||
}
|
||||
func newShiftToken(prev token, bShiftStart int, bShiftCnt int) token {
|
||||
return &binaryShiftToken{prev, bShiftStart, bShiftCnt}
|
||||
}
|
||||
|
||||
func (st *simpleToken) prev() token {
|
||||
return st.token
|
||||
}
|
||||
func (st *simpleToken) appendTo(bits *utils.BitList, text []byte) {
|
||||
bits.AddBits(st.value, st.bitCount)
|
||||
}
|
||||
func (st *simpleToken) String() string {
|
||||
value := st.value & ((1 << st.bitCount) - 1)
|
||||
value |= 1 << st.bitCount
|
||||
return "<" + fmt.Sprintf("%b", value)[1:] + ">"
|
||||
}
|
||||
|
||||
func (bst *binaryShiftToken) prev() token {
|
||||
return bst.token
|
||||
}
|
||||
func (bst *binaryShiftToken) appendTo(bits *utils.BitList, text []byte) {
|
||||
for i := 0; i < bst.bShiftByteCnt; i++ {
|
||||
if i == 0 || (i == 31 && bst.bShiftByteCnt <= 62) {
|
||||
// We need a header before the first character, and before
|
||||
// character 31 when the total byte code is <= 62
|
||||
bits.AddBits(31, 5) // BINARY_SHIFT
|
||||
if bst.bShiftByteCnt > 62 {
|
||||
bits.AddBits(bst.bShiftByteCnt-31, 16)
|
||||
} else if i == 0 {
|
||||
// 1 <= binaryShiftByteCode <= 62
|
||||
if bst.bShiftByteCnt < 31 {
|
||||
bits.AddBits(bst.bShiftByteCnt, 5)
|
||||
} else {
|
||||
bits.AddBits(31, 5)
|
||||
}
|
||||
} else {
|
||||
// 32 <= binaryShiftCount <= 62 and i == 31
|
||||
bits.AddBits(bst.bShiftByteCnt-31, 5)
|
||||
}
|
||||
}
|
||||
bits.AddByte(text[bst.bShiftStart+i])
|
||||
}
|
||||
}
|
||||
|
||||
func (bst *binaryShiftToken) String() string {
|
||||
return fmt.Sprintf("<%d::%d>", bst.bShiftStart, (bst.bShiftStart + bst.bShiftByteCnt - 1))
|
||||
}
|
42
vendor/github.com/boombuler/barcode/barcode.go
generated
vendored
Normal file
42
vendor/github.com/boombuler/barcode/barcode.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
package barcode
|
||||
|
||||
import "image"
|
||||
|
||||
const (
|
||||
TypeAztec = "Aztec"
|
||||
TypeCodabar = "Codabar"
|
||||
TypeCode128 = "Code 128"
|
||||
TypeCode39 = "Code 39"
|
||||
TypeCode93 = "Code 93"
|
||||
TypeDataMatrix = "DataMatrix"
|
||||
TypeEAN8 = "EAN 8"
|
||||
TypeEAN13 = "EAN 13"
|
||||
TypePDF = "PDF417"
|
||||
TypeQR = "QR Code"
|
||||
Type2of5 = "2 of 5"
|
||||
Type2of5Interleaved = "2 of 5 (interleaved)"
|
||||
)
|
||||
|
||||
// Contains some meta information about a barcode
|
||||
type Metadata struct {
|
||||
// the name of the barcode kind
|
||||
CodeKind string
|
||||
// contains 1 for 1D barcodes or 2 for 2D barcodes
|
||||
Dimensions byte
|
||||
}
|
||||
|
||||
// a rendered and encoded barcode
|
||||
type Barcode interface {
|
||||
image.Image
|
||||
// returns some meta information about the barcode
|
||||
Metadata() Metadata
|
||||
// the data that was encoded in this barcode
|
||||
Content() string
|
||||
}
|
||||
|
||||
// Additional interface that some barcodes might implement to provide
|
||||
// the value of its checksum.
|
||||
type BarcodeIntCS interface {
|
||||
Barcode
|
||||
CheckSum() int
|
||||
}
|
49
vendor/github.com/boombuler/barcode/codabar/encoder.go
generated
vendored
Normal file
49
vendor/github.com/boombuler/barcode/codabar/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Package codabar can create Codabar barcodes
|
||||
package codabar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
var encodingTable = map[rune][]bool{
|
||||
'0': []bool{true, false, true, false, true, false, false, true, true},
|
||||
'1': []bool{true, false, true, false, true, true, false, false, true},
|
||||
'2': []bool{true, false, true, false, false, true, false, true, true},
|
||||
'3': []bool{true, true, false, false, true, false, true, false, true},
|
||||
'4': []bool{true, false, true, true, false, true, false, false, true},
|
||||
'5': []bool{true, true, false, true, false, true, false, false, true},
|
||||
'6': []bool{true, false, false, true, false, true, false, true, true},
|
||||
'7': []bool{true, false, false, true, false, true, true, false, true},
|
||||
'8': []bool{true, false, false, true, true, false, true, false, true},
|
||||
'9': []bool{true, true, false, true, false, false, true, false, true},
|
||||
'-': []bool{true, false, true, false, false, true, true, false, true},
|
||||
'$': []bool{true, false, true, true, false, false, true, false, true},
|
||||
':': []bool{true, true, false, true, false, true, true, false, true, true},
|
||||
'/': []bool{true, true, false, true, true, false, true, false, true, true},
|
||||
'.': []bool{true, true, false, true, true, false, true, true, false, true},
|
||||
'+': []bool{true, false, true, true, false, false, true, true, false, false, true, true},
|
||||
'A': []bool{true, false, true, true, false, false, true, false, false, true},
|
||||
'B': []bool{true, false, true, false, false, true, false, false, true, true},
|
||||
'C': []bool{true, false, false, true, false, false, true, false, true, true},
|
||||
'D': []bool{true, false, true, false, false, true, true, false, false, true},
|
||||
}
|
||||
|
||||
// Encode creates a codabar barcode for the given content
|
||||
func Encode(content string) (barcode.Barcode, error) {
|
||||
checkValid, _ := regexp.Compile(`[ABCD][0123456789\-\$\:/\.\+]*[ABCD]$`)
|
||||
if content == "!" || checkValid.ReplaceAllString(content, "!") != "!" {
|
||||
return nil, fmt.Errorf("can not encode \"%s\"", content)
|
||||
}
|
||||
resBits := new(utils.BitList)
|
||||
for i, r := range content {
|
||||
if i > 0 {
|
||||
resBits.AddBit(false)
|
||||
}
|
||||
resBits.AddBit(encodingTable[r]...)
|
||||
}
|
||||
return utils.New1DCode(barcode.TypeCodabar, content, resBits), nil
|
||||
}
|
32
vendor/github.com/boombuler/barcode/codabar/encoder_test.go
generated
vendored
Normal file
32
vendor/github.com/boombuler/barcode/codabar/encoder_test.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
package codabar
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Encode(t *testing.T) {
|
||||
_, err := Encode("FOOBAR")
|
||||
if err == nil {
|
||||
t.Error("\"FOOBAR\" should not be encodable")
|
||||
}
|
||||
|
||||
testEncode := func(txt, testResult string) {
|
||||
code, err := Encode(txt)
|
||||
if err != nil || code == nil {
|
||||
t.Fail()
|
||||
} else {
|
||||
if code.Bounds().Max.X != len(testResult) {
|
||||
t.Errorf("%v: length missmatch", txt)
|
||||
} else {
|
||||
for i, r := range testResult {
|
||||
if (code.At(i, 0) == color.Black) != (r == '1') {
|
||||
t.Errorf("%v: code missmatch on position %d", txt, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testEncode("A40156B", "10110010010101101001010101001101010110010110101001010010101101010010011")
|
||||
}
|
203
vendor/github.com/boombuler/barcode/code128/encode.go
generated
vendored
Normal file
203
vendor/github.com/boombuler/barcode/code128/encode.go
generated
vendored
Normal file
|
@ -0,0 +1,203 @@
|
|||
// Package code128 can create Code128 barcodes
|
||||
package code128
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
func strToRunes(str string) []rune {
|
||||
result := make([]rune, utf8.RuneCountInString(str))
|
||||
i := 0
|
||||
for _, r := range str {
|
||||
result[i] = r
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func shouldUseCTable(nextRunes []rune, curEncoding byte) bool {
|
||||
requiredDigits := 4
|
||||
if curEncoding == startCSymbol {
|
||||
requiredDigits = 2
|
||||
}
|
||||
if len(nextRunes) < requiredDigits {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < requiredDigits; i++ {
|
||||
if i%2 == 0 && nextRunes[i] == FNC1 {
|
||||
requiredDigits++
|
||||
if len(nextRunes) < requiredDigits {
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
if nextRunes[i] < '0' || nextRunes[i] > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func tableContainsRune(table string, r rune) bool {
|
||||
return strings.ContainsRune(table, r) || r == FNC1 || r == FNC2 || r == FNC3 || r == FNC4
|
||||
}
|
||||
|
||||
func shouldUseATable(nextRunes []rune, curEncoding byte) bool {
|
||||
nextRune := nextRunes[0]
|
||||
if !tableContainsRune(bTable, nextRune) || curEncoding == startASymbol {
|
||||
return tableContainsRune(aTable, nextRune)
|
||||
}
|
||||
if curEncoding == 0 {
|
||||
for _, r := range nextRunes {
|
||||
if tableContainsRune(abTable, r) {
|
||||
continue
|
||||
}
|
||||
if strings.ContainsRune(aOnlyTable, r) {
|
||||
return true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getCodeIndexList(content []rune) *utils.BitList {
|
||||
result := new(utils.BitList)
|
||||
curEncoding := byte(0)
|
||||
for i := 0; i < len(content); i++ {
|
||||
if shouldUseCTable(content[i:], curEncoding) {
|
||||
if curEncoding != startCSymbol {
|
||||
if curEncoding == byte(0) {
|
||||
result.AddByte(startCSymbol)
|
||||
} else {
|
||||
result.AddByte(codeCSymbol)
|
||||
}
|
||||
curEncoding = startCSymbol
|
||||
}
|
||||
if content[i] == FNC1 {
|
||||
result.AddByte(102)
|
||||
} else {
|
||||
idx := (content[i] - '0') * 10
|
||||
i++
|
||||
idx = idx + (content[i] - '0')
|
||||
result.AddByte(byte(idx))
|
||||
}
|
||||
} else if shouldUseATable(content[i:], curEncoding) {
|
||||
if curEncoding != startASymbol {
|
||||
if curEncoding == byte(0) {
|
||||
result.AddByte(startASymbol)
|
||||
} else {
|
||||
result.AddByte(codeASymbol)
|
||||
}
|
||||
curEncoding = startASymbol
|
||||
}
|
||||
var idx int
|
||||
switch content[i] {
|
||||
case FNC1:
|
||||
idx = 102
|
||||
break
|
||||
case FNC2:
|
||||
idx = 97
|
||||
break
|
||||
case FNC3:
|
||||
idx = 96
|
||||
break
|
||||
case FNC4:
|
||||
idx = 101
|
||||
break
|
||||
default:
|
||||
idx = strings.IndexRune(aTable, content[i])
|
||||
break
|
||||
}
|
||||
if idx < 0 {
|
||||
return nil
|
||||
}
|
||||
result.AddByte(byte(idx))
|
||||
} else {
|
||||
if curEncoding != startBSymbol {
|
||||
if curEncoding == byte(0) {
|
||||
result.AddByte(startBSymbol)
|
||||
} else {
|
||||
result.AddByte(codeBSymbol)
|
||||
}
|
||||
curEncoding = startBSymbol
|
||||
}
|
||||
var idx int
|
||||
switch content[i] {
|
||||
case FNC1:
|
||||
idx = 102
|
||||
break
|
||||
case FNC2:
|
||||
idx = 97
|
||||
break
|
||||
case FNC3:
|
||||
idx = 96
|
||||
break
|
||||
case FNC4:
|
||||
idx = 100
|
||||
break
|
||||
default:
|
||||
idx = strings.IndexRune(bTable, content[i])
|
||||
break
|
||||
}
|
||||
|
||||
if idx < 0 {
|
||||
return nil
|
||||
}
|
||||
result.AddByte(byte(idx))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Encode creates a Code 128 barcode for the given content
|
||||
func Encode(content string) (barcode.BarcodeIntCS, error) {
|
||||
contentRunes := strToRunes(content)
|
||||
if len(contentRunes) <= 0 || len(contentRunes) > 80 {
|
||||
return nil, fmt.Errorf("content length should be between 1 and 80 runes but got %d", len(contentRunes))
|
||||
}
|
||||
idxList := getCodeIndexList(contentRunes)
|
||||
|
||||
if idxList == nil {
|
||||
return nil, fmt.Errorf("\"%s\" could not be encoded", content)
|
||||
}
|
||||
|
||||
result := new(utils.BitList)
|
||||
sum := 0
|
||||
for i, idx := range idxList.GetBytes() {
|
||||
if i == 0 {
|
||||
sum = int(idx)
|
||||
} else {
|
||||
sum += i * int(idx)
|
||||
}
|
||||
result.AddBit(encodingTable[idx]...)
|
||||
}
|
||||
sum = sum % 103
|
||||
result.AddBit(encodingTable[sum]...)
|
||||
result.AddBit(encodingTable[stopSymbol]...)
|
||||
return utils.New1DCodeIntCheckSum(barcode.TypeCode128, content, result, sum), nil
|
||||
}
|
||||
|
||||
func EncodeWithoutChecksum(content string) (barcode.Barcode, error) {
|
||||
contentRunes := strToRunes(content)
|
||||
if len(contentRunes) <= 0 || len(contentRunes) > 80 {
|
||||
return nil, fmt.Errorf("content length should be between 1 and 80 runes but got %d", len(contentRunes))
|
||||
}
|
||||
idxList := getCodeIndexList(contentRunes)
|
||||
|
||||
if idxList == nil {
|
||||
return nil, fmt.Errorf("\"%s\" could not be encoded", content)
|
||||
}
|
||||
|
||||
result := new(utils.BitList)
|
||||
for _, idx := range idxList.GetBytes() {
|
||||
result.AddBit(encodingTable[idx]...)
|
||||
}
|
||||
result.AddBit(encodingTable[stopSymbol]...)
|
||||
return utils.New1DCode(barcode.TypeCode128, content, result), nil
|
||||
}
|
131
vendor/github.com/boombuler/barcode/code128/encode_test.go
generated
vendored
Normal file
131
vendor/github.com/boombuler/barcode/code128/encode_test.go
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
package code128
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testEncode(t *testing.T, txt, testResult string) {
|
||||
code, err := Encode(txt)
|
||||
if err != nil || code == nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
if code.Bounds().Max.X != len(testResult) {
|
||||
t.Errorf("%v: length missmatch. Got %d expected %d", txt, code.Bounds().Max.X, len(testResult))
|
||||
} else {
|
||||
encoded := ""
|
||||
failed := false
|
||||
for i, r := range testResult {
|
||||
if code.At(i, 0) == color.Black {
|
||||
encoded += "1"
|
||||
} else {
|
||||
encoded += "0"
|
||||
}
|
||||
|
||||
if (code.At(i, 0) == color.Black) != (r == '1') {
|
||||
failed = true
|
||||
t.Errorf("%v: code missmatch on position %d", txt, i)
|
||||
}
|
||||
}
|
||||
if failed {
|
||||
t.Log("Encoded: ", encoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_EncodeFunctionChars(t *testing.T) {
|
||||
encFNC1 := "11110101110"
|
||||
encFNC2 := "11110101000"
|
||||
encFNC3 := "10111100010"
|
||||
encFNC4 := "10111101110"
|
||||
encStartB := "11010010000"
|
||||
encStop := "1100011101011"
|
||||
|
||||
// Special Case FC1 can also be encoded to C Table therefor using 123 as suffix might have unexpected results.
|
||||
testEncode(t, string(FNC1)+"A23", encStartB+encFNC1+"10100011000"+"11001110010"+"11001011100"+"10100011110"+encStop)
|
||||
testEncode(t, string(FNC2)+"123", encStartB+encFNC2+"10011100110"+"11001110010"+"11001011100"+"11100010110"+encStop)
|
||||
testEncode(t, string(FNC3)+"123", encStartB+encFNC3+"10011100110"+"11001110010"+"11001011100"+"11101000110"+encStop)
|
||||
testEncode(t, string(FNC4)+"123", encStartB+encFNC4+"10011100110"+"11001110010"+"11001011100"+"11100011010"+encStop)
|
||||
}
|
||||
|
||||
func Test_Unencodable(t *testing.T) {
|
||||
if _, err := Encode(""); err == nil {
|
||||
t.Fail()
|
||||
}
|
||||
if _, err := Encode("ä"); err == nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_EncodeCTable(t *testing.T) {
|
||||
testEncode(t, "HI345678H", "110100100001100010100011000100010101110111101000101100011100010110110000101001011110111011000101000111011000101100011101011")
|
||||
testEncode(t, "334455", "11010011100101000110001000110111011101000110100100111101100011101011")
|
||||
|
||||
testEncode(t, string(FNC1)+"1234",
|
||||
"11010011100"+ // Start C
|
||||
"11110101110"+ // FNC1
|
||||
"10110011100"+ // 12
|
||||
"10001011000"+ // 34
|
||||
"11101001100"+ // CheckSum == 24
|
||||
"1100011101011") // Stop
|
||||
}
|
||||
|
||||
func Test_shouldUseCTable(t *testing.T) {
|
||||
if !shouldUseCTable([]rune{FNC1, '1', '2'}, startCSymbol) {
|
||||
t.Error("[FNC1]12 failed")
|
||||
}
|
||||
if shouldUseCTable([]rune{FNC1, '1'}, startCSymbol) {
|
||||
t.Error("[FNC1]1 failed")
|
||||
}
|
||||
if shouldUseCTable([]rune{'0', FNC1, '1'}, startCSymbol) {
|
||||
t.Error("0[FNC1]1 failed")
|
||||
}
|
||||
if !shouldUseCTable([]rune{'0', '1', FNC1, '2', '3'}, startBSymbol) {
|
||||
t.Error("01[FNC1]23 failed")
|
||||
}
|
||||
if shouldUseCTable([]rune{'0', '1', FNC1}, startBSymbol) {
|
||||
t.Error("01[FNC1] failed")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Issue16(t *testing.T) {
|
||||
if !shouldUseATable([]rune{'\r', 'A'}, 0) {
|
||||
t.Error("Code should start with A-Table if the text start with \\r")
|
||||
}
|
||||
if !shouldUseATable([]rune{FNC1, '\r'}, 0) {
|
||||
t.Error("Code should start with A-Table if the text start with <FNC1>\\r")
|
||||
}
|
||||
if shouldUseATable([]rune{FNC1, '1', '2', '3'}, 0) {
|
||||
t.Error("Code should not start with A-Table if the text start with <FNC1>123")
|
||||
}
|
||||
testEncode(t, string(FNC3)+"$P\rI", "110100001001011110001010010001100111011101101111011101011000100010110001010001100011101011")
|
||||
}
|
||||
|
||||
func Test_Datalogic(t *testing.T) {
|
||||
// <Start A><FNC3>$P\r<checksum><STOP>
|
||||
testEncode(t, string(FNC3)+"$P\r",
|
||||
"11010000100"+ // <Start A>
|
||||
"10111100010"+ // <FNC3>
|
||||
"10010001100"+ // $
|
||||
"11101110110"+ // P
|
||||
"11110111010"+ // CR
|
||||
"11000100010"+ // checksum = 'I'
|
||||
"1100011101011") // STOP
|
||||
|
||||
// <Start B><FNC3>$P,Ae,P<CR><checksum><STOP>
|
||||
testEncode(t, string(FNC3)+"$P,Ae,P\r",
|
||||
"11010010000"+ // <Start B>
|
||||
"10111100010"+ // <FNC3>
|
||||
"10010001100"+ // $
|
||||
"11101110110"+ // P
|
||||
"10110011100"+ // ,
|
||||
"10100011000"+ // A
|
||||
"10110010000"+ // e
|
||||
"10110011100"+ // ,
|
||||
"11101110110"+ // P
|
||||
"11101011110"+ // <Code A>
|
||||
"11110111010"+ // <CR>
|
||||
"10110001000"+ // checksum = 'D'
|
||||
"1100011101011") // STOP
|
||||
}
|
143
vendor/github.com/boombuler/barcode/code128/encodingtable.go
generated
vendored
Normal file
143
vendor/github.com/boombuler/barcode/code128/encodingtable.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
|||
package code128
|
||||
|
||||
var encodingTable = [107][]bool{
|
||||
[]bool{true, true, false, true, true, false, false, true, true, false, false},
|
||||
[]bool{true, true, false, false, true, true, false, true, true, false, false},
|
||||
[]bool{true, true, false, false, true, true, false, false, true, true, false},
|
||||
[]bool{true, false, false, true, false, false, true, true, false, false, false},
|
||||
[]bool{true, false, false, true, false, false, false, true, true, false, false},
|
||||
[]bool{true, false, false, false, true, false, false, true, true, false, false},
|
||||
[]bool{true, false, false, true, true, false, false, true, false, false, false},
|
||||
[]bool{true, false, false, true, true, false, false, false, true, false, false},
|
||||
[]bool{true, false, false, false, true, true, false, false, true, false, false},
|
||||
[]bool{true, true, false, false, true, false, false, true, false, false, false},
|
||||
[]bool{true, true, false, false, true, false, false, false, true, false, false},
|
||||
[]bool{true, true, false, false, false, true, false, false, true, false, false},
|
||||
[]bool{true, false, true, true, false, false, true, true, true, false, false},
|
||||
[]bool{true, false, false, true, true, false, true, true, true, false, false},
|
||||
[]bool{true, false, false, true, true, false, false, true, true, true, false},
|
||||
[]bool{true, false, true, true, true, false, false, true, true, false, false},
|
||||
[]bool{true, false, false, true, true, true, false, true, true, false, false},
|
||||
[]bool{true, false, false, true, true, true, false, false, true, true, false},
|
||||
[]bool{true, true, false, false, true, true, true, false, false, true, false},
|
||||
[]bool{true, true, false, false, true, false, true, true, true, false, false},
|
||||
[]bool{true, true, false, false, true, false, false, true, true, true, false},
|
||||
[]bool{true, true, false, true, true, true, false, false, true, false, false},
|
||||
[]bool{true, true, false, false, true, true, true, false, true, false, false},
|
||||
[]bool{true, true, true, false, true, true, false, true, true, true, false},
|
||||
[]bool{true, true, true, false, true, false, false, true, true, false, false},
|
||||
[]bool{true, true, true, false, false, true, false, true, true, false, false},
|
||||
[]bool{true, true, true, false, false, true, false, false, true, true, false},
|
||||
[]bool{true, true, true, false, true, true, false, false, true, false, false},
|
||||
[]bool{true, true, true, false, false, true, true, false, true, false, false},
|
||||
[]bool{true, true, true, false, false, true, true, false, false, true, false},
|
||||
[]bool{true, true, false, true, true, false, true, true, false, false, false},
|
||||
[]bool{true, true, false, true, true, false, false, false, true, true, false},
|
||||
[]bool{true, true, false, false, false, true, true, false, true, true, false},
|
||||
[]bool{true, false, true, false, false, false, true, true, false, false, false},
|
||||
[]bool{true, false, false, false, true, false, true, true, false, false, false},
|
||||
[]bool{true, false, false, false, true, false, false, false, true, true, false},
|
||||
[]bool{true, false, true, true, false, false, false, true, false, false, false},
|
||||
[]bool{true, false, false, false, true, true, false, true, false, false, false},
|
||||
[]bool{true, false, false, false, true, true, false, false, false, true, false},
|
||||
[]bool{true, true, false, true, false, false, false, true, false, false, false},
|
||||
[]bool{true, true, false, false, false, true, false, true, false, false, false},
|
||||
[]bool{true, true, false, false, false, true, false, false, false, true, false},
|
||||
[]bool{true, false, true, true, false, true, true, true, false, false, false},
|
||||
[]bool{true, false, true, true, false, false, false, true, true, true, false},
|
||||
[]bool{true, false, false, false, true, true, false, true, true, true, false},
|
||||
[]bool{true, false, true, true, true, false, true, true, false, false, false},
|
||||
[]bool{true, false, true, true, true, false, false, false, true, true, false},
|
||||
[]bool{true, false, false, false, true, true, true, false, true, true, false},
|
||||
[]bool{true, true, true, false, true, true, true, false, true, true, false},
|
||||
[]bool{true, true, false, true, false, false, false, true, true, true, false},
|
||||
[]bool{true, true, false, false, false, true, false, true, true, true, false},
|
||||
[]bool{true, true, false, true, true, true, false, true, false, false, false},
|
||||
[]bool{true, true, false, true, true, true, false, false, false, true, false},
|
||||
[]bool{true, true, false, true, true, true, false, true, true, true, false},
|
||||
[]bool{true, true, true, false, true, false, true, true, false, false, false},
|
||||
[]bool{true, true, true, false, true, false, false, false, true, true, false},
|
||||
[]bool{true, true, true, false, false, false, true, false, true, true, false},
|
||||
[]bool{true, true, true, false, true, true, false, true, false, false, false},
|
||||
[]bool{true, true, true, false, true, true, false, false, false, true, false},
|
||||
[]bool{true, true, true, false, false, false, true, true, false, true, false},
|
||||
[]bool{true, true, true, false, true, true, true, true, false, true, false},
|
||||
[]bool{true, true, false, false, true, false, false, false, false, true, false},
|
||||
[]bool{true, true, true, true, false, false, false, true, false, true, false},
|
||||
[]bool{true, false, true, false, false, true, true, false, false, false, false},
|
||||
[]bool{true, false, true, false, false, false, false, true, true, false, false},
|
||||
[]bool{true, false, false, true, false, true, true, false, false, false, false},
|
||||
[]bool{true, false, false, true, false, false, false, false, true, true, false},
|
||||
[]bool{true, false, false, false, false, true, false, true, true, false, false},
|
||||
[]bool{true, false, false, false, false, true, false, false, true, true, false},
|
||||
[]bool{true, false, true, true, false, false, true, false, false, false, false},
|
||||
[]bool{true, false, true, true, false, false, false, false, true, false, false},
|
||||
[]bool{true, false, false, true, true, false, true, false, false, false, false},
|
||||
[]bool{true, false, false, true, true, false, false, false, false, true, false},
|
||||
[]bool{true, false, false, false, false, true, true, false, true, false, false},
|
||||
[]bool{true, false, false, false, false, true, true, false, false, true, false},
|
||||
[]bool{true, true, false, false, false, false, true, false, false, true, false},
|
||||
[]bool{true, true, false, false, true, false, true, false, false, false, false},
|
||||
[]bool{true, true, true, true, false, true, true, true, false, true, false},
|
||||
[]bool{true, true, false, false, false, false, true, false, true, false, false},
|
||||
[]bool{true, false, false, false, true, true, true, true, false, true, false},
|
||||
[]bool{true, false, true, false, false, true, true, true, true, false, false},
|
||||
[]bool{true, false, false, true, false, true, true, true, true, false, false},
|
||||
[]bool{true, false, false, true, false, false, true, true, true, true, false},
|
||||
[]bool{true, false, true, true, true, true, false, false, true, false, false},
|
||||
[]bool{true, false, false, true, true, true, true, false, true, false, false},
|
||||
[]bool{true, false, false, true, true, true, true, false, false, true, false},
|
||||
[]bool{true, true, true, true, false, true, false, false, true, false, false},
|
||||
[]bool{true, true, true, true, false, false, true, false, true, false, false},
|
||||
[]bool{true, true, true, true, false, false, true, false, false, true, false},
|
||||
[]bool{true, true, false, true, true, false, true, true, true, true, false},
|
||||
[]bool{true, true, false, true, true, true, true, false, true, true, false},
|
||||
[]bool{true, true, true, true, false, true, true, false, true, true, false},
|
||||
[]bool{true, false, true, false, true, true, true, true, false, false, false},
|
||||
[]bool{true, false, true, false, false, false, true, true, true, true, false},
|
||||
[]bool{true, false, false, false, true, false, true, true, true, true, false},
|
||||
[]bool{true, false, true, true, true, true, false, true, false, false, false},
|
||||
[]bool{true, false, true, true, true, true, false, false, false, true, false},
|
||||
[]bool{true, true, true, true, false, true, false, true, false, false, false},
|
||||
[]bool{true, true, true, true, false, true, false, false, false, true, false},
|
||||
[]bool{true, false, true, true, true, false, true, true, true, true, false},
|
||||
[]bool{true, false, true, true, true, true, false, true, true, true, false},
|
||||
[]bool{true, true, true, false, true, false, true, true, true, true, false},
|
||||
[]bool{true, true, true, true, false, true, false, true, true, true, false},
|
||||
[]bool{true, true, false, true, false, false, false, false, true, false, false},
|
||||
[]bool{true, true, false, true, false, false, true, false, false, false, false},
|
||||
[]bool{true, true, false, true, false, false, true, true, true, false, false},
|
||||
[]bool{true, true, false, false, false, true, true, true, false, true, false, true, true},
|
||||
}
|
||||
|
||||
const startASymbol byte = 103
|
||||
const startBSymbol byte = 104
|
||||
const startCSymbol byte = 105
|
||||
|
||||
const codeASymbol byte = 101
|
||||
const codeBSymbol byte = 100
|
||||
const codeCSymbol byte = 99
|
||||
|
||||
const stopSymbol byte = 106
|
||||
|
||||
const (
|
||||
// FNC1 - Special Function 1
|
||||
FNC1 = '\u00f1'
|
||||
// FNC2 - Special Function 2
|
||||
FNC2 = '\u00f2'
|
||||
// FNC3 - Special Function 3
|
||||
FNC3 = '\u00f3'
|
||||
// FNC4 - Special Function 4
|
||||
FNC4 = '\u00f4'
|
||||
)
|
||||
|
||||
const abTable = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
|
||||
const bTable = abTable + "`abcdefghijklmnopqrstuvwxyz{|}~\u007F"
|
||||
const aOnlyTable = "\u0000\u0001\u0002\u0003\u0004" + // NUL, SOH, STX, ETX, EOT
|
||||
"\u0005\u0006\u0007\u0008\u0009" + // ENQ, ACK, BEL, BS, HT
|
||||
"\u000A\u000B\u000C\u000D\u000E" + // LF, VT, FF, CR, SO
|
||||
"\u000F\u0010\u0011\u0012\u0013" + // SI, DLE, DC1, DC2, DC3
|
||||
"\u0014\u0015\u0016\u0017\u0018" + // DC4, NAK, SYN, ETB, CAN
|
||||
"\u0019\u001A\u001B\u001C\u001D" + // EM, SUB, ESC, FS, GS
|
||||
"\u001E\u001F" // RS, US
|
||||
const aTable = abTable + aOnlyTable
|
152
vendor/github.com/boombuler/barcode/code39/encoder.go
generated
vendored
Normal file
152
vendor/github.com/boombuler/barcode/code39/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,152 @@
|
|||
// Package code39 can create Code39 barcodes
|
||||
package code39
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type encodeInfo struct {
|
||||
value int
|
||||
data []bool
|
||||
}
|
||||
|
||||
var encodeTable = map[rune]encodeInfo{
|
||||
'0': encodeInfo{0, []bool{true, false, true, false, false, true, true, false, true, true, false, true}},
|
||||
'1': encodeInfo{1, []bool{true, true, false, true, false, false, true, false, true, false, true, true}},
|
||||
'2': encodeInfo{2, []bool{true, false, true, true, false, false, true, false, true, false, true, true}},
|
||||
'3': encodeInfo{3, []bool{true, true, false, true, true, false, false, true, false, true, false, true}},
|
||||
'4': encodeInfo{4, []bool{true, false, true, false, false, true, true, false, true, false, true, true}},
|
||||
'5': encodeInfo{5, []bool{true, true, false, true, false, false, true, true, false, true, false, true}},
|
||||
'6': encodeInfo{6, []bool{true, false, true, true, false, false, true, true, false, true, false, true}},
|
||||
'7': encodeInfo{7, []bool{true, false, true, false, false, true, false, true, true, false, true, true}},
|
||||
'8': encodeInfo{8, []bool{true, true, false, true, false, false, true, false, true, true, false, true}},
|
||||
'9': encodeInfo{9, []bool{true, false, true, true, false, false, true, false, true, true, false, true}},
|
||||
'A': encodeInfo{10, []bool{true, true, false, true, false, true, false, false, true, false, true, true}},
|
||||
'B': encodeInfo{11, []bool{true, false, true, true, false, true, false, false, true, false, true, true}},
|
||||
'C': encodeInfo{12, []bool{true, true, false, true, true, false, true, false, false, true, false, true}},
|
||||
'D': encodeInfo{13, []bool{true, false, true, false, true, true, false, false, true, false, true, true}},
|
||||
'E': encodeInfo{14, []bool{true, true, false, true, false, true, true, false, false, true, false, true}},
|
||||
'F': encodeInfo{15, []bool{true, false, true, true, false, true, true, false, false, true, false, true}},
|
||||
'G': encodeInfo{16, []bool{true, false, true, false, true, false, false, true, true, false, true, true}},
|
||||
'H': encodeInfo{17, []bool{true, true, false, true, false, true, false, false, true, true, false, true}},
|
||||
'I': encodeInfo{18, []bool{true, false, true, true, false, true, false, false, true, true, false, true}},
|
||||
'J': encodeInfo{19, []bool{true, false, true, false, true, true, false, false, true, true, false, true}},
|
||||
'K': encodeInfo{20, []bool{true, true, false, true, false, true, false, true, false, false, true, true}},
|
||||
'L': encodeInfo{21, []bool{true, false, true, true, false, true, false, true, false, false, true, true}},
|
||||
'M': encodeInfo{22, []bool{true, true, false, true, true, false, true, false, true, false, false, true}},
|
||||
'N': encodeInfo{23, []bool{true, false, true, false, true, true, false, true, false, false, true, true}},
|
||||
'O': encodeInfo{24, []bool{true, true, false, true, false, true, true, false, true, false, false, true}},
|
||||
'P': encodeInfo{25, []bool{true, false, true, true, false, true, true, false, true, false, false, true}},
|
||||
'Q': encodeInfo{26, []bool{true, false, true, false, true, false, true, true, false, false, true, true}},
|
||||
'R': encodeInfo{27, []bool{true, true, false, true, false, true, false, true, true, false, false, true}},
|
||||
'S': encodeInfo{28, []bool{true, false, true, true, false, true, false, true, true, false, false, true}},
|
||||
'T': encodeInfo{29, []bool{true, false, true, false, true, true, false, true, true, false, false, true}},
|
||||
'U': encodeInfo{30, []bool{true, true, false, false, true, false, true, false, true, false, true, true}},
|
||||
'V': encodeInfo{31, []bool{true, false, false, true, true, false, true, false, true, false, true, true}},
|
||||
'W': encodeInfo{32, []bool{true, true, false, false, true, true, false, true, false, true, false, true}},
|
||||
'X': encodeInfo{33, []bool{true, false, false, true, false, true, true, false, true, false, true, true}},
|
||||
'Y': encodeInfo{34, []bool{true, true, false, false, true, false, true, true, false, true, false, true}},
|
||||
'Z': encodeInfo{35, []bool{true, false, false, true, true, false, true, true, false, true, false, true}},
|
||||
'-': encodeInfo{36, []bool{true, false, false, true, false, true, false, true, true, false, true, true}},
|
||||
'.': encodeInfo{37, []bool{true, true, false, false, true, false, true, false, true, true, false, true}},
|
||||
' ': encodeInfo{38, []bool{true, false, false, true, true, false, true, false, true, true, false, true}},
|
||||
'$': encodeInfo{39, []bool{true, false, false, true, false, false, true, false, false, true, false, true}},
|
||||
'/': encodeInfo{40, []bool{true, false, false, true, false, false, true, false, true, false, false, true}},
|
||||
'+': encodeInfo{41, []bool{true, false, false, true, false, true, false, false, true, false, false, true}},
|
||||
'%': encodeInfo{42, []bool{true, false, true, false, false, true, false, false, true, false, false, true}},
|
||||
'*': encodeInfo{-1, []bool{true, false, false, true, false, true, true, false, true, true, false, true}},
|
||||
}
|
||||
|
||||
var extendedTable = map[rune]string{
|
||||
0: `%U`, 1: `$A`, 2: `$B`, 3: `$C`, 4: `$D`, 5: `$E`, 6: `$F`, 7: `$G`, 8: `$H`, 9: `$I`, 10: `$J`,
|
||||
11: `$K`, 12: `$L`, 13: `$M`, 14: `$N`, 15: `$O`, 16: `$P`, 17: `$Q`, 18: `$R`, 19: `$S`, 20: `$T`,
|
||||
21: `$U`, 22: `$V`, 23: `$W`, 24: `$X`, 25: `$Y`, 26: `$Z`, 27: `%A`, 28: `%B`, 29: `%C`, 30: `%D`,
|
||||
31: `%E`, 33: `/A`, 34: `/B`, 35: `/C`, 36: `/D`, 37: `/E`, 38: `/F`, 39: `/G`, 40: `/H`, 41: `/I`,
|
||||
42: `/J`, 43: `/K`, 44: `/L`, 47: `/O`, 58: `/Z`, 59: `%F`, 60: `%G`, 61: `%H`, 62: `%I`, 63: `%J`,
|
||||
64: `%V`, 91: `%K`, 92: `%L`, 93: `%M`, 94: `%N`, 95: `%O`, 96: `%W`, 97: `+A`, 98: `+B`, 99: `+C`,
|
||||
100: `+D`, 101: `+E`, 102: `+F`, 103: `+G`, 104: `+H`, 105: `+I`, 106: `+J`, 107: `+K`, 108: `+L`,
|
||||
109: `+M`, 110: `+N`, 111: `+O`, 112: `+P`, 113: `+Q`, 114: `+R`, 115: `+S`, 116: `+T`, 117: `+U`,
|
||||
118: `+V`, 119: `+W`, 120: `+X`, 121: `+Y`, 122: `+Z`, 123: `%P`, 124: `%Q`, 125: `%R`, 126: `%S`,
|
||||
127: `%T`,
|
||||
}
|
||||
|
||||
func getChecksum(content string) string {
|
||||
sum := 0
|
||||
for _, r := range content {
|
||||
info, ok := encodeTable[r]
|
||||
if !ok || info.value < 0 {
|
||||
return "#"
|
||||
}
|
||||
|
||||
sum += info.value
|
||||
}
|
||||
|
||||
sum = sum % 43
|
||||
for r, v := range encodeTable {
|
||||
if v.value == sum {
|
||||
return string(r)
|
||||
}
|
||||
}
|
||||
return "#"
|
||||
}
|
||||
|
||||
func prepare(content string) (string, error) {
|
||||
result := ""
|
||||
for _, r := range content {
|
||||
if r > 127 {
|
||||
return "", errors.New("Only ASCII strings can be encoded")
|
||||
}
|
||||
val, ok := extendedTable[r]
|
||||
if ok {
|
||||
result += val
|
||||
} else {
|
||||
result += string([]rune{r})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Encode returns a code39 barcode for the given content
|
||||
// if includeChecksum is set to true, a checksum character is calculated and added to the content
|
||||
func Encode(content string, includeChecksum bool, fullASCIIMode bool) (barcode.BarcodeIntCS, error) {
|
||||
if fullASCIIMode {
|
||||
var err error
|
||||
content, err = prepare(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if strings.ContainsRune(content, '*') {
|
||||
return nil, errors.New("invalid data! try full ascii mode")
|
||||
}
|
||||
|
||||
data := "*" + content
|
||||
if includeChecksum {
|
||||
data += getChecksum(content)
|
||||
}
|
||||
data += "*"
|
||||
|
||||
result := new(utils.BitList)
|
||||
|
||||
for i, r := range data {
|
||||
if i != 0 {
|
||||
result.AddBit(false)
|
||||
}
|
||||
|
||||
info, ok := encodeTable[r]
|
||||
if !ok {
|
||||
return nil, errors.New("invalid data! try full ascii mode")
|
||||
}
|
||||
result.AddBit(info.data...)
|
||||
}
|
||||
|
||||
checkSum, err := strconv.ParseInt(getChecksum(content), 10, 64)
|
||||
if err != nil {
|
||||
checkSum = 0
|
||||
}
|
||||
return utils.New1DCodeIntCheckSum(barcode.TypeCode39, content, result, int(checkSum)), nil
|
||||
}
|
31
vendor/github.com/boombuler/barcode/code39/encoder_test.go
generated
vendored
Normal file
31
vendor/github.com/boombuler/barcode/code39/encoder_test.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
package code39
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func doTest(t *testing.T, addCS, fullASCII bool, data, testResult string) {
|
||||
code, err := Encode(data, addCS, fullASCII)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(testResult) != code.Bounds().Max.X {
|
||||
t.Errorf("Invalid code size. Expected %d got %d", len(testResult), code.Bounds().Max.X)
|
||||
}
|
||||
for i, r := range testResult {
|
||||
if (code.At(i, 0) == color.Black) != (r == '1') {
|
||||
t.Errorf("Failed at position %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Encode(t *testing.T) {
|
||||
doTest(t, false, false, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
||||
"1001011011010110101001011010110100101101101101001010101011001011011010110010101"+
|
||||
"011011001010101010011011011010100110101011010011010101011001101011010101001101011010"+
|
||||
"100110110110101001010101101001101101011010010101101101001010101011001101101010110010"+
|
||||
"101101011001010101101100101100101010110100110101011011001101010101001011010110110010"+
|
||||
"110101010011011010101010011011010110100101011010110010101101101100101010101001101011"+
|
||||
"011010011010101011001101010101001011011011010010110101011001011010100101101101")
|
||||
}
|
131
vendor/github.com/boombuler/barcode/code93/encoder.go
generated
vendored
Normal file
131
vendor/github.com/boombuler/barcode/code93/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Package code93 can create Code93 barcodes
|
||||
package code93
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type encodeInfo struct {
|
||||
value int
|
||||
data int
|
||||
}
|
||||
|
||||
const (
|
||||
// Special Function 1 ($)
|
||||
FNC1 = '\u00f1'
|
||||
// Special Function 2 (%)
|
||||
FNC2 = '\u00f2'
|
||||
// Special Function 3 (/)
|
||||
FNC3 = '\u00f3'
|
||||
// Special Function 4 (+)
|
||||
FNC4 = '\u00f4'
|
||||
)
|
||||
|
||||
var encodeTable = map[rune]encodeInfo{
|
||||
'0': encodeInfo{0, 0x114}, '1': encodeInfo{1, 0x148}, '2': encodeInfo{2, 0x144},
|
||||
'3': encodeInfo{3, 0x142}, '4': encodeInfo{4, 0x128}, '5': encodeInfo{5, 0x124},
|
||||
'6': encodeInfo{6, 0x122}, '7': encodeInfo{7, 0x150}, '8': encodeInfo{8, 0x112},
|
||||
'9': encodeInfo{9, 0x10A}, 'A': encodeInfo{10, 0x1A8}, 'B': encodeInfo{11, 0x1A4},
|
||||
'C': encodeInfo{12, 0x1A2}, 'D': encodeInfo{13, 0x194}, 'E': encodeInfo{14, 0x192},
|
||||
'F': encodeInfo{15, 0x18A}, 'G': encodeInfo{16, 0x168}, 'H': encodeInfo{17, 0x164},
|
||||
'I': encodeInfo{18, 0x162}, 'J': encodeInfo{19, 0x134}, 'K': encodeInfo{20, 0x11A},
|
||||
'L': encodeInfo{21, 0x158}, 'M': encodeInfo{22, 0x14C}, 'N': encodeInfo{23, 0x146},
|
||||
'O': encodeInfo{24, 0x12C}, 'P': encodeInfo{25, 0x116}, 'Q': encodeInfo{26, 0x1B4},
|
||||
'R': encodeInfo{27, 0x1B2}, 'S': encodeInfo{28, 0x1AC}, 'T': encodeInfo{29, 0x1A6},
|
||||
'U': encodeInfo{30, 0x196}, 'V': encodeInfo{31, 0x19A}, 'W': encodeInfo{32, 0x16C},
|
||||
'X': encodeInfo{33, 0x166}, 'Y': encodeInfo{34, 0x136}, 'Z': encodeInfo{35, 0x13A},
|
||||
'-': encodeInfo{36, 0x12E}, '.': encodeInfo{37, 0x1D4}, ' ': encodeInfo{38, 0x1D2},
|
||||
'$': encodeInfo{39, 0x1CA}, '/': encodeInfo{40, 0x16E}, '+': encodeInfo{41, 0x176},
|
||||
'%': encodeInfo{42, 0x1AE}, FNC1: encodeInfo{43, 0x126}, FNC2: encodeInfo{44, 0x1DA},
|
||||
FNC3: encodeInfo{45, 0x1D6}, FNC4: encodeInfo{46, 0x132}, '*': encodeInfo{47, 0x15E},
|
||||
}
|
||||
|
||||
var extendedTable = []string{
|
||||
"\u00f2U", "\u00f1A", "\u00f1B", "\u00f1C", "\u00f1D", "\u00f1E", "\u00f1F", "\u00f1G",
|
||||
"\u00f1H", "\u00f1I", "\u00f1J", "\u00f1K", "\u00f1L", "\u00f1M", "\u00f1N", "\u00f1O",
|
||||
"\u00f1P", "\u00f1Q", "\u00f1R", "\u00f1S", "\u00f1T", "\u00f1U", "\u00f1V", "\u00f1W",
|
||||
"\u00f1X", "\u00f1Y", "\u00f1Z", "\u00f2A", "\u00f2B", "\u00f2C", "\u00f2D", "\u00f2E",
|
||||
" ", "\u00f3A", "\u00f3B", "\u00f3C", "\u00f3D", "\u00f3E", "\u00f3F", "\u00f3G",
|
||||
"\u00f3H", "\u00f3I", "\u00f3J", "\u00f3K", "\u00f3L", "-", ".", "\u00f3O",
|
||||
"0", "1", "2", "3", "4", "5", "6", "7",
|
||||
"8", "9", "\u00f3Z", "\u00f2F", "\u00f2G", "\u00f2H", "\u00f2I", "\u00f2J",
|
||||
"\u00f2V", "A", "B", "C", "D", "E", "F", "G",
|
||||
"H", "I", "J", "K", "L", "M", "N", "O",
|
||||
"P", "Q", "R", "S", "T", "U", "V", "W",
|
||||
"X", "Y", "Z", "\u00f2K", "\u00f2L", "\u00f2M", "\u00f2N", "\u00f2O",
|
||||
"\u00f2W", "\u00f4A", "\u00f4B", "\u00f4C", "\u00f4D", "\u00f4E", "\u00f4F", "\u00f4G",
|
||||
"\u00f4H", "\u00f4I", "\u00f4J", "\u00f4K", "\u00f4L", "\u00f4M", "\u00f4N", "\u00f4O",
|
||||
"\u00f4P", "\u00f4Q", "\u00f4R", "\u00f4S", "\u00f4T", "\u00f4U", "\u00f4V", "\u00f4W",
|
||||
"\u00f4X", "\u00f4Y", "\u00f4Z", "\u00f2P", "\u00f2Q", "\u00f2R", "\u00f2S", "\u00f2T",
|
||||
}
|
||||
|
||||
func prepare(content string) (string, error) {
|
||||
result := ""
|
||||
for _, r := range content {
|
||||
if r > 127 {
|
||||
return "", errors.New("Only ASCII strings can be encoded")
|
||||
}
|
||||
result += extendedTable[int(r)]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Encode returns a code93 barcode for the given content
|
||||
// if includeChecksum is set to true, two checksum characters are calculated and added to the content
|
||||
func Encode(content string, includeChecksum bool, fullASCIIMode bool) (barcode.Barcode, error) {
|
||||
if fullASCIIMode {
|
||||
var err error
|
||||
content, err = prepare(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if strings.ContainsRune(content, '*') {
|
||||
return nil, errors.New("invalid data! content may not contain '*'")
|
||||
}
|
||||
|
||||
data := content + string(getChecksum(content, 20))
|
||||
data += string(getChecksum(data, 15))
|
||||
|
||||
data = "*" + data + "*"
|
||||
result := new(utils.BitList)
|
||||
|
||||
for _, r := range data {
|
||||
info, ok := encodeTable[r]
|
||||
if !ok {
|
||||
return nil, errors.New("invalid data!")
|
||||
}
|
||||
result.AddBits(info.data, 9)
|
||||
}
|
||||
result.AddBit(true)
|
||||
|
||||
return utils.New1DCode(barcode.TypeCode93, content, result), nil
|
||||
}
|
||||
|
||||
func getChecksum(content string, maxWeight int) rune {
|
||||
weight := 1
|
||||
total := 0
|
||||
|
||||
data := []rune(content)
|
||||
for i := len(data) - 1; i >= 0; i-- {
|
||||
r := data[i]
|
||||
info, ok := encodeTable[r]
|
||||
if !ok {
|
||||
return ' '
|
||||
}
|
||||
total += info.value * weight
|
||||
if weight++; weight > maxWeight {
|
||||
weight = 1
|
||||
}
|
||||
}
|
||||
total = total % 47
|
||||
for r, info := range encodeTable {
|
||||
if info.value == total {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return ' '
|
||||
}
|
39
vendor/github.com/boombuler/barcode/code93/encoder_test.go
generated
vendored
Normal file
39
vendor/github.com/boombuler/barcode/code93/encoder_test.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package code93
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func doTest(t *testing.T, data, testResult string) {
|
||||
code, err := Encode(data, true, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(testResult) != code.Bounds().Max.X {
|
||||
t.Errorf("Invalid code size. Expected %d got %d", len(testResult), code.Bounds().Max.X)
|
||||
}
|
||||
for i, r := range testResult {
|
||||
if (code.At(i, 0) == color.Black) != (r == '1') {
|
||||
t.Errorf("Failed at position %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_CheckSum(t *testing.T) {
|
||||
if r := getChecksum("TEST93", 20); r != '+' {
|
||||
t.Errorf("Checksum C-Failed. Got %s", string(r))
|
||||
}
|
||||
if r := getChecksum("TEST93+", 15); r != '6' {
|
||||
t.Errorf("Checksum K-Failed. Got %s", string(r))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Encode(t *testing.T) {
|
||||
doTest(t, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
||||
"1010111101101010001101001001101000101100101001100100101100010101011010001011001"+
|
||||
"001011000101001101001000110101010110001010011001010001101001011001000101101101101001"+
|
||||
"101100101101011001101001101100101101100110101011011001011001101001101101001110101000"+
|
||||
"101001010010001010001001010000101001010001001001001001000101010100001000100101000010"+
|
||||
"101001110101010000101010111101")
|
||||
}
|
213
vendor/github.com/boombuler/barcode/datamatrix/codelayout.go
generated
vendored
Normal file
213
vendor/github.com/boombuler/barcode/datamatrix/codelayout.go
generated
vendored
Normal file
|
@ -0,0 +1,213 @@
|
|||
package datamatrix
|
||||
|
||||
import (
|
||||
"github.com/boombuler/barcode/utils"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type setValFunc func(byte)
|
||||
|
||||
type codeLayout struct {
|
||||
matrix *utils.BitList
|
||||
occupy *utils.BitList
|
||||
size *dmCodeSize
|
||||
}
|
||||
|
||||
func newCodeLayout(size *dmCodeSize) *codeLayout {
|
||||
result := new(codeLayout)
|
||||
result.matrix = utils.NewBitList(size.MatrixColumns() * size.MatrixRows())
|
||||
result.occupy = utils.NewBitList(size.MatrixColumns() * size.MatrixRows())
|
||||
result.size = size
|
||||
return result
|
||||
}
|
||||
|
||||
func (l *codeLayout) Occupied(row, col int) bool {
|
||||
return l.occupy.GetBit(col + row*l.size.MatrixColumns())
|
||||
}
|
||||
|
||||
func (l *codeLayout) Set(row, col int, value, bitNum byte) {
|
||||
val := ((value >> (7 - bitNum)) & 1) == 1
|
||||
if row < 0 {
|
||||
row += l.size.MatrixRows()
|
||||
col += 4 - ((l.size.MatrixRows() + 4) % 8)
|
||||
}
|
||||
if col < 0 {
|
||||
col += l.size.MatrixColumns()
|
||||
row += 4 - ((l.size.MatrixColumns() + 4) % 8)
|
||||
}
|
||||
if l.Occupied(row, col) {
|
||||
panic("Field already occupied row: " + strconv.Itoa(row) + " col: " + strconv.Itoa(col))
|
||||
}
|
||||
|
||||
l.occupy.SetBit(col+row*l.size.MatrixColumns(), true)
|
||||
|
||||
l.matrix.SetBit(col+row*l.size.MatrixColumns(), val)
|
||||
}
|
||||
|
||||
func (l *codeLayout) SetSimple(row, col int, value byte) {
|
||||
l.Set(row-2, col-2, value, 0)
|
||||
l.Set(row-2, col-1, value, 1)
|
||||
l.Set(row-1, col-2, value, 2)
|
||||
l.Set(row-1, col-1, value, 3)
|
||||
l.Set(row-1, col-0, value, 4)
|
||||
l.Set(row-0, col-2, value, 5)
|
||||
l.Set(row-0, col-1, value, 6)
|
||||
l.Set(row-0, col-0, value, 7)
|
||||
}
|
||||
|
||||
func (l *codeLayout) Corner1(value byte) {
|
||||
l.Set(l.size.MatrixRows()-1, 0, value, 0)
|
||||
l.Set(l.size.MatrixRows()-1, 1, value, 1)
|
||||
l.Set(l.size.MatrixRows()-1, 2, value, 2)
|
||||
l.Set(0, l.size.MatrixColumns()-2, value, 3)
|
||||
l.Set(0, l.size.MatrixColumns()-1, value, 4)
|
||||
l.Set(1, l.size.MatrixColumns()-1, value, 5)
|
||||
l.Set(2, l.size.MatrixColumns()-1, value, 6)
|
||||
l.Set(3, l.size.MatrixColumns()-1, value, 7)
|
||||
}
|
||||
|
||||
func (l *codeLayout) Corner2(value byte) {
|
||||
l.Set(l.size.MatrixRows()-3, 0, value, 0)
|
||||
l.Set(l.size.MatrixRows()-2, 0, value, 1)
|
||||
l.Set(l.size.MatrixRows()-1, 0, value, 2)
|
||||
l.Set(0, l.size.MatrixColumns()-4, value, 3)
|
||||
l.Set(0, l.size.MatrixColumns()-3, value, 4)
|
||||
l.Set(0, l.size.MatrixColumns()-2, value, 5)
|
||||
l.Set(0, l.size.MatrixColumns()-1, value, 6)
|
||||
l.Set(1, l.size.MatrixColumns()-1, value, 7)
|
||||
}
|
||||
|
||||
func (l *codeLayout) Corner3(value byte) {
|
||||
l.Set(l.size.MatrixRows()-3, 0, value, 0)
|
||||
l.Set(l.size.MatrixRows()-2, 0, value, 1)
|
||||
l.Set(l.size.MatrixRows()-1, 0, value, 2)
|
||||
l.Set(0, l.size.MatrixColumns()-2, value, 3)
|
||||
l.Set(0, l.size.MatrixColumns()-1, value, 4)
|
||||
l.Set(1, l.size.MatrixColumns()-1, value, 5)
|
||||
l.Set(2, l.size.MatrixColumns()-1, value, 6)
|
||||
l.Set(3, l.size.MatrixColumns()-1, value, 7)
|
||||
}
|
||||
|
||||
func (l *codeLayout) Corner4(value byte) {
|
||||
l.Set(l.size.MatrixRows()-1, 0, value, 0)
|
||||
l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, value, 1)
|
||||
l.Set(0, l.size.MatrixColumns()-3, value, 2)
|
||||
l.Set(0, l.size.MatrixColumns()-2, value, 3)
|
||||
l.Set(0, l.size.MatrixColumns()-1, value, 4)
|
||||
l.Set(1, l.size.MatrixColumns()-3, value, 5)
|
||||
l.Set(1, l.size.MatrixColumns()-2, value, 6)
|
||||
l.Set(1, l.size.MatrixColumns()-1, value, 7)
|
||||
}
|
||||
|
||||
func (l *codeLayout) SetValues(data []byte) {
|
||||
idx := 0
|
||||
row := 4
|
||||
col := 0
|
||||
|
||||
for (row < l.size.MatrixRows()) || (col < l.size.MatrixColumns()) {
|
||||
if (row == l.size.MatrixRows()) && (col == 0) {
|
||||
l.Corner1(data[idx])
|
||||
idx++
|
||||
}
|
||||
if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%4 != 0) {
|
||||
l.Corner2(data[idx])
|
||||
idx++
|
||||
}
|
||||
if (row == l.size.MatrixRows()-2) && (col == 0) && (l.size.MatrixColumns()%8 == 4) {
|
||||
l.Corner3(data[idx])
|
||||
idx++
|
||||
}
|
||||
|
||||
if (row == l.size.MatrixRows()+4) && (col == 2) && (l.size.MatrixColumns()%8 == 0) {
|
||||
l.Corner4(data[idx])
|
||||
idx++
|
||||
}
|
||||
|
||||
for true {
|
||||
if (row < l.size.MatrixRows()) && (col >= 0) && !l.Occupied(row, col) {
|
||||
l.SetSimple(row, col, data[idx])
|
||||
idx++
|
||||
}
|
||||
row -= 2
|
||||
col += 2
|
||||
if (row < 0) || (col >= l.size.MatrixColumns()) {
|
||||
break
|
||||
}
|
||||
}
|
||||
row += 1
|
||||
col += 3
|
||||
|
||||
for true {
|
||||
if (row >= 0) && (col < l.size.MatrixColumns()) && !l.Occupied(row, col) {
|
||||
l.SetSimple(row, col, data[idx])
|
||||
idx++
|
||||
}
|
||||
row += 2
|
||||
col -= 2
|
||||
if (row >= l.size.MatrixRows()) || (col < 0) {
|
||||
break
|
||||
}
|
||||
}
|
||||
row += 3
|
||||
col += 1
|
||||
}
|
||||
|
||||
if !l.Occupied(l.size.MatrixRows()-1, l.size.MatrixColumns()-1) {
|
||||
l.Set(l.size.MatrixRows()-1, l.size.MatrixColumns()-1, 255, 0)
|
||||
l.Set(l.size.MatrixRows()-2, l.size.MatrixColumns()-2, 255, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *codeLayout) Merge() *datamatrixCode {
|
||||
result := newDataMatrixCode(l.size)
|
||||
|
||||
//dotted horizontal lines
|
||||
for r := 0; r < l.size.Rows; r += (l.size.RegionRows() + 2) {
|
||||
for c := 0; c < l.size.Columns; c += 2 {
|
||||
result.set(c, r, true)
|
||||
}
|
||||
}
|
||||
|
||||
//solid horizontal line
|
||||
for r := l.size.RegionRows() + 1; r < l.size.Rows; r += (l.size.RegionRows() + 2) {
|
||||
for c := 0; c < l.size.Columns; c++ {
|
||||
result.set(c, r, true)
|
||||
}
|
||||
}
|
||||
|
||||
//dotted vertical lines
|
||||
for c := l.size.RegionColumns() + 1; c < l.size.Columns; c += (l.size.RegionColumns() + 2) {
|
||||
for r := 1; r < l.size.Rows; r += 2 {
|
||||
result.set(c, r, true)
|
||||
}
|
||||
}
|
||||
|
||||
//solid vertical line
|
||||
for c := 0; c < l.size.Columns; c += (l.size.RegionColumns() + 2) {
|
||||
for r := 0; r < l.size.Rows; r++ {
|
||||
result.set(c, r, true)
|
||||
}
|
||||
}
|
||||
count := 0
|
||||
for hRegion := 0; hRegion < l.size.RegionCountHorizontal; hRegion++ {
|
||||
for vRegion := 0; vRegion < l.size.RegionCountVertical; vRegion++ {
|
||||
for x := 0; x < l.size.RegionColumns(); x++ {
|
||||
colMatrix := (l.size.RegionColumns() * hRegion) + x
|
||||
colResult := ((2 + l.size.RegionColumns()) * hRegion) + x + 1
|
||||
|
||||
for y := 0; y < l.size.RegionRows(); y++ {
|
||||
rowMatrix := (l.size.RegionRows() * vRegion) + y
|
||||
rowResult := ((2 + l.size.RegionRows()) * vRegion) + y + 1
|
||||
val := l.matrix.GetBit(colMatrix + rowMatrix*l.size.MatrixColumns())
|
||||
if val {
|
||||
count++
|
||||
}
|
||||
|
||||
result.set(colResult, rowResult, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
73
vendor/github.com/boombuler/barcode/datamatrix/codesize.go
generated
vendored
Normal file
73
vendor/github.com/boombuler/barcode/datamatrix/codesize.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
package datamatrix
|
||||
|
||||
type dmCodeSize struct {
|
||||
Rows int
|
||||
Columns int
|
||||
RegionCountHorizontal int
|
||||
RegionCountVertical int
|
||||
ECCCount int
|
||||
BlockCount int
|
||||
}
|
||||
|
||||
func (s *dmCodeSize) RegionRows() int {
|
||||
return (s.Rows - (s.RegionCountHorizontal * 2)) / s.RegionCountHorizontal
|
||||
}
|
||||
|
||||
func (s *dmCodeSize) RegionColumns() int {
|
||||
return (s.Columns - (s.RegionCountVertical * 2)) / s.RegionCountVertical
|
||||
}
|
||||
|
||||
func (s *dmCodeSize) MatrixRows() int {
|
||||
return s.RegionRows() * s.RegionCountHorizontal
|
||||
}
|
||||
|
||||
func (s *dmCodeSize) MatrixColumns() int {
|
||||
return s.RegionColumns() * s.RegionCountVertical
|
||||
}
|
||||
|
||||
func (s *dmCodeSize) DataCodewords() int {
|
||||
return ((s.MatrixColumns() * s.MatrixRows()) / 8) - s.ECCCount
|
||||
}
|
||||
|
||||
func (s *dmCodeSize) DataCodewordsForBlock(idx int) int {
|
||||
if s.Rows == 144 && s.Columns == 144 {
|
||||
// Special Case...
|
||||
if idx < 8 {
|
||||
return 156
|
||||
} else {
|
||||
return 155
|
||||
}
|
||||
}
|
||||
return s.DataCodewords() / s.BlockCount
|
||||
}
|
||||
|
||||
func (s *dmCodeSize) ErrorCorrectionCodewordsPerBlock() int {
|
||||
return s.ECCCount / s.BlockCount
|
||||
}
|
||||
|
||||
var codeSizes []*dmCodeSize = []*dmCodeSize{
|
||||
&dmCodeSize{10, 10, 1, 1, 5, 1},
|
||||
&dmCodeSize{12, 12, 1, 1, 7, 1},
|
||||
&dmCodeSize{14, 14, 1, 1, 10, 1},
|
||||
&dmCodeSize{16, 16, 1, 1, 12, 1},
|
||||
&dmCodeSize{18, 18, 1, 1, 14, 1},
|
||||
&dmCodeSize{20, 20, 1, 1, 18, 1},
|
||||
&dmCodeSize{22, 22, 1, 1, 20, 1},
|
||||
&dmCodeSize{24, 24, 1, 1, 24, 1},
|
||||
&dmCodeSize{26, 26, 1, 1, 28, 1},
|
||||
&dmCodeSize{32, 32, 2, 2, 36, 1},
|
||||
&dmCodeSize{36, 36, 2, 2, 42, 1},
|
||||
&dmCodeSize{40, 40, 2, 2, 48, 1},
|
||||
&dmCodeSize{44, 44, 2, 2, 56, 1},
|
||||
&dmCodeSize{48, 48, 2, 2, 68, 1},
|
||||
&dmCodeSize{52, 52, 2, 2, 84, 2},
|
||||
&dmCodeSize{64, 64, 4, 4, 112, 2},
|
||||
&dmCodeSize{72, 72, 4, 4, 144, 4},
|
||||
&dmCodeSize{80, 80, 4, 4, 192, 4},
|
||||
&dmCodeSize{88, 88, 4, 4, 224, 4},
|
||||
&dmCodeSize{96, 96, 4, 4, 272, 4},
|
||||
&dmCodeSize{104, 104, 4, 4, 336, 6},
|
||||
&dmCodeSize{120, 120, 6, 6, 408, 6},
|
||||
&dmCodeSize{132, 132, 6, 6, 496, 8},
|
||||
&dmCodeSize{144, 144, 6, 6, 620, 10},
|
||||
}
|
102
vendor/github.com/boombuler/barcode/datamatrix/datamatrix_test.go
generated
vendored
Normal file
102
vendor/github.com/boombuler/barcode/datamatrix/datamatrix_test.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
package datamatrix
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func codeFromStr(str string, size *dmCodeSize) *datamatrixCode {
|
||||
code := newDataMatrixCode(size)
|
||||
idx := 0
|
||||
for _, r := range str {
|
||||
x := idx % size.Columns
|
||||
y := idx / size.Columns
|
||||
|
||||
switch r {
|
||||
case '#':
|
||||
code.set(x, y, true)
|
||||
case '.':
|
||||
code.set(x, y, false)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
idx++
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func Test_Issue12(t *testing.T) {
|
||||
data := `{"po":12,"batchAction":"start_end"}`
|
||||
realData := addPadding(encodeText(data), 36)
|
||||
wantedData := []byte{124, 35, 113, 112, 35, 59, 142, 45, 35, 99, 98, 117, 100, 105, 66, 100, 117, 106, 112, 111, 35, 59, 35, 116, 117, 98, 115, 117, 96, 102, 111, 101, 35, 126, 129, 181}
|
||||
|
||||
if bytes.Compare(realData, wantedData) != 0 {
|
||||
t.Error("Data Encoding failed")
|
||||
return
|
||||
}
|
||||
|
||||
var codeSize *dmCodeSize
|
||||
for _, s := range codeSizes {
|
||||
if s.DataCodewords() >= len(wantedData) {
|
||||
codeSize = s
|
||||
break
|
||||
}
|
||||
}
|
||||
realECC := ec.calcECC(realData, codeSize)[len(realData):]
|
||||
wantedECC := []byte{196, 53, 147, 192, 151, 213, 107, 61, 98, 251, 50, 71, 186, 15, 43, 111, 165, 243, 209, 79, 128, 109, 251, 4}
|
||||
if bytes.Compare(realECC, wantedECC) != 0 {
|
||||
t.Errorf("Error correction calculation failed\nGot: %v", realECC)
|
||||
return
|
||||
}
|
||||
|
||||
barcode := `
|
||||
#.#.#.#.#.#.#.#.#.#.#.#.
|
||||
#....###..#..#....#...##
|
||||
##.......#...#.#.#....#.
|
||||
#.###...##..#...##.##..#
|
||||
##...####..##..#.#.#.##.
|
||||
#.###.##.###..#######.##
|
||||
#..###...##.##..#.##.##.
|
||||
#.#.#.#.#.#.###....#.#.#
|
||||
##.#...#.#.#..#...#####.
|
||||
#...####..#...##..#.#..#
|
||||
##...#...##.###.#.....#.
|
||||
#.###.#.##.#.....###..##
|
||||
##..#####...#..##...###.
|
||||
###...#.####.##.#.#.#..#
|
||||
#..###..#.#.####.#.###..
|
||||
###.#.#..#..#.###.#.##.#
|
||||
#####.##.###..#.####.#..
|
||||
#.##.#......#.#..#.#.###
|
||||
###.#....######.#...##..
|
||||
##...#..##.###..#...####
|
||||
#.######.###.##..#...##.
|
||||
#..#..#.##.#..####...#.#
|
||||
###.###..#..##.#.##...#.
|
||||
########################`
|
||||
|
||||
bc, err := Encode(data)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
realResult := bc.(*datamatrixCode)
|
||||
if realResult.Columns != 24 || realResult.Rows != 24 {
|
||||
t.Errorf("Got wrong barcode size %dx%d", realResult.Columns, realResult.Rows)
|
||||
return
|
||||
}
|
||||
|
||||
wantedResult := codeFromStr(barcode, realResult.dmCodeSize)
|
||||
|
||||
for x := 0; x < wantedResult.Columns; x++ {
|
||||
for y := 0; y < wantedResult.Rows; y++ {
|
||||
r := realResult.get(x, y)
|
||||
w := wantedResult.get(x, y)
|
||||
if w != r {
|
||||
t.Errorf("Failed at: c%d/r%d", x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
50
vendor/github.com/boombuler/barcode/datamatrix/datamatrixcode.go
generated
vendored
Normal file
50
vendor/github.com/boombuler/barcode/datamatrix/datamatrixcode.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
package datamatrix
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type datamatrixCode struct {
|
||||
*utils.BitList
|
||||
*dmCodeSize
|
||||
content string
|
||||
}
|
||||
|
||||
func newDataMatrixCode(size *dmCodeSize) *datamatrixCode {
|
||||
return &datamatrixCode{utils.NewBitList(size.Rows * size.Columns), size, ""}
|
||||
}
|
||||
|
||||
func (c *datamatrixCode) Content() string {
|
||||
return c.content
|
||||
}
|
||||
|
||||
func (c *datamatrixCode) Metadata() barcode.Metadata {
|
||||
return barcode.Metadata{barcode.TypeDataMatrix, 2}
|
||||
}
|
||||
|
||||
func (c *datamatrixCode) ColorModel() color.Model {
|
||||
return color.Gray16Model
|
||||
}
|
||||
|
||||
func (c *datamatrixCode) Bounds() image.Rectangle {
|
||||
return image.Rect(0, 0, c.Columns, c.Rows)
|
||||
}
|
||||
|
||||
func (c *datamatrixCode) At(x, y int) color.Color {
|
||||
if c.get(x, y) {
|
||||
return color.Black
|
||||
}
|
||||
return color.White
|
||||
}
|
||||
|
||||
func (c *datamatrixCode) get(x, y int) bool {
|
||||
return c.GetBit(x*c.Rows + y)
|
||||
}
|
||||
|
||||
func (c *datamatrixCode) set(x, y int, value bool) {
|
||||
c.SetBit(x*c.Rows+y, value)
|
||||
}
|
75
vendor/github.com/boombuler/barcode/datamatrix/encoder.go
generated
vendored
Normal file
75
vendor/github.com/boombuler/barcode/datamatrix/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
// Package datamatrix can create Datamatrix barcodes
|
||||
package datamatrix
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
)
|
||||
|
||||
// Encode returns a Datamatrix barcode for the given content
|
||||
func Encode(content string) (barcode.Barcode, error) {
|
||||
data := encodeText(content)
|
||||
|
||||
var size *dmCodeSize
|
||||
for _, s := range codeSizes {
|
||||
if s.DataCodewords() >= len(data) {
|
||||
size = s
|
||||
break
|
||||
}
|
||||
}
|
||||
if size == nil {
|
||||
return nil, errors.New("to much data to encode")
|
||||
}
|
||||
data = addPadding(data, size.DataCodewords())
|
||||
data = ec.calcECC(data, size)
|
||||
code := render(data, size)
|
||||
if code != nil {
|
||||
code.content = content
|
||||
return code, nil
|
||||
}
|
||||
return nil, errors.New("unable to render barcode")
|
||||
}
|
||||
|
||||
func render(data []byte, size *dmCodeSize) *datamatrixCode {
|
||||
cl := newCodeLayout(size)
|
||||
|
||||
cl.SetValues(data)
|
||||
|
||||
return cl.Merge()
|
||||
}
|
||||
|
||||
func encodeText(content string) []byte {
|
||||
var result []byte
|
||||
input := []byte(content)
|
||||
|
||||
for i := 0; i < len(input); {
|
||||
c := input[i]
|
||||
i++
|
||||
|
||||
if c >= '0' && c <= '9' && i < len(input) && input[i] >= '0' && input[i] <= '9' {
|
||||
// two numbers...
|
||||
c2 := input[i]
|
||||
i++
|
||||
cw := byte(((c-'0')*10 + (c2 - '0')) + 130)
|
||||
result = append(result, cw)
|
||||
} else if c > 127 {
|
||||
// not correct... needs to be redone later...
|
||||
result = append(result, 235, c-127)
|
||||
} else {
|
||||
result = append(result, c+1)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func addPadding(data []byte, toCount int) []byte {
|
||||
if len(data) < toCount {
|
||||
data = append(data, 129)
|
||||
}
|
||||
for len(data) < toCount {
|
||||
R := ((149 * (len(data) + 1)) % 253) + 1
|
||||
data = append(data, byte((129+R)%254))
|
||||
}
|
||||
return data
|
||||
}
|
45
vendor/github.com/boombuler/barcode/datamatrix/errorcorrection.go
generated
vendored
Normal file
45
vendor/github.com/boombuler/barcode/datamatrix/errorcorrection.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
package datamatrix
|
||||
|
||||
import (
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type errorCorrection struct {
|
||||
rs *utils.ReedSolomonEncoder
|
||||
}
|
||||
|
||||
var ec *errorCorrection = newErrorCorrection()
|
||||
|
||||
func newErrorCorrection() *errorCorrection {
|
||||
gf := utils.NewGaloisField(301, 256, 1)
|
||||
|
||||
return &errorCorrection{utils.NewReedSolomonEncoder(gf)}
|
||||
}
|
||||
|
||||
func (ec *errorCorrection) calcECC(data []byte, size *dmCodeSize) []byte {
|
||||
dataSize := len(data)
|
||||
// make some space for error correction codes
|
||||
data = append(data, make([]byte, size.ECCCount)...)
|
||||
|
||||
for block := 0; block < size.BlockCount; block++ {
|
||||
dataCnt := size.DataCodewordsForBlock(block)
|
||||
|
||||
buff := make([]int, dataCnt)
|
||||
// copy the data for the current block to buff
|
||||
j := 0
|
||||
for i := block; i < dataSize; i += size.BlockCount {
|
||||
buff[j] = int(data[i])
|
||||
j++
|
||||
}
|
||||
// calc the error correction codes
|
||||
ecc := ec.rs.Encode(buff, size.ErrorCorrectionCodewordsPerBlock())
|
||||
// and append them to the result
|
||||
j = 0
|
||||
for i := block; i < size.ErrorCorrectionCodewordsPerBlock()*size.BlockCount; i += size.BlockCount {
|
||||
data[dataSize+i] = byte(ecc[j])
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
28
vendor/github.com/boombuler/barcode/datamatrix/errorcorrection_test.go
generated
vendored
Normal file
28
vendor/github.com/boombuler/barcode/datamatrix/errorcorrection_test.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
package datamatrix
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_CalcECC(t *testing.T) {
|
||||
data := []byte{142, 164, 186}
|
||||
var size *dmCodeSize = nil
|
||||
for _, s := range codeSizes {
|
||||
if s.DataCodewords() >= len(data) {
|
||||
size = s
|
||||
break
|
||||
}
|
||||
}
|
||||
if size == nil {
|
||||
t.Error("size not found")
|
||||
}
|
||||
|
||||
if bytes.Compare(ec.calcECC(data, size), []byte{142, 164, 186, 114, 25, 5, 88, 102}) != 0 {
|
||||
t.Error("ECC Test 1 failed")
|
||||
}
|
||||
data = []byte{66, 129, 70}
|
||||
if bytes.Compare(ec.calcECC(data, size), []byte{66, 129, 70, 138, 234, 82, 82, 95}) != 0 {
|
||||
t.Error("ECC Test 2 failed")
|
||||
}
|
||||
}
|
187
vendor/github.com/boombuler/barcode/ean/encoder.go
generated
vendored
Normal file
187
vendor/github.com/boombuler/barcode/ean/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,187 @@
|
|||
// Package ean can create EAN 8 and EAN 13 barcodes.
|
||||
package ean
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type encodedNumber struct {
|
||||
LeftOdd []bool
|
||||
LeftEven []bool
|
||||
Right []bool
|
||||
CheckSum []bool
|
||||
}
|
||||
|
||||
var encoderTable = map[rune]encodedNumber{
|
||||
'0': encodedNumber{
|
||||
[]bool{false, false, false, true, true, false, true},
|
||||
[]bool{false, true, false, false, true, true, true},
|
||||
[]bool{true, true, true, false, false, true, false},
|
||||
[]bool{false, false, false, false, false, false},
|
||||
},
|
||||
'1': encodedNumber{
|
||||
[]bool{false, false, true, true, false, false, true},
|
||||
[]bool{false, true, true, false, false, true, true},
|
||||
[]bool{true, true, false, false, true, true, false},
|
||||
[]bool{false, false, true, false, true, true},
|
||||
},
|
||||
'2': encodedNumber{
|
||||
[]bool{false, false, true, false, false, true, true},
|
||||
[]bool{false, false, true, true, false, true, true},
|
||||
[]bool{true, true, false, true, true, false, false},
|
||||
[]bool{false, false, true, true, false, true},
|
||||
},
|
||||
'3': encodedNumber{
|
||||
[]bool{false, true, true, true, true, false, true},
|
||||
[]bool{false, true, false, false, false, false, true},
|
||||
[]bool{true, false, false, false, false, true, false},
|
||||
[]bool{false, false, true, true, true, false},
|
||||
},
|
||||
'4': encodedNumber{
|
||||
[]bool{false, true, false, false, false, true, true},
|
||||
[]bool{false, false, true, true, true, false, true},
|
||||
[]bool{true, false, true, true, true, false, false},
|
||||
[]bool{false, true, false, false, true, true},
|
||||
},
|
||||
'5': encodedNumber{
|
||||
[]bool{false, true, true, false, false, false, true},
|
||||
[]bool{false, true, true, true, false, false, true},
|
||||
[]bool{true, false, false, true, true, true, false},
|
||||
[]bool{false, true, true, false, false, true},
|
||||
},
|
||||
'6': encodedNumber{
|
||||
[]bool{false, true, false, true, true, true, true},
|
||||
[]bool{false, false, false, false, true, false, true},
|
||||
[]bool{true, false, true, false, false, false, false},
|
||||
[]bool{false, true, true, true, false, false},
|
||||
},
|
||||
'7': encodedNumber{
|
||||
[]bool{false, true, true, true, false, true, true},
|
||||
[]bool{false, false, true, false, false, false, true},
|
||||
[]bool{true, false, false, false, true, false, false},
|
||||
[]bool{false, true, false, true, false, true},
|
||||
},
|
||||
'8': encodedNumber{
|
||||
[]bool{false, true, true, false, true, true, true},
|
||||
[]bool{false, false, false, true, false, false, true},
|
||||
[]bool{true, false, false, true, false, false, false},
|
||||
[]bool{false, true, false, true, true, false},
|
||||
},
|
||||
'9': encodedNumber{
|
||||
[]bool{false, false, false, true, false, true, true},
|
||||
[]bool{false, false, true, false, true, true, true},
|
||||
[]bool{true, true, true, false, true, false, false},
|
||||
[]bool{false, true, true, false, true, false},
|
||||
},
|
||||
}
|
||||
|
||||
func calcCheckNum(code string) rune {
|
||||
x3 := len(code) == 7
|
||||
sum := 0
|
||||
for _, r := range code {
|
||||
curNum := utils.RuneToInt(r)
|
||||
if curNum < 0 || curNum > 9 {
|
||||
return 'B'
|
||||
}
|
||||
if x3 {
|
||||
curNum = curNum * 3
|
||||
}
|
||||
x3 = !x3
|
||||
sum += curNum
|
||||
}
|
||||
|
||||
return utils.IntToRune((10 - (sum % 10)) % 10)
|
||||
}
|
||||
|
||||
func encodeEAN8(code string) *utils.BitList {
|
||||
result := new(utils.BitList)
|
||||
result.AddBit(true, false, true)
|
||||
|
||||
for cpos, r := range code {
|
||||
num, ok := encoderTable[r]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
var data []bool
|
||||
if cpos < 4 {
|
||||
data = num.LeftOdd
|
||||
} else {
|
||||
data = num.Right
|
||||
}
|
||||
|
||||
if cpos == 4 {
|
||||
result.AddBit(false, true, false, true, false)
|
||||
}
|
||||
result.AddBit(data...)
|
||||
}
|
||||
result.AddBit(true, false, true)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func encodeEAN13(code string) *utils.BitList {
|
||||
result := new(utils.BitList)
|
||||
result.AddBit(true, false, true)
|
||||
|
||||
var firstNum []bool
|
||||
for cpos, r := range code {
|
||||
num, ok := encoderTable[r]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if cpos == 0 {
|
||||
firstNum = num.CheckSum
|
||||
continue
|
||||
}
|
||||
|
||||
var data []bool
|
||||
if cpos < 7 { // Left
|
||||
if firstNum[cpos-1] {
|
||||
data = num.LeftEven
|
||||
} else {
|
||||
data = num.LeftOdd
|
||||
}
|
||||
} else {
|
||||
data = num.Right
|
||||
}
|
||||
|
||||
if cpos == 7 {
|
||||
result.AddBit(false, true, false, true, false)
|
||||
}
|
||||
result.AddBit(data...)
|
||||
}
|
||||
result.AddBit(true, false, true)
|
||||
return result
|
||||
}
|
||||
|
||||
// Encode returns a EAN 8 or EAN 13 barcode for the given code
|
||||
func Encode(code string) (barcode.BarcodeIntCS, error) {
|
||||
var checkSum int
|
||||
if len(code) == 7 || len(code) == 12 {
|
||||
code += string(calcCheckNum(code))
|
||||
checkSum = utils.RuneToInt(calcCheckNum(code))
|
||||
} else if len(code) == 8 || len(code) == 13 {
|
||||
check := code[0 : len(code)-1]
|
||||
check += string(calcCheckNum(check))
|
||||
if check != code {
|
||||
return nil, errors.New("checksum missmatch")
|
||||
}
|
||||
checkSum = utils.RuneToInt(rune(code[len(code)-1]))
|
||||
}
|
||||
|
||||
if len(code) == 8 {
|
||||
result := encodeEAN8(code)
|
||||
if result != nil {
|
||||
return utils.New1DCodeIntCheckSum(barcode.TypeEAN8, code, result, checkSum), nil
|
||||
}
|
||||
} else if len(code) == 13 {
|
||||
result := encodeEAN13(code)
|
||||
if result != nil {
|
||||
return utils.New1DCodeIntCheckSum(barcode.TypeEAN13, code, result, checkSum), nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("invalid ean code data")
|
||||
}
|
46
vendor/github.com/boombuler/barcode/ean/encoder_test.go
generated
vendored
Normal file
46
vendor/github.com/boombuler/barcode/ean/encoder_test.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
package ean
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testHelper(t *testing.T, testCode, testResult, kind string, checkMetadata bool) {
|
||||
code, err := Encode(testCode)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if checkMetadata && (code.Metadata().Dimensions != 1 || code.Content() != testCode || code.Metadata().CodeKind != kind) {
|
||||
t.Error("Metadata missmatch")
|
||||
}
|
||||
if len(testResult) != code.Bounds().Max.X {
|
||||
t.Fail()
|
||||
}
|
||||
for i, r := range testResult {
|
||||
if (code.At(i, 0) == color.Black) != (r == '1') {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_EncodeEAN(t *testing.T) {
|
||||
testHelper(t, "5901234123457", "10100010110100111011001100100110111101001110101010110011011011001000010101110010011101000100101", "EAN 13", true)
|
||||
testHelper(t, "55123457", "1010110001011000100110010010011010101000010101110010011101000100101", "EAN 8", true)
|
||||
testHelper(t, "5512345", "1010110001011000100110010010011010101000010101110010011101000100101", "EAN 8", false)
|
||||
_, err := Encode("55123458") //<-- Invalid checksum
|
||||
if err == nil {
|
||||
t.Error("Invalid checksum not detected")
|
||||
}
|
||||
_, err = Encode("invalid")
|
||||
if err == nil {
|
||||
t.Error("\"invalid\" should not be encodable")
|
||||
}
|
||||
_, err = Encode("invalid")
|
||||
if err == nil {
|
||||
t.Error("\"invalid\" should not be encodable")
|
||||
}
|
||||
bits := encodeEAN13("invalid error")
|
||||
if bits != nil {
|
||||
t.Error("\"invalid error\" should not be encodable")
|
||||
}
|
||||
}
|
416
vendor/github.com/boombuler/barcode/pdf417/codewords.go
generated
vendored
Normal file
416
vendor/github.com/boombuler/barcode/pdf417/codewords.go
generated
vendored
Normal file
|
@ -0,0 +1,416 @@
|
|||
package pdf417
|
||||
|
||||
const start_word = 0x1fea8
|
||||
const stop_word = 0x3fa29
|
||||
|
||||
var codewords = [][]int{
|
||||
[]int{
|
||||
0x1d5c0, 0x1eaf0, 0x1f57c, 0x1d4e0, 0x1ea78, 0x1f53e, 0x1a8c0,
|
||||
0x1d470, 0x1a860, 0x15040, 0x1a830, 0x15020, 0x1adc0, 0x1d6f0,
|
||||
0x1eb7c, 0x1ace0, 0x1d678, 0x1eb3e, 0x158c0, 0x1ac70, 0x15860,
|
||||
0x15dc0, 0x1aef0, 0x1d77c, 0x15ce0, 0x1ae78, 0x1d73e, 0x15c70,
|
||||
0x1ae3c, 0x15ef0, 0x1af7c, 0x15e78, 0x1af3e, 0x15f7c, 0x1f5fa,
|
||||
0x1d2e0, 0x1e978, 0x1f4be, 0x1a4c0, 0x1d270, 0x1e93c, 0x1a460,
|
||||
0x1d238, 0x14840, 0x1a430, 0x1d21c, 0x14820, 0x1a418, 0x14810,
|
||||
0x1a6e0, 0x1d378, 0x1e9be, 0x14cc0, 0x1a670, 0x1d33c, 0x14c60,
|
||||
0x1a638, 0x1d31e, 0x14c30, 0x1a61c, 0x14ee0, 0x1a778, 0x1d3be,
|
||||
0x14e70, 0x1a73c, 0x14e38, 0x1a71e, 0x14f78, 0x1a7be, 0x14f3c,
|
||||
0x14f1e, 0x1a2c0, 0x1d170, 0x1e8bc, 0x1a260, 0x1d138, 0x1e89e,
|
||||
0x14440, 0x1a230, 0x1d11c, 0x14420, 0x1a218, 0x14410, 0x14408,
|
||||
0x146c0, 0x1a370, 0x1d1bc, 0x14660, 0x1a338, 0x1d19e, 0x14630,
|
||||
0x1a31c, 0x14618, 0x1460c, 0x14770, 0x1a3bc, 0x14738, 0x1a39e,
|
||||
0x1471c, 0x147bc, 0x1a160, 0x1d0b8, 0x1e85e, 0x14240, 0x1a130,
|
||||
0x1d09c, 0x14220, 0x1a118, 0x1d08e, 0x14210, 0x1a10c, 0x14208,
|
||||
0x1a106, 0x14360, 0x1a1b8, 0x1d0de, 0x14330, 0x1a19c, 0x14318,
|
||||
0x1a18e, 0x1430c, 0x14306, 0x1a1de, 0x1438e, 0x14140, 0x1a0b0,
|
||||
0x1d05c, 0x14120, 0x1a098, 0x1d04e, 0x14110, 0x1a08c, 0x14108,
|
||||
0x1a086, 0x14104, 0x141b0, 0x14198, 0x1418c, 0x140a0, 0x1d02e,
|
||||
0x1a04c, 0x1a046, 0x14082, 0x1cae0, 0x1e578, 0x1f2be, 0x194c0,
|
||||
0x1ca70, 0x1e53c, 0x19460, 0x1ca38, 0x1e51e, 0x12840, 0x19430,
|
||||
0x12820, 0x196e0, 0x1cb78, 0x1e5be, 0x12cc0, 0x19670, 0x1cb3c,
|
||||
0x12c60, 0x19638, 0x12c30, 0x12c18, 0x12ee0, 0x19778, 0x1cbbe,
|
||||
0x12e70, 0x1973c, 0x12e38, 0x12e1c, 0x12f78, 0x197be, 0x12f3c,
|
||||
0x12fbe, 0x1dac0, 0x1ed70, 0x1f6bc, 0x1da60, 0x1ed38, 0x1f69e,
|
||||
0x1b440, 0x1da30, 0x1ed1c, 0x1b420, 0x1da18, 0x1ed0e, 0x1b410,
|
||||
0x1da0c, 0x192c0, 0x1c970, 0x1e4bc, 0x1b6c0, 0x19260, 0x1c938,
|
||||
0x1e49e, 0x1b660, 0x1db38, 0x1ed9e, 0x16c40, 0x12420, 0x19218,
|
||||
0x1c90e, 0x16c20, 0x1b618, 0x16c10, 0x126c0, 0x19370, 0x1c9bc,
|
||||
0x16ec0, 0x12660, 0x19338, 0x1c99e, 0x16e60, 0x1b738, 0x1db9e,
|
||||
0x16e30, 0x12618, 0x16e18, 0x12770, 0x193bc, 0x16f70, 0x12738,
|
||||
0x1939e, 0x16f38, 0x1b79e, 0x16f1c, 0x127bc, 0x16fbc, 0x1279e,
|
||||
0x16f9e, 0x1d960, 0x1ecb8, 0x1f65e, 0x1b240, 0x1d930, 0x1ec9c,
|
||||
0x1b220, 0x1d918, 0x1ec8e, 0x1b210, 0x1d90c, 0x1b208, 0x1b204,
|
||||
0x19160, 0x1c8b8, 0x1e45e, 0x1b360, 0x19130, 0x1c89c, 0x16640,
|
||||
0x12220, 0x1d99c, 0x1c88e, 0x16620, 0x12210, 0x1910c, 0x16610,
|
||||
0x1b30c, 0x19106, 0x12204, 0x12360, 0x191b8, 0x1c8de, 0x16760,
|
||||
0x12330, 0x1919c, 0x16730, 0x1b39c, 0x1918e, 0x16718, 0x1230c,
|
||||
0x12306, 0x123b8, 0x191de, 0x167b8, 0x1239c, 0x1679c, 0x1238e,
|
||||
0x1678e, 0x167de, 0x1b140, 0x1d8b0, 0x1ec5c, 0x1b120, 0x1d898,
|
||||
0x1ec4e, 0x1b110, 0x1d88c, 0x1b108, 0x1d886, 0x1b104, 0x1b102,
|
||||
0x12140, 0x190b0, 0x1c85c, 0x16340, 0x12120, 0x19098, 0x1c84e,
|
||||
0x16320, 0x1b198, 0x1d8ce, 0x16310, 0x12108, 0x19086, 0x16308,
|
||||
0x1b186, 0x16304, 0x121b0, 0x190dc, 0x163b0, 0x12198, 0x190ce,
|
||||
0x16398, 0x1b1ce, 0x1638c, 0x12186, 0x16386, 0x163dc, 0x163ce,
|
||||
0x1b0a0, 0x1d858, 0x1ec2e, 0x1b090, 0x1d84c, 0x1b088, 0x1d846,
|
||||
0x1b084, 0x1b082, 0x120a0, 0x19058, 0x1c82e, 0x161a0, 0x12090,
|
||||
0x1904c, 0x16190, 0x1b0cc, 0x19046, 0x16188, 0x12084, 0x16184,
|
||||
0x12082, 0x120d8, 0x161d8, 0x161cc, 0x161c6, 0x1d82c, 0x1d826,
|
||||
0x1b042, 0x1902c, 0x12048, 0x160c8, 0x160c4, 0x160c2, 0x18ac0,
|
||||
0x1c570, 0x1e2bc, 0x18a60, 0x1c538, 0x11440, 0x18a30, 0x1c51c,
|
||||
0x11420, 0x18a18, 0x11410, 0x11408, 0x116c0, 0x18b70, 0x1c5bc,
|
||||
0x11660, 0x18b38, 0x1c59e, 0x11630, 0x18b1c, 0x11618, 0x1160c,
|
||||
0x11770, 0x18bbc, 0x11738, 0x18b9e, 0x1171c, 0x117bc, 0x1179e,
|
||||
0x1cd60, 0x1e6b8, 0x1f35e, 0x19a40, 0x1cd30, 0x1e69c, 0x19a20,
|
||||
0x1cd18, 0x1e68e, 0x19a10, 0x1cd0c, 0x19a08, 0x1cd06, 0x18960,
|
||||
0x1c4b8, 0x1e25e, 0x19b60, 0x18930, 0x1c49c, 0x13640, 0x11220,
|
||||
0x1cd9c, 0x1c48e, 0x13620, 0x19b18, 0x1890c, 0x13610, 0x11208,
|
||||
0x13608, 0x11360, 0x189b8, 0x1c4de, 0x13760, 0x11330, 0x1cdde,
|
||||
0x13730, 0x19b9c, 0x1898e, 0x13718, 0x1130c, 0x1370c, 0x113b8,
|
||||
0x189de, 0x137b8, 0x1139c, 0x1379c, 0x1138e, 0x113de, 0x137de,
|
||||
0x1dd40, 0x1eeb0, 0x1f75c, 0x1dd20, 0x1ee98, 0x1f74e, 0x1dd10,
|
||||
0x1ee8c, 0x1dd08, 0x1ee86, 0x1dd04, 0x19940, 0x1ccb0, 0x1e65c,
|
||||
0x1bb40, 0x19920, 0x1eedc, 0x1e64e, 0x1bb20, 0x1dd98, 0x1eece,
|
||||
0x1bb10, 0x19908, 0x1cc86, 0x1bb08, 0x1dd86, 0x19902, 0x11140,
|
||||
0x188b0, 0x1c45c, 0x13340, 0x11120, 0x18898, 0x1c44e, 0x17740,
|
||||
0x13320, 0x19998, 0x1ccce, 0x17720, 0x1bb98, 0x1ddce, 0x18886,
|
||||
0x17710, 0x13308, 0x19986, 0x17708, 0x11102, 0x111b0, 0x188dc,
|
||||
0x133b0, 0x11198, 0x188ce, 0x177b0, 0x13398, 0x199ce, 0x17798,
|
||||
0x1bbce, 0x11186, 0x13386, 0x111dc, 0x133dc, 0x111ce, 0x177dc,
|
||||
0x133ce, 0x1dca0, 0x1ee58, 0x1f72e, 0x1dc90, 0x1ee4c, 0x1dc88,
|
||||
0x1ee46, 0x1dc84, 0x1dc82, 0x198a0, 0x1cc58, 0x1e62e, 0x1b9a0,
|
||||
0x19890, 0x1ee6e, 0x1b990, 0x1dccc, 0x1cc46, 0x1b988, 0x19884,
|
||||
0x1b984, 0x19882, 0x1b982, 0x110a0, 0x18858, 0x1c42e, 0x131a0,
|
||||
0x11090, 0x1884c, 0x173a0, 0x13190, 0x198cc, 0x18846, 0x17390,
|
||||
0x1b9cc, 0x11084, 0x17388, 0x13184, 0x11082, 0x13182, 0x110d8,
|
||||
0x1886e, 0x131d8, 0x110cc, 0x173d8, 0x131cc, 0x110c6, 0x173cc,
|
||||
0x131c6, 0x110ee, 0x173ee, 0x1dc50, 0x1ee2c, 0x1dc48, 0x1ee26,
|
||||
0x1dc44, 0x1dc42, 0x19850, 0x1cc2c, 0x1b8d0, 0x19848, 0x1cc26,
|
||||
0x1b8c8, 0x1dc66, 0x1b8c4, 0x19842, 0x1b8c2, 0x11050, 0x1882c,
|
||||
0x130d0, 0x11048, 0x18826, 0x171d0, 0x130c8, 0x19866, 0x171c8,
|
||||
0x1b8e6, 0x11042, 0x171c4, 0x130c2, 0x171c2, 0x130ec, 0x171ec,
|
||||
0x171e6, 0x1ee16, 0x1dc22, 0x1cc16, 0x19824, 0x19822, 0x11028,
|
||||
0x13068, 0x170e8, 0x11022, 0x13062, 0x18560, 0x10a40, 0x18530,
|
||||
0x10a20, 0x18518, 0x1c28e, 0x10a10, 0x1850c, 0x10a08, 0x18506,
|
||||
0x10b60, 0x185b8, 0x1c2de, 0x10b30, 0x1859c, 0x10b18, 0x1858e,
|
||||
0x10b0c, 0x10b06, 0x10bb8, 0x185de, 0x10b9c, 0x10b8e, 0x10bde,
|
||||
0x18d40, 0x1c6b0, 0x1e35c, 0x18d20, 0x1c698, 0x18d10, 0x1c68c,
|
||||
0x18d08, 0x1c686, 0x18d04, 0x10940, 0x184b0, 0x1c25c, 0x11b40,
|
||||
0x10920, 0x1c6dc, 0x1c24e, 0x11b20, 0x18d98, 0x1c6ce, 0x11b10,
|
||||
0x10908, 0x18486, 0x11b08, 0x18d86, 0x10902, 0x109b0, 0x184dc,
|
||||
0x11bb0, 0x10998, 0x184ce, 0x11b98, 0x18dce, 0x11b8c, 0x10986,
|
||||
0x109dc, 0x11bdc, 0x109ce, 0x11bce, 0x1cea0, 0x1e758, 0x1f3ae,
|
||||
0x1ce90, 0x1e74c, 0x1ce88, 0x1e746, 0x1ce84, 0x1ce82, 0x18ca0,
|
||||
0x1c658, 0x19da0, 0x18c90, 0x1c64c, 0x19d90, 0x1cecc, 0x1c646,
|
||||
0x19d88, 0x18c84, 0x19d84, 0x18c82, 0x19d82, 0x108a0, 0x18458,
|
||||
0x119a0, 0x10890, 0x1c66e, 0x13ba0, 0x11990, 0x18ccc, 0x18446,
|
||||
0x13b90, 0x19dcc, 0x10884, 0x13b88, 0x11984, 0x10882, 0x11982,
|
||||
0x108d8, 0x1846e, 0x119d8, 0x108cc, 0x13bd8, 0x119cc, 0x108c6,
|
||||
0x13bcc, 0x119c6, 0x108ee, 0x119ee, 0x13bee, 0x1ef50, 0x1f7ac,
|
||||
0x1ef48, 0x1f7a6, 0x1ef44, 0x1ef42, 0x1ce50, 0x1e72c, 0x1ded0,
|
||||
0x1ef6c, 0x1e726, 0x1dec8, 0x1ef66, 0x1dec4, 0x1ce42, 0x1dec2,
|
||||
0x18c50, 0x1c62c, 0x19cd0, 0x18c48, 0x1c626, 0x1bdd0, 0x19cc8,
|
||||
0x1ce66, 0x1bdc8, 0x1dee6, 0x18c42, 0x1bdc4, 0x19cc2, 0x1bdc2,
|
||||
0x10850, 0x1842c, 0x118d0, 0x10848, 0x18426, 0x139d0, 0x118c8,
|
||||
0x18c66, 0x17bd0, 0x139c8, 0x19ce6, 0x10842, 0x17bc8, 0x1bde6,
|
||||
0x118c2, 0x17bc4, 0x1086c, 0x118ec, 0x10866, 0x139ec, 0x118e6,
|
||||
0x17bec, 0x139e6, 0x17be6, 0x1ef28, 0x1f796, 0x1ef24, 0x1ef22,
|
||||
0x1ce28, 0x1e716, 0x1de68, 0x1ef36, 0x1de64, 0x1ce22, 0x1de62,
|
||||
0x18c28, 0x1c616, 0x19c68, 0x18c24, 0x1bce8, 0x19c64, 0x18c22,
|
||||
0x1bce4, 0x19c62, 0x1bce2, 0x10828, 0x18416, 0x11868, 0x18c36,
|
||||
0x138e8, 0x11864, 0x10822, 0x179e8, 0x138e4, 0x11862, 0x179e4,
|
||||
0x138e2, 0x179e2, 0x11876, 0x179f6, 0x1ef12, 0x1de34, 0x1de32,
|
||||
0x19c34, 0x1bc74, 0x1bc72, 0x11834, 0x13874, 0x178f4, 0x178f2,
|
||||
0x10540, 0x10520, 0x18298, 0x10510, 0x10508, 0x10504, 0x105b0,
|
||||
0x10598, 0x1058c, 0x10586, 0x105dc, 0x105ce, 0x186a0, 0x18690,
|
||||
0x1c34c, 0x18688, 0x1c346, 0x18684, 0x18682, 0x104a0, 0x18258,
|
||||
0x10da0, 0x186d8, 0x1824c, 0x10d90, 0x186cc, 0x10d88, 0x186c6,
|
||||
0x10d84, 0x10482, 0x10d82, 0x104d8, 0x1826e, 0x10dd8, 0x186ee,
|
||||
0x10dcc, 0x104c6, 0x10dc6, 0x104ee, 0x10dee, 0x1c750, 0x1c748,
|
||||
0x1c744, 0x1c742, 0x18650, 0x18ed0, 0x1c76c, 0x1c326, 0x18ec8,
|
||||
0x1c766, 0x18ec4, 0x18642, 0x18ec2, 0x10450, 0x10cd0, 0x10448,
|
||||
0x18226, 0x11dd0, 0x10cc8, 0x10444, 0x11dc8, 0x10cc4, 0x10442,
|
||||
0x11dc4, 0x10cc2, 0x1046c, 0x10cec, 0x10466, 0x11dec, 0x10ce6,
|
||||
0x11de6, 0x1e7a8, 0x1e7a4, 0x1e7a2, 0x1c728, 0x1cf68, 0x1e7b6,
|
||||
0x1cf64, 0x1c722, 0x1cf62, 0x18628, 0x1c316, 0x18e68, 0x1c736,
|
||||
0x19ee8, 0x18e64, 0x18622, 0x19ee4, 0x18e62, 0x19ee2, 0x10428,
|
||||
0x18216, 0x10c68, 0x18636, 0x11ce8, 0x10c64, 0x10422, 0x13de8,
|
||||
0x11ce4, 0x10c62, 0x13de4, 0x11ce2, 0x10436, 0x10c76, 0x11cf6,
|
||||
0x13df6, 0x1f7d4, 0x1f7d2, 0x1e794, 0x1efb4, 0x1e792, 0x1efb2,
|
||||
0x1c714, 0x1cf34, 0x1c712, 0x1df74, 0x1cf32, 0x1df72, 0x18614,
|
||||
0x18e34, 0x18612, 0x19e74, 0x18e32, 0x1bef4,
|
||||
},
|
||||
[]int{
|
||||
0x1f560, 0x1fab8, 0x1ea40, 0x1f530, 0x1fa9c, 0x1ea20, 0x1f518,
|
||||
0x1fa8e, 0x1ea10, 0x1f50c, 0x1ea08, 0x1f506, 0x1ea04, 0x1eb60,
|
||||
0x1f5b8, 0x1fade, 0x1d640, 0x1eb30, 0x1f59c, 0x1d620, 0x1eb18,
|
||||
0x1f58e, 0x1d610, 0x1eb0c, 0x1d608, 0x1eb06, 0x1d604, 0x1d760,
|
||||
0x1ebb8, 0x1f5de, 0x1ae40, 0x1d730, 0x1eb9c, 0x1ae20, 0x1d718,
|
||||
0x1eb8e, 0x1ae10, 0x1d70c, 0x1ae08, 0x1d706, 0x1ae04, 0x1af60,
|
||||
0x1d7b8, 0x1ebde, 0x15e40, 0x1af30, 0x1d79c, 0x15e20, 0x1af18,
|
||||
0x1d78e, 0x15e10, 0x1af0c, 0x15e08, 0x1af06, 0x15f60, 0x1afb8,
|
||||
0x1d7de, 0x15f30, 0x1af9c, 0x15f18, 0x1af8e, 0x15f0c, 0x15fb8,
|
||||
0x1afde, 0x15f9c, 0x15f8e, 0x1e940, 0x1f4b0, 0x1fa5c, 0x1e920,
|
||||
0x1f498, 0x1fa4e, 0x1e910, 0x1f48c, 0x1e908, 0x1f486, 0x1e904,
|
||||
0x1e902, 0x1d340, 0x1e9b0, 0x1f4dc, 0x1d320, 0x1e998, 0x1f4ce,
|
||||
0x1d310, 0x1e98c, 0x1d308, 0x1e986, 0x1d304, 0x1d302, 0x1a740,
|
||||
0x1d3b0, 0x1e9dc, 0x1a720, 0x1d398, 0x1e9ce, 0x1a710, 0x1d38c,
|
||||
0x1a708, 0x1d386, 0x1a704, 0x1a702, 0x14f40, 0x1a7b0, 0x1d3dc,
|
||||
0x14f20, 0x1a798, 0x1d3ce, 0x14f10, 0x1a78c, 0x14f08, 0x1a786,
|
||||
0x14f04, 0x14fb0, 0x1a7dc, 0x14f98, 0x1a7ce, 0x14f8c, 0x14f86,
|
||||
0x14fdc, 0x14fce, 0x1e8a0, 0x1f458, 0x1fa2e, 0x1e890, 0x1f44c,
|
||||
0x1e888, 0x1f446, 0x1e884, 0x1e882, 0x1d1a0, 0x1e8d8, 0x1f46e,
|
||||
0x1d190, 0x1e8cc, 0x1d188, 0x1e8c6, 0x1d184, 0x1d182, 0x1a3a0,
|
||||
0x1d1d8, 0x1e8ee, 0x1a390, 0x1d1cc, 0x1a388, 0x1d1c6, 0x1a384,
|
||||
0x1a382, 0x147a0, 0x1a3d8, 0x1d1ee, 0x14790, 0x1a3cc, 0x14788,
|
||||
0x1a3c6, 0x14784, 0x14782, 0x147d8, 0x1a3ee, 0x147cc, 0x147c6,
|
||||
0x147ee, 0x1e850, 0x1f42c, 0x1e848, 0x1f426, 0x1e844, 0x1e842,
|
||||
0x1d0d0, 0x1e86c, 0x1d0c8, 0x1e866, 0x1d0c4, 0x1d0c2, 0x1a1d0,
|
||||
0x1d0ec, 0x1a1c8, 0x1d0e6, 0x1a1c4, 0x1a1c2, 0x143d0, 0x1a1ec,
|
||||
0x143c8, 0x1a1e6, 0x143c4, 0x143c2, 0x143ec, 0x143e6, 0x1e828,
|
||||
0x1f416, 0x1e824, 0x1e822, 0x1d068, 0x1e836, 0x1d064, 0x1d062,
|
||||
0x1a0e8, 0x1d076, 0x1a0e4, 0x1a0e2, 0x141e8, 0x1a0f6, 0x141e4,
|
||||
0x141e2, 0x1e814, 0x1e812, 0x1d034, 0x1d032, 0x1a074, 0x1a072,
|
||||
0x1e540, 0x1f2b0, 0x1f95c, 0x1e520, 0x1f298, 0x1f94e, 0x1e510,
|
||||
0x1f28c, 0x1e508, 0x1f286, 0x1e504, 0x1e502, 0x1cb40, 0x1e5b0,
|
||||
0x1f2dc, 0x1cb20, 0x1e598, 0x1f2ce, 0x1cb10, 0x1e58c, 0x1cb08,
|
||||
0x1e586, 0x1cb04, 0x1cb02, 0x19740, 0x1cbb0, 0x1e5dc, 0x19720,
|
||||
0x1cb98, 0x1e5ce, 0x19710, 0x1cb8c, 0x19708, 0x1cb86, 0x19704,
|
||||
0x19702, 0x12f40, 0x197b0, 0x1cbdc, 0x12f20, 0x19798, 0x1cbce,
|
||||
0x12f10, 0x1978c, 0x12f08, 0x19786, 0x12f04, 0x12fb0, 0x197dc,
|
||||
0x12f98, 0x197ce, 0x12f8c, 0x12f86, 0x12fdc, 0x12fce, 0x1f6a0,
|
||||
0x1fb58, 0x16bf0, 0x1f690, 0x1fb4c, 0x169f8, 0x1f688, 0x1fb46,
|
||||
0x168fc, 0x1f684, 0x1f682, 0x1e4a0, 0x1f258, 0x1f92e, 0x1eda0,
|
||||
0x1e490, 0x1fb6e, 0x1ed90, 0x1f6cc, 0x1f246, 0x1ed88, 0x1e484,
|
||||
0x1ed84, 0x1e482, 0x1ed82, 0x1c9a0, 0x1e4d8, 0x1f26e, 0x1dba0,
|
||||
0x1c990, 0x1e4cc, 0x1db90, 0x1edcc, 0x1e4c6, 0x1db88, 0x1c984,
|
||||
0x1db84, 0x1c982, 0x1db82, 0x193a0, 0x1c9d8, 0x1e4ee, 0x1b7a0,
|
||||
0x19390, 0x1c9cc, 0x1b790, 0x1dbcc, 0x1c9c6, 0x1b788, 0x19384,
|
||||
0x1b784, 0x19382, 0x1b782, 0x127a0, 0x193d8, 0x1c9ee, 0x16fa0,
|
||||
0x12790, 0x193cc, 0x16f90, 0x1b7cc, 0x193c6, 0x16f88, 0x12784,
|
||||
0x16f84, 0x12782, 0x127d8, 0x193ee, 0x16fd8, 0x127cc, 0x16fcc,
|
||||
0x127c6, 0x16fc6, 0x127ee, 0x1f650, 0x1fb2c, 0x165f8, 0x1f648,
|
||||
0x1fb26, 0x164fc, 0x1f644, 0x1647e, 0x1f642, 0x1e450, 0x1f22c,
|
||||
0x1ecd0, 0x1e448, 0x1f226, 0x1ecc8, 0x1f666, 0x1ecc4, 0x1e442,
|
||||
0x1ecc2, 0x1c8d0, 0x1e46c, 0x1d9d0, 0x1c8c8, 0x1e466, 0x1d9c8,
|
||||
0x1ece6, 0x1d9c4, 0x1c8c2, 0x1d9c2, 0x191d0, 0x1c8ec, 0x1b3d0,
|
||||
0x191c8, 0x1c8e6, 0x1b3c8, 0x1d9e6, 0x1b3c4, 0x191c2, 0x1b3c2,
|
||||
0x123d0, 0x191ec, 0x167d0, 0x123c8, 0x191e6, 0x167c8, 0x1b3e6,
|
||||
0x167c4, 0x123c2, 0x167c2, 0x123ec, 0x167ec, 0x123e6, 0x167e6,
|
||||
0x1f628, 0x1fb16, 0x162fc, 0x1f624, 0x1627e, 0x1f622, 0x1e428,
|
||||
0x1f216, 0x1ec68, 0x1f636, 0x1ec64, 0x1e422, 0x1ec62, 0x1c868,
|
||||
0x1e436, 0x1d8e8, 0x1c864, 0x1d8e4, 0x1c862, 0x1d8e2, 0x190e8,
|
||||
0x1c876, 0x1b1e8, 0x1d8f6, 0x1b1e4, 0x190e2, 0x1b1e2, 0x121e8,
|
||||
0x190f6, 0x163e8, 0x121e4, 0x163e4, 0x121e2, 0x163e2, 0x121f6,
|
||||
0x163f6, 0x1f614, 0x1617e, 0x1f612, 0x1e414, 0x1ec34, 0x1e412,
|
||||
0x1ec32, 0x1c834, 0x1d874, 0x1c832, 0x1d872, 0x19074, 0x1b0f4,
|
||||
0x19072, 0x1b0f2, 0x120f4, 0x161f4, 0x120f2, 0x161f2, 0x1f60a,
|
||||
0x1e40a, 0x1ec1a, 0x1c81a, 0x1d83a, 0x1903a, 0x1b07a, 0x1e2a0,
|
||||
0x1f158, 0x1f8ae, 0x1e290, 0x1f14c, 0x1e288, 0x1f146, 0x1e284,
|
||||
0x1e282, 0x1c5a0, 0x1e2d8, 0x1f16e, 0x1c590, 0x1e2cc, 0x1c588,
|
||||
0x1e2c6, 0x1c584, 0x1c582, 0x18ba0, 0x1c5d8, 0x1e2ee, 0x18b90,
|
||||
0x1c5cc, 0x18b88, 0x1c5c6, 0x18b84, 0x18b82, 0x117a0, 0x18bd8,
|
||||
0x1c5ee, 0x11790, 0x18bcc, 0x11788, 0x18bc6, 0x11784, 0x11782,
|
||||
0x117d8, 0x18bee, 0x117cc, 0x117c6, 0x117ee, 0x1f350, 0x1f9ac,
|
||||
0x135f8, 0x1f348, 0x1f9a6, 0x134fc, 0x1f344, 0x1347e, 0x1f342,
|
||||
0x1e250, 0x1f12c, 0x1e6d0, 0x1e248, 0x1f126, 0x1e6c8, 0x1f366,
|
||||
0x1e6c4, 0x1e242, 0x1e6c2, 0x1c4d0, 0x1e26c, 0x1cdd0, 0x1c4c8,
|
||||
0x1e266, 0x1cdc8, 0x1e6e6, 0x1cdc4, 0x1c4c2, 0x1cdc2, 0x189d0,
|
||||
0x1c4ec, 0x19bd0, 0x189c8, 0x1c4e6, 0x19bc8, 0x1cde6, 0x19bc4,
|
||||
0x189c2, 0x19bc2, 0x113d0, 0x189ec, 0x137d0, 0x113c8, 0x189e6,
|
||||
0x137c8, 0x19be6, 0x137c4, 0x113c2, 0x137c2, 0x113ec, 0x137ec,
|
||||
0x113e6, 0x137e6, 0x1fba8, 0x175f0, 0x1bafc, 0x1fba4, 0x174f8,
|
||||
0x1ba7e, 0x1fba2, 0x1747c, 0x1743e, 0x1f328, 0x1f996, 0x132fc,
|
||||
0x1f768, 0x1fbb6, 0x176fc, 0x1327e, 0x1f764, 0x1f322, 0x1767e,
|
||||
0x1f762, 0x1e228, 0x1f116, 0x1e668, 0x1e224, 0x1eee8, 0x1f776,
|
||||
0x1e222, 0x1eee4, 0x1e662, 0x1eee2, 0x1c468, 0x1e236, 0x1cce8,
|
||||
0x1c464, 0x1dde8, 0x1cce4, 0x1c462, 0x1dde4, 0x1cce2, 0x1dde2,
|
||||
0x188e8, 0x1c476, 0x199e8, 0x188e4, 0x1bbe8, 0x199e4, 0x188e2,
|
||||
0x1bbe4, 0x199e2, 0x1bbe2, 0x111e8, 0x188f6, 0x133e8, 0x111e4,
|
||||
0x177e8, 0x133e4, 0x111e2, 0x177e4, 0x133e2, 0x177e2, 0x111f6,
|
||||
0x133f6, 0x1fb94, 0x172f8, 0x1b97e, 0x1fb92, 0x1727c, 0x1723e,
|
||||
0x1f314, 0x1317e, 0x1f734, 0x1f312, 0x1737e, 0x1f732, 0x1e214,
|
||||
0x1e634, 0x1e212, 0x1ee74, 0x1e632, 0x1ee72, 0x1c434, 0x1cc74,
|
||||
0x1c432, 0x1dcf4, 0x1cc72, 0x1dcf2, 0x18874, 0x198f4, 0x18872,
|
||||
0x1b9f4, 0x198f2, 0x1b9f2, 0x110f4, 0x131f4, 0x110f2, 0x173f4,
|
||||
0x131f2, 0x173f2, 0x1fb8a, 0x1717c, 0x1713e, 0x1f30a, 0x1f71a,
|
||||
0x1e20a, 0x1e61a, 0x1ee3a, 0x1c41a, 0x1cc3a, 0x1dc7a, 0x1883a,
|
||||
0x1987a, 0x1b8fa, 0x1107a, 0x130fa, 0x171fa, 0x170be, 0x1e150,
|
||||
0x1f0ac, 0x1e148, 0x1f0a6, 0x1e144, 0x1e142, 0x1c2d0, 0x1e16c,
|
||||
0x1c2c8, 0x1e166, 0x1c2c4, 0x1c2c2, 0x185d0, 0x1c2ec, 0x185c8,
|
||||
0x1c2e6, 0x185c4, 0x185c2, 0x10bd0, 0x185ec, 0x10bc8, 0x185e6,
|
||||
0x10bc4, 0x10bc2, 0x10bec, 0x10be6, 0x1f1a8, 0x1f8d6, 0x11afc,
|
||||
0x1f1a4, 0x11a7e, 0x1f1a2, 0x1e128, 0x1f096, 0x1e368, 0x1e124,
|
||||
0x1e364, 0x1e122, 0x1e362, 0x1c268, 0x1e136, 0x1c6e8, 0x1c264,
|
||||
0x1c6e4, 0x1c262, 0x1c6e2, 0x184e8, 0x1c276, 0x18de8, 0x184e4,
|
||||
0x18de4, 0x184e2, 0x18de2, 0x109e8, 0x184f6, 0x11be8, 0x109e4,
|
||||
0x11be4, 0x109e2, 0x11be2, 0x109f6, 0x11bf6, 0x1f9d4, 0x13af8,
|
||||
0x19d7e, 0x1f9d2, 0x13a7c, 0x13a3e, 0x1f194, 0x1197e, 0x1f3b4,
|
||||
0x1f192, 0x13b7e, 0x1f3b2, 0x1e114, 0x1e334, 0x1e112, 0x1e774,
|
||||
0x1e332, 0x1e772, 0x1c234, 0x1c674, 0x1c232, 0x1cef4, 0x1c672,
|
||||
0x1cef2, 0x18474, 0x18cf4, 0x18472, 0x19df4, 0x18cf2, 0x19df2,
|
||||
0x108f4, 0x119f4, 0x108f2, 0x13bf4, 0x119f2, 0x13bf2, 0x17af0,
|
||||
0x1bd7c, 0x17a78, 0x1bd3e, 0x17a3c, 0x17a1e, 0x1f9ca, 0x1397c,
|
||||
0x1fbda, 0x17b7c, 0x1393e, 0x17b3e, 0x1f18a, 0x1f39a, 0x1f7ba,
|
||||
0x1e10a, 0x1e31a, 0x1e73a, 0x1ef7a, 0x1c21a, 0x1c63a, 0x1ce7a,
|
||||
0x1defa, 0x1843a, 0x18c7a, 0x19cfa, 0x1bdfa, 0x1087a, 0x118fa,
|
||||
0x139fa, 0x17978, 0x1bcbe, 0x1793c, 0x1791e, 0x138be, 0x179be,
|
||||
0x178bc, 0x1789e, 0x1785e, 0x1e0a8, 0x1e0a4, 0x1e0a2, 0x1c168,
|
||||
0x1e0b6, 0x1c164, 0x1c162, 0x182e8, 0x1c176, 0x182e4, 0x182e2,
|
||||
0x105e8, 0x182f6, 0x105e4, 0x105e2, 0x105f6, 0x1f0d4, 0x10d7e,
|
||||
0x1f0d2, 0x1e094, 0x1e1b4, 0x1e092, 0x1e1b2, 0x1c134, 0x1c374,
|
||||
0x1c132, 0x1c372, 0x18274, 0x186f4, 0x18272, 0x186f2, 0x104f4,
|
||||
0x10df4, 0x104f2, 0x10df2, 0x1f8ea, 0x11d7c, 0x11d3e, 0x1f0ca,
|
||||
0x1f1da, 0x1e08a, 0x1e19a, 0x1e3ba, 0x1c11a, 0x1c33a, 0x1c77a,
|
||||
0x1823a, 0x1867a, 0x18efa, 0x1047a, 0x10cfa, 0x11dfa, 0x13d78,
|
||||
0x19ebe, 0x13d3c, 0x13d1e, 0x11cbe, 0x13dbe, 0x17d70, 0x1bebc,
|
||||
0x17d38, 0x1be9e, 0x17d1c, 0x17d0e, 0x13cbc, 0x17dbc, 0x13c9e,
|
||||
0x17d9e, 0x17cb8, 0x1be5e, 0x17c9c, 0x17c8e, 0x13c5e, 0x17cde,
|
||||
0x17c5c, 0x17c4e, 0x17c2e, 0x1c0b4, 0x1c0b2, 0x18174, 0x18172,
|
||||
0x102f4, 0x102f2, 0x1e0da, 0x1c09a, 0x1c1ba, 0x1813a, 0x1837a,
|
||||
0x1027a, 0x106fa, 0x10ebe, 0x11ebc, 0x11e9e, 0x13eb8, 0x19f5e,
|
||||
0x13e9c, 0x13e8e, 0x11e5e, 0x13ede, 0x17eb0, 0x1bf5c, 0x17e98,
|
||||
0x1bf4e, 0x17e8c, 0x17e86, 0x13e5c, 0x17edc, 0x13e4e, 0x17ece,
|
||||
0x17e58, 0x1bf2e, 0x17e4c, 0x17e46, 0x13e2e, 0x17e6e, 0x17e2c,
|
||||
0x17e26, 0x10f5e, 0x11f5c, 0x11f4e, 0x13f58, 0x19fae, 0x13f4c,
|
||||
0x13f46, 0x11f2e, 0x13f6e, 0x13f2c, 0x13f26,
|
||||
},
|
||||
[]int{
|
||||
0x1abe0, 0x1d5f8, 0x153c0, 0x1a9f0, 0x1d4fc, 0x151e0, 0x1a8f8,
|
||||
0x1d47e, 0x150f0, 0x1a87c, 0x15078, 0x1fad0, 0x15be0, 0x1adf8,
|
||||
0x1fac8, 0x159f0, 0x1acfc, 0x1fac4, 0x158f8, 0x1ac7e, 0x1fac2,
|
||||
0x1587c, 0x1f5d0, 0x1faec, 0x15df8, 0x1f5c8, 0x1fae6, 0x15cfc,
|
||||
0x1f5c4, 0x15c7e, 0x1f5c2, 0x1ebd0, 0x1f5ec, 0x1ebc8, 0x1f5e6,
|
||||
0x1ebc4, 0x1ebc2, 0x1d7d0, 0x1ebec, 0x1d7c8, 0x1ebe6, 0x1d7c4,
|
||||
0x1d7c2, 0x1afd0, 0x1d7ec, 0x1afc8, 0x1d7e6, 0x1afc4, 0x14bc0,
|
||||
0x1a5f0, 0x1d2fc, 0x149e0, 0x1a4f8, 0x1d27e, 0x148f0, 0x1a47c,
|
||||
0x14878, 0x1a43e, 0x1483c, 0x1fa68, 0x14df0, 0x1a6fc, 0x1fa64,
|
||||
0x14cf8, 0x1a67e, 0x1fa62, 0x14c7c, 0x14c3e, 0x1f4e8, 0x1fa76,
|
||||
0x14efc, 0x1f4e4, 0x14e7e, 0x1f4e2, 0x1e9e8, 0x1f4f6, 0x1e9e4,
|
||||
0x1e9e2, 0x1d3e8, 0x1e9f6, 0x1d3e4, 0x1d3e2, 0x1a7e8, 0x1d3f6,
|
||||
0x1a7e4, 0x1a7e2, 0x145e0, 0x1a2f8, 0x1d17e, 0x144f0, 0x1a27c,
|
||||
0x14478, 0x1a23e, 0x1443c, 0x1441e, 0x1fa34, 0x146f8, 0x1a37e,
|
||||
0x1fa32, 0x1467c, 0x1463e, 0x1f474, 0x1477e, 0x1f472, 0x1e8f4,
|
||||
0x1e8f2, 0x1d1f4, 0x1d1f2, 0x1a3f4, 0x1a3f2, 0x142f0, 0x1a17c,
|
||||
0x14278, 0x1a13e, 0x1423c, 0x1421e, 0x1fa1a, 0x1437c, 0x1433e,
|
||||
0x1f43a, 0x1e87a, 0x1d0fa, 0x14178, 0x1a0be, 0x1413c, 0x1411e,
|
||||
0x141be, 0x140bc, 0x1409e, 0x12bc0, 0x195f0, 0x1cafc, 0x129e0,
|
||||
0x194f8, 0x1ca7e, 0x128f0, 0x1947c, 0x12878, 0x1943e, 0x1283c,
|
||||
0x1f968, 0x12df0, 0x196fc, 0x1f964, 0x12cf8, 0x1967e, 0x1f962,
|
||||
0x12c7c, 0x12c3e, 0x1f2e8, 0x1f976, 0x12efc, 0x1f2e4, 0x12e7e,
|
||||
0x1f2e2, 0x1e5e8, 0x1f2f6, 0x1e5e4, 0x1e5e2, 0x1cbe8, 0x1e5f6,
|
||||
0x1cbe4, 0x1cbe2, 0x197e8, 0x1cbf6, 0x197e4, 0x197e2, 0x1b5e0,
|
||||
0x1daf8, 0x1ed7e, 0x169c0, 0x1b4f0, 0x1da7c, 0x168e0, 0x1b478,
|
||||
0x1da3e, 0x16870, 0x1b43c, 0x16838, 0x1b41e, 0x1681c, 0x125e0,
|
||||
0x192f8, 0x1c97e, 0x16de0, 0x124f0, 0x1927c, 0x16cf0, 0x1b67c,
|
||||
0x1923e, 0x16c78, 0x1243c, 0x16c3c, 0x1241e, 0x16c1e, 0x1f934,
|
||||
0x126f8, 0x1937e, 0x1fb74, 0x1f932, 0x16ef8, 0x1267c, 0x1fb72,
|
||||
0x16e7c, 0x1263e, 0x16e3e, 0x1f274, 0x1277e, 0x1f6f4, 0x1f272,
|
||||
0x16f7e, 0x1f6f2, 0x1e4f4, 0x1edf4, 0x1e4f2, 0x1edf2, 0x1c9f4,
|
||||
0x1dbf4, 0x1c9f2, 0x1dbf2, 0x193f4, 0x193f2, 0x165c0, 0x1b2f0,
|
||||
0x1d97c, 0x164e0, 0x1b278, 0x1d93e, 0x16470, 0x1b23c, 0x16438,
|
||||
0x1b21e, 0x1641c, 0x1640e, 0x122f0, 0x1917c, 0x166f0, 0x12278,
|
||||
0x1913e, 0x16678, 0x1b33e, 0x1663c, 0x1221e, 0x1661e, 0x1f91a,
|
||||
0x1237c, 0x1fb3a, 0x1677c, 0x1233e, 0x1673e, 0x1f23a, 0x1f67a,
|
||||
0x1e47a, 0x1ecfa, 0x1c8fa, 0x1d9fa, 0x191fa, 0x162e0, 0x1b178,
|
||||
0x1d8be, 0x16270, 0x1b13c, 0x16238, 0x1b11e, 0x1621c, 0x1620e,
|
||||
0x12178, 0x190be, 0x16378, 0x1213c, 0x1633c, 0x1211e, 0x1631e,
|
||||
0x121be, 0x163be, 0x16170, 0x1b0bc, 0x16138, 0x1b09e, 0x1611c,
|
||||
0x1610e, 0x120bc, 0x161bc, 0x1209e, 0x1619e, 0x160b8, 0x1b05e,
|
||||
0x1609c, 0x1608e, 0x1205e, 0x160de, 0x1605c, 0x1604e, 0x115e0,
|
||||
0x18af8, 0x1c57e, 0x114f0, 0x18a7c, 0x11478, 0x18a3e, 0x1143c,
|
||||
0x1141e, 0x1f8b4, 0x116f8, 0x18b7e, 0x1f8b2, 0x1167c, 0x1163e,
|
||||
0x1f174, 0x1177e, 0x1f172, 0x1e2f4, 0x1e2f2, 0x1c5f4, 0x1c5f2,
|
||||
0x18bf4, 0x18bf2, 0x135c0, 0x19af0, 0x1cd7c, 0x134e0, 0x19a78,
|
||||
0x1cd3e, 0x13470, 0x19a3c, 0x13438, 0x19a1e, 0x1341c, 0x1340e,
|
||||
0x112f0, 0x1897c, 0x136f0, 0x11278, 0x1893e, 0x13678, 0x19b3e,
|
||||
0x1363c, 0x1121e, 0x1361e, 0x1f89a, 0x1137c, 0x1f9ba, 0x1377c,
|
||||
0x1133e, 0x1373e, 0x1f13a, 0x1f37a, 0x1e27a, 0x1e6fa, 0x1c4fa,
|
||||
0x1cdfa, 0x189fa, 0x1bae0, 0x1dd78, 0x1eebe, 0x174c0, 0x1ba70,
|
||||
0x1dd3c, 0x17460, 0x1ba38, 0x1dd1e, 0x17430, 0x1ba1c, 0x17418,
|
||||
0x1ba0e, 0x1740c, 0x132e0, 0x19978, 0x1ccbe, 0x176e0, 0x13270,
|
||||
0x1993c, 0x17670, 0x1bb3c, 0x1991e, 0x17638, 0x1321c, 0x1761c,
|
||||
0x1320e, 0x1760e, 0x11178, 0x188be, 0x13378, 0x1113c, 0x17778,
|
||||
0x1333c, 0x1111e, 0x1773c, 0x1331e, 0x1771e, 0x111be, 0x133be,
|
||||
0x177be, 0x172c0, 0x1b970, 0x1dcbc, 0x17260, 0x1b938, 0x1dc9e,
|
||||
0x17230, 0x1b91c, 0x17218, 0x1b90e, 0x1720c, 0x17206, 0x13170,
|
||||
0x198bc, 0x17370, 0x13138, 0x1989e, 0x17338, 0x1b99e, 0x1731c,
|
||||
0x1310e, 0x1730e, 0x110bc, 0x131bc, 0x1109e, 0x173bc, 0x1319e,
|
||||
0x1739e, 0x17160, 0x1b8b8, 0x1dc5e, 0x17130, 0x1b89c, 0x17118,
|
||||
0x1b88e, 0x1710c, 0x17106, 0x130b8, 0x1985e, 0x171b8, 0x1309c,
|
||||
0x1719c, 0x1308e, 0x1718e, 0x1105e, 0x130de, 0x171de, 0x170b0,
|
||||
0x1b85c, 0x17098, 0x1b84e, 0x1708c, 0x17086, 0x1305c, 0x170dc,
|
||||
0x1304e, 0x170ce, 0x17058, 0x1b82e, 0x1704c, 0x17046, 0x1302e,
|
||||
0x1706e, 0x1702c, 0x17026, 0x10af0, 0x1857c, 0x10a78, 0x1853e,
|
||||
0x10a3c, 0x10a1e, 0x10b7c, 0x10b3e, 0x1f0ba, 0x1e17a, 0x1c2fa,
|
||||
0x185fa, 0x11ae0, 0x18d78, 0x1c6be, 0x11a70, 0x18d3c, 0x11a38,
|
||||
0x18d1e, 0x11a1c, 0x11a0e, 0x10978, 0x184be, 0x11b78, 0x1093c,
|
||||
0x11b3c, 0x1091e, 0x11b1e, 0x109be, 0x11bbe, 0x13ac0, 0x19d70,
|
||||
0x1cebc, 0x13a60, 0x19d38, 0x1ce9e, 0x13a30, 0x19d1c, 0x13a18,
|
||||
0x19d0e, 0x13a0c, 0x13a06, 0x11970, 0x18cbc, 0x13b70, 0x11938,
|
||||
0x18c9e, 0x13b38, 0x1191c, 0x13b1c, 0x1190e, 0x13b0e, 0x108bc,
|
||||
0x119bc, 0x1089e, 0x13bbc, 0x1199e, 0x13b9e, 0x1bd60, 0x1deb8,
|
||||
0x1ef5e, 0x17a40, 0x1bd30, 0x1de9c, 0x17a20, 0x1bd18, 0x1de8e,
|
||||
0x17a10, 0x1bd0c, 0x17a08, 0x1bd06, 0x17a04, 0x13960, 0x19cb8,
|
||||
0x1ce5e, 0x17b60, 0x13930, 0x19c9c, 0x17b30, 0x1bd9c, 0x19c8e,
|
||||
0x17b18, 0x1390c, 0x17b0c, 0x13906, 0x17b06, 0x118b8, 0x18c5e,
|
||||
0x139b8, 0x1189c, 0x17bb8, 0x1399c, 0x1188e, 0x17b9c, 0x1398e,
|
||||
0x17b8e, 0x1085e, 0x118de, 0x139de, 0x17bde, 0x17940, 0x1bcb0,
|
||||
0x1de5c, 0x17920, 0x1bc98, 0x1de4e, 0x17910, 0x1bc8c, 0x17908,
|
||||
0x1bc86, 0x17904, 0x17902, 0x138b0, 0x19c5c, 0x179b0, 0x13898,
|
||||
0x19c4e, 0x17998, 0x1bcce, 0x1798c, 0x13886, 0x17986, 0x1185c,
|
||||
0x138dc, 0x1184e, 0x179dc, 0x138ce, 0x179ce, 0x178a0, 0x1bc58,
|
||||
0x1de2e, 0x17890, 0x1bc4c, 0x17888, 0x1bc46, 0x17884, 0x17882,
|
||||
0x13858, 0x19c2e, 0x178d8, 0x1384c, 0x178cc, 0x13846, 0x178c6,
|
||||
0x1182e, 0x1386e, 0x178ee, 0x17850, 0x1bc2c, 0x17848, 0x1bc26,
|
||||
0x17844, 0x17842, 0x1382c, 0x1786c, 0x13826, 0x17866, 0x17828,
|
||||
0x1bc16, 0x17824, 0x17822, 0x13816, 0x17836, 0x10578, 0x182be,
|
||||
0x1053c, 0x1051e, 0x105be, 0x10d70, 0x186bc, 0x10d38, 0x1869e,
|
||||
0x10d1c, 0x10d0e, 0x104bc, 0x10dbc, 0x1049e, 0x10d9e, 0x11d60,
|
||||
0x18eb8, 0x1c75e, 0x11d30, 0x18e9c, 0x11d18, 0x18e8e, 0x11d0c,
|
||||
0x11d06, 0x10cb8, 0x1865e, 0x11db8, 0x10c9c, 0x11d9c, 0x10c8e,
|
||||
0x11d8e, 0x1045e, 0x10cde, 0x11dde, 0x13d40, 0x19eb0, 0x1cf5c,
|
||||
0x13d20, 0x19e98, 0x1cf4e, 0x13d10, 0x19e8c, 0x13d08, 0x19e86,
|
||||
0x13d04, 0x13d02, 0x11cb0, 0x18e5c, 0x13db0, 0x11c98, 0x18e4e,
|
||||
0x13d98, 0x19ece, 0x13d8c, 0x11c86, 0x13d86, 0x10c5c, 0x11cdc,
|
||||
0x10c4e, 0x13ddc, 0x11cce, 0x13dce, 0x1bea0, 0x1df58, 0x1efae,
|
||||
0x1be90, 0x1df4c, 0x1be88, 0x1df46, 0x1be84, 0x1be82, 0x13ca0,
|
||||
0x19e58, 0x1cf2e, 0x17da0, 0x13c90, 0x19e4c, 0x17d90, 0x1becc,
|
||||
0x19e46, 0x17d88, 0x13c84, 0x17d84, 0x13c82, 0x17d82, 0x11c58,
|
||||
0x18e2e, 0x13cd8, 0x11c4c, 0x17dd8, 0x13ccc, 0x11c46, 0x17dcc,
|
||||
0x13cc6, 0x17dc6, 0x10c2e, 0x11c6e, 0x13cee, 0x17dee, 0x1be50,
|
||||
0x1df2c, 0x1be48, 0x1df26, 0x1be44, 0x1be42, 0x13c50, 0x19e2c,
|
||||
0x17cd0, 0x13c48, 0x19e26, 0x17cc8, 0x1be66, 0x17cc4, 0x13c42,
|
||||
0x17cc2, 0x11c2c, 0x13c6c, 0x11c26, 0x17cec, 0x13c66, 0x17ce6,
|
||||
0x1be28, 0x1df16, 0x1be24, 0x1be22, 0x13c28, 0x19e16, 0x17c68,
|
||||
0x13c24, 0x17c64, 0x13c22, 0x17c62, 0x11c16, 0x13c36, 0x17c76,
|
||||
0x1be14, 0x1be12, 0x13c14, 0x17c34, 0x13c12, 0x17c32, 0x102bc,
|
||||
0x1029e, 0x106b8, 0x1835e, 0x1069c, 0x1068e, 0x1025e, 0x106de,
|
||||
0x10eb0, 0x1875c, 0x10e98, 0x1874e, 0x10e8c, 0x10e86, 0x1065c,
|
||||
0x10edc, 0x1064e, 0x10ece, 0x11ea0, 0x18f58, 0x1c7ae, 0x11e90,
|
||||
0x18f4c, 0x11e88, 0x18f46, 0x11e84, 0x11e82, 0x10e58, 0x1872e,
|
||||
0x11ed8, 0x18f6e, 0x11ecc, 0x10e46, 0x11ec6, 0x1062e, 0x10e6e,
|
||||
0x11eee, 0x19f50, 0x1cfac, 0x19f48, 0x1cfa6, 0x19f44, 0x19f42,
|
||||
0x11e50, 0x18f2c, 0x13ed0, 0x19f6c, 0x18f26, 0x13ec8, 0x11e44,
|
||||
0x13ec4, 0x11e42, 0x13ec2, 0x10e2c, 0x11e6c, 0x10e26, 0x13eec,
|
||||
0x11e66, 0x13ee6, 0x1dfa8, 0x1efd6, 0x1dfa4, 0x1dfa2, 0x19f28,
|
||||
0x1cf96, 0x1bf68, 0x19f24, 0x1bf64, 0x19f22, 0x1bf62, 0x11e28,
|
||||
0x18f16, 0x13e68, 0x11e24, 0x17ee8, 0x13e64, 0x11e22, 0x17ee4,
|
||||
0x13e62, 0x17ee2, 0x10e16, 0x11e36, 0x13e76, 0x17ef6, 0x1df94,
|
||||
0x1df92, 0x19f14, 0x1bf34, 0x19f12, 0x1bf32, 0x11e14, 0x13e34,
|
||||
0x11e12, 0x17e74, 0x13e32, 0x17e72, 0x1df8a, 0x19f0a, 0x1bf1a,
|
||||
0x11e0a, 0x13e1a, 0x17e3a, 0x1035c, 0x1034e, 0x10758, 0x183ae,
|
||||
0x1074c, 0x10746, 0x1032e, 0x1076e, 0x10f50, 0x187ac, 0x10f48,
|
||||
0x187a6, 0x10f44, 0x10f42, 0x1072c, 0x10f6c, 0x10726, 0x10f66,
|
||||
0x18fa8, 0x1c7d6, 0x18fa4, 0x18fa2, 0x10f28, 0x18796, 0x11f68,
|
||||
0x18fb6, 0x11f64, 0x10f22, 0x11f62, 0x10716, 0x10f36, 0x11f76,
|
||||
0x1cfd4, 0x1cfd2, 0x18f94, 0x19fb4, 0x18f92, 0x19fb2, 0x10f14,
|
||||
0x11f34, 0x10f12, 0x13f74, 0x11f32, 0x13f72, 0x1cfca, 0x18f8a,
|
||||
0x19f9a, 0x10f0a, 0x11f1a, 0x13f3a, 0x103ac, 0x103a6, 0x107a8,
|
||||
0x183d6, 0x107a4, 0x107a2, 0x10396, 0x107b6, 0x187d4, 0x187d2,
|
||||
0x10794, 0x10fb4, 0x10792, 0x10fb2, 0x1c7ea,
|
||||
},
|
||||
}
|
||||
|
||||
func getCodeword(tableId int, word int) int {
|
||||
return codewords[tableId][word]
|
||||
}
|
57
vendor/github.com/boombuler/barcode/pdf417/dimensions.go
generated
vendored
Normal file
57
vendor/github.com/boombuler/barcode/pdf417/dimensions.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package pdf417
|
||||
|
||||
import "math"
|
||||
|
||||
const (
|
||||
minCols = 2
|
||||
maxCols = 30
|
||||
maxRows = 30
|
||||
minRows = 2
|
||||
moduleHeight = 2
|
||||
preferred_ratio = 3.0
|
||||
)
|
||||
|
||||
func calculateNumberOfRows(m, k, c int) int {
|
||||
r := ((m + 1 + k) / c) + 1
|
||||
if c*r >= (m + 1 + k + c) {
|
||||
r--
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func calcDimensions(dataWords, eccWords int) (cols, rows int) {
|
||||
ratio := 0.0
|
||||
cols = 0
|
||||
rows = 0
|
||||
|
||||
for c := minCols; c <= maxCols; c++ {
|
||||
r := calculateNumberOfRows(dataWords, eccWords, c)
|
||||
|
||||
if r < minRows {
|
||||
break
|
||||
}
|
||||
|
||||
if r > maxRows {
|
||||
continue
|
||||
}
|
||||
|
||||
newRatio := float64(17*cols+69) / float64(rows*moduleHeight)
|
||||
if rows != 0 && math.Abs(newRatio-preferred_ratio) > math.Abs(ratio-preferred_ratio) {
|
||||
continue
|
||||
}
|
||||
|
||||
ratio = newRatio
|
||||
cols = c
|
||||
rows = r
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
r := calculateNumberOfRows(dataWords, eccWords, minCols)
|
||||
if r < minRows {
|
||||
rows = minRows
|
||||
cols = minCols
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
162
vendor/github.com/boombuler/barcode/pdf417/encoder.go
generated
vendored
Normal file
162
vendor/github.com/boombuler/barcode/pdf417/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
|||
// Package pdf417 can create PDF-417 barcodes
|
||||
package pdf417
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
padding_codeword = 900
|
||||
)
|
||||
|
||||
// Encodes the given data as PDF417 barcode.
|
||||
// securityLevel should be between 0 and 8. The higher the number, the more
|
||||
// additional error-correction codes are added.
|
||||
func Encode(data string, securityLevel byte) (barcode.Barcode, error) {
|
||||
if securityLevel >= 9 {
|
||||
return nil, fmt.Errorf("Invalid security level %d", securityLevel)
|
||||
}
|
||||
|
||||
sl := securitylevel(securityLevel)
|
||||
|
||||
dataWords, err := highlevelEncode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns, rows := calcDimensions(len(dataWords), sl.ErrorCorrectionWordCount())
|
||||
if columns < minCols || columns > maxCols || rows < minRows || rows > maxRows {
|
||||
return nil, fmt.Errorf("Unable to fit data in barcode")
|
||||
}
|
||||
|
||||
barcode := new(pdfBarcode)
|
||||
barcode.data = data
|
||||
|
||||
codeWords, err := encodeData(dataWords, columns, sl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grid := [][]int{}
|
||||
for i := 0; i < len(codeWords); i += columns {
|
||||
grid = append(grid, codeWords[i:min(i+columns, len(codeWords))])
|
||||
}
|
||||
|
||||
codes := [][]int{}
|
||||
|
||||
for rowNum, row := range grid {
|
||||
table := rowNum % 3
|
||||
rowCodes := make([]int, 0, columns+4)
|
||||
|
||||
rowCodes = append(rowCodes, start_word)
|
||||
rowCodes = append(rowCodes, getCodeword(table, getLeftCodeWord(rowNum, rows, columns, securityLevel)))
|
||||
|
||||
for _, word := range row {
|
||||
rowCodes = append(rowCodes, getCodeword(table, word))
|
||||
}
|
||||
|
||||
rowCodes = append(rowCodes, getCodeword(table, getRightCodeWord(rowNum, rows, columns, securityLevel)))
|
||||
rowCodes = append(rowCodes, stop_word)
|
||||
|
||||
codes = append(codes, rowCodes)
|
||||
}
|
||||
|
||||
barcode.code = renderBarcode(codes)
|
||||
barcode.width = (columns+4)*17 + 1
|
||||
|
||||
return barcode, nil
|
||||
}
|
||||
|
||||
func encodeData(dataWords []int, columns int, sl securitylevel) ([]int, error) {
|
||||
dataCount := len(dataWords)
|
||||
|
||||
ecCount := sl.ErrorCorrectionWordCount()
|
||||
|
||||
padWords := getPadding(dataCount, ecCount, columns)
|
||||
dataWords = append(dataWords, padWords...)
|
||||
|
||||
length := len(dataWords) + 1
|
||||
dataWords = append([]int{length}, dataWords...)
|
||||
|
||||
ecWords := sl.Compute(dataWords)
|
||||
|
||||
return append(dataWords, ecWords...), nil
|
||||
}
|
||||
|
||||
func getLeftCodeWord(rowNum int, rows int, columns int, securityLevel byte) int {
|
||||
tableId := rowNum % 3
|
||||
|
||||
var x int
|
||||
|
||||
switch tableId {
|
||||
case 0:
|
||||
x = (rows - 3) / 3
|
||||
case 1:
|
||||
x = int(securityLevel) * 3
|
||||
x += (rows - 1) % 3
|
||||
case 2:
|
||||
x = columns - 1
|
||||
}
|
||||
|
||||
return 30*(rowNum/3) + x
|
||||
}
|
||||
|
||||
func getRightCodeWord(rowNum int, rows int, columns int, securityLevel byte) int {
|
||||
tableId := rowNum % 3
|
||||
|
||||
var x int
|
||||
|
||||
switch tableId {
|
||||
case 0:
|
||||
x = columns - 1
|
||||
case 1:
|
||||
x = (rows - 1) / 3
|
||||
case 2:
|
||||
x = int(securityLevel) * 3
|
||||
x += (rows - 1) % 3
|
||||
}
|
||||
|
||||
return 30*(rowNum/3) + x
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a <= b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func getPadding(dataCount int, ecCount int, columns int) []int {
|
||||
totalCount := dataCount + ecCount + 1
|
||||
mod := totalCount % columns
|
||||
|
||||
padding := []int{}
|
||||
|
||||
if mod > 0 {
|
||||
padCount := columns - mod
|
||||
padding = make([]int, padCount)
|
||||
for i := 0; i < padCount; i++ {
|
||||
padding[i] = padding_codeword
|
||||
}
|
||||
}
|
||||
|
||||
return padding
|
||||
}
|
||||
|
||||
func renderBarcode(codes [][]int) *utils.BitList {
|
||||
bl := new(utils.BitList)
|
||||
for _, row := range codes {
|
||||
lastIdx := len(row) - 1
|
||||
for i, col := range row {
|
||||
if i == lastIdx {
|
||||
bl.AddBits(col, 18)
|
||||
} else {
|
||||
bl.AddBits(col, 17)
|
||||
}
|
||||
}
|
||||
}
|
||||
return bl
|
||||
}
|
151
vendor/github.com/boombuler/barcode/pdf417/errorcorrection.go
generated
vendored
Normal file
151
vendor/github.com/boombuler/barcode/pdf417/errorcorrection.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
package pdf417
|
||||
|
||||
type securitylevel byte
|
||||
|
||||
func (level securitylevel) ErrorCorrectionWordCount() int {
|
||||
return 1 << (uint(level) + 1)
|
||||
}
|
||||
|
||||
var correctionFactors = [][]int{
|
||||
// Level 0
|
||||
[]int{27, 917},
|
||||
|
||||
// Level 1
|
||||
[]int{522, 568, 723, 809},
|
||||
|
||||
// Level 2
|
||||
[]int{237, 308, 436, 284, 646, 653, 428, 379},
|
||||
|
||||
// Level 3
|
||||
[]int{
|
||||
274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42,
|
||||
176, 65,
|
||||
},
|
||||
|
||||
// Level 4
|
||||
[]int{
|
||||
361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687,
|
||||
284, 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803, 133,
|
||||
231, 390, 685, 330, 63, 410,
|
||||
},
|
||||
|
||||
// Level 5
|
||||
[]int{
|
||||
539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877,
|
||||
381, 612, 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511,
|
||||
400, 672, 762, 283, 184, 440, 35, 519, 31, 460, 594, 225, 535, 517,
|
||||
352, 605, 158, 651, 201, 488, 502, 648, 733, 717, 83, 404, 97, 280,
|
||||
771, 840, 629, 4, 381, 843, 623, 264, 543,
|
||||
},
|
||||
|
||||
// Level 6
|
||||
[]int{
|
||||
521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925,
|
||||
749, 415, 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631,
|
||||
292, 908, 490, 704, 516, 258, 457, 907, 594, 723, 674, 292, 272, 96,
|
||||
684, 432, 686, 606, 860, 569, 193, 219, 129, 186, 236, 287, 192,
|
||||
775, 278, 173, 40, 379, 712, 463, 646, 776, 171, 491, 297, 763, 156,
|
||||
732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898, 784, 663, 627,
|
||||
378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, 157,
|
||||
374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587,
|
||||
804, 34, 211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86,
|
||||
801, 4, 108, 539,
|
||||
},
|
||||
|
||||
// Level 7
|
||||
[]int{
|
||||
524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786,
|
||||
138, 720, 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194,
|
||||
280, 201, 280, 828, 757, 710, 814, 919, 89, 68, 569, 11, 204, 796,
|
||||
605, 540, 913, 801, 700, 799, 137, 439, 418, 592, 668, 353, 859,
|
||||
370, 694, 325, 240, 216, 257, 284, 549, 209, 884, 315, 70, 329, 793,
|
||||
490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521, 307,
|
||||
291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289,
|
||||
470, 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136,
|
||||
538, 906, 90, 2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355,
|
||||
588, 188, 462, 10, 134, 628, 320, 479, 130, 739, 71, 263, 318, 374,
|
||||
601, 192, 605, 142, 673, 687, 234, 722, 384, 177, 752, 607, 640,
|
||||
455, 193, 689, 707, 805, 641, 48, 60, 732, 621, 895, 544, 261, 852,
|
||||
655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528, 503, 118,
|
||||
49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550,
|
||||
73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791,
|
||||
893, 754, 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299,
|
||||
922, 191, 910, 532, 609, 829, 189, 20, 167, 29, 872, 449, 83, 402,
|
||||
41, 656, 505, 579, 481, 173, 404, 251, 688, 95, 497, 555, 642, 543,
|
||||
307, 159, 924, 558, 648, 55, 497, 10,
|
||||
},
|
||||
|
||||
// Level 8
|
||||
[]int{
|
||||
352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380,
|
||||
350, 492, 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88,
|
||||
87, 193, 352, 781, 846, 75, 327, 520, 435, 543, 203, 666, 249, 346,
|
||||
781, 621, 640, 268, 794, 534, 539, 781, 408, 390, 644, 102, 476,
|
||||
499, 290, 632, 545, 37, 858, 916, 552, 41, 542, 289, 122, 272, 383,
|
||||
800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741, 290, 204, 681,
|
||||
407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, 808,
|
||||
684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516,
|
||||
258, 240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328,
|
||||
596, 786, 303, 570, 381, 415, 641, 156, 237, 151, 429, 531, 207,
|
||||
676, 710, 89, 168, 304, 402, 40, 708, 575, 162, 864, 229, 65, 861,
|
||||
841, 512, 164, 477, 221, 92, 358, 785, 288, 357, 850, 836, 827, 736,
|
||||
707, 94, 8, 494, 114, 521, 2, 499, 851, 543, 152, 729, 771, 95, 248,
|
||||
361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820, 669, 45, 902,
|
||||
452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578, 37,
|
||||
124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578,
|
||||
911, 283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777,
|
||||
699, 688, 43, 408, 842, 383, 721, 521, 560, 644, 714, 559, 62, 145,
|
||||
873, 663, 713, 159, 672, 729, 624, 59, 193, 417, 158, 209, 563, 564,
|
||||
343, 693, 109, 608, 563, 365, 181, 772, 677, 310, 248, 353, 708,
|
||||
410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, 618, 586, 424,
|
||||
833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45, 787,
|
||||
680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644,
|
||||
905, 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662,
|
||||
513, 56, 252, 341, 242, 797, 838, 837, 720, 224, 307, 631, 61, 87,
|
||||
560, 310, 756, 665, 397, 808, 851, 309, 473, 795, 378, 31, 647, 915,
|
||||
459, 806, 590, 731, 425, 216, 548, 249, 321, 881, 699, 535, 673,
|
||||
782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791, 660, 162, 498,
|
||||
308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437, 375,
|
||||
273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687,
|
||||
842, 37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321,
|
||||
54, 669, 316, 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316,
|
||||
486, 721, 610, 46, 656, 447, 171, 616, 464, 190, 531, 297, 321, 762,
|
||||
752, 533, 175, 134, 14, 381, 433, 717, 45, 111, 20, 596, 284, 736,
|
||||
138, 646, 411, 877, 669, 141, 919, 45, 780, 407, 164, 332, 899, 165,
|
||||
726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647, 63, 310, 863,
|
||||
251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263,
|
||||
},
|
||||
}
|
||||
|
||||
func (level securitylevel) Compute(data []int) []int {
|
||||
// Correction factors for the given level
|
||||
factors := correctionFactors[int(level)]
|
||||
|
||||
// Number of correction code words
|
||||
count := level.ErrorCorrectionWordCount()
|
||||
|
||||
// Correction code words array, prepopulated with zeros
|
||||
ecWords := make([]int, count)
|
||||
|
||||
for _, value := range data {
|
||||
temp := (value + ecWords[0]) % 929
|
||||
|
||||
for i := count - 1; i >= 0; i-- {
|
||||
add := 0
|
||||
|
||||
if i > 0 {
|
||||
add = ecWords[count-i]
|
||||
}
|
||||
|
||||
ecWords[count-1-i] = (add + 929 - (temp*factors[i])%929) % 929
|
||||
}
|
||||
}
|
||||
|
||||
for key, word := range ecWords {
|
||||
if word > 0 {
|
||||
ecWords[key] = 929 - word
|
||||
}
|
||||
}
|
||||
|
||||
return ecWords
|
||||
}
|
61
vendor/github.com/boombuler/barcode/pdf417/errorcorrection_test.go
generated
vendored
Normal file
61
vendor/github.com/boombuler/barcode/pdf417/errorcorrection_test.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
package pdf417
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var inputData = []int{16, 902, 1, 278, 827, 900, 295, 902, 2, 326, 823, 544, 900, 149, 900, 900}
|
||||
|
||||
func TestReedSolomonComputeLevel0(t *testing.T) {
|
||||
var level securitylevel = 0
|
||||
expected := []int{156, 765}
|
||||
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 0")
|
||||
}
|
||||
|
||||
func TestReedSolomonComputeLevel1(t *testing.T) {
|
||||
var level securitylevel = 1
|
||||
expected := []int{168, 875, 63, 355}
|
||||
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 1")
|
||||
}
|
||||
|
||||
func TestReedSolomonComputeLevel2(t *testing.T) {
|
||||
var level securitylevel = 2
|
||||
expected := []int{628, 715, 393, 299, 863, 601, 169, 708}
|
||||
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 2")
|
||||
}
|
||||
|
||||
func TestReedSolomonComputeLevel3(t *testing.T) {
|
||||
var level securitylevel = 3
|
||||
expected := []int{232, 176, 793, 616, 476, 406, 855, 445, 84, 518, 522, 721, 607, 2, 42, 578}
|
||||
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 3")
|
||||
}
|
||||
|
||||
func TestReedSolomonComputeLevel4(t *testing.T) {
|
||||
var level securitylevel = 4
|
||||
expected := []int{281, 156, 276, 668, 44, 252, 877, 30, 549, 856, 773, 639, 420, 330, 693, 329, 283, 723, 480, 482, 102, 925, 535, 892, 374, 472, 837, 331, 343, 608, 390, 364}
|
||||
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 4")
|
||||
}
|
||||
|
||||
func TestReedSolomonComputeLevel5(t *testing.T) {
|
||||
var level securitylevel = 5
|
||||
expected := []int{31, 850, 18, 870, 53, 477, 837, 130, 533, 186, 266, 450, 39, 492, 542, 653, 499, 887, 618, 103, 364, 313, 906, 396, 270, 735, 593, 81, 557, 712, 810, 48, 167, 533, 205, 577, 503, 126, 449, 189, 859, 471, 493, 849, 554, 76, 878, 893, 168, 497, 251, 704, 311, 650, 283, 268, 462, 223, 659, 763, 176, 34, 544, 304}
|
||||
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 5")
|
||||
}
|
||||
|
||||
func TestReedSolomonComputeLevel6(t *testing.T) {
|
||||
var level securitylevel = 6
|
||||
expected := []int{345, 775, 909, 489, 650, 568, 869, 577, 574, 349, 885, 317, 492, 222, 783, 451, 647, 385, 168, 366, 118, 655, 643, 551, 179, 880, 880, 752, 132, 206, 765, 862, 727, 240, 32, 266, 911, 287, 813, 437, 868, 201, 681, 867, 567, 398, 508, 564, 504, 676, 785, 554, 831, 566, 424, 93, 515, 275, 61, 544, 272, 621, 374, 922, 779, 663, 789, 295, 631, 536, 755, 465, 485, 416, 76, 412, 76, 431, 28, 614, 767, 419, 600, 779, 94, 584, 647, 846, 121, 97, 790, 205, 424, 793, 263, 271, 694, 522, 437, 817, 382, 164, 113, 849, 178, 602, 554, 261, 415, 737, 401, 675, 203, 271, 649, 120, 765, 209, 522, 687, 420, 32, 60, 266, 270, 228, 304, 270}
|
||||
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 6")
|
||||
}
|
||||
|
||||
func TestReedSolomonComputeLevel7(t *testing.T) {
|
||||
var level securitylevel = 7
|
||||
expected := []int{142, 203, 799, 4, 105, 137, 793, 914, 225, 636, 60, 171, 490, 180, 414, 141, 399, 599, 829, 288, 108, 268, 444, 481, 795, 146, 655, 778, 189, 32, 597, 206, 208, 711, 845, 608, 642, 636, 540, 795, 845, 466, 492, 659, 138, 800, 912, 171, 92, 438, 225, 301, 777, 449, 230, 448, 326, 182, 892, 681, 543, 582, 732, 758, 162, 587, 685, 378, 646, 356, 354, 25, 839, 839, 556, 253, 501, 771, 745, 616, 473, 293, 669, 822, 613, 684, 229, 265, 110, 438, 144, 727, 317, 605, 414, 497, 82, 278, 267, 323, 43, 894, 624, 282, 790, 579, 430, 255, 802, 553, 922, 604, 68, 692, 809, 909, 663, 589, 735, 670, 298, 158, 201, 68, 124, 64, 67, 338, 694, 373, 225, 579, 309, 699, 920, 432, 717, 72, 126, 819, 142, 755, 473, 630, 331, 758, 730, 65, 359, 451, 236, 16, 56, 31, 87, 587, 125, 385, 384, 197, 352, 383, 173, 271, 38, 558, 810, 260, 521, 680, 7, 319, 650, 334, 695, 708, 0, 562, 365, 204, 114, 185, 560, 746, 767, 449, 797, 688, 63, 135, 818, 805, 3, 536, 908, 532, 400, 698, 49, 212, 630, 93, 157, 275, 3, 20, 611, 179, 302, 282, 876, 665, 241, 206, 474, 80, 217, 460, 462, 751, 719, 571, 536, 794, 522, 385, 598, 756, 162, 212, 758, 662, 361, 223, 587, 857, 503, 382, 615, 86, 283, 541, 847, 518, 406, 736, 486, 408, 226, 342, 784, 772, 211, 888, 234, 335}
|
||||
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 7")
|
||||
}
|
||||
|
||||
func TestReedSolomonComputeLevel8(t *testing.T) {
|
||||
var level securitylevel = 8
|
||||
expected := []int{538, 446, 840, 510, 163, 708, 177, 666, 423, 600, 707, 913, 770, 571, 156, 683, 676, 697, 898, 776, 128, 851, 163, 854, 135, 661, 880, 279, 92, 324, 397, 207, 379, 223, 574, 9, 70, 858, 878, 579, 61, 551, 261, 388, 315, 856, 266, 865, 923, 38, 313, 62, 381, 198, 265, 256, 385, 878, 347, 532, 821, 53, 855, 225, 697, 826, 263, 334, 207, 565, 460, 496, 705, 599, 383, 289, 178, 168, 401, 268, 555, 190, 922, 284, 180, 810, 891, 832, 636, 813, 894, 495, 701, 484, 204, 793, 129, 164, 444, 228, 636, 98, 809, 57, 736, 697, 727, 534, 889, 480, 898, 773, 234, 851, 880, 843, 714, 443, 412, 489, 578, 468, 367, 663, 11, 686, 319, 352, 345, 670, 106, 106, 219, 466, 439, 350, 538, 66, 852, 175, 465, 731, 332, 110, 926, 491, 18, 422, 736, 797, 624, 376, 728, 526, 735, 200, 502, 923, 789, 529, 923, 706, 384, 869, 172, 548, 520, 463, 813, 384, 793, 231, 190, 653, 864, 351, 400, 525, 487, 828, 654, 307, 141, 638, 770, 775, 282, 54, 758, 197, 492, 320, 86, 790, 275, 237, 923, 25, 591, 605, 61, 824, 79, 631, 532, 337, 867, 423, 340, 597, 682, 923, 287, 408, 503, 361, 881, 196, 468, 759, 746, 389, 124, 784, 198, 865, 538, 451, 178, 772, 653, 121, 497, 598, 711, 716, 241, 159, 429, 88, 799, 761, 639, 105, 54, 807, 351, 435, 793, 873, 360, 8, 881, 479, 693, 576, 849, 875, 771, 621, 134, 863, 8, 171, 799, 924, 103, 63, 491, 538, 597, 855, 697, 499, 7, 886, 286, 85, 107, 220, 319, 124, 197, 150, 729, 899, 585, 540, 676, 414, 256, 856, 596, 259, 882, 436, 26, 273, 753, 127, 679, 390, 654, 42, 276, 420, 247, 629, 116, 803, 131, 25, 403, 645, 462, 897, 151, 622, 108, 167, 227, 831, 887, 662, 739, 263, 829, 56, 624, 317, 908, 378, 39, 393, 861, 338, 202, 179, 907, 109, 360, 736, 554, 342, 594, 125, 433, 394, 195, 698, 844, 912, 530, 842, 337, 294, 528, 231, 735, 93, 8, 579, 42, 148, 609, 233, 782, 887, 888, 915, 620, 78, 137, 161, 282, 217, 775, 564, 33, 195, 36, 584, 679, 775, 476, 309, 230, 303, 708, 143, 679, 502, 814, 193, 508, 532, 542, 580, 603, 641, 338, 361, 542, 537, 810, 394, 764, 136, 167, 611, 881, 775, 267, 433, 142, 202, 828, 363, 101, 728, 660, 583, 483, 786, 717, 190, 809, 422, 567, 741, 695, 310, 120, 177, 47, 494, 345, 508, 16, 639, 402, 625, 286, 298, 358, 54, 705, 916, 291, 424, 375, 883, 655, 675, 498, 498, 884, 862, 365, 310, 805, 763, 855, 354, 777, 543, 53, 773, 120, 408, 234, 728, 438, 914, 3, 670, 546, 465, 449, 923, 51, 546, 709, 648, 96, 320, 682, 326, 848, 234, 855, 791, 20, 97, 901, 351, 317, 764, 767, 312, 206, 139, 610, 578, 646, 264, 389, 238, 675, 595, 430, 88}
|
||||
compareIntSlice(t, expected, level.Compute(inputData), "SecLvl 8")
|
||||
}
|
354
vendor/github.com/boombuler/barcode/pdf417/highlevel.go
generated
vendored
Normal file
354
vendor/github.com/boombuler/barcode/pdf417/highlevel.go
generated
vendored
Normal file
|
@ -0,0 +1,354 @@
|
|||
package pdf417
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type encodingMode byte
|
||||
|
||||
type subMode byte
|
||||
|
||||
const (
|
||||
encText encodingMode = iota
|
||||
encNumeric
|
||||
encBinary
|
||||
|
||||
subUpper subMode = iota
|
||||
subLower
|
||||
subMixed
|
||||
subPunct
|
||||
|
||||
latch_to_text = 900
|
||||
latch_to_byte_padded = 901
|
||||
latch_to_numeric = 902
|
||||
latch_to_byte = 924
|
||||
shift_to_byte = 913
|
||||
|
||||
min_numeric_count = 13
|
||||
)
|
||||
|
||||
var (
|
||||
mixedMap map[rune]int
|
||||
punctMap map[rune]int
|
||||
)
|
||||
|
||||
func init() {
|
||||
mixedMap = make(map[rune]int)
|
||||
mixedRaw := []rune{
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 38, 13, 9, 44, 58,
|
||||
35, 45, 46, 36, 47, 43, 37, 42, 61, 94, 0, 32, 0, 0, 0,
|
||||
}
|
||||
for idx, ch := range mixedRaw {
|
||||
if ch > 0 {
|
||||
mixedMap[ch] = idx
|
||||
}
|
||||
}
|
||||
|
||||
punctMap = make(map[rune]int)
|
||||
punctRaw := []rune{
|
||||
59, 60, 62, 64, 91, 92, 93, 95, 96, 126, 33, 13, 9, 44, 58,
|
||||
10, 45, 46, 36, 47, 34, 124, 42, 40, 41, 63, 123, 125, 39, 0,
|
||||
}
|
||||
for idx, ch := range punctRaw {
|
||||
if ch > 0 {
|
||||
punctMap[ch] = idx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func determineConsecutiveDigitCount(data []rune) int {
|
||||
cnt := 0
|
||||
for _, r := range data {
|
||||
if utils.RuneToInt(r) == -1 {
|
||||
break
|
||||
}
|
||||
cnt++
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
func encodeNumeric(digits []rune) ([]int, error) {
|
||||
digitCount := len(digits)
|
||||
chunkCount := digitCount / 44
|
||||
if digitCount%44 != 0 {
|
||||
chunkCount++
|
||||
}
|
||||
|
||||
codeWords := []int{}
|
||||
|
||||
for i := 0; i < chunkCount; i++ {
|
||||
start := i * 44
|
||||
end := start + 44
|
||||
if end > digitCount {
|
||||
end = digitCount
|
||||
}
|
||||
chunk := digits[start:end]
|
||||
|
||||
chunkNum := big.NewInt(0)
|
||||
_, ok := chunkNum.SetString("1"+string(chunk), 10)
|
||||
|
||||
if !ok {
|
||||
return nil, errors.New("Failed converting: " + string(chunk))
|
||||
}
|
||||
|
||||
cws := []int{}
|
||||
|
||||
for chunkNum.Cmp(big.NewInt(0)) > 0 {
|
||||
newChunk, cw := chunkNum.DivMod(chunkNum, big.NewInt(900), big.NewInt(0))
|
||||
chunkNum = newChunk
|
||||
cws = append([]int{int(cw.Int64())}, cws...)
|
||||
}
|
||||
|
||||
codeWords = append(codeWords, cws...)
|
||||
}
|
||||
|
||||
return codeWords, nil
|
||||
}
|
||||
|
||||
func determineConsecutiveTextCount(msg []rune) int {
|
||||
result := 0
|
||||
|
||||
isText := func(ch rune) bool {
|
||||
return ch == '\t' || ch == '\n' || ch == '\r' || (ch >= 32 && ch <= 126)
|
||||
}
|
||||
|
||||
for i, ch := range msg {
|
||||
numericCount := determineConsecutiveDigitCount(msg[i:])
|
||||
if numericCount >= min_numeric_count || (numericCount == 0 && !isText(ch)) {
|
||||
break
|
||||
}
|
||||
|
||||
result++
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func encodeText(text []rune, submode subMode) (subMode, []int) {
|
||||
isAlphaUpper := func(ch rune) bool {
|
||||
return ch == ' ' || (ch >= 'A' && ch <= 'Z')
|
||||
}
|
||||
isAlphaLower := func(ch rune) bool {
|
||||
return ch == ' ' || (ch >= 'a' && ch <= 'z')
|
||||
}
|
||||
isMixed := func(ch rune) bool {
|
||||
_, ok := mixedMap[ch]
|
||||
return ok
|
||||
}
|
||||
isPunctuation := func(ch rune) bool {
|
||||
_, ok := punctMap[ch]
|
||||
return ok
|
||||
}
|
||||
|
||||
idx := 0
|
||||
var tmp []int
|
||||
for idx < len(text) {
|
||||
ch := text[idx]
|
||||
switch submode {
|
||||
case subUpper:
|
||||
if isAlphaUpper(ch) {
|
||||
if ch == ' ' {
|
||||
tmp = append(tmp, 26) //space
|
||||
} else {
|
||||
tmp = append(tmp, int(ch-'A'))
|
||||
}
|
||||
} else {
|
||||
if isAlphaLower(ch) {
|
||||
submode = subLower
|
||||
tmp = append(tmp, 27) // lower latch
|
||||
continue
|
||||
} else if isMixed(ch) {
|
||||
submode = subMixed
|
||||
tmp = append(tmp, 28) // mixed latch
|
||||
continue
|
||||
} else {
|
||||
tmp = append(tmp, 29) // punctuation switch
|
||||
tmp = append(tmp, punctMap[ch])
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
case subLower:
|
||||
if isAlphaLower(ch) {
|
||||
if ch == ' ' {
|
||||
tmp = append(tmp, 26) //space
|
||||
} else {
|
||||
tmp = append(tmp, int(ch-'a'))
|
||||
}
|
||||
} else {
|
||||
if isAlphaUpper(ch) {
|
||||
tmp = append(tmp, 27) //upper switch
|
||||
tmp = append(tmp, int(ch-'A'))
|
||||
break
|
||||
} else if isMixed(ch) {
|
||||
submode = subMixed
|
||||
tmp = append(tmp, 28) //mixed latch
|
||||
continue
|
||||
} else {
|
||||
tmp = append(tmp, 29) //punctuation switch
|
||||
tmp = append(tmp, punctMap[ch])
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
case subMixed:
|
||||
if isMixed(ch) {
|
||||
tmp = append(tmp, mixedMap[ch])
|
||||
} else {
|
||||
if isAlphaUpper(ch) {
|
||||
submode = subUpper
|
||||
tmp = append(tmp, 28) //upper latch
|
||||
continue
|
||||
} else if isAlphaLower(ch) {
|
||||
submode = subLower
|
||||
tmp = append(tmp, 27) //lower latch
|
||||
continue
|
||||
} else {
|
||||
if idx+1 < len(text) {
|
||||
next := text[idx+1]
|
||||
if isPunctuation(next) {
|
||||
submode = subPunct
|
||||
tmp = append(tmp, 25) //punctuation latch
|
||||
continue
|
||||
}
|
||||
}
|
||||
tmp = append(tmp, 29) //punctuation switch
|
||||
tmp = append(tmp, punctMap[ch])
|
||||
}
|
||||
}
|
||||
break
|
||||
default: //subPunct
|
||||
if isPunctuation(ch) {
|
||||
tmp = append(tmp, punctMap[ch])
|
||||
} else {
|
||||
submode = subUpper
|
||||
tmp = append(tmp, 29) //upper latch
|
||||
continue
|
||||
}
|
||||
}
|
||||
idx++
|
||||
}
|
||||
|
||||
h := 0
|
||||
result := []int{}
|
||||
for i, val := range tmp {
|
||||
if i%2 != 0 {
|
||||
h = (h * 30) + val
|
||||
result = append(result, h)
|
||||
} else {
|
||||
h = val
|
||||
}
|
||||
}
|
||||
if len(tmp)%2 != 0 {
|
||||
result = append(result, (h*30)+29)
|
||||
}
|
||||
return submode, result
|
||||
}
|
||||
|
||||
func determineConsecutiveBinaryCount(msg []byte) int {
|
||||
result := 0
|
||||
|
||||
for i, _ := range msg {
|
||||
numericCount := determineConsecutiveDigitCount([]rune(string(msg[i:])))
|
||||
if numericCount >= min_numeric_count {
|
||||
break
|
||||
}
|
||||
textCount := determineConsecutiveTextCount([]rune(string(msg[i:])))
|
||||
if textCount > 5 {
|
||||
break
|
||||
}
|
||||
result++
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func encodeBinary(data []byte, startmode encodingMode) []int {
|
||||
result := []int{}
|
||||
|
||||
count := len(data)
|
||||
if count == 1 && startmode == encText {
|
||||
result = append(result, shift_to_byte)
|
||||
} else if (count % 6) == 0 {
|
||||
result = append(result, latch_to_byte)
|
||||
} else {
|
||||
result = append(result, latch_to_byte_padded)
|
||||
}
|
||||
|
||||
idx := 0
|
||||
// Encode sixpacks
|
||||
if count >= 6 {
|
||||
words := make([]int, 5)
|
||||
for (count - idx) >= 6 {
|
||||
var t int64 = 0
|
||||
for i := 0; i < 6; i++ {
|
||||
t = t << 8
|
||||
t += int64(data[idx+i])
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
words[4-i] = int(t % 900)
|
||||
t = t / 900
|
||||
}
|
||||
result = append(result, words...)
|
||||
idx += 6
|
||||
}
|
||||
}
|
||||
//Encode rest (remaining n<5 bytes if any)
|
||||
for i := idx; i < count; i++ {
|
||||
result = append(result, int(data[i]&0xff))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func highlevelEncode(dataStr string) ([]int, error) {
|
||||
encodingMode := encText
|
||||
textSubMode := subUpper
|
||||
|
||||
result := []int{}
|
||||
|
||||
data := []byte(dataStr)
|
||||
|
||||
for len(data) > 0 {
|
||||
numericCount := determineConsecutiveDigitCount([]rune(string(data)))
|
||||
if numericCount >= min_numeric_count || numericCount == len(data) {
|
||||
result = append(result, latch_to_numeric)
|
||||
encodingMode = encNumeric
|
||||
textSubMode = subUpper
|
||||
numData, err := encodeNumeric([]rune(string(data[:numericCount])))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, numData...)
|
||||
data = data[numericCount:]
|
||||
} else {
|
||||
textCount := determineConsecutiveTextCount([]rune(string(data)))
|
||||
if textCount >= 5 || textCount == len(data) {
|
||||
if encodingMode != encText {
|
||||
result = append(result, latch_to_text)
|
||||
encodingMode = encText
|
||||
textSubMode = subUpper
|
||||
}
|
||||
var txtData []int
|
||||
textSubMode, txtData = encodeText([]rune(string(data[:textCount])), textSubMode)
|
||||
result = append(result, txtData...)
|
||||
data = data[textCount:]
|
||||
} else {
|
||||
binaryCount := determineConsecutiveBinaryCount(data)
|
||||
if binaryCount == 0 {
|
||||
binaryCount = 1
|
||||
}
|
||||
bytes := data[:binaryCount]
|
||||
if len(bytes) != 1 || encodingMode != encText {
|
||||
encodingMode = encBinary
|
||||
textSubMode = subUpper
|
||||
}
|
||||
byteData := encodeBinary(bytes, encodingMode)
|
||||
result = append(result, byteData...)
|
||||
data = data[binaryCount:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
41
vendor/github.com/boombuler/barcode/pdf417/highlevel_test.go
generated
vendored
Normal file
41
vendor/github.com/boombuler/barcode/pdf417/highlevel_test.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
package pdf417
|
||||
|
||||
import "testing"
|
||||
|
||||
func compareIntSlice(t *testing.T, expected, actual []int, testStr string) {
|
||||
if len(actual) != len(expected) {
|
||||
t.Errorf("Invalid slice size. Expected %d got %d while encoding %q", len(expected), len(actual), testStr)
|
||||
return
|
||||
}
|
||||
for i, a := range actual {
|
||||
if e := expected[i]; e != a {
|
||||
t.Errorf("Unexpected value at position %d. Expected %d got %d while encoding %q", i, e, a, testStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHighlevelEncode(t *testing.T) {
|
||||
runTest := func(msg string, expected ...int) {
|
||||
if codes, err := highlevelEncode(msg); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
compareIntSlice(t, expected, codes, msg)
|
||||
}
|
||||
}
|
||||
|
||||
runTest("01234", 902, 112, 434)
|
||||
runTest("Super !", 567, 615, 137, 809, 329)
|
||||
runTest("Super ", 567, 615, 137, 809)
|
||||
runTest("ABC123", 1, 88, 32, 119)
|
||||
runTest("123ABC", 841, 63, 840, 32)
|
||||
}
|
||||
|
||||
func TestBinaryEncoder(t *testing.T) {
|
||||
runTest := func(msg string, expected ...int) {
|
||||
codes := encodeBinary([]byte(msg), encText)
|
||||
compareIntSlice(t, expected, codes, msg)
|
||||
}
|
||||
|
||||
runTest("alcool", 924, 163, 238, 432, 766, 244)
|
||||
runTest("alcoolique", 901, 163, 238, 432, 766, 244, 105, 113, 117, 101)
|
||||
}
|
40
vendor/github.com/boombuler/barcode/pdf417/pdfcode.go
generated
vendored
Normal file
40
vendor/github.com/boombuler/barcode/pdf417/pdfcode.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
package pdf417
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type pdfBarcode struct {
|
||||
data string
|
||||
width int
|
||||
code *utils.BitList
|
||||
}
|
||||
|
||||
func (c *pdfBarcode) Metadata() barcode.Metadata {
|
||||
return barcode.Metadata{barcode.TypePDF, 2}
|
||||
}
|
||||
|
||||
func (c *pdfBarcode) Content() string {
|
||||
return c.data
|
||||
}
|
||||
|
||||
func (c *pdfBarcode) ColorModel() color.Model {
|
||||
return color.Gray16Model
|
||||
}
|
||||
|
||||
func (c *pdfBarcode) Bounds() image.Rectangle {
|
||||
height := c.code.Len() / c.width
|
||||
|
||||
return image.Rect(0, 0, c.width, height*moduleHeight)
|
||||
}
|
||||
|
||||
func (c *pdfBarcode) At(x, y int) color.Color {
|
||||
if c.code.GetBit((y/moduleHeight)*c.width + x) {
|
||||
return color.Black
|
||||
}
|
||||
return color.White
|
||||
}
|
66
vendor/github.com/boombuler/barcode/qr/alphanumeric.go
generated
vendored
Normal file
66
vendor/github.com/boombuler/barcode/qr/alphanumeric.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
const charSet string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
|
||||
|
||||
func stringToAlphaIdx(content string) <-chan int {
|
||||
result := make(chan int)
|
||||
go func() {
|
||||
for _, r := range content {
|
||||
idx := strings.IndexRune(charSet, r)
|
||||
result <- idx
|
||||
if idx < 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
close(result)
|
||||
}()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func encodeAlphaNumeric(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
|
||||
|
||||
contentLenIsOdd := len(content)%2 == 1
|
||||
contentBitCount := (len(content) / 2) * 11
|
||||
if contentLenIsOdd {
|
||||
contentBitCount += 6
|
||||
}
|
||||
vi := findSmallestVersionInfo(ecl, alphaNumericMode, contentBitCount)
|
||||
if vi == nil {
|
||||
return nil, nil, errors.New("To much data to encode")
|
||||
}
|
||||
|
||||
res := new(utils.BitList)
|
||||
res.AddBits(int(alphaNumericMode), 4)
|
||||
res.AddBits(len(content), vi.charCountBits(alphaNumericMode))
|
||||
|
||||
encoder := stringToAlphaIdx(content)
|
||||
|
||||
for idx := 0; idx < len(content)/2; idx++ {
|
||||
c1 := <-encoder
|
||||
c2 := <-encoder
|
||||
if c1 < 0 || c2 < 0 {
|
||||
return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, AlphaNumeric)
|
||||
}
|
||||
res.AddBits(c1*45+c2, 11)
|
||||
}
|
||||
if contentLenIsOdd {
|
||||
c := <-encoder
|
||||
if c < 0 {
|
||||
return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, AlphaNumeric)
|
||||
}
|
||||
res.AddBits(c, 6)
|
||||
}
|
||||
|
||||
addPaddingAndTerminator(res, vi)
|
||||
|
||||
return res, vi, nil
|
||||
}
|
44
vendor/github.com/boombuler/barcode/qr/alphanumeric_test.go
generated
vendored
Normal file
44
vendor/github.com/boombuler/barcode/qr/alphanumeric_test.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func makeString(length int, content string) string {
|
||||
res := ""
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
res += content
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func Test_AlphaNumericEncoding(t *testing.T) {
|
||||
encode := AlphaNumeric.getEncoder()
|
||||
|
||||
x, vi, err := encode("HELLO WORLD", M)
|
||||
|
||||
if x == nil || vi == nil || vi.Version != 1 || bytes.Compare(x.GetBytes(), []byte{32, 91, 11, 120, 209, 114, 220, 77, 67, 64, 236, 17, 236, 17, 236, 17}) != 0 {
|
||||
t.Errorf("\"HELLO WORLD\" failed to encode: %s", err)
|
||||
}
|
||||
|
||||
x, vi, err = encode(makeString(4296, "A"), L)
|
||||
if x == nil || vi == nil || err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
x, vi, err = encode(makeString(4297, "A"), L)
|
||||
if x != nil || vi != nil || err == nil {
|
||||
t.Fail()
|
||||
}
|
||||
x, vi, err = encode("ABc", L)
|
||||
if x != nil || vi != nil || err == nil {
|
||||
t.Fail()
|
||||
}
|
||||
x, vi, err = encode("hello world", M)
|
||||
|
||||
if x != nil || vi != nil || err == nil {
|
||||
t.Error("\"hello world\" should not be encodable in alphanumeric mode")
|
||||
}
|
||||
}
|
23
vendor/github.com/boombuler/barcode/qr/automatic.go
generated
vendored
Normal file
23
vendor/github.com/boombuler/barcode/qr/automatic.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
func encodeAuto(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
|
||||
bits, vi, _ := Numeric.getEncoder()(content, ecl)
|
||||
if bits != nil && vi != nil {
|
||||
return bits, vi, nil
|
||||
}
|
||||
bits, vi, _ = AlphaNumeric.getEncoder()(content, ecl)
|
||||
if bits != nil && vi != nil {
|
||||
return bits, vi, nil
|
||||
}
|
||||
bits, vi, _ = Unicode.getEncoder()(content, ecl)
|
||||
if bits != nil && vi != nil {
|
||||
return bits, vi, nil
|
||||
}
|
||||
return nil, nil, fmt.Errorf("No encoding found to encode \"%s\"", content)
|
||||
}
|
30
vendor/github.com/boombuler/barcode/qr/automatic_test.go
generated
vendored
Normal file
30
vendor/github.com/boombuler/barcode/qr/automatic_test.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_AutomaticEncoding(t *testing.T) {
|
||||
tests := map[string]encodeFn{
|
||||
"0123456789": Numeric.getEncoder(),
|
||||
"ALPHA NUMERIC": AlphaNumeric.getEncoder(),
|
||||
"unicode encoing": Unicode.getEncoder(),
|
||||
"very long unicode encoding" + makeString(3000, "A"): nil,
|
||||
}
|
||||
|
||||
for str, enc := range tests {
|
||||
testValue, _, _ := Auto.getEncoder()(str, M)
|
||||
if enc != nil {
|
||||
correctValue, _, _ := enc(str, M)
|
||||
if testValue == nil || bytes.Compare(correctValue.GetBytes(), testValue.GetBytes()) != 0 {
|
||||
t.Errorf("wrong encoding used for '%s'", str)
|
||||
}
|
||||
} else {
|
||||
if testValue != nil {
|
||||
t.Errorf("wrong encoding used for '%s'", str)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
59
vendor/github.com/boombuler/barcode/qr/blocks.go
generated
vendored
Normal file
59
vendor/github.com/boombuler/barcode/qr/blocks.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package qr
|
||||
|
||||
type block struct {
|
||||
data []byte
|
||||
ecc []byte
|
||||
}
|
||||
type blockList []*block
|
||||
|
||||
func splitToBlocks(data <-chan byte, vi *versionInfo) blockList {
|
||||
result := make(blockList, vi.NumberOfBlocksInGroup1+vi.NumberOfBlocksInGroup2)
|
||||
|
||||
for b := 0; b < int(vi.NumberOfBlocksInGroup1); b++ {
|
||||
blk := new(block)
|
||||
blk.data = make([]byte, vi.DataCodeWordsPerBlockInGroup1)
|
||||
for cw := 0; cw < int(vi.DataCodeWordsPerBlockInGroup1); cw++ {
|
||||
blk.data[cw] = <-data
|
||||
}
|
||||
blk.ecc = ec.calcECC(blk.data, vi.ErrorCorrectionCodewordsPerBlock)
|
||||
result[b] = blk
|
||||
}
|
||||
|
||||
for b := 0; b < int(vi.NumberOfBlocksInGroup2); b++ {
|
||||
blk := new(block)
|
||||
blk.data = make([]byte, vi.DataCodeWordsPerBlockInGroup2)
|
||||
for cw := 0; cw < int(vi.DataCodeWordsPerBlockInGroup2); cw++ {
|
||||
blk.data[cw] = <-data
|
||||
}
|
||||
blk.ecc = ec.calcECC(blk.data, vi.ErrorCorrectionCodewordsPerBlock)
|
||||
result[int(vi.NumberOfBlocksInGroup1)+b] = blk
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (bl blockList) interleave(vi *versionInfo) []byte {
|
||||
var maxCodewordCount int
|
||||
if vi.DataCodeWordsPerBlockInGroup1 > vi.DataCodeWordsPerBlockInGroup2 {
|
||||
maxCodewordCount = int(vi.DataCodeWordsPerBlockInGroup1)
|
||||
} else {
|
||||
maxCodewordCount = int(vi.DataCodeWordsPerBlockInGroup2)
|
||||
}
|
||||
resultLen := (vi.DataCodeWordsPerBlockInGroup1+vi.ErrorCorrectionCodewordsPerBlock)*vi.NumberOfBlocksInGroup1 +
|
||||
(vi.DataCodeWordsPerBlockInGroup2+vi.ErrorCorrectionCodewordsPerBlock)*vi.NumberOfBlocksInGroup2
|
||||
|
||||
result := make([]byte, 0, resultLen)
|
||||
for i := 0; i < maxCodewordCount; i++ {
|
||||
for b := 0; b < len(bl); b++ {
|
||||
if len(bl[b].data) > i {
|
||||
result = append(result, bl[b].data[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 0; i < int(vi.ErrorCorrectionCodewordsPerBlock); i++ {
|
||||
for b := 0; b < len(bl); b++ {
|
||||
result = append(result, bl[b].ecc[i])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
36
vendor/github.com/boombuler/barcode/qr/blocks_test.go
generated
vendored
Normal file
36
vendor/github.com/boombuler/barcode/qr/blocks_test.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Blocks(t *testing.T) {
|
||||
byteIt := make(chan byte)
|
||||
go func() {
|
||||
for _, b := range []byte{67, 85, 70, 134, 87, 38, 85, 194, 119, 50, 6, 18, 6, 103, 38, 246, 246, 66, 7, 118, 134, 242, 7, 38, 86, 22, 198, 199, 146, 6, 182, 230, 247, 119, 50, 7, 118, 134, 87, 38, 82, 6, 134, 151, 50, 7, 70, 247, 118, 86, 194, 6, 151, 50, 16, 236, 17, 236, 17, 236, 17, 236} {
|
||||
byteIt <- b
|
||||
}
|
||||
close(byteIt)
|
||||
}()
|
||||
vi := &versionInfo{5, Q, 18, 2, 15, 2, 16}
|
||||
|
||||
data := splitToBlocks(byteIt, vi).interleave(vi)
|
||||
if bytes.Compare(data, []byte{67, 246, 182, 70, 85, 246, 230, 247, 70, 66, 247, 118, 134, 7, 119, 86, 87, 118, 50, 194, 38, 134, 7, 6, 85, 242, 118, 151, 194, 7, 134, 50, 119, 38, 87, 16, 50, 86, 38, 236, 6, 22, 82, 17, 18, 198, 6, 236, 6, 199, 134, 17, 103, 146, 151, 236, 38, 6, 50, 17, 7, 236, 213, 87, 148, 235, 199, 204, 116, 159, 11, 96, 177, 5, 45, 60, 212, 173, 115, 202, 76, 24, 247, 182, 133, 147, 241, 124, 75, 59, 223, 157, 242, 33, 229, 200, 238, 106, 248, 134, 76, 40, 154, 27, 195, 255, 117, 129, 230, 172, 154, 209, 189, 82, 111, 17, 10, 2, 86, 163, 108, 131, 161, 163, 240, 32, 111, 120, 192, 178, 39, 133, 141, 236}) != 0 {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
byteIt2 := make(chan byte)
|
||||
go func() {
|
||||
for _, b := range []byte{67, 85, 70, 134, 87, 38, 85, 194, 119, 50, 6, 18, 6, 103, 38, 246, 246, 66, 7, 118, 134, 242, 7, 38, 86, 22, 198, 199, 146, 6, 182, 230, 247, 119, 50, 7, 118, 134, 87, 38, 82, 6, 134, 151, 50, 7, 70, 247, 118, 86, 194, 6, 151, 50, 16, 236, 17, 236, 17, 236, 17, 236} {
|
||||
byteIt2 <- b
|
||||
}
|
||||
close(byteIt2)
|
||||
}()
|
||||
vi = &versionInfo{5, Q, 18, 2, 16, 2, 15}
|
||||
|
||||
data = splitToBlocks(byteIt2, vi).interleave(vi)
|
||||
if bytes.Compare(data, []byte{67, 246, 247, 247, 85, 66, 119, 118, 70, 7, 50, 86, 134, 118, 7, 194, 87, 134, 118, 6, 38, 242, 134, 151, 85, 7, 87, 50, 194, 38, 38, 16, 119, 86, 82, 236, 50, 22, 6, 17, 6, 198, 134, 236, 18, 199, 151, 17, 6, 146, 50, 236, 103, 6, 7, 17, 38, 182, 70, 236, 246, 230, 71, 101, 27, 62, 13, 91, 166, 86, 138, 16, 78, 229, 102, 11, 199, 107, 2, 182, 132, 103, 89, 66, 136, 69, 78, 255, 116, 129, 126, 163, 219, 234, 158, 216, 42, 234, 97, 62, 186, 59, 123, 148, 220, 191, 254, 145, 82, 95, 129, 79, 236, 254, 30, 174, 228, 50, 181, 110, 150, 205, 34, 235, 242, 0, 115, 147, 58, 243, 28, 140, 221, 219}) != 0 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
416
vendor/github.com/boombuler/barcode/qr/encoder.go
generated
vendored
Normal file
416
vendor/github.com/boombuler/barcode/qr/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,416 @@
|
|||
// Package qr can be used to create QR barcodes.
|
||||
package qr
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type encodeFn func(content string, eccLevel ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error)
|
||||
|
||||
// Encoding mode for QR Codes.
|
||||
type Encoding byte
|
||||
|
||||
const (
|
||||
// Auto will choose ths best matching encoding
|
||||
Auto Encoding = iota
|
||||
// Numeric encoding only encodes numbers [0-9]
|
||||
Numeric
|
||||
// AlphaNumeric encoding only encodes uppercase letters, numbers and [Space], $, %, *, +, -, ., /, :
|
||||
AlphaNumeric
|
||||
// Unicode encoding encodes the string as utf-8
|
||||
Unicode
|
||||
// only for testing purpose
|
||||
unknownEncoding
|
||||
)
|
||||
|
||||
func (e Encoding) getEncoder() encodeFn {
|
||||
switch e {
|
||||
case Auto:
|
||||
return encodeAuto
|
||||
case Numeric:
|
||||
return encodeNumeric
|
||||
case AlphaNumeric:
|
||||
return encodeAlphaNumeric
|
||||
case Unicode:
|
||||
return encodeUnicode
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e Encoding) String() string {
|
||||
switch e {
|
||||
case Auto:
|
||||
return "Auto"
|
||||
case Numeric:
|
||||
return "Numeric"
|
||||
case AlphaNumeric:
|
||||
return "AlphaNumeric"
|
||||
case Unicode:
|
||||
return "Unicode"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Encode returns a QR barcode with the given content, error correction level and uses the given encoding
|
||||
func Encode(content string, level ErrorCorrectionLevel, mode Encoding) (barcode.Barcode, error) {
|
||||
bits, vi, err := mode.getEncoder()(content, level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blocks := splitToBlocks(bits.IterateBytes(), vi)
|
||||
data := blocks.interleave(vi)
|
||||
result := render(data, vi)
|
||||
result.content = content
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func render(data []byte, vi *versionInfo) *qrcode {
|
||||
dim := vi.modulWidth()
|
||||
results := make([]*qrcode, 8)
|
||||
for i := 0; i < 8; i++ {
|
||||
results[i] = newBarcode(dim)
|
||||
}
|
||||
|
||||
occupied := newBarcode(dim)
|
||||
|
||||
setAll := func(x int, y int, val bool) {
|
||||
occupied.Set(x, y, true)
|
||||
for i := 0; i < 8; i++ {
|
||||
results[i].Set(x, y, val)
|
||||
}
|
||||
}
|
||||
|
||||
drawFinderPatterns(vi, setAll)
|
||||
drawAlignmentPatterns(occupied, vi, setAll)
|
||||
|
||||
//Timing Pattern:
|
||||
var i int
|
||||
for i = 0; i < dim; i++ {
|
||||
if !occupied.Get(i, 6) {
|
||||
setAll(i, 6, i%2 == 0)
|
||||
}
|
||||
if !occupied.Get(6, i) {
|
||||
setAll(6, i, i%2 == 0)
|
||||
}
|
||||
}
|
||||
// Dark Module
|
||||
setAll(8, dim-8, true)
|
||||
|
||||
drawVersionInfo(vi, setAll)
|
||||
drawFormatInfo(vi, -1, occupied.Set)
|
||||
for i := 0; i < 8; i++ {
|
||||
drawFormatInfo(vi, i, results[i].Set)
|
||||
}
|
||||
|
||||
// Write the data
|
||||
var curBitNo int
|
||||
|
||||
for pos := range iterateModules(occupied) {
|
||||
var curBit bool
|
||||
if curBitNo < len(data)*8 {
|
||||
curBit = ((data[curBitNo/8] >> uint(7-(curBitNo%8))) & 1) == 1
|
||||
} else {
|
||||
curBit = false
|
||||
}
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
setMasked(pos.X, pos.Y, curBit, i, results[i].Set)
|
||||
}
|
||||
curBitNo++
|
||||
}
|
||||
|
||||
lowestPenalty := ^uint(0)
|
||||
lowestPenaltyIdx := -1
|
||||
for i := 0; i < 8; i++ {
|
||||
p := results[i].calcPenalty()
|
||||
if p < lowestPenalty {
|
||||
lowestPenalty = p
|
||||
lowestPenaltyIdx = i
|
||||
}
|
||||
}
|
||||
return results[lowestPenaltyIdx]
|
||||
}
|
||||
|
||||
func setMasked(x, y int, val bool, mask int, set func(int, int, bool)) {
|
||||
switch mask {
|
||||
case 0:
|
||||
val = val != (((y + x) % 2) == 0)
|
||||
break
|
||||
case 1:
|
||||
val = val != ((y % 2) == 0)
|
||||
break
|
||||
case 2:
|
||||
val = val != ((x % 3) == 0)
|
||||
break
|
||||
case 3:
|
||||
val = val != (((y + x) % 3) == 0)
|
||||
break
|
||||
case 4:
|
||||
val = val != (((y/2 + x/3) % 2) == 0)
|
||||
break
|
||||
case 5:
|
||||
val = val != (((y*x)%2)+((y*x)%3) == 0)
|
||||
break
|
||||
case 6:
|
||||
val = val != ((((y*x)%2)+((y*x)%3))%2 == 0)
|
||||
break
|
||||
case 7:
|
||||
val = val != ((((y+x)%2)+((y*x)%3))%2 == 0)
|
||||
}
|
||||
set(x, y, val)
|
||||
}
|
||||
|
||||
func iterateModules(occupied *qrcode) <-chan image.Point {
|
||||
result := make(chan image.Point)
|
||||
allPoints := make(chan image.Point)
|
||||
go func() {
|
||||
curX := occupied.dimension - 1
|
||||
curY := occupied.dimension - 1
|
||||
isUpward := true
|
||||
|
||||
for true {
|
||||
if isUpward {
|
||||
allPoints <- image.Pt(curX, curY)
|
||||
allPoints <- image.Pt(curX-1, curY)
|
||||
curY--
|
||||
if curY < 0 {
|
||||
curY = 0
|
||||
curX -= 2
|
||||
if curX == 6 {
|
||||
curX--
|
||||
}
|
||||
if curX < 0 {
|
||||
break
|
||||
}
|
||||
isUpward = false
|
||||
}
|
||||
} else {
|
||||
allPoints <- image.Pt(curX, curY)
|
||||
allPoints <- image.Pt(curX-1, curY)
|
||||
curY++
|
||||
if curY >= occupied.dimension {
|
||||
curY = occupied.dimension - 1
|
||||
curX -= 2
|
||||
if curX == 6 {
|
||||
curX--
|
||||
}
|
||||
isUpward = true
|
||||
if curX < 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(allPoints)
|
||||
}()
|
||||
go func() {
|
||||
for pt := range allPoints {
|
||||
if !occupied.Get(pt.X, pt.Y) {
|
||||
result <- pt
|
||||
}
|
||||
}
|
||||
close(result)
|
||||
}()
|
||||
return result
|
||||
}
|
||||
|
||||
func drawFinderPatterns(vi *versionInfo, set func(int, int, bool)) {
|
||||
dim := vi.modulWidth()
|
||||
drawPattern := func(xoff int, yoff int) {
|
||||
for x := -1; x < 8; x++ {
|
||||
for y := -1; y < 8; y++ {
|
||||
val := (x == 0 || x == 6 || y == 0 || y == 6 || (x > 1 && x < 5 && y > 1 && y < 5)) && (x <= 6 && y <= 6 && x >= 0 && y >= 0)
|
||||
|
||||
if x+xoff >= 0 && x+xoff < dim && y+yoff >= 0 && y+yoff < dim {
|
||||
set(x+xoff, y+yoff, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
drawPattern(0, 0)
|
||||
drawPattern(0, dim-7)
|
||||
drawPattern(dim-7, 0)
|
||||
}
|
||||
|
||||
func drawAlignmentPatterns(occupied *qrcode, vi *versionInfo, set func(int, int, bool)) {
|
||||
drawPattern := func(xoff int, yoff int) {
|
||||
for x := -2; x <= 2; x++ {
|
||||
for y := -2; y <= 2; y++ {
|
||||
val := x == -2 || x == 2 || y == -2 || y == 2 || (x == 0 && y == 0)
|
||||
set(x+xoff, y+yoff, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
positions := vi.alignmentPatternPlacements()
|
||||
|
||||
for _, x := range positions {
|
||||
for _, y := range positions {
|
||||
if occupied.Get(x, y) {
|
||||
continue
|
||||
}
|
||||
drawPattern(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var formatInfos = map[ErrorCorrectionLevel]map[int][]bool{
|
||||
L: {
|
||||
0: []bool{true, true, true, false, true, true, true, true, true, false, false, false, true, false, false},
|
||||
1: []bool{true, true, true, false, false, true, false, true, true, true, true, false, false, true, true},
|
||||
2: []bool{true, true, true, true, true, false, true, true, false, true, false, true, false, true, false},
|
||||
3: []bool{true, true, true, true, false, false, false, true, false, false, true, true, true, false, true},
|
||||
4: []bool{true, true, false, false, true, true, false, false, false, true, false, true, true, true, true},
|
||||
5: []bool{true, true, false, false, false, true, true, false, false, false, true, true, false, false, false},
|
||||
6: []bool{true, true, false, true, true, false, false, false, true, false, false, false, false, false, true},
|
||||
7: []bool{true, true, false, true, false, false, true, false, true, true, true, false, true, true, false},
|
||||
},
|
||||
M: {
|
||||
0: []bool{true, false, true, false, true, false, false, false, false, false, true, false, false, true, false},
|
||||
1: []bool{true, false, true, false, false, false, true, false, false, true, false, false, true, false, true},
|
||||
2: []bool{true, false, true, true, true, true, false, false, true, true, true, true, true, false, false},
|
||||
3: []bool{true, false, true, true, false, true, true, false, true, false, false, true, false, true, true},
|
||||
4: []bool{true, false, false, false, true, false, true, true, true, true, true, true, false, false, true},
|
||||
5: []bool{true, false, false, false, false, false, false, true, true, false, false, true, true, true, false},
|
||||
6: []bool{true, false, false, true, true, true, true, true, false, false, true, false, true, true, true},
|
||||
7: []bool{true, false, false, true, false, true, false, true, false, true, false, false, false, false, false},
|
||||
},
|
||||
Q: {
|
||||
0: []bool{false, true, true, false, true, false, true, false, true, false, true, true, true, true, true},
|
||||
1: []bool{false, true, true, false, false, false, false, false, true, true, false, true, false, false, false},
|
||||
2: []bool{false, true, true, true, true, true, true, false, false, true, true, false, false, false, true},
|
||||
3: []bool{false, true, true, true, false, true, false, false, false, false, false, false, true, true, false},
|
||||
4: []bool{false, true, false, false, true, false, false, true, false, true, true, false, true, false, false},
|
||||
5: []bool{false, true, false, false, false, false, true, true, false, false, false, false, false, true, true},
|
||||
6: []bool{false, true, false, true, true, true, false, true, true, false, true, true, false, true, false},
|
||||
7: []bool{false, true, false, true, false, true, true, true, true, true, false, true, true, false, true},
|
||||
},
|
||||
H: {
|
||||
0: []bool{false, false, true, false, true, true, false, true, false, false, false, true, false, false, true},
|
||||
1: []bool{false, false, true, false, false, true, true, true, false, true, true, true, true, true, false},
|
||||
2: []bool{false, false, true, true, true, false, false, true, true, true, false, false, true, true, true},
|
||||
3: []bool{false, false, true, true, false, false, true, true, true, false, true, false, false, false, false},
|
||||
4: []bool{false, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
|
||||
5: []bool{false, false, false, false, false, true, false, false, true, false, true, false, true, false, true},
|
||||
6: []bool{false, false, false, true, true, false, true, false, false, false, false, true, true, false, false},
|
||||
7: []bool{false, false, false, true, false, false, false, false, false, true, true, true, false, true, true},
|
||||
},
|
||||
}
|
||||
|
||||
func drawFormatInfo(vi *versionInfo, usedMask int, set func(int, int, bool)) {
|
||||
var formatInfo []bool
|
||||
|
||||
if usedMask == -1 {
|
||||
formatInfo = []bool{true, true, true, true, true, true, true, true, true, true, true, true, true, true, true} // Set all to true cause -1 --> occupied mask.
|
||||
} else {
|
||||
formatInfo = formatInfos[vi.Level][usedMask]
|
||||
}
|
||||
|
||||
if len(formatInfo) == 15 {
|
||||
dim := vi.modulWidth()
|
||||
set(0, 8, formatInfo[0])
|
||||
set(1, 8, formatInfo[1])
|
||||
set(2, 8, formatInfo[2])
|
||||
set(3, 8, formatInfo[3])
|
||||
set(4, 8, formatInfo[4])
|
||||
set(5, 8, formatInfo[5])
|
||||
set(7, 8, formatInfo[6])
|
||||
set(8, 8, formatInfo[7])
|
||||
set(8, 7, formatInfo[8])
|
||||
set(8, 5, formatInfo[9])
|
||||
set(8, 4, formatInfo[10])
|
||||
set(8, 3, formatInfo[11])
|
||||
set(8, 2, formatInfo[12])
|
||||
set(8, 1, formatInfo[13])
|
||||
set(8, 0, formatInfo[14])
|
||||
|
||||
set(8, dim-1, formatInfo[0])
|
||||
set(8, dim-2, formatInfo[1])
|
||||
set(8, dim-3, formatInfo[2])
|
||||
set(8, dim-4, formatInfo[3])
|
||||
set(8, dim-5, formatInfo[4])
|
||||
set(8, dim-6, formatInfo[5])
|
||||
set(8, dim-7, formatInfo[6])
|
||||
set(dim-8, 8, formatInfo[7])
|
||||
set(dim-7, 8, formatInfo[8])
|
||||
set(dim-6, 8, formatInfo[9])
|
||||
set(dim-5, 8, formatInfo[10])
|
||||
set(dim-4, 8, formatInfo[11])
|
||||
set(dim-3, 8, formatInfo[12])
|
||||
set(dim-2, 8, formatInfo[13])
|
||||
set(dim-1, 8, formatInfo[14])
|
||||
}
|
||||
}
|
||||
|
||||
var versionInfoBitsByVersion = map[byte][]bool{
|
||||
7: []bool{false, false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false},
|
||||
8: []bool{false, false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false},
|
||||
9: []bool{false, false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true},
|
||||
10: []bool{false, false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true},
|
||||
11: []bool{false, false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false},
|
||||
12: []bool{false, false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
|
||||
13: []bool{false, false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true},
|
||||
14: []bool{false, false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true},
|
||||
15: []bool{false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false},
|
||||
16: []bool{false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false},
|
||||
17: []bool{false, true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true},
|
||||
18: []bool{false, true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true},
|
||||
19: []bool{false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false},
|
||||
20: []bool{false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true, false},
|
||||
21: []bool{false, true, false, true, false, true, false, true, true, false, true, false, false, false, false, false, true, true},
|
||||
22: []bool{false, true, false, true, true, false, true, false, false, false, true, true, false, false, true, false, false, true},
|
||||
23: []bool{false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false, false},
|
||||
24: []bool{false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false, false},
|
||||
25: []bool{false, true, true, false, false, true, false, false, false, true, true, true, true, false, false, false, false, true},
|
||||
26: []bool{false, true, true, false, true, false, true, true, true, true, true, false, true, false, true, false, true, true},
|
||||
27: []bool{false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true, false},
|
||||
28: []bool{false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true, false},
|
||||
29: []bool{false, true, true, true, false, true, false, false, true, true, false, false, true, true, true, true, true, true},
|
||||
30: []bool{false, true, true, true, true, false, true, true, false, true, false, true, true, true, false, true, false, true},
|
||||
31: []bool{false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false, false},
|
||||
32: []bool{true, false, false, false, false, false, true, false, false, true, true, true, false, true, false, true, false, true},
|
||||
33: []bool{true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false, false},
|
||||
34: []bool{true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true, false},
|
||||
35: []bool{true, false, false, false, true, true, false, true, true, true, true, false, false, true, true, true, true, true},
|
||||
36: []bool{true, false, false, true, false, false, true, false, true, true, false, false, false, false, true, false, true, true},
|
||||
37: []bool{true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true, false},
|
||||
38: []bool{true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false, false},
|
||||
39: []bool{true, false, false, true, true, true, false, true, false, true, false, true, false, false, false, false, false, true},
|
||||
40: []bool{true, false, true, false, false, false, true, true, false, false, false, true, true, false, true, false, false, true},
|
||||
}
|
||||
|
||||
func drawVersionInfo(vi *versionInfo, set func(int, int, bool)) {
|
||||
versionInfoBits, ok := versionInfoBitsByVersion[vi.Version]
|
||||
|
||||
if ok && len(versionInfoBits) > 0 {
|
||||
for i := 0; i < len(versionInfoBits); i++ {
|
||||
x := (vi.modulWidth() - 11) + i%3
|
||||
y := i / 3
|
||||
set(x, y, versionInfoBits[len(versionInfoBits)-i-1])
|
||||
set(y, x, versionInfoBits[len(versionInfoBits)-i-1])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func addPaddingAndTerminator(bl *utils.BitList, vi *versionInfo) {
|
||||
for i := 0; i < 4 && bl.Len() < vi.totalDataBytes()*8; i++ {
|
||||
bl.AddBit(false)
|
||||
}
|
||||
|
||||
for bl.Len()%8 != 0 {
|
||||
bl.AddBit(false)
|
||||
}
|
||||
|
||||
for i := 0; bl.Len() < vi.totalDataBytes()*8; i++ {
|
||||
if i%2 == 0 {
|
||||
bl.AddByte(236)
|
||||
} else {
|
||||
bl.AddByte(17)
|
||||
}
|
||||
}
|
||||
}
|
134
vendor/github.com/boombuler/barcode/qr/encoder_test.go
generated
vendored
Normal file
134
vendor/github.com/boombuler/barcode/qr/encoder_test.go
generated
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/png"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
)
|
||||
|
||||
type test struct {
|
||||
Text string
|
||||
Mode Encoding
|
||||
ECL ErrorCorrectionLevel
|
||||
Result string
|
||||
}
|
||||
|
||||
var tests = []test{
|
||||
test{
|
||||
Text: "hello world",
|
||||
Mode: Unicode,
|
||||
ECL: H,
|
||||
Result: `
|
||||
+++++++.+.+.+...+.+++++++
|
||||
+.....+.++...+++..+.....+
|
||||
+.+++.+.+.+.++.++.+.+++.+
|
||||
+.+++.+....++.++..+.+++.+
|
||||
+.+++.+..+...++.+.+.+++.+
|
||||
+.....+.+..+..+++.+.....+
|
||||
+++++++.+.+.+.+.+.+++++++
|
||||
........++..+..+.........
|
||||
..+++.+.+++.+.++++++..+++
|
||||
+++..+..+...++.+...+..+..
|
||||
+...+.++++....++.+..++.++
|
||||
++.+.+.++...+...+.+....++
|
||||
..+..+++.+.+++++.++++++++
|
||||
+.+++...+..++..++..+..+..
|
||||
+.....+..+.+.....+++++.++
|
||||
+.+++.....+...+.+.+++...+
|
||||
+.+..+++...++.+.+++++++..
|
||||
........+....++.+...+.+..
|
||||
+++++++......++++.+.+.+++
|
||||
+.....+....+...++...++.+.
|
||||
+.+++.+.+.+...+++++++++..
|
||||
+.+++.+.++...++...+.++..+
|
||||
+.+++.+.++.+++++..++.+..+
|
||||
+.....+..+++..++.+.++...+
|
||||
+++++++....+..+.+..+..+++`,
|
||||
},
|
||||
}
|
||||
|
||||
func Test_GetUnknownEncoder(t *testing.T) {
|
||||
if unknownEncoding.getEncoder() != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_EncodingStringer(t *testing.T) {
|
||||
tests := map[Encoding]string{
|
||||
Auto: "Auto",
|
||||
Numeric: "Numeric",
|
||||
AlphaNumeric: "AlphaNumeric",
|
||||
Unicode: "Unicode",
|
||||
unknownEncoding: "",
|
||||
}
|
||||
|
||||
for enc, str := range tests {
|
||||
if enc.String() != str {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_InvalidEncoding(t *testing.T) {
|
||||
_, err := Encode("hello world", H, Numeric)
|
||||
if err == nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func imgStrToBools(str string) []bool {
|
||||
res := make([]bool, 0, len(str))
|
||||
for _, r := range str {
|
||||
if r == '+' {
|
||||
res = append(res, true)
|
||||
} else if r == '.' {
|
||||
res = append(res, false)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func Test_Encode(t *testing.T) {
|
||||
for _, tst := range tests {
|
||||
res, err := Encode(tst.Text, tst.ECL, tst.Mode)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
qrCode, ok := res.(*qrcode)
|
||||
if !ok {
|
||||
t.Fail()
|
||||
}
|
||||
testRes := imgStrToBools(tst.Result)
|
||||
if (qrCode.dimension * qrCode.dimension) != len(testRes) {
|
||||
t.Fail()
|
||||
}
|
||||
t.Logf("dim %d", qrCode.dimension)
|
||||
for i := 0; i < len(testRes); i++ {
|
||||
x := i % qrCode.dimension
|
||||
y := i / qrCode.dimension
|
||||
if qrCode.Get(x, y) != testRes[i] {
|
||||
t.Errorf("Failed at index %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleEncode() {
|
||||
f, _ := os.Create("qrcode.png")
|
||||
defer f.Close()
|
||||
|
||||
qrcode, err := Encode("hello world", L, Auto)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
qrcode, err = barcode.Scale(qrcode, 100, 100)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
png.Encode(f, qrcode)
|
||||
}
|
||||
}
|
||||
}
|
29
vendor/github.com/boombuler/barcode/qr/errorcorrection.go
generated
vendored
Normal file
29
vendor/github.com/boombuler/barcode/qr/errorcorrection.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type errorCorrection struct {
|
||||
rs *utils.ReedSolomonEncoder
|
||||
}
|
||||
|
||||
var ec = newErrorCorrection()
|
||||
|
||||
func newErrorCorrection() *errorCorrection {
|
||||
fld := utils.NewGaloisField(285, 256, 0)
|
||||
return &errorCorrection{utils.NewReedSolomonEncoder(fld)}
|
||||
}
|
||||
|
||||
func (ec *errorCorrection) calcECC(data []byte, eccCount byte) []byte {
|
||||
dataInts := make([]int, len(data))
|
||||
for i := 0; i < len(data); i++ {
|
||||
dataInts[i] = int(data[i])
|
||||
}
|
||||
res := ec.rs.Encode(dataInts, int(eccCount))
|
||||
result := make([]byte, len(res))
|
||||
for i := 0; i < len(res); i++ {
|
||||
result[i] = byte(res[i])
|
||||
}
|
||||
return result
|
||||
}
|
60
vendor/github.com/boombuler/barcode/qr/errorcorrection_test.go
generated
vendored
Normal file
60
vendor/github.com/boombuler/barcode/qr/errorcorrection_test.go
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_ErrorCorrection(t *testing.T) {
|
||||
doTest := func(b []byte, ecc []byte) {
|
||||
cnt := byte(len(ecc))
|
||||
res := ec.calcECC(b, cnt)
|
||||
if bytes.Compare(res, ecc) != 0 {
|
||||
t.Errorf("ECC error!\nGot: %v\nExpected:%v", res, ecc)
|
||||
}
|
||||
}
|
||||
// Issue #5
|
||||
doTest([]byte{66, 196, 148, 21, 99, 19, 151, 151, 53, 149, 54, 195, 4, 133, 87, 84, 115, 85, 22, 148, 52, 71, 102, 68, 134, 182, 247, 119, 22, 68, 117, 134, 35, 4, 134, 38, 21, 84, 21, 117, 87, 164, 135, 115, 211, 208, 236, 17, 236, 17, 236, 17, 236, 17, 236}, []byte{187, 187, 171, 253, 164, 129, 104, 133, 3, 75, 87, 98, 241, 146, 138})
|
||||
|
||||
// Other tests
|
||||
doTest([]byte{17, 168, 162, 241, 255, 205, 240, 179, 88, 101, 71, 130, 2, 54, 147, 111, 232, 58, 202, 171, 85, 22, 229, 187}, []byte{30, 142, 171, 131, 189})
|
||||
doTest([]byte{36, 153, 55, 100, 228, 252, 0, 35, 85, 7, 237, 117, 182, 73, 83, 244, 8, 64, 55, 252, 200, 250, 72, 92, 97, 125, 96}, []byte{129, 124, 218, 148, 49, 108, 68, 255, 58, 212, 56, 60, 142, 45, 216, 124, 253, 214, 206, 208, 145, 169, 43})
|
||||
doTest([]byte{250, 195, 230, 128, 31, 168, 86, 123, 244, 129, 74, 130, 222, 225, 140, 129, 114, 132, 128, 88, 96, 13, 165, 132, 116, 22, 42, 81, 219, 3, 102, 156, 69, 70, 90, 68, 7, 245, 150, 160, 252, 121, 20}, []byte{124, 23, 233, 71, 200, 211, 54, 141, 10, 23, 206, 147, 116, 35, 45, 218, 158, 193, 80, 194, 129, 147, 8, 78, 229, 112, 89, 161, 167, 203, 11, 245, 186, 187, 17, 7, 175})
|
||||
doTest([]byte{121, 234, 24, 188, 218, 238, 248, 223, 98, 124, 237, 30, 98, 12, 9, 126, 5, 160, 240, 27, 174, 60, 152, 134, 71, 122, 125, 238, 223, 91, 231, 248, 230, 152, 250, 44, 17, 149, 0, 20, 109, 188, 227, 202}, []byte{209, 71, 225, 216, 240, 127, 111, 98, 194, 133, 114, 63, 35, 167, 184, 4, 209, 211, 40, 14, 74, 37, 21, 76, 95, 206, 90, 152, 110, 64, 6, 92, 80, 255, 127, 35, 111, 25, 1, 73})
|
||||
doTest([]byte{165, 233, 141, 34, 247, 216, 35, 163, 61, 61, 81, 146, 116, 96, 113, 10, 0, 6, 148, 244, 55, 201, 17, 220, 109, 111}, []byte{93, 173, 231, 160})
|
||||
doTest([]byte{173, 242, 89, 205, 24, 33, 213, 147, 96, 189, 100, 15, 213, 67, 91, 189, 218, 127, 32, 160, 162, 99, 187, 221, 53, 121, 238, 219, 215, 176, 181, 135, 56, 71, 246, 74, 228}, []byte{194, 130, 43, 168, 223, 144, 223, 49, 5, 162, 62, 218, 50, 205, 249, 84, 188, 25, 109, 110, 49, 224, 194, 244, 83, 221, 236, 71, 197, 159, 182})
|
||||
doTest([]byte{82, 138, 221, 169, 67, 161, 132, 31, 243, 110, 83, 1, 238, 79, 255, 57, 74, 54, 123, 151, 159, 50, 250, 188, 176, 8, 221, 215, 141, 77, 16}, []byte{197, 122, 225, 65, 40, 69, 153, 100, 73, 245, 150, 213, 104, 127, 3})
|
||||
doTest([]byte{5, 206, 21, 196, 185, 120, 60, 177, 90, 251, 109, 131, 174, 199, 55, 56, 14, 171, 19, 104, 236, 218, 31, 144, 33, 249, 58, 195, 173, 145, 166, 93, 122, 171, 232, 128, 233, 116, 144, 189, 62, 230, 68, 55, 140, 56, 1, 65, 165, 158, 127}, []byte{73, 141, 230, 252, 225, 173, 251, 194, 150, 98, 141, 241, 246, 11, 16, 8, 42})
|
||||
doTest([]byte{112, 106, 43, 174, 133, 163, 192, 61, 121, 3, 200, 84, 15, 9, 3, 222, 183, 78, 153, 26, 85, 41, 5, 149, 232, 3, 233, 247, 249, 29, 15, 18, 4, 96, 9, 64, 188, 210}, []byte{16, 254, 143, 110, 63, 167, 213, 242, 95, 78, 215, 145, 231, 59, 158, 36, 149, 247, 123, 114, 247, 202, 15, 56, 229, 163, 186, 73, 82, 230, 111, 108, 111, 182, 193, 46, 116})
|
||||
doTest([]byte{208, 128, 197, 227, 124, 226, 125, 46, 253, 98, 238, 80, 229, 134, 167, 70, 101, 150, 198, 130, 185, 200, 68, 91}, []byte{229, 167, 187, 39, 92, 90, 210, 25, 206, 237, 90, 194, 206, 39, 2, 11, 78, 48, 247})
|
||||
doTest([]byte{79, 175, 255, 194, 34, 229, 234, 200, 74, 213, 100, 33, 24, 5, 133, 186, 249, 151, 46, 190, 44, 126, 184, 195, 219, 37, 11, 225, 23, 8, 59, 106, 239, 198, 146, 205, 47, 59, 63, 9, 102, 29, 60, 209, 226, 67, 126, 193, 252, 255, 206, 172, 44, 53, 137, 209, 246}, []byte{237, 8, 12, 44, 90, 243, 24, 100, 123, 216, 185, 91, 182, 60, 9, 145, 126, 254, 139, 24, 211, 150, 219, 28, 138, 197, 13, 109, 227, 31, 60, 128, 237, 181, 183, 2, 138, 232, 112, 5})
|
||||
doTest([]byte{253, 217, 8, 176, 66, 153, 249, 49, 82, 114, 184, 139, 190, 87}, []byte{28, 55, 193, 193, 179, 246, 222, 5, 95, 96, 13, 242})
|
||||
doTest([]byte{15, 65, 231, 224, 151, 167, 74, 228, 23}, []byte{200, 90, 82})
|
||||
doTest([]byte{61, 186, 61, 193, 215, 243, 84, 66, 48, 93, 108, 249, 55, 232}, []byte{0, 180, 53, 152, 134, 252, 165, 168})
|
||||
doTest([]byte{78, 68, 116, 15, 85}, []byte{36})
|
||||
doTest([]byte{122, 143}, []byte{245})
|
||||
doTest([]byte{78, 85, 143, 35}, []byte{226, 85})
|
||||
doTest([]byte{11, 188, 118, 21, 177, 224, 151, 105, 21, 245, 251, 162, 72, 175, 248, 134, 123, 251, 160, 163, 42, 57, 53, 222, 195, 49, 199, 151, 5, 236, 160, 57, 212, 241, 44, 43}, []byte{186, 106})
|
||||
doTest([]byte{157, 99, 220, 166, 63, 18, 225, 215, 71, 95, 99, 200, 218, 147, 131, 245, 222, 209, 135, 152, 82, 128, 24, 0, 100, 40, 84, 193, 205, 86, 130, 204, 235, 100, 94, 61}, []byte{41, 171, 66, 233})
|
||||
doTest([]byte{249, 34, 253, 235, 233, 104, 52, 60, 17, 13, 182, 223, 19, 91, 164, 2, 196, 29, 74, 219, 65, 23, 190, 31, 10, 241, 221, 150, 221, 118, 53, 69, 45, 90, 215, 100, 155, 102, 150, 176, 203, 39, 22, 70, 10, 238}, []byte{161, 49, 179, 149, 178, 146, 208, 144, 19, 158, 180, 152, 243, 138, 143, 243, 82, 112, 229, 10, 113, 255, 139, 246})
|
||||
doTest([]byte{39, 232, 159, 64, 242, 235, 66, 226, 100, 221, 225, 247, 139, 157, 95, 155}, []byte{41, 9, 244})
|
||||
doTest([]byte{177, 185, 131, 64, 103, 93, 134, 153, 15, 26, 0, 119, 21, 27, 174, 181, 111, 245, 214, 244, 83, 66, 24, 244, 255, 189, 133, 158, 37, 46, 199, 123, 110, 153, 61, 137, 163, 231, 129, 65, 186, 89, 219, 39, 226, 236, 199, 197, 73, 213}, []byte{37, 59, 125, 211, 249, 177, 107, 79, 107, 47, 242, 168, 49, 38, 168, 198, 199, 91, 212, 22, 107, 244})
|
||||
doTest([]byte{196, 226, 29, 110, 161, 143, 64, 169, 216, 231, 115}, []byte{253, 93, 218, 129, 37})
|
||||
doTest([]byte{133, 8, 124, 221, 36, 17, 135, 115, 149, 58, 250, 103, 241, 18, 19, 246, 191, 85, 80, 255, 93, 182, 140, 123, 206, 232, 20, 166, 216, 105, 210, 229, 249, 212, 93, 227, 75, 231, 36, 195, 166, 246, 47, 168, 35, 7, 176, 124, 44, 179, 24, 145}, []byte{78, 57, 134, 181, 215, 149, 111, 51, 172, 58, 114, 3, 140, 186, 126, 40, 190})
|
||||
doTest([]byte{245, 206, 124, 0, 15, 59, 253, 225, 155}, []byte{65, 14, 188, 213, 18, 113, 161, 16})
|
||||
doTest([]byte{20, 109, 28, 180, 48, 170, 216, 48, 140, 89, 103}, []byte{193, 147, 50, 209, 160})
|
||||
doTest([]byte{87, 198, 56, 151, 121, 37, 81, 64, 193, 24, 222, 142, 102, 74, 216, 233, 198, 197, 90, 4, 65, 14, 154, 147, 200, 252, 8, 64, 97, 150, 136, 141}, []byte{231, 190, 32, 90, 100, 40, 41, 103, 200, 200, 243, 75, 177, 7, 93, 28, 83, 47, 188, 236, 20, 95, 69, 104, 155, 102, 110, 197})
|
||||
doTest([]byte{168, 72, 2, 101, 103, 118, 218, 38, 82, 85, 62, 37, 201, 96, 255, 71, 198}, []byte{129, 33, 28, 228, 195, 120, 101, 46, 119, 126})
|
||||
doTest([]byte{130, 162, 73, 44, 165, 207, 124, 28, 17, 223, 43, 143, 81, 70, 205, 161, 143, 230, 97, 94, 228, 41, 26, 187, 69, 85, 162, 51, 168, 64, 26, 207, 245, 128}, []byte{6, 171})
|
||||
doTest([]byte{95, 28, 93, 149, 234, 89, 201, 71, 39, 197, 236, 223, 251, 190, 112, 96, 101, 53, 40, 88, 136, 141, 230, 80, 45, 73, 116, 208, 197, 91, 154, 209, 128, 214, 66, 114, 137, 204, 115, 139, 96, 211, 148, 127, 104, 194}, []byte{10, 102, 57, 95, 61, 212, 130, 71, 74, 58, 82, 115, 238, 213, 251, 184, 203, 250, 55, 186, 37, 16, 71, 247, 146, 194, 74, 208, 221, 6, 81, 172, 204, 73, 102, 40, 247, 174, 213, 37, 225, 246, 8, 58})
|
||||
doTest([]byte{207, 185, 106, 191, 87, 109, 110, 210, 54, 12, 103, 161, 228}, []byte{214, 138, 159, 195, 154, 236, 33, 243, 53, 79, 227})
|
||||
doTest([]byte{203, 43, 26, 94, 37, 123, 254, 215, 153, 193, 157, 248, 180, 249, 103, 232, 107, 17, 138, 0, 11, 240, 218, 122, 19, 103, 112, 60, 125, 100, 209, 166, 103, 81, 200, 84, 77, 100, 18, 110, 209, 225, 209, 254, 185, 116, 186, 216, 206, 36, 252, 144, 90, 247, 117, 219, 81, 160}, []byte{185, 176, 106, 253, 76, 153, 185, 211, 187, 153, 210, 31, 99, 4, 46, 145, 221, 99, 236, 19, 126, 138, 66, 26, 40, 217, 170, 217, 147})
|
||||
doTest([]byte{11, 193, 90, 52, 239, 247, 144, 99, 48, 19, 154, 6, 255, 28, 47, 41, 30, 220}, []byte{235, 165, 125, 82, 28, 116, 21, 133, 243, 222, 241, 20, 134})
|
||||
doTest([]byte{173, 151, 109, 88, 104, 65, 76, 111, 219, 237, 2, 173, 25, 84, 98, 16, 135, 157, 14, 194, 228, 86, 167, 187, 137, 245, 144, 61, 200, 76, 188, 117, 223, 172, 16, 116, 84, 1, 203, 173, 170, 32, 135, 67, 16}, []byte{150, 31, 11, 211, 82, 221, 251, 84, 254, 121, 68, 34, 211, 142, 197, 246, 138, 204, 60, 197, 210, 238, 142, 234, 187, 200, 179, 228})
|
||||
doTest([]byte{171, 185, 30, 162, 129, 205, 254, 186, 86, 239, 178, 206, 115, 177, 14, 166, 143, 48, 141, 205, 109, 67, 238, 187, 134, 210, 96, 23, 195, 206, 100, 171, 156, 8, 229, 131, 169, 169, 59, 167, 224, 241, 185, 132, 162, 50, 87, 252, 156, 122, 248, 19, 130, 31, 127}, []byte{62, 42, 216, 109, 23, 176, 255, 137, 139, 90, 7, 186, 175, 243, 160, 206, 37, 94, 157, 217, 11, 169, 126, 41, 73, 133, 212, 232, 249, 117, 70, 147, 137, 156, 43, 243, 234, 155, 94, 38, 59, 211, 218, 165, 3, 33, 231, 237, 92, 16, 128})
|
||||
doTest([]byte{98, 28, 174, 108, 231, 247, 135, 139, 6, 50, 107, 203, 138, 252, 229, 245, 230, 236, 124, 138, 105, 25, 83, 122}, []byte{97, 214, 25, 2, 14, 48, 65, 212, 241, 200, 81, 57, 176, 59, 16, 55, 20, 91, 66})
|
||||
doTest([]byte{73, 214, 80, 41, 125, 136, 126, 184, 70, 141, 140, 58, 249, 250, 49, 249, 155, 0, 236, 49, 17, 125, 18, 29}, []byte{128, 16, 47, 235, 125, 128, 97, 245, 177, 210, 219, 195})
|
||||
doTest([]byte{3, 220, 98, 73, 200, 52, 8, 107, 173, 177, 58, 221, 180, 226, 76, 210, 182, 88, 104, 171, 243, 129, 88, 112, 126, 83, 141, 50, 106, 204, 195, 51, 141, 75, 132, 161}, []byte{110, 178, 213, 174, 1, 241, 95})
|
||||
doTest([]byte{196, 88, 50, 142, 76, 128, 190, 189, 76, 9, 228, 62, 198, 186, 180, 240, 62, 130, 132, 242}, []byte{244, 89, 17, 143, 3, 180, 150, 242, 167, 214, 209, 133, 120, 213, 173, 59, 25, 158, 251})
|
||||
doTest([]byte{166, 214, 1, 225, 237, 7, 80, 104, 94, 170, 125, 184, 148, 16, 121, 101, 52, 216, 177, 192, 6, 132, 77, 44, 5, 9, 126, 156, 12, 2, 29, 99, 51, 78, 177, 92, 140, 107, 146, 183, 109, 227, 171, 57, 193, 14, 37}, []byte{245, 46, 189, 11, 202, 195, 89, 53, 215, 172, 132, 196, 145, 141, 239, 160, 242, 7, 85, 251, 193, 85})
|
||||
}
|
56
vendor/github.com/boombuler/barcode/qr/numeric.go
generated
vendored
Normal file
56
vendor/github.com/boombuler/barcode/qr/numeric.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
func encodeNumeric(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
|
||||
contentBitCount := (len(content) / 3) * 10
|
||||
switch len(content) % 3 {
|
||||
case 1:
|
||||
contentBitCount += 4
|
||||
case 2:
|
||||
contentBitCount += 7
|
||||
}
|
||||
vi := findSmallestVersionInfo(ecl, numericMode, contentBitCount)
|
||||
if vi == nil {
|
||||
return nil, nil, errors.New("To much data to encode")
|
||||
}
|
||||
res := new(utils.BitList)
|
||||
res.AddBits(int(numericMode), 4)
|
||||
res.AddBits(len(content), vi.charCountBits(numericMode))
|
||||
|
||||
for pos := 0; pos < len(content); pos += 3 {
|
||||
var curStr string
|
||||
if pos+3 <= len(content) {
|
||||
curStr = content[pos : pos+3]
|
||||
} else {
|
||||
curStr = content[pos:]
|
||||
}
|
||||
|
||||
i, err := strconv.Atoi(curStr)
|
||||
if err != nil || i < 0 {
|
||||
return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, Numeric)
|
||||
}
|
||||
var bitCnt byte
|
||||
switch len(curStr) % 3 {
|
||||
case 0:
|
||||
bitCnt = 10
|
||||
case 1:
|
||||
bitCnt = 4
|
||||
break
|
||||
case 2:
|
||||
bitCnt = 7
|
||||
break
|
||||
}
|
||||
|
||||
res.AddBits(i, bitCnt)
|
||||
}
|
||||
|
||||
addPaddingAndTerminator(res, vi)
|
||||
return res, vi, nil
|
||||
}
|
26
vendor/github.com/boombuler/barcode/qr/numeric_test.go
generated
vendored
Normal file
26
vendor/github.com/boombuler/barcode/qr/numeric_test.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_NumericEncoding(t *testing.T) {
|
||||
encode := Numeric.getEncoder()
|
||||
x, vi, err := encode("01234567", H)
|
||||
if x == nil || vi == nil || vi.Version != 1 || bytes.Compare(x.GetBytes(), []byte{16, 32, 12, 86, 97, 128, 236, 17, 236}) != 0 {
|
||||
t.Error("\"01234567\" failed to encode")
|
||||
}
|
||||
x, vi, err = encode("0123456789012345", H)
|
||||
if x == nil || vi == nil || vi.Version != 1 || bytes.Compare(x.GetBytes(), []byte{16, 64, 12, 86, 106, 110, 20, 234, 80}) != 0 {
|
||||
t.Error("\"0123456789012345\" failed to encode")
|
||||
}
|
||||
x, vi, err = encode("foo", H)
|
||||
if err == nil {
|
||||
t.Error("Numeric encoding should not be able to encode \"foo\"")
|
||||
}
|
||||
x, vi, err = encode(makeString(14297, "1"), H)
|
||||
if x != nil || vi != nil || err == nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
166
vendor/github.com/boombuler/barcode/qr/qrcode.go
generated
vendored
Normal file
166
vendor/github.com/boombuler/barcode/qr/qrcode.go
generated
vendored
Normal file
|
@ -0,0 +1,166 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type qrcode struct {
|
||||
dimension int
|
||||
data *utils.BitList
|
||||
content string
|
||||
}
|
||||
|
||||
func (qr *qrcode) Content() string {
|
||||
return qr.content
|
||||
}
|
||||
|
||||
func (qr *qrcode) Metadata() barcode.Metadata {
|
||||
return barcode.Metadata{barcode.TypeQR, 2}
|
||||
}
|
||||
|
||||
func (qr *qrcode) ColorModel() color.Model {
|
||||
return color.Gray16Model
|
||||
}
|
||||
|
||||
func (qr *qrcode) Bounds() image.Rectangle {
|
||||
return image.Rect(0, 0, qr.dimension, qr.dimension)
|
||||
}
|
||||
|
||||
func (qr *qrcode) At(x, y int) color.Color {
|
||||
if qr.Get(x, y) {
|
||||
return color.Black
|
||||
}
|
||||
return color.White
|
||||
}
|
||||
|
||||
func (qr *qrcode) Get(x, y int) bool {
|
||||
return qr.data.GetBit(x*qr.dimension + y)
|
||||
}
|
||||
|
||||
func (qr *qrcode) Set(x, y int, val bool) {
|
||||
qr.data.SetBit(x*qr.dimension+y, val)
|
||||
}
|
||||
|
||||
func (qr *qrcode) calcPenalty() uint {
|
||||
return qr.calcPenaltyRule1() + qr.calcPenaltyRule2() + qr.calcPenaltyRule3() + qr.calcPenaltyRule4()
|
||||
}
|
||||
|
||||
func (qr *qrcode) calcPenaltyRule1() uint {
|
||||
var result uint
|
||||
for x := 0; x < qr.dimension; x++ {
|
||||
checkForX := false
|
||||
var cntX uint
|
||||
checkForY := false
|
||||
var cntY uint
|
||||
|
||||
for y := 0; y < qr.dimension; y++ {
|
||||
if qr.Get(x, y) == checkForX {
|
||||
cntX++
|
||||
} else {
|
||||
checkForX = !checkForX
|
||||
if cntX >= 5 {
|
||||
result += cntX - 2
|
||||
}
|
||||
cntX = 1
|
||||
}
|
||||
|
||||
if qr.Get(y, x) == checkForY {
|
||||
cntY++
|
||||
} else {
|
||||
checkForY = !checkForY
|
||||
if cntY >= 5 {
|
||||
result += cntY - 2
|
||||
}
|
||||
cntY = 1
|
||||
}
|
||||
}
|
||||
|
||||
if cntX >= 5 {
|
||||
result += cntX - 2
|
||||
}
|
||||
if cntY >= 5 {
|
||||
result += cntY - 2
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (qr *qrcode) calcPenaltyRule2() uint {
|
||||
var result uint
|
||||
for x := 0; x < qr.dimension-1; x++ {
|
||||
for y := 0; y < qr.dimension-1; y++ {
|
||||
check := qr.Get(x, y)
|
||||
if qr.Get(x, y+1) == check && qr.Get(x+1, y) == check && qr.Get(x+1, y+1) == check {
|
||||
result += 3
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (qr *qrcode) calcPenaltyRule3() uint {
|
||||
pattern1 := []bool{true, false, true, true, true, false, true, false, false, false, false}
|
||||
pattern2 := []bool{false, false, false, false, true, false, true, true, true, false, true}
|
||||
|
||||
var result uint
|
||||
for x := 0; x <= qr.dimension-len(pattern1); x++ {
|
||||
for y := 0; y < qr.dimension; y++ {
|
||||
pattern1XFound := true
|
||||
pattern2XFound := true
|
||||
pattern1YFound := true
|
||||
pattern2YFound := true
|
||||
|
||||
for i := 0; i < len(pattern1); i++ {
|
||||
iv := qr.Get(x+i, y)
|
||||
if iv != pattern1[i] {
|
||||
pattern1XFound = false
|
||||
}
|
||||
if iv != pattern2[i] {
|
||||
pattern2XFound = false
|
||||
}
|
||||
iv = qr.Get(y, x+i)
|
||||
if iv != pattern1[i] {
|
||||
pattern1YFound = false
|
||||
}
|
||||
if iv != pattern2[i] {
|
||||
pattern2YFound = false
|
||||
}
|
||||
}
|
||||
if pattern1XFound || pattern2XFound {
|
||||
result += 40
|
||||
}
|
||||
if pattern1YFound || pattern2YFound {
|
||||
result += 40
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (qr *qrcode) calcPenaltyRule4() uint {
|
||||
totalNum := qr.data.Len()
|
||||
trueCnt := 0
|
||||
for i := 0; i < totalNum; i++ {
|
||||
if qr.data.GetBit(i) {
|
||||
trueCnt++
|
||||
}
|
||||
}
|
||||
percDark := float64(trueCnt) * 100 / float64(totalNum)
|
||||
floor := math.Abs(math.Floor(percDark/5) - 10)
|
||||
ceil := math.Abs(math.Ceil(percDark/5) - 10)
|
||||
return uint(math.Min(floor, ceil) * 10)
|
||||
}
|
||||
|
||||
func newBarcode(dim int) *qrcode {
|
||||
res := new(qrcode)
|
||||
res.dimension = dim
|
||||
res.data = utils.NewBitList(dim * dim)
|
||||
return res
|
||||
}
|
126
vendor/github.com/boombuler/barcode/qr/qrcode_test.go
generated
vendored
Normal file
126
vendor/github.com/boombuler/barcode/qr/qrcode_test.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_NewQRCode(t *testing.T) {
|
||||
bc := newBarcode(2)
|
||||
if bc == nil {
|
||||
t.Fail()
|
||||
}
|
||||
if bc.data.Len() != 4 {
|
||||
t.Fail()
|
||||
}
|
||||
if bc.dimension != 2 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_QRBasics(t *testing.T) {
|
||||
qr := newBarcode(10)
|
||||
if qr.ColorModel() != color.Gray16Model {
|
||||
t.Fail()
|
||||
}
|
||||
code, _ := Encode("test", L, Unicode)
|
||||
if code.Content() != "test" {
|
||||
t.Fail()
|
||||
}
|
||||
if code.Metadata().Dimensions != 2 {
|
||||
t.Fail()
|
||||
}
|
||||
bounds := code.Bounds()
|
||||
if bounds.Min.X != 0 || bounds.Min.Y != 0 || bounds.Max.X != 21 || bounds.Max.Y != 21 {
|
||||
t.Fail()
|
||||
}
|
||||
if code.At(0, 0) != color.Black || code.At(0, 7) != color.White {
|
||||
t.Fail()
|
||||
}
|
||||
qr = code.(*qrcode)
|
||||
if !qr.Get(0, 0) || qr.Get(0, 7) {
|
||||
t.Fail()
|
||||
}
|
||||
sum := qr.calcPenaltyRule1() + qr.calcPenaltyRule2() + qr.calcPenaltyRule3() + qr.calcPenaltyRule4()
|
||||
if qr.calcPenalty() != sum {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Penalty1(t *testing.T) {
|
||||
qr := newBarcode(7)
|
||||
if qr.calcPenaltyRule1() != 70 {
|
||||
t.Fail()
|
||||
}
|
||||
qr.Set(0, 0, true)
|
||||
if qr.calcPenaltyRule1() != 68 {
|
||||
t.Fail()
|
||||
}
|
||||
qr.Set(0, 6, true)
|
||||
if qr.calcPenaltyRule1() != 66 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Penalty2(t *testing.T) {
|
||||
qr := newBarcode(3)
|
||||
if qr.calcPenaltyRule2() != 12 {
|
||||
t.Fail()
|
||||
}
|
||||
qr.Set(0, 0, true)
|
||||
qr.Set(1, 1, true)
|
||||
qr.Set(2, 0, true)
|
||||
if qr.calcPenaltyRule2() != 0 {
|
||||
t.Fail()
|
||||
}
|
||||
qr.Set(1, 1, false)
|
||||
if qr.calcPenaltyRule2() != 6 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Penalty3(t *testing.T) {
|
||||
runTest := func(content string, result uint) {
|
||||
code, _ := Encode(content, L, AlphaNumeric)
|
||||
qr := code.(*qrcode)
|
||||
if qr.calcPenaltyRule3() != result {
|
||||
t.Errorf("Failed Penalty Rule 3 for content \"%s\" got %d but expected %d", content, qr.calcPenaltyRule3(), result)
|
||||
}
|
||||
}
|
||||
runTest("A", 80)
|
||||
runTest("FOO", 40)
|
||||
runTest("0815", 0)
|
||||
}
|
||||
|
||||
func Test_Penalty4(t *testing.T) {
|
||||
qr := newBarcode(3)
|
||||
if qr.calcPenaltyRule4() != 100 {
|
||||
t.Fail()
|
||||
}
|
||||
qr.Set(0, 0, true)
|
||||
if qr.calcPenaltyRule4() != 70 {
|
||||
t.Fail()
|
||||
}
|
||||
qr.Set(0, 1, true)
|
||||
if qr.calcPenaltyRule4() != 50 {
|
||||
t.Fail()
|
||||
}
|
||||
qr.Set(0, 2, true)
|
||||
if qr.calcPenaltyRule4() != 30 {
|
||||
t.Fail()
|
||||
}
|
||||
qr.Set(1, 0, true)
|
||||
if qr.calcPenaltyRule4() != 10 {
|
||||
t.Fail()
|
||||
}
|
||||
qr.Set(1, 1, true)
|
||||
if qr.calcPenaltyRule4() != 10 {
|
||||
t.Fail()
|
||||
}
|
||||
qr = newBarcode(2)
|
||||
qr.Set(0, 0, true)
|
||||
qr.Set(1, 0, true)
|
||||
if qr.calcPenaltyRule4() != 0 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
27
vendor/github.com/boombuler/barcode/qr/unicode.go
generated
vendored
Normal file
27
vendor/github.com/boombuler/barcode/qr/unicode.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
func encodeUnicode(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
|
||||
data := []byte(content)
|
||||
|
||||
vi := findSmallestVersionInfo(ecl, byteMode, len(data)*8)
|
||||
if vi == nil {
|
||||
return nil, nil, errors.New("To much data to encode")
|
||||
}
|
||||
|
||||
// It's not correct to add the unicode bytes to the result directly but most readers can't handle the
|
||||
// required ECI header...
|
||||
res := new(utils.BitList)
|
||||
res.AddBits(int(byteMode), 4)
|
||||
res.AddBits(len(content), vi.charCountBits(byteMode))
|
||||
for _, b := range data {
|
||||
res.AddByte(b)
|
||||
}
|
||||
addPaddingAndTerminator(res, vi)
|
||||
return res, vi, nil
|
||||
}
|
18
vendor/github.com/boombuler/barcode/qr/unicode_test.go
generated
vendored
Normal file
18
vendor/github.com/boombuler/barcode/qr/unicode_test.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_UnicodeEncoding(t *testing.T) {
|
||||
encode := Unicode.getEncoder()
|
||||
x, vi, err := encode("A", H) // 65
|
||||
if x == nil || vi == nil || vi.Version != 1 || bytes.Compare(x.GetBytes(), []byte{64, 20, 16, 236, 17, 236, 17, 236, 17}) != 0 {
|
||||
t.Errorf("\"A\" failed to encode: %s", err)
|
||||
}
|
||||
_, _, err = encode(makeString(3000, "A"), H)
|
||||
if err == nil {
|
||||
t.Error("Unicode encoding should not be able to encode a 3kb string")
|
||||
}
|
||||
}
|
310
vendor/github.com/boombuler/barcode/qr/versioninfo.go
generated
vendored
Normal file
310
vendor/github.com/boombuler/barcode/qr/versioninfo.go
generated
vendored
Normal file
|
@ -0,0 +1,310 @@
|
|||
package qr
|
||||
|
||||
import "math"
|
||||
|
||||
// ErrorCorrectionLevel indicates the amount of "backup data" stored in the QR code
|
||||
type ErrorCorrectionLevel byte
|
||||
|
||||
const (
|
||||
// L recovers 7% of data
|
||||
L ErrorCorrectionLevel = iota
|
||||
// M recovers 15% of data
|
||||
M
|
||||
// Q recovers 25% of data
|
||||
Q
|
||||
// H recovers 30% of data
|
||||
H
|
||||
)
|
||||
|
||||
func (ecl ErrorCorrectionLevel) String() string {
|
||||
switch ecl {
|
||||
case L:
|
||||
return "L"
|
||||
case M:
|
||||
return "M"
|
||||
case Q:
|
||||
return "Q"
|
||||
case H:
|
||||
return "H"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
type encodingMode byte
|
||||
|
||||
const (
|
||||
numericMode encodingMode = 1
|
||||
alphaNumericMode encodingMode = 2
|
||||
byteMode encodingMode = 4
|
||||
kanjiMode encodingMode = 8
|
||||
)
|
||||
|
||||
type versionInfo struct {
|
||||
Version byte
|
||||
Level ErrorCorrectionLevel
|
||||
ErrorCorrectionCodewordsPerBlock byte
|
||||
NumberOfBlocksInGroup1 byte
|
||||
DataCodeWordsPerBlockInGroup1 byte
|
||||
NumberOfBlocksInGroup2 byte
|
||||
DataCodeWordsPerBlockInGroup2 byte
|
||||
}
|
||||
|
||||
var versionInfos = []*versionInfo{
|
||||
&versionInfo{1, L, 7, 1, 19, 0, 0},
|
||||
&versionInfo{1, M, 10, 1, 16, 0, 0},
|
||||
&versionInfo{1, Q, 13, 1, 13, 0, 0},
|
||||
&versionInfo{1, H, 17, 1, 9, 0, 0},
|
||||
&versionInfo{2, L, 10, 1, 34, 0, 0},
|
||||
&versionInfo{2, M, 16, 1, 28, 0, 0},
|
||||
&versionInfo{2, Q, 22, 1, 22, 0, 0},
|
||||
&versionInfo{2, H, 28, 1, 16, 0, 0},
|
||||
&versionInfo{3, L, 15, 1, 55, 0, 0},
|
||||
&versionInfo{3, M, 26, 1, 44, 0, 0},
|
||||
&versionInfo{3, Q, 18, 2, 17, 0, 0},
|
||||
&versionInfo{3, H, 22, 2, 13, 0, 0},
|
||||
&versionInfo{4, L, 20, 1, 80, 0, 0},
|
||||
&versionInfo{4, M, 18, 2, 32, 0, 0},
|
||||
&versionInfo{4, Q, 26, 2, 24, 0, 0},
|
||||
&versionInfo{4, H, 16, 4, 9, 0, 0},
|
||||
&versionInfo{5, L, 26, 1, 108, 0, 0},
|
||||
&versionInfo{5, M, 24, 2, 43, 0, 0},
|
||||
&versionInfo{5, Q, 18, 2, 15, 2, 16},
|
||||
&versionInfo{5, H, 22, 2, 11, 2, 12},
|
||||
&versionInfo{6, L, 18, 2, 68, 0, 0},
|
||||
&versionInfo{6, M, 16, 4, 27, 0, 0},
|
||||
&versionInfo{6, Q, 24, 4, 19, 0, 0},
|
||||
&versionInfo{6, H, 28, 4, 15, 0, 0},
|
||||
&versionInfo{7, L, 20, 2, 78, 0, 0},
|
||||
&versionInfo{7, M, 18, 4, 31, 0, 0},
|
||||
&versionInfo{7, Q, 18, 2, 14, 4, 15},
|
||||
&versionInfo{7, H, 26, 4, 13, 1, 14},
|
||||
&versionInfo{8, L, 24, 2, 97, 0, 0},
|
||||
&versionInfo{8, M, 22, 2, 38, 2, 39},
|
||||
&versionInfo{8, Q, 22, 4, 18, 2, 19},
|
||||
&versionInfo{8, H, 26, 4, 14, 2, 15},
|
||||
&versionInfo{9, L, 30, 2, 116, 0, 0},
|
||||
&versionInfo{9, M, 22, 3, 36, 2, 37},
|
||||
&versionInfo{9, Q, 20, 4, 16, 4, 17},
|
||||
&versionInfo{9, H, 24, 4, 12, 4, 13},
|
||||
&versionInfo{10, L, 18, 2, 68, 2, 69},
|
||||
&versionInfo{10, M, 26, 4, 43, 1, 44},
|
||||
&versionInfo{10, Q, 24, 6, 19, 2, 20},
|
||||
&versionInfo{10, H, 28, 6, 15, 2, 16},
|
||||
&versionInfo{11, L, 20, 4, 81, 0, 0},
|
||||
&versionInfo{11, M, 30, 1, 50, 4, 51},
|
||||
&versionInfo{11, Q, 28, 4, 22, 4, 23},
|
||||
&versionInfo{11, H, 24, 3, 12, 8, 13},
|
||||
&versionInfo{12, L, 24, 2, 92, 2, 93},
|
||||
&versionInfo{12, M, 22, 6, 36, 2, 37},
|
||||
&versionInfo{12, Q, 26, 4, 20, 6, 21},
|
||||
&versionInfo{12, H, 28, 7, 14, 4, 15},
|
||||
&versionInfo{13, L, 26, 4, 107, 0, 0},
|
||||
&versionInfo{13, M, 22, 8, 37, 1, 38},
|
||||
&versionInfo{13, Q, 24, 8, 20, 4, 21},
|
||||
&versionInfo{13, H, 22, 12, 11, 4, 12},
|
||||
&versionInfo{14, L, 30, 3, 115, 1, 116},
|
||||
&versionInfo{14, M, 24, 4, 40, 5, 41},
|
||||
&versionInfo{14, Q, 20, 11, 16, 5, 17},
|
||||
&versionInfo{14, H, 24, 11, 12, 5, 13},
|
||||
&versionInfo{15, L, 22, 5, 87, 1, 88},
|
||||
&versionInfo{15, M, 24, 5, 41, 5, 42},
|
||||
&versionInfo{15, Q, 30, 5, 24, 7, 25},
|
||||
&versionInfo{15, H, 24, 11, 12, 7, 13},
|
||||
&versionInfo{16, L, 24, 5, 98, 1, 99},
|
||||
&versionInfo{16, M, 28, 7, 45, 3, 46},
|
||||
&versionInfo{16, Q, 24, 15, 19, 2, 20},
|
||||
&versionInfo{16, H, 30, 3, 15, 13, 16},
|
||||
&versionInfo{17, L, 28, 1, 107, 5, 108},
|
||||
&versionInfo{17, M, 28, 10, 46, 1, 47},
|
||||
&versionInfo{17, Q, 28, 1, 22, 15, 23},
|
||||
&versionInfo{17, H, 28, 2, 14, 17, 15},
|
||||
&versionInfo{18, L, 30, 5, 120, 1, 121},
|
||||
&versionInfo{18, M, 26, 9, 43, 4, 44},
|
||||
&versionInfo{18, Q, 28, 17, 22, 1, 23},
|
||||
&versionInfo{18, H, 28, 2, 14, 19, 15},
|
||||
&versionInfo{19, L, 28, 3, 113, 4, 114},
|
||||
&versionInfo{19, M, 26, 3, 44, 11, 45},
|
||||
&versionInfo{19, Q, 26, 17, 21, 4, 22},
|
||||
&versionInfo{19, H, 26, 9, 13, 16, 14},
|
||||
&versionInfo{20, L, 28, 3, 107, 5, 108},
|
||||
&versionInfo{20, M, 26, 3, 41, 13, 42},
|
||||
&versionInfo{20, Q, 30, 15, 24, 5, 25},
|
||||
&versionInfo{20, H, 28, 15, 15, 10, 16},
|
||||
&versionInfo{21, L, 28, 4, 116, 4, 117},
|
||||
&versionInfo{21, M, 26, 17, 42, 0, 0},
|
||||
&versionInfo{21, Q, 28, 17, 22, 6, 23},
|
||||
&versionInfo{21, H, 30, 19, 16, 6, 17},
|
||||
&versionInfo{22, L, 28, 2, 111, 7, 112},
|
||||
&versionInfo{22, M, 28, 17, 46, 0, 0},
|
||||
&versionInfo{22, Q, 30, 7, 24, 16, 25},
|
||||
&versionInfo{22, H, 24, 34, 13, 0, 0},
|
||||
&versionInfo{23, L, 30, 4, 121, 5, 122},
|
||||
&versionInfo{23, M, 28, 4, 47, 14, 48},
|
||||
&versionInfo{23, Q, 30, 11, 24, 14, 25},
|
||||
&versionInfo{23, H, 30, 16, 15, 14, 16},
|
||||
&versionInfo{24, L, 30, 6, 117, 4, 118},
|
||||
&versionInfo{24, M, 28, 6, 45, 14, 46},
|
||||
&versionInfo{24, Q, 30, 11, 24, 16, 25},
|
||||
&versionInfo{24, H, 30, 30, 16, 2, 17},
|
||||
&versionInfo{25, L, 26, 8, 106, 4, 107},
|
||||
&versionInfo{25, M, 28, 8, 47, 13, 48},
|
||||
&versionInfo{25, Q, 30, 7, 24, 22, 25},
|
||||
&versionInfo{25, H, 30, 22, 15, 13, 16},
|
||||
&versionInfo{26, L, 28, 10, 114, 2, 115},
|
||||
&versionInfo{26, M, 28, 19, 46, 4, 47},
|
||||
&versionInfo{26, Q, 28, 28, 22, 6, 23},
|
||||
&versionInfo{26, H, 30, 33, 16, 4, 17},
|
||||
&versionInfo{27, L, 30, 8, 122, 4, 123},
|
||||
&versionInfo{27, M, 28, 22, 45, 3, 46},
|
||||
&versionInfo{27, Q, 30, 8, 23, 26, 24},
|
||||
&versionInfo{27, H, 30, 12, 15, 28, 16},
|
||||
&versionInfo{28, L, 30, 3, 117, 10, 118},
|
||||
&versionInfo{28, M, 28, 3, 45, 23, 46},
|
||||
&versionInfo{28, Q, 30, 4, 24, 31, 25},
|
||||
&versionInfo{28, H, 30, 11, 15, 31, 16},
|
||||
&versionInfo{29, L, 30, 7, 116, 7, 117},
|
||||
&versionInfo{29, M, 28, 21, 45, 7, 46},
|
||||
&versionInfo{29, Q, 30, 1, 23, 37, 24},
|
||||
&versionInfo{29, H, 30, 19, 15, 26, 16},
|
||||
&versionInfo{30, L, 30, 5, 115, 10, 116},
|
||||
&versionInfo{30, M, 28, 19, 47, 10, 48},
|
||||
&versionInfo{30, Q, 30, 15, 24, 25, 25},
|
||||
&versionInfo{30, H, 30, 23, 15, 25, 16},
|
||||
&versionInfo{31, L, 30, 13, 115, 3, 116},
|
||||
&versionInfo{31, M, 28, 2, 46, 29, 47},
|
||||
&versionInfo{31, Q, 30, 42, 24, 1, 25},
|
||||
&versionInfo{31, H, 30, 23, 15, 28, 16},
|
||||
&versionInfo{32, L, 30, 17, 115, 0, 0},
|
||||
&versionInfo{32, M, 28, 10, 46, 23, 47},
|
||||
&versionInfo{32, Q, 30, 10, 24, 35, 25},
|
||||
&versionInfo{32, H, 30, 19, 15, 35, 16},
|
||||
&versionInfo{33, L, 30, 17, 115, 1, 116},
|
||||
&versionInfo{33, M, 28, 14, 46, 21, 47},
|
||||
&versionInfo{33, Q, 30, 29, 24, 19, 25},
|
||||
&versionInfo{33, H, 30, 11, 15, 46, 16},
|
||||
&versionInfo{34, L, 30, 13, 115, 6, 116},
|
||||
&versionInfo{34, M, 28, 14, 46, 23, 47},
|
||||
&versionInfo{34, Q, 30, 44, 24, 7, 25},
|
||||
&versionInfo{34, H, 30, 59, 16, 1, 17},
|
||||
&versionInfo{35, L, 30, 12, 121, 7, 122},
|
||||
&versionInfo{35, M, 28, 12, 47, 26, 48},
|
||||
&versionInfo{35, Q, 30, 39, 24, 14, 25},
|
||||
&versionInfo{35, H, 30, 22, 15, 41, 16},
|
||||
&versionInfo{36, L, 30, 6, 121, 14, 122},
|
||||
&versionInfo{36, M, 28, 6, 47, 34, 48},
|
||||
&versionInfo{36, Q, 30, 46, 24, 10, 25},
|
||||
&versionInfo{36, H, 30, 2, 15, 64, 16},
|
||||
&versionInfo{37, L, 30, 17, 122, 4, 123},
|
||||
&versionInfo{37, M, 28, 29, 46, 14, 47},
|
||||
&versionInfo{37, Q, 30, 49, 24, 10, 25},
|
||||
&versionInfo{37, H, 30, 24, 15, 46, 16},
|
||||
&versionInfo{38, L, 30, 4, 122, 18, 123},
|
||||
&versionInfo{38, M, 28, 13, 46, 32, 47},
|
||||
&versionInfo{38, Q, 30, 48, 24, 14, 25},
|
||||
&versionInfo{38, H, 30, 42, 15, 32, 16},
|
||||
&versionInfo{39, L, 30, 20, 117, 4, 118},
|
||||
&versionInfo{39, M, 28, 40, 47, 7, 48},
|
||||
&versionInfo{39, Q, 30, 43, 24, 22, 25},
|
||||
&versionInfo{39, H, 30, 10, 15, 67, 16},
|
||||
&versionInfo{40, L, 30, 19, 118, 6, 119},
|
||||
&versionInfo{40, M, 28, 18, 47, 31, 48},
|
||||
&versionInfo{40, Q, 30, 34, 24, 34, 25},
|
||||
&versionInfo{40, H, 30, 20, 15, 61, 16},
|
||||
}
|
||||
|
||||
func (vi *versionInfo) totalDataBytes() int {
|
||||
g1Data := int(vi.NumberOfBlocksInGroup1) * int(vi.DataCodeWordsPerBlockInGroup1)
|
||||
g2Data := int(vi.NumberOfBlocksInGroup2) * int(vi.DataCodeWordsPerBlockInGroup2)
|
||||
return (g1Data + g2Data)
|
||||
}
|
||||
|
||||
func (vi *versionInfo) charCountBits(m encodingMode) byte {
|
||||
switch m {
|
||||
case numericMode:
|
||||
if vi.Version < 10 {
|
||||
return 10
|
||||
} else if vi.Version < 27 {
|
||||
return 12
|
||||
}
|
||||
return 14
|
||||
|
||||
case alphaNumericMode:
|
||||
if vi.Version < 10 {
|
||||
return 9
|
||||
} else if vi.Version < 27 {
|
||||
return 11
|
||||
}
|
||||
return 13
|
||||
|
||||
case byteMode:
|
||||
if vi.Version < 10 {
|
||||
return 8
|
||||
}
|
||||
return 16
|
||||
|
||||
case kanjiMode:
|
||||
if vi.Version < 10 {
|
||||
return 8
|
||||
} else if vi.Version < 27 {
|
||||
return 10
|
||||
}
|
||||
return 12
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (vi *versionInfo) modulWidth() int {
|
||||
return ((int(vi.Version) - 1) * 4) + 21
|
||||
}
|
||||
|
||||
func (vi *versionInfo) alignmentPatternPlacements() []int {
|
||||
if vi.Version == 1 {
|
||||
return make([]int, 0)
|
||||
}
|
||||
|
||||
first := 6
|
||||
last := vi.modulWidth() - 7
|
||||
space := float64(last - first)
|
||||
count := int(math.Ceil(space/28)) + 1
|
||||
|
||||
result := make([]int, count)
|
||||
result[0] = first
|
||||
result[len(result)-1] = last
|
||||
if count > 2 {
|
||||
step := int(math.Ceil(float64(last-first) / float64(count-1)))
|
||||
if step%2 == 1 {
|
||||
frac := float64(last-first) / float64(count-1)
|
||||
_, x := math.Modf(frac)
|
||||
if x >= 0.5 {
|
||||
frac = math.Ceil(frac)
|
||||
} else {
|
||||
frac = math.Floor(frac)
|
||||
}
|
||||
|
||||
if int(frac)%2 == 0 {
|
||||
step--
|
||||
} else {
|
||||
step++
|
||||
}
|
||||
}
|
||||
|
||||
for i := 1; i <= count-2; i++ {
|
||||
result[i] = last - (step * (count - 1 - i))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func findSmallestVersionInfo(ecl ErrorCorrectionLevel, mode encodingMode, dataBits int) *versionInfo {
|
||||
dataBits = dataBits + 4 // mode indicator
|
||||
for _, vi := range versionInfos {
|
||||
if vi.Level == ecl {
|
||||
if (vi.totalDataBytes() * 8) >= (dataBits + int(vi.charCountBits(mode))) {
|
||||
return vi
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
157
vendor/github.com/boombuler/barcode/qr/versioninfo_test.go
generated
vendored
Normal file
157
vendor/github.com/boombuler/barcode/qr/versioninfo_test.go
generated
vendored
Normal file
|
@ -0,0 +1,157 @@
|
|||
package qr
|
||||
|
||||
import "testing"
|
||||
|
||||
var testvi = &versionInfo{7, M, 0, 1, 10, 2, 5} // Fake versionInfo to run some of the tests
|
||||
|
||||
func Test_ErrorCorrectionStringer(t *testing.T) {
|
||||
tests := map[ErrorCorrectionLevel]string{
|
||||
L: "L", M: "M", Q: "Q", H: "H", ErrorCorrectionLevel(99): "unknown",
|
||||
}
|
||||
for ecl, str := range tests {
|
||||
if ecl.String() != str {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_CharCountBits(t *testing.T) {
|
||||
v1 := &versionInfo{5, M, 0, 0, 0, 0, 0}
|
||||
v2 := &versionInfo{15, M, 0, 0, 0, 0, 0}
|
||||
v3 := &versionInfo{30, M, 0, 0, 0, 0, 0}
|
||||
|
||||
if v1.charCountBits(numericMode) != 10 {
|
||||
t.Fail()
|
||||
}
|
||||
if v1.charCountBits(alphaNumericMode) != 9 {
|
||||
t.Fail()
|
||||
}
|
||||
if v1.charCountBits(byteMode) != 8 {
|
||||
t.Fail()
|
||||
}
|
||||
if v1.charCountBits(kanjiMode) != 8 {
|
||||
t.Fail()
|
||||
}
|
||||
if v2.charCountBits(numericMode) != 12 {
|
||||
t.Fail()
|
||||
}
|
||||
if v2.charCountBits(alphaNumericMode) != 11 {
|
||||
t.Fail()
|
||||
}
|
||||
if v2.charCountBits(byteMode) != 16 {
|
||||
t.Fail()
|
||||
}
|
||||
if v2.charCountBits(kanjiMode) != 10 {
|
||||
t.Fail()
|
||||
}
|
||||
if v3.charCountBits(numericMode) != 14 {
|
||||
t.Fail()
|
||||
}
|
||||
if v3.charCountBits(alphaNumericMode) != 13 {
|
||||
t.Fail()
|
||||
}
|
||||
if v3.charCountBits(byteMode) != 16 {
|
||||
t.Fail()
|
||||
}
|
||||
if v3.charCountBits(kanjiMode) != 12 {
|
||||
t.Fail()
|
||||
}
|
||||
if v1.charCountBits(encodingMode(3)) != 0 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_TotalDataBytes(t *testing.T) {
|
||||
if testvi.totalDataBytes() != 20 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ModulWidth(t *testing.T) {
|
||||
if testvi.modulWidth() != 45 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_FindSmallestVersionInfo(t *testing.T) {
|
||||
if findSmallestVersionInfo(H, alphaNumericMode, 10208) != nil {
|
||||
t.Error("there should be no version with this capacity")
|
||||
}
|
||||
test := func(cap int, tVersion byte) {
|
||||
v := findSmallestVersionInfo(H, alphaNumericMode, cap)
|
||||
if v == nil || v.Version != tVersion {
|
||||
t.Errorf("version %d should be returned.", tVersion)
|
||||
}
|
||||
}
|
||||
test(10191, 40)
|
||||
test(5591, 29)
|
||||
test(5592, 30)
|
||||
test(190, 3)
|
||||
test(200, 4)
|
||||
}
|
||||
|
||||
type aligmnentTest struct {
|
||||
version byte
|
||||
patterns []int
|
||||
}
|
||||
|
||||
var allAligmnentTests = []*aligmnentTest{
|
||||
&aligmnentTest{1, []int{}},
|
||||
&aligmnentTest{2, []int{6, 18}},
|
||||
&aligmnentTest{3, []int{6, 22}},
|
||||
&aligmnentTest{4, []int{6, 26}},
|
||||
&aligmnentTest{5, []int{6, 30}},
|
||||
&aligmnentTest{6, []int{6, 34}},
|
||||
&aligmnentTest{7, []int{6, 22, 38}},
|
||||
&aligmnentTest{8, []int{6, 24, 42}},
|
||||
&aligmnentTest{9, []int{6, 26, 46}},
|
||||
&aligmnentTest{10, []int{6, 28, 50}},
|
||||
&aligmnentTest{11, []int{6, 30, 54}},
|
||||
&aligmnentTest{12, []int{6, 32, 58}},
|
||||
&aligmnentTest{13, []int{6, 34, 62}},
|
||||
&aligmnentTest{14, []int{6, 26, 46, 66}},
|
||||
&aligmnentTest{15, []int{6, 26, 48, 70}},
|
||||
&aligmnentTest{16, []int{6, 26, 50, 74}},
|
||||
&aligmnentTest{17, []int{6, 30, 54, 78}},
|
||||
&aligmnentTest{18, []int{6, 30, 56, 82}},
|
||||
&aligmnentTest{19, []int{6, 30, 58, 86}},
|
||||
&aligmnentTest{20, []int{6, 34, 62, 90}},
|
||||
&aligmnentTest{21, []int{6, 28, 50, 72, 94}},
|
||||
&aligmnentTest{22, []int{6, 26, 50, 74, 98}},
|
||||
&aligmnentTest{23, []int{6, 30, 54, 78, 102}},
|
||||
&aligmnentTest{24, []int{6, 28, 54, 80, 106}},
|
||||
&aligmnentTest{25, []int{6, 32, 58, 84, 110}},
|
||||
&aligmnentTest{26, []int{6, 30, 58, 86, 114}},
|
||||
&aligmnentTest{27, []int{6, 34, 62, 90, 118}},
|
||||
&aligmnentTest{28, []int{6, 26, 50, 74, 98, 122}},
|
||||
&aligmnentTest{29, []int{6, 30, 54, 78, 102, 126}},
|
||||
&aligmnentTest{30, []int{6, 26, 52, 78, 104, 130}},
|
||||
&aligmnentTest{31, []int{6, 30, 56, 82, 108, 134}},
|
||||
&aligmnentTest{32, []int{6, 34, 60, 86, 112, 138}},
|
||||
&aligmnentTest{33, []int{6, 30, 58, 86, 114, 142}},
|
||||
&aligmnentTest{34, []int{6, 34, 62, 90, 118, 146}},
|
||||
&aligmnentTest{35, []int{6, 30, 54, 78, 102, 126, 150}},
|
||||
&aligmnentTest{36, []int{6, 24, 50, 76, 102, 128, 154}},
|
||||
&aligmnentTest{37, []int{6, 28, 54, 80, 106, 132, 158}},
|
||||
&aligmnentTest{38, []int{6, 32, 58, 84, 110, 136, 162}},
|
||||
&aligmnentTest{39, []int{6, 26, 54, 82, 110, 138, 166}},
|
||||
&aligmnentTest{40, []int{6, 30, 58, 86, 114, 142, 170}},
|
||||
}
|
||||
|
||||
func Test_AlignmentPatternPlacements(t *testing.T) {
|
||||
for _, at := range allAligmnentTests {
|
||||
vi := &versionInfo{at.version, M, 0, 0, 0, 0, 0}
|
||||
|
||||
res := vi.alignmentPatternPlacements()
|
||||
if len(res) != len(at.patterns) {
|
||||
t.Errorf("number of alignmentpatterns missmatch for version %d", at.version)
|
||||
}
|
||||
for i := 0; i < len(res); i++ {
|
||||
if res[i] != at.patterns[i] {
|
||||
t.Errorf("alignmentpatterns for version %d missmatch on index %d", at.version, i)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
134
vendor/github.com/boombuler/barcode/scaledbarcode.go
generated
vendored
Normal file
134
vendor/github.com/boombuler/barcode/scaledbarcode.go
generated
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
package barcode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
)
|
||||
|
||||
type wrapFunc func(x, y int) color.Color
|
||||
|
||||
type scaledBarcode struct {
|
||||
wrapped Barcode
|
||||
wrapperFunc wrapFunc
|
||||
rect image.Rectangle
|
||||
}
|
||||
|
||||
type intCSscaledBC struct {
|
||||
scaledBarcode
|
||||
}
|
||||
|
||||
func (bc *scaledBarcode) Content() string {
|
||||
return bc.wrapped.Content()
|
||||
}
|
||||
|
||||
func (bc *scaledBarcode) Metadata() Metadata {
|
||||
return bc.wrapped.Metadata()
|
||||
}
|
||||
|
||||
func (bc *scaledBarcode) ColorModel() color.Model {
|
||||
return bc.wrapped.ColorModel()
|
||||
}
|
||||
|
||||
func (bc *scaledBarcode) Bounds() image.Rectangle {
|
||||
return bc.rect
|
||||
}
|
||||
|
||||
func (bc *scaledBarcode) At(x, y int) color.Color {
|
||||
return bc.wrapperFunc(x, y)
|
||||
}
|
||||
|
||||
func (bc *intCSscaledBC) CheckSum() int {
|
||||
if cs, ok := bc.wrapped.(BarcodeIntCS); ok {
|
||||
return cs.CheckSum()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Scale returns a resized barcode with the given width and height.
|
||||
func Scale(bc Barcode, width, height int) (Barcode, error) {
|
||||
switch bc.Metadata().Dimensions {
|
||||
case 1:
|
||||
return scale1DCode(bc, width, height)
|
||||
case 2:
|
||||
return scale2DCode(bc, width, height)
|
||||
}
|
||||
|
||||
return nil, errors.New("unsupported barcode format")
|
||||
}
|
||||
|
||||
func newScaledBC(wrapped Barcode, wrapperFunc wrapFunc, rect image.Rectangle) Barcode {
|
||||
result := &scaledBarcode{
|
||||
wrapped: wrapped,
|
||||
wrapperFunc: wrapperFunc,
|
||||
rect: rect,
|
||||
}
|
||||
|
||||
if _, ok := wrapped.(BarcodeIntCS); ok {
|
||||
return &intCSscaledBC{*result}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func scale2DCode(bc Barcode, width, height int) (Barcode, error) {
|
||||
orgBounds := bc.Bounds()
|
||||
orgWidth := orgBounds.Max.X - orgBounds.Min.X
|
||||
orgHeight := orgBounds.Max.Y - orgBounds.Min.Y
|
||||
|
||||
factor := int(math.Min(float64(width)/float64(orgWidth), float64(height)/float64(orgHeight)))
|
||||
if factor <= 0 {
|
||||
return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx%d", orgWidth, orgHeight)
|
||||
}
|
||||
|
||||
offsetX := (width - (orgWidth * factor)) / 2
|
||||
offsetY := (height - (orgHeight * factor)) / 2
|
||||
|
||||
wrap := func(x, y int) color.Color {
|
||||
if x < offsetX || y < offsetY {
|
||||
return color.White
|
||||
}
|
||||
x = (x - offsetX) / factor
|
||||
y = (y - offsetY) / factor
|
||||
if x >= orgWidth || y >= orgHeight {
|
||||
return color.White
|
||||
}
|
||||
return bc.At(x, y)
|
||||
}
|
||||
|
||||
return newScaledBC(
|
||||
bc,
|
||||
wrap,
|
||||
image.Rect(0, 0, width, height),
|
||||
), nil
|
||||
}
|
||||
|
||||
func scale1DCode(bc Barcode, width, height int) (Barcode, error) {
|
||||
orgBounds := bc.Bounds()
|
||||
orgWidth := orgBounds.Max.X - orgBounds.Min.X
|
||||
factor := int(float64(width) / float64(orgWidth))
|
||||
|
||||
if factor <= 0 {
|
||||
return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx1", orgWidth)
|
||||
}
|
||||
offsetX := (width - (orgWidth * factor)) / 2
|
||||
|
||||
wrap := func(x, y int) color.Color {
|
||||
if x < offsetX {
|
||||
return color.White
|
||||
}
|
||||
x = (x - offsetX) / factor
|
||||
|
||||
if x >= orgWidth {
|
||||
return color.White
|
||||
}
|
||||
return bc.At(x, 0)
|
||||
}
|
||||
|
||||
return newScaledBC(
|
||||
bc,
|
||||
wrap,
|
||||
image.Rect(0, 0, width, height),
|
||||
), nil
|
||||
}
|
138
vendor/github.com/boombuler/barcode/twooffive/encoder.go
generated
vendored
Normal file
138
vendor/github.com/boombuler/barcode/twooffive/encoder.go
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
|||
// Package twooffive can create interleaved and standard "2 of 5" barcodes.
|
||||
package twooffive
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
const patternWidth = 5
|
||||
|
||||
type pattern [patternWidth]bool
|
||||
type encodeInfo struct {
|
||||
start []bool
|
||||
end []bool
|
||||
widths map[bool]int
|
||||
}
|
||||
|
||||
var (
|
||||
encodingTable = map[rune]pattern{
|
||||
'0': pattern{false, false, true, true, false},
|
||||
'1': pattern{true, false, false, false, true},
|
||||
'2': pattern{false, true, false, false, true},
|
||||
'3': pattern{true, true, false, false, false},
|
||||
'4': pattern{false, false, true, false, true},
|
||||
'5': pattern{true, false, true, false, false},
|
||||
'6': pattern{false, true, true, false, false},
|
||||
'7': pattern{false, false, false, true, true},
|
||||
'8': pattern{true, false, false, true, false},
|
||||
'9': pattern{false, true, false, true, false},
|
||||
}
|
||||
|
||||
modes = map[bool]encodeInfo{
|
||||
false: encodeInfo{ // non-interleaved
|
||||
start: []bool{true, true, false, true, true, false, true, false},
|
||||
end: []bool{true, true, false, true, false, true, true},
|
||||
widths: map[bool]int{
|
||||
true: 3,
|
||||
false: 1,
|
||||
},
|
||||
},
|
||||
true: encodeInfo{ // interleaved
|
||||
start: []bool{true, false, true, false},
|
||||
end: []bool{true, true, false, true},
|
||||
widths: map[bool]int{
|
||||
true: 3,
|
||||
false: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
nonInterleavedSpace = pattern{false, false, false, false, false}
|
||||
)
|
||||
|
||||
// AddCheckSum calculates the correct check-digit and appends it to the given content.
|
||||
func AddCheckSum(content string) (string, error) {
|
||||
if content == "" {
|
||||
return "", errors.New("content is empty")
|
||||
}
|
||||
|
||||
even := len(content)%2 == 1
|
||||
sum := 0
|
||||
for _, r := range content {
|
||||
if _, ok := encodingTable[r]; ok {
|
||||
value := utils.RuneToInt(r)
|
||||
if even {
|
||||
sum += value * 3
|
||||
} else {
|
||||
sum += value
|
||||
}
|
||||
even = !even
|
||||
} else {
|
||||
return "", fmt.Errorf("can not encode \"%s\"", content)
|
||||
}
|
||||
}
|
||||
|
||||
return content + string(utils.IntToRune(sum%10)), nil
|
||||
}
|
||||
|
||||
// Encode creates a codabar barcode for the given content
|
||||
func Encode(content string, interleaved bool) (barcode.Barcode, error) {
|
||||
if content == "" {
|
||||
return nil, errors.New("content is empty")
|
||||
}
|
||||
|
||||
if interleaved && len(content)%2 == 1 {
|
||||
return nil, errors.New("can only encode even number of digits in interleaved mode")
|
||||
}
|
||||
|
||||
mode := modes[interleaved]
|
||||
resBits := new(utils.BitList)
|
||||
resBits.AddBit(mode.start...)
|
||||
|
||||
var lastRune *rune
|
||||
for _, r := range content {
|
||||
var a, b pattern
|
||||
if interleaved {
|
||||
if lastRune == nil {
|
||||
lastRune = new(rune)
|
||||
*lastRune = r
|
||||
continue
|
||||
} else {
|
||||
var o1, o2 bool
|
||||
a, o1 = encodingTable[*lastRune]
|
||||
b, o2 = encodingTable[r]
|
||||
if !o1 || !o2 {
|
||||
return nil, fmt.Errorf("can not encode \"%s\"", content)
|
||||
}
|
||||
lastRune = nil
|
||||
}
|
||||
} else {
|
||||
var ok bool
|
||||
a, ok = encodingTable[r]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("can not encode \"%s\"", content)
|
||||
}
|
||||
b = nonInterleavedSpace
|
||||
}
|
||||
|
||||
for i := 0; i < patternWidth; i++ {
|
||||
for x := 0; x < mode.widths[a[i]]; x++ {
|
||||
resBits.AddBit(true)
|
||||
}
|
||||
for x := 0; x < mode.widths[b[i]]; x++ {
|
||||
resBits.AddBit(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resBits.AddBit(mode.end...)
|
||||
|
||||
if interleaved {
|
||||
return utils.New1DCode(barcode.Type2of5Interleaved, content, resBits), nil
|
||||
} else {
|
||||
return utils.New1DCode(barcode.Type2of5, content, resBits), nil
|
||||
}
|
||||
}
|
45
vendor/github.com/boombuler/barcode/twooffive/encoder_test.go
generated
vendored
Normal file
45
vendor/github.com/boombuler/barcode/twooffive/encoder_test.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
package twooffive
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_AddCheckSum(t *testing.T) {
|
||||
if sum, err := AddCheckSum("1234567"); err != nil || sum != "12345670" {
|
||||
t.Fail()
|
||||
}
|
||||
if _, err := AddCheckSum("1ABC"); err == nil {
|
||||
t.Fail()
|
||||
}
|
||||
if _, err := AddCheckSum(""); err == nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Encode(t *testing.T) {
|
||||
_, err := Encode("FOOBAR", false)
|
||||
if err == nil {
|
||||
t.Error("\"FOOBAR\" should not be encodable")
|
||||
}
|
||||
|
||||
testEncode := func(interleaved bool, txt, testResult string) {
|
||||
code, err := Encode(txt, interleaved)
|
||||
if err != nil || code == nil {
|
||||
t.Fail()
|
||||
} else {
|
||||
if code.Bounds().Max.X != len(testResult) {
|
||||
t.Errorf("%v: length missmatch! %v != %v", txt, code.Bounds().Max.X, len(testResult))
|
||||
} else {
|
||||
for i, r := range testResult {
|
||||
if (code.At(i, 0) == color.Black) != (r == '1') {
|
||||
t.Errorf("%v: code missmatch on position %d", txt, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testEncode(false, "12345670", "1101101011101010101110101110101011101110111010101010101110101110111010111010101011101110101010101011101110101011101110101101011")
|
||||
testEncode(true, "12345670", "10101110100010101110001110111010001010001110100011100010101010100011100011101101")
|
||||
}
|
57
vendor/github.com/boombuler/barcode/utils/base1dcode.go
generated
vendored
Normal file
57
vendor/github.com/boombuler/barcode/utils/base1dcode.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Package utils contain some utilities which are needed to create barcodes
|
||||
package utils
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
)
|
||||
|
||||
type base1DCode struct {
|
||||
*BitList
|
||||
kind string
|
||||
content string
|
||||
}
|
||||
|
||||
type base1DCodeIntCS struct {
|
||||
base1DCode
|
||||
checksum int
|
||||
}
|
||||
|
||||
func (c *base1DCode) Content() string {
|
||||
return c.content
|
||||
}
|
||||
|
||||
func (c *base1DCode) Metadata() barcode.Metadata {
|
||||
return barcode.Metadata{c.kind, 1}
|
||||
}
|
||||
|
||||
func (c *base1DCode) ColorModel() color.Model {
|
||||
return color.Gray16Model
|
||||
}
|
||||
|
||||
func (c *base1DCode) Bounds() image.Rectangle {
|
||||
return image.Rect(0, 0, c.Len(), 1)
|
||||
}
|
||||
|
||||
func (c *base1DCode) At(x, y int) color.Color {
|
||||
if c.GetBit(x) {
|
||||
return color.Black
|
||||
}
|
||||
return color.White
|
||||
}
|
||||
|
||||
func (c *base1DCodeIntCS) CheckSum() int {
|
||||
return c.checksum
|
||||
}
|
||||
|
||||
// New1DCode creates a new 1D barcode where the bars are represented by the bits in the bars BitList
|
||||
func New1DCodeIntCheckSum(codeKind, content string, bars *BitList, checksum int) barcode.BarcodeIntCS {
|
||||
return &base1DCodeIntCS{base1DCode{bars, codeKind, content}, checksum}
|
||||
}
|
||||
|
||||
// New1DCode creates a new 1D barcode where the bars are represented by the bits in the bars BitList
|
||||
func New1DCode(codeKind, content string, bars *BitList) barcode.Barcode {
|
||||
return &base1DCode{bars, codeKind, content}
|
||||
}
|
119
vendor/github.com/boombuler/barcode/utils/bitlist.go
generated
vendored
Normal file
119
vendor/github.com/boombuler/barcode/utils/bitlist.go
generated
vendored
Normal file
|
@ -0,0 +1,119 @@
|
|||
package utils
|
||||
|
||||
// BitList is a list that contains bits
|
||||
type BitList struct {
|
||||
count int
|
||||
data []int32
|
||||
}
|
||||
|
||||
// NewBitList returns a new BitList with the given length
|
||||
// all bits are initialize with false
|
||||
func NewBitList(capacity int) *BitList {
|
||||
bl := new(BitList)
|
||||
bl.count = capacity
|
||||
x := 0
|
||||
if capacity%32 != 0 {
|
||||
x = 1
|
||||
}
|
||||
bl.data = make([]int32, capacity/32+x)
|
||||
return bl
|
||||
}
|
||||
|
||||
// Len returns the number of contained bits
|
||||
func (bl *BitList) Len() int {
|
||||
return bl.count
|
||||
}
|
||||
|
||||
func (bl *BitList) grow() {
|
||||
growBy := len(bl.data)
|
||||
if growBy < 128 {
|
||||
growBy = 128
|
||||
} else if growBy >= 1024 {
|
||||
growBy = 1024
|
||||
}
|
||||
|
||||
nd := make([]int32, len(bl.data)+growBy)
|
||||
copy(nd, bl.data)
|
||||
bl.data = nd
|
||||
}
|
||||
|
||||
// AddBit appends the given bits to the end of the list
|
||||
func (bl *BitList) AddBit(bits ...bool) {
|
||||
for _, bit := range bits {
|
||||
itmIndex := bl.count / 32
|
||||
for itmIndex >= len(bl.data) {
|
||||
bl.grow()
|
||||
}
|
||||
bl.SetBit(bl.count, bit)
|
||||
bl.count++
|
||||
}
|
||||
}
|
||||
|
||||
// SetBit sets the bit at the given index to the given value
|
||||
func (bl *BitList) SetBit(index int, value bool) {
|
||||
itmIndex := index / 32
|
||||
itmBitShift := 31 - (index % 32)
|
||||
if value {
|
||||
bl.data[itmIndex] = bl.data[itmIndex] | 1<<uint(itmBitShift)
|
||||
} else {
|
||||
bl.data[itmIndex] = bl.data[itmIndex] & ^(1 << uint(itmBitShift))
|
||||
}
|
||||
}
|
||||
|
||||
// GetBit returns the bit at the given index
|
||||
func (bl *BitList) GetBit(index int) bool {
|
||||
itmIndex := index / 32
|
||||
itmBitShift := 31 - (index % 32)
|
||||
return ((bl.data[itmIndex] >> uint(itmBitShift)) & 1) == 1
|
||||
}
|
||||
|
||||
// AddByte appends all 8 bits of the given byte to the end of the list
|
||||
func (bl *BitList) AddByte(b byte) {
|
||||
for i := 7; i >= 0; i-- {
|
||||
bl.AddBit(((b >> uint(i)) & 1) == 1)
|
||||
}
|
||||
}
|
||||
|
||||
// AddBits appends the last (LSB) 'count' bits of 'b' the the end of the list
|
||||
func (bl *BitList) AddBits(b int, count byte) {
|
||||
for i := int(count) - 1; i >= 0; i-- {
|
||||
bl.AddBit(((b >> uint(i)) & 1) == 1)
|
||||
}
|
||||
}
|
||||
|
||||
// GetBytes returns all bits of the BitList as a []byte
|
||||
func (bl *BitList) GetBytes() []byte {
|
||||
len := bl.count >> 3
|
||||
if (bl.count % 8) != 0 {
|
||||
len++
|
||||
}
|
||||
result := make([]byte, len)
|
||||
for i := 0; i < len; i++ {
|
||||
shift := (3 - (i % 4)) * 8
|
||||
result[i] = (byte)((bl.data[i/4] >> uint(shift)) & 0xFF)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// IterateBytes iterates through all bytes contained in the BitList
|
||||
func (bl *BitList) IterateBytes() <-chan byte {
|
||||
res := make(chan byte)
|
||||
|
||||
go func() {
|
||||
c := bl.count
|
||||
shift := 24
|
||||
i := 0
|
||||
for c > 0 {
|
||||
res <- byte((bl.data[i] >> uint(shift)) & 0xFF)
|
||||
shift -= 8
|
||||
if shift < 0 {
|
||||
shift = 24
|
||||
i++
|
||||
}
|
||||
c -= 8
|
||||
}
|
||||
close(res)
|
||||
}()
|
||||
|
||||
return res
|
||||
}
|
65
vendor/github.com/boombuler/barcode/utils/galoisfield.go
generated
vendored
Normal file
65
vendor/github.com/boombuler/barcode/utils/galoisfield.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
package utils
|
||||
|
||||
// GaloisField encapsulates galois field arithmetics
|
||||
type GaloisField struct {
|
||||
Size int
|
||||
Base int
|
||||
ALogTbl []int
|
||||
LogTbl []int
|
||||
}
|
||||
|
||||
// NewGaloisField creates a new galois field
|
||||
func NewGaloisField(pp, fieldSize, b int) *GaloisField {
|
||||
result := new(GaloisField)
|
||||
|
||||
result.Size = fieldSize
|
||||
result.Base = b
|
||||
result.ALogTbl = make([]int, fieldSize)
|
||||
result.LogTbl = make([]int, fieldSize)
|
||||
|
||||
x := 1
|
||||
for i := 0; i < fieldSize; i++ {
|
||||
result.ALogTbl[i] = x
|
||||
x = x * 2
|
||||
if x >= fieldSize {
|
||||
x = (x ^ pp) & (fieldSize - 1)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < fieldSize; i++ {
|
||||
result.LogTbl[result.ALogTbl[i]] = int(i)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (gf *GaloisField) Zero() *GFPoly {
|
||||
return NewGFPoly(gf, []int{0})
|
||||
}
|
||||
|
||||
// AddOrSub add or substract two numbers
|
||||
func (gf *GaloisField) AddOrSub(a, b int) int {
|
||||
return a ^ b
|
||||
}
|
||||
|
||||
// Multiply multiplys two numbers
|
||||
func (gf *GaloisField) Multiply(a, b int) int {
|
||||
if a == 0 || b == 0 {
|
||||
return 0
|
||||
}
|
||||
return gf.ALogTbl[(gf.LogTbl[a]+gf.LogTbl[b])%(gf.Size-1)]
|
||||
}
|
||||
|
||||
// Divide divides two numbers
|
||||
func (gf *GaloisField) Divide(a, b int) int {
|
||||
if b == 0 {
|
||||
panic("divide by zero")
|
||||
} else if a == 0 {
|
||||
return 0
|
||||
}
|
||||
return gf.ALogTbl[(gf.LogTbl[a]-gf.LogTbl[b])%(gf.Size-1)]
|
||||
}
|
||||
|
||||
func (gf *GaloisField) Invers(num int) int {
|
||||
return gf.ALogTbl[(gf.Size-1)-gf.LogTbl[num]]
|
||||
}
|
59
vendor/github.com/boombuler/barcode/utils/galoisfield_test.go
generated
vendored
Normal file
59
vendor/github.com/boombuler/barcode/utils/galoisfield_test.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_GF(t *testing.T) {
|
||||
log := []int{
|
||||
0, 255, 1, 240, 2, 225, 241, 53, 3, 38, 226, 133, 242, 43, 54, 210,
|
||||
4, 195, 39, 114, 227, 106, 134, 28, 243, 140, 44, 23, 55, 118, 211, 234,
|
||||
5, 219, 196, 96, 40, 222, 115, 103, 228, 78, 107, 125, 135, 8, 29, 162,
|
||||
244, 186, 141, 180, 45, 99, 24, 49, 56, 13, 119, 153, 212, 199, 235, 91,
|
||||
6, 76, 220, 217, 197, 11, 97, 184, 41, 36, 223, 253, 116, 138, 104, 193,
|
||||
229, 86, 79, 171, 108, 165, 126, 145, 136, 34, 9, 74, 30, 32, 163, 84,
|
||||
245, 173, 187, 204, 142, 81, 181, 190, 46, 88, 100, 159, 25, 231, 50, 207,
|
||||
57, 147, 14, 67, 120, 128, 154, 248, 213, 167, 200, 63, 236, 110, 92, 176,
|
||||
7, 161, 77, 124, 221, 102, 218, 95, 198, 90, 12, 152, 98, 48, 185, 179,
|
||||
42, 209, 37, 132, 224, 52, 254, 239, 117, 233, 139, 22, 105, 27, 194, 113,
|
||||
230, 206, 87, 158, 80, 189, 172, 203, 109, 175, 166, 62, 127, 247, 146, 66,
|
||||
137, 192, 35, 252, 10, 183, 75, 216, 31, 83, 33, 73, 164, 144, 85, 170,
|
||||
246, 65, 174, 61, 188, 202, 205, 157, 143, 169, 82, 72, 182, 215, 191, 251,
|
||||
47, 178, 89, 151, 101, 94, 160, 123, 26, 112, 232, 21, 51, 238, 208, 131,
|
||||
58, 69, 148, 18, 15, 16, 68, 17, 121, 149, 129, 19, 155, 59, 249, 70,
|
||||
214, 250, 168, 71, 201, 156, 64, 60, 237, 130, 111, 20, 93, 122, 177, 150,
|
||||
}
|
||||
|
||||
alog := []int{
|
||||
1, 2, 4, 8, 16, 32, 64, 128, 45, 90, 180, 69, 138, 57, 114, 228,
|
||||
229, 231, 227, 235, 251, 219, 155, 27, 54, 108, 216, 157, 23, 46, 92, 184,
|
||||
93, 186, 89, 178, 73, 146, 9, 18, 36, 72, 144, 13, 26, 52, 104, 208,
|
||||
141, 55, 110, 220, 149, 7, 14, 28, 56, 112, 224, 237, 247, 195, 171, 123,
|
||||
246, 193, 175, 115, 230, 225, 239, 243, 203, 187, 91, 182, 65, 130, 41, 82,
|
||||
164, 101, 202, 185, 95, 190, 81, 162, 105, 210, 137, 63, 126, 252, 213, 135,
|
||||
35, 70, 140, 53, 106, 212, 133, 39, 78, 156, 21, 42, 84, 168, 125, 250,
|
||||
217, 159, 19, 38, 76, 152, 29, 58, 116, 232, 253, 215, 131, 43, 86, 172,
|
||||
117, 234, 249, 223, 147, 11, 22, 44, 88, 176, 77, 154, 25, 50, 100, 200,
|
||||
189, 87, 174, 113, 226, 233, 255, 211, 139, 59, 118, 236, 245, 199, 163, 107,
|
||||
214, 129, 47, 94, 188, 85, 170, 121, 242, 201, 191, 83, 166, 97, 194, 169,
|
||||
127, 254, 209, 143, 51, 102, 204, 181, 71, 142, 49, 98, 196, 165, 103, 206,
|
||||
177, 79, 158, 17, 34, 68, 136, 61, 122, 244, 197, 167, 99, 198, 161, 111,
|
||||
222, 145, 15, 30, 60, 120, 240, 205, 183, 67, 134, 33, 66, 132, 37, 74,
|
||||
148, 5, 10, 20, 40, 80, 160, 109, 218, 153, 31, 62, 124, 248, 221, 151,
|
||||
3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75, 150, 1,
|
||||
}
|
||||
|
||||
gf := NewGaloisField(301, 256, 1)
|
||||
if len(gf.LogTbl) != len(gf.ALogTbl) || len(gf.LogTbl) != len(log) {
|
||||
t.Fail()
|
||||
}
|
||||
for i := 0; i < len(log); i++ {
|
||||
if gf.LogTbl[i] != log[i] {
|
||||
t.Error("Invalid Log Table")
|
||||
}
|
||||
if gf.ALogTbl[i] != alog[i] {
|
||||
t.Error("Invalid ALog Table")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
103
vendor/github.com/boombuler/barcode/utils/gfpoly.go
generated
vendored
Normal file
103
vendor/github.com/boombuler/barcode/utils/gfpoly.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
package utils
|
||||
|
||||
type GFPoly struct {
|
||||
gf *GaloisField
|
||||
Coefficients []int
|
||||
}
|
||||
|
||||
func (gp *GFPoly) Degree() int {
|
||||
return len(gp.Coefficients) - 1
|
||||
}
|
||||
|
||||
func (gp *GFPoly) Zero() bool {
|
||||
return gp.Coefficients[0] == 0
|
||||
}
|
||||
|
||||
// GetCoefficient returns the coefficient of x ^ degree
|
||||
func (gp *GFPoly) GetCoefficient(degree int) int {
|
||||
return gp.Coefficients[gp.Degree()-degree]
|
||||
}
|
||||
|
||||
func (gp *GFPoly) AddOrSubstract(other *GFPoly) *GFPoly {
|
||||
if gp.Zero() {
|
||||
return other
|
||||
} else if other.Zero() {
|
||||
return gp
|
||||
}
|
||||
smallCoeff := gp.Coefficients
|
||||
largeCoeff := other.Coefficients
|
||||
if len(smallCoeff) > len(largeCoeff) {
|
||||
largeCoeff, smallCoeff = smallCoeff, largeCoeff
|
||||
}
|
||||
sumDiff := make([]int, len(largeCoeff))
|
||||
lenDiff := len(largeCoeff) - len(smallCoeff)
|
||||
copy(sumDiff, largeCoeff[:lenDiff])
|
||||
for i := lenDiff; i < len(largeCoeff); i++ {
|
||||
sumDiff[i] = int(gp.gf.AddOrSub(int(smallCoeff[i-lenDiff]), int(largeCoeff[i])))
|
||||
}
|
||||
return NewGFPoly(gp.gf, sumDiff)
|
||||
}
|
||||
|
||||
func (gp *GFPoly) MultByMonominal(degree int, coeff int) *GFPoly {
|
||||
if coeff == 0 {
|
||||
return gp.gf.Zero()
|
||||
}
|
||||
size := len(gp.Coefficients)
|
||||
result := make([]int, size+degree)
|
||||
for i := 0; i < size; i++ {
|
||||
result[i] = int(gp.gf.Multiply(int(gp.Coefficients[i]), int(coeff)))
|
||||
}
|
||||
return NewGFPoly(gp.gf, result)
|
||||
}
|
||||
|
||||
func (gp *GFPoly) Multiply(other *GFPoly) *GFPoly {
|
||||
if gp.Zero() || other.Zero() {
|
||||
return gp.gf.Zero()
|
||||
}
|
||||
aCoeff := gp.Coefficients
|
||||
aLen := len(aCoeff)
|
||||
bCoeff := other.Coefficients
|
||||
bLen := len(bCoeff)
|
||||
product := make([]int, aLen+bLen-1)
|
||||
for i := 0; i < aLen; i++ {
|
||||
ac := int(aCoeff[i])
|
||||
for j := 0; j < bLen; j++ {
|
||||
bc := int(bCoeff[j])
|
||||
product[i+j] = int(gp.gf.AddOrSub(int(product[i+j]), gp.gf.Multiply(ac, bc)))
|
||||
}
|
||||
}
|
||||
return NewGFPoly(gp.gf, product)
|
||||
}
|
||||
|
||||
func (gp *GFPoly) Divide(other *GFPoly) (quotient *GFPoly, remainder *GFPoly) {
|
||||
quotient = gp.gf.Zero()
|
||||
remainder = gp
|
||||
fld := gp.gf
|
||||
denomLeadTerm := other.GetCoefficient(other.Degree())
|
||||
inversDenomLeadTerm := fld.Invers(int(denomLeadTerm))
|
||||
for remainder.Degree() >= other.Degree() && !remainder.Zero() {
|
||||
degreeDiff := remainder.Degree() - other.Degree()
|
||||
scale := int(fld.Multiply(int(remainder.GetCoefficient(remainder.Degree())), inversDenomLeadTerm))
|
||||
term := other.MultByMonominal(degreeDiff, scale)
|
||||
itQuot := NewMonominalPoly(fld, degreeDiff, scale)
|
||||
quotient = quotient.AddOrSubstract(itQuot)
|
||||
remainder = remainder.AddOrSubstract(term)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NewMonominalPoly(field *GaloisField, degree int, coeff int) *GFPoly {
|
||||
if coeff == 0 {
|
||||
return field.Zero()
|
||||
}
|
||||
result := make([]int, degree+1)
|
||||
result[0] = coeff
|
||||
return NewGFPoly(field, result)
|
||||
}
|
||||
|
||||
func NewGFPoly(field *GaloisField, coefficients []int) *GFPoly {
|
||||
for len(coefficients) > 1 && coefficients[0] == 0 {
|
||||
coefficients = coefficients[1:]
|
||||
}
|
||||
return &GFPoly{field, coefficients}
|
||||
}
|
44
vendor/github.com/boombuler/barcode/utils/reedsolomon.go
generated
vendored
Normal file
44
vendor/github.com/boombuler/barcode/utils/reedsolomon.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ReedSolomonEncoder struct {
|
||||
gf *GaloisField
|
||||
polynomes []*GFPoly
|
||||
m *sync.Mutex
|
||||
}
|
||||
|
||||
func NewReedSolomonEncoder(gf *GaloisField) *ReedSolomonEncoder {
|
||||
return &ReedSolomonEncoder{
|
||||
gf, []*GFPoly{NewGFPoly(gf, []int{1})}, new(sync.Mutex),
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *ReedSolomonEncoder) getPolynomial(degree int) *GFPoly {
|
||||
rs.m.Lock()
|
||||
defer rs.m.Unlock()
|
||||
|
||||
if degree >= len(rs.polynomes) {
|
||||
last := rs.polynomes[len(rs.polynomes)-1]
|
||||
for d := len(rs.polynomes); d <= degree; d++ {
|
||||
next := last.Multiply(NewGFPoly(rs.gf, []int{1, rs.gf.ALogTbl[d-1+rs.gf.Base]}))
|
||||
rs.polynomes = append(rs.polynomes, next)
|
||||
last = next
|
||||
}
|
||||
}
|
||||
return rs.polynomes[degree]
|
||||
}
|
||||
|
||||
func (rs *ReedSolomonEncoder) Encode(data []int, eccCount int) []int {
|
||||
generator := rs.getPolynomial(eccCount)
|
||||
info := NewGFPoly(rs.gf, data)
|
||||
info = info.MultByMonominal(eccCount, 1)
|
||||
_, remainder := info.Divide(generator)
|
||||
|
||||
result := make([]int, eccCount)
|
||||
numZero := int(eccCount) - len(remainder.Coefficients)
|
||||
copy(result[numZero:], remainder.Coefficients)
|
||||
return result
|
||||
}
|
19
vendor/github.com/boombuler/barcode/utils/runeint.go
generated
vendored
Normal file
19
vendor/github.com/boombuler/barcode/utils/runeint.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
package utils
|
||||
|
||||
// RuneToInt converts a rune between '0' and '9' to an integer between 0 and 9
|
||||
// If the rune is outside of this range -1 is returned.
|
||||
func RuneToInt(r rune) int {
|
||||
if r >= '0' && r <= '9' {
|
||||
return int(r - '0')
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// IntToRune converts a digit 0 - 9 to the rune '0' - '9'. If the given int is outside
|
||||
// of this range 'F' is returned!
|
||||
func IntToRune(i int) rune {
|
||||
if i >= 0 && i <= 9 {
|
||||
return rune(i + '0')
|
||||
}
|
||||
return 'F'
|
||||
}
|
24
vendor/github.com/boombuler/barcode/utils/runeint_test.go
generated
vendored
Normal file
24
vendor/github.com/boombuler/barcode/utils/runeint_test.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package utils
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_RuneToIntIntToRune(t *testing.T) {
|
||||
if IntToRune(0) != '0' {
|
||||
t.Errorf("failed IntToRune(0) returned %d", string(IntToRune(0)))
|
||||
}
|
||||
if IntToRune(9) != '9' {
|
||||
t.Errorf("failed IntToRune(9) returned %d", IntToRune(9))
|
||||
}
|
||||
if IntToRune(10) != 'F' {
|
||||
t.Errorf("failed IntToRune(10) returned %d", IntToRune(10))
|
||||
}
|
||||
if RuneToInt('0') != 0 {
|
||||
t.Error("failed RuneToInt('0') returned %d", RuneToInt(0))
|
||||
}
|
||||
if RuneToInt('9') != 9 {
|
||||
t.Error("failed RuneToInt('9') returned %d", RuneToInt(9))
|
||||
}
|
||||
if RuneToInt('F') != -1 {
|
||||
t.Error("failed RuneToInt('F') returned %d", RuneToInt('F'))
|
||||
}
|
||||
}
|
6
vendor/github.com/pquerna/otp/.travis.yml
generated
vendored
Normal file
6
vendor/github.com/pquerna/otp/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.7
|
||||
- 1.8
|
||||
- tip
|
202
vendor/github.com/pquerna/otp/LICENSE
generated
vendored
Normal file
202
vendor/github.com/pquerna/otp/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
5
vendor/github.com/pquerna/otp/NOTICE
generated
vendored
Normal file
5
vendor/github.com/pquerna/otp/NOTICE
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
otp
|
||||
Copyright (c) 2014, Paul Querna
|
||||
|
||||
This product includes software developed by
|
||||
Paul Querna (http://paul.querna.org/).
|
60
vendor/github.com/pquerna/otp/README.md
generated
vendored
Normal file
60
vendor/github.com/pquerna/otp/README.md
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
# otp: One Time Password utilities Go / Golang
|
||||
|
||||
[![GoDoc](https://godoc.org/github.com/pquerna/otp?status.svg)](https://godoc.org/github.com/pquerna/otp) [![Build Status](https://travis-ci.org/pquerna/otp.svg?branch=master)](https://travis-ci.org/pquerna/otp)
|
||||
|
||||
# Why One Time Passwords?
|
||||
|
||||
One Time Passwords (OTPs) are an mechanism to improve security over passwords alone. When a Time-based OTP (TOTP) is stored on a user's phone, and combined with something the user knows (Password), you have an easy on-ramp to [Multi-factor authentication](http://en.wikipedia.org/wiki/Multi-factor_authentication) without adding a dependency on a SMS provider. This Password and TOTP combination is used by many popular websites including Google, Github, Facebook, Salesforce and many others.
|
||||
|
||||
The `otp` library enables you to easily add TOTPs to your own application, increasing your user's security against mass-password breaches and malware.
|
||||
|
||||
Because TOTP is standardized and widely deployed, there are many [mobile clients and software implementations](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm#Client_implementations).
|
||||
|
||||
## `otp` Supports:
|
||||
|
||||
* Generating QR Code images for easy user enrollment.
|
||||
* Time-based One-time Password Algorithm (TOTP) (RFC 6238): Time based OTP, the most commonly used method.
|
||||
* HMAC-based One-time Password Algorithm (HOTP) (RFC 4226): Counter based OTP, which TOTP is based upon.
|
||||
* Generation and Validation of codes for either algorithm.
|
||||
|
||||
## Implementing TOTP in your application:
|
||||
|
||||
### User Enrollment
|
||||
|
||||
For an example of a working enrollment work flow, [Github has documented theirs](https://help.github.com/articles/configuring-two-factor-authentication-via-a-totp-mobile-app/
|
||||
), but the basics are:
|
||||
|
||||
1. Generate new TOTP Key for a User. `key,_ := totp.Generate(...)`.
|
||||
1. Display the Key's Secret and QR-Code for the User. `key.Secret()` and `key.Image(...)`.
|
||||
1. Test that the user can successfully use their TOTP. `totp.Validate(...)`.
|
||||
1. Store TOTP Secret for the User in your backend. `key.Secret()`
|
||||
1. Provide the user with "recovery codes". (See Recovery Codes bellow)
|
||||
|
||||
### Code Generation
|
||||
|
||||
* In either TOTP or HOTP cases, use the `GenerateCode` function and a counter or
|
||||
`time.Time` struct to generate a valid code compatible with most implementations.
|
||||
* For uncommon or custom settings, or to catch unlikely errors, use `GenerateCodeCustom`
|
||||
in either module.
|
||||
|
||||
### Validation
|
||||
|
||||
1. Prompt and validate User's password as normal.
|
||||
1. If the user has TOTP enabled, prompt for TOTP passcode.
|
||||
1. Retrieve the User's TOTP Secret from your backend.
|
||||
1. Validate the user's passcode. `totp.Validate(...)`
|
||||
|
||||
|
||||
### Recovery Codes
|
||||
|
||||
When a user loses access to their TOTP device, they would no longer have access to their account. Because TOTPs are often configured on mobile devices that can be lost, stolen or damaged, this is a common problem. For this reason many providers give their users "backup codes" or "recovery codes". These are a set of one time use codes that can be used instead of the TOTP. These can simply be randomly generated strings that you store in your backend. [Github's documentation provides an overview of the user experience](
|
||||
https://help.github.com/articles/downloading-your-two-factor-authentication-recovery-codes/).
|
||||
|
||||
|
||||
## Improvements, bugs, adding feature, etc:
|
||||
|
||||
Please [open issues in Github](https://github.com/pquerna/otp/issues) for ideas, bugs, and general thoughts. Pull requests are of course preferred :)
|
||||
|
||||
## License
|
||||
|
||||
`otp` is licensed under the [Apache License, Version 2.0](./LICENSE)
|
70
vendor/github.com/pquerna/otp/doc.go
generated
vendored
Normal file
70
vendor/github.com/pquerna/otp/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* Copyright 2014 Paul Querna
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Package otp implements both HOTP and TOTP based
|
||||
// one time passcodes in a Google Authenticator compatible manner.
|
||||
//
|
||||
// When adding a TOTP for a user, you must store the "secret" value
|
||||
// persistently. It is recommend to store the secret in an encrypted field in your
|
||||
// datastore. Due to how TOTP works, it is not possible to store a hash
|
||||
// for the secret value like you would a password.
|
||||
//
|
||||
// To enroll a user, you must first generate an OTP for them. Google
|
||||
// Authenticator supports using a QR code as an enrollment method:
|
||||
//
|
||||
// import (
|
||||
// "github.com/pquerna/otp/totp"
|
||||
//
|
||||
// "bytes"
|
||||
// "image/png"
|
||||
// )
|
||||
//
|
||||
// key, err := totp.Generate(totp.GenerateOpts{
|
||||
// Issuer: "Example.com",
|
||||
// AccountName: "alice@example.com",
|
||||
// })
|
||||
//
|
||||
// // Convert TOTP key into a QR code encoded as a PNG image.
|
||||
// var buf bytes.Buffer
|
||||
// img, err := key.Image(200, 200)
|
||||
// png.Encode(&buf, img)
|
||||
//
|
||||
// // display the QR code to the user.
|
||||
// display(buf.Bytes())
|
||||
//
|
||||
// // Now Validate that the user's successfully added the passcode.
|
||||
// passcode := promptForPasscode()
|
||||
// valid := totp.Validate(passcode, key.Secret())
|
||||
//
|
||||
// if valid {
|
||||
// // User successfully used their TOTP, save it to your backend!
|
||||
// storeSecret("alice@example.com", key.Secret())
|
||||
// }
|
||||
//
|
||||
// Validating a TOTP passcode is very easy, just prompt the user for a passcode
|
||||
// and retrieve the associated user's previously stored secret.
|
||||
// import "github.com/pquerna/otp/totp"
|
||||
//
|
||||
// passcode := promptForPasscode()
|
||||
// secret := getSecret("alice@example.com")
|
||||
//
|
||||
// valid := totp.Validate(passcode, secret)
|
||||
//
|
||||
// if valid {
|
||||
// // Success! continue login process.
|
||||
// }
|
||||
package otp
|
63
vendor/github.com/pquerna/otp/example/main.go
generated
vendored
Normal file
63
vendor/github.com/pquerna/otp/example/main.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func display(key *otp.Key, data []byte) {
|
||||
fmt.Printf("Issuer: %s\n", key.Issuer())
|
||||
fmt.Printf("Account Name: %s\n", key.AccountName())
|
||||
fmt.Printf("Secret: %s\n", key.Secret())
|
||||
fmt.Println("Writing PNG to qr-code.png....")
|
||||
ioutil.WriteFile("qr-code.png", data, 0644)
|
||||
fmt.Println("")
|
||||
fmt.Println("Please add your TOTP to your OTP Application now!")
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
func promptForPasscode() string {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Print("Enter Passcode: ")
|
||||
text, _ := reader.ReadString('\n')
|
||||
return text
|
||||
}
|
||||
|
||||
func main() {
|
||||
key, err := totp.Generate(totp.GenerateOpts{
|
||||
Issuer: "Example.com",
|
||||
AccountName: "alice@example.com",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Convert TOTP key into a PNG
|
||||
var buf bytes.Buffer
|
||||
img, err := key.Image(200, 200)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
png.Encode(&buf, img)
|
||||
|
||||
// display the QR code to the user.
|
||||
display(key, buf.Bytes())
|
||||
|
||||
// Now Validate that the user's successfully added the passcode.
|
||||
fmt.Println("Validating TOTP...")
|
||||
passcode := promptForPasscode()
|
||||
valid := totp.Validate(passcode, key.Secret())
|
||||
if valid {
|
||||
println("Valid passcode!")
|
||||
os.Exit(0)
|
||||
} else {
|
||||
println("Invalid passocde!")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
188
vendor/github.com/pquerna/otp/hotp/hotp.go
generated
vendored
Normal file
188
vendor/github.com/pquerna/otp/hotp/hotp.go
generated
vendored
Normal file
|
@ -0,0 +1,188 @@
|
|||
/**
|
||||
* Copyright 2014 Paul Querna
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package hotp
|
||||
|
||||
import (
|
||||
"github.com/pquerna/otp"
|
||||
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/base32"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const debug = false
|
||||
|
||||
// Validate a HOTP passcode given a counter and secret.
|
||||
// This is a shortcut for ValidateCustom, with parameters that
|
||||
// are compataible with Google-Authenticator.
|
||||
func Validate(passcode string, counter uint64, secret string) bool {
|
||||
rv, _ := ValidateCustom(
|
||||
passcode,
|
||||
counter,
|
||||
secret,
|
||||
ValidateOpts{
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
},
|
||||
)
|
||||
return rv
|
||||
}
|
||||
|
||||
// ValidateOpts provides options for ValidateCustom().
|
||||
type ValidateOpts struct {
|
||||
// Digits as part of the input. Defaults to 6.
|
||||
Digits otp.Digits
|
||||
// Algorithm to use for HMAC. Defaults to SHA1.
|
||||
Algorithm otp.Algorithm
|
||||
}
|
||||
|
||||
// GenerateCode creates a HOTP passcode given a counter and secret.
|
||||
// This is a shortcut for GenerateCodeCustom, with parameters that
|
||||
// are compataible with Google-Authenticator.
|
||||
func GenerateCode(secret string, counter uint64) (string, error) {
|
||||
return GenerateCodeCustom(secret, counter, ValidateOpts{
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
})
|
||||
}
|
||||
|
||||
// GenerateCodeCustom uses a counter and secret value and options struct to
|
||||
// create a passcode.
|
||||
func GenerateCodeCustom(secret string, counter uint64, opts ValidateOpts) (passcode string, err error) {
|
||||
// As noted in issue #10 and #17 this adds support for TOTP secrets that are
|
||||
// missing their padding.
|
||||
secret = strings.TrimSpace(secret)
|
||||
if n := len(secret) % 8; n != 0 {
|
||||
secret = secret + strings.Repeat("=", 8-n)
|
||||
}
|
||||
|
||||
secretBytes, err := base32.StdEncoding.DecodeString(secret)
|
||||
if err != nil {
|
||||
return "", otp.ErrValidateSecretInvalidBase32
|
||||
}
|
||||
|
||||
buf := make([]byte, 8)
|
||||
mac := hmac.New(opts.Algorithm.Hash, secretBytes)
|
||||
binary.BigEndian.PutUint64(buf, counter)
|
||||
if debug {
|
||||
fmt.Printf("counter=%v\n", counter)
|
||||
fmt.Printf("buf=%v\n", buf)
|
||||
}
|
||||
|
||||
mac.Write(buf)
|
||||
sum := mac.Sum(nil)
|
||||
|
||||
// "Dynamic truncation" in RFC 4226
|
||||
// http://tools.ietf.org/html/rfc4226#section-5.4
|
||||
offset := sum[len(sum)-1] & 0xf
|
||||
value := int64(((int(sum[offset]) & 0x7f) << 24) |
|
||||
((int(sum[offset+1] & 0xff)) << 16) |
|
||||
((int(sum[offset+2] & 0xff)) << 8) |
|
||||
(int(sum[offset+3]) & 0xff))
|
||||
|
||||
l := opts.Digits.Length()
|
||||
mod := int32(value % int64(math.Pow10(l)))
|
||||
|
||||
if debug {
|
||||
fmt.Printf("offset=%v\n", offset)
|
||||
fmt.Printf("value=%v\n", value)
|
||||
fmt.Printf("mod'ed=%v\n", mod)
|
||||
}
|
||||
|
||||
return opts.Digits.Format(mod), nil
|
||||
}
|
||||
|
||||
// ValidateCustom validates an HOTP with customizable options. Most users should
|
||||
// use Validate().
|
||||
func ValidateCustom(passcode string, counter uint64, secret string, opts ValidateOpts) (bool, error) {
|
||||
passcode = strings.TrimSpace(passcode)
|
||||
|
||||
if len(passcode) != opts.Digits.Length() {
|
||||
return false, otp.ErrValidateInputInvalidLength
|
||||
}
|
||||
|
||||
otpstr, err := GenerateCodeCustom(secret, counter, opts)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare([]byte(otpstr), []byte(passcode)) == 1 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// GenerateOpts provides options for .Generate()
|
||||
type GenerateOpts struct {
|
||||
// Name of the issuing Organization/Company.
|
||||
Issuer string
|
||||
// Name of the User's Account (eg, email address)
|
||||
AccountName string
|
||||
// Size in size of the generated Secret. Defaults to 10 bytes.
|
||||
SecretSize uint
|
||||
// Digits to request. Defaults to 6.
|
||||
Digits otp.Digits
|
||||
// Algorithm to use for HMAC. Defaults to SHA1.
|
||||
Algorithm otp.Algorithm
|
||||
}
|
||||
|
||||
// Generate creates a new HOTP Key.
|
||||
func Generate(opts GenerateOpts) (*otp.Key, error) {
|
||||
// url encode the Issuer/AccountName
|
||||
if opts.Issuer == "" {
|
||||
return nil, otp.ErrGenerateMissingIssuer
|
||||
}
|
||||
|
||||
if opts.AccountName == "" {
|
||||
return nil, otp.ErrGenerateMissingAccountName
|
||||
}
|
||||
|
||||
if opts.SecretSize == 0 {
|
||||
opts.SecretSize = 10
|
||||
}
|
||||
|
||||
// otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
|
||||
|
||||
v := url.Values{}
|
||||
secret := make([]byte, opts.SecretSize)
|
||||
_, err := rand.Read(secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v.Set("secret", strings.TrimRight(base32.StdEncoding.EncodeToString(secret), "="))
|
||||
v.Set("issuer", opts.Issuer)
|
||||
v.Set("algorithm", opts.Algorithm.String())
|
||||
v.Set("digits", opts.Digits.String())
|
||||
|
||||
u := url.URL{
|
||||
Scheme: "otpauth",
|
||||
Host: "hotp",
|
||||
Path: "/" + opts.Issuer + ":" + opts.AccountName,
|
||||
RawQuery: v.Encode(),
|
||||
}
|
||||
|
||||
return otp.NewKeyFromURL(u.String())
|
||||
}
|
162
vendor/github.com/pquerna/otp/hotp/hotp_test.go
generated
vendored
Normal file
162
vendor/github.com/pquerna/otp/hotp/hotp_test.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
* Copyright 2014 Paul Querna
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package hotp
|
||||
|
||||
import (
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"encoding/base32"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type tc struct {
|
||||
Counter uint64
|
||||
TOTP string
|
||||
Mode otp.Algorithm
|
||||
Secret string
|
||||
}
|
||||
|
||||
var (
|
||||
secSha1 = base32.StdEncoding.EncodeToString([]byte("12345678901234567890"))
|
||||
|
||||
rfcMatrixTCs = []tc{
|
||||
tc{0, "755224", otp.AlgorithmSHA1, secSha1},
|
||||
tc{1, "287082", otp.AlgorithmSHA1, secSha1},
|
||||
tc{2, "359152", otp.AlgorithmSHA1, secSha1},
|
||||
tc{3, "969429", otp.AlgorithmSHA1, secSha1},
|
||||
tc{4, "338314", otp.AlgorithmSHA1, secSha1},
|
||||
tc{5, "254676", otp.AlgorithmSHA1, secSha1},
|
||||
tc{6, "287922", otp.AlgorithmSHA1, secSha1},
|
||||
tc{7, "162583", otp.AlgorithmSHA1, secSha1},
|
||||
tc{8, "399871", otp.AlgorithmSHA1, secSha1},
|
||||
tc{9, "520489", otp.AlgorithmSHA1, secSha1},
|
||||
}
|
||||
)
|
||||
|
||||
// Test values from http://tools.ietf.org/html/rfc4226#appendix-D
|
||||
func TestValidateRFCMatrix(t *testing.T) {
|
||||
|
||||
for _, tx := range rfcMatrixTCs {
|
||||
valid, err := ValidateCustom(tx.TOTP, tx.Counter, tx.Secret,
|
||||
ValidateOpts{
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: tx.Mode,
|
||||
})
|
||||
require.NoError(t, err,
|
||||
"unexpected error totp=%s mode=%v counter=%v", tx.TOTP, tx.Mode, tx.Counter)
|
||||
require.True(t, valid,
|
||||
"unexpected totp failure totp=%s mode=%v counter=%v", tx.TOTP, tx.Mode, tx.Counter)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateRFCMatrix(t *testing.T) {
|
||||
for _, tx := range rfcMatrixTCs {
|
||||
passcode, err := GenerateCodeCustom(tx.Secret, tx.Counter,
|
||||
ValidateOpts{
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: tx.Mode,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tx.TOTP, passcode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateInvalid(t *testing.T) {
|
||||
secSha1 := base32.StdEncoding.EncodeToString([]byte("12345678901234567890"))
|
||||
|
||||
valid, err := ValidateCustom("foo", 11, secSha1,
|
||||
ValidateOpts{
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
})
|
||||
require.Equal(t, otp.ErrValidateInputInvalidLength, err, "Expected Invalid length error.")
|
||||
require.Equal(t, false, valid, "Valid should be false when we have an error.")
|
||||
|
||||
valid, err = ValidateCustom("foo", 11, secSha1,
|
||||
ValidateOpts{
|
||||
Digits: otp.DigitsEight,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
})
|
||||
require.Equal(t, otp.ErrValidateInputInvalidLength, err, "Expected Invalid length error.")
|
||||
require.Equal(t, false, valid, "Valid should be false when we have an error.")
|
||||
|
||||
valid, err = ValidateCustom("000000", 11, secSha1,
|
||||
ValidateOpts{
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
})
|
||||
require.NoError(t, err, "Expected no error.")
|
||||
require.Equal(t, false, valid, "Valid should be false.")
|
||||
|
||||
valid = Validate("000000", 11, secSha1)
|
||||
require.Equal(t, false, valid, "Valid should be false.")
|
||||
}
|
||||
|
||||
// This tests for issue #10 - secrets without padding
|
||||
func TestValidatePadding(t *testing.T) {
|
||||
valid, err := ValidateCustom("831097", 0, "JBSWY3DPEHPK3PX",
|
||||
ValidateOpts{
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
})
|
||||
require.NoError(t, err, "Expected no error.")
|
||||
require.Equal(t, true, valid, "Valid should be true.")
|
||||
}
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
k, err := Generate(GenerateOpts{
|
||||
Issuer: "SnakeOil",
|
||||
AccountName: "alice@example.com",
|
||||
})
|
||||
require.NoError(t, err, "generate basic TOTP")
|
||||
require.Equal(t, "SnakeOil", k.Issuer(), "Extracting Issuer")
|
||||
require.Equal(t, "alice@example.com", k.AccountName(), "Extracting Account Name")
|
||||
require.Equal(t, 16, len(k.Secret()), "Secret is 16 bytes long as base32.")
|
||||
|
||||
k, err = Generate(GenerateOpts{
|
||||
Issuer: "SnakeOil",
|
||||
AccountName: "alice@example.com",
|
||||
SecretSize: 20,
|
||||
})
|
||||
require.NoError(t, err, "generate larger TOTP")
|
||||
require.Equal(t, 32, len(k.Secret()), "Secret is 32 bytes long as base32.")
|
||||
|
||||
k, err = Generate(GenerateOpts{
|
||||
Issuer: "",
|
||||
AccountName: "alice@example.com",
|
||||
})
|
||||
require.Equal(t, otp.ErrGenerateMissingIssuer, err, "generate missing issuer")
|
||||
require.Nil(t, k, "key should be nil on error.")
|
||||
|
||||
k, err = Generate(GenerateOpts{
|
||||
Issuer: "Foobar, Inc",
|
||||
AccountName: "",
|
||||
})
|
||||
require.Equal(t, otp.ErrGenerateMissingAccountName, err, "generate missing account name.")
|
||||
require.Nil(t, k, "key should be nil on error.")
|
||||
|
||||
k, err = Generate(GenerateOpts{
|
||||
Issuer: "SnakeOil",
|
||||
AccountName: "alice@example.com",
|
||||
SecretSize: 17, // anything that is not divisable by 5, really
|
||||
})
|
||||
require.NoError(t, err, "Secret size is valid when length not divisable by 5.")
|
||||
require.NotContains(t, k.Secret(), "=", "Secret has no escaped characters.")
|
||||
}
|
202
vendor/github.com/pquerna/otp/otp.go
generated
vendored
Normal file
202
vendor/github.com/pquerna/otp/otp.go
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
|||
/**
|
||||
* Copyright 2014 Paul Querna
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package otp
|
||||
|
||||
import (
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/qr"
|
||||
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"image"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Error when attempting to convert the secret from base32 to raw bytes.
|
||||
var ErrValidateSecretInvalidBase32 = errors.New("Decoding of secret as base32 failed.")
|
||||
|
||||
// The user provided passcode length was not expected.
|
||||
var ErrValidateInputInvalidLength = errors.New("Input length unexpected")
|
||||
|
||||
// When generating a Key, the Issuer must be set.
|
||||
var ErrGenerateMissingIssuer = errors.New("Issuer must be set")
|
||||
|
||||
// When generating a Key, the Account Name must be set.
|
||||
var ErrGenerateMissingAccountName = errors.New("AccountName must be set")
|
||||
|
||||
// Key represents an TOTP or HTOP key.
|
||||
type Key struct {
|
||||
orig string
|
||||
url *url.URL
|
||||
}
|
||||
|
||||
// NewKeyFromURL creates a new Key from an TOTP or HOTP url.
|
||||
//
|
||||
// The URL format is documented here:
|
||||
// https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
||||
//
|
||||
func NewKeyFromURL(orig string) (*Key, error) {
|
||||
s := strings.TrimSpace(orig)
|
||||
|
||||
u, err := url.Parse(s)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Key{
|
||||
orig: s,
|
||||
url: u,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (k *Key) String() string {
|
||||
return k.orig
|
||||
}
|
||||
|
||||
// Image returns an QR-Code image of the specified width and height,
|
||||
// suitable for use by many clients like Google-Authenricator
|
||||
// to enroll a user's TOTP/HOTP key.
|
||||
func (k *Key) Image(width int, height int) (image.Image, error) {
|
||||
b, err := qr.Encode(k.orig, qr.M, qr.Auto)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err = barcode.Scale(b, width, height)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Type returns "hotp" or "totp".
|
||||
func (k *Key) Type() string {
|
||||
return k.url.Host
|
||||
}
|
||||
|
||||
// Issuer returns the name of the issuing organization.
|
||||
func (k *Key) Issuer() string {
|
||||
q := k.url.Query()
|
||||
|
||||
issuer := q.Get("issuer")
|
||||
|
||||
if issuer != "" {
|
||||
return issuer
|
||||
}
|
||||
|
||||
p := strings.TrimPrefix(k.url.Path, "/")
|
||||
i := strings.Index(p, ":")
|
||||
|
||||
if i == -1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return p[:i]
|
||||
}
|
||||
|
||||
// AccountName returns the name of the user's account.
|
||||
func (k *Key) AccountName() string {
|
||||
p := strings.TrimPrefix(k.url.Path, "/")
|
||||
i := strings.Index(p, ":")
|
||||
|
||||
if i == -1 {
|
||||
return p
|
||||
}
|
||||
|
||||
return p[i+1:]
|
||||
}
|
||||
|
||||
// Secret returns the opaque secret for this Key.
|
||||
func (k *Key) Secret() string {
|
||||
q := k.url.Query()
|
||||
|
||||
return q.Get("secret")
|
||||
}
|
||||
|
||||
// Algorithm represents the hashing function to use in the HMAC
|
||||
// operation needed for OTPs.
|
||||
type Algorithm int
|
||||
|
||||
const (
|
||||
AlgorithmSHA1 Algorithm = iota
|
||||
AlgorithmSHA256
|
||||
AlgorithmSHA512
|
||||
AlgorithmMD5
|
||||
)
|
||||
|
||||
func (a Algorithm) String() string {
|
||||
switch a {
|
||||
case AlgorithmSHA1:
|
||||
return "SHA1"
|
||||
case AlgorithmSHA256:
|
||||
return "SHA256"
|
||||
case AlgorithmSHA512:
|
||||
return "SHA512"
|
||||
case AlgorithmMD5:
|
||||
return "MD5"
|
||||
}
|
||||
panic("unreached")
|
||||
}
|
||||
|
||||
func (a Algorithm) Hash() hash.Hash {
|
||||
switch a {
|
||||
case AlgorithmSHA1:
|
||||
return sha1.New()
|
||||
case AlgorithmSHA256:
|
||||
return sha256.New()
|
||||
case AlgorithmSHA512:
|
||||
return sha512.New()
|
||||
case AlgorithmMD5:
|
||||
return md5.New()
|
||||
}
|
||||
panic("unreached")
|
||||
}
|
||||
|
||||
// Digits represents the number of digits present in the
|
||||
// user's OTP passcode. Six and Eight are the most common values.
|
||||
type Digits int
|
||||
|
||||
const (
|
||||
DigitsSix Digits = 6
|
||||
DigitsEight Digits = 8
|
||||
)
|
||||
|
||||
// Format converts an integer into the zero-filled size for this Digits.
|
||||
func (d Digits) Format(in int32) string {
|
||||
f := fmt.Sprintf("%%0%dd", d)
|
||||
return fmt.Sprintf(f, in)
|
||||
}
|
||||
|
||||
// Length returns the number of characters for this Digits.
|
||||
func (d Digits) Length() int {
|
||||
return int(d)
|
||||
}
|
||||
|
||||
func (d Digits) String() string {
|
||||
return fmt.Sprintf("%d", d)
|
||||
}
|
55
vendor/github.com/pquerna/otp/otp_test.go
generated
vendored
Normal file
55
vendor/github.com/pquerna/otp/otp_test.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* Copyright 2014 Paul Querna
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package otp
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKeyAllThere(t *testing.T) {
|
||||
k, err := NewKeyFromURL(`otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example`)
|
||||
require.NoError(t, err, "failed to parse url")
|
||||
require.Equal(t, "totp", k.Type(), "Extracting Type")
|
||||
require.Equal(t, "Example", k.Issuer(), "Extracting Issuer")
|
||||
require.Equal(t, "alice@google.com", k.AccountName(), "Extracting Account Name")
|
||||
require.Equal(t, "JBSWY3DPEHPK3PXP", k.Secret(), "Extracting Secret")
|
||||
}
|
||||
|
||||
func TestKeyIssuerOnlyInPath(t *testing.T) {
|
||||
k, err := NewKeyFromURL(`otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP`)
|
||||
require.NoError(t, err, "failed to parse url")
|
||||
require.Equal(t, "Example", k.Issuer(), "Extracting Issuer")
|
||||
require.Equal(t, "alice@google.com", k.AccountName(), "Extracting Account Name")
|
||||
}
|
||||
|
||||
func TestKeyNoIssuer(t *testing.T) {
|
||||
k, err := NewKeyFromURL(`otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP`)
|
||||
require.NoError(t, err, "failed to parse url")
|
||||
require.Equal(t, "", k.Issuer(), "Extracting Issuer")
|
||||
require.Equal(t, "alice@google.com", k.AccountName(), "Extracting Account Name")
|
||||
}
|
||||
|
||||
func TestKeyWithNewLine(t *testing.T) {
|
||||
w, err := NewKeyFromURL(`otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
sec := w.Secret()
|
||||
require.Equal(t, "JBSWY3DPEHPK3PXP", sec)
|
||||
}
|
193
vendor/github.com/pquerna/otp/totp/totp.go
generated
vendored
Normal file
193
vendor/github.com/pquerna/otp/totp/totp.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
|||
/**
|
||||
* Copyright 2014 Paul Querna
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package totp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/hotp"
|
||||
|
||||
"crypto/rand"
|
||||
"encoding/base32"
|
||||
"math"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Validate a TOTP using the current time.
|
||||
// A shortcut for ValidateCustom, Validate uses a configuration
|
||||
// that is compatible with Google-Authenticator and most clients.
|
||||
func Validate(passcode string, secret string) bool {
|
||||
rv, _ := ValidateCustom(
|
||||
passcode,
|
||||
secret,
|
||||
time.Now().UTC(),
|
||||
ValidateOpts{
|
||||
Period: 30,
|
||||
Skew: 1,
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
},
|
||||
)
|
||||
return rv
|
||||
}
|
||||
|
||||
// GenerateCode creates a TOTP token using the current time.
|
||||
// A shortcut for GenerateCodeCustom, GenerateCode uses a configuration
|
||||
// that is compatible with Google-Authenticator and most clients.
|
||||
func GenerateCode(secret string, t time.Time) (string, error) {
|
||||
return GenerateCodeCustom(secret, t, ValidateOpts{
|
||||
Period: 30,
|
||||
Skew: 1,
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
})
|
||||
}
|
||||
|
||||
// ValidateOpts provides options for ValidateCustom().
|
||||
type ValidateOpts struct {
|
||||
// Number of seconds a TOTP hash is valid for. Defaults to 30 seconds.
|
||||
Period uint
|
||||
// Periods before or after the current time to allow. Value of 1 allows up to Period
|
||||
// of either side of the specified time. Defaults to 0 allowed skews. Values greater
|
||||
// than 1 are likely sketchy.
|
||||
Skew uint
|
||||
// Digits as part of the input. Defaults to 6.
|
||||
Digits otp.Digits
|
||||
// Algorithm to use for HMAC. Defaults to SHA1.
|
||||
Algorithm otp.Algorithm
|
||||
}
|
||||
|
||||
// GenerateCodeCustom takes a timepoint and produces a passcode using a
|
||||
// secret and the provided opts. (Under the hood, this is making an adapted
|
||||
// call to hotp.GenerateCodeCustom)
|
||||
func GenerateCodeCustom(secret string, t time.Time, opts ValidateOpts) (passcode string, err error) {
|
||||
if opts.Period == 0 {
|
||||
opts.Period = 30
|
||||
}
|
||||
counter := uint64(math.Floor(float64(t.Unix()) / float64(opts.Period)))
|
||||
passcode, err = hotp.GenerateCodeCustom(secret, counter, hotp.ValidateOpts{
|
||||
Digits: opts.Digits,
|
||||
Algorithm: opts.Algorithm,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return passcode, nil
|
||||
}
|
||||
|
||||
// ValidateCustom validates a TOTP given a user specified time and custom options.
|
||||
// Most users should use Validate() to provide an interpolatable TOTP experience.
|
||||
func ValidateCustom(passcode string, secret string, t time.Time, opts ValidateOpts) (bool, error) {
|
||||
if opts.Period == 0 {
|
||||
opts.Period = 30
|
||||
}
|
||||
|
||||
counters := []uint64{}
|
||||
counter := int64(math.Floor(float64(t.Unix()) / float64(opts.Period)))
|
||||
|
||||
counters = append(counters, uint64(counter))
|
||||
for i := 1; i <= int(opts.Skew); i++ {
|
||||
counters = append(counters, uint64(counter+int64(i)))
|
||||
counters = append(counters, uint64(counter-int64(i)))
|
||||
}
|
||||
|
||||
for _, counter := range counters {
|
||||
rv, err := hotp.ValidateCustom(passcode, counter, secret, hotp.ValidateOpts{
|
||||
Digits: opts.Digits,
|
||||
Algorithm: opts.Algorithm,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if rv == true {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// GenerateOpts provides options for Generate(). The default values
|
||||
// are compatible with Google-Authenticator.
|
||||
type GenerateOpts struct {
|
||||
// Name of the issuing Organization/Company.
|
||||
Issuer string
|
||||
// Name of the User's Account (eg, email address)
|
||||
AccountName string
|
||||
// Number of seconds a TOTP hash is valid for. Defaults to 30 seconds.
|
||||
Period uint
|
||||
// Size in size of the generated Secret. Defaults to 10 bytes.
|
||||
SecretSize uint
|
||||
// Digits to request. Defaults to 6.
|
||||
Digits otp.Digits
|
||||
// Algorithm to use for HMAC. Defaults to SHA1.
|
||||
Algorithm otp.Algorithm
|
||||
}
|
||||
|
||||
// Generate a new TOTP Key.
|
||||
func Generate(opts GenerateOpts) (*otp.Key, error) {
|
||||
// url encode the Issuer/AccountName
|
||||
if opts.Issuer == "" {
|
||||
return nil, otp.ErrGenerateMissingIssuer
|
||||
}
|
||||
|
||||
if opts.AccountName == "" {
|
||||
return nil, otp.ErrGenerateMissingAccountName
|
||||
}
|
||||
|
||||
if opts.Period == 0 {
|
||||
opts.Period = 30
|
||||
}
|
||||
|
||||
if opts.SecretSize == 0 {
|
||||
opts.SecretSize = 10
|
||||
}
|
||||
|
||||
if opts.Digits == 0 {
|
||||
opts.Digits = otp.DigitsSix
|
||||
}
|
||||
|
||||
// otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
|
||||
|
||||
v := url.Values{}
|
||||
secret := make([]byte, opts.SecretSize)
|
||||
_, err := rand.Read(secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v.Set("secret", strings.TrimRight(base32.StdEncoding.EncodeToString(secret), "="))
|
||||
v.Set("issuer", opts.Issuer)
|
||||
v.Set("period", strconv.FormatUint(uint64(opts.Period), 10))
|
||||
v.Set("algorithm", opts.Algorithm.String())
|
||||
v.Set("digits", opts.Digits.String())
|
||||
|
||||
u := url.URL{
|
||||
Scheme: "otpauth",
|
||||
Host: "totp",
|
||||
Path: "/" + opts.Issuer + ":" + opts.AccountName,
|
||||
RawQuery: v.Encode(),
|
||||
}
|
||||
|
||||
return otp.NewKeyFromURL(u.String())
|
||||
}
|
146
vendor/github.com/pquerna/otp/totp/totp_test.go
generated
vendored
Normal file
146
vendor/github.com/pquerna/otp/totp/totp_test.go
generated
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
/**
|
||||
* Copyright 2014 Paul Querna
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package totp
|
||||
|
||||
import (
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"encoding/base32"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type tc struct {
|
||||
TS int64
|
||||
TOTP string
|
||||
Mode otp.Algorithm
|
||||
Secret string
|
||||
}
|
||||
|
||||
var (
|
||||
secSha1 = base32.StdEncoding.EncodeToString([]byte("12345678901234567890"))
|
||||
secSha256 = base32.StdEncoding.EncodeToString([]byte("12345678901234567890123456789012"))
|
||||
secSha512 = base32.StdEncoding.EncodeToString([]byte("1234567890123456789012345678901234567890123456789012345678901234"))
|
||||
|
||||
rfcMatrixTCs = []tc{
|
||||
tc{59, "94287082", otp.AlgorithmSHA1, secSha1},
|
||||
tc{59, "46119246", otp.AlgorithmSHA256, secSha256},
|
||||
tc{59, "90693936", otp.AlgorithmSHA512, secSha512},
|
||||
tc{1111111109, "07081804", otp.AlgorithmSHA1, secSha1},
|
||||
tc{1111111109, "68084774", otp.AlgorithmSHA256, secSha256},
|
||||
tc{1111111109, "25091201", otp.AlgorithmSHA512, secSha512},
|
||||
tc{1111111111, "14050471", otp.AlgorithmSHA1, secSha1},
|
||||
tc{1111111111, "67062674", otp.AlgorithmSHA256, secSha256},
|
||||
tc{1111111111, "99943326", otp.AlgorithmSHA512, secSha512},
|
||||
tc{1234567890, "89005924", otp.AlgorithmSHA1, secSha1},
|
||||
tc{1234567890, "91819424", otp.AlgorithmSHA256, secSha256},
|
||||
tc{1234567890, "93441116", otp.AlgorithmSHA512, secSha512},
|
||||
tc{2000000000, "69279037", otp.AlgorithmSHA1, secSha1},
|
||||
tc{2000000000, "90698825", otp.AlgorithmSHA256, secSha256},
|
||||
tc{2000000000, "38618901", otp.AlgorithmSHA512, secSha512},
|
||||
tc{20000000000, "65353130", otp.AlgorithmSHA1, secSha1},
|
||||
tc{20000000000, "77737706", otp.AlgorithmSHA256, secSha256},
|
||||
tc{20000000000, "47863826", otp.AlgorithmSHA512, secSha512},
|
||||
}
|
||||
)
|
||||
|
||||
//
|
||||
// Test vectors from http://tools.ietf.org/html/rfc6238#appendix-B
|
||||
// NOTE -- the test vectors are documented as having the SAME
|
||||
// secret -- this is WRONG -- they have a variable secret
|
||||
// depending upon the hmac algorithm:
|
||||
// http://www.rfc-editor.org/errata_search.php?rfc=6238
|
||||
// this only took a few hours of head/desk interaction to figure out.
|
||||
//
|
||||
func TestValidateRFCMatrix(t *testing.T) {
|
||||
for _, tx := range rfcMatrixTCs {
|
||||
valid, err := ValidateCustom(tx.TOTP, tx.Secret, time.Unix(tx.TS, 0).UTC(),
|
||||
ValidateOpts{
|
||||
Digits: otp.DigitsEight,
|
||||
Algorithm: tx.Mode,
|
||||
})
|
||||
require.NoError(t, err,
|
||||
"unexpected error totp=%s mode=%v ts=%v", tx.TOTP, tx.Mode, tx.TS)
|
||||
require.True(t, valid,
|
||||
"unexpected totp failure totp=%s mode=%v ts=%v", tx.TOTP, tx.Mode, tx.TS)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateRFCTCs(t *testing.T) {
|
||||
for _, tx := range rfcMatrixTCs {
|
||||
passcode, err := GenerateCodeCustom(tx.Secret, time.Unix(tx.TS, 0).UTC(),
|
||||
ValidateOpts{
|
||||
Digits: otp.DigitsEight,
|
||||
Algorithm: tx.Mode,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tx.TOTP, passcode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateSkew(t *testing.T) {
|
||||
secSha1 := base32.StdEncoding.EncodeToString([]byte("12345678901234567890"))
|
||||
|
||||
tests := []tc{
|
||||
tc{29, "94287082", otp.AlgorithmSHA1, secSha1},
|
||||
tc{59, "94287082", otp.AlgorithmSHA1, secSha1},
|
||||
tc{61, "94287082", otp.AlgorithmSHA1, secSha1},
|
||||
}
|
||||
|
||||
for _, tx := range tests {
|
||||
valid, err := ValidateCustom(tx.TOTP, tx.Secret, time.Unix(tx.TS, 0).UTC(),
|
||||
ValidateOpts{
|
||||
Digits: otp.DigitsEight,
|
||||
Algorithm: tx.Mode,
|
||||
Skew: 1,
|
||||
})
|
||||
require.NoError(t, err,
|
||||
"unexpected error totp=%s mode=%v ts=%v", tx.TOTP, tx.Mode, tx.TS)
|
||||
require.True(t, valid,
|
||||
"unexpected totp failure totp=%s mode=%v ts=%v", tx.TOTP, tx.Mode, tx.TS)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
k, err := Generate(GenerateOpts{
|
||||
Issuer: "SnakeOil",
|
||||
AccountName: "alice@example.com",
|
||||
})
|
||||
require.NoError(t, err, "generate basic TOTP")
|
||||
require.Equal(t, "SnakeOil", k.Issuer(), "Extracting Issuer")
|
||||
require.Equal(t, "alice@example.com", k.AccountName(), "Extracting Account Name")
|
||||
require.Equal(t, 16, len(k.Secret()), "Secret is 16 bytes long as base32.")
|
||||
|
||||
k, err = Generate(GenerateOpts{
|
||||
Issuer: "SnakeOil",
|
||||
AccountName: "alice@example.com",
|
||||
SecretSize: 20,
|
||||
})
|
||||
require.NoError(t, err, "generate larger TOTP")
|
||||
require.Equal(t, 32, len(k.Secret()), "Secret is 32 bytes long as base32.")
|
||||
|
||||
k, err = Generate(GenerateOpts{
|
||||
Issuer: "SnakeOil",
|
||||
AccountName: "alice@example.com",
|
||||
SecretSize: 13, // anything that is not divisable by 5, really
|
||||
})
|
||||
require.NoError(t, err, "Secret size is valid when length not divisable by 5.")
|
||||
require.NotContains(t, k.Secret(), "=", "Secret has no escaped characters.")
|
||||
}
|
Loading…
Reference in a new issue