2020-09-01 08:06:37 -07:00
|
|
|
package qr
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-09-02 03:46:27 -07:00
|
|
|
encoder "github.com/skip2/go-qrcode"
|
|
|
|
"image"
|
2020-09-29 08:16:20 -07:00
|
|
|
"image/draw"
|
|
|
|
"image/gif"
|
2020-09-02 03:46:27 -07:00
|
|
|
"math/rand"
|
|
|
|
"os"
|
|
|
|
"reflect"
|
2020-09-01 08:06:37 -07:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2020-09-02 03:46:27 -07:00
|
|
|
type TestQrProcessor struct {
|
2020-09-29 08:16:20 -07:00
|
|
|
qr string
|
|
|
|
chunkSize int
|
2020-09-02 03:46:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewTestQRProcessor() *TestQrProcessor {
|
|
|
|
return &TestQrProcessor{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *TestQrProcessor) ReadQR() ([]byte, error) {
|
2020-09-29 08:16:20 -07:00
|
|
|
file, err := os.Open(p.qr)
|
2020-09-02 03:46:27 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
2020-09-29 08:16:20 -07:00
|
|
|
decodedGIF, err := gif.DecodeAll(file)
|
2020-09-02 03:46:27 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-29 08:16:20 -07:00
|
|
|
chunks := make([]*chunk, 0)
|
|
|
|
decodedChunksCount := uint(0)
|
|
|
|
for _, frame := range decodedGIF.Image {
|
|
|
|
data, err := ReadDataFromQR(frame)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
decodedChunk, err := decodeChunk(data)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if cap(chunks) == 0 {
|
|
|
|
chunks = make([]*chunk, decodedChunk.Total)
|
|
|
|
}
|
|
|
|
if chunks[decodedChunk.Index] != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
chunks[decodedChunk.Index] = decodedChunk
|
|
|
|
decodedChunksCount++
|
|
|
|
if decodedChunksCount == decodedChunk.Total {
|
|
|
|
break
|
|
|
|
}
|
2020-09-02 03:46:27 -07:00
|
|
|
}
|
2020-09-29 08:16:20 -07:00
|
|
|
data := make([]byte, 0)
|
|
|
|
for _, c := range chunks {
|
|
|
|
data = append(data, c.Data...)
|
|
|
|
}
|
|
|
|
if err = os.Remove(p.qr); err != nil {
|
2020-09-02 03:46:27 -07:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *TestQrProcessor) WriteQR(path string, data []byte) error {
|
2020-09-29 08:16:20 -07:00
|
|
|
chunks, err := DataToChunks(data, p.chunkSize)
|
2020-09-02 03:46:27 -07:00
|
|
|
if err != nil {
|
2020-09-29 08:16:20 -07:00
|
|
|
return fmt.Errorf("failed to divide data on chunks: %w", err)
|
2020-09-02 03:46:27 -07:00
|
|
|
}
|
2020-09-29 08:16:20 -07:00
|
|
|
outGif := &gif.GIF{}
|
|
|
|
for _, c := range chunks {
|
|
|
|
code, err := encoder.New(string(c), encoder.Medium)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create a QR code: %w", err)
|
|
|
|
}
|
|
|
|
frame := code.Image(512)
|
|
|
|
bounds := frame.Bounds()
|
|
|
|
palettedImage := image.NewPaletted(bounds, palette)
|
|
|
|
draw.Draw(palettedImage, palettedImage.Rect, frame, bounds.Min, draw.Src)
|
2020-09-02 03:46:27 -07:00
|
|
|
|
2020-09-29 08:16:20 -07:00
|
|
|
outGif.Image = append(outGif.Image, palettedImage)
|
|
|
|
outGif.Delay = append(outGif.Delay, 10)
|
|
|
|
}
|
|
|
|
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to open file: %w", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
if err := gif.EncodeAll(f, outGif); err != nil {
|
|
|
|
return fmt.Errorf("failed to encode qr gif: %w", err)
|
|
|
|
}
|
|
|
|
p.qr = path
|
2020-09-02 03:46:27 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func genBytes(n int) []byte {
|
|
|
|
data := make([]byte, n)
|
|
|
|
if _, err := rand.Read(data); err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestReadDataFromQRChunks(t *testing.T) {
|
|
|
|
N := 5000
|
|
|
|
|
|
|
|
data := genBytes(N)
|
|
|
|
|
|
|
|
p := NewTestQRProcessor()
|
2020-09-29 08:16:20 -07:00
|
|
|
p.chunkSize = 128
|
2020-09-02 03:46:27 -07:00
|
|
|
|
2020-09-29 08:16:20 -07:00
|
|
|
if err := p.WriteQR("/tmp/test_gif.gif", data); err != nil {
|
|
|
|
t.Fatalf(err.Error())
|
2020-09-01 08:06:37 -07:00
|
|
|
}
|
|
|
|
|
2020-09-29 08:16:20 -07:00
|
|
|
recoveredDataFromQRChunks, err := p.ReadQR()
|
2020-09-01 08:06:37 -07:00
|
|
|
if err != nil {
|
2020-09-02 03:46:27 -07:00
|
|
|
t.Fatalf(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(data, recoveredDataFromQRChunks) {
|
|
|
|
t.Fatal("recovered data from chunks and initial data are not equal!")
|
2020-09-01 08:06:37 -07:00
|
|
|
}
|
|
|
|
}
|