makiuchi-d
gozxing
Go

Port of ZXing (https://github.com/zxing/zxing) core to pure Go.

Last updated Jul 1, 2026
661
Stars
79
Forks
15
Issues
+3
Stars/day
Attention Score
29
Language breakdown
Go 100.0%
Files click to expand
README

gozxing A Barcode Scanning/Encoding Library for Go

Build Status codecov

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 | :heavycheckmark: | :heavycheckmark: | | Data Matrix | :heavycheckmark: | :heavycheckmark: | | Aztec | :heavycheckmark: | | | PDF 417 | | | | MaxiCode | | |

1D product barcodes

| Format | Scanning | Encoding | |-------------|--------------------|--------------------| | UPC-A | :heavycheckmark: | :heavycheckmark: | | UPC-E | :heavycheckmark: | :heavycheckmark: | | EAN-8 | :heavycheckmark: | :heavycheckmark: | | EAN-13 | :heavycheckmark: | :heavycheckmark: |

1D industrial barcode

| Format | Scanning | Encoding | |--------------|--------------------|--------------------| | Code 39 | :heavycheckmark: | :heavycheckmark: | | Code 93 | :heavycheckmark: | :heavycheckmark: | | Code 128 | :heavycheckmark: | :heavycheckmark: | | Codabar | :heavycheckmark: | :heavycheckmark: | | ITF | :heavycheckmark: | :heavycheckmark: | | RSS-14 | :heavycheckmark: | - | | RSS-Expanded | | |

Special reader/writer

| Reader/Writer | Porting status | |------------------------------|--------------------| | MultiFormatReader | | | MultiFormatWriter | | | ByQuadrantReader | | | GenericMultipleBarcodeReader | | | QRCodeMultiReader | :heavycheckmark: | | MultiFormatUPCEANReader | :heavycheckmark: | | MultiFormatOneDReader | |

Usage Examples

Scanning QR code

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

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.BarcodeFormatCODE_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) }

Thread Safety

Starting from version v0.1.2, BinaryBitmap and HybridBinarizer are thread-safe for concurrent access. Multiple goroutines can safely call GetBlackMatrix() on the same instance without external synchronization.

Why This Matters

Creating a BinaryBitmap from an image can be computationally significant. For high-performance services, caching these preprocessed bitmaps can offer tangible benefits.

Performance Benchmarks

The following benchmark demonstrates why caching BinaryBitmap instances is valuable:

func BenchmarkCachingImpact(b *testing.B) {
    // Generate a 400x400 QR code
    key, _ := totp.Generate(totp.GenerateOpts{
        Issuer:      "BenchmarkApp",
        AccountName: "bench@example.com",
    })
    img, _ := key.Image(400, 400)
    
    b.Run("WithoutCaching", func(b *testing.B) {
        reader := qrcode.NewQRCodeReader()
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
            // Create new BinaryBitmap each time (expensive!)
            bmp, _ := gozxing.NewBinaryBitmapFromImage(img)
            ,  = reader.Decode(bmp, nil)
        }
    })
    
    b.Run("WithCaching", func(b *testing.B) {
        // Create BinaryBitmap once and reuse
        bmp, _ := gozxing.NewBinaryBitmapFromImage(img)
        reader := qrcode.NewQRCodeReader()
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
            // Reuse the same BinaryBitmap (fast!)
            ,  = reader.Decode(bmp, nil)
        }
    })
}

Results on Apple M1 Ultra:

BenchmarkCachingImpact/WithoutCaching-20     498    2392097 ns/op BenchmarkCachingImpact/WithCaching-20       5311     188575 ns/op

This shows a 12.7x performance improvement when caching BinaryBitmap instances.

For a pseudocode example of how you might leverage the added thread-safety of gozxing's BinaryBitmap now:

import (
    "bytes"
    "crypto/sha256"
    "encoding/hex"
    "image/jpeg"
    "sync"
    
    "github.com/makiuchi-d/gozxing"
    "github.com/makiuchi-d/gozxing/qrcode"
)

type QRResult struct { Text string }

// High-performance QR service that caches preprocessed images type QRService struct { cache sync.Map // image_hash -> *gozxing.BinaryBitmap }

// ProcessQR handles QR detection for uploaded images. // Without caching: Each request creates a new BinaryBitmap (1.7ms overhead) // With caching: Reuse BinaryBitmap for duplicate images (12x faster) func (s QRService) ProcessQR(imageData []byte) (QRResult, error) { hash := sha256.Sum256(imageData) hashStr := hex.EncodeToString(hash[:]) // Check if we've already preprocessed this image if cached, ok := s.cache.Load(hashStr); ok { // Multiple goroutines may decode the same cached bitmap // This is now safe with v0.1.2+ return s.decodeQR(cached.(*gozxing.BinaryBitmap)) } // Preprocess new image (expensive: ~1.8ms for 400x400) img, _ := jpeg.Decode(bytes.NewReader(imageData)) bmp, _ := gozxing.NewBinaryBitmapFromImage(img) // Cache for future requests s.cache.Store(hashStr, bmp) return s.decodeQR(bmp) }

func (s QRService) decodeQR(bmp gozxing.BinaryBitmap) (*QRResult, error) { reader := qrcode.NewQRCodeReader() result, err := reader.Decode(bmp, nil) // Safe for concurrent use if err != nil { return nil, err } return &QRResult{Text: result.GetText()}, nil }

🔗 More in this category

© 2026 GitRepoTrend · makiuchi-d/gozxing · Updated daily from GitHub