First attempt at centering images and QRs (#250)

This was tested on ZJ-5890 with success. By default centering is
deactivated for backward compatibility. Trying to center a QR code in
native mode will raise an exception as we do not know ATM if the native
rendering is centered by default or not.

* Added basic tests for center feature

* Check image size before centering
This commit is contained in:
Romain Porte 2017-08-31 09:25:35 +02:00 committed by Patrick Kanzler
parent 50c627fbb0
commit b648cfd67f
6 changed files with 74 additions and 10 deletions

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

@ -87,7 +87,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.
@ -108,14 +108,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 +178,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 +191,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 +218,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)