feat: Waku v2 bridge

Issue #12610
This commit is contained in:
Michal Iskierko
2023-11-12 13:29:38 +01:00
parent 56e7bd01ca
commit 6d31343205
6716 changed files with 1982502 additions and 5891 deletions

16
vendor/github.com/yeqown/go-qrcode/v2/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,16 @@
vendor/
draft/
testdata/
js/
.idea/
.vscode/
# example/
*.png
default.jpeg
# *.json
*.log
.DS_store
tmp.png
go.sum

21
vendor/github.com/yeqown/go-qrcode/v2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 yeqown
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.

14
vendor/github.com/yeqown/go-qrcode/v2/Makefile generated vendored Normal file
View File

@@ -0,0 +1,14 @@
release: release-osx release-linux
release-osx:
- mkdir -p draft/osx
GOOS=darwin GOARCH=amd64 go build -o draft/osx/go-qrcode ./cmd/go-qrcode.go
cd draft/osx && tar -zcvf ../go-qrcode.osx.tar.gz .
release-linux:
- mkdir -p draft/linux
GOOS=linux GOARCH=amd64 go build -o draft/linux/go-qrcode ./cmd/go-qrcode.go
cd draft/linux && tar -zcvf ../go-qrcode.linux.tar.gz .
test-all:
go test -v --count=1 ./...

139
vendor/github.com/yeqown/go-qrcode/v2/README.md generated vendored Normal file
View File

@@ -0,0 +1,139 @@
# go-qrcode #
[![Go Report Card](https://goreportcard.com/badge/github.com/yeqown/go-qrcode)](https://goreportcard.com/report/github.com/yeqown/go-qrcode)
[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/yeqown/go-qrcode/v2)
[![Go](https://github.com/yeqown/go-qrcode/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/yeqown/go-qrcode/actions/workflows/go.yml) ![](https://changkun.de/urlstat?mode=github&repo=yeqown/go-qrcode)
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/yeqown/go-qrcode)
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/yeqown/go-qrcode)
[![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
<img src="./assets/repository_qrcode.png" width="100px" align="right"/>
QR code (abbreviated from Quick Response Code) is the trademark for a type of matrix barcode (or two-dimensional barcode) first designed in 1994 for the automotive industry in Japan. A barcode is a machine-readable optical label that contains information about the item to which it is attached. A QR code uses four standardized encoding modes (numeric, alphanumeric, byte/binary, and kanji) to store data efficiently; extensions may also be used
### Features
- [x] Normally generate QR code across `version 1` to `version 40`.
- [x] Automatically analyze QR version by source text.
- [x] Specifying cell shape allowably with `WithCustomShape`, `WithCircleShape` (default is `rectangle`)
- [x] Specifying output file's format with `WithBuiltinImageEncoder`, `WithCustomImageEncoder` (default is `JPEG`)
- [x] Not only shape of cell, but also color of QR Code background and foreground color.
- [x] `WithLogoImage`, `WithLogoImageFilePNG`, `WithLogoImageFileJPEG` help you add an icon at the central of QR Code.
- [x] `WithBorderWidth` allows to specify any width of 4 sides around the qrcode.
- [x] `WebAssembly` support, check out the [Example](./example/webassembly/README.md) and [README](cmd/wasm/README.md) for more detail.
- [x] support Halftone QR Codes, check out the [Example](./example/with-halftone).
### Install
```sh
go get -u github.com/yeqown/go-qrcode/v2
```
### Quick Start
link to [CODE](./example/main.go)
```go
package main
import (
"github.com/yeqown/go-qrcode/v2"
"github.com/yeqown/go-qrcode/writer/standard"
)
func main() {
qrc, err := qrcode.New("https://github.com/yeqown/go-qrcode")
if err != nil {
fmt.Printf("could not generate QRCode: %v", err)
return
}
w, err := standard.New("../assets/repo-qrcode.jpeg")
if err != nil {
fmt.Printf("standard.New failed: %v", err)
return
}
// save file
if err = qrc.Save(w); err != nil {
fmt.Printf("could not save image: %v", err)
}
}
```
### Options
```go
const (
// EncModeNone mode ...
EncModeNone encMode = 1 << iota
// EncModeNumeric mode ...
EncModeNumeric
// EncModeAlphanumeric mode ...
EncModeAlphanumeric
// EncModeByte mode ...
EncModeByte
// EncModeJP mode ...
EncModeJP
)
// WithEncodingMode sets the encoding mode.
func WithEncodingMode(mode encMode) EncodeOption {}
const (
// ErrorCorrectionLow :Level L: 7% error recovery.
ErrorCorrectionLow ecLevel = iota + 1
// ErrorCorrectionMedium :Level M: 15% error recovery. Good default choice.
ErrorCorrectionMedium
// ErrorCorrectionQuart :Level Q: 25% error recovery.
ErrorCorrectionQuart
// ErrorCorrectionHighest :Level H: 30% error recovery.
ErrorCorrectionHighest
)
// WithErrorCorrectionLevel sets the error correction level.
func WithErrorCorrectionLevel(ecLevel ecLevel) EncodeOption {}
```
following are some shots:
<div>
<img src="./assets/example_fg_bg.jpeg" width="160px" align="left" title="with bg-fg color">
<img src="./assets/example_logo.jpeg" width="160px" align="left" title="with logo image">
<img src="./assets/example_circle.jpeg" width="160px" align="left" title="customize block shape">
<img src="./assets/example_transparent.png" width="160px" title="with transparent bg">
</div>
<div>
<img src="./assets/example_halftone0.jpeg" width="160px" align="left" title="halftone0">
<img src="./assets/example_halftone1.jpeg" width="160px" align="left" title="halftone1">
<img src="./assets/example_halftone2.jpeg" width="160px" align="left" title="halftone2">
<img src="./assets/example_halftone3.jpeg" width="160px" title="halftone3">
</div>
<br>
### Built-in Writers
- [Standard Writer](./writer/standard/README.md), prints QRCode into file and stream
- [Terminal Writer](./writer/terminal/README.md), prints QRCode into terminal
Of course, you can also code your own writer, just implement [Writer](./writer/README.md) interface.
### Migrating from v1
`go-qrcode.v2` is a major upgrade from v1, and it is not backward compatible. `v2` redesigned
the API, and it is more flexible and powerful. Features are split into different modules (according to functionality).
- github.com/yeqown/go-qrcode/v2 **_core_**
- github.com/yeqown/go-qrcode/writer/standard **_writer/imageFile_**
- github.com/yeqown/go-qrcode/writer/terminal **_writer/terminal_**
Check [example/migrating-from-v1](./example/migrating-from-v1/main.go) for more details.
### Links
* [QRCode Tourist](https://www.thonky.com/qr-code-tutorial/)
* [QRCode Wiki](https://en.wikipedia.org/wiki/QR_code)
* [二维码详解 (QRCode analysis in CN-zh)](https://zhuanlan.zhihu.com/p/21463650)
* [数据编码 (How to encode data payload in QRCode in CN-zh)](https://zhuanlan.zhihu.com/p/25432676)

109
vendor/github.com/yeqown/go-qrcode/v2/debug.go generated vendored Normal file
View File

@@ -0,0 +1,109 @@
package qrcode
import (
"fmt"
"image"
"image/color"
"image/jpeg"
"io"
"log"
"os"
"sync"
)
var (
// _debug mode switch, true means enable debug mode, false means disable.
_debug = false
_debugOnce sync.Once
)
func debugEnabled() bool {
// load debug switch from environment only once.
_debugOnce.Do(func() {
switch os.Getenv("QRCODE_DEBUG") {
case "1", "true", "TRUE", "enabled", "ENABLED":
_debug = true
}
})
return _debug
}
// SetDebugMode open debug switch, you can also enable debug by runtime
// environments variables: QRCODE_DEBUG=1 [1, true, TRUE, enabled, ENABLED] which is recommended.
func SetDebugMode() {
_debug = true
}
func debugLogf(format string, v ...interface{}) {
if !debugEnabled() {
return
}
log.Printf("[qrcode] DEBUG: "+format, v...)
}
func debugDraw(filename string, mat Matrix) error {
if !debugEnabled() {
return nil
}
fd, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return fmt.Errorf("debugDraw open file %s failed: %w", filename, err)
}
defer func(fd *os.File) {
_ = fd.Close()
}(fd)
return debugDrawTo(fd, mat)
}
func debugDrawTo(w io.Writer, mat Matrix) error {
if !debugEnabled() {
return nil
}
// width as image width, height as image height
padding := 10
blockWidth := 10
width := mat.Width()*blockWidth + 2*padding
height := width
img := image.NewGray16(image.Rect(0, 0, width, height))
rectangle := func(x1, y1 int, x2, y2 int, img *image.Gray16, c color.Gray16) {
for x := x1; x < x2; x++ {
for y := y1; y < y2; y++ {
img.SetGray16(x, y, c)
}
}
}
// background
rectangle(0, 0, width, height, img, color.Gray16{Y: 0xff12})
mat.iter(IterDirection_COLUMN, func(x int, y int, v qrvalue) {
sx := x*blockWidth + padding
sy := y*blockWidth + padding
es := (x+1)*blockWidth + padding
ey := (y+1)*blockWidth + padding
// choose color, false use black, others use black on white background
var gray color.Gray16
switch v.qrbool() {
case false:
gray = color.White
default:
gray = color.Black
}
rectangle(sx, sy, es, ey, img, gray)
})
// save to writer
err := jpeg.Encode(w, img, nil)
if err != nil {
return fmt.Errorf("debugDrawTo: encode image in JPEG failed: %v", err)
}
return nil
}

350
vendor/github.com/yeqown/go-qrcode/v2/encoder.go generated vendored Normal file
View File

@@ -0,0 +1,350 @@
// Package qrcode ...
// encoder.go working for data encoding
package qrcode
import (
"fmt"
"log"
"github.com/yeqown/reedsolomon/binary"
)
// encMode ...
type encMode uint
const (
// a qrbool of EncModeAuto will trigger a detection of the letter set from the input data,
EncModeAuto = 0
// EncModeNone mode ...
EncModeNone encMode = 1 << iota
// EncModeNumeric mode ...
EncModeNumeric
// EncModeAlphanumeric mode ...
EncModeAlphanumeric
// EncModeByte mode ...
EncModeByte
// EncModeJP mode ...
EncModeJP
)
var (
paddingByte1, _ = binary.NewFromBinaryString("11101100")
paddingByte2, _ = binary.NewFromBinaryString("00010001")
)
// getEncModeName ...
func getEncModeName(mode encMode) string {
switch mode {
case EncModeNone:
return "none"
case EncModeNumeric:
return "numeric"
case EncModeAlphanumeric:
return "alphanumeric"
case EncModeByte:
return "byte"
case EncModeJP:
return "japan"
default:
return "unknown"
}
}
// getEncodeModeIndicator ...
func getEncodeModeIndicator(mode encMode) *binary.Binary {
switch mode {
case EncModeNumeric:
return binary.New(false, false, false, true)
case EncModeAlphanumeric:
return binary.New(false, false, true, false)
case EncModeByte:
return binary.New(false, true, false, false)
case EncModeJP:
return binary.New(true, false, false, false)
default:
panic("no indicator")
}
}
// encoder ... data to bit stream ...
type encoder struct {
// self init
dst *binary.Binary
data []byte // raw input data
// initial params
mode encMode // encode mode
ecLv ecLevel // error correction level
// self load
version version // QR version ref
}
func newEncoder(m encMode, ec ecLevel, v version) *encoder {
return &encoder{
dst: nil,
data: nil,
mode: m,
ecLv: ec,
version: v,
}
}
// Encode ...
// 1. encode raw data into bitset
// 2. append _defaultPadding data
//
func (e *encoder) Encode(byts []byte) (*binary.Binary, error) {
e.dst = binary.New()
e.data = byts
// append mode indicator symbol
indicator := getEncodeModeIndicator(e.mode)
e.dst.Append(indicator)
// append chars length counter bits symbol
e.dst.AppendUint32(uint32(len(byts)), e.charCountBits())
// encode data with specified mode
switch e.mode {
case EncModeNumeric:
e.encodeNumeric()
case EncModeAlphanumeric:
e.encodeAlphanumeric()
case EncModeByte:
e.encodeByte()
case EncModeJP:
panic("this has not been finished")
}
// fill and _defaultPadding bits
e.breakUpInto8bit()
return e.dst, nil
}
// 0001b mode indicator
func (e *encoder) encodeNumeric() {
if e.dst == nil {
log.Println("e.dst is nil")
return
}
for i := 0; i < len(e.data); i += 3 {
charsRemaining := len(e.data) - i
var value uint32
bitsUsed := 1
for j := 0; j < charsRemaining && j < 3; j++ {
value *= 10
value += uint32(e.data[i+j] - 0x30)
bitsUsed += 3
}
e.dst.AppendUint32(value, bitsUsed)
}
}
// 0010b mode indicator
func (e *encoder) encodeAlphanumeric() {
if e.dst == nil {
log.Println("e.dst is nil")
return
}
for i := 0; i < len(e.data); i += 2 {
charsRemaining := len(e.data) - i
var value uint32
for j := 0; j < charsRemaining && j < 2; j++ {
value *= 45
value += encodeAlphanumericCharacter(e.data[i+j])
}
bitsUsed := 6
if charsRemaining > 1 {
bitsUsed = 11
}
e.dst.AppendUint32(value, bitsUsed)
}
}
// 0100b mode indicator
func (e *encoder) encodeByte() {
if e.dst == nil {
log.Println("e.dst is nil")
return
}
for _, b := range e.data {
_ = e.dst.AppendByte(b, 8)
}
}
// Break Up into 8-bit Codewords and Add Pad Bytes if Necessary
func (e *encoder) breakUpInto8bit() {
// fill ending code (max 4bit)
// depends on max capacity of current version and EC level
maxCap := e.version.NumTotalCodewords() * 8
if less := maxCap - e.dst.Len(); less < 0 {
err := fmt.Errorf(
"wrong version(%d) cap(%d bits) and could not contain all bits: %d bits",
e.version.Ver, maxCap, e.dst.Len(),
)
panic(err)
} else if less < 4 {
e.dst.AppendNumBools(less, false)
} else {
e.dst.AppendNumBools(4, false)
}
// append `0` to be 8 times bits length
if mod := e.dst.Len() % 8; mod != 0 {
e.dst.AppendNumBools(8-mod, false)
}
// _defaultPadding bytes
// _defaultPadding byte 11101100 00010001
if n := maxCap - e.dst.Len(); n > 0 {
debugLogf("maxCap: %d, len: %d, less: %d", maxCap, e.dst.Len(), n)
for i := 1; i <= (n / 8); i++ {
if i%2 == 1 {
e.dst.Append(paddingByte1)
} else {
e.dst.Append(paddingByte2)
}
}
}
}
// 字符计数指示符位长字典
var charCountMap = map[string]int{
"9_numeric": 10,
"9_alphanumeric": 9,
"9_byte": 8,
"9_japan": 8,
"26_numeric": 12,
"26_alphanumeric": 11,
"26_byte": 16,
"26_japan": 10,
"40_numeric": 14,
"40_alphanumeric": 13,
"40_byte": 16,
"40_japan": 12,
}
// charCountBits
func (e *encoder) charCountBits() int {
var lv int
if v := e.version.Ver; v <= 9 {
lv = 9
} else if v <= 26 {
lv = 26
} else {
lv = 40
}
pos := fmt.Sprintf("%d_%s", lv, getEncModeName(e.mode))
return charCountMap[pos]
}
// 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 %c", v)
}
return 0
}
// analyzeEncFunc returns true is current byte matched in current mode,
// otherwise means you should use a bigger character set to check.
type analyzeEncFunc func(byte) bool
// analyzeEncodeModeFromRaw try to detect letter set of input data,
// so that encoder can determine which mode should be use.
// reference: https://en.wikipedia.org/wiki/QR_code
//
// case1: only numbers, use EncModeNumeric.
// case2: could not use EncModeNumeric, but you can find all of them in character mapping, use EncModeAlphanumeric.
// case3: could not use EncModeAlphanumeric, but you can find all of them in ISO-8859-1 character set, use EncModeByte.
// case4: could not use EncModeByte, use EncModeJP, no more choice.
//
func analyzeEncodeModeFromRaw(raw []byte) encMode {
analyzeFnMapping := map[encMode]analyzeEncFunc{
EncModeNumeric: analyzeNum,
EncModeAlphanumeric: analyzeAlphaNum,
EncModeByte: nil,
EncModeJP: nil,
}
var (
f analyzeEncFunc
mode = EncModeNumeric
)
// loop to check each character in raw data,
// from low mode to higher while current mode could bearing the input data.
for _, byt := range raw {
reAnalyze:
if f = analyzeFnMapping[mode]; f == nil {
break
}
// issue#28 @borislavone reports this bug.
// FIXED(@yeqown): next encMode analyzeVersionAuto func did not check the previous byte,
// add goto statement to reanalyze previous byte which can't be analyzed in last encMode.
if !f(byt) {
mode <<= 1
goto reAnalyze
}
}
return mode
}
// analyzeNum is byt in num encMode
func analyzeNum(byt byte) bool {
return byt >= '0' && byt <= '9'
}
// analyzeAlphaNum is byt in alpha number
func analyzeAlphaNum(byt byte) bool {
if (byt >= '0' && byt <= '9') || (byt >= 'A' && byt <= 'Z') {
return true
}
switch byt {
case ' ', '$', '%', '*', '+', '-', '.', '/', ':':
return true
}
return false
}
//// analyzeByte is byt in bytes.
//func analyzeByte(byt byte) qrbool {
// return false
//}

View File

@@ -0,0 +1,71 @@
package qrcode
type EncodeOption interface {
apply(option *encodingOption)
}
// DefaultEncodingOption with EncMode = EncModeAuto, EcLevel = ErrorCorrectionQuart
func DefaultEncodingOption() *encodingOption {
return &encodingOption{
EncMode: EncModeAuto,
EcLevel: ErrorCorrectionQuart,
}
}
type encodingOption struct {
// Version of target QR code.
Version int
// EncMode specifies which encMode to use
EncMode encMode
// EcLevel specifies which ecLevel to use
EcLevel ecLevel
// PS: The version (which implicitly defines the byte capacity of the qrcode) is dynamically selected at runtime
}
type fnEncodingOption struct {
fn func(*encodingOption)
}
func (f fnEncodingOption) apply(option *encodingOption) {
f.fn(option)
}
func newFnEncodingOption(fn func(*encodingOption)) fnEncodingOption {
return fnEncodingOption{fn: fn}
}
// WithEncodingMode sets the encoding mode.
func WithEncodingMode(mode encMode) EncodeOption {
return newFnEncodingOption(func(option *encodingOption) {
if name := getEncModeName(mode); name == "" {
return
}
option.EncMode = mode
})
}
// WithErrorCorrectionLevel sets the error correction level.
func WithErrorCorrectionLevel(ecLevel ecLevel) EncodeOption {
return newFnEncodingOption(func(option *encodingOption) {
if ecLevel < ErrorCorrectionLow || ecLevel > ErrorCorrectionHighest {
return
}
option.EcLevel = ecLevel
})
}
// WithVersion sets the version of target QR code.
func WithVersion(version int) EncodeOption {
return newFnEncodingOption(func(option *encodingOption) {
if version < 1 || version > 40 {
return
}
option.Version = version
})
}

59
vendor/github.com/yeqown/go-qrcode/v2/kmp_variant.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
package qrcode
// kmp is variant of kmp algorithm to count the pattern been in
// src slice.
// DONE(@yeqown): implement this in generic way.
func kmp[v comparable](src, pattern []v, next []int) (count int) {
if next == nil {
next = kmpGetNext(pattern)
}
slen := len(src)
plen := len(pattern)
i := 0 // cursor of src
j := 0 // cursor of pattern
loop:
for i < slen && j < plen {
if j == -1 || src[i] == pattern[j] {
i++
j++
} else {
j = next[j]
}
}
if j == plen {
if i-j >= 0 {
count++
}
// reset cursor to count duplicate pattern.
// such as: "aaaa" and "aa", we want 3 rather than 2.
i -= plen - 1
j = 0
goto loop
}
return count
}
func kmpGetNext[v comparable](pattern []v) []int {
fail := make([]int, len(pattern))
fail[0] = -1
j := 0
k := -1
for j < len(pattern)-1 {
if k == -1 || pattern[j] == pattern[k] {
k++
j++
fail[j] = k
} else {
k = fail[k]
}
}
return fail
}

138
vendor/github.com/yeqown/go-qrcode/v2/mask.go generated vendored Normal file
View File

@@ -0,0 +1,138 @@
package qrcode
// maskPatternModulo ...
// mask Pattern ref to: https://www.thonky.com/qr-code-tutorial/mask-patterns
type maskPatternModulo uint32
const (
// modulo0 (x+y) mod 2 == 0
modulo0 maskPatternModulo = iota
// modulo1 (x) mod 2 == 0
modulo1
// modulo2 (y) mod 3 == 0
modulo2
// modulo3 (x+y) mod 3 == 0
modulo3
// modulo4 (floor (x/ 2) + floor (y/ 3) mod 2 == 0
modulo4
// modulo5 (x * y) mod 2) + (x * y) mod 3) == 0
modulo5
// modulo6 (x * y) mod 2) + (x * y) mod 3) mod 2 == 0
modulo6
// modulo7 (x + y) mod 2) + (x * y) mod 3) mod 2 == 0
modulo7
)
type mask struct {
mat *Matrix // matrix
mode maskPatternModulo // mode
moduloFn moduloFunc // moduloFn masking function
}
// newMask ...
func newMask(mat *Matrix, mode maskPatternModulo) *mask {
m := &mask{
mat: mat.Copy(),
mode: mode,
moduloFn: getModuloFunc(mode),
}
m.masking()
return m
}
// moduloFunc to define what's modulo func
type moduloFunc func(int, int) bool
func getModuloFunc(mode maskPatternModulo) (f moduloFunc) {
f = nil
switch mode {
case modulo0:
f = modulo0Func
case modulo1:
f = modulo1Func
case modulo2:
f = modulo2Func
case modulo3:
f = modulo3Func
case modulo4:
f = modulo4Func
case modulo5:
f = modulo5Func
case modulo6:
f = modulo6Func
case modulo7:
f = modulo7Func
}
return
}
// init generate maks by mode
func (m *mask) masking() {
moduloFn := m.moduloFn
if moduloFn == nil {
panic("impossible panic, contact maintainer plz")
}
m.mat.iter(IterDirection_COLUMN, func(x, y int, s qrvalue) {
// skip the function modules
if v, _ := m.mat.at(x, y); v.qrtype() != QRType_INIT {
_ = m.mat.set(x, y, QRValue_INIT_V0)
return
}
if moduloFn(x, y) {
_ = m.mat.set(x, y, QRValue_DATA_V1)
} else {
_ = m.mat.set(x, y, QRValue_DATA_V0)
}
})
}
// modulo0Func for maskPattern function
// modulo0 (x+y) mod 2 == 0
func modulo0Func(x, y int) bool {
return (x+y)%2 == 0
}
// modulo1Func for maskPattern function
// modulo1 (y) mod 2 == 0
func modulo1Func(x, y int) bool {
return y%2 == 0
}
// modulo2Func for maskPattern function
// modulo2 (x) mod 3 == 0
func modulo2Func(x, y int) bool {
return x%3 == 0
}
// modulo3Func for maskPattern function
// modulo3 (x+y) mod 3 == 0
func modulo3Func(x, y int) bool {
return (x+y)%3 == 0
}
// modulo4Func for maskPattern function
// modulo4 (floor (x/ 2) + floor (y/ 3) mod 2 == 0
func modulo4Func(x, y int) bool {
return (x/3+y/2)%2 == 0
}
// modulo5Func for maskPattern function
// modulo5 (x * y) mod 2 + (x * y) mod 3 == 0
func modulo5Func(x, y int) bool {
return (x*y)%2+(x*y)%3 == 0
}
// modulo6Func for maskPattern function
// modulo6 (x * y) mod 2) + (x * y) mod 3) mod 2 == 0
func modulo6Func(x, y int) bool {
return ((x*y)%2+(x*y)%3)%2 == 0
}
// modulo7Func for maskPattern function
// modulo7 (x + y) mod 2) + (x * y) mod 3) mod 2 == 0
func modulo7Func(x, y int) bool {
return ((x+y)%2+(x*y)%3)%2 == 0
}

View File

@@ -0,0 +1,172 @@
package qrcode
import (
"math"
)
// evaluation calculate a score after masking matrix.
//
// reference:
// - https://www.thonky.com/qr-code-tutorial/data-masking#Determining-the-Best-Mask
func evaluation(mat *Matrix) (score int) {
debugLogf("calculate maskScore starting")
score1 := rule1(mat)
score2 := rule2(mat)
score3 := rule3(mat)
score4 := rule4(mat)
score = score1 + score2 + score3 + score4
debugLogf("maskScore: rule1=%d, rule2=%d, rule3=%d, rule4=%d", score1, score2, score3, score4)
return score
}
// check each row one-by-one. If there are five consecutive modules of the same color,
// add 3 to the penalty. If there are more modules of the same color after the first five,
// add 1 for each additional module of the same color. Afterward, check each column one-by-one,
// checking for the same condition. Add the horizontal and vertical total to obtain penalty score
func rule1(mat *Matrix) (score int) {
// prerequisites:
// mat.Width() == mat.Height()
if mat.Width() != mat.Height() {
debugLogf("matrix width != height, skip rule1")
return math.MaxInt32
}
dimension := mat.Width()
scoreLine := func(arr []qrvalue) int {
lScore, cnt, cur := 0, 0, QRValue_INIT_V0
for _, v := range arr {
if !samestate(v, cur) {
cur = v
cnt = 1
continue
}
cnt++
if cnt == 5 {
lScore += 3
} else if cnt > 5 {
lScore++
}
}
return lScore
}
for cur := 0; cur < dimension; cur++ {
row := mat.Row(cur)
col := mat.Col(cur)
score += scoreLine(row)
score += scoreLine(col)
}
return score
}
// rule2
// look for areas of the same color that are at least 2x2 modules or larger.
// The QR code specification says that for a solid-color block of size m × n,
// the penalty score is 3 × (m - 1) × (n - 1).
func rule2(mat *Matrix) int {
var (
score int
s0, s1, s2, s3 qrvalue
)
for x := 0; x < mat.Width()-1; x++ {
for y := 0; y < mat.Height()-1; y++ {
s0, _ = mat.at(x, y)
s1, _ = mat.at(x+1, y)
s2, _ = mat.at(x, y+1)
s3, _ = mat.at(x+1, y+1)
if s0 == s1 && s2 == s3 && s1 == s2 {
score += 3
}
}
}
return score
}
// rule3 calculate punishment score in rule3, find pattern in QR Code matrix.
// Looks for patterns of dark-light-dark-dark-dark-light-dark that have four
// light modules on either side. In other words, it looks for any of the
// following two patterns: 1011101 0000 or 0000 1011101.
//
// Each time this pattern is found, add 40 to the penalty score.
func rule3(mat *Matrix) (score int) {
var (
pattern1 = binaryToQRValueSlice("1011101 0000")
pattern2 = binaryToQRValueSlice("0000 1011101")
pattern1Next = kmpGetNext(pattern1)
pattern2Next = kmpGetNext(pattern2)
)
// prerequisites:
//
// mat.Width() == mat.Height()
if mat.Width() != mat.Height() {
debugLogf("rule3 got matrix but not matched prerequisites")
return math.MaxInt32
}
dimension := mat.Width()
for i := 0; i < dimension; i++ {
col := mat.Col(i)
row := mat.Row(i)
// DONE(@yeqown): statePattern1 and statePattern2 are fixed, so maybe kmpGetNext
// could cache result to speed up.
score += 40 * kmp(col, pattern1, pattern1Next)
score += 40 * kmp(col, pattern2, pattern2Next)
score += 40 * kmp(row, pattern1, pattern1Next)
score += 40 * kmp(row, pattern2, pattern2Next)
}
return score
}
// rule4 is based on the ratio of light modules to dark modules:
//
// 1. Count the total number of modules in the matrix.
// 2. Count how many dark modules there are in the matrix.
// 3. Calculate the percent of modules in the matrix that are dark: (darkmodules / totalmodules) * 100
// 4. Determine the previous and next multiple of five of this percent.
// 5. Subtract 50 from each of these multiples of five and take the absolute qrbool of the result.
// 6. Divide each of these by five. For example, 10/5 = 2 and 5/5 = 1.
// 7. Finally, take the smallest of the two numbers and multiply it by 10.
//
func rule4(mat *Matrix) int {
// prerequisites:
//
// mat.Width() == mat.Height()
if mat.Width() != mat.Height() {
debugLogf("rule4 got matrix but not matched prerequisites")
return math.MaxInt32
}
dimension := mat.Width()
dark, total := 0, dimension*dimension
for i := 0; i < dimension; i++ {
col := mat.Col(i)
// count dark modules
for j := 0; j < dimension; j++ {
if samestate(col[j], QRValue_DATA_V1) {
dark++
}
}
}
ratio := (dark * 100) / total // in range [0, 100]
step := 0
if ratio%5 == 0 {
step = 1
}
previous := abs((ratio/5-step)*5 - 50)
next := abs((ratio/5+1-step)*5 - 50)
return min(previous, next) / 5 * 10
}

167
vendor/github.com/yeqown/go-qrcode/v2/matrix.go generated vendored Normal file
View File

@@ -0,0 +1,167 @@
package qrcode
import (
"errors"
"fmt"
)
var (
// ErrorOutRangeOfW x out of range of Width
ErrorOutRangeOfW = errors.New("out of range of width")
// ErrorOutRangeOfH y out of range of Height
ErrorOutRangeOfH = errors.New("out of range of height")
)
// newMatrix generate a matrix with map[][]qrbool
func newMatrix(width, height int) *Matrix {
mat := make([][]qrvalue, width)
for w := 0; w < width; w++ {
mat[w] = make([]qrvalue, height)
}
m := &Matrix{
mat: mat,
width: width,
height: height,
}
m.init()
return m
}
// Matrix is a matrix data type
// width:3 height: 4 for [3][4]int
type Matrix struct {
mat [][]qrvalue
width int
height int
}
// do some init work
func (m *Matrix) init() {
for w := 0; w < m.width; w++ {
for h := 0; h < m.height; h++ {
m.mat[w][h] = QRValue_INIT_V0
}
}
}
// print to stdout
func (m *Matrix) print() {
m.iter(IterDirection_ROW, func(x, y int, s qrvalue) {
fmt.Printf("%s ", s)
if (x + 1) == m.width {
fmt.Println()
}
})
}
// Copy matrix into a new Matrix
func (m *Matrix) Copy() *Matrix {
mat2 := make([][]qrvalue, m.width)
for w := 0; w < m.width; w++ {
mat2[w] = make([]qrvalue, m.height)
copy(mat2[w], m.mat[w])
}
m2 := &Matrix{
width: m.width,
height: m.height,
mat: mat2,
}
return m2
}
// Width ... width
func (m *Matrix) Width() int {
return m.width
}
// Height ... height
func (m *Matrix) Height() int {
return m.height
}
// set [w][h] as true
func (m *Matrix) set(w, h int, c qrvalue) error {
if w >= m.width || w < 0 {
return ErrorOutRangeOfW
}
if h >= m.height || h < 0 {
return ErrorOutRangeOfH
}
m.mat[w][h] = c
return nil
}
// at state qrvalue from matrix with position {x, y}
func (m *Matrix) at(w, h int) (qrvalue, error) {
if w >= m.width || w < 0 {
return QRValue_INIT_V0, ErrorOutRangeOfW
}
if h >= m.height || h < 0 {
return QRValue_INIT_V0, ErrorOutRangeOfH
}
return m.mat[w][h], nil
}
// iterDirection scan matrix direction
type iterDirection uint8
const (
// IterDirection_ROW for row first
IterDirection_ROW iterDirection = iota + 1
// IterDirection_COLUMN for column first
IterDirection_COLUMN
)
// Iterate the Matrix with loop direction IterDirection_ROW major or IterDirection_COLUMN major.
// IterDirection_COLUMN is recommended.
func (m *Matrix) Iterate(direction iterDirection, fn func(x, y int, s QRValue)) {
m.iter(direction, fn)
}
func (m *Matrix) iter(dir iterDirection, visitFn func(x int, y int, v qrvalue)) {
// row direction first
if dir == IterDirection_ROW {
for h := 0; h < m.height; h++ {
for w := 0; w < m.width; w++ {
visitFn(w, h, m.mat[w][h])
}
}
return
}
// column direction first
for w := 0; w < m.width; w++ {
for h := 0; h < m.height; h++ {
visitFn(w, h, m.mat[w][h])
}
}
return
}
// Row return a row of matrix, cur should be y dimension.
func (m *Matrix) Row(cur int) []qrvalue {
if cur >= m.height || cur < 0 {
return nil
}
col := make([]qrvalue, m.height)
for w := 0; w < m.width; w++ {
col[w] = m.mat[w][cur]
}
return col
}
// Col return a slice of column, cur should be x dimension.
func (m *Matrix) Col(cur int) []qrvalue {
if cur >= m.width || cur < 0 {
return nil
}
return m.mat[cur]
}

125
vendor/github.com/yeqown/go-qrcode/v2/matrix_type.go generated vendored Normal file
View File

@@ -0,0 +1,125 @@
package qrcode
type QRType = qrtype
// qrtype
type qrtype uint8
const (
// QRType_INIT represents the initial block state of the matrix
QRType_INIT qrtype = 1 << 1
// QRType_DATA represents the data block state of the matrix
QRType_DATA qrtype = 2 << 1
// QRType_VERSION indicates the version block of matrix
QRType_VERSION qrtype = 3 << 1
// QRType_FORMAT indicates the format block of matrix
QRType_FORMAT qrtype = 4 << 1
// QRType_FINDER indicates the finder block of matrix
QRType_FINDER qrtype = 5 << 1
// QRType_DARK ...
QRType_DARK qrtype = 6 << 1
QRType_SPLITTER qrtype = 7 << 1
QRType_TIMING qrtype = 8 << 1
)
func (s qrtype) String() string {
switch s {
case QRType_INIT:
return "I"
case QRType_DATA:
return "d"
case QRType_VERSION:
return "V"
case QRType_FORMAT:
return "f"
case QRType_FINDER:
return "F"
case QRType_DARK:
return "D"
case QRType_SPLITTER:
return "S"
case QRType_TIMING:
return "T"
}
return "?"
}
type QRValue = qrvalue
func (v QRValue) Type() qrtype {
return v.qrtype()
}
func (v QRValue) IsSet() bool {
return v.qrbool()
}
// qrvalue represents the value of the matrix, it is composed of the qrtype(7bits) and the value(1bits).
// such as: 0b0000,0011 (QRValue_DATA_V1) represents the qrtype is QRType_DATA and the value is 1.
type qrvalue uint8
var (
// QRValue_INIT_V0 represents the value 0
QRValue_INIT_V0 = qrvalue(QRType_INIT | 0)
// QRValue_DATA_V0 represents the block has been set to false
QRValue_DATA_V0 = qrvalue(QRType_DATA | 0)
// QRValue_DATA_V1 represents the block has been set to TRUE
QRValue_DATA_V1 = qrvalue(QRType_DATA | 1)
// QRValue_VERSION_V0 represents the block has been set to false
QRValue_VERSION_V0 = qrvalue(QRType_VERSION | 0)
// QRValue_VERSION_V1 represents the block has been set to TRUE
QRValue_VERSION_V1 = qrvalue(QRType_VERSION | 1)
// QRValue_FORMAT_V0 represents the block has been set to false
QRValue_FORMAT_V0 = qrvalue(QRType_FORMAT | 0)
// QRValue_FORMAT_V1 represents the block has been set to TRUE
QRValue_FORMAT_V1 = qrvalue(QRType_FORMAT | 1)
// QRValue_FINDER_V0 represents the block has been set to false
QRValue_FINDER_V0 = qrvalue(QRType_FINDER | 0)
// QRValue_FINDER_V1 represents the block has been set to TRUE
QRValue_FINDER_V1 = qrvalue(QRType_FINDER | 1)
// QRValue_DARK_V0 represents the block has been set to false
QRValue_DARK_V0 = qrvalue(QRType_DARK | 0)
// QRValue_DARK_V1 represents the block has been set to TRUE
QRValue_DARK_V1 = qrvalue(QRType_DARK | 1)
// QRValue_SPLITTER_V0 represents the block has been set to false
QRValue_SPLITTER_V0 = qrvalue(QRType_SPLITTER | 0)
// QRValue_SPLITTER_V1 represents the block has been set to TRUE
QRValue_SPLITTER_V1 = qrvalue(QRType_SPLITTER | 1)
// QRValue_TIMING_V0 represents the block has been set to false
QRValue_TIMING_V0 = qrvalue(QRType_TIMING | 0)
// QRValue_TIMING_V1 represents the block has been set to TRUE
QRValue_TIMING_V1 = qrvalue(QRType_TIMING | 1)
)
func (v qrvalue) qrtype() qrtype {
return qrtype(v & 0xfe)
}
func (v qrvalue) qrbool() bool {
return v&0x01 == 1
}
func (v qrvalue) String() string {
t := v.qrtype()
if v.qrbool() {
return t.String() + "1"
}
return t.String() + "0"
}
func (v qrvalue) xor(v2 qrvalue) qrvalue {
if v != v2 {
return QRValue_DATA_V1
}
return QRValue_DATA_V0
}

738
vendor/github.com/yeqown/go-qrcode/v2/qrcode.go generated vendored Normal file
View File

@@ -0,0 +1,738 @@
package qrcode
import (
"fmt"
"log"
"math"
"sync"
"github.com/yeqown/reedsolomon"
"github.com/yeqown/reedsolomon/binary"
)
// New generate a QRCode struct to create
func New(text string) (*QRCode, error) {
dst := DefaultEncodingOption()
return build(text, dst)
}
// NewWith generate a QRCode struct with
// specified `ver`(QR version) and `ecLv`(Error Correction level)
func NewWith(text string, opts ...EncodeOption) (*QRCode, error) {
dst := DefaultEncodingOption()
for _, opt := range opts {
opt.apply(dst)
}
return build(text, dst)
}
func build(text string, option *encodingOption) (*QRCode, error) {
qrc := &QRCode{
sourceText: text,
sourceRawBytes: []byte(text),
dataBSet: nil,
mat: nil,
ecBSet: nil,
v: version{},
encodingOption: option,
encoder: nil,
}
// initialize QRCode instance
if err := qrc.init(); err != nil {
return nil, err
}
qrc.masking()
return qrc, nil
}
// QRCode contains fields to generate QRCode matrix, outputImageOptions to Draw image,
// etc.
type QRCode struct {
sourceText string // sourceText input text
sourceRawBytes []byte // raw Data to transfer
dataBSet *binary.Binary // final data bit stream of encode data
mat *Matrix // matrix grid to store final bitmap
ecBSet *binary.Binary // final error correction bitset
encodingOption *encodingOption
encoder *encoder // encoder ptr to call its methods ~
v version // indicate the QR version to encode.
}
func (q *QRCode) Save(w Writer) error {
if w == nil {
w = nonWriter{}
}
defer func() {
if err := w.Close(); err != nil {
log.Printf("[WARNNING] [go-qrcode] close writer failed: %v\n", err)
}
}()
return w.Write(*q.mat)
}
func (q *QRCode) Dimension() int {
if q.mat == nil {
return 0
}
return q.mat.Width()
}
// init fill QRCode instance from settings and sourceText.
func (q *QRCode) init() (err error) {
// choose encode mode (num, alpha num, byte, Japanese)
if q.encodingOption.EncMode == EncModeAuto {
q.encodingOption.EncMode = analyzeEncodeModeFromRaw(q.sourceRawBytes)
}
// choose version
if _, err = q.calcVersion(); err != nil {
return fmt.Errorf("init: calc version failed: %v", err)
}
q.mat = newMatrix(q.v.Dimension(), q.v.Dimension())
_ = q.applyEncoder()
var (
dataBlocks []dataBlock // data encoding blocks
ecBlocks []ecBlock // error correction blocks
)
// data encoding, and be split into blocks
if dataBlocks, err = q.dataEncoding(); err != nil {
return err
}
// generate er bitsets, and also be split into blocks
if ecBlocks, err = q.errorCorrectionEncoding(dataBlocks); err != nil {
return err
}
// arrange data blocks and EC blocks
q.arrangeBits(dataBlocks, ecBlocks)
// append ec bits after data bits
q.dataBSet.Append(q.ecBSet)
// append remainder bits
q.dataBSet.AppendNumBools(q.v.RemainderBits, false)
// initial the 2d matrix
q.prefillMatrix()
return nil
}
// calcVersion
func (q *QRCode) calcVersion() (ver *version, err error) {
var needAnalyze = true
opt := q.encodingOption
if opt.Version >= 1 && opt.Version <= 40 &&
opt.EcLevel >= ErrorCorrectionLow && opt.EcLevel <= ErrorCorrectionHighest {
// only version and EC level are specified, can skip analyzeVersionAuto
needAnalyze = false
}
// automatically parse version
if needAnalyze {
// analyzeVersion the input data to choose to adapt version
analyzed, err2 := analyzeVersion(q.sourceRawBytes, opt.EcLevel, opt.EncMode)
if err2 != nil {
err = fmt.Errorf("calcVersion: analyzeVersionAuto failed: %v", err2)
return nil, err
}
opt.Version = analyzed.Ver
}
q.v = loadVersion(opt.Version, opt.EcLevel)
return
}
// applyEncoder
func (q *QRCode) applyEncoder() error {
q.encoder = newEncoder(q.encodingOption.EncMode, q.encodingOption.EcLevel, q.v)
return nil
}
// dataEncoding ref to:
// https://www.thonky.com/qr-code-tutorial/data-encoding
func (q *QRCode) dataEncoding() (blocks []dataBlock, err error) {
var (
bset *binary.Binary
)
bset, err = q.encoder.Encode(q.sourceRawBytes)
if err != nil {
err = fmt.Errorf("could not encode data: %v", err)
return
}
blocks = make([]dataBlock, q.v.TotalNumBlocks())
// split bitset into data Block
start, end, blockID := 0, 0, 0
for _, g := range q.v.Groups {
for j := 0; j < g.NumBlocks; j++ {
start = end
end = start + g.NumDataCodewords*8
blocks[blockID].Data, err = bset.Subset(start, end)
if err != nil {
panic(err)
}
blocks[blockID].StartOffset = end - start
blocks[blockID].NumECBlock = g.ECBlockwordsPerBlock
blockID++
}
}
return
}
// dataBlock ...
type dataBlock struct {
Data *binary.Binary
StartOffset int // length
NumECBlock int // error correction codewords num per data block
}
// ecBlock ...
type ecBlock struct {
Data *binary.Binary
// StartOffset int // length
}
// errorCorrectionEncoding ref to:
// https://www.thonky.com/qr-code-tutorial /error-correction-coding
func (q *QRCode) errorCorrectionEncoding(dataBlocks []dataBlock) (blocks []ecBlock, err error) {
// start, end, blockID := 0, 0, 0
blocks = make([]ecBlock, q.v.TotalNumBlocks())
for idx, b := range dataBlocks {
debugLogf("numOfECBlock: %d", b.NumECBlock)
bset := reedsolomon.Encode(b.Data, b.NumECBlock)
blocks[idx].Data, err = bset.Subset(b.StartOffset, bset.Len())
if err != nil {
panic(err)
}
// blocks[idx].StartOffset = b.StartOffset
}
return
}
// arrangeBits ... and save into dataBSet
func (q *QRCode) arrangeBits(dataBlocks []dataBlock, ecBlocks []ecBlock) {
if debugEnabled() {
log.Println("arrangeBits called, before")
for i := 0; i < len(ecBlocks); i++ {
debugLogf("ec block_%d: %v", i, ecBlocks[i])
}
for i := 0; i < len(dataBlocks); i++ {
debugLogf("data block_%d: %v", i, dataBlocks[i])
}
}
// arrange data blocks
var (
overflowCnt = 0
endFlag = false
curIdx = 0
start, end int
)
// check if bitsets initialized, or initial them
if q.dataBSet == nil {
q.dataBSet = binary.New()
}
if q.ecBSet == nil {
q.ecBSet = binary.New()
}
for !endFlag {
for _, block := range dataBlocks {
start = curIdx * 8
end = start + 8
if start >= block.Data.Len() {
overflowCnt++
continue
}
subBin, err := block.Data.Subset(start, end)
if err != nil {
panic(err)
}
q.dataBSet.Append(subBin)
debugLogf("arrange data blocks info: start: %d, end: %d, len: %d, overflowCnt: %d, curIdx: %d",
start, end, block.Data.Len(), overflowCnt, curIdx,
)
}
curIdx++
// loop finish check
if overflowCnt >= len(dataBlocks) {
endFlag = true
}
}
// arrange ec blocks and reinitialize
endFlag = false
overflowCnt = 0
curIdx = 0
for !endFlag {
for _, block := range ecBlocks {
start = curIdx * 8
end = start + 8
if start >= block.Data.Len() {
overflowCnt++
continue
}
subBin, err := block.Data.Subset(start, end)
if err != nil {
panic(err)
}
q.ecBSet.Append(subBin)
}
curIdx++
// loop finish check
if overflowCnt >= len(ecBlocks) {
endFlag = true
}
}
debugLogf("arrangeBits called, after")
debugLogf("data bitsets: %s", q.dataBSet.String())
debugLogf("ec bitsets: %s", q.ecBSet.String())
}
// prefillMatrix with version info: ref to:
// http://www.thonky.com/qr-code-tutorial/module-placement-matrix
func (q *QRCode) prefillMatrix() {
dimension := q.v.Dimension()
if q.mat == nil {
q.mat = newMatrix(dimension, dimension)
}
// add finder left-top
addFinder(q.mat, 0, 0)
addSplitter(q.mat, 7, 7, dimension)
debugLogf("finish left-top finder")
// add finder right-top
addFinder(q.mat, dimension-7, 0)
addSplitter(q.mat, dimension-8, 7, dimension)
debugLogf("finish right-top finder")
// add finder left-bottom
addFinder(q.mat, 0, dimension-7)
addSplitter(q.mat, 7, dimension-8, dimension)
debugLogf("finish left-bottom finder")
// only version-1 QR code has no alignment module
if q.v.Ver > 1 {
// add align-mode related to version cfg
for _, loc := range loadAlignmentPatternLoc(q.v.Ver) {
addAlignment(q.mat, loc.X, loc.Y)
}
debugLogf("finish align")
}
// add timing line
addTimingLine(q.mat, dimension)
// add darkBlock always be position (4*ver+9, 8)
addDarkBlock(q.mat, 8, 4*q.v.Ver+9)
// reserveFormatBlock for version and format info
reserveFormatBlock(q.mat, dimension)
// reserveVersionBlock for version over 7
// only version 7 and larger version should add version info
if q.v.Ver >= 7 {
reserveVersionBlock(q.mat, dimension)
}
}
// add finder module
func addFinder(m *Matrix, top, left int) {
// black outer
x, y := top, left
for i := 0; i < 24; i++ {
_ = m.set(x, y, QRValue_FINDER_V1)
if i < 6 {
x = x + 1
} else if i < 12 {
y = y + 1
} else if i < 18 {
x = x - 1
} else {
y = y - 1
}
}
// white inner
x, y = top+1, left+1
for i := 0; i < 16; i++ {
_ = m.set(x, y, QRValue_FINDER_V0)
if i < 4 {
x = x + 1
} else if i < 8 {
y = y + 1
} else if i < 12 {
x = x - 1
} else {
y = y - 1
}
}
// black inner
for x = left + 2; x < left+5; x++ {
for y = top + 2; y < top+5; y++ {
_ = m.set(x, y, QRValue_FINDER_V1)
}
}
}
// add splitter module
func addSplitter(m *Matrix, x, y, dimension int) {
// top-left
if x == 7 && y == 7 {
for pos := 0; pos < 8; pos++ {
_ = m.set(x, pos, QRValue_SPLITTER_V0)
_ = m.set(pos, y, QRValue_SPLITTER_V0)
}
return
}
// top-right
if x == dimension-8 && y == 7 {
for pos := 0; pos < 8; pos++ {
_ = m.set(x, y-pos, QRValue_SPLITTER_V0)
_ = m.set(x+pos, y, QRValue_SPLITTER_V0)
}
return
}
// bottom-left
if x == 7 && y == dimension-8 {
for pos := 0; pos < 8; pos++ {
_ = m.set(x, y+pos, QRValue_SPLITTER_V0)
_ = m.set(x-pos, y, QRValue_SPLITTER_V0)
}
return
}
}
// add matrix align module
func addAlignment(m *Matrix, centerX, centerY int) {
_ = m.set(centerX, centerY, QRValue_DATA_V1)
// black
x, y := centerX-2, centerY-2
for i := 0; i < 16; i++ {
_ = m.set(x, y, QRValue_DATA_V1)
if i < 4 {
x = x + 1
} else if i < 8 {
y = y + 1
} else if i < 12 {
x = x - 1
} else {
y = y - 1
}
}
// white
x, y = centerX-1, centerY-1
for i := 0; i < 8; i++ {
_ = m.set(x, y, QRValue_DATA_V0)
if i < 2 {
x = x + 1
} else if i < 4 {
y = y + 1
} else if i < 6 {
x = x - 1
} else {
y = y - 1
}
}
}
// addTimingLine ...
func addTimingLine(m *Matrix, dimension int) {
for pos := 8; pos < dimension-8; pos++ {
if pos%2 == 0 {
_ = m.set(6, pos, QRValue_TIMING_V1)
_ = m.set(pos, 6, QRValue_TIMING_V1)
} else {
_ = m.set(6, pos, QRValue_TIMING_V0)
_ = m.set(pos, 6, QRValue_TIMING_V0)
}
}
}
// addDarkBlock ...
func addDarkBlock(m *Matrix, x, y int) {
_ = m.set(x, y, QRValue_DARK_V1)
}
// reserveFormatBlock maintain the position in matrix for format info
func reserveFormatBlock(m *Matrix, dimension int) {
for pos := 1; pos < 9; pos++ {
// skip timing line
if pos == 6 {
_ = m.set(8, dimension-pos, QRValue_FORMAT_V0)
_ = m.set(dimension-pos, 8, QRValue_FORMAT_V0)
continue
}
// skip dark module
if pos == 8 {
_ = m.set(8, pos, QRValue_FORMAT_V0) // top-left-column
_ = m.set(pos, 8, QRValue_FORMAT_V0) // top-left-row
_ = m.set(dimension-pos, 8, QRValue_FORMAT_V0) // top-right-row
continue
}
_ = m.set(8, pos, QRValue_FORMAT_V0) // top-left-column
_ = m.set(pos, 8, QRValue_FORMAT_V0) // top-left-row
_ = m.set(dimension-pos, 8, QRValue_FORMAT_V0) // top-right-row
_ = m.set(8, dimension-pos, QRValue_FORMAT_V0) // bottom-left-column
}
// fix(@yeqown): b4b5ae3 reduced two format reversed blocks on top-left-column and top-left-row.
_ = m.set(0, 8, QRValue_FORMAT_V0)
_ = m.set(8, 0, QRValue_FORMAT_V0)
}
// reserveVersionBlock maintain the position in matrix for version info
func reserveVersionBlock(m *Matrix, dimension int) {
// 3x6=18 cells
for i := 1; i <= 3; i++ {
for pos := 0; pos < 6; pos++ {
_ = m.set(dimension-8-i, pos, QRValue_VERSION_V0)
_ = m.set(pos, dimension-8-i, QRValue_VERSION_V0)
}
}
}
// fillDataBinary fill q.dataBSet binary stream into q.mat.
// References:
// * http://www.thonky.com/qr-code-tutorial/module-placement-matrix#Place-the-Data-Bits
//
func (q *QRCode) fillDataBinary(m *Matrix, dimension int) {
var (
// x always move from right, left right loop (2 rows), y move upward, downward, upward loop
x, y = dimension - 1, dimension - 1
l = q.dataBSet.Len()
upForward = true
pos int
)
for i := 0; pos < l; i++ {
// debugLogf("fillDataBinary: dimension: %d, len: %d: pos: %d", dimension, l, pos)
set := QRValue_DATA_V0
if q.dataBSet.At(pos) {
set = QRValue_DATA_V1
}
state, err := m.at(x, y)
if err != nil {
if err == ErrorOutRangeOfW {
break
}
if err == ErrorOutRangeOfH {
// turn around while y is out of range.
x = x - 2
switch upForward {
case true:
y = y + 1
default:
y = y - 1
}
if x == 7 || x == 6 {
x = x - 1
}
upForward = !upForward
state, err = m.at(x, y) // renew state qrbool after turn around writing direction.
}
}
// data bit should only be set into un-set block in matrix.
if state.qrtype() == QRType_INIT {
_ = m.set(x, y, set)
pos++
debugLogf("normal set turn forward: upForward: %v, x: %d, y: %d", upForward, x, y)
}
// DO NOT CHANGE FOLLOWING CODE FOR NOW !!!
// change x, y
mod2 := i % 2
// in one 8bit block
if upForward {
if mod2 == 0 {
x = x - 1
} else {
y = y - 1
x = x + 1
}
} else {
if mod2 == 0 {
x = x - 1
} else {
y = y + 1
x = x + 1
}
}
}
debugLogf("fillDone and x: %d, y: %d, pos: %d, total: %d", x, y, pos, l)
}
// draw from bitset to matrix.Matrix, calculate all mask modula score,
// then decide which mask to use according to the mask's score (the lowest one).
func (q *QRCode) masking() {
type maskScore struct {
Score int
Idx int
}
var (
masks = make([]*mask, 8)
mats = make([]*Matrix, 8)
lowScore = math.MaxInt32
markMatsIdx int
scoreChan = make(chan maskScore, 8)
wg sync.WaitGroup
)
dimension := q.v.Dimension()
// fill bitset into matrix
cpy := q.mat.Copy()
q.fillDataBinary(cpy, dimension)
// init mask and mats
for i := 0; i < 8; i++ {
masks[i] = newMask(q.mat, maskPatternModulo(i))
mats[i] = cpy.Copy()
}
// generate 8 matrix with mask
for i := 0; i < 8; i++ {
wg.Add(1)
go func(i int) {
_ = debugDraw(fmt.Sprintf("draft/mats_%d.jpeg", i), *mats[i])
_ = debugDraw(fmt.Sprintf("draft/mask_%d.jpeg", i), *masks[i].mat)
// xor with mask
q.xorMask(mats[i], masks[i])
_ = debugDraw(fmt.Sprintf("draft/mats_mask_%d.jpeg", i), *mats[i])
// fill format info
q.fillFormatInfo(mats[i], maskPatternModulo(i), dimension)
// version7 and larger version has version info
if q.v.Ver >= 7 {
q.fillVersionInfo(mats[i], dimension)
}
// calculate score and decide the lowest score and Draw
score := evaluation(mats[i])
debugLogf("cur idx: %d, score: %d, current lowest: mats[%d]:%d", i, score, markMatsIdx, lowScore)
scoreChan <- maskScore{
Score: score,
Idx: i,
}
_ = debugDraw(fmt.Sprintf("draft/qrcode_mask_%d.jpeg", i), *mats[i])
wg.Done()
}(i)
}
wg.Wait()
close(scoreChan)
for c := range scoreChan {
if c.Score < lowScore {
lowScore = c.Score
markMatsIdx = c.Idx
}
}
q.mat = mats[markMatsIdx]
}
// all mask patter and check the maskScore choose the lowest mask result
func (q *QRCode) xorMask(m *Matrix, mask *mask) {
mask.mat.iter(IterDirection_COLUMN, func(x, y int, v qrvalue) {
// skip the empty place
if v.qrtype() == QRType_INIT {
return
}
v2, _ := m.at(x, y)
_ = m.set(x, y, v2.xor(v))
})
}
// fillVersionInfo ref to:
// https://www.thonky.com/qr-code-tutorial/format-version-tables
func (q *QRCode) fillVersionInfo(m *Matrix, dimension int) {
bin := q.v.verInfo()
// from high bit to lowest
pos := 0
for j := 5; j >= 0; j-- {
for i := 1; i <= 3; i++ {
if bin.At(pos) {
_ = m.set(dimension-8-i, j, QRValue_VERSION_V1)
_ = m.set(j, dimension-8-i, QRValue_VERSION_V1)
} else {
_ = m.set(dimension-8-i, j, QRValue_VERSION_V0)
_ = m.set(j, dimension-8-i, QRValue_VERSION_V0)
}
pos++
}
}
}
// fill format info ref to:
// https://www.thonky.com/qr-code-tutorial/format-version-tables
func (q *QRCode) fillFormatInfo(m *Matrix, mode maskPatternModulo, dimension int) {
fmtBSet := q.v.formatInfo(int(mode))
debugLogf("fmtBitSet: %s", fmtBSet.String())
var (
x, y = 0, dimension - 1
)
for pos := 0; pos < 15; pos++ {
if fmtBSet.At(pos) {
// row
_ = m.set(x, 8, QRValue_FORMAT_V1)
// column
_ = m.set(8, y, QRValue_FORMAT_V1)
} else {
// row
_ = m.set(x, 8, QRValue_FORMAT_V0)
// column
_ = m.set(8, y, QRValue_FORMAT_V0)
}
x = x + 1
y = y - 1
// row skip
if x == 6 {
x = 7
} else if x == 8 {
x = dimension - 8
}
// column skip
if y == dimension-8 {
y = 8
} else if y == 6 {
y = 5
}
}
}

38
vendor/github.com/yeqown/go-qrcode/v2/utilities.go generated vendored Normal file
View File

@@ -0,0 +1,38 @@
package qrcode
// samestate judge two matrix qrtype is same with binary semantic.
// QRValue_DATA_V0/QRType_INIT only equal to QRValue_DATA_V0, other state are equal to each other.
func samestate(s1, s2 qrvalue) bool {
return s1.qrbool() == s2.qrbool()
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
func binaryToQRValueSlice(s string) []qrvalue {
var states = make([]qrvalue, 0, len(s))
for _, c := range s {
switch c {
case '1':
states = append(states, QRValue_DATA_V1)
case '0':
states = append(states, QRValue_DATA_V0)
default:
continue
}
}
return states
}

409
vendor/github.com/yeqown/go-qrcode/v2/version.go generated vendored Normal file
View File

@@ -0,0 +1,409 @@
package qrcode
import (
"errors"
"log"
"strconv"
// "github.com/skip2/go-qrcode/bitset"
"github.com/yeqown/reedsolomon/binary"
)
// ecLevel error correction level
type ecLevel int
const (
// ErrorCorrectionLow :Level L: 7% error recovery.
ErrorCorrectionLow ecLevel = iota + 1
// ErrorCorrectionMedium :Level M: 15% error recovery. Good default choice.
ErrorCorrectionMedium
// ErrorCorrectionQuart :Level Q: 25% error recovery.
ErrorCorrectionQuart
// ErrorCorrectionHighest :Level H: 30% error recovery.
ErrorCorrectionHighest
formatInfoBitsNum = 15 // format info bits num
verInfoBitsNum = 18 // version info length bits num
)
var (
errInvalidErrorCorrectionLevel = errors.New("invalid error correction level")
errAnalyzeVersionFailed = errors.New("could not match version! " +
"check your content length is in limitation of encode mode and error correction level")
errMissMatchedVersion = errors.New("could not match version")
errMissMatchedEncodeType = errors.New("could not match the encode type")
// versions []version
// Each QR Code contains a 15-bit Format Information qrbool. The 15 bits
// consist of 5 data bits concatenated with 10 error correction bits.
//
// The 5 data bits consist of:
// - 2 bits for the error correction level (L=01, M=00, G=11, H=10).
// - 3 bits for the data mask pattern identifier.
//
// formatBitSequence is a mapping from the 5 data bits to the completed 15-bit
// Format Information qrbool.
//
// For example, a QR Code using error correction level L, and data mask
// pattern identifier 001:
//
// 01 | 001 = 01001 = 0x9
// formatBitSequence[0x9].qrCode = 0x72f3 = 111001011110011
formatBitSequence = []struct {
regular uint32
micro uint32
}{
{0x5412, 0x4445}, {0x5125, 0x4172}, {0x5e7c, 0x4e2b}, {0x5b4b, 0x4b1c},
{0x45f9, 0x55ae}, {0x40ce, 0x5099}, {0x4f97, 0x5fc0}, {0x4aa0, 0x5af7},
{0x77c4, 0x6793}, {0x72f3, 0x62a4}, {0x7daa, 0x6dfd}, {0x789d, 0x68ca},
{0x662f, 0x7678}, {0x6318, 0x734f}, {0x6c41, 0x7c16}, {0x6976, 0x7921},
{0x1689, 0x06de}, {0x13be, 0x03e9}, {0x1ce7, 0x0cb0}, {0x19d0, 0x0987},
{0x0762, 0x1735}, {0x0255, 0x1202}, {0x0d0c, 0x1d5b}, {0x083b, 0x186c},
{0x355f, 0x2508}, {0x3068, 0x203f}, {0x3f31, 0x2f66}, {0x3a06, 0x2a51},
{0x24b4, 0x34e3}, {0x2183, 0x31d4}, {0x2eda, 0x3e8d}, {0x2bed, 0x3bba},
}
// QR Codes version 7 and higher contain an 18-bit version Information qrbool,
// consisting of a 6 data bits and 12 error correction bits.
//
// versionBitSequence is a mapping from QR Code version to the completed
// 18-bit version Information qrbool.
//
// For example, a QR code of version 7:
// versionBitSequence[0x7] = 0x07c94 = 000111110010010100
versionBitSequence = []uint32{
0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x07c94,
0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 0x0f928,
0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec,
0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, 0x1f250,
0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, 0x27541, 0x28c69,
}
)
// capacity struct includes data type max capacity
type capacity struct {
Numeric int `json:"n"` // num capacity
AlphaNumeric int `json:"a"` // char capacity
Byte int `json:"b"` // byte capacity (utf-8 also)
JP int `json:"j"` // Japanese capacity
}
// group contains fields to generate ECBlocks
// and append _defaultPadding bit
type group struct {
// NumBlocks num of blocks
NumBlocks int `json:"nbs"`
// NumDataCodewords Number of data codewords.
NumDataCodewords int `json:"ndcs"`
// ECBlockwordsPerBlock ...
ECBlockwordsPerBlock int `json:"ecbs_pb"`
}
// version ...
type version struct {
// version code 1-40
Ver int `json:"ver"`
// ECLevel error correction 0, 1, 2, 3
ECLevel ecLevel `json:"eclv"`
// Cap includes each type's max capacity (specified by `Ver` and `ecLevel`)
// ref to: https://www.thonky.com/qr-code-tutorial/character-capacities
Cap capacity `json:"cap"`
// RemainderBits remainder bits need to append finally
RemainderBits int `json:"rembits"`
// groups info to generate
// ref to: https://www.thonky.com/qr-code-tutorial/error-correction-table
// numGroup = len(Groups)
Groups []group `json:"groups"`
}
// Dimension ...
func (v version) Dimension() int {
return v.Ver*4 + 17
}
// NumTotalCodewords total data codewords
func (v version) NumTotalCodewords() int {
var total int
for _, g := range v.Groups {
total = total + (g.NumBlocks * g.NumDataCodewords)
}
return total
}
// NumGroups ... need group num. ref to version config file
func (v version) NumGroups() int {
return len(v.Groups)
}
// TotalNumBlocks ... total data blocks num, ref to version config file
func (v version) TotalNumBlocks() int {
var total int
for _, g := range v.Groups {
total = total + g.NumBlocks
}
return total
}
// VerInfo version info bitset
func (v version) verInfo() *binary.Binary {
if v.Ver < 7 {
return nil
}
result := binary.New()
result.AppendUint32(versionBitSequence[v.Ver], verInfoBitsNum)
return result
}
// formatInfo returns the 15-bit Format Information qrbool for a QR
// code.
func (v version) formatInfo(maskPattern int) *binary.Binary {
formatID := 0
switch v.ECLevel {
case ErrorCorrectionLow:
formatID = 0x08 // 0b01000
case ErrorCorrectionMedium:
formatID = 0x00 // 0b00000
case ErrorCorrectionQuart:
formatID = 0x18 // 0b11000
case ErrorCorrectionHighest:
formatID = 0x10 // 0b10000
default:
log.Panicf("Invalid level %d", v.ECLevel)
}
if maskPattern < 0 || maskPattern > 7 {
log.Panicf("Invalid maskPattern %d", maskPattern)
}
formatID |= maskPattern & 0x7
result := binary.New()
result.AppendUint32(formatBitSequence[formatID].regular, formatInfoBitsNum)
return result
}
var emptyVersion = version{Ver: -1}
// binarySearchVersion speed up searching target version in versions.
// low, high to set the left and right bound of the search range (min:0 to max:159).
// compare represents the function to compare the target version with the cursor version.
// negative means lower direction, positive means higher direction, zero mean hit.
func binarySearchVersion(low, high int, compare func(*version) int) (hit version, found bool) {
// left low and high in a valid range
if low > high || low > _VERSIONS_ITEM_COUNT || high < 0 {
return emptyVersion, false
}
if low < 0 {
low = 0
}
if high >= _VERSIONS_ITEM_COUNT {
high = len(versions) - 1
}
for low <= high {
mid := (low + high) / 2
r := compare(&versions[mid])
if r == 0 {
hit = versions[mid]
found = true
break
}
if r > 0 {
// move toward higher direction
low = mid + 1
} else {
// move toward lower direction
high = mid
}
}
return hit, found
}
// defaultBinaryCompare built-in compare function for binary search.
func defaultBinaryCompare(ver int, ec ecLevel) func(cursor *version) int {
return func(cursor *version) int {
switch r := ver - cursor.Ver; r {
case 0:
default:
// v is bigger return positive; otherwise return negative.
return r
}
return int(ec - cursor.ECLevel)
}
}
// loadVersion get version config by specified version indicator and error correction level.
// we can speed up this process, by shrink the range to search.
func loadVersion(lv int, ec ecLevel) version {
// each version only has 4 items in versions array,
// and them are ordered[ASC] already.
high := lv*4 - 1
low := (lv - 1) * 4
for i := low; i <= high; i++ {
if versions[i].ECLevel == ec {
return versions[i]
}
}
panic(errMissMatchedVersion)
}
// analyzeVersion the raw text, and then decide which version should be chosen
// according to the text length , error correction level and encode mode to choose the
// closest capacity of version.
//
// check out http://muyuchengfeng.xyz/%E4%BA%8C%E7%BB%B4%E7%A0%81-%E5%AD%97%E7%AC%A6%E5%AE%B9%E9%87%8F%E8%A1%A8/
// for more details.
func analyzeVersion(raw []byte, ec ecLevel, mode encMode) (*version, error) {
step := 0
switch ec {
case ErrorCorrectionLow:
step = 0
case ErrorCorrectionMedium:
step = 1
case ErrorCorrectionQuart:
step = 2
case ErrorCorrectionHighest:
step = 3
default:
return nil, errInvalidErrorCorrectionLevel
}
want, mark := len(raw), 0
for ; step < 160; step += 4 {
switch mode {
case EncModeNumeric:
mark = versions[step].Cap.Numeric
case EncModeAlphanumeric:
mark = versions[step].Cap.AlphaNumeric
case EncModeByte:
mark = versions[step].Cap.Byte
case EncModeJP:
mark = versions[step].Cap.JP
default:
return nil, errMissMatchedEncodeType
}
if mark >= want {
return &versions[step], nil
}
}
debugLogf("mismatched version, version's length: %d, ec: %v", len(versions), ec)
return nil, errAnalyzeVersionFailed
}
var (
// https://www.thonky.com/qr-code-tutorial/alignment-pattern-locations
// DONE(@yeqown): add more version
alignPatternLocation = map[int][]int{
2: {6, 18},
3: {6, 22},
4: {6, 26},
5: {6, 30},
6: {6, 34},
7: {6, 22, 38},
8: {6, 24, 42},
9: {6, 26, 46},
10: {6, 28, 50},
11: {6, 30, 54},
12: {6, 32, 58},
13: {6, 34, 62},
14: {6, 26, 46, 66},
15: {6, 26, 48, 70},
16: {6, 26, 50, 74},
17: {6, 30, 54, 78},
18: {6, 30, 56, 82},
19: {6, 30, 58, 86},
20: {6, 34, 62, 90},
21: {6, 28, 50, 72, 94},
22: {6, 26, 50, 74, 98},
23: {6, 30, 54, 78, 102},
24: {6, 28, 54, 80, 106},
25: {6, 32, 58, 84, 110},
26: {6, 30, 58, 86, 114},
27: {6, 34, 62, 90, 118},
28: {6, 26, 50, 74, 98, 122},
29: {6, 30, 54, 78, 102, 126},
30: {6, 26, 52, 78, 104, 130},
31: {6, 30, 56, 82, 108, 134},
32: {6, 34, 60, 86, 112, 138},
33: {6, 30, 58, 86, 114, 142},
34: {6, 34, 62, 90, 118, 146},
35: {6, 30, 54, 78, 102, 126, 150},
36: {6, 24, 50, 76, 102, 128, 154},
37: {6, 28, 54, 80, 106, 132, 158},
38: {6, 32, 58, 84, 110, 136, 162},
39: {6, 26, 54, 82, 110, 138, 166},
40: {6, 30, 58, 86, 114, 142, 170},
}
alignPatternCache = map[int][]loc{}
)
// loc point position(x,y)
type loc struct {
X int // for width
Y int // for height
}
// loadAlignmentPatternLoc ...
func loadAlignmentPatternLoc(ver int) (locs []loc) {
if ver < 2 {
return
}
var ok bool
if locs, ok = alignPatternCache[ver]; ok {
return
}
dimension := ver*4 + 17
positions, ok := alignPatternLocation[ver]
if !ok {
panic("could not found align at version: " + strconv.Itoa(ver))
}
for _, pos1 := range positions {
for _, pos2 := range positions {
if !valid(pos1, pos2, dimension) {
continue
}
locs = append(locs, loc{X: pos1, Y: pos2})
}
}
alignPatternCache[ver] = locs
return
}
// x, y center position x,y so
func valid(x, y, dimension int) bool {
// valid left-top
if (x-2) < 7 && (y-2) < 7 {
return false
}
// valid right-top
if (x+2) > dimension-7 && (y-2) < 7 {
return false
}
// valid left-bottom
if (x-2) < 7 && (y+2) > dimension-7 {
return false
}
return true
}

3532
vendor/github.com/yeqown/go-qrcode/v2/version_cfg.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

25
vendor/github.com/yeqown/go-qrcode/v2/writer.go generated vendored Normal file
View File

@@ -0,0 +1,25 @@
package qrcode
// Writer is the interface of a QR code writer, it defines the rule of how to
// `print` the code image from matrix. There's built-in writer to output into
// file, terminal.
type Writer interface {
// Write writes the code image into itself stream, such as io.Writer,
// terminal output stream, and etc
Write(mat Matrix) error
// Close the writer stream if it exists after QRCode.Save() is called.
Close() error
}
var _ Writer = (*nonWriter)(nil)
type nonWriter struct{}
func (n nonWriter) Close() error {
return nil
}
func (n nonWriter) Write(mat Matrix) error {
return nil
}