feature/ui asi tak vsechno uz funguje pres qr
This commit is contained in:
parent
81df2170f3
commit
c286d54e98
510
qr_support.go
510
qr_support.go
@ -1,20 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"log"
|
||||
"math"
|
||||
"os/exec"
|
||||
"bytes"
|
||||
"errors"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"log"
|
||||
"math"
|
||||
"os/exec"
|
||||
|
||||
"github.com/liyue201/goqr"
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
qrx "github.com/makiuchi-d/gozxing/qrcode"
|
||||
qrgen "github.com/skip2/go-qrcode"
|
||||
"github.com/liyue201/goqr"
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
qrx "github.com/makiuchi-d/gozxing/qrcode"
|
||||
qrgen "github.com/skip2/go-qrcode"
|
||||
)
|
||||
|
||||
// GenerateQRPNG returns PNG bytes for the given text.
|
||||
@ -22,8 +22,8 @@ func GenerateQRPNG(text string, size int) ([]byte, error) {
|
||||
if size <= 0 {
|
||||
size = 256
|
||||
}
|
||||
// Use Low EC for larger modules; improves decode reliability for long payloads
|
||||
png, err := qrgen.Encode(text, qrgen.Low, size)
|
||||
// Use Low EC for larger modules; improves decode reliability for long payloads
|
||||
png, err := qrgen.Encode(text, qrgen.Low, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -77,32 +77,32 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
bounds := img.Bounds()
|
||||
w, h := bounds.Dx(), bounds.Dy()
|
||||
|
||||
// Helper: nearest-neighbour scale
|
||||
scale := func(src image.Image, fw, fh int) image.Image {
|
||||
if fw <= 0 || fh <= 0 {
|
||||
return src
|
||||
}
|
||||
dst := image.NewNRGBA(image.Rect(0, 0, fw, fh))
|
||||
sb := src.Bounds()
|
||||
sw, sh := float64(sb.Dx()), float64(sb.Dy())
|
||||
for y := 0; y < fh; y++ {
|
||||
sy := int(float64(y)/float64(fh)*sh + 0.5)
|
||||
if sy >= sb.Dy() {
|
||||
sy = sb.Dy() - 1
|
||||
}
|
||||
for x := 0; x < fw; x++ {
|
||||
sx := int(float64(x)/float64(fw)*sw + 0.5)
|
||||
if sx >= sb.Dx() {
|
||||
sx = sb.Dx() - 1
|
||||
}
|
||||
dst.Set(x, y, src.At(sb.Min.X+sx, sb.Min.Y+sy))
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
// Helper: nearest-neighbour scale
|
||||
scale := func(src image.Image, fw, fh int) image.Image {
|
||||
if fw <= 0 || fh <= 0 {
|
||||
return src
|
||||
}
|
||||
dst := image.NewNRGBA(image.Rect(0, 0, fw, fh))
|
||||
sb := src.Bounds()
|
||||
sw, sh := float64(sb.Dx()), float64(sb.Dy())
|
||||
for y := 0; y < fh; y++ {
|
||||
sy := int(float64(y)/float64(fh)*sh + 0.5)
|
||||
if sy >= sb.Dy() {
|
||||
sy = sb.Dy() - 1
|
||||
}
|
||||
for x := 0; x < fw; x++ {
|
||||
sx := int(float64(x)/float64(fw)*sw + 0.5)
|
||||
if sx >= sb.Dx() {
|
||||
sx = sb.Dx() - 1
|
||||
}
|
||||
dst.Set(x, y, src.At(sb.Min.X+sx, sb.Min.Y+sy))
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Helper: binarize with adaptive threshold (simple mean + variance fudge)
|
||||
binarize := func(src image.Image) image.Image {
|
||||
// Helper: binarize with adaptive threshold (simple mean + variance fudge)
|
||||
binarize := func(src image.Image) image.Image {
|
||||
b := src.Bounds()
|
||||
gray := image.NewGray(b)
|
||||
var sum, sum2 float64
|
||||
@ -213,60 +213,60 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
}
|
||||
|
||||
var firstErr error
|
||||
for idx, attempt := range attempts {
|
||||
codes, err := goqr.Recognize(attempt)
|
||||
if err != nil {
|
||||
if firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
log.Printf("[qr] attempt %d error: %v", idx, err)
|
||||
continue
|
||||
}
|
||||
if len(codes) > 0 {
|
||||
log.Printf("[qr] attempt %d success (%d codes)", idx, len(codes))
|
||||
return string(codes[0].Payload), nil
|
||||
}
|
||||
log.Printf("[qr] attempt %d no codes", idx)
|
||||
}
|
||||
for idx, attempt := range attempts {
|
||||
codes, err := goqr.Recognize(attempt)
|
||||
if err != nil {
|
||||
if firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
log.Printf("[qr] attempt %d error: %v", idx, err)
|
||||
continue
|
||||
}
|
||||
if len(codes) > 0 {
|
||||
log.Printf("[qr] attempt %d success (%d codes)", idx, len(codes))
|
||||
return string(codes[0].Payload), nil
|
||||
}
|
||||
log.Printf("[qr] attempt %d no codes", idx)
|
||||
}
|
||||
|
||||
// ZXing helper used across multiple branches
|
||||
decodeZX := func(src image.Image, hints map[gozxing.DecodeHintType]interface{}) (string, error) {
|
||||
binSrc := gozxing.NewLuminanceSourceFromImage(src)
|
||||
// Try hybrid first (usually better for photos)
|
||||
bmpH, _ := gozxing.NewBinaryBitmap(gozxing.NewHybridBinarizer(binSrc))
|
||||
reader := qrx.NewQRCodeReader()
|
||||
if res, err := reader.Decode(bmpH, hints); err == nil {
|
||||
return res.GetText(), nil
|
||||
}
|
||||
// Fallback to global histogram
|
||||
bmpG, _ := gozxing.NewBinaryBitmap(gozxing.NewGlobalHistgramBinarizer(binSrc))
|
||||
if res, err := reader.Decode(bmpG, hints); err == nil {
|
||||
return res.GetText(), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
// Also try ZXing on all preprocessed attempts
|
||||
for idx, attempt := range attempts {
|
||||
if txt, err := decodeZX(attempt, map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
gozxing.DecodeHintType_PURE_BARCODE: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] zxing attempt %d success (pure)", idx)
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(attempt, map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] zxing attempt %d success (try-harder)", idx)
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
// ZXing helper used across multiple branches
|
||||
decodeZX := func(src image.Image, hints map[gozxing.DecodeHintType]interface{}) (string, error) {
|
||||
binSrc := gozxing.NewLuminanceSourceFromImage(src)
|
||||
// Try hybrid first (usually better for photos)
|
||||
bmpH, _ := gozxing.NewBinaryBitmap(gozxing.NewHybridBinarizer(binSrc))
|
||||
reader := qrx.NewQRCodeReader()
|
||||
if res, err := reader.Decode(bmpH, hints); err == nil {
|
||||
return res.GetText(), nil
|
||||
}
|
||||
// Fallback to global histogram
|
||||
bmpG, _ := gozxing.NewBinaryBitmap(gozxing.NewGlobalHistgramBinarizer(binSrc))
|
||||
if res, err := reader.Decode(bmpG, hints); err == nil {
|
||||
return res.GetText(), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
// Also try ZXing on all preprocessed attempts
|
||||
for idx, attempt := range attempts {
|
||||
if txt, err := decodeZX(attempt, map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
gozxing.DecodeHintType_PURE_BARCODE: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] zxing attempt %d success (pure)", idx)
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(attempt, map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] zxing attempt %d success (try-harder)", idx)
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
if firstErr != nil {
|
||||
log.Printf("[qr] preprocess firstErr: %v", firstErr)
|
||||
}
|
||||
|
||||
// --- Heuristic fallback: auto-invert + quiet-zone pad + gozxing ---
|
||||
// --- Heuristic fallback: auto-invert + quiet-zone pad + gozxing ---
|
||||
addQuietZone := func(src image.Image) image.Image {
|
||||
b := src.Bounds()
|
||||
pad := int(math.Max(float64(b.Dx()/32), 8))
|
||||
@ -311,132 +311,132 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
if needInvert {
|
||||
base = invert(base)
|
||||
}
|
||||
modified = append(modified, addQuietZone(base))
|
||||
modified = append(modified, addQuietZone(invert(base)))
|
||||
for _, m := range modified {
|
||||
if codes, err := goqr.Recognize(m); err == nil && len(codes) > 0 {
|
||||
log.Printf("[qr] quiet-zone/invert success")
|
||||
return string(codes[0].Payload), nil
|
||||
}
|
||||
// Also try ZXing on these variants before moving on
|
||||
if txt, err := decodeZX(m, map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
gozxing.DecodeHintType_PURE_BARCODE: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (quiet-zone+pure)")
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(m, map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (quiet-zone+try-harder)")
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
// ZXing with TRY_HARDER and PURE_BARCODE hints which often fix dense PNGs
|
||||
hints := map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
// Many of our images are perfect render PNGs from go-qrcode -> enable PURE_BARCODE
|
||||
gozxing.DecodeHintType_PURE_BARCODE: true,
|
||||
}
|
||||
if txt, err := decodeZX(base, hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (hints)")
|
||||
return txt, nil
|
||||
} else if err != nil {
|
||||
log.Printf("[qr] gozxing(hints) err: %v", err)
|
||||
}
|
||||
// Try again without PURE_BARCODE in case quiet zone is cropped
|
||||
hints2 := map[gozxing.DecodeHintType]interface{}{gozxing.DecodeHintType_TRY_HARDER: true}
|
||||
if txt, err := decodeZX(base, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (try-harder)")
|
||||
return txt, nil
|
||||
} else if err != nil {
|
||||
log.Printf("[qr] gozxing(try-harder) err: %v", err)
|
||||
}
|
||||
// Try ZXing on rotations as well
|
||||
for _, r := range rotate(base) {
|
||||
if txt, err := decodeZX(r, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (rotated)")
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(r, hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (rotated pure)")
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
modified = append(modified, addQuietZone(base))
|
||||
modified = append(modified, addQuietZone(invert(base)))
|
||||
for _, m := range modified {
|
||||
if codes, err := goqr.Recognize(m); err == nil && len(codes) > 0 {
|
||||
log.Printf("[qr] quiet-zone/invert success")
|
||||
return string(codes[0].Payload), nil
|
||||
}
|
||||
// Also try ZXing on these variants before moving on
|
||||
if txt, err := decodeZX(m, map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
gozxing.DecodeHintType_PURE_BARCODE: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (quiet-zone+pure)")
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(m, map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
}); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (quiet-zone+try-harder)")
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
// ZXing with TRY_HARDER and PURE_BARCODE hints which often fix dense PNGs
|
||||
hints := map[gozxing.DecodeHintType]interface{}{
|
||||
gozxing.DecodeHintType_TRY_HARDER: true,
|
||||
// Many of our images are perfect render PNGs from go-qrcode -> enable PURE_BARCODE
|
||||
gozxing.DecodeHintType_PURE_BARCODE: true,
|
||||
}
|
||||
if txt, err := decodeZX(base, hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (hints)")
|
||||
return txt, nil
|
||||
} else if err != nil {
|
||||
log.Printf("[qr] gozxing(hints) err: %v", err)
|
||||
}
|
||||
// Try again without PURE_BARCODE in case quiet zone is cropped
|
||||
hints2 := map[gozxing.DecodeHintType]interface{}{gozxing.DecodeHintType_TRY_HARDER: true}
|
||||
if txt, err := decodeZX(base, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (try-harder)")
|
||||
return txt, nil
|
||||
} else if err != nil {
|
||||
log.Printf("[qr] gozxing(try-harder) err: %v", err)
|
||||
}
|
||||
// Try ZXing on rotations as well
|
||||
for _, r := range rotate(base) {
|
||||
if txt, err := decodeZX(r, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (rotated)")
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(r, hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing success (rotated pure)")
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Extra pure-QR recovery: try small insets and simple upscales with PURE_BARCODE.
|
||||
inset := func(src image.Image, px int) image.Image {
|
||||
if px <= 0 {
|
||||
return src
|
||||
}
|
||||
b := src.Bounds()
|
||||
r := image.Rect(b.Min.X+px, b.Min.Y+px, b.Max.X-px, b.Max.Y-px)
|
||||
if r.Dx() <= 10 || r.Dy() <= 10 {
|
||||
return src
|
||||
}
|
||||
out := image.NewRGBA(image.Rect(0, 0, r.Dx(), r.Dy()))
|
||||
draw.Draw(out, out.Bounds(), src, r.Min, draw.Src)
|
||||
return out
|
||||
}
|
||||
cropSides := func(src image.Image, l, t, r, b int) image.Image {
|
||||
bb := src.Bounds()
|
||||
rr := image.Rect(bb.Min.X+l, bb.Min.Y+t, bb.Max.X-r, bb.Max.Y-b)
|
||||
if rr.Dx() <= 10 || rr.Dy() <= 10 || rr.Min.X >= rr.Max.X || rr.Min.Y >= rr.Max.Y {
|
||||
return src
|
||||
}
|
||||
out := image.NewRGBA(image.Rect(0, 0, rr.Dx(), rr.Dy()))
|
||||
draw.Draw(out, out.Bounds(), src, rr.Min, draw.Src)
|
||||
return out
|
||||
}
|
||||
tryPure := func(src image.Image) (string, bool) {
|
||||
for _, px := range []int{1, 2, 3} {
|
||||
if txt, err := decodeZX(inset(src, px), hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing pure+inset %d success", px)
|
||||
return txt, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
// Try on base, binarized base and scaled variants
|
||||
if txt, ok := tryPure(base); ok {
|
||||
return txt, nil
|
||||
}
|
||||
if txt, ok := tryPure(binarize(base)); ok {
|
||||
return txt, nil
|
||||
}
|
||||
if w < 800 && h < 800 { // upscale to mitigate rounding of module size
|
||||
if txt, ok := tryPure(scale(base, w*2, h*2)); ok {
|
||||
return txt, nil
|
||||
}
|
||||
if txt, ok := tryPure(scale(base, w*3, h*3)); ok {
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
// Extra pure-QR recovery: try small insets and simple upscales with PURE_BARCODE.
|
||||
inset := func(src image.Image, px int) image.Image {
|
||||
if px <= 0 {
|
||||
return src
|
||||
}
|
||||
b := src.Bounds()
|
||||
r := image.Rect(b.Min.X+px, b.Min.Y+px, b.Max.X-px, b.Max.Y-px)
|
||||
if r.Dx() <= 10 || r.Dy() <= 10 {
|
||||
return src
|
||||
}
|
||||
out := image.NewRGBA(image.Rect(0, 0, r.Dx(), r.Dy()))
|
||||
draw.Draw(out, out.Bounds(), src, r.Min, draw.Src)
|
||||
return out
|
||||
}
|
||||
cropSides := func(src image.Image, l, t, r, b int) image.Image {
|
||||
bb := src.Bounds()
|
||||
rr := image.Rect(bb.Min.X+l, bb.Min.Y+t, bb.Max.X-r, bb.Max.Y-b)
|
||||
if rr.Dx() <= 10 || rr.Dy() <= 10 || rr.Min.X >= rr.Max.X || rr.Min.Y >= rr.Max.Y {
|
||||
return src
|
||||
}
|
||||
out := image.NewRGBA(image.Rect(0, 0, rr.Dx(), rr.Dy()))
|
||||
draw.Draw(out, out.Bounds(), src, rr.Min, draw.Src)
|
||||
return out
|
||||
}
|
||||
tryPure := func(src image.Image) (string, bool) {
|
||||
for _, px := range []int{1, 2, 3} {
|
||||
if txt, err := decodeZX(inset(src, px), hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing pure+inset %d success", px)
|
||||
return txt, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
// Try on base, binarized base and scaled variants
|
||||
if txt, ok := tryPure(base); ok {
|
||||
return txt, nil
|
||||
}
|
||||
if txt, ok := tryPure(binarize(base)); ok {
|
||||
return txt, nil
|
||||
}
|
||||
if w < 800 && h < 800 { // upscale to mitigate rounding of module size
|
||||
if txt, ok := tryPure(scale(base, w*2, h*2)); ok {
|
||||
return txt, nil
|
||||
}
|
||||
if txt, ok := tryPure(scale(base, w*3, h*3)); ok {
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Brute-force small asymmetric crops to satisfy ZXing's 1 (mod 4) dimension constraint.
|
||||
for _, src := range []image.Image{base, binarize(base)} {
|
||||
for l := 0; l <= 2; l++ {
|
||||
for t := 0; t <= 2; t++ {
|
||||
for r := 0; r <= 2; r++ {
|
||||
for btm := 0; btm <= 2; btm++ {
|
||||
cand := cropSides(src, l, t, r, btm)
|
||||
if cand == src {
|
||||
continue
|
||||
}
|
||||
if txt, err := decodeZX(cand, hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing pure+asym inset %d,%d,%d,%d success", l, t, r, btm)
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(cand, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing asym inset %d,%d,%d,%d success", l, t, r, btm)
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Brute-force small asymmetric crops to satisfy ZXing's 1 (mod 4) dimension constraint.
|
||||
for _, src := range []image.Image{base, binarize(base)} {
|
||||
for l := 0; l <= 2; l++ {
|
||||
for t := 0; t <= 2; t++ {
|
||||
for r := 0; r <= 2; r++ {
|
||||
for btm := 0; btm <= 2; btm++ {
|
||||
cand := cropSides(src, l, t, r, btm)
|
||||
if cand == src {
|
||||
continue
|
||||
}
|
||||
if txt, err := decodeZX(cand, hints); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing pure+asym inset %d,%d,%d,%d success", l, t, r, btm)
|
||||
return txt, nil
|
||||
}
|
||||
if txt, err := decodeZX(cand, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing asym inset %d,%d,%d,%d success", l, t, r, btm)
|
||||
return txt, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bounding box crop of dark pixels, then upscale and retry
|
||||
boundsAll := base.Bounds()
|
||||
@ -466,48 +466,48 @@ func DecodeQR(img image.Image) (string, error) {
|
||||
crop := image.NewRGBA(image.Rect(0, 0, maxX-minX+1, maxY-minY+1))
|
||||
draw.Draw(crop, crop.Bounds(), base, image.Point{X: minX, Y: minY}, draw.Src)
|
||||
log.Printf("[qr] crop bbox size=%dx%d", crop.Bounds().Dx(), crop.Bounds().Dy())
|
||||
for _, up := range []int{2, 3, 4} {
|
||||
cw := crop.Bounds().Dx() * up
|
||||
ch := crop.Bounds().Dy() * up
|
||||
if cw > 2400 || ch > 2400 {
|
||||
continue
|
||||
}
|
||||
scaled := image.NewRGBA(image.Rect(0, 0, cw, ch))
|
||||
for y := 0; y < ch; y++ {
|
||||
sy := y / up
|
||||
for x := 0; x < cw; x++ {
|
||||
sx := x / up
|
||||
scaled.Set(x, y, crop.At(sx, sy))
|
||||
}
|
||||
}
|
||||
if txt, err := decodeZX(scaled, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing crop+scale success up=%d", up)
|
||||
return txt, nil
|
||||
} else if err != nil {
|
||||
log.Printf("[qr] crop up=%d fail: %v", up, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if firstErr != nil {
|
||||
return "", firstErr
|
||||
}
|
||||
// Final external fallback: try zbarimg if available on the system
|
||||
if cmd, lookErr := exec.LookPath("zbarimg"); lookErr == nil {
|
||||
buf := &bytes.Buffer{}
|
||||
if err := png.Encode(buf, img); err == nil {
|
||||
c := exec.Command(cmd, "-q", "--raw", "-")
|
||||
c.Stdin = bytes.NewReader(buf.Bytes())
|
||||
out, err := c.Output()
|
||||
if err == nil && len(out) > 0 {
|
||||
log.Printf("[qr] zbarimg success")
|
||||
return string(out), nil
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("[qr] zbarimg error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("no qr code found")
|
||||
for _, up := range []int{2, 3, 4} {
|
||||
cw := crop.Bounds().Dx() * up
|
||||
ch := crop.Bounds().Dy() * up
|
||||
if cw > 2400 || ch > 2400 {
|
||||
continue
|
||||
}
|
||||
scaled := image.NewRGBA(image.Rect(0, 0, cw, ch))
|
||||
for y := 0; y < ch; y++ {
|
||||
sy := y / up
|
||||
for x := 0; x < cw; x++ {
|
||||
sx := x / up
|
||||
scaled.Set(x, y, crop.At(sx, sy))
|
||||
}
|
||||
}
|
||||
if txt, err := decodeZX(scaled, hints2); err == nil && txt != "" {
|
||||
log.Printf("[qr] gozxing crop+scale success up=%d", up)
|
||||
return txt, nil
|
||||
} else if err != nil {
|
||||
log.Printf("[qr] crop up=%d fail: %v", up, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if firstErr != nil {
|
||||
return "", firstErr
|
||||
}
|
||||
// Final external fallback: try zbarimg if available on the system
|
||||
if cmd, lookErr := exec.LookPath("zbarimg"); lookErr == nil {
|
||||
buf := &bytes.Buffer{}
|
||||
if err := png.Encode(buf, img); err == nil {
|
||||
c := exec.Command(cmd, "-q", "--raw", "-")
|
||||
c.Stdin = bytes.NewReader(buf.Bytes())
|
||||
out, err := c.Output()
|
||||
if err == nil && len(out) > 0 {
|
||||
log.Printf("[qr] zbarimg success")
|
||||
return string(out), nil
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("[qr] zbarimg error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("no qr code found")
|
||||
}
|
||||
|
||||
// LoadPNG decodes raw PNG bytes to image.Image.
|
||||
|
||||
0
static/style.css
Normal file → Executable file
0
static/style.css
Normal file → Executable file
0
templates/decrypt.html
Normal file → Executable file
0
templates/decrypt.html
Normal file → Executable file
0
templates/encrypt.html
Normal file → Executable file
0
templates/encrypt.html
Normal file → Executable file
0
templates/index.html
Normal file → Executable file
0
templates/index.html
Normal file → Executable file
218
ui.go
218
ui.go
@ -233,22 +233,22 @@ func readImageClipboard() (image.Image, error) {
|
||||
|
||||
// Identity tab
|
||||
func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.CanvasObject {
|
||||
// Toolbar: choose what to encode into the QR to keep density manageable
|
||||
mode := "combined" // combined, cert, pub
|
||||
chooser := widget.NewSelect([]string{"Veřejný+Cert", "Jen Cert", "Jen Veřejný"}, func(string) {})
|
||||
chooser.Selected = "Veřejný+Cert"
|
||||
var update func()
|
||||
chooser.OnChanged = func(v string) {
|
||||
switch v {
|
||||
case "Jen Cert":
|
||||
mode = "cert"
|
||||
case "Jen Veřejný":
|
||||
mode = "pub"
|
||||
default:
|
||||
mode = "combined"
|
||||
}
|
||||
update()
|
||||
}
|
||||
// Toolbar: choose what to encode into the QR to keep density manageable
|
||||
mode := "combined" // combined, cert, pub
|
||||
chooser := widget.NewSelect([]string{"Veřejný+Cert", "Jen Cert", "Jen Veřejný"}, func(string) {})
|
||||
chooser.Selected = "Veřejný+Cert"
|
||||
var update func()
|
||||
chooser.OnChanged = func(v string) {
|
||||
switch v {
|
||||
case "Jen Cert":
|
||||
mode = "cert"
|
||||
case "Jen Veřejný":
|
||||
mode = "pub"
|
||||
default:
|
||||
mode = "combined"
|
||||
}
|
||||
update()
|
||||
}
|
||||
deleteBtn := widget.NewButton("Smazat identitu", func() {
|
||||
pw := widget.NewPasswordEntry()
|
||||
form := widget.NewForm(widget.NewFormItem("Heslo", pw))
|
||||
@ -267,12 +267,12 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.
|
||||
d.Resize(fyne.NewSize(420, 200))
|
||||
d.Show()
|
||||
})
|
||||
makeQR := func(data string, target *canvas.Image) {
|
||||
if data == "" {
|
||||
target.Image = nil
|
||||
target.Refresh()
|
||||
return
|
||||
}
|
||||
makeQR := func(data string, target *canvas.Image) {
|
||||
if data == "" {
|
||||
target.Image = nil
|
||||
target.Refresh()
|
||||
return
|
||||
}
|
||||
if b, err := GenerateQRPNG(data, 512); err == nil {
|
||||
if im, err2 := LoadPNG(b); err2 == nil {
|
||||
target.Image = im
|
||||
@ -280,56 +280,156 @@ func buildIdentityTab(parts *uiParts, svc ServiceFacade, vaultPath string) fyne.
|
||||
}
|
||||
}
|
||||
}
|
||||
update = func() {
|
||||
if parts.showQR {
|
||||
var text string
|
||||
switch mode {
|
||||
case "cert":
|
||||
text = strings.TrimSpace(svc.PublicCert())
|
||||
case "pub":
|
||||
text = strings.TrimSpace(svc.PublicPEM())
|
||||
default:
|
||||
text = strings.TrimSpace(svc.PublicPEM() + "\n" + svc.PublicCert())
|
||||
}
|
||||
makeQR(text, parts.pubQR)
|
||||
// debug save for comparison
|
||||
if parts.pubQR.Image != nil {
|
||||
_ = os.MkdirAll("qr_debug", 0o755)
|
||||
if f, e := os.Create(filepath.Join("qr_debug", "identity_current.png")); e == nil {
|
||||
png.Encode(f, parts.pubQR.Image)
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parts.pubQR.Image = nil
|
||||
parts.pubQR.Refresh()
|
||||
}
|
||||
}
|
||||
update()
|
||||
box := container.NewVBox(container.NewHBox(widget.NewLabel("QR obsah:"), chooser, layout.NewSpacer(), widget.NewButtonWithIcon("Kopírovat jako obrázek", theme.ContentPasteIcon(), func() { copyImageToClipboard(parts.pubQR.Image, parts) })), parts.pubQR)
|
||||
return container.NewVScroll(container.NewVBox(container.NewHBox(widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), layout.NewSpacer()), box, deleteBtn))
|
||||
update = func() {
|
||||
if parts.showQR {
|
||||
var text string
|
||||
switch mode {
|
||||
case "cert":
|
||||
text = strings.TrimSpace(svc.PublicCert())
|
||||
case "pub":
|
||||
text = strings.TrimSpace(svc.PublicPEM())
|
||||
default:
|
||||
text = strings.TrimSpace(svc.PublicPEM() + "\n" + svc.PublicCert())
|
||||
}
|
||||
makeQR(text, parts.pubQR)
|
||||
// debug save for comparison
|
||||
if parts.pubQR.Image != nil {
|
||||
_ = os.MkdirAll("qr_debug", 0o755)
|
||||
if f, e := os.Create(filepath.Join("qr_debug", "identity_current.png")); e == nil {
|
||||
png.Encode(f, parts.pubQR.Image)
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parts.pubQR.Image = nil
|
||||
parts.pubQR.Refresh()
|
||||
}
|
||||
}
|
||||
update()
|
||||
box := container.NewVBox(container.NewHBox(widget.NewLabel("QR obsah:"), chooser, layout.NewSpacer(), widget.NewButtonWithIcon("Kopírovat jako obrázek", theme.ContentPasteIcon(), func() { copyImageToClipboard(parts.pubQR.Image, parts) })), parts.pubQR)
|
||||
return container.NewVScroll(container.NewVBox(container.NewHBox(widget.NewLabelWithStyle("Moje identita", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), layout.NewSpacer()), box, deleteBtn))
|
||||
}
|
||||
|
||||
// Decrypt tab
|
||||
func buildDecryptTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
parts.plainOut.Disable()
|
||||
decrypt := func() {
|
||||
js := parts.payload.Text
|
||||
if js == "" {
|
||||
parts.payload.Disable()
|
||||
parts.payload.SetPlaceHolder("Cyphertext se načte po vložení QR kódu…")
|
||||
parts.payload.OnChanged = nil
|
||||
parts.payloadQR.FillMode = canvas.ImageFillContain
|
||||
parts.payloadQR.SetMinSize(fyne.NewSize(260, 260))
|
||||
|
||||
decrypt := func(text string) {
|
||||
trimmed := strings.TrimSpace(text)
|
||||
if trimmed == "" {
|
||||
parts.plainOut.SetText("")
|
||||
return
|
||||
}
|
||||
go func(j string) {
|
||||
res, err := svc.Decrypt(j)
|
||||
if err != nil {
|
||||
fyne.Do(func() { parts.plainOut.SetText("") })
|
||||
fyne.Do(func() {
|
||||
parts.plainOut.SetText("")
|
||||
parts.showToast("Chyba dešifrování")
|
||||
})
|
||||
return
|
||||
}
|
||||
fyne.Do(func() { parts.plainOut.SetText(res) })
|
||||
}(js)
|
||||
}(trimmed)
|
||||
}
|
||||
parts.payload.OnChanged = func(string) { decrypt() }
|
||||
return container.NewVBox(widget.NewLabelWithStyle("Dešifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}), parts.payload, parts.plainOut)
|
||||
|
||||
setPayload := func(cipher string, img image.Image) {
|
||||
parts.payload.SetText(cipher)
|
||||
parts.payloadQR.Image = img
|
||||
parts.payloadQR.Refresh()
|
||||
decrypt(cipher)
|
||||
}
|
||||
|
||||
decodeFromImage := func(img image.Image) {
|
||||
if img == nil {
|
||||
parts.showToast("Žádný QR obrázek")
|
||||
return
|
||||
}
|
||||
txt, err := DecodeQR(img)
|
||||
if err != nil {
|
||||
bounds := img.Bounds()
|
||||
inv := image.NewRGBA(bounds)
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
r, g, b, a := img.At(x, y).RGBA()
|
||||
inv.Set(x, y, color.RGBA{uint8(255 - r/257), uint8(255 - g/257), uint8(255 - b/257), uint8(a / 257)})
|
||||
}
|
||||
}
|
||||
if txt2, err2 := DecodeQR(inv); err2 == nil {
|
||||
setPayload(txt2, img)
|
||||
parts.showToast("Načteno z invert QR")
|
||||
return
|
||||
}
|
||||
parts.showToast("QR nenalezen: " + err.Error())
|
||||
return
|
||||
}
|
||||
setPayload(txt, img)
|
||||
parts.showToast("Načteno z QR")
|
||||
}
|
||||
|
||||
pasteQRBtn := widget.NewButtonWithIcon("Vložit ze schránky", theme.ContentPasteIcon(), func() {
|
||||
img, err := readImageClipboard()
|
||||
if err != nil {
|
||||
parts.showToast("Chyba schránky: " + err.Error())
|
||||
return
|
||||
}
|
||||
decodeFromImage(img)
|
||||
})
|
||||
|
||||
openQRBtn := widget.NewButtonWithIcon("Otevřít obrázek", theme.FolderOpenIcon(), func() {
|
||||
win := fyne.CurrentApp().Driver().AllWindows()[0]
|
||||
fd := dialog.NewFileOpen(func(rc fyne.URIReadCloser, err error) {
|
||||
if err != nil || rc == nil {
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
data, readErr := io.ReadAll(rc)
|
||||
if readErr != nil {
|
||||
parts.showToast("Chyba čtení souboru")
|
||||
return
|
||||
}
|
||||
img, _, decErr := image.Decode(bytes.NewReader(data))
|
||||
if decErr != nil {
|
||||
parts.showToast("Neplatný obrázek: " + decErr.Error())
|
||||
return
|
||||
}
|
||||
decodeFromImage(img)
|
||||
}, win)
|
||||
fd.SetFilter(storage.NewExtensionFileFilter([]string{".png", ".jpg", ".jpeg"}))
|
||||
fd.Show()
|
||||
})
|
||||
|
||||
copyPayloadBtn := widget.NewButtonWithIcon("Kopírovat payload", theme.ContentCopyIcon(), func() {
|
||||
if parts.payload.Text == "" {
|
||||
return
|
||||
}
|
||||
copyClip(parts.payload.Text, parts)
|
||||
})
|
||||
|
||||
clearBtn := widget.NewButtonWithIcon("Vymazat", theme.ContentClearIcon(), func() {
|
||||
parts.payload.SetText("")
|
||||
parts.payloadQR.Image = nil
|
||||
parts.payloadQR.Refresh()
|
||||
parts.plainOut.SetText("")
|
||||
parts.showToast("Vymazáno")
|
||||
})
|
||||
|
||||
toolbar := container.NewHBox(pasteQRBtn, openQRBtn, copyPayloadBtn, clearBtn, layout.NewSpacer())
|
||||
|
||||
return container.NewVBox(
|
||||
widget.NewLabelWithStyle("Dešifrování", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||
toolbar,
|
||||
container.NewHBox(parts.payloadQR, layout.NewSpacer()),
|
||||
widget.NewLabel("Payload"),
|
||||
parts.payload,
|
||||
widget.NewLabel("Výsledek"),
|
||||
parts.plainOut,
|
||||
)
|
||||
}
|
||||
|
||||
// Per-contact encryption popup (QR-only output)
|
||||
@ -639,6 +739,9 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
if useEncrypt {
|
||||
parts.peer.SetText(cert)
|
||||
}
|
||||
if popup != nil {
|
||||
popup.Hide()
|
||||
}
|
||||
}
|
||||
if ask {
|
||||
dialog.NewCustomConfirm("Common Name", "Použít", "Ponechat", widget.NewLabel(fmt.Sprintf("Common Name: %s\nPoužít jako název?", cn)), func(ok bool) {
|
||||
@ -651,7 +754,6 @@ func buildContactsTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
return
|
||||
}
|
||||
proceed(name)
|
||||
popup.Hide()
|
||||
}
|
||||
delBtn := widget.NewButtonWithIcon("Smazat", theme.DeleteIcon(), func() {
|
||||
if existing == nil {
|
||||
|
||||
12
vendor/github.com/makiuchi-d/gozxing/.gitignore
generated
vendored
Normal file
12
vendor/github.com/makiuchi-d/gozxing/.gitignore
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
230
vendor/github.com/makiuchi-d/gozxing/LICENSE
generated
vendored
Normal file
230
vendor/github.com/makiuchi-d/gozxing/LICENSE
generated
vendored
Normal file
@ -0,0 +1,230 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Daisuke MAKIUCHI (MakKi; makki_d)
|
||||
|
||||
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.
|
||||
|
||||
|
||||
========================================================================
|
||||
Original zxing License
|
||||
Copyright 2007-2018 ZXing authors
|
||||
========================================================================
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
113
vendor/github.com/makiuchi-d/gozxing/README.md
generated
vendored
Normal file
113
vendor/github.com/makiuchi-d/gozxing/README.md
generated
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
# gozxing A Barcode Scanning/Encoding Library for Go
|
||||
|
||||
[](https://github.com/makiuchi-d/gozxing/actions/workflows/main.yml)
|
||||
[](https://codecov.io/gh/makiuchi-d/gozxing)
|
||||
|
||||
[ZXing](https://github.com/zxing/zxing) is an open-source, multi-format 1D/2D barcode image processing library for Java.
|
||||
This project is a port of ZXing core library to pure Go.
|
||||
|
||||
## Porting Status (supported formats)
|
||||
|
||||
### 2D barcodes
|
||||
|
||||
| Format | Scanning | Encoding |
|
||||
|-------------|--------------------|--------------------|
|
||||
| QR Code | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Data Matrix | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Aztec | :heavy_check_mark: | |
|
||||
| PDF 417 | | |
|
||||
| MaxiCode | | |
|
||||
|
||||
|
||||
### 1D product barcodes
|
||||
|
||||
| Format | Scanning | Encoding |
|
||||
|-------------|--------------------|--------------------|
|
||||
| UPC-A | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| UPC-E | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| EAN-8 | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| EAN-13 | :heavy_check_mark: | :heavy_check_mark: |
|
||||
|
||||
### 1D industrial barcode
|
||||
|
||||
| Format | Scanning | Encoding |
|
||||
|--------------|--------------------|--------------------|
|
||||
| Code 39 | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Code 93 | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Code 128 | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Codabar | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| ITF | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| RSS-14 | :heavy_check_mark: | - |
|
||||
| RSS-Expanded | | |
|
||||
|
||||
### Special reader/writer
|
||||
|
||||
| Reader/Writer | Porting status |
|
||||
|------------------------------|--------------------|
|
||||
| MultiFormatReader | |
|
||||
| MultiFormatWriter | |
|
||||
| ByQuadrantReader | |
|
||||
| GenericMultipleBarcodeReader | |
|
||||
| QRCodeMultiReader | :heavy_check_mark: |
|
||||
| MultiFormatUPCEANReader | :heavy_check_mark: |
|
||||
| MultiFormatOneDReader | |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Scanning QR code
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
_ "image/jpeg"
|
||||
"os"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
"github.com/makiuchi-d/gozxing/qrcode"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// open and decode image file
|
||||
file, _ := os.Open("qrcode.jpg")
|
||||
img, _, _ := image.Decode(file)
|
||||
|
||||
// prepare BinaryBitmap
|
||||
bmp, _ := gozxing.NewBinaryBitmapFromImage(img)
|
||||
|
||||
// decode image
|
||||
qrReader := qrcode.NewQRCodeReader()
|
||||
result, _ := qrReader.Decode(bmp, nil)
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
```
|
||||
|
||||
### Generating CODE128 barcode
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/png"
|
||||
"os"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
"github.com/makiuchi-d/gozxing/oned"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Generate a barcode image (*BitMatrix)
|
||||
enc := oned.NewCode128Writer()
|
||||
img, _ := enc.Encode("Hello, Gophers!", gozxing.BarcodeFormat_CODE_128, 250, 50, nil)
|
||||
|
||||
file, _ := os.Create("barcode.png")
|
||||
defer file.Close()
|
||||
|
||||
// *BitMatrix implements the image.Image interface,
|
||||
// so it is able to be passed to png.Encode directly.
|
||||
_ = png.Encode(file, img)
|
||||
}
|
||||
```
|
||||
107
vendor/github.com/makiuchi-d/gozxing/barcode_format.go
generated
vendored
Normal file
107
vendor/github.com/makiuchi-d/gozxing/barcode_format.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
package gozxing
|
||||
|
||||
type BarcodeFormat int
|
||||
type BarcodeFormats []BarcodeFormat
|
||||
|
||||
const (
|
||||
/** Aztec 2D barcode format. */
|
||||
BarcodeFormat_AZTEC = BarcodeFormat(iota)
|
||||
|
||||
/** CODABAR 1D format. */
|
||||
BarcodeFormat_CODABAR
|
||||
|
||||
/** Code 39 1D format. */
|
||||
BarcodeFormat_CODE_39
|
||||
|
||||
/** Code 93 1D format. */
|
||||
BarcodeFormat_CODE_93
|
||||
|
||||
/** Code 128 1D format. */
|
||||
BarcodeFormat_CODE_128
|
||||
|
||||
/** Data Matrix 2D barcode format. */
|
||||
BarcodeFormat_DATA_MATRIX
|
||||
|
||||
/** EAN-8 1D format. */
|
||||
BarcodeFormat_EAN_8
|
||||
|
||||
/** EAN-13 1D format. */
|
||||
BarcodeFormat_EAN_13
|
||||
|
||||
/** ITF (Interleaved Two of Five) 1D format. */
|
||||
BarcodeFormat_ITF
|
||||
|
||||
/** MaxiCode 2D barcode format. */
|
||||
BarcodeFormat_MAXICODE
|
||||
|
||||
/** PDF417 format. */
|
||||
BarcodeFormat_PDF_417
|
||||
|
||||
/** QR Code 2D barcode format. */
|
||||
BarcodeFormat_QR_CODE
|
||||
|
||||
/** RSS 14 */
|
||||
BarcodeFormat_RSS_14
|
||||
|
||||
/** RSS EXPANDED */
|
||||
BarcodeFormat_RSS_EXPANDED
|
||||
|
||||
/** UPC-A 1D format. */
|
||||
BarcodeFormat_UPC_A
|
||||
|
||||
/** UPC-E 1D format. */
|
||||
BarcodeFormat_UPC_E
|
||||
|
||||
/** UPC/EAN extension format. Not a stand-alone format. */
|
||||
BarcodeFormat_UPC_EAN_EXTENSION
|
||||
)
|
||||
|
||||
func (f BarcodeFormat) String() string {
|
||||
switch f {
|
||||
case BarcodeFormat_AZTEC:
|
||||
return "AZTEC"
|
||||
case BarcodeFormat_CODABAR:
|
||||
return "CODABAR"
|
||||
case BarcodeFormat_CODE_39:
|
||||
return "CODE_39"
|
||||
case BarcodeFormat_CODE_93:
|
||||
return "CODE_93"
|
||||
case BarcodeFormat_CODE_128:
|
||||
return "CODE_128"
|
||||
case BarcodeFormat_DATA_MATRIX:
|
||||
return "DATA_MATRIX"
|
||||
case BarcodeFormat_EAN_8:
|
||||
return "EAN_8"
|
||||
case BarcodeFormat_EAN_13:
|
||||
return "EAN_13"
|
||||
case BarcodeFormat_ITF:
|
||||
return "ITF"
|
||||
case BarcodeFormat_MAXICODE:
|
||||
return "MAXICODE"
|
||||
case BarcodeFormat_PDF_417:
|
||||
return "PDF_417"
|
||||
case BarcodeFormat_QR_CODE:
|
||||
return "QR_CODE"
|
||||
case BarcodeFormat_RSS_14:
|
||||
return "RSS_14"
|
||||
case BarcodeFormat_RSS_EXPANDED:
|
||||
return "RSS_EXPANDED"
|
||||
case BarcodeFormat_UPC_A:
|
||||
return "UPC_A"
|
||||
case BarcodeFormat_UPC_E:
|
||||
return "UPC_E"
|
||||
case BarcodeFormat_UPC_EAN_EXTENSION:
|
||||
return "UPC_EAN_EXTENSION"
|
||||
default:
|
||||
return "unknown format"
|
||||
}
|
||||
}
|
||||
|
||||
func (barcodes BarcodeFormats) Contains(c BarcodeFormat) bool {
|
||||
for _, bc := range barcodes {
|
||||
if bc == c {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
45
vendor/github.com/makiuchi-d/gozxing/binarizr.go
generated
vendored
Normal file
45
vendor/github.com/makiuchi-d/gozxing/binarizr.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package gozxing
|
||||
|
||||
type Binarizer interface {
|
||||
GetLuminanceSource() LuminanceSource
|
||||
|
||||
/**
|
||||
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||
* For callers which only examine one row of pixels at a time, the same BitArray should be reused
|
||||
* and passed in with each call for performance. However it is legal to keep more than one row
|
||||
* at a time if needed.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0, bitmap height)
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||
* @return The array of bits for this row (true means black).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
GetBlackRow(y int, row *BitArray) (*BitArray, error)
|
||||
|
||||
/**
|
||||
* Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
|
||||
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
|
||||
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
|
||||
* fetched using getBlackRow(), so don't mix and match between them.
|
||||
*
|
||||
* @return The 2D array of bits for the image (true means black).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
GetBlackMatrix() (*BitMatrix, error)
|
||||
|
||||
/**
|
||||
* Creates a new object with the same type as this Binarizer implementation, but with pristine
|
||||
* state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
|
||||
* of 1 bit data. See Effective Java for why we can't use Java's clone() method.
|
||||
*
|
||||
* @param source The LuminanceSource this Binarizer will operate on.
|
||||
* @return A new concrete Binarizer implementation object.
|
||||
*/
|
||||
CreateBinarizer(source LuminanceSource) Binarizer
|
||||
|
||||
GetWidth() int
|
||||
GetHeight() int
|
||||
}
|
||||
88
vendor/github.com/makiuchi-d/gozxing/binary_bitmap.go
generated
vendored
Normal file
88
vendor/github.com/makiuchi-d/gozxing/binary_bitmap.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type BinaryBitmap struct {
|
||||
binarizer Binarizer
|
||||
matrix *BitMatrix
|
||||
}
|
||||
|
||||
func NewBinaryBitmap(binarizer Binarizer) (*BinaryBitmap, error) {
|
||||
if binarizer == nil {
|
||||
return nil, errors.New("IllegalArgumentException: Binarizer must be non-null")
|
||||
}
|
||||
return &BinaryBitmap{binarizer, nil}, nil
|
||||
}
|
||||
|
||||
func (this *BinaryBitmap) GetWidth() int {
|
||||
return this.binarizer.GetWidth()
|
||||
}
|
||||
|
||||
func (this *BinaryBitmap) GetHeight() int {
|
||||
return this.binarizer.GetHeight()
|
||||
}
|
||||
|
||||
func (this *BinaryBitmap) GetBlackRow(y int, row *BitArray) (*BitArray, error) {
|
||||
return this.binarizer.GetBlackRow(y, row)
|
||||
}
|
||||
|
||||
func (this *BinaryBitmap) GetBlackMatrix() (*BitMatrix, error) {
|
||||
// The matrix is created on demand the first time it is requested, then cached. There are two
|
||||
// reasons for this:
|
||||
// 1. This work will never be done if the caller only installs 1D Reader objects, or if a
|
||||
// 1D Reader finds a barcode before the 2D Readers run.
|
||||
// 2. This work will only be done once even if the caller installs multiple 2D Readers.
|
||||
if this.matrix == nil {
|
||||
var e error
|
||||
this.matrix, e = this.binarizer.GetBlackMatrix()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
return this.matrix, nil
|
||||
}
|
||||
|
||||
func (this *BinaryBitmap) IsCropSupported() bool {
|
||||
return this.binarizer.GetLuminanceSource().IsCropSupported()
|
||||
}
|
||||
|
||||
func (this *BinaryBitmap) Crop(left, top, width, height int) (*BinaryBitmap, error) {
|
||||
newSource, e := this.binarizer.GetLuminanceSource().Crop(left, top, width, height)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return NewBinaryBitmap(this.binarizer.CreateBinarizer(newSource))
|
||||
}
|
||||
|
||||
func (this *BinaryBitmap) IsRotateSupported() bool {
|
||||
return this.binarizer.GetLuminanceSource().IsRotateSupported()
|
||||
}
|
||||
|
||||
func (this *BinaryBitmap) RotateCounterClockwise() (*BinaryBitmap, error) {
|
||||
newSource, e := this.binarizer.GetLuminanceSource().RotateCounterClockwise()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return NewBinaryBitmap(this.binarizer.CreateBinarizer(newSource))
|
||||
}
|
||||
|
||||
func (this *BinaryBitmap) RotateCounterClockwise45() (*BinaryBitmap, error) {
|
||||
newSource, e := this.binarizer.GetLuminanceSource().RotateCounterClockwise45()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return NewBinaryBitmap(this.binarizer.CreateBinarizer(newSource))
|
||||
}
|
||||
|
||||
func (this *BinaryBitmap) String() string {
|
||||
matrix, e := this.GetBlackMatrix()
|
||||
if e != nil {
|
||||
if _, ok := e.(NotFoundException); ok {
|
||||
return ""
|
||||
}
|
||||
return e.Error()
|
||||
}
|
||||
return matrix.String()
|
||||
}
|
||||
255
vendor/github.com/makiuchi-d/gozxing/bit_array.go
generated
vendored
Normal file
255
vendor/github.com/makiuchi-d/gozxing/bit_array.go
generated
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type BitArray struct {
|
||||
bits []uint32
|
||||
size int
|
||||
}
|
||||
|
||||
func NewEmptyBitArray() *BitArray {
|
||||
return &BitArray{makeArray(1), 0}
|
||||
}
|
||||
|
||||
func NewBitArray(size int) *BitArray {
|
||||
return &BitArray{makeArray(size), size}
|
||||
}
|
||||
|
||||
func (b *BitArray) GetSize() int {
|
||||
return b.size
|
||||
}
|
||||
|
||||
func (b *BitArray) GetSizeInBytes() int {
|
||||
return (b.size + 7) / 8
|
||||
}
|
||||
|
||||
func (b *BitArray) ensureCapacity(size int) {
|
||||
if size > len(b.bits)*32 {
|
||||
newBits := makeArray(size)
|
||||
copy(newBits, b.bits)
|
||||
b.bits = newBits
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BitArray) Get(i int) bool {
|
||||
return (b.bits[i/32] & (1 << uint(i%32))) != 0
|
||||
}
|
||||
|
||||
func (b *BitArray) Set(i int) {
|
||||
b.bits[i/32] |= 1 << uint(i%32)
|
||||
}
|
||||
|
||||
func (b *BitArray) Flip(i int) {
|
||||
b.bits[i/32] ^= 1 << uint(i%32)
|
||||
}
|
||||
|
||||
func (b *BitArray) GetNextSet(from int) int {
|
||||
if from >= b.size {
|
||||
return b.size
|
||||
}
|
||||
bitsOffset := from / 32
|
||||
currentBits := b.bits[bitsOffset]
|
||||
currentBits &= -(1 << uint(from&0x1F))
|
||||
for currentBits == 0 {
|
||||
bitsOffset++
|
||||
if bitsOffset == len(b.bits) {
|
||||
return b.size
|
||||
}
|
||||
currentBits = b.bits[bitsOffset]
|
||||
}
|
||||
result := (bitsOffset * 32) + bits.TrailingZeros32(currentBits)
|
||||
if result > b.size {
|
||||
return b.size
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (b *BitArray) GetNextUnset(from int) int {
|
||||
if from >= b.size {
|
||||
return b.size
|
||||
}
|
||||
bitsOffset := from / 32
|
||||
currentBits := ^b.bits[bitsOffset]
|
||||
currentBits &= -(1 << uint(from&0x1F))
|
||||
for currentBits == 0 {
|
||||
bitsOffset++
|
||||
if bitsOffset == len(b.bits) {
|
||||
return b.size
|
||||
}
|
||||
currentBits = ^b.bits[bitsOffset]
|
||||
}
|
||||
result := (bitsOffset * 32) + bits.TrailingZeros32(currentBits)
|
||||
if result > b.size {
|
||||
return b.size
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (b *BitArray) SetBulk(i int, newBits uint32) {
|
||||
b.bits[i/32] = newBits
|
||||
}
|
||||
|
||||
func (b *BitArray) SetRange(start, end int) error {
|
||||
if end < start || start < 0 || end > b.size {
|
||||
return errors.New("IllegalArgumentException")
|
||||
}
|
||||
if end == start {
|
||||
return nil
|
||||
}
|
||||
end--
|
||||
firstInt := start / 32
|
||||
lastInt := end / 32
|
||||
for i := firstInt; i <= lastInt; i++ {
|
||||
firstBit := 0
|
||||
lastBit := 31
|
||||
if i == firstInt {
|
||||
firstBit = start % 32
|
||||
}
|
||||
if i == lastInt {
|
||||
lastBit = end % 32
|
||||
}
|
||||
mask := (2 << uint(lastBit)) - (1 << uint(firstBit))
|
||||
b.bits[i] |= uint32(mask)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BitArray) Clear() {
|
||||
for i := range b.bits {
|
||||
b.bits[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BitArray) IsRange(start, end int, value bool) (bool, error) {
|
||||
if end < start || start < 0 || end > b.size {
|
||||
return false, errors.New("IllegalArgumentException")
|
||||
}
|
||||
if end == start {
|
||||
return true, nil
|
||||
}
|
||||
end--
|
||||
firstInt := start / 32
|
||||
lastInt := end / 32
|
||||
for i := firstInt; i <= lastInt; i++ {
|
||||
firstBit := 0
|
||||
lastBit := 31
|
||||
if i == firstInt {
|
||||
firstBit = start % 32
|
||||
}
|
||||
if i == lastInt {
|
||||
lastBit = end % 32
|
||||
}
|
||||
mask := uint32((2 << uint(lastBit)) - (1 << uint(firstBit)))
|
||||
expect := uint32(0)
|
||||
if value {
|
||||
expect = mask
|
||||
}
|
||||
if (b.bits[i] & mask) != expect {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (b *BitArray) AppendBit(bit bool) {
|
||||
b.ensureCapacity(b.size + 1)
|
||||
if bit {
|
||||
b.bits[b.size/32] |= 1 << uint(b.size%32)
|
||||
}
|
||||
b.size++
|
||||
}
|
||||
|
||||
func (b *BitArray) AppendBits(value int, numBits int) error {
|
||||
if numBits < 0 || numBits > 32 {
|
||||
return errors.New("IllegalArgumentException: Num bits must be between 0 and 32")
|
||||
}
|
||||
b.ensureCapacity(b.size + numBits)
|
||||
for numBitsLeft := numBits; numBitsLeft > 0; numBitsLeft-- {
|
||||
b.AppendBit(((value >> uint(numBitsLeft-1)) & 0x01) == 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BitArray) AppendBitArray(other *BitArray) {
|
||||
otherSize := other.size
|
||||
b.ensureCapacity(b.size + otherSize)
|
||||
for i := 0; i < otherSize; i++ {
|
||||
b.AppendBit(other.Get(i))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BitArray) Xor(other *BitArray) error {
|
||||
if b.size != other.size {
|
||||
return errors.New("IllegalArgumentException: Sizes don't match")
|
||||
}
|
||||
for i := 0; i < len(b.bits); i++ {
|
||||
b.bits[i] ^= other.bits[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BitArray) ToBytes(bitOffset int, array []byte, offset, numBytes int) {
|
||||
for i := 0; i < numBytes; i++ {
|
||||
theByte := byte(0)
|
||||
for j := 0; j < 8; j++ {
|
||||
if b.Get(bitOffset) {
|
||||
theByte |= 1 << uint(7-j)
|
||||
}
|
||||
bitOffset++
|
||||
}
|
||||
array[offset+i] = theByte
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BitArray) GetBitArray() []uint32 {
|
||||
return b.bits
|
||||
}
|
||||
|
||||
func (b *BitArray) Reverse() {
|
||||
newBits := make([]uint32, len(b.bits))
|
||||
len := (b.size - 1) / 32
|
||||
oldBitsLen := len + 1
|
||||
for i := 0; i < oldBitsLen; i++ {
|
||||
newBits[len-i] = bits.Reverse32(b.bits[i])
|
||||
}
|
||||
if b.size != oldBitsLen*32 {
|
||||
leftOffset := uint(oldBitsLen*32 - b.size)
|
||||
currentInt := newBits[0] >> leftOffset
|
||||
for i := 1; i < oldBitsLen; i++ {
|
||||
nextInt := newBits[i]
|
||||
currentInt |= nextInt << uint(32-leftOffset)
|
||||
newBits[i-1] = currentInt
|
||||
currentInt = nextInt >> leftOffset
|
||||
}
|
||||
newBits[oldBitsLen-1] = currentInt
|
||||
}
|
||||
b.bits = newBits
|
||||
}
|
||||
|
||||
func makeArray(size int) []uint32 {
|
||||
return make([]uint32, (size+31)/32)
|
||||
}
|
||||
|
||||
// equals()
|
||||
// hasCode()
|
||||
|
||||
func (b *BitArray) String() string {
|
||||
result := make([]byte, 0, b.size+(b.size/8)+1)
|
||||
for i := 0; i < b.size; i++ {
|
||||
if (i % 8) == 0 {
|
||||
result = append(result, ' ')
|
||||
}
|
||||
if b.Get(i) {
|
||||
result = append(result, 'X')
|
||||
} else {
|
||||
result = append(result, '.')
|
||||
}
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// clone()
|
||||
392
vendor/github.com/makiuchi-d/gozxing/bit_matrix.go
generated
vendored
Normal file
392
vendor/github.com/makiuchi-d/gozxing/bit_matrix.go
generated
vendored
Normal file
@ -0,0 +1,392 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
"strings"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type BitMatrix struct {
|
||||
width int
|
||||
height int
|
||||
rowSize int
|
||||
bits []uint32
|
||||
}
|
||||
|
||||
func NewSquareBitMatrix(dimension int) (*BitMatrix, error) {
|
||||
return NewBitMatrix(dimension, dimension)
|
||||
}
|
||||
|
||||
func NewBitMatrix(width, height int) (*BitMatrix, error) {
|
||||
if width < 1 || height < 1 {
|
||||
return nil, errors.New("IllegalArgumentException: Both dimensions must be greater than 0")
|
||||
}
|
||||
rowSize := (width + 31) / 32
|
||||
bits := make([]uint32, rowSize*height)
|
||||
return &BitMatrix{width, height, rowSize, bits}, nil
|
||||
}
|
||||
|
||||
func ParseBoolMapToBitMatrix(image [][]bool) (*BitMatrix, error) {
|
||||
var width, height int
|
||||
height = len(image)
|
||||
if height > 0 {
|
||||
width = len(image[0])
|
||||
}
|
||||
bits, e := NewBitMatrix(width, height)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
for i := 0; i < height; i++ {
|
||||
imageI := image[i]
|
||||
for j := 0; j < width; j++ {
|
||||
if imageI[j] {
|
||||
bits.Set(j, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
return bits, nil
|
||||
}
|
||||
|
||||
func ParseStringToBitMatrix(stringRepresentation, setString, unsetString string) (*BitMatrix, error) {
|
||||
if stringRepresentation == "" {
|
||||
return nil, errors.New("IllegalArgumentException")
|
||||
}
|
||||
|
||||
bits := make([]bool, len(stringRepresentation))
|
||||
bitsPos := 0
|
||||
rowStartPos := 0
|
||||
rowLength := -1
|
||||
nRows := 0
|
||||
pos := 0
|
||||
for pos < len(stringRepresentation) {
|
||||
if c := stringRepresentation[pos]; c == '\n' || c == '\r' {
|
||||
if bitsPos > rowStartPos {
|
||||
if rowLength == -1 {
|
||||
rowLength = bitsPos - rowStartPos
|
||||
} else if bitsPos-rowStartPos != rowLength {
|
||||
return nil, errors.New("IllegalArgumentException: row length do not match")
|
||||
}
|
||||
rowStartPos = bitsPos
|
||||
nRows++
|
||||
}
|
||||
pos++
|
||||
} else if strings.HasPrefix(stringRepresentation[pos:], setString) {
|
||||
pos += len(setString)
|
||||
bits[bitsPos] = true
|
||||
bitsPos++
|
||||
} else if strings.HasPrefix(stringRepresentation[pos:], unsetString) {
|
||||
pos += len(unsetString)
|
||||
bits[bitsPos] = false
|
||||
bitsPos++
|
||||
} else {
|
||||
return nil, errors.New(
|
||||
"IllegalArgumentException: illegal character encountered: " + stringRepresentation[pos:])
|
||||
}
|
||||
}
|
||||
|
||||
if bitsPos > rowStartPos {
|
||||
if rowLength == -1 {
|
||||
rowLength = bitsPos - rowStartPos
|
||||
} else if bitsPos-rowStartPos != rowLength {
|
||||
return nil, errors.New("IllegalArgumentException: row length do not match")
|
||||
}
|
||||
nRows++
|
||||
}
|
||||
matrix, e := NewBitMatrix(rowLength, nRows)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
for i := 0; i < bitsPos; i++ {
|
||||
if bits[i] {
|
||||
matrix.Set(i%rowLength, i/rowLength)
|
||||
}
|
||||
}
|
||||
return matrix, nil
|
||||
}
|
||||
|
||||
func (b *BitMatrix) Get(x, y int) bool {
|
||||
if x < 0 || x >= b.width || y < 0 || y >= b.height {
|
||||
return false
|
||||
}
|
||||
offset := (y * b.rowSize) + (x / 32)
|
||||
return ((b.bits[offset] >> uint(x%32)) & 1) != 0
|
||||
}
|
||||
|
||||
func (b *BitMatrix) Set(x, y int) {
|
||||
offset := (y * b.rowSize) + (x / 32)
|
||||
b.bits[offset] |= 1 << uint(x%32)
|
||||
}
|
||||
|
||||
func (b *BitMatrix) Unset(x, y int) {
|
||||
offset := (y * b.rowSize) + (x / 32)
|
||||
b.bits[offset] &= ^(1 << uint(x%32))
|
||||
}
|
||||
|
||||
func (b *BitMatrix) Flip(x, y int) {
|
||||
offset := (y * b.rowSize) + (x / 32)
|
||||
b.bits[offset] ^= 1 << uint(x%32)
|
||||
}
|
||||
|
||||
func (b *BitMatrix) FlipAll() {
|
||||
max := len(b.bits)
|
||||
for i := 0; i < max; i++ {
|
||||
b.bits[i] = ^b.bits[i]
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BitMatrix) Xor(mask *BitMatrix) error {
|
||||
if b.width != mask.width || b.height != mask.height || b.rowSize != mask.rowSize {
|
||||
return errors.New("IllegalArgumentException: input matrix dimensions do not match")
|
||||
}
|
||||
for y := 0; y < b.height; y++ {
|
||||
bOffset := y * b.rowSize
|
||||
mOffset := y * mask.rowSize
|
||||
for x := 0; x < b.rowSize; x++ {
|
||||
b.bits[bOffset+x] ^= mask.bits[mOffset+x]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BitMatrix) Clear() {
|
||||
max := len(b.bits)
|
||||
for i := 0; i < max; i++ {
|
||||
b.bits[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BitMatrix) SetRegion(left, top, width, height int) error {
|
||||
if top < 0 || left < 0 {
|
||||
return errors.New("IllegalArgumentException: Left and top must be nonnegative")
|
||||
}
|
||||
if height < 1 || width < 1 {
|
||||
return errors.New("IllegalArgumentException: Height and width must be at least 1")
|
||||
}
|
||||
right := left + width
|
||||
bottom := top + height
|
||||
if bottom > b.height || right > b.width {
|
||||
return errors.New("IllegalArgumentException: The region must fit inside the matrix")
|
||||
}
|
||||
for y := top; y < bottom; y++ {
|
||||
offset := y * b.rowSize
|
||||
for x := left; x < right; x++ {
|
||||
b.bits[offset+(x/32)] |= 1 << uint(x%32)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BitMatrix) GetRow(y int, row *BitArray) *BitArray {
|
||||
if row == nil || row.GetSize() < b.width {
|
||||
row = NewBitArray(b.width)
|
||||
} else {
|
||||
row.Clear()
|
||||
}
|
||||
offset := y * b.rowSize
|
||||
for x := 0; x < b.rowSize; x++ {
|
||||
row.SetBulk(x*32, b.bits[offset+x])
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
func (b *BitMatrix) SetRow(y int, row *BitArray) {
|
||||
offset := y * b.rowSize
|
||||
copy(b.bits[offset:offset+b.rowSize], row.bits)
|
||||
}
|
||||
|
||||
func (b *BitMatrix) Rotate180() {
|
||||
height := b.height
|
||||
rowSize := b.rowSize
|
||||
for i := 0; i < height/2; i++ {
|
||||
topOffset := i * rowSize
|
||||
bottomOffset := (height-i)*rowSize - 1
|
||||
for j := 0; j < rowSize; j++ {
|
||||
top := topOffset + j
|
||||
bottom := bottomOffset - j
|
||||
b.bits[top], b.bits[bottom] = b.bits[bottom], b.bits[top]
|
||||
}
|
||||
}
|
||||
if height%2 != 0 {
|
||||
offset := rowSize * (height - 1) / 2
|
||||
for j := 0; j < rowSize/2; j++ {
|
||||
left := offset + j
|
||||
right := offset + rowSize - 1 - j
|
||||
b.bits[left], b.bits[right] = b.bits[right], b.bits[left]
|
||||
}
|
||||
}
|
||||
|
||||
if shift := uint(b.width % 32); shift != 0 {
|
||||
for i := 0; i < height; i++ {
|
||||
offset := rowSize * i
|
||||
b.bits[offset] = bits.Reverse32(b.bits[offset]) >> uint(32-shift)
|
||||
for j := 1; j < rowSize; j++ {
|
||||
curbits := bits.Reverse32(b.bits[offset+j])
|
||||
b.bits[offset+j-1] |= curbits << shift
|
||||
b.bits[offset+j] = curbits >> uint(32-shift)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BitMatrix) Rotate90() {
|
||||
newWidth := b.height
|
||||
newHeight := b.width
|
||||
newRowSize := (newWidth + 31) / 32
|
||||
newBits := make([]uint32, newRowSize*newHeight)
|
||||
|
||||
for y := 0; y < b.height; y++ {
|
||||
for x := 0; x < b.width; x++ {
|
||||
offset := y*b.rowSize + (x / 32)
|
||||
if ((b.bits[offset] >> (x & 0x1f)) & 1) != 0 {
|
||||
newOffset := (newHeight-1-x)*newRowSize + (y / 32)
|
||||
newBits[newOffset] |= 1 << (y & 0x1f)
|
||||
}
|
||||
}
|
||||
}
|
||||
b.width = newWidth
|
||||
b.height = newHeight
|
||||
b.rowSize = newRowSize
|
||||
b.bits = newBits
|
||||
}
|
||||
|
||||
func (b *BitMatrix) GetEnclosingRectangle() []int {
|
||||
left := b.width
|
||||
top := b.height
|
||||
right := -1
|
||||
bottom := -1
|
||||
|
||||
for y := 0; y < b.height; y++ {
|
||||
for x32 := 0; x32 < b.rowSize; x32++ {
|
||||
theBits := b.bits[y*b.rowSize+x32]
|
||||
if theBits != 0 {
|
||||
if y < top {
|
||||
top = y
|
||||
}
|
||||
if y > bottom {
|
||||
bottom = y
|
||||
}
|
||||
if x32*32 < left {
|
||||
bit := 0
|
||||
for (theBits << uint(31-bit)) == 0 {
|
||||
bit++
|
||||
}
|
||||
if (x32*32 + bit) < left {
|
||||
left = x32*32 + bit
|
||||
}
|
||||
}
|
||||
if x32*32+31 > right {
|
||||
bit := 31
|
||||
for (theBits >> uint(bit)) == 0 {
|
||||
bit--
|
||||
}
|
||||
if (x32*32 + bit) > right {
|
||||
right = x32*32 + bit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if right < left || bottom < top {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []int{left, top, right - left + 1, bottom - top + 1}
|
||||
}
|
||||
|
||||
func (b *BitMatrix) GetTopLeftOnBit() []int {
|
||||
bitsOffset := 0
|
||||
for bitsOffset < len(b.bits) && b.bits[bitsOffset] == 0 {
|
||||
bitsOffset++
|
||||
}
|
||||
if bitsOffset == len(b.bits) {
|
||||
return nil
|
||||
}
|
||||
y := bitsOffset / b.rowSize
|
||||
x := (bitsOffset % b.rowSize) * 32
|
||||
|
||||
theBits := b.bits[bitsOffset]
|
||||
bit := uint(0)
|
||||
for (theBits << (31 - bit)) == 0 {
|
||||
bit++
|
||||
}
|
||||
x += int(bit)
|
||||
return []int{x, y}
|
||||
}
|
||||
|
||||
func (b *BitMatrix) GetBottomRightOnBit() []int {
|
||||
bitsOffset := len(b.bits) - 1
|
||||
for bitsOffset >= 0 && b.bits[bitsOffset] == 0 {
|
||||
bitsOffset--
|
||||
}
|
||||
if bitsOffset < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
y := bitsOffset / b.rowSize
|
||||
x := (bitsOffset % b.rowSize) * 32
|
||||
|
||||
theBits := b.bits[bitsOffset]
|
||||
bit := uint(31)
|
||||
for (theBits >> bit) == 0 {
|
||||
bit--
|
||||
}
|
||||
x += int(bit)
|
||||
|
||||
return []int{x, y}
|
||||
}
|
||||
|
||||
func (b *BitMatrix) GetWidth() int {
|
||||
return b.width
|
||||
}
|
||||
|
||||
func (b *BitMatrix) GetHeight() int {
|
||||
return b.height
|
||||
}
|
||||
|
||||
func (b *BitMatrix) GetRowSize() int {
|
||||
return b.rowSize
|
||||
}
|
||||
|
||||
// public boolean equals(Object o)
|
||||
// public int hashCode()
|
||||
|
||||
func (b *BitMatrix) String() string {
|
||||
return b.ToString("X ", " ")
|
||||
}
|
||||
|
||||
func (b *BitMatrix) ToString(setString, unsetString string) string {
|
||||
return b.ToStringWithLineSeparator(setString, unsetString, "\n")
|
||||
}
|
||||
|
||||
func (b *BitMatrix) ToStringWithLineSeparator(setString, unsetString, lineSeparator string) string {
|
||||
setBytes := []byte(setString)
|
||||
unsetBytes := []byte(unsetString)
|
||||
lineSepBytes := []byte(lineSeparator)
|
||||
|
||||
lineSize := len(lineSeparator)
|
||||
if len(setString) > len(unsetString) {
|
||||
lineSize += b.width * len(setString)
|
||||
} else {
|
||||
lineSize += b.width * len(unsetString)
|
||||
}
|
||||
result := make([]byte, 0, b.height*lineSize)
|
||||
|
||||
for y := 0; y < b.height; y++ {
|
||||
for x := 0; x < b.width; x++ {
|
||||
var s []byte
|
||||
if b.Get(x, y) {
|
||||
s = setBytes
|
||||
} else {
|
||||
s = unsetBytes
|
||||
}
|
||||
result = append(result, s...)
|
||||
}
|
||||
result = append(result, lineSepBytes...)
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// public BitMatrix clone()
|
||||
25
vendor/github.com/makiuchi-d/gozxing/checksum_exception.go
generated
vendored
Normal file
25
vendor/github.com/makiuchi-d/gozxing/checksum_exception.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package gozxing
|
||||
|
||||
type ChecksumException interface {
|
||||
ReaderException
|
||||
checksumException()
|
||||
}
|
||||
|
||||
type checksumException struct {
|
||||
exception
|
||||
}
|
||||
|
||||
func (checksumException) readerException() {}
|
||||
func (checksumException) checksumException() {}
|
||||
|
||||
func NewChecksumException(args ...interface{}) ChecksumException {
|
||||
return checksumException{
|
||||
newException("ChecksumException", args...),
|
||||
}
|
||||
}
|
||||
|
||||
func WrapChecksumException(e error) ChecksumException {
|
||||
return checksumException{
|
||||
wrapException("ChecksumException", e),
|
||||
}
|
||||
}
|
||||
74
vendor/github.com/makiuchi-d/gozxing/common/bit_source.go
generated
vendored
Normal file
74
vendor/github.com/makiuchi-d/gozxing/common/bit_source.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type BitSource struct {
|
||||
bytes []byte
|
||||
byteOffset int
|
||||
bitOffset int
|
||||
}
|
||||
|
||||
func NewBitSource(bytes []byte) *BitSource {
|
||||
return &BitSource{
|
||||
bytes: bytes,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *BitSource) GetBitOffset() int {
|
||||
return this.bitOffset
|
||||
}
|
||||
|
||||
func (this *BitSource) GetByteOffset() int {
|
||||
return this.byteOffset
|
||||
}
|
||||
|
||||
func (this *BitSource) ReadBits(numBits int) (int, error) {
|
||||
if numBits < 1 || numBits > 32 || numBits > this.Available() {
|
||||
return 0, errors.Errorf("IllegalArgumentException: %v", numBits)
|
||||
}
|
||||
|
||||
result := 0
|
||||
|
||||
// First, read remainder from current byte
|
||||
if this.bitOffset > 0 {
|
||||
bitsLeft := 8 - this.bitOffset
|
||||
toRead := bitsLeft
|
||||
if numBits < bitsLeft {
|
||||
toRead = numBits
|
||||
}
|
||||
bitsToNotRead := uint(bitsLeft - toRead)
|
||||
mask := byte((0xFF >> uint(8-toRead)) << bitsToNotRead)
|
||||
result = int(this.bytes[this.byteOffset]&mask) >> bitsToNotRead
|
||||
numBits -= toRead
|
||||
this.bitOffset += toRead
|
||||
if this.bitOffset == 8 {
|
||||
this.bitOffset = 0
|
||||
this.byteOffset++
|
||||
}
|
||||
}
|
||||
|
||||
// Next read whole bytes
|
||||
if numBits > 0 {
|
||||
for numBits >= 8 {
|
||||
result = (result << 8) | int(this.bytes[this.byteOffset]&0xFF)
|
||||
this.byteOffset++
|
||||
numBits -= 8
|
||||
}
|
||||
|
||||
// Finally read a partial byte
|
||||
if numBits > 0 {
|
||||
bitsToNotRead := uint(8 - numBits)
|
||||
mask := byte((0xFF >> bitsToNotRead) << bitsToNotRead)
|
||||
result = (result << uint(numBits)) | int((this.bytes[this.byteOffset]&mask)>>bitsToNotRead)
|
||||
this.bitOffset += numBits
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (this *BitSource) Available() int {
|
||||
return 8*(len(this.bytes)-this.byteOffset) - this.bitOffset
|
||||
}
|
||||
108
vendor/github.com/makiuchi-d/gozxing/common/character_set_eci.go
generated
vendored
Normal file
108
vendor/github.com/makiuchi-d/gozxing/common/character_set_eci.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/encoding/ianaindex"
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
"golang.org/x/text/encoding/korean"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/encoding/traditionalchinese"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
)
|
||||
|
||||
type CharacterSetECI struct {
|
||||
values []int
|
||||
charset encoding.Encoding
|
||||
name string
|
||||
otherEncodingNames []string
|
||||
}
|
||||
|
||||
var (
|
||||
valueToECI = map[int]*CharacterSetECI{}
|
||||
nameToECI = map[string]*CharacterSetECI{}
|
||||
|
||||
asciiEnc, _ = ianaindex.IANA.Encoding("US-ASCII")
|
||||
utf16beEnc, _ = ianaindex.IANA.Encoding("UTF-16BE")
|
||||
|
||||
CharacterSetECI_Cp437 = newCharsetECI([]int{0, 2}, charmap.CodePage437, "Cp437")
|
||||
CharacterSetECI_ISO8859_1 = newCharsetECI([]int{1, 3}, charmap.ISO8859_1, "ISO-8859-1", "ISO8859_1")
|
||||
CharacterSetECI_ISO8859_2 = newCharsetECI([]int{4}, charmap.ISO8859_2, "ISO-8859-2", "ISO8859_2")
|
||||
CharacterSetECI_ISO8859_3 = newCharsetECI([]int{5}, charmap.ISO8859_3, "ISO-8859-3", "ISO8859_3")
|
||||
CharacterSetECI_ISO8859_4 = newCharsetECI([]int{6}, charmap.ISO8859_4, "ISO-8859-4", "ISO8859_4")
|
||||
CharacterSetECI_ISO8859_5 = newCharsetECI([]int{7}, charmap.ISO8859_5, "ISO-8859-5", "ISO8859_5")
|
||||
//CharacterSetECI_ISO8859_6 = newCharsetECI([]int{8}, charmap.ISO8859_6, "ISO-8859-6", "ISO8859_6")
|
||||
CharacterSetECI_ISO8859_7 = newCharsetECI([]int{9}, charmap.ISO8859_7, "ISO-8859-7", "ISO8859_7")
|
||||
//CharacterSetECI_ISO8859_8 = newCharsetECI([]int{10}, charmap.ISO8859_8, "ISO-8859-8", "ISO8859_8")
|
||||
CharacterSetECI_ISO8859_9 = newCharsetECI([]int{11}, charmap.ISO8859_9, "ISO-8859-9", "ISO8859_9")
|
||||
//CharacterSetECI_ISO8859_10 = newCharsetECI([]int{12}, charmap.ISO8859_10, "ISO-8859-10", "ISO8859_10")
|
||||
//CharacterSetECI_ISO8859_11 = newCharsetECI([]int{13}, charmap.ISO8859_11, "TIS-620", "ISO-8859-11", "ISO8859_11") // golang does not support
|
||||
|
||||
CharacterSetECI_ISO8859_13 = newCharsetECI([]int{15}, charmap.ISO8859_13, "ISO-8859-13", "ISO8859_13")
|
||||
//CharacterSetECI_ISO8859_14 = newCharsetECI([]int{16}, charmap.ISO8859_14, "ISO-8859-14", "ISO8859_14")
|
||||
CharacterSetECI_ISO8859_15 = newCharsetECI([]int{17}, charmap.ISO8859_15, "ISO-8859-15", "ISO8859_15")
|
||||
CharacterSetECI_ISO8859_16 = newCharsetECI([]int{18}, charmap.ISO8859_16, "ISO-8859-16", "ISO8859_16")
|
||||
CharacterSetECI_SJIS = newCharsetECI([]int{20}, japanese.ShiftJIS, "Shift_JIS", "SJIS")
|
||||
CharacterSetECI_Cp1250 = newCharsetECI([]int{21}, charmap.Windows1250, "windows-1250", "Cp1250")
|
||||
CharacterSetECI_Cp1251 = newCharsetECI([]int{22}, charmap.Windows1251, "windows-1251", "Cp1251")
|
||||
CharacterSetECI_Cp1252 = newCharsetECI([]int{23}, charmap.Windows1252, "windows-1252", "Cp1252")
|
||||
CharacterSetECI_Cp1256 = newCharsetECI([]int{24}, charmap.Windows1256, "windows-1256", "Cp1256")
|
||||
CharacterSetECI_UnicodeBigUnmarked = newCharsetECI([]int{25}, utf16beEnc, "UTF-16BE", "UnicodeBig", "UnicodeBigUnmarked")
|
||||
CharacterSetECI_UTF8 = newCharsetECI([]int{26}, unicode.UTF8, "UTF-8", "UTF8")
|
||||
CharacterSetECI_ASCII = newCharsetECI([]int{27, 170}, asciiEnc, "ASCII", "US-ASCII")
|
||||
CharacterSetECI_Big5 = newCharsetECI([]int{28}, traditionalchinese.Big5, "Big5")
|
||||
CharacterSetECI_GB18030 = newCharsetECI([]int{29}, simplifiedchinese.GB18030, "GB18030", "GB2312", "EUC_CN", "GBK") // BG18030 is upward compatible with others
|
||||
CharacterSetECI_EUC_KR = newCharsetECI([]int{30}, korean.EUCKR, "EUC-KR", "EUC_KR")
|
||||
)
|
||||
|
||||
func newCharsetECI(values []int, charset encoding.Encoding, encodingNames ...string) *CharacterSetECI {
|
||||
c := &CharacterSetECI{
|
||||
values: values,
|
||||
charset: charset,
|
||||
name: encodingNames[0],
|
||||
otherEncodingNames: encodingNames[1:],
|
||||
}
|
||||
for _, val := range values {
|
||||
valueToECI[val] = c
|
||||
}
|
||||
for _, name := range encodingNames {
|
||||
nameToECI[name] = c
|
||||
}
|
||||
iananame, _ := ianaindex.IANA.Name(charset)
|
||||
nameToECI[iananame] = c
|
||||
return c
|
||||
}
|
||||
|
||||
func (this *CharacterSetECI) GetValue() int {
|
||||
return this.values[0]
|
||||
}
|
||||
|
||||
func (this *CharacterSetECI) Name() string {
|
||||
return this.name
|
||||
}
|
||||
|
||||
func (this *CharacterSetECI) GetCharset() encoding.Encoding {
|
||||
return this.charset
|
||||
}
|
||||
|
||||
func GetCharacterSetECI(charset encoding.Encoding) (*CharacterSetECI, bool) {
|
||||
name, err := ianaindex.IANA.Name(charset)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
eci, ok := nameToECI[name]
|
||||
return eci, ok
|
||||
}
|
||||
|
||||
func GetCharacterSetECIByValue(value int) (*CharacterSetECI, error) {
|
||||
if value < 0 || value >= 900 {
|
||||
return nil, gozxing.NewFormatException()
|
||||
}
|
||||
return valueToECI[value], nil
|
||||
}
|
||||
|
||||
func GetCharacterSetECIByName(name string) (*CharacterSetECI, bool) {
|
||||
eci, ok := nameToECI[name]
|
||||
return eci, ok
|
||||
}
|
||||
104
vendor/github.com/makiuchi-d/gozxing/common/decoder_result.go
generated
vendored
Normal file
104
vendor/github.com/makiuchi-d/gozxing/common/decoder_result.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
package common
|
||||
|
||||
type DecoderResult struct {
|
||||
rawBytes []byte
|
||||
numBits int
|
||||
text string
|
||||
byteSegments [][]byte
|
||||
ecLevel string
|
||||
errorsCorrected int
|
||||
erasures int
|
||||
other interface{}
|
||||
structuredAppendParity int
|
||||
structuredAppendSequenceNumber int
|
||||
symbologyModifier int
|
||||
}
|
||||
|
||||
func NewDecoderResult(rawBytes []byte, text string, byteSegments [][]byte, ecLevel string) *DecoderResult {
|
||||
return NewDecoderResultWithParams(rawBytes, text, byteSegments, ecLevel, -1, -1, 0)
|
||||
}
|
||||
|
||||
func NewDecoderResultWithSymbologyModifier(rawBytes []byte, text string, byteSegments [][]byte, ecLevel string, symbologyModifier int) *DecoderResult {
|
||||
return NewDecoderResultWithParams(rawBytes, text, byteSegments, ecLevel, -1, -1, symbologyModifier)
|
||||
}
|
||||
|
||||
func NewDecoderResultWithSA(rawBytes []byte, text string, byteSegments [][]byte, ecLevel string, saSequence, saParity int) *DecoderResult {
|
||||
return NewDecoderResultWithParams(rawBytes, text, byteSegments, ecLevel, saSequence, saParity, 0)
|
||||
}
|
||||
|
||||
func NewDecoderResultWithParams(rawBytes []byte, text string, byteSegments [][]byte, ecLevel string, saSequence, saParity, symbologyModifier int) *DecoderResult {
|
||||
return &DecoderResult{
|
||||
rawBytes: rawBytes,
|
||||
numBits: 8 * len(rawBytes),
|
||||
text: text,
|
||||
byteSegments: byteSegments,
|
||||
ecLevel: ecLevel,
|
||||
structuredAppendParity: saParity,
|
||||
structuredAppendSequenceNumber: saSequence,
|
||||
symbologyModifier: symbologyModifier,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *DecoderResult) GetRawBytes() []byte {
|
||||
return this.rawBytes
|
||||
}
|
||||
|
||||
func (this *DecoderResult) GetNumBits() int {
|
||||
return this.numBits
|
||||
}
|
||||
|
||||
func (this *DecoderResult) SetNumBits(numBits int) {
|
||||
this.numBits = numBits
|
||||
}
|
||||
|
||||
func (this *DecoderResult) GetText() string {
|
||||
return this.text
|
||||
}
|
||||
|
||||
func (this *DecoderResult) GetByteSegments() [][]byte {
|
||||
return this.byteSegments
|
||||
}
|
||||
|
||||
func (this *DecoderResult) GetECLevel() string {
|
||||
return this.ecLevel
|
||||
}
|
||||
|
||||
func (this *DecoderResult) GetErrorsCorrected() int {
|
||||
return this.errorsCorrected
|
||||
}
|
||||
|
||||
func (this *DecoderResult) SetErrorsCorrected(errorsCorrected int) {
|
||||
this.errorsCorrected = errorsCorrected
|
||||
}
|
||||
|
||||
func (this *DecoderResult) GetErasures() int {
|
||||
return this.erasures
|
||||
}
|
||||
|
||||
func (this *DecoderResult) SetErasures(erasures int) {
|
||||
this.erasures = erasures
|
||||
}
|
||||
|
||||
func (this *DecoderResult) GetOther() interface{} {
|
||||
return this.other
|
||||
}
|
||||
|
||||
func (this *DecoderResult) SetOther(other interface{}) {
|
||||
this.other = other
|
||||
}
|
||||
|
||||
func (this *DecoderResult) HasStructuredAppend() bool {
|
||||
return this.structuredAppendParity >= 0 && this.structuredAppendSequenceNumber >= 0
|
||||
}
|
||||
|
||||
func (this *DecoderResult) GetStructuredAppendParity() int {
|
||||
return this.structuredAppendParity
|
||||
}
|
||||
|
||||
func (this *DecoderResult) GetStructuredAppendSequenceNumber() int {
|
||||
return this.structuredAppendSequenceNumber
|
||||
}
|
||||
|
||||
func (this *DecoderResult) GetSymbologyModifier() int {
|
||||
return this.symbologyModifier
|
||||
}
|
||||
70
vendor/github.com/makiuchi-d/gozxing/common/default_grid_sampler.go
generated
vendored
Normal file
70
vendor/github.com/makiuchi-d/gozxing/common/default_grid_sampler.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
type DefaultGridSampler struct{}
|
||||
|
||||
func NewDefaultGridSampler() GridSampler {
|
||||
return DefaultGridSampler{}
|
||||
}
|
||||
|
||||
func (s DefaultGridSampler) SampleGrid(image *gozxing.BitMatrix, dimensionX, dimensionY int,
|
||||
p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY float64,
|
||||
p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY float64) (*gozxing.BitMatrix, error) {
|
||||
|
||||
transform := PerspectiveTransform_QuadrilateralToQuadrilateral(
|
||||
p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
|
||||
p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY)
|
||||
|
||||
return s.SampleGridWithTransform(image, dimensionX, dimensionY, transform)
|
||||
}
|
||||
|
||||
func (s DefaultGridSampler) SampleGridWithTransform(image *gozxing.BitMatrix,
|
||||
dimensionX, dimensionY int, transform *PerspectiveTransform) (*gozxing.BitMatrix, error) {
|
||||
|
||||
if dimensionX <= 0 || dimensionY <= 0 {
|
||||
return nil, gozxing.NewNotFoundException("dimensions X, Y = %v, %v", dimensionX, dimensionY)
|
||||
}
|
||||
bits, _ := gozxing.NewBitMatrix(dimensionX, dimensionY) // always success
|
||||
points := make([]float64, 2*dimensionX)
|
||||
for y := 0; y < dimensionY; y++ {
|
||||
max := len(points)
|
||||
iValue := float64(y) + 0.5
|
||||
for x := 0; x < max; x += 2 {
|
||||
points[x] = float64(x/2) + 0.5
|
||||
points[x+1] = iValue
|
||||
}
|
||||
transform.TransformPoints(points)
|
||||
// Quick check to see if points transformed to something inside the image;
|
||||
// sufficient to check the endpoints
|
||||
e := GridSampler_checkAndNudgePoints(image, points)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapNotFoundException(e)
|
||||
}
|
||||
for x := 0; x < max; x += 2 {
|
||||
px := int(points[x])
|
||||
py := int(points[x+1])
|
||||
|
||||
if px >= image.GetWidth() || py >= image.GetHeight() {
|
||||
// cause of ArrayIndexOutOfBoundsException in image.Get(px, py)
|
||||
|
||||
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
|
||||
// transform gets "twisted" such that it maps a straight line of points to a set of points
|
||||
// whose endpoints are in bounds, but others are not. There is probably some mathematical
|
||||
// way to detect this about the transformation that I don't know yet.
|
||||
// This results in an ugly runtime exception despite our clever checks above -- can't have
|
||||
// that. We could check each point's coordinates but that feels duplicative. We settle for
|
||||
// catching and wrapping ArrayIndexOutOfBoundsException.
|
||||
return nil, gozxing.NewNotFoundException()
|
||||
}
|
||||
|
||||
if image.Get(px, py) {
|
||||
// Black(-ish) pixel
|
||||
bits.Set(x/2, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
return bits, nil
|
||||
}
|
||||
22
vendor/github.com/makiuchi-d/gozxing/common/detector_result.go
generated
vendored
Normal file
22
vendor/github.com/makiuchi-d/gozxing/common/detector_result.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
type DetectorResult struct {
|
||||
bits *gozxing.BitMatrix
|
||||
points []gozxing.ResultPoint
|
||||
}
|
||||
|
||||
func NewDetectorResult(bits *gozxing.BitMatrix, points []gozxing.ResultPoint) *DetectorResult {
|
||||
return &DetectorResult{bits, points}
|
||||
}
|
||||
|
||||
func (d *DetectorResult) GetBits() *gozxing.BitMatrix {
|
||||
return d.bits
|
||||
}
|
||||
|
||||
func (d *DetectorResult) GetPoints() []gozxing.ResultPoint {
|
||||
return d.points
|
||||
}
|
||||
81
vendor/github.com/makiuchi-d/gozxing/common/grid_sampler.go
generated
vendored
Normal file
81
vendor/github.com/makiuchi-d/gozxing/common/grid_sampler.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
type GridSampler interface {
|
||||
SampleGrid(image *gozxing.BitMatrix, dimensionX, dimensionY int,
|
||||
p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY float64,
|
||||
p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY float64) (*gozxing.BitMatrix, error)
|
||||
|
||||
SampleGridWithTransform(image *gozxing.BitMatrix,
|
||||
dimensionX, dimensionY int, transform *PerspectiveTransform) (*gozxing.BitMatrix, error)
|
||||
}
|
||||
|
||||
var gridSampler GridSampler = NewDefaultGridSampler()
|
||||
|
||||
func GridSampler_SetGridSampler(newGridSampler GridSampler) {
|
||||
gridSampler = newGridSampler
|
||||
}
|
||||
|
||||
func GridSampler_GetInstance() GridSampler {
|
||||
return gridSampler
|
||||
}
|
||||
|
||||
func GridSampler_checkAndNudgePoints(image *gozxing.BitMatrix, points []float64) error {
|
||||
width := image.GetWidth()
|
||||
height := image.GetHeight()
|
||||
// Check and nudge points from start until we see some that are OK:
|
||||
nudged := true
|
||||
maxOffset := len(points) - 1 // points.length must be even
|
||||
for offset := 0; offset < maxOffset && nudged; offset += 2 {
|
||||
x := int(points[offset])
|
||||
y := int(points[offset+1])
|
||||
if x < -1 || x > width || y < -1 || y > height {
|
||||
return gozxing.NewNotFoundException(
|
||||
"(w, h) = (%v, %v), (x, y) = (%v, %v)", width, height, x, y)
|
||||
}
|
||||
nudged = false
|
||||
if x == -1 {
|
||||
points[offset] = 0.0
|
||||
nudged = true
|
||||
} else if x == width {
|
||||
points[offset] = float64(width - 1)
|
||||
nudged = true
|
||||
}
|
||||
if y == -1 {
|
||||
points[offset+1] = 0.0
|
||||
nudged = true
|
||||
} else if y == height {
|
||||
points[offset+1] = float64(height)
|
||||
nudged = true
|
||||
}
|
||||
}
|
||||
// Check and nudge points from end:
|
||||
nudged = true
|
||||
for offset := len(points) - 2; offset >= 0 && nudged; offset -= 2 {
|
||||
x := int(points[offset])
|
||||
y := int(points[offset+1])
|
||||
if x < -1 || x > width || y < -1 || y > height {
|
||||
return gozxing.NewNotFoundException(
|
||||
"(w, h) = (%v, %v), (x, y) = (%v, %v)", width, height, x, y)
|
||||
}
|
||||
nudged = false
|
||||
if x == -1 {
|
||||
points[offset] = 0.0
|
||||
nudged = true
|
||||
} else if x == width {
|
||||
points[offset] = float64(width - 1)
|
||||
nudged = true
|
||||
}
|
||||
if y == -1 {
|
||||
points[offset+1] = 0.0
|
||||
nudged = true
|
||||
} else if y == height {
|
||||
points[offset+1] = float64(height - 1)
|
||||
nudged = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
95
vendor/github.com/makiuchi-d/gozxing/common/perspective_transform.go
generated
vendored
Normal file
95
vendor/github.com/makiuchi-d/gozxing/common/perspective_transform.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
package common
|
||||
|
||||
type PerspectiveTransform struct {
|
||||
a11, a21, a31 float64
|
||||
a12, a22, a32 float64
|
||||
a13, a23, a33 float64
|
||||
}
|
||||
|
||||
func PerspectiveTransform_QuadrilateralToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3,
|
||||
x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p float64) *PerspectiveTransform {
|
||||
|
||||
qToS := PerspectiveTransform_QuadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3)
|
||||
sToQ := PerspectiveTransform_SquareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p)
|
||||
return sToQ.times(qToS)
|
||||
}
|
||||
|
||||
func (p *PerspectiveTransform) TransformPoints(points []float64) {
|
||||
maxI := len(points) - 1 // points.length must be even
|
||||
for i := 0; i < maxI; i += 2 {
|
||||
x := points[i]
|
||||
y := points[i+1]
|
||||
denominator := p.a13*x + p.a23*y + p.a33
|
||||
points[i] = (p.a11*x + p.a21*y + p.a31) / denominator
|
||||
points[i+1] = (p.a12*x + p.a22*y + p.a32) / denominator
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PerspectiveTransform) TransformPointsXY(xValues, yValues []float64) {
|
||||
n := len(xValues)
|
||||
for i := 0; i < n; i++ {
|
||||
x := xValues[i]
|
||||
y := yValues[i]
|
||||
denominator := p.a13*x + p.a23*y + p.a33
|
||||
xValues[i] = (p.a11*x + p.a21*y + p.a31) / denominator
|
||||
yValues[i] = (p.a12*x + p.a22*y + p.a32) / denominator
|
||||
}
|
||||
}
|
||||
|
||||
func PerspectiveTransform_SquareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3 float64) *PerspectiveTransform {
|
||||
dx3 := x0 - x1 + x2 - x3
|
||||
dy3 := y0 - y1 + y2 - y3
|
||||
if dx3 == 0.0 && dy3 == 0.0 {
|
||||
// Affine
|
||||
return &PerspectiveTransform{
|
||||
x1 - x0, x2 - x1, x0,
|
||||
y1 - y0, y2 - y1, y0,
|
||||
0.0, 0.0, 1.0}
|
||||
} else {
|
||||
dx1 := x1 - x2
|
||||
dx2 := x3 - x2
|
||||
dy1 := y1 - y2
|
||||
dy2 := y3 - y2
|
||||
denominator := dx1*dy2 - dx2*dy1
|
||||
a13 := (dx3*dy2 - dx2*dy3) / denominator
|
||||
a23 := (dx1*dy3 - dx3*dy1) / denominator
|
||||
return &PerspectiveTransform{
|
||||
x1 - x0 + a13*x1, x3 - x0 + a23*x3, x0,
|
||||
y1 - y0 + a13*y1, y3 - y0 + a23*y3, y0,
|
||||
a13, a23, 1.0}
|
||||
}
|
||||
}
|
||||
|
||||
func PerspectiveTransform_QuadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3 float64) *PerspectiveTransform {
|
||||
// Here, the adjoint serves as the inverse:
|
||||
return PerspectiveTransform_SquareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint()
|
||||
}
|
||||
|
||||
func (p *PerspectiveTransform) buildAdjoint() *PerspectiveTransform {
|
||||
// Adjoint is the transpose of the cofactor matrix:
|
||||
return &PerspectiveTransform{
|
||||
p.a22*p.a33 - p.a23*p.a32,
|
||||
p.a23*p.a31 - p.a21*p.a33,
|
||||
p.a21*p.a32 - p.a22*p.a31,
|
||||
p.a13*p.a32 - p.a12*p.a33,
|
||||
p.a11*p.a33 - p.a13*p.a31,
|
||||
p.a12*p.a31 - p.a11*p.a32,
|
||||
p.a12*p.a23 - p.a13*p.a22,
|
||||
p.a13*p.a21 - p.a11*p.a23,
|
||||
p.a11*p.a22 - p.a12*p.a21,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PerspectiveTransform) times(other *PerspectiveTransform) *PerspectiveTransform {
|
||||
return &PerspectiveTransform{
|
||||
p.a11*other.a11 + p.a21*other.a12 + p.a31*other.a13,
|
||||
p.a11*other.a21 + p.a21*other.a22 + p.a31*other.a23,
|
||||
p.a11*other.a31 + p.a21*other.a32 + p.a31*other.a33,
|
||||
p.a12*other.a11 + p.a22*other.a12 + p.a32*other.a13,
|
||||
p.a12*other.a21 + p.a22*other.a22 + p.a32*other.a23,
|
||||
p.a12*other.a31 + p.a22*other.a32 + p.a32*other.a33,
|
||||
p.a13*other.a11 + p.a23*other.a12 + p.a33*other.a13,
|
||||
p.a13*other.a21 + p.a23*other.a22 + p.a33*other.a23,
|
||||
p.a13*other.a31 + p.a23*other.a32 + p.a33*other.a33,
|
||||
}
|
||||
}
|
||||
120
vendor/github.com/makiuchi-d/gozxing/common/reedsolomon/generic_gf.go
generated
vendored
Normal file
120
vendor/github.com/makiuchi-d/gozxing/common/reedsolomon/generic_gf.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
package reedsolomon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
var (
|
||||
GenericGF_AZTEC_DATA_12 = NewGenericGF(0x1069, 4096, 1) // x^12 + x^6 + x^5 + x^3 + 1
|
||||
GenericGF_AZTEC_DATA_10 = NewGenericGF(0x409, 1024, 1) // x^10 + x^3 + 1
|
||||
GenericGF_AZTEC_DATA_6 = NewGenericGF(0x43, 64, 1) // x^6 + x + 1
|
||||
GenericGF_AZTEC_PARAM = NewGenericGF(0x13, 16, 1) // x^4 + x + 1
|
||||
GenericGF_QR_CODE_FIELD_256 = NewGenericGF(0x011D, 256, 0) // x^8 + x^4 + x^3 + x^2 + 1
|
||||
GenericGF_DATA_MATRIX_FIELD_256 = NewGenericGF(0x012D, 256, 1) // x^8 + x^5 + x^3 + x^2 + 1
|
||||
GenericGF_AZTEC_DATA_8 = GenericGF_DATA_MATRIX_FIELD_256
|
||||
GenericGF_MAXICODE_FIELD_64 = GenericGF_AZTEC_DATA_6
|
||||
)
|
||||
|
||||
type GenericGF struct {
|
||||
expTable []int
|
||||
logTable []int
|
||||
zero *GenericGFPoly
|
||||
one *GenericGFPoly
|
||||
size int
|
||||
primitive int
|
||||
generatorBase int
|
||||
}
|
||||
|
||||
func NewGenericGF(primitive, size, b int) *GenericGF {
|
||||
this := &GenericGF{
|
||||
primitive: primitive,
|
||||
size: size,
|
||||
generatorBase: b,
|
||||
}
|
||||
|
||||
expTable := make([]int, size)
|
||||
logTable := make([]int, size)
|
||||
x := 1
|
||||
for i := 0; i < size; i++ {
|
||||
expTable[i] = x
|
||||
x *= 2 // we're assuming the generator alpha is 2
|
||||
if x >= size {
|
||||
x ^= primitive
|
||||
x &= size - 1
|
||||
}
|
||||
}
|
||||
for i := 0; i < size-1; i++ {
|
||||
logTable[expTable[i]] = i
|
||||
}
|
||||
this.expTable = expTable
|
||||
this.logTable = logTable
|
||||
// logTable[0] == 0 but this should never be used
|
||||
this.zero, _ = NewGenericGFPoly(this, []int{0})
|
||||
this.one, _ = NewGenericGFPoly(this, []int{1})
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *GenericGF) GetZero() *GenericGFPoly {
|
||||
return this.zero
|
||||
}
|
||||
|
||||
func (this *GenericGF) GetOne() *GenericGFPoly {
|
||||
return this.one
|
||||
}
|
||||
|
||||
func (this *GenericGF) BuildMonomial(degree, coefficient int) (*GenericGFPoly, error) {
|
||||
if degree < 0 {
|
||||
return nil, errors.New("IllegalArgumentException")
|
||||
}
|
||||
if coefficient == 0 {
|
||||
return this.zero, nil
|
||||
}
|
||||
|
||||
coefficients := make([]int, degree+1)
|
||||
coefficients[0] = coefficient
|
||||
return NewGenericGFPoly(this, coefficients)
|
||||
}
|
||||
|
||||
func GenericGF_addOrSubtract(a, b int) int {
|
||||
return a ^ b
|
||||
}
|
||||
|
||||
func (this *GenericGF) Exp(a int) int {
|
||||
return this.expTable[a]
|
||||
}
|
||||
|
||||
func (this *GenericGF) Log(a int) (int, error) {
|
||||
if a == 0 {
|
||||
return 0, errors.New("IllegalArgumentException")
|
||||
}
|
||||
return this.logTable[a], nil
|
||||
}
|
||||
|
||||
func (this *GenericGF) Inverse(a int) (int, error) {
|
||||
if a == 0 {
|
||||
return 0, errors.New("IllegalArgumentException")
|
||||
}
|
||||
return this.expTable[this.size-this.logTable[a]-1], nil
|
||||
}
|
||||
|
||||
func (this *GenericGF) Multiply(a, b int) int {
|
||||
if a == 0 || b == 0 {
|
||||
return 0
|
||||
}
|
||||
return this.expTable[(this.logTable[a]+this.logTable[b])%(this.size-1)]
|
||||
}
|
||||
|
||||
func (this *GenericGF) GetSize() int {
|
||||
return this.size
|
||||
}
|
||||
|
||||
func (this *GenericGF) GetGeneratorBase() int {
|
||||
return this.generatorBase
|
||||
}
|
||||
|
||||
func (this *GenericGF) String() string {
|
||||
return fmt.Sprintf("GF(0x%x,%d)", this.primitive, this.size)
|
||||
}
|
||||
238
vendor/github.com/makiuchi-d/gozxing/common/reedsolomon/generic_gf_poly.go
generated
vendored
Normal file
238
vendor/github.com/makiuchi-d/gozxing/common/reedsolomon/generic_gf_poly.go
generated
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
package reedsolomon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type GenericGFPoly struct {
|
||||
field *GenericGF
|
||||
coefficients []int
|
||||
}
|
||||
|
||||
func NewGenericGFPoly(field *GenericGF, coefficients []int) (*GenericGFPoly, error) {
|
||||
if len(coefficients) == 0 {
|
||||
return nil, errors.New("IllegalArgumentException")
|
||||
}
|
||||
this := &GenericGFPoly{field: field}
|
||||
|
||||
coefficientsLength := len(coefficients)
|
||||
if coefficientsLength > 1 && coefficients[0] == 0 {
|
||||
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||
firstNonZero := 1
|
||||
for firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0 {
|
||||
firstNonZero++
|
||||
}
|
||||
if firstNonZero == coefficientsLength {
|
||||
this.coefficients = []int{0}
|
||||
} else {
|
||||
this.coefficients = coefficients[firstNonZero:]
|
||||
}
|
||||
} else {
|
||||
this.coefficients = coefficients
|
||||
}
|
||||
|
||||
return this, nil
|
||||
}
|
||||
|
||||
func (this *GenericGFPoly) GetCoefficients() []int {
|
||||
return this.coefficients
|
||||
}
|
||||
|
||||
func (this *GenericGFPoly) GetDegree() int {
|
||||
return len(this.coefficients) - 1
|
||||
}
|
||||
|
||||
func (this *GenericGFPoly) IsZero() bool {
|
||||
return this.coefficients[0] == 0
|
||||
}
|
||||
|
||||
func (this *GenericGFPoly) GetCoefficient(degree int) int {
|
||||
return this.coefficients[len(this.coefficients)-1-degree]
|
||||
}
|
||||
|
||||
func (this *GenericGFPoly) EvaluateAt(a int) int {
|
||||
if a == 0 {
|
||||
// Just return the x^0 coefficient
|
||||
return this.GetCoefficient(0)
|
||||
}
|
||||
if a == 1 {
|
||||
// Just the sum of the coefficients
|
||||
result := 0
|
||||
for _, coefficient := range this.coefficients {
|
||||
result = GenericGF_addOrSubtract(result, coefficient)
|
||||
}
|
||||
return result
|
||||
}
|
||||
result := this.coefficients[0]
|
||||
size := len(this.coefficients)
|
||||
for i := 1; i < size; i++ {
|
||||
result = GenericGF_addOrSubtract(this.field.Multiply(a, result), this.coefficients[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (this *GenericGFPoly) AddOrSubtract(other *GenericGFPoly) (*GenericGFPoly, error) {
|
||||
if this.field != other.field {
|
||||
return nil, errors.New("IllegalArgumentException: GenericGFPolys do not have same GenericGF field")
|
||||
}
|
||||
if this.IsZero() {
|
||||
return other, nil
|
||||
}
|
||||
if other.IsZero() {
|
||||
return this, nil
|
||||
}
|
||||
|
||||
smallerCoefficients := this.coefficients
|
||||
largerCoefficients := other.coefficients
|
||||
if len(smallerCoefficients) > len(largerCoefficients) {
|
||||
smallerCoefficients, largerCoefficients = largerCoefficients, smallerCoefficients
|
||||
}
|
||||
sumDiff := make([]int, len(largerCoefficients))
|
||||
lengthDiff := len(largerCoefficients) - len(smallerCoefficients)
|
||||
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
||||
copy(sumDiff, largerCoefficients[:lengthDiff])
|
||||
for i := lengthDiff; i < len(largerCoefficients); i++ {
|
||||
sumDiff[i] = GenericGF_addOrSubtract(smallerCoefficients[i-lengthDiff], largerCoefficients[i])
|
||||
}
|
||||
|
||||
return NewGenericGFPoly(this.field, sumDiff)
|
||||
}
|
||||
|
||||
func (this *GenericGFPoly) Multiply(other *GenericGFPoly) (*GenericGFPoly, error) {
|
||||
if this.field != other.field {
|
||||
return nil, errors.New("IllegalArgumentException: GenericGFPolys do not have same GenericGF field")
|
||||
}
|
||||
if this.IsZero() || other.IsZero() {
|
||||
return this.field.GetZero(), nil
|
||||
}
|
||||
aCoefficients := this.coefficients
|
||||
aLength := len(aCoefficients)
|
||||
bCoefficients := other.coefficients
|
||||
bLength := len(bCoefficients)
|
||||
product := make([]int, aLength+bLength-1)
|
||||
for i := 0; i < aLength; i++ {
|
||||
aCoeff := aCoefficients[i]
|
||||
for j := 0; j < bLength; j++ {
|
||||
product[i+j] = GenericGF_addOrSubtract(product[i+j],
|
||||
this.field.Multiply(aCoeff, bCoefficients[j]))
|
||||
}
|
||||
}
|
||||
return NewGenericGFPoly(this.field, product)
|
||||
}
|
||||
|
||||
func (this *GenericGFPoly) MultiplyBy(scalar int) *GenericGFPoly {
|
||||
if scalar == 0 {
|
||||
return this.field.GetZero()
|
||||
}
|
||||
if scalar == 1 {
|
||||
return this
|
||||
}
|
||||
size := len(this.coefficients)
|
||||
product := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
product[i] = this.field.Multiply(this.coefficients[i], scalar)
|
||||
}
|
||||
ret, _ := NewGenericGFPoly(this.field, product)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (this *GenericGFPoly) MultiplyByMonomial(degree, coefficient int) (*GenericGFPoly, error) {
|
||||
if degree < 0 {
|
||||
return nil, errors.New("IllegalArgumentException")
|
||||
}
|
||||
if coefficient == 0 {
|
||||
return this.field.GetZero(), nil
|
||||
}
|
||||
size := len(this.coefficients)
|
||||
product := make([]int, size+degree)
|
||||
for i := 0; i < size; i++ {
|
||||
product[i] = this.field.Multiply(this.coefficients[i], coefficient)
|
||||
}
|
||||
return NewGenericGFPoly(this.field, product)
|
||||
}
|
||||
|
||||
func (this *GenericGFPoly) Divide(other *GenericGFPoly) (quotient, remainder *GenericGFPoly, e error) {
|
||||
if this.field != other.field {
|
||||
return nil, nil, errors.New("IllegalArgumentException: GenericGFPolys do not have same GenericGF field")
|
||||
}
|
||||
if other.IsZero() {
|
||||
return nil, nil, errors.New("IllegalArgumentException: Divide by 0")
|
||||
}
|
||||
|
||||
quotient = this.field.GetZero()
|
||||
remainder = this
|
||||
|
||||
denominatorLeadingTerm := other.GetCoefficient(other.GetDegree())
|
||||
inverseDenominatorLeadingTerm, e := this.field.Inverse(denominatorLeadingTerm)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
|
||||
for remainder.GetDegree() >= other.GetDegree() && !remainder.IsZero() {
|
||||
degreeDifference := remainder.GetDegree() - other.GetDegree()
|
||||
scale := this.field.Multiply(remainder.GetCoefficient(remainder.GetDegree()), inverseDenominatorLeadingTerm)
|
||||
|
||||
term, e := other.MultiplyByMonomial(degreeDifference, scale)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
iterationQuotient, e := this.field.BuildMonomial(degreeDifference, scale)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
quotient, e = quotient.AddOrSubtract(iterationQuotient)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
remainder, e = remainder.AddOrSubtract(term)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
}
|
||||
|
||||
return quotient, remainder, nil
|
||||
}
|
||||
|
||||
func (this *GenericGFPoly) String() string {
|
||||
if this.IsZero() {
|
||||
return "0"
|
||||
}
|
||||
result := make([]byte, 0, 8*this.GetDegree())
|
||||
for degree := this.GetDegree(); degree >= 0; degree-- {
|
||||
coefficient := this.GetCoefficient(degree)
|
||||
if coefficient != 0 {
|
||||
if coefficient < 0 {
|
||||
if degree == this.GetDegree() {
|
||||
result = append(result, '-')
|
||||
} else {
|
||||
result = append(result, []byte(" - ")...)
|
||||
}
|
||||
coefficient = -coefficient
|
||||
} else {
|
||||
if len(result) > 0 {
|
||||
result = append(result, []byte(" + ")...)
|
||||
}
|
||||
}
|
||||
if degree == 0 || coefficient != 1 {
|
||||
alphaPower, _ := this.field.Log(coefficient)
|
||||
if alphaPower == 0 {
|
||||
result = append(result, byte('1'))
|
||||
} else if alphaPower == 1 {
|
||||
result = append(result, byte('a'))
|
||||
} else {
|
||||
result = append(result, []byte(fmt.Sprintf("a^%d", alphaPower))...)
|
||||
}
|
||||
}
|
||||
if degree != 0 {
|
||||
if degree == 1 {
|
||||
result = append(result, byte('x'))
|
||||
} else {
|
||||
result = append(result, []byte(fmt.Sprintf("x^%d", degree))...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
204
vendor/github.com/makiuchi-d/gozxing/common/reedsolomon/reedsolomon_decoder.go
generated
vendored
Normal file
204
vendor/github.com/makiuchi-d/gozxing/common/reedsolomon/reedsolomon_decoder.go
generated
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
package reedsolomon
|
||||
|
||||
import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type ReedSolomonDecoder struct {
|
||||
field *GenericGF
|
||||
}
|
||||
|
||||
func NewReedSolomonDecoder(field *GenericGF) *ReedSolomonDecoder {
|
||||
return &ReedSolomonDecoder{field}
|
||||
}
|
||||
|
||||
func (this *ReedSolomonDecoder) Decode(received []int, twoS int) ReedSolomonException {
|
||||
poly, e := NewGenericGFPoly(this.field, received)
|
||||
if e != nil {
|
||||
return WrapReedSolomonException(e)
|
||||
}
|
||||
syndromeCoefficients := make([]int, twoS)
|
||||
noError := true
|
||||
for i := 0; i < twoS; i++ {
|
||||
eval := poly.EvaluateAt(this.field.Exp(i + this.field.GetGeneratorBase()))
|
||||
syndromeCoefficients[len(syndromeCoefficients)-1-i] = eval
|
||||
if eval != 0 {
|
||||
noError = false
|
||||
}
|
||||
}
|
||||
if noError {
|
||||
return nil
|
||||
}
|
||||
syndrome, e := NewGenericGFPoly(this.field, syndromeCoefficients)
|
||||
if e != nil {
|
||||
return WrapReedSolomonException(e)
|
||||
}
|
||||
monomial, e := this.field.BuildMonomial(twoS, 1)
|
||||
if e != nil {
|
||||
return WrapReedSolomonException(e)
|
||||
}
|
||||
sigma, omega, e := this.runEuclideanAlgorithm(monomial, syndrome, twoS)
|
||||
if e != nil {
|
||||
return WrapReedSolomonException(e)
|
||||
}
|
||||
errorLocations, e := this.findErrorLocations(sigma)
|
||||
if e != nil {
|
||||
return WrapReedSolomonException(e)
|
||||
}
|
||||
errorMagnitudes, e := this.findErrorMagnitudes(omega, errorLocations)
|
||||
if e != nil {
|
||||
return WrapReedSolomonException(e)
|
||||
}
|
||||
for i := 0; i < len(errorLocations); i++ {
|
||||
log, e := this.field.Log(errorLocations[i])
|
||||
if e != nil {
|
||||
return WrapReedSolomonException(e)
|
||||
}
|
||||
position := len(received) - 1 - log
|
||||
if position < 0 {
|
||||
return NewReedSolomonException("Bad error location")
|
||||
}
|
||||
received[position] = GenericGF_addOrSubtract(received[position], errorMagnitudes[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *ReedSolomonDecoder) runEuclideanAlgorithm(a, b *GenericGFPoly, R int) (sigma, omega *GenericGFPoly, e error) {
|
||||
// Assume a's degree is >= b's
|
||||
if a.GetDegree() < b.GetDegree() {
|
||||
a, b = b, a
|
||||
}
|
||||
|
||||
rLast := a
|
||||
r := b
|
||||
tLast := this.field.GetZero()
|
||||
t := this.field.GetOne()
|
||||
|
||||
// Run Euclidean algorithm until r's degree is less than R/2
|
||||
for r.GetDegree() >= R/2 {
|
||||
rLastLast := rLast
|
||||
tLastLast := tLast
|
||||
rLast = r
|
||||
tLast = t
|
||||
|
||||
// Divide rLastLast by rLast, with quotient in q and remainder in r
|
||||
if rLast.IsZero() {
|
||||
// Oops, Euclidean algorithm already terminated?
|
||||
return nil, nil, NewReedSolomonException("r_{i-1} was zero")
|
||||
}
|
||||
r = rLastLast
|
||||
q := this.field.GetZero()
|
||||
denominatorLeadingTerm := rLast.GetCoefficient(rLast.GetDegree())
|
||||
dltInverse, e := this.field.Inverse(denominatorLeadingTerm)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
for r.GetDegree() >= rLast.GetDegree() && !r.IsZero() {
|
||||
degreeDiff := r.GetDegree() - rLast.GetDegree()
|
||||
scale := this.field.Multiply(r.GetCoefficient(r.GetDegree()), dltInverse)
|
||||
monomial, e := this.field.BuildMonomial(degreeDiff, scale)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
q, e = q.AddOrSubtract(monomial)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
polynomial, e := rLast.MultiplyByMonomial(degreeDiff, scale)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
r, e = r.AddOrSubtract(polynomial)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
}
|
||||
|
||||
q, e = q.Multiply(tLast)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
t, e = q.AddOrSubtract(tLastLast)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
|
||||
if r.GetDegree() >= rLast.GetDegree() {
|
||||
return nil, nil, errors.New("IllegalStateException: Division algorithm failed to reduce polynomial?")
|
||||
}
|
||||
}
|
||||
|
||||
sigmaTildeAtZero := t.GetCoefficient(0)
|
||||
if sigmaTildeAtZero == 0 {
|
||||
return nil, nil, NewReedSolomonException("sigmaTilde(0) was zero")
|
||||
}
|
||||
|
||||
inverse, e := this.field.Inverse(sigmaTildeAtZero)
|
||||
if e != nil {
|
||||
return nil, nil, e
|
||||
}
|
||||
|
||||
return t.MultiplyBy(inverse), r.MultiplyBy(inverse), nil
|
||||
}
|
||||
|
||||
func (this *ReedSolomonDecoder) findErrorLocations(errorLocator *GenericGFPoly) ([]int, error) {
|
||||
// This is a direct application of Chien's search
|
||||
numErrors := errorLocator.GetDegree()
|
||||
if numErrors == 1 { // shortcut
|
||||
return []int{errorLocator.GetCoefficient(1)}, nil
|
||||
}
|
||||
result := make([]int, numErrors)
|
||||
e := 0
|
||||
for i := 1; i < this.field.GetSize() && e < numErrors; i++ {
|
||||
if errorLocator.EvaluateAt(i) == 0 {
|
||||
var err error
|
||||
result[e], err = this.field.Inverse(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e++
|
||||
}
|
||||
}
|
||||
if e != numErrors {
|
||||
return nil, NewReedSolomonException("Error locator degree does not match number of roots")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (this *ReedSolomonDecoder) findErrorMagnitudes(errorEvaluator *GenericGFPoly, errorLocations []int) ([]int, error) {
|
||||
// This is directly applying Forney's Formula
|
||||
s := len(errorLocations)
|
||||
result := make([]int, s)
|
||||
for i := 0; i < s; i++ {
|
||||
xiInverse, e := this.field.Inverse(errorLocations[i])
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
denominator := 1
|
||||
for j := 0; j < s; j++ {
|
||||
if i != j {
|
||||
//denominator = field.multiply(denominator,
|
||||
// GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
|
||||
// Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
|
||||
// Below is a funny-looking workaround from Steven Parkes
|
||||
term := this.field.Multiply(errorLocations[j], xiInverse)
|
||||
var termPlus1 int
|
||||
if (term & 0x1) == 0 {
|
||||
termPlus1 = term | 1
|
||||
} else {
|
||||
termPlus1 = term & ^1
|
||||
}
|
||||
denominator = this.field.Multiply(denominator, termPlus1)
|
||||
}
|
||||
}
|
||||
inverse, e := this.field.Inverse(denominator)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
result[i] = this.field.Multiply(errorEvaluator.EvaluateAt(xiInverse), inverse)
|
||||
if this.field.GetGeneratorBase() != 0 {
|
||||
result[i] = this.field.Multiply(result[i], xiInverse)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
59
vendor/github.com/makiuchi-d/gozxing/common/reedsolomon/reedsolomon_encoder.go
generated
vendored
Normal file
59
vendor/github.com/makiuchi-d/gozxing/common/reedsolomon/reedsolomon_encoder.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
package reedsolomon
|
||||
|
||||
import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type ReedSolomonEncoder struct {
|
||||
field *GenericGF
|
||||
cachedGenerators []*GenericGFPoly
|
||||
}
|
||||
|
||||
func NewReedSolomonEncoder(field *GenericGF) *ReedSolomonEncoder {
|
||||
gen, _ := NewGenericGFPoly(field, []int{1})
|
||||
return &ReedSolomonEncoder{
|
||||
field: field,
|
||||
cachedGenerators: []*GenericGFPoly{gen},
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ReedSolomonEncoder) buildGenerator(degree int) *GenericGFPoly {
|
||||
size := len(this.cachedGenerators)
|
||||
if degree >= size {
|
||||
lastGenerator := this.cachedGenerators[size-1]
|
||||
for d := size; d <= degree; d++ {
|
||||
poly, _ := NewGenericGFPoly(
|
||||
this.field, []int{1, this.field.Exp(d - 1 + this.field.GetGeneratorBase())})
|
||||
nextGenerator, _ := lastGenerator.Multiply(poly)
|
||||
this.cachedGenerators = append(this.cachedGenerators, nextGenerator)
|
||||
lastGenerator = nextGenerator
|
||||
}
|
||||
}
|
||||
return this.cachedGenerators[degree]
|
||||
}
|
||||
|
||||
func (this *ReedSolomonEncoder) Encode(toEncode []int, ecBytes int) error {
|
||||
if ecBytes <= 0 {
|
||||
return errors.New("(IllegalArgumentException: No error correction bytes")
|
||||
}
|
||||
dataBytes := len(toEncode) - ecBytes
|
||||
if dataBytes <= 0 {
|
||||
return errors.New("IllegalArgumentException: No data bytes provided")
|
||||
}
|
||||
generator := this.buildGenerator(ecBytes)
|
||||
infoCoefficients := make([]int, dataBytes)
|
||||
copy(infoCoefficients, toEncode)
|
||||
info, _ := NewGenericGFPoly(this.field, infoCoefficients)
|
||||
info, _ = info.MultiplyByMonomial(ecBytes, 1)
|
||||
_, remainder, e := info.Divide(generator)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
coefficients := remainder.GetCoefficients()
|
||||
numZeroCoefficients := ecBytes - len(coefficients)
|
||||
for i := 0; i < numZeroCoefficients; i++ {
|
||||
toEncode[dataBytes+i] = 0
|
||||
}
|
||||
copy(toEncode[dataBytes+numZeroCoefficients:], coefficients)
|
||||
return nil
|
||||
}
|
||||
60
vendor/github.com/makiuchi-d/gozxing/common/reedsolomon/reedsolomon_exception.go
generated
vendored
Normal file
60
vendor/github.com/makiuchi-d/gozxing/common/reedsolomon/reedsolomon_exception.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
package reedsolomon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type ReedSolomonException interface {
|
||||
error
|
||||
reedSolomonException()
|
||||
}
|
||||
|
||||
type reedSolomonException struct {
|
||||
msg string
|
||||
next error
|
||||
frame errors.Frame
|
||||
}
|
||||
|
||||
func (reedSolomonException) reedSolomonException() {}
|
||||
|
||||
func (e reedSolomonException) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
func (e reedSolomonException) Unwrap() error {
|
||||
return e.next
|
||||
}
|
||||
|
||||
func (e reedSolomonException) Format(s fmt.State, v rune) {
|
||||
errors.FormatError(e, s, v)
|
||||
}
|
||||
|
||||
func (e reedSolomonException) FormatError(p errors.Printer) error {
|
||||
p.Print(e.msg)
|
||||
e.frame.Format(p)
|
||||
return e.next
|
||||
}
|
||||
|
||||
func NewReedSolomonException(msg string) ReedSolomonException {
|
||||
return reedSolomonException{
|
||||
"ReedSolomonException: " + msg,
|
||||
nil,
|
||||
errors.Caller(1),
|
||||
}
|
||||
}
|
||||
|
||||
func WrapReedSolomonException(err error) ReedSolomonException {
|
||||
msg := err.Error()
|
||||
if !strings.HasPrefix(msg, "ReedSolomonException") {
|
||||
msg = "ReedSolomonException: " + msg
|
||||
}
|
||||
|
||||
return reedSolomonException{
|
||||
msg,
|
||||
err,
|
||||
errors.Caller(1),
|
||||
}
|
||||
}
|
||||
208
vendor/github.com/makiuchi-d/gozxing/common/string_utils.go
generated
vendored
Normal file
208
vendor/github.com/makiuchi-d/gozxing/common/string_utils.go
generated
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/encoding/ianaindex"
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
const (
|
||||
StringUtils_ASSUME_SHIFT_JIS = false
|
||||
// Retained for ABI compatibility with earlier versions
|
||||
StringUtils_SHIFT_JIS = "SJIS"
|
||||
StringUtils_GB2312 = "GB2312"
|
||||
)
|
||||
|
||||
var (
|
||||
StringUtils_PLATFORM_DEFAULT_ENCODING = unicode.UTF8
|
||||
StringUtils_SHIFT_JIS_CHARSET = japanese.ShiftJIS // "SJIS"
|
||||
StringUtils_GB2312_CHARSET = simplifiedchinese.GB18030 // "GB2312"
|
||||
StringUtils_EUC_JP = japanese.EUCJP // "EUC_JP"
|
||||
)
|
||||
|
||||
func StringUtils_guessEncoding(bytes []byte, hints map[gozxing.DecodeHintType]interface{}) (string, error) {
|
||||
c, err := StringUtils_guessCharset(bytes, hints)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if c == StringUtils_SHIFT_JIS_CHARSET {
|
||||
return "SJIS", nil
|
||||
} else if c == unicode.UTF8 {
|
||||
return "UTF8", nil
|
||||
} else if c == charmap.ISO8859_1 {
|
||||
return "ISO8859_1", nil
|
||||
}
|
||||
return ianaindex.IANA.Name(c)
|
||||
}
|
||||
|
||||
func StringUtils_guessCharset(bytes []byte, hints map[gozxing.DecodeHintType]interface{}) (encoding.Encoding, error) {
|
||||
if hint, ok := hints[gozxing.DecodeHintType_CHARACTER_SET]; ok {
|
||||
if charset, ok := hint.(encoding.Encoding); ok {
|
||||
return charset, nil
|
||||
}
|
||||
name := fmt.Sprintf("%v", hint)
|
||||
if eci, ok := GetCharacterSetECIByName(name); ok {
|
||||
return eci.GetCharset(), nil
|
||||
}
|
||||
|
||||
return ianaindex.IANA.Encoding(name)
|
||||
}
|
||||
|
||||
// First try UTF-16, assuming anything with its BOM is UTF-16
|
||||
if len(bytes) > 2 {
|
||||
if bytes[0] == 0xfe && bytes[1] == 0xff {
|
||||
return unicode.UTF16(unicode.BigEndian, unicode.UseBOM), nil
|
||||
}
|
||||
if bytes[0] == 0xff && bytes[1] == 0xfe {
|
||||
return unicode.UTF16(unicode.LittleEndian, unicode.UseBOM), nil
|
||||
}
|
||||
}
|
||||
|
||||
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
|
||||
// which should be by far the most common encodings.
|
||||
length := len(bytes)
|
||||
canBeISO88591 := true
|
||||
canBeShiftJIS := true
|
||||
canBeUTF8 := true
|
||||
utf8BytesLeft := 0
|
||||
utf2BytesChars := 0
|
||||
utf3BytesChars := 0
|
||||
utf4BytesChars := 0
|
||||
sjisBytesLeft := 0
|
||||
sjisKatakanaChars := 0
|
||||
sjisCurKatakanaWordLength := 0
|
||||
sjisCurDoubleBytesWordLength := 0
|
||||
sjisMaxKatakanaWordLength := 0
|
||||
sjisMaxDoubleBytesWordLength := 0
|
||||
isoHighOther := 0
|
||||
|
||||
utf8bom := len(bytes) > 3 &&
|
||||
bytes[0] == 0xEF &&
|
||||
bytes[1] == 0xBB &&
|
||||
bytes[2] == 0xBF
|
||||
|
||||
for i := 0; i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8); i++ {
|
||||
|
||||
value := bytes[i] & 0xFF
|
||||
|
||||
// UTF-8 stuff
|
||||
if canBeUTF8 {
|
||||
if utf8BytesLeft > 0 {
|
||||
if (value & 0x80) == 0 {
|
||||
canBeUTF8 = false
|
||||
} else {
|
||||
utf8BytesLeft--
|
||||
}
|
||||
} else if (value & 0x80) != 0 {
|
||||
if (value & 0x40) == 0 {
|
||||
canBeUTF8 = false
|
||||
} else {
|
||||
utf8BytesLeft++
|
||||
if (value & 0x20) == 0 {
|
||||
utf2BytesChars++
|
||||
} else {
|
||||
utf8BytesLeft++
|
||||
if (value & 0x10) == 0 {
|
||||
utf3BytesChars++
|
||||
} else {
|
||||
utf8BytesLeft++
|
||||
if (value & 0x08) == 0 {
|
||||
utf4BytesChars++
|
||||
} else {
|
||||
canBeUTF8 = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ISO-8859-1 stuff
|
||||
if canBeISO88591 {
|
||||
if value > 0x7F && value < 0xA0 {
|
||||
canBeISO88591 = false
|
||||
} else if value > 0x9F && (value < 0xC0 || value == 0xD7 || value == 0xF7) {
|
||||
isoHighOther++
|
||||
}
|
||||
}
|
||||
|
||||
// Shift_JIS stuff
|
||||
if canBeShiftJIS {
|
||||
if sjisBytesLeft > 0 {
|
||||
if value < 0x40 || value == 0x7F || value > 0xFC {
|
||||
canBeShiftJIS = false
|
||||
} else {
|
||||
sjisBytesLeft--
|
||||
}
|
||||
} else if value == 0x80 || value == 0xA0 || value > 0xEF {
|
||||
canBeShiftJIS = false
|
||||
} else if value > 0xA0 && value < 0xE0 {
|
||||
sjisKatakanaChars++
|
||||
sjisCurDoubleBytesWordLength = 0
|
||||
sjisCurKatakanaWordLength++
|
||||
if sjisCurKatakanaWordLength > sjisMaxKatakanaWordLength {
|
||||
sjisMaxKatakanaWordLength = sjisCurKatakanaWordLength
|
||||
}
|
||||
} else if value > 0x7F {
|
||||
sjisBytesLeft++
|
||||
//sjisDoubleBytesChars++;
|
||||
sjisCurKatakanaWordLength = 0
|
||||
sjisCurDoubleBytesWordLength++
|
||||
if sjisCurDoubleBytesWordLength > sjisMaxDoubleBytesWordLength {
|
||||
sjisMaxDoubleBytesWordLength = sjisCurDoubleBytesWordLength
|
||||
}
|
||||
} else {
|
||||
//sjisLowChars++;
|
||||
sjisCurKatakanaWordLength = 0
|
||||
sjisCurDoubleBytesWordLength = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if canBeUTF8 && utf8BytesLeft > 0 {
|
||||
canBeUTF8 = false
|
||||
}
|
||||
if canBeShiftJIS && sjisBytesLeft > 0 {
|
||||
canBeShiftJIS = false
|
||||
}
|
||||
|
||||
// Easy -- if there is BOM or at least 1 valid not-single byte character (and no evidence it can't be UTF-8), done
|
||||
if canBeUTF8 && (utf8bom || utf2BytesChars+utf3BytesChars+utf4BytesChars > 0) {
|
||||
return unicode.UTF8, nil
|
||||
}
|
||||
// Easy -- if assuming Shift_JIS or at least 3 valid consecutive not-ascii characters (and no evidence it can't be), done
|
||||
if canBeShiftJIS && (sjisMaxKatakanaWordLength >= 3 || sjisMaxDoubleBytesWordLength >= 3) {
|
||||
return StringUtils_SHIFT_JIS_CHARSET, nil
|
||||
}
|
||||
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough for short words. The crude heuristic is:
|
||||
// - If we saw
|
||||
// - only two consecutive katakana chars in the whole text, or
|
||||
// - at least 10% of bytes that could be "upper" not-alphanumeric Latin1,
|
||||
// - then we conclude Shift_JIS, else ISO-8859-1
|
||||
if canBeISO88591 && canBeShiftJIS {
|
||||
if (sjisMaxKatakanaWordLength == 2 && sjisKatakanaChars == 2) || isoHighOther*10 >= length {
|
||||
return StringUtils_SHIFT_JIS_CHARSET, nil
|
||||
}
|
||||
return charmap.ISO8859_1, nil
|
||||
}
|
||||
|
||||
// Otherwise, try in order ISO-8859-1, Shift JIS, UTF-8 and fall back to default platform encoding
|
||||
if canBeISO88591 {
|
||||
return charmap.ISO8859_1, nil
|
||||
}
|
||||
if canBeShiftJIS {
|
||||
return StringUtils_SHIFT_JIS_CHARSET, nil
|
||||
}
|
||||
if canBeUTF8 {
|
||||
return unicode.UTF8, nil
|
||||
}
|
||||
// Otherwise, we take a wild guess with platform encoding
|
||||
return StringUtils_PLATFORM_DEFAULT_ENCODING, nil
|
||||
}
|
||||
32
vendor/github.com/makiuchi-d/gozxing/common/util/math_utils.go
generated
vendored
Normal file
32
vendor/github.com/makiuchi-d/gozxing/common/util/math_utils.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
func MathUtils_Round(d float64) int {
|
||||
if d < 0.0 {
|
||||
return int(d - 0.5)
|
||||
}
|
||||
return int(d + 0.5)
|
||||
}
|
||||
|
||||
func MathUtils_DistanceFloat(aX, aY, bX, bY float64) float64 {
|
||||
xDiff := aX - bX
|
||||
yDiff := aY - bY
|
||||
return math.Sqrt(xDiff*xDiff + yDiff*yDiff)
|
||||
}
|
||||
|
||||
func MathUtils_DistanceInt(aX, aY, bX, bY int) float64 {
|
||||
xDiff := aX - bX
|
||||
yDiff := aY - bY
|
||||
return math.Sqrt(float64(xDiff*xDiff + yDiff*yDiff))
|
||||
}
|
||||
|
||||
func MathUtils_Sum(arr []int) int {
|
||||
count := 0
|
||||
for _, a := range arr {
|
||||
count += a
|
||||
}
|
||||
return count
|
||||
}
|
||||
109
vendor/github.com/makiuchi-d/gozxing/decode_hint_type.go
generated
vendored
Normal file
109
vendor/github.com/makiuchi-d/gozxing/decode_hint_type.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
package gozxing
|
||||
|
||||
type DecodeHintType int
|
||||
|
||||
const (
|
||||
/**
|
||||
* Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
|
||||
*/
|
||||
DecodeHintType_OTHER DecodeHintType = iota
|
||||
|
||||
/**
|
||||
* Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
DecodeHintType_PURE_BARCODE
|
||||
|
||||
/**
|
||||
* Image is known to be of one of a few possible formats.
|
||||
* Maps to a {@link List} of {@link BarcodeFormat}s.
|
||||
*/
|
||||
DecodeHintType_POSSIBLE_FORMATS
|
||||
|
||||
/**
|
||||
* Spend more time to try to find a barcode; optimize for accuracy, not speed.
|
||||
* Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||
*/
|
||||
DecodeHintType_TRY_HARDER
|
||||
|
||||
/**
|
||||
* Specifies what character encoding to use when decoding, where applicable (type String)
|
||||
*/
|
||||
DecodeHintType_CHARACTER_SET
|
||||
|
||||
/**
|
||||
* Allowed lengths of encoded data -- reject anything else. Maps to an {@code int[]}.
|
||||
*/
|
||||
DecodeHintType_ALLOWED_LENGTHS
|
||||
|
||||
/**
|
||||
* Assume Code 39 codes employ a check digit. Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
DecodeHintType_ASSUME_CODE_39_CHECK_DIGIT
|
||||
|
||||
/**
|
||||
* Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.
|
||||
* For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
DecodeHintType_ASSUME_GS1
|
||||
|
||||
/**
|
||||
* If true, return the start and end digits in a Codabar barcode instead of stripping them. They
|
||||
* are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them
|
||||
* to not be. Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||
*/
|
||||
DecodeHintType_RETURN_CODABAR_START_END
|
||||
|
||||
/**
|
||||
* The caller needs to be notified via callback when a possible {@link ResultPoint}
|
||||
* is found. Maps to a {@link ResultPointCallback}.
|
||||
*/
|
||||
DecodeHintType_NEED_RESULT_POINT_CALLBACK
|
||||
|
||||
/**
|
||||
* Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this.
|
||||
* Maps to an {@code int[]} of the allowed extension lengths, for example [2], [5], or [2, 5].
|
||||
* If it is optional to have an extension, do not set this hint. If this is set,
|
||||
* and a UPC or EAN barcode is found but an extension is not, then no result will be returned
|
||||
* at all.
|
||||
*/
|
||||
DecodeHintType_ALLOWED_EAN_EXTENSIONS
|
||||
|
||||
/**
|
||||
* If true, also tries to decode as inverted image. All configured decoders are simply called a
|
||||
* second time with an inverted image. Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||
*/
|
||||
DecodeHintType_ALSO_INVERTED
|
||||
)
|
||||
|
||||
func (t DecodeHintType) String() string {
|
||||
switch t {
|
||||
case DecodeHintType_OTHER:
|
||||
return "OTHER"
|
||||
case DecodeHintType_PURE_BARCODE:
|
||||
return "PURE_BARCODE"
|
||||
case DecodeHintType_POSSIBLE_FORMATS:
|
||||
return "POSSIBLE_FORMATS"
|
||||
case DecodeHintType_TRY_HARDER:
|
||||
return "TRY_HARDER"
|
||||
case DecodeHintType_CHARACTER_SET:
|
||||
return "CHARACTER_SET"
|
||||
case DecodeHintType_ALLOWED_LENGTHS:
|
||||
return "ALLOWED_LENGTHS"
|
||||
case DecodeHintType_ASSUME_CODE_39_CHECK_DIGIT:
|
||||
return "ASSUME_CODE_39_CHECK_DIGIT"
|
||||
case DecodeHintType_ASSUME_GS1:
|
||||
return "ASSUME_GS1"
|
||||
case DecodeHintType_RETURN_CODABAR_START_END:
|
||||
return "RETURN_CODABAR_START_END"
|
||||
case DecodeHintType_NEED_RESULT_POINT_CALLBACK:
|
||||
return "NEED_RESULT_POINT_CALLBACK"
|
||||
case DecodeHintType_ALLOWED_EAN_EXTENSIONS:
|
||||
return "ALLOWED_EAN_EXTENSIONS"
|
||||
case DecodeHintType_ALSO_INVERTED:
|
||||
return "ALSO_INVERTED"
|
||||
}
|
||||
return "Unknown DecodeHintType"
|
||||
}
|
||||
39
vendor/github.com/makiuchi-d/gozxing/dimension.go
generated
vendored
Normal file
39
vendor/github.com/makiuchi-d/gozxing/dimension.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Dimension struct {
|
||||
width int
|
||||
height int
|
||||
}
|
||||
|
||||
func NewDimension(width, height int) (*Dimension, error) {
|
||||
if width < 0 || height < 0 {
|
||||
return nil, errors.New("IllegalArgumentException")
|
||||
}
|
||||
return &Dimension{width, height}, nil
|
||||
}
|
||||
|
||||
func (this *Dimension) GetWidth() int {
|
||||
return this.width
|
||||
}
|
||||
|
||||
func (this *Dimension) GetHeight() int {
|
||||
return this.height
|
||||
}
|
||||
|
||||
func (this *Dimension) Equals(other *Dimension) bool {
|
||||
return this.width == other.width && this.height == other.height
|
||||
}
|
||||
|
||||
func (this *Dimension) HashCode() int {
|
||||
return this.width*32713 + this.height
|
||||
}
|
||||
|
||||
func (this *Dimension) String() string {
|
||||
return fmt.Sprintf("%dx%d", this.width, this.height)
|
||||
}
|
||||
135
vendor/github.com/makiuchi-d/gozxing/encode_hint_type.go
generated
vendored
Normal file
135
vendor/github.com/makiuchi-d/gozxing/encode_hint_type.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
package gozxing
|
||||
|
||||
type EncodeHintType int
|
||||
|
||||
const (
|
||||
/**
|
||||
* Specifies what degree of error correction to use, for example in QR Codes.
|
||||
* Type depends on the encoder. For example for QR codes it's type
|
||||
* {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}.
|
||||
* For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words.
|
||||
* For PDF417 it is of type {@link Integer}, valid values being 0 to 8.
|
||||
* In all cases, it can also be a {@link String} representation of the desired value as well.
|
||||
* Note: an Aztec symbol should have a minimum of 25% EC words.
|
||||
*/
|
||||
EncodeHintType_ERROR_CORRECTION = iota
|
||||
|
||||
/**
|
||||
* Specifies what character encoding to use where applicable (type {@link String})
|
||||
*/
|
||||
EncodeHintType_CHARACTER_SET
|
||||
|
||||
/**
|
||||
* Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})
|
||||
*/
|
||||
EncodeHintType_DATA_MATRIX_SHAPE
|
||||
|
||||
/**
|
||||
* Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
*
|
||||
* @deprecated use width/height params in
|
||||
* {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)}
|
||||
*/
|
||||
EncodeHintType_MIN_SIZE
|
||||
|
||||
/**
|
||||
* Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
*
|
||||
* @deprecated without replacement
|
||||
*/
|
||||
EncodeHintType_MAX_SIZE
|
||||
|
||||
/**
|
||||
* Specifies margin, in pixels, to use when generating the barcode. The meaning can vary
|
||||
* by format; for example it controls margin before and after the barcode horizontally for
|
||||
* most 1D formats. (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
EncodeHintType_MARGIN
|
||||
|
||||
/**
|
||||
* Specifies whether to use compact mode for PDF417 (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String} value).
|
||||
*/
|
||||
EncodeHintType_PDF417_COMPACT
|
||||
|
||||
/**
|
||||
* Specifies what compaction mode to use for PDF417 (type
|
||||
* {@link com.google.zxing.pdf417.encoder.Compaction Compaction} or {@link String} value of one of its
|
||||
* enum values).
|
||||
*/
|
||||
EncodeHintType_PDF417_COMPACTION
|
||||
|
||||
/**
|
||||
* Specifies the minimum and maximum number of rows and columns for PDF417 (type
|
||||
* {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}).
|
||||
*/
|
||||
EncodeHintType_PDF417_DIMENSIONS
|
||||
|
||||
/**
|
||||
* Specifies the required number of layers for an Aztec code.
|
||||
* A negative number (-1, -2, -3, -4) specifies a compact Aztec code.
|
||||
* 0 indicates to use the minimum number of layers (the default).
|
||||
* A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code.
|
||||
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
EncodeHintType_AZTEC_LAYERS
|
||||
|
||||
/**
|
||||
* Specifies the exact version of QR code to be encoded.
|
||||
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
EncodeHintType_QR_VERSION
|
||||
|
||||
/**
|
||||
* Specifies the QR code mask pattern to be used. Allowed values are
|
||||
* 0..QRCode.NUM_MASK_PATTERNS-1. By default the code will automatically select
|
||||
* the optimal mask pattern.
|
||||
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
EncodeHintType_QR_MASK_PATTERN
|
||||
|
||||
/**
|
||||
* Specifies whether the data should be encoded to the GS1 standard (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String } value).
|
||||
*/
|
||||
EncodeHintType_GS1_FORMAT
|
||||
|
||||
/**
|
||||
* Forces which encoding will be used. Currently only used for Code-128 code sets (Type {@link String}). Valid values are "A", "B", "C".
|
||||
*/
|
||||
EncodeHintType_FORCE_CODE_SET
|
||||
)
|
||||
|
||||
func (this EncodeHintType) String() string {
|
||||
switch this {
|
||||
case EncodeHintType_ERROR_CORRECTION:
|
||||
return "ERROR_CORRECTION"
|
||||
case EncodeHintType_CHARACTER_SET:
|
||||
return "CHARACTER_SET"
|
||||
case EncodeHintType_DATA_MATRIX_SHAPE:
|
||||
return "DATA_MATRIX_SHAPE"
|
||||
case EncodeHintType_MIN_SIZE:
|
||||
return "MIN_SIZE"
|
||||
case EncodeHintType_MAX_SIZE:
|
||||
return "MAX_SIZE"
|
||||
case EncodeHintType_MARGIN:
|
||||
return "MARGIN"
|
||||
case EncodeHintType_PDF417_COMPACT:
|
||||
return "PDF417_COMPACT"
|
||||
case EncodeHintType_PDF417_COMPACTION:
|
||||
return "PDF417_COMPACTION"
|
||||
case EncodeHintType_PDF417_DIMENSIONS:
|
||||
return "PDF417_DIMENSIONS"
|
||||
case EncodeHintType_AZTEC_LAYERS:
|
||||
return "AZTEC_LAYERS"
|
||||
case EncodeHintType_QR_VERSION:
|
||||
return "QR_VERSION"
|
||||
case EncodeHintType_QR_MASK_PATTERN:
|
||||
return "QR_MASK_PATTERN"
|
||||
case EncodeHintType_GS1_FORMAT:
|
||||
return "GS1_FORMAT"
|
||||
case EncodeHintType_FORCE_CODE_SET:
|
||||
return "FORCE_CODE_SET"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
25
vendor/github.com/makiuchi-d/gozxing/format_exception.go
generated
vendored
Normal file
25
vendor/github.com/makiuchi-d/gozxing/format_exception.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package gozxing
|
||||
|
||||
type FormatException interface {
|
||||
ReaderException
|
||||
formatException()
|
||||
}
|
||||
|
||||
type formatException struct {
|
||||
exception
|
||||
}
|
||||
|
||||
func (formatException) readerException() {}
|
||||
func (formatException) formatException() {}
|
||||
|
||||
func NewFormatException(args ...interface{}) FormatException {
|
||||
return formatException{
|
||||
newException("FormatException", args...),
|
||||
}
|
||||
}
|
||||
|
||||
func WrapFormatException(e error) FormatException {
|
||||
return formatException{
|
||||
wrapException("FormatException", e),
|
||||
}
|
||||
}
|
||||
191
vendor/github.com/makiuchi-d/gozxing/global_histogram_binarizer.go
generated
vendored
Normal file
191
vendor/github.com/makiuchi-d/gozxing/global_histogram_binarizer.go
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
package gozxing
|
||||
|
||||
const (
|
||||
LUMINANCE_BITS = 5
|
||||
LUMINANCE_SHIFT = 8 - LUMINANCE_BITS
|
||||
LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS
|
||||
)
|
||||
|
||||
type GlobalHistogramBinarizer struct {
|
||||
source LuminanceSource
|
||||
luminances []byte
|
||||
buckets []int
|
||||
}
|
||||
|
||||
func NewGlobalHistgramBinarizer(source LuminanceSource) Binarizer {
|
||||
return &GlobalHistogramBinarizer{
|
||||
source: source,
|
||||
luminances: []byte{},
|
||||
buckets: make([]int, LUMINANCE_BUCKETS),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GlobalHistogramBinarizer) GetLuminanceSource() LuminanceSource {
|
||||
return this.source
|
||||
}
|
||||
|
||||
func (this *GlobalHistogramBinarizer) GetWidth() int {
|
||||
return this.source.GetWidth()
|
||||
}
|
||||
|
||||
func (this *GlobalHistogramBinarizer) GetHeight() int {
|
||||
return this.source.GetHeight()
|
||||
}
|
||||
|
||||
func (this *GlobalHistogramBinarizer) GetBlackRow(y int, row *BitArray) (*BitArray, error) {
|
||||
source := this.GetLuminanceSource()
|
||||
width := source.GetWidth()
|
||||
if row == nil || row.GetSize() < width {
|
||||
row = NewBitArray(width)
|
||||
} else {
|
||||
row.Clear()
|
||||
}
|
||||
|
||||
this.initArrays(width)
|
||||
localLuminances, e := source.GetRow(y, this.luminances)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
localBuckets := this.buckets
|
||||
for x := 0; x < width; x++ {
|
||||
localBuckets[(localLuminances[x]&0xff)>>LUMINANCE_SHIFT]++
|
||||
}
|
||||
blackPoint, e := this.estimateBlackPoint(localBuckets)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
if width < 3 {
|
||||
// Special case for very small images
|
||||
for x := 0; x < width; x++ {
|
||||
if int(localLuminances[x]&0xff) < blackPoint {
|
||||
row.Set(x)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
left := int(localLuminances[0] & 0xff)
|
||||
center := int(localLuminances[1] & 0xff)
|
||||
for x := 1; x < width-1; x++ {
|
||||
right := int(localLuminances[x+1] & 0xff)
|
||||
// A simple -1 4 -1 box filter with a weight of 2.
|
||||
if ((center*4)-left-right)/2 < blackPoint {
|
||||
row.Set(x)
|
||||
}
|
||||
left = center
|
||||
center = right
|
||||
}
|
||||
}
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func (this *GlobalHistogramBinarizer) GetBlackMatrix() (*BitMatrix, error) {
|
||||
source := this.GetLuminanceSource()
|
||||
width := source.GetWidth()
|
||||
height := source.GetHeight()
|
||||
matrix, e := NewBitMatrix(width, height)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
// Quickly calculates the histogram by sampling four rows from the image. This proved to be
|
||||
// more robust on the blackbox tests than sampling a diagonal as we used to do.
|
||||
this.initArrays(width)
|
||||
localBuckets := this.buckets
|
||||
for y := 1; y < 5; y++ {
|
||||
row := height * y / 5
|
||||
localLuminances, _ := source.GetRow(row, this.luminances)
|
||||
right := (width * 4) / 5
|
||||
for x := width / 5; x < right; x++ {
|
||||
pixel := localLuminances[x] & 0xff
|
||||
localBuckets[pixel>>LUMINANCE_SHIFT]++
|
||||
}
|
||||
}
|
||||
blackPoint, e := this.estimateBlackPoint(localBuckets)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
// We delay reading the entire image luminance until the black point estimation succeeds.
|
||||
// Although we end up reading four rows twice, it is consistent with our motto of
|
||||
// "fail quickly" which is necessary for continuous scanning.
|
||||
localLuminances := source.GetMatrix()
|
||||
for y := 0; y < height; y++ {
|
||||
offset := y * width
|
||||
for x := 0; x < width; x++ {
|
||||
pixel := int(localLuminances[offset+x] & 0xff)
|
||||
if pixel < blackPoint {
|
||||
matrix.Set(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matrix, nil
|
||||
}
|
||||
|
||||
func (this *GlobalHistogramBinarizer) CreateBinarizer(source LuminanceSource) Binarizer {
|
||||
return NewGlobalHistgramBinarizer(source)
|
||||
}
|
||||
|
||||
func (this *GlobalHistogramBinarizer) initArrays(luminanceSize int) {
|
||||
if len(this.luminances) < luminanceSize {
|
||||
this.luminances = make([]byte, luminanceSize)
|
||||
}
|
||||
for x := 0; x < LUMINANCE_BUCKETS; x++ {
|
||||
this.buckets[x] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GlobalHistogramBinarizer) estimateBlackPoint(buckets []int) (int, error) {
|
||||
// Find the tallest peak in the histogram.
|
||||
numBuckets := len(buckets)
|
||||
maxBucketCount := 0
|
||||
firstPeak := 0
|
||||
firstPeakSize := 0
|
||||
for x := 0; x < numBuckets; x++ {
|
||||
if buckets[x] > firstPeakSize {
|
||||
firstPeak = x
|
||||
firstPeakSize = buckets[x]
|
||||
}
|
||||
if buckets[x] > maxBucketCount {
|
||||
maxBucketCount = buckets[x]
|
||||
}
|
||||
}
|
||||
|
||||
// Find the second-tallest peak which is somewhat far from the tallest peak.
|
||||
secondPeak := 0
|
||||
secondPeakScore := 0
|
||||
for x := 0; x < numBuckets; x++ {
|
||||
distanceToBiggest := x - firstPeak
|
||||
// Encourage more distant second peaks by multiplying by square of distance.
|
||||
score := buckets[x] * distanceToBiggest * distanceToBiggest
|
||||
if score > secondPeakScore {
|
||||
secondPeak = x
|
||||
secondPeakScore = score
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure firstPeak corresponds to the black peak.
|
||||
if firstPeak > secondPeak {
|
||||
firstPeak, secondPeak = secondPeak, firstPeak
|
||||
}
|
||||
|
||||
// If there is too little contrast in the image to pick a meaningful black point, throw rather
|
||||
// than waste time trying to decode the image, and risk false positives.
|
||||
if secondPeak-firstPeak <= numBuckets/16 {
|
||||
return 0, NewNotFoundException()
|
||||
}
|
||||
|
||||
// Find a valley between them that is low and closer to the white peak.
|
||||
bestValley := secondPeak - 1
|
||||
bestValleyScore := -1
|
||||
for x := secondPeak - 1; x > firstPeak; x-- {
|
||||
fromFirst := x - firstPeak
|
||||
score := fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x])
|
||||
if score > bestValleyScore {
|
||||
bestValley = x
|
||||
bestValleyScore = score
|
||||
}
|
||||
}
|
||||
|
||||
return bestValley << LUMINANCE_SHIFT, nil
|
||||
}
|
||||
24
vendor/github.com/makiuchi-d/gozxing/go_image_bit_matrix.go
generated
vendored
Normal file
24
vendor/github.com/makiuchi-d/gozxing/go_image_bit_matrix.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
// implement image.Image for Go
|
||||
|
||||
func (img *BitMatrix) ColorModel() color.Model {
|
||||
return color.GrayModel
|
||||
}
|
||||
|
||||
func (img *BitMatrix) Bounds() image.Rectangle {
|
||||
return image.Rect(0, 0, img.GetWidth(), img.GetHeight())
|
||||
}
|
||||
|
||||
func (img *BitMatrix) At(x, y int) color.Color {
|
||||
c := color.Gray{0}
|
||||
if !img.Get(x, y) {
|
||||
c.Y = 255
|
||||
}
|
||||
return c
|
||||
}
|
||||
88
vendor/github.com/makiuchi-d/gozxing/go_image_luminance_source.go
generated
vendored
Normal file
88
vendor/github.com/makiuchi-d/gozxing/go_image_luminance_source.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func NewBinaryBitmapFromImage(img image.Image) (*BinaryBitmap, error) {
|
||||
src := NewLuminanceSourceFromImage(img)
|
||||
return NewBinaryBitmap(NewHybridBinarizer(src))
|
||||
}
|
||||
|
||||
type GoImageLuminanceSource struct {
|
||||
*RGBLuminanceSource
|
||||
}
|
||||
|
||||
func NewLuminanceSourceFromImage(img image.Image) LuminanceSource {
|
||||
rect := img.Bounds()
|
||||
width := rect.Max.X - rect.Min.X
|
||||
height := rect.Max.Y - rect.Min.Y
|
||||
|
||||
luminance := make([]byte, width*height)
|
||||
index := 0
|
||||
for y := rect.Min.Y; y < rect.Max.Y; y++ {
|
||||
for x := rect.Min.X; x < rect.Max.X; x++ {
|
||||
r, g, b, a := img.At(x, y).RGBA()
|
||||
lum := (r + 2*g + b) * 255 / (4 * 0xffff)
|
||||
luminance[index] = byte((lum*a + (0xffff-a)*255) / 0xffff)
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
return &GoImageLuminanceSource{&RGBLuminanceSource{
|
||||
LuminanceSourceBase{width, height},
|
||||
luminance,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
0,
|
||||
}}
|
||||
}
|
||||
|
||||
func (this *GoImageLuminanceSource) Crop(left, top, width, height int) (LuminanceSource, error) {
|
||||
cropped, e := this.RGBLuminanceSource.Crop(left, top, width, height)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return &GoImageLuminanceSource{cropped.(*RGBLuminanceSource)}, nil
|
||||
}
|
||||
|
||||
func (this *GoImageLuminanceSource) Invert() LuminanceSource {
|
||||
return LuminanceSourceInvert(this)
|
||||
}
|
||||
|
||||
func (this *GoImageLuminanceSource) IsRotateSupported() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *GoImageLuminanceSource) RotateCounterClockwise() (LuminanceSource, error) {
|
||||
width := this.GetWidth()
|
||||
height := this.GetHeight()
|
||||
top := this.top
|
||||
left := this.left
|
||||
dataWidth := this.dataWidth
|
||||
oldLuminas := this.RGBLuminanceSource.luminances
|
||||
newLuminas := make([]byte, width*height)
|
||||
|
||||
for j := 0; j < width; j++ {
|
||||
x := left + width - 1 - j
|
||||
for i := 0; i < height; i++ {
|
||||
y := top + i
|
||||
newLuminas[j*height+i] = oldLuminas[y*dataWidth+x]
|
||||
}
|
||||
}
|
||||
return &GoImageLuminanceSource{&RGBLuminanceSource{
|
||||
LuminanceSourceBase{height, width},
|
||||
newLuminas,
|
||||
height,
|
||||
width,
|
||||
0,
|
||||
0,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func (this *GoImageLuminanceSource) RotateCounterClockwise45() (LuminanceSource, error) {
|
||||
return nil, errors.New("RotateCounterClockwise45 is not implemented")
|
||||
}
|
||||
180
vendor/github.com/makiuchi-d/gozxing/hybrid_binarizer.go
generated
vendored
Normal file
180
vendor/github.com/makiuchi-d/gozxing/hybrid_binarizer.go
generated
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
package gozxing
|
||||
|
||||
const (
|
||||
BLOCK_SIZE_POWER = 3
|
||||
BLOCK_SIZE = 1 << BLOCK_SIZE_POWER // ...0100...00
|
||||
BLOCK_SIZE_MASK = BLOCK_SIZE - 1 // ...0011...11
|
||||
MINIMUM_DIMENSION = BLOCK_SIZE * 5
|
||||
MIN_DYNAMIC_RANGE = 24
|
||||
)
|
||||
|
||||
type HybridBinarizer struct {
|
||||
*GlobalHistogramBinarizer
|
||||
matrix *BitMatrix
|
||||
}
|
||||
|
||||
func NewHybridBinarizer(source LuminanceSource) Binarizer {
|
||||
return &HybridBinarizer{
|
||||
NewGlobalHistgramBinarizer(source).(*GlobalHistogramBinarizer),
|
||||
nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *HybridBinarizer) GetBlackMatrix() (*BitMatrix, error) {
|
||||
if this.matrix != nil {
|
||||
return this.matrix, nil
|
||||
}
|
||||
source := this.GetLuminanceSource()
|
||||
width := source.GetWidth()
|
||||
height := source.GetHeight()
|
||||
if width >= MINIMUM_DIMENSION && height >= MINIMUM_DIMENSION {
|
||||
luminances := source.GetMatrix()
|
||||
subWidth := width >> BLOCK_SIZE_POWER
|
||||
if (width & BLOCK_SIZE_MASK) != 0 {
|
||||
subWidth++
|
||||
}
|
||||
subHeight := height >> BLOCK_SIZE_POWER
|
||||
if (height & BLOCK_SIZE_MASK) != 0 {
|
||||
subHeight++
|
||||
}
|
||||
blackPoints := this.calculateBlackPoints(luminances, subWidth, subHeight, width, height)
|
||||
|
||||
newMatrix, _ := NewBitMatrix(width, height)
|
||||
this.calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, newMatrix)
|
||||
this.matrix = newMatrix
|
||||
} else {
|
||||
// If the image is too small, fall back to the global histogram approach.
|
||||
newMatrix, e := this.GlobalHistogramBinarizer.GetBlackMatrix()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
this.matrix = newMatrix
|
||||
}
|
||||
return this.matrix, nil
|
||||
}
|
||||
|
||||
func (this *HybridBinarizer) CreateBinarizer(source LuminanceSource) Binarizer {
|
||||
return NewHybridBinarizer(source)
|
||||
}
|
||||
|
||||
func (this *HybridBinarizer) calculateThresholdForBlock(
|
||||
luminances []byte, subWidth, subHeight, width, height int, blackPoints [][]int, matrix *BitMatrix) {
|
||||
maxYOffset := height - BLOCK_SIZE
|
||||
maxXOffset := width - BLOCK_SIZE
|
||||
for y := 0; y < subHeight; y++ {
|
||||
yoffset := y << BLOCK_SIZE_POWER
|
||||
if yoffset > maxYOffset {
|
||||
yoffset = maxYOffset
|
||||
}
|
||||
top := this.cap(y, 2, subHeight-3)
|
||||
for x := 0; x < subWidth; x++ {
|
||||
xoffset := x << BLOCK_SIZE_POWER
|
||||
if xoffset > maxXOffset {
|
||||
xoffset = maxXOffset
|
||||
}
|
||||
left := this.cap(x, 2, subWidth-3)
|
||||
sum := 0
|
||||
for z := -2; z <= 2; z++ {
|
||||
blackRow := blackPoints[top+z]
|
||||
sum += blackRow[left-2] + blackRow[left-1] + blackRow[left] + blackRow[left+1] + blackRow[left+2]
|
||||
}
|
||||
average := sum / 25
|
||||
this.thresholdBlock(luminances, xoffset, yoffset, average, width, matrix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *HybridBinarizer) cap(value, min, max int) int {
|
||||
if value < min {
|
||||
return min
|
||||
}
|
||||
if value > max {
|
||||
return max
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func (this *HybridBinarizer) thresholdBlock(luminances []byte, xoffset, yoffset, threshold, stride int, matrix *BitMatrix) {
|
||||
for y, offset := 0, yoffset*stride+xoffset; y < BLOCK_SIZE; y, offset = y+1, offset+stride {
|
||||
for x := 0; x < BLOCK_SIZE; x++ {
|
||||
// Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
|
||||
if int(luminances[offset+x]&0xFF) <= threshold {
|
||||
matrix.Set(xoffset+x, yoffset+y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *HybridBinarizer) calculateBlackPoints(luminances []byte, subWidth, subHeight, width, height int) [][]int {
|
||||
maxYOffset := height - BLOCK_SIZE
|
||||
maxXOffset := width - BLOCK_SIZE
|
||||
blackPoints := make([][]int, subHeight)
|
||||
for y := 0; y < subHeight; y++ {
|
||||
blackPoints[y] = make([]int, subWidth)
|
||||
yoffset := y << BLOCK_SIZE_POWER
|
||||
if yoffset > maxYOffset {
|
||||
yoffset = maxYOffset
|
||||
}
|
||||
for x := 0; x < subWidth; x++ {
|
||||
xoffset := x << BLOCK_SIZE_POWER
|
||||
if xoffset > maxXOffset {
|
||||
xoffset = maxXOffset
|
||||
}
|
||||
sum := 0
|
||||
min := 0xFF
|
||||
max := 0
|
||||
for yy, offset := 0, yoffset*width+xoffset; yy < BLOCK_SIZE; yy, offset = yy+1, offset+width {
|
||||
for xx := 0; xx < BLOCK_SIZE; xx++ {
|
||||
pixel := int(luminances[offset+xx] & 0xFF)
|
||||
sum += pixel
|
||||
// still looking for good contrast
|
||||
if pixel < min {
|
||||
min = pixel
|
||||
}
|
||||
if pixel > max {
|
||||
max = pixel
|
||||
}
|
||||
}
|
||||
|
||||
// short-circuit min/max tests once dynamic range is met
|
||||
if max-min > MIN_DYNAMIC_RANGE {
|
||||
// finish the rest of the rows quickly
|
||||
for yy, offset = yy+1, offset+width; yy < BLOCK_SIZE; yy, offset = yy+1, offset+width {
|
||||
for xx := 0; xx < BLOCK_SIZE; xx++ {
|
||||
sum += int(luminances[offset+xx] & 0xFF)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The default estimate is the average of the values in the block.
|
||||
average := sum >> (BLOCK_SIZE_POWER * 2)
|
||||
if max-min <= MIN_DYNAMIC_RANGE {
|
||||
// If variation within the block is low, assume this is a block with only light or only
|
||||
// dark pixels. In that case we do not want to use the average, as it would divide this
|
||||
// low contrast area into black and white pixels, essentially creating data out of noise.
|
||||
//
|
||||
// The default assumption is that the block is light/background. Since no estimate for
|
||||
// the level of dark pixels exists locally, use half the min for the block.
|
||||
average = min / 2
|
||||
|
||||
if y > 0 && x > 0 {
|
||||
// Correct the "white background" assumption for blocks that have neighbors by comparing
|
||||
// the pixels in this block to the previously calculated black points. This is based on
|
||||
// the fact that dark barcode symbology is always surrounded by some amount of light
|
||||
// background for which reasonable black point estimates were made. The bp estimated at
|
||||
// the boundaries is used for the interior.
|
||||
|
||||
// The (min < bp) is arbitrary but works better than other heuristics that were tried.
|
||||
averageNeighborBlackPoint :=
|
||||
(blackPoints[y-1][x] + (2 * blackPoints[y][x-1]) + blackPoints[y-1][x-1]) / 4
|
||||
if min < averageNeighborBlackPoint {
|
||||
average = averageNeighborBlackPoint
|
||||
}
|
||||
}
|
||||
}
|
||||
blackPoints[y][x] = average
|
||||
}
|
||||
}
|
||||
return blackPoints
|
||||
}
|
||||
64
vendor/github.com/makiuchi-d/gozxing/inverted_luminance_source.go
generated
vendored
Normal file
64
vendor/github.com/makiuchi-d/gozxing/inverted_luminance_source.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package gozxing
|
||||
|
||||
type InvertedLuminanceSource struct {
|
||||
LuminanceSource
|
||||
}
|
||||
|
||||
func NewInvertedLuminanceSource(delegate LuminanceSource) LuminanceSource {
|
||||
return &InvertedLuminanceSource{delegate}
|
||||
}
|
||||
|
||||
func (this *InvertedLuminanceSource) GetRow(y int, row []byte) ([]byte, error) {
|
||||
var e error
|
||||
row, e = this.LuminanceSource.GetRow(y, row)
|
||||
if e != nil {
|
||||
return row, e
|
||||
}
|
||||
width := this.GetWidth()
|
||||
for i := 0; i < width; i++ {
|
||||
row[i] = 255 - (row[i] & 0xff)
|
||||
}
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func (this *InvertedLuminanceSource) GetMatrix() []byte {
|
||||
matrix := this.LuminanceSource.GetMatrix()
|
||||
length := this.GetWidth() * this.GetHeight()
|
||||
invertedMatrix := make([]byte, length)
|
||||
for i := 0; i < length; i++ {
|
||||
invertedMatrix[i] = 255 - (matrix[i] & 0xff)
|
||||
}
|
||||
return invertedMatrix
|
||||
}
|
||||
|
||||
func (this *InvertedLuminanceSource) Crop(left, top, width, height int) (LuminanceSource, error) {
|
||||
cropped, e := this.LuminanceSource.Crop(left, top, width, height)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return NewInvertedLuminanceSource(cropped), nil
|
||||
}
|
||||
|
||||
func (this *InvertedLuminanceSource) Invert() LuminanceSource {
|
||||
return this.LuminanceSource
|
||||
}
|
||||
|
||||
func (this *InvertedLuminanceSource) RotateCounterClockwise() (LuminanceSource, error) {
|
||||
rotated, e := this.LuminanceSource.RotateCounterClockwise()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return NewInvertedLuminanceSource(rotated), nil
|
||||
}
|
||||
|
||||
func (this *InvertedLuminanceSource) RotateCounterClockwise45() (LuminanceSource, error) {
|
||||
rotated, e := this.LuminanceSource.RotateCounterClockwise45()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return NewInvertedLuminanceSource(rotated), nil
|
||||
}
|
||||
|
||||
func (this *InvertedLuminanceSource) String() string {
|
||||
return LuminanceSourceString(this)
|
||||
}
|
||||
151
vendor/github.com/makiuchi-d/gozxing/luminance_source.go
generated
vendored
Normal file
151
vendor/github.com/makiuchi-d/gozxing/luminance_source.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type LuminanceSource interface {
|
||||
/**
|
||||
* Fetches one row of luminance data from the underlying platform's bitmap. Values range from
|
||||
* 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
|
||||
* to bitwise and with 0xff for each value. It is preferable for implementations of this method
|
||||
* to only fetch this row rather than the whole image, since no 2D Readers may be installed and
|
||||
* getMatrix() may never be called.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0,getHeight())
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* Always use the returned object, and ignore the .length of the array.
|
||||
* @return An array containing the luminance data.
|
||||
*/
|
||||
GetRow(y int, row []byte) ([]byte, error)
|
||||
|
||||
/**
|
||||
* Fetches luminance data for the underlying bitmap. Values should be fetched using:
|
||||
* {@code int luminance = array[y * width + x] & 0xff}
|
||||
*
|
||||
* @return A row-major 2D array of luminance values. Do not use result.length as it may be
|
||||
* larger than width * height bytes on some platforms. Do not modify the contents
|
||||
* of the result.
|
||||
*/
|
||||
GetMatrix() []byte
|
||||
|
||||
/**
|
||||
* @return The width of the bitmap.
|
||||
*/
|
||||
GetWidth() int
|
||||
|
||||
/**
|
||||
* @return The height of the bitmap.
|
||||
*/
|
||||
GetHeight() int
|
||||
|
||||
/**
|
||||
* @return Whether this subclass supports cropping.
|
||||
*/
|
||||
IsCropSupported() bool
|
||||
|
||||
/**
|
||||
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||
*
|
||||
* @param left The left coordinate, which must be in [0,getWidth())
|
||||
* @param top The top coordinate, which must be in [0,getHeight())
|
||||
* @param width The width of the rectangle to crop.
|
||||
* @param height The height of the rectangle to crop.
|
||||
* @return A cropped version of this object.
|
||||
*/
|
||||
Crop(left, top, width, height int) (LuminanceSource, error)
|
||||
|
||||
/**
|
||||
* @return Whether this subclass supports counter-clockwise rotation.
|
||||
*/
|
||||
IsRotateSupported() bool
|
||||
|
||||
/**
|
||||
* @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes
|
||||
* white and vice versa, and each value becomes (255-value).
|
||||
*/
|
||||
Invert() LuminanceSource
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
RotateCounterClockwise() (LuminanceSource, error)
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
RotateCounterClockwise45() (LuminanceSource, error)
|
||||
|
||||
String() string
|
||||
}
|
||||
|
||||
type LuminanceSourceBase struct {
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
func (this *LuminanceSourceBase) GetWidth() int {
|
||||
return this.Width
|
||||
}
|
||||
|
||||
func (this *LuminanceSourceBase) GetHeight() int {
|
||||
return this.Height
|
||||
}
|
||||
|
||||
func (this *LuminanceSourceBase) IsCropSupported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *LuminanceSourceBase) Crop(left, top, width, height int) (LuminanceSource, error) {
|
||||
return nil, errors.New("UnsupportedOperationException: This luminance source does not support cropping")
|
||||
}
|
||||
|
||||
func (this *LuminanceSourceBase) IsRotateSupported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *LuminanceSourceBase) RotateCounterClockwise() (LuminanceSource, error) {
|
||||
return nil, errors.New("UnsupportedOperationException: This luminance source does not support rotation by 90 degrees")
|
||||
}
|
||||
|
||||
func (this *LuminanceSourceBase) RotateCounterClockwise45() (LuminanceSource, error) {
|
||||
return nil, errors.New("UnsupportedOperationException: This luminance source does not support rotation by 45 degrees")
|
||||
}
|
||||
|
||||
func LuminanceSourceInvert(this LuminanceSource) LuminanceSource {
|
||||
return NewInvertedLuminanceSource(this)
|
||||
}
|
||||
|
||||
func LuminanceSourceString(this LuminanceSource) string {
|
||||
width := this.GetWidth()
|
||||
height := this.GetHeight()
|
||||
row := make([]byte, width)
|
||||
result := make([]byte, 0, height*(width+1))
|
||||
|
||||
for y := 0; y < height; y++ {
|
||||
row, _ = this.GetRow(y, row)
|
||||
for x := 0; x < width; x++ {
|
||||
luminance := row[x] & 0xFF
|
||||
var c byte
|
||||
if luminance < 0x40 {
|
||||
c = '#'
|
||||
} else if luminance < 0x80 {
|
||||
c = '+'
|
||||
} else if luminance < 0xC0 {
|
||||
c = '.'
|
||||
} else {
|
||||
c = ' '
|
||||
}
|
||||
result = append(result, c)
|
||||
}
|
||||
result = append(result, '\n')
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
25
vendor/github.com/makiuchi-d/gozxing/not_found_exception.go
generated
vendored
Normal file
25
vendor/github.com/makiuchi-d/gozxing/not_found_exception.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package gozxing
|
||||
|
||||
type NotFoundException interface {
|
||||
ReaderException
|
||||
notFoundException()
|
||||
}
|
||||
|
||||
type notFoundException struct {
|
||||
exception
|
||||
}
|
||||
|
||||
func (notFoundException) readerException() {}
|
||||
func (notFoundException) notFoundException() {}
|
||||
|
||||
func NewNotFoundException(args ...interface{}) NotFoundException {
|
||||
return notFoundException{
|
||||
newException("NotFoundException", args...),
|
||||
}
|
||||
}
|
||||
|
||||
func WrapNotFoundException(e error) NotFoundException {
|
||||
return notFoundException{
|
||||
wrapException("NotFoundException", e),
|
||||
}
|
||||
}
|
||||
142
vendor/github.com/makiuchi-d/gozxing/planar_yuv_luminance_source.go
generated
vendored
Normal file
142
vendor/github.com/makiuchi-d/gozxing/planar_yuv_luminance_source.go
generated
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const thumbnailScaleFactor = 2
|
||||
|
||||
type PlanarYUVLuminanceSource struct {
|
||||
LuminanceSourceBase
|
||||
yuvData []byte
|
||||
dataWidth int
|
||||
dataHeight int
|
||||
left int
|
||||
top int
|
||||
}
|
||||
|
||||
func NewPlanarYUVLuminanceSource(yuvData []byte,
|
||||
dataWidth, dataHeight, left, top, width, height int,
|
||||
reverseHorizontal bool) (LuminanceSource, error) {
|
||||
|
||||
if left+width > dataWidth || top+height > dataHeight {
|
||||
return nil, errors.New("IllegalArgumentException: Crop rectangle does not fit within image data")
|
||||
}
|
||||
|
||||
yuvsrc := &PlanarYUVLuminanceSource{
|
||||
LuminanceSourceBase{width, height},
|
||||
yuvData,
|
||||
dataWidth,
|
||||
dataHeight,
|
||||
left,
|
||||
top,
|
||||
}
|
||||
if reverseHorizontal {
|
||||
yuvsrc.reverseHorizontal(width, height)
|
||||
}
|
||||
return yuvsrc, nil
|
||||
}
|
||||
|
||||
func (this *PlanarYUVLuminanceSource) Invert() LuminanceSource {
|
||||
return LuminanceSourceInvert(this)
|
||||
}
|
||||
|
||||
func (this *PlanarYUVLuminanceSource) String() string {
|
||||
return LuminanceSourceString(this)
|
||||
}
|
||||
|
||||
func (this *PlanarYUVLuminanceSource) GetRow(y int, row []byte) ([]byte, error) {
|
||||
if y < 0 || y >= this.GetHeight() {
|
||||
return nil, errors.Errorf("IllegalArgumentException: Requested row is outside the image: %v", y)
|
||||
}
|
||||
width := this.GetWidth()
|
||||
if row == nil || len(row) < width {
|
||||
row = make([]byte, width)
|
||||
}
|
||||
offset := (y+this.top)*this.dataWidth + this.left
|
||||
copy(row, this.yuvData[offset:offset+width])
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func (this *PlanarYUVLuminanceSource) GetMatrix() []byte {
|
||||
width := this.GetWidth()
|
||||
height := this.GetHeight()
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if width == this.dataWidth && height == this.dataHeight {
|
||||
return this.yuvData
|
||||
}
|
||||
|
||||
area := width * height
|
||||
matrix := make([]byte, area)
|
||||
inputOffset := this.top*this.dataWidth + this.left
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if width == this.dataWidth {
|
||||
copy(matrix, this.yuvData[inputOffset:inputOffset+area])
|
||||
return matrix
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
for y := 0; y < height; y++ {
|
||||
outputOffset := y * width
|
||||
copy(matrix[outputOffset:], this.yuvData[inputOffset:inputOffset+width])
|
||||
inputOffset += this.dataWidth
|
||||
}
|
||||
return matrix
|
||||
}
|
||||
|
||||
func (this *PlanarYUVLuminanceSource) IsCropSupported() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *PlanarYUVLuminanceSource) Crop(left, top, width, height int) (LuminanceSource, error) {
|
||||
return NewPlanarYUVLuminanceSource(
|
||||
this.yuvData,
|
||||
this.dataWidth,
|
||||
this.dataHeight,
|
||||
this.left+left,
|
||||
this.top+top,
|
||||
width,
|
||||
height,
|
||||
false)
|
||||
}
|
||||
|
||||
func (this *PlanarYUVLuminanceSource) RenderThumbnail() []uint {
|
||||
width := this.GetThumbnailWidth()
|
||||
height := this.GetThumbnailHeight()
|
||||
pixels := make([]uint, width*height)
|
||||
yuv := this.yuvData
|
||||
inputOffset := this.top*this.dataWidth + this.left
|
||||
|
||||
for y := 0; y < height; y++ {
|
||||
outputOffset := y * width
|
||||
for x := 0; x < width; x++ {
|
||||
grey := uint(yuv[inputOffset+x*thumbnailScaleFactor]) & 0xff
|
||||
pixels[outputOffset+x] = 0xFF000000 | (grey * 0x00010101)
|
||||
}
|
||||
inputOffset += this.dataWidth * thumbnailScaleFactor
|
||||
}
|
||||
return pixels
|
||||
}
|
||||
|
||||
// GetThumbnailWidth return width of image from {@link #renderThumbnail()}
|
||||
func (this *PlanarYUVLuminanceSource) GetThumbnailWidth() int {
|
||||
return this.GetWidth() / thumbnailScaleFactor
|
||||
}
|
||||
|
||||
// GetThumbnailHeight return height of image from {@link #renderThumbnail()}
|
||||
func (this *PlanarYUVLuminanceSource) GetThumbnailHeight() int {
|
||||
return this.GetHeight() / thumbnailScaleFactor
|
||||
}
|
||||
|
||||
func (this *PlanarYUVLuminanceSource) reverseHorizontal(width, height int) {
|
||||
yuvData := this.yuvData
|
||||
for y, rowStart := 0, this.top*this.dataWidth+this.left; y < height; y, rowStart = y+1, rowStart+this.dataWidth {
|
||||
middle := rowStart + width/2
|
||||
for x1, x2 := rowStart, rowStart+width-1; x1 < middle; x1, x2 = x1+1, x2-1 {
|
||||
yuvData[x1], yuvData[x2] = yuvData[x2], yuvData[x1]
|
||||
}
|
||||
}
|
||||
}
|
||||
206
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/bit_matrix_parser.go
generated
vendored
Normal file
206
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/bit_matrix_parser.go
generated
vendored
Normal file
@ -0,0 +1,206 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
type BitMatrixParser struct {
|
||||
bitMatrix *gozxing.BitMatrix
|
||||
parsedVersion *Version
|
||||
parsedFormatInfo *FormatInformation
|
||||
mirror bool
|
||||
}
|
||||
|
||||
func NewBitMatrixParser(bitMatrix *gozxing.BitMatrix) (*BitMatrixParser, error) {
|
||||
dimension := bitMatrix.GetHeight()
|
||||
if dimension < 21 || (dimension&0x03) != 1 {
|
||||
return nil, gozxing.NewFormatException("dimension = %v", dimension)
|
||||
}
|
||||
return &BitMatrixParser{bitMatrix: bitMatrix}, nil
|
||||
}
|
||||
|
||||
func (this *BitMatrixParser) ReadFormatInformation() (*FormatInformation, error) {
|
||||
if this.parsedFormatInfo != nil {
|
||||
return this.parsedFormatInfo, nil
|
||||
}
|
||||
|
||||
// Read top-left format info bits
|
||||
formatInfoBits1 := 0
|
||||
for i := 0; i < 6; i++ {
|
||||
formatInfoBits1 = this.copyBit(i, 8, formatInfoBits1)
|
||||
}
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
formatInfoBits1 = this.copyBit(7, 8, formatInfoBits1)
|
||||
formatInfoBits1 = this.copyBit(8, 8, formatInfoBits1)
|
||||
formatInfoBits1 = this.copyBit(8, 7, formatInfoBits1)
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
for j := 5; j >= 0; j-- {
|
||||
formatInfoBits1 = this.copyBit(8, j, formatInfoBits1)
|
||||
}
|
||||
|
||||
// Read the top-right/bottom-left pattern too
|
||||
dimension := this.bitMatrix.GetHeight()
|
||||
formatInfoBits2 := 0
|
||||
jMin := dimension - 7
|
||||
for j := dimension - 1; j >= jMin; j-- {
|
||||
formatInfoBits2 = this.copyBit(8, j, formatInfoBits2)
|
||||
}
|
||||
for i := dimension - 8; i < dimension; i++ {
|
||||
formatInfoBits2 = this.copyBit(i, 8, formatInfoBits2)
|
||||
}
|
||||
|
||||
this.parsedFormatInfo = FormatInformation_DecodeFormatInformation(uint(formatInfoBits1), uint(formatInfoBits2))
|
||||
if this.parsedFormatInfo != nil {
|
||||
return this.parsedFormatInfo, nil
|
||||
}
|
||||
return nil, gozxing.NewFormatException("failed to parse format info")
|
||||
}
|
||||
|
||||
func (this *BitMatrixParser) ReadVersion() (*Version, error) {
|
||||
if this.parsedVersion != nil {
|
||||
return this.parsedVersion, nil
|
||||
}
|
||||
|
||||
dimension := this.bitMatrix.GetHeight()
|
||||
|
||||
provisionalVersion := (dimension - 17) / 4
|
||||
if provisionalVersion <= 6 {
|
||||
return Version_GetVersionForNumber(provisionalVersion)
|
||||
}
|
||||
|
||||
// Read top-right version info: 3 wide by 6 tall
|
||||
versionBits := 0
|
||||
ijMin := dimension - 11
|
||||
for j := 5; j >= 0; j-- {
|
||||
for i := dimension - 9; i >= ijMin; i-- {
|
||||
versionBits = this.copyBit(i, j, versionBits)
|
||||
}
|
||||
}
|
||||
theParsedVersion, e := Version_decodeVersionInformation(versionBits)
|
||||
if e == nil && theParsedVersion != nil && theParsedVersion.GetDimensionForVersion() == dimension {
|
||||
this.parsedVersion = theParsedVersion
|
||||
return theParsedVersion, nil
|
||||
}
|
||||
|
||||
// Hmm, failed. Try bottom left: 6 wide by 3 tall
|
||||
versionBits = 0
|
||||
for i := 5; i >= 0; i-- {
|
||||
for j := dimension - 9; j >= ijMin; j-- {
|
||||
versionBits = this.copyBit(i, j, versionBits)
|
||||
}
|
||||
}
|
||||
theParsedVersion, e = Version_decodeVersionInformation(versionBits)
|
||||
if e == nil && theParsedVersion != nil && theParsedVersion.GetDimensionForVersion() == dimension {
|
||||
this.parsedVersion = theParsedVersion
|
||||
return theParsedVersion, nil
|
||||
}
|
||||
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
|
||||
func (this *BitMatrixParser) copyBit(i, j, versionBits int) int {
|
||||
var bit bool
|
||||
if this.mirror {
|
||||
bit = this.bitMatrix.Get(j, i)
|
||||
} else {
|
||||
bit = this.bitMatrix.Get(i, j)
|
||||
}
|
||||
if bit {
|
||||
return (versionBits << 1) | 0x1
|
||||
}
|
||||
return versionBits << 1
|
||||
}
|
||||
|
||||
func (this *BitMatrixParser) ReadCodewords() ([]byte, error) {
|
||||
|
||||
formatInfo, e := this.ReadFormatInformation()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
version, e := this.ReadVersion()
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
|
||||
// Get the data mask for the format used in this QR Code. This will exclude
|
||||
// some bits from reading as we wind through the bit matrix.
|
||||
dataMask := DataMaskValues[formatInfo.GetDataMask()]
|
||||
dimension := this.bitMatrix.GetHeight()
|
||||
dataMask.UnmaskBitMatrix(this.bitMatrix, dimension)
|
||||
|
||||
functionPattern, e := version.buildFunctionPattern()
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
|
||||
readingUp := true
|
||||
result := make([]byte, version.GetTotalCodewords())
|
||||
resultOffset := 0
|
||||
currentByte := 0
|
||||
bitsRead := 0
|
||||
// Read columns in pairs, from right to left
|
||||
for j := dimension - 1; j > 0; j -= 2 {
|
||||
if j == 6 {
|
||||
// Skip whole column with vertical alignment pattern;
|
||||
// saves time and makes the other code proceed more cleanly
|
||||
j--
|
||||
}
|
||||
// Read alternatingly from bottom to top then top to bottom
|
||||
for count := 0; count < dimension; count++ {
|
||||
i := count
|
||||
if readingUp {
|
||||
i = dimension - 1 - count
|
||||
}
|
||||
for col := 0; col < 2; col++ {
|
||||
// Ignore bits covered by the function pattern
|
||||
if !functionPattern.Get(j-col, i) {
|
||||
// Read a bit
|
||||
bitsRead++
|
||||
currentByte <<= 1
|
||||
if this.bitMatrix.Get(j-col, i) {
|
||||
currentByte |= 1
|
||||
}
|
||||
// If we've made a whole byte, save it off
|
||||
if bitsRead == 8 {
|
||||
result[resultOffset] = byte(currentByte)
|
||||
resultOffset++
|
||||
bitsRead = 0
|
||||
currentByte = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
readingUp = !readingUp // readingUp ^= true; // switch directions
|
||||
}
|
||||
if resultOffset != version.GetTotalCodewords() {
|
||||
return nil, gozxing.NewFormatException(
|
||||
"resultOffset=%v, totalCodeWords=%v", resultOffset, version.GetTotalCodewords())
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (this *BitMatrixParser) Remask() {
|
||||
if this.parsedFormatInfo == nil {
|
||||
return // We have no format information, and have no data mask
|
||||
}
|
||||
dataMask := DataMaskValues[this.parsedFormatInfo.GetDataMask()]
|
||||
dimension := this.bitMatrix.GetHeight()
|
||||
dataMask.UnmaskBitMatrix(this.bitMatrix, dimension)
|
||||
}
|
||||
|
||||
func (this *BitMatrixParser) SetMirror(mirror bool) {
|
||||
this.parsedVersion = nil
|
||||
this.parsedFormatInfo = nil
|
||||
this.mirror = mirror
|
||||
}
|
||||
|
||||
func (this *BitMatrixParser) Mirror() {
|
||||
for x := 0; x < this.bitMatrix.GetWidth(); x++ {
|
||||
for y := x + 1; y < this.bitMatrix.GetHeight(); y++ {
|
||||
if this.bitMatrix.Get(x, y) != this.bitMatrix.Get(y, x) {
|
||||
this.bitMatrix.Flip(y, x)
|
||||
this.bitMatrix.Flip(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
98
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/data_block.go
generated
vendored
Normal file
98
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/data_block.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type DataBlock struct {
|
||||
numDataCodewords int
|
||||
codewords []byte
|
||||
}
|
||||
|
||||
func NewDataBlock(numDataCodewords int, codewords []byte) *DataBlock {
|
||||
return &DataBlock{
|
||||
numDataCodewords: numDataCodewords,
|
||||
codewords: codewords,
|
||||
}
|
||||
}
|
||||
|
||||
func DataBlock_GetDataBlocks(rawCodewords []byte, version *Version, ecLevel ErrorCorrectionLevel) ([]*DataBlock, error) {
|
||||
if len(rawCodewords) != version.GetTotalCodewords() {
|
||||
return nil, errors.Errorf(
|
||||
"IllegalArgumentException: len(rawCodewords)=%v, totalCodewords=%v",
|
||||
len(rawCodewords), version.GetTotalCodewords())
|
||||
}
|
||||
|
||||
// Figure out the number and size of data blocks used by this version and
|
||||
// error correction level
|
||||
ecBlocks := version.GetECBlocksForLevel(ecLevel)
|
||||
|
||||
// First count the total number of data blocks
|
||||
totalBlocks := 0
|
||||
ecBlockArray := ecBlocks.GetECBlocks()
|
||||
for _, ecBlock := range ecBlockArray {
|
||||
totalBlocks += ecBlock.GetCount()
|
||||
}
|
||||
|
||||
// Now establish DataBlocks of the appropriate size and number of data codewords
|
||||
result := make([]*DataBlock, totalBlocks)
|
||||
numResultBlocks := 0
|
||||
for _, ecBlock := range ecBlockArray {
|
||||
for i := 0; i < ecBlock.GetCount(); i++ {
|
||||
numDataCodewords := ecBlock.GetDataCodewords()
|
||||
numBlockCodewords := ecBlocks.GetECCodewordsPerBlock() + numDataCodewords
|
||||
result[numResultBlocks] = NewDataBlock(numDataCodewords, make([]byte, numBlockCodewords))
|
||||
numResultBlocks++
|
||||
}
|
||||
}
|
||||
|
||||
// All blocks have the same amount of data, except that the last n
|
||||
// (where n may be 0) have 1 more byte. Figure out where these start.
|
||||
shorterBlocksTotalCodewords := len(result[0].codewords)
|
||||
longerBlocksStartAt := len(result) - 1
|
||||
for longerBlocksStartAt >= 0 {
|
||||
numCodewords := len(result[longerBlocksStartAt].codewords)
|
||||
if numCodewords == shorterBlocksTotalCodewords {
|
||||
break
|
||||
}
|
||||
longerBlocksStartAt--
|
||||
}
|
||||
longerBlocksStartAt++
|
||||
|
||||
shorterBlocksNumDataCodewords := shorterBlocksTotalCodewords - ecBlocks.GetECCodewordsPerBlock()
|
||||
// The last elements of result may be 1 element longer;
|
||||
// first fill out as many elements as all of them have
|
||||
rawCodewordsOffset := 0
|
||||
for i := 0; i < shorterBlocksNumDataCodewords; i++ {
|
||||
for j := 0; j < numResultBlocks; j++ {
|
||||
result[j].codewords[i] = rawCodewords[rawCodewordsOffset]
|
||||
rawCodewordsOffset++
|
||||
}
|
||||
}
|
||||
// Fill out the last data block in the longer ones
|
||||
for j := longerBlocksStartAt; j < numResultBlocks; j++ {
|
||||
result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset]
|
||||
rawCodewordsOffset++
|
||||
}
|
||||
// Now add in error correction blocks
|
||||
max := len(result[0].codewords)
|
||||
for i := shorterBlocksNumDataCodewords; i < max; i++ {
|
||||
for j := 0; j < numResultBlocks; j++ {
|
||||
iOffset := i
|
||||
if j >= longerBlocksStartAt {
|
||||
iOffset = i + 1
|
||||
}
|
||||
result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset]
|
||||
rawCodewordsOffset++
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (this *DataBlock) GetNumDataCodewords() int {
|
||||
return this.numDataCodewords
|
||||
}
|
||||
|
||||
func (this *DataBlock) GetCodewords() []byte {
|
||||
return this.codewords
|
||||
}
|
||||
99
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/data_mask.go
generated
vendored
Normal file
99
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/data_mask.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
var DataMaskValues = []DataMask{
|
||||
|
||||
// See ISO 18004:2006 6.8.1
|
||||
|
||||
/**
|
||||
* 000: mask bits for which (x + y) mod 2 == 0
|
||||
*/
|
||||
{ // DATA_MASK_000
|
||||
func(i, j int) bool {
|
||||
return ((i + j) & 0x01) == 0
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 001: mask bits for which x mod 2 == 0
|
||||
*/
|
||||
{ // DATA_MASK_001
|
||||
func(i, j int) bool {
|
||||
return (i & 0x01) == 0
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 010: mask bits for which y mod 3 == 0
|
||||
*/
|
||||
{ // DATA_MASK_010
|
||||
func(i, j int) bool {
|
||||
return j%3 == 0
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 011: mask bits for which (x + y) mod 3 == 0
|
||||
*/
|
||||
{ // DATA_MASK_011
|
||||
func(i, j int) bool {
|
||||
return (i+j)%3 == 0
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 100: mask bits for which (x/2 + y/3) mod 2 == 0
|
||||
*/
|
||||
{ // DATA_MASK_100
|
||||
func(i, j int) bool {
|
||||
return (((i / 2) + (j / 3)) & 0x01) == 0
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 101: mask bits for which xy mod 2 + xy mod 3 == 0
|
||||
* equivalently, such that xy mod 6 == 0
|
||||
*/
|
||||
{ // DATA_MASK_101
|
||||
func(i, j int) bool {
|
||||
return (i*j)%6 == 0
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
|
||||
* equivalently, such that xy mod 6 < 3
|
||||
*/
|
||||
{ // DATA_MASK_110
|
||||
func(i, j int) bool {
|
||||
return ((i * j) % 6) < 3
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
|
||||
* equivalently, such that (x + y + xy mod 3) mod 2 == 0
|
||||
*/
|
||||
{ // DATA_MASK_111
|
||||
func(i, j int) bool {
|
||||
return ((i + j + ((i * j) % 3)) & 0x01) == 0
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type DataMask struct {
|
||||
isMasked func(i, j int) bool
|
||||
}
|
||||
|
||||
func (this DataMask) UnmaskBitMatrix(bits *gozxing.BitMatrix, dimension int) {
|
||||
for i := 0; i < dimension; i++ {
|
||||
for j := 0; j < dimension; j++ {
|
||||
if this.isMasked(i, j) {
|
||||
bits.Flip(j, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
395
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/decoded_bit_stream_parser.go
generated
vendored
Normal file
395
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/decoded_bit_stream_parser.go
generated
vendored
Normal file
@ -0,0 +1,395 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/transform"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
"github.com/makiuchi-d/gozxing/common"
|
||||
)
|
||||
|
||||
const GB2312_SUBSET = 1
|
||||
|
||||
var ALPHANUMERIC_CHARS = []byte("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")
|
||||
|
||||
func DecodedBitStreamParser_Decode(
|
||||
bytes []byte, version *Version, ecLevel ErrorCorrectionLevel,
|
||||
hints map[gozxing.DecodeHintType]interface{}) (*common.DecoderResult, error) {
|
||||
|
||||
bits := common.NewBitSource(bytes)
|
||||
result := make([]byte, 0, 50)
|
||||
byteSegments := make([][]byte, 0, 1)
|
||||
symbolSequence := -1
|
||||
parityData := -1
|
||||
symbologyModifier := 0
|
||||
|
||||
var currentCharacterSetECI *common.CharacterSetECI
|
||||
fc1InEffect := false
|
||||
hasFNC1first := false
|
||||
hasFNC1second := false
|
||||
var mode *Mode
|
||||
var e error
|
||||
|
||||
for {
|
||||
// While still another segment to read...
|
||||
if bits.Available() < 4 {
|
||||
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
||||
mode = Mode_TERMINATOR
|
||||
} else {
|
||||
bit4, _ := bits.ReadBits(4) // mode is encoded by 4 bits
|
||||
mode, e = ModeForBits(bit4)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
}
|
||||
switch mode {
|
||||
case Mode_TERMINATOR:
|
||||
case Mode_FNC1_FIRST_POSITION:
|
||||
hasFNC1first = true // symbology detection
|
||||
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
||||
fc1InEffect = true
|
||||
case Mode_FNC1_SECOND_POSITION:
|
||||
hasFNC1second = true // symbology detection
|
||||
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
||||
fc1InEffect = true
|
||||
case Mode_STRUCTURED_APPEND:
|
||||
// sequence number and parity is added later to the result metadata
|
||||
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
|
||||
symbolSequence, e = bits.ReadBits(8)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
parityData, e = bits.ReadBits(8)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
case Mode_ECI:
|
||||
// Count doesn't apply to ECI
|
||||
value, e := DecodedBitStreamParser_parseECIValue(bits)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
currentCharacterSetECI, e = common.GetCharacterSetECIByValue(value)
|
||||
if e != nil || currentCharacterSetECI == nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
case Mode_HANZI:
|
||||
// First handle Hanzi mode which does not start with character count
|
||||
// Chinese mode contains a sub set indicator right after mode indicator
|
||||
subset, e := bits.ReadBits(4)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
countHanzi, e := bits.ReadBits(mode.GetCharacterCountBits(version))
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
if subset == GB2312_SUBSET {
|
||||
result, e = DecodedBitStreamParser_decodeHanziSegment(bits, result, countHanzi)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
default:
|
||||
// "Normal" QR code modes:
|
||||
// How many characters will follow, encoded in this mode?
|
||||
count, e := bits.ReadBits(mode.GetCharacterCountBits(version))
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
switch mode {
|
||||
case Mode_NUMERIC:
|
||||
result, e = DecodedBitStreamParser_decodeNumericSegment(bits, result, count)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
case Mode_ALPHANUMERIC:
|
||||
result, e = DecodedBitStreamParser_decodeAlphanumericSegment(bits, result, count, fc1InEffect)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
case Mode_BYTE:
|
||||
result, byteSegments, e = DecodedBitStreamParser_decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
case Mode_KANJI:
|
||||
result, e = DecodedBitStreamParser_decodeKanjiSegment(bits, result, count)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
default:
|
||||
return nil, gozxing.NewFormatException("Unknown mode")
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if mode == Mode_TERMINATOR {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if currentCharacterSetECI != nil {
|
||||
if hasFNC1first {
|
||||
symbologyModifier = 4
|
||||
} else if hasFNC1second {
|
||||
symbologyModifier = 6
|
||||
} else {
|
||||
symbologyModifier = 2
|
||||
}
|
||||
} else {
|
||||
if hasFNC1first {
|
||||
symbologyModifier = 3
|
||||
} else if hasFNC1second {
|
||||
symbologyModifier = 5
|
||||
} else {
|
||||
symbologyModifier = 1
|
||||
}
|
||||
}
|
||||
|
||||
if len(byteSegments) == 0 {
|
||||
byteSegments = nil
|
||||
}
|
||||
return common.NewDecoderResultWithParams(bytes,
|
||||
string(result),
|
||||
byteSegments,
|
||||
ecLevel.String(),
|
||||
symbolSequence,
|
||||
parityData,
|
||||
symbologyModifier), nil
|
||||
}
|
||||
|
||||
func DecodedBitStreamParser_decodeHanziSegment(bits *common.BitSource, result []byte, count int) ([]byte, error) {
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if count*13 > bits.Available() {
|
||||
return result, gozxing.NewFormatException("bits.Available() = %v", bits.Available())
|
||||
}
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as GB2312 afterwards
|
||||
buffer := make([]byte, 2*count)
|
||||
offset := 0
|
||||
for count > 0 {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
twoBytes, _ := bits.ReadBits(13)
|
||||
assembledTwoBytes := ((twoBytes / 0x060) << 8) | (twoBytes % 0x060)
|
||||
if assembledTwoBytes < 0x00a00 {
|
||||
// In the 0xA1A1 to 0xAAFE range
|
||||
assembledTwoBytes += 0x0A1A1
|
||||
} else {
|
||||
// In the 0xB0A1 to 0xFAFE range
|
||||
assembledTwoBytes += 0x0A6A1
|
||||
}
|
||||
buffer[offset] = (byte)((assembledTwoBytes >> 8) & 0xFF)
|
||||
buffer[offset+1] = (byte)(assembledTwoBytes & 0xFF)
|
||||
offset += 2
|
||||
count--
|
||||
}
|
||||
|
||||
dec := common.StringUtils_GB2312_CHARSET.NewDecoder()
|
||||
result, _, e := transform.Append(dec, result, buffer[:offset])
|
||||
if e != nil {
|
||||
return result, gozxing.WrapFormatException(e)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func DecodedBitStreamParser_decodeKanjiSegment(bits *common.BitSource, result []byte, count int) ([]byte, error) {
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if count*13 > bits.Available() {
|
||||
return result, gozxing.NewFormatException("bits.Available() = %v", bits.Available())
|
||||
}
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as Shift_JIS afterwards
|
||||
buffer := make([]byte, 2*count)
|
||||
offset := 0
|
||||
for count > 0 {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
twoBytes, _ := bits.ReadBits(13)
|
||||
assembledTwoBytes := ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0)
|
||||
if assembledTwoBytes < 0x01F00 {
|
||||
// In the 0x8140 to 0x9FFC range
|
||||
assembledTwoBytes += 0x08140
|
||||
} else {
|
||||
// In the 0xE040 to 0xEBBF range
|
||||
assembledTwoBytes += 0x0C140
|
||||
}
|
||||
buffer[offset] = byte(assembledTwoBytes >> 8)
|
||||
buffer[offset+1] = byte(assembledTwoBytes)
|
||||
offset += 2
|
||||
count--
|
||||
}
|
||||
|
||||
// Shift_JIS may not be supported in some environments:
|
||||
dec := common.StringUtils_SHIFT_JIS_CHARSET.NewDecoder()
|
||||
result, _, e := transform.Append(dec, result, buffer[:offset])
|
||||
if e != nil {
|
||||
return result, gozxing.WrapFormatException(e)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func DecodedBitStreamParser_decodeByteSegment(bits *common.BitSource,
|
||||
result []byte, count int, currentCharacterSetECI *common.CharacterSetECI,
|
||||
byteSegments [][]byte, hints map[gozxing.DecodeHintType]interface{}) ([]byte, [][]byte, error) {
|
||||
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if 8*count > bits.Available() {
|
||||
return result, byteSegments, gozxing.NewFormatException("bits.Available = %v", bits.Available())
|
||||
}
|
||||
|
||||
readBytes := make([]byte, count)
|
||||
for i := 0; i < count; i++ {
|
||||
b, _ := bits.ReadBits(8)
|
||||
readBytes[i] = byte(b)
|
||||
}
|
||||
|
||||
var encoding encoding.Encoding
|
||||
if currentCharacterSetECI == nil {
|
||||
// The spec isn't clear on this mode; see
|
||||
// section 6.4.5: t does not say which encoding to assuming
|
||||
// upon decoding. I have seen ISO-8859-1 used as well as
|
||||
// Shift_JIS -- without anything like an ECI designator to
|
||||
// give a hint.
|
||||
var err error
|
||||
encoding, err = common.StringUtils_guessCharset(readBytes, hints)
|
||||
if err != nil {
|
||||
return nil, nil, gozxing.WrapFormatException(err)
|
||||
}
|
||||
} else {
|
||||
encoding = currentCharacterSetECI.GetCharset()
|
||||
}
|
||||
|
||||
dec := encoding.NewDecoder()
|
||||
result, _, e := transform.Append(dec, result, readBytes)
|
||||
if e != nil {
|
||||
return result, byteSegments, gozxing.WrapFormatException(e)
|
||||
}
|
||||
|
||||
byteSegments = append(byteSegments, readBytes)
|
||||
return result, byteSegments, nil
|
||||
}
|
||||
|
||||
func toAlphaNumericChar(value int) (byte, error) {
|
||||
if value >= len(ALPHANUMERIC_CHARS) {
|
||||
return 0, gozxing.NewFormatException("%v >= len(ALPHANUMERIC_CHARS)", value)
|
||||
}
|
||||
return ALPHANUMERIC_CHARS[value], nil
|
||||
}
|
||||
|
||||
func DecodedBitStreamParser_decodeAlphanumericSegment(bits *common.BitSource, result []byte, count int, fc1InEffect bool) ([]byte, error) {
|
||||
// Read two characters at a time
|
||||
start := len(result)
|
||||
for count > 1 {
|
||||
nextTwoCharsBits, e := bits.ReadBits(11)
|
||||
if e != nil {
|
||||
return result, gozxing.WrapFormatException(e)
|
||||
}
|
||||
char, e := toAlphaNumericChar(nextTwoCharsBits / 45)
|
||||
if e != nil {
|
||||
return result, gozxing.WrapFormatException(e)
|
||||
}
|
||||
result = append(result, char)
|
||||
char, _ = toAlphaNumericChar(nextTwoCharsBits % 45)
|
||||
result = append(result, char)
|
||||
count -= 2
|
||||
}
|
||||
if count == 1 {
|
||||
// special case: one character left
|
||||
nextCharBits, e := bits.ReadBits(6)
|
||||
if e != nil {
|
||||
return result, gozxing.WrapFormatException(e)
|
||||
}
|
||||
char, e := toAlphaNumericChar(nextCharBits)
|
||||
if e != nil {
|
||||
return result, gozxing.WrapFormatException(e)
|
||||
}
|
||||
result = append(result, char)
|
||||
}
|
||||
// See section 6.4.8.1, 6.4.8.2
|
||||
if fc1InEffect {
|
||||
// We need to massage the result a bit if in an FNC1 mode:
|
||||
for i := start; i < len(result); i++ {
|
||||
if result[i] == '%' {
|
||||
if i < len(result)-1 && result[i+1] == '%' {
|
||||
// %% is rendered as %
|
||||
result = append(result[:i], result[i+1:]...)
|
||||
} else {
|
||||
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
||||
result[i] = byte(0x1D)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func DecodedBitStreamParser_decodeNumericSegment(bits *common.BitSource, result []byte, count int) ([]byte, error) {
|
||||
// Read three digits at a time
|
||||
for count >= 3 {
|
||||
// Each 10 bits encodes three digits
|
||||
threeDigitsBits, e := bits.ReadBits(10)
|
||||
if e != nil {
|
||||
return result, gozxing.WrapFormatException(e)
|
||||
}
|
||||
if threeDigitsBits >= 1000 {
|
||||
return result, gozxing.NewFormatException("threeDigitalBits = %v", threeDigitsBits)
|
||||
}
|
||||
result = append(result, byte('0'+(threeDigitsBits/100)))
|
||||
result = append(result, byte('0'+((threeDigitsBits/10)%10)))
|
||||
result = append(result, byte('0'+(threeDigitsBits%10)))
|
||||
count -= 3
|
||||
}
|
||||
if count == 2 {
|
||||
// Two digits left over to read, encoded in 7 bits
|
||||
twoDigitsBits, e := bits.ReadBits(7)
|
||||
if e != nil {
|
||||
return result, gozxing.WrapFormatException(e)
|
||||
}
|
||||
if twoDigitsBits >= 100 {
|
||||
return result, gozxing.NewFormatException("twoDigitsBits = %v", twoDigitsBits)
|
||||
}
|
||||
result = append(result, byte('0'+(twoDigitsBits/10)))
|
||||
result = append(result, byte('0'+(twoDigitsBits%10)))
|
||||
} else if count == 1 {
|
||||
// One digit left over to read
|
||||
digitBits, e := bits.ReadBits(4)
|
||||
if e != nil {
|
||||
return result, gozxing.WrapFormatException(e)
|
||||
}
|
||||
if digitBits >= 10 {
|
||||
return result, gozxing.NewFormatException("digitBits = %v", digitBits)
|
||||
}
|
||||
result = append(result, byte('0'+digitBits))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func DecodedBitStreamParser_parseECIValue(bits *common.BitSource) (int, error) {
|
||||
firstByte, e := bits.ReadBits(8)
|
||||
if e != nil {
|
||||
return -1, gozxing.WrapFormatException(e)
|
||||
}
|
||||
if (firstByte & 0x80) == 0 {
|
||||
// just one byte
|
||||
return firstByte & 0x7F, nil
|
||||
}
|
||||
if (firstByte & 0xC0) == 0x80 {
|
||||
// two bytes
|
||||
secondByte, e := bits.ReadBits(8)
|
||||
if e != nil {
|
||||
return -1, gozxing.WrapFormatException(e)
|
||||
}
|
||||
return ((firstByte & 0x3F) << 8) | secondByte, nil
|
||||
}
|
||||
if (firstByte & 0xE0) == 0xC0 {
|
||||
// three bytes
|
||||
secondThirdBytes, e := bits.ReadBits(16)
|
||||
if e != nil {
|
||||
return -1, gozxing.WrapFormatException(e)
|
||||
}
|
||||
return ((firstByte & 0x1F) << 16) | secondThirdBytes, nil
|
||||
}
|
||||
return -1, gozxing.NewFormatException()
|
||||
}
|
||||
168
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/decoder.go
generated
vendored
Normal file
168
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/decoder.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
"github.com/makiuchi-d/gozxing/common"
|
||||
"github.com/makiuchi-d/gozxing/common/reedsolomon"
|
||||
)
|
||||
|
||||
type Decoder struct {
|
||||
rsDecoder *reedsolomon.ReedSolomonDecoder
|
||||
}
|
||||
|
||||
func NewDecoder() *Decoder {
|
||||
return &Decoder{
|
||||
rsDecoder: reedsolomon.NewReedSolomonDecoder(reedsolomon.GenericGF_QR_CODE_FIELD_256),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Decoder) DecodeBoolMapWithoutHint(image [][]bool) (*common.DecoderResult, error) {
|
||||
return this.DecodeBoolMap(image, nil)
|
||||
}
|
||||
|
||||
func (this *Decoder) DecodeBoolMap(image [][]bool, hints map[gozxing.DecodeHintType]interface{}) (*common.DecoderResult, error) {
|
||||
bits, e := gozxing.ParseBoolMapToBitMatrix(image)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return this.Decode(bits, hints)
|
||||
}
|
||||
|
||||
func (this *Decoder) DecodeWithoutHint(bits *gozxing.BitMatrix) (*common.DecoderResult, error) {
|
||||
return this.Decode(bits, nil)
|
||||
}
|
||||
|
||||
func (this *Decoder) Decode(bits *gozxing.BitMatrix, hints map[gozxing.DecodeHintType]interface{}) (*common.DecoderResult, error) {
|
||||
|
||||
// Construct a parser and read version, error-correction level
|
||||
parser, e := NewBitMatrixParser(bits)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
var fece gozxing.ReaderException
|
||||
|
||||
result, e := this.decode(parser, hints)
|
||||
if e == nil {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
switch e.(type) {
|
||||
case gozxing.FormatException, gozxing.ChecksumException:
|
||||
fece = e.(gozxing.ReaderException)
|
||||
}
|
||||
e = nil
|
||||
|
||||
// Revert the bit matrix
|
||||
parser.Remask()
|
||||
|
||||
// Will be attempting a mirrored reading of the version and format info.
|
||||
parser.SetMirror(true)
|
||||
|
||||
if e == nil {
|
||||
// Preemptively read the version.
|
||||
_, e = parser.ReadVersion()
|
||||
}
|
||||
|
||||
if e == nil {
|
||||
// Preemptively read the format information.
|
||||
_, e = parser.ReadFormatInformation()
|
||||
}
|
||||
|
||||
if e == nil {
|
||||
/*
|
||||
* Since we're here, this means we have successfully detected some kind
|
||||
* of version and format information when mirrored. This is a good sign,
|
||||
* that the QR code may be mirrored, and we should try once more with a
|
||||
* mirrored content.
|
||||
*/
|
||||
// Prepare for a mirrored reading.
|
||||
parser.Mirror()
|
||||
}
|
||||
|
||||
if e == nil {
|
||||
result, e = this.decode(parser, hints)
|
||||
}
|
||||
|
||||
if e == nil {
|
||||
// Success! Notify the caller that the code was mirrored.
|
||||
result.SetOther(NewQRCodeDecoderMetaData(true))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// `e` is not nil
|
||||
switch e.(type) {
|
||||
case gozxing.FormatException, gozxing.ChecksumException:
|
||||
// Throw the exception from the original reading
|
||||
return nil, fece
|
||||
default:
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Decoder) decode(parser *BitMatrixParser, hints map[gozxing.DecodeHintType]interface{}) (*common.DecoderResult, error) {
|
||||
version, e := parser.ReadVersion()
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
formatinfo, e := parser.ReadFormatInformation()
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
ecLevel := formatinfo.GetErrorCorrectionLevel()
|
||||
|
||||
// Read codewords
|
||||
codewords, e := parser.ReadCodewords()
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
// Separate into data blocks
|
||||
dataBlocks, e := DataBlock_GetDataBlocks(codewords, version, ecLevel)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
|
||||
// Count total number of data bytes
|
||||
totalBytes := 0
|
||||
for _, dataBlock := range dataBlocks {
|
||||
totalBytes += dataBlock.GetNumDataCodewords()
|
||||
}
|
||||
resultBytes := make([]byte, totalBytes)
|
||||
resultOffset := 0
|
||||
|
||||
// Error-correct and copy data blocks together into a stream of bytes
|
||||
for _, dataBlock := range dataBlocks {
|
||||
codewordBytes := dataBlock.GetCodewords()
|
||||
numDataCodewords := dataBlock.GetNumDataCodewords()
|
||||
e := this.correctErrors(codewordBytes, numDataCodewords)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
for i := 0; i < numDataCodewords; i++ {
|
||||
resultBytes[resultOffset] = codewordBytes[i]
|
||||
resultOffset++
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the contents of that stream of bytes
|
||||
return DecodedBitStreamParser_Decode(resultBytes, version, ecLevel, hints)
|
||||
}
|
||||
|
||||
func (this *Decoder) correctErrors(codewordBytes []byte, numDataCodewords int) error {
|
||||
numCodewords := len(codewordBytes)
|
||||
// First read into an array of ints
|
||||
codewordsInts := make([]int, numCodewords)
|
||||
for i := 0; i < numCodewords; i++ {
|
||||
codewordsInts[i] = int(codewordBytes[i] & 0xFF)
|
||||
}
|
||||
|
||||
e := this.rsDecoder.Decode(codewordsInts, numCodewords-numDataCodewords)
|
||||
if e != nil {
|
||||
return gozxing.WrapChecksumException(e)
|
||||
}
|
||||
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||
// We don't care about errors in the error-correction codewords
|
||||
for i := 0; i < numDataCodewords; i++ {
|
||||
codewordBytes[i] = byte(codewordsInts[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
61
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/error_correction_level.go
generated
vendored
Normal file
61
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/error_correction_level.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type ErrorCorrectionLevel int
|
||||
|
||||
const (
|
||||
ErrorCorrectionLevel_L ErrorCorrectionLevel = 0x01 // ~7% correction
|
||||
ErrorCorrectionLevel_M ErrorCorrectionLevel = 0x00 // ~15% correction
|
||||
ErrorCorrectionLevel_Q ErrorCorrectionLevel = 0x03 // ~25% correction
|
||||
ErrorCorrectionLevel_H ErrorCorrectionLevel = 0x02 // ~30% correction
|
||||
)
|
||||
|
||||
func ErrorCorrectionLevel_ForBits(bits uint) (ErrorCorrectionLevel, error) {
|
||||
switch bits {
|
||||
case 0:
|
||||
return ErrorCorrectionLevel_M, nil
|
||||
case 1:
|
||||
return ErrorCorrectionLevel_L, nil
|
||||
case 2:
|
||||
return ErrorCorrectionLevel_H, nil
|
||||
case 3:
|
||||
return ErrorCorrectionLevel_Q, nil
|
||||
}
|
||||
return -1, errors.New("IllegalArgumentException")
|
||||
}
|
||||
|
||||
func (e ErrorCorrectionLevel) GetBits() int {
|
||||
return int(e)
|
||||
}
|
||||
|
||||
func (e ErrorCorrectionLevel) String() string {
|
||||
switch e {
|
||||
case ErrorCorrectionLevel_M:
|
||||
return "M"
|
||||
case ErrorCorrectionLevel_L:
|
||||
return "L"
|
||||
case ErrorCorrectionLevel_H:
|
||||
return "H"
|
||||
case ErrorCorrectionLevel_Q:
|
||||
return "Q"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ErrorCorrectionLevel_ValueOf(s string) (ErrorCorrectionLevel, error) {
|
||||
switch s {
|
||||
case "M":
|
||||
return ErrorCorrectionLevel_M, nil
|
||||
case "L":
|
||||
return ErrorCorrectionLevel_L, nil
|
||||
case "H":
|
||||
return ErrorCorrectionLevel_H, nil
|
||||
case "Q":
|
||||
return ErrorCorrectionLevel_Q, nil
|
||||
default:
|
||||
return -1, errors.Errorf("IllegalArgumentException: ErrorCorrectionLevel %v", s)
|
||||
}
|
||||
}
|
||||
106
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/format_information.go
generated
vendored
Normal file
106
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/format_information.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
var formatInfoMaskQR = uint(0x5412)
|
||||
var formatInfoDecodeLookup = [][]uint{
|
||||
{0x5412, 0x00},
|
||||
{0x5125, 0x01},
|
||||
{0x5E7C, 0x02},
|
||||
{0x5B4B, 0x03},
|
||||
{0x45F9, 0x04},
|
||||
{0x40CE, 0x05},
|
||||
{0x4F97, 0x06},
|
||||
{0x4AA0, 0x07},
|
||||
{0x77C4, 0x08},
|
||||
{0x72F3, 0x09},
|
||||
{0x7DAA, 0x0A},
|
||||
{0x789D, 0x0B},
|
||||
{0x662F, 0x0C},
|
||||
{0x6318, 0x0D},
|
||||
{0x6C41, 0x0E},
|
||||
{0x6976, 0x0F},
|
||||
{0x1689, 0x10},
|
||||
{0x13BE, 0x11},
|
||||
{0x1CE7, 0x12},
|
||||
{0x19D0, 0x13},
|
||||
{0x0762, 0x14},
|
||||
{0x0255, 0x15},
|
||||
{0x0D0C, 0x16},
|
||||
{0x083B, 0x17},
|
||||
{0x355F, 0x18},
|
||||
{0x3068, 0x19},
|
||||
{0x3F31, 0x1A},
|
||||
{0x3A06, 0x1B},
|
||||
{0x24B4, 0x1C},
|
||||
{0x2183, 0x1D},
|
||||
{0x2EDA, 0x1E},
|
||||
{0x2BED, 0x1F},
|
||||
}
|
||||
|
||||
type FormatInformation struct {
|
||||
errorCorrectionLevel ErrorCorrectionLevel
|
||||
dataMask byte
|
||||
}
|
||||
|
||||
func newFormatInformation(formatInfo uint) *FormatInformation {
|
||||
errorCorrectionLevel, _ := ErrorCorrectionLevel_ForBits((formatInfo >> 3) & 0x03) // always success
|
||||
return &FormatInformation{
|
||||
errorCorrectionLevel,
|
||||
byte(formatInfo & 0x07),
|
||||
}
|
||||
}
|
||||
|
||||
func FormatInformation_NumBitsDiffering(a, b uint) int {
|
||||
return bits.OnesCount(a ^ b)
|
||||
}
|
||||
|
||||
func FormatInformation_DecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2 uint) *FormatInformation {
|
||||
formatInfo := doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2)
|
||||
if formatInfo != nil {
|
||||
return formatInfo
|
||||
}
|
||||
return doDecodeFormatInformation(
|
||||
maskedFormatInfo1^formatInfoMaskQR, maskedFormatInfo2^formatInfoMaskQR)
|
||||
}
|
||||
|
||||
func doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2 uint) *FormatInformation {
|
||||
bestDifference := math.MaxInt32
|
||||
bestFormatInfo := uint(0)
|
||||
for _, decodeInfo := range formatInfoDecodeLookup {
|
||||
targetInfo := decodeInfo[0]
|
||||
if targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2 {
|
||||
return newFormatInformation(decodeInfo[1])
|
||||
}
|
||||
bitsDifference := FormatInformation_NumBitsDiffering(maskedFormatInfo1, targetInfo)
|
||||
if bitsDifference < bestDifference {
|
||||
bestFormatInfo = decodeInfo[1]
|
||||
bestDifference = bitsDifference
|
||||
}
|
||||
if maskedFormatInfo1 != maskedFormatInfo2 {
|
||||
bitsDifference = FormatInformation_NumBitsDiffering(maskedFormatInfo2, targetInfo)
|
||||
if bitsDifference < bestDifference {
|
||||
bestFormatInfo = decodeInfo[1]
|
||||
bestDifference = bitsDifference
|
||||
}
|
||||
}
|
||||
}
|
||||
if bestDifference <= 3 {
|
||||
return newFormatInformation(bestFormatInfo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FormatInformation) GetErrorCorrectionLevel() ErrorCorrectionLevel {
|
||||
return f.errorCorrectionLevel
|
||||
}
|
||||
|
||||
func (f *FormatInformation) GetDataMask() byte {
|
||||
return f.dataMask
|
||||
}
|
||||
|
||||
// public int hasCode()
|
||||
// public boolean equals(Object o)
|
||||
100
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/mode.go
generated
vendored
Normal file
100
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/mode.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Mode struct {
|
||||
characterCountBitsForVersions []int
|
||||
bits int
|
||||
}
|
||||
|
||||
var (
|
||||
Mode_TERMINATOR = NewMode([]int{0, 0, 0}, 0x00) // Not really a mode...
|
||||
Mode_NUMERIC = NewMode([]int{10, 12, 14}, 0x01)
|
||||
Mode_ALPHANUMERIC = NewMode([]int{9, 11, 13}, 0x02)
|
||||
Mode_STRUCTURED_APPEND = NewMode([]int{0, 0, 0}, 0x03) // Not supported
|
||||
Mode_BYTE = NewMode([]int{8, 16, 16}, 0x04)
|
||||
Mode_ECI = NewMode([]int{0, 0, 0}, 0x07) // character counts don't apply
|
||||
Mode_KANJI = NewMode([]int{8, 10, 12}, 0x08)
|
||||
Mode_FNC1_FIRST_POSITION = NewMode([]int{0, 0, 0}, 0x05)
|
||||
Mode_FNC1_SECOND_POSITION = NewMode([]int{0, 0, 0}, 0x09)
|
||||
/** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
|
||||
Mode_HANZI = NewMode([]int{8, 10, 12}, 0x0D)
|
||||
)
|
||||
|
||||
func NewMode(characterCountBitsForVersions []int, bits int) *Mode {
|
||||
return &Mode{characterCountBitsForVersions, bits}
|
||||
}
|
||||
|
||||
func ModeForBits(bits int) (*Mode, error) {
|
||||
switch bits {
|
||||
case 0x0:
|
||||
return Mode_TERMINATOR, nil
|
||||
case 0x1:
|
||||
return Mode_NUMERIC, nil
|
||||
case 0x2:
|
||||
return Mode_ALPHANUMERIC, nil
|
||||
case 0x3:
|
||||
return Mode_STRUCTURED_APPEND, nil
|
||||
case 0x4:
|
||||
return Mode_BYTE, nil
|
||||
case 0x5:
|
||||
return Mode_FNC1_FIRST_POSITION, nil
|
||||
case 0x7:
|
||||
return Mode_ECI, nil
|
||||
case 0x8:
|
||||
return Mode_KANJI, nil
|
||||
case 0x9:
|
||||
return Mode_FNC1_SECOND_POSITION, nil
|
||||
case 0xD:
|
||||
// 0xD is defined in GBT 18284-2000, may not be supported in foreign country
|
||||
return Mode_HANZI, nil
|
||||
default:
|
||||
return nil, errors.New("IllegalArgumentException")
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Mode) GetCharacterCountBits(version *Version) int {
|
||||
number := version.GetVersionNumber()
|
||||
var offset int
|
||||
if number <= 9 {
|
||||
offset = 0
|
||||
} else if number <= 26 {
|
||||
offset = 1
|
||||
} else {
|
||||
offset = 2
|
||||
}
|
||||
return this.characterCountBitsForVersions[offset]
|
||||
}
|
||||
|
||||
func (this *Mode) GetBits() int {
|
||||
return this.bits
|
||||
}
|
||||
|
||||
func (this *Mode) String() string {
|
||||
switch this {
|
||||
case Mode_TERMINATOR:
|
||||
return "TERMINATOR"
|
||||
case Mode_NUMERIC:
|
||||
return "NUMERIC"
|
||||
case Mode_ALPHANUMERIC:
|
||||
return "ALPHANUMERIC"
|
||||
case Mode_STRUCTURED_APPEND:
|
||||
return "STRUCTURED_APPEND"
|
||||
case Mode_BYTE:
|
||||
return "BYTE"
|
||||
case Mode_ECI:
|
||||
return "ECI"
|
||||
case Mode_KANJI:
|
||||
return "KANJI"
|
||||
case Mode_FNC1_FIRST_POSITION:
|
||||
return "FNC1_FIRST_POSITION"
|
||||
case Mode_FNC1_SECOND_POSITION:
|
||||
return "FNC1_SECOND_POSITION"
|
||||
case Mode_HANZI:
|
||||
return "HANZI"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
24
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/qrcode_decoder_meta_data.go
generated
vendored
Normal file
24
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/qrcode_decoder_meta_data.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
type QRCodeDecoderMetaData struct {
|
||||
mirrored bool
|
||||
}
|
||||
|
||||
func NewQRCodeDecoderMetaData(mirrored bool) *QRCodeDecoderMetaData {
|
||||
return &QRCodeDecoderMetaData{mirrored}
|
||||
}
|
||||
|
||||
func (this *QRCodeDecoderMetaData) IsMirrored() bool {
|
||||
return this.mirrored
|
||||
}
|
||||
|
||||
func (this *QRCodeDecoderMetaData) ApplyMirroredCorrection(points []gozxing.ResultPoint) {
|
||||
if !this.mirrored || len(points) < 3 {
|
||||
return
|
||||
}
|
||||
points[0], points[2] = points[2], points[0]
|
||||
}
|
||||
387
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/version.go
generated
vendored
Normal file
387
vendor/github.com/makiuchi-d/gozxing/qrcode/decoder/version.go
generated
vendored
Normal file
@ -0,0 +1,387 @@
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
versionNumber int
|
||||
alignmentPatternCenters []int
|
||||
ecBlocks []ECBlocks
|
||||
totalCodewords int
|
||||
}
|
||||
|
||||
var VERSION_DECODE_INFO = []int{
|
||||
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,
|
||||
}
|
||||
|
||||
func NewVersion(versionNumber int, alignmentPatternCenters []int, ecBlocks ...ECBlocks) *Version {
|
||||
total := 0
|
||||
ecCodewords := ecBlocks[0].GetECCodewordsPerBlock()
|
||||
ecbArray := ecBlocks[0].GetECBlocks()
|
||||
for _, ecBlock := range ecbArray {
|
||||
total += ecBlock.GetCount() * (ecBlock.GetDataCodewords() + ecCodewords)
|
||||
}
|
||||
return &Version{
|
||||
versionNumber,
|
||||
alignmentPatternCenters,
|
||||
ecBlocks,
|
||||
total}
|
||||
}
|
||||
|
||||
func (v *Version) GetVersionNumber() int {
|
||||
return v.versionNumber
|
||||
}
|
||||
|
||||
func (v *Version) GetAlignmentPatternCenters() []int {
|
||||
return v.alignmentPatternCenters
|
||||
}
|
||||
|
||||
func (v *Version) GetTotalCodewords() int {
|
||||
return v.totalCodewords
|
||||
}
|
||||
|
||||
func (v *Version) GetDimensionForVersion() int {
|
||||
return 17 + 4*v.versionNumber
|
||||
}
|
||||
|
||||
func (v *Version) GetECBlocksForLevel(ecLevel ErrorCorrectionLevel) *ECBlocks {
|
||||
switch ecLevel {
|
||||
case ErrorCorrectionLevel_L:
|
||||
return &v.ecBlocks[0]
|
||||
case ErrorCorrectionLevel_M:
|
||||
return &v.ecBlocks[1]
|
||||
case ErrorCorrectionLevel_Q:
|
||||
return &v.ecBlocks[2]
|
||||
case ErrorCorrectionLevel_H:
|
||||
return &v.ecBlocks[3]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Version_GetProvisionalVersionForDimension(dimension int) (*Version, error) {
|
||||
if dimension%4 != 1 {
|
||||
return nil, errors.Errorf("dimengion = %v", dimension)
|
||||
}
|
||||
return Version_GetVersionForNumber((dimension - 17) / 4)
|
||||
}
|
||||
|
||||
func Version_GetVersionForNumber(versionNumber int) (*Version, error) {
|
||||
if versionNumber < 1 || versionNumber > 40 {
|
||||
return nil, errors.Errorf("IllegalArgumentException: versionNumber = %d", versionNumber)
|
||||
}
|
||||
return VERSIONS[versionNumber-1], nil
|
||||
}
|
||||
|
||||
func Version_decodeVersionInformation(versionBits int) (*Version, error) {
|
||||
bestDifference := math.MaxInt32
|
||||
bestVersion := 0
|
||||
for i, targetVersion := range VERSION_DECODE_INFO {
|
||||
if targetVersion == versionBits {
|
||||
return Version_GetVersionForNumber(i + 7)
|
||||
}
|
||||
bitsDifference := FormatInformation_NumBitsDiffering(uint(versionBits), uint(targetVersion))
|
||||
if bitsDifference < bestDifference {
|
||||
bestVersion = i + 7
|
||||
bestDifference = bitsDifference
|
||||
}
|
||||
}
|
||||
|
||||
if bestDifference <= 3 {
|
||||
return Version_GetVersionForNumber(bestVersion)
|
||||
}
|
||||
|
||||
return nil, errors.Errorf("we didn't find a close enough match 0x%x", versionBits)
|
||||
}
|
||||
|
||||
func (v *Version) buildFunctionPattern() (*gozxing.BitMatrix, error) {
|
||||
dimension := v.GetDimensionForVersion()
|
||||
bitMatrix, e := gozxing.NewSquareBitMatrix(dimension)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
bitMatrix.SetRegion(0, 0, 9, 9)
|
||||
bitMatrix.SetRegion(dimension-8, 0, 8, 9)
|
||||
bitMatrix.SetRegion(0, dimension-8, 9, 8)
|
||||
|
||||
max := len(v.alignmentPatternCenters)
|
||||
for x := 0; x < max; x++ {
|
||||
i := v.alignmentPatternCenters[x] - 2
|
||||
for y := 0; y < max; y++ {
|
||||
if (x == 0 && (y == 0 || y == max-1)) || (x == max-1 && y == 0) {
|
||||
continue
|
||||
}
|
||||
bitMatrix.SetRegion(v.alignmentPatternCenters[y]-2, i, 5, 5)
|
||||
}
|
||||
}
|
||||
|
||||
bitMatrix.SetRegion(6, 9, 1, dimension-17)
|
||||
bitMatrix.SetRegion(9, 6, dimension-17, 1)
|
||||
|
||||
if v.versionNumber > 6 {
|
||||
bitMatrix.SetRegion(dimension-11, 0, 3, 6)
|
||||
bitMatrix.SetRegion(0, dimension-11, 6, 3)
|
||||
}
|
||||
|
||||
return bitMatrix, nil
|
||||
}
|
||||
|
||||
type ECBlocks struct {
|
||||
ecCodewordsPerBlock int
|
||||
ecBlocks []ECB
|
||||
}
|
||||
|
||||
func (b *ECBlocks) GetECCodewordsPerBlock() int {
|
||||
return b.ecCodewordsPerBlock
|
||||
}
|
||||
|
||||
func (b *ECBlocks) GetNumBlocks() int {
|
||||
total := 0
|
||||
for _, ecBlock := range b.ecBlocks {
|
||||
total += ecBlock.GetCount()
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func (b *ECBlocks) GetTotalECCodewords() int {
|
||||
return b.ecCodewordsPerBlock * b.GetNumBlocks()
|
||||
}
|
||||
|
||||
func (b *ECBlocks) GetECBlocks() []ECB {
|
||||
return b.ecBlocks
|
||||
}
|
||||
|
||||
type ECB struct {
|
||||
count int
|
||||
dataCodewords int
|
||||
}
|
||||
|
||||
func (e ECB) GetCount() int {
|
||||
return e.count
|
||||
}
|
||||
|
||||
func (e ECB) GetDataCodewords() int {
|
||||
return e.dataCodewords
|
||||
}
|
||||
|
||||
func (this *Version) String() string {
|
||||
if this == nil {
|
||||
return ""
|
||||
}
|
||||
return strconv.Itoa(this.versionNumber)
|
||||
}
|
||||
|
||||
var VERSIONS = []*Version{
|
||||
NewVersion(1, []int{},
|
||||
ECBlocks{7, []ECB{{1, 19}}},
|
||||
ECBlocks{10, []ECB{{1, 16}}},
|
||||
ECBlocks{13, []ECB{{1, 13}}},
|
||||
ECBlocks{17, []ECB{{1, 9}}}),
|
||||
NewVersion(2, []int{6, 18},
|
||||
ECBlocks{10, []ECB{{1, 34}}},
|
||||
ECBlocks{16, []ECB{{1, 28}}},
|
||||
ECBlocks{22, []ECB{{1, 22}}},
|
||||
ECBlocks{28, []ECB{{1, 16}}}),
|
||||
NewVersion(3, []int{6, 22},
|
||||
ECBlocks{15, []ECB{{1, 55}}},
|
||||
ECBlocks{26, []ECB{{1, 44}}},
|
||||
ECBlocks{18, []ECB{{2, 17}}},
|
||||
ECBlocks{22, []ECB{{2, 13}}}),
|
||||
NewVersion(4, []int{6, 26},
|
||||
ECBlocks{20, []ECB{{1, 80}}},
|
||||
ECBlocks{18, []ECB{{2, 32}}},
|
||||
ECBlocks{26, []ECB{{2, 24}}},
|
||||
ECBlocks{16, []ECB{{4, 9}}}),
|
||||
NewVersion(5, []int{6, 30},
|
||||
ECBlocks{26, []ECB{{1, 108}}},
|
||||
ECBlocks{24, []ECB{{2, 43}}},
|
||||
ECBlocks{18, []ECB{{2, 15}, {2, 16}}},
|
||||
ECBlocks{22, []ECB{{2, 11}, {2, 12}}}),
|
||||
NewVersion(6, []int{6, 34},
|
||||
ECBlocks{18, []ECB{{2, 68}}},
|
||||
ECBlocks{16, []ECB{{4, 27}}},
|
||||
ECBlocks{24, []ECB{{4, 19}}},
|
||||
ECBlocks{28, []ECB{{4, 15}}}),
|
||||
NewVersion(7, []int{6, 22, 38},
|
||||
ECBlocks{20, []ECB{{2, 78}}},
|
||||
ECBlocks{18, []ECB{{4, 31}}},
|
||||
ECBlocks{18, []ECB{{2, 14}, {4, 15}}},
|
||||
ECBlocks{26, []ECB{{4, 13}, {1, 14}}}),
|
||||
NewVersion(8, []int{6, 24, 42},
|
||||
ECBlocks{24, []ECB{{2, 97}}},
|
||||
ECBlocks{22, []ECB{{2, 38}, {2, 39}}},
|
||||
ECBlocks{22, []ECB{{4, 18}, {2, 19}}},
|
||||
ECBlocks{26, []ECB{{4, 14}, {2, 15}}}),
|
||||
NewVersion(9, []int{6, 26, 46},
|
||||
ECBlocks{30, []ECB{{2, 116}}},
|
||||
ECBlocks{22, []ECB{{3, 36}, {2, 37}}},
|
||||
ECBlocks{20, []ECB{{4, 16}, {4, 17}}},
|
||||
ECBlocks{24, []ECB{{4, 12}, {4, 13}}}),
|
||||
NewVersion(10, []int{6, 28, 50},
|
||||
ECBlocks{18, []ECB{{2, 68}, {2, 69}}},
|
||||
ECBlocks{26, []ECB{{4, 43}, {1, 44}}},
|
||||
ECBlocks{24, []ECB{{6, 19}, {2, 20}}},
|
||||
ECBlocks{28, []ECB{{6, 15}, {2, 16}}}),
|
||||
NewVersion(11, []int{6, 30, 54},
|
||||
ECBlocks{20, []ECB{{4, 81}}},
|
||||
ECBlocks{30, []ECB{{1, 50}, {4, 51}}},
|
||||
ECBlocks{28, []ECB{{4, 22}, {4, 23}}},
|
||||
ECBlocks{24, []ECB{{3, 12}, {8, 13}}}),
|
||||
NewVersion(12, []int{6, 32, 58},
|
||||
ECBlocks{24, []ECB{{2, 92}, {2, 93}}},
|
||||
ECBlocks{22, []ECB{{6, 36}, {2, 37}}},
|
||||
ECBlocks{26, []ECB{{4, 20}, {6, 21}}},
|
||||
ECBlocks{28, []ECB{{7, 14}, {4, 15}}}),
|
||||
NewVersion(13, []int{6, 34, 62},
|
||||
ECBlocks{26, []ECB{{4, 107}}},
|
||||
ECBlocks{22, []ECB{{8, 37}, {1, 38}}},
|
||||
ECBlocks{24, []ECB{{8, 20}, {4, 21}}},
|
||||
ECBlocks{22, []ECB{{12, 11}, {4, 12}}}),
|
||||
NewVersion(14, []int{6, 26, 46, 66},
|
||||
ECBlocks{30, []ECB{{3, 115}, {1, 116}}},
|
||||
ECBlocks{24, []ECB{{4, 40}, {5, 41}}},
|
||||
ECBlocks{20, []ECB{{11, 16}, {5, 17}}},
|
||||
ECBlocks{24, []ECB{{11, 12}, {5, 13}}}),
|
||||
NewVersion(15, []int{6, 26, 48, 70},
|
||||
ECBlocks{22, []ECB{{5, 87}, {1, 88}}},
|
||||
ECBlocks{24, []ECB{{5, 41}, {5, 42}}},
|
||||
ECBlocks{30, []ECB{{5, 24}, {7, 25}}},
|
||||
ECBlocks{24, []ECB{{11, 12}, {7, 13}}}),
|
||||
NewVersion(16, []int{6, 26, 50, 74},
|
||||
ECBlocks{24, []ECB{{5, 98}, {1, 99}}},
|
||||
ECBlocks{28, []ECB{{7, 45}, {3, 46}}},
|
||||
ECBlocks{24, []ECB{{15, 19}, {2, 20}}},
|
||||
ECBlocks{30, []ECB{{3, 15}, {13, 16}}}),
|
||||
NewVersion(17, []int{6, 30, 54, 78},
|
||||
ECBlocks{28, []ECB{{1, 107}, {5, 108}}},
|
||||
ECBlocks{28, []ECB{{10, 46}, {1, 47}}},
|
||||
ECBlocks{28, []ECB{{1, 22}, {15, 23}}},
|
||||
ECBlocks{28, []ECB{{2, 14}, {17, 15}}}),
|
||||
NewVersion(18, []int{6, 30, 56, 82},
|
||||
ECBlocks{30, []ECB{{5, 120}, {1, 121}}},
|
||||
ECBlocks{26, []ECB{{9, 43}, {4, 44}}},
|
||||
ECBlocks{28, []ECB{{17, 22}, {1, 23}}},
|
||||
ECBlocks{28, []ECB{{2, 14}, {19, 15}}}),
|
||||
NewVersion(19, []int{6, 30, 58, 86},
|
||||
ECBlocks{28, []ECB{{3, 113}, {4, 114}}},
|
||||
ECBlocks{26, []ECB{{3, 44}, {11, 45}}},
|
||||
ECBlocks{26, []ECB{{17, 21}, {4, 22}}},
|
||||
ECBlocks{26, []ECB{{9, 13}, {16, 14}}}),
|
||||
NewVersion(20, []int{6, 34, 62, 90},
|
||||
ECBlocks{28, []ECB{{3, 107}, {5, 108}}},
|
||||
ECBlocks{26, []ECB{{3, 41}, {13, 42}}},
|
||||
ECBlocks{30, []ECB{{15, 24}, {5, 25}}},
|
||||
ECBlocks{28, []ECB{{15, 15}, {10, 16}}}),
|
||||
NewVersion(21, []int{6, 28, 50, 72, 94},
|
||||
ECBlocks{28, []ECB{{4, 116}, {4, 117}}},
|
||||
ECBlocks{26, []ECB{{17, 42}}},
|
||||
ECBlocks{28, []ECB{{17, 22}, {6, 23}}},
|
||||
ECBlocks{30, []ECB{{19, 16}, {6, 17}}}),
|
||||
NewVersion(22, []int{6, 26, 50, 74, 98},
|
||||
ECBlocks{28, []ECB{{2, 111}, {7, 112}}},
|
||||
ECBlocks{28, []ECB{{17, 46}}},
|
||||
ECBlocks{30, []ECB{{7, 24}, {16, 25}}},
|
||||
ECBlocks{24, []ECB{{34, 13}}}),
|
||||
NewVersion(23, []int{6, 30, 54, 78, 102},
|
||||
ECBlocks{30, []ECB{{4, 121}, {5, 122}}},
|
||||
ECBlocks{28, []ECB{{4, 47}, {14, 48}}},
|
||||
ECBlocks{30, []ECB{{11, 24}, {14, 25}}},
|
||||
ECBlocks{30, []ECB{{16, 15}, {14, 16}}}),
|
||||
NewVersion(24, []int{6, 28, 54, 80, 106},
|
||||
ECBlocks{30, []ECB{{6, 117}, {4, 118}}},
|
||||
ECBlocks{28, []ECB{{6, 45}, {14, 46}}},
|
||||
ECBlocks{30, []ECB{{11, 24}, {16, 25}}},
|
||||
ECBlocks{30, []ECB{{30, 16}, {2, 17}}}),
|
||||
NewVersion(25, []int{6, 32, 58, 84, 110},
|
||||
ECBlocks{26, []ECB{{8, 106}, {4, 107}}},
|
||||
ECBlocks{28, []ECB{{8, 47}, {13, 48}}},
|
||||
ECBlocks{30, []ECB{{7, 24}, {22, 25}}},
|
||||
ECBlocks{30, []ECB{{22, 15}, {13, 16}}}),
|
||||
NewVersion(26, []int{6, 30, 58, 86, 114},
|
||||
ECBlocks{28, []ECB{{10, 114}, {2, 115}}},
|
||||
ECBlocks{28, []ECB{{19, 46}, {4, 47}}},
|
||||
ECBlocks{28, []ECB{{28, 22}, {6, 23}}},
|
||||
ECBlocks{30, []ECB{{33, 16}, {4, 17}}}),
|
||||
NewVersion(27, []int{6, 34, 62, 90, 118},
|
||||
ECBlocks{30, []ECB{{8, 122}, {4, 123}}},
|
||||
ECBlocks{28, []ECB{{22, 45}, {3, 46}}},
|
||||
ECBlocks{30, []ECB{{8, 23}, {26, 24}}},
|
||||
ECBlocks{30, []ECB{{12, 15}, {28, 16}}}),
|
||||
NewVersion(28, []int{6, 26, 50, 74, 98, 122},
|
||||
ECBlocks{30, []ECB{{3, 117}, {10, 118}}},
|
||||
ECBlocks{28, []ECB{{3, 45}, {23, 46}}},
|
||||
ECBlocks{30, []ECB{{4, 24}, {31, 25}}},
|
||||
ECBlocks{30, []ECB{{11, 15}, {31, 16}}}),
|
||||
NewVersion(29, []int{6, 30, 54, 78, 102, 126},
|
||||
ECBlocks{30, []ECB{{7, 116}, {7, 117}}},
|
||||
ECBlocks{28, []ECB{{21, 45}, {7, 46}}},
|
||||
ECBlocks{30, []ECB{{1, 23}, {37, 24}}},
|
||||
ECBlocks{30, []ECB{{19, 15}, {26, 16}}}),
|
||||
NewVersion(30, []int{6, 26, 52, 78, 104, 130},
|
||||
ECBlocks{30, []ECB{{5, 115}, {10, 116}}},
|
||||
ECBlocks{28, []ECB{{19, 47}, {10, 48}}},
|
||||
ECBlocks{30, []ECB{{15, 24}, {25, 25}}},
|
||||
ECBlocks{30, []ECB{{23, 15}, {25, 16}}}),
|
||||
NewVersion(31, []int{6, 30, 56, 82, 108, 134},
|
||||
ECBlocks{30, []ECB{{13, 115}, {3, 116}}},
|
||||
ECBlocks{28, []ECB{{2, 46}, {29, 47}}},
|
||||
ECBlocks{30, []ECB{{42, 24}, {1, 25}}},
|
||||
ECBlocks{30, []ECB{{23, 15}, {28, 16}}}),
|
||||
NewVersion(32, []int{6, 34, 60, 86, 112, 138},
|
||||
ECBlocks{30, []ECB{{17, 115}}},
|
||||
ECBlocks{28, []ECB{{10, 46}, {23, 47}}},
|
||||
ECBlocks{30, []ECB{{10, 24}, {35, 25}}},
|
||||
ECBlocks{30, []ECB{{19, 15}, {35, 16}}}),
|
||||
NewVersion(33, []int{6, 30, 58, 86, 114, 142},
|
||||
ECBlocks{30, []ECB{{17, 115}, {1, 116}}},
|
||||
ECBlocks{28, []ECB{{14, 46}, {21, 47}}},
|
||||
ECBlocks{30, []ECB{{29, 24}, {19, 25}}},
|
||||
ECBlocks{30, []ECB{{11, 15}, {46, 16}}}),
|
||||
NewVersion(34, []int{6, 34, 62, 90, 118, 146},
|
||||
ECBlocks{30, []ECB{{13, 115}, {6, 116}}},
|
||||
ECBlocks{28, []ECB{{14, 46}, {23, 47}}},
|
||||
ECBlocks{30, []ECB{{44, 24}, {7, 25}}},
|
||||
ECBlocks{30, []ECB{{59, 16}, {1, 17}}}),
|
||||
NewVersion(35, []int{6, 30, 54, 78, 102, 126, 150},
|
||||
ECBlocks{30, []ECB{{12, 121}, {7, 122}}},
|
||||
ECBlocks{28, []ECB{{12, 47}, {26, 48}}},
|
||||
ECBlocks{30, []ECB{{39, 24}, {14, 25}}},
|
||||
ECBlocks{30, []ECB{{22, 15}, {41, 16}}}),
|
||||
NewVersion(36, []int{6, 24, 50, 76, 102, 128, 154},
|
||||
ECBlocks{30, []ECB{{6, 121}, {14, 122}}},
|
||||
ECBlocks{28, []ECB{{6, 47}, {34, 48}}},
|
||||
ECBlocks{30, []ECB{{46, 24}, {10, 25}}},
|
||||
ECBlocks{30, []ECB{{2, 15}, {64, 16}}}),
|
||||
NewVersion(37, []int{6, 28, 54, 80, 106, 132, 158},
|
||||
ECBlocks{30, []ECB{{17, 122}, {4, 123}}},
|
||||
ECBlocks{28, []ECB{{29, 46}, {14, 47}}},
|
||||
ECBlocks{30, []ECB{{49, 24}, {10, 25}}},
|
||||
ECBlocks{30, []ECB{{24, 15}, {46, 16}}}),
|
||||
NewVersion(38, []int{6, 32, 58, 84, 110, 136, 162},
|
||||
ECBlocks{30, []ECB{{4, 122}, {18, 123}}},
|
||||
ECBlocks{28, []ECB{{13, 46}, {32, 47}}},
|
||||
ECBlocks{30, []ECB{{48, 24}, {14, 25}}},
|
||||
ECBlocks{30, []ECB{{42, 15}, {32, 16}}}),
|
||||
NewVersion(39, []int{6, 26, 54, 82, 110, 138, 166},
|
||||
ECBlocks{30, []ECB{{20, 117}, {4, 118}}},
|
||||
ECBlocks{28, []ECB{{40, 47}, {7, 48}}},
|
||||
ECBlocks{30, []ECB{{43, 24}, {22, 25}}},
|
||||
ECBlocks{30, []ECB{{10, 15}, {67, 16}}}),
|
||||
NewVersion(40, []int{6, 30, 58, 86, 114, 142, 170},
|
||||
ECBlocks{30, []ECB{{19, 118}, {6, 119}}},
|
||||
ECBlocks{28, []ECB{{18, 47}, {31, 48}}},
|
||||
ECBlocks{30, []ECB{{34, 24}, {34, 25}}},
|
||||
ECBlocks{30, []ECB{{20, 15}, {61, 16}}}),
|
||||
}
|
||||
34
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/alignment_pattern.go
generated
vendored
Normal file
34
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/alignment_pattern.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
package detector
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
type AlignmentPattern struct {
|
||||
gozxing.ResultPoint
|
||||
estimatedModuleSize float64
|
||||
}
|
||||
|
||||
func NewAlignmentPattern(posX, posY, estimatedModuleSize float64) *AlignmentPattern {
|
||||
return &AlignmentPattern{
|
||||
gozxing.NewResultPoint(posX, posY),
|
||||
estimatedModuleSize,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlignmentPattern) AboutEquals(moduleSize, i, j float64) bool {
|
||||
if math.Abs(i-a.GetY()) <= moduleSize && math.Abs(j-a.GetX()) <= moduleSize {
|
||||
moduleSizeDiff := math.Abs(moduleSize - a.estimatedModuleSize)
|
||||
return moduleSizeDiff <= 1.0 || moduleSizeDiff <= a.estimatedModuleSize
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *AlignmentPattern) CombineEstimate(i, j, newModuleSize float64) *AlignmentPattern {
|
||||
combinedX := (a.GetX() + j) / 2
|
||||
combinedY := (a.GetY() + i) / 2
|
||||
combinedModuleSize := (a.estimatedModuleSize + newModuleSize) / 2
|
||||
return NewAlignmentPattern(combinedX, combinedY, combinedModuleSize)
|
||||
}
|
||||
203
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/alignment_pattern_finder.go
generated
vendored
Normal file
203
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/alignment_pattern_finder.go
generated
vendored
Normal file
@ -0,0 +1,203 @@
|
||||
package detector
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
type AlignmentPatternFinder struct {
|
||||
image *gozxing.BitMatrix
|
||||
possibleCenters []*AlignmentPattern
|
||||
startX int
|
||||
startY int
|
||||
width int
|
||||
height int
|
||||
moduleSize float64
|
||||
crossCheckStateCount []int
|
||||
resultPointCallback gozxing.ResultPointCallback
|
||||
}
|
||||
|
||||
func NewAlignmentPatternFinder(image *gozxing.BitMatrix, startX, startY, width, height int, moduleSize float64, resultPointCallback gozxing.ResultPointCallback) *AlignmentPatternFinder {
|
||||
return &AlignmentPatternFinder{
|
||||
image: image,
|
||||
possibleCenters: make([]*AlignmentPattern, 0),
|
||||
startX: startX,
|
||||
startY: startY,
|
||||
width: width,
|
||||
height: height,
|
||||
moduleSize: moduleSize,
|
||||
crossCheckStateCount: make([]int, 3),
|
||||
resultPointCallback: resultPointCallback,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *AlignmentPatternFinder) Find() (*AlignmentPattern, gozxing.NotFoundException) {
|
||||
startX := this.startX
|
||||
height := this.height
|
||||
maxJ := startX + this.width
|
||||
middleI := this.startY + (this.height / 2)
|
||||
// We are looking for black/white/black modules in 1:1:1 ratio;
|
||||
// this tracks the number of black/white/black modules seen so far
|
||||
stateCount := make([]int, 3)
|
||||
for iGen := 0; iGen < height; iGen++ {
|
||||
// Search from middle outwards
|
||||
i := middleI
|
||||
if iGen&1 == 0 {
|
||||
i += (iGen + 1) / 2
|
||||
} else {
|
||||
i -= (iGen + 1) / 2
|
||||
}
|
||||
stateCount[0] = 0
|
||||
stateCount[1] = 0
|
||||
stateCount[2] = 0
|
||||
j := startX
|
||||
// Burn off leading white pixels before anything else; if we start in the middle of
|
||||
// a white run, it doesn't make sense to count its length, since we don't know if the
|
||||
// white run continued to the left of the start point
|
||||
for j < maxJ && !this.image.Get(j, i) {
|
||||
j++
|
||||
}
|
||||
currentState := 0
|
||||
for j < maxJ {
|
||||
if this.image.Get(j, i) {
|
||||
// Black pixel
|
||||
if currentState == 1 { // Counting black pixels
|
||||
stateCount[1]++
|
||||
} else { // Counting white pixels
|
||||
if currentState == 2 { // A winner?
|
||||
if this.foundPatternCross(stateCount) { // Yes
|
||||
confirmed := this.handlePossibleCenter(stateCount, i, j)
|
||||
if confirmed != nil {
|
||||
return confirmed, nil
|
||||
}
|
||||
}
|
||||
stateCount[0] = stateCount[2]
|
||||
stateCount[1] = 1
|
||||
stateCount[2] = 0
|
||||
currentState = 1
|
||||
} else {
|
||||
currentState++
|
||||
stateCount[currentState]++
|
||||
}
|
||||
}
|
||||
} else { // White pixel
|
||||
if currentState == 1 { // Counting black pixels
|
||||
currentState++
|
||||
}
|
||||
stateCount[currentState]++
|
||||
}
|
||||
j++
|
||||
}
|
||||
if this.foundPatternCross(stateCount) {
|
||||
confirmed := this.handlePossibleCenter(stateCount, i, maxJ)
|
||||
if confirmed != nil {
|
||||
return confirmed, nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Hmm, nothing we saw was observed and confirmed twice. If we had
|
||||
// any guess at all, return it.
|
||||
if len(this.possibleCenters) > 0 {
|
||||
return this.possibleCenters[0], nil
|
||||
}
|
||||
|
||||
return nil, gozxing.NewNotFoundException()
|
||||
}
|
||||
|
||||
func AlignmentPatternFinder_centerFromEnd(stateCount []int, end int) float64 {
|
||||
return float64(end-stateCount[2]) - float64(stateCount[1])/2.0
|
||||
}
|
||||
|
||||
func (this *AlignmentPatternFinder) foundPatternCross(stateCount []int) bool {
|
||||
moduleSize := this.moduleSize
|
||||
maxVariance := moduleSize / 2
|
||||
for i := 0; i < 3; i++ {
|
||||
if math.Abs(moduleSize-float64(stateCount[i])) >= maxVariance {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *AlignmentPatternFinder) crossCheckVertical(startI, centerJ, maxCount, originalStateCountTotal int) float64 {
|
||||
image := this.image
|
||||
|
||||
maxI := image.GetHeight()
|
||||
stateCount := this.crossCheckStateCount
|
||||
stateCount[0] = 0
|
||||
stateCount[1] = 0
|
||||
stateCount[2] = 0
|
||||
|
||||
// Start counting up from center
|
||||
i := startI
|
||||
for i >= 0 && image.Get(centerJ, i) && stateCount[1] <= maxCount {
|
||||
stateCount[1]++
|
||||
i--
|
||||
}
|
||||
// If already too many modules in this state or ran off the edge:
|
||||
if i < 0 || stateCount[1] > maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
for i >= 0 && !image.Get(centerJ, i) && stateCount[0] <= maxCount {
|
||||
stateCount[0]++
|
||||
i--
|
||||
}
|
||||
if stateCount[0] > maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
// Now also count down from center
|
||||
i = startI + 1
|
||||
for i < maxI && image.Get(centerJ, i) && stateCount[1] <= maxCount {
|
||||
stateCount[1]++
|
||||
i++
|
||||
}
|
||||
if i == maxI || stateCount[1] > maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
for i < maxI && !image.Get(centerJ, i) && stateCount[2] <= maxCount {
|
||||
stateCount[2]++
|
||||
i++
|
||||
}
|
||||
if stateCount[2] > maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
stateCountTotal := stateCount[0] + stateCount[1] + stateCount[2]
|
||||
abs := stateCountTotal - originalStateCountTotal
|
||||
if abs < 0 {
|
||||
abs = -abs
|
||||
}
|
||||
if 5*abs >= 2*originalStateCountTotal {
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
if this.foundPatternCross(stateCount) {
|
||||
return AlignmentPatternFinder_centerFromEnd(stateCount, i)
|
||||
}
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
func (this *AlignmentPatternFinder) handlePossibleCenter(stateCount []int, i, j int) *AlignmentPattern {
|
||||
stateCountTotal := stateCount[0] + stateCount[1] + stateCount[2]
|
||||
centerJ := AlignmentPatternFinder_centerFromEnd(stateCount, j)
|
||||
centerI := this.crossCheckVertical(i, int(centerJ), 2*stateCount[1], stateCountTotal)
|
||||
if !math.IsNaN(centerI) {
|
||||
estimatedModuleSize := float64(stateCount[0]+stateCount[1]+stateCount[2]) / 3
|
||||
for _, center := range this.possibleCenters {
|
||||
if center.AboutEquals(estimatedModuleSize, centerI, centerJ) {
|
||||
return center.CombineEstimate(centerI, centerJ, estimatedModuleSize)
|
||||
}
|
||||
}
|
||||
// Hadn't found this before; save it
|
||||
point := NewAlignmentPattern(centerJ, centerI, estimatedModuleSize)
|
||||
this.possibleCenters = append(this.possibleCenters, point)
|
||||
if this.resultPointCallback != nil {
|
||||
this.resultPointCallback(point)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
340
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/detector.go
generated
vendored
Normal file
340
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/detector.go
generated
vendored
Normal file
@ -0,0 +1,340 @@
|
||||
package detector
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
"github.com/makiuchi-d/gozxing/common"
|
||||
"github.com/makiuchi-d/gozxing/common/util"
|
||||
"github.com/makiuchi-d/gozxing/qrcode/decoder"
|
||||
)
|
||||
|
||||
type Detector struct {
|
||||
image *gozxing.BitMatrix
|
||||
resultPointCallback gozxing.ResultPointCallback
|
||||
}
|
||||
|
||||
func NewDetector(image *gozxing.BitMatrix) *Detector {
|
||||
return &Detector{image, nil}
|
||||
}
|
||||
|
||||
func (this *Detector) GetImage() *gozxing.BitMatrix {
|
||||
return this.image
|
||||
}
|
||||
|
||||
func (this *Detector) GetResultPointCallback() gozxing.ResultPointCallback {
|
||||
return this.resultPointCallback
|
||||
}
|
||||
|
||||
func (this *Detector) DetectWithoutHints() (*common.DetectorResult, error) {
|
||||
return this.Detect(nil)
|
||||
}
|
||||
|
||||
func (this *Detector) Detect(hints map[gozxing.DecodeHintType]interface{}) (*common.DetectorResult, error) {
|
||||
if hints != nil {
|
||||
if cb, ok := hints[gozxing.DecodeHintType_NEED_RESULT_POINT_CALLBACK]; ok {
|
||||
this.resultPointCallback, _ = cb.(gozxing.ResultPointCallback)
|
||||
}
|
||||
}
|
||||
|
||||
finder := NewFinderPatternFinder(this.image, this.resultPointCallback)
|
||||
info, e := finder.Find(hints)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
return this.ProcessFinderPatternInfo(info)
|
||||
}
|
||||
|
||||
func (this *Detector) ProcessFinderPatternInfo(info *FinderPatternInfo) (*common.DetectorResult, error) {
|
||||
topLeft := info.GetTopLeft()
|
||||
topRight := info.GetTopRight()
|
||||
bottomLeft := info.GetBottomLeft()
|
||||
|
||||
moduleSize := this.calculateModuleSize(topLeft, topRight, bottomLeft)
|
||||
if moduleSize < 1.0 {
|
||||
return nil, gozxing.NewNotFoundException("moduleSize = %v", moduleSize)
|
||||
}
|
||||
dimension, e := this.computeDimension(topLeft, topRight, bottomLeft, moduleSize)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
provisionalVersion, e := decoder.Version_GetProvisionalVersionForDimension(dimension)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapFormatException(e)
|
||||
}
|
||||
modulesBetweenFPCenters := provisionalVersion.GetDimensionForVersion() - 7
|
||||
|
||||
var alignmentPattern *AlignmentPattern
|
||||
// Anything above version 1 has an alignment pattern
|
||||
if len(provisionalVersion.GetAlignmentPatternCenters()) > 0 {
|
||||
// Guess where a "bottom right" finder pattern would have been
|
||||
bottomRightX := topRight.GetX() - topLeft.GetX() + bottomLeft.GetX()
|
||||
bottomRightY := topRight.GetY() - topLeft.GetY() + bottomLeft.GetY()
|
||||
|
||||
// Estimate that alignment pattern is closer by 3 modules
|
||||
// from "bottom right" to known top left location
|
||||
correctionToTopLeft := 1.0 - 3.0/float64(modulesBetweenFPCenters)
|
||||
estAlignmentX := int(topLeft.GetX() + correctionToTopLeft*(bottomRightX-topLeft.GetX()))
|
||||
estAlignmentY := int(topLeft.GetY() + correctionToTopLeft*(bottomRightY-topLeft.GetY()))
|
||||
|
||||
// Kind of arbitrary -- expand search radius before giving up
|
||||
for i := 4; i <= 16; i <<= 1 {
|
||||
alignmentPattern, e = this.findAlignmentInRegion(moduleSize,
|
||||
estAlignmentX,
|
||||
estAlignmentY,
|
||||
float64(i))
|
||||
if e == nil {
|
||||
break
|
||||
} else if _, ok := e.(gozxing.NotFoundException); !ok {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
// If we didn't find alignment pattern... well try anyway without it
|
||||
}
|
||||
|
||||
transform := Detector_createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension)
|
||||
|
||||
bits, e := Detector_sampleGrid(this.image, transform, dimension)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapNotFoundException(e)
|
||||
}
|
||||
|
||||
var points []gozxing.ResultPoint
|
||||
if alignmentPattern == nil {
|
||||
points = []gozxing.ResultPoint{bottomLeft, topLeft, topRight}
|
||||
} else {
|
||||
points = []gozxing.ResultPoint{bottomLeft, topLeft, topRight, alignmentPattern}
|
||||
}
|
||||
return common.NewDetectorResult(bits, points), nil
|
||||
}
|
||||
|
||||
func Detector_createTransform(topLeft, topRight, bottomLeft gozxing.ResultPoint, alignmentPattern *AlignmentPattern, dimension int) *common.PerspectiveTransform {
|
||||
dimMinusThree := float64(dimension) - 3.5
|
||||
var bottomRightX float64
|
||||
var bottomRightY float64
|
||||
var sourceBottomRightX float64
|
||||
var sourceBottomRightY float64
|
||||
if alignmentPattern != nil {
|
||||
bottomRightX = alignmentPattern.GetX()
|
||||
bottomRightY = alignmentPattern.GetY()
|
||||
sourceBottomRightX = dimMinusThree - 3.0
|
||||
sourceBottomRightY = sourceBottomRightX
|
||||
} else {
|
||||
// Don't have an alignment pattern, just make up the bottom-right point
|
||||
bottomRightX = (topRight.GetX() - topLeft.GetX()) + bottomLeft.GetX()
|
||||
bottomRightY = (topRight.GetY() - topLeft.GetY()) + bottomLeft.GetY()
|
||||
sourceBottomRightX = dimMinusThree
|
||||
sourceBottomRightY = dimMinusThree
|
||||
}
|
||||
|
||||
return common.PerspectiveTransform_QuadrilateralToQuadrilateral(
|
||||
3.5,
|
||||
3.5,
|
||||
dimMinusThree,
|
||||
3.5,
|
||||
sourceBottomRightX,
|
||||
sourceBottomRightY,
|
||||
3.5,
|
||||
dimMinusThree,
|
||||
topLeft.GetX(),
|
||||
topLeft.GetY(),
|
||||
topRight.GetX(),
|
||||
topRight.GetY(),
|
||||
bottomRightX,
|
||||
bottomRightY,
|
||||
bottomLeft.GetX(),
|
||||
bottomLeft.GetY())
|
||||
}
|
||||
|
||||
func Detector_sampleGrid(image *gozxing.BitMatrix, transform *common.PerspectiveTransform, dimension int) (*gozxing.BitMatrix, error) {
|
||||
sampler := common.GridSampler_GetInstance()
|
||||
return sampler.SampleGridWithTransform(image, dimension, dimension, transform)
|
||||
}
|
||||
|
||||
func (this *Detector) computeDimension(topLeft, topRight, bottomLeft gozxing.ResultPoint, moduleSize float64) (int, error) {
|
||||
tltrCentersDimension := util.MathUtils_Round(gozxing.ResultPoint_Distance(topLeft, topRight) / moduleSize)
|
||||
tlblCentersDimension := util.MathUtils_Round(gozxing.ResultPoint_Distance(topLeft, bottomLeft) / moduleSize)
|
||||
dimension := ((tltrCentersDimension + tlblCentersDimension) / 2) + 7
|
||||
switch dimension % 4 {
|
||||
default: // 1? do nothing
|
||||
break
|
||||
case 0:
|
||||
dimension++
|
||||
break
|
||||
case 2:
|
||||
dimension--
|
||||
break
|
||||
case 3:
|
||||
return 0, gozxing.NewNotFoundException("dimension = %v", dimension)
|
||||
}
|
||||
return dimension, nil
|
||||
}
|
||||
|
||||
func (this *Detector) calculateModuleSize(topLeft, topRight, bottomLeft gozxing.ResultPoint) float64 {
|
||||
// Take the average
|
||||
return (this.calculateModuleSizeOneWay(topLeft, topRight) +
|
||||
this.calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2
|
||||
}
|
||||
|
||||
func (this *Detector) calculateModuleSizeOneWay(pattern, otherPattern gozxing.ResultPoint) float64 {
|
||||
moduleSizeEst1 := this.sizeOfBlackWhiteBlackRunBothWays(int(pattern.GetX()),
|
||||
int(pattern.GetY()),
|
||||
int(otherPattern.GetX()),
|
||||
int(otherPattern.GetY()))
|
||||
moduleSizeEst2 := this.sizeOfBlackWhiteBlackRunBothWays(int(otherPattern.GetX()),
|
||||
int(otherPattern.GetY()),
|
||||
int(pattern.GetX()),
|
||||
int(pattern.GetY()))
|
||||
if math.IsNaN(moduleSizeEst1) {
|
||||
return moduleSizeEst2 / 7.0
|
||||
}
|
||||
if math.IsNaN(moduleSizeEst2) {
|
||||
return moduleSizeEst1 / 7.0
|
||||
}
|
||||
// Average them, and divide by 7 since we've counted the width of 3 black modules,
|
||||
// and 1 white and 1 black module on either side. Ergo, divide sum by 14.
|
||||
return (moduleSizeEst1 + moduleSizeEst2) / 14.0
|
||||
}
|
||||
|
||||
func (this *Detector) sizeOfBlackWhiteBlackRunBothWays(fromX, fromY, toX, toY int) float64 {
|
||||
|
||||
result := this.sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY)
|
||||
|
||||
// Now count other way -- don't run off image though of course
|
||||
scale := float64(1.0)
|
||||
otherToX := fromX - (toX - fromX)
|
||||
if otherToX < 0 {
|
||||
scale = float64(fromX) / float64(fromX-otherToX)
|
||||
otherToX = 0
|
||||
} else if otherToX >= this.image.GetWidth() {
|
||||
scale = float64(this.image.GetWidth()-1-fromX) / float64(otherToX-fromX)
|
||||
otherToX = this.image.GetWidth() - 1
|
||||
}
|
||||
otherToY := int(float64(fromY) - float64(toY-fromY)*scale)
|
||||
|
||||
scale = 1.0
|
||||
if otherToY < 0 {
|
||||
scale = float64(fromY) / float64(fromY-otherToY)
|
||||
otherToY = 0
|
||||
} else if otherToY >= this.image.GetHeight() {
|
||||
scale = float64(this.image.GetHeight()-1-fromY) / float64(otherToY-fromY)
|
||||
otherToY = this.image.GetHeight() - 1
|
||||
}
|
||||
otherToX = int(float64(fromX) + float64(otherToX-fromX)*scale)
|
||||
|
||||
result += this.sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY)
|
||||
|
||||
// Middle pixel is double-counted this way; subtract 1
|
||||
return result - 1.0
|
||||
}
|
||||
|
||||
func (this *Detector) sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY int) float64 {
|
||||
// Mild variant of Bresenham's algorithm;
|
||||
// see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
|
||||
steep := false
|
||||
dx := toX - fromX
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
}
|
||||
dy := toY - fromY
|
||||
if dy < 0 {
|
||||
dy = -dy
|
||||
}
|
||||
if dy > dx {
|
||||
steep = true
|
||||
fromX, fromY = fromY, fromX
|
||||
toX, toY = toY, toX
|
||||
dx, dy = dy, dx
|
||||
}
|
||||
|
||||
error := -dx / 2
|
||||
xstep := 1
|
||||
if fromX >= toX {
|
||||
xstep = -1
|
||||
}
|
||||
ystep := 1
|
||||
if fromY >= toY {
|
||||
ystep = -1
|
||||
}
|
||||
|
||||
// In black pixels, looking for white, first or second time.
|
||||
state := 0
|
||||
// Loop up until x == toX, but not beyond
|
||||
xLimit := toX + xstep
|
||||
for x, y := fromX, fromY; x != xLimit; x += xstep {
|
||||
realX := x
|
||||
realY := y
|
||||
if steep {
|
||||
realX = y
|
||||
realY = x
|
||||
}
|
||||
|
||||
// Does current pixel mean we have moved white to black or vice versa?
|
||||
// Scanning black in state 0,2 and white in state 1, so if we find the wrong
|
||||
// color, advance to next state or end if we are in state 2 already
|
||||
if (state == 1) == this.image.Get(realX, realY) {
|
||||
if state == 2 {
|
||||
return util.MathUtils_DistanceInt(x, y, fromX, fromY)
|
||||
}
|
||||
state++
|
||||
}
|
||||
|
||||
error += dy
|
||||
if error > 0 {
|
||||
if y == toY {
|
||||
break
|
||||
}
|
||||
y += ystep
|
||||
error -= dx
|
||||
}
|
||||
}
|
||||
// Found black-white-black; give the benefit of the doubt that the next pixel outside the image
|
||||
// is "white" so this last point at (toX+xStep,toY) is the right ending. This is really a
|
||||
// small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this.
|
||||
if state == 2 {
|
||||
return util.MathUtils_DistanceInt(toX+xstep, toY, fromX, fromY)
|
||||
}
|
||||
// else we didn't find even black-white-black; no estimate is really possible
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
func (this *Detector) findAlignmentInRegion(overallEstModuleSize float64, estAlignmentX, estAlignmentY int, allowanceFactor float64) (*AlignmentPattern, error) {
|
||||
// Look for an alignment pattern (3 modules in size) around where it
|
||||
// should be
|
||||
allowance := int(allowanceFactor * overallEstModuleSize)
|
||||
alignmentAreaLeftX := estAlignmentX - allowance
|
||||
if alignmentAreaLeftX < 0 {
|
||||
alignmentAreaLeftX = 0
|
||||
}
|
||||
alignmentAreaRightX := estAlignmentX + allowance
|
||||
if a := this.image.GetWidth() - 1; a < alignmentAreaRightX {
|
||||
alignmentAreaRightX = a
|
||||
}
|
||||
if x := float64(alignmentAreaRightX - alignmentAreaLeftX); x < overallEstModuleSize*3 {
|
||||
return nil, gozxing.NewNotFoundException("x = %v, moduleSize = %v", x, overallEstModuleSize)
|
||||
}
|
||||
|
||||
alignmentAreaTopY := estAlignmentY - allowance
|
||||
if alignmentAreaTopY < 0 {
|
||||
alignmentAreaTopY = 0
|
||||
}
|
||||
alignmentAreaBottomY := estAlignmentY + allowance
|
||||
if a := this.image.GetHeight() - 1; a < alignmentAreaBottomY {
|
||||
alignmentAreaBottomY = a
|
||||
}
|
||||
|
||||
if y := float64(alignmentAreaBottomY - alignmentAreaTopY); y < overallEstModuleSize*3 {
|
||||
return nil, gozxing.NewNotFoundException("y = %v, moduleSize = %v", y, overallEstModuleSize)
|
||||
}
|
||||
|
||||
alignmentFinder := NewAlignmentPatternFinder(
|
||||
this.image,
|
||||
alignmentAreaLeftX,
|
||||
alignmentAreaTopY,
|
||||
alignmentAreaRightX-alignmentAreaLeftX,
|
||||
alignmentAreaBottomY-alignmentAreaTopY,
|
||||
overallEstModuleSize,
|
||||
this.resultPointCallback)
|
||||
return alignmentFinder.Find()
|
||||
}
|
||||
49
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/finder_pattern.go
generated
vendored
Normal file
49
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/finder_pattern.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package detector
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
type FinderPattern struct {
|
||||
gozxing.ResultPoint
|
||||
estimatedModuleSize float64
|
||||
count int
|
||||
}
|
||||
|
||||
func NewFinderPattern1(posX, posY, estimatedModuleSize float64) *FinderPattern {
|
||||
return NewFinderPattern(posX, posY, estimatedModuleSize, 1)
|
||||
}
|
||||
|
||||
func NewFinderPattern(posX, posY, estimatedModuleSize float64, count int) *FinderPattern {
|
||||
return &FinderPattern{
|
||||
gozxing.NewResultPoint(posX, posY),
|
||||
estimatedModuleSize,
|
||||
count,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FinderPattern) GetEstimatedModuleSize() float64 {
|
||||
return f.estimatedModuleSize
|
||||
}
|
||||
|
||||
func (f *FinderPattern) GetCount() int {
|
||||
return f.count
|
||||
}
|
||||
|
||||
func (f *FinderPattern) AboutEquals(moduleSize, i, j float64) bool {
|
||||
if math.Abs(i-f.GetY()) <= moduleSize && math.Abs(j-f.GetX()) <= moduleSize {
|
||||
moduleSizeDiff := math.Abs(moduleSize - f.estimatedModuleSize)
|
||||
return moduleSizeDiff <= 1.0 || moduleSizeDiff <= f.estimatedModuleSize
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *FinderPattern) CombineEstimate(i, j, newModuleSize float64) *FinderPattern {
|
||||
combinedCount := float64(f.count + 1)
|
||||
combinedX := (float64(f.count)*f.GetX() + j) / combinedCount
|
||||
combinedY := (float64(f.count)*f.GetY() + i) / combinedCount
|
||||
combinedModuleSize := (float64(f.count)*f.estimatedModuleSize + newModuleSize) / combinedCount
|
||||
return NewFinderPattern(combinedX, combinedY, combinedModuleSize, int(combinedCount))
|
||||
}
|
||||
544
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/finder_pattern_finder.go
generated
vendored
Normal file
544
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/finder_pattern_finder.go
generated
vendored
Normal file
@ -0,0 +1,544 @@
|
||||
package detector
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
)
|
||||
|
||||
const (
|
||||
FinderPatternFinder_CENTER_QUORUM = 2
|
||||
FinderPatternFinder_MIN_SKIP = 3
|
||||
FinderPatternFinder_MAX_MODULES = 97
|
||||
)
|
||||
|
||||
type FinderPatternFinder struct {
|
||||
image *gozxing.BitMatrix
|
||||
possibleCenters []*FinderPattern
|
||||
hasSkipped bool
|
||||
crossCheckStateCount []int
|
||||
resultPointCallback gozxing.ResultPointCallback
|
||||
}
|
||||
|
||||
func NewFinderPatternFinder(image *gozxing.BitMatrix, resultPointCallback gozxing.ResultPointCallback) *FinderPatternFinder {
|
||||
return &FinderPatternFinder{
|
||||
image: image,
|
||||
possibleCenters: make([]*FinderPattern, 0),
|
||||
crossCheckStateCount: make([]int, 5),
|
||||
resultPointCallback: resultPointCallback,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FinderPatternFinder) GetImage() *gozxing.BitMatrix {
|
||||
return f.image
|
||||
}
|
||||
|
||||
func (f *FinderPatternFinder) GetPossibleCenters() []*FinderPattern {
|
||||
return f.possibleCenters
|
||||
}
|
||||
|
||||
func (f *FinderPatternFinder) Find(hints map[gozxing.DecodeHintType]interface{}) (*FinderPatternInfo, gozxing.NotFoundException) {
|
||||
_, tryHarder := hints[gozxing.DecodeHintType_TRY_HARDER]
|
||||
maxI := f.image.GetHeight()
|
||||
maxJ := f.image.GetWidth()
|
||||
|
||||
iSkip := (3 * maxI) / (4 * FinderPatternFinder_MAX_MODULES)
|
||||
if iSkip < FinderPatternFinder_MIN_SKIP || tryHarder {
|
||||
iSkip = FinderPatternFinder_MIN_SKIP
|
||||
}
|
||||
|
||||
done := false
|
||||
stateCount := make([]int, 5)
|
||||
for i := iSkip - 1; i < maxI && !done; i += iSkip {
|
||||
FinderPatternFinder_doClearCounts(stateCount)
|
||||
currentState := 0
|
||||
for j := 0; j < maxJ; j++ {
|
||||
if f.image.Get(j, i) {
|
||||
if (currentState & 1) == 1 {
|
||||
currentState++
|
||||
}
|
||||
stateCount[currentState]++
|
||||
} else {
|
||||
if (currentState & 1) == 0 {
|
||||
if currentState == 4 {
|
||||
if FinderPatternFinder_foundPatternCross(stateCount) {
|
||||
confirmed := f.HandlePossibleCenter(stateCount, i, j)
|
||||
if confirmed {
|
||||
iSkip = 2
|
||||
if f.hasSkipped {
|
||||
done = f.HaveMultiplyConfirmedCenters()
|
||||
} else {
|
||||
rowSkip := f.FindRowSkip()
|
||||
if rowSkip > stateCount[2] {
|
||||
i += rowSkip - stateCount[2] - iSkip
|
||||
j = maxJ - 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FinderPatternFinder_doShiftCounts2(stateCount)
|
||||
currentState = 3
|
||||
continue
|
||||
}
|
||||
currentState = 0
|
||||
FinderPatternFinder_doClearCounts(stateCount)
|
||||
} else {
|
||||
FinderPatternFinder_doShiftCounts2(stateCount)
|
||||
currentState = 3
|
||||
}
|
||||
} else {
|
||||
currentState++
|
||||
stateCount[currentState]++
|
||||
}
|
||||
} else {
|
||||
stateCount[currentState]++
|
||||
}
|
||||
}
|
||||
}
|
||||
if FinderPatternFinder_foundPatternCross(stateCount) {
|
||||
confirmed := f.HandlePossibleCenter(stateCount, i, maxJ)
|
||||
if confirmed {
|
||||
iSkip = stateCount[0]
|
||||
if f.hasSkipped {
|
||||
done = f.HaveMultiplyConfirmedCenters()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fps, e := f.SelectBestPatterns()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
bl, tl, tr := gozxing.ResultPoint_OrderBestPatterns(fps[0], fps[1], fps[2])
|
||||
info := NewFinderPatternInfo(bl.(*FinderPattern), tl.(*FinderPattern), tr.(*FinderPattern))
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func FinderPatternFinder_centerFromEnd(stateCount []int, end int) float64 {
|
||||
return float64(end-stateCount[4]-stateCount[3]) - float64(stateCount[2])/2
|
||||
}
|
||||
|
||||
func FinderPatternFinder_foundPatternCross(stateCount []int) bool {
|
||||
totalModuleSize := 0
|
||||
for i := 0; i < 5; i++ {
|
||||
count := stateCount[i]
|
||||
if count == 0 {
|
||||
return false
|
||||
}
|
||||
totalModuleSize += count
|
||||
}
|
||||
if totalModuleSize < 7 {
|
||||
return false
|
||||
}
|
||||
moduleSize := float64(totalModuleSize) / 7.0
|
||||
maxVariance := moduleSize / 2.0
|
||||
return math.Abs(moduleSize-float64(stateCount[0])) < maxVariance &&
|
||||
math.Abs(moduleSize-float64(stateCount[1])) < maxVariance &&
|
||||
math.Abs(3.0*moduleSize-float64(stateCount[2])) < 3*maxVariance &&
|
||||
math.Abs(moduleSize-float64(stateCount[3])) < maxVariance &&
|
||||
math.Abs(moduleSize-float64(stateCount[4])) < maxVariance
|
||||
}
|
||||
|
||||
func FinderPatternFinder_foundPatternDiagonal(stateCount []int) bool {
|
||||
totalModuleSize := 0
|
||||
for i := 0; i < 5; i++ {
|
||||
count := stateCount[i]
|
||||
if count == 0 {
|
||||
return false
|
||||
}
|
||||
totalModuleSize += count
|
||||
}
|
||||
if totalModuleSize < 7 {
|
||||
return false
|
||||
}
|
||||
moduleSize := float64(totalModuleSize) / 7.0
|
||||
maxVariance := moduleSize / 1.333
|
||||
return math.Abs(moduleSize-float64(stateCount[0])) < maxVariance &&
|
||||
math.Abs(moduleSize-float64(stateCount[1])) < maxVariance &&
|
||||
math.Abs(3.0*moduleSize-float64(stateCount[2])) < 3*maxVariance &&
|
||||
math.Abs(moduleSize-float64(stateCount[3])) < maxVariance &&
|
||||
math.Abs(moduleSize-float64(stateCount[4])) < maxVariance
|
||||
}
|
||||
|
||||
func (f *FinderPatternFinder) GetCrossCheckStateCount() []int {
|
||||
FinderPatternFinder_doClearCounts(f.crossCheckStateCount)
|
||||
return f.crossCheckStateCount
|
||||
}
|
||||
|
||||
func FinderPatternFinder_ClearCounts(counts []int) {
|
||||
FinderPatternFinder_doClearCounts(counts)
|
||||
}
|
||||
|
||||
func FinderPatternFinder_ShiftCounts2(stateCount []int) {
|
||||
FinderPatternFinder_doShiftCounts2(stateCount)
|
||||
}
|
||||
|
||||
func FinderPatternFinder_doClearCounts(counts []int) {
|
||||
for x := 0; x < len(counts); x++ {
|
||||
counts[x] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func FinderPatternFinder_doShiftCounts2(stateCount []int) {
|
||||
stateCount[0] = stateCount[2]
|
||||
stateCount[1] = stateCount[3]
|
||||
stateCount[2] = stateCount[4]
|
||||
stateCount[3] = 1
|
||||
stateCount[4] = 0
|
||||
}
|
||||
|
||||
func (f *FinderPatternFinder) crossCheckDiagonal(centerI, centerJ int) bool {
|
||||
stateCount := f.GetCrossCheckStateCount()
|
||||
|
||||
i := 0
|
||||
for centerI >= i && centerJ >= i && f.image.Get(centerJ-i, centerI-i) {
|
||||
stateCount[2]++
|
||||
i++
|
||||
}
|
||||
if stateCount[2] == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for centerI >= i && centerJ >= i && !f.image.Get(centerJ-i, centerI-i) {
|
||||
stateCount[1]++
|
||||
i++
|
||||
}
|
||||
if stateCount[1] == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for centerI >= i && centerJ >= i && f.image.Get(centerJ-i, centerI-i) {
|
||||
stateCount[0]++
|
||||
i++
|
||||
}
|
||||
if stateCount[0] == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
maxI := f.image.GetHeight()
|
||||
maxJ := f.image.GetWidth()
|
||||
|
||||
i = 1
|
||||
for centerI+i < maxI && centerJ+i < maxJ && f.image.Get(centerJ+i, centerI+i) {
|
||||
stateCount[2]++
|
||||
i++
|
||||
}
|
||||
for centerI+i < maxI && centerJ+i < maxJ && !f.image.Get(centerJ+i, centerI+i) {
|
||||
stateCount[3]++
|
||||
i++
|
||||
}
|
||||
if stateCount[3] == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for centerI+i < maxI && centerJ+i < maxJ && f.image.Get(centerJ+i, centerI+i) {
|
||||
stateCount[4]++
|
||||
i++
|
||||
}
|
||||
if stateCount[4] == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return FinderPatternFinder_foundPatternDiagonal(stateCount)
|
||||
}
|
||||
|
||||
func (f *FinderPatternFinder) CrossCheckVertical(startI, centerJ, maxCount, originalStateCountTotal int) float64 {
|
||||
image := f.image
|
||||
|
||||
maxI := image.GetHeight()
|
||||
stateCount := f.GetCrossCheckStateCount()
|
||||
|
||||
// Start counting up from center
|
||||
i := startI
|
||||
|
||||
for i >= 0 && image.Get(centerJ, i) {
|
||||
stateCount[2]++
|
||||
i--
|
||||
}
|
||||
if i < 0 {
|
||||
return math.NaN()
|
||||
}
|
||||
for i >= 0 && !image.Get(centerJ, i) && stateCount[1] <= maxCount {
|
||||
stateCount[1]++
|
||||
i--
|
||||
}
|
||||
|
||||
if i < 0 || stateCount[1] > maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
for i >= 0 && image.Get(centerJ, i) && stateCount[0] <= maxCount {
|
||||
stateCount[0]++
|
||||
i--
|
||||
}
|
||||
if stateCount[0] > maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
// Now also count down from center
|
||||
i = startI + 1
|
||||
for i < maxI && image.Get(centerJ, i) {
|
||||
stateCount[2]++
|
||||
i++
|
||||
}
|
||||
if i == maxI {
|
||||
return math.NaN()
|
||||
}
|
||||
for i < maxI && !image.Get(centerJ, i) && stateCount[3] < maxCount {
|
||||
stateCount[3]++
|
||||
i++
|
||||
}
|
||||
if i == maxI || stateCount[3] >= maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
for i < maxI && image.Get(centerJ, i) && stateCount[4] < maxCount {
|
||||
stateCount[4]++
|
||||
i++
|
||||
}
|
||||
if stateCount[4] >= maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
stateCountTotal := stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]
|
||||
if 5*math.Abs(float64(stateCountTotal-originalStateCountTotal)) >= float64(2*originalStateCountTotal) {
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
if FinderPatternFinder_foundPatternCross(stateCount) {
|
||||
return FinderPatternFinder_centerFromEnd(stateCount, i)
|
||||
}
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
func (f *FinderPatternFinder) CrossCheckHorizontal(startJ, centerI, maxCount, originalStateCountTotal int) float64 {
|
||||
image := f.image
|
||||
|
||||
maxJ := image.GetWidth()
|
||||
stateCount := f.GetCrossCheckStateCount()
|
||||
|
||||
j := startJ
|
||||
for j >= 0 && image.Get(j, centerI) {
|
||||
stateCount[2]++
|
||||
j--
|
||||
}
|
||||
if j < 0 {
|
||||
return math.NaN()
|
||||
}
|
||||
for j >= 0 && !image.Get(j, centerI) && stateCount[1] <= maxCount {
|
||||
stateCount[1]++
|
||||
j--
|
||||
}
|
||||
if j < 0 || stateCount[1] > maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
for j >= 0 && image.Get(j, centerI) && stateCount[0] <= maxCount {
|
||||
stateCount[0]++
|
||||
j--
|
||||
}
|
||||
if stateCount[0] > maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
j = startJ + 1
|
||||
for j < maxJ && image.Get(j, centerI) {
|
||||
stateCount[2]++
|
||||
j++
|
||||
}
|
||||
if j == maxJ {
|
||||
return math.NaN()
|
||||
}
|
||||
for j < maxJ && !image.Get(j, centerI) && stateCount[3] < maxCount {
|
||||
stateCount[3]++
|
||||
j++
|
||||
}
|
||||
if j == maxJ || stateCount[3] >= maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
for j < maxJ && image.Get(j, centerI) && stateCount[4] < maxCount {
|
||||
stateCount[4]++
|
||||
j++
|
||||
}
|
||||
if stateCount[4] >= maxCount {
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
stateCountTotal := stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]
|
||||
if 5*math.Abs(float64(stateCountTotal-originalStateCountTotal)) >= float64(originalStateCountTotal) {
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
if FinderPatternFinder_foundPatternCross(stateCount) {
|
||||
return FinderPatternFinder_centerFromEnd(stateCount, j)
|
||||
}
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
func (f *FinderPatternFinder) HandlePossibleCenterWithPureBarcode(stateCount []int, i, j int, pureBarcode bool) bool {
|
||||
return f.HandlePossibleCenter(stateCount, i, j)
|
||||
}
|
||||
|
||||
func (f *FinderPatternFinder) HandlePossibleCenter(stateCount []int, i, j int) bool {
|
||||
stateCountTotal := stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]
|
||||
centerJ := FinderPatternFinder_centerFromEnd(stateCount, j)
|
||||
|
||||
centerI := f.CrossCheckVertical(i, int(centerJ), stateCount[2], stateCountTotal)
|
||||
if !math.IsNaN(centerI) {
|
||||
centerJ = f.CrossCheckHorizontal(int(centerJ), int(centerI), stateCount[2], stateCountTotal)
|
||||
if !math.IsNaN(centerJ) && f.crossCheckDiagonal(int(centerI), int(centerJ)) {
|
||||
estimatedModuleSize := float64(stateCountTotal) / 7.0
|
||||
found := false
|
||||
for index := 0; index < len(f.possibleCenters); index++ {
|
||||
center := f.possibleCenters[index]
|
||||
|
||||
if center.AboutEquals(estimatedModuleSize, centerI, centerJ) {
|
||||
f.possibleCenters[index] = center.CombineEstimate(centerI, centerJ, estimatedModuleSize)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
point := NewFinderPattern1(centerJ, centerI, estimatedModuleSize)
|
||||
f.possibleCenters = append(f.possibleCenters, point)
|
||||
if f.resultPointCallback != nil {
|
||||
f.resultPointCallback(point)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *FinderPatternFinder) FindRowSkip() int {
|
||||
if len(f.possibleCenters) <= 1 {
|
||||
return 0
|
||||
}
|
||||
var firstConfirmedCenter *FinderPattern
|
||||
for _, center := range f.possibleCenters {
|
||||
if center.GetCount() >= FinderPatternFinder_CENTER_QUORUM {
|
||||
if firstConfirmedCenter == nil {
|
||||
firstConfirmedCenter = center
|
||||
} else {
|
||||
f.hasSkipped = true
|
||||
return int((math.Abs(firstConfirmedCenter.GetX()-center.GetX()) -
|
||||
math.Abs(firstConfirmedCenter.GetY()-center.GetY())) / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *FinderPatternFinder) HaveMultiplyConfirmedCenters() bool {
|
||||
confirmedCount := 0
|
||||
totalModuleSize := 0.0
|
||||
max := len(f.possibleCenters)
|
||||
for _, pattern := range f.possibleCenters {
|
||||
if pattern.GetCount() >= FinderPatternFinder_CENTER_QUORUM {
|
||||
confirmedCount++
|
||||
totalModuleSize += pattern.GetEstimatedModuleSize()
|
||||
}
|
||||
}
|
||||
if confirmedCount < 3 {
|
||||
return false
|
||||
}
|
||||
|
||||
average := totalModuleSize / float64(max)
|
||||
totalDeviation := 0.0
|
||||
for _, pattern := range f.possibleCenters {
|
||||
totalDeviation += math.Abs(pattern.GetEstimatedModuleSize() - average)
|
||||
}
|
||||
return totalDeviation <= 0.05*totalModuleSize
|
||||
}
|
||||
|
||||
// squaredDistance get square of distance between a and b.
|
||||
func squaredDistance(a, b *FinderPattern) float64 {
|
||||
x := a.GetX() - b.GetX()
|
||||
y := a.GetY() - b.GetY()
|
||||
return x*x + y*y
|
||||
}
|
||||
|
||||
// SelectBestPatterns return the 3 best {@link FinderPattern}s from our list of candidates.
|
||||
// The "best" are those have similar module size and form a shape closer to a isosceles right triangle.
|
||||
// @throws NotFoundException if 3 such finder patterns do not exist
|
||||
func (f *FinderPatternFinder) SelectBestPatterns() ([]*FinderPattern, gozxing.NotFoundException) {
|
||||
startSize := float64(len(f.possibleCenters))
|
||||
if startSize < 3 {
|
||||
return nil, gozxing.NewNotFoundException("startSize = %v", startSize)
|
||||
}
|
||||
|
||||
sort.Slice(f.possibleCenters, estimatedModuleComparator(f.possibleCenters))
|
||||
|
||||
distortion := math.MaxFloat64
|
||||
bestPatterns := []*FinderPattern{nil, nil, nil}
|
||||
|
||||
for i := 0; i < len(f.possibleCenters)-2; i++ {
|
||||
fpi := f.possibleCenters[i]
|
||||
minModuleSize := fpi.GetEstimatedModuleSize()
|
||||
|
||||
for j := i + 1; j < len(f.possibleCenters)-1; j++ {
|
||||
fpj := f.possibleCenters[j]
|
||||
square0 := squaredDistance(fpi, fpj)
|
||||
|
||||
for k := j + 1; k < len(f.possibleCenters); k++ {
|
||||
fpk := f.possibleCenters[k]
|
||||
maxModuleSize := fpk.GetEstimatedModuleSize()
|
||||
if maxModuleSize > minModuleSize*1.4 {
|
||||
// module size is not similar
|
||||
continue
|
||||
}
|
||||
|
||||
a := square0
|
||||
b := squaredDistance(fpj, fpk)
|
||||
c := squaredDistance(fpi, fpk)
|
||||
|
||||
// sorts ascending - inlined
|
||||
if a < b {
|
||||
if b > c {
|
||||
if a < c {
|
||||
b, c = c, b
|
||||
} else {
|
||||
a, b, c = c, a, b
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if b < c {
|
||||
if a < c {
|
||||
a, b = b, a
|
||||
} else {
|
||||
a, b, c = b, c, a
|
||||
}
|
||||
} else {
|
||||
a, c = c, a
|
||||
}
|
||||
}
|
||||
|
||||
// a^2 + b^2 = c^2 (Pythagorean theorem), and a = b (isosceles triangle).
|
||||
// Since any right triangle satisfies the formula c^2 - b^2 - a^2 = 0,
|
||||
// we need to check both two equal sides separately.
|
||||
// The value of |c^2 - 2 * b^2| + |c^2 - 2 * a^2| increases as dissimilarity
|
||||
// from isosceles right triangle.
|
||||
d := math.Abs(c-2*b) + math.Abs(c-2*a)
|
||||
if d < distortion {
|
||||
distortion = d
|
||||
bestPatterns[0] = fpi
|
||||
bestPatterns[1] = fpj
|
||||
bestPatterns[2] = fpk
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if distortion == math.MaxFloat64 {
|
||||
return nil, gozxing.NewNotFoundException("module size is too different")
|
||||
}
|
||||
|
||||
return bestPatterns, nil
|
||||
}
|
||||
|
||||
// estimatedModuleComparator Orders by FinderPatternFinder#getEstimatedModuleSize()
|
||||
func estimatedModuleComparator(patterns []*FinderPattern) func(int, int) bool {
|
||||
return func(i, j int) bool {
|
||||
return patterns[j].GetEstimatedModuleSize() > patterns[i].GetEstimatedModuleSize()
|
||||
}
|
||||
}
|
||||
26
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/finder_pattern_info.go
generated
vendored
Normal file
26
vendor/github.com/makiuchi-d/gozxing/qrcode/detector/finder_pattern_info.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package detector
|
||||
|
||||
type FinderPatternInfo struct {
|
||||
bottomLeft *FinderPattern
|
||||
topLeft *FinderPattern
|
||||
topRight *FinderPattern
|
||||
}
|
||||
|
||||
func NewFinderPatternInfo(bottomLeft, topLeft, topRight *FinderPattern) *FinderPatternInfo {
|
||||
return &FinderPatternInfo{
|
||||
bottomLeft: bottomLeft,
|
||||
topLeft: topLeft,
|
||||
topRight: topRight,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FinderPatternInfo) GetBottomLeft() *FinderPattern {
|
||||
return f.bottomLeft
|
||||
}
|
||||
|
||||
func (f *FinderPatternInfo) GetTopLeft() *FinderPattern {
|
||||
return f.topLeft
|
||||
}
|
||||
func (f *FinderPatternInfo) GetTopRight() *FinderPattern {
|
||||
return f.topRight
|
||||
}
|
||||
18
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/block_pair.go
generated
vendored
Normal file
18
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/block_pair.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package encoder
|
||||
|
||||
type BlockPair struct {
|
||||
dataBytes []byte
|
||||
errorCorrectionBytes []byte
|
||||
}
|
||||
|
||||
func NewBlockPair(data []byte, errorCorrection []byte) *BlockPair {
|
||||
return &BlockPair{data, errorCorrection}
|
||||
}
|
||||
|
||||
func (this *BlockPair) GetDataBytes() []byte {
|
||||
return this.dataBytes
|
||||
}
|
||||
|
||||
func (this *BlockPair) GetErrorCorrectionBytes() []byte {
|
||||
return this.errorCorrectionBytes
|
||||
}
|
||||
69
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/byte_matrix.go
generated
vendored
Normal file
69
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/byte_matrix.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
package encoder
|
||||
|
||||
type ByteMatrix struct {
|
||||
bytes [][]int8
|
||||
width int
|
||||
height int
|
||||
}
|
||||
|
||||
func NewByteMatrix(width, height int) *ByteMatrix {
|
||||
bytes := make([][]int8, height)
|
||||
for i := 0; i < height; i++ {
|
||||
bytes[i] = make([]int8, width)
|
||||
}
|
||||
return &ByteMatrix{bytes, width, height}
|
||||
}
|
||||
|
||||
func (this *ByteMatrix) GetHeight() int {
|
||||
return this.height
|
||||
}
|
||||
|
||||
func (this *ByteMatrix) GetWidth() int {
|
||||
return this.width
|
||||
}
|
||||
|
||||
func (this *ByteMatrix) Get(x, y int) int8 {
|
||||
return this.bytes[y][x]
|
||||
}
|
||||
|
||||
func (this *ByteMatrix) GetArray() [][]int8 {
|
||||
return this.bytes
|
||||
}
|
||||
|
||||
func (this *ByteMatrix) Set(x, y int, value int8) {
|
||||
this.bytes[y][x] = value
|
||||
}
|
||||
|
||||
func (this *ByteMatrix) SetBool(x, y int, value bool) {
|
||||
if value {
|
||||
this.bytes[y][x] = 1
|
||||
} else {
|
||||
this.bytes[y][x] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ByteMatrix) Clear(value int8) {
|
||||
for y := range this.bytes {
|
||||
for x := range this.bytes[y] {
|
||||
this.bytes[y][x] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ByteMatrix) String() string {
|
||||
result := make([]byte, 0, 2*(this.width+1)*this.height)
|
||||
for _, row := range this.bytes {
|
||||
for _, b := range row {
|
||||
switch b {
|
||||
case 0:
|
||||
result = append(result, " 0"...)
|
||||
case 1:
|
||||
result = append(result, " 1"...)
|
||||
default:
|
||||
result = append(result, " "...)
|
||||
}
|
||||
}
|
||||
result = append(result, '\n')
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
630
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/encoder.go
generated
vendored
Normal file
630
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/encoder.go
generated
vendored
Normal file
@ -0,0 +1,630 @@
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
|
||||
textencoding "golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
"github.com/makiuchi-d/gozxing/common"
|
||||
"github.com/makiuchi-d/gozxing/common/reedsolomon"
|
||||
"github.com/makiuchi-d/gozxing/qrcode/decoder"
|
||||
)
|
||||
|
||||
var (
|
||||
Encoder_DEFAULT_BYTE_MODE_ENCODING textencoding.Encoding = unicode.UTF8 // original default is "ISO-8859-1"
|
||||
)
|
||||
|
||||
var alphanumericTable = []int{
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f
|
||||
36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f
|
||||
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f
|
||||
}
|
||||
|
||||
// calculateMaskPenalty The mask penalty calculation is complicated.
|
||||
// See Table 21 of JISX0510:2004 (p.45) for details.
|
||||
// Basically it applies four rules and summate all penalties.
|
||||
func calculateMaskPenalty(matrix *ByteMatrix) int {
|
||||
return MaskUtil_applyMaskPenaltyRule1(matrix) +
|
||||
MaskUtil_applyMaskPenaltyRule2(matrix) +
|
||||
MaskUtil_applyMaskPenaltyRule3(matrix) +
|
||||
MaskUtil_applyMaskPenaltyRule4(matrix)
|
||||
}
|
||||
|
||||
func Encoder_encodeWithoutHint(content string, ecLevel decoder.ErrorCorrectionLevel) (*QRCode, gozxing.WriterException) {
|
||||
return Encoder_encode(content, ecLevel, nil)
|
||||
}
|
||||
|
||||
func Encoder_encode(content string, ecLevel decoder.ErrorCorrectionLevel, hints map[gozxing.EncodeHintType]interface{}) (*QRCode, gozxing.WriterException) {
|
||||
// Determine what character encoding has been specified by the caller, if any
|
||||
encoding := Encoder_DEFAULT_BYTE_MODE_ENCODING
|
||||
encodingHint, hasEncodingHint := hints[gozxing.EncodeHintType_CHARACTER_SET]
|
||||
if hasEncodingHint {
|
||||
if eci, ok := common.GetCharacterSetECIByName(fmt.Sprintf("%v", encodingHint)); ok {
|
||||
encoding = eci.GetCharset()
|
||||
} else {
|
||||
return nil, gozxing.NewWriterException(encodingHint)
|
||||
}
|
||||
}
|
||||
|
||||
// Pick an encoding mode appropriate for the content. Note that this will not attempt to use
|
||||
// multiple modes / segments even if that were more efficient. Twould be nice.
|
||||
mode := chooseMode(content, encoding)
|
||||
|
||||
// This will store the header information, like mode and
|
||||
// length, as well as "header" segments like an ECI segment.
|
||||
headerBits := gozxing.NewEmptyBitArray()
|
||||
|
||||
// Append ECI segment if applicable
|
||||
if mode == decoder.Mode_BYTE && hasEncodingHint {
|
||||
eci, ok := common.GetCharacterSetECI(encoding)
|
||||
if ok && eci != nil {
|
||||
appendECI(eci, headerBits)
|
||||
}
|
||||
}
|
||||
|
||||
// Append the FNC1 mode header for GS1 formatted data if applicable
|
||||
gs1FormatHint, hasGS1FormatHint := hints[gozxing.EncodeHintType_GS1_FORMAT]
|
||||
if hasGS1FormatHint {
|
||||
appendGS1, ok := gs1FormatHint.(bool)
|
||||
if !ok {
|
||||
s, ok := gs1FormatHint.(string)
|
||||
if ok {
|
||||
appendGS1, _ = strconv.ParseBool(s)
|
||||
}
|
||||
}
|
||||
if appendGS1 {
|
||||
// GS1 formatted codes are prefixed with a FNC1 in first position mode header
|
||||
appendModeInfo(decoder.Mode_FNC1_FIRST_POSITION, headerBits)
|
||||
}
|
||||
}
|
||||
|
||||
// (With ECI in place,) Write the mode marker
|
||||
appendModeInfo(mode, headerBits)
|
||||
|
||||
// Collect data within the main segment, separately, to count its size if needed. Don't add it to
|
||||
// main payload yet.
|
||||
dataBits := gozxing.NewEmptyBitArray()
|
||||
e := appendBytes(content, mode, dataBits, encoding)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
var version *decoder.Version
|
||||
if versionHint, ok := hints[gozxing.EncodeHintType_QR_VERSION]; ok {
|
||||
versionNumber, ok := versionHint.(int)
|
||||
if !ok {
|
||||
if s, ok := versionHint.(string); ok {
|
||||
versionNumber, _ = strconv.Atoi(s)
|
||||
}
|
||||
}
|
||||
var e error
|
||||
version, e = decoder.Version_GetVersionForNumber(versionNumber)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapWriterException(e)
|
||||
}
|
||||
bitsNeeded := calculateBitsNeeded(mode, headerBits, dataBits, version)
|
||||
if !willFit(bitsNeeded, version, ecLevel) {
|
||||
return nil, gozxing.NewWriterException("Data too big for requested version")
|
||||
}
|
||||
} else {
|
||||
version, e = recommendVersion(ecLevel, mode, headerBits, dataBits)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
|
||||
headerAndDataBits := gozxing.NewEmptyBitArray()
|
||||
headerAndDataBits.AppendBitArray(headerBits)
|
||||
// Find "length" of main segment and write it
|
||||
numLetters := len(content)
|
||||
if mode == decoder.Mode_BYTE {
|
||||
numLetters = dataBits.GetSizeInBytes()
|
||||
} else if mode == decoder.Mode_KANJI {
|
||||
numLetters = utf8.RuneCountInString(content)
|
||||
}
|
||||
|
||||
e = appendLengthInfo(numLetters, version, mode, headerAndDataBits)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
// Put data together into the overall payload
|
||||
headerAndDataBits.AppendBitArray(dataBits)
|
||||
|
||||
ecBlocks := version.GetECBlocksForLevel(ecLevel)
|
||||
numDataBytes := version.GetTotalCodewords() - ecBlocks.GetTotalECCodewords()
|
||||
|
||||
// Terminate the bits properly.
|
||||
e = terminateBits(numDataBytes, headerAndDataBits)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
// Interleave data bits with error correction code.
|
||||
finalBits, e := interleaveWithECBytes(
|
||||
headerAndDataBits, version.GetTotalCodewords(), numDataBytes, ecBlocks.GetNumBlocks())
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
qrCode := NewQRCode()
|
||||
|
||||
qrCode.SetECLevel(ecLevel)
|
||||
qrCode.SetMode(mode)
|
||||
qrCode.SetVersion(version)
|
||||
|
||||
// Choose the mask pattern and set to "qrCode".
|
||||
dimension := version.GetDimensionForVersion()
|
||||
matrix := NewByteMatrix(dimension, dimension)
|
||||
|
||||
// Enable manual selection of the pattern to be used via hint
|
||||
maskPattern := -1
|
||||
if hintMaskPattern, ok := hints[gozxing.EncodeHintType_QR_MASK_PATTERN]; ok {
|
||||
switch mask := hintMaskPattern.(type) {
|
||||
case int:
|
||||
maskPattern = mask
|
||||
case string:
|
||||
if m, e := strconv.Atoi(mask); e == nil {
|
||||
maskPattern = m
|
||||
}
|
||||
}
|
||||
if !QRCode_IsValidMaskPattern(maskPattern) {
|
||||
maskPattern = -1
|
||||
}
|
||||
}
|
||||
|
||||
if maskPattern == -1 {
|
||||
maskPattern, e = chooseMaskPattern(finalBits, ecLevel, version, matrix)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
qrCode.SetMaskPattern(maskPattern)
|
||||
|
||||
// Build the matrix and set it to "qrCode".
|
||||
_ = MatrixUtil_buildMatrix(finalBits, ecLevel, version, maskPattern, matrix)
|
||||
qrCode.SetMatrix(matrix)
|
||||
|
||||
return qrCode, nil
|
||||
}
|
||||
|
||||
// recommendVersion Decides the smallest version of QR code that will contain all of the provided data.
|
||||
// @throws WriterException if the data cannot fit in any version
|
||||
func recommendVersion(ecLevel decoder.ErrorCorrectionLevel, mode *decoder.Mode,
|
||||
headerBits *gozxing.BitArray, dataBits *gozxing.BitArray) (*decoder.Version, gozxing.WriterException) {
|
||||
// Hard part: need to know version to know how many bits length takes. But need to know how many
|
||||
// bits it takes to know version. First we take a guess at version by assuming version will be
|
||||
// the minimum, 1:
|
||||
version1, _ := decoder.Version_GetVersionForNumber(1)
|
||||
provisionalBitsNeeded := calculateBitsNeeded(mode, headerBits, dataBits, version1)
|
||||
provisionalVersion, e := chooseVersion(provisionalBitsNeeded, ecLevel)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
// Use that guess to calculate the right version. I am still not sure this works in 100% of cases.
|
||||
bitsNeeded := calculateBitsNeeded(mode, headerBits, dataBits, provisionalVersion)
|
||||
return chooseVersion(bitsNeeded, ecLevel)
|
||||
}
|
||||
|
||||
func calculateBitsNeeded(
|
||||
mode *decoder.Mode,
|
||||
headerBits *gozxing.BitArray,
|
||||
dataBits *gozxing.BitArray,
|
||||
version *decoder.Version) int {
|
||||
return headerBits.GetSize() + mode.GetCharacterCountBits(version) + dataBits.GetSize()
|
||||
}
|
||||
|
||||
// getAlphanumericCode returns the code point of the table used in alphanumeric mode or
|
||||
// if there is no corresponding code in the table.
|
||||
func getAlphanumericCode(code uint8) int {
|
||||
if int(code) < len(alphanumericTable) {
|
||||
return alphanumericTable[code]
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// chooseMode Choose the best mode by examining the content. Note that 'encoding' is used as a hint;
|
||||
// if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}.
|
||||
func chooseMode(content string, encoding textencoding.Encoding) *decoder.Mode {
|
||||
if common.StringUtils_SHIFT_JIS_CHARSET == encoding && isOnlyDoubleByteKanji(content) {
|
||||
// Choose Kanji mode if all input are double-byte characters
|
||||
return decoder.Mode_KANJI
|
||||
}
|
||||
hasNumeric := false
|
||||
hasAlphanumeric := false
|
||||
for i := 0; i < len(content); i++ {
|
||||
c := content[i]
|
||||
if c >= '0' && c <= '9' {
|
||||
hasNumeric = true
|
||||
} else if getAlphanumericCode(c) != -1 {
|
||||
hasAlphanumeric = true
|
||||
} else {
|
||||
return decoder.Mode_BYTE
|
||||
}
|
||||
}
|
||||
if hasAlphanumeric {
|
||||
return decoder.Mode_ALPHANUMERIC
|
||||
}
|
||||
if hasNumeric {
|
||||
return decoder.Mode_NUMERIC
|
||||
}
|
||||
return decoder.Mode_BYTE
|
||||
}
|
||||
|
||||
func isOnlyDoubleByteKanji(content string) bool {
|
||||
enc := common.StringUtils_SHIFT_JIS_CHARSET.NewEncoder()
|
||||
bytes, e := enc.Bytes([]byte(content))
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
length := len(bytes)
|
||||
if length%2 != 0 {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < length; i += 2 {
|
||||
byte1 := bytes[i] & 0xFF
|
||||
if (byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func chooseMaskPattern(bits *gozxing.BitArray, ecLevel decoder.ErrorCorrectionLevel,
|
||||
version *decoder.Version, matrix *ByteMatrix) (int, gozxing.WriterException) {
|
||||
|
||||
minPenalty := math.MaxInt32 // Lower penalty is better.
|
||||
bestMaskPattern := -1
|
||||
// We try all mask patterns to choose the best one.
|
||||
for maskPattern := 0; maskPattern < QRCode_NUM_MASK_PATERNS; maskPattern++ {
|
||||
e := MatrixUtil_buildMatrix(bits, ecLevel, version, maskPattern, matrix)
|
||||
if e != nil {
|
||||
return -1, gozxing.WrapWriterException(e)
|
||||
}
|
||||
penalty := calculateMaskPenalty(matrix)
|
||||
if penalty < minPenalty {
|
||||
minPenalty = penalty
|
||||
bestMaskPattern = maskPattern
|
||||
}
|
||||
}
|
||||
return bestMaskPattern, nil
|
||||
}
|
||||
|
||||
func chooseVersion(numInputBits int, ecLevel decoder.ErrorCorrectionLevel) (*decoder.Version, gozxing.WriterException) {
|
||||
for versionNum := 1; versionNum <= 40; versionNum++ {
|
||||
version, _ := decoder.Version_GetVersionForNumber(versionNum)
|
||||
if willFit(numInputBits, version, ecLevel) {
|
||||
return version, nil
|
||||
}
|
||||
}
|
||||
return nil, gozxing.NewWriterException("Data too big")
|
||||
}
|
||||
|
||||
// willFit returns true if the number of input bits will fit in a code with the specified version and
|
||||
// error correction level.
|
||||
func willFit(numInputBits int, version *decoder.Version, ecLevel decoder.ErrorCorrectionLevel) bool {
|
||||
// In the following comments, we use numbers of Version 7-H.
|
||||
// numBytes = 196
|
||||
numBytes := version.GetTotalCodewords()
|
||||
// getNumECBytes = 130
|
||||
ecBlocks := version.GetECBlocksForLevel(ecLevel)
|
||||
numEcBytes := ecBlocks.GetTotalECCodewords()
|
||||
// getNumDataBytes = 196 - 130 = 66
|
||||
numDataBytes := numBytes - numEcBytes
|
||||
totalInputBytes := (numInputBits + 7) / 8
|
||||
return numDataBytes >= totalInputBytes
|
||||
}
|
||||
|
||||
// terminateBits Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
|
||||
func terminateBits(numDataBytes int, bits *gozxing.BitArray) gozxing.WriterException {
|
||||
capacity := numDataBytes * 8
|
||||
if bits.GetSize() > capacity {
|
||||
return gozxing.NewWriterException(
|
||||
"data bits cannot fit in the QR Code %v > %v", bits.GetSize(), capacity)
|
||||
}
|
||||
for i := 0; i < 4 && bits.GetSize() < capacity; i++ {
|
||||
bits.AppendBit(false)
|
||||
}
|
||||
// Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
|
||||
// If the last byte isn't 8-bit aligned, we'll add padding bits.
|
||||
numBitsInLastByte := bits.GetSize() & 0x07
|
||||
if numBitsInLastByte > 0 {
|
||||
for i := numBitsInLastByte; i < 8; i++ {
|
||||
bits.AppendBit(false)
|
||||
}
|
||||
}
|
||||
// If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
|
||||
numPaddingBytes := numDataBytes - bits.GetSizeInBytes()
|
||||
for i := 0; i < numPaddingBytes; i++ {
|
||||
v := 0x11
|
||||
if (i & 0x1) == 0 {
|
||||
v = 0xEC
|
||||
}
|
||||
_ = bits.AppendBits(v, 8)
|
||||
}
|
||||
if bits.GetSize() != capacity {
|
||||
return gozxing.NewWriterException("bits.GetSize()=%d, capacity=&d", bits.GetSize(), capacity)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getNumDataBytesAndNumECBytesForBlockID Get number of data bytes and number of
|
||||
// error correction bytes for block id "blockID".
|
||||
// Returns are "numDataBytesInBlock", and "numECBytesInBlock".
|
||||
// See table 12 in 8.5.1 of JISX0510:2004 (p.30)
|
||||
func getNumDataBytesAndNumECBytesForBlockID(numTotalBytes, numDataBytes, numRSBlocks, blockID int) (int, int, gozxing.WriterException) {
|
||||
if blockID >= numRSBlocks {
|
||||
return 0, 0, gozxing.NewWriterException("Block ID too large")
|
||||
}
|
||||
// numRsBlocksInGroup2 = 196 % 5 = 1
|
||||
numRsBlocksInGroup2 := numTotalBytes % numRSBlocks
|
||||
// numRsBlocksInGroup1 = 5 - 1 = 4
|
||||
numRsBlocksInGroup1 := numRSBlocks - numRsBlocksInGroup2
|
||||
// numTotalBytesInGroup1 = 196 / 5 = 39
|
||||
numTotalBytesInGroup1 := numTotalBytes / numRSBlocks
|
||||
// numTotalBytesInGroup2 = 39 + 1 = 40
|
||||
numTotalBytesInGroup2 := numTotalBytesInGroup1 + 1
|
||||
// numDataBytesInGroup1 = 66 / 5 = 13
|
||||
numDataBytesInGroup1 := numDataBytes / numRSBlocks
|
||||
// numDataBytesInGroup2 = 13 + 1 = 14
|
||||
numDataBytesInGroup2 := numDataBytesInGroup1 + 1
|
||||
// numEcBytesInGroup1 = 39 - 13 = 26
|
||||
numEcBytesInGroup1 := numTotalBytesInGroup1 - numDataBytesInGroup1
|
||||
// numEcBytesInGroup2 = 40 - 14 = 26
|
||||
numEcBytesInGroup2 := numTotalBytesInGroup2 - numDataBytesInGroup2
|
||||
// Sanity checks.
|
||||
// 26 = 26
|
||||
if numEcBytesInGroup1 != numEcBytesInGroup2 {
|
||||
return 0, 0, gozxing.NewWriterException("EC bytes mismatch")
|
||||
}
|
||||
// 5 = 4 + 1.
|
||||
if numRSBlocks != numRsBlocksInGroup1+numRsBlocksInGroup2 {
|
||||
return 0, 0, gozxing.NewWriterException("RS blocks mismatch")
|
||||
}
|
||||
// 196 = (13 + 26) * 4 + (14 + 26) * 1
|
||||
if numTotalBytes !=
|
||||
((numDataBytesInGroup1+numEcBytesInGroup1)*numRsBlocksInGroup1)+
|
||||
((numDataBytesInGroup2+numEcBytesInGroup2)*numRsBlocksInGroup2) {
|
||||
return 0, 0, gozxing.NewWriterException("Total bytes mismatch")
|
||||
}
|
||||
|
||||
if blockID < numRsBlocksInGroup1 {
|
||||
return numDataBytesInGroup1, numEcBytesInGroup1, nil
|
||||
}
|
||||
return numDataBytesInGroup2, numEcBytesInGroup2, nil
|
||||
}
|
||||
|
||||
// interleaveWithECBytes Interleave "bits" with corresponding error correction bytes.
|
||||
// On success, store the result in "result".
|
||||
// The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
|
||||
func interleaveWithECBytes(bits *gozxing.BitArray, numTotalBytes, numDataBytes, numRSBlocks int) (*gozxing.BitArray, gozxing.WriterException) {
|
||||
|
||||
// "bits" must have "getNumDataBytes" bytes of data.
|
||||
if bits.GetSizeInBytes() != numDataBytes {
|
||||
return nil, gozxing.NewWriterException("Number of bits and data bytes does not match")
|
||||
}
|
||||
|
||||
// Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll
|
||||
// store the divided data bytes blocks and error correction bytes blocks into "blocks".
|
||||
dataBytesOffset := 0
|
||||
maxNumDataBytes := 0
|
||||
maxNumEcBytes := 0
|
||||
|
||||
// Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
|
||||
blocks := make([]*BlockPair, 0)
|
||||
|
||||
for i := 0; i < numRSBlocks; i++ {
|
||||
numDataBytesInBlock, numEcBytesInBlock, e := getNumDataBytesAndNumECBytesForBlockID(
|
||||
numTotalBytes, numDataBytes, numRSBlocks, i)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
size := numDataBytesInBlock
|
||||
dataBytes := make([]byte, size)
|
||||
bits.ToBytes(8*dataBytesOffset, dataBytes, 0, size)
|
||||
ecBytes, e := generateECBytes(dataBytes, numEcBytesInBlock)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
blocks = append(blocks, NewBlockPair(dataBytes, ecBytes))
|
||||
|
||||
if maxNumDataBytes < size {
|
||||
maxNumDataBytes = size
|
||||
}
|
||||
if maxNumEcBytes < len(ecBytes) {
|
||||
maxNumEcBytes = len(ecBytes)
|
||||
}
|
||||
dataBytesOffset += numDataBytesInBlock
|
||||
}
|
||||
if numDataBytes != dataBytesOffset {
|
||||
return nil, gozxing.NewWriterException("Data bytes does not match offset")
|
||||
}
|
||||
|
||||
result := gozxing.NewEmptyBitArray()
|
||||
|
||||
// First, place data blocks.
|
||||
for i := 0; i < maxNumDataBytes; i++ {
|
||||
for _, block := range blocks {
|
||||
dataBytes := block.GetDataBytes()
|
||||
if i < len(dataBytes) {
|
||||
_ = result.AppendBits(int(dataBytes[i]), 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Then, place error correction blocks.
|
||||
for i := 0; i < maxNumEcBytes; i++ {
|
||||
for _, block := range blocks {
|
||||
ecBytes := block.GetErrorCorrectionBytes()
|
||||
if i < len(ecBytes) {
|
||||
_ = result.AppendBits(int(ecBytes[i]), 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
if numTotalBytes != result.GetSizeInBytes() { // Should be same.
|
||||
return nil, gozxing.NewWriterException(
|
||||
"Interleaving error: %v and %v differ", numTotalBytes, result.GetSizeInBytes())
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func generateECBytes(dataBytes []byte, numEcBytesInBlock int) ([]byte, gozxing.WriterException) {
|
||||
numDataBytes := len(dataBytes)
|
||||
toEncode := make([]int, numDataBytes+numEcBytesInBlock)
|
||||
for i := 0; i < numDataBytes; i++ {
|
||||
toEncode[i] = int(dataBytes[i]) & 0xFF
|
||||
}
|
||||
e := reedsolomon.NewReedSolomonEncoder(reedsolomon.GenericGF_QR_CODE_FIELD_256).Encode(toEncode, numEcBytesInBlock)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapWriterException(e)
|
||||
}
|
||||
|
||||
ecBytes := make([]byte, numEcBytesInBlock)
|
||||
for i := 0; i < numEcBytesInBlock; i++ {
|
||||
ecBytes[i] = byte(toEncode[numDataBytes+i])
|
||||
}
|
||||
return ecBytes, nil
|
||||
}
|
||||
|
||||
// appendModeInfo Append mode info. On success, store the result in "bits".
|
||||
func appendModeInfo(mode *decoder.Mode, bits *gozxing.BitArray) {
|
||||
_ = bits.AppendBits(mode.GetBits(), 4)
|
||||
}
|
||||
|
||||
// appendLengthInfo Append length info. On success, store the result in "bits".
|
||||
func appendLengthInfo(numLetters int, version *decoder.Version, mode *decoder.Mode, bits *gozxing.BitArray) gozxing.WriterException {
|
||||
numBits := mode.GetCharacterCountBits(version)
|
||||
if numLetters >= (1 << uint(numBits)) {
|
||||
return gozxing.NewWriterException(
|
||||
"%v is bigger than %v", numLetters, (1 << uint(numBits)))
|
||||
}
|
||||
_ = bits.AppendBits(numLetters, numBits)
|
||||
return nil
|
||||
}
|
||||
|
||||
// appendBytes Append "bytes" in "mode" mode (encoding) into "bits".
|
||||
// On success, store the result in "bits".
|
||||
func appendBytes(content string, mode *decoder.Mode, bits *gozxing.BitArray, encoding textencoding.Encoding) gozxing.WriterException {
|
||||
switch mode {
|
||||
case decoder.Mode_NUMERIC:
|
||||
appendNumericBytes(content, bits)
|
||||
return nil
|
||||
case decoder.Mode_ALPHANUMERIC:
|
||||
return appendAlphanumericBytes(content, bits)
|
||||
case decoder.Mode_BYTE:
|
||||
return append8BitBytes(content, bits, encoding)
|
||||
case decoder.Mode_KANJI:
|
||||
return appendKanjiBytes(content, bits)
|
||||
default:
|
||||
return gozxing.NewWriterException("Invalid mode: %v", mode)
|
||||
}
|
||||
}
|
||||
|
||||
func appendNumericBytes(content string, bits *gozxing.BitArray) {
|
||||
length := len(content)
|
||||
i := 0
|
||||
for i < length {
|
||||
num1 := int(content[i]) - '0'
|
||||
if i+2 < length {
|
||||
// Encode three numeric letters in ten bits.
|
||||
num2 := int(content[i+1]) - '0'
|
||||
num3 := int(content[i+2]) - '0'
|
||||
_ = bits.AppendBits(num1*100+num2*10+num3, 10)
|
||||
i += 3
|
||||
} else if i+1 < length {
|
||||
// Encode two numeric letters in seven bits.
|
||||
num2 := int(content[i+1]) - '0'
|
||||
_ = bits.AppendBits(num1*10+num2, 7)
|
||||
i += 2
|
||||
} else {
|
||||
// Encode one numeric letter in four bits.
|
||||
_ = bits.AppendBits(num1, 4)
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func appendAlphanumericBytes(content string, bits *gozxing.BitArray) gozxing.WriterException {
|
||||
length := len(content)
|
||||
i := 0
|
||||
for i < length {
|
||||
code1 := getAlphanumericCode(content[i])
|
||||
if code1 == -1 {
|
||||
return gozxing.NewWriterException("appendAlphanumericBytes")
|
||||
}
|
||||
if i+1 < length {
|
||||
code2 := getAlphanumericCode(content[i+1])
|
||||
if code2 == -1 {
|
||||
return gozxing.NewWriterException("appendAlphanumericBytes")
|
||||
}
|
||||
// Encode two alphanumeric letters in 11 bits.
|
||||
_ = bits.AppendBits(code1*45+code2, 11)
|
||||
i += 2
|
||||
} else {
|
||||
// Encode one alphanumeric letter in six bits.
|
||||
_ = bits.AppendBits(code1, 6)
|
||||
i++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func append8BitBytes(content string, bits *gozxing.BitArray, encoding textencoding.Encoding) gozxing.WriterException {
|
||||
bytes := []byte(content)
|
||||
|
||||
var e error
|
||||
bytes, e = encoding.NewEncoder().Bytes([]byte(content))
|
||||
if e != nil {
|
||||
return gozxing.WrapWriterException(e)
|
||||
}
|
||||
|
||||
for _, b := range bytes {
|
||||
_ = bits.AppendBits(int(b), 8)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendKanjiBytes(content string, bits *gozxing.BitArray) gozxing.WriterException {
|
||||
enc := common.StringUtils_SHIFT_JIS_CHARSET.NewEncoder()
|
||||
bytes, e := enc.Bytes([]byte(content))
|
||||
if e != nil {
|
||||
return gozxing.WrapWriterException(e)
|
||||
}
|
||||
if len(bytes)%2 != 0 {
|
||||
return gozxing.NewWriterException("Kanji byte size not even")
|
||||
}
|
||||
maxI := len(bytes) - 1 // bytes.length must be even
|
||||
for i := 0; i < maxI; i += 2 {
|
||||
byte1 := int(bytes[i]) & 0xFF
|
||||
byte2 := int(bytes[i+1]) & 0xFF
|
||||
code := (byte1 << 8) | byte2
|
||||
subtracted := -1
|
||||
if code >= 0x8140 && code <= 0x9ffc {
|
||||
subtracted = code - 0x8140
|
||||
} else if code >= 0xe040 && code <= 0xebbf {
|
||||
subtracted = code - 0xc140
|
||||
}
|
||||
if subtracted == -1 {
|
||||
return gozxing.NewWriterException("Invalid byte sequence")
|
||||
}
|
||||
encoded := ((subtracted >> 8) * 0xc0) + (subtracted & 0xff)
|
||||
_ = bits.AppendBits(encoded, 13)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendECI(eci *common.CharacterSetECI, bits *gozxing.BitArray) {
|
||||
_ = bits.AppendBits(decoder.Mode_ECI.GetBits(), 4)
|
||||
// This is correct for values up to 127, which is all we need now.
|
||||
_ = bits.AppendBits(eci.GetValue(), 8)
|
||||
}
|
||||
206
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/mask_util.go
generated
vendored
Normal file
206
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/mask_util.go
generated
vendored
Normal file
@ -0,0 +1,206 @@
|
||||
package encoder
|
||||
|
||||
import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const (
|
||||
// Penalty weights from section 6.8.2.1
|
||||
maskUtilN1 = 3
|
||||
maskUtilN2 = 3
|
||||
maskUtilN3 = 40
|
||||
maskUtilN4 = 10
|
||||
)
|
||||
|
||||
// MaskUtil_applyMaskPenaltyRule1 Apply mask penalty rule 1 and return the penalty.
|
||||
// Find repetitive cells with the same color and give penalty to them. Example: 00000 or 11111.
|
||||
func MaskUtil_applyMaskPenaltyRule1(matrix *ByteMatrix) int {
|
||||
return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false)
|
||||
}
|
||||
|
||||
// MaskUtil_applyMaskPenaltyRule2 Apply mask penalty rule 2 and return the penalty.
|
||||
// Find 2x2 blocks with the same color and give penalty to them.
|
||||
// This is actually equivalent to the spec's rule, which is to find MxN blocks and give a penalty
|
||||
// proportional to (M-1)x(N-1), because this is the number of 2x2 blocks inside such a block.
|
||||
func MaskUtil_applyMaskPenaltyRule2(matrix *ByteMatrix) int {
|
||||
penalty := 0
|
||||
array := matrix.GetArray()
|
||||
width := matrix.GetWidth()
|
||||
height := matrix.GetHeight()
|
||||
for y := 0; y < height-1; y++ {
|
||||
arrayY := array[y]
|
||||
for x := 0; x < width-1; x++ {
|
||||
value := arrayY[x]
|
||||
if value == arrayY[x+1] && value == array[y+1][x] && value == array[y+1][x+1] {
|
||||
penalty++
|
||||
}
|
||||
}
|
||||
}
|
||||
return maskUtilN2 * penalty
|
||||
}
|
||||
|
||||
// MaskUtil_applyMaskPenaltyRule3 Apply mask penalty rule 3 and return the penalty.
|
||||
// Find consecutive runs of 1:1:3:1:1:4 starting with black, or 4:1:1:3:1:1 starting with white,
|
||||
// and give penalty to them. If we find patterns like 000010111010000, we give penalty once.
|
||||
func MaskUtil_applyMaskPenaltyRule3(matrix *ByteMatrix) int {
|
||||
numPenalties := 0
|
||||
array := matrix.GetArray()
|
||||
width := matrix.GetWidth()
|
||||
height := matrix.GetHeight()
|
||||
for y := 0; y < height; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
arrayY := array[y] // We can at least optimize this access
|
||||
if x+6 < width &&
|
||||
arrayY[x] == 1 &&
|
||||
arrayY[x+1] == 0 &&
|
||||
arrayY[x+2] == 1 &&
|
||||
arrayY[x+3] == 1 &&
|
||||
arrayY[x+4] == 1 &&
|
||||
arrayY[x+5] == 0 &&
|
||||
arrayY[x+6] == 1 &&
|
||||
(isWhiteHorizontal(arrayY, x-4, x) || isWhiteHorizontal(arrayY, x+7, x+11)) {
|
||||
numPenalties++
|
||||
}
|
||||
if y+6 < height &&
|
||||
array[y][x] == 1 &&
|
||||
array[y+1][x] == 0 &&
|
||||
array[y+2][x] == 1 &&
|
||||
array[y+3][x] == 1 &&
|
||||
array[y+4][x] == 1 &&
|
||||
array[y+5][x] == 0 &&
|
||||
array[y+6][x] == 1 &&
|
||||
(isWhiteVertical(array, x, y-4, y) || isWhiteVertical(array, x, y+7, y+11)) {
|
||||
numPenalties++
|
||||
}
|
||||
}
|
||||
}
|
||||
return numPenalties * maskUtilN3
|
||||
}
|
||||
|
||||
func isWhiteHorizontal(rowArray []int8, from, to int) bool {
|
||||
if from < 0 {
|
||||
from = 0
|
||||
}
|
||||
if to > len(rowArray) {
|
||||
to = len(rowArray)
|
||||
}
|
||||
for i := from; i < to; i++ {
|
||||
if rowArray[i] == 1 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isWhiteVertical(array [][]int8, col, from, to int) bool {
|
||||
if from < 0 {
|
||||
from = 0
|
||||
}
|
||||
if to > len(array) {
|
||||
to = len(array)
|
||||
}
|
||||
for i := from; i < to; i++ {
|
||||
if array[i][col] == 1 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MaskUtil_applyMaskPenaltyRule4 Apply mask penalty rule 4 and return the penalty.
|
||||
// Calculate the ratio of dark cells and give penalty if the ratio is far from 50%.
|
||||
// It gives 10 penalty for 5% distance.
|
||||
func MaskUtil_applyMaskPenaltyRule4(matrix *ByteMatrix) int {
|
||||
numDarkCells := 0
|
||||
array := matrix.GetArray()
|
||||
width := matrix.GetWidth()
|
||||
height := matrix.GetHeight()
|
||||
for y := 0; y < height; y++ {
|
||||
arrayY := array[y]
|
||||
for x := 0; x < width; x++ {
|
||||
if arrayY[x] == 1 {
|
||||
numDarkCells++
|
||||
}
|
||||
}
|
||||
}
|
||||
numTotalCells := matrix.GetHeight() * matrix.GetWidth()
|
||||
distance := numDarkCells*2 - numTotalCells
|
||||
if distance < 0 {
|
||||
distance = -distance
|
||||
}
|
||||
fivePercentVariances := distance * 10 / numTotalCells
|
||||
return fivePercentVariances * maskUtilN4
|
||||
}
|
||||
|
||||
// MaskUtil_getDataMaskBit Return the mask bit for "getMaskPattern" at "x" and "y".
|
||||
// See 8.8 of JISX0510:2004 for mask pattern conditions.
|
||||
func MaskUtil_getDataMaskBit(maskPattern, x, y int) (bool, error) {
|
||||
var intermediate int
|
||||
switch maskPattern {
|
||||
case 0:
|
||||
intermediate = (y + x) & 0x1
|
||||
break
|
||||
case 1:
|
||||
intermediate = y & 0x1
|
||||
break
|
||||
case 2:
|
||||
intermediate = x % 3
|
||||
break
|
||||
case 3:
|
||||
intermediate = (y + x) % 3
|
||||
break
|
||||
case 4:
|
||||
intermediate = ((y / 2) + (x / 3)) & 0x1
|
||||
break
|
||||
case 5:
|
||||
temp := y * x
|
||||
intermediate = (temp & 0x1) + (temp % 3)
|
||||
break
|
||||
case 6:
|
||||
temp := y * x
|
||||
intermediate = ((temp & 0x1) + (temp % 3)) & 0x1
|
||||
break
|
||||
case 7:
|
||||
temp := y * x
|
||||
intermediate = ((temp % 3) + ((y + x) & 0x1)) & 0x1
|
||||
break
|
||||
default:
|
||||
return false, errors.Errorf("IllegalArgumentException: Invalid mask pattern: %d", maskPattern)
|
||||
}
|
||||
return (intermediate == 0), nil
|
||||
}
|
||||
|
||||
func applyMaskPenaltyRule1Internal(matrix *ByteMatrix, isHorizontal bool) int {
|
||||
penalty := 0
|
||||
iLimit := matrix.GetWidth()
|
||||
jLimit := matrix.GetHeight()
|
||||
if isHorizontal {
|
||||
iLimit, jLimit = jLimit, iLimit
|
||||
}
|
||||
array := matrix.GetArray()
|
||||
for i := 0; i < iLimit; i++ {
|
||||
numSameBitCells := 0
|
||||
prevBit := -1
|
||||
for j := 0; j < jLimit; j++ {
|
||||
var bit int
|
||||
if isHorizontal {
|
||||
bit = int(array[i][j])
|
||||
} else {
|
||||
bit = int(array[j][i])
|
||||
}
|
||||
if bit == prevBit {
|
||||
numSameBitCells++
|
||||
} else {
|
||||
if numSameBitCells >= 5 {
|
||||
penalty += maskUtilN1 + (numSameBitCells - 5)
|
||||
}
|
||||
numSameBitCells = 1 // Include the cell itself.
|
||||
prevBit = bit
|
||||
}
|
||||
}
|
||||
if numSameBitCells >= 5 {
|
||||
penalty += maskUtilN1 + (numSameBitCells - 5)
|
||||
}
|
||||
}
|
||||
return penalty
|
||||
}
|
||||
509
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/matrix_util.go
generated
vendored
Normal file
509
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/matrix_util.go
generated
vendored
Normal file
@ -0,0 +1,509 @@
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
"github.com/makiuchi-d/gozxing/qrcode/decoder"
|
||||
)
|
||||
|
||||
var (
|
||||
matrixUtil_POSITION_DETECTION_PATTERN = [][]int8{
|
||||
{1, 1, 1, 1, 1, 1, 1},
|
||||
{1, 0, 0, 0, 0, 0, 1},
|
||||
{1, 0, 1, 1, 1, 0, 1},
|
||||
{1, 0, 1, 1, 1, 0, 1},
|
||||
{1, 0, 1, 1, 1, 0, 1},
|
||||
{1, 0, 0, 0, 0, 0, 1},
|
||||
{1, 1, 1, 1, 1, 1, 1},
|
||||
}
|
||||
|
||||
matrixUtil_POSITION_ADJUSTMENT_PATTERN = [][]int8{
|
||||
{1, 1, 1, 1, 1},
|
||||
{1, 0, 0, 0, 1},
|
||||
{1, 0, 1, 0, 1},
|
||||
{1, 0, 0, 0, 1},
|
||||
{1, 1, 1, 1, 1},
|
||||
}
|
||||
|
||||
// From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu.
|
||||
matrixUtil_POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = [][]int{
|
||||
{-1, -1, -1, -1, -1, -1, -1}, // Version 1
|
||||
{6, 18, -1, -1, -1, -1, -1}, // Version 2
|
||||
{6, 22, -1, -1, -1, -1, -1}, // Version 3
|
||||
{6, 26, -1, -1, -1, -1, -1}, // Version 4
|
||||
{6, 30, -1, -1, -1, -1, -1}, // Version 5
|
||||
{6, 34, -1, -1, -1, -1, -1}, // Version 6
|
||||
{6, 22, 38, -1, -1, -1, -1}, // Version 7
|
||||
{6, 24, 42, -1, -1, -1, -1}, // Version 8
|
||||
{6, 26, 46, -1, -1, -1, -1}, // Version 9
|
||||
{6, 28, 50, -1, -1, -1, -1}, // Version 10
|
||||
{6, 30, 54, -1, -1, -1, -1}, // Version 11
|
||||
{6, 32, 58, -1, -1, -1, -1}, // Version 12
|
||||
{6, 34, 62, -1, -1, -1, -1}, // Version 13
|
||||
{6, 26, 46, 66, -1, -1, -1}, // Version 14
|
||||
{6, 26, 48, 70, -1, -1, -1}, // Version 15
|
||||
{6, 26, 50, 74, -1, -1, -1}, // Version 16
|
||||
{6, 30, 54, 78, -1, -1, -1}, // Version 17
|
||||
{6, 30, 56, 82, -1, -1, -1}, // Version 18
|
||||
{6, 30, 58, 86, -1, -1, -1}, // Version 19
|
||||
{6, 34, 62, 90, -1, -1, -1}, // Version 20
|
||||
{6, 28, 50, 72, 94, -1, -1}, // Version 21
|
||||
{6, 26, 50, 74, 98, -1, -1}, // Version 22
|
||||
{6, 30, 54, 78, 102, -1, -1}, // Version 23
|
||||
{6, 28, 54, 80, 106, -1, -1}, // Version 24
|
||||
{6, 32, 58, 84, 110, -1, -1}, // Version 25
|
||||
{6, 30, 58, 86, 114, -1, -1}, // Version 26
|
||||
{6, 34, 62, 90, 118, -1, -1}, // Version 27
|
||||
{6, 26, 50, 74, 98, 122, -1}, // Version 28
|
||||
{6, 30, 54, 78, 102, 126, -1}, // Version 29
|
||||
{6, 26, 52, 78, 104, 130, -1}, // Version 30
|
||||
{6, 30, 56, 82, 108, 134, -1}, // Version 31
|
||||
{6, 34, 60, 86, 112, 138, -1}, // Version 32
|
||||
{6, 30, 58, 86, 114, 142, -1}, // Version 33
|
||||
{6, 34, 62, 90, 118, 146, -1}, // Version 34
|
||||
{6, 30, 54, 78, 102, 126, 150}, // Version 35
|
||||
{6, 24, 50, 76, 102, 128, 154}, // Version 36
|
||||
{6, 28, 54, 80, 106, 132, 158}, // Version 37
|
||||
{6, 32, 58, 84, 110, 136, 162}, // Version 38
|
||||
{6, 26, 54, 82, 110, 138, 166}, // Version 39
|
||||
{6, 30, 58, 86, 114, 142, 170}, // Version 40
|
||||
}
|
||||
|
||||
// Type info cells at the left top corner.
|
||||
matrixUtil_TYPE_INFO_COORDINATES = [][]int{
|
||||
{8, 0},
|
||||
{8, 1},
|
||||
{8, 2},
|
||||
{8, 3},
|
||||
{8, 4},
|
||||
{8, 5},
|
||||
{8, 7},
|
||||
{8, 8},
|
||||
{7, 8},
|
||||
{5, 8},
|
||||
{4, 8},
|
||||
{3, 8},
|
||||
{2, 8},
|
||||
{1, 8},
|
||||
{0, 8},
|
||||
}
|
||||
|
||||
// From Appendix D in JISX0510:2004 (p. 67)
|
||||
matrixUtil_VERSION_INFO_POLY = 0x1f25 // 1 1111 0010 0101
|
||||
|
||||
// From Appendix C in JISX0510:2004 (p.65).
|
||||
matrixUtil_TYPE_INFO_POLY = 0x537
|
||||
matrixUtil_TYPE_INFO_MASK_PATTERN = 0x5412
|
||||
)
|
||||
|
||||
// MatrixUtil_clearMatrix Set all cells to -1.
|
||||
// -1 means that the cell is empty (not set yet).
|
||||
func clearMatrix(matrix *ByteMatrix) {
|
||||
matrix.Clear(-1)
|
||||
}
|
||||
|
||||
// MatrixUtil_buildMatrix Build 2D matrix of QR Code from
|
||||
// "dataBits" with "ecLevel", "version" and "getMaskPattern".
|
||||
// On success, store the result in "matrix" and return true.
|
||||
func MatrixUtil_buildMatrix(
|
||||
dataBits *gozxing.BitArray,
|
||||
ecLevel decoder.ErrorCorrectionLevel,
|
||||
version *decoder.Version,
|
||||
maskPattern int,
|
||||
matrix *ByteMatrix) error {
|
||||
|
||||
clearMatrix(matrix)
|
||||
|
||||
e := embedBasicPatterns(version, matrix)
|
||||
if e == nil {
|
||||
// Type information appear with any version.
|
||||
e = embedTypeInfo(ecLevel, maskPattern, matrix)
|
||||
}
|
||||
if e == nil {
|
||||
// Version info appear if version >= 7.
|
||||
e = maybeEmbedVersionInfo(version, matrix)
|
||||
}
|
||||
if e == nil {
|
||||
// Data should be embedded at end.
|
||||
e = embedDataBits(dataBits, maskPattern, matrix)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// embedBasicPatterns Embed basic patterns. On success, modify the matrix and return true.
|
||||
// The basic patterns are:
|
||||
// - Position detection patterns
|
||||
// - Timing patterns
|
||||
// - Dark dot at the left bottom corner
|
||||
// - Position adjustment patterns, if need be
|
||||
func embedBasicPatterns(version *decoder.Version, matrix *ByteMatrix) gozxing.WriterException {
|
||||
// Let's get started with embedding big squares at corners.
|
||||
e := embedPositionDetectionPatternsAndSeparators(matrix)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
// Then, embed the dark dot at the left bottom corner.
|
||||
e = embedDarkDotAtLeftBottomCorner(matrix)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
// Position adjustment patterns appear if version >= 2.
|
||||
maybeEmbedPositionAdjustmentPatterns(version, matrix)
|
||||
|
||||
// Timing patterns should be embedded after position adj. patterns.
|
||||
embedTimingPatterns(matrix)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// embedTypeInfo Embed type information. On success, modify the matrix.
|
||||
func embedTypeInfo(ecLevel decoder.ErrorCorrectionLevel, maskPattern int, matrix *ByteMatrix) gozxing.WriterException {
|
||||
typeInfoBits := gozxing.NewEmptyBitArray()
|
||||
|
||||
e := makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
for i := 0; i < typeInfoBits.GetSize(); i++ {
|
||||
// Place bits in LSB to MSB order. LSB (least significant bit) is the last value in
|
||||
// "typeInfoBits".
|
||||
bit := typeInfoBits.Get(typeInfoBits.GetSize() - 1 - i)
|
||||
|
||||
// Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46).
|
||||
coordinates := matrixUtil_TYPE_INFO_COORDINATES[i]
|
||||
x1 := coordinates[0]
|
||||
y1 := coordinates[1]
|
||||
matrix.SetBool(x1, y1, bit)
|
||||
|
||||
var x2, y2 int
|
||||
if i < 8 {
|
||||
// Right top corner.
|
||||
x2 = matrix.GetWidth() - i - 1
|
||||
y2 = 8
|
||||
} else {
|
||||
// Left bottom corner.
|
||||
x2 = 8
|
||||
y2 = matrix.GetHeight() - 7 + (i - 8)
|
||||
matrix.SetBool(x2, y2, bit)
|
||||
}
|
||||
matrix.SetBool(x2, y2, bit)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// maybeEmbedVersionInfo Embed version information if need be.
|
||||
// On success, modify the matrix and return true.
|
||||
// See 8.10 of JISX0510:2004 (p.47) for how to embed version information.
|
||||
func maybeEmbedVersionInfo(version *decoder.Version, matrix *ByteMatrix) gozxing.WriterException {
|
||||
if version.GetVersionNumber() < 7 { // Version info is necessary if version >= 7.
|
||||
return nil // Don't need version info.
|
||||
}
|
||||
versionInfoBits := gozxing.NewEmptyBitArray()
|
||||
e := makeVersionInfoBits(version, versionInfoBits)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
bitIndex := 6*3 - 1 // It will decrease from 17 to 0.
|
||||
for i := 0; i < 6; i++ {
|
||||
for j := 0; j < 3; j++ {
|
||||
// Place bits in LSB (least significant bit) to MSB order.
|
||||
bit := versionInfoBits.Get(bitIndex)
|
||||
bitIndex--
|
||||
// Left bottom corner.
|
||||
matrix.SetBool(i, matrix.GetHeight()-11+j, bit)
|
||||
// Right bottom corner.
|
||||
matrix.SetBool(matrix.GetHeight()-11+j, i, bit)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// embedDataBits Embed "dataBits" using "getMaskPattern".
|
||||
// On success, modify the matrix and return true.
|
||||
// For debugging purposes, it skips masking process if "getMaskPattern" is -1.
|
||||
// See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.
|
||||
func embedDataBits(dataBits *gozxing.BitArray, maskPattern int, matrix *ByteMatrix) gozxing.WriterException {
|
||||
bitIndex := 0
|
||||
direction := -1
|
||||
// Start from the right bottom cell.
|
||||
x := matrix.GetWidth() - 1
|
||||
y := matrix.GetHeight() - 1
|
||||
for x > 0 {
|
||||
// Skip the vertical timing pattern.
|
||||
if x == 6 {
|
||||
x -= 1
|
||||
}
|
||||
for y >= 0 && y < matrix.GetHeight() {
|
||||
for i := 0; i < 2; i++ {
|
||||
xx := x - i
|
||||
// Skip the cell if it's not empty.
|
||||
if !isEmpty(matrix.Get(xx, y)) {
|
||||
continue
|
||||
}
|
||||
var bit bool
|
||||
if bitIndex < dataBits.GetSize() {
|
||||
bit = dataBits.Get(bitIndex)
|
||||
bitIndex++
|
||||
} else {
|
||||
// Padding bit. If there is no bit left, we'll fill the left cells with 0, as described
|
||||
// in 8.4.9 of JISX0510:2004 (p. 24).
|
||||
bit = false
|
||||
}
|
||||
|
||||
// Skip masking if mask_pattern is -1.
|
||||
if maskPattern != -1 {
|
||||
maskBit, e := MaskUtil_getDataMaskBit(maskPattern, xx, y)
|
||||
if e != nil {
|
||||
return gozxing.WrapWriterException(e)
|
||||
}
|
||||
if maskBit {
|
||||
bit = !bit
|
||||
}
|
||||
}
|
||||
matrix.SetBool(xx, y, bit)
|
||||
}
|
||||
y += direction
|
||||
}
|
||||
direction = -direction // Reverse the direction.
|
||||
y += direction
|
||||
x -= 2 // Move to the left.
|
||||
}
|
||||
// All bits should be consumed.
|
||||
if bitIndex != dataBits.GetSize() {
|
||||
return gozxing.NewWriterException(
|
||||
"Not all bits consumed: %v/%v", bitIndex, dataBits.GetSize())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findMSBSet Return the position of the most significant bit set (to one) in the "value".
|
||||
// The most significant bit is position 32. If there is no bit set, return 0. Examples:
|
||||
// - findMSBSet(0) => 0
|
||||
// - findMSBSet(1) => 1
|
||||
// - findMSBSet(255) => 8
|
||||
func findMSBSet(value int) int {
|
||||
return 32 - bits.LeadingZeros32(uint32(value))
|
||||
}
|
||||
|
||||
// calculateBCHCode Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly".
|
||||
// The BCH code is used for encoding type information and version information.
|
||||
// Example: Calculation of version information of 7.
|
||||
// f(x) is created from 7.
|
||||
// - 7 = 000111 in 6 bits
|
||||
// - f(x) = x^2 + x^1 + x^0
|
||||
// g(x) is given by the standard (p. 67)
|
||||
// - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1
|
||||
// Multiply f(x) by x^(18 - 6)
|
||||
// - f'(x) = f(x) * x^(18 - 6)
|
||||
// - f'(x) = x^14 + x^13 + x^12
|
||||
// Calculate the remainder of f'(x) / g(x)
|
||||
// x^2
|
||||
// __________________________________________________
|
||||
// g(x) )x^14 + x^13 + x^12
|
||||
// x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2
|
||||
// --------------------------------------------------
|
||||
// x^11 + x^10 + x^7 + x^4 + x^2
|
||||
//
|
||||
// The remainder is x^11 + x^10 + x^7 + x^4 + x^2
|
||||
// Encode it in binary: 110010010100
|
||||
// The return value is 0xc94 (1100 1001 0100)
|
||||
//
|
||||
// Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit
|
||||
// operations. We don't care if coefficients are positive or negative.
|
||||
func calculateBCHCode(value, poly int) (int, error) {
|
||||
if poly == 0 {
|
||||
return 0, errors.New("IllegalArgumentException: 0 polynomial")
|
||||
}
|
||||
// If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1
|
||||
// from 13 to make it 12.
|
||||
msbSetInPoly := findMSBSet(poly)
|
||||
|
||||
value <<= uint(msbSetInPoly - 1)
|
||||
// Do the division business using exclusive-or operations.
|
||||
for findMSBSet(value) >= msbSetInPoly {
|
||||
value ^= poly << uint(findMSBSet(value)-msbSetInPoly)
|
||||
}
|
||||
// Now the "value" is the remainder (i.e. the BCH code)
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// maskTypeInfoBits Make bit vector of type information.
|
||||
// On success, store the result in "bits" and return true.
|
||||
// Encode error correction level and mask pattern. See 8.9 of JISX0510:2004 (p.45) for details.
|
||||
func makeTypeInfoBits(ecLevel decoder.ErrorCorrectionLevel, maskPattern int, bits *gozxing.BitArray) gozxing.WriterException {
|
||||
if !QRCode_IsValidMaskPattern(maskPattern) {
|
||||
return gozxing.NewWriterException("Invalid mask pattern")
|
||||
}
|
||||
typeInfo := (ecLevel.GetBits() << 3) | maskPattern
|
||||
bits.AppendBits(typeInfo, 5)
|
||||
|
||||
bchCode, _ := calculateBCHCode(typeInfo, matrixUtil_TYPE_INFO_POLY)
|
||||
bits.AppendBits(bchCode, 10)
|
||||
|
||||
maskBits := gozxing.NewEmptyBitArray()
|
||||
maskBits.AppendBits(matrixUtil_TYPE_INFO_MASK_PATTERN, 15)
|
||||
bits.Xor(maskBits)
|
||||
|
||||
if bits.GetSize() != 15 { // Just in case.
|
||||
return gozxing.NewWriterException(
|
||||
"should not happen but we got: %v", bits.GetSize())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// makeVersionInfoBits Make bit vector of version information.
|
||||
// On success, store the result in "bits" and return true.
|
||||
// See 8.10 of JISX0510:2004 (p.45) for details.
|
||||
func makeVersionInfoBits(version *decoder.Version, bits *gozxing.BitArray) gozxing.WriterException {
|
||||
bits.AppendBits(version.GetVersionNumber(), 6)
|
||||
bchCode, _ := calculateBCHCode(version.GetVersionNumber(), matrixUtil_VERSION_INFO_POLY)
|
||||
bits.AppendBits(bchCode, 12)
|
||||
|
||||
if bits.GetSize() != 18 { // Just in case.
|
||||
return gozxing.NewWriterException(
|
||||
"should not happen but we got: %v", bits.GetSize())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isEmpty Check if "value" is empty.
|
||||
func isEmpty(value int8) bool {
|
||||
return value == -1
|
||||
}
|
||||
|
||||
func embedTimingPatterns(matrix *ByteMatrix) {
|
||||
// -8 is for skipping position detection patterns (size 7), and two horizontal/vertical
|
||||
// separation patterns (size 1). Thus, 8 = 7 + 1.
|
||||
for i := 8; i < matrix.GetWidth()-8; i++ {
|
||||
bit := int8((i + 1) % 2)
|
||||
// Horizontal line.
|
||||
if isEmpty(matrix.Get(i, 6)) {
|
||||
matrix.Set(i, 6, bit)
|
||||
}
|
||||
// Vertical line.
|
||||
if isEmpty(matrix.Get(6, i)) {
|
||||
matrix.Set(6, i, bit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// embedDarkDotAtLeftBottomCorner Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)
|
||||
func embedDarkDotAtLeftBottomCorner(matrix *ByteMatrix) gozxing.WriterException {
|
||||
if matrix.Get(8, matrix.GetHeight()-8) == 0 {
|
||||
return gozxing.NewWriterException("embedDarkDotAtLeftBottomCorner")
|
||||
}
|
||||
matrix.Set(8, matrix.GetHeight()-8, 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func embedHorizontalSeparationPattern(xStart, yStart int, matrix *ByteMatrix) gozxing.WriterException {
|
||||
for x := 0; x < 8; x++ {
|
||||
if !isEmpty(matrix.Get(xStart+x, yStart)) {
|
||||
return gozxing.NewWriterException(
|
||||
"embedHorizontalSeparationPattern(%d, %d)", xStart, yStart)
|
||||
}
|
||||
matrix.Set(xStart+x, yStart, 0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func embedVerticalSeparationPattern(xStart, yStart int, matrix *ByteMatrix) gozxing.WriterException {
|
||||
for y := 0; y < 7; y++ {
|
||||
if !isEmpty(matrix.Get(xStart, yStart+y)) {
|
||||
return gozxing.NewWriterException(
|
||||
"embedVerticalSeparationPattern(%d, %d)", xStart, yStart)
|
||||
}
|
||||
matrix.Set(xStart, yStart+y, 0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func embedPositionAdjustmentPattern(xStart, yStart int, matrix *ByteMatrix) {
|
||||
for y := 0; y < 5; y++ {
|
||||
patternY := matrixUtil_POSITION_ADJUSTMENT_PATTERN[y]
|
||||
for x := 0; x < 5; x++ {
|
||||
matrix.Set(xStart+x, yStart+y, patternY[x])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func embedPositionDetectionPattern(xStart, yStart int, matrix *ByteMatrix) {
|
||||
for y := 0; y < 7; y++ {
|
||||
patternY := matrixUtil_POSITION_DETECTION_PATTERN[y]
|
||||
for x := 0; x < 7; x++ {
|
||||
matrix.Set(xStart+x, yStart+y, patternY[x])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// embedPositionDetectionPatternsAndSeparators Embed position detection patterns and
|
||||
// surrounding vertical/horizontal separators.
|
||||
func embedPositionDetectionPatternsAndSeparators(matrix *ByteMatrix) gozxing.WriterException {
|
||||
// Embed three big squares at corners.
|
||||
pdpWidth := len(matrixUtil_POSITION_DETECTION_PATTERN[0])
|
||||
// Left top corner.
|
||||
embedPositionDetectionPattern(0, 0, matrix)
|
||||
// Right top corner.
|
||||
embedPositionDetectionPattern(matrix.GetWidth()-pdpWidth, 0, matrix)
|
||||
// Left bottom corner.
|
||||
embedPositionDetectionPattern(0, matrix.GetWidth()-pdpWidth, matrix)
|
||||
|
||||
// Embed horizontal separation patterns around the squares.
|
||||
hspWidth := 8
|
||||
// Left top corner.
|
||||
e := embedHorizontalSeparationPattern(0, hspWidth-1, matrix)
|
||||
if e == nil {
|
||||
// Right top corner.
|
||||
e = embedHorizontalSeparationPattern(matrix.GetWidth()-hspWidth, hspWidth-1, matrix)
|
||||
}
|
||||
if e == nil {
|
||||
// Left bottom corner.
|
||||
e = embedHorizontalSeparationPattern(0, matrix.GetWidth()-hspWidth, matrix)
|
||||
}
|
||||
|
||||
// Embed vertical separation patterns around the squares.
|
||||
vspSize := 7
|
||||
if e == nil {
|
||||
// Left top corner.
|
||||
e = embedVerticalSeparationPattern(vspSize, 0, matrix)
|
||||
}
|
||||
if e == nil {
|
||||
// Right top corner.
|
||||
e = embedVerticalSeparationPattern(matrix.GetHeight()-vspSize-1, 0, matrix)
|
||||
}
|
||||
if e == nil {
|
||||
// Left bottom corner.
|
||||
e = embedVerticalSeparationPattern(vspSize, matrix.GetHeight()-vspSize, matrix)
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// maybeEmbedPositionAdjustmentPatterns Embed position adjustment patterns if need be.
|
||||
func maybeEmbedPositionAdjustmentPatterns(version *decoder.Version, matrix *ByteMatrix) {
|
||||
if version.GetVersionNumber() < 2 { // The patterns appear if version >= 2
|
||||
return
|
||||
}
|
||||
index := version.GetVersionNumber() - 1
|
||||
coordinates := matrixUtil_POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index]
|
||||
for _, y := range coordinates {
|
||||
if y >= 0 {
|
||||
for _, x := range coordinates {
|
||||
if x >= 0 && isEmpty(matrix.Get(x, y)) {
|
||||
// If the cell is unset, we embed the position adjustment pattern here.
|
||||
// -2 is necessary since the x/y coordinates point to the center of the pattern, not the
|
||||
// left top corner.
|
||||
embedPositionAdjustmentPattern(x-2, y-2, matrix)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/qrcode.go
generated
vendored
Normal file
88
vendor/github.com/makiuchi-d/gozxing/qrcode/encoder/qrcode.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/makiuchi-d/gozxing/qrcode/decoder"
|
||||
)
|
||||
|
||||
const QRCode_NUM_MASK_PATERNS = 8
|
||||
|
||||
type QRCode struct {
|
||||
mode *decoder.Mode
|
||||
ecLevel decoder.ErrorCorrectionLevel
|
||||
version *decoder.Version
|
||||
maskPattern int
|
||||
matrix *ByteMatrix
|
||||
}
|
||||
|
||||
func NewQRCode() *QRCode {
|
||||
return &QRCode{
|
||||
maskPattern: -1,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *QRCode) GetMode() *decoder.Mode {
|
||||
return this.mode
|
||||
}
|
||||
|
||||
func (this *QRCode) GetECLevel() decoder.ErrorCorrectionLevel {
|
||||
return this.ecLevel
|
||||
}
|
||||
|
||||
func (this *QRCode) GetVersion() *decoder.Version {
|
||||
return this.version
|
||||
}
|
||||
|
||||
func (this *QRCode) GetMaskPattern() int {
|
||||
return this.maskPattern
|
||||
}
|
||||
|
||||
func (this *QRCode) GetMatrix() *ByteMatrix {
|
||||
return this.matrix
|
||||
}
|
||||
|
||||
func (this *QRCode) String() string {
|
||||
result := make([]byte, 0, 200)
|
||||
result = append(result, "<<\n"...)
|
||||
result = append(result, " mode: "...)
|
||||
result = append(result, this.mode.String()...)
|
||||
result = append(result, "\n ecLevel: "...)
|
||||
result = append(result, this.ecLevel.String()...)
|
||||
result = append(result, "\n version: "...)
|
||||
result = append(result, this.version.String()...)
|
||||
result = append(result, "\n maskPattern: "...)
|
||||
result = append(result, strconv.Itoa(this.maskPattern)...)
|
||||
if this.matrix == nil {
|
||||
result = append(result, "\n matrix: nil\n"...)
|
||||
} else {
|
||||
result = append(result, "\n matrix:\n"...)
|
||||
result = append(result, this.matrix.String()...)
|
||||
}
|
||||
result = append(result, ">>\n"...)
|
||||
return string(result)
|
||||
}
|
||||
|
||||
func (this *QRCode) SetMode(value *decoder.Mode) {
|
||||
this.mode = value
|
||||
}
|
||||
|
||||
func (this *QRCode) SetECLevel(value decoder.ErrorCorrectionLevel) {
|
||||
this.ecLevel = value
|
||||
}
|
||||
|
||||
func (this *QRCode) SetVersion(value *decoder.Version) {
|
||||
this.version = value
|
||||
}
|
||||
|
||||
func (this *QRCode) SetMaskPattern(value int) {
|
||||
this.maskPattern = value
|
||||
}
|
||||
|
||||
func (this *QRCode) SetMatrix(value *ByteMatrix) {
|
||||
this.matrix = value
|
||||
}
|
||||
|
||||
func QRCode_IsValidMaskPattern(maskPattern int) bool {
|
||||
return maskPattern >= 0 && maskPattern < QRCode_NUM_MASK_PATERNS
|
||||
}
|
||||
199
vendor/github.com/makiuchi-d/gozxing/qrcode/qrcode_reader.go
generated
vendored
Normal file
199
vendor/github.com/makiuchi-d/gozxing/qrcode/qrcode_reader.go
generated
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
package qrcode
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
"github.com/makiuchi-d/gozxing/common"
|
||||
"github.com/makiuchi-d/gozxing/common/util"
|
||||
"github.com/makiuchi-d/gozxing/qrcode/decoder"
|
||||
"github.com/makiuchi-d/gozxing/qrcode/detector"
|
||||
)
|
||||
|
||||
type QRCodeReader struct {
|
||||
decoder *decoder.Decoder
|
||||
}
|
||||
|
||||
func NewQRCodeReader() gozxing.Reader {
|
||||
return &QRCodeReader{
|
||||
decoder.NewDecoder(),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *QRCodeReader) GetDecoder() *decoder.Decoder {
|
||||
return this.decoder
|
||||
}
|
||||
|
||||
func (this *QRCodeReader) DecodeWithoutHints(image *gozxing.BinaryBitmap) (*gozxing.Result, error) {
|
||||
return this.Decode(image, nil)
|
||||
}
|
||||
|
||||
func (this *QRCodeReader) Decode(image *gozxing.BinaryBitmap, hints map[gozxing.DecodeHintType]interface{}) (*gozxing.Result, error) {
|
||||
var decoderResult *common.DecoderResult
|
||||
var points []gozxing.ResultPoint
|
||||
|
||||
blackMatrix, e := image.GetBlackMatrix()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
if _, ok := hints[gozxing.DecodeHintType_PURE_BARCODE]; ok {
|
||||
bits, e := this.extractPureBits(blackMatrix)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
decoderResult, e = this.decoder.Decode(bits, hints)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
points = []gozxing.ResultPoint{}
|
||||
} else {
|
||||
detectorResult, e := detector.NewDetector(blackMatrix).Detect(hints)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
decoderResult, e = this.decoder.Decode(detectorResult.GetBits(), hints)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
points = detectorResult.GetPoints()
|
||||
}
|
||||
|
||||
// If the code was mirrored: swap the bottom-left and the top-right points.
|
||||
if metadata, ok := decoderResult.GetOther().(*decoder.QRCodeDecoderMetaData); ok {
|
||||
metadata.ApplyMirroredCorrection(points)
|
||||
}
|
||||
|
||||
result := gozxing.NewResult(decoderResult.GetText(), decoderResult.GetRawBytes(), points, gozxing.BarcodeFormat_QR_CODE)
|
||||
byteSegments := decoderResult.GetByteSegments()
|
||||
if len(byteSegments) > 0 {
|
||||
result.PutMetadata(gozxing.ResultMetadataType_BYTE_SEGMENTS, byteSegments)
|
||||
}
|
||||
ecLevel := decoderResult.GetECLevel()
|
||||
if ecLevel != "" {
|
||||
result.PutMetadata(gozxing.ResultMetadataType_ERROR_CORRECTION_LEVEL, ecLevel)
|
||||
}
|
||||
if decoderResult.HasStructuredAppend() {
|
||||
result.PutMetadata(
|
||||
gozxing.ResultMetadataType_STRUCTURED_APPEND_SEQUENCE,
|
||||
decoderResult.GetStructuredAppendSequenceNumber())
|
||||
result.PutMetadata(
|
||||
gozxing.ResultMetadataType_STRUCTURED_APPEND_PARITY,
|
||||
decoderResult.GetStructuredAppendParity())
|
||||
}
|
||||
result.PutMetadata(
|
||||
gozxing.ResultMetadataType_SYMBOLOGY_IDENTIFIER, "]Q"+strconv.Itoa(decoderResult.GetSymbologyModifier()))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (this *QRCodeReader) Reset() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
func (this *QRCodeReader) extractPureBits(image *gozxing.BitMatrix) (*gozxing.BitMatrix, error) {
|
||||
|
||||
leftTopBlack := image.GetTopLeftOnBit()
|
||||
rightBottomBlack := image.GetBottomRightOnBit()
|
||||
if leftTopBlack == nil || rightBottomBlack == nil {
|
||||
return nil, gozxing.NewNotFoundException()
|
||||
}
|
||||
|
||||
moduleSize, e := this.moduleSize(leftTopBlack, image)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
top := leftTopBlack[1]
|
||||
bottom := rightBottomBlack[1]
|
||||
left := leftTopBlack[0]
|
||||
right := rightBottomBlack[0]
|
||||
|
||||
// Sanity check!
|
||||
if left >= right || top >= bottom {
|
||||
return nil, gozxing.NewNotFoundException(
|
||||
"(left,right)=(%v,%v), (top,bottom)=(%v,%v)", left, right, top, bottom)
|
||||
}
|
||||
|
||||
if bottom-top != right-left {
|
||||
// Special case, where bottom-right module wasn't black so we found something else in the last row
|
||||
// Assume it's a square, so use height as the width
|
||||
right = left + (bottom - top)
|
||||
if right >= image.GetWidth() {
|
||||
// Abort if that would not make sense -- off image
|
||||
return nil, gozxing.NewNotFoundException("right = %v, width = %v", right, image.GetWidth())
|
||||
}
|
||||
}
|
||||
|
||||
matrixWidth := util.MathUtils_Round(float64(right-left+1) / moduleSize)
|
||||
matrixHeight := util.MathUtils_Round(float64(bottom-top+1) / moduleSize)
|
||||
if matrixWidth <= 0 || matrixHeight <= 0 {
|
||||
return nil, gozxing.NewNotFoundException("matrixWidth/Height = %v, %v", matrixWidth, matrixHeight)
|
||||
}
|
||||
if matrixHeight != matrixWidth {
|
||||
// Only possibly decode square regions
|
||||
return nil, gozxing.NewNotFoundException("matrixWidth/Height = %v, %v", matrixWidth, matrixHeight)
|
||||
}
|
||||
|
||||
// Push in the "border" by half the module width so that we start
|
||||
// sampling in the middle of the module. Just in case the image is a
|
||||
// little off, this will help recover.
|
||||
nudge := int(moduleSize / 2.0)
|
||||
top += nudge
|
||||
left += nudge
|
||||
|
||||
// But careful that this does not sample off the edge
|
||||
// "right" is the farthest-right valid pixel location -- right+1 is not necessarily
|
||||
// This is positive by how much the inner x loop below would be too large
|
||||
nudgedTooFarRight := left + int(float64(matrixWidth-1)*moduleSize) - right
|
||||
if nudgedTooFarRight > 0 {
|
||||
if nudgedTooFarRight > nudge {
|
||||
// Neither way fits; abort
|
||||
return nil, gozxing.NewNotFoundException("Neither way fits")
|
||||
}
|
||||
left -= nudgedTooFarRight
|
||||
}
|
||||
// See logic above
|
||||
nudgedTooFarDown := top + int(float64(matrixHeight-1)*moduleSize) - bottom
|
||||
if nudgedTooFarDown > 0 {
|
||||
if nudgedTooFarDown > nudge {
|
||||
// Neither way fits; abort
|
||||
return nil, gozxing.NewNotFoundException("Neither way fits")
|
||||
}
|
||||
top -= nudgedTooFarDown
|
||||
}
|
||||
|
||||
// Now just read off the bits
|
||||
bits, _ := gozxing.NewBitMatrix(matrixWidth, matrixHeight)
|
||||
for y := 0; y < matrixHeight; y++ {
|
||||
iOffset := top + int(float64(y)*moduleSize)
|
||||
for x := 0; x < matrixWidth; x++ {
|
||||
if image.Get(left+int(float64(x)*moduleSize), iOffset) {
|
||||
bits.Set(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
return bits, nil
|
||||
}
|
||||
|
||||
func (this *QRCodeReader) moduleSize(leftTopBlack []int, image *gozxing.BitMatrix) (float64, error) {
|
||||
height := image.GetHeight()
|
||||
width := image.GetWidth()
|
||||
x := leftTopBlack[0]
|
||||
y := leftTopBlack[1]
|
||||
inBlack := true
|
||||
transitions := 0
|
||||
for x < width && y < height {
|
||||
if inBlack != image.Get(x, y) {
|
||||
transitions++
|
||||
if transitions == 5 {
|
||||
break
|
||||
}
|
||||
inBlack = !inBlack
|
||||
}
|
||||
x++
|
||||
y++
|
||||
}
|
||||
if x == width || y == height {
|
||||
return 0, gozxing.NewNotFoundException()
|
||||
}
|
||||
return float64(x-leftTopBlack[0]) / 7.0, nil
|
||||
}
|
||||
130
vendor/github.com/makiuchi-d/gozxing/qrcode/qrcode_writer.go
generated
vendored
Normal file
130
vendor/github.com/makiuchi-d/gozxing/qrcode/qrcode_writer.go
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
package qrcode
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/makiuchi-d/gozxing"
|
||||
"github.com/makiuchi-d/gozxing/qrcode/decoder"
|
||||
"github.com/makiuchi-d/gozxing/qrcode/encoder"
|
||||
)
|
||||
|
||||
const (
|
||||
qrcodeWriter_QUIET_ZONE_SIZE = 4
|
||||
)
|
||||
|
||||
type QRCodeWriter struct{}
|
||||
|
||||
func NewQRCodeWriter() *QRCodeWriter {
|
||||
return &QRCodeWriter{}
|
||||
}
|
||||
|
||||
func (this *QRCodeWriter) EncodeWithoutHint(
|
||||
contents string, format gozxing.BarcodeFormat, width, height int) (*gozxing.BitMatrix, error) {
|
||||
return this.Encode(contents, format, width, height, nil)
|
||||
}
|
||||
|
||||
func (this *QRCodeWriter) Encode(
|
||||
contents string, format gozxing.BarcodeFormat, width, height int,
|
||||
hints map[gozxing.EncodeHintType]interface{}) (*gozxing.BitMatrix, error) {
|
||||
|
||||
if len(contents) == 0 {
|
||||
return nil, gozxing.NewWriterException("IllegalArgumentException: Found empty contents")
|
||||
}
|
||||
|
||||
if format != gozxing.BarcodeFormat_QR_CODE {
|
||||
return nil, gozxing.NewWriterException(
|
||||
"IllegalArgumentException: Can only encode QR_CODE, but got %v", format)
|
||||
}
|
||||
|
||||
if width < 0 || height < 0 {
|
||||
return nil, gozxing.NewWriterException(
|
||||
"IllegalArgumentException: Requested dimensions are too small: %vx%v", width, height)
|
||||
}
|
||||
|
||||
errorCorrectionLevel := decoder.ErrorCorrectionLevel_L
|
||||
quietZone := qrcodeWriter_QUIET_ZONE_SIZE
|
||||
if hints != nil {
|
||||
if ec, ok := hints[gozxing.EncodeHintType_ERROR_CORRECTION]; ok {
|
||||
if ecl, ok := ec.(decoder.ErrorCorrectionLevel); ok {
|
||||
errorCorrectionLevel = ecl
|
||||
} else if str, ok := ec.(string); ok {
|
||||
ecl, e := decoder.ErrorCorrectionLevel_ValueOf(str)
|
||||
if e != nil {
|
||||
return nil, gozxing.NewWriterException("EncodeHintType_ERROR_CORRECTION: %w", e)
|
||||
}
|
||||
errorCorrectionLevel = ecl
|
||||
} else {
|
||||
return nil, gozxing.NewWriterException(
|
||||
"IllegalArgumentException: EncodeHintType_ERROR_CORRECTION %v", ec)
|
||||
}
|
||||
}
|
||||
if m, ok := hints[gozxing.EncodeHintType_MARGIN]; ok {
|
||||
if qz, ok := m.(int); ok {
|
||||
quietZone = qz
|
||||
} else if str, ok := m.(string); ok {
|
||||
qz, e := strconv.Atoi(str)
|
||||
if e != nil {
|
||||
return nil, gozxing.NewWriterException("EncodeHintType_MARGIN = \"%v\": %w", m, e)
|
||||
}
|
||||
quietZone = qz
|
||||
} else {
|
||||
return nil, gozxing.NewWriterException(
|
||||
"IllegalArgumentException: EncodeHintType_MARGIN %v", m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
code, e := encoder.Encoder_encode(contents, errorCorrectionLevel, hints)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return renderResult(code, width, height, quietZone)
|
||||
}
|
||||
|
||||
// renderResult Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
|
||||
// 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
|
||||
func renderResult(code *encoder.QRCode, width, height, quietZone int) (*gozxing.BitMatrix, error) {
|
||||
input := code.GetMatrix()
|
||||
if input == nil {
|
||||
return nil, gozxing.NewWriterException("IllegalStateException")
|
||||
}
|
||||
inputWidth := input.GetWidth()
|
||||
inputHeight := input.GetHeight()
|
||||
qrWidth := inputWidth + (quietZone * 2)
|
||||
qrHeight := inputHeight + (quietZone * 2)
|
||||
outputWidth := qrWidth
|
||||
if outputWidth < width {
|
||||
outputWidth = width
|
||||
}
|
||||
outputHeight := qrHeight
|
||||
if outputHeight < height {
|
||||
outputHeight = height
|
||||
}
|
||||
|
||||
multiple := outputWidth / qrWidth
|
||||
if h := outputHeight / qrHeight; multiple > h {
|
||||
multiple = h
|
||||
}
|
||||
// Padding includes both the quiet zone and the extra white pixels to accommodate the requested
|
||||
// dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
|
||||
// If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
|
||||
// handle all the padding from 100x100 (the actual QR) up to 200x160.
|
||||
leftPadding := (outputWidth - (inputWidth * multiple)) / 2
|
||||
topPadding := (outputHeight - (inputHeight * multiple)) / 2
|
||||
|
||||
output, e := gozxing.NewBitMatrix(outputWidth, outputHeight)
|
||||
if e != nil {
|
||||
return nil, gozxing.WrapWriterException(e)
|
||||
}
|
||||
|
||||
for inputY, outputY := 0, topPadding; inputY < inputHeight; inputY, outputY = inputY+1, outputY+multiple {
|
||||
// Write the contents of this row of the barcode
|
||||
for inputX, outputX := 0, leftPadding; inputX < inputWidth; inputX, outputX = inputX+1, outputX+multiple {
|
||||
if input.Get(inputX, inputY) == 1 {
|
||||
output.SetRegion(outputX, outputY, multiple, multiple)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
36
vendor/github.com/makiuchi-d/gozxing/reader.go
generated
vendored
Normal file
36
vendor/github.com/makiuchi-d/gozxing/reader.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package gozxing
|
||||
|
||||
type Reader interface {
|
||||
/**
|
||||
* Locates and decodes a barcode in some format within an image.
|
||||
*
|
||||
* @param image image of barcode to decode
|
||||
* @return String which the barcode encodes
|
||||
* @throws NotFoundException if no potential barcode is found
|
||||
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
||||
* @throws FormatException if a potential barcode is found but format is invalid
|
||||
*/
|
||||
DecodeWithoutHints(image *BinaryBitmap) (*Result, error)
|
||||
|
||||
/**
|
||||
* Locates and decodes a barcode in some format within an image. This method also accepts
|
||||
* hints, each possibly associated to some data, which may help the implementation decode.
|
||||
*
|
||||
* @param image image of barcode to decode
|
||||
* @param hints passed as a {@link Map} from {@link DecodeHintType}
|
||||
* to arbitrary data. The
|
||||
* meaning of the data depends upon the hint type. The implementation may or may not do
|
||||
* anything with these hints.
|
||||
* @return String which the barcode encodes
|
||||
* @throws NotFoundException if no potential barcode is found
|
||||
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
||||
* @throws FormatException if a potential barcode is found but format is invalid
|
||||
*/
|
||||
Decode(image *BinaryBitmap, hints map[DecodeHintType]interface{}) (*Result, error)
|
||||
|
||||
/**
|
||||
* Resets any internal state the implementation has after a decode, to prepare it
|
||||
* for reuse.
|
||||
*/
|
||||
Reset()
|
||||
}
|
||||
68
vendor/github.com/makiuchi-d/gozxing/reader_exception.go
generated
vendored
Normal file
68
vendor/github.com/makiuchi-d/gozxing/reader_exception.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type ReaderException interface {
|
||||
error
|
||||
readerException()
|
||||
}
|
||||
|
||||
type readerException struct {
|
||||
exception
|
||||
}
|
||||
|
||||
func WrapReaderException(e error) ReaderException {
|
||||
return readerException{
|
||||
wrapException("ReaderException", e),
|
||||
}
|
||||
}
|
||||
|
||||
func (readerException) readerException() {}
|
||||
|
||||
type exception struct {
|
||||
msg string
|
||||
next error
|
||||
frame errors.Frame
|
||||
}
|
||||
|
||||
func newException(prefix string, args ...interface{}) exception {
|
||||
msg := prefix
|
||||
if len(args) > 0 {
|
||||
msg += ": " + fmt.Sprintf(args[0].(string), args[1:]...)
|
||||
}
|
||||
return exception{
|
||||
msg,
|
||||
nil,
|
||||
errors.Caller(2),
|
||||
}
|
||||
}
|
||||
|
||||
func wrapException(msg string, next error) exception {
|
||||
return exception{
|
||||
msg,
|
||||
next,
|
||||
errors.Caller(2),
|
||||
}
|
||||
}
|
||||
|
||||
func (e exception) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
func (e exception) Unwrap() error {
|
||||
return e.next
|
||||
}
|
||||
|
||||
func (e exception) Format(s fmt.State, v rune) {
|
||||
errors.FormatError(e, s, v)
|
||||
}
|
||||
|
||||
func (e exception) FormatError(p errors.Printer) error {
|
||||
p.Print(e.msg)
|
||||
e.frame.Format(p)
|
||||
return e.next
|
||||
}
|
||||
97
vendor/github.com/makiuchi-d/gozxing/result.go
generated
vendored
Normal file
97
vendor/github.com/makiuchi-d/gozxing/result.go
generated
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
text string
|
||||
rawBytes []byte
|
||||
numBits int
|
||||
resultPoints []ResultPoint
|
||||
format BarcodeFormat
|
||||
resultMetadata map[ResultMetadataType]interface{}
|
||||
timestamp int64
|
||||
}
|
||||
|
||||
func NewResult(text string, rawBytes []byte, resultPoints []ResultPoint, format BarcodeFormat) *Result {
|
||||
return NewResultWithTimestamp(
|
||||
text, rawBytes, resultPoints, format, time.Now().UnixNano()/int64(time.Millisecond))
|
||||
}
|
||||
|
||||
func NewResultWithTimestamp(text string, rawBytes []byte, resultPoints []ResultPoint, format BarcodeFormat, timestamp int64) *Result {
|
||||
return NewResultWithNumBits(
|
||||
text, rawBytes, 8*len(rawBytes), resultPoints, format, timestamp)
|
||||
}
|
||||
|
||||
func NewResultWithNumBits(text string, rawBytes []byte, numBits int, resultPoints []ResultPoint, format BarcodeFormat, timestamp int64) *Result {
|
||||
return &Result{
|
||||
text: text,
|
||||
rawBytes: rawBytes,
|
||||
numBits: numBits,
|
||||
resultPoints: resultPoints,
|
||||
format: format,
|
||||
resultMetadata: nil,
|
||||
timestamp: timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Result) GetText() string {
|
||||
return this.text
|
||||
}
|
||||
|
||||
func (this *Result) GetRawBytes() []byte {
|
||||
return this.rawBytes
|
||||
}
|
||||
|
||||
func (this *Result) GetNumBits() int {
|
||||
return this.numBits
|
||||
}
|
||||
|
||||
func (this *Result) GetResultPoints() []ResultPoint {
|
||||
return this.resultPoints
|
||||
}
|
||||
|
||||
func (this *Result) GetBarcodeFormat() BarcodeFormat {
|
||||
return this.format
|
||||
}
|
||||
|
||||
func (this *Result) GetResultMetadata() map[ResultMetadataType]interface{} {
|
||||
return this.resultMetadata
|
||||
}
|
||||
|
||||
func (this *Result) PutMetadata(mdtype ResultMetadataType, value interface{}) {
|
||||
if this.resultMetadata == nil {
|
||||
this.resultMetadata = make(map[ResultMetadataType]interface{}, 1)
|
||||
}
|
||||
this.resultMetadata[mdtype] = value
|
||||
}
|
||||
|
||||
func (this *Result) PutAllMetadata(metadata map[ResultMetadataType]interface{}) {
|
||||
if len(metadata) > 0 {
|
||||
if this.resultMetadata == nil {
|
||||
this.resultMetadata = metadata
|
||||
} else {
|
||||
for k, v := range metadata {
|
||||
this.resultMetadata[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Result) AddResultPoints(newPoints []ResultPoint) {
|
||||
oldPoints := this.resultPoints
|
||||
if len(oldPoints) == 0 {
|
||||
this.resultPoints = newPoints
|
||||
} else if len(newPoints) > 0 {
|
||||
this.resultPoints = append(this.resultPoints, newPoints...)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Result) GetTimestamp() int64 {
|
||||
return this.timestamp
|
||||
}
|
||||
|
||||
func (this *Result) String() string {
|
||||
return this.text
|
||||
}
|
||||
112
vendor/github.com/makiuchi-d/gozxing/result_metadata_type.go
generated
vendored
Normal file
112
vendor/github.com/makiuchi-d/gozxing/result_metadata_type.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
package gozxing
|
||||
|
||||
type ResultMetadataType int
|
||||
|
||||
const (
|
||||
/**
|
||||
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
|
||||
*/
|
||||
ResultMetadataType_OTHER = ResultMetadataType(iota)
|
||||
|
||||
/**
|
||||
* Denotes the likely approximate orientation of the barcode in the image. This value
|
||||
* is given as degrees rotated clockwise from the normal, upright orientation.
|
||||
* For example a 1D barcode which was found by reading top-to-bottom would be
|
||||
* said to have orientation "90". This key maps to an {@link Integer} whose
|
||||
* value is in the range [0,360).
|
||||
*/
|
||||
ResultMetadataType_ORIENTATION
|
||||
|
||||
/**
|
||||
* <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'
|
||||
* which is sometimes used to encode binary data. While {@link Result} makes available
|
||||
* the complete raw bytes in the barcode for these formats, it does not offer the bytes
|
||||
* from the byte segments alone.</p>
|
||||
*
|
||||
* <p>This maps to a {@link java.util.List} of byte arrays corresponding to the
|
||||
* raw bytes in the byte segments in the barcode, in order.</p>
|
||||
*/
|
||||
ResultMetadataType_BYTE_SEGMENTS
|
||||
|
||||
/**
|
||||
* Error correction level used, if applicable. The value type depends on the
|
||||
* format, but is typically a String.
|
||||
*/
|
||||
ResultMetadataType_ERROR_CORRECTION_LEVEL
|
||||
|
||||
/**
|
||||
* For some periodicals, indicates the issue number as an {@link Integer}.
|
||||
*/
|
||||
ResultMetadataType_ISSUE_NUMBER
|
||||
|
||||
/**
|
||||
* For some products, indicates the suggested retail price in the barcode as a
|
||||
* formatted {@link String}.
|
||||
*/
|
||||
ResultMetadataType_SUGGESTED_PRICE
|
||||
|
||||
/**
|
||||
* For some products, the possible country of manufacture as a {@link String} denoting the
|
||||
* ISO country code. Some map to multiple possible countries, like "US/CA".
|
||||
*/
|
||||
ResultMetadataType_POSSIBLE_COUNTRY
|
||||
|
||||
/**
|
||||
* For some products, the extension text
|
||||
*/
|
||||
ResultMetadataType_UPC_EAN_EXTENSION
|
||||
|
||||
/**
|
||||
* PDF417-specific metadata
|
||||
*/
|
||||
ResultMetadataType_PDF417_EXTRA_METADATA
|
||||
|
||||
/**
|
||||
* If the code format supports structured append and the current scanned code is part of one then the
|
||||
* sequence number is given with it.
|
||||
*/
|
||||
ResultMetadataType_STRUCTURED_APPEND_SEQUENCE
|
||||
|
||||
/**
|
||||
* If the code format supports structured append and the current scanned code is part of one then the
|
||||
* parity is given with it.
|
||||
*/
|
||||
ResultMetadataType_STRUCTURED_APPEND_PARITY
|
||||
|
||||
/**
|
||||
* Barcode Symbology Identifier.
|
||||
* Note: According to the GS1 specification the identifier may have to replace a leading FNC1/GS character when prepending to the barcode content.
|
||||
*/
|
||||
ResultMetadataType_SYMBOLOGY_IDENTIFIER
|
||||
)
|
||||
|
||||
func (t ResultMetadataType) String() string {
|
||||
switch t {
|
||||
case ResultMetadataType_OTHER:
|
||||
return "OTHER"
|
||||
case ResultMetadataType_ORIENTATION:
|
||||
return "ORIENTATION"
|
||||
case ResultMetadataType_BYTE_SEGMENTS:
|
||||
return "BYTE_SEGMENTS"
|
||||
case ResultMetadataType_ERROR_CORRECTION_LEVEL:
|
||||
return "ERROR_CORRECTION_LEVEL"
|
||||
case ResultMetadataType_ISSUE_NUMBER:
|
||||
return "ISSUE_NUMBER"
|
||||
case ResultMetadataType_SUGGESTED_PRICE:
|
||||
return "SUGGESTED_PRICE"
|
||||
case ResultMetadataType_POSSIBLE_COUNTRY:
|
||||
return "POSSIBLE_COUNTRY"
|
||||
case ResultMetadataType_UPC_EAN_EXTENSION:
|
||||
return "UPC_EAN_EXTENSION"
|
||||
case ResultMetadataType_PDF417_EXTRA_METADATA:
|
||||
return "PDF417_EXTRA_METADATA"
|
||||
case ResultMetadataType_STRUCTURED_APPEND_SEQUENCE:
|
||||
return "STRUCTURED_APPEND_SEQUENCE"
|
||||
case ResultMetadataType_STRUCTURED_APPEND_PARITY:
|
||||
return "STRUCTURED_APPEND_PARITY"
|
||||
case ResultMetadataType_SYMBOLOGY_IDENTIFIER:
|
||||
return "SYMBOLOGY_IDENTIFIER"
|
||||
default:
|
||||
return "unknown metadata type"
|
||||
}
|
||||
}
|
||||
72
vendor/github.com/makiuchi-d/gozxing/result_point.go
generated
vendored
Normal file
72
vendor/github.com/makiuchi-d/gozxing/result_point.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
"github.com/makiuchi-d/gozxing/common/util"
|
||||
)
|
||||
|
||||
type ResultPoint interface {
|
||||
GetX() float64
|
||||
GetY() float64
|
||||
}
|
||||
|
||||
type ResultPointBase struct {
|
||||
x float64
|
||||
y float64
|
||||
}
|
||||
|
||||
func NewResultPoint(x, y float64) ResultPoint {
|
||||
return ResultPointBase{x, y}
|
||||
}
|
||||
|
||||
func (rp ResultPointBase) GetX() float64 {
|
||||
return rp.x
|
||||
}
|
||||
|
||||
func (rp ResultPointBase) GetY() float64 {
|
||||
return rp.y
|
||||
}
|
||||
|
||||
// Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC
|
||||
// and BC is less than AC, and the angle between BC and BA is less than 180 degrees.
|
||||
// @param patterns array of three {@code ResultPoint} to order
|
||||
func ResultPoint_OrderBestPatterns(pattern0, pattern1, pattern2 ResultPoint) (pointA, pointB, pointC ResultPoint) {
|
||||
// Find distances between pattern centers
|
||||
zeroOneDistance := ResultPoint_Distance(pattern0, pattern1)
|
||||
oneTwoDistance := ResultPoint_Distance(pattern1, pattern2)
|
||||
zeroTwoDistance := ResultPoint_Distance(pattern0, pattern2)
|
||||
|
||||
// Assume one closest to other two is B; A and C will just be guesses at first
|
||||
if oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance {
|
||||
pointB = pattern0
|
||||
pointA = pattern1
|
||||
pointC = pattern2
|
||||
} else if zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance {
|
||||
pointB = pattern1
|
||||
pointA = pattern0
|
||||
pointC = pattern2
|
||||
} else {
|
||||
pointB = pattern2
|
||||
pointA = pattern0
|
||||
pointC = pattern1
|
||||
}
|
||||
|
||||
// Use cross product to figure out whether A and C are correct or flipped.
|
||||
// This asks whether BC x BA has a positive z component, which is the arrangement
|
||||
// we want for A, B, C. If it's negative, then we've got it flipped around and
|
||||
// should swap A and C.
|
||||
if crossProductZ(pointA, pointB, pointC) < 0.0 {
|
||||
pointA, pointC = pointC, pointA
|
||||
}
|
||||
|
||||
return pointA, pointB, pointC
|
||||
}
|
||||
|
||||
func ResultPoint_Distance(pattern1, pattern2 ResultPoint) float64 {
|
||||
return util.MathUtils_DistanceFloat(pattern1.GetX(), pattern1.GetY(), pattern2.GetX(), pattern2.GetY())
|
||||
}
|
||||
|
||||
func crossProductZ(pointA, pointB, pointC ResultPoint) float64 {
|
||||
bX := pointB.GetX()
|
||||
bY := pointB.GetY()
|
||||
return ((pointC.GetX() - bX) * (pointA.GetY() - bY)) - ((pointC.GetY() - bY) * (pointA.GetX() - bX))
|
||||
}
|
||||
3
vendor/github.com/makiuchi-d/gozxing/result_point_callback.go
generated
vendored
Normal file
3
vendor/github.com/makiuchi-d/gozxing/result_point_callback.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
package gozxing
|
||||
|
||||
type ResultPointCallback func(ResultPoint)
|
||||
113
vendor/github.com/makiuchi-d/gozxing/rgb_luminance_source.go
generated
vendored
Normal file
113
vendor/github.com/makiuchi-d/gozxing/rgb_luminance_source.go
generated
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
package gozxing
|
||||
|
||||
import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type RGBLuminanceSource struct {
|
||||
LuminanceSourceBase
|
||||
luminances []byte
|
||||
dataWidth int
|
||||
dataHeight int
|
||||
left int
|
||||
top int
|
||||
}
|
||||
|
||||
func NewRGBLuminanceSource(width, height int, pixels []int) LuminanceSource {
|
||||
dataWidth := width
|
||||
dataHeight := height
|
||||
left := 0
|
||||
top := 0
|
||||
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
//
|
||||
// Total number of pixels suffices, can ignore shape
|
||||
size := width * height
|
||||
luminances := make([]byte, size)
|
||||
for offset := 0; offset < size; offset++ {
|
||||
pixel := pixels[offset]
|
||||
r := (pixel >> 16) & 0xff // red
|
||||
g2 := (pixel >> 7) & 0x1fe // 2 * green
|
||||
b := pixel & 0xff // blue
|
||||
// Calculate green-favouring average cheaply
|
||||
luminances[offset] = byte((r + g2 + b) / 4)
|
||||
}
|
||||
|
||||
return &RGBLuminanceSource{
|
||||
LuminanceSourceBase{width, height},
|
||||
luminances,
|
||||
dataWidth,
|
||||
dataHeight,
|
||||
left,
|
||||
top,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *RGBLuminanceSource) GetRow(y int, row []byte) ([]byte, error) {
|
||||
if y < 0 || y >= this.GetHeight() {
|
||||
return row, errors.Errorf("IllegalArgumentException: Requested row is outside the image: %d", y)
|
||||
}
|
||||
width := this.GetWidth()
|
||||
if row == nil || len(row) < width {
|
||||
row = make([]byte, width)
|
||||
}
|
||||
offset := (y+this.top)*this.dataWidth + this.left
|
||||
copy(row, this.luminances[offset:offset+width])
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func (this *RGBLuminanceSource) GetMatrix() []byte {
|
||||
width := this.GetWidth()
|
||||
height := this.GetHeight()
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if width == this.dataWidth && height == this.dataHeight {
|
||||
return this.luminances
|
||||
}
|
||||
|
||||
area := width * height
|
||||
matrix := make([]byte, area)
|
||||
inputOffset := this.top*this.dataWidth + this.left
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if width == this.dataWidth {
|
||||
copy(matrix, this.luminances[inputOffset:inputOffset+area])
|
||||
return matrix
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
for y := 0; y < height; y++ {
|
||||
outputOffset := y * width
|
||||
copy(matrix[outputOffset:outputOffset+width], this.luminances[inputOffset:inputOffset+width])
|
||||
inputOffset += this.dataWidth
|
||||
}
|
||||
return matrix
|
||||
}
|
||||
|
||||
func (this *RGBLuminanceSource) IsCropSupported() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *RGBLuminanceSource) Crop(left, top, width, height int) (LuminanceSource, error) {
|
||||
if left+width > this.dataWidth || top+height > this.dataHeight {
|
||||
return nil, errors.New("IllegalArgumentException: Crop rectangle does not fit within image data")
|
||||
}
|
||||
return &RGBLuminanceSource{
|
||||
LuminanceSourceBase: LuminanceSourceBase{width, height},
|
||||
luminances: this.luminances,
|
||||
dataWidth: this.dataWidth,
|
||||
dataHeight: this.dataHeight,
|
||||
left: this.left + left,
|
||||
top: this.top + top,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *RGBLuminanceSource) Invert() LuminanceSource {
|
||||
return LuminanceSourceInvert(this)
|
||||
}
|
||||
|
||||
func (this *RGBLuminanceSource) String() string {
|
||||
return LuminanceSourceString(this)
|
||||
}
|
||||
26
vendor/github.com/makiuchi-d/gozxing/writer.go
generated
vendored
Normal file
26
vendor/github.com/makiuchi-d/gozxing/writer.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package gozxing
|
||||
|
||||
type Writer interface {
|
||||
/**
|
||||
* Encode a barcode using the default settings.
|
||||
*
|
||||
* @param contents The contents to encode in the barcode
|
||||
* @param format The barcode format to generate
|
||||
* @param width The preferred width in pixels
|
||||
* @param height The preferred height in pixels
|
||||
* @return {@link BitMatrix} representing encoded barcode image
|
||||
* @throws WriterException if contents cannot be encoded legally in a format
|
||||
*/
|
||||
EncodeWithoutHint(contents string, format BarcodeFormat, width, height int) (*BitMatrix, error)
|
||||
|
||||
/**
|
||||
* @param contents The contents to encode in the barcode
|
||||
* @param format The barcode format to generate
|
||||
* @param width The preferred width in pixels
|
||||
* @param height The preferred height in pixels
|
||||
* @param hints Additional parameters to supply to the encoder
|
||||
* @return {@link BitMatrix} representing encoded barcode image
|
||||
* @throws WriterException if contents cannot be encoded legally in a format
|
||||
*/
|
||||
Encode(contents string, format BarcodeFormat, width, height int, hints map[EncodeHintType]interface{}) (*BitMatrix, error)
|
||||
}
|
||||
24
vendor/github.com/makiuchi-d/gozxing/writer_exception.go
generated
vendored
Normal file
24
vendor/github.com/makiuchi-d/gozxing/writer_exception.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package gozxing
|
||||
|
||||
type WriterException interface {
|
||||
error
|
||||
writerException()
|
||||
}
|
||||
|
||||
type writerException struct {
|
||||
exception
|
||||
}
|
||||
|
||||
func (writerException) writerException() {}
|
||||
|
||||
func NewWriterException(args ...interface{}) WriterException {
|
||||
return writerException{
|
||||
newException("WriterException", args...),
|
||||
}
|
||||
}
|
||||
|
||||
func WrapWriterException(err error) WriterException {
|
||||
return writerException{
|
||||
wrapException("WriterException", err),
|
||||
}
|
||||
}
|
||||
74
vendor/golang.org/x/text/encoding/ianaindex/ascii.go
generated
vendored
Normal file
74
vendor/golang.org/x/text/encoding/ianaindex/ascii.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ianaindex
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
type asciiDecoder struct {
|
||||
transform.NopResetter
|
||||
}
|
||||
|
||||
func (d asciiDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
for _, c := range src {
|
||||
if c > unicode.MaxASCII {
|
||||
r := unicode.ReplacementChar
|
||||
if nDst+utf8.RuneLen(r) > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
nDst += utf8.EncodeRune(dst[nDst:], r)
|
||||
nSrc++
|
||||
continue
|
||||
}
|
||||
|
||||
if nDst >= len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = c
|
||||
nDst++
|
||||
nSrc++
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
type asciiEncoder struct {
|
||||
transform.NopResetter
|
||||
}
|
||||
|
||||
func (d asciiEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
for _, c := range src {
|
||||
if c > unicode.MaxASCII {
|
||||
err = internal.RepertoireError(encoding.ASCIISub)
|
||||
break
|
||||
}
|
||||
|
||||
if nDst >= len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = c
|
||||
nDst++
|
||||
nSrc++
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
var asciiEnc = &internal.Encoding{
|
||||
Encoding: &internal.SimpleEncoding{
|
||||
asciiDecoder{},
|
||||
asciiEncoder{},
|
||||
},
|
||||
Name: "US-ASCII",
|
||||
MIB: identifier.ASCII,
|
||||
}
|
||||
214
vendor/golang.org/x/text/encoding/ianaindex/ianaindex.go
generated
vendored
Normal file
214
vendor/golang.org/x/text/encoding/ianaindex/ianaindex.go
generated
vendored
Normal file
@ -0,0 +1,214 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// Package ianaindex maps names to Encodings as specified by the IANA registry.
|
||||
// This includes both the MIME and IANA names.
|
||||
//
|
||||
// See http://www.iana.org/assignments/character-sets/character-sets.xhtml for
|
||||
// more details.
|
||||
package ianaindex
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
"golang.org/x/text/encoding/korean"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/encoding/traditionalchinese"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
)
|
||||
|
||||
// TODO: remove the "Status... incomplete" in the package doc comment.
|
||||
// TODO: allow users to specify their own aliases?
|
||||
// TODO: allow users to specify their own indexes?
|
||||
// TODO: allow canonicalizing names
|
||||
|
||||
// NOTE: only use these top-level variables if we can get the linker to drop
|
||||
// the indexes when they are not used. Make them a function or perhaps only
|
||||
// support MIME otherwise.
|
||||
|
||||
var (
|
||||
// MIME is an index to map MIME names.
|
||||
MIME *Index = mime
|
||||
|
||||
// IANA is an index that supports all names and aliases using IANA names as
|
||||
// the canonical identifier.
|
||||
IANA *Index = iana
|
||||
|
||||
// MIB is an index that associates the MIB display name with an Encoding.
|
||||
MIB *Index = mib
|
||||
|
||||
mime = &Index{mimeName, ianaToMIB, ianaAliases, encodings[:]}
|
||||
iana = &Index{ianaName, ianaToMIB, ianaAliases, encodings[:]}
|
||||
mib = &Index{mibName, ianaToMIB, ianaAliases, encodings[:]}
|
||||
)
|
||||
|
||||
// Index maps names registered by IANA to Encodings.
|
||||
// Currently different Indexes only differ in the names they return for
|
||||
// encodings. In the future they may also differ in supported aliases.
|
||||
type Index struct {
|
||||
names func(i int) string
|
||||
toMIB []identifier.MIB // Sorted slice of supported MIBs
|
||||
alias map[string]int
|
||||
enc []encoding.Encoding
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalidName = errors.New("ianaindex: invalid encoding name")
|
||||
errUnknown = errors.New("ianaindex: unknown Encoding")
|
||||
errUnsupported = errors.New("ianaindex: unsupported Encoding")
|
||||
)
|
||||
|
||||
// Encoding returns an Encoding for IANA-registered names. Matching is
|
||||
// case-insensitive.
|
||||
//
|
||||
// If the provided name doesn't match a IANA-registered charset, an error is
|
||||
// returned. If the name matches a IANA-registered charset but isn't supported,
|
||||
// a nil encoding and a nil error are returned.
|
||||
func (x *Index) Encoding(name string) (encoding.Encoding, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
// First try without lowercasing (possibly creating an allocation).
|
||||
i, ok := x.alias[name]
|
||||
if !ok {
|
||||
i, ok = x.alias[strings.ToLower(name)]
|
||||
if !ok {
|
||||
return nil, errInvalidName
|
||||
}
|
||||
}
|
||||
return x.enc[i], nil
|
||||
}
|
||||
|
||||
// Name reports the canonical name of the given Encoding. It will return an
|
||||
// error if the e is not associated with a known encoding scheme.
|
||||
func (x *Index) Name(e encoding.Encoding) (string, error) {
|
||||
id, ok := e.(identifier.Interface)
|
||||
if !ok {
|
||||
return "", errUnknown
|
||||
}
|
||||
mib, _ := id.ID()
|
||||
if mib == 0 {
|
||||
return "", errUnknown
|
||||
}
|
||||
v := findMIB(x.toMIB, mib)
|
||||
if v == -1 {
|
||||
return "", errUnsupported
|
||||
}
|
||||
return x.names(v), nil
|
||||
}
|
||||
|
||||
// TODO: the coverage of this index is rather spotty. Allowing users to set
|
||||
// encodings would allow:
|
||||
// - users to increase coverage
|
||||
// - allow a partially loaded set of encodings in case the user doesn't need to
|
||||
// them all.
|
||||
// - write an OS-specific wrapper for supported encodings and set them.
|
||||
// The exact definition of Set depends a bit on if and how we want to let users
|
||||
// write their own Encoding implementations. Also, it is not possible yet to
|
||||
// only partially load the encodings without doing some refactoring. Until this
|
||||
// is solved, we might as well not support Set.
|
||||
// // Set sets the e to be used for the encoding scheme identified by name. Only
|
||||
// // canonical names may be used. An empty name assigns e to its internally
|
||||
// // associated encoding scheme.
|
||||
// func (x *Index) Set(name string, e encoding.Encoding) error {
|
||||
// panic("TODO: implement")
|
||||
// }
|
||||
|
||||
func findMIB(x []identifier.MIB, mib identifier.MIB) int {
|
||||
i := sort.Search(len(x), func(i int) bool { return x[i] >= mib })
|
||||
if i < len(x) && x[i] == mib {
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
const maxMIMENameLen = '0' - 1 // officially 40, but we leave some buffer.
|
||||
|
||||
func mimeName(x int) string {
|
||||
n := ianaNames[x]
|
||||
// See gen.go for a description of the encoding.
|
||||
if n[0] <= maxMIMENameLen {
|
||||
return n[1:n[0]]
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func ianaName(x int) string {
|
||||
n := ianaNames[x]
|
||||
// See gen.go for a description of the encoding.
|
||||
if n[0] <= maxMIMENameLen {
|
||||
return n[n[0]:]
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func mibName(x int) string {
|
||||
return mibNames[x]
|
||||
}
|
||||
|
||||
var encodings = [numIANA]encoding.Encoding{
|
||||
enc3: asciiEnc,
|
||||
enc106: unicode.UTF8,
|
||||
enc1015: unicode.UTF16(unicode.BigEndian, unicode.UseBOM),
|
||||
enc1013: unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM),
|
||||
enc1014: unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM),
|
||||
enc2028: charmap.CodePage037,
|
||||
enc2011: charmap.CodePage437,
|
||||
enc2009: charmap.CodePage850,
|
||||
enc2010: charmap.CodePage852,
|
||||
enc2046: charmap.CodePage855,
|
||||
enc2089: charmap.CodePage858,
|
||||
enc2048: charmap.CodePage860,
|
||||
enc2013: charmap.CodePage862,
|
||||
enc2050: charmap.CodePage863,
|
||||
enc2052: charmap.CodePage865,
|
||||
enc2086: charmap.CodePage866,
|
||||
enc2102: charmap.CodePage1047,
|
||||
enc2091: charmap.CodePage1140,
|
||||
enc4: charmap.ISO8859_1,
|
||||
enc5: charmap.ISO8859_2,
|
||||
enc6: charmap.ISO8859_3,
|
||||
enc7: charmap.ISO8859_4,
|
||||
enc8: charmap.ISO8859_5,
|
||||
enc9: charmap.ISO8859_6,
|
||||
enc81: charmap.ISO8859_6E,
|
||||
enc82: charmap.ISO8859_6I,
|
||||
enc10: charmap.ISO8859_7,
|
||||
enc11: charmap.ISO8859_8,
|
||||
enc84: charmap.ISO8859_8E,
|
||||
enc85: charmap.ISO8859_8I,
|
||||
enc12: charmap.ISO8859_9,
|
||||
enc13: charmap.ISO8859_10,
|
||||
enc109: charmap.ISO8859_13,
|
||||
enc110: charmap.ISO8859_14,
|
||||
enc111: charmap.ISO8859_15,
|
||||
enc112: charmap.ISO8859_16,
|
||||
enc2084: charmap.KOI8R,
|
||||
enc2088: charmap.KOI8U,
|
||||
enc2027: charmap.Macintosh,
|
||||
enc2109: charmap.Windows874,
|
||||
enc2250: charmap.Windows1250,
|
||||
enc2251: charmap.Windows1251,
|
||||
enc2252: charmap.Windows1252,
|
||||
enc2253: charmap.Windows1253,
|
||||
enc2254: charmap.Windows1254,
|
||||
enc2255: charmap.Windows1255,
|
||||
enc2256: charmap.Windows1256,
|
||||
enc2257: charmap.Windows1257,
|
||||
enc2258: charmap.Windows1258,
|
||||
enc18: japanese.EUCJP,
|
||||
enc39: japanese.ISO2022JP,
|
||||
enc17: japanese.ShiftJIS,
|
||||
enc38: korean.EUCKR,
|
||||
enc114: simplifiedchinese.GB18030,
|
||||
enc113: simplifiedchinese.GBK,
|
||||
enc2085: simplifiedchinese.HZGB2312,
|
||||
enc2026: traditionalchinese.Big5,
|
||||
}
|
||||
2355
vendor/golang.org/x/text/encoding/ianaindex/tables.go
generated
vendored
Normal file
2355
vendor/golang.org/x/text/encoding/ianaindex/tables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
27
vendor/golang.org/x/xerrors/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/xerrors/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
22
vendor/golang.org/x/xerrors/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/xerrors/PATENTS
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
2
vendor/golang.org/x/xerrors/README
generated
vendored
Normal file
2
vendor/golang.org/x/xerrors/README
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
This repository holds the transition packages for the new Go 1.13 error values.
|
||||
See golang.org/design/29934-error-values.
|
||||
193
vendor/golang.org/x/xerrors/adaptor.go
generated
vendored
Normal file
193
vendor/golang.org/x/xerrors/adaptor.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xerrors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// FormatError calls the FormatError method of f with an errors.Printer
|
||||
// configured according to s and verb, and writes the result to s.
|
||||
func FormatError(f Formatter, s fmt.State, verb rune) {
|
||||
// Assuming this function is only called from the Format method, and given
|
||||
// that FormatError takes precedence over Format, it cannot be called from
|
||||
// any package that supports errors.Formatter. It is therefore safe to
|
||||
// disregard that State may be a specific printer implementation and use one
|
||||
// of our choice instead.
|
||||
|
||||
// limitations: does not support printing error as Go struct.
|
||||
|
||||
var (
|
||||
sep = " " // separator before next error
|
||||
p = &state{State: s}
|
||||
direct = true
|
||||
)
|
||||
|
||||
var err error = f
|
||||
|
||||
switch verb {
|
||||
// Note that this switch must match the preference order
|
||||
// for ordinary string printing (%#v before %+v, and so on).
|
||||
|
||||
case 'v':
|
||||
if s.Flag('#') {
|
||||
if stringer, ok := err.(fmt.GoStringer); ok {
|
||||
io.WriteString(&p.buf, stringer.GoString())
|
||||
goto exit
|
||||
}
|
||||
// proceed as if it were %v
|
||||
} else if s.Flag('+') {
|
||||
p.printDetail = true
|
||||
sep = "\n - "
|
||||
}
|
||||
case 's':
|
||||
case 'q', 'x', 'X':
|
||||
// Use an intermediate buffer in the rare cases that precision,
|
||||
// truncation, or one of the alternative verbs (q, x, and X) are
|
||||
// specified.
|
||||
direct = false
|
||||
|
||||
default:
|
||||
p.buf.WriteString("%!")
|
||||
p.buf.WriteRune(verb)
|
||||
p.buf.WriteByte('(')
|
||||
switch {
|
||||
case err != nil:
|
||||
p.buf.WriteString(reflect.TypeOf(f).String())
|
||||
default:
|
||||
p.buf.WriteString("<nil>")
|
||||
}
|
||||
p.buf.WriteByte(')')
|
||||
io.Copy(s, &p.buf)
|
||||
return
|
||||
}
|
||||
|
||||
loop:
|
||||
for {
|
||||
switch v := err.(type) {
|
||||
case Formatter:
|
||||
err = v.FormatError((*printer)(p))
|
||||
case fmt.Formatter:
|
||||
v.Format(p, 'v')
|
||||
break loop
|
||||
default:
|
||||
io.WriteString(&p.buf, v.Error())
|
||||
break loop
|
||||
}
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if p.needColon || !p.printDetail {
|
||||
p.buf.WriteByte(':')
|
||||
p.needColon = false
|
||||
}
|
||||
p.buf.WriteString(sep)
|
||||
p.inDetail = false
|
||||
p.needNewline = false
|
||||
}
|
||||
|
||||
exit:
|
||||
width, okW := s.Width()
|
||||
prec, okP := s.Precision()
|
||||
|
||||
if !direct || (okW && width > 0) || okP {
|
||||
// Construct format string from State s.
|
||||
format := []byte{'%'}
|
||||
if s.Flag('-') {
|
||||
format = append(format, '-')
|
||||
}
|
||||
if s.Flag('+') {
|
||||
format = append(format, '+')
|
||||
}
|
||||
if s.Flag(' ') {
|
||||
format = append(format, ' ')
|
||||
}
|
||||
if okW {
|
||||
format = strconv.AppendInt(format, int64(width), 10)
|
||||
}
|
||||
if okP {
|
||||
format = append(format, '.')
|
||||
format = strconv.AppendInt(format, int64(prec), 10)
|
||||
}
|
||||
format = append(format, string(verb)...)
|
||||
fmt.Fprintf(s, string(format), p.buf.String())
|
||||
} else {
|
||||
io.Copy(s, &p.buf)
|
||||
}
|
||||
}
|
||||
|
||||
var detailSep = []byte("\n ")
|
||||
|
||||
// state tracks error printing state. It implements fmt.State.
|
||||
type state struct {
|
||||
fmt.State
|
||||
buf bytes.Buffer
|
||||
|
||||
printDetail bool
|
||||
inDetail bool
|
||||
needColon bool
|
||||
needNewline bool
|
||||
}
|
||||
|
||||
func (s *state) Write(b []byte) (n int, err error) {
|
||||
if s.printDetail {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if s.inDetail && s.needColon {
|
||||
s.needNewline = true
|
||||
if b[0] == '\n' {
|
||||
b = b[1:]
|
||||
}
|
||||
}
|
||||
k := 0
|
||||
for i, c := range b {
|
||||
if s.needNewline {
|
||||
if s.inDetail && s.needColon {
|
||||
s.buf.WriteByte(':')
|
||||
s.needColon = false
|
||||
}
|
||||
s.buf.Write(detailSep)
|
||||
s.needNewline = false
|
||||
}
|
||||
if c == '\n' {
|
||||
s.buf.Write(b[k:i])
|
||||
k = i + 1
|
||||
s.needNewline = true
|
||||
}
|
||||
}
|
||||
s.buf.Write(b[k:])
|
||||
if !s.inDetail {
|
||||
s.needColon = true
|
||||
}
|
||||
} else if !s.inDetail {
|
||||
s.buf.Write(b)
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// printer wraps a state to implement an xerrors.Printer.
|
||||
type printer state
|
||||
|
||||
func (s *printer) Print(args ...interface{}) {
|
||||
if !s.inDetail || s.printDetail {
|
||||
fmt.Fprint((*state)(s), args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *printer) Printf(format string, args ...interface{}) {
|
||||
if !s.inDetail || s.printDetail {
|
||||
fmt.Fprintf((*state)(s), format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *printer) Detail() bool {
|
||||
s.inDetail = true
|
||||
return s.printDetail
|
||||
}
|
||||
1
vendor/golang.org/x/xerrors/codereview.cfg
generated
vendored
Normal file
1
vendor/golang.org/x/xerrors/codereview.cfg
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
issuerepo: golang/go
|
||||
22
vendor/golang.org/x/xerrors/doc.go
generated
vendored
Normal file
22
vendor/golang.org/x/xerrors/doc.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package xerrors implements functions to manipulate errors.
|
||||
//
|
||||
// This package is based on the Go 2 proposal for error values:
|
||||
// https://golang.org/design/29934-error-values
|
||||
//
|
||||
// These functions were incorporated into the standard library's errors package
|
||||
// in Go 1.13:
|
||||
// - Is
|
||||
// - As
|
||||
// - Unwrap
|
||||
//
|
||||
// Also, Errorf's %w verb was incorporated into fmt.Errorf.
|
||||
//
|
||||
// Use this package to get equivalent behavior in all supported Go versions.
|
||||
//
|
||||
// No other features of this package were included in Go 1.13, and at present
|
||||
// there are no plans to include any of them.
|
||||
package xerrors // import "golang.org/x/xerrors"
|
||||
33
vendor/golang.org/x/xerrors/errors.go
generated
vendored
Normal file
33
vendor/golang.org/x/xerrors/errors.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xerrors
|
||||
|
||||
import "fmt"
|
||||
|
||||
// errorString is a trivial implementation of error.
|
||||
type errorString struct {
|
||||
s string
|
||||
frame Frame
|
||||
}
|
||||
|
||||
// New returns an error that formats as the given text.
|
||||
//
|
||||
// The returned error contains a Frame set to the caller's location and
|
||||
// implements Formatter to show this information when printed with details.
|
||||
func New(text string) error {
|
||||
return &errorString{text, Caller(1)}
|
||||
}
|
||||
|
||||
func (e *errorString) Error() string {
|
||||
return e.s
|
||||
}
|
||||
|
||||
func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) }
|
||||
|
||||
func (e *errorString) FormatError(p Printer) (next error) {
|
||||
p.Print(e.s)
|
||||
e.frame.Format(p)
|
||||
return nil
|
||||
}
|
||||
187
vendor/golang.org/x/xerrors/fmt.go
generated
vendored
Normal file
187
vendor/golang.org/x/xerrors/fmt.go
generated
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xerrors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/xerrors/internal"
|
||||
)
|
||||
|
||||
const percentBangString = "%!"
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string as a
|
||||
// value that satisfies error.
|
||||
//
|
||||
// The returned error includes the file and line number of the caller when
|
||||
// formatted with additional detail enabled. If the last argument is an error
|
||||
// the returned error's Format method will return it if the format string ends
|
||||
// with ": %s", ": %v", or ": %w". If the last argument is an error and the
|
||||
// format string ends with ": %w", the returned error implements an Unwrap
|
||||
// method returning it.
|
||||
//
|
||||
// If the format specifier includes a %w verb with an error operand in a
|
||||
// position other than at the end, the returned error will still implement an
|
||||
// Unwrap method returning the operand, but the error's Format method will not
|
||||
// return the wrapped error.
|
||||
//
|
||||
// It is invalid to include more than one %w verb or to supply it with an
|
||||
// operand that does not implement the error interface. The %w verb is otherwise
|
||||
// a synonym for %v.
|
||||
func Errorf(format string, a ...interface{}) error {
|
||||
format = formatPlusW(format)
|
||||
// Support a ": %[wsv]" suffix, which works well with xerrors.Formatter.
|
||||
wrap := strings.HasSuffix(format, ": %w")
|
||||
idx, format2, ok := parsePercentW(format)
|
||||
percentWElsewhere := !wrap && idx >= 0
|
||||
if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) {
|
||||
err := errorAt(a, len(a)-1)
|
||||
if err == nil {
|
||||
return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)}
|
||||
}
|
||||
// TODO: this is not entirely correct. The error value could be
|
||||
// printed elsewhere in format if it mixes numbered with unnumbered
|
||||
// substitutions. With relatively small changes to doPrintf we can
|
||||
// have it optionally ignore extra arguments and pass the argument
|
||||
// list in its entirety.
|
||||
msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...)
|
||||
frame := Frame{}
|
||||
if internal.EnableTrace {
|
||||
frame = Caller(1)
|
||||
}
|
||||
if wrap {
|
||||
return &wrapError{msg, err, frame}
|
||||
}
|
||||
return &noWrapError{msg, err, frame}
|
||||
}
|
||||
// Support %w anywhere.
|
||||
// TODO: don't repeat the wrapped error's message when %w occurs in the middle.
|
||||
msg := fmt.Sprintf(format2, a...)
|
||||
if idx < 0 {
|
||||
return &noWrapError{msg, nil, Caller(1)}
|
||||
}
|
||||
err := errorAt(a, idx)
|
||||
if !ok || err == nil {
|
||||
// Too many %ws or argument of %w is not an error. Approximate the Go
|
||||
// 1.13 fmt.Errorf message.
|
||||
return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)}
|
||||
}
|
||||
frame := Frame{}
|
||||
if internal.EnableTrace {
|
||||
frame = Caller(1)
|
||||
}
|
||||
return &wrapError{msg, err, frame}
|
||||
}
|
||||
|
||||
func errorAt(args []interface{}, i int) error {
|
||||
if i < 0 || i >= len(args) {
|
||||
return nil
|
||||
}
|
||||
err, ok := args[i].(error)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// formatPlusW is used to avoid the vet check that will barf at %w.
|
||||
func formatPlusW(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// Return the index of the only %w in format, or -1 if none.
|
||||
// Also return a rewritten format string with %w replaced by %v, and
|
||||
// false if there is more than one %w.
|
||||
// TODO: handle "%[N]w".
|
||||
func parsePercentW(format string) (idx int, newFormat string, ok bool) {
|
||||
// Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go.
|
||||
idx = -1
|
||||
ok = true
|
||||
n := 0
|
||||
sz := 0
|
||||
var isW bool
|
||||
for i := 0; i < len(format); i += sz {
|
||||
if format[i] != '%' {
|
||||
sz = 1
|
||||
continue
|
||||
}
|
||||
// "%%" is not a format directive.
|
||||
if i+1 < len(format) && format[i+1] == '%' {
|
||||
sz = 2
|
||||
continue
|
||||
}
|
||||
sz, isW = parsePrintfVerb(format[i:])
|
||||
if isW {
|
||||
if idx >= 0 {
|
||||
ok = false
|
||||
} else {
|
||||
idx = n
|
||||
}
|
||||
// "Replace" the last character, the 'w', with a 'v'.
|
||||
p := i + sz - 1
|
||||
format = format[:p] + "v" + format[p+1:]
|
||||
}
|
||||
n++
|
||||
}
|
||||
return idx, format, ok
|
||||
}
|
||||
|
||||
// Parse the printf verb starting with a % at s[0].
|
||||
// Return how many bytes it occupies and whether the verb is 'w'.
|
||||
func parsePrintfVerb(s string) (int, bool) {
|
||||
// Assume only that the directive is a sequence of non-letters followed by a single letter.
|
||||
sz := 0
|
||||
var r rune
|
||||
for i := 1; i < len(s); i += sz {
|
||||
r, sz = utf8.DecodeRuneInString(s[i:])
|
||||
if unicode.IsLetter(r) {
|
||||
return i + sz, r == 'w'
|
||||
}
|
||||
}
|
||||
return len(s), false
|
||||
}
|
||||
|
||||
type noWrapError struct {
|
||||
msg string
|
||||
err error
|
||||
frame Frame
|
||||
}
|
||||
|
||||
func (e *noWrapError) Error() string {
|
||||
return fmt.Sprint(e)
|
||||
}
|
||||
|
||||
func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
|
||||
|
||||
func (e *noWrapError) FormatError(p Printer) (next error) {
|
||||
p.Print(e.msg)
|
||||
e.frame.Format(p)
|
||||
return e.err
|
||||
}
|
||||
|
||||
type wrapError struct {
|
||||
msg string
|
||||
err error
|
||||
frame Frame
|
||||
}
|
||||
|
||||
func (e *wrapError) Error() string {
|
||||
return fmt.Sprint(e)
|
||||
}
|
||||
|
||||
func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
|
||||
|
||||
func (e *wrapError) FormatError(p Printer) (next error) {
|
||||
p.Print(e.msg)
|
||||
e.frame.Format(p)
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e *wrapError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
34
vendor/golang.org/x/xerrors/format.go
generated
vendored
Normal file
34
vendor/golang.org/x/xerrors/format.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xerrors
|
||||
|
||||
// A Formatter formats error messages.
|
||||
type Formatter interface {
|
||||
error
|
||||
|
||||
// FormatError prints the receiver's first error and returns the next error in
|
||||
// the error chain, if any.
|
||||
FormatError(p Printer) (next error)
|
||||
}
|
||||
|
||||
// A Printer formats error messages.
|
||||
//
|
||||
// The most common implementation of Printer is the one provided by package fmt
|
||||
// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message
|
||||
// typically provide their own implementations.
|
||||
type Printer interface {
|
||||
// Print appends args to the message output.
|
||||
Print(args ...interface{})
|
||||
|
||||
// Printf writes a formatted string.
|
||||
Printf(format string, args ...interface{})
|
||||
|
||||
// Detail reports whether error detail is requested.
|
||||
// After the first call to Detail, all text written to the Printer
|
||||
// is formatted as additional detail, or ignored when
|
||||
// detail has not been requested.
|
||||
// If Detail returns false, the caller can avoid printing the detail at all.
|
||||
Detail() bool
|
||||
}
|
||||
56
vendor/golang.org/x/xerrors/frame.go
generated
vendored
Normal file
56
vendor/golang.org/x/xerrors/frame.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xerrors
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// A Frame contains part of a call stack.
|
||||
type Frame struct {
|
||||
// Make room for three PCs: the one we were asked for, what it called,
|
||||
// and possibly a PC for skipPleaseUseCallersFrames. See:
|
||||
// https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169
|
||||
frames [3]uintptr
|
||||
}
|
||||
|
||||
// Caller returns a Frame that describes a frame on the caller's stack.
|
||||
// The argument skip is the number of frames to skip over.
|
||||
// Caller(0) returns the frame for the caller of Caller.
|
||||
func Caller(skip int) Frame {
|
||||
var s Frame
|
||||
runtime.Callers(skip+1, s.frames[:])
|
||||
return s
|
||||
}
|
||||
|
||||
// location reports the file, line, and function of a frame.
|
||||
//
|
||||
// The returned function may be "" even if file and line are not.
|
||||
func (f Frame) location() (function, file string, line int) {
|
||||
frames := runtime.CallersFrames(f.frames[:])
|
||||
if _, ok := frames.Next(); !ok {
|
||||
return "", "", 0
|
||||
}
|
||||
fr, ok := frames.Next()
|
||||
if !ok {
|
||||
return "", "", 0
|
||||
}
|
||||
return fr.Function, fr.File, fr.Line
|
||||
}
|
||||
|
||||
// Format prints the stack as error detail.
|
||||
// It should be called from an error's Format implementation
|
||||
// after printing any other error detail.
|
||||
func (f Frame) Format(p Printer) {
|
||||
if p.Detail() {
|
||||
function, file, line := f.location()
|
||||
if function != "" {
|
||||
p.Printf("%s\n ", function)
|
||||
}
|
||||
if file != "" {
|
||||
p.Printf("%s:%d\n", file, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
8
vendor/golang.org/x/xerrors/internal/internal.go
generated
vendored
Normal file
8
vendor/golang.org/x/xerrors/internal/internal.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
// EnableTrace indicates whether stack information should be recorded in errors.
|
||||
var EnableTrace = true
|
||||
106
vendor/golang.org/x/xerrors/wrap.go
generated
vendored
Normal file
106
vendor/golang.org/x/xerrors/wrap.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xerrors
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// A Wrapper provides context around another error.
|
||||
type Wrapper interface {
|
||||
// Unwrap returns the next error in the error chain.
|
||||
// If there is no next error, Unwrap returns nil.
|
||||
Unwrap() error
|
||||
}
|
||||
|
||||
// Opaque returns an error with the same error formatting as err
|
||||
// but that does not match err and cannot be unwrapped.
|
||||
func Opaque(err error) error {
|
||||
return noWrapper{err}
|
||||
}
|
||||
|
||||
type noWrapper struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (e noWrapper) FormatError(p Printer) (next error) {
|
||||
if f, ok := e.error.(Formatter); ok {
|
||||
return f.FormatError(p)
|
||||
}
|
||||
p.Print(e.error)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err implements
|
||||
// Unwrap. Otherwise, Unwrap returns nil.
|
||||
func Unwrap(err error) error {
|
||||
u, ok := err.(Wrapper)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return u.Unwrap()
|
||||
}
|
||||
|
||||
// Is reports whether any error in err's chain matches target.
|
||||
//
|
||||
// An error is considered to match a target if it is equal to that target or if
|
||||
// it implements a method Is(error) bool such that Is(target) returns true.
|
||||
func Is(err, target error) bool {
|
||||
if target == nil {
|
||||
return err == target
|
||||
}
|
||||
|
||||
isComparable := reflect.TypeOf(target).Comparable()
|
||||
for {
|
||||
if isComparable && err == target {
|
||||
return true
|
||||
}
|
||||
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
|
||||
return true
|
||||
}
|
||||
// TODO: consider supporing target.Is(err). This would allow
|
||||
// user-definable predicates, but also may allow for coping with sloppy
|
||||
// APIs, thereby making it easier to get away with them.
|
||||
if err = Unwrap(err); err == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As finds the first error in err's chain that matches the type to which target
|
||||
// points, and if so, sets the target to its value and returns true. An error
|
||||
// matches a type if it is assignable to the target type, or if it has a method
|
||||
// As(interface{}) bool such that As(target) returns true. As will panic if target
|
||||
// is not a non-nil pointer to a type which implements error or is of interface type.
|
||||
//
|
||||
// The As method should set the target to its value and return true if err
|
||||
// matches the type to which target points.
|
||||
func As(err error, target interface{}) bool {
|
||||
if target == nil {
|
||||
panic("errors: target cannot be nil")
|
||||
}
|
||||
val := reflect.ValueOf(target)
|
||||
typ := val.Type()
|
||||
if typ.Kind() != reflect.Ptr || val.IsNil() {
|
||||
panic("errors: target must be a non-nil pointer")
|
||||
}
|
||||
if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) {
|
||||
panic("errors: *target must be interface or implement error")
|
||||
}
|
||||
targetType := typ.Elem()
|
||||
for err != nil {
|
||||
if reflect.TypeOf(err).AssignableTo(targetType) {
|
||||
val.Elem().Set(reflect.ValueOf(err))
|
||||
return true
|
||||
}
|
||||
if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
|
||||
return true
|
||||
}
|
||||
err = Unwrap(err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
15
vendor/modules.txt
vendored
15
vendor/modules.txt
vendored
@ -138,6 +138,16 @@ github.com/jsummers/gobmp
|
||||
# github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea
|
||||
## explicit; go 1.12
|
||||
github.com/liyue201/goqr
|
||||
# github.com/makiuchi-d/gozxing v0.1.0
|
||||
## explicit; go 1.17
|
||||
github.com/makiuchi-d/gozxing
|
||||
github.com/makiuchi-d/gozxing/common
|
||||
github.com/makiuchi-d/gozxing/common/reedsolomon
|
||||
github.com/makiuchi-d/gozxing/common/util
|
||||
github.com/makiuchi-d/gozxing/qrcode
|
||||
github.com/makiuchi-d/gozxing/qrcode/decoder
|
||||
github.com/makiuchi-d/gozxing/qrcode/detector
|
||||
github.com/makiuchi-d/gozxing/qrcode/encoder
|
||||
# github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
## explicit
|
||||
github.com/nfnt/resize
|
||||
@ -221,6 +231,7 @@ golang.org/x/sys/windows/registry
|
||||
golang.org/x/text/encoding
|
||||
golang.org/x/text/encoding/charmap
|
||||
golang.org/x/text/encoding/htmlindex
|
||||
golang.org/x/text/encoding/ianaindex
|
||||
golang.org/x/text/encoding/internal
|
||||
golang.org/x/text/encoding/internal/identifier
|
||||
golang.org/x/text/encoding/japanese
|
||||
@ -236,6 +247,10 @@ golang.org/x/text/language
|
||||
golang.org/x/text/runes
|
||||
golang.org/x/text/transform
|
||||
golang.org/x/text/unicode/bidi
|
||||
# golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
## explicit; go 1.11
|
||||
golang.org/x/xerrors
|
||||
golang.org/x/xerrors/internal
|
||||
# gopkg.in/yaml.v3 v3.0.1
|
||||
## explicit
|
||||
gopkg.in/yaml.v3
|
||||
|
||||
Loading…
Reference in New Issue
Block a user