From 9bc3b30a6097326cdac6fbcb6d5c4c9430c75847 Mon Sep 17 00:00:00 2001 From: Romain Porte Date: Tue, 18 Jul 2017 18:50:38 +0200 Subject: [PATCH 1/7] Optional feed for cut (closes #213) --- src/escpos/escpos.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index ec4df3b..64e8e05 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -574,7 +574,7 @@ class Escpos(object): self._raw(LINESPACING_FUNCS[divisor] + six.int2byte(spacing)) - def cut(self, mode='FULL'): + def cut(self, mode='FULL', feed=True): """ Cut paper. Without any arguments the paper will be cut completely. With 'mode=PART' a partial cut will @@ -584,9 +584,11 @@ class Escpos(object): .. todo:: Check this function on TM-T88II. :param mode: set to 'PART' for a partial cut. default: 'FULL' + :param feed: print and feed before cutting. default: true :raises ValueError: if mode not in ('FULL', 'PART') """ - self.print_and_feed(6) + if feed: + self.print_and_feed(6) mode = mode.upper() if mode not in ('FULL', 'PART'): From 9e47ff2505d83469d589b90e50490863a04d4d56 Mon Sep 17 00:00:00 2001 From: Romain Porte Date: Sun, 23 Jul 2017 11:11:22 +0200 Subject: [PATCH 2/7] Added test for cut without feed, fixed raw code for it --- src/escpos/escpos.py | 8 ++++++-- test/test_function_cut.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 test/test_function_cut.py diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 64e8e05..ef2c026 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -587,8 +587,12 @@ class Escpos(object): :param feed: print and feed before cutting. default: true :raises ValueError: if mode not in ('FULL', 'PART') """ - if feed: - self.print_and_feed(6) + + if not feed: + self._raw(GS + b'V' + six.int2byte(66) + b'\x00') + return + + self.print_and_feed(6) mode = mode.upper() if mode not in ('FULL', 'PART'): diff --git a/test/test_function_cut.py b/test/test_function_cut.py new file mode 100644 index 0000000..d485e0e --- /dev/null +++ b/test/test_function_cut.py @@ -0,0 +1,17 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six + +import escpos.printer as printer +from escpos.constants import GS + + +def test_cut_without_feed(): + """Test cut without feeding paper""" + instance = printer.Dummy() + instance.cut(feed=False) + expected = GS + b'V' + six.int2byte(66) + b'\x00' + assert(instance.output == expected) From 82c67aa646db825e98e5048aa08291d1354e7da8 Mon Sep 17 00:00:00 2001 From: csoft2k Date: Wed, 26 Jul 2017 10:01:08 +0200 Subject: [PATCH 3/7] Fix tabs behaviour (#238) The changes done in this commit should help with the open issues: #5, #27 and #161. The old implementation lacked the NUL char at the end of the command, as defined on the Epson ESC/POS Reference Guide (see https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=53 ). Also, the horizontal tab control character (CTL_HT) shouldn't be there. This implementation allows setting up to 32 tabs with a given tab width. Both values are checked to be in the valid ranges defined on the guide. Also, the TabPosError exception text has been rewritten to define the stated above. --- src/escpos/escpos.py | 18 +++++++++++------- src/escpos/exceptions.py | 3 ++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index ef2c026..cb72204 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -33,7 +33,7 @@ from .constants import LINESPACING_FUNCS, LINESPACING_RESET from .constants import LINE_DISPLAY_OPEN, LINE_DISPLAY_CLEAR, LINE_DISPLAY_CLOSE from .constants import CD_KICK_DEC_SEQUENCE, CD_KICK_5, CD_KICK_2, PAPER_FULL_CUT, PAPER_PART_CUT from .constants import HW_RESET, HW_SELECT, HW_INIT -from .constants import CTL_VT, CTL_HT, CTL_CR, CTL_FF, CTL_LF, CTL_SET_HT, PANEL_BUTTON_OFF, PANEL_BUTTON_ON +from .constants import CTL_VT, CTL_CR, CTL_FF, CTL_LF, CTL_SET_HT, PANEL_BUTTON_OFF, PANEL_BUTTON_ON from .constants import TXT_STYLE from .constants import RT_STATUS_ONLINE, RT_MASK_ONLINE @@ -695,7 +695,7 @@ class Escpos(object): else: raise ValueError("n must be betwen 0 and 255") - def control(self, ctl, pos=4): + def control(self, ctl, count=5, tab_size=8): """ Feed control sequences :param ctl: string for the following control sequences: @@ -706,7 +706,8 @@ class Escpos(object): * HT *for Horizontal Tab* * VT *for Vertical Tab* - :param pos: integer between 1 and 16, controls the horizontal tab position + :param count: integer between 1 and 32, controls the horizontal tab count. Defaults to 5. + :param tab_size: integer between 1 and 255, controls the horizontal tab size in characters. Defaults to 8 :raises: :py:exc:`~escpos.exceptions.TabPosError` """ # Set position @@ -717,13 +718,16 @@ class Escpos(object): elif ctl.upper() == "CR": self._raw(CTL_CR) elif ctl.upper() == "HT": - if not (1 <= pos <= 16): + if not (0 <= count <= 32 and + 1 <= tab_size <= 255 and + count * tab_size < 256): raise TabPosError() else: # Set tab positions - self._raw(CTL_SET_HT + six.int2byte(pos)) - - self._raw(CTL_HT) + self._raw(CTL_SET_HT) + for iterator in range(1, count): + self._raw(six.int2byte(iterator * tab_size)) + self._raw(NUL) elif ctl.upper() == "VT": self._raw(CTL_VT) diff --git a/src/escpos/exceptions.py b/src/escpos/exceptions.py index 0662792..781e3e9 100644 --- a/src/escpos/exceptions.py +++ b/src/escpos/exceptions.py @@ -150,7 +150,8 @@ class CashDrawerError(Error): class TabPosError(Error): - """ Valid tab positions must be in the range 0 to 16. + """ Valid tab positions must be set by using from 1 to 32 tabs, and between 1 and 255 tab size values. + Both values multiplied must not exceed 255, since it is the maximum tab value. This exception is raised by :py:meth:`escpos.escpos.Escpos.control`. The returncode for this exception is `70`. From cf0cf127fe749af0698cf35a14fd4ea4cce6fd7a Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Thu, 27 Jul 2017 16:50:41 +0200 Subject: [PATCH 4/7] add changelog for next release --- CHANGELOG.rst | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5ffef2b..1515095 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,32 @@ Changelog ********* +2017-07-27 - Version 3.0a2 - "It's My Party And I'll Sing If I Want To" +----------------------------------------------------------------------- +This release is the third alpha release of the new version 3.0. Please +be aware that the API will still change until v3.0 is released. + +changes +^^^^^^^ +- refactor of the set-method +- preliminary support of POS "line display" printing +- improvement of tests +- added ImageWidthError +- list authors in repository +- add support for software-based barcode-rendering +- fix SerialException when trying to close device on __del__ +- added the DLE EOT querying command for USB +- ensure QR codes have a large enough border +- make feed for cut optional +- fix the behavior of horizontal tabs + +contributors +^^^^^^^^^^^^ +- csoft2k +- Patrick Kanzler +- Romain Porte +- Ahmed Tahri + 2017-03-29 - Version 3.0a1 - "Headcrash" ---------------------------------------- This release is the second alpha release of the new version 3.0. Please From c7080165a7420b5cfe8cf17c5717ee7da2b7b60f Mon Sep 17 00:00:00 2001 From: Romain Porte Date: Thu, 27 Jul 2017 22:45:51 +0200 Subject: [PATCH 5/7] Added test script for hard and soft barcodes (#243) --- examples/barcodes.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 examples/barcodes.py diff --git a/examples/barcodes.py b/examples/barcodes.py new file mode 100644 index 0000000..7e9c242 --- /dev/null +++ b/examples/barcodes.py @@ -0,0 +1,11 @@ +from escpos.printer import Usb + + +# Adapt to your needs +p = Usb(0x0416, 0x5011, profile="POS-5890") + +# Print software and then hardware barcode with the same content +p.soft_barcode('code39', '123456') +p.text('\n') +p.text('\n') +p.barcode('123456', 'CODE39') From 1f57b04974fa016eb91148e09142c25acac8680a Mon Sep 17 00:00:00 2001 From: csoft2k Date: Thu, 27 Jul 2017 23:05:50 +0200 Subject: [PATCH 6/7] Paper sensor querying command (#242) The DLE EOT command allows querying the status of several features of the printer. Added to the online/offline status developed in #237, this commit adds a paper sensor querying. Tested with an Epson TM-T20II, which only has an end-paper sensor. The near-end paper sensor should be tested with a compatible printer. However, the implementation is quite straight-forward. --- src/escpos/constants.py | 9 +++++++-- src/escpos/escpos.py | 30 ++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/escpos/constants.py b/src/escpos/constants.py index a764290..75bdc1b 100644 --- a/src/escpos/constants.py +++ b/src/escpos/constants.py @@ -251,5 +251,10 @@ S_RASTER_2H = _PRINT_RASTER_IMG(b'\x02') # Set raster image double height S_RASTER_Q = _PRINT_RASTER_IMG(b'\x03') # Set raster image quadruple # Status Command -RT_STATUS_ONLINE = DLE + EOT + b'\x01'; -RT_MASK_ONLINE = 8; +RT_STATUS = DLE + EOT +RT_STATUS_ONLINE = RT_STATUS + b'\x01' +RT_STATUS_PAPER = RT_STATUS + b'\x04' +RT_MASK_ONLINE = 8 +RT_MASK_PAPER = 18 +RT_MASK_LOWPAPER = 30 +RT_MASK_NOPAPER = 114 \ No newline at end of file diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index cb72204..f13948b 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -36,6 +36,7 @@ from .constants import HW_RESET, HW_SELECT, HW_INIT from .constants import CTL_VT, CTL_CR, CTL_FF, CTL_LF, CTL_SET_HT, PANEL_BUTTON_OFF, PANEL_BUTTON_ON from .constants import TXT_STYLE from .constants import RT_STATUS_ONLINE, RT_MASK_ONLINE +from .constants import RT_STATUS_PAPER, RT_MASK_PAPER, RT_MASK_LOWPAPER, RT_MASK_NOPAPER from .exceptions import BarcodeTypeError, BarcodeSizeError, TabPosError from .exceptions import CashDrawerError, SetVariableError, BarcodeCodeError @@ -754,19 +755,40 @@ class Escpos(object): else: self._raw(PANEL_BUTTON_OFF) - def query_status(self): + def query_status(self, mode): """ Queries the printer for its status, and returns an array of integers containing it. + :param mode: Integer that sets the status mode queried to the printer. + RT_STATUS_ONLINE: Printer status. + RT_STATUS_PAPER: Paper sensor. :rtype: array(integer)""" - self._raw(RT_STATUS_ONLINE) + self._raw(mode) time.sleep(1) status = self._read() - return status or [RT_MASK_ONLINE] + return status def is_online(self): """ Queries the printer its online status. When online, returns True; False otherwise. :rtype: bool: True if online, False if offline.""" - return not (self.query_status()[0] & RT_MASK_ONLINE) + status = self.query_status(RT_STATUS_ONLINE) + if len(status) == 0: + return False + return not (status & RT_MASK_ONLINE) + + def paper_status(self): + """ Queries the printer its paper status. + Returns 2 if there is plenty of paper, 1 if the paper has arrived to + the near-end sensor and 0 if there is no paper. + :rtype: int: 2: Paper is adequate. 1: Paper ending. 0: No paper.""" + status = self.query_status(RT_STATUS_PAPER) + if len(status) == 0: + return 2 + if (status[0] & RT_MASK_NOPAPER == RT_MASK_NOPAPER): + return 0 + if (status[0] & RT_MASK_LOWPAPER == RT_MASK_LOWPAPER): + return 1 + if (status[0] & RT_MASK_PAPER == RT_MASK_PAPER): + return 2 class EscposIO(object): From f8a2174108c4d2030922f5b7c6d7385518ad7e08 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Thu, 27 Jul 2017 23:06:59 +0200 Subject: [PATCH 7/7] fix typo --- src/escpos/escpos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index f13948b..6d5cf5c 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -251,9 +251,9 @@ class Escpos(object): """ max_input = (256 << (out_bytes * 8) - 1) if not 1 <= out_bytes <= 4: - raise ValueError("Can only output 1-4 byes") + raise ValueError("Can only output 1-4 bytes") if not 0 <= inp_number <= max_input: - raise ValueError("Number too large. Can only output up to {0} in {1} byes".format(max_input, out_bytes)) + raise ValueError("Number too large. Can only output up to {0} in {1} bytes".format(max_input, out_bytes)) outp = b'' for _ in range(0, out_bytes): outp += six.int2byte(inp_number % 256)