From 910f2fbf2ffd4c4af3ff10ba59ed6a06aacdacaa Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Mar 2016 15:54:43 +0200 Subject: [PATCH 1/2] add with-statement * with imported from mosquito (adaption of the code in 86a3e8dfb257e3af0ec141ee5d97717501d6e945 by @mosquito) * encoding of file to utf-8 * added rudimentary test for with * implement close function for all printers --- escpos/constants.py | 1 + escpos/escpos.py | 91 +++++++++++++++++++++++++++++++++++++ escpos/exceptions.py | 1 + escpos/printer.py | 14 ++++-- test/test_with_statement.py | 24 ++++++++++ 5 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 test/test_with_statement.py diff --git a/escpos/constants.py b/escpos/constants.py index e268edb..0e1ce82 100644 --- a/escpos/constants.py +++ b/escpos/constants.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Set of ESC/POS Commands (Constants) This module contains constants that are described in the esc/pos-documentation. diff --git a/escpos/escpos.py b/escpos/escpos.py index 33cd6c9..9b6cb87 100644 --- a/escpos/escpos.py +++ b/escpos/escpos.py @@ -1,4 +1,5 @@ #!/usr/bin/python +# -*- coding: utf-8 -*- """ Main class This module contains the abstract base class :py:class:`Escpos`. @@ -38,6 +39,10 @@ class Escpos(object): :param columns: Text columns used by the printer. Defaults to 32.""" self.columns = columns + def __del__(self): + """ call self.close upon deletion """ + self.close() + @abstractmethod def _raw(self, msg): """ Sends raw data to the printer @@ -620,3 +625,89 @@ class Escpos(object): self._raw(PANEL_BUTTON_ON) else: self._raw(PANEL_BUTTON_OFF) + + +class EscposIO(object): + """ESC/POS Printer IO object + + Allows the class to be used together with the `with`-statement. You have to define a printer instance + and assign it to the EsposIO-class. + This example explains the usage: + + .. code-block:: Python + + with EscposIO(printer.Serial('/dev/ttyUSB0')) as p: + p.set(font='a', height=2, align='center', text_type='bold') + p.printer.set(align='left') + p.printer.image('logo.gif') + p.writelines('Big line\\n', font='b') + p.writelines('Привет') + p.writelines('BIG TEXT', width=2) + + After the `with`-statement the printer automatically cuts the paper if `autocut` is `True`. + """ + + def __init__(self, printer, autocut=True, autoclose=True, **kwargs): + """ + :param printer: An EscPos-printer object + :type printer: escpos.Escpos + :param autocut: If True, paper is automatically cut after the `with`-statement *default*: True + :param kwargs: These arguments will be passed to :py:meth:`escpos.Escpos.set()` + """ + self.printer = printer + self.params = kwargs + self.autocut = autocut + self.autoclose = autoclose + + def set(self, **kwargs): + """ Set the printer-parameters + + Controls which parameters will be passed to :py:meth:`Escpos.set() `. + For more information on the parameters see the :py:meth:`set() `-methods + documentation. These parameters can also be passed with this class' constructor or the + :py:meth:`~escpos.escpos.EscposIO.writelines()`-method. + + :param kwargs: keyword-parameters that will be passed to :py:meth:`Escpos.set() ` + """ + self.params.update(kwargs) + + def writelines(self, text, **kwargs): + params = dict(self.params) + params.update(kwargs) + + if isinstance(text, six.text_type): + lines = text.split('\n') + elif isinstance(text, list) or isinstance(text, tuple): + lines = text + else: + lines = ["{0}".format(text), ] + + # TODO check unicode handling + # TODO flush? or on print? (this should prob rather be handled by the _raw-method) + for line in lines: + self.printer.set(**params) + if isinstance(text, six.text_type): + self.printer.text(u"{0}\n".format(line)) + else: + self.printer.text("{0}\n".format(line)) + + def close(self): + """ called upon closing the `with`-statement + """ + self.printer.close() + + def __enter__(self, **kwargs): + return self + + def __exit__(self, type, value, traceback): + """ + + If :py:attr:`autocut ` is `True` (set by this class' constructor), + then :py:meth:`printer.cut() ` will be called here. + """ + if not (type is not None and issubclass(type, Exception)): + if self.autocut: + self.printer.cut() + + if self.autoclose: + self.close() diff --git a/escpos/exceptions.py b/escpos/exceptions.py index 0a51782..629b94f 100644 --- a/escpos/exceptions.py +++ b/escpos/exceptions.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ ESC/POS Exceptions classes Result/Exit codes: diff --git a/escpos/printer.py b/escpos/printer.py index ee9e227..654b995 100644 --- a/escpos/printer.py +++ b/escpos/printer.py @@ -1,5 +1,6 @@ #!/usr/bin/python -""" This module contains the implentations of abstract base class :py:class:`Escpos`. +# -*- coding: utf-8 -*- +""" This module contains the implementations of abstract base class :py:class:`Escpos`. :author: `Manuel F Martinez `_ and others :organization: Bashlinux and `python-escpos `_ @@ -82,7 +83,7 @@ class Usb(Escpos): """ self.device.write(self.out_ep, msg, self.interface) - def __del__(self): + def close(self): """ Release USB interface """ if self.device: usb.util.dispose_resources(self.device) @@ -147,7 +148,7 @@ class Serial(Escpos): """ self.device.write(msg) - def __del__(self): + def close(self): """ Close Serial interface """ if self.device is not None: self.device.flush() @@ -207,7 +208,7 @@ class Network(Escpos): """ self.device.sendall(msg) - def __del__(self): + def close(self): """ Close TCP connection """ self.device.shutdown(socket.SHUT_RDWR) self.device.close() @@ -255,7 +256,7 @@ class File(Escpos): """ self.device.write(msg) - def __del__(self): + def close(self): """ Close system file """ self.device.flush() self.device.close() @@ -294,3 +295,6 @@ class Dummy(Escpos): def output(self): """ Get the data that was sent to this printer """ return b''.join(self._output_list) + + def close(self): + pass diff --git a/test/test_with_statement.py b/test/test_with_statement.py new file mode 100644 index 0000000..a59a719 --- /dev/null +++ b/test/test_with_statement.py @@ -0,0 +1,24 @@ +#!/usr/bin/python +"""test the facility which enables usage of the with-statement + +:author: `Patrick Kanzler `_ +:organization: `python-escpos `_ +:copyright: Copyright (c) 2016 `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 escpos.printer as printer +import escpos.escpos as escpos +import os + +def test_with_statement(): + """Use with statement""" + dummy_printer = printer.Dummy() + with escpos.EscposIO(dummy_printer) as p: + p.writelines('Some text.\n') + # TODO extend these tests as they don't really do anything at the moment From 1e490b6de889a59865e2007ce286b2c706472e13 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Fri, 17 Jun 2016 23:04:10 +0200 Subject: [PATCH 2/2] add basic support for multilanguage --- escpos/escpos.py | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/escpos/escpos.py b/escpos/escpos.py index 9b6cb87..b0d25ef 100644 --- a/escpos/escpos.py +++ b/escpos/escpos.py @@ -32,6 +32,7 @@ class Escpos(object): class. """ device = None + codepage = None def __init__(self, columns=32): """ Initialize ESCPOS Printer @@ -207,48 +208,71 @@ class Escpos(object): :param code: Name of CharCode :raises: :py:exc:`~escpos.exceptions.CharCodeError` """ + # TODO improve this (rather unhandy code) + # TODO check the codepages if code.upper() == "USA": self._raw(CHARCODE_PC437) + self.codepage = 'cp437' elif code.upper() == "JIS": self._raw(CHARCODE_JIS) + self.codepage = 'cp932' elif code.upper() == "MULTILINGUAL": self._raw(CHARCODE_PC850) + self.codepage = 'cp850' elif code.upper() == "PORTUGUESE": self._raw(CHARCODE_PC860) + self.codepage = 'cp860' elif code.upper() == "CA_FRENCH": self._raw(CHARCODE_PC863) + self.codepage = 'cp863' elif code.upper() == "NORDIC": self._raw(CHARCODE_PC865) + self.codepage = 'cp865' elif code.upper() == "WEST_EUROPE": self._raw(CHARCODE_WEU) + self.codepage = 'latin_1' elif code.upper() == "GREEK": self._raw(CHARCODE_GREEK) + self.codepage = 'cp737' elif code.upper() == "HEBREW": self._raw(CHARCODE_HEBREW) + self.codepage = 'cp862' # elif code.upper() == "LATVIAN": # this is not listed in the constants # self._raw(CHARCODE_PC755) + # self.codepage = 'cp' elif code.upper() == "WPC1252": self._raw(CHARCODE_PC1252) + self.codepage = 'cp1252' elif code.upper() == "CIRILLIC2": self._raw(CHARCODE_PC866) + self.codepage = 'cp866' elif code.upper() == "LATIN2": self._raw(CHARCODE_PC852) + self.codepage = 'cp852' elif code.upper() == "EURO": self._raw(CHARCODE_PC858) + self.codepage = 'cp858' elif code.upper() == "THAI42": self._raw(CHARCODE_THAI42) + self.codepage = 'cp874' elif code.upper() == "THAI11": self._raw(CHARCODE_THAI11) + self.codepage = 'cp874' elif code.upper() == "THAI13": self._raw(CHARCODE_THAI13) + self.codepage = 'cp874' elif code.upper() == "THAI14": self._raw(CHARCODE_THAI14) + self.codepage = 'cp874' elif code.upper() == "THAI16": self._raw(CHARCODE_THAI16) + self.codepage = 'cp874' elif code.upper() == "THAI17": self._raw(CHARCODE_THAI17) + self.codepage = 'cp874' elif code.upper() == "THAI18": self._raw(CHARCODE_THAI18) + self.codepage = 'cp874' else: raise CharCodeError() @@ -385,14 +409,16 @@ class Escpos(object): """ Print alpha-numeric text The text has to be encoded in the currently selected codepage. - - .. todo:: rework this in order to proberly handle encoding + The input text has to be encoded in unicode. :param txt: text to be printed :raises: :py:exc:`~escpos.exceptions.TextError` """ if txt: - self._raw(txt.encode()) + if self.codepage: + self._raw(txt.encode(self.codepage)) + else: + self._raw(txt.encode()) else: # TODO: why is it problematic to print an empty string? raise TextError() @@ -400,6 +426,8 @@ class Escpos(object): def block_text(self, txt, columns=None): """ Text is printed wrapped to specified columns + Text has to be encoded in unicode. + :param txt: text to be printed :param columns: amount of columns :return: None