Compare commits
5 Commits
update-rea
...
debian/jes
Author | SHA1 | Date | |
---|---|---|---|
![]() |
83185bf2f3 | ||
![]() |
57ce8389d7 | ||
![]() |
b2ce102ca5 | ||
![]() |
d50c407edb | ||
![]() |
2a2ba9a0e2 |
1
.gitignore
vendored
@@ -20,7 +20,6 @@ dist/
|
|||||||
.coverage
|
.coverage
|
||||||
src/escpos/version.py
|
src/escpos/version.py
|
||||||
.hypothesis
|
.hypothesis
|
||||||
.pytest_cache/
|
|
||||||
|
|
||||||
# testing temporary directories
|
# testing temporary directories
|
||||||
test/test-cli-output/
|
test/test-cli-output/
|
||||||
|
1
.gitmodules
vendored
@@ -1,4 +1,3 @@
|
|||||||
[submodule "capabilities-data"]
|
[submodule "capabilities-data"]
|
||||||
path = capabilities-data
|
path = capabilities-data
|
||||||
url = https://github.com/receipt-print-hq/escpos-printer-db.git
|
url = https://github.com/receipt-print-hq/escpos-printer-db.git
|
||||||
branch = master
|
|
||||||
|
15
.mailmap
@@ -1,15 +0,0 @@
|
|||||||
<dev@pkanzler.de> <patrick.kanzler@fablab.fau.de>
|
|
||||||
<manpaz@gmail.com> <manpaz@bashlinux.com>
|
|
||||||
Manuel F Martinez <manpaz@gmail.com> manpaz <manpaz@bashlinux.com>
|
|
||||||
<emailofdavis@gmail.com> <davis.goglin@oregonicecream.com>
|
|
||||||
Davis Goglin <emailofdavis@gmail.com> davisgoglin <emailofdavis@gmail.com>
|
|
||||||
Michael Billington <michael.billington@gmail.com> Michael <michael.billington@gmail.com>
|
|
||||||
Cody (Quantified Code Bot) <cody@quantifiedcode.com> Cody <cody@quantifiedcode.com>
|
|
||||||
Renato Lorenzi <renato.lorenzi@senior.com.br> Renato.Lorenzi <renato.lorenzi@senior.com.br>
|
|
||||||
Ahmed Tahri <nyuubi.10@gmail.com> TAHRI Ahmed <nyuubi.10@gmail.com>
|
|
||||||
Michael Elsdörfer <michael@elsdoerfer.com> Michael Elsdörfer <michael@elsdoerfer.info>
|
|
||||||
Juanmi Taboada <juanmi@juanmitaboada.com> Juanmi Taboada <juanmi@juanmitaboada.com>
|
|
||||||
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>
|
|
@@ -1,10 +0,0 @@
|
|||||||
version: 2
|
|
||||||
formats:
|
|
||||||
- pdf
|
|
||||||
- epub
|
|
||||||
python:
|
|
||||||
version: 3.7
|
|
||||||
install:
|
|
||||||
- requirements: doc/requirements.txt
|
|
||||||
- method: setuptools
|
|
||||||
path: .
|
|
46
.travis.yml
@@ -1,36 +1,16 @@
|
|||||||
language: python
|
language: python
|
||||||
sudo: false
|
sudo: false
|
||||||
cache: pip
|
cache: pip
|
||||||
dist: xenial
|
|
||||||
git:
|
|
||||||
depth: 100000
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
packages:
|
packages:
|
||||||
- graphviz
|
- graphviz
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- ESCPOS_CAPABILITIES_FILE=/home/travis/build/python-escpos/python-escpos/capabilities-data/dist/capabilities.json
|
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
|
||||||
include:
|
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
|
- python: 2.7
|
||||||
env: TOXENV=py27
|
env: TOXENV=py27
|
||||||
|
- python: 3.3
|
||||||
|
env: TOXENV=py33
|
||||||
- python: 3.4
|
- python: 3.4
|
||||||
env: TOXENV=py34
|
env: TOXENV=py34
|
||||||
- python: 3.5
|
- python: 3.5
|
||||||
@@ -39,34 +19,24 @@ matrix:
|
|||||||
env: TOXENV=py36
|
env: TOXENV=py36
|
||||||
- python: 3.6-dev
|
- python: 3.6-dev
|
||||||
env: TOXENV=py36
|
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
|
- python: nightly
|
||||||
env: TOXENV=py38
|
env: TOXENV=py37
|
||||||
- python: pypy
|
- python: pypy
|
||||||
env: TOXENV=pypy
|
env: TOXENV=pypy
|
||||||
- python: pypy3
|
- python: pypy3
|
||||||
env: TOXENV=pypy3
|
env: TOXENV=pypy3
|
||||||
- python: 3.7
|
- python: 2.7
|
||||||
env: TOXENV=docs
|
env: TOXENV=docs
|
||||||
- python: 3.7
|
- python: 2.7
|
||||||
|
env: TOXENV=flake8
|
||||||
|
- python: 3.6
|
||||||
env: TOXENV=flake8
|
env: TOXENV=flake8
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- python: 2.7
|
|
||||||
- python: 3.6-dev
|
- python: 3.6-dev
|
||||||
- python: 3.7-dev
|
|
||||||
- python: 3.8-dev
|
|
||||||
- python: nightly
|
- python: nightly
|
||||||
- python: pypy3
|
- python: pypy3
|
||||||
- os: windows
|
|
||||||
- os: osx
|
|
||||||
before_install:
|
before_install:
|
||||||
- pip install tox codecov 'sphinx>=1.5.1'
|
- pip install tox codecov 'sphinx>=1.5.1'
|
||||||
- ./doc/generate_authors.sh --check
|
|
||||||
script:
|
script:
|
||||||
- tox
|
- tox
|
||||||
- codecov
|
- codecov
|
||||||
@@ -86,4 +56,4 @@ deploy:
|
|||||||
tags: true
|
tags: true
|
||||||
repo: python-escpos/python-escpos
|
repo: python-escpos/python-escpos
|
||||||
branch: master
|
branch: master
|
||||||
condition: $TRAVIS_PYTHON_VERSION = "3.7"
|
condition: $TRAVIS_PYTHON_VERSION = "3.5"
|
||||||
|
39
AUTHORS
@@ -1,39 +0,0 @@
|
|||||||
Ahmed Tahri
|
|
||||||
akeonly
|
|
||||||
Alex Debiasio
|
|
||||||
Asuki Kono
|
|
||||||
belono
|
|
||||||
Christoph Heuel
|
|
||||||
Cody (Quantified Code Bot)
|
|
||||||
csoft2k
|
|
||||||
Curtis // mashedkeyboard
|
|
||||||
Davis Goglin
|
|
||||||
Dean Rispin
|
|
||||||
Dmytro Katyukha
|
|
||||||
Gerard Marull-Paretas
|
|
||||||
Hark
|
|
||||||
Joel Lehtonen
|
|
||||||
Justin Vieira
|
|
||||||
kennedy
|
|
||||||
Kristi
|
|
||||||
ldos
|
|
||||||
Lucy Linder
|
|
||||||
Manuel F Martinez
|
|
||||||
Michael Billington
|
|
||||||
Michael Elsdörfer
|
|
||||||
mrwunderbar666
|
|
||||||
Nathan Bookham
|
|
||||||
Omer Akram
|
|
||||||
Patrick Kanzler
|
|
||||||
primax79
|
|
||||||
Qian Linfeng
|
|
||||||
Ramon Poca
|
|
||||||
reck31
|
|
||||||
Renato Lorenzi
|
|
||||||
Romain Porte
|
|
||||||
Sam Cheng
|
|
||||||
Sergio Pulgarin
|
|
||||||
Stephan Sokolow
|
|
||||||
Thijs Triemstra
|
|
||||||
Thomas van den Berg
|
|
||||||
ysuolmai
|
|
@@ -1,102 +1,6 @@
|
|||||||
*********
|
*********
|
||||||
Changelog
|
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
|
|
||||||
be aware that the API will still change until v3.0 is released.
|
|
||||||
|
|
||||||
changes
|
|
||||||
^^^^^^^
|
|
||||||
- raise exception when TypeError occurs in cashdraw (#268)
|
|
||||||
- Feature/clear content in dummy printer (#271)
|
|
||||||
- fix is_online() (#282)
|
|
||||||
- improve documentation
|
|
||||||
- Modified submodule to always pull from master branch (#283)
|
|
||||||
- parameter for implementation of nonnative qrcode (#289)
|
|
||||||
- improve platform independence (#296)
|
|
||||||
|
|
||||||
contributors
|
|
||||||
^^^^^^^^^^^^
|
|
||||||
- Christoph Heuel
|
|
||||||
- Patrick Kanzler
|
|
||||||
- kennedy
|
|
||||||
- primax79
|
|
||||||
- reck31
|
|
||||||
- Thijs Triemstra
|
|
||||||
|
|
||||||
2017-10-08 - Version 3.0a3 - "Just Testing"
|
|
||||||
-------------------------------------------
|
|
||||||
This release is the fourth alpha release of the new version 3.0. Please
|
|
||||||
be aware that the API will still change until v3.0 is released.
|
|
||||||
|
|
||||||
changes
|
|
||||||
^^^^^^^
|
|
||||||
- minor changes in documentation, tests and examples
|
|
||||||
- pickle capabilities for faster startup
|
|
||||||
- first implementation of centering images and QR
|
|
||||||
- check barcodes based on regex
|
|
||||||
|
|
||||||
contributors
|
|
||||||
^^^^^^^^^^^^
|
|
||||||
- Patrick Kanzler
|
|
||||||
- Lucy Linder
|
|
||||||
- Romain Porte
|
|
||||||
- Sergio Pulgarin
|
|
||||||
|
|
||||||
2017-08-04 - 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 and Serial
|
|
||||||
- ensure QR codes have a large enough border
|
|
||||||
- make feed for cut optional
|
|
||||||
- fix the behavior of horizontal tabs
|
|
||||||
- added test script for hard an soft barcodes
|
|
||||||
- implemented paper sensor querying command
|
|
||||||
- added weather forecast example script
|
|
||||||
- added a method for simpler newlines
|
|
||||||
|
|
||||||
contributors
|
|
||||||
^^^^^^^^^^^^
|
|
||||||
- csoft2k
|
|
||||||
- Patrick Kanzler
|
|
||||||
- mrwunderbar666
|
|
||||||
- Romain Porte
|
|
||||||
- Ahmed Tahri
|
|
||||||
|
|
||||||
2017-03-29 - Version 3.0a1 - "Headcrash"
|
2017-03-29 - Version 3.0a1 - "Headcrash"
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
@@ -12,16 +12,6 @@ The pull requests and issues will be prefilled with templates. Please fill in yo
|
|||||||
This project uses `semantic versioning <http://semver.org/>`_ and tries to adhere to the proposed rules as
|
This project uses `semantic versioning <http://semver.org/>`_ and tries to adhere to the proposed rules as
|
||||||
well as possible.
|
well as possible.
|
||||||
|
|
||||||
Author-list
|
|
||||||
-----------
|
|
||||||
|
|
||||||
This project keeps a list of authors. This can be auto-generated by calling `./doc/generate-authors.sh`.
|
|
||||||
When contributing the first time, please include a commit with the output of this script in place.
|
|
||||||
Otherwise the integration-check will fail.
|
|
||||||
|
|
||||||
When you change your username or mail-address, please also update the `.mailmap` and the authors-list.
|
|
||||||
You can find a good documentation on the mapping-feature in the `documentation of git-shortlog <https://git-scm.com/docs/git-shortlog#_mapping_authors>`_.
|
|
||||||
|
|
||||||
Style-Guide
|
Style-Guide
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
@@ -48,11 +38,9 @@ Often you can achieve compatibility quite easily with a tool from the `six`-pack
|
|||||||
|
|
||||||
PEP8
|
PEP8
|
||||||
^^^^
|
^^^^
|
||||||
The entire codebase adheres to the rules of PEP8.
|
This is not yet consequently done in every piece of code, but please try to ensure
|
||||||
These rules are enforced by running `flake8` in the integration-checks.
|
that your code honors PEP8.
|
||||||
Please adhere to these rules as your contribution can only be merged if the check succeeds.
|
The checks by Landscape and QuantifiedCode that run on every PR will provide you with hints.
|
||||||
You can use flake8 or similar tools locally in order to check your code.
|
|
||||||
Apart from that the travis-log and the check by Landscape will provide you with hints.
|
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
^^^
|
^^^
|
||||||
|
37
README.rst
@@ -6,6 +6,10 @@ python-escpos - Python library to manipulate ESC/POS Printers
|
|||||||
:target: https://travis-ci.org/python-escpos/python-escpos
|
:target: https://travis-ci.org/python-escpos/python-escpos
|
||||||
:alt: Continous Integration
|
:alt: Continous Integration
|
||||||
|
|
||||||
|
.. image:: https://www.quantifiedcode.com/api/v1/project/95748b89a3974700800b85e4ed3d32c4/badge.svg
|
||||||
|
:target: https://www.quantifiedcode.com/app/project/95748b89a3974700800b85e4ed3d32c4
|
||||||
|
:alt: Code issues
|
||||||
|
|
||||||
.. image:: https://landscape.io/github/python-escpos/python-escpos/master/landscape.svg?style=flat
|
.. image:: https://landscape.io/github/python-escpos/python-escpos/master/landscape.svg?style=flat
|
||||||
:target: https://landscape.io/github/python-escpos/python-escpos/master
|
:target: https://landscape.io/github/python-escpos/python-escpos/master
|
||||||
:alt: Code Health
|
:alt: Code Health
|
||||||
@@ -43,11 +47,10 @@ Dependencies
|
|||||||
|
|
||||||
This library makes use of:
|
This library makes use of:
|
||||||
|
|
||||||
* `pyusb <https://github.com/walac/pyusb>`_ for USB-printers
|
* pyusb for USB-printers
|
||||||
* `Pillow <https://github.com/python-pillow/Pillow>`_ for image printing
|
* Pillow for image printing
|
||||||
* `qrcode <https://github.com/lincolnloop/python-qrcode>`_ for the generation of QR-codes
|
* qrcode for the generation of QR-codes
|
||||||
* `pyserial <https://github.com/pyserial/pyserial>`_ for serial printers
|
* pyserial for serial printers
|
||||||
* `viivakoodi <https://github.com/kxepal/viivakoodi>`_ for the generation of barcodes
|
|
||||||
|
|
||||||
Documentation and Usage
|
Documentation and Usage
|
||||||
-----------------------
|
-----------------------
|
||||||
@@ -65,31 +68,9 @@ The basic usage is:
|
|||||||
p.barcode('1324354657687', 'EAN13', 64, 2, '', '')
|
p.barcode('1324354657687', 'EAN13', 64, 2, '', '')
|
||||||
p.cut()
|
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 <https://python-escpos.readthedocs.io>`_.
|
The full project-documentation is available on `Read the Docs <https://python-escpos.readthedocs.io>`_.
|
||||||
|
|
||||||
Contributing
|
Contributing
|
||||||
------------
|
------------
|
||||||
|
|
||||||
This project is open for any contribution! Please see `CONTRIBUTING.rst <http://python-escpos.readthedocs.io/en/latest/dev/contributing.html>`_ for more information.
|
This project is open for any contribution! Please see CONTRIBUTING.rst for more information.
|
||||||
|
|
||||||
|
|
||||||
Disclaimer
|
|
||||||
----------
|
|
||||||
|
|
||||||
None of the vendors cited in this project agree or endorse any of the patterns or implementations.
|
|
||||||
Its names are used only to maintain context.
|
|
||||||
|
|
||||||
|
|
||||||
|
94
debian/changelog
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
python-escpos (1.0.8-3) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Christoph Heuel ]
|
||||||
|
* Fix text wrapping error after image
|
||||||
|
|
||||||
|
[ Patrick Kanzler ]
|
||||||
|
* moved .hgignore to .gitignore
|
||||||
|
* REFACTOR chained boolean expression in escpos
|
||||||
|
* REFACTOR use new-style class for Escpos
|
||||||
|
* REFACTOR style and PEP8, fixes #66
|
||||||
|
* REFACTOR do not shadow built-ins
|
||||||
|
* ADD requirements.txt and requirements to setup.py
|
||||||
|
* DOC, IMPROVE improve docstrings and add abstract method _raw to Escpos
|
||||||
|
* FIX constant definition for PC1252
|
||||||
|
|
||||||
|
-- Christoph Heuel <mail@christoph-heuel.net> Sun, 13 Dec 2015 14:27:27 +0100
|
||||||
|
|
||||||
|
python-escpos (1.0.8-2) unstable; urgency=low
|
||||||
|
|
||||||
|
* Imported source
|
||||||
|
* First debianization
|
||||||
|
|
||||||
|
-- Christoph Heuel <mail@christoph-heuel.net> Sat, 12 Dec 2015 19:13:33 +0100
|
||||||
|
|
||||||
|
python-escpos (1.0.8-1) unstable; urgency=low
|
||||||
|
|
||||||
|
[ Manuel F Martinez ]
|
||||||
|
* Added donation message
|
||||||
|
|
||||||
|
[ Joel Lehtonen ]
|
||||||
|
* Support for images vertically longer than 256 pixels
|
||||||
|
|
||||||
|
[ Christoph Heuel ]
|
||||||
|
* Fix mixed tabs/space error
|
||||||
|
|
||||||
|
[ Hark ]
|
||||||
|
* Prevent crash when using libusb0 printers
|
||||||
|
|
||||||
|
[ Manuel F Martinez ]
|
||||||
|
* Updated README and documentation
|
||||||
|
|
||||||
|
[ Christoph Heuel ]
|
||||||
|
* Add flush function
|
||||||
|
* Debian packaging
|
||||||
|
|
||||||
|
-- Manuel F Martinez <manpaz@gmail.com> Sat, 12 Dec 2015 20:59:53 +0100
|
||||||
|
|
||||||
|
python-escpos (1.0.7-1) unstable; urgency=low
|
||||||
|
|
||||||
|
[ Kristi ]
|
||||||
|
* Raising the right error when wrong charcode is used
|
||||||
|
|
||||||
|
[ Christoph Heuel ]
|
||||||
|
* After running 2to3 tool
|
||||||
|
* Fix for string operation
|
||||||
|
* Integer is needed, not float
|
||||||
|
* Add text wrapping
|
||||||
|
|
||||||
|
[ Manuel F Martinez ]
|
||||||
|
* Updated URL for the documentation
|
||||||
|
* Updated documentation URL to local wiki
|
||||||
|
* Updated setup URLs
|
||||||
|
|
||||||
|
[ Christoph Heuel ]
|
||||||
|
* Introduce new direct_image
|
||||||
|
|
||||||
|
[ Manuel F Martinez ]
|
||||||
|
* Fixed License version mismatch
|
||||||
|
|
||||||
|
[ Christoph Heuel ]
|
||||||
|
* Use unhexlify
|
||||||
|
* Hexlify text
|
||||||
|
|
||||||
|
[ Manuel F Martinez ]
|
||||||
|
* Updated accordingly to the wiki
|
||||||
|
|
||||||
|
[ ldos ]
|
||||||
|
* Extended params for serial printers
|
||||||
|
|
||||||
|
[ Manuel F Martinez ]
|
||||||
|
* Fixed issues with transparent images
|
||||||
|
* Updated project version
|
||||||
|
|
||||||
|
-- Manuel F Martinez <manpaz@gmail.com> Sat, 12 Dec 2015 20:59:53 +0100
|
||||||
|
|
||||||
|
python-escpos (1.0.4-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Manuel F Martinez ]
|
||||||
|
* Added density support
|
||||||
|
* Added quad support
|
||||||
|
* fixed code tabulators
|
||||||
|
* Updated version
|
||||||
|
|
||||||
|
-- Manuel F Martinez <manpaz@gmail.com> Sat, 12 Dec 2015 20:59:53 +0100
|
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
9
|
55
debian/control
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
Source: python-escpos
|
||||||
|
Section: unknown
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Christoph Heuel <mail@christoph-heuel.net>
|
||||||
|
Build-Depends: debhelper (>= 9), dh-python
|
||||||
|
Standards-Version: 3.9.5
|
||||||
|
Homepage: https://github.com/braveheuel/python-escpos
|
||||||
|
#Vcs-Git: git@github.com:braveheuel/python-escpos.git
|
||||||
|
#Vcs-Browser: https://github.com/braveheuel/python-escpos
|
||||||
|
|
||||||
|
Package: python-escpos
|
||||||
|
Architecture: all
|
||||||
|
Depends: ${misc:Depends}
|
||||||
|
Description: Python library to manipulate ESC/POS Printers (Python 2)
|
||||||
|
Python ESC/POS is a library which lets the user have access to all
|
||||||
|
those printers handled by ESC/POS commands, as defined by Epson,
|
||||||
|
from a Python application.
|
||||||
|
.
|
||||||
|
The standard usage is send raw text to the printer, but in also
|
||||||
|
helps the user to enhance the experience with those printers by
|
||||||
|
facilitating the bar code printing in many different standards,
|
||||||
|
as well as manipulating images so they can be printed as brand
|
||||||
|
logo or any other usage images migh have.
|
||||||
|
.
|
||||||
|
Text can be aligned/justified and fonts can be changed by size,
|
||||||
|
type and weight.
|
||||||
|
.
|
||||||
|
Also, this module handles some hardware functionalities like, cut
|
||||||
|
paper, carrier return, printer reset and others concerned to the
|
||||||
|
carriage alignment.
|
||||||
|
.
|
||||||
|
This package covers Python 2 code.
|
||||||
|
|
||||||
|
Package: python3-escpos
|
||||||
|
Architecture: all
|
||||||
|
Depends: ${misc:Depends}
|
||||||
|
Description: Python library to manipulate ESC/POS Printers (Python 3)
|
||||||
|
Python ESC/POS is a library which lets the user have access to all
|
||||||
|
those printers handled by ESC/POS commands, as defined by Epson,
|
||||||
|
from a Python application.
|
||||||
|
.
|
||||||
|
The standard usage is send raw text to the printer, but in also
|
||||||
|
helps the user to enhance the experience with those printers by
|
||||||
|
facilitating the bar code printing in many different standards,
|
||||||
|
as well as manipulating images so they can be printed as brand
|
||||||
|
logo or any other usage images migh have.
|
||||||
|
.
|
||||||
|
Text can be aligned/justified and fonts can be changed by size,
|
||||||
|
type and weight.
|
||||||
|
.
|
||||||
|
Also, this module handles some hardware functionalities like, cut
|
||||||
|
paper, carrier return, printer reset and others concerned to the
|
||||||
|
carriage alignment.
|
||||||
|
.
|
||||||
|
This package covers Python 3 code.
|
26
debian/copyright
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: python-escpos
|
||||||
|
Source: https://github.com/manpaz/python-escpos
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2015 Manuel F Martinez <manpaz@bashlinux.com>
|
||||||
|
License: GPL v3
|
||||||
|
|
||||||
|
Files: debian/*
|
||||||
|
Copyright: 2015 Christoph Heuel <mail@christoph-heuel.net>
|
||||||
|
License: GPL-2+
|
||||||
|
This package is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
.
|
||||||
|
This package is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
.
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
.
|
||||||
|
On Debian systems, the complete text of the GNU General
|
||||||
|
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
|
1
debian/docs
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
README.rst
|
6
debian/gbp.conf
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
upstream-branch=master
|
||||||
|
upstream-tree = tag
|
||||||
|
debian-branch = debian/jessie
|
||||||
|
upstream-tag = v%(version)s
|
||||||
|
debian-tag = v%(version)s
|
28
debian/rules
vendored
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
# See debhelper(7) (uncomment to enable)
|
||||||
|
# output every command that modifies files on the build system.
|
||||||
|
#DH_VERBOSE = 1
|
||||||
|
|
||||||
|
# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/*
|
||||||
|
DPKG_EXPORT_BUILDFLAGS = 1
|
||||||
|
include /usr/share/dpkg/default.mk
|
||||||
|
|
||||||
|
# see FEATURE AREAS in dpkg-buildflags(1)
|
||||||
|
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||||
|
|
||||||
|
# see ENVIRONMENT in dpkg-buildflags(1)
|
||||||
|
# package maintainers to append CFLAGS
|
||||||
|
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
|
||||||
|
# package maintainers to append LDFLAGS
|
||||||
|
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
||||||
|
|
||||||
|
|
||||||
|
# main packaging script based on dh7 syntax
|
||||||
|
%:
|
||||||
|
dh $@ --with python2,python3 --buildsystem=pybuild
|
||||||
|
|
||||||
|
# debmake generated override targets
|
||||||
|
# This is example for Cmake (See http://bugs.debian.org/641051 )
|
||||||
|
#override_dh_auto_configure:
|
||||||
|
# dh_auto_configure -- \
|
||||||
|
# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)
|
1
debian/source/format
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.0 (quilt)
|
@@ -1,19 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
GENLIST=$(git shortlog -s -n | cut -f2 | sort -f)
|
|
||||||
AUTHORSFILE="$(dirname $0)/../AUTHORS"
|
|
||||||
TEMPAUTHORSFILE="/tmp/python-escpos-authorsfile"
|
|
||||||
|
|
||||||
if [ "$#" -eq 1 ]
|
|
||||||
then
|
|
||||||
echo "$GENLIST">$TEMPAUTHORSFILE
|
|
||||||
echo "\nAuthorsfile in version control:\n"
|
|
||||||
cat $AUTHORSFILE
|
|
||||||
echo "\nNew authorsfile:\n"
|
|
||||||
cat $TEMPAUTHORSFILE
|
|
||||||
echo "\nUsing diff on files...\n"
|
|
||||||
diff -q --from-file $AUTHORSFILE $TEMPAUTHORSFILE
|
|
||||||
else
|
|
||||||
echo "$GENLIST">$AUTHORSFILE
|
|
||||||
fi
|
|
||||||
|
|
@@ -5,4 +5,3 @@ pyserial
|
|||||||
sphinx-rtd-theme
|
sphinx-rtd-theme
|
||||||
setuptools-scm
|
setuptools-scm
|
||||||
docutils>=0.12
|
docutils>=0.12
|
||||||
viivakoodi
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
*****
|
*****
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
:Last Reviewed: 2017-06-10
|
|
||||||
|
|
||||||
Define your printer
|
Define your printer
|
||||||
-------------------
|
-------------------
|
||||||
@@ -44,7 +43,7 @@ to have and the second yields the "Output Endpoint" address.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
p = printer.Usb(0x04b8,0x0202)
|
Epson = printer.Usb(0x04b8,0x0202)
|
||||||
|
|
||||||
By default the "Interface" number is "0" and the "Output Endpoint"
|
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
|
address is "0x01". If you have other values then you can define them on
|
||||||
@@ -55,7 +54,7 @@ on 0x81 and out\_ep=0x02, then the printer definition should look like:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
p = printer.Usb(0x1a2b,0x1a2b,0,0x81,0x02)
|
Generic = printer.Usb(0x1a2b,0x1a2b,0,0x81,0x02)
|
||||||
|
|
||||||
Network printer
|
Network printer
|
||||||
^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^
|
||||||
@@ -67,7 +66,7 @@ IP by DHCP or you set it manually.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
p = printer.Network("192.168.1.99")
|
Epson = printer.Network("192.168.1.99")
|
||||||
|
|
||||||
Serial printer
|
Serial printer
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
@@ -81,10 +80,7 @@ to.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
p = printer.Serial("/dev/tty0")
|
Epson = printer.Serial("/dev/tty0")
|
||||||
|
|
||||||
# on a Windows OS serial devices are typically accessible as COM
|
|
||||||
p = printer.Serial("COM1")
|
|
||||||
|
|
||||||
Other printers
|
Other printers
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
@@ -96,7 +92,7 @@ passing the device node name.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
p = printer.File("/dev/usb/lp1")
|
Epson = printer.File("/dev/usb/lp1")
|
||||||
|
|
||||||
The default is "/dev/usb/lp0", so if the printer is located on that
|
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.
|
node, then you don't necessary need to pass the node name.
|
||||||
@@ -111,17 +107,17 @@ on a USB interface.
|
|||||||
|
|
||||||
from escpos import *
|
from escpos import *
|
||||||
""" Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """
|
""" Seiko Epson Corp. Receipt Printer M129 Definitions (EPSON TM-T88IV) """
|
||||||
p = printer.Usb(0x04b8,0x0202)
|
Epson = printer.Usb(0x04b8,0x0202)
|
||||||
# Print text
|
# Print text
|
||||||
p.text("Hello World\n")
|
Epson.text("Hello World\n")
|
||||||
# Print image
|
# Print image
|
||||||
p.image("logo.gif")
|
Epson.image("logo.gif")
|
||||||
# Print QR Code
|
# Print QR Code
|
||||||
p.qr("You can readme from your smartphone")
|
Epson.qr("You can readme from your smartphone")
|
||||||
# Print barcode
|
# Print barcode
|
||||||
p.barcode('1324354657687','EAN13',64,2,'','')
|
Epson.barcode('1324354657687','EAN13',64,2,'','')
|
||||||
# Cut paper
|
# Cut paper
|
||||||
p.cut()
|
Epson.cut()
|
||||||
|
|
||||||
Configuration File
|
Configuration File
|
||||||
------------------
|
------------------
|
||||||
@@ -161,12 +157,10 @@ The printer section
|
|||||||
|
|
||||||
The ``printer`` configuration section defines a default printer to create.
|
The ``printer`` configuration section defines a default printer to create.
|
||||||
|
|
||||||
The only required paramter is ``type``. The value of this has to be one of the
|
The only required paramter is ``type``. The value of this should be one of the
|
||||||
printers defined in :doc:`/user/printers`.
|
printers defined in :doc:`/user/printers`.
|
||||||
|
|
||||||
The rest of the given parameters will be passed on to the initialization of the printer class.
|
The rest of the parameters are whatever you want to pass to the printer.
|
||||||
Use these to overwrite the default values as specified in :doc:`/user/printers`.
|
|
||||||
This implies that the parameters have to match the parameter-names of the respective printer class.
|
|
||||||
|
|
||||||
An example file printer::
|
An example file printer::
|
||||||
|
|
||||||
@@ -177,19 +171,10 @@ An example file printer::
|
|||||||
And for a network printer::
|
And for a network printer::
|
||||||
|
|
||||||
printer:
|
printer:
|
||||||
type: Network
|
type: network
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 9000
|
port: 9000
|
||||||
|
|
||||||
An USB-printer could be defined by::
|
|
||||||
|
|
||||||
printer:
|
|
||||||
type: Usb
|
|
||||||
idVendor: 0x1234
|
|
||||||
idProduct: 0x5678
|
|
||||||
in_ep: 0x66
|
|
||||||
out_ep: 0x01
|
|
||||||
|
|
||||||
Printing text right
|
Printing text right
|
||||||
-------------------
|
-------------------
|
||||||
Python-escpos is designed to accept unicode. So make sure that you use ``u'strings'`` or import ``unicode_literals``
|
Python-escpos is designed to accept unicode. So make sure that you use ``u'strings'`` or import ``unicode_literals``
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
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')
|
|
@@ -37,14 +37,14 @@ def print_codepage(printer, codepage):
|
|||||||
sep = ""
|
sep = ""
|
||||||
|
|
||||||
# Table header
|
# Table header
|
||||||
printer.set(font='b')
|
printer.set(text_type='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()
|
printer.set()
|
||||||
|
|
||||||
# The table
|
# The table
|
||||||
for x in range(0, 16):
|
for x in range(0, 16):
|
||||||
# First column
|
# First column
|
||||||
printer.set(font='b')
|
printer.set(text_type='B')
|
||||||
printer._raw("{} ".format(hex(x)[2:]))
|
printer._raw("{} ".format(hex(x)[2:]))
|
||||||
printer.set()
|
printer.set()
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 4.7 KiB |
@@ -1,10 +0,0 @@
|
|||||||
# Climacons by Adam Whitcroft
|
|
||||||
|
|
||||||
75 climatically categorised pictographs for web and UI design by [@adamwhitcroft](http://www.twitter.com/#!/adamwhitcroft).
|
|
||||||
|
|
||||||
Visit the [Climacons](http://adamwhitcroft.com/climacons/) website for more information.
|
|
||||||
|
|
||||||
Visit [Adam Whitcroft on GitHub](https://github.com/AdamWhitcroft)
|
|
||||||
|
|
||||||
## License
|
|
||||||
You are free to use any of the Climacons Icons (the "icons") in any personal or commercial work without obligation of payment (monetary or otherwise) or attribution, however a credit for the work would be appreciated. **Do not** redistribute or sell and **do not** claim creative credit. Intellectual property rights are not transferred with the download of the icons.
|
|
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,19 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
from escpos.printer import Usb
|
|
||||||
|
|
||||||
|
|
||||||
def usage():
|
|
||||||
print("usage: qr_code.py <content>")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
if len(sys.argv) != 2:
|
|
||||||
usage()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
content = sys.argv[1]
|
|
||||||
|
|
||||||
# Adapt to your needs
|
|
||||||
p = Usb(0x0416, 0x5011, profile="POS-5890")
|
|
||||||
p.qr(content, center=True)
|
|
@@ -1,9 +0,0 @@
|
|||||||
from escpos.printer import Usb
|
|
||||||
|
|
||||||
|
|
||||||
# Adapt to your needs
|
|
||||||
p = Usb(0x0416, 0x5011, profile="POS-5890")
|
|
||||||
|
|
||||||
# Some software barcodes
|
|
||||||
p.soft_barcode('code128', 'Hello')
|
|
||||||
p.soft_barcode('code39', '123456')
|
|
@@ -1,127 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
|
|
||||||
# Adapted script from Adafruit
|
|
||||||
# Weather forecast for Raspberry Pi w/Adafruit Mini Thermal Printer.
|
|
||||||
# Retrieves data from DarkSky.net's API, prints current conditions and
|
|
||||||
# forecasts for next two days.
|
|
||||||
# Weather example using nice bitmaps.
|
|
||||||
# Written by Adafruit Industries. MIT license.
|
|
||||||
# Adapted and enhanced for escpos library by MrWunderbar666
|
|
||||||
|
|
||||||
# Icons taken from http://adamwhitcroft.com/climacons/
|
|
||||||
# Check out his github: https://github.com/AdamWhitcroft/climacons
|
|
||||||
|
|
||||||
|
|
||||||
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/")
|
|
||||||
|
|
||||||
# Adapt to your needs
|
|
||||||
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"
|
|
||||||
|
|
||||||
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"
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
# Dumps one forecast line to the printer
|
|
||||||
def forecast(idx):
|
|
||||||
date = datetime.fromtimestamp(int(data['daily']['data'][idx]['time']))
|
|
||||||
day = calendar.day_name[date.weekday()]
|
|
||||||
lo = data['daily']['data'][idx]['temperatureMin']
|
|
||||||
hi = data['daily']['data'][idx]['temperatureMax']
|
|
||||||
cond = data['daily']['data'][idx]['summary']
|
|
||||||
print(date)
|
|
||||||
print(day)
|
|
||||||
print(lo)
|
|
||||||
print(hi)
|
|
||||||
print(cond)
|
|
||||||
time.sleep(1)
|
|
||||||
printer.set(
|
|
||||||
font='a',
|
|
||||||
height=2,
|
|
||||||
align='left',
|
|
||||||
bold=False,
|
|
||||||
double_height=False)
|
|
||||||
printer.text(day + ' \n ')
|
|
||||||
time.sleep(5) # Sleep to prevent printer buffer overflow
|
|
||||||
printer.text('\n')
|
|
||||||
printer.image(forecast_icon(idx))
|
|
||||||
printer.text('low ' + str(lo))
|
|
||||||
printer.text(deg)
|
|
||||||
printer.text('\n')
|
|
||||||
printer.text(' high ' + str(hi))
|
|
||||||
printer.text(deg)
|
|
||||||
printer.text('\n')
|
|
||||||
# take care of pesky unicode dash
|
|
||||||
printer.text(cond.replace(u'\u2013', '-').encode('utf-8'))
|
|
||||||
printer.text('\n \n')
|
|
||||||
|
|
||||||
|
|
||||||
def icon():
|
|
||||||
icon = data['currently']['icon']
|
|
||||||
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
|
|
||||||
response = urllib.urlopen(url)
|
|
||||||
data = json.loads(response.read())
|
|
||||||
|
|
||||||
printer.print_and_feed(n=1)
|
|
||||||
printer.control("LF")
|
|
||||||
printer.set(font='a', height=2, align='center', bold=True, double_height=True)
|
|
||||||
printer.text("Weather Forecast")
|
|
||||||
printer.text("\n")
|
|
||||||
printer.set(align='center')
|
|
||||||
|
|
||||||
|
|
||||||
# Print current conditions
|
|
||||||
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.set(font='a', height=2, align='left', bold=False, double_height=False)
|
|
||||||
temp = data['currently']['temperature']
|
|
||||||
cond = data['currently']['summary']
|
|
||||||
printer.text(temp)
|
|
||||||
printer.text(' ')
|
|
||||||
printer.text(deg)
|
|
||||||
printer.text(' ')
|
|
||||||
printer.text('\n')
|
|
||||||
printer.text('Sky: ' + cond)
|
|
||||||
printer.text('\n')
|
|
||||||
printer.text('\n')
|
|
||||||
|
|
||||||
# Print forecast
|
|
||||||
printer.set(font='a', height=2, align='center', bold=True, double_height=False)
|
|
||||||
printer.text('Forecast: \n')
|
|
||||||
forecast(0)
|
|
||||||
forecast(1)
|
|
||||||
printer.cut()
|
|
||||||
printer.control("LF")
|
|
7
readthedocs.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
formats:
|
||||||
|
- pdf
|
||||||
|
- epub
|
||||||
|
requirements_file: doc/requirements.txt
|
||||||
|
python:
|
||||||
|
version: 2
|
||||||
|
setup_py_install: true
|
44
setup.py
@@ -3,6 +3,7 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
from setuptools.command.test import test as test_command
|
||||||
|
|
||||||
|
|
||||||
base_dir = os.path.dirname(__file__)
|
base_dir = os.path.dirname(__file__)
|
||||||
@@ -18,6 +19,33 @@ def read(fname):
|
|||||||
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
||||||
|
|
||||||
|
|
||||||
|
class Tox(test_command):
|
||||||
|
"""proxy class that enables tox to be run with setup.py test"""
|
||||||
|
user_options = [('tox-args=', 'a', "Arguments to pass to tox")]
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
"""initialize the user-options"""
|
||||||
|
test_command.initialize_options(self)
|
||||||
|
self.tox_args = None
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
"""finalize user-options"""
|
||||||
|
test_command.finalize_options(self)
|
||||||
|
self.test_args = []
|
||||||
|
self.test_suite = True
|
||||||
|
|
||||||
|
def run_tests(self):
|
||||||
|
"""run tox and pass on user-options"""
|
||||||
|
# import here, cause outside the eggs aren't loaded
|
||||||
|
import tox
|
||||||
|
import shlex
|
||||||
|
args = self.tox_args
|
||||||
|
if args:
|
||||||
|
args = shlex.split(self.tox_args)
|
||||||
|
errno = tox.cmdline(args=args)
|
||||||
|
sys.exit(errno)
|
||||||
|
|
||||||
|
|
||||||
setuptools_scm_template = """\
|
setuptools_scm_template = """\
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
# file generated by setuptools_scm
|
# file generated by setuptools_scm
|
||||||
@@ -40,6 +68,7 @@ setup(
|
|||||||
url='https://github.com/python-escpos/python-escpos',
|
url='https://github.com/python-escpos/python-escpos',
|
||||||
download_url='https://github.com/python-escpos/python-escpos/archive/master.zip',
|
download_url='https://github.com/python-escpos/python-escpos/archive/master.zip',
|
||||||
description='Python library to manipulate ESC/POS Printers',
|
description='Python library to manipulate ESC/POS Printers',
|
||||||
|
bugtrack_url='https://github.com/python-escpos/python-escpos/issues',
|
||||||
license='MIT',
|
license='MIT',
|
||||||
long_description=read('README.rst'),
|
long_description=read('README.rst'),
|
||||||
author='Manuel F Martinez and others',
|
author='Manuel F Martinez and others',
|
||||||
@@ -65,11 +94,12 @@ setup(
|
|||||||
'License :: OSI Approved :: MIT License',
|
'License :: OSI Approved :: MIT License',
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
|
'Programming Language :: Python :: 2',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 3',
|
||||||
|
'Programming Language :: Python :: 3.3',
|
||||||
'Programming Language :: Python :: 3.4',
|
'Programming Language :: Python :: 3.4',
|
||||||
'Programming Language :: Python :: 3.5',
|
'Programming Language :: Python :: 3.5',
|
||||||
'Programming Language :: Python :: 3.6',
|
|
||||||
'Programming Language :: Python :: 3.7',
|
|
||||||
'Programming Language :: Python :: Implementation :: CPython',
|
'Programming Language :: Python :: Implementation :: CPython',
|
||||||
'Programming Language :: Python :: Implementation :: PyPy',
|
'Programming Language :: Python :: Implementation :: PyPy',
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||||
@@ -82,11 +112,10 @@ setup(
|
|||||||
'pyserial',
|
'pyserial',
|
||||||
'six',
|
'six',
|
||||||
'appdirs',
|
'appdirs',
|
||||||
'PyYAML',
|
'pyyaml',
|
||||||
'argparse',
|
'argparse',
|
||||||
'argcomplete',
|
'argcomplete',
|
||||||
'future',
|
'future'
|
||||||
'viivakoodi>=0.8'
|
|
||||||
],
|
],
|
||||||
setup_requires=[
|
setup_requires=[
|
||||||
'setuptools_scm',
|
'setuptools_scm',
|
||||||
@@ -94,15 +123,16 @@ setup(
|
|||||||
tests_require=[
|
tests_require=[
|
||||||
'jaconv',
|
'jaconv',
|
||||||
'tox',
|
'tox',
|
||||||
'pytest!=3.2.0,!=3.3.0',
|
'pytest',
|
||||||
'pytest-cov',
|
'pytest-cov',
|
||||||
'pytest-mock',
|
'pytest-mock',
|
||||||
'nose',
|
'nose',
|
||||||
'scripttest',
|
'scripttest',
|
||||||
'mock',
|
'mock',
|
||||||
'hypothesis!=3.56.9,<4',
|
'hypothesis',
|
||||||
'flake8'
|
'flake8'
|
||||||
],
|
],
|
||||||
|
cmdclass={'test': Tox},
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'python-escpos = escpos.cli:main'
|
'python-escpos = escpos.cli:main'
|
||||||
|
@@ -1,54 +1,22 @@
|
|||||||
import re
|
import re
|
||||||
from os import environ, path
|
|
||||||
import pickle
|
|
||||||
import logging
|
|
||||||
import time
|
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
from os import environ, path
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from tempfile import gettempdir
|
|
||||||
import platform
|
|
||||||
|
|
||||||
logging.basicConfig()
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
pickle_dir = environ.get('ESCPOS_CAPABILITIES_PICKLE_DIR', gettempdir())
|
|
||||||
pickle_path = path.join(pickle_dir, '{v}.capabilities.pickle'.format(v=platform.python_version()))
|
|
||||||
capabilities_path = environ.get(
|
|
||||||
'ESCPOS_CAPABILITIES_FILE',
|
|
||||||
path.join(path.dirname(__file__), 'capabilities.json'))
|
|
||||||
|
|
||||||
# Load external printer database
|
# Load external printer database
|
||||||
t0 = time.time()
|
if 'ESCPOS_CAPABILITIES_FILE' in environ:
|
||||||
logger.debug('Using capabilities from file: %s', capabilities_path)
|
file_path = environ['ESCPOS_CAPABILITIES_FILE']
|
||||||
if path.exists(pickle_path):
|
|
||||||
if path.getmtime(capabilities_path) > path.getmtime(pickle_path):
|
|
||||||
logger.debug('Found a more recent capabilities file')
|
|
||||||
full_load = True
|
|
||||||
else:
|
else:
|
||||||
full_load = False
|
file_path = path.join(path.dirname(__file__), 'capabilities.json')
|
||||||
logger.debug('Loading capabilities from pickle in %s', pickle_path)
|
|
||||||
with open(pickle_path, 'rb') as cf:
|
|
||||||
CAPABILITIES = pickle.load(cf)
|
|
||||||
else:
|
|
||||||
logger.debug('Capabilities pickle file not found: %s', pickle_path)
|
|
||||||
full_load = True
|
|
||||||
|
|
||||||
if full_load:
|
|
||||||
logger.debug('Loading and pickling capabilities')
|
|
||||||
with open(capabilities_path) as cp, open(pickle_path, 'wb') as pp:
|
|
||||||
CAPABILITIES = yaml.safe_load(cp)
|
|
||||||
pickle.dump(CAPABILITIES, pp, protocol=2)
|
|
||||||
|
|
||||||
logger.debug('Finished loading capabilities took %.2fs', time.time() - t0)
|
|
||||||
|
|
||||||
|
with open(file_path) as f:
|
||||||
|
CAPABILITIES = yaml.load(f)
|
||||||
|
|
||||||
PROFILES = CAPABILITIES['profiles']
|
PROFILES = CAPABILITIES['profiles']
|
||||||
|
|
||||||
|
|
||||||
class NotSupported(Exception):
|
class NotSupported(Exception):
|
||||||
"""Raised if a requested feature is not supported by the
|
"""Raised if a requested feature is not suppored by the
|
||||||
printer profile.
|
printer profile.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
@@ -91,7 +59,7 @@ class BaseProfile(object):
|
|||||||
return self.features.get(feature)
|
return self.features.get(feature)
|
||||||
|
|
||||||
def get_code_pages(self):
|
def get_code_pages(self):
|
||||||
"""Return the support code pages as a ``{name: index}`` dict.
|
"""Return the support code pages as a {name: index} dict.
|
||||||
"""
|
"""
|
||||||
return {v: k for k, v in self.codePages.items()}
|
return {v: k for k, v in self.codePages.items()}
|
||||||
|
|
||||||
|
@@ -64,11 +64,6 @@ _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
|
||||||
|
|
||||||
# Line display printing
|
|
||||||
LINE_DISPLAY_OPEN = ESC + b'\x3d\x02'
|
|
||||||
LINE_DISPLAY_CLEAR = ESC + b'\x40'
|
|
||||||
LINE_DISPLAY_CLOSE = ESC + b'\x3d\x01'
|
|
||||||
|
|
||||||
# Sheet modes
|
# Sheet modes
|
||||||
SHEET_SLIP_MODE = ESC + b'\x63\x30\x04' # slip paper
|
SHEET_SLIP_MODE = ESC + b'\x63\x30\x04' # slip paper
|
||||||
SHEET_ROLL_MODE = ESC + b'\x63\x30\x01' # paper roll
|
SHEET_ROLL_MODE = ESC + b'\x63\x30\x01' # paper roll
|
||||||
@@ -76,90 +71,51 @@ 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.
|
||||||
|
TXT_FLIP_ON = ESC + b'\x7b\x01'
|
||||||
|
TXT_FLIP_OFF = ESC + b'\x7b\x00'
|
||||||
|
TXT_SMOOTH_ON = GS + b'\x62\x01'
|
||||||
|
TXT_SMOOTH_OFF = GS + b'\x62\x00'
|
||||||
TXT_SIZE = GS + b'!'
|
TXT_SIZE = GS + b'!'
|
||||||
|
TXT_WIDTH = {1: 0x00,
|
||||||
TXT_NORMAL = ESC + b'!\x00' # Normal text
|
|
||||||
|
|
||||||
|
|
||||||
TXT_STYLE = {
|
|
||||||
'bold': {
|
|
||||||
False: ESC + b'\x45\x00', # Bold font OFF
|
|
||||||
True: ESC + b'\x45\x01' # Bold font ON
|
|
||||||
},
|
|
||||||
'underline': {
|
|
||||||
0: ESC + b'\x2d\x00', # Underline font OFF
|
|
||||||
1: ESC + b'\x2d\x01', # Underline font 1-dot ON
|
|
||||||
2: ESC + b'\x2d\x02' # Underline font 2-dot ON
|
|
||||||
},
|
|
||||||
'size': {
|
|
||||||
'normal': TXT_NORMAL + ESC + b'!\x00', # Normal text
|
|
||||||
'2h': TXT_NORMAL + ESC + b'!\x10', # Double height text
|
|
||||||
'2w': TXT_NORMAL + ESC + b'!\x20', # Double width text
|
|
||||||
'2x': TXT_NORMAL + ESC + b'!\x30' # Quad area text
|
|
||||||
},
|
|
||||||
'font': {
|
|
||||||
'a': ESC + b'\x4d\x00', # Font type A
|
|
||||||
'b': ESC + b'\x4d\x00' # Font type B
|
|
||||||
},
|
|
||||||
'align': {
|
|
||||||
'left': ESC + b'\x61\x00', # Left justification
|
|
||||||
'center': ESC + b'\x61\x01', # Centering
|
|
||||||
'right': ESC + b'\x61\x02' # Right justification
|
|
||||||
},
|
|
||||||
'invert': {
|
|
||||||
True: GS + b'\x42\x01', # Inverse Printing ON
|
|
||||||
False: GS + b'\x42\x00' # Inverse Printing OFF
|
|
||||||
},
|
|
||||||
'color': {
|
|
||||||
'black': ESC + b'\x72\x00', # Default Color
|
|
||||||
'red': ESC + b'\x72\x01' # Alternative Color, Usually Red
|
|
||||||
},
|
|
||||||
'flip': {
|
|
||||||
True: ESC + b'\x7b\x01', # Flip ON
|
|
||||||
False: ESC + b'\x7b\x00' # Flip OFF
|
|
||||||
},
|
|
||||||
'density': {
|
|
||||||
0: GS + b'\x7c\x00', # Printing Density -50%
|
|
||||||
1: GS + b'\x7c\x01', # Printing Density -37.5%
|
|
||||||
2: GS + b'\x7c\x02', # Printing Density -25%
|
|
||||||
3: GS + b'\x7c\x03', # Printing Density -12.5%
|
|
||||||
4: GS + b'\x7c\x04', # Printing Density 0%
|
|
||||||
5: GS + b'\x7c\x08', # Printing Density +50%
|
|
||||||
6: GS + b'\x7c\x07', # Printing Density +37.5%
|
|
||||||
7: GS + b'\x7c\x06', # Printing Density +25%
|
|
||||||
8: GS + b'\x7c\x05' # Printing Density +12.5%
|
|
||||||
},
|
|
||||||
'smooth': {
|
|
||||||
True: GS + b'\x62\x01', # Smooth ON
|
|
||||||
False: GS + b'\x62\x00' # Smooth OFF
|
|
||||||
},
|
|
||||||
'height': { # Custom text height
|
|
||||||
1: 0x00,
|
|
||||||
2: 0x01,
|
|
||||||
3: 0x02,
|
|
||||||
4: 0x03,
|
|
||||||
5: 0x04,
|
|
||||||
6: 0x05,
|
|
||||||
7: 0x06,
|
|
||||||
8: 0x07
|
|
||||||
},
|
|
||||||
'width': { # Custom text width
|
|
||||||
1: 0x00,
|
|
||||||
2: 0x10,
|
2: 0x10,
|
||||||
3: 0x20,
|
3: 0x20,
|
||||||
4: 0x30,
|
4: 0x30,
|
||||||
5: 0x40,
|
5: 0x40,
|
||||||
6: 0x50,
|
6: 0x50,
|
||||||
7: 0x60,
|
7: 0x60,
|
||||||
8: 0x70
|
8: 0x70}
|
||||||
}
|
TXT_HEIGHT = {1: 0x00,
|
||||||
}
|
2: 0x01,
|
||||||
|
3: 0x02,
|
||||||
|
4: 0x03,
|
||||||
|
5: 0x04,
|
||||||
|
6: 0x05,
|
||||||
|
7: 0x06,
|
||||||
|
8: 0x07}
|
||||||
|
TXT_NORMAL = ESC + b'!\x00' # Normal text
|
||||||
|
TXT_2HEIGHT = ESC + b'!\x10' # Double height text
|
||||||
|
TXT_2WIDTH = ESC + b'!\x20' # Double width text
|
||||||
|
TXT_4SQUARE = ESC + b'!\x30' # Quad area text
|
||||||
|
TXT_UNDERL_OFF = ESC + b'\x2d\x00' # Underline font OFF
|
||||||
|
TXT_UNDERL_ON = ESC + b'\x2d\x01' # Underline font 1-dot ON
|
||||||
|
TXT_UNDERL2_ON = ESC + b'\x2d\x02' # Underline font 2-dot ON
|
||||||
|
TXT_BOLD_OFF = ESC + b'\x45\x00' # Bold font OFF
|
||||||
|
TXT_BOLD_ON = ESC + b'\x45\x01' # Bold font ON
|
||||||
|
TXT_ALIGN_LT = ESC + b'\x61\x00' # Left justification
|
||||||
|
TXT_ALIGN_CT = ESC + b'\x61\x01' # Centering
|
||||||
|
TXT_ALIGN_RT = ESC + b'\x61\x02' # Right justification
|
||||||
|
TXT_INVERT_ON = GS + b'\x42\x01' # Inverse Printing ON
|
||||||
|
TXT_INVERT_OFF = GS + b'\x42\x00' # Inverse Printing OFF
|
||||||
|
|
||||||
# Fonts
|
# Fonts
|
||||||
SET_FONT = lambda n: ESC + b'\x4d' + n
|
SET_FONT = lambda n: ESC + b'\x4d' + n
|
||||||
TXT_FONT_A = SET_FONT(b'\x00') # Font type A
|
TXT_FONT_A = SET_FONT(b'\x00') # Font type A
|
||||||
TXT_FONT_B = SET_FONT(b'\x01') # Font type B
|
TXT_FONT_B = SET_FONT(b'\x01') # Font type B
|
||||||
|
|
||||||
|
# Text colors
|
||||||
|
TXT_COLOR_BLACK = ESC + b'\x72\x00' # Default Color
|
||||||
|
TXT_COLOR_RED = ESC + b'\x72\x01' # Alternative Color (Usually Red)
|
||||||
|
|
||||||
# Spacing
|
# Spacing
|
||||||
LINESPACING_RESET = ESC + b'2'
|
LINESPACING_RESET = ESC + b'2'
|
||||||
LINESPACING_FUNCS = {
|
LINESPACING_FUNCS = {
|
||||||
@@ -225,24 +181,6 @@ BARCODE_TYPE_B = {
|
|||||||
'GS1 DATABAR EXPANDED': _SET_BARCODE_TYPE(78),
|
'GS1 DATABAR EXPANDED': _SET_BARCODE_TYPE(78),
|
||||||
}
|
}
|
||||||
|
|
||||||
BARCODE_FORMATS = {
|
|
||||||
'UPC-A': ([(11, 12)], "^[0-9]{11,12}$"),
|
|
||||||
'UPC-E': ([(7, 8), (11, 12)], "^([0-9]{7,8}|[0-9]{11,12})$"),
|
|
||||||
'EAN13': ([(12, 13)], "^[0-9]{12,13}$"),
|
|
||||||
'EAN8': ([(7, 8)], "^[0-9]{7,8}$"),
|
|
||||||
'CODE39': ([(1, 255)], "^([0-9A-Z \$\%\+\-\.\/]+|\*[0-9A-Z \$\%\+\-\.\/]+\*)$"),
|
|
||||||
'ITF': ([(2, 255)], "^([0-9]{2})+$"),
|
|
||||||
'NW7': ([(1, 255)], "^[A-Da-d][0-9\$\+\-\.\/\:]+[A-Da-d]$"),
|
|
||||||
'CODABAR': ([(1, 255)], "^[A-Da-d][0-9\$\+\-\.\/\:]+[A-Da-d]$"), # Same as NW7
|
|
||||||
'CODE93': ([(1, 255)], "^[\\x00-\\x7F]+$"),
|
|
||||||
'CODE128': ([(2, 255)], "^\{[A-C][\\x00-\\x7F]+$"),
|
|
||||||
'GS1-128': ([(2, 255)], "^\{[A-C][\\x00-\\x7F]+$"), # same as CODE128
|
|
||||||
'GS1 DATABAR OMNIDIRECTIONAL': ([(13,13)], "^[0-9]{13}$"),
|
|
||||||
'GS1 DATABAR TRUNCATED': ([(13,13)], "^[0-9]{13}$"), # same as GS1 omnidirectional
|
|
||||||
'GS1 DATABAR LIMITED': ([(13,13)], "^[01][0-9]{12}$"),
|
|
||||||
'GS1 DATABAR EXPANDED': ([(2,255)], "^\([0-9][A-Za-z0-9 \!\"\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\_\{]+$"),
|
|
||||||
}
|
|
||||||
|
|
||||||
BARCODE_TYPES = {
|
BARCODE_TYPES = {
|
||||||
'A': BARCODE_TYPE_A,
|
'A': BARCODE_TYPE_A,
|
||||||
'B': BARCODE_TYPE_B,
|
'B': BARCODE_TYPE_B,
|
||||||
@@ -268,11 +206,13 @@ S_RASTER_2W = _PRINT_RASTER_IMG(b'\x01') # Set raster image double width
|
|||||||
S_RASTER_2H = _PRINT_RASTER_IMG(b'\x02') # Set raster image double height
|
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
|
S_RASTER_Q = _PRINT_RASTER_IMG(b'\x03') # Set raster image quadruple
|
||||||
|
|
||||||
# Status Command
|
# Printing Density
|
||||||
RT_STATUS = DLE + EOT
|
PD_N50 = GS + b'\x7c\x00' # Printing Density -50%
|
||||||
RT_STATUS_ONLINE = RT_STATUS + b'\x01'
|
PD_N37 = GS + b'\x7c\x01' # Printing Density -37.5%
|
||||||
RT_STATUS_PAPER = RT_STATUS + b'\x04'
|
PD_N25 = GS + b'\x7c\x02' # Printing Density -25%
|
||||||
RT_MASK_ONLINE = 8
|
PD_N12 = GS + b'\x7c\x03' # Printing Density -12.5%
|
||||||
RT_MASK_PAPER = 18
|
PD_0 = GS + b'\x7c\x04' # Printing Density 0%
|
||||||
RT_MASK_LOWPAPER = 30
|
PD_P50 = GS + b'\x7c\x08' # Printing Density +50%
|
||||||
RT_MASK_NOPAPER = 114
|
PD_P37 = GS + b'\x7c\x07' # Printing Density +37.5%
|
||||||
|
PD_P25 = GS + b'\x7c\x06' # Printing Density +25%
|
||||||
|
PD_P12 = GS + b'\x7c\x05' # Printing Density +12.5%
|
||||||
|
@@ -18,32 +18,22 @@ from __future__ import unicode_literals
|
|||||||
import qrcode
|
import qrcode
|
||||||
import textwrap
|
import textwrap
|
||||||
import six
|
import six
|
||||||
import time
|
|
||||||
from re import match as re_match
|
|
||||||
|
|
||||||
import barcode
|
|
||||||
from barcode.writer import ImageWriter
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from .constants import ESC, GS, NUL, QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q
|
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 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 TXT_ALIGN_CT, TXT_ALIGN_LT, TXT_ALIGN_RT, BARCODE_FONT_A, BARCODE_FONT_B
|
||||||
from .constants import BARCODE_TXT_OFF, BARCODE_TXT_BTH, BARCODE_TXT_ABV, BARCODE_TXT_BLW
|
from .constants import BARCODE_TXT_OFF, BARCODE_TXT_BTH, BARCODE_TXT_ABV, BARCODE_TXT_BLW
|
||||||
from .constants import TXT_SIZE, TXT_NORMAL
|
from .constants import TXT_HEIGHT, TXT_WIDTH, TXT_SIZE, TXT_NORMAL, TXT_SMOOTH_OFF, TXT_SMOOTH_ON
|
||||||
from .constants import SET_FONT
|
from .constants import TXT_FLIP_OFF, TXT_FLIP_ON, TXT_2WIDTH, TXT_2HEIGHT, TXT_4SQUARE
|
||||||
from .constants import LINESPACING_FUNCS, LINESPACING_RESET
|
from .constants import TXT_UNDERL_OFF, TXT_UNDERL_ON, TXT_BOLD_OFF, TXT_BOLD_ON, SET_FONT, TXT_UNDERL2_ON
|
||||||
from .constants import LINE_DISPLAY_OPEN, LINE_DISPLAY_CLEAR, LINE_DISPLAY_CLOSE
|
from .constants import TXT_INVERT_OFF, TXT_INVERT_ON, LINESPACING_FUNCS, LINESPACING_RESET
|
||||||
|
from .constants import PD_0, PD_N12, PD_N25, PD_N37, PD_N50, PD_P50, PD_P37, PD_P25, PD_P12
|
||||||
from .constants import CD_KICK_DEC_SEQUENCE, CD_KICK_5, CD_KICK_2, PAPER_FULL_CUT, PAPER_PART_CUT
|
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 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 CTL_VT, CTL_HT, 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 BarcodeTypeError, BarcodeSizeError, TabPosError
|
||||||
from .exceptions import CashDrawerError, SetVariableError, BarcodeCodeError
|
from .exceptions import CashDrawerError, SetVariableError, BarcodeCodeError
|
||||||
from .exceptions import ImageWidthError
|
|
||||||
|
|
||||||
from .magicencode import MagicEncode
|
from .magicencode import MagicEncode
|
||||||
|
|
||||||
@@ -83,14 +73,8 @@ class Escpos(object):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _read(self):
|
|
||||||
""" Returns a NotImplementedError if the instance of the class doesn't override this method.
|
|
||||||
:raises NotImplementedError
|
|
||||||
"""
|
|
||||||
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):
|
fragment_height=960):
|
||||||
""" 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.
|
||||||
@@ -111,26 +95,10 @@ class Escpos(object):
|
|||||||
: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:* 960
|
:param fragment_height: Images larger than this will be split into multiple fragments *default:* 960
|
||||||
:param center: Center image horizontally *default:* False
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
im = EscposImage(img_source)
|
im = EscposImage(img_source)
|
||||||
|
|
||||||
try:
|
|
||||||
max_width = int(self.profile.profile_data['media']['width']['pixels'])
|
|
||||||
|
|
||||||
if im.width > max_width:
|
|
||||||
raise ImageWidthError('{} > {}'.format(im.width, max_width))
|
|
||||||
|
|
||||||
if center:
|
|
||||||
im.center(max_width)
|
|
||||||
except KeyError:
|
|
||||||
# If the printer's pixel width is not known, print anyways...
|
|
||||||
pass
|
|
||||||
except ValueError:
|
|
||||||
# If the max_width cannot be converted to an int, print anyways...
|
|
||||||
pass
|
|
||||||
|
|
||||||
if im.height > fragment_height:
|
if im.height > fragment_height:
|
||||||
fragments = im.split(fragment_height)
|
fragments = im.split(fragment_height)
|
||||||
for fragment in fragments:
|
for fragment in fragments:
|
||||||
@@ -181,8 +149,7 @@ class Escpos(object):
|
|||||||
header = self._int_low_high(len(data) + 2, 2)
|
header = self._int_low_high(len(data) + 2, 2)
|
||||||
self._raw(GS + b'(L' + header + m + fn + data)
|
self._raw(GS + b'(L' + header + m + fn + data)
|
||||||
|
|
||||||
def qr(self, content, ec=QR_ECLEVEL_L, size=3, model=QR_MODEL_2,
|
def qr(self, content, ec=QR_ECLEVEL_L, size=3, model=QR_MODEL_2, native=False):
|
||||||
native=False, center=False, impl="bitImageRaster"):
|
|
||||||
""" Print QR Code for the provided string
|
""" Print QR Code for the provided string
|
||||||
|
|
||||||
:param content: The content of the code. Numeric data will be more efficiently compacted.
|
:param content: The content of the code. Numeric data will be more efficiently compacted.
|
||||||
@@ -194,8 +161,6 @@ class Escpos(object):
|
|||||||
by all printers).
|
by all printers).
|
||||||
:param native: True to render the code on the printer, False to render the code as an image and send it to the
|
: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)
|
printer (Default)
|
||||||
:param center: Centers the code *default:* False
|
|
||||||
:param impl: Image-printing-implementation, refer to :meth:`.image()` for details
|
|
||||||
"""
|
"""
|
||||||
# Basic validation
|
# Basic validation
|
||||||
if ec not in [QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q]:
|
if ec not in [QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q]:
|
||||||
@@ -222,17 +187,9 @@ class Escpos(object):
|
|||||||
qr_code.make(fit=True)
|
qr_code.make(fit=True)
|
||||||
qr_img = qr_code.make_image()
|
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
|
# Convert the RGB image in printable image
|
||||||
self.text('\n')
|
self.image(im)
|
||||||
self.image(im, center=center, impl=impl)
|
|
||||||
self.text('\n')
|
|
||||||
self.text('\n')
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if center:
|
|
||||||
raise NotImplementedError("Centering not implemented for native QR rendering")
|
|
||||||
|
|
||||||
# Native 2D code printing
|
# Native 2D code printing
|
||||||
cn = b'1' # Code type for QR code
|
cn = b'1' # Code type for QR code
|
||||||
# Select model: 1, 2 or micro.
|
# Select model: 1, 2 or micro.
|
||||||
@@ -267,9 +224,9 @@ class Escpos(object):
|
|||||||
"""
|
"""
|
||||||
max_input = (256 << (out_bytes * 8) - 1)
|
max_input = (256 << (out_bytes * 8) - 1)
|
||||||
if not 1 <= out_bytes <= 4:
|
if not 1 <= out_bytes <= 4:
|
||||||
raise ValueError("Can only output 1-4 bytes")
|
raise ValueError("Can only output 1-4 byes")
|
||||||
if not 0 <= inp_number <= max_input:
|
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} byes".format(max_input, out_bytes))
|
||||||
outp = b''
|
outp = b''
|
||||||
for _ in range(0, out_bytes):
|
for _ in range(0, out_bytes):
|
||||||
outp += six.int2byte(inp_number % 256)
|
outp += six.int2byte(inp_number % 256)
|
||||||
@@ -291,42 +248,17 @@ class Escpos(object):
|
|||||||
else:
|
else:
|
||||||
self.magic.force_encoding(code)
|
self.magic.force_encoding(code)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_barcode(bc, code):
|
|
||||||
"""
|
|
||||||
This method checks if the barcode is in the proper format.
|
|
||||||
The validation concerns the barcode length and the set of characters, but won't compute/validate any checksum.
|
|
||||||
The full set of requirement for each barcode type is available in the ESC/POS documentation.
|
|
||||||
|
|
||||||
As an example, using EAN13, the barcode `12345678901` will be correct, because it can be rendered by the
|
|
||||||
printer. But it does not suit the EAN13 standard, because the checksum digit is missing. Adding a wrong
|
|
||||||
checksum in the end will also be considered correct, but adding a letter won't (EAN13 is numeric only).
|
|
||||||
|
|
||||||
.. todo:: Add a method to compute the checksum for the different standards
|
|
||||||
|
|
||||||
.. todo:: For fixed-length standards with mandatory checksum (EAN, UPC),
|
|
||||||
compute and add the checksum automatically if missing.
|
|
||||||
|
|
||||||
: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:
|
|
||||||
return False
|
|
||||||
|
|
||||||
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):
|
align_ct=True, function_type=None):
|
||||||
""" Print Barcode
|
""" Print Barcode
|
||||||
|
|
||||||
This method allows to print barcodes. The rendering of the barcode is done by the printer and therefore has to
|
This method allows to print barcodes. The rendering of the barcode is done by the printer and therefore has to
|
||||||
be supported by the unit. By default, this method will check whether your barcode text is correct, that is
|
be supported by the unit. Currently you have to check manually whether your barcode text is correct. Uncorrect
|
||||||
the characters and lengths are supported by ESCPOS. Call the method with `check=False` to disable the check, but
|
barcodes may lead to unexpected printer behaviour. There are two forms of the barcode function. Type A is
|
||||||
note that uncorrect barcodes may lead to unexpected printer behaviour.
|
default but has fewer barcodes, while type B has some more to choose from.
|
||||||
There are two forms of the barcode function. Type A is default but has fewer barcodes,
|
|
||||||
while type B has some more to choose from.
|
.. todo:: Add a method to check barcode codes. Alternatively or as an addition write explanations about each
|
||||||
|
barcode-type. Research whether the check digits can be computed autmatically.
|
||||||
|
|
||||||
Use the parameters `height` and `width` for adjusting of the barcode size. Please take notice that the barcode
|
Use the parameters `height` and `width` for adjusting of the barcode size. Please take notice that the barcode
|
||||||
will not be printed if it is outside of the printable area. (Which should be impossible with this method, so
|
will not be printed if it is outside of the printable area. (Which should be impossible with this method, so
|
||||||
@@ -394,10 +326,6 @@ class Escpos(object):
|
|||||||
function based on the current profile.
|
function based on the current profile.
|
||||||
*default*: A
|
*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:meth:`.check_barcode()`
|
|
||||||
for more information. *default*: True.
|
|
||||||
|
|
||||||
:raises: :py:exc:`~escpos.exceptions.BarcodeSizeError`,
|
:raises: :py:exc:`~escpos.exceptions.BarcodeSizeError`,
|
||||||
:py:exc:`~escpos.exceptions.BarcodeTypeError`,
|
:py:exc:`~escpos.exceptions.BarcodeTypeError`,
|
||||||
:py:exc:`~escpos.exceptions.BarcodeCodeError`
|
:py:exc:`~escpos.exceptions.BarcodeCodeError`
|
||||||
@@ -420,22 +348,15 @@ class Escpos(object):
|
|||||||
bc_types = BARCODE_TYPES[function_type.upper()]
|
bc_types = BARCODE_TYPES[function_type.upper()]
|
||||||
if bc.upper() not in bc_types.keys():
|
if bc.upper() not in bc_types.keys():
|
||||||
raise BarcodeTypeError((
|
raise BarcodeTypeError((
|
||||||
"Barcode '{bc}' not valid for barcode function type "
|
"Barcode type '{bc}' not valid for barcode function type "
|
||||||
"{function_type}").format(
|
"{function_type}").format(
|
||||||
bc=bc,
|
bc=bc,
|
||||||
function_type=function_type,
|
function_type=function_type,
|
||||||
))
|
))
|
||||||
|
|
||||||
if check and not self.check_barcode(bc, code):
|
|
||||||
raise BarcodeCodeError((
|
|
||||||
"Barcode '{code}' not in a valid format for type '{bc}'").format(
|
|
||||||
code=code,
|
|
||||||
bc=bc,
|
|
||||||
))
|
|
||||||
|
|
||||||
# Align Bar Code()
|
# Align Bar Code()
|
||||||
if align_ct:
|
if align_ct:
|
||||||
self._raw(TXT_STYLE['align']['center'])
|
self._raw(TXT_ALIGN_CT)
|
||||||
# Height
|
# Height
|
||||||
if 1 <= height <= 255:
|
if 1 <= height <= 255:
|
||||||
self._raw(BARCODE_HEIGHT + six.int2byte(height))
|
self._raw(BARCODE_HEIGHT + six.int2byte(height))
|
||||||
@@ -475,32 +396,6 @@ class Escpos(object):
|
|||||||
if function_type.upper() == "A":
|
if function_type.upper() == "A":
|
||||||
self._raw(NUL)
|
self._raw(NUL)
|
||||||
|
|
||||||
def soft_barcode(self, barcode_type, data, impl='bitImageColumn',
|
|
||||||
module_height=5, module_width=0.2, text_distance=1):
|
|
||||||
|
|
||||||
image_writer = ImageWriter()
|
|
||||||
|
|
||||||
# Check if barcode type exists
|
|
||||||
if barcode_type not in barcode.PROVIDED_BARCODES:
|
|
||||||
raise BarcodeTypeError(
|
|
||||||
'Barcode type {} not supported by software barcode renderer'
|
|
||||||
.format(barcode_type))
|
|
||||||
|
|
||||||
# Render the barcode to a fake file
|
|
||||||
barcode_class = barcode.get_barcode_class(barcode_type)
|
|
||||||
my_code = barcode_class(data, writer=image_writer)
|
|
||||||
|
|
||||||
with open(os.devnull, "wb") as nullfile:
|
|
||||||
my_code.write(nullfile, {
|
|
||||||
'module_height': module_height,
|
|
||||||
'module_width': module_width,
|
|
||||||
'text_distance': text_distance
|
|
||||||
})
|
|
||||||
|
|
||||||
# Retrieve the Pillow image and print it
|
|
||||||
image = my_code.writer._image
|
|
||||||
self.image(image, impl=impl)
|
|
||||||
|
|
||||||
def text(self, txt):
|
def text(self, txt):
|
||||||
""" Print alpha-numeric text
|
""" Print alpha-numeric text
|
||||||
|
|
||||||
@@ -513,113 +408,132 @@ class Escpos(object):
|
|||||||
txt = six.text_type(txt)
|
txt = six.text_type(txt)
|
||||||
self.magic.write(txt)
|
self.magic.write(txt)
|
||||||
|
|
||||||
def textln(self, txt=''):
|
|
||||||
"""Print alpha-numeric text with a newline
|
|
||||||
|
|
||||||
The text has to be encoded in the currently selected codepage.
|
|
||||||
The input text has to be encoded in unicode.
|
|
||||||
|
|
||||||
:param txt: text to be printed with a newline
|
|
||||||
:raises: :py:exc:`~escpos.exceptions.TextError`
|
|
||||||
"""
|
|
||||||
self.text('{}\n'.format(txt))
|
|
||||||
|
|
||||||
def ln(self, count=1):
|
|
||||||
"""Print a newline or more
|
|
||||||
|
|
||||||
:param count: number of newlines to print
|
|
||||||
:raises: :py:exc:`ValueError` if count < 0
|
|
||||||
"""
|
|
||||||
if count < 0:
|
|
||||||
raise ValueError('Count cannot be lesser than 0')
|
|
||||||
if count > 0:
|
|
||||||
self.text('\n' * count)
|
|
||||||
|
|
||||||
def block_text(self, txt, font=None, columns=None):
|
def block_text(self, txt, font=None, columns=None):
|
||||||
""" Text is printed wrapped to specified columns
|
""" Text is printed wrapped to specified columns
|
||||||
|
|
||||||
Text has to be encoded in unicode.
|
Text has to be encoded in unicode.
|
||||||
|
|
||||||
:param txt: text to be printed
|
:param txt: text to be printed
|
||||||
:param font: font to be used, can be :code:`a` or :code:`b`
|
:param font: font to be used, can be :code:`a` or :code`b`
|
||||||
:param columns: amount of columns
|
:param columns: amount of columns
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
col_count = self.profile.get_columns(font) if columns is None else columns
|
col_count = self.profile.get_columns(font) if columns is None else columns
|
||||||
self.text(textwrap.fill(txt, col_count))
|
self.text(textwrap.fill(txt, col_count))
|
||||||
|
|
||||||
def set(self, align='left', font='a', bold=False, underline=0, width=1,
|
def set(self, align='left', font='a', text_type='normal', width=1,
|
||||||
height=1, density=9, invert=False, smooth=False, flip=False,
|
height=1, density=9, invert=False, smooth=False, flip=False):
|
||||||
double_width=False, double_height=False, custom_size=False):
|
|
||||||
""" Set text properties by sending them to the printer
|
""" Set text properties by sending them to the printer
|
||||||
|
|
||||||
:param align: horizontal position for text, possible values are:
|
:param align: horizontal position for text, possible values are:
|
||||||
|
|
||||||
* 'center'
|
* CENTER
|
||||||
* 'left'
|
* LEFT
|
||||||
* 'right'
|
* RIGHT
|
||||||
|
|
||||||
*default*: 'left'
|
*default*: LEFT
|
||||||
|
|
||||||
:param font: font given as an index, a name, or one of the
|
:param font: font given as an index, a name, or one of the
|
||||||
special values 'a' or 'b', referring to fonts 0 and 1.
|
special values 'a' or 'b', refering to fonts 0 and 1.
|
||||||
:param bold: text in bold, *default*: False
|
:param text_type: text type, possible values are:
|
||||||
:param underline: underline mode for text, decimal range 0-2, *default*: 0
|
|
||||||
:param double_height: doubles the height of the text
|
* B for bold
|
||||||
:param double_width: doubles the width of the text
|
* U for underlined
|
||||||
:param custom_size: uses custom size specified by width and height
|
* U2 for underlined, version 2
|
||||||
parameters. Cannot be used with double_width or double_height.
|
* BU for bold and underlined
|
||||||
:param width: text width multiplier when custom_size is used, decimal range 1-8, *default*: 1
|
* BU2 for bold and underlined, version 2
|
||||||
:param height: text height multiplier when custom_size is used, decimal range 1-8, *default*: 1
|
* NORMAL for normal text
|
||||||
|
|
||||||
|
*default*: NORMAL
|
||||||
|
:param width: text width multiplier, decimal range 1-8, *default*: 1
|
||||||
|
:param height: text height multiplier, decimal range 1-8, *default*: 1
|
||||||
:param density: print density, value from 0-8, if something else is supplied the density remains unchanged
|
:param density: print density, value from 0-8, if something else is supplied the density remains unchanged
|
||||||
:param invert: True enables white on black printing, *default*: False
|
:param invert: True enables white on black printing, *default*: False
|
||||||
:param smooth: True enables text smoothing. Effective on 4x4 size text and larger, *default*: False
|
:param smooth: True enables text smoothing. Effective on 4x4 size text and larger, *default*: False
|
||||||
:param flip: True enables upside-down printing, *default*: False
|
:param flip: True enables upside-down printing, *default*: False
|
||||||
|
|
||||||
:type font: str
|
|
||||||
:type invert: bool
|
:type invert: bool
|
||||||
:type bold: bool
|
|
||||||
:type underline: bool
|
|
||||||
:type smooth: bool
|
|
||||||
:type flip: bool
|
|
||||||
:type custom_size: bool
|
|
||||||
:type double_width: bool
|
|
||||||
:type double_height: bool
|
|
||||||
:type align: str
|
|
||||||
:type width: int
|
|
||||||
:type height: int
|
|
||||||
:type density: int
|
|
||||||
"""
|
"""
|
||||||
|
# Width
|
||||||
if custom_size:
|
if height == 2 and width == 2:
|
||||||
if 1 <= width <= 8 and 1 <= height <= 8 and isinstance(width, int) and\
|
self._raw(TXT_NORMAL)
|
||||||
isinstance(height, int):
|
self._raw(TXT_4SQUARE)
|
||||||
size_byte = TXT_STYLE['width'][width] + TXT_STYLE['height'][height]
|
elif height == 2 and width == 1:
|
||||||
self._raw(TXT_SIZE + six.int2byte(size_byte))
|
self._raw(TXT_NORMAL)
|
||||||
|
self._raw(TXT_2HEIGHT)
|
||||||
|
elif width == 2 and height == 1:
|
||||||
|
self._raw(TXT_NORMAL)
|
||||||
|
self._raw(TXT_2WIDTH)
|
||||||
|
elif width == 1 and height == 1:
|
||||||
|
self._raw(TXT_NORMAL)
|
||||||
|
elif 1 <= width <= 8 and 1 <= height <= 8 and isinstance(width, int) and isinstance(height, int):
|
||||||
|
self._raw(TXT_SIZE + six.int2byte(TXT_WIDTH[width] + TXT_HEIGHT[height]))
|
||||||
else:
|
else:
|
||||||
raise SetVariableError()
|
raise SetVariableError()
|
||||||
|
# Upside down
|
||||||
|
if flip:
|
||||||
|
self._raw(TXT_FLIP_ON)
|
||||||
else:
|
else:
|
||||||
self._raw(TXT_NORMAL)
|
self._raw(TXT_FLIP_OFF)
|
||||||
if double_width and double_height:
|
# Smoothing
|
||||||
self._raw(TXT_STYLE['size']['2x'])
|
if smooth:
|
||||||
elif double_width:
|
self._raw(TXT_SMOOTH_ON)
|
||||||
self._raw(TXT_STYLE['size']['2w'])
|
|
||||||
elif double_height:
|
|
||||||
self._raw(TXT_STYLE['size']['2h'])
|
|
||||||
else:
|
else:
|
||||||
self._raw(TXT_STYLE['size']['normal'])
|
self._raw(TXT_SMOOTH_OFF)
|
||||||
|
# Type
|
||||||
self._raw(TXT_STYLE['flip'][flip])
|
if text_type.upper() == "B":
|
||||||
self._raw(TXT_STYLE['smooth'][smooth])
|
self._raw(TXT_BOLD_ON)
|
||||||
self._raw(TXT_STYLE['bold'][bold])
|
self._raw(TXT_UNDERL_OFF)
|
||||||
self._raw(TXT_STYLE['underline'][underline])
|
elif text_type.upper() == "U":
|
||||||
|
self._raw(TXT_BOLD_OFF)
|
||||||
|
self._raw(TXT_UNDERL_ON)
|
||||||
|
elif text_type.upper() == "U2":
|
||||||
|
self._raw(TXT_BOLD_OFF)
|
||||||
|
self._raw(TXT_UNDERL2_ON)
|
||||||
|
elif text_type.upper() == "BU":
|
||||||
|
self._raw(TXT_BOLD_ON)
|
||||||
|
self._raw(TXT_UNDERL_ON)
|
||||||
|
elif text_type.upper() == "BU2":
|
||||||
|
self._raw(TXT_BOLD_ON)
|
||||||
|
self._raw(TXT_UNDERL2_ON)
|
||||||
|
elif text_type.upper() == "NORMAL":
|
||||||
|
self._raw(TXT_BOLD_OFF)
|
||||||
|
self._raw(TXT_UNDERL_OFF)
|
||||||
|
# Font
|
||||||
self._raw(SET_FONT(six.int2byte(self.profile.get_font(font))))
|
self._raw(SET_FONT(six.int2byte(self.profile.get_font(font))))
|
||||||
self._raw(TXT_STYLE['align'][align])
|
|
||||||
|
|
||||||
if density != 9:
|
# Align
|
||||||
self._raw(TXT_STYLE['density'][density])
|
if align.upper() == "CENTER":
|
||||||
|
self._raw(TXT_ALIGN_CT)
|
||||||
self._raw(TXT_STYLE['invert'][invert])
|
elif align.upper() == "RIGHT":
|
||||||
|
self._raw(TXT_ALIGN_RT)
|
||||||
|
elif align.upper() == "LEFT":
|
||||||
|
self._raw(TXT_ALIGN_LT)
|
||||||
|
# Density
|
||||||
|
if density == 0:
|
||||||
|
self._raw(PD_N50)
|
||||||
|
elif density == 1:
|
||||||
|
self._raw(PD_N37)
|
||||||
|
elif density == 2:
|
||||||
|
self._raw(PD_N25)
|
||||||
|
elif density == 3:
|
||||||
|
self._raw(PD_N12)
|
||||||
|
elif density == 4:
|
||||||
|
self._raw(PD_0)
|
||||||
|
elif density == 5:
|
||||||
|
self._raw(PD_P12)
|
||||||
|
elif density == 6:
|
||||||
|
self._raw(PD_P25)
|
||||||
|
elif density == 7:
|
||||||
|
self._raw(PD_P37)
|
||||||
|
elif density == 8:
|
||||||
|
self._raw(PD_P50)
|
||||||
|
else: # DEFAULT: DOES NOTHING
|
||||||
|
pass
|
||||||
|
# Invert Printing
|
||||||
|
if invert:
|
||||||
|
self._raw(TXT_INVERT_ON)
|
||||||
|
else:
|
||||||
|
self._raw(TXT_INVERT_OFF)
|
||||||
|
|
||||||
def line_spacing(self, spacing=None, divisor=180):
|
def line_spacing(self, spacing=None, divisor=180):
|
||||||
""" Set line character spacing.
|
""" Set line character spacing.
|
||||||
@@ -650,7 +564,7 @@ class Escpos(object):
|
|||||||
|
|
||||||
self._raw(LINESPACING_FUNCS[divisor] + six.int2byte(spacing))
|
self._raw(LINESPACING_FUNCS[divisor] + six.int2byte(spacing))
|
||||||
|
|
||||||
def cut(self, mode='FULL', feed=True):
|
def cut(self, mode='FULL'):
|
||||||
""" Cut paper.
|
""" Cut paper.
|
||||||
|
|
||||||
Without any arguments the paper will be cut completely. With 'mode=PART' a partial cut will
|
Without any arguments the paper will be cut completely. With 'mode=PART' a partial cut will
|
||||||
@@ -660,14 +574,8 @@ class Escpos(object):
|
|||||||
.. todo:: Check this function on TM-T88II.
|
.. todo:: Check this function on TM-T88II.
|
||||||
|
|
||||||
:param mode: set to 'PART' for a partial cut. default: 'FULL'
|
: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')
|
:raises ValueError: if mode not in ('FULL', 'PART')
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not feed:
|
|
||||||
self._raw(GS + b'V' + six.int2byte(66) + b'\x00')
|
|
||||||
return
|
|
||||||
|
|
||||||
self.print_and_feed(6)
|
self.print_and_feed(6)
|
||||||
|
|
||||||
mode = mode.upper()
|
mode = mode.upper()
|
||||||
@@ -701,44 +609,8 @@ class Escpos(object):
|
|||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
self._raw(CD_KICK_DEC_SEQUENCE(*pin))
|
self._raw(CD_KICK_DEC_SEQUENCE(*pin))
|
||||||
except TypeError as err:
|
except:
|
||||||
raise CashDrawerError(err)
|
raise CashDrawerError()
|
||||||
|
|
||||||
def linedisplay_select(self, select_display=False):
|
|
||||||
""" Selects the line display or the printer
|
|
||||||
|
|
||||||
This method is used for line displays that are daisy-chained between your computer and printer.
|
|
||||||
If you set `select_display` to true, only the display is selected and if you set it to false,
|
|
||||||
only the printer is selected.
|
|
||||||
|
|
||||||
:param select_display: whether the display should be selected or the printer
|
|
||||||
:type select_display: bool
|
|
||||||
"""
|
|
||||||
if select_display:
|
|
||||||
self._raw(LINE_DISPLAY_OPEN)
|
|
||||||
else:
|
|
||||||
self._raw(LINE_DISPLAY_CLOSE)
|
|
||||||
|
|
||||||
def linedisplay_clear(self):
|
|
||||||
""" Clears the line display and resets the cursor
|
|
||||||
|
|
||||||
This method is used for line displays that are daisy-chained between your computer and printer.
|
|
||||||
"""
|
|
||||||
self._raw(LINE_DISPLAY_CLEAR)
|
|
||||||
|
|
||||||
def linedisplay(self, text):
|
|
||||||
"""
|
|
||||||
Display text on a line display connected to your printer
|
|
||||||
|
|
||||||
You should connect a line display to your printer. You can do this by daisy-chaining
|
|
||||||
the display between your computer and printer.
|
|
||||||
|
|
||||||
:param text: Text to display
|
|
||||||
"""
|
|
||||||
self.linedisplay_select(select_display=True)
|
|
||||||
self.linedisplay_clear()
|
|
||||||
self.text(text)
|
|
||||||
self.linedisplay_select(select_display=False)
|
|
||||||
|
|
||||||
def hw(self, hw):
|
def hw(self, hw):
|
||||||
""" Hardware operations
|
""" Hardware operations
|
||||||
@@ -772,7 +644,7 @@ class Escpos(object):
|
|||||||
else:
|
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):
|
def control(self, ctl, pos=4):
|
||||||
""" Feed control sequences
|
""" Feed control sequences
|
||||||
|
|
||||||
:param ctl: string for the following control sequences:
|
:param ctl: string for the following control sequences:
|
||||||
@@ -783,8 +655,7 @@ class Escpos(object):
|
|||||||
* HT *for Horizontal Tab*
|
* HT *for Horizontal Tab*
|
||||||
* VT *for Vertical Tab*
|
* VT *for Vertical Tab*
|
||||||
|
|
||||||
:param count: integer between 1 and 32, controls the horizontal tab count. Defaults to 5.
|
:param pos: integer between 1 and 16, controls the horizontal tab position
|
||||||
:param tab_size: integer between 1 and 255, controls the horizontal tab size in characters. Defaults to 8
|
|
||||||
:raises: :py:exc:`~escpos.exceptions.TabPosError`
|
:raises: :py:exc:`~escpos.exceptions.TabPosError`
|
||||||
"""
|
"""
|
||||||
# Set position
|
# Set position
|
||||||
@@ -795,16 +666,13 @@ class Escpos(object):
|
|||||||
elif ctl.upper() == "CR":
|
elif ctl.upper() == "CR":
|
||||||
self._raw(CTL_CR)
|
self._raw(CTL_CR)
|
||||||
elif ctl.upper() == "HT":
|
elif ctl.upper() == "HT":
|
||||||
if not (0 <= count <= 32 and
|
if not (1 <= pos <= 16):
|
||||||
1 <= tab_size <= 255 and
|
|
||||||
count * tab_size < 256):
|
|
||||||
raise TabPosError()
|
raise TabPosError()
|
||||||
else:
|
else:
|
||||||
# Set tab positions
|
# Set tab positions
|
||||||
self._raw(CTL_SET_HT)
|
self._raw(CTL_SET_HT + six.int2byte(pos))
|
||||||
for iterator in range(1, count):
|
|
||||||
self._raw(six.int2byte(iterator * tab_size))
|
self._raw(CTL_HT)
|
||||||
self._raw(NUL)
|
|
||||||
elif ctl.upper() == "VT":
|
elif ctl.upper() == "VT":
|
||||||
self._raw(CTL_VT)
|
self._raw(CTL_VT)
|
||||||
|
|
||||||
@@ -831,58 +699,12 @@ class Escpos(object):
|
|||||||
else:
|
else:
|
||||||
self._raw(PANEL_BUTTON_OFF)
|
self._raw(PANEL_BUTTON_OFF)
|
||||||
|
|
||||||
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(mode)
|
|
||||||
time.sleep(1)
|
|
||||||
status = self._read()
|
|
||||||
return status
|
|
||||||
|
|
||||||
def is_online(self):
|
|
||||||
"""
|
|
||||||
Queries the online status of the printer.
|
|
||||||
|
|
||||||
:returns: When online, returns ``True``; ``False`` otherwise.
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
status = self.query_status(RT_STATUS_ONLINE)
|
|
||||||
if len(status) == 0:
|
|
||||||
return False
|
|
||||||
return not (status[0] & RT_MASK_ONLINE)
|
|
||||||
|
|
||||||
def paper_status(self):
|
|
||||||
"""
|
|
||||||
Queries the paper status of the printer.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
:returns: 2: Paper is adequate. 1: Paper ending. 0: No paper.
|
|
||||||
:rtype: int
|
|
||||||
"""
|
|
||||||
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):
|
class EscposIO(object):
|
||||||
"""ESC/POS Printer IO object
|
"""ESC/POS Printer IO object
|
||||||
|
|
||||||
Allows the class to be used together with the `with`-statement. You have to define a printer instance
|
Allows the class to be used together with the `with`-statement. You have to define a printer instance
|
||||||
and assign it to the EscposIO class.
|
and assign it to the EsposIO-class.
|
||||||
This example explains the usage:
|
This example explains the usage:
|
||||||
|
|
||||||
.. code-block:: Python
|
.. code-block:: Python
|
||||||
|
@@ -8,7 +8,6 @@ Result/Exit codes:
|
|||||||
- `20` = Barcode size values are out of range :py:exc:`~escpos.exceptions.BarcodeSizeError`
|
- `20` = Barcode size values are out of range :py:exc:`~escpos.exceptions.BarcodeSizeError`
|
||||||
- `30` = Barcode text not supplied :py:exc:`~escpos.exceptions.BarcodeCodeError`
|
- `30` = Barcode text not supplied :py:exc:`~escpos.exceptions.BarcodeCodeError`
|
||||||
- `40` = Image height is too large :py:exc:`~escpos.exceptions.ImageSizeError`
|
- `40` = Image height is too large :py:exc:`~escpos.exceptions.ImageSizeError`
|
||||||
- `41` = Image width is too large :py:exc:`~escpos.exceptions.ImageWidthError`
|
|
||||||
- `50` = No string supplied to be printed :py:exc:`~escpos.exceptions.TextError`
|
- `50` = No string supplied to be printed :py:exc:`~escpos.exceptions.TextError`
|
||||||
- `60` = Invalid pin to send Cash Drawer pulse :py:exc:`~escpos.exceptions.CashDrawerError`
|
- `60` = Invalid pin to send Cash Drawer pulse :py:exc:`~escpos.exceptions.CashDrawerError`
|
||||||
- `70` = Invalid number of tab positions :py:exc:`~escpos.exceptions.TabPosError`
|
- `70` = Invalid number of tab positions :py:exc:`~escpos.exceptions.TabPosError`
|
||||||
@@ -77,10 +76,9 @@ class BarcodeSizeError(Error):
|
|||||||
|
|
||||||
|
|
||||||
class BarcodeCodeError(Error):
|
class BarcodeCodeError(Error):
|
||||||
""" No Barcode code was supplied, or it is incorrect.
|
""" No Barcode code was supplied.
|
||||||
|
|
||||||
No data for the barcode has been supplied in :py:meth:`escpos.escpos.Escpos.barcode` or the the `check` parameter
|
No data for the barcode has been supplied in :py:meth:`escpos.escpos.Escpos.barcode`.
|
||||||
was True and the check failed.
|
|
||||||
The returncode for this exception is `30`.
|
The returncode for this exception is `30`.
|
||||||
"""
|
"""
|
||||||
def __init__(self, msg=""):
|
def __init__(self, msg=""):
|
||||||
@@ -106,20 +104,6 @@ class ImageSizeError(Error):
|
|||||||
return "Image height is longer than 255px and can't be printed ({msg})".format(msg=self.msg)
|
return "Image height is longer than 255px and can't be printed ({msg})".format(msg=self.msg)
|
||||||
|
|
||||||
|
|
||||||
class ImageWidthError(Error):
|
|
||||||
""" Image width is too large.
|
|
||||||
|
|
||||||
The return code for this exception is `41`.
|
|
||||||
"""
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
class TextError(Error):
|
class TextError(Error):
|
||||||
""" Text string must be supplied to the `text()` method.
|
""" Text string must be supplied to the `text()` method.
|
||||||
|
|
||||||
@@ -151,8 +135,7 @@ class CashDrawerError(Error):
|
|||||||
|
|
||||||
|
|
||||||
class TabPosError(Error):
|
class TabPosError(Error):
|
||||||
""" Valid tab positions must be set by using from 1 to 32 tabs, and between 1 and 255 tab size values.
|
""" Valid tab positions must be in the range 0 to 16.
|
||||||
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`.
|
This exception is raised by :py:meth:`escpos.escpos.Escpos.control`.
|
||||||
The returncode for this exception is `70`.
|
The returncode for this exception is `70`.
|
||||||
|
@@ -115,19 +115,3 @@ class EscposImage(object):
|
|||||||
box = (left, upper, right, lower)
|
box = (left, upper, right, lower)
|
||||||
fragments.append(self.img_original.crop(box))
|
fragments.append(self.img_original.crop(box))
|
||||||
return fragments
|
return fragments
|
||||||
|
|
||||||
def center(self, max_width):
|
|
||||||
"""In-place image centering
|
|
||||||
|
|
||||||
:param: Maximum width in order to deduce x offset for centering
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
old_width, height = self._im.size
|
|
||||||
new_size = (max_width, height)
|
|
||||||
|
|
||||||
new_im = Image.new("1", new_size)
|
|
||||||
paste_x = int((max_width - old_width) / 2)
|
|
||||||
|
|
||||||
new_im.paste(self._im, (paste_x, 0))
|
|
||||||
|
|
||||||
self._im = new_im
|
|
||||||
|
@@ -34,45 +34,28 @@ class Usb(Escpos):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, idVendor, idProduct, usb_args=None, timeout=0, in_ep=0x82, out_ep=0x01,
|
def __init__(self, idVendor, idProduct, timeout=0, in_ep=0x82, out_ep=0x01, *args, **kwargs): # noqa: N803
|
||||||
*args, **kwargs): # noqa: N803
|
|
||||||
"""
|
"""
|
||||||
:param idVendor: Vendor ID
|
:param idVendor: Vendor ID
|
||||||
:param idProduct: Product 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 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.idProduct = idProduct
|
||||||
self.timeout = timeout
|
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()
|
||||||
|
|
||||||
usb_args = usb_args or {}
|
def open(self):
|
||||||
if idVendor:
|
""" Search device on USB tree and set it as escpos device """
|
||||||
usb_args['idVendor'] = idVendor
|
self.device = usb.core.find(idVendor=self.idVendor, idProduct=self.idProduct)
|
||||||
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:
|
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.")
|
||||||
|
|
||||||
self.idVendor = self.device.idVendor
|
|
||||||
self.idProduct = self.device.idProduct
|
|
||||||
|
|
||||||
# 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
|
check_driver = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -101,10 +84,6 @@ class Usb(Escpos):
|
|||||||
"""
|
"""
|
||||||
self.device.write(self.out_ep, msg, self.timeout)
|
self.device.write(self.out_ep, msg, self.timeout)
|
||||||
|
|
||||||
def _read(self):
|
|
||||||
""" Reads a data buffer and returns it to the caller. """
|
|
||||||
return self.device.read(self.in_ep, 16)
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
""" Release USB interface """
|
""" Release USB interface """
|
||||||
if self.device:
|
if self.device:
|
||||||
@@ -152,8 +131,6 @@ class Serial(Escpos):
|
|||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
""" Setup serial port and set is as escpos device """
|
""" Setup serial port and set is as escpos device """
|
||||||
if self.device is not None and self.device.is_open:
|
|
||||||
self.close()
|
|
||||||
self.device = serial.Serial(port=self.devfile, baudrate=self.baudrate,
|
self.device = serial.Serial(port=self.devfile, baudrate=self.baudrate,
|
||||||
bytesize=self.bytesize, parity=self.parity,
|
bytesize=self.bytesize, parity=self.parity,
|
||||||
stopbits=self.stopbits, timeout=self.timeout,
|
stopbits=self.stopbits, timeout=self.timeout,
|
||||||
@@ -172,13 +149,9 @@ class Serial(Escpos):
|
|||||||
"""
|
"""
|
||||||
self.device.write(msg)
|
self.device.write(msg)
|
||||||
|
|
||||||
def _read(self):
|
|
||||||
""" Reads a data buffer and returns it to the caller. """
|
|
||||||
return self.device.read(16)
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
""" Close Serial interface """
|
""" Close Serial interface """
|
||||||
if self.device is not None and self.device.is_open:
|
if self.device is not None:
|
||||||
self.device.flush()
|
self.device.flush()
|
||||||
self.device.close()
|
self.device.close()
|
||||||
|
|
||||||
@@ -236,11 +209,6 @@ class Network(Escpos):
|
|||||||
"""
|
"""
|
||||||
self.device.sendall(msg)
|
self.device.sendall(msg)
|
||||||
|
|
||||||
def _read(self):
|
|
||||||
""" Read data from the TCP socket """
|
|
||||||
|
|
||||||
return self.device.recv(16)
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
""" Close TCP connection """
|
""" Close TCP connection """
|
||||||
if self.device is not None:
|
if self.device is not None:
|
||||||
@@ -334,58 +302,5 @@ class Dummy(Escpos):
|
|||||||
""" Get the data that was sent to this printer """
|
""" Get the data that was sent to this printer """
|
||||||
return b''.join(self._output_list)
|
return b''.join(self._output_list)
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
""" Clear the buffer of the printer
|
|
||||||
|
|
||||||
This method can be called if you send the contents to a physical printer
|
|
||||||
and want to use the Dummy printer for new output.
|
|
||||||
"""
|
|
||||||
del self._output_list[:]
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
pass
|
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)
|
|
||||||
|
@@ -7,7 +7,7 @@ from __future__ import unicode_literals
|
|||||||
import escpos.printer as printer
|
import escpos.printer as printer
|
||||||
from escpos.constants import BARCODE_TYPE_A, BARCODE_TYPE_B
|
from escpos.constants import BARCODE_TYPE_A, BARCODE_TYPE_B
|
||||||
from escpos.capabilities import Profile, BARCODE_B
|
from escpos.capabilities import Profile, BARCODE_B
|
||||||
from escpos.exceptions import BarcodeTypeError, BarcodeCodeError
|
from escpos.exceptions import BarcodeTypeError
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@@ -36,17 +36,3 @@ def test_lacks_support(bctype, supports_b):
|
|||||||
instance.barcode('test', bctype)
|
instance.barcode('test', bctype)
|
||||||
|
|
||||||
assert instance.output == b''
|
assert instance.output == b''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("bctype,data", [
|
|
||||||
('EAN13', 'AA'),
|
|
||||||
('CODE128', '{D2354AA'),
|
|
||||||
])
|
|
||||||
def test_code_check(bctype, data):
|
|
||||||
"""should raise an error if the barcode code is invalid.
|
|
||||||
"""
|
|
||||||
instance = printer.Dummy()
|
|
||||||
with pytest.raises(BarcodeCodeError):
|
|
||||||
instance.barcode(data, bctype)
|
|
||||||
|
|
||||||
assert instance.output == b''
|
|
||||||
|
@@ -1,19 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import escpos.printer as printer
|
|
||||||
from escpos.exceptions import CashDrawerError
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
def test_raise_CashDrawerError():
|
|
||||||
"""should raise an error if the sequence is invalid.
|
|
||||||
"""
|
|
||||||
instance = printer.Dummy()
|
|
||||||
with pytest.raises(CashDrawerError):
|
|
||||||
# call with sequence that is too long
|
|
||||||
instance.cashdraw([1,1,1,1,1,1])
|
|
||||||
|
|
@@ -1,104 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
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 pytest
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("bctype,data", [
|
|
||||||
('UPC-A', '01234567890'),
|
|
||||||
('UPC-A', '012345678905'),
|
|
||||||
('UPC-E', '01234567'),
|
|
||||||
('UPC-E', '0123456'),
|
|
||||||
('UPC-E', '012345678905'),
|
|
||||||
('EAN13', '0123456789012'),
|
|
||||||
('EAN13', '012345678901'),
|
|
||||||
('EAN8', '01234567'),
|
|
||||||
('EAN8', '0123456'),
|
|
||||||
('CODE39', 'ABC-1234'),
|
|
||||||
('CODE39', 'ABC-1234-$$-+A'),
|
|
||||||
('CODE39', '*WIKIPEDIA*'),
|
|
||||||
('ITF', '010203040506070809'),
|
|
||||||
('ITF', '11221133113344556677889900'),
|
|
||||||
('CODABAR', 'A2030405060B'),
|
|
||||||
('CODABAR', 'C11221133113344556677889900D'),
|
|
||||||
('CODABAR', 'D0D'),
|
|
||||||
('NW7', 'A2030405060B'),
|
|
||||||
('NW7', 'C11221133113344556677889900D'),
|
|
||||||
('NW7', 'D0D'),
|
|
||||||
('CODE93', 'A2030405060B'),
|
|
||||||
('CODE93', '+:$&23-7@$'),
|
|
||||||
('CODE93', 'D0D'),
|
|
||||||
('CODE128', '{A2030405060B'),
|
|
||||||
('CODE128', '{C+:$&23-7@$'),
|
|
||||||
('CODE128', '{B0D'),
|
|
||||||
('GS1-128', '{A2030405060B'),
|
|
||||||
('GS1-128', '{C+:$&23-7@$'),
|
|
||||||
('GS1-128', '{B0D'),
|
|
||||||
('GS1 DATABAR OMNIDIRECTIONAL', '0123456789123'),
|
|
||||||
('GS1 DATABAR TRUNCATED', '0123456789123'),
|
|
||||||
('GS1 DATABAR LIMITED', '0123456789123'),
|
|
||||||
('GS1 DATABAR EXPANDED', '(9A{A20304+-%&06a0B'),
|
|
||||||
('GS1 DATABAR EXPANDED', '(1 {C+:&23-7%'),
|
|
||||||
('GS1 DATABAR EXPANDED', '(00000001234567678'),
|
|
||||||
])
|
|
||||||
def test_check_valid_barcode(bctype, data):
|
|
||||||
assert (printer.Escpos.check_barcode(bctype, data))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("bctype,data", [
|
|
||||||
('UPC-A', '01234567890123'), # too long
|
|
||||||
('UPC-A', '0123456789'), # too short
|
|
||||||
('UPC-A', '72527273-711'), # invalid '-'
|
|
||||||
('UPC-A', 'A12345678901'), # invalid 'A'
|
|
||||||
('UPC-E', '01234567890123'), # too long
|
|
||||||
('UPC-E', '012345'), # too short
|
|
||||||
('UPC-E', '72527-2'), # invalid '-'
|
|
||||||
('UPC-E', 'A123456'), # invalid 'A'
|
|
||||||
('EAN13', '0123456789'), # too short
|
|
||||||
('EAN13', 'A123456789012'), # invalid 'A'
|
|
||||||
('EAN13', '012345678901234'), # too long
|
|
||||||
('EAN8', '012345'), # too short
|
|
||||||
('EAN8', 'A123456789012'), # invalid 'A'
|
|
||||||
('EAN8', '012345678901234'), # too long
|
|
||||||
('CODE39', 'ALKJ_34'), # invalid '_'
|
|
||||||
('CODE39', 'A' * 256), # too long
|
|
||||||
('ITF', '010203040'), # odd length
|
|
||||||
('ITF', '0' * 256), # too long
|
|
||||||
('ITF', 'AB01'), # invalid 'A'
|
|
||||||
('CODABAR', '010203040'), # no start/stop
|
|
||||||
('CODABAR', '0' * 256), # too long
|
|
||||||
('CODABAR', 'AB-01F'), # invalid 'B'
|
|
||||||
('NW7', '010203040'), # no start/stop
|
|
||||||
('NW7', '0' * 256), # too long
|
|
||||||
('NW7', 'AB-01F'), # invalid 'B'
|
|
||||||
('CODE93', 'é010203040'), # invalid 'é'
|
|
||||||
('CODE93', '0' * 256), # too long
|
|
||||||
('CODE128', '010203040'), # missing leading {
|
|
||||||
('CODE128', '{D2354AA'), # second char not between A-C
|
|
||||||
('CODE128', '0' * 256), # too long
|
|
||||||
('GS1-128', '010203040'), # missing leading {
|
|
||||||
('GS1-128', '{D2354AA'), # second char not between A-C
|
|
||||||
('GS1-128', '0' * 256), # too long
|
|
||||||
('GS1 DATABAR OMNIDIRECTIONAL', '01234567891234'), # too long
|
|
||||||
('GS1 DATABAR OMNIDIRECTIONAL', '012345678912'), # too short
|
|
||||||
('GS1 DATABAR OMNIDIRECTIONAL', '012345678A1234'), # invalid 'A'
|
|
||||||
('GS1 DATABAR TRUNCATED', '01234567891234'), # too long
|
|
||||||
('GS1 DATABAR TRUNCATED', '012345678912'), # too short
|
|
||||||
('GS1 DATABAR TRUNCATED', '012345678A1234'), # invalid 'A'
|
|
||||||
('GS1 DATABAR LIMITED', '01234567891234'), # too long
|
|
||||||
('GS1 DATABAR LIMITED', '012345678912'), # too short
|
|
||||||
('GS1 DATABAR LIMITED', '012345678A1234'), # invalid 'A'
|
|
||||||
('GS1 DATABAR LIMITED', '02345678912341'), # invalid start (should be 01)
|
|
||||||
('GS1 DATABAR EXPANDED', '010203040'), # missing leading (
|
|
||||||
('GS1-128', '(' + ('0' * 256)), # too long
|
|
||||||
('GS1 DATABAR EXPANDED', '(a{D2354AA'), # second char not between 0-9
|
|
||||||
('GS1 DATABAR EXPANDED', 'IT will fail'), # first char not '('
|
|
||||||
])
|
|
||||||
def test_check_invalid_barcode(bctype, data):
|
|
||||||
assert (not printer.Escpos.check_barcode(bctype, data))
|
|
@@ -1,17 +0,0 @@
|
|||||||
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)
|
|
@@ -1,8 +0,0 @@
|
|||||||
from nose.tools import assert_raises
|
|
||||||
from escpos.printer import Dummy
|
|
||||||
|
|
||||||
def test_printer_dummy_clear():
|
|
||||||
printer = Dummy()
|
|
||||||
printer.text("Hello")
|
|
||||||
printer.clear()
|
|
||||||
assert(printer.output == b'')
|
|
@@ -12,12 +12,8 @@ from __future__ import division
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
import escpos.printer as printer
|
import escpos.printer as printer
|
||||||
from escpos.exceptions import ImageWidthError
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
# Raster format print
|
# Raster format print
|
||||||
@@ -143,37 +139,3 @@ def test_large_graphics():
|
|||||||
instance = printer.Dummy()
|
instance = printer.Dummy()
|
||||||
instance.image('test/resources/black_white.png', impl="bitImageRaster", fragment_height=1)
|
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')
|
assert(instance.output == b'\x1dv0\x00\x01\x00\x01\x00\xc0\x1dv0\x00\x01\x00\x01\x00\x00')
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def dummy_with_width():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.profile.profile_data = {
|
|
||||||
'media': {
|
|
||||||
'width': {
|
|
||||||
'pixels': 384
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return instance
|
|
||||||
|
|
||||||
|
|
||||||
def test_width_too_large(dummy_with_width):
|
|
||||||
"""
|
|
||||||
Test printing an image that is too large in width.
|
|
||||||
"""
|
|
||||||
instance = dummy_with_width
|
|
||||||
|
|
||||||
with pytest.raises(ImageWidthError):
|
|
||||||
instance.image(Image.new("RGB", (385, 200)))
|
|
||||||
|
|
||||||
instance.image(Image.new("RGB", (384, 200)))
|
|
||||||
|
|
||||||
|
|
||||||
def test_center_image(dummy_with_width):
|
|
||||||
instance = dummy_with_width
|
|
||||||
|
|
||||||
with pytest.raises(ImageWidthError):
|
|
||||||
instance.image(Image.new("RGB", (385, 200)), center=True)
|
|
||||||
|
|
||||||
instance.image(Image.new("RGB", (384, 200)), center=True)
|
|
||||||
|
@@ -1,35 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
"""tests for line display
|
|
||||||
|
|
||||||
:author: `Patrick Kanzler <patrick.kanzler@fablab.fau.de>`_
|
|
||||||
:organization: `python-escpos <https://github.com/python-escpos>`_
|
|
||||||
:copyright: Copyright (c) 2017 `python-escpos <https://github.com/python-escpos>`_
|
|
||||||
:license: MIT
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import escpos.printer as printer
|
|
||||||
|
|
||||||
|
|
||||||
def test_function_linedisplay_select_on():
|
|
||||||
"""test the linedisplay_select function (activate)"""
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.linedisplay_select(select_display=True)
|
|
||||||
assert(instance.output == b'\x1B\x3D\x02')
|
|
||||||
|
|
||||||
def test_function_linedisplay_select_off():
|
|
||||||
"""test the linedisplay_select function (deactivate)"""
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.linedisplay_select(select_display=False)
|
|
||||||
assert(instance.output == b'\x1B\x3D\x01')
|
|
||||||
|
|
||||||
def test_function_linedisplay_clear():
|
|
||||||
"""test the linedisplay_clear function"""
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.linedisplay_clear()
|
|
||||||
assert(instance.output == b'\x1B\x40')
|
|
||||||
|
|
@@ -12,18 +12,43 @@ from __future__ import division
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from nose.tools import with_setup
|
||||||
|
|
||||||
import escpos.printer as printer
|
import escpos.printer as printer
|
||||||
|
import os
|
||||||
|
|
||||||
|
devfile = 'testfile'
|
||||||
|
|
||||||
|
|
||||||
|
def setup_testfile():
|
||||||
|
"""create a testfile as devfile"""
|
||||||
|
fhandle = open(devfile, 'a')
|
||||||
|
try:
|
||||||
|
os.utime(devfile, None)
|
||||||
|
finally:
|
||||||
|
fhandle.close()
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_testfile():
|
||||||
|
"""destroy testfile again"""
|
||||||
|
os.remove(devfile)
|
||||||
|
|
||||||
|
|
||||||
|
@with_setup(setup_testfile, teardown_testfile)
|
||||||
def test_function_panel_button_on():
|
def test_function_panel_button_on():
|
||||||
"""test the panel button function (enabling) by comparing output"""
|
"""test the panel button function (enabling) by comparing output"""
|
||||||
instance = printer.Dummy()
|
instance = printer.File(devfile=devfile)
|
||||||
instance.panel_buttons()
|
instance.panel_buttons()
|
||||||
assert(instance.output == b'\x1B\x63\x35\x00')
|
instance.flush()
|
||||||
|
with open(devfile, "rb") as f:
|
||||||
|
assert(f.read() == b'\x1B\x63\x35\x00')
|
||||||
|
|
||||||
|
|
||||||
|
@with_setup(setup_testfile, teardown_testfile)
|
||||||
def test_function_panel_button_off():
|
def test_function_panel_button_off():
|
||||||
"""test the panel button function (disabling) by comparing output"""
|
"""test the panel button function (disabling) by comparing output"""
|
||||||
instance = printer.Dummy()
|
instance = printer.File(devfile=devfile)
|
||||||
instance.panel_buttons(False)
|
instance.panel_buttons(False)
|
||||||
assert(instance.output == b'\x1B\x63\x35\x01')
|
instance.flush()
|
||||||
|
with open(devfile, "rb") as f:
|
||||||
|
assert(f.read() == b'\x1B\x63\x35\x01')
|
||||||
|
@@ -13,8 +13,6 @@ from __future__ import print_function
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from nose.tools import raises
|
from nose.tools import raises
|
||||||
import pytest
|
|
||||||
|
|
||||||
import escpos.printer as printer
|
import escpos.printer as printer
|
||||||
from escpos.constants import QR_ECLEVEL_H, QR_MODEL_1
|
from escpos.constants import QR_ECLEVEL_H, QR_MODEL_1
|
||||||
|
|
||||||
@@ -27,6 +25,7 @@ def test_defaults():
|
|||||||
b'(k\x07\x001P01234\x1d(k\x03\x001Q0'
|
b'(k\x07\x001P01234\x1d(k\x03\x001Q0'
|
||||||
assert(instance.output == expected)
|
assert(instance.output == expected)
|
||||||
|
|
||||||
|
|
||||||
def test_empty():
|
def test_empty():
|
||||||
"""Test QR printing blank code"""
|
"""Test QR printing blank code"""
|
||||||
instance = printer.Dummy()
|
instance = printer.Dummy()
|
||||||
@@ -82,17 +81,14 @@ def test_invalid_model():
|
|||||||
instance.qr("1234", native=True, model="Hello")
|
instance.qr("1234", native=True, model="Hello")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip("this test has to be debugged")
|
|
||||||
def test_image():
|
def test_image():
|
||||||
"""Test QR as image"""
|
"""Test QR as image"""
|
||||||
instance = printer.Dummy()
|
instance = printer.Dummy()
|
||||||
instance.qr("1", native=False, size=1)
|
instance.qr("1", native=False, size=1)
|
||||||
print(instance.output)
|
print(instance.output)
|
||||||
expected = b'\x1bt\x00\n' \
|
expected = b'\x1dv0\x00\x03\x00\x17\x00\x00\x00\x00\x7f]\xfcA\x19\x04]it]et' \
|
||||||
b'\x1dv0\x00\x03\x00\x17\x00\x00\x00\x00\x7f]\xfcA\x19\x04]it]et' \
|
|
||||||
b']ItA=\x04\x7fU\xfc\x00\x0c\x00y~t4\x7f =\xa84j\xd9\xf0\x05\xd4\x90\x00' \
|
b']ItA=\x04\x7fU\xfc\x00\x0c\x00y~t4\x7f =\xa84j\xd9\xf0\x05\xd4\x90\x00' \
|
||||||
b'i(\x7f<\xa8A \xd8]\'\xc4]y\xf8]E\x80Ar\x94\x7fR@\x00\x00\x00' \
|
b'i(\x7f<\xa8A \xd8]\'\xc4]y\xf8]E\x80Ar\x94\x7fR@\x00\x00\x00'
|
||||||
b'\n\n'
|
|
||||||
assert(instance.output == expected)
|
assert(instance.output == expected)
|
||||||
|
|
||||||
|
|
||||||
@@ -101,13 +97,3 @@ def test_image_invalid_model():
|
|||||||
"""Test unsupported QR model as image"""
|
"""Test unsupported QR model as image"""
|
||||||
instance = printer.Dummy()
|
instance = printer.Dummy()
|
||||||
instance.qr("1234", native=False, model=QR_MODEL_1)
|
instance.qr("1234", native=False, model=QR_MODEL_1)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def instance():
|
|
||||||
return printer.Dummy()
|
|
||||||
|
|
||||||
|
|
||||||
def test_center_not_implementer(instance):
|
|
||||||
with pytest.raises(NotImplementedError):
|
|
||||||
instance.qr("test", center=True, native=True)
|
|
||||||
|
@@ -13,7 +13,6 @@ from __future__ import division
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import pytest
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from escpos.printer import Dummy
|
from escpos.printer import Dummy
|
||||||
@@ -31,12 +30,3 @@ def test_type_of_object_passed_to_image_function(img_function):
|
|||||||
d.qr("LoremIpsum")
|
d.qr("LoremIpsum")
|
||||||
args, kwargs = img_function.call_args
|
args, kwargs = img_function.call_args
|
||||||
assert isinstance(args[0], Image.Image)
|
assert isinstance(args[0], Image.Image)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def instance():
|
|
||||||
return Dummy()
|
|
||||||
|
|
||||||
|
|
||||||
def test_center(instance):
|
|
||||||
instance.qr("LoremIpsum", center=True)
|
|
||||||
|
@@ -1,280 +0,0 @@
|
|||||||
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 TXT_NORMAL, TXT_STYLE, SET_FONT
|
|
||||||
from escpos.constants import TXT_SIZE
|
|
||||||
|
|
||||||
|
|
||||||
# Default test, please copy and paste this block to test set method calls
|
|
||||||
|
|
||||||
def test_default_values():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set()
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert(instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
# Size tests
|
|
||||||
|
|
||||||
def test_set_size_2h():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(double_height=True)
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['2h'], # Double height text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_size_2w():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(double_width=True)
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['2w'], # Double width text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_size_2x():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(double_height=True, double_width=True)
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['2x'], # Double text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_size_custom():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(custom_size=True, width=8, height=7)
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_SIZE, # Custom text size, no normal reset
|
|
||||||
six.int2byte(TXT_STYLE['width'][8] + TXT_STYLE['height'][7]),
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
# Flip
|
|
||||||
|
|
||||||
def test_set_flip():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(flip=True)
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size
|
|
||||||
TXT_STYLE['flip'][True], # Flip ON
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
# Smooth
|
|
||||||
|
|
||||||
def test_smooth():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(smooth=True)
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][True], # Smooth ON
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert(instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
|
|
||||||
# Type
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_bold():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(bold=True)
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][True], # Bold ON
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_underline():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(underline=1)
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][1], # Underline ON, type 1
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_underline2():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(underline=2)
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][2], # Underline ON, type 2
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
|
|
||||||
# Align
|
|
||||||
|
|
||||||
def test_align_center():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(align='center')
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['center'], # Align center
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert(instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
|
|
||||||
def test_align_right():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(align='right')
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['right'], # Align right
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert(instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
|
|
||||||
# Densities
|
|
||||||
|
|
||||||
def test_densities():
|
|
||||||
|
|
||||||
for density in range(8):
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(density=density)
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['density'][density], # Custom density from 0 to 8
|
|
||||||
TXT_STYLE['invert'][False] # Inverted OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
assert(instance.output == b''.join(expected_sequence))
|
|
||||||
|
|
||||||
|
|
||||||
# Invert
|
|
||||||
|
|
||||||
def test_invert():
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.set(invert=True)
|
|
||||||
|
|
||||||
expected_sequence = (
|
|
||||||
TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size
|
|
||||||
TXT_STYLE['flip'][False], # Flip OFF
|
|
||||||
TXT_STYLE['smooth'][False], # Smooth OFF
|
|
||||||
TXT_STYLE['bold'][False], # Bold OFF
|
|
||||||
TXT_STYLE['underline'][0], # Underline OFF
|
|
||||||
SET_FONT(b'\x00'), # Default font
|
|
||||||
TXT_STYLE['align']['left'], # Align left
|
|
||||||
TXT_STYLE['invert'][True] # Inverted ON
|
|
||||||
)
|
|
||||||
|
|
||||||
assert(instance.output == b''.join(expected_sequence))
|
|
@@ -1,16 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
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 pytest
|
|
||||||
|
|
||||||
|
|
||||||
def test_soft_barcode():
|
|
||||||
"""just execute soft_barcode
|
|
||||||
"""
|
|
||||||
instance = printer.Dummy()
|
|
||||||
instance.soft_barcode("ean8", "1234")
|
|
||||||
|
|
@@ -39,27 +39,3 @@ def test_block_text():
|
|||||||
"All the presidents men were eating falafel for breakfast.", font='a')
|
"All the presidents men were eating falafel for breakfast.", font='a')
|
||||||
assert printer.output == \
|
assert printer.output == \
|
||||||
b'All the presidents men were eating falafel\nfor breakfast.'
|
b'All the presidents men were eating falafel\nfor breakfast.'
|
||||||
|
|
||||||
|
|
||||||
def test_textln():
|
|
||||||
printer = get_printer()
|
|
||||||
printer.textln('hello, world')
|
|
||||||
assert printer.output == b'hello, world\n'
|
|
||||||
|
|
||||||
|
|
||||||
def test_textln_empty():
|
|
||||||
printer = get_printer()
|
|
||||||
printer.textln()
|
|
||||||
assert printer.output == b'\n'
|
|
||||||
|
|
||||||
|
|
||||||
def test_ln():
|
|
||||||
printer = get_printer()
|
|
||||||
printer.ln()
|
|
||||||
assert printer.output == b'\n'
|
|
||||||
|
|
||||||
|
|
||||||
def test_multiple_ln():
|
|
||||||
printer = get_printer()
|
|
||||||
printer.ln(3)
|
|
||||||
assert printer.output == b'\n\n\n'
|
|
||||||
|
@@ -12,10 +12,30 @@ from __future__ import division
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from nose.tools import with_setup
|
||||||
|
|
||||||
import escpos.printer as printer
|
import escpos.printer as printer
|
||||||
|
import os
|
||||||
|
|
||||||
|
devfile = 'testfile'
|
||||||
|
|
||||||
|
|
||||||
|
def setup_testfile():
|
||||||
|
"""create a testfile as devfile"""
|
||||||
|
fhandle = open(devfile, 'a')
|
||||||
|
try:
|
||||||
|
os.utime(devfile, None)
|
||||||
|
finally:
|
||||||
|
fhandle.close()
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_testfile():
|
||||||
|
"""destroy testfile again"""
|
||||||
|
os.remove(devfile)
|
||||||
|
|
||||||
|
|
||||||
|
@with_setup(setup_testfile, teardown_testfile)
|
||||||
def test_instantiation():
|
def test_instantiation():
|
||||||
"""test the instantiation of a escpos-printer class and basic printing"""
|
"""test the instantiation of a escpos-printer class and basic printing"""
|
||||||
instance = printer.Dummy()
|
instance = printer.File(devfile=devfile)
|
||||||
instance.text('This is a test\n')
|
instance.text('This is a test\n')
|
||||||
|
@@ -16,7 +16,7 @@ from __future__ import unicode_literals
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from hypothesis import given, settings
|
from hypothesis import given
|
||||||
from hypothesis.strategies import text
|
from hypothesis.strategies import text
|
||||||
|
|
||||||
import escpos.printer as printer
|
import escpos.printer as printer
|
||||||
@@ -27,8 +27,6 @@ else:
|
|||||||
mock_open_call = '__builtin__.open'
|
mock_open_call = '__builtin__.open'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip("this test is broken and has to be fixed or discarded")
|
|
||||||
@settings(use_coverage=False)
|
|
||||||
@given(path=text())
|
@given(path=text())
|
||||||
def test_load_file_printer(mocker, path):
|
def test_load_file_printer(mocker, path):
|
||||||
"""test the loading of the file-printer"""
|
"""test the loading of the file-printer"""
|
||||||
@@ -39,8 +37,6 @@ def test_load_file_printer(mocker, path):
|
|||||||
mock_open.assert_called_with(path, "wb")
|
mock_open.assert_called_with(path, "wb")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip("this test is broken and has to be fixed or discarded")
|
|
||||||
@settings(deadline=None, use_coverage=False)
|
|
||||||
@given(txt=text())
|
@given(txt=text())
|
||||||
def test_auto_flush(mocker, txt):
|
def test_auto_flush(mocker, txt):
|
||||||
"""test auto_flush in file-printer"""
|
"""test auto_flush in file-printer"""
|
||||||
@@ -61,8 +57,6 @@ def test_auto_flush(mocker, txt):
|
|||||||
assert mock_device.flush.called
|
assert mock_device.flush.called
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip("this test is broken and has to be fixed or discarded")
|
|
||||||
@settings(deadline=None, use_coverage=False)
|
|
||||||
@given(txt=text())
|
@given(txt=text())
|
||||||
def test_flush_on_close(mocker, txt):
|
def test_flush_on_close(mocker, txt):
|
||||||
"""test flush on close in file-printer"""
|
"""test flush on close in file-printer"""
|
||||||
|
@@ -1,32 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
"""test the raising of errors with the error module
|
|
||||||
|
|
||||||
:author: `Patrick Kanzler <patrick.kanzler@fablab.fau.de>`_
|
|
||||||
:organization: `python-escpos <https://github.com/python-escpos>`_
|
|
||||||
:copyright: Copyright (c) 2017 `python-escpos <https://github.com/python-escpos>`_
|
|
||||||
:license: MIT
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
import escpos
|
|
||||||
import escpos.exceptions
|
|
||||||
|
|
||||||
|
|
||||||
def test_raise_error_wrongly():
|
|
||||||
"""raise error the wrong way
|
|
||||||
|
|
||||||
should reproduce https://github.com/python-escpos/python-escpos/issues/257
|
|
||||||
"""
|
|
||||||
with pytest.raises(AttributeError):
|
|
||||||
raise escpos.Error("This should raise an AttributeError.")
|
|
||||||
|
|
||||||
|
|
||||||
def tests_raise_error():
|
|
||||||
"""raise error the right way"""
|
|
||||||
with pytest.raises(escpos.exceptions.Error):
|
|
||||||
raise escpos.exceptions.Error("This should raise an error.")
|
|
9
tox.ini
@@ -1,5 +1,5 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist = py27, py34, py35, py36, py37, docs, flake8
|
envlist = py27, py34, py35, docs, flake8
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps = nose
|
deps = nose
|
||||||
@@ -7,20 +7,17 @@ deps = nose
|
|||||||
coverage
|
coverage
|
||||||
scripttest
|
scripttest
|
||||||
mock
|
mock
|
||||||
pytest!=3.2.0,!=3.3.0
|
pytest
|
||||||
pytest-cov
|
pytest-cov
|
||||||
pytest-mock
|
pytest-mock
|
||||||
hypothesis!=3.56.9,<4
|
hypothesis
|
||||||
viivakoodi
|
|
||||||
commands = py.test --cov escpos
|
commands = py.test --cov escpos
|
||||||
passenv = ESCPOS_CAPABILITIES_PICKLE_DIR ESCPOS_CAPABILITIES_FILE CI TRAVIS TRAVIS_* APPVEYOR APPVEYOR_* CODECOV_*
|
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
basepython = python
|
basepython = python
|
||||||
changedir = doc
|
changedir = doc
|
||||||
deps = sphinx>=1.5.1
|
deps = sphinx>=1.5.1
|
||||||
setuptools_scm
|
setuptools_scm
|
||||||
viivakoodi
|
|
||||||
commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
|
commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
|
||||||
|
|
||||||
[testenv:flake8]
|
[testenv:flake8]
|
||||||
|