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

41 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
11452034a3 Merge pull request #157 from python-escpos/development
v2.1.2
2016-08-02 16:32:19 +02:00
Patrick Kanzler
d2e2ea88a6 doc write changelog for version v2.1.2 2016-08-02 16:07:05 +02:00
Patrick Kanzler
2416303805 Merge pull request #156 from python-escpos/fix/code128-printing
fix printing of CODE128
2016-08-02 15:57:52 +02:00
Patrick Kanzler
38f9835931 fix printing of CODE128
The control sequence {A or {B or {C can't be part of the qr code.
For this the user has to supply this sequence.
2016-08-02 15:39:31 +02:00
Patrick Kanzler
7c732ee615 doc fix lists 2016-08-02 04:38:04 +02:00
Patrick Kanzler
3d98eb8b9c fix file-printer did not flush
The file-printer did not automatically flush and thus behaved
differently to the other printer-classes.
Now the default behaviour is to flush after every call of _raw(). This
can be disabled by calling the file-printer with auto_flush=False.

fixes #106
2016-08-02 04:25:54 +02:00
Patrick Kanzler
619d80a867 doc add changelog-stump for post-2.1.1 release 2016-08-02 00:45:36 +02:00
Patrick Kanzler
d3f74ced5d doc update changelog for release v2.1.1 2016-08-02 00:41:10 +02:00
Patrick Kanzler
0524b0576e Merge pull request #154 from python-escpos/development
release v2.1.1
2016-08-02 00:39:06 +02:00
Patrick Kanzler
c92d4463ae update changelog 2016-08-02 00:27:08 +02:00
Patrick Kanzler
6e74748773 Merge pull request #153 from python-escpos/improve/add-hypothesis-support
Improve/add hypothesis support
2016-08-02 00:25:00 +02:00
Patrick Kanzler
a2e188cecf move pypy3 to the allowed failures and add hypothesis
pypy3 is not compatible with hypothesis, which is needed for testing
2016-08-02 00:10:39 +02:00
Patrick Kanzler
c1a6da9aaa configure codecov.io
For $reasons codecov.io stopped working. Now with my account explicitly
set as "bot" it works again.
2016-08-02 00:10:39 +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
37baf5cd34 update changelog 2016-08-01 23:59:46 +02:00
Patrick Kanzler
5c209dd557 Merge pull request #151 from Renato-Lorenzi/improve/rename-interface-to-timeout
Change the interface param to timeout
2016-08-01 23:54:35 +02:00
Renato.Lorenzi
bef1a9cccf Change the interface param to timeout 2016-08-01 09:36:48 -03:00
Patrick Kanzler
10977b06e7 doc add hint on image preprocessing 2016-08-01 14:02:49 +02:00
Patrick Kanzler
042f945a09 doc added stump for next release 2016-07-23 14:57:24 +02:00
22 changed files with 411 additions and 20 deletions

1
.gitignore vendored
View File

@@ -18,6 +18,7 @@ build/
dist/ dist/
.coverage .coverage
src/escpos/version.py src/escpos/version.py
.hypothesis
# testing temporary directories # testing temporary directories
test/test-cli-output/ test/test-cli-output/

View File

@@ -28,8 +28,13 @@ matrix:
allow_failures: allow_failures:
- python: 3.5-dev - python: 3.5-dev
- python: nightly - python: nightly
- python: pypy3
before_install: before_install:
- pip install tox codecov - pip install tox codecov
script: script:
- tox - tox
- codecov - codecov
notifications:
email:
on_success: never
on_failure: change

View File

@@ -1,6 +1,59 @@
********* *********
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"
------------------------------------------------
changes
^^^^^^^
- fix File-printer: flush after every call of _raw()
- fix lists in documentation
- fix CODE128: by adding the control character to the barcode-selection-sequence the barcode became unusable
contributors
^^^^^^^^^^^^
- Patrick Kanzler
2016-08-02 - Version 2.1.1 - "Contents May Differ"
--------------------------------------------------
changes
^^^^^^^
- rename variable interface in USB-class to timeout
- add support for hypothesis and move pypy3 to the allowed failures (pypy3 is not supported by hypothesis)
contributors
^^^^^^^^^^^^
- Patrick Kanzler
- Renato Lorenzi
2016-07-23 - Version 2.1.0 - "But Who's Counting?" 2016-07-23 - Version 2.1.0 - "But Who's Counting?"
-------------------------------------------------- --------------------------------------------------

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

@@ -1,4 +1,16 @@
codecov:
bot: patkan
coverage: coverage:
status:
project:
default: # status context
target: auto
threshold: "1%"
patch:
default:
target: auto
threshold: "1%"
range: "60...100" range: "60...100"
comment: off comment: off

View File

@@ -18,6 +18,7 @@ Content
user/raspi user/raspi
user/todo user/todo
user/usage user/usage
user/barcode
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1

34
doc/user/barcode.rst Normal file
View File

@@ -0,0 +1,34 @@
Printing Barcodes
-----------------
:Last Reviewed: 2016-07-31
Most ESC/POS-printers implement barcode-printing.
The barcode-commandset is implemented in the barcode-method.
For a list of compatible barcodes you should check the manual of your printer.
As a rule of thumb: even older Epson-models support most 1D-barcodes.
To be sure just try some implementations and have a look at the notices below.
barcode-method
~~~~~~~~~~~~~~
The barcode-method is rather low-level and orients itself on the implementation of ESC/POS.
In the future this class could be supplemented by a high-level class that helps the user generating the payload.
.. py:currentmodule:: escpos.escpos
.. automethod:: Escpos.barcode
:noindex:
CODE128
~~~~~~~
Code128 barcodes need a certain format.
For now the user has to make sure that the payload is correct.
For alphanumeric CODE128 you have to preface your payload with `{B`.
.. code-block:: Python
from escpos.printer import Dummy, Serial
p = Serial()
# print CODE128 012ABCDabcd
p.barcode("{B012ABCDabcd", "CODE128", function_type="B")
A very good description on CODE128 is also on `Wikipedia <https://en.wikipedia.org/wiki/Code_128>`_.

View File

@@ -88,6 +88,7 @@ set("align", "font", "type", width, height, invert, smooth, flip)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Set text properties. Set text properties.
* ``align`` set horizontal position for text, the possible values are: * ``align`` set horizontal position for text, the possible values are:
* CENTER * CENTER
@@ -106,6 +107,7 @@ cut("mode")
^^^^^^^^^^^ ^^^^^^^^^^^
Cut paper. Cut paper.
* ``mode`` set a full or partial cut. *Default:* full * ``mode`` set a full or partial cut. *Default:* full
**Partial cut is not implemented in all printers.** **Partial cut is not implemented in all printers.**
@@ -132,6 +134,7 @@ control("align")
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
Carrier feed and tabs. Carrier feed and tabs.
* ``align`` is a string which takes any of the following values: * ``align`` is a string which takes any of the following values:
* LF *for Line Feed* * LF *for Line Feed*

View File

@@ -32,6 +32,7 @@ Network("host", port)
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
Based on socket Based on socket
* ``host`` is an alphanumeric host name, could be either DNS host name or IP address. * ``host`` is an alphanumeric host name, could be either DNS host name or IP address.
* ``port`` to write to (default = 9100) * ``port`` to write to (default = 9100)
@@ -40,7 +41,8 @@ Problems with a network-attached printer can have numerous causes. Make sure tha
Often you can check the IP address by triggering the self-test of the device. As a next step try to send text Often you can check the IP address by triggering the self-test of the device. As a next step try to send text
manually to the device. You could use for example: manually to the device. You could use for example:
:: .. ::
echo "OK\n" | nc IPADDRESS 9100 echo "OK\n" | nc IPADDRESS 9100
# the port number is often 9100 # the port number is often 9100
@@ -50,4 +52,5 @@ File("file\_name")
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
Printcap printers Printcap printers
* ``file_name`` is the full path to the device file name * ``file_name`` is the full path to the device file name

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
@@ -116,7 +116,7 @@ setup(
setup_requires=[ setup_requires=[
'setuptools_scm', 'setuptools_scm',
], ],
tests_require=['tox', 'nose', 'scripttest'], tests_require=['tox', 'nose', 'scripttest', 'mock', 'hypothesis'],
cmdclass={'test': Tox}, cmdclass={'test': Tox},
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [

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
@@ -168,10 +187,7 @@ BARCODE_TYPE_B = {
'NW7': _SET_BARCODE_TYPE(71), 'NW7': _SET_BARCODE_TYPE(71),
'CODABAR': _SET_BARCODE_TYPE(71), # Same as NW7 'CODABAR': _SET_BARCODE_TYPE(71), # Same as NW7
'CODE93': _SET_BARCODE_TYPE(72), 'CODE93': _SET_BARCODE_TYPE(72),
# These are all the same barcode, but using different charcter sets 'CODE128': _SET_BARCODE_TYPE(73),
'CODE128A': _SET_BARCODE_TYPE(73) + b'{A', # CODE128 character set A
'CODE128B': _SET_BARCODE_TYPE(73) + b'{B', # CODE128 character set B
'CODE128C': _SET_BARCODE_TYPE(73) + b'{C', # CODE128 character set C
'GS1-128': _SET_BARCODE_TYPE(74), 'GS1-128': _SET_BARCODE_TYPE(74),
'GS1 DATABAR OMNIDIRECTIONAL': _SET_BARCODE_TYPE(75), 'GS1 DATABAR OMNIDIRECTIONAL': _SET_BARCODE_TYPE(75),
'GS1 DATABAR TRUNCATED': _SET_BARCODE_TYPE(76), 'GS1 DATABAR TRUNCATED': _SET_BARCODE_TYPE(76),

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,10 +77,21 @@ 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
density_byte = (0 if high_density_horizontal else 1) + (0 if high_density_vertical else 2) density_byte = (0 if high_density_horizontal else 1) + (0 if high_density_vertical else 2)
@@ -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

@@ -34,18 +34,18 @@ class Usb(Escpos):
""" """
def __init__(self, idVendor, idProduct, interface=0, in_ep=0x82, out_ep=0x01, *args, **kwargs): def __init__(self, idVendor, idProduct, timeout=0, in_ep=0x82, out_ep=0x01, *args, **kwargs):
""" """
:param idVendor: Vendor ID :param idVendor: Vendor ID
:param idProduct: Product ID :param idProduct: Product ID
:param interface: USB device interface :param timeout: Is the time limit of the USB operation. Default without timeout.
:param in_ep: Input end point :param in_ep: Input end point
:param out_ep: Output end point :param out_ep: Output end point
""" """
Escpos.__init__(self, *args, **kwargs) Escpos.__init__(self, *args, **kwargs)
self.idVendor = idVendor self.idVendor = idVendor
self.idProduct = idProduct self.idProduct = idProduct
self.interface = interface self.timeout = timeout
self.in_ep = in_ep self.in_ep = in_ep
self.out_ep = out_ep self.out_ep = out_ep
self.open() self.open()
@@ -82,7 +82,7 @@ class Usb(Escpos):
:param msg: arbitrary code to be printed :param msg: arbitrary code to be printed
:type msg: bytes :type msg: bytes
""" """
self.device.write(self.out_ep, msg, self.interface) self.device.write(self.out_ep, msg, self.timeout)
def close(self): def close(self):
""" Release USB interface """ """ Release USB interface """
@@ -229,13 +229,15 @@ class File(Escpos):
""" """
def __init__(self, devfile="/dev/usb/lp0", *args, **kwargs): def __init__(self, devfile="/dev/usb/lp0", auto_flush=True, *args, **kwargs):
""" """
:param devfile : Device file under dev filesystem :param devfile : Device file under dev filesystem
:param auto_flush: automatically call flush after every call of _raw()
""" """
Escpos.__init__(self, *args, **kwargs) Escpos.__init__(self, *args, **kwargs)
self.devfile = devfile self.devfile = devfile
self.auto_flush = auto_flush
self.open() self.open()
def open(self): def open(self):
@@ -256,6 +258,8 @@ class File(Escpos):
:type msg: bytes :type msg: bytes
""" """
self.device.write(msg) self.device.write(msg)
if self.auto_flush:
self.flush()
def close(self): def close(self):
""" Close system file """ """ Close system file """

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.

70
test/test_printer_file.py Normal file
View File

@@ -0,0 +1,70 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""tests for the File printer
: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 six
import mock
from hypothesis import given
from hypothesis.strategies import text
import escpos.printer as printer
if six.PY3:
mock_open_call = 'builtins.open'
else:
mock_open_call = '__builtin__.open'
@given(path=text())
@mock.patch(mock_open_call)
@mock.patch('escpos.escpos.Escpos.__init__')
def test_load_file_printer(mock_escpos, mock_open, path):
"""test the loading of the file-printer"""
printer.File(devfile=path)
assert mock_escpos.called
mock_open.assert_called_with(path, "wb")
@given(txt=text())
@mock.patch.object(printer.File, 'device')
@mock.patch(mock_open_call)
@mock.patch('escpos.escpos.Escpos.__init__')
def test_auto_flush(mock_escpos, mock_open, mock_device, txt):
"""test auto_flush in file-printer"""
p = printer.File(auto_flush=False)
# inject the mocked device-object
p.device = mock_device
p._raw(txt)
assert not mock_device.flush.called
mock_device.reset_mock()
p = printer.File(auto_flush=True)
# inject the mocked device-object
p.device = mock_device
p._raw(txt)
assert mock_device.flush.called
@given(txt=text())
@mock.patch.object(printer.File, 'device')
@mock.patch(mock_open_call)
def test_flush_on_close(mock_open, mock_device, txt):
"""test flush on close in file-printer"""
p = printer.File(auto_flush=False)
# inject the mocked device-object
p.device = mock_device
p._raw(txt)
assert not mock_device.flush.called
p.close()
assert mock_device.flush.called
assert mock_device.close.called

View File

@@ -5,6 +5,8 @@ envlist = py27, py34, py35, docs
deps = nose deps = nose
coverage coverage
scripttest scripttest
mock
hypothesis
commands = nosetests --with-coverage --cover-erase --cover-branches commands = nosetests --with-coverage --cover-erase --cover-branches
[testenv:docs] [testenv:docs]