You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

149 lines
5.0 KiB

// Copyright 2026 Axel Waggershauser
// SPDX-License-Identifier: Apache-2.0
package zxingcpp
/*
#include "zxingcgo.h"
*/
import "C"
import (
"fmt"
"image"
"runtime"
"unsafe"
)
// ReaderOption is a functional option for configuring reader options.
type ReaderOption func(*C.ZXing_ReaderOptions)
// TryHarder instructs the reader to prioritize accuracy over speed.
func TryHarder(v bool) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setTryHarder(o, C.bool(v)) }
}
// TryRotate enables detection in rotated images (90, 180, 270 degrees).
func TryRotate(v bool) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setTryRotate(o, C.bool(v)) }
}
// TryInvert enables detection of inverted (reversed reflectance) codes when supported.
func TryInvert(v bool) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setTryInvert(o, C.bool(v)) }
}
// TryDownscale enables detection in downscaled images depending on image size.
func TryDownscale(v bool) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setTryDownscale(o, C.bool(v)) }
}
// IsPure indicates that the input contains a single perfectly aligned barcode (generated image).
func IsPure(v bool) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setIsPure(o, C.bool(v)) }
}
// ValidateOptionalChecksum enables validation of optional checksums where applicable (e.g. Code39, ITF).
func ValidateOptionalChecksum(v bool) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setValidateOptionalChecksum(o, C.bool(v)) }
}
// ReturnErrors makes ReadBarcodes return barcodes with errors (e.g. checksum errors) when true.
func ReturnErrors(v bool) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setReturnErrors(o, C.bool(v)) }
}
// WithMinLineCount sets the number of scan lines in a linear barcode that must agree to accept the result.
func WithMinLineCount(n int) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setMinLineCount(o, C.int(n)) }
}
// WithMaxNumberOfSymbols sets the maximum number of symbols (barcodes) to detect.
func WithMaxNumberOfSymbols(n int) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setMaxNumberOfSymbols(o, C.int(n)) }
}
// WithBinarizer selects the binarizer used for grayscale-to-binary conversion.
func WithBinarizer(v Binarizer) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setBinarizer(o, C.ZXing_Binarizer(v)) }
}
// WithEanAddOnSymbol controls how EAN-2/5 add-on symbols are handled (ignore/read/require).
func WithEanAddOnSymbol(v EanAddOnSymbol) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setEanAddOnSymbol(o, C.ZXing_EanAddOnSymbol(v)) }
}
// WithTextMode sets the TextMode that controls the output of Barcode.text().
func WithTextMode(v TextMode) ReaderOption {
return func(o *C.ZXing_ReaderOptions) { C.ZXing_ReaderOptions_setTextMode(o, C.ZXing_TextMode(v)) }
}
// WithFormats restricts the set of BarcodeFormats to search for (default: all supported formats).
func WithFormats(formats ...BarcodeFormat) ReaderOption {
return func(o *C.ZXing_ReaderOptions) {
ptr := (*C.ZXing_BarcodeFormat)(unsafe.Pointer(unsafe.SliceData(formats)))
C.ZXing_ReaderOptions_setFormats(o, ptr, C.int(len(formats)))
runtime.KeepAlive(formats)
}
}
func readBarcodesFromImageView(iv *ImageView, opts *C.ZXing_ReaderOptions) ([]*Barcode, error) {
if iv == nil || iv.ptr == nil {
return nil, fmt.Errorf("iv is nil")
}
res := C.ZXing_ReadBarcodes(iv.ptr, opts)
if res == nil {
return nil, zxLastError()
}
size := int(C.ZXing_Barcodes_size(res))
if size <= 0 {
return nil, nil
}
defer C.ZXing_Barcodes_delete(res)
out := make([]*Barcode, 0, size)
for i := range size {
bc := C.ZXing_Barcodes_move(res, C.int(i))
if bc == nil {
return nil, zxLastError("failed to move barcode")
}
out = append(out, &Barcode{ptr: bc})
}
return out, nil
}
// ReadBarcodes scans for barcodes from either a *ImageView or a standard image.Image with optional configuration options,
// returning a slice of found barcodes. Example usage:
//
// barcodes, err := ReadBarcodes(img, WithFormats(BarcodeFormatQRCode), TryInvert(false))
func ReadBarcodes(src any, opts ...ReaderOption) ([]*Barcode, error) {
var ro *C.ZXing_ReaderOptions
if len(opts) > 0 {
ro = C.ZXing_ReaderOptions_new()
if ro == nil {
panic("failed to create ReaderOptions") // only out of memory should cause this, so panic is reasonable
}
defer C.ZXing_ReaderOptions_delete(ro)
for _, opt := range opts {
opt(ro)
}
}
switch v := src.(type) {
case *ImageView:
return readBarcodesFromImageView(v, ro)
case image.Image:
iv, err := NewImageViewFromImage(v)
if err != nil {
return nil, err
}
defer iv.Close()
return readBarcodesFromImageView(iv, ro)
default:
return nil, fmt.Errorf("unsupported source type %T; expected *ImageView or image.Image", src)
}
}