update pyzbar shim
This commit is contained in:
20
pyzbar/pyproject.toml
Normal file
20
pyzbar/pyproject.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=64", "wheel", 'tomli; python_version < "3.11"']
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "pyzbar"
|
||||
version = "0.1.9"
|
||||
description = "pyzbar barcode reader with bundled zbar library (pre-built)"
|
||||
requires-python = ">=3.8"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["pyzbar*"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
pyzbar = ["install/**/*", "*.so"]
|
||||
|
||||
[tool.shim]
|
||||
repo_url = "https://github.com/greatgitsby/op-dependencies"
|
||||
tag = "pyzbar/v0.1.9"
|
||||
datadir = "install"
|
||||
22
pyzbar/pyzbar/__init__.py
Normal file
22
pyzbar/pyzbar/__init__.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Read one-dimensional barcodes and QR codes from Python 2 and 3."""
|
||||
import os
|
||||
|
||||
__version__ = '0.1.9'
|
||||
|
||||
DIR = os.path.join(os.path.dirname(__file__), "install")
|
||||
LIB_DIR = os.path.join(DIR, "lib")
|
||||
|
||||
|
||||
def smoketest():
|
||||
import platform
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
lib_name = "libzbar.dylib"
|
||||
else:
|
||||
lib_name = "libzbar.so"
|
||||
lib_path = os.path.join(LIB_DIR, lib_name)
|
||||
assert os.path.isfile(lib_path), f"{lib_name} not found at {lib_path}"
|
||||
|
||||
from .pyzbar import decode
|
||||
result = decode((b'\x00', 1, 1))
|
||||
assert isinstance(result, list), "decode() did not return a list"
|
||||
70
pyzbar/pyzbar/locations.py
Normal file
70
pyzbar/pyzbar/locations.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from collections import namedtuple
|
||||
from itertools import chain
|
||||
from operator import itemgetter
|
||||
|
||||
|
||||
__all__ = ['bounding_box', 'convex_hull', 'Point', 'Rect']
|
||||
|
||||
|
||||
Point = namedtuple('Point', ['x', 'y'])
|
||||
Rect = namedtuple('Rect', ['left', 'top', 'width', 'height'])
|
||||
|
||||
|
||||
def bounding_box(locations):
|
||||
"""Computes the bounding box of an iterable of (x, y) coordinates.
|
||||
|
||||
Args:
|
||||
locations: iterable of (x, y) tuples.
|
||||
|
||||
Returns:
|
||||
`Rect`: Coordinates of the bounding box.
|
||||
"""
|
||||
x_values = list(map(itemgetter(0), locations))
|
||||
x_min, x_max = min(x_values), max(x_values)
|
||||
y_values = list(map(itemgetter(1), locations))
|
||||
y_min, y_max = min(y_values), max(y_values)
|
||||
return Rect(x_min, y_min, x_max - x_min, y_max - y_min)
|
||||
|
||||
|
||||
def convex_hull(points):
|
||||
"""Computes the convex hull of an iterable of (x, y) coordinates.
|
||||
|
||||
Args:
|
||||
points: iterable of (x, y) tuples.
|
||||
|
||||
Returns:
|
||||
`list`: instances of `Point` - vertices of the convex hull in
|
||||
counter-clockwise order, starting from the vertex with the
|
||||
lexicographically smallest coordinates.
|
||||
|
||||
Andrew's monotone chain algorithm. O(n log n) complexity.
|
||||
https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
|
||||
"""
|
||||
|
||||
def is_not_clockwise(p0, p1, p2):
|
||||
return 0 <= (
|
||||
(p1[0] - p0[0]) * (p2[1] - p0[1]) -
|
||||
(p1[1] - p0[1]) * (p2[0] - p0[0])
|
||||
)
|
||||
|
||||
def go(points_):
|
||||
res = []
|
||||
for p in points_:
|
||||
while 1 < len(res) and is_not_clockwise(res[-2], res[-1], p):
|
||||
res.pop()
|
||||
res.append(p)
|
||||
|
||||
# The last point in each list is the first point in the other list
|
||||
res.pop()
|
||||
|
||||
return res
|
||||
|
||||
# Discard duplicates and sort by x then y
|
||||
points = sorted(set(points))
|
||||
|
||||
# Algorithm needs at least two points
|
||||
hull = (
|
||||
points if len(points) < 2 else chain(go(points), go(reversed(points)))
|
||||
)
|
||||
|
||||
return list(map(Point._make, hull))
|
||||
236
pyzbar/pyzbar/pyzbar.py
Normal file
236
pyzbar/pyzbar/pyzbar.py
Normal file
@@ -0,0 +1,236 @@
|
||||
from collections import namedtuple
|
||||
from contextlib import contextmanager
|
||||
from ctypes import cast, c_void_p, string_at
|
||||
|
||||
from .locations import bounding_box, convex_hull, Point, Rect
|
||||
from .pyzbar_error import PyZbarError
|
||||
from .wrapper import (
|
||||
zbar_image_scanner_set_config,
|
||||
zbar_image_scanner_create, zbar_image_scanner_destroy,
|
||||
zbar_image_create, zbar_image_destroy, zbar_image_set_format,
|
||||
zbar_image_set_size, zbar_image_set_data, zbar_scan_image,
|
||||
zbar_image_first_symbol, zbar_symbol_get_data_length,
|
||||
zbar_symbol_get_data, zbar_symbol_get_orientation,
|
||||
zbar_symbol_get_loc_size, zbar_symbol_get_loc_x, zbar_symbol_get_loc_y,
|
||||
zbar_symbol_get_quality, zbar_symbol_next, ZBarConfig, ZBarOrientation,
|
||||
ZBarSymbol, EXTERNAL_DEPENDENCIES,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'decode', 'Point', 'Rect', 'Decoded', 'ZBarSymbol', 'EXTERNAL_DEPENDENCIES', 'ORIENTATION_AVAILABLE'
|
||||
]
|
||||
|
||||
|
||||
ORIENTATION_AVAILABLE = zbar_symbol_get_orientation is not None
|
||||
|
||||
Decoded = namedtuple('Decoded', 'data type rect polygon quality orientation')
|
||||
|
||||
# ZBar's magic 'fourcc' numbers that represent image formats
|
||||
_FOURCC = {
|
||||
'L800': 808466521,
|
||||
'GRAY': 1497715271
|
||||
}
|
||||
|
||||
_RANGEFN = getattr(globals(), 'xrange', range)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _image():
|
||||
"""A context manager for `zbar_image`, created and destoyed by
|
||||
`zbar_image_create` and `zbar_image_destroy`.
|
||||
|
||||
Yields:
|
||||
POINTER(zbar_image): The created image
|
||||
|
||||
Raises:
|
||||
PyZbarError: If the image could not be created.
|
||||
"""
|
||||
image = zbar_image_create()
|
||||
if not image:
|
||||
raise PyZbarError('Could not create zbar image')
|
||||
else:
|
||||
try:
|
||||
yield image
|
||||
finally:
|
||||
zbar_image_destroy(image)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _image_scanner():
|
||||
"""A context manager for `zbar_image_scanner`, created and destroyed by
|
||||
`zbar_image_scanner_create` and `zbar_image_scanner_destroy`.
|
||||
|
||||
Yields:
|
||||
POINTER(zbar_image_scanner): The created scanner
|
||||
|
||||
Raises:
|
||||
PyZbarError: If the decoder could not be created.
|
||||
"""
|
||||
scanner = zbar_image_scanner_create()
|
||||
if not scanner:
|
||||
raise PyZbarError('Could not create image scanner')
|
||||
else:
|
||||
try:
|
||||
yield scanner
|
||||
finally:
|
||||
zbar_image_scanner_destroy(scanner)
|
||||
|
||||
|
||||
def _symbols_for_image(image):
|
||||
"""Generator of symbols.
|
||||
|
||||
Args:
|
||||
image: `zbar_image`
|
||||
|
||||
Yields:
|
||||
POINTER(zbar_symbol): Symbol
|
||||
"""
|
||||
symbol = zbar_image_first_symbol(image)
|
||||
while symbol:
|
||||
yield symbol
|
||||
symbol = zbar_symbol_next(symbol)
|
||||
|
||||
|
||||
def _decode_symbols(symbols):
|
||||
"""Generator of decoded symbol information.
|
||||
|
||||
Args:
|
||||
symbols: iterable of instances of `POINTER(zbar_symbol)`
|
||||
|
||||
Yields:
|
||||
Decoded: decoded symbol
|
||||
"""
|
||||
for symbol in symbols:
|
||||
data = string_at(
|
||||
zbar_symbol_get_data(symbol),
|
||||
zbar_symbol_get_data_length(symbol)
|
||||
)
|
||||
# The 'type' int should be a value in the ZBarSymbol enumeration
|
||||
try:
|
||||
symbol_type = ZBarSymbol(symbol.contents.type)
|
||||
except ValueError:
|
||||
# This release of zbar supports a type that pyzbar does not know about
|
||||
symbol_type = "Unrecognised type [{0}]".format(symbol.contents.type)
|
||||
else:
|
||||
symbol_type = symbol_type.name
|
||||
|
||||
quality = zbar_symbol_get_quality(symbol)
|
||||
polygon = convex_hull(
|
||||
(
|
||||
zbar_symbol_get_loc_x(symbol, index),
|
||||
zbar_symbol_get_loc_y(symbol, index)
|
||||
)
|
||||
for index in _RANGEFN(zbar_symbol_get_loc_size(symbol))
|
||||
)
|
||||
|
||||
if zbar_symbol_get_orientation:
|
||||
orientation = ZBarOrientation(zbar_symbol_get_orientation(symbol)).name
|
||||
else:
|
||||
orientation = None
|
||||
|
||||
yield Decoded(
|
||||
data=data,
|
||||
type=symbol_type,
|
||||
rect=bounding_box(polygon),
|
||||
polygon=polygon,
|
||||
orientation=orientation,
|
||||
quality=quality,
|
||||
)
|
||||
|
||||
|
||||
def _pixel_data(image):
|
||||
"""Returns (pixels, width, height)
|
||||
|
||||
Returns:
|
||||
:obj: `tuple` (pixels, width, height)
|
||||
"""
|
||||
# Test for PIL.Image, numpy.ndarray, and imageio.core.util without
|
||||
# requiring that cv2, PIL, or imageio are installed.
|
||||
|
||||
image_type = str(type(image))
|
||||
if 'PIL.' in image_type:
|
||||
if 'L' != image.mode:
|
||||
image = image.convert('L')
|
||||
pixels = image.tobytes()
|
||||
width, height = image.size
|
||||
elif 'numpy.ndarray' in image_type or 'imageio.core.util' in image_type:
|
||||
# Different versions of imageio use a subclass of numpy.ndarray
|
||||
# called either imageio.core.util.Image or imageio.core.util.Array.
|
||||
if 3 == len(image.shape):
|
||||
# Take just the first channel
|
||||
image = image[:, :, 0]
|
||||
if 'uint8' != str(image.dtype):
|
||||
image = image.astype('uint8')
|
||||
try:
|
||||
pixels = image.tobytes()
|
||||
except AttributeError:
|
||||
# `numpy.ndarray.tobytes()` introduced in `numpy` 1.9.0 - use the
|
||||
# older `tostring` method.
|
||||
pixels = image.tostring()
|
||||
height, width = image.shape[:2]
|
||||
else:
|
||||
# image should be a tuple (pixels, width, height)
|
||||
pixels, width, height = image
|
||||
|
||||
# Check dimensions
|
||||
if 0 != len(pixels) % (width * height):
|
||||
raise PyZbarError(
|
||||
(
|
||||
'Inconsistent dimensions: image data of {0} bytes is not '
|
||||
'divisible by (width x height = {1})'
|
||||
).format(len(pixels), (width * height))
|
||||
)
|
||||
|
||||
# Compute bits-per-pixel
|
||||
bpp = 8 * len(pixels) // (width * height)
|
||||
if 8 != bpp:
|
||||
raise PyZbarError(
|
||||
'Unsupported bits-per-pixel [{0}]. Only [8] is supported.'.format(
|
||||
bpp
|
||||
)
|
||||
)
|
||||
|
||||
return pixels, width, height
|
||||
|
||||
|
||||
def decode(image, symbols=None):
|
||||
"""Decodes datamatrix barcodes in `image`.
|
||||
|
||||
Args:
|
||||
image: `numpy.ndarray`, `PIL.Image` or tuple (pixels, width, height)
|
||||
symbols: iter(ZBarSymbol) the symbol types to decode; if `None`, uses
|
||||
`zbar`'s default behaviour, which is to decode all symbol types.
|
||||
|
||||
Returns:
|
||||
:obj:`list` of :obj:`Decoded`: The values decoded from barcodes.
|
||||
"""
|
||||
pixels, width, height = _pixel_data(image)
|
||||
|
||||
results = []
|
||||
with _image_scanner() as scanner:
|
||||
if symbols:
|
||||
# Disable all but the symbols of interest
|
||||
disable = set(ZBarSymbol).difference(symbols)
|
||||
for symbol in disable:
|
||||
zbar_image_scanner_set_config(
|
||||
scanner, symbol, ZBarConfig.CFG_ENABLE, 0
|
||||
)
|
||||
# I think it likely that zbar will detect all symbol types by
|
||||
# default, in which case enabling the types of interest is
|
||||
# redundant but it seems sensible to be over-cautious and enable
|
||||
# them.
|
||||
for symbol in symbols:
|
||||
zbar_image_scanner_set_config(
|
||||
scanner, symbol, ZBarConfig.CFG_ENABLE, 1
|
||||
)
|
||||
with _image() as img:
|
||||
zbar_image_set_format(img, _FOURCC['L800'])
|
||||
zbar_image_set_size(img, width, height)
|
||||
zbar_image_set_data(img, cast(pixels, c_void_p), len(pixels), None)
|
||||
decoded = zbar_scan_image(scanner, img)
|
||||
if decoded < 0:
|
||||
raise PyZbarError('Unsupported image format')
|
||||
else:
|
||||
results.extend(_decode_symbols(_symbols_for_image(img)))
|
||||
|
||||
return results
|
||||
5
pyzbar/pyzbar/pyzbar_error.py
Normal file
5
pyzbar/pyzbar/pyzbar_error.py
Normal file
@@ -0,0 +1,5 @@
|
||||
__all__ = ["PyZbarError"]
|
||||
|
||||
|
||||
class PyZbarError(Exception):
|
||||
pass
|
||||
295
pyzbar/pyzbar/wrapper.py
Normal file
295
pyzbar/pyzbar/wrapper.py
Normal file
@@ -0,0 +1,295 @@
|
||||
"""Low-level wrapper around zbar's interface
|
||||
"""
|
||||
from ctypes import (
|
||||
c_ubyte, c_char_p, c_int, c_uint, c_ulong, c_void_p, Structure,
|
||||
CFUNCTYPE, POINTER
|
||||
)
|
||||
from enum import IntEnum, unique
|
||||
|
||||
from . import zbar_library
|
||||
|
||||
|
||||
__all__ = [
|
||||
'EXTERNAL_DEPENDENCIES', 'LIBZBAR', 'ZBarConfig', 'ZBarSymbol', 'ZBarOrientation',
|
||||
'zbar_image_create', 'zbar_image_destroy', 'zbar_image_first_symbol',
|
||||
'zbar_image_scanner_create', 'zbar_image_scanner_destroy',
|
||||
'zbar_image_scanner_set_config', 'zbar_image_set_data',
|
||||
'zbar_image_set_format', 'zbar_image_set_size', 'zbar_scan_image',
|
||||
'zbar_symbol_get_data_length', 'zbar_symbol_get_data',
|
||||
'zbar_symbol_get_loc_size', 'zbar_symbol_get_loc_x',
|
||||
'zbar_symbol_get_loc_y', 'zbar_symbol_next',
|
||||
'zbar_symbol_get_orientation', 'zbar_symbol_get_quality',
|
||||
]
|
||||
|
||||
# Globals populated in load_libzbar
|
||||
LIBZBAR = None
|
||||
"""ctypes.CDLL
|
||||
"""
|
||||
|
||||
EXTERNAL_DEPENDENCIES = []
|
||||
"""List of instances of ctypes.CDLL. Helpful when freezing.
|
||||
"""
|
||||
|
||||
# Types
|
||||
c_ubyte_p = POINTER(c_ubyte)
|
||||
c_uint_p = POINTER(c_uint)
|
||||
c_ulong_p = POINTER(c_ulong)
|
||||
"""unsigned char* type
|
||||
"""
|
||||
|
||||
|
||||
# Defines and enums
|
||||
@unique
|
||||
class ZBarSymbol(IntEnum):
|
||||
NONE = 0 # /**< no symbol decoded */
|
||||
PARTIAL = 1 # /**< intermediate status */
|
||||
EAN2 = 2 # /**< GS1 2-digit add-on */
|
||||
EAN5 = 5 # /**< GS1 5-digit add-on */
|
||||
EAN8 = 8 # /**< EAN-8 */
|
||||
UPCE = 9 # /**< UPC-E */
|
||||
ISBN10 = 10 # /**< ISBN-10 (from EAN-13). @since 0.4 */
|
||||
UPCA = 12 # /**< UPC-A */
|
||||
EAN13 = 13 # /**< EAN-13 */
|
||||
ISBN13 = 14 # /**< ISBN-13 (from EAN-13). @since 0.4 */
|
||||
COMPOSITE = 15 # /**< EAN/UPC composite */
|
||||
I25 = 25 # /**< Interleaved 2 of 5. @since 0.4 */
|
||||
DATABAR = 34 # /**< GS1 DataBar (RSS). @since 0.11 */
|
||||
DATABAR_EXP = 35 # /**< GS1 DataBar Expanded. @since 0.11 */
|
||||
CODABAR = 38 # /**< Codabar. @since 0.11 */
|
||||
CODE39 = 39 # /**< Code 39. @since 0.4 */
|
||||
PDF417 = 57 # /**< PDF417. @since 0.6 */
|
||||
QRCODE = 64 # /**< QR Code. @since 0.10 */
|
||||
SQCODE = 80 # /**< SQ Code. @since 0.20.1 */
|
||||
CODE93 = 93 # /**< Code 93. @since 0.11 */
|
||||
CODE128 = 128 # /**< Code 128 */
|
||||
|
||||
|
||||
@unique
|
||||
class ZBarConfig(IntEnum):
|
||||
CFG_ENABLE = 0 # /**< enable symbology/feature */
|
||||
CFG_ADD_CHECK = 1 # /**< enable check digit when optional */
|
||||
CFG_EMIT_CHECK = 2 # /**< return check digit when present */
|
||||
CFG_ASCII = 3 # /**< enable full ASCII character set */
|
||||
CFG_NUM = 4 # /**< number of boolean decoder configs */
|
||||
|
||||
CFG_MIN_LEN = 0x20 # /**< minimum data length for valid decode */
|
||||
CFG_MAX_LEN = 0x21 # /**< maximum data length for valid decode */
|
||||
|
||||
CFG_UNCERTAINTY = 0x40 # /**< required video consistency frames */
|
||||
|
||||
CFG_POSITION = 0x80 # /**< enable scanner to collect position data */
|
||||
|
||||
CFG_X_DENSITY = 0x100 # /**< image scanner vertical scan density */
|
||||
CFG_Y_DENSITY = 0x101 # /**< image scanner horizontal scan density */
|
||||
|
||||
|
||||
@unique
|
||||
class ZBarOrientation(IntEnum):
|
||||
UNKNOWN = -1 # /**< unable to determine orientation */
|
||||
UP = 0 # /**< upright, read left to right */
|
||||
RIGHT = 1 # /**< sideways, read top to bottom */
|
||||
DOWN = 2 # /**< upside-down, read right to left */
|
||||
LEFT = 3 # /**< sideways, read bottom to top */
|
||||
|
||||
|
||||
# Structs
|
||||
class zbar_image_scanner(Structure):
|
||||
"""Opaque C++ class with private implementation
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class zbar_image(Structure):
|
||||
"""Opaque C++ class with private implementation
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class zbar_symbol(Structure):
|
||||
"""Opaque C++ class with private implementation
|
||||
|
||||
The first item in the structure is an integeger value in the ZBarSymbol
|
||||
enumeration.
|
||||
"""
|
||||
_fields_ = [
|
||||
('type', c_int),
|
||||
]
|
||||
|
||||
|
||||
def load_libzbar():
|
||||
"""Loads the zbar shared library and its dependencies.
|
||||
|
||||
Populates the globals LIBZBAR and EXTERNAL_DEPENDENCIES.
|
||||
"""
|
||||
global LIBZBAR
|
||||
global EXTERNAL_DEPENDENCIES
|
||||
if not LIBZBAR:
|
||||
libzbar, dependencies = zbar_library.load()
|
||||
LIBZBAR = libzbar
|
||||
EXTERNAL_DEPENDENCIES = [LIBZBAR] + dependencies
|
||||
|
||||
return LIBZBAR
|
||||
|
||||
|
||||
# Function signatures
|
||||
def zbar_function(fname, restype, *args):
|
||||
"""Returns a foreign function exported by `zbar`.
|
||||
|
||||
Args:
|
||||
fname (:obj:`str`): Name of the exported function as string.
|
||||
restype (:obj:): Return type - one of the `ctypes` primitive C data
|
||||
types.
|
||||
*args: Arguments - a sequence of `ctypes` primitive C data types.
|
||||
|
||||
Returns:
|
||||
cddl.CFunctionType: A wrapper around the function.
|
||||
"""
|
||||
prototype = CFUNCTYPE(restype, *args)
|
||||
return prototype((fname, load_libzbar()))
|
||||
|
||||
|
||||
zbar_version = zbar_function(
|
||||
'zbar_version',
|
||||
c_int,
|
||||
c_uint_p, # major,
|
||||
c_uint_p, # minor
|
||||
)
|
||||
|
||||
zbar_set_verbosity = zbar_function(
|
||||
'zbar_set_verbosity',
|
||||
None,
|
||||
c_int
|
||||
)
|
||||
|
||||
zbar_image_scanner_create = zbar_function(
|
||||
'zbar_image_scanner_create',
|
||||
POINTER(zbar_image_scanner)
|
||||
)
|
||||
|
||||
zbar_image_scanner_destroy = zbar_function(
|
||||
'zbar_image_scanner_destroy',
|
||||
None,
|
||||
POINTER(zbar_image_scanner)
|
||||
)
|
||||
|
||||
zbar_parse_config = zbar_function(
|
||||
'zbar_parse_config',
|
||||
c_int,
|
||||
c_char_p, # config_string,
|
||||
POINTER(c_int), # symbology - values in ZBarSymbol
|
||||
POINTER(c_int), # config - values in ZBarConfig
|
||||
POINTER(c_int), # value
|
||||
)
|
||||
|
||||
zbar_image_scanner_set_config = zbar_function(
|
||||
'zbar_image_scanner_set_config',
|
||||
c_int,
|
||||
POINTER(zbar_image_scanner), # scanner
|
||||
c_int, # symbology - values in ZBarSymbol
|
||||
c_int, # config - values in ZBarConfig
|
||||
c_int # value
|
||||
)
|
||||
|
||||
zbar_image_create = zbar_function(
|
||||
'zbar_image_create',
|
||||
POINTER(zbar_image)
|
||||
)
|
||||
|
||||
zbar_image_destroy = zbar_function(
|
||||
'zbar_image_destroy',
|
||||
None,
|
||||
POINTER(zbar_image)
|
||||
)
|
||||
|
||||
zbar_image_set_format = zbar_function(
|
||||
'zbar_image_set_format',
|
||||
None,
|
||||
POINTER(zbar_image),
|
||||
c_uint
|
||||
)
|
||||
|
||||
zbar_image_set_size = zbar_function(
|
||||
'zbar_image_set_size',
|
||||
None,
|
||||
POINTER(zbar_image),
|
||||
c_uint, # width
|
||||
c_uint # height
|
||||
)
|
||||
|
||||
zbar_image_set_data = zbar_function(
|
||||
'zbar_image_set_data',
|
||||
None,
|
||||
POINTER(zbar_image),
|
||||
c_void_p, # data
|
||||
c_ulong, # raw_image_data_length
|
||||
c_void_p # A function pointer(!)
|
||||
)
|
||||
|
||||
zbar_scan_image = zbar_function(
|
||||
'zbar_scan_image',
|
||||
c_int,
|
||||
POINTER(zbar_image_scanner),
|
||||
POINTER(zbar_image)
|
||||
)
|
||||
|
||||
zbar_image_first_symbol = zbar_function(
|
||||
'zbar_image_first_symbol',
|
||||
POINTER(zbar_symbol),
|
||||
POINTER(zbar_image)
|
||||
)
|
||||
|
||||
zbar_symbol_get_data_length = zbar_function(
|
||||
'zbar_symbol_get_data_length',
|
||||
c_uint,
|
||||
POINTER(zbar_symbol)
|
||||
)
|
||||
|
||||
zbar_symbol_get_data = zbar_function(
|
||||
'zbar_symbol_get_data',
|
||||
c_ubyte_p,
|
||||
POINTER(zbar_symbol)
|
||||
)
|
||||
|
||||
zbar_symbol_get_loc_size = zbar_function(
|
||||
'zbar_symbol_get_loc_size',
|
||||
c_uint,
|
||||
POINTER(zbar_symbol)
|
||||
)
|
||||
|
||||
zbar_symbol_get_loc_x = zbar_function(
|
||||
'zbar_symbol_get_loc_x',
|
||||
c_int,
|
||||
POINTER(zbar_symbol),
|
||||
c_uint
|
||||
)
|
||||
|
||||
zbar_symbol_get_loc_y = zbar_function(
|
||||
'zbar_symbol_get_loc_y',
|
||||
c_int,
|
||||
POINTER(zbar_symbol),
|
||||
c_uint
|
||||
)
|
||||
|
||||
|
||||
try:
|
||||
zbar_symbol_get_orientation = zbar_function(
|
||||
'zbar_symbol_get_orientation',
|
||||
c_uint,
|
||||
POINTER(zbar_symbol)
|
||||
)
|
||||
except AttributeError:
|
||||
# This function not present in the original pre-20
|
||||
zbar_symbol_get_orientation = None
|
||||
|
||||
|
||||
zbar_symbol_next = zbar_function(
|
||||
'zbar_symbol_next',
|
||||
POINTER(zbar_symbol),
|
||||
POINTER(zbar_symbol)
|
||||
)
|
||||
|
||||
zbar_symbol_get_quality = zbar_function(
|
||||
'zbar_symbol_get_quality',
|
||||
c_int,
|
||||
POINTER(zbar_symbol)
|
||||
)
|
||||
22
pyzbar/pyzbar/zbar_library.py
Normal file
22
pyzbar/pyzbar/zbar_library.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Loads the zbar shared library bundled with this package."""
|
||||
import platform
|
||||
|
||||
from ctypes import cdll
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def load():
|
||||
lib_dir = Path(__file__).parent / 'install' / 'lib'
|
||||
|
||||
if platform.system() == 'Darwin':
|
||||
lib_name = 'libzbar.dylib'
|
||||
else:
|
||||
lib_name = 'libzbar.so'
|
||||
|
||||
lib_path = lib_dir / lib_name
|
||||
if not lib_path.exists():
|
||||
raise ImportError(
|
||||
f'Bundled zbar shared library not found at {lib_path}'
|
||||
)
|
||||
|
||||
return cdll.LoadLibrary(str(lib_path)), []
|
||||
103
pyzbar/setup.py
Normal file
103
pyzbar/setup.py
Normal file
@@ -0,0 +1,103 @@
|
||||
"""Shim setup.py: downloads pre-built wheels from GitHub Releases at install time."""
|
||||
import os
|
||||
import platform
|
||||
import time
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
from urllib.error import URLError
|
||||
from urllib.request import urlopen
|
||||
|
||||
try:
|
||||
import tomllib
|
||||
except ImportError:
|
||||
import tomli as tomllib
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools.command.build_py import build_py
|
||||
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
with open(os.path.join(_HERE, "pyproject.toml"), "rb") as _f:
|
||||
_cfg = tomllib.load(_f)
|
||||
|
||||
REPO_URL = _cfg["tool"]["shim"]["repo_url"]
|
||||
TAG = _cfg["tool"]["shim"]["tag"]
|
||||
DATADIR = _cfg["tool"]["shim"]["datadir"]
|
||||
VERSION = _cfg["project"]["version"]
|
||||
MODULE = _cfg["project"]["name"].replace("-", "_")
|
||||
|
||||
PLATFORM_MAP = {
|
||||
("Linux", "x86_64"): "linux_x86_64",
|
||||
("Linux", "aarch64"): "linux_aarch64",
|
||||
("Darwin", "arm64"): "macosx_11_0_arm64",
|
||||
}
|
||||
|
||||
|
||||
class InstallPrebuilt(build_py):
|
||||
def run(self):
|
||||
module_dir = os.path.join(_HERE, MODULE)
|
||||
data_dir = os.path.join(module_dir, DATADIR)
|
||||
|
||||
if not os.path.exists(os.path.join(data_dir, "bin")):
|
||||
key = (platform.system(), platform.machine())
|
||||
plat = PLATFORM_MAP.get(key)
|
||||
if plat is None:
|
||||
raise RuntimeError(f"unsupported platform: {key}")
|
||||
|
||||
whl_name = f"{MODULE}-{VERSION}-py3-none-{plat}.whl"
|
||||
url = f"{REPO_URL}/releases/download/{TAG}/{whl_name}"
|
||||
|
||||
print(f"Downloading {url} ...")
|
||||
for attempt in range(3):
|
||||
try:
|
||||
raw = urlopen(url, timeout=60).read()
|
||||
break
|
||||
except (URLError, OSError) as e:
|
||||
if attempt == 2:
|
||||
raise
|
||||
wait = 2 ** attempt
|
||||
print(f"Download failed ({e}), retrying in {wait}s ...")
|
||||
time.sleep(wait)
|
||||
|
||||
print(f"Extracting {DATADIR} ...")
|
||||
with zipfile.ZipFile(BytesIO(raw)) as zf:
|
||||
prefix = f"{MODULE}/{DATADIR}/"
|
||||
alt_prefix = f"{MODULE}-{VERSION}.data/purelib/{MODULE}/{DATADIR}/"
|
||||
for info in zf.infolist():
|
||||
for p in (prefix, alt_prefix):
|
||||
if info.filename.startswith(p):
|
||||
rel = info.filename[len(p):]
|
||||
if not rel:
|
||||
continue
|
||||
dest = os.path.join(data_dir, rel)
|
||||
if info.is_dir():
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
else:
|
||||
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
||||
with open(dest, "wb") as f:
|
||||
f.write(zf.read(info))
|
||||
if info.external_attr >> 16 & 0o111:
|
||||
os.chmod(dest, 0o755)
|
||||
break
|
||||
|
||||
# Also extract compiled extension modules (.so)
|
||||
ext_prefix = f"{MODULE}/"
|
||||
ext_alt_prefix = f"{MODULE}-{VERSION}.data/purelib/{MODULE}/"
|
||||
for info in zf.infolist():
|
||||
if not info.filename.endswith('.so'):
|
||||
continue
|
||||
for p in (ext_prefix, ext_alt_prefix):
|
||||
if info.filename.startswith(p):
|
||||
rel = info.filename[len(p):]
|
||||
if rel and '/' not in rel:
|
||||
dest = os.path.join(module_dir, rel)
|
||||
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
||||
with open(dest, "wb") as f:
|
||||
f.write(zf.read(info))
|
||||
if info.external_attr >> 16 & 0o111:
|
||||
os.chmod(dest, 0o755)
|
||||
break
|
||||
|
||||
super().run()
|
||||
|
||||
|
||||
setup(cmdclass={"build_py": InstallPrebuilt})
|
||||
Reference in New Issue
Block a user