1
0
mirror of https://github.com/python-escpos/python-escpos synced 2025-09-13 09:09:58 +00:00

24 Commits

Author SHA1 Message Date
Patrick Kanzler
cbe38648f5 Merge pull request #169 from python-escpos/development
v2.2.0
2016-08-26 15:53:50 +02:00
Patrick Kanzler
d43bcd187e Merge branch 'master' into development 2016-08-26 14:46:23 +02:00
Patrick Kanzler
6b069a4529 update CHANGELOG 2016-08-26 14:27:17 +02:00
Patrick Kanzler
cf41069829 Merge branch 'linespacing' of git://github.com/miracle2k/python-escpos into miracle2k-linespacing
Conflicts:
	src/escpos/constants.py
2016-08-26 14:16:06 +02:00
Patrick Kanzler
bde6eaa336 Merge pull request #164 from miracle2k/constants
Constants
2016-08-26 12:25:24 +02:00
Michael Elsdörfer
632a104219 Fix docstring warning. 2016-08-26 11:59:40 +02:00
Michael Elsdörfer
ae9b3785c2 Fix broken tests. 2016-08-26 11:48:58 +02:00
Michael Elsdörfer
07d47765aa Allow linespacing reset. Make this the default. 2016-08-26 10:38:36 +02:00
Michael Elsdörfer
854b75be30 Support changing the line spacing. 2016-08-26 10:34:52 +02:00
Michael Elsdörfer
df0c874f6e Add constants for sheet mode, colors. 2016-08-26 10:30:29 +02:00
Michael Elsdörfer
5c3d7dab72 Change setup.py shebang.
This supports using the current virtualenv.
2016-08-26 10:30:29 +02:00
Patrick Kanzler
798893caee refactor access of private member _img of qrcode
Since version 2.5 python-qrcode allows the direct access of the
PIL-functions. (We require version 4 and above).
Thus, we can simply call qr_img.convert() without accessing the private
member.
This refactoring is identical in functionality.
2016-08-15 23:24:06 +02:00
Patrick Kanzler
e8d91a6735 test add type-check for the qr-printing 2016-08-15 23:23:07 +02:00
Patrick Kanzler
996b3fd332 DOC fix demo-code in README.rst
fixes #159
2016-08-11 11:25:40 +02:00
Patrick Kanzler
a38c124bb1 Merge branch 'master' of github.com:python-escpos/python-escpos 2016-08-10 01:31:56 +02:00
Patrick Kanzler
e44d89bd33 DOC update changelog for v2.1.3 2016-08-10 01:30:19 +02:00
Patrick Kanzler
7312db4adb Merge pull request #158 from python-escpos/development
v2.1.3
2016-08-10 01:23:47 +02:00
Patrick Kanzler
59dccd79da test add test for image-splitting-method 2016-08-07 14:39:58 +02:00
Patrick Kanzler
603b34cadb test add test for the fragment-splitting 2016-08-07 13:49:46 +02:00
Patrick Kanzler
340a47d2f6 Merge pull request #152 from python-escpos/fix/large-image-printing
Fix/large image printing
2016-08-07 12:22:25 +02:00
Patrick Kanzler
dfe2cdbff8 configure readthedocs with yml 2016-08-02 18:39:56 +02:00
Patrick Kanzler
eea2a6f9c0 travis: configure email-notifications 2016-08-02 16:41:47 +02:00
Patrick Kanzler
2ecf73074c improve large image printing
images longer than 1024 pixels will be split into multiple fragments.
2016-08-02 00:04:43 +02:00
Patrick Kanzler
10977b06e7 doc add hint on image preprocessing 2016-08-01 14:02:49 +02:00
13 changed files with 247 additions and 9 deletions

View File

@@ -34,3 +34,7 @@ before_install:
script: script:
- tox - tox
- codecov - codecov
notifications:
email:
on_success: never
on_failure: change

View File

@@ -1,6 +1,34 @@
********* *********
Changelog Changelog
********* *********
2016-08-26 - Version 2.2.0 - "Fate Amenable To Change"
------------------------------------------------------
changes
^^^^^^^
- fix improper API-use in qrcode()
- change setup.py shebang to make it compatible with virtualenvs.
- add constants for sheet mode and colors
- support changing the linespacing
contributors
^^^^^^^^^^^^
- Michael Elsdörfer
- Patrick Kanzler
2016-08-10 - Version 2.1.3 - "Ethics Gradient"
----------------------------------------------
changes
^^^^^^^
- configure readthedocs and travis
- update doc with hint on image preprocessing
- add fix for printing large images (by splitting them into multiple images)
contributors
^^^^^^^^^^^^
- Patrick Kanzler
2016-08-02 - Version 2.1.2 - "Death and Gravity" 2016-08-02 - Version 2.1.2 - "Death and Gravity"
------------------------------------------------ ------------------------------------------------

View File

@@ -54,14 +54,14 @@ The basic usage is:
.. code:: python .. code:: python
from escpos import * from escpos.printer import Usb
""" Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """ """ Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """
Epson = escpos.Escpos(0x04b8,0x0202,0) p = Usb(0x04b8,0x0202,0)
Epson.text("Hello World\n") p.text("Hello World\n")
Epson.image("logo.gif") p.image("logo.gif")
Epson.barcode('1324354657687','EAN13',64,2,'','') p.barcode('1324354657687','EAN13',64,2,'','')
Epson.cut() p.cut()
The full project-documentation is available on `Read the Docs <https://python-escpos.readthedocs.io>`_. The full project-documentation is available on `Read the Docs <https://python-escpos.readthedocs.io>`_.

View File

@@ -204,6 +204,37 @@ Here you can download an example, that will print a set of common barcodes:
* :download:`barcode.bin </download/barcode.bin>` by `@mike42 <https://github.com/mike42>`_ * :download:`barcode.bin </download/barcode.bin>` by `@mike42 <https://github.com/mike42>`_
Hint: preprocess printing
-------------------------
Printing images directly to the printer is rather slow.
One factor that slows down the process is the transmission over e.g. serial port.
Apart from configuring your printer to use the maximum baudrate (in the case of serial-printers), there is not much
that you can do.
However you could use the :py:class:`escpos.printer.Dummy`-printer to preprocess your image.
This is probably best explained by an example:
.. code-block:: Python
from escpos.printer import Serial, Dummy
p = Serial()
d = Dummy()
# create ESC/POS for the print job, this should go really fast
d.text("This is my image:\n")
d.image("funny_cat.png")
d.cut()
# send code to printer
p._raw(d.output)
This way you could also store the code in a file and print later.
You could then for example print the code from another process than your main-program and thus reduce the waiting time.
(Of course this will not make the printer print faster.)
How to update your code for USB printers How to update your code for USB printers
---------------------------------------- ----------------------------------------

7
readthedocs.yml Normal file
View File

@@ -0,0 +1,7 @@
formats:
- pdf
- epub
requirements_file: doc/requirements.txt
python:
version: 2
setup_py_install: true

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python
import os import os
import sys import sys

View File

@@ -55,11 +55,18 @@ _CUT_PAPER = lambda m: GS + b'V' + m
PAPER_FULL_CUT = _CUT_PAPER(b'\x00') # Full cut paper PAPER_FULL_CUT = _CUT_PAPER(b'\x00') # Full cut paper
PAPER_PART_CUT = _CUT_PAPER(b'\x01') # Partial cut paper PAPER_PART_CUT = _CUT_PAPER(b'\x01') # Partial cut paper
# Beep
BEEP = b'\x07'
# Panel buttons (e.g. the FEED button) # Panel buttons (e.g. the FEED button)
_PANEL_BUTTON = lambda n: ESC + b'c5' + six.int2byte(n) _PANEL_BUTTON = lambda n: ESC + b'c5' + six.int2byte(n)
PANEL_BUTTON_ON = _PANEL_BUTTON(0) # enable all panel buttons PANEL_BUTTON_ON = _PANEL_BUTTON(0) # enable all panel buttons
PANEL_BUTTON_OFF = _PANEL_BUTTON(1) # disable all panel buttons PANEL_BUTTON_OFF = _PANEL_BUTTON(1) # disable all panel buttons
# Sheet modes
SHEET_SLIP_MODE = ESC + b'\x63\x30\x04' # slip paper
SHEET_ROLL_MODE = ESC + b'\x63\x30\x01' # paper roll
# Text format # Text format
# TODO: Acquire the "ESC/POS Application Programming Guide for Paper Roll # TODO: Acquire the "ESC/POS Application Programming Guide for Paper Roll
# Printers" and tidy up this stuff too. # Printers" and tidy up this stuff too.
@@ -101,6 +108,18 @@ TXT_ALIGN_RT = ESC + b'\x61\x02' # Right justification
TXT_INVERT_ON = GS + b'\x42\x01' # Inverse Printing ON TXT_INVERT_ON = GS + b'\x42\x01' # Inverse Printing ON
TXT_INVERT_OFF = GS + b'\x42\x00' # Inverse Printing OFF TXT_INVERT_OFF = GS + b'\x42\x00' # Inverse Printing OFF
# Text colors
TXT_COLOR_BLACK = ESC + b'\x72\x00' # Default Color
TXT_COLOR_RED = ESC + b'\x72\x01' # Alternative Color (Usually Red)
# Spacing
LINESPACING_RESET = ESC + b'2'
LINESPACING_FUNCS = {
60: ESC + b'A', # line_spacing/60 of an inch, 0 <= line_spacing <= 85
360: ESC + b'+', # line_spacing/360 of an inch, 0 <= line_spacing <= 255
180: ESC + b'3', # line_spacing/180 of an inch, 0 <= line_spacing <= 255
}
# Char code table # Char code table
CHARCODE_PC437 = ESC + b'\x74\x00' # USA: Standard Europe CHARCODE_PC437 = ESC + b'\x74\x00' # USA: Standard Europe
CHARCODE_JIS = ESC + b'\x74\x01' # Japanese Katakana CHARCODE_JIS = ESC + b'\x74\x01' # Japanese Katakana
@@ -186,7 +205,7 @@ QR_ECLEVEL_L = 0
QR_ECLEVEL_M = 1 QR_ECLEVEL_M = 1
QR_ECLEVEL_Q = 2 QR_ECLEVEL_Q = 2
QR_ECLEVEL_H = 3 QR_ECLEVEL_H = 3
# QRcode models # QRcode models
QR_MODEL_1 = 1 QR_MODEL_1 = 1
QR_MODEL_2 = 2 QR_MODEL_2 = 2

View File

@@ -56,7 +56,8 @@ class Escpos(object):
""" """
pass pass
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=1024):
""" 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.
@@ -76,9 +77,20 @@ class Escpos(object):
:param high_density_vertical: print in high density in vertical direction *default:* True :param high_density_vertical: print in high density in vertical direction *default:* True
: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:* 1024
""" """
im = EscposImage(img_source) im = EscposImage(img_source)
if im.height > fragment_height:
fragments = im.split(fragment_height)
for fragment in fragments:
self.image(fragment,
high_density_vertical=high_density_vertical,
high_density_horizontal=high_density_horizontal,
impl=impl,
fragment_height=fragment_height)
return
if impl == "bitImageRaster": if impl == "bitImageRaster":
# GS v 0, raster format bit image # GS v 0, raster format bit image
@@ -553,6 +565,35 @@ class Escpos(object):
else: else:
self._raw(TXT_INVERT_OFF) self._raw(TXT_INVERT_OFF)
def line_spacing(self, spacing=None, divisor=180):
""" Set line character spacing.
If no spacing is given, we reset it to the default.
There are different commands for setting the line spacing, using
a different denominator:
'+'' line_spacing/360 of an inch, 0 <= line_spacing <= 255
'3' line_spacing/180 of an inch, 0 <= line_spacing <= 255
'A' line_spacing/60 of an inch, 0 <= line_spacing <= 85
Some printers may not support all of them. The most commonly
available command (using a divisor of 180) is chosen.
"""
if spacing is None:
self._raw(LINESPACING_RESET)
return
if divisor not in LINESPACING_FUNCS:
raise ValueError("divisor must be either 360, 180 or 60")
if (divisor in [360, 180] \
and (not(0 <= spacing <= 255))):
raise ValueError("spacing must be a int between 0 and 255 when divisor is 360 or 180")
if divisor == 60 and (not(0 <= spacing <= 85)):
raise ValueError("spacing must be a int between 0 and 85 when divisor is 60")
self._raw(LINESPACING_FUNCS[divisor] + six.int2byte(spacing))
def cut(self, mode=''): def cut(self, mode=''):
""" Cut paper. """ Cut paper.

View File

@@ -8,6 +8,12 @@ This module contains the image format handler :py:class:`EscposImage`.
:license: GNU GPL v3 :license: GNU GPL v3
""" """
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import math
from PIL import Image, ImageOps from PIL import Image, ImageOps
@@ -30,6 +36,9 @@ class EscposImage(object):
else: else:
img_original = Image.open(img_source) img_original = Image.open(img_source)
# store image for eventual further processing (splitting)
self.img_original = img_original
# Convert to white RGB background, paste over white background # Convert to white RGB background, paste over white background
# to strip alpha. # to strip alpha.
img_original = img_original.convert('RGBA') img_original = img_original.convert('RGBA')
@@ -88,3 +97,21 @@ class EscposImage(object):
Convert image to raster-format binary Convert image to raster-format binary
""" """
return self._im.tobytes() return self._im.tobytes()
def split(self, fragment_height):
"""
Split an image into multiple fragments after fragment_height pixels
:param fragment_height: height of fragment
:return: list of PIL objects
"""
passes = int(math.ceil(self.height/fragment_height))
fragments = []
for n in range(0, passes):
left = 0
right = self.width
upper = n * fragment_height
lower = min((n + 1) * fragment_height, self.height)
box = (left, upper, right, lower)
fragments.append(self.img_original.crop(box))
return fragments

View File

@@ -130,3 +130,12 @@ def test_graphics_transparent():
instance = printer.Dummy() instance = printer.Dummy()
instance.image('test/resources/black_transparent.png', impl="graphics") instance.image('test/resources/black_transparent.png', impl="graphics")
assert(instance.output == b'\x1d(L\x0c\x000p0\x01\x011\x02\x00\x02\x00\xc0\x00\x1d(L\x02\x0002') assert(instance.output == b'\x1d(L\x0c\x000p0\x01\x011\x02\x00\x02\x00\xc0\x00\x1d(L\x02\x0002')
def test_large_graphics():
"""
Test whether 'large' graphics that induce a fragmentation are handled correctly.
"""
instance = printer.Dummy()
instance.image('test/resources/black_white.png', impl="bitImageRaster", fragment_height=1)
assert(instance.output == b'\x1dv0\x00\x01\x00\x01\x00\xc0\x1dv0\x00\x01\x00\x01\x00\x00')

View File

@@ -0,0 +1,32 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""tests for the non-native part of qr()
:author: `Patrick Kanzler <patrick.kanzler@fablab.fau.de>`_
:organization: `python-escpos <https://github.com/python-escpos>`_
:copyright: Copyright (c) 2016 `python-escpos <https://github.com/python-escpos>`_
:license: GNU GPL v3
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import mock
from escpos.printer import Dummy
from PIL import Image
@mock.patch('escpos.printer.Dummy.image', spec=Dummy)
def test_type_of_object_passed_to_image_function(img_function):
"""
Test the type of object that is passed to the image function during non-native qr-printing.
The type should be PIL.Image
"""
d = Dummy()
d.qr("LoremIpsum")
args, kwargs = img_function.call_args
assert isinstance(args[0], Image.Image)

26
test/test_functions.py Normal file
View File

@@ -0,0 +1,26 @@
from nose.tools import assert_raises
from escpos.printer import Dummy
def test_line_spacing_code_gen():
printer = Dummy()
printer.line_spacing(10)
assert printer.output == b'\x1b3\n'
def test_line_spacing_rest():
printer = Dummy()
printer.line_spacing()
assert printer.output == b'\x1b2'
def test_line_spacing_error_handling():
printer = Dummy()
with assert_raises(ValueError):
printer.line_spacing(99, divisor=44)
with assert_raises(ValueError):
printer.line_spacing(divisor=80, spacing=86)
with assert_raises(ValueError):
printer.line_spacing(divisor=360, spacing=256)
with assert_raises(ValueError):
printer.line_spacing(divisor=180, spacing=256)

View File

@@ -43,6 +43,20 @@ def test_image_white():
_load_and_check_img('canvas_white.' + img_format, 1, 1, b'\x00', [b'\x00']) _load_and_check_img('canvas_white.' + img_format, 1, 1, b'\x00', [b'\x00'])
def test_split():
"""
test whether the split-function works as expected
"""
im = EscposImage('test/resources/black_white.png')
(upper_part, lower_part) = im.split(1)
upper_part = EscposImage(upper_part)
lower_part = EscposImage(lower_part)
assert(upper_part.width == lower_part.width == 2)
assert(upper_part.height == lower_part.height == 1)
assert(upper_part.to_raster_format() == b'\xc0')
assert(lower_part.to_raster_format() == b'\x00')
def _load_and_check_img(filename, width_expected, height_expected, raster_format_expected, column_format_expected): def _load_and_check_img(filename, width_expected, height_expected, raster_format_expected, column_format_expected):
""" """
Load an image, and test whether raster & column formatted output, sizes, etc match expectations. Load an image, and test whether raster & column formatted output, sizes, etc match expectations.