Merge branch 'development' into feature/check-barcodes

This commit is contained in:
Lucy Linder 2017-08-31 12:21:00 +02:00 committed by GitHub
commit 2d9a4c9655
10 changed files with 118 additions and 21 deletions

View File

@ -9,3 +9,4 @@ Renato Lorenzi <renato.lorenzi@senior.com.br> Renato.Lorenzi <renato.lore
Ahmed Tahri <nyuubi.10@gmail.com> TAHRI Ahmed <nyuubi.10@gmail.com> Ahmed Tahri <nyuubi.10@gmail.com> TAHRI Ahmed <nyuubi.10@gmail.com>
Michael Elsdörfer <michael@elsdoerfer.com> Michael Elsdörfer <michael@elsdoerfer.info> 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>

View File

@ -19,6 +19,7 @@ Qian Linfeng
Renato Lorenzi Renato Lorenzi
Romain Porte Romain Porte
Sam Cheng Sam Cheng
Sergio Pulgarin
Stephan Sokolow Stephan Sokolow
Thijs Triemstra Thijs Triemstra
Thomas van den Berg Thomas van den Berg

View File

@ -37,14 +37,14 @@ def print_codepage(printer, codepage):
sep = "" sep = ""
# Table header # 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._raw(" {}\n".format(sep.join(map(lambda s: hex(s)[2:], range(0, 16)))))
printer.set() printer.set()
# The table # The table
for x in range(0, 16): for x in range(0, 16):
# First column # First column
printer.set(text_type='B') printer.set(font='b')
printer._raw("{} ".format(hex(x)[2:])) printer._raw("{} ".format(hex(x)[2:]))
printer.set() printer.set()

View File

@ -16,4 +16,4 @@ if __name__ == '__main__':
# Adapt to your needs # Adapt to your needs
p = Usb(0x0416, 0x5011, profile="POS-5890") p = Usb(0x0416, 0x5011, profile="POS-5890")
p.qr(content) p.qr(content, center=True)

View File

@ -1,16 +1,47 @@
import re import re
import six
from os import environ, path from os import environ, path
import pickle
import logging
import time
import six
import yaml 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: logging.basicConfig()
CAPABILITIES = yaml.load(f) 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)
logger.debug('Finished loading capabilities took %.2fs', time.time() - t0)
PROFILES = CAPABILITIES['profiles'] PROFILES = CAPABILITIES['profiles']

View File

@ -88,7 +88,7 @@ class Escpos(object):
raise NotImplementedError() raise NotImplementedError()
def image(self, img_source, high_density_vertical=True, high_density_horizontal=True, impl="bitImageRaster", 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 """ Print an image
You can select whether the printer should print in high density or not. The default value is high density. You can select whether the printer should print in high density or not. The default value is high density.
@ -109,14 +109,19 @@ class Escpos(object):
:param high_density_horizontal: print in high density in horizontal direction *default:* True :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 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 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) im = EscposImage(img_source)
try: try:
max_width = int(self.profile.profile_data['media']['width']['pixels']) max_width = int(self.profile.profile_data['media']['width']['pixels'])
if im.width > max_width: if im.width > max_width:
raise ImageWidthError('{} > {}'.format(im.width, max_width)) raise ImageWidthError('{} > {}'.format(im.width, max_width))
if center:
im.center(max_width)
except KeyError: except KeyError:
# If the printer's pixel width is not known, print anyways... # If the printer's pixel width is not known, print anyways...
pass pass
@ -174,7 +179,8 @@ class Escpos(object):
header = self._int_low_high(len(data) + 2, 2) header = self._int_low_high(len(data) + 2, 2)
self._raw(GS + b'(L' + header + m + fn + data) 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 """ Print QR Code for the provided string
:param content: The content of the code. Numeric data will be more efficiently compacted. :param content: The content of the code. Numeric data will be more efficiently compacted.
@ -186,6 +192,7 @@ class Escpos(object):
by all printers). 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 :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) printer (Default)
:param center: Centers the code *default:* False
""" """
# Basic validation # Basic validation
if ec not in [QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q]: if ec not in [QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q]:
@ -212,12 +219,17 @@ class Escpos(object):
qr_code.make(fit=True) qr_code.make(fit=True)
qr_img = qr_code.make_image() qr_img = qr_code.make_image()
im = qr_img._img.convert("RGB") im = qr_img._img.convert("RGB")
# Convert the RGB image in printable image # Convert the RGB image in printable image
self.text('\n') self.text('\n')
self.image(im) self.image(im, center=center)
self.text('\n') self.text('\n')
self.text('\n') self.text('\n')
return return
if center:
raise NotImplementedError("Centering not implemented for native QR rendering")
# Native 2D code printing # Native 2D code printing
cn = b'1' # Code type for QR code cn = b'1' # Code type for QR code
# Select model: 1, 2 or micro. # Select model: 1, 2 or micro.

View File

@ -115,3 +115,19 @@ class EscposImage(object):
box = (left, upper, right, lower) box = (left, upper, right, lower)
fragments.append(self.img_original.crop(box)) fragments.append(self.img_original.crop(box))
return fragments 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

View File

@ -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') assert(instance.output == b'\x1dv0\x00\x01\x00\x01\x00\xc0\x1dv0\x00\x01\x00\x01\x00\x00')
def test_width_too_large(): @pytest.fixture
""" def dummy_with_width():
Test printing an image that is too large in width.
"""
instance = printer.Dummy() instance = printer.Dummy()
instance.profile.profile_data = { instance.profile.profile_data = {
'media': { '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): with pytest.raises(ImageWidthError):
instance.image(Image.new("RGB", (385, 200))) 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)

View File

@ -13,6 +13,8 @@ from __future__ import print_function
from __future__ import unicode_literals from __future__ import unicode_literals
from nose.tools import raises from nose.tools import raises
import pytest
import escpos.printer as printer import escpos.printer as printer
from escpos.constants import QR_ECLEVEL_H, QR_MODEL_1 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' b'(k\x07\x001P01234\x1d(k\x03\x001Q0'
assert(instance.output == expected) assert(instance.output == expected)
def test_empty(): def test_empty():
"""Test QR printing blank code""" """Test QR printing blank code"""
instance = printer.Dummy() instance = printer.Dummy()
@ -99,3 +100,13 @@ def test_image_invalid_model():
"""Test unsupported QR model as image""" """Test unsupported QR model as image"""
instance = printer.Dummy() instance = printer.Dummy()
instance.qr("1234", native=False, model=QR_MODEL_1) 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)

View File

@ -13,6 +13,7 @@ from __future__ import division
from __future__ import print_function from __future__ import print_function
from __future__ import unicode_literals from __future__ import unicode_literals
import pytest
import mock import mock
from escpos.printer import Dummy from escpos.printer import Dummy
@ -30,3 +31,12 @@ def test_type_of_object_passed_to_image_function(img_function):
d.qr("LoremIpsum") d.qr("LoremIpsum")
args, kwargs = img_function.call_args args, kwargs = img_function.call_args
assert isinstance(args[0], Image.Image) assert isinstance(args[0], Image.Image)
@pytest.fixture
def instance():
return Dummy()
def test_center(instance):
instance.qr("LoremIpsum", center=True)