Skip to content

Commit

Permalink
Merge pull request python-escpos#266 from python-escpos/development
Browse files Browse the repository at this point in the history
release v3.0a3
  • Loading branch information
patkan authored Oct 8, 2017
2 parents af29fcc + 10e1dfe commit d1e7052
Show file tree
Hide file tree
Showing 23 changed files with 378 additions and 42 deletions.
3 changes: 2 additions & 1 deletion .mailmap
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ Cody (Quantified Code Bot) <[email protected]> Cody <[email protected]
Renato Lorenzi <[email protected]> Renato.Lorenzi <[email protected]>
Ahmed Tahri <[email protected]> TAHRI Ahmed <[email protected]>
Michael Elsdörfer <[email protected]> Michael Elsdörfer <[email protected]>
csoft2k <[email protected]>
csoft2k <[email protected]>
Sergio Pulgarin <[email protected]>
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Hark
Joel Lehtonen
Kristi
ldos
Lucy Linder
Manuel F Martinez
Michael Billington
Michael Elsdörfer
Expand All @@ -22,6 +23,7 @@ Qian Linfeng
Renato Lorenzi
Romain Porte
Sam Cheng
Sergio Pulgarin
Stephan Sokolow
Thijs Triemstra
Thomas van den Berg
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -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"
-----------------------------------------------------------------------
Expand Down
9 changes: 6 additions & 3 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-----------
Expand Down Expand Up @@ -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
^^^
Expand Down
11 changes: 10 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.


2 changes: 1 addition & 1 deletion doc/generate_authors.sh
Original file line number Diff line number Diff line change
@@ -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"

Expand Down
4 changes: 2 additions & 2 deletions examples/codepage_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
2 changes: 1 addition & 1 deletion examples/qr_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ def usage():

# Adapt to your needs
p = Usb(0x0416, 0x5011, profile="POS-5890")
p.qr(content)
p.qr(content, center=True)
2 changes: 1 addition & 1 deletion examples/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,5 @@ def icon():
printer.text('Forecast: \n')
forecast(0)
forecast(1)
printer.cut
printer.cut()
printer.control("LF")
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ def run_tests(self):
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',
Expand Down Expand Up @@ -114,7 +113,7 @@ def run_tests(self):
'pyserial',
'six',
'appdirs',
'pyyaml',
'PyYAML',
'argparse',
'argcomplete',
'future',
Expand Down
43 changes: 37 additions & 6 deletions src/escpos/capabilities.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,47 @@
import re
import six
from os import environ, path
import pickle
import logging
import time

import six
import yaml


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
if 'ESCPOS_CAPABILITIES_FILE' in environ:
file_path = environ['ESCPOS_CAPABILITIES_FILE']
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:
file_path = path.join(path.dirname(__file__), 'capabilities.json')
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)

with open(file_path) as f:
CAPABILITIES = yaml.load(f)

PROFILES = CAPABILITIES['profiles']

Expand Down
18 changes: 18 additions & 0 deletions src/escpos/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,24 @@
'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,
Expand Down
75 changes: 62 additions & 13 deletions src/escpos/escpos.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@
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
Expand Down Expand Up @@ -80,14 +81,14 @@ def _raw(self, msg):
"""
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.
Expand All @@ -108,14 +109,19 @@ def image(self, img_source, high_density_vertical=True, high_density_horizontal=
: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
Expand Down Expand Up @@ -173,7 +179,8 @@ def _image_send_graphics_data(self, m, fn, data):
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.
Expand All @@ -185,6 +192,7 @@ def qr(self, content, ec=QR_ECLEVEL_L, size=3, model=QR_MODEL_2, native=False):
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]:
Expand All @@ -211,12 +219,17 @@ def qr(self, content, ec=QR_ECLEVEL_L, size=3, model=QR_MODEL_2, native=False):
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.
Expand Down Expand Up @@ -275,17 +288,42 @@ def charcode(self, code="AUTO"):
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
Expand Down Expand Up @@ -353,6 +391,10 @@ def barcode(self, code, bc, height=64, width=3, pos="BELOW", font="A",
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`
Expand All @@ -375,12 +417,19 @@ def barcode(self, code, bc, height=64, width=3, pos="BELOW", font="A",
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'])
Expand Down
Loading

0 comments on commit d1e7052

Please sign in to comment.