diff --git a/.mailmap b/.mailmap index 5b909f6..4540049 100644 --- a/.mailmap +++ b/.mailmap @@ -12,3 +12,4 @@ Juanmi Taboada Juanmi Taboada Sergio Pulgarin reck31 +Alex Debiasio diff --git a/.travis.yml b/.travis.yml index e871b54..815eb23 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: python sudo: false cache: pip +dist: xenial git: depth: 100000 addons: @@ -22,23 +23,26 @@ matrix: env: TOXENV=py36 - python: 3.6-dev env: TOXENV=py36 + - python: 3.7 + env: TOXENV=py37 - python: 3.7-dev env: TOXENV=py37 + - python: 3.8-dev + env: TOXENV=py38 - python: nightly - env: TOXENV=py37 + env: TOXENV=py38 - python: pypy env: TOXENV=pypy - python: pypy3 env: TOXENV=pypy3 - - python: 2.7 + - python: 3.7 env: TOXENV=docs - - python: 2.7 - env: TOXENV=flake8 - - python: 3.6 + - python: 3.7 env: TOXENV=flake8 allow_failures: - python: 3.6-dev - python: 3.7-dev + - python: 3.8-dev - python: nightly - python: pypy3 before_install: @@ -63,4 +67,4 @@ deploy: tags: true repo: python-escpos/python-escpos branch: master - condition: $TRAVIS_PYTHON_VERSION = "3.6" + condition: $TRAVIS_PYTHON_VERSION = "3.7" diff --git a/AUTHORS b/AUTHORS index aa3cce8..89f10bb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,6 @@ Ahmed Tahri +akeonly +Alex Debiasio Asuki Kono belono Christoph Heuel @@ -8,8 +10,10 @@ Curtis // mashedkeyboard Davis Goglin Dean Rispin Dmytro Katyukha +Gerard Marull-Paretas Hark Joel Lehtonen +Justin Vieira kennedy Kristi ldos @@ -19,9 +23,11 @@ Michael Billington Michael Elsdörfer mrwunderbar666 Nathan Bookham +Omer Akram Patrick Kanzler primax79 Qian Linfeng +Ramon Poca reck31 Renato Lorenzi Romain Porte diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 360ef2b..abae03f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,29 @@ ********* Changelog ********* +2019-06-16 - Version 3.0a5 - "Lightly Seared On The Reality Grill" +------------------------------------------------------------------ +This release is the sixth alpha release of the new version 3.0. Please +be aware that the API is subject to change until v3.0 is released. + +changes +^^^^^^^ +- allow arbitrary USB arguments in USB-class +- add Win32Raw-Printer on Windows-platforms +- add and improve Windows support of USB-class +- use pyyaml safe_load() +- improve doc +- implement _read method of Network printer class + +contributors +^^^^^^^^^^^^ +- Patrick Kanzler +- Gerard Marull-Paretas +- Ramon Poca +- akeonly +- Omer Akram +- Justin Vieira + 2018-05-15 - Version 3.0a4 - "Kakistocrat" ------------------------------------------ This release is the fifth alpha release of the new version 3.0. Please diff --git a/README.rst b/README.rst index fc551e1..3eebf6d 100644 --- a/README.rst +++ b/README.rst @@ -65,6 +65,19 @@ The basic usage is: p.barcode('1324354657687', 'EAN13', 64, 2, '', '') p.cut() + +Another example based on the Network printer class: + +.. code:: python + + from escpos.printer import Network + + kitchen = Network("192.168.1.100") #Printer IP Address + kitchen.text("Hello World\n") + kitchen.barcode('1324354657687', 'EAN13', 64, 2, '', '') + kitchen.cut() + + The full project-documentation is available on `Read the Docs `_. Contributing diff --git a/doc/user/usage.rst b/doc/user/usage.rst index 203389b..37f234a 100644 --- a/doc/user/usage.rst +++ b/doc/user/usage.rst @@ -44,7 +44,7 @@ to have and the second yields the "Output Endpoint" address. :: - Epson = printer.Usb(0x04b8,0x0202) + p = printer.Usb(0x04b8,0x0202) By default the "Interface" number is "0" and the "Output Endpoint" address is "0x01". If you have other values then you can define them on @@ -55,7 +55,7 @@ on 0x81 and out\_ep=0x02, then the printer definition should look like: :: - Generic = printer.Usb(0x1a2b,0x1a2b,0,0x81,0x02) + p = printer.Usb(0x1a2b,0x1a2b,0,0x81,0x02) Network printer ^^^^^^^^^^^^^^^ @@ -67,7 +67,7 @@ IP by DHCP or you set it manually. :: - Epson = printer.Network("192.168.1.99") + p = printer.Network("192.168.1.99") Serial printer ^^^^^^^^^^^^^^ @@ -81,7 +81,10 @@ to. :: - Epson = printer.Serial("/dev/tty0") + p = printer.Serial("/dev/tty0") + + # on a Windows OS serial devices are typically accessible as COM + p = printer.Serial("COM1") Other printers ^^^^^^^^^^^^^^ @@ -93,7 +96,7 @@ passing the device node name. :: - Epson = printer.File("/dev/usb/lp1") + p = printer.File("/dev/usb/lp1") The default is "/dev/usb/lp0", so if the printer is located on that node, then you don't necessary need to pass the node name. @@ -108,17 +111,17 @@ on a USB interface. from escpos import * """ Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """ - Epson = printer.Usb(0x04b8,0x0202) + p = printer.Usb(0x04b8,0x0202) # Print text - Epson.text("Hello World\n") + p.text("Hello World\n") # Print image - Epson.image("logo.gif") + p.image("logo.gif") # Print QR Code - Epson.qr("You can readme from your smartphone") + p.qr("You can readme from your smartphone") # Print barcode - Epson.barcode('1324354657687','EAN13',64,2,'','') + p.barcode('1324354657687','EAN13',64,2,'','') # Cut paper - Epson.cut() + p.cut() Configuration File ------------------ diff --git a/setup.py b/setup.py index 6b21450..a09bc83 100755 --- a/setup.py +++ b/setup.py @@ -65,8 +65,6 @@ setup( 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', @@ -102,7 +100,7 @@ setup( 'nose', 'scripttest', 'mock', - 'hypothesis!=3.56.9', + 'hypothesis!=3.56.9,<4', 'flake8' ], entry_points={ diff --git a/src/escpos/capabilities.py b/src/escpos/capabilities.py index 0cf76e1..c4b6ab5 100644 --- a/src/escpos/capabilities.py +++ b/src/escpos/capabilities.py @@ -38,7 +38,7 @@ else: if full_load: logger.debug('Loading and pickling capabilities') with open(capabilities_path) as cp, open(pickle_path, 'wb') as pp: - CAPABILITIES = yaml.load(cp) + CAPABILITIES = yaml.safe_load(cp) pickle.dump(CAPABILITIES, pp, protocol=2) logger.debug('Finished loading capabilities took %.2fs', time.time() - t0) diff --git a/src/escpos/printer.py b/src/escpos/printer.py index a652b98..54eb74a 100644 --- a/src/escpos/printer.py +++ b/src/escpos/printer.py @@ -34,41 +34,58 @@ class Usb(Escpos): """ - def __init__(self, idVendor, idProduct, timeout=0, in_ep=0x82, out_ep=0x01, *args, **kwargs): # noqa: N803 + def __init__(self, idVendor, idProduct, usb_args=None, timeout=0, in_ep=0x82, out_ep=0x01, + *args, **kwargs): # noqa: N803 """ :param idVendor: Vendor ID :param idProduct: Product ID + :param usb_args: Optional USB arguments (e.g. custom_match) :param timeout: Is the time limit of the USB operation. Default without timeout. :param in_ep: Input end point :param out_ep: Output end point """ Escpos.__init__(self, *args, **kwargs) - self.idVendor = idVendor - self.idProduct = idProduct self.timeout = timeout self.in_ep = in_ep self.out_ep = out_ep - self.open() - def open(self): - """ Search device on USB tree and set it as escpos device """ - self.device = usb.core.find(idVendor=self.idVendor, idProduct=self.idProduct) + usb_args = usb_args or {} + if idVendor: + usb_args['idVendor'] = idVendor + if idProduct: + usb_args['idProduct'] = idProduct + self.open(usb_args) + + def open(self, usb_args): + """ Search device on USB tree and set it as escpos device. + + :param usb_args: USB arguments + """ + self.device = usb.core.find(**usb_args) if self.device is None: raise USBNotFoundError("Device not found or cable not plugged in.") - check_driver = None + self.idVendor = self.device.idVendor + self.idProduct = self.device.idProduct - try: - check_driver = self.device.is_kernel_driver_active(0) - except NotImplementedError: - pass + # pyusb has three backends: libusb0, libusb1 and openusb but + # only libusb1 backend implements the methods is_kernel_driver_active() + # and detach_kernel_driver(). + # This helps enable this library to work on Windows. + if self.device.backend.__module__.endswith("libusb1"): + check_driver = None - if check_driver is None or check_driver: try: - self.device.detach_kernel_driver(0) - except usb.core.USBError as e: - if check_driver is not None: - print("Could not detatch kernel driver: {0}".format(str(e))) + check_driver = self.device.is_kernel_driver_active(0) + except NotImplementedError: + pass + + if check_driver is None or check_driver: + try: + self.device.detach_kernel_driver(0) + except usb.core.USBError as e: + if check_driver is not None: + print("Could not detatch kernel driver: {0}".format(str(e))) try: self.device.set_configuration() @@ -219,6 +236,11 @@ class Network(Escpos): """ self.device.sendall(msg) + def _read(self): + """ Read data from the TCP socket """ + + return self.device.recv(16) + def close(self): """ Close TCP connection """ if self.device is not None: @@ -322,3 +344,48 @@ class Dummy(Escpos): def close(self): pass + + +_WIN32PRINT = False +try: + import win32print + _WIN32PRINT = True +except ImportError: + pass + +if _WIN32PRINT: + class Win32Raw(Escpos): + def __init__(self, printer_name=None, *args, **kwargs): + Escpos.__init__(self, *args, **kwargs) + if printer_name is not None: + self.printer_name = printer_name + else: + self.printer_name = win32print.GetDefaultPrinter() + self.hPrinter = None + + def open(self, job_name="python-escpos"): + if self.printer_name is None: + raise Exception("Printer not found") + self.hPrinter = win32print.OpenPrinter(self.printer_name) + self.current_job = win32print.StartDocPrinter(self.hPrinter, 1, (job_name, None, "RAW")) + win32print.StartPagePrinter(self.hPrinter) + + def close(self): + if not self.hPrinter: + return + win32print.EndPagePrinter(self.hPrinter) + win32print.EndDocPrinter(self.hPrinter) + win32print.ClosePrinter(self.hPrinter) + self.hPrinter = None + + def _raw(self, msg): + """ Print any command sent in raw format + + :param msg: arbitrary code to be printed + :type msg: bytes + """ + if self.printer_name is None: + raise Exception("Printer not found") + if self.hPrinter is None: + raise Exception("Printer job not opened") + win32print.WritePrinter(self.hPrinter, msg) diff --git a/tox.ini b/tox.ini index 142ca43..9dc7a4f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27, py34, py35, docs, flake8 +envlist = py27, py34, py35, py36, py37, docs, flake8 [testenv] deps = nose @@ -10,7 +10,7 @@ deps = nose pytest!=3.2.0,!=3.3.0 pytest-cov pytest-mock - hypothesis!=3.56.9 + hypothesis!=3.56.9,<4 viivakoodi commands = py.test --cov escpos passenv = ESCPOS_CAPABILITIES_PICKLE_DIR ESCPOS_CAPABILITIES_FILE CI TRAVIS TRAVIS_* APPVEYOR APPVEYOR_* CODECOV_*