forked from xi/go-tinyqr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathqrcode.go
153 lines (128 loc) · 2.93 KB
/
qrcode.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package qrcode
import (
"bytes"
"errors"
"fmt"
)
func getVersion(data []byte) *version {
var numByteCharCountBits int
for _, v := range versions {
if v.version < 10 {
numByteCharCountBits = 8
} else {
numByteCharCountBits = 16
}
if v.numDataBits() >= len(data)*8+8+numByteCharCountBits {
return &v
}
}
return nil
}
func encodeContent(data []byte, version *version) []byte {
encoded := NewBitset()
encoded.Write(0b0100, 4)
if version.version < 10 {
encoded.Write(uint(len(data)), 8)
} else {
encoded.Write(uint(len(data)), 16)
}
for _, b := range data {
encoded.Write(uint(b), 8)
}
encoded.Write(0, 4)
for encoded.Length < version.numDataBits() {
encoded.Write(0b11101100, 8)
if encoded.Length < version.numDataBits() {
encoded.Write(0b00010001, 8)
}
}
return encoded.Bytes
}
func encodeBlocks(data []byte, version *version) *Bitset {
result := NewBitset()
content := make([][]byte, 0)
ecc := make([][]byte, 0)
start := 0
for _, g := range version.groups {
numECCodeWords := g.numCodewords - g.numDataCodewords
for j := 0; j < g.numBlocks; j++ {
end := start + g.numDataCodewords
content = append(content, data[start:end])
ecc = append(ecc, getErrorCorrection(data[start:end], numECCodeWords))
start = end
}
}
// Interleave the blocks.
working := true
for i := 0; working; i += 1 {
working = false
for _, c := range content {
if i < len(c) {
result.Write(uint(c[i]), 8)
working = true
}
}
}
working = true
for i := 0; working; i += 1 {
working = false
for _, c := range ecc {
if i < len(c) {
result.Write(uint(c[i]), 8)
working = true
}
}
}
result.Write(0, version.numRemainderBits)
return result
}
func terminal(b *bitmap, padding int) string {
var buf bytes.Buffer
// if there is an odd number of rows, just shorten the final margin
for y := -padding; y+1 < b.size+padding; y += 2 {
for x := -padding; x < b.size+padding; x += 1 {
if y < 0 || x < 0 || y >= b.size || x >= b.size {
buf.WriteString("█")
} else if y+1 == b.size {
if b.get(x, y) {
buf.WriteString("▄")
} else {
buf.WriteString("█")
}
} else if b.get(x, y) == b.get(x, y+1) {
if b.get(x, y) {
buf.WriteString(" ")
} else {
buf.WriteString("█")
}
} else {
if b.get(x, y) {
buf.WriteString("▄")
} else {
buf.WriteString("▀")
}
}
}
buf.WriteString("\n")
}
return buf.String()
}
func Print(content string) error {
output, err := GetString(content)
if err != nil {
return err
}
fmt.Print(output)
return nil
}
func GetString(content string) (string, error) {
bytes := []byte(content)
version := getVersion(bytes)
if version == nil {
return "", errors.New("content too long to encode")
}
encodedContent := encodeContent(bytes, version)
encodedBlocks := encodeBlocks(encodedContent, version)
bitmap := render(encodedBlocks, version)
return terminal(bitmap, 2), nil
}