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