feature/ui asi tak vsechno uz funguje pres qr

This commit is contained in:
Lukas Batelka 2025-09-28 21:03:39 +02:00
parent 81df2170f3
commit c286d54e98
91 changed files with 12919 additions and 313 deletions

0
README.md Normal file → Executable file
View File

View File

@ -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
View File

0
templates/decrypt.html Normal file → Executable file
View File

0
templates/encrypt.html Normal file → Executable file
View File

0
templates/index.html Normal file → Executable file
View File

218
ui.go
View File

@ -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
View 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
View 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
View File

@ -0,0 +1,113 @@
# gozxing A Barcode Scanning/Encoding Library for Go
[![Build Status](https://github.com/makiuchi-d/gozxing/actions/workflows/main.yml/badge.svg)](https://github.com/makiuchi-d/gozxing/actions/workflows/main.yml)
[![codecov](https://codecov.io/gh/makiuchi-d/gozxing/branch/master/graph/badge.svg)](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
View 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
View 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
View 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
View 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
View 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()

View 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),
}
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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,
}
}

View 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)
}

View 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)
}

View 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
}

View 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
}

View 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),
}
}

View 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
}

View 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
}

View 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
View 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)
}

View 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 ""
}

View 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),
}
}

View 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
}

View 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
}

View 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")
}

View 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
}

View 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)
}

View 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)
}

View 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),
}
}

View 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]
}
}
}

View 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)
}
}
}
}

View 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
}

View 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)
}
}
}
}

View 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()
}

View 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
}

View 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)
}
}

View 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)

View 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 ""
}
}

View 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]
}

View 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}}}),
}

View 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)
}

View 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
}

View 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()
}

View 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))
}

View 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()
}
}

View 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
}

View 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
}

View 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)
}

View 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)
}

View 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
}

View 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)
}
}
}
}
}

View 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
}

View 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
}

View 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
View 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()
}

View 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
View 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
}

View 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
View 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))
}

View File

@ -0,0 +1,3 @@
package gozxing
type ResultPointCallback func(ResultPoint)

View 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
View 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)
}

View 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
View 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,
}

View 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

File diff suppressed because it is too large Load Diff

27
vendor/golang.org/x/xerrors/LICENSE generated vendored Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1 @@
issuerepo: golang/go

22
vendor/golang.org/x/xerrors/doc.go generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -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