@@ -58,6 +58,9 @@ import ( | |||
web "github.com/dutchcoders/transfer.sh-web" | |||
"github.com/gorilla/mux" | |||
"github.com/russross/blackfriday" | |||
qrcode "github.com/skip2/go-qrcode" | |||
"encoding/base64" | |||
) | |||
var ( | |||
@@ -147,6 +150,15 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
return | |||
} | |||
var png []byte | |||
png, err = qrcode.Encode(resolveUrl(r, getURL(r).ResolveReference(r.URL), true), qrcode.High, 150) | |||
if err != nil { | |||
http.Error(w, err.Error(), http.StatusInternalServerError) | |||
return | |||
} | |||
qrCode := base64.StdEncoding.EncodeToString(png) | |||
data := struct { | |||
ContentType string | |||
Content html_template.HTML | |||
@@ -155,6 +167,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
ContentLength uint64 | |||
GAKey string | |||
UserVoiceKey string | |||
QRCode string | |||
}{ | |||
contentType, | |||
content, | |||
@@ -163,6 +176,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
contentLength, | |||
s.gaKey, | |||
s.userVoiceKey, | |||
qrCode, | |||
} | |||
if err := htmlTemplates.ExecuteTemplate(w, templatePath, data); err != nil { | |||
@@ -0,0 +1,4 @@ | |||
*.sw* | |||
*.png | |||
*.directory | |||
qrcode/qrcode |
@@ -0,0 +1,8 @@ | |||
language: go | |||
go: | |||
- 1.7 | |||
script: | |||
- go test -v ./... | |||
@@ -0,0 +1,19 @@ | |||
Copyright (c) 2014 Tom Harwood | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in | |||
all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
THE SOFTWARE. |
@@ -0,0 +1,80 @@ | |||
# go-qrcode # | |||
<img src='https://skip.org/img/nyancat-youtube-qr.png' align='right'> | |||
Package qrcode implements a QR Code encoder. [![Build Status](https://travis-ci.org/skip2/go-qrcode.svg?branch=master)](https://travis-ci.org/skip2/go-qrcode) | |||
A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be encoded, with URLs being a popular choice :) | |||
Each QR Code contains error recovery information to aid reading damaged or obscured codes. There are four levels of error recovery: Low, medium, high and highest. QR Codes with a higher recovery level are more robust to damage, at the cost of being physically larger. | |||
## Install | |||
go get -u github.com/skip2/go-qrcode/... | |||
A command-line tool `qrcode` will be built into `$GOPATH/bin/`. | |||
## Usage | |||
import qrcode "github.com/skip2/go-qrcode" | |||
- **Create a PNG image:** | |||
var png []byte | |||
png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256) | |||
- **Create a PNG image and write to a file:** | |||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png") | |||
- **Create a PNG image with custom colors and write to file:** | |||
err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png") | |||
All examples use the qrcode.Medium error Recovery Level and create a fixed | |||
256x256px size QR Code. The last function creates a white on black instead of black | |||
on white QR Code. | |||
The maximum capacity of a QR Code varies according to the content encoded and | |||
the error recovery level. The maximum capacity is 2,953 bytes, 4,296 | |||
alphanumeric characters, 7,089 numeric digits, or a combination of these. | |||
## Documentation | |||
[![godoc](https://godoc.org/github.com/skip2/go-qrcode?status.png)](https://godoc.org/github.com/skip2/go-qrcode) | |||
## Demoapp | |||
[http://go-qrcode.appspot.com](http://go-qrcode.appspot.com) | |||
## CLI | |||
A command-line tool `qrcode` will be built into `$GOPATH/bin/`. | |||
``` | |||
qrcode -- QR Code encoder in Go | |||
https://github.com/skip2/go-qrcode | |||
Flags: | |||
-o string | |||
out PNG file prefix, empty for stdout | |||
-s int | |||
image size (pixel) (default 256) | |||
Usage: | |||
1. Arguments except for flags are joined by " " and used to generate QR code. | |||
Default output is STDOUT, pipe to imagemagick command "display" to display | |||
on any X server. | |||
qrcode hello word | display | |||
2. Save to file if "display" not available: | |||
qrcode "homepage: https://github.com/skip2/go-qrcode" > out.png | |||
``` | |||
## Links | |||
- [http://en.wikipedia.org/wiki/QR_code](http://en.wikipedia.org/wiki/QR_code) | |||
- [ISO/IEC 18004:2006](http://www.iso.org/iso/catalogue_detail.htm?csnumber=43655) - Main QR Code specification (approx CHF 198,00)<br> | |||
- [https://github.com/qpliu/qrencode-go/](https://github.com/qpliu/qrencode-go/) - alternative Go QR encoding library based on [ZXing](https://github.com/zxing/zxing) |
@@ -0,0 +1,273 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
// Package bitset implements an append only bit array. | |||
// | |||
// To create a Bitset and append some bits: | |||
// // Bitset Contents | |||
// b := bitset.New() // {} | |||
// b.AppendBools(true, true, false) // {1, 1, 0} | |||
// b.AppendBools(true) // {1, 1, 0, 1} | |||
// b.AppendValue(0x02, 4) // {1, 1, 0, 1, 0, 0, 1, 0} | |||
// | |||
// To read values: | |||
// | |||
// len := b.Len() // 8 | |||
// v := b.At(0) // 1 | |||
// v = b.At(1) // 1 | |||
// v = b.At(2) // 0 | |||
// v = b.At(8) // 0 | |||
package bitset | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"log" | |||
) | |||
const ( | |||
b0 = false | |||
b1 = true | |||
) | |||
// Bitset stores an array of bits. | |||
type Bitset struct { | |||
// The number of bits stored. | |||
numBits int | |||
// Storage for individual bits. | |||
bits []byte | |||
} | |||
// New returns an initialised Bitset with optional initial bits v. | |||
func New(v ...bool) *Bitset { | |||
b := &Bitset{numBits: 0, bits: make([]byte, 0)} | |||
b.AppendBools(v...) | |||
return b | |||
} | |||
// Clone returns a copy. | |||
func Clone(from *Bitset) *Bitset { | |||
return &Bitset{numBits: from.numBits, bits: from.bits[:]} | |||
} | |||
// Substr returns a substring, consisting of the bits from indexes start to end. | |||
func (b *Bitset) Substr(start int, end int) *Bitset { | |||
if start > end || end > b.numBits { | |||
log.Panicf("Out of range start=%d end=%d numBits=%d", start, end, b.numBits) | |||
} | |||
result := New() | |||
result.ensureCapacity(end - start) | |||
for i := start; i < end; i++ { | |||
if b.At(i) { | |||
result.bits[result.numBits/8] |= 0x80 >> uint(result.numBits%8) | |||
} | |||
result.numBits++ | |||
} | |||
return result | |||
} | |||
// NewFromBase2String constructs and returns a Bitset from a string. The string | |||
// consists of '1', '0' or ' ' characters, e.g. "1010 0101". The '1' and '0' | |||
// characters represent true/false bits respectively, and ' ' characters are | |||
// ignored. | |||
// | |||
// The function panics if the input string contains other characters. | |||
func NewFromBase2String(b2string string) *Bitset { | |||
b := &Bitset{numBits: 0, bits: make([]byte, 0)} | |||
for _, c := range b2string { | |||
switch c { | |||
case '1': | |||
b.AppendBools(true) | |||
case '0': | |||
b.AppendBools(false) | |||
case ' ': | |||
default: | |||
log.Panicf("Invalid char %c in NewFromBase2String", c) | |||
} | |||
} | |||
return b | |||
} | |||
// AppendBytes appends a list of whole bytes. | |||
func (b *Bitset) AppendBytes(data []byte) { | |||
for _, d := range data { | |||
b.AppendByte(d, 8) | |||
} | |||
} | |||
// AppendByte appends the numBits least significant bits from value. | |||
func (b *Bitset) AppendByte(value byte, numBits int) { | |||
b.ensureCapacity(numBits) | |||
if numBits > 8 { | |||
log.Panicf("numBits %d out of range 0-8", numBits) | |||
} | |||
for i := numBits - 1; i >= 0; i-- { | |||
if value&(1<<uint(i)) != 0 { | |||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) | |||
} | |||
b.numBits++ | |||
} | |||
} | |||
// AppendUint32 appends the numBits least significant bits from value. | |||
func (b *Bitset) AppendUint32(value uint32, numBits int) { | |||
b.ensureCapacity(numBits) | |||
if numBits > 32 { | |||
log.Panicf("numBits %d out of range 0-32", numBits) | |||
} | |||
for i := numBits - 1; i >= 0; i-- { | |||
if value&(1<<uint(i)) != 0 { | |||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) | |||
} | |||
b.numBits++ | |||
} | |||
} | |||
// ensureCapacity ensures the Bitset can store an additional |numBits|. | |||
// | |||
// The underlying array is expanded if necessary. To prevent frequent | |||
// reallocation, expanding the underlying array at least doubles its capacity. | |||
func (b *Bitset) ensureCapacity(numBits int) { | |||
numBits += b.numBits | |||
newNumBytes := numBits / 8 | |||
if numBits%8 != 0 { | |||
newNumBytes++ | |||
} | |||
if len(b.bits) >= newNumBytes { | |||
return | |||
} | |||
b.bits = append(b.bits, make([]byte, newNumBytes+2*len(b.bits))...) | |||
} | |||
// Append bits copied from |other|. | |||
// | |||
// The new length is b.Len() + other.Len(). | |||
func (b *Bitset) Append(other *Bitset) { | |||
b.ensureCapacity(other.numBits) | |||
for i := 0; i < other.numBits; i++ { | |||
if other.At(i) { | |||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) | |||
} | |||
b.numBits++ | |||
} | |||
} | |||
// AppendBools appends bits to the Bitset. | |||
func (b *Bitset) AppendBools(bits ...bool) { | |||
b.ensureCapacity(len(bits)) | |||
for _, v := range bits { | |||
if v { | |||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8) | |||
} | |||
b.numBits++ | |||
} | |||
} | |||
// AppendNumBools appends num bits of value value. | |||
func (b *Bitset) AppendNumBools(num int, value bool) { | |||
for i := 0; i < num; i++ { | |||
b.AppendBools(value) | |||
} | |||
} | |||
// String returns a human readable representation of the Bitset's contents. | |||
func (b *Bitset) String() string { | |||
var bitString string | |||
for i := 0; i < b.numBits; i++ { | |||
if (i % 8) == 0 { | |||
bitString += " " | |||
} | |||
if (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 { | |||
bitString += "1" | |||
} else { | |||
bitString += "0" | |||
} | |||
} | |||
return fmt.Sprintf("numBits=%d, bits=%s", b.numBits, bitString) | |||
} | |||
// Len returns the length of the Bitset in bits. | |||
func (b *Bitset) Len() int { | |||
return b.numBits | |||
} | |||
// Bits returns the contents of the Bitset. | |||
func (b *Bitset) Bits() []bool { | |||
result := make([]bool, b.numBits) | |||
var i int | |||
for i = 0; i < b.numBits; i++ { | |||
result[i] = (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 | |||
} | |||
return result | |||
} | |||
// At returns the value of the bit at |index|. | |||
func (b *Bitset) At(index int) bool { | |||
if index >= b.numBits { | |||
log.Panicf("Index %d out of range", index) | |||
} | |||
return (b.bits[index/8] & (0x80 >> byte(index%8))) != 0 | |||
} | |||
// Equals returns true if the Bitset equals other. | |||
func (b *Bitset) Equals(other *Bitset) bool { | |||
if b.numBits != other.numBits { | |||
return false | |||
} | |||
if !bytes.Equal(b.bits[0:b.numBits/8], other.bits[0:b.numBits/8]) { | |||
return false | |||
} | |||
for i := 8 * (b.numBits / 8); i < b.numBits; i++ { | |||
a := (b.bits[i/8] & (0x80 >> byte(i%8))) | |||
b := (other.bits[i/8] & (0x80 >> byte(i%8))) | |||
if a != b { | |||
return false | |||
} | |||
} | |||
return true | |||
} | |||
// ByteAt returns a byte consisting of upto 8 bits starting at index. | |||
func (b *Bitset) ByteAt(index int) byte { | |||
if index < 0 || index >= b.numBits { | |||
log.Panicf("Index %d out of range", index) | |||
} | |||
var result byte | |||
for i := index; i < index+8 && i < b.numBits; i++ { | |||
result <<= 1 | |||
if b.At(i) { | |||
result |= 1 | |||
} | |||
} | |||
return result | |||
} |
@@ -0,0 +1,321 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package bitset | |||
import ( | |||
rand "math/rand" | |||
"testing" | |||
) | |||
func TestNewBitset(t *testing.T) { | |||
tests := [][]bool{ | |||
{}, | |||
{b1}, | |||
{b0}, | |||
{b1, b0}, | |||
{b1, b0, b1}, | |||
{b0, b0, b1}, | |||
} | |||
for _, v := range tests { | |||
result := New(v...) | |||
if !equal(result.Bits(), v) { | |||
t.Errorf("%s", result.String()) | |||
t.Errorf("%v => %v, want %v", v, result.Bits(), v) | |||
} | |||
} | |||
} | |||
func TestAppend(t *testing.T) { | |||
randomBools := make([]bool, 128) | |||
rng := rand.New(rand.NewSource(1)) | |||
for i := 0; i < len(randomBools); i++ { | |||
randomBools[i] = rng.Intn(2) == 1 | |||
} | |||
for i := 0; i < len(randomBools)-1; i++ { | |||
a := New(randomBools[0:i]...) | |||
b := New(randomBools[i:]...) | |||
a.Append(b) | |||
if !equal(a.Bits(), randomBools) { | |||
t.Errorf("got %v, want %v", a.Bits(), randomBools) | |||
} | |||
} | |||
} | |||
func TestAppendByte(t *testing.T) { | |||
tests := []struct { | |||
initial *Bitset | |||
value byte | |||
numBits int | |||
expected *Bitset | |||
}{ | |||
{ | |||
New(), | |||
0x01, | |||
1, | |||
New(b1), | |||
}, | |||
{ | |||
New(b1), | |||
0x01, | |||
1, | |||
New(b1, b1), | |||
}, | |||
{ | |||
New(b0), | |||
0x01, | |||
1, | |||
New(b0, b1), | |||
}, | |||
{ | |||
New(b1, b0, b1, b0, b1, b0, b1), | |||
0xAA, // 0b10101010 | |||
2, | |||
New(b1, b0, b1, b0, b1, b0, b1, b1, b0), | |||
}, | |||
{ | |||
New(b1, b0, b1, b0, b1, b0, b1), | |||
0xAA, // 0b10101010 | |||
8, | |||
New(b1, b0, b1, b0, b1, b0, b1, b1, b0, b1, b0, b1, b0, b1, b0), | |||
}, | |||
} | |||
for _, test := range tests { | |||
test.initial.AppendByte(test.value, test.numBits) | |||
if !equal(test.initial.Bits(), test.expected.Bits()) { | |||
t.Errorf("Got %v, expected %v", test.initial.Bits(), | |||
test.expected.Bits()) | |||
} | |||
} | |||
} | |||
func TestAppendUint32(t *testing.T) { | |||
tests := []struct { | |||
initial *Bitset | |||
value uint32 | |||
numBits int | |||
expected *Bitset | |||
}{ | |||
{ | |||
New(), | |||
0xAAAAAAAF, | |||
4, | |||
New(b1, b1, b1, b1), | |||
}, | |||
{ | |||
New(), | |||
0xFFFFFFFF, | |||
32, | |||
New(b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, | |||
b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1, b1), | |||
}, | |||
{ | |||
New(), | |||
0x0, | |||
32, | |||
New(b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, | |||
b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0), | |||
}, | |||
{ | |||
New(), | |||
0xAAAAAAAA, | |||
32, | |||
New(b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, | |||
b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0), | |||
}, | |||
{ | |||
New(), | |||
0xAAAAAAAA, | |||
31, | |||
New(b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, | |||
b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0, b1, b0), | |||
}, | |||
} | |||
for _, test := range tests { | |||
test.initial.AppendUint32(test.value, test.numBits) | |||
if !equal(test.initial.Bits(), test.expected.Bits()) { | |||
t.Errorf("Got %v, expected %v", test.initial.Bits(), | |||
test.expected.Bits()) | |||
} | |||
} | |||
} | |||
func TestAppendBools(t *testing.T) { | |||
randomBools := make([]bool, 128) | |||
rng := rand.New(rand.NewSource(1)) | |||
for i := 0; i < len(randomBools); i++ { | |||
randomBools[i] = rng.Intn(2) == 1 | |||
} | |||
for i := 0; i < len(randomBools)-1; i++ { | |||
result := New(randomBools[0:i]...) | |||
result.AppendBools(randomBools[i:]...) | |||
if !equal(result.Bits(), randomBools) { | |||
t.Errorf("got %v, want %v", result.Bits(), randomBools) | |||
} | |||
} | |||
} | |||
func BenchmarkShortAppend(b *testing.B) { | |||
bitset := New() | |||
for i := 0; i < b.N; i++ { | |||
bitset.AppendBools(b0, b1, b0, b1, b0, b1, b0) | |||
} | |||
} | |||
func TestLen(t *testing.T) { | |||
randomBools := make([]bool, 128) | |||
rng := rand.New(rand.NewSource(1)) | |||
for i := 0; i < len(randomBools); i++ { | |||
randomBools[i] = rng.Intn(2) == 1 | |||
} | |||
for i := 0; i < len(randomBools)-1; i++ { | |||
result := New(randomBools[0:i]...) | |||
if result.Len() != i { | |||
t.Errorf("Len = %d, want %d", result.Len(), i) | |||
} | |||
} | |||
} | |||
func TestAt(t *testing.T) { | |||
test := []bool{b0, b1, b0, b1, b0, b1, b1, b0, b1} | |||
bitset := New(test...) | |||
for i, v := range test { | |||
result := bitset.At(i) | |||
if result != test[i] { | |||
t.Errorf("bitset[%d] => %t, want %t", i, result, v) | |||
} | |||
} | |||
} | |||
func equal(a []bool, b []bool) bool { | |||
if len(a) != len(b) { | |||
return false | |||
} | |||
for i := 0; i < len(a); i++ { | |||
if a[i] != b[i] { | |||
return false | |||
} | |||
} | |||
return true | |||
} | |||
func TestExample(t *testing.T) { | |||
b := New() // {} | |||
b.AppendBools(true, true, false) // {1, 1, 0} | |||
b.AppendBools(true) // {1, 1, 0, 1} | |||
b.AppendByte(0x02, 4) // {1, 1, 0, 1, 0, 0, 1, 0} | |||
expected := []bool{b1, b1, b0, b1, b0, b0, b1, b0} | |||
if !equal(b.Bits(), expected) { | |||
t.Errorf("Got %v, expected %v", b.Bits(), expected) | |||
} | |||
} | |||
func TestByteAt(t *testing.T) { | |||
data := []bool{b0, b1, b0, b1, b0, b1, b1, b0, b1} | |||
tests := []struct { | |||
index int | |||
expected byte | |||
}{ | |||
{ | |||
0, | |||
0x56, | |||
}, | |||
{ | |||
1, | |||
0xad, | |||
}, | |||
{ | |||
2, | |||
0x2d, | |||
}, | |||
{ | |||
5, | |||
0x0d, | |||
}, | |||
{ | |||
8, | |||
0x01, | |||
}, | |||
} | |||
for _, test := range tests { | |||
b := New() | |||
b.AppendBools(data...) | |||
result := b.ByteAt(test.index) | |||
if result != test.expected { | |||
t.Errorf("Got %#x, expected %#x", result, test.expected) | |||
} | |||
} | |||
} | |||
func TestSubstr(t *testing.T) { | |||
data := []bool{b0, b1, b0, b1, b0, b1, b1, b0} | |||
tests := []struct { | |||
start int | |||
end int | |||
expected []bool | |||
}{ | |||
{ | |||
0, | |||
8, | |||
[]bool{b0, b1, b0, b1, b0, b1, b1, b0}, | |||
}, | |||
{ | |||
0, | |||
0, | |||
[]bool{}, | |||
}, | |||
{ | |||
0, | |||
1, | |||
[]bool{b0}, | |||
}, | |||
{ | |||
2, | |||
4, | |||
[]bool{b0, b1}, | |||
}, | |||
} | |||
for _, test := range tests { | |||
b := New() | |||
b.AppendBools(data...) | |||
result := b.Substr(test.start, test.end) | |||
expected := New() | |||
expected.AppendBools(test.expected...) | |||
if !result.Equals(expected) { | |||
t.Errorf("Got %s, expected %s", result.String(), expected.String()) | |||
} | |||
} | |||
} |
@@ -0,0 +1,455 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package qrcode | |||
import ( | |||
"errors" | |||
"log" | |||
bitset "github.com/skip2/go-qrcode/bitset" | |||
) | |||
// Data encoding. | |||
// | |||
// The main data portion of a QR Code consists of one or more segments of data. | |||
// A segment consists of: | |||
// | |||
// - The segment Data Mode: numeric, alphanumeric, or byte. | |||
// - The length of segment in bits. | |||
// - Encoded data. | |||
// | |||
// For example, the string "123ZZ#!#!" may be represented as: | |||
// | |||
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"] | |||
// | |||
// Multiple data modes exist to minimise the size of encoded data. For example, | |||
// 8-bit bytes require 8 bits to encode each, but base 10 numeric data can be | |||
// encoded at a higher density of 3 numbers (e.g. 123) per 10 bits. | |||
// | |||
// Some data can be represented in multiple modes. Numeric data can be | |||
// represented in all three modes, whereas alphanumeric data (e.g. 'A') can be | |||
// represented in alphanumeric and byte mode. | |||
// | |||
// Starting a new segment (to use a different Data Mode) has a cost, the bits to | |||
// state the new segment Data Mode and length. To minimise each QR Code's symbol | |||
// size, an optimisation routine coalesces segment types where possible, to | |||
// reduce the encoded data length. | |||
// | |||
// There are several other data modes available (e.g. Kanji mode) which are not | |||
// implemented here. | |||
// A segment encoding mode. | |||
type dataMode uint8 | |||
const ( | |||
// Each dataMode is a subset of the subsequent dataMode: | |||
// dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte | |||
// | |||
// This ordering is important for determining which data modes a character can | |||
// be encoded with. E.g. 'E' can be encoded in both dataModeAlphanumeric and | |||
// dataModeByte. | |||
dataModeNone dataMode = 1 << iota | |||
dataModeNumeric | |||
dataModeAlphanumeric | |||
dataModeByte | |||
) | |||
// dataModeString returns d as a short printable string. | |||
func dataModeString(d dataMode) string { | |||
switch d { | |||
case dataModeNone: | |||
return "none" | |||
case dataModeNumeric: | |||
return "numeric" | |||
case dataModeAlphanumeric: | |||
return "alphanumeric" | |||
case dataModeByte: | |||
return "byte" | |||
} | |||
return "unknown" | |||
} | |||
type dataEncoderType uint8 | |||
const ( | |||
dataEncoderType1To9 dataEncoderType = iota | |||
dataEncoderType10To26 | |||
dataEncoderType27To40 | |||
) | |||
// segment is a single segment of data. | |||
type segment struct { | |||
// Data Mode (e.g. numeric). | |||
dataMode dataMode | |||
// segment data (e.g. "abc"). | |||
data []byte | |||
} | |||
// A dataEncoder encodes data for a particular QR Code version. | |||
type dataEncoder struct { | |||
// Minimum & maximum versions supported. | |||
minVersion int | |||
maxVersion int | |||
// Mode indicator bit sequences. | |||
numericModeIndicator *bitset.Bitset | |||
alphanumericModeIndicator *bitset.Bitset | |||
byteModeIndicator *bitset.Bitset | |||
// Character count lengths. | |||
numNumericCharCountBits int | |||
numAlphanumericCharCountBits int | |||
numByteCharCountBits int | |||
// The raw input data. | |||
data []byte | |||
// The data classified into unoptimised segments. | |||
actual []segment | |||
// The data classified into optimised segments. | |||
optimised []segment | |||
} | |||
// newDataEncoder constructs a dataEncoder. | |||
func newDataEncoder(t dataEncoderType) *dataEncoder { | |||
d := &dataEncoder{} | |||
switch t { | |||
case dataEncoderType1To9: | |||
d = &dataEncoder{ | |||
minVersion: 1, | |||
maxVersion: 9, | |||
numericModeIndicator: bitset.New(b0, b0, b0, b1), | |||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), | |||
byteModeIndicator: bitset.New(b0, b1, b0, b0), | |||
numNumericCharCountBits: 10, | |||
numAlphanumericCharCountBits: 9, | |||
numByteCharCountBits: 8, | |||
} | |||
case dataEncoderType10To26: | |||
d = &dataEncoder{ | |||
minVersion: 10, | |||
maxVersion: 26, | |||
numericModeIndicator: bitset.New(b0, b0, b0, b1), | |||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), | |||
byteModeIndicator: bitset.New(b0, b1, b0, b0), | |||
numNumericCharCountBits: 12, | |||
numAlphanumericCharCountBits: 11, | |||
numByteCharCountBits: 16, | |||
} | |||
case dataEncoderType27To40: | |||
d = &dataEncoder{ | |||
minVersion: 27, | |||
maxVersion: 40, | |||
numericModeIndicator: bitset.New(b0, b0, b0, b1), | |||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0), | |||
byteModeIndicator: bitset.New(b0, b1, b0, b0), | |||
numNumericCharCountBits: 14, | |||
numAlphanumericCharCountBits: 13, | |||
numByteCharCountBits: 16, | |||
} | |||
default: | |||
log.Panic("Unknown dataEncoderType") | |||
} | |||
return d | |||
} | |||
// encode data as one or more segments and return the encoded data. | |||
// | |||
// The returned data does not include the terminator bit sequence. | |||
func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) { | |||
d.data = data | |||
d.actual = nil | |||
d.optimised = nil | |||
if len(data) == 0 { | |||
return nil, errors.New("no data to encode") | |||
} | |||
// Classify data into unoptimised segments. | |||
d.classifyDataModes() | |||
// Optimise segments. | |||
err := d.optimiseDataModes() | |||
if err != nil { | |||
return nil, err | |||
} | |||
// Encode data. | |||
encoded := bitset.New() | |||
for _, s := range d.optimised { | |||
d.encodeDataRaw(s.data, s.dataMode, encoded) | |||
} | |||
return encoded, nil | |||
} | |||
// classifyDataModes classifies the raw data into unoptimised segments. | |||
// e.g. "123ZZ#!#!" => | |||
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"]. | |||
func (d *dataEncoder) classifyDataModes() { | |||
var start int | |||
mode := dataModeNone | |||
for i, v := range d.data { | |||
newMode := dataModeNone | |||
switch { | |||
case v >= 0x30 && v <= 0x39: | |||
newMode = dataModeNumeric | |||
case v == 0x20 || v == 0x24 || v == 0x25 || v == 0x2a || v == 0x2b || v == | |||
0x2d || v == 0x2e || v == 0x2f || v == 0x3a || (v >= 0x41 && v <= 0x5a): | |||
newMode = dataModeAlphanumeric | |||
default: | |||
newMode = dataModeByte | |||
} | |||
if newMode != mode { | |||
if i > 0 { | |||
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:i]}) | |||
start = i | |||
} | |||
mode = newMode | |||
} | |||
} | |||
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]}) | |||
} | |||
// optimiseDataModes optimises the list of segments to reduce the overall output | |||
// encoded data length. | |||
// | |||
// The algorithm coalesces adjacent segments. segments are only coalesced when | |||
// the Data Modes are compatible, and when the coalesced segment has a shorter | |||
// encoded length than separate segments. | |||
// | |||
// Multiple segments may be coalesced. For example a string of alternating | |||
// alphanumeric/numeric segments ANANANANA can be optimised to just A. | |||
func (d *dataEncoder) optimiseDataModes() error { | |||
for i := 0; i < len(d.actual); { | |||
mode := d.actual[i].dataMode | |||
numChars := len(d.actual[i].data) | |||
j := i + 1 | |||
for j < len(d.actual) { | |||
nextNumChars := len(d.actual[j].data) | |||
nextMode := d.actual[j].dataMode | |||
if nextMode > mode { | |||
break | |||
} | |||
coalescedLength, err := d.encodedLength(mode, numChars+nextNumChars) | |||
if err != nil { | |||
return err | |||
} | |||
seperateLength1, err := d.encodedLength(mode, numChars) | |||
if err != nil { | |||
return err | |||
} | |||
seperateLength2, err := d.encodedLength(nextMode, nextNumChars) | |||
if err != nil { | |||
return err | |||
} | |||
if coalescedLength < seperateLength1+seperateLength2 { | |||
j++ | |||
numChars += nextNumChars | |||
} else { | |||
break | |||
} | |||
} | |||
optimised := segment{dataMode: mode, | |||
data: make([]byte, 0, numChars)} | |||
for k := i; k < j; k++ { | |||
optimised.data = append(optimised.data, d.actual[k].data...) | |||
} | |||
d.optimised = append(d.optimised, optimised) | |||
i = j | |||
} | |||
return nil | |||
} | |||
// encodeDataRaw encodes data in dataMode. The encoded data is appended to | |||
// encoded. | |||
func (d *dataEncoder) encodeDataRaw(data []byte, dataMode dataMode, encoded *bitset.Bitset) { | |||
modeIndicator := d.modeIndicator(dataMode) | |||
charCountBits := d.charCountBits(dataMode) | |||
// Append mode indicator. | |||
encoded.Append(modeIndicator) | |||
// Append character count. | |||
encoded.AppendUint32(uint32(len(data)), charCountBits) | |||
// Append data. | |||
switch dataMode { | |||
case dataModeNumeric: | |||
for i := 0; i < len(data); i += 3 { | |||
charsRemaining := len(data) - i | |||
var value uint32 | |||
bitsUsed := 1 | |||
for j := 0; j < charsRemaining && j < 3; j++ { | |||
value *= 10 | |||
value += uint32(data[i+j] - 0x30) | |||
bitsUsed += 3 | |||
} | |||
encoded.AppendUint32(value, bitsUsed) | |||
} | |||
case dataModeAlphanumeric: | |||
for i := 0; i < len(data); i += 2 { | |||
charsRemaining := len(data) - i | |||
var value uint32 | |||
for j := 0; j < charsRemaining && j < 2; j++ { | |||
value *= 45 | |||
value += encodeAlphanumericCharacter(data[i+j]) | |||
} | |||
bitsUsed := 6 | |||
if charsRemaining > 1 { | |||
bitsUsed = 11 | |||
} | |||
encoded.AppendUint32(value, bitsUsed) | |||
} | |||
case dataModeByte: | |||
for _, b := range data { | |||
encoded.AppendByte(b, 8) | |||
} | |||
} | |||
} | |||
// modeIndicator returns the segment header bits for a segment of type dataMode. | |||
func (d *dataEncoder) modeIndicator(dataMode dataMode) *bitset.Bitset { | |||
switch dataMode { | |||
case dataModeNumeric: | |||
return d.numericModeIndicator | |||
case dataModeAlphanumeric: | |||
return d.alphanumericModeIndicator | |||
case dataModeByte: | |||
return d.byteModeIndicator | |||
default: | |||
log.Panic("Unknown data mode") | |||
} | |||
return nil | |||
} | |||
// charCountBits returns the number of bits used to encode the length of a data | |||
// segment of type dataMode. | |||
func (d *dataEncoder) charCountBits(dataMode dataMode) int { | |||
switch dataMode { | |||
case dataModeNumeric: | |||
return d.numNumericCharCountBits | |||
case dataModeAlphanumeric: | |||
return d.numAlphanumericCharCountBits | |||
case dataModeByte: | |||
return d.numByteCharCountBits | |||
default: | |||
log.Panic("Unknown data mode") | |||
} | |||
return 0 | |||
} | |||
// encodedLength returns the number of bits required to encode n symbols in | |||
// dataMode. | |||
// | |||
// The number of bits required is affected by: | |||
// - QR code type - Mode Indicator length. | |||
// - Data mode - number of bits used to represent data length. | |||
// - Data mode - how the data is encoded. | |||
// - Number of symbols encoded. | |||
// | |||
// An error is returned if the mode is not supported, or the length requested is | |||
// too long to be represented. | |||
func (d *dataEncoder) encodedLength(dataMode dataMode, n int) (int, error) { | |||
modeIndicator := d.modeIndicator(dataMode) | |||
charCountBits := d.charCountBits(dataMode) | |||
if modeIndicator == nil { | |||
return 0, errors.New("mode not supported") | |||
} | |||
maxLength := (1 << uint8(charCountBits)) - 1 | |||
if n > maxLength { | |||
return 0, errors.New("length too long to be represented") | |||
} | |||
length := modeIndicator.Len() + charCountBits | |||
switch dataMode { | |||
case dataModeNumeric: | |||
length += 10 * (n / 3) | |||
if n%3 != 0 { | |||
length += 1 + 3*(n%3) | |||
} | |||
case dataModeAlphanumeric: | |||
length += 11 * (n / 2) | |||
length += 6 * (n % 2) | |||
case dataModeByte: | |||
length += 8 * n | |||
} | |||
return length, nil | |||
} | |||
// encodeAlphanumericChar returns the QR Code encoded value of v. | |||
// | |||
// v must be a QR Code defined alphanumeric character: 0-9, A-Z, SP, $%*+-./ or | |||
// :. The characters are mapped to values in the range 0-44 respectively. | |||
func encodeAlphanumericCharacter(v byte) uint32 { | |||
c := uint32(v) | |||
switch { | |||
case c >= '0' && c <= '9': | |||
// 0-9 encoded as 0-9. | |||
return c - '0' | |||
case c >= 'A' && c <= 'Z': | |||
// A-Z encoded as 10-35. | |||
return c - 'A' + 10 | |||
case c == ' ': | |||
return 36 | |||
case c == '$': | |||
return 37 | |||
case c == '%': | |||
return 38 | |||
case c == '*': | |||
return 39 | |||
case c == '+': | |||
return 40 | |||
case c == '-': | |||
return 41 | |||
case c == '.': | |||
return 42 | |||
case c == '/': | |||
return 43 | |||
case c == ':': | |||
return 44 | |||
default: | |||
log.Panicf("encodeAlphanumericCharacter() with non alphanumeric char %v.", v) | |||
} | |||
return 0 | |||
} |
@@ -0,0 +1,328 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package qrcode | |||
import ( | |||
"fmt" | |||
"reflect" | |||
"testing" | |||
bitset "github.com/skip2/go-qrcode/bitset" | |||
) | |||
func TestClassifyDataMode(t *testing.T) { | |||
type Test struct { | |||
} | |||
tests := []struct { | |||
data []byte | |||
actual []segment | |||
}{ | |||
{ | |||
[]byte{0x30}, | |||
[]segment{ | |||
{ | |||
dataModeNumeric, | |||
[]byte{0x30}, | |||
}, | |||
}, | |||
}, | |||
{ | |||
[]byte{0x30, 0x41, 0x42, 0x43, 0x20, 0x00, 0xf0, 0xf1, 0xf2, 0x31}, | |||
[]segment{ | |||
{ | |||
dataModeNumeric, | |||
[]byte{0x30}, | |||
}, | |||
{ | |||
dataModeAlphanumeric, | |||
[]byte{0x41, 0x42, 0x43, 0x20}, | |||
}, | |||
{ | |||
dataModeByte, | |||
[]byte{0x00, 0xf0, 0xf1, 0xf2}, | |||
}, | |||
{ | |||
dataModeNumeric, | |||
[]byte{0x31}, | |||
}, | |||
}, | |||
}, | |||
} | |||
for _, test := range tests { | |||
encoder := newDataEncoder(dataEncoderType1To9) | |||
encoder.encode(test.data) | |||
if !reflect.DeepEqual(test.actual, encoder.actual) { | |||
t.Errorf("Got %v, expected %v", encoder.actual, test.actual) | |||
} | |||
} | |||
} | |||
func TestByteModeLengthCalculations(t *testing.T) { | |||
tests := []struct { | |||
dataEncoderType dataEncoderType | |||
dataMode dataMode | |||
numSymbols int | |||
expectedLength int | |||
}{} | |||
for i, test := range tests { | |||
encoder := newDataEncoder(test.dataEncoderType) | |||
var resultLength int | |||
resultLength, err := encoder.encodedLength(test.dataMode, test.numSymbols) | |||
if test.expectedLength == -1 { | |||
if err == nil { | |||
t.Errorf("Test %d: got length %d, expected error", i, resultLength) | |||
} | |||
} else if resultLength != test.expectedLength { | |||
t.Errorf("Test %d: got length %d, expected length %d", i, resultLength, | |||
test.expectedLength) | |||
} | |||
} | |||
} | |||
func TestSingleModeEncodings(t *testing.T) { | |||
tests := []struct { | |||
dataEncoderType dataEncoderType | |||
dataMode dataMode | |||
data string | |||
expected *bitset.Bitset | |||
}{ | |||
{ | |||
dataEncoderType1To9, | |||
dataModeNumeric, | |||
"01234567", | |||
bitset.NewFromBase2String("0001 0000001000 0000001100 0101011001 1000011"), | |||
}, | |||
{ | |||
dataEncoderType1To9, | |||
dataModeAlphanumeric, | |||
"AC-42", | |||
bitset.NewFromBase2String("0010 000000101 00111001110 11100111001 000010"), | |||
}, | |||
{ | |||
dataEncoderType1To9, | |||
dataModeByte, | |||
"123", | |||
bitset.NewFromBase2String("0100 00000011 00110001 00110010 00110011"), | |||
}, | |||
{ | |||
dataEncoderType10To26, | |||
dataModeByte, | |||
"123", | |||
bitset.NewFromBase2String("0100 00000000 00000011 00110001 00110010 00110011"), | |||
}, | |||
{ | |||
dataEncoderType27To40, | |||
dataModeByte, | |||
"123", | |||
bitset.NewFromBase2String("0100 00000000 00000011 00110001 00110010 00110011"), | |||
}, | |||
} | |||
for _, test := range tests { | |||
encoder := newDataEncoder(test.dataEncoderType) | |||
encoded := bitset.New() | |||
encoder.encodeDataRaw([]byte(test.data), test.dataMode, encoded) | |||
if !test.expected.Equals(encoded) { | |||
t.Errorf("For %s got %s, expected %s", test.data, encoded.String(), | |||
test.expected.String()) | |||
} | |||
} | |||
} | |||
type testModeSegment struct { | |||
dataMode dataMode | |||
numChars int | |||
} | |||
func TestOptimiseEncoding(t *testing.T) { | |||
tests := []struct { | |||
dataEncoderType dataEncoderType | |||
actual []testModeSegment | |||
optimised []testModeSegment | |||
}{ | |||
// Coalescing multiple segments. | |||
{ | |||
dataEncoderType1To9, | |||
[]testModeSegment{ | |||
{dataModeAlphanumeric, 1}, // length = 4 + 9 + 6 = 19 bits | |||
{dataModeNumeric, 1}, // length = 4 + 10 + 4 = 18 bits | |||
{dataModeAlphanumeric, 1}, // 19 bits. | |||
{dataModeNumeric, 1}, // 18 bits. | |||
{dataModeAlphanumeric, 1}, // 19 bits. | |||
// total = 93 bits. | |||
}, | |||
[]testModeSegment{ | |||
{dataModeAlphanumeric, 5}, // length = 4 + 9 + 22 + 6 = 41. | |||
}, | |||
}, | |||
// Coalesing not necessary. | |||
{ | |||
dataEncoderType1To9, | |||
[]testModeSegment{ | |||
{dataModeAlphanumeric, 1}, | |||
{dataModeNumeric, 20}, | |||
}, | |||
[]testModeSegment{ | |||
{dataModeAlphanumeric, 1}, | |||
{dataModeNumeric, 20}, | |||
}, | |||
}, | |||
// Switch to more general dataMode. | |||
{ | |||
dataEncoderType1To9, | |||
[]testModeSegment{ | |||
{dataModeAlphanumeric, 1}, | |||
{dataModeByte, 1}, | |||
{dataModeNumeric, 1}, | |||
}, | |||
[]testModeSegment{ | |||
{dataModeAlphanumeric, 1}, | |||
{dataModeByte, 2}, | |||
}, | |||
}, | |||
// https://www.google.com/123 | |||
// BBBBBAAABBBABBBBBBABBBANNN | |||
{ | |||
dataEncoderType1To9, | |||
[]testModeSegment{ | |||
{dataModeByte, 5}, | |||
{dataModeAlphanumeric, 3}, | |||
{dataModeByte, 3}, | |||
{dataModeAlphanumeric, 1}, | |||
{dataModeByte, 6}, | |||
{dataModeAlphanumeric, 1}, | |||
{dataModeAlphanumeric, 4}, | |||
{dataModeNumeric, 3}, | |||
}, | |||
[]testModeSegment{ | |||
{dataModeByte, 23}, | |||
{dataModeNumeric, 3}, | |||
}, | |||
}, | |||
// HTTPS://WWW.GOOGLE.COM/123 | |||
// AAAAAAAAAAAAAAAAAAAAAAANNN | |||
{ | |||
dataEncoderType1To9, | |||
[]testModeSegment{ | |||
{dataModeAlphanumeric, 23}, | |||
{dataModeNumeric, 3}, | |||
}, | |||
[]testModeSegment{ | |||
{dataModeAlphanumeric, 26}, | |||
}, | |||
}, | |||
{ | |||
dataEncoderType27To40, | |||
[]testModeSegment{ | |||
{dataModeByte, 1}, | |||
{dataModeNumeric, 1}, | |||
{dataModeByte, 1}, | |||
{dataModeNumeric, 1}, | |||
{dataModeByte, 1}, | |||
{dataModeNumeric, 1}, | |||
{dataModeByte, 1}, | |||
{dataModeNumeric, 1}, | |||
}, | |||
[]testModeSegment{ | |||
{dataModeByte, 8}, | |||
}, | |||
}, | |||
} | |||
for _, test := range tests { | |||
numTotalChars := 0 | |||
for _, v := range test.actual { | |||
numTotalChars += v.numChars | |||
} | |||
data := make([]byte, numTotalChars) | |||
i := 0 | |||
for _, v := range test.actual { | |||
for j := 0; j < v.numChars; j++ { | |||
switch v.dataMode { | |||
case dataModeNumeric: | |||
data[i] = '1' | |||
case dataModeAlphanumeric: | |||
data[i] = 'A' | |||
case dataModeByte: | |||
data[i] = '#' | |||
default: | |||
t.Fatal("Unrecognised data mode") | |||
} | |||
i++ | |||
} | |||
} | |||
encoder := newDataEncoder(test.dataEncoderType) | |||
_, err := encoder.encode(data) | |||
if err != nil { | |||
t.Errorf("Got %s, expected valid encoding", err.Error()) | |||
} else { | |||
ok := true | |||
if len(encoder.optimised) != len(test.optimised) { | |||
ok = false | |||
} else { | |||
for i, s := range test.optimised { | |||
if encoder.optimised[i].dataMode != s.dataMode || | |||
len(encoder.optimised[i].data) != s.numChars { | |||
ok = false | |||
break | |||
} | |||
} | |||
} | |||
if !ok { | |||
t.Errorf("got %s, expected %s", segmentsString(encoder.optimised), | |||
testModeSegmentsString(test.optimised)) | |||
} | |||
} | |||
} | |||
} | |||
func testModeSegmentsString(segments []testModeSegment) string { | |||
result := "[" | |||
for i, segment := range segments { | |||
if i > 0 { | |||
result += ", " | |||
} | |||
result += fmt.Sprintf("%d*%s", segment.numChars, | |||
dataModeString(segment.dataMode)) | |||
} | |||
result += "]" | |||
return result | |||
} | |||
func segmentsString(segments []segment) string { | |||
result := "[" | |||
for i, segment := range segments { | |||
if i > 0 { | |||
result += ", " | |||
} | |||
result += fmt.Sprintf("%d*%s", len(segment.data), | |||
dataModeString(segment.dataMode)) | |||
} | |||
result += "]" | |||
return result | |||
} |
@@ -0,0 +1,31 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
/* | |||
Amendments Thu, 2017-December-14: | |||
- test integration (go test -v) | |||
- idiomatic go code | |||
*/ | |||
package qrcode | |||
import ( | |||
"fmt" | |||
"os" | |||
"testing" | |||
) | |||
func TestExampleEncode(t *testing.T) { | |||
if png, err := Encode("https://example.org", Medium, 256); err != nil { | |||
t.Errorf("Error: %s", err.Error()) | |||
} else { | |||
fmt.Printf("PNG is %d bytes long", len(png)) | |||
} | |||
} | |||
func TestExampleWriteFile(t *testing.T) { | |||
filename := "example.png" | |||
if err := WriteFile("https://example.org", Medium, 256, filename); err != nil { | |||
if err = os.Remove(filename); err != nil { | |||
t.Errorf("Error: %s", err.Error()) | |||
} | |||
} | |||
} |
@@ -0,0 +1,554 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
/* | |||
Package qrcode implements a QR Code encoder. | |||
A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be | |||
encoded. | |||
A QR Code contains error recovery information to aid reading damaged or | |||
obscured codes. There are four levels of error recovery: qrcode.{Low, Medium, | |||
High, Highest}. QR Codes with a higher recovery level are more robust to damage, | |||
at the cost of being physically larger. | |||
Three functions cover most use cases: | |||
- Create a PNG image: | |||
var png []byte | |||
png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256) | |||
- Create a PNG image and write to a file: | |||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png") | |||
- Create a PNG image with custom colors and write to file: | |||
err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png") | |||
All examples use the qrcode.Medium error Recovery Level and create a fixed | |||
256x256px size QR Code. The last function creates a white on black instead of black | |||
on white QR Code. | |||
To generate a variable sized image instead, specify a negative size (in place of | |||
the 256 above), such as -4 or -5. Larger negative numbers create larger images: | |||
A size of -5 sets each module (QR Code "pixel") to be 5px wide/high. | |||
- Create a PNG image (variable size, with minimum white padding) and write to a file: | |||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, -5, "qr.png") | |||
The maximum capacity of a QR Code varies according to the content encoded and | |||
the error recovery level. The maximum capacity is 2,953 bytes, 4,296 | |||
alphanumeric characters, 7,089 numeric digits, or a combination of these. | |||
This package implements a subset of QR Code 2005, as defined in ISO/IEC | |||
18004:2006. | |||
*/ | |||
package qrcode | |||
import ( | |||
"bytes" | |||
"errors" | |||
"image" | |||
"image/color" | |||
"image/png" | |||
"io" | |||
"io/ioutil" | |||
"log" | |||
"os" | |||
bitset "github.com/skip2/go-qrcode/bitset" | |||
reedsolomon "github.com/skip2/go-qrcode/reedsolomon" | |||
) | |||
// Encode a QR Code and return a raw PNG image. | |||
// | |||
// size is both the image width and height in pixels. If size is too small then | |||
// a larger image is silently returned. Negative values for size cause a | |||
// variable sized image to be returned: See the documentation for Image(). | |||
// | |||
// To serve over HTTP, remember to send a Content-Type: image/png header. | |||
func Encode(content string, level RecoveryLevel, size int) ([]byte, error) { | |||
var q *QRCode | |||
q, err := New(content, level) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return q.PNG(size) | |||
} | |||
// WriteFile encodes, then writes a QR Code to the given filename in PNG format. | |||
// | |||
// size is both the image width and height in pixels. If size is too small then | |||
// a larger image is silently written. Negative values for size cause a variable | |||
// sized image to be written: See the documentation for Image(). | |||
func WriteFile(content string, level RecoveryLevel, size int, filename string) error { | |||
var q *QRCode | |||
q, err := New(content, level) | |||
if err != nil { | |||
return err | |||
} | |||
return q.WriteFile(size, filename) | |||
} | |||
// WriteColorFile encodes, then writes a QR Code to the given filename in PNG format. | |||
// With WriteColorFile you can also specify the colors you want to use. | |||
// | |||
// size is both the image width and height in pixels. If size is too small then | |||
// a larger image is silently written. Negative values for size cause a variable | |||
// sized image to be written: See the documentation for Image(). | |||
func WriteColorFile(content string, level RecoveryLevel, size int, background, | |||
foreground color.Color, filename string) error { | |||
var q *QRCode | |||
q, err := New(content, level) | |||
q.BackgroundColor = background | |||
q.ForegroundColor = foreground | |||
if err != nil { | |||
return err | |||
} | |||
return q.WriteFile(size, filename) | |||
} | |||
// A QRCode represents a valid encoded QRCode. | |||
type QRCode struct { | |||
// Original content encoded. | |||
Content string | |||
// QR Code type. | |||
Level RecoveryLevel | |||
VersionNumber int | |||
// User settable drawing options. | |||
ForegroundColor color.Color | |||
BackgroundColor color.Color | |||
encoder *dataEncoder | |||
version qrCodeVersion | |||
data *bitset.Bitset | |||
symbol *symbol | |||
mask int | |||
} | |||
// New constructs a QRCode. | |||
// | |||
// var q *qrcode.QRCode | |||
// q, err := qrcode.New("my content", qrcode.Medium) | |||
// | |||
// An error occurs if the content is too long. | |||
func New(content string, level RecoveryLevel) (*QRCode, error) { | |||
encoders := []dataEncoderType{dataEncoderType1To9, dataEncoderType10To26, | |||
dataEncoderType27To40} | |||
var encoder *dataEncoder | |||
var encoded *bitset.Bitset | |||
var chosenVersion *qrCodeVersion | |||
var err error | |||
for _, t := range encoders { | |||
encoder = newDataEncoder(t) | |||
encoded, err = encoder.encode([]byte(content)) | |||
if err != nil { | |||
continue | |||
} | |||
chosenVersion = chooseQRCodeVersion(level, encoder, encoded.Len()) | |||
if chosenVersion != nil { | |||
break | |||
} | |||
} | |||
if err != nil { | |||
return nil, err | |||
} else if chosenVersion == nil { | |||
return nil, errors.New("content too long to encode") | |||
} | |||
q := &QRCode{ | |||
Content: content, | |||
Level: level, | |||
VersionNumber: chosenVersion.version, | |||
ForegroundColor: color.Black, | |||
BackgroundColor: color.White, | |||
encoder: encoder, | |||
data: encoded, | |||
version: *chosenVersion, | |||
} | |||
q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len())) | |||
return q, nil | |||
} | |||
func newWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) { | |||
var encoder *dataEncoder | |||
switch { | |||
case version >= 1 && version <= 9: | |||
encoder = newDataEncoder(dataEncoderType1To9) | |||
case version >= 10 && version <= 26: | |||
encoder = newDataEncoder(dataEncoderType10To26) | |||
case version >= 27 && version <= 40: | |||
encoder = newDataEncoder(dataEncoderType27To40) | |||
default: | |||
log.Fatalf("Invalid version %d (expected 1-40 inclusive)", version) | |||
} | |||
var encoded *bitset.Bitset | |||
encoded, err := encoder.encode([]byte(content)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
chosenVersion := getQRCodeVersion(level, version) | |||
if chosenVersion == nil { | |||
return nil, errors.New("cannot find QR Code version") | |||
} | |||
q := &QRCode{ | |||
Content: content, | |||
Level: level, | |||
VersionNumber: chosenVersion.version, | |||
ForegroundColor: color.Black, | |||
BackgroundColor: color.White, | |||
encoder: encoder, | |||
data: encoded, | |||
version: *chosenVersion, | |||
} | |||
q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len())) | |||
return q, nil | |||
} | |||
// Bitmap returns the QR Code as a 2D array of 1-bit pixels. | |||
// | |||
// bitmap[y][x] is true if the pixel at (x, y) is set. | |||
// | |||
// The bitmap includes the required "quiet zone" around the QR Code to aid | |||
// decoding. | |||
func (q *QRCode) Bitmap() [][]bool { | |||
return q.symbol.bitmap() | |||
} | |||
// Image returns the QR Code as an image.Image. | |||
// | |||
// A positive size sets a fixed image width and height (e.g. 256 yields an | |||
// 256x256px image). | |||
// | |||
// Depending on the amount of data encoded, fixed size images can have different | |||
// amounts of padding (white space around the QR Code). As an alternative, a | |||
// variable sized image can be generated instead: | |||
// | |||
// A negative size causes a variable sized image to be returned. The image | |||
// returned is the minimum size required for the QR Code. Choose a larger | |||
// negative number to increase the scale of the image. e.g. a size of -5 causes | |||
// each module (QR Code "pixel") to be 5px in size. | |||
func (q *QRCode) Image(size int) image.Image { | |||
// Minimum pixels (both width and height) required. | |||
realSize := q.symbol.size | |||
// Variable size support. | |||
if size < 0 { | |||
size = size * -1 * realSize | |||
} | |||
// Actual pixels available to draw the symbol. Automatically increase the | |||
// image size if it's not large enough. | |||
if size < realSize { | |||
size = realSize | |||
} | |||
// Size of each module drawn. | |||
pixelsPerModule := size / realSize | |||
// Center the symbol within the image. | |||
offset := (size - realSize*pixelsPerModule) / 2 | |||
rect := image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{size, size}} | |||
// Saves a few bytes to have them in this order | |||
p := color.Palette([]color.Color{q.BackgroundColor, q.ForegroundColor}) | |||
img := image.NewPaletted(rect, p) | |||
for i := 0; i < size; i++ { | |||
for j := 0; j < size; j++ { | |||
img.Set(i, j, q.BackgroundColor) | |||
} | |||
} | |||
bitmap := q.symbol.bitmap() | |||
for y, row := range bitmap { | |||
for x, v := range row { | |||
if v { | |||
startX := x*pixelsPerModule + offset | |||
startY := y*pixelsPerModule + offset | |||
for i := startX; i < startX+pixelsPerModule; i++ { | |||
for j := startY; j < startY+pixelsPerModule; j++ { | |||
img.Set(i, j, q.ForegroundColor) | |||
} | |||
} | |||
} | |||
} | |||
} | |||
return img | |||
} | |||
// PNG returns the QR Code as a PNG image. | |||
// | |||
// size is both the image width and height in pixels. If size is too small then | |||
// a larger image is silently returned. Negative values for size cause a | |||
// variable sized image to be returned: See the documentation for Image(). | |||
func (q *QRCode) PNG(size int) ([]byte, error) { | |||
img := q.Image(size) | |||
encoder := png.Encoder{CompressionLevel: png.BestCompression} | |||
var b bytes.Buffer | |||
err := encoder.Encode(&b, img) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return b.Bytes(), nil | |||
} | |||
// Write writes the QR Code as a PNG image to io.Writer. | |||
// | |||
// size is both the image width and height in pixels. If size is too small then | |||
// a larger image is silently written. Negative values for size cause a | |||
// variable sized image to be written: See the documentation for Image(). | |||
func (q *QRCode) Write(size int, out io.Writer) error { | |||
var png []byte | |||
png, err := q.PNG(size) | |||
if err != nil { | |||
return err | |||
} | |||
_, err = out.Write(png) | |||
return err | |||
} | |||
// WriteFile writes the QR Code as a PNG image to the specified file. | |||
// | |||
// size is both the image width and height in pixels. If size is too small then | |||
// a larger image is silently written. Negative values for size cause a | |||
// variable sized image to be written: See the documentation for Image(). | |||
func (q *QRCode) WriteFile(size int, filename string) error { | |||
var png []byte | |||
png, err := q.PNG(size) | |||
if err != nil { | |||
return err | |||
} | |||
return ioutil.WriteFile(filename, png, os.FileMode(0644)) | |||
} | |||
// encode completes the steps required to encode the QR Code. These include | |||
// adding the terminator bits and padding, splitting the data into blocks and | |||
// applying the error correction, and selecting the best data mask. | |||
func (q *QRCode) encode(numTerminatorBits int) { | |||
q.addTerminatorBits(numTerminatorBits) | |||
q.addPadding() | |||
encoded := q.encodeBlocks() | |||
const numMasks int = 8 | |||
penalty := 0 | |||
for mask := 0; mask < numMasks; mask++ { | |||
var s *symbol | |||
var err error | |||
s, err = buildRegularSymbol(q.version, mask, encoded) | |||
if err != nil { | |||
log.Panic(err.Error()) | |||
} | |||
numEmptyModules := s.numEmptyModules() | |||
if numEmptyModules != 0 { | |||
log.Panicf("bug: numEmptyModules is %d (expected 0) (version=%d)", | |||
numEmptyModules, q.VersionNumber) | |||
} | |||
p := s.penaltyScore() | |||
//log.Printf("mask=%d p=%3d p1=%3d p2=%3d p3=%3d p4=%d\n", mask, p, s.penalty1(), s.penalty2(), s.penalty3(), s.penalty4()) | |||
if q.symbol == nil || p < penalty { | |||
q.symbol = s | |||
q.mask = mask | |||
penalty = p | |||
} | |||
} | |||
} | |||
// addTerminatorBits adds final terminator bits to the encoded data. | |||
// | |||
// The number of terminator bits required is determined when the QR Code version | |||
// is chosen (which itself depends on the length of the data encoded). The | |||
// terminator bits are thus added after the QR Code version | |||
// is chosen, rather than at the data encoding stage. | |||
func (q *QRCode) addTerminatorBits(numTerminatorBits int) { | |||
q.data.AppendNumBools(numTerminatorBits, false) | |||
} | |||
// encodeBlocks takes the completed (terminated & padded) encoded data, splits | |||
// the data into blocks (as specified by the QR Code version), applies error | |||
// correction to each block, then interleaves the blocks together. | |||
// | |||
// The QR Code's final data sequence is returned. | |||
func (q *QRCode) encodeBlocks() *bitset.Bitset { | |||
// Split into blocks. | |||
type dataBlock struct { | |||
data *bitset.Bitset | |||
ecStartOffset int | |||
} | |||
block := make([]dataBlock, q.version.numBlocks()) | |||
start := 0 | |||
end := 0 | |||
blockID := 0 | |||
for _, b := range q.version.block { | |||
for j := 0; j < b.numBlocks; j++ { | |||
start = end | |||
end = start + b.numDataCodewords*8 | |||
// Apply error correction to each block. | |||
numErrorCodewords := b.numCodewords - b.numDataCodewords | |||
block[blockID].data = reedsolomon.Encode(q.data.Substr(start, end), numErrorCodewords) | |||
block[blockID].ecStartOffset = end - start | |||
blockID++ | |||
} | |||
} | |||
// Interleave the blocks. | |||
result := bitset.New() | |||
// Combine data blocks. | |||
working := true | |||
for i := 0; working; i += 8 { | |||
working = false | |||
for j, b := range block { | |||
if i >= block[j].ecStartOffset { | |||
continue | |||
} | |||
result.Append(b.data.Substr(i, i+8)) | |||
working = true | |||
} | |||
} | |||
// Combine error correction blocks. | |||
working = true | |||
for i := 0; working; i += 8 { | |||
working = false | |||
for j, b := range block { | |||
offset := i + block[j].ecStartOffset | |||
if offset >= block[j].data.Len() { | |||
continue | |||
} | |||
result.Append(b.data.Substr(offset, offset+8)) | |||
working = true | |||
} | |||
} | |||
// Append remainder bits. | |||
result.AppendNumBools(q.version.numRemainderBits, false) | |||
return result | |||
} | |||
// max returns the maximum of a and b. | |||
func max(a int, b int) int { | |||
if a > b { | |||
return a | |||
} | |||
return b | |||
} | |||
// addPadding pads the encoded data upto the full length required. | |||
func (q *QRCode) addPadding() { | |||
numDataBits := q.version.numDataBits() | |||
if q.data.Len() == numDataBits { | |||
return | |||
} | |||
// Pad to the nearest codeword boundary. | |||
q.data.AppendNumBools(q.version.numBitsToPadToCodeword(q.data.Len()), false) | |||
// Pad codewords 0b11101100 and 0b00010001. | |||
padding := [2]*bitset.Bitset{ | |||
bitset.New(true, true, true, false, true, true, false, false), | |||
bitset.New(false, false, false, true, false, false, false, true), | |||
} | |||
// Insert pad codewords alternately. | |||
i := 0 | |||
for numDataBits-q.data.Len() >= 8 { | |||
q.data.Append(padding[i]) | |||
i = 1 - i // Alternate between 0 and 1. | |||
} | |||
if q.data.Len() != numDataBits { | |||
log.Panicf("BUG: got len %d, expected %d", q.data.Len(), numDataBits) | |||
} | |||
} | |||
// ToString produces a multi-line string that forms a QR-code image. | |||
func (q *QRCode) ToString(inverseColor bool) string { | |||
bits := q.Bitmap() | |||
var buf bytes.Buffer | |||
for y := range bits { | |||
for x := range bits[y] { | |||
if bits[y][x] != inverseColor { | |||
buf.WriteString(" ") | |||
} else { | |||
buf.WriteString("██") | |||
} | |||
} | |||
buf.WriteString("\n") | |||
} | |||
return buf.String() | |||
} |
@@ -0,0 +1,85 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package main | |||
import ( | |||
"flag" | |||
"fmt" | |||
"os" | |||
"strings" | |||
qrcode "github.com/skip2/go-qrcode" | |||
) | |||
func main() { | |||
outFile := flag.String("o", "", "out PNG file prefix, empty for stdout") | |||
size := flag.Int("s", 256, "image size (pixel)") | |||
textArt := flag.Bool("t", false, "print as text-art on stdout") | |||
negative := flag.Bool("i", false, "invert black and white") | |||
flag.Usage = func() { | |||
fmt.Fprintf(os.Stderr, `qrcode -- QR Code encoder in Go | |||
https://github.com/skip2/go-qrcode | |||
Flags: | |||
`) | |||
flag.PrintDefaults() | |||
fmt.Fprintf(os.Stderr, ` | |||
Usage: | |||
1. Arguments except for flags are joined by " " and used to generate QR code. | |||
Default output is STDOUT, pipe to imagemagick command "display" to display | |||
on any X server. | |||
qrcode hello word | display | |||
2. Save to file if "display" not available: | |||
qrcode "homepage: https://github.com/skip2/go-qrcode" > out.png | |||
`) | |||
} | |||
flag.Parse() | |||
if len(flag.Args()) == 0 { | |||
flag.Usage() | |||
checkError(fmt.Errorf("Error: no content given")) | |||
} | |||
content := strings.Join(flag.Args(), " ") | |||
var err error | |||
var q *qrcode.QRCode | |||
q, err = qrcode.New(content, qrcode.Highest) | |||
checkError(err) | |||
if *textArt { | |||
art := q.ToString(*negative) | |||
fmt.Println(art) | |||
return | |||
} | |||
if *negative { | |||
q.ForegroundColor, q.BackgroundColor = q.BackgroundColor, q.ForegroundColor | |||
} | |||
var png []byte | |||
png, err = q.PNG(*size) | |||
checkError(err) | |||
if *outFile == "" { | |||
os.Stdout.Write(png) | |||
} else { | |||
var fh *os.File | |||
fh, err = os.Create(*outFile + ".png") | |||
checkError(err) | |||
defer fh.Close() | |||
fh.Write(png) | |||
} | |||
} | |||
func checkError(err error) { | |||
if err != nil { | |||
fmt.Fprintf(os.Stderr, "%s\n", err) | |||
os.Exit(1) | |||
} | |||
} |
@@ -0,0 +1,232 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package qrcode | |||
import ( | |||
"bytes" | |||
"flag" | |||
"fmt" | |||
"math/rand" | |||
"os/exec" | |||
"strings" | |||
"testing" | |||
) | |||
// These tests use zbarimg to decode generated QR Codes to ensure they are | |||
// readable. sudo apt-get install zbar-tools, or download from | |||
// http://zbar.sourceforge.net. | |||
// | |||
// By default these tests are disabled to avoid a dependency on zbarimg if | |||
// you're not running the tests. Use the -test-decode flag (go test | |||
// -test-decode) to enable. | |||
var testDecode *bool = flag.Bool("test-decode", | |||
false, | |||
"Enable decode tests. Requires zbarimg installed.") | |||
var testDecodeFuzz *bool = flag.Bool("test-decode-fuzz", | |||
false, | |||
"Enable decode fuzz tests. Requires zbarimg installed.") | |||
func TestDecodeBasic(t *testing.T) { | |||
if !*testDecode { | |||
t.Skip("Decode tests not enabled") | |||
} | |||
tests := []struct { | |||
content string | |||
numRepetitions int | |||
level RecoveryLevel | |||
}{ | |||
{ | |||
"A", | |||
1, | |||
Low, | |||
}, | |||
{ | |||
"A", | |||
1, | |||
Medium, | |||
}, | |||
{ | |||
"A", | |||
1, | |||
High, | |||
}, | |||
{ | |||
"A", | |||
1, | |||
Highest, | |||
}, | |||
{ | |||
"01234567", | |||
1, | |||
Medium, | |||
}, | |||
} | |||
for _, test := range tests { | |||
content := strings.Repeat(test.content, test.numRepetitions) | |||
q, err := New(content, test.level) | |||
if err != nil { | |||
t.Error(err.Error()) | |||
} | |||
err = zbarimgCheck(q) | |||
if err != nil { | |||
t.Error(err.Error()) | |||
} | |||
} | |||
} | |||
func TestDecodeAllVersionLevels(t *testing.T) { | |||
if !*testDecode { | |||
t.Skip("Decode tests not enabled") | |||
} | |||
for version := 1; version <= 40; version++ { | |||
for _, level := range []RecoveryLevel{Low, Medium, High, Highest} { | |||
t.Logf("Version=%d Level=%d", | |||
version, | |||
level) | |||
q, err := newWithForcedVersion( | |||
fmt.Sprintf("v-%d l-%d", version, level), version, level) | |||
if err != nil { | |||
t.Fatal(err.Error()) | |||
return | |||
} | |||
err = zbarimgCheck(q) | |||
if err != nil { | |||
t.Errorf("Version=%d Level=%d, err=%s, expected success", | |||
version, | |||
level, | |||
err.Error()) | |||
continue | |||
} | |||
} | |||
} | |||
} | |||
func TestDecodeAllCharacters(t *testing.T) { | |||
if !*testDecode { | |||
t.Skip("Decode tests not enabled") | |||
} | |||
var content string | |||
// zbarimg has trouble with null bytes, hence start from ASCII 1. | |||
for i := 1; i < 256; i++ { | |||
content += string(i) | |||
} | |||
q, err := New(content, Low) | |||
if err != nil { | |||
t.Error(err.Error()) | |||
} | |||
err = zbarimgCheck(q) | |||
if err != nil { | |||
t.Error(err.Error()) | |||
} | |||
} | |||
func TestDecodeFuzz(t *testing.T) { | |||
if !*testDecodeFuzz { | |||
t.Skip("Decode fuzz tests not enabled") | |||
} | |||
r := rand.New(rand.NewSource(0)) | |||
const iterations int = 32 | |||
const maxLength int = 128 | |||
for i := 0; i < iterations; i++ { | |||
len := r.Intn(maxLength-1) + 1 | |||
var content string | |||
for j := 0; j < len; j++ { | |||
// zbarimg seems to have trouble with special characters, test printable | |||
// characters only for now. | |||
content += string(32 + r.Intn(94)) | |||
} | |||
for _, level := range []RecoveryLevel{Low, Medium, High, Highest} { | |||
q, err := New(content, level) | |||
if err != nil { | |||
t.Error(err.Error()) | |||
} | |||
err = zbarimgCheck(q) | |||
if err != nil { | |||
t.Error(err.Error()) | |||
} | |||
} | |||
} | |||
} | |||
func zbarimgCheck(q *QRCode) error { | |||
s, err := zbarimgDecode(q) | |||
if err != nil { | |||
return err | |||
} | |||
if s != q.Content { | |||
q.WriteFile(256, fmt.Sprintf("%x.png", q.Content)) | |||
return fmt.Errorf("got '%s' (%x) expected '%s' (%x)", s, s, q.Content, q.Content) | |||
} | |||
return nil | |||
} | |||
func zbarimgDecode(q *QRCode) (string, error) { | |||
var png []byte | |||
// 512x512px | |||
png, err := q.PNG(512) | |||
if err != nil { | |||
return "", err | |||
} | |||
cmd := exec.Command("zbarimg", "--quiet", "-Sdisable", | |||
"-Sqrcode.enable", "/dev/stdin") | |||
var out bytes.Buffer | |||
cmd.Stdin = bytes.NewBuffer(png) | |||
cmd.Stdout = &out | |||
err = cmd.Run() | |||
if err != nil { | |||
return "", err | |||
} | |||
return strings.TrimSuffix(strings.TrimPrefix(out.String(), "QR-Code:"), "\n"), nil | |||
} | |||
func BenchmarkDecodeTest(b *testing.B) { | |||
if !*testDecode { | |||
b.Skip("Decode benchmarks not enabled") | |||
} | |||
for n := 0; n < b.N; n++ { | |||
q, err := New("content", Medium) | |||
if err != nil { | |||
b.Error(err.Error()) | |||
} | |||
err = zbarimgCheck(q) | |||
if err != nil { | |||
b.Error(err.Error()) | |||
} | |||
} | |||
} |
@@ -0,0 +1,173 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package qrcode | |||
import ( | |||
"strings" | |||
"testing" | |||
) | |||
func TestQRCodeMaxCapacity(t *testing.T) { | |||
if testing.Short() { | |||
t.Skip("Skipping TestQRCodeCapacity") | |||
} | |||
tests := []struct { | |||
string string | |||
numRepetitions int | |||
}{ | |||
{ | |||
"0", | |||
7089, | |||
}, | |||
{ | |||
"A", | |||
4296, | |||
}, | |||
{ | |||
"#", | |||
2953, | |||
}, | |||
// Alternate byte/numeric data types. Optimises to 2,952 bytes. | |||
{ | |||
"#1", | |||
1476, | |||
}, | |||
} | |||
for _, test := range tests { | |||
_, err := New(strings.Repeat(test.string, test.numRepetitions), Low) | |||
if err != nil { | |||
t.Errorf("%d x '%s' got %s expected success", test.numRepetitions, | |||
test.string, err.Error()) | |||
} | |||
} | |||
for _, test := range tests { | |||
_, err := New(strings.Repeat(test.string, test.numRepetitions+1), Low) | |||
if err == nil { | |||
t.Errorf("%d x '%s' chars encodable, expected not encodable", | |||
test.numRepetitions+1, test.string) | |||
} | |||
} | |||
} | |||
func TestQRCodeVersionCapacity(t *testing.T) { | |||
tests := []struct { | |||
version int | |||
level RecoveryLevel | |||
maxNumeric int | |||
maxAlphanumeric int | |||
maxByte int | |||
}{ | |||
{ | |||
1, | |||
Low, | |||
41, | |||
25, | |||
17, | |||
}, | |||
{ | |||
2, | |||
Low, | |||
77, | |||
47, | |||
32, | |||
}, | |||
{ | |||
2, | |||
Highest, | |||
34, | |||
20, | |||
14, | |||
}, | |||
{ | |||
40, | |||
Low, | |||
7089, | |||
4296, | |||
2953, | |||
}, | |||
{ | |||
40, | |||
Highest, | |||
3057, | |||
1852, | |||
1273, | |||
}, | |||
} | |||
for i, test := range tests { | |||
numericData := strings.Repeat("1", test.maxNumeric) | |||
alphanumericData := strings.Repeat("A", test.maxAlphanumeric) | |||
byteData := strings.Repeat("#", test.maxByte) | |||
var n *QRCode | |||
var a *QRCode | |||
var b *QRCode | |||
var err error | |||
n, err = New(numericData, test.level) | |||
if err != nil { | |||
t.Fatal(err.Error()) | |||
} | |||
a, err = New(alphanumericData, test.level) | |||
if err != nil { | |||
t.Fatal(err.Error()) | |||
} | |||
b, err = New(byteData, test.level) | |||
if err != nil { | |||
t.Fatal(err.Error()) | |||
} | |||
if n.VersionNumber != test.version { | |||
t.Fatalf("Test #%d numeric has version #%d, expected #%d", i, | |||
n.VersionNumber, test.version) | |||
} | |||
if a.VersionNumber != test.version { | |||
t.Fatalf("Test #%d alphanumeric has version #%d, expected #%d", i, | |||
a.VersionNumber, test.version) | |||
} | |||
if b.VersionNumber != test.version { | |||
t.Fatalf("Test #%d byte has version #%d, expected #%d", i, | |||
b.VersionNumber, test.version) | |||
} | |||
} | |||
} | |||
func TestQRCodeISOAnnexIExample(t *testing.T) { | |||
var q *QRCode | |||
q, err := New("01234567", Medium) | |||
if err != nil { | |||
t.Fatalf("Error producing ISO Annex I Example: %s, expected success", | |||
err.Error()) | |||
} | |||
const expectedMask int = 2 | |||
if q.mask != 2 { | |||
t.Errorf("ISO Annex I example mask got %d, expected %d\n", q.mask, | |||
expectedMask) | |||
} | |||
} | |||
func BenchmarkQRCodeURLSize(b *testing.B) { | |||
for n := 0; n < b.N; n++ { | |||
New("http://www.example.org", Medium) | |||
} | |||
} | |||
func BenchmarkQRCodeMaximumSize(b *testing.B) { | |||
for n := 0; n < b.N; n++ { | |||
// 7089 is the maximum encodable number of numeric digits. | |||
New(strings.Repeat("0", 7089), Low) | |||
} | |||
} |
@@ -0,0 +1,387 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package reedsolomon | |||
// Addition, subtraction, multiplication, and division in GF(2^8). | |||
// Operations are performed modulo x^8 + x^4 + x^3 + x^2 + 1. | |||
// http://en.wikipedia.org/wiki/Finite_field_arithmetic | |||
import "log" | |||
const ( | |||
gfZero = gfElement(0) | |||
gfOne = gfElement(1) | |||
) | |||
var ( | |||
gfExpTable = [256]gfElement{ | |||
/* 0 - 9 */ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, | |||
/* 10 - 19 */ 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, | |||
/* 20 - 29 */ 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, | |||
/* 30 - 39 */ 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, | |||
/* 40 - 49 */ 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, | |||
/* 50 - 59 */ 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, | |||
/* 60 - 69 */ 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, | |||
/* 70 - 79 */ 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, | |||
/* 80 - 89 */ 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, | |||
/* 90 - 99 */ 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, | |||
/* 100 - 109 */ 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, | |||
/* 110 - 119 */ 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, | |||
/* 120 - 129 */ 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, | |||
/* 130 - 139 */ 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, | |||
/* 140 - 149 */ 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, | |||
/* 150 - 159 */ 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, | |||
/* 160 - 169 */ 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, | |||
/* 170 - 179 */ 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, | |||
/* 180 - 189 */ 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, | |||
/* 190 - 199 */ 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, | |||
/* 200 - 209 */ 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, | |||
/* 210 - 219 */ 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, | |||
/* 220 - 229 */ 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, | |||
/* 230 - 239 */ 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, | |||
/* 240 - 249 */ 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, | |||
/* 250 - 255 */ 108, 216, 173, 71, 142, 1} | |||
gfLogTable = [256]int{ | |||
/* 0 - 9 */ -1, 0, 1, 25, 2, 50, 26, 198, 3, 223, | |||
/* 10 - 19 */ 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, | |||
/* 20 - 29 */ 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, | |||
/* 30 - 39 */ 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, | |||
/* 40 - 49 */ 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, | |||
/* 50 - 59 */ 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, | |||
/* 60 - 69 */ 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, | |||
/* 70 - 79 */ 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, | |||
/* 80 - 89 */ 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, | |||
/* 90 - 99 */ 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, | |||
/* 100 - 109 */ 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, | |||
/* 110 - 119 */ 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, | |||
/* 120 - 129 */ 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, | |||
/* 130 - 139 */ 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, | |||
/* 140 - 149 */ 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, | |||
/* 150 - 159 */ 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, | |||
/* 160 - 169 */ 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, | |||
/* 170 - 179 */ 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, | |||
/* 180 - 189 */ 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, | |||
/* 190 - 199 */ 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, | |||
/* 200 - 209 */ 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, | |||
/* 210 - 219 */ 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, | |||
/* 220 - 229 */ 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, | |||
/* 230 - 239 */ 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, | |||
/* 240 - 249 */ 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, | |||
/* 250 - 255 */ 244, 234, 168, 80, 88, 175} | |||
) | |||
// gfElement is an element in GF(2^8). | |||
type gfElement uint8 | |||
// newGFElement creates and returns a new gfElement. | |||
func newGFElement(data byte) gfElement { | |||
return gfElement(data) | |||
} | |||
// gfAdd returns a + b. | |||
func gfAdd(a, b gfElement) gfElement { | |||
return a ^ b | |||
} | |||
// gfSub returns a - b. | |||
// | |||
// Note addition is equivalent to subtraction in GF(2). | |||
func gfSub(a, b gfElement) gfElement { | |||
return a ^ b | |||
} | |||
// gfMultiply returns a * b. | |||
func gfMultiply(a, b gfElement) gfElement { | |||
if a == gfZero || b == gfZero { | |||
return gfZero | |||
} | |||
return gfExpTable[(gfLogTable[a]+gfLogTable[b])%255] | |||
} | |||
// gfDivide returns a / b. | |||
// | |||
// Divide by zero results in a panic. | |||
func gfDivide(a, b gfElement) gfElement { | |||
if a == gfZero { | |||
return gfZero | |||
} else if b == gfZero { | |||
log.Panicln("Divide by zero") | |||
} | |||
return gfMultiply(a, gfInverse(b)) | |||
} | |||
// gfInverse returns the multiplicative inverse of a, a^-1. | |||
// | |||
// a * a^-1 = 1 | |||
func gfInverse(a gfElement) gfElement { | |||
if a == gfZero { | |||
log.Panicln("No multiplicative inverse of 0") | |||
} | |||
return gfExpTable[255-gfLogTable[a]] | |||
} | |||
// a^i | bits | polynomial | decimal | |||
// -------------------------------------------------------------------------- | |||
// 0 | 000000000 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 0 | |||
// a^0 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1 | |||
// a^1 | 000000010 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 2 | |||
// a^2 | 000000100 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 4 | |||
// a^3 | 000001000 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 8 | |||
// a^4 | 000010000 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 16 | |||
// a^5 | 000100000 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 32 | |||
// a^6 | 001000000 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 64 | |||
// a^7 | 010000000 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 128 | |||
// a^8 | 000011101 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 29 | |||
// a^9 | 000111010 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 58 | |||
// a^10 | 001110100 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 116 | |||
// a^11 | 011101000 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 232 | |||
// a^12 | 011001101 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 205 | |||
// a^13 | 010000111 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 135 | |||
// a^14 | 000010011 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 19 | |||
// a^15 | 000100110 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 38 | |||
// a^16 | 001001100 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 76 | |||
// a^17 | 010011000 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 152 | |||
// a^18 | 000101101 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 45 | |||
// a^19 | 001011010 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 90 | |||
// a^20 | 010110100 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 180 | |||
// a^21 | 001110101 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 117 | |||
// a^22 | 011101010 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 234 | |||
// a^23 | 011001001 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 201 | |||
// a^24 | 010001111 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 143 | |||
// a^25 | 000000011 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 3 | |||
// a^26 | 000000110 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 6 | |||
// a^27 | 000001100 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 12 | |||
// a^28 | 000011000 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 24 | |||
// a^29 | 000110000 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 48 | |||
// a^30 | 001100000 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 96 | |||
// a^31 | 011000000 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 192 | |||
// a^32 | 010011101 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 157 | |||
// a^33 | 000100111 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 39 | |||
// a^34 | 001001110 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 78 | |||
// a^35 | 010011100 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 156 | |||
// a^36 | 000100101 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 37 | |||
// a^37 | 001001010 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 74 | |||
// a^38 | 010010100 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 148 | |||
// a^39 | 000110101 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 53 | |||
// a^40 | 001101010 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 106 | |||
// a^41 | 011010100 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 212 | |||
// a^42 | 010110101 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 181 | |||
// a^43 | 001110111 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 119 | |||
// a^44 | 011101110 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 238 | |||
// a^45 | 011000001 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 193 | |||
// a^46 | 010011111 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 159 | |||
// a^47 | 000100011 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 35 | |||
// a^48 | 001000110 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 70 | |||
// a^49 | 010001100 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 140 | |||
// a^50 | 000000101 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 5 | |||
// a^51 | 000001010 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 10 | |||
// a^52 | 000010100 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 20 | |||
// a^53 | 000101000 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 40 | |||
// a^54 | 001010000 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 80 | |||
// a^55 | 010100000 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 160 | |||
// a^56 | 001011101 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 93 | |||
// a^57 | 010111010 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 186 | |||
// a^58 | 001101001 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 105 | |||
// a^59 | 011010010 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 210 | |||
// a^60 | 010111001 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 185 | |||
// a^61 | 001101111 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 111 | |||
// a^62 | 011011110 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 222 | |||
// a^63 | 010100001 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 161 | |||
// a^64 | 001011111 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 95 | |||
// a^65 | 010111110 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 190 | |||
// a^66 | 001100001 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 97 | |||
// a^67 | 011000010 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 194 | |||
// a^68 | 010011001 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 153 | |||
// a^69 | 000101111 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 47 | |||
// a^70 | 001011110 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 94 | |||
// a^71 | 010111100 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 188 | |||
// a^72 | 001100101 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 101 | |||
// a^73 | 011001010 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 202 | |||
// a^74 | 010001001 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 137 | |||
// a^75 | 000001111 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 15 | |||
// a^76 | 000011110 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 30 | |||
// a^77 | 000111100 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 60 | |||
// a^78 | 001111000 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 120 | |||
// a^79 | 011110000 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 240 | |||
// a^80 | 011111101 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 253 | |||
// a^81 | 011100111 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 231 | |||
// a^82 | 011010011 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 211 | |||
// a^83 | 010111011 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 187 | |||
// a^84 | 001101011 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 107 | |||
// a^85 | 011010110 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 214 | |||
// a^86 | 010110001 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 177 | |||
// a^87 | 001111111 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 127 | |||
// a^88 | 011111110 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 254 | |||
// a^89 | 011100001 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 225 | |||
// a^90 | 011011111 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 223 | |||
// a^91 | 010100011 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 163 | |||
// a^92 | 001011011 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 91 | |||
// a^93 | 010110110 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 182 | |||
// a^94 | 001110001 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 113 | |||
// a^95 | 011100010 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 226 | |||
// a^96 | 011011001 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 217 | |||
// a^97 | 010101111 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 175 | |||
// a^98 | 001000011 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 67 | |||
// a^99 | 010000110 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 134 | |||
// a^100 | 000010001 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 17 | |||
// a^101 | 000100010 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 34 | |||
// a^102 | 001000100 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 68 | |||
// a^103 | 010001000 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 136 | |||
// a^104 | 000001101 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 13 | |||
// a^105 | 000011010 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 26 | |||
// a^106 | 000110100 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 52 | |||
// a^107 | 001101000 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 104 | |||
// a^108 | 011010000 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 208 | |||
// a^109 | 010111101 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 189 | |||
// a^110 | 001100111 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 103 | |||
// a^111 | 011001110 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 206 | |||
// a^112 | 010000001 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 129 | |||
// a^113 | 000011111 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 31 | |||
// a^114 | 000111110 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 62 | |||
// a^115 | 001111100 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 124 | |||
// a^116 | 011111000 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 248 | |||
// a^117 | 011101101 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 237 | |||
// a^118 | 011000111 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 199 | |||
// a^119 | 010010011 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 147 | |||
// a^120 | 000111011 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 59 | |||
// a^121 | 001110110 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 118 | |||
// a^122 | 011101100 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 236 | |||
// a^123 | 011000101 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 197 | |||
// a^124 | 010010111 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 151 | |||
// a^125 | 000110011 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 51 | |||
// a^126 | 001100110 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 102 | |||
// a^127 | 011001100 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 204 | |||
// a^128 | 010000101 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 133 | |||
// a^129 | 000010111 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 23 | |||
// a^130 | 000101110 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 46 | |||
// a^131 | 001011100 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 92 | |||
// a^132 | 010111000 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 184 | |||
// a^133 | 001101101 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 109 | |||
// a^134 | 011011010 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 218 | |||
// a^135 | 010101001 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 169 | |||
// a^136 | 001001111 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 79 | |||
// a^137 | 010011110 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 158 | |||
// a^138 | 000100001 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 33 | |||
// a^139 | 001000010 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 66 | |||
// a^140 | 010000100 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 132 | |||
// a^141 | 000010101 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 21 | |||
// a^142 | 000101010 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 42 | |||
// a^143 | 001010100 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 84 | |||
// a^144 | 010101000 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 168 | |||
// a^145 | 001001101 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 77 | |||
// a^146 | 010011010 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 154 | |||
// a^147 | 000101001 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 41 | |||
// a^148 | 001010010 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 82 | |||
// a^149 | 010100100 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 164 | |||
// a^150 | 001010101 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 85 | |||
// a^151 | 010101010 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 170 | |||
// a^152 | 001001001 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 73 | |||
// a^153 | 010010010 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 146 | |||
// a^154 | 000111001 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 57 | |||
// a^155 | 001110010 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 114 | |||
// a^156 | 011100100 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 228 | |||
// a^157 | 011010101 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 213 | |||
// a^158 | 010110111 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 183 | |||
// a^159 | 001110011 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 115 | |||
// a^160 | 011100110 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 230 | |||
// a^161 | 011010001 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 209 | |||
// a^162 | 010111111 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 191 | |||
// a^163 | 001100011 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 99 | |||
// a^164 | 011000110 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 198 | |||
// a^165 | 010010001 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 145 | |||
// a^166 | 000111111 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 63 | |||
// a^167 | 001111110 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 126 | |||
// a^168 | 011111100 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 252 | |||
// a^169 | 011100101 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 229 | |||
// a^170 | 011010111 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 215 | |||
// a^171 | 010110011 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 179 | |||
// a^172 | 001111011 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 123 | |||
// a^173 | 011110110 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 246 | |||
// a^174 | 011110001 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 241 | |||
// a^175 | 011111111 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 255 | |||
// a^176 | 011100011 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 227 | |||
// a^177 | 011011011 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 219 | |||
// a^178 | 010101011 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 171 | |||
// a^179 | 001001011 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 75 | |||
// a^180 | 010010110 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 150 | |||
// a^181 | 000110001 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 49 | |||
// a^182 | 001100010 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 98 | |||
// a^183 | 011000100 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 196 | |||
// a^184 | 010010101 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 149 | |||
// a^185 | 000110111 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 55 | |||
// a^186 | 001101110 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 110 | |||
// a^187 | 011011100 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 220 | |||
// a^188 | 010100101 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 165 | |||
// a^189 | 001010111 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 87 | |||
// a^190 | 010101110 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 174 | |||
// a^191 | 001000001 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 65 | |||
// a^192 | 010000010 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 130 | |||
// a^193 | 000011001 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 25 | |||
// a^194 | 000110010 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 50 | |||
// a^195 | 001100100 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 100 | |||
// a^196 | 011001000 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 200 | |||
// a^197 | 010001101 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 141 | |||
// a^198 | 000000111 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 7 | |||
// a^199 | 000001110 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 14 | |||
// a^200 | 000011100 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 28 | |||
// a^201 | 000111000 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 56 | |||
// a^202 | 001110000 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 112 | |||
// a^203 | 011100000 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 224 | |||
// a^204 | 011011101 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 221 | |||
// a^205 | 010100111 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 167 | |||
// a^206 | 001010011 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 83 | |||
// a^207 | 010100110 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 166 | |||
// a^208 | 001010001 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 81 | |||
// a^209 | 010100010 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 162 | |||
// a^210 | 001011001 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 89 | |||
// a^211 | 010110010 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 178 | |||
// a^212 | 001111001 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 121 | |||
// a^213 | 011110010 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 242 | |||
// a^214 | 011111001 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 249 | |||
// a^215 | 011101111 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 239 | |||
// a^216 | 011000011 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 195 | |||
// a^217 | 010011011 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 155 | |||
// a^218 | 000101011 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 43 | |||
// a^219 | 001010110 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 86 | |||
// a^220 | 010101100 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 172 | |||
// a^221 | 001000101 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 69 | |||
// a^222 | 010001010 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 138 | |||
// a^223 | 000001001 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 9 | |||
// a^224 | 000010010 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 18 | |||
// a^225 | 000100100 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 36 | |||
// a^226 | 001001000 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 72 | |||
// a^227 | 010010000 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 144 | |||
// a^228 | 000111101 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 61 | |||
// a^229 | 001111010 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 122 | |||
// a^230 | 011110100 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 244 | |||
// a^231 | 011110101 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 245 | |||
// a^232 | 011110111 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 247 | |||
// a^233 | 011110011 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 243 | |||
// a^234 | 011111011 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 251 | |||
// a^235 | 011101011 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 235 | |||
// a^236 | 011001011 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 203 | |||
// a^237 | 010001011 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 139 | |||
// a^238 | 000001011 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 11 | |||
// a^239 | 000010110 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 22 | |||
// a^240 | 000101100 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 44 | |||
// a^241 | 001011000 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 88 | |||
// a^242 | 010110000 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 176 | |||
// a^243 | 001111101 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 125 | |||
// a^244 | 011111010 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 250 | |||
// a^245 | 011101001 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 233 | |||
// a^246 | 011001111 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 207 | |||
// a^247 | 010000011 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 131 | |||
// a^248 | 000011011 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 27 | |||
// a^249 | 000110110 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 54 | |||
// a^250 | 001101100 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 108 | |||
// a^251 | 011011000 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 216 | |||
// a^252 | 010101101 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 173 | |||
// a^253 | 001000111 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 71 | |||
// a^254 | 010001110 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 142 | |||
// a^255 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1 |
@@ -0,0 +1,83 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package reedsolomon | |||
import "testing" | |||
func TestGFMultiplicationIdentities(t *testing.T) { | |||
for i := 0; i < 256; i++ { | |||
value := gfElement(i) | |||
if gfMultiply(gfZero, value) != gfZero { | |||
t.Errorf("0 . %d != 0", value) | |||
} | |||
if gfMultiply(value, gfOne) != value { | |||
t.Errorf("%d . 1 == %d, want %d", value, gfMultiply(value, gfOne), value) | |||
} | |||
} | |||
} | |||
func TestGFMultiplicationAndDivision(t *testing.T) { | |||
// a * b == result | |||
var tests = []struct { | |||
a gfElement | |||
b gfElement | |||
result gfElement | |||
}{ | |||
{0, 29, 0}, | |||
{1, 1, 1}, | |||
{1, 32, 32}, | |||
{2, 4, 8}, | |||
{16, 128, 232}, | |||
{17, 17, 28}, | |||
{27, 9, 195}, | |||
} | |||
for _, test := range tests { | |||
result := gfMultiply(test.a, test.b) | |||
if result != test.result { | |||
t.Errorf("%d * %d = %d, want %d", test.a, test.b, result, test.result) | |||
} | |||
if test.b != gfZero && test.result != gfZero { | |||
b := gfDivide(test.result, test.a) | |||
if b != test.b { | |||
t.Errorf("%d / %d = %d, want %d", test.result, test.a, b, test.b) | |||
} | |||
} | |||
} | |||
} | |||
func TestGFInverse(t *testing.T) { | |||
for i := 1; i < 256; i++ { | |||
a := gfElement(i) | |||
inverse := gfInverse(a) | |||
result := gfMultiply(a, inverse) | |||
if result != gfOne { | |||
t.Errorf("%d * %d^-1 == %d, want %d", a, inverse, result, gfOne) | |||
} | |||
} | |||
} | |||
func TestGFDivide(t *testing.T) { | |||
for i := 1; i < 256; i++ { | |||
for j := 1; j < 256; j++ { | |||
// a * b == product | |||
a := gfElement(i) | |||
b := gfElement(j) | |||
product := gfMultiply(a, b) | |||
// product / b == a | |||
result := gfDivide(product, b) | |||
if result != a { | |||
t.Errorf("%d / %d == %d, want %d", product, b, result, a) | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,216 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package reedsolomon | |||
import ( | |||
"fmt" | |||
"log" | |||
bitset "github.com/skip2/go-qrcode/bitset" | |||
) | |||
// gfPoly is a polynomial over GF(2^8). | |||
type gfPoly struct { | |||
// The ith value is the coefficient of the ith degree of x. | |||
// term[0]*(x^0) + term[1]*(x^1) + term[2]*(x^2) ... | |||
term []gfElement | |||
} | |||
// newGFPolyFromData returns |data| as a polynomial over GF(2^8). | |||
// | |||
// Each data byte becomes the coefficient of an x term. | |||
// | |||
// For an n byte input the polynomial is: | |||
// data[n-1]*(x^n-1) + data[n-2]*(x^n-2) ... + data[0]*(x^0). | |||
func newGFPolyFromData(data *bitset.Bitset) gfPoly { | |||
numTotalBytes := data.Len() / 8 | |||
if data.Len()%8 != 0 { | |||
numTotalBytes++ | |||
} | |||
result := gfPoly{term: make([]gfElement, numTotalBytes)} | |||
i := numTotalBytes - 1 | |||
for j := 0; j < data.Len(); j += 8 { | |||
result.term[i] = gfElement(data.ByteAt(j)) | |||
i-- | |||
} | |||
return result | |||
} | |||
// newGFPolyMonomial returns term*(x^degree). | |||
func newGFPolyMonomial(term gfElement, degree int) gfPoly { | |||
if term == gfZero { | |||
return gfPoly{} | |||
} | |||
result := gfPoly{term: make([]gfElement, degree+1)} | |||
result.term[degree] = term | |||
return result | |||
} | |||
func (e gfPoly) data(numTerms int) []byte { | |||
result := make([]byte, numTerms) | |||
i := numTerms - len(e.term) | |||
for j := len(e.term) - 1; j >= 0; j-- { | |||
result[i] = byte(e.term[j]) | |||
i++ | |||
} | |||
return result | |||
} | |||
// numTerms returns the number of | |||
func (e gfPoly) numTerms() int { | |||
return len(e.term) | |||
} | |||
// gfPolyMultiply returns a * b. | |||
func gfPolyMultiply(a, b gfPoly) gfPoly { | |||
numATerms := a.numTerms() | |||
numBTerms := b.numTerms() | |||
result := gfPoly{term: make([]gfElement, numATerms+numBTerms)} | |||
for i := 0; i < numATerms; i++ { | |||
for j := 0; j < numBTerms; j++ { | |||
if a.term[i] != 0 && b.term[j] != 0 { | |||
monomial := gfPoly{term: make([]gfElement, i+j+1)} | |||
monomial.term[i+j] = gfMultiply(a.term[i], b.term[j]) | |||
result = gfPolyAdd(result, monomial) | |||
} | |||
} | |||
} | |||
return result.normalised() | |||
} | |||
// gfPolyRemainder return the remainder of numerator / denominator. | |||
func gfPolyRemainder(numerator, denominator gfPoly) gfPoly { | |||
if denominator.equals(gfPoly{}) { | |||
log.Panicln("Remainder by zero") | |||
} | |||
remainder := numerator | |||
for remainder.numTerms() >= denominator.numTerms() { | |||
degree := remainder.numTerms() - denominator.numTerms() | |||
coefficient := gfDivide(remainder.term[remainder.numTerms()-1], | |||
denominator.term[denominator.numTerms()-1]) | |||
divisor := gfPolyMultiply(denominator, | |||
newGFPolyMonomial(coefficient, degree)) | |||
remainder = gfPolyAdd(remainder, divisor) | |||
} | |||
return remainder.normalised() | |||
} | |||
// gfPolyAdd returns a + b. | |||
func gfPolyAdd(a, b gfPoly) gfPoly { | |||
numATerms := a.numTerms() | |||
numBTerms := b.numTerms() | |||
numTerms := numATerms | |||
if numBTerms > numTerms { | |||
numTerms = numBTerms | |||
} | |||
result := gfPoly{term: make([]gfElement, numTerms)} | |||
for i := 0; i < numTerms; i++ { | |||
switch { | |||
case numATerms > i && numBTerms > i: | |||
result.term[i] = gfAdd(a.term[i], b.term[i]) | |||
case numATerms > i: | |||
result.term[i] = a.term[i] | |||
default: | |||
result.term[i] = b.term[i] | |||
} | |||
} | |||
return result.normalised() | |||
} | |||
func (e gfPoly) normalised() gfPoly { | |||
numTerms := e.numTerms() | |||
maxNonzeroTerm := numTerms - 1 | |||
for i := numTerms - 1; i >= 0; i-- { | |||
if e.term[i] != 0 { | |||
break | |||
} | |||
maxNonzeroTerm = i - 1 | |||
} | |||
if maxNonzeroTerm < 0 { | |||
return gfPoly{} | |||
} else if maxNonzeroTerm < numTerms-1 { | |||
e.term = e.term[0 : maxNonzeroTerm+1] | |||
} | |||
return e | |||
} | |||
func (e gfPoly) string(useIndexForm bool) string { | |||
var str string | |||
numTerms := e.numTerms() | |||
for i := numTerms - 1; i >= 0; i-- { | |||
if e.term[i] > 0 { | |||
if len(str) > 0 { | |||
str += " + " | |||
} | |||
if !useIndexForm { | |||
str += fmt.Sprintf("%dx^%d", e.term[i], i) | |||
} else { | |||
str += fmt.Sprintf("a^%dx^%d", gfLogTable[e.term[i]], i) | |||
} | |||
} | |||
} | |||
if len(str) == 0 { | |||
str = "0" | |||
} | |||
return str | |||
} | |||
// equals returns true if e == other. | |||
func (e gfPoly) equals(other gfPoly) bool { | |||
var minecPoly *gfPoly | |||
var maxecPoly *gfPoly | |||
if e.numTerms() > other.numTerms() { | |||
minecPoly = &other | |||
maxecPoly = &e | |||
} else { | |||
minecPoly = &e | |||
maxecPoly = &other | |||
} | |||
numMinTerms := minecPoly.numTerms() | |||
numMaxTerms := maxecPoly.numTerms() | |||
for i := 0; i < numMinTerms; i++ { | |||
if e.term[i] != other.term[i] { | |||
return false | |||
} | |||
} | |||
for i := numMinTerms; i < numMaxTerms; i++ { | |||
if maxecPoly.term[i] != 0 { | |||
return false | |||
} | |||
} | |||
return true | |||
} |
@@ -0,0 +1,182 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package reedsolomon | |||
import ( | |||
"testing" | |||
) | |||
func TestGFPolyAdd(t *testing.T) { | |||
// a + b == result | |||
var tests = []struct { | |||
a gfPoly | |||
b gfPoly | |||
result gfPoly | |||
}{ | |||
{ | |||
gfPoly{[]gfElement{0, 0, 0}}, | |||
gfPoly{[]gfElement{0}}, | |||
gfPoly{[]gfElement{}}, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{1, 0}}, | |||
gfPoly{[]gfElement{1, 0}}, | |||
gfPoly{[]gfElement{0, 0}}, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{0xA0, 0x80, 0xFF, 0x00}}, | |||
gfPoly{[]gfElement{0x0A, 0x82}}, | |||
gfPoly{[]gfElement{0xAA, 0x02, 0xFF}}, | |||
}, | |||
} | |||
for _, test := range tests { | |||
result := gfPolyAdd(test.a, test.b) | |||
if !test.result.equals(result) { | |||
t.Errorf("%s * %s != %s (got %s)\n", test.a.string(false), test.b.string(false), | |||
test.result.string(false), result.string(false)) | |||
} | |||
if len(result.term) > 0 && result.term[len(result.term)-1] == 0 { | |||
t.Errorf("Result's maximum term coefficient is zero") | |||
} | |||
} | |||
} | |||
func TestGFPolyequals(t *testing.T) { | |||
// a == b if isEqual | |||
var tests = []struct { | |||
a gfPoly | |||
b gfPoly | |||
isEqual bool | |||
}{ | |||
{ | |||
gfPoly{[]gfElement{0}}, | |||
gfPoly{[]gfElement{0}}, | |||
true, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{1}}, | |||
gfPoly{[]gfElement{0}}, | |||
false, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{1, 0, 1, 0, 1}}, | |||
gfPoly{[]gfElement{1, 0, 1, 0, 1}}, | |||
true, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{1, 0, 1}}, | |||
gfPoly{[]gfElement{1, 0, 1, 0, 0}}, | |||
true, | |||
}, | |||
} | |||
for _, test := range tests { | |||
isEqual := test.a.equals(test.b) | |||
if isEqual != test.isEqual { | |||
t.Errorf("%s and %s equality is %t (got %t)\n", test.a.string(false), test.b.string(false), | |||
test.isEqual, isEqual) | |||
} | |||
} | |||
} | |||
func TestGFPolyMultiply(t *testing.T) { | |||
// a * b == result | |||
var tests = []struct { | |||
a gfPoly | |||
b gfPoly | |||
result gfPoly | |||
}{ | |||
{ | |||
gfPoly{[]gfElement{0, 0, 1}}, | |||
gfPoly{[]gfElement{9}}, | |||
gfPoly{[]gfElement{0, 0, 9}}, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{0, 16, 1}}, | |||
gfPoly{[]gfElement{128, 2}}, | |||
gfPoly{[]gfElement{0, 232, 160, 2}}, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{254, 120, 88, 44, 11, 1}}, | |||
gfPoly{[]gfElement{16, 2, 0, 51, 44}}, | |||
gfPoly{[]gfElement{91, 50, 25, 184, 194, 105, 45, 244, 58, 44}}, | |||
}, | |||
} | |||
for _, test := range tests { | |||
result := gfPolyMultiply(test.a, test.b) | |||
if !test.result.equals(result) { | |||
t.Errorf("%s * %s = %s (got %s)\n", | |||
test.a.string(false), | |||
test.b.string(false), | |||
test.result.string(false), | |||
result.string(false)) | |||
} | |||
} | |||
} | |||
func TestGFPolyRemainder(t *testing.T) { | |||
// numerator / denominator == quotient + remainder. | |||
var tests = []struct { | |||
numerator gfPoly | |||
denominator gfPoly | |||
remainder gfPoly | |||
}{ | |||
{ | |||
gfPoly{[]gfElement{1}}, | |||
gfPoly{[]gfElement{1}}, | |||
gfPoly{[]gfElement{0}}, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{1, 0}}, | |||
gfPoly{[]gfElement{1}}, | |||
gfPoly{[]gfElement{0}}, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{1}}, | |||
gfPoly{[]gfElement{1, 0}}, | |||
gfPoly{[]gfElement{1}}, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{1, 0, 1}}, | |||
gfPoly{[]gfElement{0, 1}}, | |||
gfPoly{[]gfElement{1}}, | |||
}, | |||
// (x^12 + x^10) / (x^10 + x^8 + x^5 + x^4 + x^2 + x^1 + x^0) = | |||
// (x^10 + x^8 + x^5 + x^4 + x^2 + x^1 + x^0) * x^2 + | |||
// (x^7 + x^6 + x^4 + x^3 + x^2) (the remainder) | |||
{ | |||
gfPoly{[]gfElement{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1}}, | |||
gfPoly{[]gfElement{1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1}}, | |||
gfPoly{[]gfElement{0, 0, 1, 1, 1, 0, 1, 1}}, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{91, 50, 25, 184, 194, 105, 45, 244, 58, 44}}, | |||
gfPoly{[]gfElement{254, 120, 88, 44, 11, 1}}, | |||
gfPoly{[]gfElement{}}, | |||
}, | |||
{ | |||
gfPoly{[]gfElement{0, 0, 0, 0, 0, 0, 195, 172, 24, 64}}, | |||
gfPoly{[]gfElement{116, 147, 63, 198, 31, 1}}, | |||
gfPoly{[]gfElement{48, 174, 34, 13, 134}}, | |||
}, | |||
} | |||
for _, test := range tests { | |||
remainder := gfPolyRemainder(test.numerator, test.denominator) | |||
if !test.remainder.equals(remainder) { | |||
t.Errorf("%s / %s, remainder = %s (got %s)\n", | |||
test.numerator.string(false), | |||
test.denominator.string(false), | |||
test.remainder.string(false), | |||
remainder.string(false)) | |||
} | |||
} | |||
} |
@@ -0,0 +1,73 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
// Package reedsolomon provides error correction encoding for QR Code 2005. | |||
// | |||
// QR Code 2005 uses a Reed-Solomon error correcting code to detect and correct | |||
// errors encountered during decoding. | |||
// | |||
// The generated RS codes are systematic, and consist of the input data with | |||
// error correction bytes appended. | |||
package reedsolomon | |||
import ( | |||
"log" | |||
bitset "github.com/skip2/go-qrcode/bitset" | |||
) | |||
// Encode data for QR Code 2005 using the appropriate Reed-Solomon code. | |||
// | |||
// numECBytes is the number of error correction bytes to append, and is | |||
// determined by the target QR Code's version and error correction level. | |||
// | |||
// ISO/IEC 18004 table 9 specifies the numECBytes required. e.g. a 1-L code has | |||
// numECBytes=7. | |||
func Encode(data *bitset.Bitset, numECBytes int) *bitset.Bitset { | |||
// Create a polynomial representing |data|. | |||
// | |||
// The bytes are interpreted as the sequence of coefficients of a polynomial. | |||
// The last byte's value becomes the x^0 coefficient, the second to last | |||
// becomes the x^1 coefficient and so on. | |||
ecpoly := newGFPolyFromData(data) | |||
ecpoly = gfPolyMultiply(ecpoly, newGFPolyMonomial(gfOne, numECBytes)) | |||
// Pick the generator polynomial. | |||
generator := rsGeneratorPoly(numECBytes) | |||
// Generate the error correction bytes. | |||
remainder := gfPolyRemainder(ecpoly, generator) | |||
// Combine the data & error correcting bytes. | |||
// The mathematically correct answer is: | |||
// | |||
// result := gfPolyAdd(ecpoly, remainder). | |||
// | |||
// The encoding used by QR Code 2005 is slightly different this result: To | |||
// preserve the original |data| bit sequence exactly, the data and remainder | |||
// are combined manually below. This ensures any most significant zero bits | |||
// are preserved (and not optimised away). | |||
result := bitset.Clone(data) | |||
result.AppendBytes(remainder.data(numECBytes)) | |||
return result | |||
} | |||
// rsGeneratorPoly returns the Reed-Solomon generator polynomial with |degree|. | |||
// | |||
// The generator polynomial is calculated as: | |||
// (x + a^0)(x + a^1)...(x + a^degree-1) | |||
func rsGeneratorPoly(degree int) gfPoly { | |||
if degree < 2 { | |||
log.Panic("degree < 2") | |||
} | |||
generator := gfPoly{term: []gfElement{1}} | |||
for i := 0; i < degree; i++ { | |||
nextPoly := gfPoly{term: []gfElement{gfExpTable[i], 1}} | |||
generator = gfPolyMultiply(generator, nextPoly) | |||
} | |||
return generator | |||
} |
@@ -0,0 +1,89 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package reedsolomon | |||
import ( | |||
"testing" | |||
bitset "github.com/skip2/go-qrcode/bitset" | |||
) | |||
func TestGeneratorPoly(t *testing.T) { | |||
var tests = []struct { | |||
degree int | |||
generator gfPoly | |||
}{ | |||
// x^2 + 3x^1 + 2x^0 (the shortest generator poly) | |||
{ | |||
2, | |||
gfPoly{term: []gfElement{2, 3, 1}}, | |||
}, | |||
// x^5 + 31x^4 + 198x^3 + 63x^2 + 147x^1 + 116x^0 | |||
{ | |||
5, | |||
gfPoly{term: []gfElement{116, 147, 63, 198, 31, 1}}, | |||
}, | |||
// x^68 + 131x^67 + 115x^66 + 9x^65 + 39x^64 + 18x^63 + 182x^62 + 60x^61 + | |||
// 94x^60 + 223x^59 + 230x^58 + 157x^57 + 142x^56 + 119x^55 + 85x^54 + | |||
// 107x^53 + 34x^52 + 174x^51 + 167x^50 + 109x^49 + 20x^48 + 185x^47 + | |||
// 112x^46 + 145x^45 + 172x^44 + 224x^43 + 170x^42 + 182x^41 + 107x^40 + | |||
// 38x^39 + 107x^38 + 71x^37 + 246x^36 + 230x^35 + 225x^34 + 144x^33 + | |||
// 20x^32 + 14x^31 + 175x^30 + 226x^29 + 245x^28 + 20x^27 + 219x^26 + | |||
// 212x^25 + 51x^24 + 158x^23 + 88x^22 + 63x^21 + 36x^20 + 199x^19 + 4x^18 + | |||
// 80x^17 + 157x^16 + 211x^15 + 239x^14 + 255x^13 + 7x^12 + 119x^11 + 11x^10 | |||
// + 235x^9 + 12x^8 + 34x^7 + 149x^6 + 204x^5 + 8x^4 + 32x^3 + 29x^2 + 99x^1 | |||
// + 11x^0 (the longest generator poly) | |||
{ | |||
68, | |||
gfPoly{term: []gfElement{11, 99, 29, 32, 8, 204, 149, 34, 12, | |||
235, 11, 119, 7, 255, 239, 211, 157, 80, 4, 199, 36, 63, 88, 158, 51, 212, | |||
219, 20, 245, 226, 175, 14, 20, 144, 225, 230, 246, 71, 107, 38, 107, 182, | |||
170, 224, 172, 145, 112, 185, 20, 109, 167, 174, 34, 107, 85, 119, 142, | |||
157, 230, 223, 94, 60, 182, 18, 39, 9, 115, 131, 1}}, | |||
}, | |||
} | |||
for _, test := range tests { | |||
generator := rsGeneratorPoly(test.degree) | |||
if !generator.equals(test.generator) { | |||
t.Errorf("degree=%d generator=%s, want %s", test.degree, | |||
generator.string(true), test.generator.string(true)) | |||
} | |||
} | |||
} | |||
func TestEncode(t *testing.T) { | |||
var tests = []struct { | |||
numECBytes int | |||
data string | |||
rsCode string | |||
}{ | |||
{ | |||
5, | |||
"01000000 00011000 10101100 11000011 00000000", | |||
"01000000 00011000 10101100 11000011 00000000 10000110 00001101 00100010 10101110 00110000", | |||
}, | |||
{ | |||
10, | |||
"00010000 00100000 00001100 01010110 01100001 10000000 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001", | |||
"00010000 00100000 00001100 01010110 01100001 10000000 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 11101100 00010001 10100101 00100100 11010100 11000001 11101101 00110110 11000111 10000111 00101100 01010101", | |||
}, | |||
} | |||
for _, test := range tests { | |||
data := bitset.NewFromBase2String(test.data) | |||
rsCode := bitset.NewFromBase2String(test.rsCode) | |||
result := Encode(data, test.numECBytes) | |||
if !rsCode.Equals(result) { | |||
t.Errorf("data=%s, numECBytes=%d, encoded=%s, want %s", | |||
data.String(), | |||
test.numECBytes, | |||
result.String(), | |||
rsCode) | |||
} | |||
} | |||
} |
@@ -0,0 +1,309 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package qrcode | |||
import ( | |||
bitset "github.com/skip2/go-qrcode/bitset" | |||
) | |||
type regularSymbol struct { | |||
version qrCodeVersion | |||
mask int | |||
data *bitset.Bitset | |||
symbol *symbol | |||
size int | |||
} | |||
// Abbreviated true/false. | |||
const ( | |||
b0 = false | |||
b1 = true | |||
) | |||
var ( | |||
alignmentPatternCenter = [][]int{ | |||
{}, // Version 0 doesn't exist. | |||
{}, // Version 1 doesn't use alignment patterns. | |||
{6, 18}, | |||
{6, 22}, | |||
{6, 26}, | |||
{6, 30}, | |||
{6, 34}, | |||
{6, 22, 38}, | |||
{6, 24, 42}, | |||
{6, 26, 46}, | |||
{6, 28, 50}, | |||
{6, 30, 54}, | |||
{6, 32, 58}, | |||
{6, 34, 62}, | |||
{6, 26, 46, 66}, | |||
{6, 26, 48, 70}, | |||
{6, 26, 50, 74}, | |||
{6, 30, 54, 78}, | |||
{6, 30, 56, 82}, | |||
{6, 30, 58, 86}, | |||
{6, 34, 62, 90}, | |||
{6, 28, 50, 72, 94}, | |||
{6, 26, 50, 74, 98}, | |||
{6, 30, 54, 78, 102}, | |||
{6, 28, 54, 80, 106}, | |||
{6, 32, 58, 84, 110}, | |||
{6, 30, 58, 86, 114}, | |||
{6, 34, 62, 90, 118}, | |||
{6, 26, 50, 74, 98, 122}, | |||
{6, 30, 54, 78, 102, 126}, | |||
{6, 26, 52, 78, 104, 130}, | |||
{6, 30, 56, 82, 108, 134}, | |||
{6, 34, 60, 86, 112, 138}, | |||
{6, 30, 58, 86, 114, 142}, | |||
{6, 34, 62, 90, 118, 146}, | |||
{6, 30, 54, 78, 102, 126, 150}, | |||
{6, 24, 50, 76, 102, 128, 154}, | |||
{6, 28, 54, 80, 106, 132, 158}, | |||
{6, 32, 58, 84, 110, 136, 162}, | |||
{6, 26, 54, 82, 110, 138, 166}, | |||
{6, 30, 58, 86, 114, 142, 170}, | |||
} | |||
finderPattern = [][]bool{ | |||
{b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b0, b0, b0, b0, b0, b1}, | |||
{b1, b0, b1, b1, b1, b0, b1}, | |||
{b1, b0, b1, b1, b1, b0, b1}, | |||
{b1, b0, b1, b1, b1, b0, b1}, | |||
{b1, b0, b0, b0, b0, b0, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1}, | |||
} | |||
finderPatternSize = 7 | |||
finderPatternHorizontalBorder = [][]bool{ | |||
{b0, b0, b0, b0, b0, b0, b0, b0}, | |||
} | |||
finderPatternVerticalBorder = [][]bool{ | |||
{b0}, | |||
{b0}, | |||
{b0}, | |||
{b0}, | |||
{b0}, | |||
{b0}, | |||
{b0}, | |||
{b0}, | |||
} | |||
alignmentPattern = [][]bool{ | |||
{b1, b1, b1, b1, b1}, | |||
{b1, b0, b0, b0, b1}, | |||
{b1, b0, b1, b0, b1}, | |||
{b1, b0, b0, b0, b1}, | |||
{b1, b1, b1, b1, b1}, | |||
} | |||
) | |||
func buildRegularSymbol(version qrCodeVersion, mask int, | |||
data *bitset.Bitset) (*symbol, error) { | |||
m := ®ularSymbol{ | |||
version: version, | |||
mask: mask, | |||
data: data, | |||
symbol: newSymbol(version.symbolSize(), version.quietZoneSize()), | |||
size: version.symbolSize(), | |||
} | |||
m.addFinderPatterns() | |||
m.addAlignmentPatterns() | |||
m.addTimingPatterns() | |||
m.addFormatInfo() | |||
m.addVersionInfo() | |||
ok, err := m.addData() | |||
if !ok { | |||
return nil, err | |||
} | |||
return m.symbol, nil | |||
} | |||
func (m *regularSymbol) addFinderPatterns() { | |||
fpSize := finderPatternSize | |||
fp := finderPattern | |||
fpHBorder := finderPatternHorizontalBorder | |||
fpVBorder := finderPatternVerticalBorder | |||
// Top left Finder Pattern. | |||
m.symbol.set2dPattern(0, 0, fp) | |||
m.symbol.set2dPattern(0, fpSize, fpHBorder) | |||
m.symbol.set2dPattern(fpSize, 0, fpVBorder) | |||
// Top right Finder Pattern. | |||
m.symbol.set2dPattern(m.size-fpSize, 0, fp) | |||
m.symbol.set2dPattern(m.size-fpSize-1, fpSize, fpHBorder) | |||
m.symbol.set2dPattern(m.size-fpSize-1, 0, fpVBorder) | |||
// Bottom left Finder Pattern. | |||
m.symbol.set2dPattern(0, m.size-fpSize, fp) | |||
m.symbol.set2dPattern(0, m.size-fpSize-1, fpHBorder) | |||
m.symbol.set2dPattern(fpSize, m.size-fpSize-1, fpVBorder) | |||
} | |||
func (m *regularSymbol) addAlignmentPatterns() { | |||
for _, x := range alignmentPatternCenter[m.version.version] { | |||
for _, y := range alignmentPatternCenter[m.version.version] { | |||
if !m.symbol.empty(x, y) { | |||
continue | |||
} | |||
m.symbol.set2dPattern(x-2, y-2, alignmentPattern) | |||
} | |||
} | |||
} | |||
func (m *regularSymbol) addTimingPatterns() { | |||
value := true | |||
for i := finderPatternSize + 1; i < m.size-finderPatternSize; i++ { | |||
m.symbol.set(i, finderPatternSize-1, value) | |||
m.symbol.set(finderPatternSize-1, i, value) | |||
value = !value | |||
} | |||
} | |||
func (m *regularSymbol) addFormatInfo() { | |||
fpSize := finderPatternSize | |||
l := formatInfoLengthBits - 1 | |||
f := m.version.formatInfo(m.mask) | |||
// Bits 0-7, under the top right finder pattern. | |||
for i := 0; i <= 7; i++ { | |||
m.symbol.set(m.size-i-1, fpSize+1, f.At(l-i)) | |||
} | |||
// Bits 0-5, right of the top left finder pattern. | |||
for i := 0; i <= 5; i++ { | |||
m.symbol.set(fpSize+1, i, f.At(l-i)) | |||
} | |||
// Bits 6-8 on the corner of the top left finder pattern. | |||
m.symbol.set(fpSize+1, fpSize, f.At(l-6)) | |||
m.symbol.set(fpSize+1, fpSize+1, f.At(l-7)) | |||
m.symbol.set(fpSize, fpSize+1, f.At(l-8)) | |||
// Bits 9-14 on the underside of the top left finder pattern. | |||
for i := 9; i <= 14; i++ { | |||
m.symbol.set(14-i, fpSize+1, f.At(l-i)) | |||
} | |||
// Bits 8-14 on the right side of the bottom left finder pattern. | |||
for i := 8; i <= 14; i++ { | |||
m.symbol.set(fpSize+1, m.size-fpSize+i-8, f.At(l-i)) | |||
} | |||
// Always dark symbol. | |||
m.symbol.set(fpSize+1, m.size-fpSize-1, true) | |||
} | |||
func (m *regularSymbol) addVersionInfo() { | |||
fpSize := finderPatternSize | |||
v := m.version.versionInfo() | |||
l := versionInfoLengthBits - 1 | |||
if v == nil { | |||
return | |||
} | |||
for i := 0; i < v.Len(); i++ { | |||
// Above the bottom left finder pattern. | |||
m.symbol.set(i/3, m.size-fpSize-4+i%3, v.At(l-i)) | |||
// Left of the top right finder pattern. | |||
m.symbol.set(m.size-fpSize-4+i%3, i/3, v.At(l-i)) | |||
} | |||
} | |||
type direction uint8 | |||
const ( | |||
up direction = iota | |||
down | |||
) | |||
func (m *regularSymbol) addData() (bool, error) { | |||
xOffset := 1 | |||
dir := up | |||
x := m.size - 2 | |||
y := m.size - 1 | |||
for i := 0; i < m.data.Len(); i++ { | |||
var mask bool | |||
switch m.mask { | |||
case 0: | |||
mask = (y+x+xOffset)%2 == 0 | |||
case 1: | |||
mask = y%2 == 0 | |||
case 2: | |||
mask = (x+xOffset)%3 == 0 | |||
case 3: | |||
mask = (y+x+xOffset)%3 == 0 | |||
case 4: | |||
mask = (y/2+(x+xOffset)/3)%2 == 0 | |||
case 5: | |||
mask = (y*(x+xOffset))%2+(y*(x+xOffset))%3 == 0 | |||
case 6: | |||
mask = ((y*(x+xOffset))%2+((y*(x+xOffset))%3))%2 == 0 | |||
case 7: | |||
mask = ((y+x+xOffset)%2+((y*(x+xOffset))%3))%2 == 0 | |||
} | |||
// != is equivalent to XOR. | |||
m.symbol.set(x+xOffset, y, mask != m.data.At(i)) | |||
if i == m.data.Len()-1 { | |||
break | |||
} | |||
// Find next free bit in the symbol. | |||
for { | |||
if xOffset == 1 { | |||
xOffset = 0 | |||
} else { | |||
xOffset = 1 | |||
if dir == up { | |||
if y > 0 { | |||
y-- | |||
} else { | |||
dir = down | |||
x -= 2 | |||
} | |||
} else { | |||
if y < m.size-1 { | |||
y++ | |||
} else { | |||
dir = up | |||
x -= 2 | |||
} | |||
} | |||
} | |||
// Skip over the vertical timing pattern entirely. | |||
if x == 5 { | |||
x-- | |||
} | |||
if m.symbol.empty(x+xOffset, y) { | |||
break | |||
} | |||
} | |||
} | |||
return true, nil | |||
} |
@@ -0,0 +1,31 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package qrcode | |||
import ( | |||
"fmt" | |||
"testing" | |||
bitset "github.com/skip2/go-qrcode/bitset" | |||
) | |||
func TestBuildRegularSymbol(t *testing.T) { | |||
for k := 0; k <= 7; k++ { | |||
v := getQRCodeVersion(Low, 1) | |||
data := bitset.New() | |||
for i := 0; i < 26; i++ { | |||
data.AppendNumBools(8, false) | |||
} | |||
s, err := buildRegularSymbol(*v, k, data) | |||
if err != nil { | |||
fmt.Println(err.Error()) | |||
} else { | |||
_ = s | |||
//fmt.Print(m.string()) | |||
} | |||
} | |||
} |
@@ -0,0 +1,309 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package qrcode | |||
// symbol is a 2D array of bits representing a QR Code symbol. | |||
// | |||
// A symbol consists of size*size modules, with each module normally drawn as a | |||
// black or white square. The symbol also has a border of quietZoneSize modules. | |||
// | |||
// A (fictional) size=2, quietZoneSize=1 QR Code looks like: | |||
// | |||
// +----+ | |||
// | | | |||
// | ab | | |||
// | cd | | |||
// | | | |||
// +----+ | |||
// | |||
// For ease of implementation, the functions to set/get bits ignore the border, | |||
// so (0,0)=a, (0,1)=b, (1,0)=c, and (1,1)=d. The entire symbol (including the | |||
// border) is returned by bitmap(). | |||
// | |||
type symbol struct { | |||
// Value of module at [y][x]. True is set. | |||
module [][]bool | |||
// True if the module at [y][x] is used (to either true or false). | |||
// Used to identify unused modules. | |||
isUsed [][]bool | |||
// Combined width/height of the symbol and quiet zones. | |||
// | |||
// size = symbolSize + 2*quietZoneSize. | |||
size int | |||
// Width/height of the symbol only. | |||
symbolSize int | |||
// Width/height of a single quiet zone. | |||
quietZoneSize int | |||
} | |||
// newSymbol constructs a symbol of size size*size, with a border of | |||
// quietZoneSize. | |||
func newSymbol(size int, quietZoneSize int) *symbol { | |||
var m symbol | |||
m.module = make([][]bool, size+2*quietZoneSize) | |||
m.isUsed = make([][]bool, size+2*quietZoneSize) | |||
for i := range m.module { | |||
m.module[i] = make([]bool, size+2*quietZoneSize) | |||
m.isUsed[i] = make([]bool, size+2*quietZoneSize) | |||
} | |||
m.size = size + 2*quietZoneSize | |||
m.symbolSize = size | |||
m.quietZoneSize = quietZoneSize | |||
return &m | |||
} | |||
// get returns the module value at (x, y). | |||
func (m *symbol) get(x int, y int) (v bool) { | |||
v = m.module[y+m.quietZoneSize][x+m.quietZoneSize] | |||
return | |||
} | |||
// empty returns true if the module at (x, y) has not been set (to either true | |||
// or false). | |||
func (m *symbol) empty(x int, y int) bool { | |||
return !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] | |||
} | |||
// numEmptyModules returns the number of empty modules. | |||
// | |||
// Initially numEmptyModules is symbolSize * symbolSize. After every module has | |||
// been set (to either true or false), the number of empty modules is zero. | |||
func (m *symbol) numEmptyModules() int { | |||
var count int | |||
for y := 0; y < m.symbolSize; y++ { | |||
for x := 0; x < m.symbolSize; x++ { | |||
if !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] { | |||
count++ | |||
} | |||
} | |||
} | |||
return count | |||
} | |||
// set sets the module at (x, y) to v. | |||
func (m *symbol) set(x int, y int, v bool) { | |||
m.module[y+m.quietZoneSize][x+m.quietZoneSize] = v | |||
m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] = true | |||
} | |||
// set2dPattern sets a 2D array of modules, starting at (x, y). | |||
func (m *symbol) set2dPattern(x int, y int, v [][]bool) { | |||
for j, row := range v { | |||
for i, value := range row { | |||
m.set(x+i, y+j, value) | |||
} | |||
} | |||
} | |||
// bitmap returns the entire symbol, including the quiet zone. | |||
func (m *symbol) bitmap() [][]bool { | |||
module := make([][]bool, len(m.module)) | |||
for i := range m.module { | |||
module[i] = m.module[i][:] | |||
} | |||
return module | |||
} | |||
// string returns a pictorial representation of the symbol, suitable for | |||
// printing in a TTY. | |||
func (m *symbol) string() string { | |||
var result string | |||
for _, row := range m.module { | |||
for _, value := range row { | |||
switch value { | |||
case true: | |||
result += " " | |||
case false: | |||
// Unicode 'FULL BLOCK' (U+2588). | |||
result += "██" | |||
} | |||
} | |||
result += "\n" | |||
} | |||
return result | |||
} | |||
// Constants used to weight penalty calculations. Specified by ISO/IEC | |||
// 18004:2006. | |||
const ( | |||
penaltyWeight1 = 3 | |||
penaltyWeight2 = 3 | |||
penaltyWeight3 = 40 | |||
penaltyWeight4 = 10 | |||
) | |||
// penaltyScore returns the penalty score of the symbol. The penalty score | |||
// consists of the sum of the four individual penalty types. | |||
func (m *symbol) penaltyScore() int { | |||
return m.penalty1() + m.penalty2() + m.penalty3() + m.penalty4() | |||
} | |||
// penalty1 returns the penalty score for "adjacent modules in row/column with | |||
// same colour". | |||
// | |||
// The numbers of adjacent matching modules and scores are: | |||
// 0-5: score = 0 | |||
// 6+ : score = penaltyWeight1 + (numAdjacentModules - 5) | |||
func (m *symbol) penalty1() int { | |||
penalty := 0 | |||
for x := 0; x < m.symbolSize; x++ { | |||
lastValue := m.get(x, 0) | |||
count := 1 | |||
for y := 1; y < m.symbolSize; y++ { | |||
v := m.get(x, y) | |||
if v != lastValue { | |||
count = 1 | |||
lastValue = v | |||
} else { | |||
count++ | |||
if count == 6 { | |||
penalty += penaltyWeight1 + 1 | |||
} else if count > 6 { | |||
penalty++ | |||
} | |||
} | |||
} | |||
} | |||
for y := 0; y < m.symbolSize; y++ { | |||
lastValue := m.get(0, y) | |||
count := 1 | |||
for x := 1; x < m.symbolSize; x++ { | |||
v := m.get(x, y) | |||
if v != lastValue { | |||
count = 1 | |||
lastValue = v | |||
} else { | |||
count++ | |||
if count == 6 { | |||
penalty += penaltyWeight1 + 1 | |||
} else if count > 6 { | |||
penalty++ | |||
} | |||
} | |||
} | |||
} | |||
return penalty | |||
} | |||
// penalty2 returns the penalty score for "block of modules in the same colour". | |||
// | |||
// m*n: score = penaltyWeight2 * (m-1) * (n-1). | |||
func (m *symbol) penalty2() int { | |||
penalty := 0 | |||
for y := 1; y < m.symbolSize; y++ { | |||
for x := 1; x < m.symbolSize; x++ { | |||
topLeft := m.get(x-1, y-1) | |||
above := m.get(x, y-1) | |||
left := m.get(x-1, y) | |||
current := m.get(x, y) | |||
if current == left && current == above && current == topLeft { | |||
penalty++ | |||
} | |||
} | |||
} | |||
return penalty * penaltyWeight2 | |||
} | |||
// penalty3 returns the penalty score for "1:1:3:1:1 ratio | |||
// (dark:light:dark:light:dark) pattern in row/column, preceded or followed by | |||
// light area 4 modules wide". | |||
// | |||
// Existence of the pattern scores penaltyWeight3. | |||
func (m *symbol) penalty3() int { | |||
penalty := 0 | |||
for y := 0; y < m.symbolSize; y++ { | |||
var bitBuffer int16 = 0x00 | |||
for x := 0; x < m.symbolSize; x++ { | |||
bitBuffer <<= 1 | |||
if v := m.get(x, y); v { | |||
bitBuffer |= 1 | |||
} | |||
switch bitBuffer & 0x7ff { | |||
// 0b000 0101 1101 or 0b10111010000 | |||
// 0x05d or 0x5d0 | |||
case 0x05d, 0x5d0: | |||
penalty += penaltyWeight3 | |||
bitBuffer = 0xFF | |||
default: | |||
if x == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d { | |||
penalty += penaltyWeight3 | |||
bitBuffer = 0xFF | |||
} | |||
} | |||
} | |||
} | |||
for x := 0; x < m.symbolSize; x++ { | |||
var bitBuffer int16 = 0x00 | |||
for y := 0; y < m.symbolSize; y++ { | |||
bitBuffer <<= 1 | |||
if v := m.get(x, y); v { | |||
bitBuffer |= 1 | |||
} | |||
switch bitBuffer & 0x7ff { | |||
// 0b000 0101 1101 or 0b10111010000 | |||
// 0x05d or 0x5d0 | |||
case 0x05d, 0x5d0: | |||
penalty += penaltyWeight3 | |||
bitBuffer = 0xFF | |||
default: | |||
if y == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d { | |||
penalty += penaltyWeight3 | |||
bitBuffer = 0xFF | |||
} | |||
} | |||
} | |||
} | |||
return penalty | |||
} | |||
// penalty4 returns the penalty score... | |||
func (m *symbol) penalty4() int { | |||
numModules := m.symbolSize * m.symbolSize | |||
numDarkModules := 0 | |||
for x := 0; x < m.symbolSize; x++ { | |||
for y := 0; y < m.symbolSize; y++ { | |||
if v := m.get(x, y); v { | |||
numDarkModules++ | |||
} | |||
} | |||
} | |||
numDarkModuleDeviation := numModules/2 - numDarkModules | |||
if numDarkModuleDeviation < 0 { | |||
numDarkModuleDeviation *= -1 | |||
} | |||
return penaltyWeight4 * (numDarkModuleDeviation / (numModules / 20)) | |||
} |
@@ -0,0 +1,334 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package qrcode | |||
import "testing" | |||
func TestSymbolBasic(t *testing.T) { | |||
size := 10 | |||
quietZoneSize := 4 | |||
m := newSymbol(size, quietZoneSize) | |||
if m.size != size+quietZoneSize*2 { | |||
t.Errorf("Symbol size is %d, expected %d", m.size, size+quietZoneSize*2) | |||
} | |||
for i := 0; i < size; i++ { | |||
for j := 0; j < size; j++ { | |||
v := m.get(i, j) | |||
if v != false { | |||
t.Errorf("New symbol not empty") | |||
} | |||
if !m.empty(i, j) { | |||
t.Errorf("New symbol is not empty") | |||
} | |||
value := i*j%2 == 0 | |||
m.set(i, j, value) | |||
v = m.get(i, j) | |||
if v != value { | |||
t.Errorf("Symbol ignores set bits") | |||
} | |||
if m.empty(i, j) { | |||
t.Errorf("Symbol ignores set bits") | |||
} | |||
} | |||
} | |||
} | |||
func TestSymbolPenalties(t *testing.T) { | |||
tests := []struct { | |||
pattern [][]bool | |||
expectedPenalty1 int | |||
expectedPenalty2 int | |||
expectedPenalty3 int | |||
expectedPenalty4 int | |||
}{ | |||
{ | |||
[][]bool{ | |||
{b0, b1, b0, b1, b0, b1}, | |||
{b1, b0, b1, b0, b1, b0}, | |||
{b0, b1, b0, b1, b0, b1}, | |||
{b1, b0, b1, b0, b1, b0}, | |||
{b0, b1, b0, b1, b0, b1}, | |||
{b1, b0, b1, b0, b1, b0}, | |||
}, | |||
0, // No adjacent modules of same color. | |||
0, // No 2x2+ sized blocks. | |||
0, // No 1:1:3:1:1 pattern. | |||
-1, | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b1, b0, b1}, | |||
{b1, b0, b1, b0, b1, b0}, | |||
{b0, b1, b0, b1, b0, b1}, | |||
{b1, b0, b1, b0, b1, b0}, | |||
{b0, b1, b0, b1, b0, b1}, | |||
{b1, b0, b1, b0, b1, b0}, | |||
}, | |||
0, // 5 adjacent modules of same colour, score = 0. | |||
0, // No 2x2+ sized blocks. | |||
0, // No 1:1:3:1:1 pattern. | |||
-1, | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b0, b0, b0}, | |||
{b1, b0, b1, b0, b1, b0}, | |||
{b0, b1, b0, b1, b0, b1}, | |||
{b1, b0, b1, b0, b1, b0}, | |||
{b0, b1, b0, b1, b0, b1}, | |||
{b1, b0, b1, b0, b1, b0}, | |||
}, | |||
4, // 6 adjacent modules of same colour, score = 3 + (6-5) | |||
0, // No 2x2+ sized blocks. | |||
0, // No 1:1:3:1:1 pattern. | |||
-1, | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b0, b0, b0, b0}, | |||
{b1, b0, b1, b0, b1, b0, b1}, | |||
{b1, b0, b0, b0, b0, b0, b1}, | |||
{b1, b0, b1, b0, b1, b0, b1}, | |||
{b1, b0, b0, b0, b0, b0, b1}, | |||
{b1, b0, b1, b0, b1, b0, b1}, | |||
{b1, b0, b0, b0, b0, b0, b0}, | |||
}, | |||
28, // 3+(7-5) + 3+(6-5) + 3+(6-5) + 3+(6-5) + 3+(7-5) + 3+(7-5) = 28 | |||
0, // No 2x2+ sized blocks. | |||
0, // No 1:1:3:1:1 pattern. | |||
-1, | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b1, b0, b1}, | |||
{b0, b0, b1, b0, b1, b0}, | |||
{b0, b1, b0, b1, b0, b1}, | |||
{b1, b0, b1, b1, b1, b0}, | |||
{b0, b1, b1, b1, b0, b1}, | |||
{b1, b0, b1, b0, b1, b0}, | |||
}, | |||
-1, | |||
6, // 3*(2-1)*(2-1) + 3(2-1)*(2-1) | |||
0, // No 1:1:3:1:1 pattern. | |||
-1, | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b0, b0, b1}, | |||
{b0, b0, b0, b0, b0, b1}, | |||
{b0, b0, b0, b0, b0, b1}, | |||
{b0, b0, b0, b0, b0, b1}, | |||
{b0, b0, b0, b0, b0, b1}, | |||
{b0, b0, b0, b0, b0, b1}, | |||
}, | |||
-1, | |||
60, // 3 * (5-1) * (6-1) | |||
0, // No 1:1:3:1:1 pattern. | |||
-1, | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b0, b0, b1}, | |||
{b0, b0, b0, b0, b0, b1}, | |||
{b1, b1, b0, b1, b0, b1}, | |||
{b1, b1, b0, b1, b0, b1}, | |||
{b1, b1, b0, b1, b0, b1}, | |||
{b1, b1, b0, b1, b0, b1}, | |||
}, | |||
-1, | |||
21, // 3*(5-1)*(2-1) + 3*(2-1)*(4-1) = 3*4 + 3*3 | |||
0, // No 1:1:3:1:1 pattern. | |||
-1, | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
{b0, b0, b0, b0, b1, b0, b1, b1, b1, b0, b1, b0}, | |||
}, | |||
-1, | |||
-1, | |||
480, // 12* 1:1:3:1:1 patterns, 12 * 40. | |||
-1, | |||
}, | |||
{ | |||
[][]bool{ | |||
{b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b1, b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b1, b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b1, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
}, | |||
-1, | |||
-1, | |||
80, // 2* 1:1:3:1:1 patterns, 2 * 40. | |||
-1, | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
}, | |||
-1, | |||
-1, | |||
-1, | |||
100, // 10 * (10 steps of 5% deviation from 50% black/white). | |||
}, | |||
{ | |||
[][]bool{ | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
}, | |||
-1, | |||
-1, | |||
-1, | |||
100, // 10 * (10 steps of 5% deviation from 50% black/white). | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
}, | |||
-1, | |||
-1, | |||
-1, | |||
0, // Exactly 50%/50% black/white. | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
}, | |||
-1, | |||
-1, | |||
-1, | |||
20, // 10 * (2 steps of 5% deviation towards white). | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
}, | |||
-1, | |||
-1, | |||
-1, | |||
30, // 10 * (3 steps of 5% deviation towards white). | |||
}, | |||
{ | |||
[][]bool{ | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b0}, | |||
{b0, b0, b0, b0, b0, b0, b0, b0, b0, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
{b1, b1, b1, b1, b1, b1, b1, b1, b1, b1}, | |||
}, | |||
-1, | |||
-1, | |||
-1, | |||
30, // 10 * (3 steps of 5% deviation towards white). | |||
}, | |||
} | |||
for i, test := range tests { | |||
s := newSymbol(len(test.pattern[0]), 4) | |||
s.set2dPattern(0, 0, test.pattern) | |||
penalty1 := s.penalty1() | |||
penalty2 := s.penalty2() | |||
penalty3 := s.penalty3() | |||
penalty4 := s.penalty4() | |||
ok := true | |||
if test.expectedPenalty1 != -1 && test.expectedPenalty1 != penalty1 { | |||
ok = false | |||
} | |||
if test.expectedPenalty2 != -1 && test.expectedPenalty2 != penalty2 { | |||
ok = false | |||
} | |||
if test.expectedPenalty3 != -1 && test.expectedPenalty3 != penalty3 { | |||
ok = false | |||
} | |||
if test.expectedPenalty4 != -1 && test.expectedPenalty4 != penalty4 { | |||
ok = false | |||
} | |||
if !ok { | |||
t.Fatalf("Penalty test #%d p1=%d, p2=%d, p3=%d, p4=%d (expected p1=%d, p2=%d, p3=%d, p4=%d)", i, penalty1, penalty2, penalty3, penalty4, | |||
test.expectedPenalty1, test.expectedPenalty2, test.expectedPenalty3, | |||
test.expectedPenalty4) | |||
} | |||
} | |||
} |
@@ -0,0 +1,158 @@ | |||
// go-qrcode | |||
// Copyright 2014 Tom Harwood | |||
package qrcode | |||
import ( | |||
"testing" | |||
bitset "github.com/skip2/go-qrcode/bitset" | |||
) | |||
func TestFormatInfo(t *testing.T) { | |||
tests := []struct { | |||
level RecoveryLevel | |||
maskPattern int | |||
expected uint32 | |||
}{ | |||
{ // L=01 M=00 Q=11 H=10 | |||
Low, | |||
1, | |||
0x72f3, | |||
}, | |||
{ | |||
Medium, | |||
2, | |||
0x5e7c, | |||
}, | |||
{ | |||
High, | |||
3, | |||
0x3a06, | |||
}, | |||
{ | |||
Highest, | |||
4, | |||
0x0762, | |||
}, | |||
{ | |||
Low, | |||
5, | |||
0x6318, | |||
}, | |||
{ | |||
Medium, | |||
6, | |||
0x4f97, | |||
}, | |||
{ | |||
High, | |||
7, | |||
0x2bed, | |||
}, | |||
} | |||
for i, test := range tests { | |||
v := getQRCodeVersion(test.level, 1) | |||
result := v.formatInfo(test.maskPattern) | |||
expected := bitset.New() | |||
expected.AppendUint32(test.expected, formatInfoLengthBits) | |||
if !expected.Equals(result) { | |||
t.Errorf("formatInfo test #%d got %s, expected %s", i, result.String(), | |||
expected.String()) | |||
} | |||
} | |||
} | |||
func TestVersionInfo(t *testing.T) { | |||
tests := []struct { | |||
version int | |||
expected uint32 | |||
}{ | |||
{ | |||
7, | |||
0x007c94, | |||
}, | |||
{ | |||
10, | |||
0x00a4d3, | |||
}, | |||
{ | |||
20, | |||
0x0149a6, | |||
}, | |||
{ | |||
30, | |||
0x01ed75, | |||
}, | |||
{ | |||
40, | |||
0x028c69, | |||
}, | |||
} | |||
for i, test := range tests { | |||
var v *qrCodeVersion | |||
v = getQRCodeVersion(Low, test.version) | |||
result := v.versionInfo() | |||
expected := bitset.New() | |||
expected.AppendUint32(test.expected, versionInfoLengthBits) | |||
if !expected.Equals(result) { | |||
t.Errorf("versionInfo test #%d got %s, expected %s", i, result.String(), | |||
expected.String()) | |||
} | |||
} | |||
} | |||
func TestNumBitsToPadToCodeoword(t *testing.T) { | |||
tests := []struct { | |||
level RecoveryLevel | |||
version int | |||
numDataBits int | |||
expected int | |||
}{ | |||
{ | |||
Low, | |||
1, | |||
0, | |||
0, | |||
}, { | |||
Low, | |||
1, | |||
1, | |||
7, | |||
}, { | |||
Low, | |||
1, | |||
7, | |||
1, | |||
}, { | |||
Low, | |||
1, | |||
8, | |||
0, | |||
}, | |||
} | |||
for i, test := range tests { | |||
var v *qrCodeVersion | |||
v = getQRCodeVersion(test.level, test.version) | |||
result := v.numBitsToPadToCodeword(test.numDataBits) | |||
if result != test.expected { | |||
t.Errorf("numBitsToPadToCodeword test %d (version=%d numDataBits=%d), got %d, expected %d", | |||
i, test.version, test.numDataBits, result, test.expected) | |||
} | |||
} | |||
} |