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

@ -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>

View File

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

View File

@ -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()

View File

@ -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)

View File

@ -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)
logger.debug('Finished loading capabilities took %.2fs', time.time() - t0)
PROFILES = CAPABILITIES['profiles']

View File

@ -88,7 +88,7 @@ class Escpos(object):
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.
@ -109,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
@ -174,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.
@ -186,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]:
@ -212,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.

View File

@ -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

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')
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)

View File

@ -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)

View File

@ -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)