mirror of
				https://github.com/python-escpos/python-escpos
				synced 2025-10-23 09:30:00 +00:00 
			
		
		
		
	
							
								
								
									
										3
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								.mailmap
									
									
									
									
									
								
							@@ -8,4 +8,5 @@ Cody (Quantified Code Bot) <cody@quantifiedcode.com> Cody <cody@quantifiedcode.c
 | 
			
		||||
Renato Lorenzi <renato.lorenzi@senior.com.br>        Renato.Lorenzi <renato.lorenzi@senior.com.br>
 | 
			
		||||
Ahmed Tahri <nyuubi.10@gmail.com>                    TAHRI Ahmed <nyuubi.10@gmail.com>
 | 
			
		||||
Michael Elsdörfer <michael@elsdoerfer.com>           Michael Elsdörfer <michael@elsdoerfer.info>
 | 
			
		||||
csoft2k <csoft2k@hotmail.com>
 | 
			
		||||
csoft2k <csoft2k@hotmail.com>
 | 
			
		||||
Sergio Pulgarin <sergio.pulgarin@gmail.com>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							@@ -12,6 +12,7 @@ Hark
 | 
			
		||||
Joel Lehtonen
 | 
			
		||||
Kristi
 | 
			
		||||
ldos
 | 
			
		||||
Lucy Linder
 | 
			
		||||
Manuel F Martinez
 | 
			
		||||
Michael Billington
 | 
			
		||||
Michael Elsdörfer
 | 
			
		||||
@@ -22,6 +23,7 @@ Qian Linfeng
 | 
			
		||||
Renato Lorenzi
 | 
			
		||||
Romain Porte
 | 
			
		||||
Sam Cheng
 | 
			
		||||
Sergio Pulgarin
 | 
			
		||||
Stephan Sokolow
 | 
			
		||||
Thijs Triemstra
 | 
			
		||||
Thomas van den Berg
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,24 @@
 | 
			
		||||
*********
 | 
			
		||||
Changelog
 | 
			
		||||
*********
 | 
			
		||||
2017-10-08 - Version 3.0a3 - "Just Testing"
 | 
			
		||||
-------------------------------------------
 | 
			
		||||
This release is the fourth alpha release of the new version 3.0. Please
 | 
			
		||||
be aware that the API will still change until v3.0 is released.
 | 
			
		||||
 | 
			
		||||
changes
 | 
			
		||||
^^^^^^^
 | 
			
		||||
- minor changes in documentation, tests and examples
 | 
			
		||||
- pickle capabilities for faster startup
 | 
			
		||||
- first implementation of centering images and QR
 | 
			
		||||
- check barcodes based on regex
 | 
			
		||||
 | 
			
		||||
contributors
 | 
			
		||||
^^^^^^^^^^^^
 | 
			
		||||
- Patrick Kanzler
 | 
			
		||||
- Lucy Linder
 | 
			
		||||
- Romain Porte
 | 
			
		||||
- Sergio Pulgarin
 | 
			
		||||
 | 
			
		||||
2017-08-04 - Version 3.0a2 - "It's My Party And I'll Sing If I Want To"
 | 
			
		||||
-----------------------------------------------------------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ When contributing the first time, please include a commit with the output of thi
 | 
			
		||||
Otherwise the integration-check will fail.
 | 
			
		||||
 | 
			
		||||
When you change your username or mail-address, please also update the `.mailmap` and the authors-list.
 | 
			
		||||
You can find a good documentation on the mapping-feature in the `documentation of git-shortlog <https://git-scm.com/docs/git-shortlog#_mapping_authors>`_.
 | 
			
		||||
 | 
			
		||||
Style-Guide
 | 
			
		||||
-----------
 | 
			
		||||
@@ -47,9 +48,11 @@ Often you can achieve compatibility quite easily with a tool from the `six`-pack
 | 
			
		||||
 | 
			
		||||
PEP8
 | 
			
		||||
^^^^
 | 
			
		||||
This is not yet consequently done in every piece of code, but please try to ensure
 | 
			
		||||
that your code honors PEP8.
 | 
			
		||||
The checks by Landscape and QuantifiedCode that run on every PR will provide you with hints.
 | 
			
		||||
The entire codebase adheres to the rules of PEP8.
 | 
			
		||||
These rules are enforced by running `flake8` in the integration-checks.
 | 
			
		||||
Please adhere to these rules as your contribution can only be merged if the check succeeds.
 | 
			
		||||
You can use flake8 or similar tools locally in order to check your code.
 | 
			
		||||
Apart from that the travis-log and the check by Landscape will provide you with hints.
 | 
			
		||||
 | 
			
		||||
GIT
 | 
			
		||||
^^^
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								README.rst
									
									
									
									
									
								
							@@ -70,4 +70,13 @@ The full project-documentation is available on `Read the Docs <https://python-es
 | 
			
		||||
Contributing
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
This project is open for any contribution! Please see CONTRIBUTING.rst for more information.
 | 
			
		||||
This project is open for any contribution! Please see `CONTRIBUTING.rst <http://python-escpos.readthedocs.io/en/latest/dev/contributing.html>`_ for more information.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Disclaimer
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
None of the vendors cited in this project agree or endorse any of the patterns or implementations.
 | 
			
		||||
Its names are used only to maintain context.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
GENLIST=$(git shortlog -s -n | cut -f2 | sort)
 | 
			
		||||
GENLIST=$(git shortlog -s -n | cut -f2 | sort -f)
 | 
			
		||||
AUTHORSFILE="$(dirname $0)/../AUTHORS"
 | 
			
		||||
TEMPAUTHORSFILE="/tmp/python-escpos-authorsfile"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,14 +37,14 @@ def print_codepage(printer, codepage):
 | 
			
		||||
    sep = ""
 | 
			
		||||
 | 
			
		||||
    # Table header
 | 
			
		||||
    printer.set(text_type='B')
 | 
			
		||||
    printer.set(font='b')
 | 
			
		||||
    printer._raw("  {}\n".format(sep.join(map(lambda s: hex(s)[2:], range(0, 16)))))
 | 
			
		||||
    printer.set()
 | 
			
		||||
 | 
			
		||||
    # The table
 | 
			
		||||
    for x in range(0, 16):
 | 
			
		||||
        # First column
 | 
			
		||||
        printer.set(text_type='B')
 | 
			
		||||
        printer.set(font='b')
 | 
			
		||||
        printer._raw("{} ".format(hex(x)[2:]))
 | 
			
		||||
        printer.set()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,4 +16,4 @@ if __name__ == '__main__':
 | 
			
		||||
 | 
			
		||||
    # Adapt to your needs
 | 
			
		||||
    p = Usb(0x0416, 0x5011, profile="POS-5890")
 | 
			
		||||
    p.qr(content)
 | 
			
		||||
    p.qr(content, center=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -123,5 +123,5 @@ printer.set(font='a', height=2, align='center', bold=True, double_height=False)
 | 
			
		||||
printer.text('Forecast: \n')
 | 
			
		||||
forecast(0)
 | 
			
		||||
forecast(1)
 | 
			
		||||
printer.cut
 | 
			
		||||
printer.cut()
 | 
			
		||||
printer.control("LF")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								setup.py
									
									
									
									
									
								
							@@ -68,7 +68,6 @@ setup(
 | 
			
		||||
    url='https://github.com/python-escpos/python-escpos',
 | 
			
		||||
    download_url='https://github.com/python-escpos/python-escpos/archive/master.zip',
 | 
			
		||||
    description='Python library to manipulate ESC/POS Printers',
 | 
			
		||||
    bugtrack_url='https://github.com/python-escpos/python-escpos/issues',
 | 
			
		||||
    license='MIT',
 | 
			
		||||
    long_description=read('README.rst'),
 | 
			
		||||
    author='Manuel F Martinez and others',
 | 
			
		||||
@@ -114,7 +113,7 @@ setup(
 | 
			
		||||
        'pyserial',
 | 
			
		||||
        'six',
 | 
			
		||||
        'appdirs',
 | 
			
		||||
        'pyyaml',
 | 
			
		||||
        'PyYAML',
 | 
			
		||||
        'argparse',
 | 
			
		||||
        'argcomplete',
 | 
			
		||||
        'future',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,47 @@
 | 
			
		||||
import re
 | 
			
		||||
import six
 | 
			
		||||
from os import environ, path
 | 
			
		||||
import pickle
 | 
			
		||||
import logging
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import yaml
 | 
			
		||||
 | 
			
		||||
# Load external printer database
 | 
			
		||||
if 'ESCPOS_CAPABILITIES_FILE' in environ:
 | 
			
		||||
    file_path = environ['ESCPOS_CAPABILITIES_FILE']
 | 
			
		||||
else:
 | 
			
		||||
    file_path = path.join(path.dirname(__file__), 'capabilities.json')
 | 
			
		||||
 | 
			
		||||
with open(file_path) as f:
 | 
			
		||||
    CAPABILITIES = yaml.load(f)
 | 
			
		||||
logging.basicConfig()
 | 
			
		||||
logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pickle_dir = environ.get('ESCPOS_CAPABILITIES_PICKLE_DIR', '/tmp/')
 | 
			
		||||
pickle_path = path.join(pickle_dir, 'capabilities.pickle')
 | 
			
		||||
capabilities_path = environ.get(
 | 
			
		||||
    'ESCPOS_CAPABILITIES_FILE',
 | 
			
		||||
    path.join(path.dirname(__file__), 'capabilities.json'))
 | 
			
		||||
 | 
			
		||||
# Load external printer database
 | 
			
		||||
t0 = time.time()
 | 
			
		||||
logger.debug('Using capabilities from file: %s', capabilities_path)
 | 
			
		||||
if path.exists(pickle_path):
 | 
			
		||||
    if path.getmtime(capabilities_path) > path.getmtime(pickle_path):
 | 
			
		||||
        logger.debug('Found a more recent capabilities file')
 | 
			
		||||
        full_load = True
 | 
			
		||||
    else:
 | 
			
		||||
        full_load = False
 | 
			
		||||
        logger.debug('Loading capabilities from pickle in %s', pickle_path)
 | 
			
		||||
        with open(pickle_path, 'rb') as cf:
 | 
			
		||||
            CAPABILITIES = pickle.load(cf)
 | 
			
		||||
else:
 | 
			
		||||
    logger.debug('Capabilities pickle file not found: %s', pickle_path)
 | 
			
		||||
    full_load = True
 | 
			
		||||
 | 
			
		||||
if full_load:
 | 
			
		||||
    logger.debug('Loading and pickling capabilities')
 | 
			
		||||
    with open(capabilities_path) as cp, open(pickle_path, 'wb') as pp:
 | 
			
		||||
        CAPABILITIES = yaml.load(cp)
 | 
			
		||||
        pickle.dump(CAPABILITIES, pp, protocol=2)
 | 
			
		||||
 | 
			
		||||
logger.debug('Finished loading capabilities took %.2fs', time.time() - t0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PROFILES = CAPABILITIES['profiles']
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -225,6 +225,24 @@ BARCODE_TYPE_B = {
 | 
			
		||||
    'GS1 DATABAR EXPANDED':        _SET_BARCODE_TYPE(78),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BARCODE_FORMATS = {
 | 
			
		||||
    'UPC-A':                       ([(11, 12)], "^[0-9]{11,12}$"),
 | 
			
		||||
    'UPC-E':                       ([(7, 8), (11, 12)], "^([0-9]{7,8}|[0-9]{11,12})$"),
 | 
			
		||||
    'EAN13':                       ([(12, 13)], "^[0-9]{12,13}$"),
 | 
			
		||||
    'EAN8':                        ([(7, 8)], "^[0-9]{7,8}$"),
 | 
			
		||||
    'CODE39':                      ([(1, 255)], "^([0-9A-Z \$\%\+\-\.\/]+|\*[0-9A-Z \$\%\+\-\.\/]+\*)$"),
 | 
			
		||||
    'ITF':                         ([(2, 255)], "^([0-9]{2})+$"),
 | 
			
		||||
    'NW7':                         ([(1, 255)], "^[A-Da-d][0-9\$\+\-\.\/\:]+[A-Da-d]$"),
 | 
			
		||||
    'CODABAR':                     ([(1, 255)], "^[A-Da-d][0-9\$\+\-\.\/\:]+[A-Da-d]$"),  # Same as NW7
 | 
			
		||||
    'CODE93':                      ([(1, 255)], "^[\\x00-\\x7F]+$"),
 | 
			
		||||
    'CODE128':                     ([(2, 255)], "^\{[A-C][\\x00-\\x7F]+$"),
 | 
			
		||||
    'GS1-128':                     ([(2, 255)], "^\{[A-C][\\x00-\\x7F]+$"), # same as CODE128
 | 
			
		||||
    'GS1 DATABAR OMNIDIRECTIONAL': ([(13,13)], "^[0-9]{13}$"),
 | 
			
		||||
    'GS1 DATABAR TRUNCATED':       ([(13,13)], "^[0-9]{13}$"), # same as GS1 omnidirectional
 | 
			
		||||
    'GS1 DATABAR LIMITED':         ([(13,13)], "^[01][0-9]{12}$"),
 | 
			
		||||
    'GS1 DATABAR EXPANDED':        ([(2,255)], "^\([0-9][A-Za-z0-9 \!\"\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\_\{]+$"),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BARCODE_TYPES = {
 | 
			
		||||
    'A': BARCODE_TYPE_A,
 | 
			
		||||
    'B': BARCODE_TYPE_B,
 | 
			
		||||
 
 | 
			
		||||
@@ -19,13 +19,14 @@ import qrcode
 | 
			
		||||
import textwrap
 | 
			
		||||
import six
 | 
			
		||||
import time
 | 
			
		||||
from re import match as re_match
 | 
			
		||||
 | 
			
		||||
import barcode
 | 
			
		||||
from barcode.writer import ImageWriter
 | 
			
		||||
 | 
			
		||||
from .constants import ESC, GS, NUL, QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q
 | 
			
		||||
from .constants import QR_MODEL_1, QR_MODEL_2, QR_MICRO, BARCODE_TYPES, BARCODE_HEIGHT, BARCODE_WIDTH
 | 
			
		||||
from .constants import BARCODE_FONT_A, BARCODE_FONT_B
 | 
			
		||||
from .constants import BARCODE_FONT_A, BARCODE_FONT_B, BARCODE_FORMATS
 | 
			
		||||
from .constants import BARCODE_TXT_OFF, BARCODE_TXT_BTH, BARCODE_TXT_ABV, BARCODE_TXT_BLW
 | 
			
		||||
from .constants import TXT_SIZE, TXT_NORMAL
 | 
			
		||||
from .constants import SET_FONT
 | 
			
		||||
@@ -80,14 +81,14 @@ class Escpos(object):
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def _read(self, msg):
 | 
			
		||||
    def _read(self):
 | 
			
		||||
        """ Returns a NotImplementedError if the instance of the class doesn't override this method.
 | 
			
		||||
        :raises NotImplementedError
 | 
			
		||||
        """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def image(self, img_source, high_density_vertical=True, high_density_horizontal=True, impl="bitImageRaster",
 | 
			
		||||
              fragment_height=960):
 | 
			
		||||
              fragment_height=960, center=False):
 | 
			
		||||
        """ Print an image
 | 
			
		||||
 | 
			
		||||
        You can select whether the printer should print in high density or not. The default value is high density.
 | 
			
		||||
@@ -108,14 +109,19 @@ class Escpos(object):
 | 
			
		||||
        :param high_density_horizontal: print in high density in horizontal direction *default:* True
 | 
			
		||||
        :param impl: choose image printing mode between `bitImageRaster`, `graphics` or `bitImageColumn`
 | 
			
		||||
        :param fragment_height: Images larger than this will be split into multiple fragments *default:* 960
 | 
			
		||||
        :param center: Center image horizontally *default:* False
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        im = EscposImage(img_source)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            max_width = int(self.profile.profile_data['media']['width']['pixels'])
 | 
			
		||||
 | 
			
		||||
            if im.width > max_width:
 | 
			
		||||
                raise ImageWidthError('{} > {}'.format(im.width, max_width))
 | 
			
		||||
 | 
			
		||||
            if center:
 | 
			
		||||
                im.center(max_width)
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            # If the printer's pixel width is not known, print anyways...
 | 
			
		||||
            pass
 | 
			
		||||
@@ -173,7 +179,8 @@ class Escpos(object):
 | 
			
		||||
        header = self._int_low_high(len(data) + 2, 2)
 | 
			
		||||
        self._raw(GS + b'(L' + header + m + fn + data)
 | 
			
		||||
 | 
			
		||||
    def qr(self, content, ec=QR_ECLEVEL_L, size=3, model=QR_MODEL_2, native=False):
 | 
			
		||||
    def qr(self, content, ec=QR_ECLEVEL_L, size=3, model=QR_MODEL_2,
 | 
			
		||||
           native=False, center=False):
 | 
			
		||||
        """ Print QR Code for the provided string
 | 
			
		||||
 | 
			
		||||
        :param content: The content of the code. Numeric data will be more efficiently compacted.
 | 
			
		||||
@@ -185,6 +192,7 @@ class Escpos(object):
 | 
			
		||||
            by all printers).
 | 
			
		||||
        :param native: True to render the code on the printer, False to render the code as an image and send it to the
 | 
			
		||||
            printer (Default)
 | 
			
		||||
        :param center: Centers the code *default:* False
 | 
			
		||||
        """
 | 
			
		||||
        # Basic validation
 | 
			
		||||
        if ec not in [QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q]:
 | 
			
		||||
@@ -211,12 +219,17 @@ class Escpos(object):
 | 
			
		||||
            qr_code.make(fit=True)
 | 
			
		||||
            qr_img = qr_code.make_image()
 | 
			
		||||
            im = qr_img._img.convert("RGB")
 | 
			
		||||
 | 
			
		||||
            # Convert the RGB image in printable image
 | 
			
		||||
            self.text('\n')
 | 
			
		||||
            self.image(im)
 | 
			
		||||
            self.image(im, center=center)
 | 
			
		||||
            self.text('\n')
 | 
			
		||||
            self.text('\n')
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if center:
 | 
			
		||||
            raise NotImplementedError("Centering not implemented for native QR rendering")
 | 
			
		||||
 | 
			
		||||
        # Native 2D code printing
 | 
			
		||||
        cn = b'1'  # Code type for QR code
 | 
			
		||||
        # Select model: 1, 2 or micro.
 | 
			
		||||
@@ -275,17 +288,42 @@ class Escpos(object):
 | 
			
		||||
        else:
 | 
			
		||||
            self.magic.force_encoding(code)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def check_barcode(bc, code):
 | 
			
		||||
        """
 | 
			
		||||
        This method checks if the barcode is in the proper format.
 | 
			
		||||
        The validation concerns the barcode length and the set of characters, but won't compute/validate any checksum.
 | 
			
		||||
        The full set of requirement for each barcode type is available in the ESC/POS documentation.
 | 
			
		||||
 | 
			
		||||
        As an example, using EAN13, the barcode `12345678901` will be correct, because it can be rendered by the
 | 
			
		||||
        printer. But it does not suit the EAN13 standard, because the checksum digit is missing. Adding a wrong
 | 
			
		||||
        checksum in the end will also be considered correct, but adding a letter won't (EAN13 is numeric only).
 | 
			
		||||
 | 
			
		||||
        .. todo:: Add a method to compute the checksum for the different standards
 | 
			
		||||
 | 
			
		||||
        .. todo:: For fixed-length standards with mandatory checksum (EAN, UPC),
 | 
			
		||||
            compute and add the checksum automatically if missing.
 | 
			
		||||
 | 
			
		||||
        :param bc: barcode format, see :py:func`~escpos.Escpos.barcode`
 | 
			
		||||
        :param code: alphanumeric data to be printed as bar code, see :py:func`~escpos.Escpos.barcode`
 | 
			
		||||
        :return: bool
 | 
			
		||||
        """
 | 
			
		||||
        if bc not in BARCODE_FORMATS:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        bounds, regex = BARCODE_FORMATS[bc]
 | 
			
		||||
        return any(bound[0] <= len(code) <= bound[1] for bound in bounds) and re_match(regex, code)
 | 
			
		||||
 | 
			
		||||
    def barcode(self, code, bc, height=64, width=3, pos="BELOW", font="A",
 | 
			
		||||
                align_ct=True, function_type=None):
 | 
			
		||||
                align_ct=True, function_type=None, check=True):
 | 
			
		||||
        """ Print Barcode
 | 
			
		||||
 | 
			
		||||
        This method allows to print barcodes. The rendering of the barcode is done by the printer and therefore has to
 | 
			
		||||
        be supported by the unit. Currently you have to check manually whether your barcode text is correct. Uncorrect
 | 
			
		||||
        barcodes may lead to unexpected printer behaviour. There are two forms of the barcode function. Type A is
 | 
			
		||||
        default but has fewer barcodes, while type B has some more to choose from.
 | 
			
		||||
 | 
			
		||||
        .. todo:: Add a method to check barcode codes. Alternatively or as an addition write explanations about each
 | 
			
		||||
                  barcode-type. Research whether the check digits can be computed autmatically.
 | 
			
		||||
        be supported by the unit. By default, this method will check whether your barcode text is correct, that is
 | 
			
		||||
        the characters and lengths are supported by ESCPOS. Call the method with `check=False` to disable the check, but
 | 
			
		||||
        note that uncorrect barcodes may lead to unexpected printer behaviour.
 | 
			
		||||
        There are two forms of the barcode function. Type A is default but has fewer barcodes,
 | 
			
		||||
        while type B has some more to choose from.
 | 
			
		||||
 | 
			
		||||
        Use the parameters `height` and `width` for adjusting of the barcode size. Please take notice that the barcode
 | 
			
		||||
        will not be printed if it is outside of the printable area. (Which should be impossible with this method, so
 | 
			
		||||
@@ -353,6 +391,10 @@ class Escpos(object):
 | 
			
		||||
            function based on the current profile.
 | 
			
		||||
            *default*: A
 | 
			
		||||
 | 
			
		||||
        :param check: If this parameter is True, the barcode format will be checked to ensure it meets the bc
 | 
			
		||||
            requirements as defigned in the esc/pos documentation. See py:func:`~escpos.Escpos.check_barcode`
 | 
			
		||||
            for more information. *default*: True.
 | 
			
		||||
 | 
			
		||||
        :raises: :py:exc:`~escpos.exceptions.BarcodeSizeError`,
 | 
			
		||||
                 :py:exc:`~escpos.exceptions.BarcodeTypeError`,
 | 
			
		||||
                 :py:exc:`~escpos.exceptions.BarcodeCodeError`
 | 
			
		||||
@@ -375,12 +417,19 @@ class Escpos(object):
 | 
			
		||||
        bc_types = BARCODE_TYPES[function_type.upper()]
 | 
			
		||||
        if bc.upper() not in bc_types.keys():
 | 
			
		||||
            raise BarcodeTypeError((
 | 
			
		||||
                "Barcode type '{bc}' not valid for barcode function type "
 | 
			
		||||
                "Barcode '{bc}' not valid for barcode function type "
 | 
			
		||||
                "{function_type}").format(
 | 
			
		||||
                    bc=bc,
 | 
			
		||||
                    function_type=function_type,
 | 
			
		||||
                ))
 | 
			
		||||
 | 
			
		||||
        if check and not self.check_barcode(bc, code):
 | 
			
		||||
            raise BarcodeCodeError((
 | 
			
		||||
                "Barcode '{code}' not in a valid format for type '{bc}'").format(
 | 
			
		||||
                code=code,
 | 
			
		||||
                bc=bc,
 | 
			
		||||
            ))
 | 
			
		||||
 | 
			
		||||
        # Align Bar Code()
 | 
			
		||||
        if align_ct:
 | 
			
		||||
            self._raw(TXT_STYLE['align']['center'])
 | 
			
		||||
 
 | 
			
		||||
@@ -77,9 +77,10 @@ class BarcodeSizeError(Error):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BarcodeCodeError(Error):
 | 
			
		||||
    """ No Barcode code was supplied.
 | 
			
		||||
    """ No Barcode code was supplied, or it is incorrect.
 | 
			
		||||
 | 
			
		||||
    No data for the barcode has been supplied in :py:meth:`escpos.escpos.Escpos.barcode`.
 | 
			
		||||
    No data for the barcode has been supplied in :py:meth:`escpos.escpos.Escpos.barcode` or the the `check` parameter
 | 
			
		||||
    was True and the check failed.
 | 
			
		||||
    The returncode for this exception is `30`.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, msg=""):
 | 
			
		||||
 
 | 
			
		||||
@@ -115,3 +115,19 @@ class EscposImage(object):
 | 
			
		||||
            box = (left, upper, right, lower)
 | 
			
		||||
            fragments.append(self.img_original.crop(box))
 | 
			
		||||
        return fragments
 | 
			
		||||
 | 
			
		||||
    def center(self, max_width):
 | 
			
		||||
        """In-place image centering
 | 
			
		||||
 | 
			
		||||
        :param: Maximum width in order to deduce x offset for centering
 | 
			
		||||
        :return: None
 | 
			
		||||
        """
 | 
			
		||||
        old_width, height = self._im.size
 | 
			
		||||
        new_size = (max_width, height)
 | 
			
		||||
 | 
			
		||||
        new_im = Image.new("1", new_size)
 | 
			
		||||
        paste_x = int((max_width - old_width) / 2)
 | 
			
		||||
 | 
			
		||||
        new_im.paste(self._im, (paste_x, 0))
 | 
			
		||||
 | 
			
		||||
        self._im = new_im
 | 
			
		||||
 
 | 
			
		||||
@@ -243,7 +243,7 @@ class File(Escpos):
 | 
			
		||||
    def __init__(self, devfile="/dev/usb/lp0", auto_flush=True, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        :param devfile : Device file under dev filesystem
 | 
			
		||||
        :param devfile: Device file under dev filesystem
 | 
			
		||||
        :param auto_flush: automatically call flush after every call of _raw()
 | 
			
		||||
        """
 | 
			
		||||
        Escpos.__init__(self, *args, **kwargs)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ from __future__ import unicode_literals
 | 
			
		||||
import escpos.printer as printer
 | 
			
		||||
from escpos.constants import BARCODE_TYPE_A, BARCODE_TYPE_B
 | 
			
		||||
from escpos.capabilities import Profile, BARCODE_B
 | 
			
		||||
from escpos.exceptions import BarcodeTypeError
 | 
			
		||||
from escpos.exceptions import BarcodeTypeError, BarcodeCodeError
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -36,3 +36,17 @@ def test_lacks_support(bctype, supports_b):
 | 
			
		||||
        instance.barcode('test', bctype)
 | 
			
		||||
 | 
			
		||||
    assert instance.output == b''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize("bctype,data", [
 | 
			
		||||
    ('EAN13', 'AA'),
 | 
			
		||||
    ('CODE128', '{D2354AA'),
 | 
			
		||||
])
 | 
			
		||||
def test_code_check(bctype, data):
 | 
			
		||||
    """should raise an error if the barcode code is invalid.
 | 
			
		||||
    """
 | 
			
		||||
    instance = printer.Dummy()
 | 
			
		||||
    with pytest.raises(BarcodeCodeError):
 | 
			
		||||
        instance.barcode(data, bctype)
 | 
			
		||||
 | 
			
		||||
    assert instance.output == b''
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										104
									
								
								test/test_function_check_barcode.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								test/test_function_check_barcode.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
#!/usr/bin/python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from __future__ import absolute_import
 | 
			
		||||
from __future__ import division
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
import escpos.printer as printer
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize("bctype,data", [
 | 
			
		||||
    ('UPC-A', '01234567890'),
 | 
			
		||||
    ('UPC-A', '012345678905'),
 | 
			
		||||
    ('UPC-E', '01234567'),
 | 
			
		||||
    ('UPC-E', '0123456'),
 | 
			
		||||
    ('UPC-E', '012345678905'),
 | 
			
		||||
    ('EAN13', '0123456789012'),
 | 
			
		||||
    ('EAN13', '012345678901'),
 | 
			
		||||
    ('EAN8', '01234567'),
 | 
			
		||||
    ('EAN8', '0123456'),
 | 
			
		||||
    ('CODE39', 'ABC-1234'),
 | 
			
		||||
    ('CODE39', 'ABC-1234-$$-+A'),
 | 
			
		||||
    ('CODE39', '*WIKIPEDIA*'),
 | 
			
		||||
    ('ITF', '010203040506070809'),
 | 
			
		||||
    ('ITF', '11221133113344556677889900'),
 | 
			
		||||
    ('CODABAR', 'A2030405060B'),
 | 
			
		||||
    ('CODABAR', 'C11221133113344556677889900D'),
 | 
			
		||||
    ('CODABAR', 'D0D'),
 | 
			
		||||
    ('NW7', 'A2030405060B'),
 | 
			
		||||
    ('NW7', 'C11221133113344556677889900D'),
 | 
			
		||||
    ('NW7', 'D0D'),
 | 
			
		||||
    ('CODE93', 'A2030405060B'),
 | 
			
		||||
    ('CODE93', '+:$&23-7@$'),
 | 
			
		||||
    ('CODE93', 'D0D'),
 | 
			
		||||
    ('CODE128', '{A2030405060B'),
 | 
			
		||||
    ('CODE128', '{C+:$&23-7@$'),
 | 
			
		||||
    ('CODE128', '{B0D'),
 | 
			
		||||
    ('GS1-128', '{A2030405060B'),
 | 
			
		||||
    ('GS1-128', '{C+:$&23-7@$'),
 | 
			
		||||
    ('GS1-128', '{B0D'),
 | 
			
		||||
    ('GS1 DATABAR OMNIDIRECTIONAL', '0123456789123'),
 | 
			
		||||
    ('GS1 DATABAR TRUNCATED', '0123456789123'),
 | 
			
		||||
    ('GS1 DATABAR LIMITED', '0123456789123'),
 | 
			
		||||
    ('GS1 DATABAR EXPANDED', '(9A{A20304+-%&06a0B'),
 | 
			
		||||
    ('GS1 DATABAR EXPANDED', '(1 {C+:&23-7%'),
 | 
			
		||||
    ('GS1 DATABAR EXPANDED', '(00000001234567678'),
 | 
			
		||||
])
 | 
			
		||||
def test_check_valid_barcode(bctype, data):
 | 
			
		||||
    assert (printer.Escpos.check_barcode(bctype, data))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize("bctype,data", [
 | 
			
		||||
    ('UPC-A', '01234567890123'),  # too long
 | 
			
		||||
    ('UPC-A', '0123456789'),  # too short
 | 
			
		||||
    ('UPC-A', '72527273-711'),  # invalid '-'
 | 
			
		||||
    ('UPC-A', 'A12345678901'),  # invalid 'A'
 | 
			
		||||
    ('UPC-E', '01234567890123'),  # too long
 | 
			
		||||
    ('UPC-E', '012345'),  # too short
 | 
			
		||||
    ('UPC-E', '72527-2'),  # invalid '-'
 | 
			
		||||
    ('UPC-E', 'A123456'),  # invalid 'A'
 | 
			
		||||
    ('EAN13', '0123456789'),  # too short
 | 
			
		||||
    ('EAN13', 'A123456789012'),  # invalid 'A'
 | 
			
		||||
    ('EAN13', '012345678901234'),  # too long
 | 
			
		||||
    ('EAN8', '012345'),  # too short
 | 
			
		||||
    ('EAN8', 'A123456789012'),  # invalid 'A'
 | 
			
		||||
    ('EAN8', '012345678901234'),  # too long
 | 
			
		||||
    ('CODE39', 'ALKJ_34'),  # invalid '_'
 | 
			
		||||
    ('CODE39', 'A' * 256),  # too long
 | 
			
		||||
    ('ITF', '010203040'),  # odd length
 | 
			
		||||
    ('ITF', '0' * 256),  # too long
 | 
			
		||||
    ('ITF', 'AB01'),  # invalid 'A'
 | 
			
		||||
    ('CODABAR', '010203040'),  # no start/stop
 | 
			
		||||
    ('CODABAR', '0' * 256),  # too long
 | 
			
		||||
    ('CODABAR', 'AB-01F'),  # invalid 'B'
 | 
			
		||||
    ('NW7', '010203040'),  # no start/stop
 | 
			
		||||
    ('NW7', '0' * 256),  # too long
 | 
			
		||||
    ('NW7', 'AB-01F'),  # invalid 'B'
 | 
			
		||||
    ('CODE93', 'é010203040'),  # invalid 'é'
 | 
			
		||||
    ('CODE93', '0' * 256),  # too long
 | 
			
		||||
    ('CODE128', '010203040'),  # missing leading {
 | 
			
		||||
    ('CODE128', '{D2354AA'),  # second char not between A-C
 | 
			
		||||
    ('CODE128', '0' * 256),  # too long
 | 
			
		||||
    ('GS1-128', '010203040'),  # missing leading {
 | 
			
		||||
    ('GS1-128', '{D2354AA'),  # second char not between A-C
 | 
			
		||||
    ('GS1-128', '0' * 256),  # too long
 | 
			
		||||
    ('GS1 DATABAR OMNIDIRECTIONAL', '01234567891234'),  # too long
 | 
			
		||||
    ('GS1 DATABAR OMNIDIRECTIONAL', '012345678912'),  # too short
 | 
			
		||||
    ('GS1 DATABAR OMNIDIRECTIONAL', '012345678A1234'),  # invalid 'A'
 | 
			
		||||
    ('GS1 DATABAR TRUNCATED', '01234567891234'),  # too long
 | 
			
		||||
    ('GS1 DATABAR TRUNCATED', '012345678912'),  # too short
 | 
			
		||||
    ('GS1 DATABAR TRUNCATED', '012345678A1234'),  # invalid 'A'
 | 
			
		||||
    ('GS1 DATABAR LIMITED', '01234567891234'),  # too long
 | 
			
		||||
    ('GS1 DATABAR LIMITED', '012345678912'),  # too short
 | 
			
		||||
    ('GS1 DATABAR LIMITED', '012345678A1234'),  # invalid 'A'
 | 
			
		||||
    ('GS1 DATABAR LIMITED', '02345678912341'),  # invalid start (should be 01)
 | 
			
		||||
    ('GS1 DATABAR EXPANDED', '010203040'),  # missing leading (
 | 
			
		||||
    ('GS1-128', '(' + ('0' * 256)),  # too long
 | 
			
		||||
    ('GS1 DATABAR EXPANDED', '(a{D2354AA'),  # second char not between 0-9
 | 
			
		||||
    ('GS1 DATABAR EXPANDED', 'IT will fail'),  # first char not '('
 | 
			
		||||
])
 | 
			
		||||
def test_check_invalid_barcode(bctype, data):
 | 
			
		||||
    assert (not printer.Escpos.check_barcode(bctype, data))
 | 
			
		||||
@@ -145,10 +145,8 @@ def test_large_graphics():
 | 
			
		||||
    assert(instance.output == b'\x1dv0\x00\x01\x00\x01\x00\xc0\x1dv0\x00\x01\x00\x01\x00\x00')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_width_too_large():
 | 
			
		||||
    """
 | 
			
		||||
    Test printing an image that is too large in width.
 | 
			
		||||
    """
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def dummy_with_width():
 | 
			
		||||
    instance = printer.Dummy()
 | 
			
		||||
    instance.profile.profile_data = {
 | 
			
		||||
        'media': {
 | 
			
		||||
@@ -157,8 +155,25 @@ def test_width_too_large():
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_width_too_large(dummy_with_width):
 | 
			
		||||
    """
 | 
			
		||||
    Test printing an image that is too large in width.
 | 
			
		||||
    """
 | 
			
		||||
    instance = dummy_with_width
 | 
			
		||||
 | 
			
		||||
    with pytest.raises(ImageWidthError):
 | 
			
		||||
        instance.image(Image.new("RGB", (385, 200)))
 | 
			
		||||
 | 
			
		||||
    instance.image(Image.new("RGB", (384, 200)))
 | 
			
		||||
    instance.image(Image.new("RGB", (384, 200)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_center_image(dummy_with_width):
 | 
			
		||||
    instance = dummy_with_width
 | 
			
		||||
 | 
			
		||||
    with pytest.raises(ImageWidthError):
 | 
			
		||||
        instance.image(Image.new("RGB", (385, 200)), center=True)
 | 
			
		||||
 | 
			
		||||
    instance.image(Image.new("RGB", (384, 200)), center=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@ from __future__ import print_function
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
from nose.tools import raises
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
import escpos.printer as printer
 | 
			
		||||
from escpos.constants import QR_ECLEVEL_H, QR_MODEL_1
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +27,6 @@ def test_defaults():
 | 
			
		||||
        b'(k\x07\x001P01234\x1d(k\x03\x001Q0'
 | 
			
		||||
    assert(instance.output == expected)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_empty():
 | 
			
		||||
    """Test QR printing blank code"""
 | 
			
		||||
    instance = printer.Dummy()
 | 
			
		||||
@@ -99,3 +100,13 @@ def test_image_invalid_model():
 | 
			
		||||
    """Test unsupported QR model as image"""
 | 
			
		||||
    instance = printer.Dummy()
 | 
			
		||||
    instance.qr("1234", native=False, model=QR_MODEL_1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def instance():
 | 
			
		||||
    return printer.Dummy()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_center_not_implementer(instance):
 | 
			
		||||
    with pytest.raises(NotImplementedError):
 | 
			
		||||
        instance.qr("test", center=True, native=True)
 | 
			
		||||
@@ -13,6 +13,7 @@ from __future__ import division
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
import mock
 | 
			
		||||
 | 
			
		||||
from escpos.printer import Dummy
 | 
			
		||||
@@ -30,3 +31,12 @@ def test_type_of_object_passed_to_image_function(img_function):
 | 
			
		||||
    d.qr("LoremIpsum")
 | 
			
		||||
    args, kwargs = img_function.call_args
 | 
			
		||||
    assert isinstance(args[0], Image.Image)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def instance():
 | 
			
		||||
    return Dummy()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_center(instance):
 | 
			
		||||
    instance.qr("LoremIpsum", center=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ from __future__ import unicode_literals
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
from hypothesis import given
 | 
			
		||||
from hypothesis import given, settings
 | 
			
		||||
from hypothesis.strategies import text
 | 
			
		||||
 | 
			
		||||
import escpos.printer as printer
 | 
			
		||||
@@ -27,6 +27,7 @@ else:
 | 
			
		||||
    mock_open_call = '__builtin__.open'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@settings(use_coverage=False)
 | 
			
		||||
@given(path=text())
 | 
			
		||||
def test_load_file_printer(mocker, path):
 | 
			
		||||
    """test the loading of the file-printer"""
 | 
			
		||||
@@ -37,6 +38,7 @@ def test_load_file_printer(mocker, path):
 | 
			
		||||
    mock_open.assert_called_with(path, "wb")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@settings(deadline=None, use_coverage=False)
 | 
			
		||||
@given(txt=text())
 | 
			
		||||
def test_auto_flush(mocker, txt):
 | 
			
		||||
    """test auto_flush in file-printer"""
 | 
			
		||||
@@ -57,6 +59,7 @@ def test_auto_flush(mocker, txt):
 | 
			
		||||
    assert mock_device.flush.called
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@settings(deadline=None, use_coverage=False)
 | 
			
		||||
@given(txt=text())
 | 
			
		||||
def test_flush_on_close(mocker, txt):
 | 
			
		||||
    """test flush on close in file-printer"""
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								test/test_raise_arbitrary_error.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								test/test_raise_arbitrary_error.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
#!/usr/bin/python
 | 
			
		||||
"""test the raising of errors with the error module
 | 
			
		||||
 | 
			
		||||
:author: `Patrick Kanzler <patrick.kanzler@fablab.fau.de>`_
 | 
			
		||||
:organization: `python-escpos <https://github.com/python-escpos>`_
 | 
			
		||||
:copyright: Copyright (c) 2017 `python-escpos <https://github.com/python-escpos>`_
 | 
			
		||||
:license: MIT
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from __future__ import absolute_import
 | 
			
		||||
from __future__ import division
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
import escpos
 | 
			
		||||
import escpos.exceptions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_raise_error_wrongly():
 | 
			
		||||
    """raise error the wrong way
 | 
			
		||||
 | 
			
		||||
    should reproduce https://github.com/python-escpos/python-escpos/issues/257
 | 
			
		||||
    """
 | 
			
		||||
    with pytest.raises(AttributeError):
 | 
			
		||||
        raise escpos.Error("This should raise an AttributeError.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def tests_raise_error():
 | 
			
		||||
    """raise error the right way"""
 | 
			
		||||
    with pytest.raises(escpos.exceptions.Error):
 | 
			
		||||
        raise escpos.exceptions.Error("This should raise an error.")
 | 
			
		||||
		Reference in New Issue
	
	Block a user