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

52 Commits

Author SHA1 Message Date
Patrick Kanzler
a68347ee1c update mailmap-file 2019-06-30 17:43:20 +02:00
Brian 'Redbeard' Harrington
c40f2abe07 bug/doc: Fix invalid EAN-13 barcode in examples
In the various examples and python-escpos CLI there are a number of uses
of `13243546557687` as an EAN-13 example.  This EAN barcode is invalid
as the checksum should be `0` and not `7`.

```
$ python test_print.py
Traceback (most recent call last):
  File "test_print.py", line 5, in <module>
    p.barcode('13243546557687', 'EAN13', 64, 2, '', '')
  File "/home/pi/fatt-display/lib/python3.7/site-packages/escpos/escpos.py", line 433, in barcode
    bc=bc,
escpos.exceptions.BarcodeCodeError: No Barcode code was supplied (Barcode '13243546557687' not in a valid format for type 'EAN13')
```

This patch set removes `13243546557687` and replaces it with the valid
number `40063813339310`.

In researching the list of [assigned prefixes issued by
G1][g1-prefixes] there seemed to be no "officially" defined test  prefix, so
this change was made to be minimally invasive using the number from the
existing test cases.

Resolves #350

Affects #176

[g1-prefixes]: https://www.gs1.org/standards/id-keys/company-prefix
[test-code]: https://www.barcodelookup.com/4006381333931
2019-06-29 19:19:16 -07:00
Brian 'Redbeard' Harrington
fb82e5dc4a AUTHORS: Adding Brian Redbeard to AUTHORS
As per `CONTRIBUTING.rst` including a commit for Brian 'redbeard'
Harrington to AUTHORS
2019-06-29 19:19:16 -07:00
Patrick Kanzler
fa140c2df5 cleanup todo page 2019-06-19 15:50:19 +02:00
Patrick Kanzler
8b3076871f Merge pull request #346 from python-escpos/development
release v3.0a6
2019-06-19 15:28:46 +02:00
Patrick Kanzler
46429b1092 update changelog 2019-06-19 14:39:52 +02:00
Patrick Kanzler
99ca096f82 Merge pull request #345 from python-escpos/setup-fix-capabilities-path
improve package structure with capabilities file
2019-06-19 14:30:00 +02:00
Patrick Kanzler
2d97c0bbbd improve package structure with capabilities file 2019-06-19 14:14:59 +02:00
Patrick Kanzler
6c6fe9bccf Merge pull request #342 from python-escpos/108-test-on-windows-travis
Add Windows and macOS configuration for Travis CI
2019-06-16 12:13:30 +02:00
Patrick Kanzler
c5e46a888d fix allowed failures 2019-06-16 03:08:19 +02:00
Patrick Kanzler
46942820a5 update env variables 2019-06-16 03:05:42 +02:00
Patrick Kanzler
e50e295acc add osx 2019-06-16 03:01:36 +02:00
Patrick Kanzler
2d7458fa49 activate fast_finish 2019-06-16 02:47:46 +02:00
Patrick Kanzler
a6f635c0d5 fix capabilities file 2019-06-16 02:45:40 +02:00
Patrick Kanzler
2d0f045457 add first draft of Windows conf for #108 2019-06-16 02:45:40 +02:00
Patrick Kanzler
293b8632ff Merge pull request #341 from python-escpos/308-update-docstring-qr
fix links and impl (in qr-method) in docstrings
2019-06-16 02:17:27 +02:00
Patrick Kanzler
5ff73595b6 fix links and impl (in qr-method) in docstrings
fixes #308
2019-06-16 01:54:47 +02:00
Patrick Kanzler
4ecab402b8 Merge pull request #339 from python-escpos/development
release v3.0a5
2019-06-16 00:31:07 +02:00
Patrick Kanzler
c56e43da84 update changelog 2019-06-16 00:17:04 +02:00
Patrick Kanzler
88af26f46e Merge pull request #338 from alexdebiasio/development
Implemented _read method of Network printer class. Solves issue #286
2019-06-16 00:01:12 +02:00
Patrick Kanzler
9dd966c2a3 update mailmap 2019-06-15 23:40:00 +02:00
Patrick Kanzler
a7d959428f update authors 2019-06-15 22:50:50 +02:00
Alex Debiasio
8bf0e08659 Implemented _read method of Network printer class 2019-06-13 21:28:46 +02:00
Patrick Kanzler
5ac5a24b50 Toolchain update travis drop py2x (#336)
* drop python 2 from supported languages

Python 2 compatibility will not be actively revoked, but will
not be worked on.

* update travis config

* remove flake8-test with Py2.7
2019-06-11 00:24:09 +02:00
Patrick Kanzler
63252515b5 Merge pull request #335 from python-escpos/322-doc-serial-printer-on-windows
doc add example for Serial on Windows
2019-06-10 22:26:34 +02:00
Patrick Kanzler
29a546821b doc add example for Serial on Windows
fixes #322
2019-06-10 22:05:26 +02:00
Patrick Kanzler
4ddd18279f Merge pull request #320 from ramonpoca/development
Add Win32Raw printer to available printers
2019-06-07 10:11:53 +02:00
Patrick Kanzler
de761e96e3 Merge branch 'development' into development 2019-06-07 09:53:14 +02:00
Patrick Kanzler
ed7bce6932 Merge pull request #329 from akeonly/patch-1
Update README.rst : example for network printer
2019-06-05 00:05:08 +02:00
Patrick Kanzler
edd567785c fix whitespace 2019-06-04 23:57:19 +02:00
Patrick Kanzler
f1054876da update authors 2019-06-04 23:55:33 +02:00
Patrick Kanzler
490e0657dd Merge branch 'development' into patch-1 2019-06-04 23:04:39 +02:00
Patrick Kanzler
b4c32b5a4a Merge branch 'development' into development 2019-06-04 23:04:25 +02:00
Patrick Kanzler
40b30225d3 Merge pull request #330 from om26er/windows-support
Enable Windows Support
2019-06-04 23:04:09 +02:00
Patrick Kanzler
19e3ec6895 Merge pull request #331 from teamorchard/use-pyyaml-safe-load
Update to use pyyaml safe_load()
2019-06-04 22:45:05 +02:00
Patrick Kanzler
df539da854 Merge branch 'development' into development 2019-06-04 22:31:47 +02:00
Patrick Kanzler
4534038b39 Merge branch 'development' into patch-1 2019-06-04 22:31:33 +02:00
Patrick Kanzler
adf85f7784 Merge branch 'development' into windows-support 2019-06-04 22:31:08 +02:00
Patrick Kanzler
aaa8162967 Merge branch 'development' into use-pyyaml-safe-load 2019-06-04 22:30:47 +02:00
Patrick Kanzler
0461adc212 restrict hypothesis-version to below 4 2019-06-04 22:30:23 +02:00
Justin Vieira
91ff83e506 Update to use pyyaml safe_load(), as load() is unsafe and disabled on some systems 2019-05-29 16:43:19 -04:00
Omer Akram
29ef88f591 Better comment 2019-05-22 17:25:33 +05:00
Omer Akram
7c01a30d6c fix a typo 2019-05-22 17:20:15 +05:00
Omer Akram
035c425581 Add author 2019-05-22 17:09:27 +05:00
Omer Akram
d20646b2a9 Make windows enablement code more intelligent 2019-05-22 17:01:13 +05:00
Omer Akram
206822ac69 Enable Windows Support 2019-05-22 16:39:26 +05:00
akeonly
dc08792e72 Update README.rst : example for network printer
Add example for Network Printer
2019-05-11 18:33:33 +07:00
Ramon Poca
2886075ce9 Fix initializer mess 2018-12-31 14:24:10 +01:00
Ramon Poca
73fff6291d Fix syntax 2018-12-31 13:57:53 +01:00
Ramon Poca
d5b9d99093 Update AUTHORS 2018-12-31 10:43:31 +01:00
Ramon Poca
18c51358aa Add Win32Raw printer to available printers 2018-12-13 08:01:05 -08:00
Gerard Marull-Paretas
52719c0b7d Allow arbitrary USB args 2018-11-21 22:01:13 +01:00
27 changed files with 394 additions and 342 deletions

View File

@@ -12,3 +12,5 @@ Juanmi Taboada <juanmi@juanmitaboada.com> Juanmi Taboada <juanmi@juan
csoft2k <csoft2k@hotmail.com>
Sergio Pulgarin <sergio.pulgarin@gmail.com>
reck31 <rakesh.gunduka@gmail.com>
Alex Debiasio <alex.debiasio@thinkin.io> <alex.debiasio@studenti.unitn.it>
Brian 'Redbeard' Harrington <redbeard@dead-city.org>

View File

@@ -1,6 +1,7 @@
language: python
sudo: false
cache: pip
dist: xenial
git:
depth: 100000
addons:
@@ -11,7 +12,23 @@ env:
global:
- ESCPOS_CAPABILITIES_FILE=/home/travis/build/python-escpos/python-escpos/capabilities-data/dist/capabilities.json
matrix:
fast_finish: true
include:
- name: "Python 3.7 on Windows"
os: windows
language: shell
before_install:
- choco install python
- pip install tox codecov 'sphinx>=1.5.1'
env:
- TOXENV=py37
- PATH=/c/Python37:/c/Python37/Scripts:$PATH
- ESCPOS_CAPABILITIES_FILE=C:/Users/travis/build/python-escpos/python-escpos/capabilities-data/dist/capabilities.json
- name: "Python 3.7 on macOS"
os: osx
osx_image: xcode10.2
language: shell
env: TOXENV=py37 ESCPOS_CAPABILITIES_FILE=/Users/travis/build/python-escpos/python-escpos/capabilities-data/dist/capabilities.json
- python: 2.7
env: TOXENV=py27
- python: 3.4
@@ -22,25 +39,31 @@ 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: 2.7
- python: 3.6-dev
- python: 3.7-dev
- python: 3.8-dev
- python: nightly
- python: pypy3
- os: windows
- os: osx
before_install:
- pip install tox codecov 'sphinx>=1.5.1'
- ./doc/generate_authors.sh --check
@@ -63,4 +86,4 @@ deploy:
tags: true
repo: python-escpos/python-escpos
branch: master
condition: $TRAVIS_PYTHON_VERSION = "3.6"
condition: $TRAVIS_PYTHON_VERSION = "3.7"

View File

@@ -1,6 +1,9 @@
Ahmed Tahri
akeonly
Alex Debiasio
Asuki Kono
belono
Brian 'Redbeard' Harrington
Christoph Heuel
Cody (Quantified Code Bot)
csoft2k
@@ -8,8 +11,10 @@ Curtis // mashedkeyboard
Davis Goglin
Dean Rispin
Dmytro Katyukha
Gerard Marull-Paretas
Hark
Joel Lehtonen
Justin Vieira
kennedy
Kristi
ldos
@@ -19,9 +24,11 @@ Michael Billington
Michael Elsdörfer
mrwunderbar666
Nathan Bookham
Omer Akram
Patrick Kanzler
primax79
Qian Linfeng
Ramon Poca
reck31
Renato Lorenzi
Romain Porte

View File

@@ -1,6 +1,45 @@
*********
Changelog
*********
2019-06-19 - Version 3.0a6 - "Mistake not..."
---------------------------------------------
This release is the seventh alpha release of the new version 3.0.
Please be aware the the API is subject to change until v3.0 is
released.
changes
^^^^^^^
- fix inclusion of the capabilities-file
- execute CI jobs also on Windows and macOS-targets
- improve documentation
contributors
^^^^^^^^^^^^
- Patrick Kanzler
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

View File

@@ -1,6 +1,5 @@
include *.rst
include *.txt
include COPYING
include LICENSE
include INSTALL
include tox.ini

View File

@@ -59,12 +59,25 @@ The basic usage is:
from escpos.printer import Usb
""" Seiko Epson Corp. Receipt Printer (EPSON TM-T88III) """
p = Usb(0x04b8, 0x0202, 0, profile='TM-T88III')
p.text('Hello World\n')
p.image('logo.gif')
p.barcode('1324354657687', 'EAN13', 64, 2, '', '')
p = Usb(0x04b8, 0x0202, 0, profile="TM-T88III")
p.text("Hello World\n")
p.image("logo.gif")
p.barcode('4006381333931', '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('4006381333931', 'EAN13', 64, 2, '', '')
kitchen.cut()
The full project-documentation is available on `Read the Docs <https://python-escpos.readthedocs.io>`_.
Contributing

View File

@@ -2,38 +2,9 @@
TODO
****
Introduction
------------
python-escpos is the initial idea, from here we can start to build a
robust library to get most of the ESC/POS printers working with this
library.
Eventually, this library must be able to cover almost all the defined
models detailed in the ESC/POS Command Specification Manual.
Details
-------
What things are planned to work on?
Testing
~~~~~~~
* Test on many printers as possible (USB, Serial, Network)
* automate testing
Design
~~~~~~
* Add all those sequences which are not common, but part of the ESC/POS
Command Specifications.
* Port to Python 3
* Windows compatibility (hidapi instead libusb?)
* PDF417 support
* use something similar to the `capabilities` in escpos-php
Open points and issues of the project are tracked in the GitHub issues.
Some annotations still remain in the code and should be moved over time
into the issue tracker.
Todos in the codebase
~~~~~~~~~~~~~~~~~~~~~

View File

@@ -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('4006381333931','EAN13',64,2,'','')
# Cut paper
Epson.cut()
p.cut()
Configuration File
------------------
@@ -214,7 +217,7 @@ advantage of the fact that `_raw()` accepts binary strings.)
from escpos import printer
p = printer.Serial() # adapt this to your printer model
file = open('binary-blob.bin', 'rb') # read in the file containing your commands in binary-mode
file = open("binary-blob.bin", "rb") # read in the file containing your commands in binary-mode
data = file.read()
file.close()
@@ -273,8 +276,8 @@ This is probably best explained by an example:
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.text("This is my image:\n")
d.image("funny_cat.png")
d.cut()
# send code to printer

View File

@@ -1,14 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from escpos.printer import Usb
# Adapt to your needs
p = Usb(0x0416, 0x5011, profile='POS-5890')
p = Usb(0x0416, 0x5011, profile="POS-5890")
# Print software and then hardware barcode with the same content
p.soft_barcode('code39', '123456')

View File

@@ -1,17 +1,13 @@
# -*- coding: utf-8 -*-
"""Prints code page tables.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import print_function
import six
import sys
from escpos import printer
from escpos.constants import CODEPAGE_CHANGE, CTL_CR, CTL_FF, CTL_HT, CTL_LF, CTL_VT, ESC
import six
from escpos.constants import CODEPAGE_CHANGE, ESC, CTL_LF, CTL_FF, CTL_CR, CTL_HT, CTL_VT
def main():
@@ -21,9 +17,9 @@ def main():
for codepage in sys.argv[1:] or ['USA']:
dummy.set(height=2, width=2)
dummy._raw(codepage + '\n\n\n')
dummy._raw(codepage + "\n\n\n")
print_codepage(dummy, codepage)
dummy._raw('\n\n')
dummy._raw("\n\n")
dummy.cut()
@@ -34,22 +30,22 @@ def print_codepage(printer, codepage):
if codepage.isdigit():
codepage = int(codepage)
printer._raw(CODEPAGE_CHANGE + six.int2byte(codepage))
printer._raw('after')
printer._raw("after")
else:
printer.charcode(codepage)
sep = ''
sep = ""
# Table header
printer.set(font='b')
printer._raw(' {}\n'.format(sep.join(map(lambda s: hex(s)[2:], range(0, 16)))))
printer._raw(" {}\n".format(sep.join(map(lambda s: hex(s)[2:], range(0, 16)))))
printer.set()
# The table
for x in range(0, 16):
# First column
printer.set(font='b')
printer._raw('{} '.format(hex(x)[2:]))
printer._raw("{} ".format(hex(x)[2:]))
printer.set()
for y in range(0, 16):

View File

@@ -1,16 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import sys
from escpos.printer import Usb
def usage():
print('usage: qr_code.py <content>')
print("usage: qr_code.py <content>")
if __name__ == '__main__':
@@ -21,5 +15,5 @@ if __name__ == '__main__':
content = sys.argv[1]
# Adapt to your needs
p = Usb(0x0416, 0x5011, profile='POS-5890')
p = Usb(0x0416, 0x5011, profile="POS-5890")
p.qr(content, center=True)

View File

@@ -1,14 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from escpos.printer import Usb
# Adapt to your needs
p = Usb(0x0416, 0x5011, profile='POS-5890')
p = Usb(0x0416, 0x5011, profile="POS-5890")
# Some software barcodes
p.soft_barcode('code128', 'Hello')

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#!/usr/bin/python
# Adapted script from Adafruit
# Weather forecast for Raspberry Pi w/Adafruit Mini Thermal Printer.
@@ -12,38 +12,35 @@
# Icons taken from http://adamwhitcroft.com/climacons/
# Check out his github: https://github.com/AdamWhitcroft/climacons
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import calendar
import json
import os
import time
import urllib
from __future__ import print_function
from datetime import datetime
import calendar
import urllib
import json
import time
import os
from escpos.printer import Usb
""" Setting up the main pathing """
this_dir, this_filename = os.path.split(__file__)
GRAPHICS_PATH = os.path.join(this_dir, 'graphics/climacons/')
GRAPHICS_PATH = os.path.join(this_dir, "graphics/climacons/")
# Adapt to your needs
printer = Usb(0x0416, 0x5011, profile='POS-5890')
printer = Usb(0x0416, 0x5011, profile="POS-5890")
# You can get your API Key on www.darksky.net and register a dev account.
# Technically you can use any other weather service, of course :)
API_KEY = 'YOUR API KEY'
API_KEY = "YOUR API KEY"
LAT = '22.345490' # Your Location
LONG = '114.189945' # Your Location
LAT = "22.345490" # Your Location
LONG = "114.189945" # Your Location
def forecast_icon(idx):
icon = data['daily']['data'][idx]['icon']
image = GRAPHICS_PATH + icon + '.png'
image = GRAPHICS_PATH + icon + ".png"
return image
@@ -83,23 +80,23 @@ def forecast(idx):
def icon():
icon = data['currently']['icon']
image = GRAPHICS_PATH + icon + '.png'
image = GRAPHICS_PATH + icon + ".png"
return image
deg = ' C' # Degree symbol on thermal printer, need to find a better way to use a proper degree symbol
# if you want Fahrenheit change units= to 'us'
url = 'https://api.darksky.net/forecast/' + API_KEY + '/' + LAT + ',' + LONG + \
'?exclude=[alerts,minutely,hourly,flags]&units=si' # change last bit to 'us' for Fahrenheit
url = "https://api.darksky.net/forecast/" + API_KEY + "/" + LAT + "," + LONG + \
"?exclude=[alerts,minutely,hourly,flags]&units=si" # change last bit to 'us' for Fahrenheit
response = urllib.urlopen(url)
data = json.loads(response.read())
printer.print_and_feed(n=1)
printer.control('LF')
printer.control("LF")
printer.set(font='a', height=2, align='center', bold=True, double_height=True)
printer.text('Weather Forecast')
printer.text('\n')
printer.text("Weather Forecast")
printer.text("\n")
printer.set(align='center')
@@ -107,7 +104,7 @@ printer.set(align='center')
printer.set(font='a', height=2, align='center', bold=True, double_height=False)
printer.text('Current conditions: \n')
printer.image(icon())
printer.text('\n')
printer.text("\n")
printer.set(font='a', height=2, align='left', bold=False, double_height=False)
temp = data['currently']['temperature']
@@ -127,4 +124,4 @@ printer.text('Forecast: \n')
forecast(0)
forecast(1)
printer.cut()
printer.control('LF')
printer.control("LF")

View File

@@ -9,13 +9,4 @@ universal=1
[flake8]
exclude = .git,.tox,.github,.eggs,__pycache__,doc/conf.py,build,dist,capabilities-data,test,src/escpos/constants.py
max-line-length = 120
accept-encoding = utf-8, utf-16
require-code = True
# FI12 __future__ import "with_statement" missing
# FI15 __future__ import "generator_stop" missing
# FI16 __future__ import "nested_scopes" missing
# FI17 __future__ import "generators" missing
# FI5x __future__ import "xxx" present
# I101 Imported names are in the wrong order.
ignore = FI12,FI15,FI16,FI17,FI5, I101
copyright-check = True
# future-imports = absolute_import, division, print_function, unicode_literals # we are not there yet

View File

@@ -1,19 +1,12 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
# from __future__ import unicode_literals
import os
import sys
from setuptools import find_packages, setup
base_dir = os.path.dirname(__file__)
src_dir = os.path.join(base_dir, 'src')
src_dir = os.path.join(base_dir, "src")
# When executing the setup.py, we need to be able to import ourselves, this
# means that we need to add the src/ directory to the sys.path.
@@ -61,9 +54,9 @@ setup(
'receipt,',
],
platforms='any',
package_dir={'': 'src'},
packages=find_packages(where='src', exclude=['tests', 'tests.*']),
package_data={'': ['COPYING', 'src/escpos/capabilities.json']},
package_dir={"": "src"},
packages=find_packages(where="src", exclude=["tests", "tests.*"]),
package_data={'escpos': ['capabilities.json']},
include_package_data=True,
classifiers=[
'Development Status :: 4 - Beta',
@@ -72,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',
@@ -109,7 +100,7 @@ setup(
'nose',
'scripttest',
'mock',
'hypothesis!=3.56.9',
'hypothesis!=3.56.9,<4',
'flake8'
],
entry_points={

View File

@@ -7,7 +7,7 @@ from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
__all__ = ['constants', 'escpos', 'exceptions', 'printer']
__all__ = ["constants", "escpos", "exceptions", "printer"]
try:
from .version import version as __version__ # noqa

View File

@@ -1,23 +1,15 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import logging
import pickle
import platform
import re
import time
from os import environ, path
from tempfile import gettempdir
import pickle
import logging
import time
import six
import yaml
from tempfile import gettempdir
import platform
logging.basicConfig()
logger = logging.getLogger(__name__)
@@ -46,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)

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# PYTHON_ARGCOMPLETE_OK
""" CLI
@@ -16,15 +15,13 @@ from __future__ import print_function
from __future__ import unicode_literals
import argparse
import sys
try:
import argcomplete
except ImportError:
# this CLI works nevertheless without argcomplete
pass # noqa
import sys
import six
from . import config
from . import version
@@ -56,7 +53,7 @@ DEMO_FUNCTIONS = {
'barcodes_a': [
{'bc': 'UPC-A', 'code': '13243546576'},
{'bc': 'UPC-E', 'code': '132435'},
{'bc': 'EAN13', 'code': '1324354657687'},
{'bc': 'EAN13', 'code': '4006381333931'},
{'bc': 'EAN8', 'code': '1324354'},
{'bc': 'CODE39', 'code': 'TEST'},
{'bc': 'ITF', 'code': '55867492279103'},
@@ -65,13 +62,13 @@ DEMO_FUNCTIONS = {
'barcodes_b': [
{'bc': 'UPC-A', 'code': '13243546576', 'function_type': 'B'},
{'bc': 'UPC-E', 'code': '132435', 'function_type': 'B'},
{'bc': 'EAN13', 'code': '1324354657687', 'function_type': 'B'},
{'bc': 'EAN13', 'code': '4006381333931', 'function_type': 'B'},
{'bc': 'EAN8', 'code': '1324354', 'function_type': 'B'},
{'bc': 'CODE39', 'code': 'TEST', 'function_type': 'B'},
{'bc': 'ITF', 'code': '55867492279103', 'function_type': 'B'},
{'bc': 'NW7', 'code': 'A00000000A', 'function_type': 'B'},
{'bc': 'CODE93', 'code': 'A00000000A', 'function_type': 'B'},
{'bc': 'CODE93', 'code': '1324354657687', 'function_type': 'B'},
{'bc': 'CODE93', 'code': '4006381333931', 'function_type': 'B'},
{'bc': 'CODE128A', 'code': 'TEST', 'function_type': 'B'},
{'bc': 'CODE128B', 'code': 'TEST', 'function_type': 'B'},
{'bc': 'CODE128C', 'code': 'TEST', 'function_type': 'B'},
@@ -553,7 +550,7 @@ def main():
# print command with args
getattr(printer, target_command)(**command_arguments)
if target_command in REQUIRES_NEWLINE:
printer.text('\n')
printer.text("\n")
else:
command_arguments['printer'] = printer
globals()[target_command](**command_arguments)

View File

@@ -1,11 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from .capabilities import CAPABILITIES

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" ESC/POS configuration manager.
This module contains the implentations of abstract base class :py:class:`Config`.
@@ -11,13 +10,11 @@ from __future__ import print_function
from __future__ import unicode_literals
import os
import appdirs
import yaml
from . import exceptions
from . import printer
from . import exceptions
class Config(object):

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
# -*- coding: utf-8 -*-
""" Main class
@@ -15,41 +15,42 @@ from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import os
import qrcode
import textwrap
import six
import time
from abc import ABCMeta, abstractmethod # abstract base class support
from re import match as re_match
import barcode
from barcode.writer import ImageWriter
import qrcode
import os
import six
from .capabilities import BARCODE_B, get_profile
from .constants import ESC, GS, NUL, QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q
from .constants import QR_MODEL_1, QR_MODEL_2, QR_MICRO, BARCODE_TYPES, BARCODE_HEIGHT, BARCODE_WIDTH
from .constants import BARCODE_FONT_A, BARCODE_FONT_B, BARCODE_FORMATS
from .constants import BARCODE_TXT_OFF, BARCODE_TXT_BTH, BARCODE_TXT_ABV, BARCODE_TXT_BLW
from .constants import BARCODE_TYPES, BARCODE_HEIGHT, BARCODE_WIDTH
from .constants import CD_KICK_DEC_SEQUENCE, CD_KICK_5, CD_KICK_2, PAPER_FULL_CUT, PAPER_PART_CUT
from .constants import CTL_VT, CTL_CR, CTL_FF, CTL_LF, CTL_SET_HT, PANEL_BUTTON_OFF, PANEL_BUTTON_ON
from .constants import ESC, GS, NUL, QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q
from .constants import HW_INIT, HW_RESET, HW_SELECT
from .constants import TXT_SIZE, TXT_NORMAL
from .constants import SET_FONT
from .constants import LINESPACING_FUNCS, LINESPACING_RESET
from .constants import LINE_DISPLAY_OPEN, LINE_DISPLAY_CLEAR, LINE_DISPLAY_CLOSE
from .constants import QR_MODEL_1, QR_MODEL_2, QR_MICRO
from .constants import RT_MASK_ONLINE, RT_STATUS_ONLINE
from .constants import RT_STATUS_PAPER, RT_MASK_PAPER, RT_MASK_LOWPAPER, RT_MASK_NOPAPER
from .constants import SET_FONT
from .constants import TXT_SIZE, TXT_NORMAL
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_CR, CTL_FF, CTL_LF, CTL_SET_HT, PANEL_BUTTON_OFF, PANEL_BUTTON_ON
from .constants import TXT_STYLE
from .exceptions import BarcodeCodeError, BarcodeSizeError, BarcodeTypeError
from .exceptions import CashDrawerError, ImageWidthError
from .exceptions import SetVariableError, TabPosError
from .image import EscposImage
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
from .exceptions import ImageWidthError
from .magicencode import MagicEncode
from abc import ABCMeta, abstractmethod # abstract base class support
from escpos.image import EscposImage
from escpos.capabilities import get_profile, BARCODE_B
@six.add_metaclass(ABCMeta)
class Escpos(object):
@@ -88,7 +89,7 @@ class Escpos(object):
"""
raise NotImplementedError()
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=960, center=False):
""" Print an image
@@ -140,14 +141,14 @@ class Escpos(object):
fragment_height=fragment_height)
return
if impl == 'bitImageRaster':
if impl == "bitImageRaster":
# GS v 0, raster format bit image
density_byte = (0 if high_density_horizontal else 1) + (0 if high_density_vertical else 2)
header = GS + b'v0' + six.int2byte(density_byte) + self._int_low_high(im.width_bytes, 2) +\
header = GS + b"v0" + six.int2byte(density_byte) + self._int_low_high(im.width_bytes, 2) +\
self._int_low_high(im.height, 2)
self._raw(header + im.to_raster_format())
if impl == 'graphics':
if impl == "graphics":
# GS ( L raster format graphics
img_header = self._int_low_high(im.width, 2) + self._int_low_high(im.height, 2)
tone = b'0'
@@ -159,14 +160,14 @@ class Escpos(object):
self._image_send_graphics_data(b'0', b'p', header + raster_data)
self._image_send_graphics_data(b'0', b'2', b'')
if impl == 'bitImageColumn':
if impl == "bitImageColumn":
# ESC *, column format bit image
density_byte = (1 if high_density_horizontal else 0) + (32 if high_density_vertical else 0)
header = ESC + b'*' + six.int2byte(density_byte) + self._int_low_high(im.width, 2)
outp = [ESC + b'3' + six.int2byte(16)] # Adjust line-feed size
header = ESC + b"*" + six.int2byte(density_byte) + self._int_low_high(im.width, 2)
outp = [ESC + b"3" + six.int2byte(16)] # Adjust line-feed size
for blob in im.to_column_format(high_density_vertical):
outp.append(header + blob + b'\n')
outp.append(ESC + b'2') # Reset line-feed size
outp.append(header + blob + b"\n")
outp.append(ESC + b"2") # Reset line-feed size
self._raw(b''.join(outp))
def _image_send_graphics_data(self, m, fn, data):
@@ -181,7 +182,7 @@ class Escpos(object):
self._raw(GS + b'(L' + header + m + fn + data)
def qr(self, content, ec=QR_ECLEVEL_L, size=3, model=QR_MODEL_2,
native=False, center=False, impl='bitImageRaster'):
native=False, center=False, impl="bitImageRaster"):
""" Print QR Code for the provided string
:param content: The content of the code. Numeric data will be more efficiently compacted.
@@ -194,21 +195,22 @@ class Escpos(object):
:param native: True to render the code on the printer, False to render the code as an image and send it to the
printer (Default)
:param center: Centers the code *default:* False
:param impl: Image-printing-implementation, refer to :meth:`.image()` for details
"""
# Basic validation
if ec not in [QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q]:
raise ValueError('Invalid error correction level')
raise ValueError("Invalid error correction level")
if not 1 <= size <= 16:
raise ValueError('Invalid block size (must be 1-16)')
raise ValueError("Invalid block size (must be 1-16)")
if model not in [QR_MODEL_1, QR_MODEL_2, QR_MICRO]:
raise ValueError('Invalid QR model (must be one of QR_MODEL_1, QR_MODEL_2, QR_MICRO)')
if content == '':
raise ValueError("Invalid QR model (must be one of QR_MODEL_1, QR_MODEL_2, QR_MICRO)")
if content == "":
# Handle edge case by printing nothing.
return
if not native:
# Map ESC/POS error correction levels to python 'qrcode' library constant and render to an image
if model != QR_MODEL_2:
raise ValueError('Invalid QR model for qrlib rendering (must be QR_MODEL_2)')
raise ValueError("Invalid QR model for qrlib rendering (must be QR_MODEL_2)")
python_qr_ec = {
QR_ECLEVEL_H: qrcode.constants.ERROR_CORRECT_H,
QR_ECLEVEL_L: qrcode.constants.ERROR_CORRECT_L,
@@ -219,7 +221,7 @@ class Escpos(object):
qr_code.add_data(content)
qr_code.make(fit=True)
qr_img = qr_code.make_image()
im = qr_img._img.convert('RGB')
im = qr_img._img.convert("RGB")
# Convert the RGB image in printable image
self.text('\n')
@@ -229,7 +231,7 @@ class Escpos(object):
return
if center:
raise NotImplementedError('Centering not implemented for native QR rendering')
raise NotImplementedError("Centering not implemented for native QR rendering")
# Native 2D code printing
cn = b'1' # Code type for QR code
@@ -252,7 +254,7 @@ class Escpos(object):
:param m: Modifier/variant for function. Often '0' where used.
"""
if len(m) > 1 or len(cn) != 1 or len(fn) != 1:
raise ValueError('cn and fn must be one byte each.')
raise ValueError("cn and fn must be one byte each.")
header = self._int_low_high(len(data) + len(m) + 2, 2)
self._raw(GS + b'(k' + header + cn + fn + m + data)
@@ -265,16 +267,16 @@ class Escpos(object):
"""
max_input = (256 << (out_bytes * 8) - 1)
if not 1 <= out_bytes <= 4:
raise ValueError('Can only output 1-4 bytes')
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} bytes'.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)
inp_number //= 256
return outp
def charcode(self, code='AUTO'):
def charcode(self, code="AUTO"):
""" Set Character Code Table
Sets the control sequence from ``CHARCODE`` in :py:mod:`escpos.constants` as active. It will be sent with
@@ -284,7 +286,7 @@ class Escpos(object):
:param code: Name of CharCode
:raises: :py:exc:`~escpos.exceptions.CharCodeError`
"""
if code.upper() == 'AUTO':
if code.upper() == "AUTO":
self.magic.force_encoding(False)
else:
self.magic.force_encoding(code)
@@ -305,8 +307,8 @@ class Escpos(object):
.. todo:: For fixed-length standards with mandatory checksum (EAN, UPC),
compute and add the checksum automatically if missing.
:param bc: barcode format, see :py:func`~escpos.Escpos.barcode`
:param code: alphanumeric data to be printed as bar code, see :py:func`~escpos.Escpos.barcode`
:param bc: barcode format, see :py:meth:`.barcode()`
:param code: alphanumeric data to be printed as bar code, see :py:meth:`.barcode()`
:return: bool
"""
if bc not in BARCODE_FORMATS:
@@ -315,7 +317,7 @@ class Escpos(object):
bounds, regex = BARCODE_FORMATS[bc]
return any(bound[0] <= len(code) <= bound[1] for bound in bounds) and re_match(regex, code)
def barcode(self, code, bc, height=64, width=3, pos='BELOW', font='A',
def barcode(self, code, bc, height=64, width=3, pos="BELOW", font="A",
align_ct=True, function_type=None, check=True):
""" Print Barcode
@@ -393,7 +395,7 @@ class Escpos(object):
*default*: A
:param check: If this parameter is True, the barcode format will be checked to ensure it meets the bc
requirements as defigned in the esc/pos documentation. See py:func:`~escpos.Escpos.check_barcode`
requirements as defigned in the esc/pos documentation. See :py:meth:`.check_barcode()`
for more information. *default*: True.
:raises: :py:exc:`~escpos.exceptions.BarcodeSizeError`,
@@ -408,21 +410,21 @@ class Escpos(object):
if bc in BARCODE_TYPES['B']:
if not self.profile.supports(BARCODE_B):
raise BarcodeTypeError((
"Barcode type '{bc}' not supported for "
'the current printer profile').format(bc=bc))
"Barcode type '{bc} not supported for "
"the current printer profile").format(bc=bc))
function_type = 'B'
else:
raise BarcodeTypeError((
"Barcode type '{bc}' is not valid").format(bc=bc))
"Barcode type '{bc} is not valid").format(bc=bc))
bc_types = BARCODE_TYPES[function_type.upper()]
if bc.upper() not in bc_types.keys():
raise BarcodeTypeError((
"Barcode '{bc}' not valid for barcode function type "
'{function_type}').format(
"{function_type}").format(
bc=bc,
function_type=function_type,)
)
function_type=function_type,
))
if check and not self.check_barcode(bc, code):
raise BarcodeCodeError((
@@ -438,30 +440,30 @@ class Escpos(object):
if 1 <= height <= 255:
self._raw(BARCODE_HEIGHT + six.int2byte(height))
else:
raise BarcodeSizeError('height = {height}'.format(height=height))
raise BarcodeSizeError("height = {height}".format(height=height))
# Width
if 2 <= width <= 6:
self._raw(BARCODE_WIDTH + six.int2byte(width))
else:
raise BarcodeSizeError('width = {width}'.format(width=width))
raise BarcodeSizeError("width = {width}".format(width=width))
# Font
if font.upper() == 'B':
if font.upper() == "B":
self._raw(BARCODE_FONT_B)
else: # DEFAULT FONT: A
self._raw(BARCODE_FONT_A)
# Position
if pos.upper() == 'OFF':
if pos.upper() == "OFF":
self._raw(BARCODE_TXT_OFF)
elif pos.upper() == 'BOTH':
elif pos.upper() == "BOTH":
self._raw(BARCODE_TXT_BTH)
elif pos.upper() == 'ABOVE':
elif pos.upper() == "ABOVE":
self._raw(BARCODE_TXT_ABV)
else: # DEFAULT POSITION: BELOW
self._raw(BARCODE_TXT_BLW)
self._raw(bc_types[bc.upper()])
if function_type.upper() == 'B':
if function_type.upper() == "B":
self._raw(six.int2byte(len(code)))
# Print Code
@@ -470,7 +472,7 @@ class Escpos(object):
else:
raise BarcodeCodeError()
if function_type.upper() == 'A':
if function_type.upper() == "A":
self._raw(NUL)
def soft_barcode(self, barcode_type, data, impl='bitImageColumn',
@@ -488,7 +490,7 @@ class Escpos(object):
barcode_class = barcode.get_barcode_class(barcode_type)
my_code = barcode_class(data, writer=image_writer)
with open(os.devnull, 'wb') as nullfile:
with open(os.devnull, "wb") as nullfile:
my_code.write(nullfile, {
'module_height': module_height,
'module_width': module_width,
@@ -590,7 +592,8 @@ class Escpos(object):
"""
if custom_size:
if 1 <= width <= 8 and 1 <= height <= 8 and isinstance(width, int) and isinstance(height, int):
if 1 <= width <= 8 and 1 <= height <= 8 and isinstance(width, int) and\
isinstance(height, int):
size_byte = TXT_STYLE['width'][width] + TXT_STYLE['height'][height]
self._raw(TXT_SIZE + six.int2byte(size_byte))
else:
@@ -638,11 +641,12 @@ class Escpos(object):
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')
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')
raise ValueError("spacing must be a int between 0 and 85 when divisor is 60")
self._raw(LINESPACING_FUNCS[divisor] + six.int2byte(spacing))
@@ -670,12 +674,12 @@ class Escpos(object):
if mode not in ('FULL', 'PART'):
raise ValueError("Mode must be one of ('FULL', 'PART')")
if mode == 'PART':
if mode == "PART":
if self.profile.supports('paperPartCut'):
self._raw(PAPER_PART_CUT)
elif self.profile.supports('paperFullCut'):
self._raw(PAPER_FULL_CUT)
elif mode == 'FULL':
elif mode == "FULL":
if self.profile.supports('paperFullCut'):
self._raw(PAPER_FULL_CUT)
elif self.profile.supports('paperPartCut'):
@@ -745,11 +749,11 @@ class Escpos(object):
* SELECT
* RESET
"""
if hw.upper() == 'INIT':
if hw.upper() == "INIT":
self._raw(HW_INIT)
elif hw.upper() == 'SELECT':
elif hw.upper() == "SELECT":
self._raw(HW_SELECT)
elif hw.upper() == 'RESET':
elif hw.upper() == "RESET":
self._raw(HW_RESET)
else: # DEFAULT: DOES NOTHING
pass
@@ -764,9 +768,9 @@ class Escpos(object):
"""
if 0 <= n <= 255:
# ESC d n
self._raw(ESC + b'd' + six.int2byte(n))
self._raw(ESC + b"d" + six.int2byte(n))
else:
raise ValueError('n must be betwen 0 and 255')
raise ValueError("n must be betwen 0 and 255")
def control(self, ctl, count=5, tab_size=8):
""" Feed control sequences
@@ -784,13 +788,13 @@ class Escpos(object):
:raises: :py:exc:`~escpos.exceptions.TabPosError`
"""
# Set position
if ctl.upper() == 'LF':
if ctl.upper() == "LF":
self._raw(CTL_LF)
elif ctl.upper() == 'FF':
elif ctl.upper() == "FF":
self._raw(CTL_FF)
elif ctl.upper() == 'CR':
elif ctl.upper() == "CR":
self._raw(CTL_CR)
elif ctl.upper() == 'HT':
elif ctl.upper() == "HT":
if not (0 <= count <= 32 and
1 <= tab_size <= 255 and
count * tab_size < 256):
@@ -801,7 +805,7 @@ class Escpos(object):
for iterator in range(1, count):
self._raw(six.int2byte(iterator * tab_size))
self._raw(NUL)
elif ctl.upper() == 'VT':
elif ctl.upper() == "VT":
self._raw(CTL_VT)
def panel_buttons(self, enable=True):
@@ -927,16 +931,16 @@ class EscposIO(object):
elif isinstance(text, list) or isinstance(text, tuple):
lines = text
else:
lines = ['{0}'.format(text), ]
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))
self.printer.text(u"{0}\n".format(line))
else:
self.printer.text('{0}\n'.format(line))
self.printer.text("{0}\n".format(line))
def close(self):
""" called upon closing the `with`-statement

View File

@@ -51,13 +51,13 @@ class BarcodeTypeError(Error):
one of those specified in :py:meth:`escpos.escpos.Escpos.barcode`.
The returned error code is `10`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 10
def __str__(self):
return 'No Barcode type is defined ({msg})'.format(msg=self.msg)
return "No Barcode type is defined ({msg})".format(msg=self.msg)
class BarcodeSizeError(Error):
@@ -67,13 +67,13 @@ class BarcodeSizeError(Error):
The size of the barcode has to be in the range that is specified in :py:meth:`escpos.escpos.Escpos.barcode`.
The resulting returncode is `20`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 20
def __str__(self):
return 'Barcode size is out of range ({msg})'.format(msg=self.msg)
return "Barcode size is out of range ({msg})".format(msg=self.msg)
class BarcodeCodeError(Error):
@@ -83,13 +83,13 @@ class BarcodeCodeError(Error):
was True and the check failed.
The returncode for this exception is `30`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 30
def __str__(self):
return 'No Barcode code was supplied ({msg})'.format(msg=self.msg)
return "No Barcode code was supplied ({msg})".format(msg=self.msg)
class ImageSizeError(Error):
@@ -97,7 +97,7 @@ class ImageSizeError(Error):
The returncode for this exception is `40`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 40
@@ -111,13 +111,13 @@ class ImageWidthError(Error):
The return code for this exception is `41`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 41
def __str__(self):
return 'Image width is too large ({msg})'.format(msg=self.msg)
return "Image width is too large ({msg})".format(msg=self.msg)
class TextError(Error):
@@ -126,13 +126,13 @@ class TextError(Error):
This exception is raised when an empty string is passed to :py:meth:`escpos.escpos.Escpos.text`.
The returncode for this exception is `50`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 50
def __str__(self):
return 'Text string must be supplied to the text() method ({msg})'.format(msg=self.msg)
return "Text string must be supplied to the text() method ({msg})".format(msg=self.msg)
class CashDrawerError(Error):
@@ -141,13 +141,13 @@ class CashDrawerError(Error):
A valid pin number has to be passed onto the method :py:meth:`escpos.escpos.Escpos.cashdraw`.
The returncode for this exception is `60`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 60
def __str__(self):
return 'Valid pin must be set to send pulse ({msg})'.format(msg=self.msg)
return "Valid pin must be set to send pulse ({msg})".format(msg=self.msg)
class TabPosError(Error):
@@ -157,13 +157,13 @@ class TabPosError(Error):
This exception is raised by :py:meth:`escpos.escpos.Escpos.control`.
The returncode for this exception is `70`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 70
def __str__(self):
return 'Valid tab positions must be in the range 0 to 16 ({msg})'.format(msg=self.msg)
return "Valid tab positions must be in the range 0 to 16 ({msg})".format(msg=self.msg)
class CharCodeError(Error):
@@ -172,13 +172,13 @@ class CharCodeError(Error):
The supplied charcode-name in :py:meth:`escpos.escpos.Escpos.charcode` is unknown.
Ths returncode for this exception is `80`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 80
def __str__(self):
return 'Valid char code must be set ({msg})'.format(msg=self.msg)
return "Valid char code must be set ({msg})".format(msg=self.msg)
class USBNotFoundError(Error):
@@ -187,13 +187,13 @@ class USBNotFoundError(Error):
The USB device seems to be not plugged in.
Ths returncode for this exception is `90`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 90
def __str__(self):
return 'USB device not found ({msg})'.format(msg=self.msg)
return "USB device not found ({msg})".format(msg=self.msg)
class SetVariableError(Error):
@@ -202,13 +202,13 @@ class SetVariableError(Error):
Check set variables against minimum and maximum values
Ths returncode for this exception is `100`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 100
def __str__(self):
return 'Set variable out of range ({msg})'.format(msg=self.msg)
return "Set variable out of range ({msg})".format(msg=self.msg)
# Configuration errors
@@ -219,13 +219,13 @@ class ConfigNotFoundError(Error):
The default or passed configuration file could not be read
Ths returncode for this exception is `200`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 200
def __str__(self):
return 'Configuration not found ({msg})'.format(msg=self.msg)
return "Configuration not found ({msg})".format(msg=self.msg)
class ConfigSyntaxError(Error):
@@ -234,13 +234,13 @@ class ConfigSyntaxError(Error):
The syntax is incorrect
Ths returncode for this exception is `210`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 210
def __str__(self):
return 'Configuration syntax is invalid ({msg})'.format(msg=self.msg)
return "Configuration syntax is invalid ({msg})".format(msg=self.msg)
class ConfigSectionMissingError(Error):
@@ -249,10 +249,10 @@ class ConfigSectionMissingError(Error):
The part of the config asked for doesn't exist in the loaded configuration
Ths returncode for this exception is `220`.
"""
def __init__(self, msg=''):
def __init__(self, msg=""):
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 220
def __str__(self):
return 'Configuration section is missing ({msg})'.format(msg=self.msg)
return "Configuration section is missing ({msg})".format(msg=self.msg)

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" Image format handling class
This module contains the image format handler :py:class:`EscposImage`.
@@ -15,7 +14,6 @@ from __future__ import print_function
from __future__ import unicode_literals
import math
from PIL import Image, ImageOps
@@ -44,14 +42,14 @@ class EscposImage(object):
# Convert to white RGB background, paste over white background
# to strip alpha.
img_original = img_original.convert('RGBA')
im = Image.new('RGB', img_original.size, (255, 255, 255))
im = Image.new("RGB", img_original.size, (255, 255, 255))
im.paste(img_original, mask=img_original.split()[3])
# Convert down to greyscale
im = im.convert('L')
im = im.convert("L")
# Invert: Only works on 'L' images
im = ImageOps.invert(im)
# Pure black and white
self._im = im.convert('1')
self._im = im.convert("1")
@property
def width(self):
@@ -107,7 +105,7 @@ class EscposImage(object):
:param fragment_height: height of fragment
:return: list of PIL objects
"""
passes = int(math.ceil(self.height // fragment_height))
passes = int(math.ceil(self.height/fragment_height))
fragments = []
for n in range(0, passes):
left = 0
@@ -127,7 +125,7 @@ class EscposImage(object):
old_width, height = self._im.size
new_size = (max_width, height)
new_im = Image.new('1', new_size)
new_im = Image.new("1", new_size)
paste_x = int((max_width - old_width) / 2)
new_im.paste(self._im, (paste_x, 0))

View File

@@ -33,7 +33,7 @@ def encode_katakana(text):
# TODO doesn't this discard all that is not in the map? Can we be sure that the input does contain only
# encodable characters? We could at least throw an exception if encoding is not possible.
pass
return b''.join(encoded)
return b"".join(encoded)
TXT_ENC_KATAKANA_MAP = {

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
# -*- coding: utf-8 -*-
""" Magic Encode
@@ -18,12 +18,10 @@ from __future__ import print_function
from __future__ import unicode_literals
from builtins import bytes
import six
from .codepages import CodePages
from .constants import CODEPAGE_CHANGE
from .exceptions import Error
from .codepages import CodePages
import six
class Encoder(object):
@@ -64,7 +62,7 @@ class Encoder(object):
raise ValueError((
'Encoding "{}" cannot be used for the current profile. '
'Valid encodings are: {}'
).format(encoding, ','.join(self.codepages.keys())))
).format(encoding, ','.join(self.codepages.keys())))
return encoding
@staticmethod
@@ -77,11 +75,11 @@ class Encoder(object):
"""
codepage = CodePages.get_encoding(encoding)
if 'data' in codepage:
encodable_chars = list(''.join(codepage['data']))
encodable_chars = list("".join(codepage['data']))
assert(len(encodable_chars) == 128)
return encodable_chars
elif 'python_encode' in codepage:
encodable_chars = [u' '] * 128
encodable_chars = [u" "] * 128
for i in range(0, 128):
codepoint = i + 128
try:
@@ -282,7 +280,7 @@ class MagicEncode(object):
def write_with_encoding(self, encoding, text):
if text is not None and type(text) is not six.text_type:
raise Error('The supplied text has to be unicode, but is of type {type}.'.format(
raise Error("The supplied text has to be unicode, but is of type {type}.".format(
type=type(text)
))

View File

@@ -13,12 +13,10 @@ from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import socket
import serial
import usb.core
import usb.util
import serial
import socket
from .escpos import Escpos
from .exceptions import USBNotFoundError
@@ -36,47 +34,64 @@ 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.')
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()
self.device.reset()
except usb.core.USBError as e:
print('Could not set configuration: {0}'.format(str(e)))
print("Could not set configuration: {0}".format(str(e)))
def _raw(self, msg):
""" Print any command sent in raw format
@@ -109,7 +124,7 @@ class Serial(Escpos):
"""
def __init__(self, devfile='/dev/ttyS0', baudrate=9600, bytesize=8, timeout=1,
def __init__(self, devfile="/dev/ttyS0", baudrate=9600, bytesize=8, timeout=1,
parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE,
xonxoff=False, dsrdtr=True, *args, **kwargs):
"""
@@ -145,9 +160,9 @@ class Serial(Escpos):
xonxoff=self.xonxoff, dsrdtr=self.dsrdtr)
if self.device is not None:
print('Serial printer enabled')
print("Serial printer enabled")
else:
print('Unable to open serial printer on: {0}'.format(str(self.devfile)))
print("Unable to open serial printer on: {0}".format(str(self.devfile)))
def _raw(self, msg):
""" Print any command sent in raw format
@@ -211,7 +226,7 @@ class Network(Escpos):
self.device.connect((self.host, self.port))
if self.device is None:
print('Could not open socket for {0}'.format(self.host))
print("Could not open socket for {0}".format(self.host))
def _raw(self, msg):
""" Print any command sent in raw format
@@ -221,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:
@@ -242,7 +262,7 @@ class File(Escpos):
"""
def __init__(self, devfile='/dev/usb/lp0', auto_flush=True, *args, **kwargs):
def __init__(self, devfile="/dev/usb/lp0", auto_flush=True, *args, **kwargs):
"""
:param devfile: Device file under dev filesystem
@@ -255,10 +275,10 @@ class File(Escpos):
def open(self):
""" Open system file """
self.device = open(self.devfile, 'wb')
self.device = open(self.devfile, "wb")
if self.device is None:
print('Could not open the specified file {0}'.format(self.devfile))
print("Could not open the specified file {0}".format(self.devfile))
def flush(self):
""" Flush printing content """
@@ -324,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)

11
tox.ini
View File

@@ -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_*
@@ -25,12 +25,7 @@ commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
[testenv:flake8]
basepython = python
# TODO add flake8-print after adding logging
# TODO add flake8-future
# TODO add flake8-docstrings
deps = flake8
flake8-coding
flake8-copyright
flake8-future-import
flake8-quotes
flake8-import-order
commands = flake8