Port of ZXing (https://github.com/zxing/zxing) core to pure Go.
gozxing A Barcode Scanning/Encoding Library for Go
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 }