pax_global_header00006660000000000000000000000064134317726660014531gustar00rootroot0000000000000052 comment=5a11703eed11b8e0ff49801fe6317fa5b270010f pylibdmtx-0.1.9/000077500000000000000000000000001343177266600135545ustar00rootroot00000000000000pylibdmtx-0.1.9/.coveragerc000066400000000000000000000003161343177266600156750ustar00rootroot00000000000000[run] omit = */site-packages/* [report] # Regexes for lines to exclude from consideration exclude_lines = # Have to re-enable the standard pragma pragma: no cover if __name__ *== *.__main__.: pylibdmtx-0.1.9/.gitignore000066400000000000000000000001221343177266600155370ustar00rootroot00000000000000*.py[co] *.egg-info/ .DS_Store build dist .tox .coverage htmlcov *dll MANIFEST.in pylibdmtx-0.1.9/.travis.yml000066400000000000000000000004431343177266600156660ustar00rootroot00000000000000language: - python sudo: - required python: - "2.7" - "3.4" - "3.5" - "3.6" - "3.7" before_install: - sudo apt-get -qq update - sudo apt-get install -y libdmtx0a python-opencv install: - pip install tox-travis coveralls script: - tox after_success: - coveralls pylibdmtx-0.1.9/CHANGELOG.md000066400000000000000000000010361343177266600153650ustar00rootroot00000000000000### v0.1.9 * #36 Fix Windows build ### v0.1.8 * #34 Python 3.7 * #24 Support libdmtx 0.7.5 (fixes decoding error) * #22 Use reStructuredText for README * #18 Encode support * #16 Improve error messages ### v0.1.6 * #11 Python 3.6 ### v0.1.6 * #9 Check for empty decoded value ### v0.1.5 * #7 Support older numpy ### v0.1.4 * #5 Better handling of DLLs ### v0.1.3 * #3 Convert images only as required ### v0.1.2 * #1 Incorrect handling of bits-per-pixels ### v0.1.1 * Long description onto PyPI ### v0.1.0 * Initial release pylibdmtx-0.1.9/DEVELOPING.md000066400000000000000000000056401343177266600155370ustar00rootroot00000000000000## Development ``` mkvirtualenv pylibdmtx pip install -U pip pip install -r requirements.pip nosetests python -m pylibdmtx.scripts.read_datamatrix pylibdmtx/tests/datamatrix.png ``` ### Testing python versions Make a virtual env and install `tox` ``` mkvirtualenv tox pip install tox ``` If you use non-standard locations for your Python builds, make the interpreters available on the `PATH` before running `tox`. ``` PATH=~/local/python-2.7.15/bin:~/local/python-3.4.9/bin:~/local/python-3.5.6/bin:~/local/python-3.6.8/bin:~/local/python-3.7.2/bin:$PATH tox ``` ### Windows Save the 32-bit and 64-bit `libdmtx.dll` files to `libdmtx-32.dll` and `libdmtx-64.dll` respectively, in the `pylibdmtx` directory. The `load_pylibdmtx` function in `wrapper.py` looks for the appropriate `DLL`s. The appropriate `DLL` is packaged up into the wheel build and is installed alongside the package source. This strategy allows the same method to be used when `pylibdmtx` is run from source, as an installed package and when included in a frozen binary. ## Releasing 1. Install tools. ``` pip install wheel ``` 2. Build Create source and wheel builds. The `win32` and `win_amd64` wheels will contain the appropriate `libdmtx.dll`. ``` rm -rf build dist MANIFEST.in pylibdmtx.egg-info cp MANIFEST.in.all MANIFEST.in ./setup.py bdist_wheel cat MANIFEST.in.all MANIFEST.in.win32 > MANIFEST.in ./setup.py bdist_wheel --plat-name=win32 # Remove these dirs to prevent win32 DLL from being included in win64 build rm -rf build pylibdmtx.egg-info cat MANIFEST.in.all MANIFEST.in.win64 > MANIFEST.in ./setup.py bdist_wheel --plat-name=win_amd64 rm -rf build MANIFEST.in pylibdmtx.egg-info ``` 3. Release to pypitest (see https://packaging.python.org/guides/using-testpypi/) ``` mkvirtualenv pypi pip install twine twine upload -r pypitest dist/* ``` 4. Test the release to pypitest * Check https://testpypi.python.org/pypi/pylibdmtx/ * If you are on Windows ``` set PATH=%PATH%;c:\python35\;c:\python35\scripts \Python35\Scripts\mkvirtualenv.bat --python=c:\python27\python.exe test1 ``` * Install dependencies that are not on testpypi.python.org. If you are on Python 2.x, these are mandatory ``` pip install enum34 pathlib ``` * Pillow for tests and `read_datamatrix`. We can't use the `pip install pylibdmtx[scripts]` form here because `Pillow` will not be on testpypi.python.org ``` pip install Pillow ``` * Install the package itself ``` pip install --index https://testpypi.python.org/simple pylibdmtx ``` * Test ``` read_datamatrix --help read_datamatrix ``` 5. If all is well, release to PyPI ``` twine upload dist/* ``` * Check https://pypi.python.org/pypi/pylibdmtx/ * Install! ``` pip install pylibdmtx[scripts] ``` pylibdmtx-0.1.9/LICENSE.txt000066400000000000000000000021231343177266600153750ustar00rootroot00000000000000MIT License Copyright (c) 2016 The Trustees of the Natural History Museum, London Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pylibdmtx-0.1.9/MANIFEST.in.all000066400000000000000000000000361343177266600160600ustar00rootroot00000000000000include pylibdmtx/tests/*.png pylibdmtx-0.1.9/MANIFEST.in.win32000066400000000000000000000001071343177266600162510ustar00rootroot00000000000000include pylibdmtx/libdmtx-32.dll include pylibdmtx/libdmtx-LICENSE.txt pylibdmtx-0.1.9/MANIFEST.in.win64000066400000000000000000000001071343177266600162560ustar00rootroot00000000000000include pylibdmtx/libdmtx-64.dll include pylibdmtx/libdmtx-LICENSE.txt pylibdmtx-0.1.9/README.rst000066400000000000000000000102341343177266600152430ustar00rootroot00000000000000pylibdmtx ========= .. image:: https://img.shields.io/badge/python-2.7%2C%203.4%2C%203.5%2C%203.6%2C%203.7-blue.svg :target: https://github.com/NaturalHistoryMuseum/pylibdmtx .. image:: https://badge.fury.io/py/pylibdmtx.svg :target: https://pypi.python.org/pypi/pylibdmtx .. image:: https://travis-ci.org/NaturalHistoryMuseum/pylibdmtx.svg?branch=master :target: https://travis-ci.org/NaturalHistoryMuseum/pylibdmtx .. image:: https://coveralls.io/repos/github/NaturalHistoryMuseum/pylibdmtx/badge.svg?branch=master :target: https://coveralls.io/github/NaturalHistoryMuseum/pylibdmtx?branch=master Read and write Data Matrix barcodes from Python 2 and 3 using the `libdmtx `__ library. - Pure python - Works with PIL / Pillow images, OpenCV / numpy ``ndarray``\ s, and raw bytes - Decodes locations of barcodes - No dependencies, other than the libdmtx library itself - Tested on Python 2.7, and Python 3.4 to 3.7 The older `pydmtx `__ package is stuck in Python 2.x-land. Installation ------------ The ``libdmtx`` ``DLL``\ s are included with the Windows Python wheels. On other operating systems, you will need to install the ``libdmtx`` shared library. Mac OS X: :: brew install libdmtx Linux: :: sudo apt-get install libdmtx0a Install this Python wrapper; use the second form to install dependencies of the ``read_datamatrix`` and ``write_datamatrix`` command-line scripts: :: pip install pylibdmtx pip install pylibdmtx[scripts] Example usage ------------- The ``decode`` function accepts instances of ``PIL.Image``. :: >>> from pylibdmtx.pylibdmtx import decode >>> from PIL import Image >>> decode(Image.open('pylibdmtx/tests/datamatrix.png')) [Decoded(data='Stegosaurus', rect=Rect(left=5, top=6, width=96, height=95)), Decoded(data='Plesiosaurus', rect=Rect(left=298, top=6, width=95, height=95))] It also accepts instances of ``numpy.ndarray``, which might come from loading images using `OpenCV `__. :: >>> import cv2 >>> decode(cv2.imread('pylibdmtx/tests/datamatrix.png')) [Decoded(data='Stegosaurus', rect=Rect(left=5, top=6, width=96, height=95)), Decoded(data='Plesiosaurus', rect=Rect(left=298, top=6, width=95, height=95))] You can also provide a tuple ``(pixels, width, height)`` :: >>> image = cv2.imread('pylibdmtx/tests/datamatrix.png') >>> height, width = image.shape[:2] >>> decode((image.tobytes(), width, height)) [Decoded(data='Stegosaurus', rect=Rect(left=5, top=6, width=96, height=95)), Decoded(data='Plesiosaurus', rect=Rect(left=298, top=6, width=95, height=95))] The ``encode`` function generates an image containing a Data Matrix barcode: :: >>> from pylibdmtx.pylibdmtx import encode >>> encoded = encode('hello world') >>> img = Image.frombytes('RGB', (encoded.width, encoded.height), encoded.pixels) >>> img.save('dmtx.png') Windows error message --------------------- If you see an ugly ``ImportError`` when importing ``pylibdmtx`` on Windows you will most likely need the `Visual C++ Redistributable Packages for Visual Studio 2013 `__. Install ``vcredist_x64.exe`` if using 64-bit Python, ``vcredist_x86.exe`` if using 32-bit Python. Limitations ----------- Feel free to submit a PR to address any of these. - I took the bone-headed approach of copying the logic in ``pydmtx``\ ’s ``decode`` function (in `pydmtxmodule.c `__); there might be more of ``libdmtx``\ ’s functionality that could usefully be exposed - I exposed the bare minimum of functions, defines, enums and typedefs neede to reimplement ``pydmtx``\ ’s ``decode`` function Contributors ------------ - Vinicius Kursancew (@kursancew) - first implementation of barcode writing - Joseph Weston (@jbweston) - support for ``libdmtx`` 0.7.5 License ------- ``pylibdmtx`` is distributed under the MIT license (see ``LICENCE.txt``). The ``libdmtx`` shared library is distributed under the Simplified BSD license (see ``libdmtx-LICENCE.txt``). pylibdmtx-0.1.9/pylibdmtx/000077500000000000000000000000001343177266600155705ustar00rootroot00000000000000pylibdmtx-0.1.9/pylibdmtx/__init__.py000066400000000000000000000001261343177266600177000ustar00rootroot00000000000000"""Read and write Data Matrix barcodes from Python 2 and 3.""" __version__ = '0.1.9' pylibdmtx-0.1.9/pylibdmtx/dmtx_library.py000066400000000000000000000027571343177266600206550ustar00rootroot00000000000000"""Loads libdmtx. """ import platform import sys from ctypes import cdll from ctypes.util import find_library from pathlib import Path __all__ = ['load'] def _windows_fname(): """For convenience during development and to aid debugging, the DLL name is specific to the bit depth of interpreter. This logic has its own function to make testing easier """ return 'libdmtx-64.dll' if sys.maxsize > 2**32 else 'libdmtx-32.dll' def load(): """Loads the libdmtx shared library. """ if 'Windows' == platform.system(): # Possible scenarios here # 1. Run from source, DLLs are in pylibdmtx directory # cdll.LoadLibrary() imports DLLs in repo root directory # 2. Wheel install into CPython installation # cdll.LoadLibrary() imports DLLs in package directory # 3. Wheel install into virtualenv # cdll.LoadLibrary() imports DLLs in package directory # 4. Frozen # cdll.LoadLibrary() imports DLLs alongside executable fname = _windows_fname() try: libdmtx = cdll.LoadLibrary(fname) except OSError: libdmtx = cdll.LoadLibrary( str(Path(__file__).parent.joinpath(fname)) ) else: # Assume a shared library on the path path = find_library('dmtx') if not path: raise ImportError('Unable to find dmtx shared library') libdmtx = cdll.LoadLibrary(path) return libdmtx pylibdmtx-0.1.9/pylibdmtx/libdmtx-LICENSE.txt000066400000000000000000000030011343177266600210460ustar00rootroot00000000000000Copyright 2005-2011 Mike Laughton and others. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the libdmtx project. pylibdmtx-0.1.9/pylibdmtx/pylibdmtx.py000066400000000000000000000265501343177266600201660ustar00rootroot00000000000000from __future__ import print_function import ctypes from collections import namedtuple from contextlib import contextmanager from ctypes import byref, cast, string_at from functools import partial from .pylibdmtx_error import PyLibDMTXError from .wrapper import ( c_ubyte_p, dmtxImageCreate, dmtxImageDestroy, dmtxDecodeCreate, dmtxDecodeDestroy, dmtxRegionDestroy, dmtxMessageDestroy, dmtxTimeAdd, dmtxTimeNow, dmtxDecodeMatrixRegion, dmtxRegionFindNext, dmtxMatrix3VMultiplyBy, dmtxDecodeSetProp, DmtxPackOrder, DmtxProperty, DmtxUndefined, DmtxVector2, EXTERNAL_DEPENDENCIES, DmtxSymbolSize, DmtxScheme, dmtxEncodeSetProp, dmtxEncodeDataMatrix, dmtxImageGetProp, dmtxEncodeCreate, dmtxEncodeDestroy ) __all__ = [ 'decode', 'encode', 'Encoded', 'ENCODING_SCHEME_NAMES', 'ENCODING_SIZE_NAMES', 'EXTERNAL_DEPENDENCIES', ] ENCODING_SCHEME_PREFIX = 'DmtxScheme' ENCODING_SIZE_PREFIX = 'DmtxSymbol' ENCODING_SCHEME_NAMES = sorted( n.name[len(ENCODING_SCHEME_PREFIX):] for n in DmtxScheme ) # Not sorting encoding size names - would need to use natural sort order; # the existing order within DmtxSymbolSize is sensible. ENCODING_SIZE_NAMES = [ n.name[len(ENCODING_SIZE_PREFIX):] for n in DmtxSymbolSize ] # A rectangle Rect = namedtuple('Rect', 'left top width height') # Results of reading a barcode Decoded = namedtuple('Decoded', 'data rect') # Results of encoding data to an image Encoded = namedtuple('Encoded', 'width height bpp pixels') # Crude mapping from bits-per-pixels to values in DmtxPackOrder enum _PACK_ORDER = { 8: DmtxPackOrder.DmtxPack8bppK, 16: DmtxPackOrder.DmtxPack16bppRGB, 24: DmtxPackOrder.DmtxPack24bppRGB, 32: DmtxPackOrder.DmtxPack32bppRGBX, } @contextmanager def _image(pixels, width, height, pack): """A context manager for `DmtxImage`, created and destroyed by `dmtxImageCreate` and `dmtxImageDestroy`. Args: pixels (:obj:): width (int): height (int): pack (int): Yields: DmtxImage: The created image Raises: PyLibDMTXError: If the image could not be created. """ image = dmtxImageCreate(pixels, width, height, pack) if not image: raise PyLibDMTXError('Could not create image') else: try: yield image finally: dmtxImageDestroy(byref(image)) @contextmanager def _decoder(image, shrink): """A context manager for `DmtxDecode`, created and destroyed by `dmtxDecodeCreate` and `dmtxDecodeDestroy`. Args: image (POINTER(DmtxImage)): shrink (int): Yields: POINTER(DmtxDecode): The created decoder Raises: PyLibDMTXError: If the decoder could not be created. """ decoder = dmtxDecodeCreate(image, shrink) if not decoder: raise PyLibDMTXError('Could not create decoder') else: try: yield decoder finally: dmtxDecodeDestroy(byref(decoder)) @contextmanager def _region(decoder, timeout): """A context manager for `DmtxRegion`, created and destroyed by `dmtxRegionFindNext` and `dmtxRegionDestroy`. Args: decoder (POINTER(DmtxDecode)): timeout (int or None): Yields: DmtxRegion: The next region or None, if all regions have been found. """ region = dmtxRegionFindNext(decoder, timeout) try: yield region finally: if region: dmtxRegionDestroy(byref(region)) @contextmanager def _decoded_matrix_region(decoder, region, corrections): """A context manager for `DmtxMessage`, created and destoyed by `dmtxDecodeMatrixRegion` and `dmtxMessageDestroy`. Args: decoder (POINTER(DmtxDecode)): region (POINTER(DmtxRegion)): corrections (int): Yields: DmtxMessage: The message. """ message = dmtxDecodeMatrixRegion(decoder, region, corrections) try: yield message finally: if message: dmtxMessageDestroy(byref(message)) def _decode_region(decoder, region, corrections, shrink): """Decodes and returns the value in a region. Args: region (DmtxRegion): Yields: Decoded or None: The decoded value. """ with _decoded_matrix_region(decoder, region, corrections) as msg: if msg: # Coordinates p00 = DmtxVector2() p11 = DmtxVector2(1.0, 1.0) dmtxMatrix3VMultiplyBy( p00, region.contents.fit2raw ) dmtxMatrix3VMultiplyBy(p11, region.contents.fit2raw) x0 = int((shrink * p00.X) + 0.5) y0 = int((shrink * p00.Y) + 0.5) x1 = int((shrink * p11.X) + 0.5) y1 = int((shrink * p11.Y) + 0.5) return Decoded( string_at(msg.contents.output), Rect(x0, y0, x1 - x0, y1 - y0) ) else: return None def _pixel_data(image): """Returns (pixels, width, height, bpp) Returns: :obj: `tuple` (pixels, width, height, bpp) """ # Test for PIL.Image and numpy.ndarray without requiring that cv2 or PIL # are installed. if 'PIL.' in str(type(image)): pixels = image.tobytes() width, height = image.size elif 'numpy.ndarray' in str(type(image)): 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 PyLibDMTXError(( '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 bpp not in _PACK_ORDER: raise PyLibDMTXError( 'Unsupported bits-per-pixel: [{0}] Should be one of {1}'.format( bpp, sorted(_PACK_ORDER.keys()) ) ) return pixels, width, height, bpp def decode(image, timeout=None, gap_size=None, shrink=1, shape=None, deviation=None, threshold=None, min_edge=None, max_edge=None, corrections=None, max_count=None): """Decodes datamatrix barcodes in `image`. Args: image: `numpy.ndarray`, `PIL.Image` or tuple (pixels, width, height) timeout (int): milliseconds gap_size (int): shrink (int): shape (int): deviation (int): threshold (int): min_edge (int): max_edge (int): corrections (int): max_count (int): stop after reading this many barcodes. `None` to read as many as possible. Returns: :obj:`list` of :obj:`Decoded`: The values decoded from barcodes. """ dmtx_timeout = None if timeout: now = dmtxTimeNow() dmtx_timeout = dmtxTimeAdd(now, timeout) if max_count is not None and max_count < 1: raise ValueError('Invalid max_count [{0}]'.format(max_count)) pixels, width, height, bpp = _pixel_data(image) results = [] with _image( cast(pixels, c_ubyte_p), width, height, _PACK_ORDER[bpp] ) as img: with _decoder(img, shrink) as decoder: properties = [ (DmtxProperty.DmtxPropScanGap, gap_size), (DmtxProperty.DmtxPropSymbolSize, shape), (DmtxProperty.DmtxPropSquareDevn, deviation), (DmtxProperty.DmtxPropEdgeThresh, threshold), (DmtxProperty.DmtxPropEdgeMin, min_edge), (DmtxProperty.DmtxPropEdgeMax, max_edge) ] # Set only those properties with a non-None value for prop, value in ((p, v) for p, v in properties if v is not None): dmtxDecodeSetProp(decoder, prop, value) if not corrections: corrections = DmtxUndefined while True: with _region(decoder, dmtx_timeout) as region: # Finished file or ran out of time before finding another # region if not region: break else: # Decoded res = _decode_region( decoder, region, corrections, shrink ) if res: results.append(res) # Stop if we've reached maximum count if max_count and len(results) == max_count: break return results @contextmanager def _encoder(): encoder = dmtxEncodeCreate() if not encoder: raise PyLibDMTXError('Could not create encoder') try: yield encoder finally: dmtxEncodeDestroy(byref(encoder)) def encode(data, scheme=None, size=None): """ Encodes `data` in a DataMatrix image. For now bpp is the libdmtx default which is 24 Args: data: bytes instance scheme: encoding scheme - one of `ENCODING_SCHEME_NAMES`, or `None`. If `None`, defaults to 'Ascii'. size: image dimensions - one of `ENCODING_SIZE_NAMES`, or `None`. If `None`, defaults to 'ShapeAuto'. Returns: Encoded: with properties `(width, height, bpp, pixels)`. You can use that result to build a PIL image: Image.frombytes('RGB', (width, height), pixels) """ size = size if size else 'ShapeAuto' size_name = '{0}{1}'.format(ENCODING_SIZE_PREFIX, size) if not hasattr(DmtxSymbolSize, size_name): raise PyLibDMTXError( 'Invalid size [{0}]: should be one of {1}'.format( size, ENCODING_SIZE_NAMES ) ) size = getattr(DmtxSymbolSize, size_name) scheme = scheme if scheme else 'Ascii' scheme_name = '{0}{1}'.format( ENCODING_SCHEME_PREFIX, scheme.capitalize() ) if not hasattr(DmtxScheme, scheme_name): raise PyLibDMTXError( 'Invalid scheme [{0}]: should be one of {1}'.format( scheme, ENCODING_SCHEME_NAMES ) ) scheme = getattr(DmtxScheme, scheme_name) with _encoder() as encoder: dmtxEncodeSetProp(encoder, DmtxProperty.DmtxPropScheme, scheme) dmtxEncodeSetProp(encoder, DmtxProperty.DmtxPropSizeRequest, size) if dmtxEncodeDataMatrix(encoder, len(data), cast(data, c_ubyte_p)) == 0: raise PyLibDMTXError( 'Could not encode data, possibly because the image is not ' 'large enough to contain the data' ) w, h, bpp = map( partial(dmtxImageGetProp, encoder[0].image), ( DmtxProperty.DmtxPropWidth, DmtxProperty.DmtxPropHeight, DmtxProperty.DmtxPropBitsPerPixel ) ) size = w * h * bpp // 8 pixels = cast( encoder[0].image[0].pxl, ctypes.POINTER(ctypes.c_ubyte * size) ) return Encoded( width=w, height=h, bpp=bpp, pixels=ctypes.string_at(pixels, size) ) pylibdmtx-0.1.9/pylibdmtx/pylibdmtx_error.py000066400000000000000000000000521343177266600213640ustar00rootroot00000000000000class PyLibDMTXError(Exception): pass pylibdmtx-0.1.9/pylibdmtx/scripts/000077500000000000000000000000001343177266600172575ustar00rootroot00000000000000pylibdmtx-0.1.9/pylibdmtx/scripts/__init__.py000066400000000000000000000000001343177266600213560ustar00rootroot00000000000000pylibdmtx-0.1.9/pylibdmtx/scripts/read_datamatrix.py000077500000000000000000000013021343177266600227610ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import argparse import sys import pylibdmtx from pylibdmtx.pylibdmtx import decode def main(args=None): if args is None: args = sys.argv[1:] parser = argparse.ArgumentParser( description='Reads datamatrix barcodes in images' ) parser.add_argument('image', nargs='+') parser.add_argument( '-v', '--version', action='version', version='%(prog)s ' + pylibdmtx.__version__ ) args = parser.parse_args(args) from PIL import Image for image in args.image: for barcode in decode(Image.open(image)): print(barcode.data) if __name__ == '__main__': main() pylibdmtx-0.1.9/pylibdmtx/scripts/write_datamatrix.py000066400000000000000000000024051343177266600232020ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import argparse import sys import pylibdmtx from pylibdmtx.pylibdmtx import ( encode, ENCODING_SIZE_NAMES, ENCODING_SCHEME_NAMES ) def main(args=None): if args is None: args = sys.argv[1:] parser = argparse.ArgumentParser( description='Writes a datamatrix barcode to a new image file' ) parser.add_argument('file', help='Filename of the output image') parser.add_argument( 'data', help='Data to be written; will be utf-8 encoded' ) parser.add_argument( '--size', help="Encoding size (not image dimensions); default is 'ShapeAuto'", choices=ENCODING_SIZE_NAMES ) parser.add_argument( '--scheme', help="Encoding method; default is 'Ascii'", choices=ENCODING_SCHEME_NAMES ) parser.add_argument( '-v', '--version', action='version', version='%(prog)s ' + pylibdmtx.__version__ ) args = parser.parse_args(args) from PIL import Image encoded = encode( args.data.encode('utf-8'), size=args.size, scheme=args.scheme ) im = Image.frombytes('RGB', (encoded.width, encoded.height), encoded.pixels) im.save(args.file) if __name__ == '__main__': main() pylibdmtx-0.1.9/pylibdmtx/tests/000077500000000000000000000000001343177266600167325ustar00rootroot00000000000000pylibdmtx-0.1.9/pylibdmtx/tests/__init__.py000066400000000000000000000000001343177266600210310ustar00rootroot00000000000000pylibdmtx-0.1.9/pylibdmtx/tests/datamatrix.png000066400000000000000000000056331343177266600216050ustar00rootroot00000000000000PNG  IHDRlD iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`oIIDATxnH@a˳ٌ,.Lr+s @`(0_:x@| @r͛ @@ @C!6o"@rM[ps_?eY_;w %#NK dcJ@)qX h K PHɈ @R wa-5Iۻ$ʧSu \߹/<ͯUTʯt=@JRȒ R2 @<  @4'@/w2jigթu pq~/4vQչfܾf}~42܁sE d h d @F@ـ8$@}>'Y 8oa.vlqXk=oܼ:ïVca^ 0뿮ʯ+54>];|x@| @r͛ @@ @C!6o"@.50|V鼵+Y;W>,0ˢ?ͯyg:݁8,4%A( d  @`),y  @@ h %#NK.V*ˠWʯUg? Dϲjsos0d*,4_8$b&@| @r͛ @` tw~;O9ͯUg]WߣT~볜֯:y݁8,4%A( d  @`),y  @@ h %#NK.Sv Ttު?S_VʯxwNg @N d'4@n= @N d'4@n= @NricQYujފO:oW}qu=OsUuΚlGU;}N @`#l@ @> d,h  O@$6-鮄:ּˠkU+yk=@]ou=W~ŧu5oթTY݁8,4%A( d  @`),y  @@ h %#NK.OL묟[#Nq~j{Q_yʯW}w %#NK dcJ@)qX h K PHɈ @R` OYTʟL5oՙW'@su^+;NWկxqR8,4%A( d  @`),y  @@ h %#NK.ei]_3]O?N'uU e]:~uR2 @<  @4'@1H%8,ƻrUwڼw ϳkX^DZYzz܁'"NK dcJ@)qX h K PHɈ @R` .鮇_u*ʯt=5NG@]Wp]xuC;'@1H%8,4%A( d  @`)py{J^F>+v@|,O"{~܁SgDh a6 >SgDh a6 Φ\IENDB`pylibdmtx-0.1.9/pylibdmtx/tests/empty.png000066400000000000000000000032331343177266600205770ustar00rootroot00000000000000PNG  IHDRllfW iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`oYiTXtXML:com.adobe.xmp 1 L'YIDATx1 Om_@a 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0`[#IENDB`pylibdmtx-0.1.9/pylibdmtx/tests/test_dmtx_library.py000066400000000000000000000076171343177266600230560ustar00rootroot00000000000000import platform import sys import unittest from contextlib import contextmanager from pathlib import Path try: from unittest.mock import call, patch except ImportError: # Python 2 from mock import call, patch from pylibdmtx import dmtx_library class TestLoad(unittest.TestCase): def setUp(self): self.addCleanup(patch.stopall) self.cdll = patch( 'pylibdmtx.dmtx_library.cdll', autospec=True ).start() self.find_library = patch( 'pylibdmtx.dmtx_library.find_library', autospec=True ).start() self.platform = patch( 'pylibdmtx.dmtx_library.platform', autospec=True ).start() self.windows_fname = patch( 'pylibdmtx.dmtx_library._windows_fname', autospec=True, return_value='dll fname' ).start() def test_found_non_windows(self): "libdmtx loaded ok on non-Windows platform" self.platform.system.return_value = 'Not windows' res = dmtx_library.load() self.platform.system.assert_called_once_with() self.find_library.assert_called_once_with('dmtx') self.cdll.LoadLibrary.assert_called_once_with( self.find_library.return_value ) self.assertEqual(self.cdll.LoadLibrary.return_value, res) self.assertEqual(0, self.windows_fname.call_count) def test_not_found_non_windows(self): "libdmtx not found on non-Windows platform" self.platform.system.return_value = 'Not windows' self.find_library.return_value = None self.assertRaises(ImportError, dmtx_library.load) self.platform.system.assert_called_once_with() self.find_library.assert_called_once_with('dmtx') def test_found_windows(self): "libdmtx found on Windows" self.platform.system.return_value = 'Windows' res = dmtx_library.load() self.platform.system.assert_called_once_with() self.cdll.LoadLibrary.assert_called_once_with( self.windows_fname.return_value ) self.assertEqual(self.cdll.LoadLibrary.return_value, res) def test_found_second_attempt_windows(self): "libdmtx found on the second attempt on Windows" self.platform.system.return_value = 'Windows' self.cdll.LoadLibrary.side_effect = [ OSError, # First call does not load DLL 'loaded library', # Second call loads DLL ] res = dmtx_library.load() self.platform.system.assert_called_once_with() self.cdll.LoadLibrary.assert_has_calls([ call(self.windows_fname.return_value), call(str(Path(dmtx_library.__file__).parent.joinpath( self.windows_fname.return_value ))), ]) self.assertEqual('loaded library', res) def test_not_found_windows(self): "libdmtx not found on Windows" self.platform.system.return_value = 'Windows' self.cdll.LoadLibrary.side_effect = OSError self.assertRaises(OSError, dmtx_library.load) self.platform.system.assert_called_once_with() # Two attempts at loading self.cdll.LoadLibrary.assert_has_calls([ call(self.windows_fname.return_value), call(str(Path(dmtx_library.__file__).parent.joinpath( self.windows_fname.return_value ))), ]) class TestWindowsFname(unittest.TestCase): def setUp(self): self.addCleanup(patch.stopall) self.sys = patch('pylibdmtx.dmtx_library.sys', autospec=True).start() def test_32bit(self): self.sys.maxsize = 2**32 self.assertEqual('libdmtx-32.dll', dmtx_library._windows_fname()) def test_64bit(self): # This is a 'long' on a 32-bit interpreter self.sys.maxsize = 2**32 + 1 self.assertEqual('libdmtx-64.dll', dmtx_library._windows_fname()) if __name__ == '__main__': unittest.main() pylibdmtx-0.1.9/pylibdmtx/tests/test_pylibdmtx.py000066400000000000000000000137271343177266600223710ustar00rootroot00000000000000import unittest from pathlib import Path try: from unittest.mock import call, patch except ImportError: # Python 2 from mock import call, patch import numpy as np from PIL import Image try: import cv2 except ImportError: cv2 = None from pylibdmtx.pylibdmtx import ( decode, encode, Decoded, Encoded, Rect, PyLibDMTXError, EXTERNAL_DEPENDENCIES ) from pylibdmtx.pylibdmtx_error import PyLibDMTXError TESTDATA = Path(__file__).parent class TestDecode(unittest.TestCase): EXPECTED = [ Decoded( data=b'Stegosaurus', rect=Rect(left=5, top=6, width=96, height=95) ), Decoded( data=b'Plesiosaurus', rect=Rect(left=298, top=6, width=95, height=95) ) ] def setUp(self): self.datamatrix = Image.open(str(TESTDATA.joinpath('datamatrix.png'))) self.empty = Image.open(str(TESTDATA.joinpath('empty.png'))) def tearDown(self): self.datamatrix = self.empty = None def test_decode(self): "Read both barcodes in `datamatrix.png`" res = decode(self.datamatrix) self.assertEqual(self.EXPECTED, res) def test_decode_single(self): "Read just one of the barcodes in `datamatrix.png`" res = decode(self.datamatrix, max_count=1) self.assertEqual(self.EXPECTED[:1], res) def test_decode_tuple(self): "Read barcodes in pixels" pixels = self.datamatrix.copy().convert('RGB').tobytes() width, height = self.datamatrix.size[:2] res = decode((pixels, width, height)) self.assertEqual(self.EXPECTED, res) def test_empty(self): "Do not show any output for an image that does not contain a barcode" res = decode(self.empty) self.assertEqual([], res) def test_decode_numpy(self): "Read image using Pillow and convert to numpy.ndarray" res = decode(np.asarray(self.datamatrix)) self.assertEqual(self.EXPECTED, res) @unittest.skipIf(cv2 is None, 'OpenCV not installed') def test_decode_opencv(self): "Read image using OpenCV" res = decode(cv2.imread(str(TESTDATA.joinpath('datamatrix.png')))) self.assertEqual(self.EXPECTED, res) def test_external_dependencies(self): "External dependencies" self.assertEqual(1, len(EXTERNAL_DEPENDENCIES)) self.assertIn('libdmtx', EXTERNAL_DEPENDENCIES[0]._name) @patch('pylibdmtx.pylibdmtx.dmtxImageCreate') def test_dmtxImageCreate_failed(self, dmtxImageCreate): dmtxImageCreate.return_value = None self.assertRaisesRegexp( PyLibDMTXError, 'Could not create image', decode, self.datamatrix ) self.assertEqual(1, dmtxImageCreate.call_count) @patch('pylibdmtx.pylibdmtx.dmtxDecodeCreate') def test_dmtxDecodeCreate_failed(self, dmtxDecodeCreate): dmtxDecodeCreate.return_value = None self.assertRaisesRegexp( PyLibDMTXError, 'Could not create decoder', decode, self.datamatrix ) self.assertEqual(1, dmtxDecodeCreate.call_count) def test_unsupported_bits_per_pixel(self): # 40 bits-per-pixel data = (list(range(3 * 3 * 5)), 3, 3) self.assertRaisesRegexp( PyLibDMTXError, ( 'Unsupported bits-per-pixel: \[40\] Should be one of ' '\[8, 16, 24, 32\]' ), decode, data ) def test_inconsistent_dimensions(self): # Image data has ten bytes. width x height indicates nine bytes data = (list(range(10)), 3, 3) self.assertRaisesRegexp( PyLibDMTXError, ( 'Inconsistent dimensions: image data of 10 bytes is not ' 'divisible by \(width x height = 9\)' ), decode, data ) class TestEncode(unittest.TestCase): def _assert_encoded_data(self, expected_data, encoded): # Check encoded data image = Image.frombytes( 'RGB', (encoded.width, encoded.height), encoded.pixels ) decoded = decode(image) self.assertEqual(1, len(decoded)) self.assertEqual(expected_data, decoded[0].data) def test_encode_defaults(self): data = b'hello world' encoded = encode(data) # Check returned data, with the exception of the pixel data self.assertEqual( Encoded(width=100, height=100, bpp=24, pixels=None), encoded._replace(pixels=None) ) self._assert_encoded_data(data, encoded) def test_encode_size(self): data = b'hello world' encoded = encode(data, size='36x36') # Check returned data, with the exception of the pixel data self.assertEqual( Encoded(width=200, height=200, bpp=24, pixels=None), encoded._replace(pixels=None) ) self._assert_encoded_data(data, encoded) def test_encode_scheme(self): data = b'hello world' encoded = encode(data, scheme='Base256') # Check returned data, with the exception of the pixel data self.assertEqual( Encoded(width=110, height=110, bpp=24, pixels=None), encoded._replace(pixels=None) ) self._assert_encoded_data(data, encoded) def test_invalid_scheme(self): self.assertRaisesRegexp( PyLibDMTXError, r"Invalid scheme \[asdf\]: should be one of \['Ascii", encode, b' ', scheme='asdf' ) def test_invalid_size(self): self.assertRaisesRegexp( PyLibDMTXError, r"Invalid size \[9x9\]: should be one of \['RectAuto'", encode, b' ', size='9x9' ) def test_image_not_large_enough(self): self.assertRaisesRegexp( PyLibDMTXError, 'Could not encode data, possibly because the image is not ' 'large enough to contain the data', encode, b' '*50, size='10x10' ) if __name__ == '__main__': unittest.main() pylibdmtx-0.1.9/pylibdmtx/tests/test_read_write_datamatrix.py000066400000000000000000000030451343177266600247100ustar00rootroot00000000000000import os import sys import tempfile import unittest from pathlib import Path from contextlib import contextmanager if 2 == sys.version_info[0]: from cStringIO import StringIO else: from io import StringIO from PIL import Image from pylibdmtx.scripts.read_datamatrix import main as main_read from pylibdmtx.scripts.write_datamatrix import main as main_write @contextmanager def capture_stdout(): sys.stdout, old_stdout = StringIO(), sys.stdout try: yield sys.stdout finally: sys.stdout = old_stdout class TestReadWriteDatamatrix(unittest.TestCase): def test_read_datamatrix(self): "Read datamatrix barcodes" with capture_stdout() as stdout: main_read([str(Path(__file__).parent.joinpath('datamatrix.png'))]) if 2 == sys.version_info[0]: expected = "Stegosaurus\nPlesiosaurus" else: expected = "b'Stegosaurus'\nb'Plesiosaurus'" self.assertEqual(expected, stdout.getvalue().strip()) def test_write_datamatrix(self): tmpfile = tempfile.NamedTemporaryFile(suffix='.png', delete=False) tmpfile.close() try: main_write(['--size', '44x44', tmpfile.name, 'Stegosaurus']) with capture_stdout() as stdout: main_read([tmpfile.name]) expected = "Stegosaurus" if 2 == sys.version_info[0] else "b'Stegosaurus'" self.assertEqual(expected, stdout.getvalue().strip()) finally: os.unlink(tmpfile.name) if __name__ == '__main__': unittest.main() pylibdmtx-0.1.9/pylibdmtx/wrapper.py000066400000000000000000000337341343177266600176340ustar00rootroot00000000000000"""Low-level wrapper around libdmtx's interface """ from ctypes import ( cdll, c_double, c_int, c_long, c_size_t, c_ubyte, c_uint, c_ulong, c_ulonglong, c_char_p, Structure, CFUNCTYPE, POINTER ) from enum import IntEnum, unique from distutils.version import LooseVersion from . import dmtx_library __all__ = [ 'DmtxPassFail', 'DmtxUndefined', 'DmtxVector2', 'EXTERNAL_DEPENDENCIES', 'LIBDMTX', 'c_ubyte_p', 'dmtxImageCreate', 'dmtxImageDestroy', 'dmtxDecodeCreate', 'dmtxDecodeDestroy', 'dmtxRegionDestroy', 'dmtxMessageDestroy', 'dmtxTimeAdd', 'dmtxMatrix3VMultiplyBy', 'dmtxDecodeSetProp', 'DmtxPackOrder', 'DmtxProperty', 'dmtxTimeNow', 'dmtxDecodeMatrixRegion', 'dmtxRegionFindNext' ] # Globals populated in load_libdmtx LIBDMTX = None """ctypes.CDLL """ EXTERNAL_DEPENDENCIES = [] """List of instances of ctypes.CDLL. Helpful when freezing. """ def load_libdmtx(): """Loads the libdmtx shared library. Populates the globals LIBDMTX and EXTERNAL_DEPENDENCIES. """ global LIBDMTX global EXTERNAL_DEPENDENCIES if not LIBDMTX: LIBDMTX = dmtx_library.load() EXTERNAL_DEPENDENCIES = [LIBDMTX] return LIBDMTX def libdmtx_function(fname, restype, *args): """Returns a foreign function exported by `libdmtx`. 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_libdmtx())) # Types c_ubyte_p = POINTER(c_ubyte) """unsigned char* type """ # Defines and enums DmtxUndefined = -1 # Define this function early so that it can be used in the definitions below. _dmtxVersion = libdmtx_function('dmtxVersion', c_char_p) def dmtxVersion(): return _dmtxVersion().decode() @unique class DmtxProperty(IntEnum): DmtxPropScheme = 100 DmtxPropSizeRequest = 101 DmtxPropMarginSize = 102 DmtxPropModuleSize = 103 DmtxPropFnc1 = 104 # Decoding properties DmtxPropEdgeMin = 200 DmtxPropEdgeMax = 201 DmtxPropScanGap = 202 DmtxPropSquareDevn = 203 DmtxPropSymbolSize = 204 DmtxPropEdgeThresh = 205 # Image properties DmtxPropWidth = 300 DmtxPropHeight = 301 DmtxPropPixelPacking = 302 DmtxPropBitsPerPixel = 303 DmtxPropBytesPerPixel = 304 DmtxPropRowPadBytes = 305 DmtxPropRowSizeBytes = 306 DmtxPropImageFlip = 307 DmtxPropChannelCount = 308 # Image modifiers DmtxPropXmin = 400 DmtxPropXmax = 401 DmtxPropYmin = 402 DmtxPropYmax = 403 DmtxPropScale = 404 @unique class DmtxPackOrder(IntEnum): DmtxPackCustom = 100 DmtxPack1bppK = 200 DmtxPack8bppK = 300 DmtxPack16bppRGB = 400 DmtxPack16bppRGBX = 401 DmtxPack16bppXRGB = 402 DmtxPack16bppBGR = 403 DmtxPack16bppBGRX = 404 DmtxPack16bppXBGR = 405 DmtxPack16bppYCbCr = 406 DmtxPack24bppRGB = 500 DmtxPack24bppBGR = 501 DmtxPack24bppYCbCr = 502 DmtxPack32bppRGBX = 600 DmtxPack32bppXRGB = 601 DmtxPack32bppBGRX = 602 DmtxPack32bppXBGR = 603 DmtxPack32bppCMYK = 604 @unique class DmtxFlip(IntEnum): DmtxFlipNone = 0x00 DmtxFlipX = 0x01 << 0 DmtxFlipY = 0x01 << 1 @unique class DmtxScheme(IntEnum): DmtxSchemeAutoFast = -2 DmtxSchemeAutoBest = -1 DmtxSchemeAscii = 0 DmtxSchemeC40 = 1 DmtxSchemeText = 2 DmtxSchemeX12 = 3 DmtxSchemeEdifact = 4 DmtxSchemeBase256 = 5 @unique class DmtxSymbolSize(IntEnum): DmtxSymbolRectAuto = -3 DmtxSymbolSquareAuto = -2 DmtxSymbolShapeAuto = -1 DmtxSymbol10x10 = 0 DmtxSymbol12x12 = 1 DmtxSymbol14x14 = 2 DmtxSymbol16x16 = 3 DmtxSymbol18x18 = 4 DmtxSymbol20x20 = 5 DmtxSymbol22x22 = 6 DmtxSymbol24x24 = 7 DmtxSymbol26x26 = 8 DmtxSymbol32x32 = 9 DmtxSymbol36x36 = 10 DmtxSymbol40x40 = 11 DmtxSymbol44x44 = 12 DmtxSymbol48x48 = 13 DmtxSymbol52x52 = 14 DmtxSymbol64x64 = 15 DmtxSymbol72x72 = 16 DmtxSymbol80x80 = 17 DmtxSymbol88x88 = 18 DmtxSymbol96x96 = 19 DmtxSymbol104x104 = 20 DmtxSymbol120x120 = 21 DmtxSymbol132x132 = 22 DmtxSymbol144x144 = 23 DmtxSymbol8x18 = 24 DmtxSymbol8x32 = 25 DmtxSymbol12x26 = 26 DmtxSymbol12x36 = 27 DmtxSymbol16x36 = 28 DmtxSymbol16x48 = 29 @unique class DmtxScheme(IntEnum): DmtxSchemeAutoFast = -2 DmtxSchemeAutoBest = -1 DmtxSchemeAscii = 0 DmtxSchemeC40 = 1 DmtxSchemeText = 2 DmtxSchemeX12 = 3 DmtxSchemeEdifact = 4 DmtxSchemeBase256 = 5 @unique class DmtxSymbolSize(IntEnum): DmtxSymbolRectAuto = -3 DmtxSymbolSquareAuto = -2 DmtxSymbolShapeAuto = -1 DmtxSymbol10x10 = 0 DmtxSymbol12x12 = 1 DmtxSymbol14x14 = 2 DmtxSymbol16x16 = 3 DmtxSymbol18x18 = 4 DmtxSymbol20x20 = 5 DmtxSymbol22x22 = 6 DmtxSymbol24x24 = 7 DmtxSymbol26x26 = 8 DmtxSymbol32x32 = 9 DmtxSymbol36x36 = 10 DmtxSymbol40x40 = 11 DmtxSymbol44x44 = 12 DmtxSymbol48x48 = 13 DmtxSymbol52x52 = 14 DmtxSymbol64x64 = 15 DmtxSymbol72x72 = 16 DmtxSymbol80x80 = 17 DmtxSymbol88x88 = 18 DmtxSymbol96x96 = 19 DmtxSymbol104x104 = 20 DmtxSymbol120x120 = 21 DmtxSymbol132x132 = 22 DmtxSymbol144x144 = 23 DmtxSymbol8x18 = 24 DmtxSymbol8x32 = 25 DmtxSymbol12x26 = 26 DmtxSymbol12x36 = 27 DmtxSymbol16x36 = 28 DmtxSymbol16x48 = 29 # Types DmtxPassFail = c_uint DmtxMatrix3 = c_double * 3 * 3 # Structs if LooseVersion(dmtxVersion()) < LooseVersion('0.7.5'): class DmtxMessage(Structure): _fields_ = [ ('arraySize', c_size_t), ('codeSize', c_size_t), ('outputSize', c_size_t), ('outputIdx', c_int), ('padCount', c_int), ('array', c_ubyte_p), ('code', c_ubyte_p), ('output', c_ubyte_p), ] else: class DmtxMessage(Structure): _fields_ = [ ('arraySize', c_size_t), ('codeSize', c_size_t), ('outputSize', c_size_t), ('outputIdx', c_int), ('padCount', c_int), ('fnc1', c_int), # libdmtx 0.7.5 inserts this field ('array', c_ubyte_p), ('code', c_ubyte_p), ('output', c_ubyte_p), ] class DmtxImage(Structure): _fields_ = [ ('width', c_int), ('height', c_int), ('pixelPacking', c_int), ('bitsPerPixel', c_int), ('bytesPerPixel', c_int), ('rowPadBytes', c_int), ('rowSizeBytes', c_int), ('imageFlip', c_int), ('channelCount', c_int), ('channelStart', c_int * 4), ('bitsPerChannel', c_int * 4), ('pxl', c_ubyte_p) ] class DmtxTime(Structure): _fields_ = [ ('sec', c_ulonglong), # Actually a time_t ('usec', c_ulong), ] class DmtxPixelLoc(Structure): _fields_ = [ ('X', c_int), ('Y', c_int), ] class DmtxVector2(Structure): _fields_ = [ ('X', c_double), ('Y', c_double), ] class DmtxPointFlow(Structure): _fields_ = [ ('plane', c_int), ('arrive', c_int), ('depart', c_int), ('mag', c_int), ('loc', DmtxPixelLoc), ] class DmtxBestLine(Structure): _fields_ = [ ('angle', c_int), ('hOffset', c_int), ('mag', c_int), ('stepBeg', c_int), ('stepPos', c_int), ('stepNeg', c_int), ('distSq', c_int), ('devn', c_double), ('locBeg', DmtxPixelLoc), ('locPos', DmtxPixelLoc), ('locNeg', DmtxPixelLoc), ] class DmtxScanGrid(Structure): _fields_ = [ ('minExtent', c_int), ('maxExtent', c_int), ('xOffset', c_int), ('yOffset', c_int), ('xMin', c_int), ('xMax', c_int), ('yMin', c_int), ('yMax', c_int), ('total', c_int), ('extent', c_int), ('jumpSize', c_int), ('pixelTotal', c_int), ('startPos', c_int), ('pixelCount', c_int), ('xCenter', c_int), ('yCenter', c_int), ] if LooseVersion(dmtxVersion()) < LooseVersion('0.7.5'): class DmtxDecode(Structure): _fields_ = [ ('edgeMin', c_int), ('edgeMax', c_int), ('scanGap', c_int), ('squareDevn', c_double), ('sizeIdxExpected', c_int), ('edgeThresh', c_int), ('xMin', c_int), ('xMax', c_int), ('yMin', c_int), ('yMax', c_int), ('scale', c_int), ('cache', c_ubyte_p), ('image', POINTER(DmtxImage)), ('grid', DmtxScanGrid), ] else: class DmtxDecode(Structure): _fields_ = [ ('edgeMin', c_int), ('edgeMax', c_int), ('scanGap', c_int), ('fnc1', c_int), # libdmtx 0.7.5 inserts this field ('squareDevn', c_double), ('sizeIdxExpected', c_int), ('edgeThresh', c_int), ('xMin', c_int), ('xMax', c_int), ('yMin', c_int), ('yMax', c_int), ('scale', c_int), ('cache', c_ubyte_p), ('image', POINTER(DmtxImage)), ('grid', DmtxScanGrid), ] class DmtxRegion(Structure): _fields_ = [ ('jumpToPos', c_int), ('jumpToNeg', c_int), ('stepsTotal', c_int), ('finalPos', DmtxPixelLoc), ('finalNeg', DmtxPixelLoc), ('boundMin', DmtxPixelLoc), ('boundMax', DmtxPixelLoc), ('flowBegin', DmtxPointFlow), ('polarity', c_int), ('stepR', c_int), ('stepT', c_int), ('locR', DmtxPixelLoc), ('locT', DmtxPixelLoc), ('leftKnown', c_int), ('leftAngle', c_int), ('leftLoc', DmtxPixelLoc), ('leftLine', DmtxBestLine), ('bottomKnown', c_int), ('bottomAngle', c_int), ('bottomLoc', DmtxPixelLoc), ('bottomLine', DmtxBestLine), ('topKnown', c_int), ('topAngle', c_int), ('topLoc', DmtxPixelLoc), ('rightKnown', c_int), ('rightAngle', c_int), ('rightLoc', DmtxPixelLoc), ('onColor', c_int), ('offColor', c_int), ('sizeIdx', c_int), ('symbolRows', c_int), ('symbolCols', c_int), ('mappingRows', c_int), ('mappingCols', c_int), ('raw2fit', DmtxMatrix3), ('fit2raw', DmtxMatrix3), ] if LooseVersion(dmtxVersion()) < LooseVersion('0.7.5'): class DmtxEncode(Structure): _fields_ = [ ('method', c_int), ('scheme', c_int), ('sizeIdxRequest', c_int), ('marginSize', c_int), ('moduleSize', c_int), ('pixelPacking', c_int), ('imageFlip', c_int), ('rowPadBytes', c_int), ('message', POINTER(DmtxMessage)), ('image', POINTER(DmtxImage)), ('region', DmtxRegion), ('xfrm', DmtxMatrix3), ('rxfrm', DmtxMatrix3), ] else: class DmtxEncode(Structure): _fields_ = [ ('method', c_int), ('scheme', c_int), ('sizeIdxRequest', c_int), ('marginSize', c_int), ('moduleSize', c_int), ('pixelPacking', c_int), ('imageFlip', c_int), ('rowPadBytes', c_int), ('fnc1', c_int), # libdmtx 0.7.5 inserts this field ('message', POINTER(DmtxMessage)), ('image', POINTER(DmtxImage)), ('region', DmtxRegion), ('xfrm', DmtxMatrix3), ('rxfrm', DmtxMatrix3), ] # Function signatures dmtxTimeNow = libdmtx_function('dmtxTimeNow', DmtxTime) dmtxTimeAdd = libdmtx_function( 'dmtxTimeAdd', DmtxTime, DmtxTime, # t c_long # msec ) dmtxDecodeCreate = libdmtx_function( 'dmtxDecodeCreate', POINTER(DmtxDecode), POINTER(DmtxImage), # img c_int, # scale ) dmtxDecodeDestroy = libdmtx_function( 'dmtxDecodeDestroy', DmtxPassFail, POINTER(POINTER(DmtxDecode)) ) dmtxDecodeSetProp = libdmtx_function( 'dmtxDecodeSetProp', DmtxPassFail, POINTER(DmtxDecode), c_int, # prop c_int # value ) dmtxImageCreate = libdmtx_function( 'dmtxImageCreate', POINTER(DmtxImage), POINTER(c_ubyte), # pxl c_int, # width c_int, # height c_int # pack ) dmtxImageDestroy = libdmtx_function( 'dmtxImageDestroy', DmtxPassFail, POINTER(POINTER(DmtxImage)) ) dmtxRegionFindNext = libdmtx_function( 'dmtxRegionFindNext', POINTER(DmtxRegion), POINTER(DmtxDecode), POINTER(DmtxTime) # timeout ) dmtxDecodeMatrixRegion = libdmtx_function( 'dmtxDecodeMatrixRegion', POINTER(DmtxMessage), POINTER(DmtxDecode), # dec POINTER(DmtxRegion), # reg c_int, # fix ) dmtxMatrix3VMultiplyBy = libdmtx_function( 'dmtxMatrix3VMultiplyBy', c_int, POINTER(DmtxVector2), DmtxMatrix3, ) dmtxMessageDestroy = libdmtx_function( 'dmtxMessageDestroy', DmtxPassFail, POINTER(POINTER(DmtxMessage)) ) dmtxRegionDestroy = libdmtx_function( 'dmtxRegionDestroy', DmtxPassFail, POINTER(POINTER(DmtxRegion)) ) dmtxImageGetProp = libdmtx_function( 'dmtxImageGetProp', c_int, POINTER(DmtxImage), c_int, # prop ) dmtxEncodeCreate = libdmtx_function( 'dmtxEncodeCreate', POINTER(DmtxEncode), ) dmtxEncodeDestroy = libdmtx_function( 'dmtxEncodeDestroy', DmtxPassFail, POINTER(POINTER(DmtxEncode)) ) dmtxEncodeSetProp = libdmtx_function( 'dmtxEncodeSetProp', DmtxPassFail, POINTER(DmtxEncode), c_int, # prop c_int # value ) dmtxEncodeDataMatrix = libdmtx_function( 'dmtxEncodeDataMatrix', DmtxPassFail, POINTER(DmtxEncode), c_int, POINTER(c_ubyte) ) pylibdmtx-0.1.9/requirements.pip000066400000000000000000000003661343177266600170160ustar00rootroot00000000000000enum34==1.1.6; python_version == '2.7' # Packages required for testing # TODO How to specify OpenCV? 'cv2>=2.4.8' coveralls>=1.1 mock>=2.0.0; python_version == '2.7' nose>=1.3.4 numpy>=1.8.2 pathlib>=1.0.1; python_version == '2.7' Pillow>=3.2.0 pylibdmtx-0.1.9/setup.cfg000066400000000000000000000000321343177266600153700ustar00rootroot00000000000000[bdist_wheel] universal=1 pylibdmtx-0.1.9/setup.py000077500000000000000000000041451343177266600152750ustar00rootroot00000000000000#!/usr/bin/env python import sys import pylibdmtx SCRIPTS = ['read_datamatrix', 'write_datamatrix'] # Optional dependency PILLOW = 'Pillow>=3.2.0' URL = 'https://github.com/NaturalHistoryMuseum/pylibdmtx/' def readme(): try: with open('README.rst') as f: return f.read() except: return 'Visit {0} for more details.'.format(URL) setup_data = { 'name': 'pylibdmtx', 'version': pylibdmtx.__version__, 'author': 'Lawrence Hudson', 'author_email': 'quicklizard@googlemail.com', 'url': URL, 'license': 'MIT', 'description': pylibdmtx.__doc__, 'long_description': readme(), 'long_description_content_type': 'text/x-rst', 'packages': ['pylibdmtx', 'pylibdmtx.scripts', 'pylibdmtx.tests'], 'test_suite': 'pylibdmtx.tests', 'scripts': [ 'pylibdmtx/scripts/{0}.py'.format(script) for script in SCRIPTS ], 'entry_points': { 'console_scripts': [ '{0}=pylibdmtx.scripts.{0}:main'.format(script) for script in SCRIPTS ], }, 'extras_require': { ':python_version=="2.7"': ['enum34>=1.1.6', 'pathlib>=1.0.1'], 'scripts': [ PILLOW, ], }, 'tests_require': [ # TODO How to specify OpenCV? 'cv2>=2.4.8', 'mock>=2.0.0; python_version=="2.7"', 'numpy>=1.8.2', PILLOW, ], 'include_package_data': True, 'classifiers': [ 'Development Status :: 4 - Beta', 'License :: OSI Approved :: MIT License', 'Topic :: Utilities', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', ], } def setuptools_setup(): from setuptools import setup setup(**setup_data) if (2, 7) == sys.version_info[:2] or (3, 4) <= sys.version_info: setuptools_setup() else: sys.exit('Python versions 2.7 and >= 3.4 are supported') pylibdmtx-0.1.9/tox.ini000066400000000000000000000005401343177266600150660ustar00rootroot00000000000000[tox] envlist = py27,py34,py35,py36,py37 [testenv] sitepackages = True deps = -rrequirements.pip # Do not call nosetests here - it may will run the-system wide nosetests, which # will be unaware of the created virtual environment commands = python -m nose --verbose --with-coverage --cover-inclusive --cover-tests --cover-package=pylibdmtx pylibdmtx